Annotation of embedaddon/libpdel/tmpl/tmpl_exec.c, revision 1.1.1.1
1.1 misho 1:
2: /*
3: * Copyright (c) 2001-2002 Packet Design, LLC.
4: * All rights reserved.
5: *
6: * Subject to the following obligations and disclaimer of warranty,
7: * use and redistribution of this software, in source or object code
8: * forms, with or without modifications are expressly permitted by
9: * Packet Design; provided, however, that:
10: *
11: * (i) Any and all reproductions of the source or object code
12: * must include the copyright notice above and the following
13: * disclaimer of warranties; and
14: * (ii) No rights are granted, in any manner or form, to use
15: * Packet Design trademarks, including the mark "PACKET DESIGN"
16: * on advertising, endorsements, or otherwise except as such
17: * appears in the above copyright notice or in the software.
18: *
19: * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
20: * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
21: * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
22: * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
23: * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
24: * OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
25: * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
26: * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
27: * RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE
28: * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
29: * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
30: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
31: * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
32: * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
33: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
35: * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
36: * THE POSSIBILITY OF SUCH DAMAGE.
37: *
38: * Author: Archie Cobbs <archie@freebsd.org>
39: */
40:
41: #include "tmpl_internal.h"
42:
43: /*
44: * Internal functions
45: */
46: static char *tmpl_invoke_defined(struct tmpl_ctx *ctx,
47: struct exec_func *func, char **errmsgp,
48: int argc, char **argv);
49: static int tmpl_invoke_defined2(struct tmpl_ctx *ctx,
50: struct exec_func *func, char **errmsgp,
51: int argc, char **argv);
52: static void tmpl_exec_cleanup(void *arg);
53: static char *evaluate_arg(struct tmpl_ctx *ctx,
54: char **errmsgp, const struct func_arg *arg);
55: static void pr_err(struct tmpl_ctx *ctx, int errnum, const char *msg);
56: #if TMPL_DEBUG
57: static const char *tmpl_rtnstr(enum exec_rtn rtn);
58: #endif
59:
60: /*
61: * Execute a template.
62: */
63: int
64: tmpl_execute(struct tmpl *tmpl, struct tmpl_ctx *ctx, FILE *output, int flags)
65: {
66: /* Avoid reentrant execution with the same context */
67: if (ctx->output != NULL) {
68: errno = EBUSY;
69: return (-1);
70: }
71:
72: /* Allow output to be NULL */
73: if (output == NULL) {
74: if ((output = fopen(_PATH_DEVNULL, "w")) == NULL)
75: return (-1);
76: ctx->close_output = 1;
77: } else
78: ctx->close_output = 0;
79:
80: /* Push cleanup hook */
81: pthread_cleanup_push(tmpl_exec_cleanup, ctx);
82:
83: /* Set up context info for this run */
84: ctx->output = output;
85: ctx->orig_output = output;
86: ctx->flags = flags;
87:
88: /* Execute template */
89: _tmpl_execute_elems(ctx, tmpl->elems, 0, tmpl->num_elems);
90:
91: /* Finish up */
92: pthread_cleanup_pop(1);
93:
94: /* Done */
95: return (0);
96: }
97:
98: /*
99: * Clean up a context after execution.
100: */
101: static void
102: tmpl_exec_cleanup(void *arg)
103: {
104: struct tmpl_ctx *const ctx = arg;
105:
106: /* Close output if needed */
107: if (ctx->close_output)
108: fclose(ctx->output);
109: ctx->output = NULL;
110: ctx->orig_output = NULL;
111: ctx->flags = 0;
112: }
113:
114: /*
115: * Invoke a single template function.
116: */
117: int
118: tmpl_execute_func(struct tmpl_ctx *ctx, FILE *output,
119: char **errmsgp, int argc, char **argv, int flags)
120: {
121: struct tmpl *t;
122: char *input;
123: FILE *sbuf;
124: int rtn;
125: int i;
126:
127: /* Avoid reentrant execution with the same context */
128: if (ctx->output != NULL) {
129: errno = EBUSY;
130: return (-1);
131: }
132:
133: /* Initialize error string */
134: *errmsgp = NULL;
135:
136: /* Sanity */
137: if (argc < 1) {
138: errno = EINVAL;
139: return (-1);
140: }
141:
142: /* Create template input containing the function call */
143: if ((sbuf = string_buf_output(TYPED_MEM_TEMP)) == NULL)
144: return (-1);
145: fprintf(sbuf, "@%s(", argv[0]);
146: for (i = 1; i < argc; i++) {
147: char *qarg;
148:
149: /* Enquote and add argument */
150: if ((qarg = string_enquote(argv[i], TYPED_MEM_TEMP)) == NULL) {
151: fclose(sbuf);
152: return (-1);
153: }
154: fprintf(sbuf, "%s%s", i > 1 ? "," : "", qarg);
155: FREE(TYPED_MEM_TEMP, qarg);
156: }
157: fprintf(sbuf, ")");
158: if ((input = string_buf_content(sbuf, 1)) == NULL) {
159: fclose(sbuf);
160: return (-1);
161: }
162: fclose(sbuf);
163:
164: /* Create a new template from the input string */
165: if ((sbuf = string_buf_input(input, strlen(input), 0)) == NULL) {
166: FREE(TYPED_MEM_TEMP, input);
167: return (-1);
168: }
169: t = tmpl_create(sbuf, NULL, TYPED_MEM_TEMP);
170: fclose(sbuf);
171: FREE(TYPED_MEM_TEMP, input);
172: if (t == NULL)
173: return (-1);
174:
175: /* Execute template */
176: rtn = tmpl_execute(t, ctx, output, flags);
177:
178: /* Clean up */
179: tmpl_destroy(&t);
180: return (rtn);
181: }
182:
183: /*
184: * Execute elements.
185: */
186: enum exec_rtn
187: _tmpl_execute_elems(struct tmpl_ctx *ctx,
188: struct tmpl_elem *const elems, int first_elem, int nelems)
189: {
190: #if TMPL_DEBUG
191: char indent[80];
192: #endif
193: enum exec_rtn rtn;
194: int i;
195:
196: /* Increase nesting */
197: ctx->depth++;
198:
199: /* Check for infinite loop */
200: if (ctx->depth >= INFINITE_LOOP) {
201: pr_err(ctx, 0, "too much recursion in template execution");
202: rtn = RTN_NORMAL;
203: goto done;
204: }
205:
206: /* Debug */
207: #if TMPL_DEBUG
208: *indent = '\0';
209: for (i = 1; i < ctx->depth; i++)
210: strlcat(indent, " ", sizeof(indent));
211: DBG("%sExecuting %d elems starting at %d\n",
212: indent, nelems, first_elem);
213: #endif
214:
215: /* Execute elements in order */
216: for (i = first_elem; i < first_elem + nelems; i++) {
217: struct tmpl_elem *const elem = &elems[i];
218:
219: DBG("%s%4d: %s\n", indent, i, _tmpl_elemstr(&elems[i], i));
220:
221: /* Handle normal text */
222: if (elem->text != NULL) {
223:
224: /* Optionally skip initial NL + whitespace */
225: if (((elem->flags & TMPL_ELEM_NL_WHITE) != 0
226: && ctx->flags & TMPL_SKIP_NL_WHITE) != 0)
227: continue;
228:
229: /* Output text */
230: fwrite(elem->text, 1, elem->len, ctx->output);
231: continue;
232: }
233:
234: /* Handle function call */
235: switch (elem->call.type) {
236: case TY_NORMAL:
237: {
238: char *errmsg = NULL;
239: char *result;
240:
241: if ((result = _tmpl_invoke(ctx,
242: &errmsg, &elem->call)) == NULL) {
243: pr_err(ctx, errno, errmsg);
244: FREE(ctx->mtype, errmsg);
245: break;
246: }
247: DBG("%s --> \"%s\"\n", indent, result);
248: (void)fputs(result, ctx->output);
249: FREE(ctx->mtype, result);
250: break;
251: }
252:
253: case TY_LOOP:
254: {
255: struct loop_ctx this;
256: char *errmsg = NULL;
257: long count;
258: char *eptr;
259: char *x;
260:
261: if (!(x = evaluate_arg(ctx,
262: &errmsg, &elem->call.args[0]))) {
263: pr_err(ctx, errno, errmsg);
264: FREE(ctx->mtype, errmsg);
265: i += elem->u.u_loop.endloop;
266: break;
267: }
268: count = strtoul(x, &eptr, 10);
269: if (*x == '\0' || *eptr != '\0'
270: || count < 0 || count == LONG_MAX) {
271: char buf[32];
272:
273: snprintf(buf, sizeof(buf),
274: "invalid loop count \"%s\"", x);
275: pr_err(ctx, 0, buf);
276: FREE(ctx->mtype, x);
277: i += elem->u.u_loop.endloop;
278: break;
279: }
280: FREE(ctx->mtype, x);
281: this.outer = ctx->loop;
282: ctx->loop = &this;
283: for (this.index = 0; this.index < count; this.index++) {
284: rtn = _tmpl_execute_elems(ctx, elems,
285: i + 1, elem->u.u_loop.endloop - 1);
286: if (rtn == RTN_BREAK)
287: break;
288: if (rtn == RTN_RETURN) {
289: ctx->loop = this.outer;
290: goto done;
291: }
292: }
293: ctx->loop = this.outer;
294: i += elem->u.u_loop.endloop;
295: break;
296: }
297:
298: case TY_WHILE:
299: while (1) {
300: char *errmsg = NULL;
301: int truth;
302: char *x;
303:
304: if (!(x = evaluate_arg(ctx,
305: &errmsg, &elem->call.args[0]))) {
306: pr_err(ctx, errno, errmsg);
307: FREE(ctx->mtype, errmsg);
308: i += elem->u.u_while.endwhile;
309: break;
310: }
311: truth = _tmpl_true(x);
312: FREE(ctx->mtype, x);
313: if (!truth)
314: break;
315: rtn = _tmpl_execute_elems(ctx, elems, i + 1,
316: elem->u.u_while.endwhile - 1);
317: if (rtn == RTN_BREAK)
318: break;
319: if (rtn == RTN_RETURN)
320: goto done;
321: }
322: i += elem->u.u_while.endwhile;
323: break;
324:
325: case TY_IF:
326: case TY_ELIF:
327: {
328: char *errmsg = NULL;
329: int first = -1;
330: int num = 0;
331: int truth;
332: char *x;
333:
334: if (!(x = evaluate_arg(ctx,
335: &errmsg, &elem->call.args[0]))) {
336: pr_err(ctx, errno, errmsg);
337: FREE(ctx->mtype, errmsg);
338: i += elem->u.u_if.endif;
339: break;
340: }
341: truth = _tmpl_true(x);
342: FREE(ctx->mtype, x);
343: if (truth) {
344: first = i + 1;
345: num = (elem->u.u_if.elsie != -1) ?
346: elem->u.u_if.elsie - 1 :
347: elem->u.u_if.endif - 1;
348: } else if (elem->u.u_if.elsie != -1) {
349: first = i + elem->u.u_if.elsie;
350: num = elem->u.u_if.endif - elem->u.u_if.elsie;
351: }
352: if (first != -1) {
353: rtn = _tmpl_execute_elems(ctx,
354: elems, first, num);
355: if (rtn == RTN_BREAK
356: || rtn == RTN_RETURN
357: || rtn == RTN_CONTINUE)
358: goto done;
359: }
360: i += elem->u.u_if.endif;
361: break;
362: }
363:
364: case TY_DEFINE:
365: {
366: char *errmsg = NULL;
367: char *name;
368:
369: if (!(name = evaluate_arg(ctx,
370: &errmsg, &elem->call.args[0]))) {
371: pr_err(ctx, errno, errmsg);
372: FREE(ctx->mtype, errmsg);
373: i += elem->u.u_define.enddef;
374: break;
375: }
376: #ifdef TMPL_DEBUG
377: DBG("%s%6sDefining function \"%s\" as %d elems"
378: " starting at %d\n", indent, "", name,
379: elem->u.u_define.enddef - 1, i + 1);
380: #endif
381: if (_tmpl_set_func(ctx, name, elems + i + 1,
382: elem->u.u_define.enddef - 1) == -1) {
383: pr_err(ctx, errno, NULL);
384: FREE(ctx->mtype, name);
385: i += elem->u.u_define.enddef;
386: break;
387: }
388: FREE(ctx->mtype, name);
389: i += elem->u.u_define.enddef;
390: break;
391: }
392:
393: case TY_ENDLOOP:
394: case TY_ENDWHILE:
395: case TY_ELSE:
396: case TY_ENDIF:
397: case TY_ENDDEF:
398: break;
399:
400: case TY_BREAK:
401: rtn = RTN_BREAK;
402: goto done;
403:
404: case TY_CONTINUE:
405: rtn = RTN_CONTINUE;
406: goto done;
407:
408: case TY_RETURN:
409: rtn = RTN_RETURN;
410: goto done;
411:
412: default:
413: assert(0);
414: }
415: }
416:
417: /* If we finished all the elements, that's a normal return */
418: rtn = RTN_NORMAL;
419:
420: done:
421: /* Debug */
422: #if TMPL_DEBUG
423: DBG("%sDone, return is %s\n", indent, tmpl_rtnstr(rtn));
424: #endif
425:
426: /* Decrease nesting */
427: ctx->depth--;
428:
429: /* Done */
430: return (rtn);
431: }
432:
433: /*
434: * Invoke a function and return the result. The function is
435: * assumed to not be a special built-in (eg, "@if").
436: */
437: char *
438: _tmpl_invoke(struct tmpl_ctx *ctx, char **errmsgp, const struct func_call *call)
439: {
440: char **args_copy = NULL;
441: char **args = NULL;
442: char *r = NULL;
443: int i;
444:
445: /* Evaluate arguments; first function argument is the function name */
446: if ((args = MALLOC(ctx->mtype,
447: (1 + call->nargs) * sizeof(*args))) == NULL)
448: return (NULL);
449: memset(args, 0, (1 + call->nargs) * sizeof(*args));
450: if ((args[0] = STRDUP(ctx->mtype, call->funcname)) == NULL)
451: goto fail;
452: for (i = 0; i < call->nargs; i++) {
453: if ((args[i + 1] = evaluate_arg(ctx,
454: errmsgp, &call->args[i])) == NULL)
455: goto fail;
456: }
457:
458: /* Save a copy of argument pointers so handlers can modify them */
459: if ((args_copy = MALLOC(ctx->mtype,
460: (1 + call->nargs) * sizeof(*args))) == NULL)
461: return (NULL);
462: memcpy(args_copy, args, (1 + call->nargs) * sizeof(*args));
463:
464: /* Invoke function, either run-time defined, built-in, or user */
465: assert(call->type == TY_NORMAL);
466: if ((i = _tmpl_find_func(ctx, call->funcname)) != -1) {
467: r = tmpl_invoke_defined(ctx, &ctx->funcs[i],
468: errmsgp, 1 + call->nargs, args_copy);
469: } else if (call->handler != NULL)
470: r = (*call->handler)(ctx, errmsgp, 1 + call->nargs, args_copy);
471: else
472: r = (*ctx->handler)(ctx, errmsgp, 1 + call->nargs, args_copy);
473:
474: fail:
475: for (i = 0; i < 1 + call->nargs; i++)
476: FREE(ctx->mtype, args[i]);
477: FREE(ctx->mtype, args);
478: FREE(ctx->mtype, args_copy);
479: return (r);
480: }
481:
482: /*
483: * Invoke a run-time defined function and return the resulting
484: * output as a string.
485: */
486: static char *
487: tmpl_invoke_defined(struct tmpl_ctx *ctx, struct exec_func *func,
488: char **errmsgp, int argc, char **argv)
489: {
490: FILE *const output_save = ctx->output;
491: char *rtn = NULL;
492: int esave;
493:
494: /* Create string output buffer to capture output */
495: if ((ctx->output = string_buf_output(ctx->mtype)) == NULL)
496: goto fail;
497:
498: /* Invoke function using existing context */
499: if (tmpl_invoke_defined2(ctx, func, errmsgp, argc, argv) == -1)
500: goto fail;
501:
502: /* Return the resulting output as a string */
503: rtn = string_buf_content(ctx->output, 1);
504:
505: fail:
506: /* Clean up and return */
507: esave = errno;
508: if (ctx->output != NULL)
509: fclose(ctx->output);
510: ctx->output = output_save;
511: errno = esave;
512: return (rtn);
513: }
514:
515: /*
516: * Invoke a runtime-defined function.
517: */
518: static int
519: tmpl_invoke_defined2(struct tmpl_ctx *ctx,
520: struct exec_func *func, char **errmsgp, int argc, char **argv)
521: {
522: char **saved_vars;
523: int rtn = 0;
524: int i;
525:
526: /* Save variables argc and arg0, arg1, ... */
527: if ((saved_vars = MALLOC(ctx->mtype,
528: (argc + 1) * sizeof(*saved_vars))) == NULL)
529: return (-1);
530: memset(saved_vars, 0, (argc + 1) * sizeof(*saved_vars));
531: for (i = 0; i <= argc; i++) {
532: char namebuf[32];
533: int j;
534:
535: if (i == argc)
536: strlcpy(namebuf, "argc", sizeof(namebuf));
537: else
538: snprintf(namebuf, sizeof(namebuf), "arg%u", i);
539: if ((j = _tmpl_find_var(ctx, namebuf)) != -1) {
540: if ((saved_vars[i] = STRDUP(ctx->mtype,
541: ctx->vars[j].value)) == NULL) {
542: while (--i >= 0)
543: FREE(ctx->mtype, saved_vars[i]);
544: FREE(ctx->mtype, saved_vars);
545: return (-1);
546: }
547: }
548: }
549:
550: /* Set new values for those variables */
551: for (i = 0; i <= argc; i++) {
552: char namebuf[32], valbuf[32];
553: char *argval;
554:
555: if (i == argc) {
556: strlcpy(namebuf, "argc", sizeof(namebuf));
557: snprintf(valbuf, sizeof(valbuf), "%u", argc);
558: argval = valbuf;
559: } else {
560: snprintf(namebuf, sizeof(namebuf), "arg%u", i);
561: argval = argv[i];
562: }
563: if (tmpl_ctx_set_var(ctx, namebuf, argval) == -1) {
564: rtn = -1;
565: goto fail;
566: }
567: }
568:
569: /* Execute function elements */
570: _tmpl_execute_elems(ctx, func->elems, 0, func->num_elems);
571:
572: fail:
573: /* Restore saved variables */
574: for (i = 0; i <= argc; i++) {
575: char argname[32];
576: int j;
577:
578: if (i == argc)
579: strlcpy(argname, "argc", sizeof(argname));
580: else
581: snprintf(argname, sizeof(argname), "arg%u", i);
582: if ((j = _tmpl_find_var(ctx, argname)) == -1) {
583: FREE(ctx->mtype, saved_vars[i]);
584: continue; /* we get here only in the 'goto fail' case */
585: }
586: if (saved_vars[i] != NULL) {
587: FREE(ctx->mtype, ctx->vars[j].value);
588: ctx->vars[j].value = saved_vars[i];
589: } else {
590: FREE(ctx->mtype, ctx->vars[j].name);
591: FREE(ctx->mtype, ctx->vars[j].value);
592: memmove(ctx->vars + j, ctx->vars + j + 1,
593: (--ctx->num_vars - j) * sizeof(*ctx->vars));
594: }
595: }
596: FREE(ctx->mtype, saved_vars);
597: return (rtn);
598: }
599:
600: /*
601: * Evaluate a function argument.
602: */
603: static char *
604: evaluate_arg(struct tmpl_ctx *ctx, char **errmsgp, const struct func_arg *arg)
605: {
606: if (arg->is_literal)
607: return (STRDUP(ctx->mtype, arg->u.literal));
608: else
609: return (_tmpl_invoke(ctx, errmsgp, &arg->u.call));
610: }
611:
612: /*
613: * Output an error string.
614: *
615: * If "msg" is not NULL, use that, otherwise use strerror(error).
616: */
617: static void
618: pr_err(struct tmpl_ctx *ctx, int error, const char *msg)
619: {
620: const int errno_save = errno;
621: char *cooked;
622:
623: /* Use strerror() error string if none more specific provided */
624: if (msg == NULL)
625: msg = strerror(errno);
626:
627: /* Format error string (if possible) and output it */
628: if (ctx->errfmtr != NULL
629: && (cooked = (*ctx->errfmtr)(ctx, msg)) != NULL) {
630: (void)fputs(cooked, ctx->output);
631: FREE(ctx->mtype, cooked);
632: } else
633: (void)fputs(msg, ctx->output);
634: errno = errno_save;
635: }
636:
637: /*
638: * Determine truth of a string.
639: */
640: int
641: _tmpl_true(const char *s)
642: {
643: char *eptr;
644: u_long val;
645:
646: val = strtol(s, &eptr, 0);
647: return (*s != '\0' && *eptr == '\0' && val != 0);
648: }
649:
650: #if TMPL_DEBUG
651: static const char *
652: tmpl_rtnstr(enum exec_rtn rtn)
653: {
654: switch (rtn) {
655: case RTN_NORMAL:
656: return ("NORMAL");
657: case RTN_BREAK:
658: return ("BREAK");
659: case RTN_CONTINUE:
660: return ("CONTINUE");
661: case RTN_RETURN:
662: return ("RETURN");
663: default:
664: assert(0);
665: }
666: return (NULL);
667: }
668: #endif
669:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>