Annotation of embedaddon/libpdel/tmpl/tmpl_parse.c, revision 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: #ifdef __FreeBSD__
! 43: #include <machine/param.h>
! 44: #endif
! 45:
! 46: #ifndef __FreeBSD__
! 47: #define __printflike(x,y)
! 48: #endif
! 49:
! 50: #define isidchar(c) (isalnum(c) || (c) == '_')
! 51:
! 52: /* Parsing */
! 53: static int parse_function(struct tmpl *tmpl, FILE *input,
! 54: struct func_call *call, int special_ok);
! 55: static int parse_argument(struct tmpl *tmpl, FILE *input,
! 56: struct func_arg *arg);
! 57: static int set_text(struct tmpl *tmpl, FILE *input, int nl_white,
! 58: struct tmpl_elem *elem, off_t start, off_t end);
! 59: static int add_elem(struct tmpl *tmpl);
! 60:
! 61: /* Semantic analysis */
! 62: static int scan_elements(struct tmpl *tmpl,
! 63: int start, int in_loop, int *num_errors);
! 64: static int set_error(struct tmpl *tmpl, struct func_call *call,
! 65: const char *fmt, ...) __printflike(3, 4);
! 66:
! 67: /* Memory management */
! 68: static void _tmpl_compact_func(const char *mtype,
! 69: struct func_call *call, char *mem, size_t *bsize);
! 70: static void _tmpl_compact_arg(const char *mtype,
! 71: struct func_arg *arg, char *mem, size_t *bsize);
! 72:
! 73: /* Built-in function handlers */
! 74: static tmpl_handler_t add_func;
! 75: static tmpl_handler_t and_func;
! 76: static tmpl_handler_t atsign_func;
! 77: static tmpl_handler_t cat_func;
! 78: static tmpl_handler_t div_func;
! 79: static tmpl_handler_t equal_func;
! 80: static tmpl_handler_t error_func;
! 81: static tmpl_handler_t eval_func;
! 82: static tmpl_handler_t flush_func;
! 83: static tmpl_handler_t ge_func;
! 84: static tmpl_handler_t get_func;
! 85: static tmpl_handler_t gt_func;
! 86: static tmpl_handler_t htmlencode_func;
! 87: static tmpl_handler_t invoke_func;
! 88: static tmpl_handler_t le_func;
! 89: static tmpl_handler_t loopindex_func;
! 90: static tmpl_handler_t lt_func;
! 91: static tmpl_handler_t mod_func;
! 92: static tmpl_handler_t mul_func;
! 93: static tmpl_handler_t not_func;
! 94: static tmpl_handler_t or_func;
! 95: static tmpl_handler_t output_func;
! 96: static tmpl_handler_t set_func;
! 97: static tmpl_handler_t sub_func;
! 98: static tmpl_handler_t urlencode_func;
! 99:
! 100: #if TMPL_DEBUG
! 101: /* Debugging */
! 102: static char *funcstr(const char *mtype, const struct func_call *call);
! 103: static char *argstr(const char *mtype, const struct func_arg *arg);
! 104: static const char *typestr(const struct tmpl_elem *elem);
! 105: #endif /* TMPL_DEBUG */
! 106:
! 107: /*
! 108: * Built-in functions
! 109: */
! 110: struct builtin_info {
! 111: const char *name;
! 112: enum func_type type;
! 113: tmpl_handler_t *handler;
! 114: int min_args;
! 115: int max_args;
! 116: };
! 117:
! 118: static const char tmpl_function_string[] = { TMPL_FUNCTION_CHARACTER, '\0' };
! 119:
! 120: static const struct builtin_info builtin_funcs[] = {
! 121: { "loop", TY_LOOP, NULL, 1, 1 },
! 122: { "endloop", TY_ENDLOOP, NULL, 0, 0 },
! 123: { "while", TY_WHILE, NULL, 1, 1 },
! 124: { "endwhile", TY_ENDWHILE, NULL, 0, 0 },
! 125: { "if", TY_IF, NULL, 1, 1 },
! 126: { "elif", TY_ELIF, NULL, 1, 1 },
! 127: { "else", TY_ELSE, NULL, 0, 0 },
! 128: { "endif", TY_ENDIF, NULL, 0, 0 },
! 129: { "define", TY_DEFINE, NULL, 1, 1 },
! 130: { "enddef", TY_ENDDEF, NULL, 0, 0 },
! 131: { "continue", TY_CONTINUE, NULL, 0, 0 },
! 132: { "break", TY_BREAK, NULL, 0, 0 },
! 133: { "return", TY_RETURN, NULL, 0, 1 },
! 134: { "equal", TY_NORMAL, equal_func, 2, 2 },
! 135: { "not", TY_NORMAL, not_func, 1, 1 },
! 136: { "and", TY_NORMAL, and_func, 1, INT_MAX },
! 137: { "or", TY_NORMAL, or_func, 1, INT_MAX },
! 138: { "add", TY_NORMAL, add_func, 1, INT_MAX },
! 139: { "sub", TY_NORMAL, sub_func, 1, INT_MAX },
! 140: { "mul", TY_NORMAL, mul_func, 2, INT_MAX },
! 141: { "div", TY_NORMAL, div_func, 2, 2 },
! 142: { "mod", TY_NORMAL, mod_func, 2, 2 },
! 143: { "lt", TY_NORMAL, lt_func, 2, 2 },
! 144: { "gt", TY_NORMAL, gt_func, 2, 2 },
! 145: { "le", TY_NORMAL, le_func, 2, 2 },
! 146: { "ge", TY_NORMAL, ge_func, 2, 2 },
! 147: { "eval", TY_NORMAL, eval_func, 1, 1 },
! 148: { "invoke", TY_NORMAL, invoke_func, 0, 0 },
! 149: { "cat", TY_NORMAL, cat_func, 0, INT_MAX },
! 150: { "error", TY_NORMAL, error_func, 1, 1 },
! 151: { "loopindex", TY_NORMAL, loopindex_func, 0, 1 },
! 152: { "get", TY_NORMAL, get_func, 1, 1 },
! 153: { "set", TY_NORMAL, set_func, 2, 2 },
! 154: { "urlencode", TY_NORMAL, urlencode_func, 1, 1 },
! 155: { "htmlencode", TY_NORMAL, htmlencode_func, 1, 1 },
! 156: { "flush", TY_NORMAL, flush_func, 0, 0 },
! 157: { "output", TY_NORMAL, output_func, 1, 1 },
! 158: { tmpl_function_string,
! 159: TY_NORMAL, atsign_func, 0, 0 },
! 160: { NULL, 0, NULL, 0, 0 }
! 161: };
! 162:
! 163: /************************************************************************
! 164: * PARSING *
! 165: ************************************************************************/
! 166:
! 167: /* Cleanup info */
! 168: struct tmpl_parse_info {
! 169: struct tmpl *tmpl;
! 170: struct func_call call;
! 171: };
! 172:
! 173: /* Internal functions */
! 174: static void tmpl_parse_cleanup(void *arg);
! 175:
! 176: /*
! 177: * Parse an input stream.
! 178: */
! 179: int
! 180: _tmpl_parse(struct tmpl *tmpl, FILE *input, int *num_errors)
! 181: {
! 182: struct tmpl_parse_info info;
! 183: struct tmpl_elem *elem = NULL;
! 184: int nl_white = 0;
! 185: off_t start = 0;
! 186: int rtn = -1;
! 187: size_t bsize;
! 188: off_t pos;
! 189: int ch;
! 190: #if TMPL_DEBUG
! 191: int i;
! 192: #endif
! 193:
! 194: /* Set up cleanup in case of thread cancellation */
! 195: info.tmpl = tmpl;
! 196: memset(&info.call, 0, sizeof(info.call));
! 197: pthread_cleanup_push(tmpl_parse_cleanup, &info);
! 198:
! 199: /* Save starting position */
! 200: if ((pos = ftello(input)) == -1)
! 201: goto fail;
! 202:
! 203: /* Read lines and parse input into elements */
! 204: *num_errors = 0;
! 205: while ((ch = getc(input)) != EOF) {
! 206: off_t end_func;
! 207:
! 208: /* Create a new element if necessary */
! 209: if (elem == NULL) {
! 210: if (add_elem(tmpl) == -1)
! 211: goto fail;
! 212: elem = &tmpl->elems[tmpl->num_elems - 1];
! 213: start = pos;
! 214: nl_white = 1;
! 215: }
! 216:
! 217: /* Look for the initial character of a function invocation */
! 218: if (ch != TMPL_FUNCTION_CHARACTER) {
! 219: if (nl_white && (!isspace(ch)
! 220: || (pos == start && ch != '\n')))
! 221: nl_white = 0;
! 222: pos++; /* more normal text */
! 223: continue;
! 224: }
! 225:
! 226: /* Try to parse a function */
! 227: if (parse_function(tmpl, input, &info.call, 1) == -1) {
! 228: if (errno == EINVAL) {
! 229: (*num_errors)++;
! 230: nl_white = 0;
! 231: pos++;
! 232: continue; /* ignore parse error */
! 233: }
! 234: goto fail;
! 235: }
! 236:
! 237: /* Remember input position just past the end of the function */
! 238: if ((end_func = ftello(input)) == -1) {
! 239: _tmpl_free_call(tmpl->mtype, &info.call);
! 240: goto fail;
! 241: }
! 242:
! 243: /*
! 244: * We parsed a function. Terminate the previous text
! 245: * element (if not empty) and add the function element.
! 246: */
! 247: if (start != pos) {
! 248: if (set_text(tmpl, input,
! 249: nl_white, elem, start, pos) == -1) {
! 250: _tmpl_free_call(tmpl->mtype, &info.call);
! 251: goto fail;
! 252: }
! 253: if (add_elem(tmpl) == -1) {
! 254: _tmpl_free_call(tmpl->mtype, &info.call);
! 255: goto fail;
! 256: }
! 257: elem = &tmpl->elems[tmpl->num_elems - 1];
! 258: nl_white = 1;
! 259: }
! 260: elem->call = info.call;
! 261: memset(&info.call, 0, sizeof(info.call));
! 262:
! 263: /* Reset input position just past the end of the function */
! 264: pos = end_func;
! 265: if (fseeko(input, pos, SEEK_SET) == -1)
! 266: goto fail;
! 267:
! 268: /* Force a new text element to start */
! 269: elem = NULL;
! 270: }
! 271:
! 272: /* Terminate last text element, if any */
! 273: if (elem != NULL
! 274: && set_text(tmpl, input, nl_white, elem, start, pos) == -1)
! 275: goto fail;
! 276:
! 277: #if TMPL_DEBUG
! 278: DBG("Initial parse results: %d elements\n", tmpl->num_elems);
! 279: for (i = 0; i < tmpl->num_elems; i++)
! 280: DBG("%4d: %s\n", i, _tmpl_elemstr(&tmpl->elems[i], i));
! 281: #endif
! 282:
! 283: /* Fill in semantic data */
! 284: if (scan_elements(tmpl, -1, 0, num_errors) == -1)
! 285: goto fail;
! 286:
! 287: #if TMPL_DEBUG
! 288: DBG("After semantic check:\n");
! 289: for (i = 0; i < tmpl->num_elems; i++)
! 290: DBG("%4d: %s\n", i, _tmpl_elemstr(&tmpl->elems[i], i));
! 291: #endif
! 292:
! 293: /* Compress all static information into a single memory block */
! 294: bsize = tmpl->num_elems * sizeof(*tmpl->elems);
! 295: _tmpl_compact_elems(tmpl->mtype,
! 296: tmpl->elems, tmpl->num_elems, NULL, &bsize);
! 297: if ((tmpl->eblock = MALLOC(tmpl->mtype, bsize)) == NULL)
! 298: goto fail;
! 299: bsize = 0;
! 300: _tmpl_compact(tmpl->mtype, tmpl->eblock, &tmpl->elems,
! 301: tmpl->num_elems * sizeof(*tmpl->elems), &bsize);
! 302: _tmpl_compact_elems(tmpl->mtype, tmpl->elems,
! 303: tmpl->num_elems, tmpl->eblock, &bsize);
! 304:
! 305: /* Success */
! 306: rtn = 0;
! 307:
! 308: fail:;
! 309: /* Done */
! 310: pthread_cleanup_pop(0);
! 311: return (rtn);
! 312: }
! 313:
! 314: /*
! 315: * Cleanup for _tmpl_parse()
! 316: */
! 317: static void
! 318: tmpl_parse_cleanup(void *arg)
! 319: {
! 320: struct tmpl_parse_info *const info = arg;
! 321:
! 322: _tmpl_free_call(info->tmpl->mtype, &info->call);
! 323: }
! 324:
! 325: /*
! 326: * Traverse parsed elements and size/move all allocated memory.
! 327: */
! 328: void
! 329: _tmpl_compact_elems(const char *mtype, struct tmpl_elem *elems,
! 330: int num_elems, char *mem, size_t *bsize)
! 331: {
! 332: int i;
! 333:
! 334: for (i = 0; i < num_elems; i++) {
! 335: struct tmpl_elem *const elem = &elems[i];
! 336:
! 337: if (elem->text != NULL) {
! 338: if ((elem->flags & TMPL_ELEM_MMAP_TEXT) == 0) {
! 339: _tmpl_compact(mtype, mem,
! 340: &elem->text, elem->len, bsize);
! 341: }
! 342: } else {
! 343: *bsize = ALIGN(*bsize);
! 344: _tmpl_compact_func(mtype, &elem->call, mem, bsize);
! 345: }
! 346: }
! 347: }
! 348:
! 349: /*
! 350: * Traverse function call and size/move all allocated memory.
! 351: */
! 352: static void
! 353: _tmpl_compact_func(const char *mtype,
! 354: struct func_call *call, char *mem, size_t *bsize)
! 355: {
! 356: int i;
! 357:
! 358: /* Do call function name */
! 359: _tmpl_compact(mtype, mem,
! 360: &call->funcname, strlen(call->funcname) + 1, bsize);
! 361:
! 362: /* Do call arguments array */
! 363: *bsize = ALIGN(*bsize);
! 364: _tmpl_compact(mtype, mem, &call->args,
! 365: call->nargs * sizeof(*call->args), bsize);
! 366:
! 367: /* Do call arguments */
! 368: for (i = 0; i < call->nargs; i++)
! 369: _tmpl_compact_arg(mtype, &call->args[i], mem, bsize);
! 370: }
! 371:
! 372: /*
! 373: * Traverse function call argument and size/move all allocated memory.
! 374: */
! 375: static void
! 376: _tmpl_compact_arg(const char *mtype,
! 377: struct func_arg *arg, char *mem, size_t *bsize)
! 378: {
! 379: if (arg->is_literal) {
! 380: _tmpl_compact(mtype, mem, &arg->u.literal,
! 381: strlen(arg->u.literal) + 1, bsize);
! 382: } else
! 383: _tmpl_compact_func(mtype, &arg->u.call, mem, bsize);
! 384: }
! 385:
! 386: /*
! 387: * Compact a region of memory
! 388: */
! 389: void
! 390: _tmpl_compact(const char *mtype, char *mem,
! 391: void *ptrp, size_t len, size_t *bsize)
! 392: {
! 393: void **const pp = ptrp;
! 394:
! 395: if (mem != NULL) {
! 396: memcpy(mem + *bsize, *pp, len);
! 397: FREE(mtype, *pp);
! 398: *pp = mem + *bsize;
! 399: }
! 400: *bsize += len;
! 401: }
! 402:
! 403: /*
! 404: * Finalize a text parse element.
! 405: */
! 406: static int
! 407: set_text(struct tmpl *tmpl, FILE *input, int nl_white,
! 408: struct tmpl_elem *elem, off_t start, off_t end)
! 409: {
! 410: const u_int len = end - start;
! 411: int ch;
! 412: int i;
! 413:
! 414: assert(elem->flags == 0);
! 415: if (tmpl->mmap_addr != NULL) {
! 416: elem->text = (char *)tmpl->mmap_addr + start;
! 417: elem->flags |= TMPL_ELEM_MMAP_TEXT;
! 418: } else {
! 419: if ((elem->text = MALLOC(tmpl->mtype, len)) == NULL)
! 420: return (-1);
! 421: if (fseeko(input, start, SEEK_SET) == -1)
! 422: return (-1);
! 423: for (i = 0; i < len; i++) {
! 424: if ((ch = getc(input)) == EOF) {
! 425: if (!ferror(input))
! 426: errno = EIO;
! 427: return (-1);
! 428: }
! 429: elem->text[i] = (char)ch;
! 430: }
! 431: }
! 432: if (nl_white)
! 433: elem->flags |= TMPL_ELEM_NL_WHITE;
! 434: elem->len = len;
! 435: return (0);
! 436: }
! 437:
! 438: /*
! 439: * Grow an array of elements by one. Secretly we allocate in
! 440: * chunks of size 'chunk'.
! 441: */
! 442: static int
! 443: add_elem(struct tmpl *tmpl)
! 444: {
! 445: static const int chunk = 128;
! 446: void *new_array;
! 447:
! 448: if (tmpl->num_elems % chunk != 0) {
! 449: tmpl->num_elems++;
! 450: return (0);
! 451: }
! 452: if ((new_array = REALLOC(tmpl->mtype, tmpl->elems,
! 453: (tmpl->num_elems + chunk) * sizeof(*tmpl->elems))) == NULL)
! 454: return (-1);
! 455: tmpl->elems = new_array;
! 456: memset(&tmpl->elems[tmpl->num_elems], 0, chunk * sizeof(*tmpl->elems));
! 457: tmpl->num_elems++;
! 458: return (0);
! 459: }
! 460:
! 461: /*
! 462: * Parse a function call. The input stream is assumed to be pointing
! 463: * just past the '@'. Upon return it will be pointing to the character
! 464: * after the function call if successful, otherwise it will be reset
! 465: * to where it started from.
! 466: *
! 467: * If errno is EINVAL upon return, then there was a parse error.
! 468: */
! 469: static int
! 470: parse_function(struct tmpl *tmpl, FILE *input,
! 471: struct func_call *call, int special_ok)
! 472: {
! 473: const struct builtin_info *builtin;
! 474: off_t start_pos;
! 475: int errno_save;
! 476: int noargs = 0;
! 477: void *mem;
! 478: int len;
! 479: int ch;
! 480:
! 481: /* Save starting position */
! 482: memset(call, 0, sizeof(*call));
! 483: if ((start_pos = ftello(input)) == -1)
! 484: return (-1);
! 485:
! 486: /* Get function name; special case for "@@" */
! 487: if ((ch = getc(input)) == TMPL_FUNCTION_CHARACTER)
! 488: len = 1;
! 489: else {
! 490: for (len = 0; ch != EOF && isidchar(ch); len++)
! 491: ch = getc(input);
! 492: if (len == 0)
! 493: goto parse_err;
! 494: }
! 495:
! 496: /* Re-scan function name into malloc'd buffer */
! 497: if ((call->funcname = MALLOC(tmpl->mtype, len + 1)) == NULL)
! 498: goto parse_err;
! 499: if (fseeko(input, start_pos, SEEK_SET) == -1)
! 500: goto sys_err;
! 501: if (fread(call->funcname, 1, len, input) != len) {
! 502: if (!ferror(input))
! 503: goto parse_err;
! 504: goto sys_err;
! 505: }
! 506: call->funcname[len] = '\0';
! 507:
! 508: /* Check for built-in function; some do not take arguments */
! 509: for (builtin = builtin_funcs; builtin->name != NULL; builtin++) {
! 510: if (strcmp(call->funcname, builtin->name) == 0) {
! 511: if (builtin->min_args == 0)
! 512: noargs = 1;
! 513: break;
! 514: }
! 515: }
! 516: ch = getc(input);
! 517: if (noargs && ch != '(') {
! 518: if (ch != EOF)
! 519: ungetc(ch, input);
! 520: goto got_args;
! 521: }
! 522:
! 523: /* Find opening parenthesis */
! 524: while (isspace(ch))
! 525: ch = getc(input);
! 526: if (ch != '(')
! 527: goto parse_err;
! 528:
! 529: /* Parse function call arguments */
! 530: while (1) {
! 531: ch = getc(input);
! 532: while (isspace(ch)) /* skip white space */
! 533: ch = getc(input);
! 534: if (ch == ')') /* no more arguments */
! 535: break;
! 536: if (call->nargs > 0) {
! 537: if (ch != ',') /* eat comma */
! 538: goto parse_err;
! 539: ch = getc(input);
! 540: while (isspace(ch)) /* skip white space */
! 541: ch = getc(input);
! 542: }
! 543: ungetc(ch, input);
! 544: if ((mem = REALLOC(tmpl->mtype, call->args,
! 545: (call->nargs + 1) * sizeof(*call->args))) == NULL)
! 546: goto sys_err;
! 547: call->args = mem;
! 548: if (parse_argument(tmpl, input, &call->args[call->nargs]) == -1)
! 549: goto sys_err;
! 550: call->nargs++;
! 551: }
! 552:
! 553: got_args:
! 554: /* Set up function call for this function */
! 555: if (builtin->name != NULL) {
! 556: if (call->nargs < builtin->min_args) {
! 557: if (set_error(tmpl, call,
! 558: "at %s %d argument%s %s for \"%c%s\"",
! 559: "least", builtin->min_args,
! 560: builtin->min_args == 1 ? "" : "s",
! 561: "required", TMPL_FUNCTION_CHARACTER,
! 562: builtin->name) == -1)
! 563: goto sys_err;
! 564: return (0);
! 565: }
! 566: if (call->nargs > builtin->max_args) {
! 567: if (set_error(tmpl, call,
! 568: "at %s %d argument%s %s for \"%c%s\"",
! 569: "most", builtin->max_args,
! 570: builtin->max_args == 1 ? "" : "s",
! 571: "allowed", TMPL_FUNCTION_CHARACTER,
! 572: builtin->name) == -1)
! 573: goto sys_err;
! 574: return (0);
! 575: }
! 576: if (!special_ok && builtin->handler == NULL) {
! 577: if (set_error(tmpl, call, "illegal nested \"%c%s\"",
! 578: TMPL_FUNCTION_CHARACTER, builtin->name) == -1)
! 579: goto sys_err;
! 580: return (0);
! 581: }
! 582: call->type = builtin->type;
! 583: call->handler = builtin->handler;
! 584: } else {
! 585: call->type = TY_NORMAL;
! 586: call->handler = NULL;
! 587: }
! 588: return (0);
! 589:
! 590: /* Error */
! 591: parse_err:
! 592: errno = EINVAL;
! 593: sys_err:
! 594: _tmpl_free_call(tmpl->mtype, call);
! 595: errno_save = errno;
! 596: if (fseeko(input, start_pos, SEEK_SET) == -1)
! 597: return (-1);
! 598: errno = errno_save;
! 599: return (-1);
! 600: }
! 601:
! 602: /*
! 603: * Parse a function argument. The input stream is assumed to be pointing
! 604: * at the first non-white space character. Upon return it will be
! 605: * pointing to the character after the argument if successful,
! 606: * otherwise it will be reset to where it started.
! 607: *
! 608: * If errno is EINVAL upon return, then there was a parse error.
! 609: */
! 610: static int
! 611: parse_argument(struct tmpl *tmpl, FILE *input, struct func_arg *arg)
! 612: {
! 613: off_t start_pos;
! 614:
! 615: /* Save starting position */
! 616: memset(arg, 0, sizeof(*arg));
! 617: if ((start_pos = ftello(input)) == -1)
! 618: return (-1);
! 619:
! 620: /* Parse argument */
! 621: switch (getc(input)) {
! 622: case '"':
! 623: arg->is_literal = 1;
! 624: if ((arg->u.literal = string_dequote(input,
! 625: tmpl->mtype)) == NULL)
! 626: return (-1);
! 627: break;
! 628: case TMPL_FUNCTION_CHARACTER:
! 629: arg->is_literal = 0;
! 630: if (parse_function(tmpl, input, &arg->u.call, 0) == -1)
! 631: return (-1);
! 632: break;
! 633: default:
! 634: if (fseeko(input, start_pos, SEEK_SET) != -1)
! 635: errno = EINVAL;
! 636: return (-1);
! 637: }
! 638: return (0);
! 639: }
! 640:
! 641: /************************************************************************
! 642: * SEMANTIC ANALYSIS *
! 643: ************************************************************************/
! 644:
! 645: /*
! 646: * Scan an element list. Returns -1 if error.
! 647: */
! 648: static int
! 649: scan_elements(struct tmpl *tmpl, int start, int in_loop, int *num_errors)
! 650: {
! 651: struct tmpl_elem *const elem = start == -1 ? NULL : &tmpl->elems[start];
! 652: int ret = 0;
! 653: int i;
! 654:
! 655: assert (elem == NULL || elem->call.type != TY_NORMAL);
! 656: for (i = start + 1; i < tmpl->num_elems; i++) {
! 657: struct tmpl_elem *const elem2 = &tmpl->elems[i];
! 658:
! 659: if (elem2->text != NULL)
! 660: continue;
! 661: switch (elem2->call.type) {
! 662: case TY_NORMAL:
! 663: break;
! 664: case TY_LOOP:
! 665: if ((i = scan_elements(tmpl, i, 1, num_errors)) == -1)
! 666: return (-1);
! 667: break;
! 668: case TY_WHILE:
! 669: if ((i = scan_elements(tmpl, i, 1, num_errors)) == -1)
! 670: return (-1);
! 671: break;
! 672: case TY_IF:
! 673: elem2->u.u_if.elsie = -1;
! 674: if ((i = scan_elements(tmpl,
! 675: i, in_loop, num_errors)) == -1)
! 676: return (-1);
! 677: break;
! 678: case TY_DEFINE:
! 679: if ((i = scan_elements(tmpl, i, 0, num_errors)) == -1)
! 680: return (-1);
! 681: break;
! 682: case TY_ENDLOOP:
! 683: if (!elem || elem->call.type != TY_LOOP)
! 684: goto unexpected;
! 685: elem->u.u_loop.endloop = i - start;
! 686: return (i);
! 687: case TY_ENDWHILE:
! 688: if (!elem || elem->call.type != TY_WHILE)
! 689: goto unexpected;
! 690: elem->u.u_while.endwhile = i - start;
! 691: return (i);
! 692: case TY_ELSE:
! 693: case TY_ELIF:
! 694: if (!elem
! 695: || (elem->call.type != TY_IF
! 696: && elem->call.type != TY_ELIF))
! 697: goto unexpected;
! 698: if (elem->u.u_if.elsie != -1)
! 699: goto unexpected;
! 700: elem->u.u_if.elsie = i - start;
! 701: if (elem2->call.type == TY_ELIF) {
! 702: elem2->u.u_if.elsie = -1;
! 703: if ((i = scan_elements(tmpl,
! 704: i, in_loop, num_errors)) == -1)
! 705: return (-1);
! 706: if (tmpl->elems[i].call.type == TY_ENDIF) {
! 707: elem->u.u_if.endif = i - start;
! 708: return (i);
! 709: }
! 710: }
! 711: break;
! 712: case TY_ENDIF:
! 713: if (!elem
! 714: || (elem->call.type != TY_IF
! 715: && elem->call.type != TY_ELIF
! 716: && elem->call.type != TY_ELSE))
! 717: goto unexpected;
! 718: elem->u.u_if.endif = i - start;
! 719: return (i);
! 720: case TY_ENDDEF:
! 721: if (!elem || elem->call.type != TY_DEFINE)
! 722: goto unexpected;
! 723: elem->u.u_define.enddef = i - start;
! 724: return (i);
! 725: case TY_BREAK:
! 726: case TY_CONTINUE:
! 727: if (!in_loop)
! 728: goto unexpected;
! 729: break;
! 730: case TY_RETURN:
! 731: break;
! 732: default:
! 733: assert(0);
! 734: }
! 735: continue;
! 736: unexpected:
! 737: {
! 738: char *fname;
! 739:
! 740: (*num_errors)++;
! 741: if ((fname = STRDUP(tmpl->mtype,
! 742: elem2->call.funcname)) == NULL)
! 743: return (-1);
! 744: if (set_error(tmpl, &elem2->call, "unexpected %c%s",
! 745: TMPL_FUNCTION_CHARACTER, fname) == -1) {
! 746: FREE(tmpl->mtype, fname);
! 747: return (-1);
! 748: }
! 749: FREE(tmpl->mtype, fname);
! 750: }
! 751: }
! 752:
! 753: /* We ran out of elements */
! 754: if (elem == NULL)
! 755: return (0);
! 756: (*num_errors)++;
! 757: switch (elem->call.type) {
! 758: case TY_LOOP:
! 759: ret = set_error(tmpl, &elem->call, "%cloop without %cendloop",
! 760: TMPL_FUNCTION_CHARACTER, TMPL_FUNCTION_CHARACTER);
! 761: break;
! 762: case TY_WHILE:
! 763: ret = set_error(tmpl, &elem->call, "%cwhile without %cendwhile",
! 764: TMPL_FUNCTION_CHARACTER, TMPL_FUNCTION_CHARACTER);
! 765: break;
! 766: case TY_IF:
! 767: case TY_ELSE:
! 768: case TY_ELIF:
! 769: ret = set_error(tmpl, &elem->call, "%cif without %cendif",
! 770: TMPL_FUNCTION_CHARACTER, TMPL_FUNCTION_CHARACTER);
! 771: break;
! 772: case TY_DEFINE:
! 773: ret = set_error(tmpl, &elem->call, "%cdefine without %cenddef",
! 774: TMPL_FUNCTION_CHARACTER, TMPL_FUNCTION_CHARACTER);
! 775: break;
! 776: case TY_NORMAL: /* set_error() already called */
! 777: break;
! 778: default:
! 779: assert(0);
! 780: }
! 781: return (ret);
! 782: }
! 783:
! 784: /************************************************************************
! 785: * ERROR HANDLING *
! 786: ************************************************************************/
! 787:
! 788: /*
! 789: * Set up a function call that prints an error.
! 790: * Replaces the previuous call.
! 791: */
! 792: static int
! 793: set_error(struct tmpl *tmpl, struct func_call *call, const char *fmt, ...)
! 794: {
! 795: char *string;
! 796: va_list args;
! 797: int slen;
! 798:
! 799: _tmpl_free_call(tmpl->mtype, call);
! 800: if ((call->funcname = STRDUP(tmpl->mtype, "error")) == NULL)
! 801: return (-1);
! 802: if ((call->args = MALLOC(tmpl->mtype, sizeof(*call->args))) == NULL) {
! 803: _tmpl_free_call(tmpl->mtype, call);
! 804: return (-1);
! 805: }
! 806: va_start(args, fmt);
! 807: slen = vsnprintf(NULL, 0, fmt, args); /* just get length */
! 808: if ((string = MALLOC(tmpl->mtype, slen + 1)) != NULL)
! 809: vsnprintf(string, slen + 1, fmt, args);
! 810: va_end(args);
! 811: if (string == NULL) {
! 812: _tmpl_free_call(tmpl->mtype, call);
! 813: return (-1);
! 814: }
! 815: call->nargs = 1;
! 816: call->args[0].is_literal = 1;
! 817: call->args[0].u.literal = string;
! 818: call->handler = error_func;
! 819: call->type = TY_NORMAL;
! 820: return (0);
! 821: }
! 822:
! 823: /************************************************************************
! 824: * BUILT-IN FUNCTIONS *
! 825: ************************************************************************/
! 826:
! 827: /*
! 828: * @error()
! 829: */
! 830: static char *
! 831: error_func(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av)
! 832: {
! 833: *errmsgp = STRDUP(ctx->mtype, av[1]);
! 834: return (NULL);
! 835: }
! 836:
! 837: /*
! 838: * @equal()
! 839: */
! 840: static char *
! 841: equal_func(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av)
! 842: {
! 843: return (STRDUP(ctx->mtype, strcmp(av[1], av[2]) == 0 ? "1" : "0"));
! 844: }
! 845:
! 846: /*
! 847: * @not()
! 848: */
! 849: static char *
! 850: not_func(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av)
! 851: {
! 852: return (STRDUP(ctx->mtype, _tmpl_true(av[1]) ? "0" : "1"));
! 853: }
! 854:
! 855: /*
! 856: * @and()
! 857: */
! 858: static char *
! 859: and_func(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av)
! 860: {
! 861: int is_true = 1;
! 862: int i;
! 863:
! 864: for (i = 1; i < ac; i++)
! 865: is_true &= _tmpl_true(av[i]);
! 866: return (STRDUP(ctx->mtype, is_true ? "1" : "0"));
! 867: }
! 868:
! 869: /*
! 870: * @or()
! 871: */
! 872: static char *
! 873: or_func(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av)
! 874: {
! 875: int is_true = 0;
! 876: int i;
! 877:
! 878: for (i = 1; i < ac; i++)
! 879: is_true |= _tmpl_true(av[i]);
! 880: return (STRDUP(ctx->mtype, is_true ? "1" : "0"));
! 881: }
! 882:
! 883: /*
! 884: * @add()
! 885: */
! 886: static char *
! 887: add_func(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av)
! 888: {
! 889: long long sum = 0;
! 890: char buf[32];
! 891: int i;
! 892:
! 893: for (i = 1; i < ac; i++)
! 894: sum += strtoll(av[i], NULL, 0);
! 895: snprintf(buf, sizeof(buf), "%lld", sum);
! 896: return (STRDUP(ctx->mtype, buf));
! 897: }
! 898:
! 899: /*
! 900: * @sub()
! 901: */
! 902: static char *
! 903: sub_func(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av)
! 904: {
! 905: long long result;
! 906: char buf[32];
! 907: int i;
! 908:
! 909: result = strtoll(av[1], NULL, 0);
! 910: for (i = 2; i < ac; i++)
! 911: result -= strtoll(av[i], NULL, 0);
! 912: snprintf(buf, sizeof(buf), "%lld", result);
! 913: return (STRDUP(ctx->mtype, buf));
! 914: }
! 915:
! 916: /*
! 917: * @mul()
! 918: */
! 919: static char *
! 920: mul_func(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av)
! 921: {
! 922: long long product = 1;
! 923: char buf[32];
! 924: int i;
! 925:
! 926: for (i = 1; i < ac; i++)
! 927: product *= strtoll(av[i], NULL, 0);
! 928: snprintf(buf, sizeof(buf), "%lld", product);
! 929: return (STRDUP(ctx->mtype, buf));
! 930: }
! 931:
! 932: /*
! 933: * @div()
! 934: */
! 935: static char *
! 936: div_func(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av)
! 937: {
! 938: long long x, y;
! 939: char buf[32];
! 940:
! 941: x = strtoll(av[1], NULL, 0);
! 942: y = strtoll(av[2], NULL, 0);
! 943: if (y == 0)
! 944: return (STRDUP(ctx->mtype, "divide by zero"));
! 945: snprintf(buf, sizeof(buf), "%lld", x / y);
! 946: return (STRDUP(ctx->mtype, buf));
! 947: }
! 948:
! 949: /*
! 950: * @mod()
! 951: */
! 952: static char *
! 953: mod_func(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av)
! 954: {
! 955: long long x, y;
! 956: char buf[32];
! 957:
! 958: x = strtoll(av[1], NULL, 0);
! 959: y = strtoll(av[2], NULL, 0);
! 960: if (y == 0)
! 961: return (STRDUP(ctx->mtype, "divide by zero"));
! 962: snprintf(buf, sizeof(buf), "%lld", x % y);
! 963: return (STRDUP(ctx->mtype, buf));
! 964: }
! 965:
! 966: /*
! 967: * @lt()
! 968: */
! 969: static char *
! 970: lt_func(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av)
! 971: {
! 972: char buf[32];
! 973:
! 974: snprintf(buf, sizeof(buf), "%d",
! 975: strtoll(av[1], NULL, 0) < strtoll(av[2], NULL, 0));
! 976: return (STRDUP(ctx->mtype, buf));
! 977: }
! 978:
! 979: /*
! 980: * @gt()
! 981: */
! 982: static char *
! 983: gt_func(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av)
! 984: {
! 985: char buf[32];
! 986:
! 987: snprintf(buf, sizeof(buf), "%d",
! 988: strtoll(av[1], NULL, 0) > strtoll(av[2], NULL, 0));
! 989: return (STRDUP(ctx->mtype, buf));
! 990: }
! 991:
! 992: /*
! 993: * @le()
! 994: */
! 995: static char *
! 996: le_func(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av)
! 997: {
! 998: char buf[32];
! 999:
! 1000: snprintf(buf, sizeof(buf), "%d",
! 1001: strtoll(av[1], NULL, 0) <= strtoll(av[2], NULL, 0));
! 1002: return (STRDUP(ctx->mtype, buf));
! 1003: }
! 1004:
! 1005: /*
! 1006: * @ge()
! 1007: */
! 1008: static char *
! 1009: ge_func(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av)
! 1010: {
! 1011: char buf[32];
! 1012:
! 1013: snprintf(buf, sizeof(buf), "%d",
! 1014: strtoll(av[1], NULL, 0) >= strtoll(av[2], NULL, 0));
! 1015: return (STRDUP(ctx->mtype, buf));
! 1016: }
! 1017:
! 1018: /*
! 1019: * @eval()
! 1020: */
! 1021: static char *
! 1022: eval_func(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av)
! 1023: {
! 1024: FILE *const output_save = ctx->output;
! 1025: struct tmpl *tmpl = NULL;
! 1026: char *rtn = NULL;
! 1027: FILE *input = NULL;
! 1028: int esave;
! 1029:
! 1030: /* Create string output buffer to capture output */
! 1031: if ((ctx->output = string_buf_output(ctx->mtype)) == NULL)
! 1032: goto fail;
! 1033:
! 1034: /* Create new template using string argument as input */
! 1035: if ((input = string_buf_input(av[1], strlen(av[1]), 0)) == NULL)
! 1036: goto fail;
! 1037: if ((tmpl = tmpl_create(input, NULL, ctx->mtype)) == NULL)
! 1038: goto fail;
! 1039:
! 1040: /* Execute parsed template using existing context */
! 1041: _tmpl_execute_elems(ctx, tmpl->elems, 0, tmpl->num_elems);
! 1042:
! 1043: /* Get the resulting output as a string */
! 1044: rtn = string_buf_content(ctx->output, 1);
! 1045:
! 1046: fail:
! 1047: /* Clean up and return */
! 1048: esave = errno;
! 1049: if (input != NULL)
! 1050: fclose(input);
! 1051: tmpl_destroy(&tmpl);
! 1052: if (ctx->output != NULL)
! 1053: fclose(ctx->output);
! 1054: ctx->output = output_save;
! 1055: errno = esave;
! 1056: return (rtn);
! 1057: }
! 1058:
! 1059: /*
! 1060: * @invoke()
! 1061: */
! 1062: static char *
! 1063: invoke_func(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av)
! 1064: {
! 1065: struct func_call call;
! 1066: char *rtn;
! 1067: int i;
! 1068:
! 1069: /* Initialize function call structure */
! 1070: memset(&call, 0, sizeof(call));
! 1071: call.type = TY_NORMAL;
! 1072:
! 1073: /* Get argument count */
! 1074: if ((i = _tmpl_find_var(ctx, "argc")) == -1
! 1075: || (call.nargs = strtol(ctx->vars[i].value, NULL, 0)) <= 0) {
! 1076: ASPRINTF(ctx->mtype, errmsgp,
! 1077: "%cinvoke(): \"argc\" must be greater than zero",
! 1078: TMPL_FUNCTION_CHARACTER);
! 1079: return (NULL);
! 1080: }
! 1081: call.nargs--;
! 1082:
! 1083: /* Get function and arguments */
! 1084: if ((call.args = MALLOC(ctx->mtype,
! 1085: call.nargs * sizeof(*call.args))) == NULL)
! 1086: return (NULL);
! 1087: for (i = 0; i <= call.nargs; i++) {
! 1088: const char *value;
! 1089: char name[16];
! 1090: int j;
! 1091:
! 1092: /* Get argN */
! 1093: snprintf(name, sizeof(name), "arg%d", i);
! 1094: value = ((j = _tmpl_find_var(ctx, name)) != -1) ?
! 1095: ctx->vars[j].value : "";
! 1096:
! 1097: /* Set argN */
! 1098: if (i == 0)
! 1099: call.funcname = (char *)value;
! 1100: else {
! 1101: call.args[i - 1].is_literal = 1;
! 1102: call.args[i - 1].u.literal = (char *)value;
! 1103: }
! 1104: }
! 1105:
! 1106: /* Invoke function */
! 1107: rtn = _tmpl_invoke(ctx, errmsgp, &call);
! 1108:
! 1109: /* Done */
! 1110: FREE(ctx->mtype, call.args);
! 1111: return (rtn);
! 1112: }
! 1113:
! 1114: /*
! 1115: * @cat()
! 1116: */
! 1117: static char *
! 1118: cat_func(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av)
! 1119: {
! 1120: int len, slen;
! 1121: char *cat;
! 1122: int i;
! 1123:
! 1124: for (len = 0, i = 1; i < ac; i++)
! 1125: len += strlen(av[i]);
! 1126: if ((cat = MALLOC(ctx->mtype, len + 1)) == NULL)
! 1127: return (NULL);
! 1128: for (len = 0, i = 1; i < ac; i++) {
! 1129: slen = strlen(av[i]);
! 1130: memcpy(cat + len, av[i], slen);
! 1131: len += slen;
! 1132: }
! 1133: cat[len] = '\0';
! 1134: return (cat);
! 1135: }
! 1136:
! 1137: /*
! 1138: * @get()
! 1139: */
! 1140: static char *
! 1141: get_func(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av)
! 1142: {
! 1143: int i;
! 1144:
! 1145: if ((i = _tmpl_find_var(ctx, av[1])) == -1)
! 1146: return (STRDUP(ctx->mtype, ""));
! 1147: return (STRDUP(ctx->mtype, ctx->vars[i].value));
! 1148: }
! 1149:
! 1150: /*
! 1151: * @set()
! 1152: */
! 1153: static char *
! 1154: set_func(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av)
! 1155: {
! 1156: if (tmpl_ctx_set_var(ctx, av[1], av[2]) == -1)
! 1157: return (NULL);
! 1158: return (STRDUP(ctx->mtype, ""));
! 1159: }
! 1160:
! 1161: /*
! 1162: * @urlencode()
! 1163: */
! 1164: static char *
! 1165: urlencode_func(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av)
! 1166: {
! 1167: char *s = av[1];
! 1168: char *enc;
! 1169: char *t;
! 1170:
! 1171: if ((enc = MALLOC(ctx->mtype, (strlen(s) * 3) + 1)) == NULL)
! 1172: return (NULL);
! 1173: for (t = enc; *s != '\0'; s++) {
! 1174: if (!isalnum(*s) && strchr("$-_.+!*'(),:@&=~", *s) == NULL)
! 1175: t += sprintf(t, "%%%02x", (u_char)*s);
! 1176: else
! 1177: *t++ = *s;
! 1178: }
! 1179: *t = '\0';
! 1180: return (enc);
! 1181: }
! 1182:
! 1183: /*
! 1184: * @htmlencode()
! 1185: */
! 1186: static char *
! 1187: htmlencode_func(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av)
! 1188: {
! 1189: char *s = av[1];
! 1190: char *esc;
! 1191: char *t;
! 1192:
! 1193: if ((esc = MALLOC(ctx->mtype, (strlen(s) * 6) + 1)) == NULL)
! 1194: return (NULL);
! 1195: for (t = esc; *s != '\0'; s++) {
! 1196: switch (*s) {
! 1197: case '<':
! 1198: t += sprintf(t, "<");
! 1199: break;
! 1200: case '>':
! 1201: t += sprintf(t, ">");
! 1202: break;
! 1203: case '"':
! 1204: t += sprintf(t, """);
! 1205: break;
! 1206: case '&':
! 1207: t += sprintf(t, "&");
! 1208: break;
! 1209: default:
! 1210: if ((u_char)*s >= 0x7e)
! 1211: t += sprintf(t, "&#%d;", (u_char)*s);
! 1212: else
! 1213: *t++ = *s;
! 1214: break;
! 1215: }
! 1216: }
! 1217: *t = '\0';
! 1218: return (esc);
! 1219: }
! 1220:
! 1221: /*
! 1222: * @flush()
! 1223: */
! 1224: static char *
! 1225: flush_func(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av)
! 1226: {
! 1227: fflush(ctx->output);
! 1228: if (ctx->orig_output != ctx->output)
! 1229: fflush(ctx->orig_output);
! 1230: return (STRDUP(ctx->mtype, ""));
! 1231: }
! 1232:
! 1233: /*
! 1234: * @output()
! 1235: */
! 1236: static char *
! 1237: output_func(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av)
! 1238: {
! 1239: fputs(av[1], ctx->orig_output);
! 1240: return (STRDUP(ctx->mtype, ""));
! 1241: }
! 1242:
! 1243: /*
! 1244: * @loopindex()
! 1245: */
! 1246: static char *
! 1247: loopindex_func(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av)
! 1248: {
! 1249: struct loop_ctx *loop;
! 1250: long level;
! 1251: char buf[64];
! 1252: char *eptr;
! 1253: int i;
! 1254:
! 1255: if (ac == 1)
! 1256: level = 0;
! 1257: else {
! 1258: level = strtol(av[1], &eptr, 0);
! 1259: if (*av[1] == '\0' || *eptr != '\0'
! 1260: || level < 0 || level == LONG_MAX)
! 1261: return (STRDUP(ctx->mtype, "-1"));
! 1262: }
! 1263: for (i = 0, loop = ctx->loop;
! 1264: i < level && loop != NULL;
! 1265: i++, loop = loop->outer);
! 1266: if (loop == NULL) {
! 1267: snprintf(buf, sizeof(buf), "%c%s called not within any loop",
! 1268: TMPL_FUNCTION_CHARACTER, av[0]);
! 1269: *errmsgp = STRDUP(ctx->mtype, buf);
! 1270: return (NULL);
! 1271: }
! 1272: snprintf(buf, sizeof(buf), "%d", loop->index);
! 1273: return (STRDUP(ctx->mtype, buf));
! 1274: }
! 1275:
! 1276: /*
! 1277: * @@
! 1278: */
! 1279: static char *
! 1280: atsign_func(struct tmpl_ctx *ctx, char **errmsgp, int ac, char **av)
! 1281: {
! 1282: return (STRDUP(ctx->mtype, tmpl_function_string));
! 1283: }
! 1284:
! 1285: /************************************************************************
! 1286: * DEBUGGING *
! 1287: ************************************************************************/
! 1288:
! 1289: #if TMPL_DEBUG
! 1290:
! 1291: #define BUFSIZE 128
! 1292:
! 1293: /*
! 1294: * Return a text version of a template element.
! 1295: */
! 1296: const char *
! 1297: _tmpl_elemstr(const struct tmpl_elem *elem, int index)
! 1298: {
! 1299: static char buf[BUFSIZE];
! 1300: char *s;
! 1301:
! 1302: snprintf(buf, sizeof(buf), "at 0x%04lx-0x%04lx type=%s",
! 1303: (u_long)elem->start, (u_long)elem->start + elem->len,
! 1304: typestr(elem));
! 1305: if (elem->text != NULL) {
! 1306: int i;
! 1307:
! 1308: strlcat(buf, " text=\"", sizeof(buf));
! 1309: for (i = 0; i < elem->len; i++) {
! 1310: switch (elem->text[i]) {
! 1311: case '\n':
! 1312: strlcat(buf, "\\n", sizeof(buf));
! 1313: break;
! 1314: case '\r':
! 1315: strlcat(buf, "\\r", sizeof(buf));
! 1316: break;
! 1317: case '\t':
! 1318: strlcat(buf, "\\t", sizeof(buf));
! 1319: break;
! 1320: default:
! 1321: if (isprint(elem->text[i])) {
! 1322: snprintf(buf + strlen(buf),
! 1323: sizeof(buf) - strlen(buf),
! 1324: "%c", elem->text[i]);
! 1325: } else {
! 1326: snprintf(buf + strlen(buf),
! 1327: sizeof(buf) - strlen(buf),
! 1328: "\\x%02x", (u_char)elem->text[i]);
! 1329: }
! 1330: }
! 1331: }
! 1332: strlcat(buf, "\"", sizeof(buf));
! 1333: return (buf);
! 1334: }
! 1335: s = funcstr(TYPED_MEM_TEMP, &elem->call);
! 1336: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %s", s);
! 1337: FREE(TYPED_MEM_TEMP, s);
! 1338: switch (elem->call.type) {
! 1339: case TY_LOOP:
! 1340: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
! 1341: " end=%d", index + elem->u.u_loop.endloop);
! 1342: break;
! 1343: case TY_IF:
! 1344: case TY_ELIF:
! 1345: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
! 1346: " else=%d endif=%d",
! 1347: index + elem->u.u_if.elsie, index + elem->u.u_if.endif);
! 1348: break;
! 1349: case TY_WHILE:
! 1350: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
! 1351: " end=%d", index + elem->u.u_while.endwhile);
! 1352: break;
! 1353: case TY_DEFINE:
! 1354: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
! 1355: " end=%d", index + elem->u.u_define.enddef);
! 1356: break;
! 1357: default:
! 1358: break;
! 1359: }
! 1360: return (buf);
! 1361: }
! 1362:
! 1363: /*
! 1364: * Returns a malloc'd string
! 1365: */
! 1366: static char *
! 1367: funcstr(const char *mtype, const struct func_call *call)
! 1368: {
! 1369: char buf[BUFSIZE];
! 1370: char *s;
! 1371: int i;
! 1372:
! 1373: snprintf(buf, sizeof(buf), "%c%s(",
! 1374: TMPL_FUNCTION_CHARACTER, call->funcname);
! 1375: for (i = 0; i < call->nargs; i++) {
! 1376: s = argstr(mtype, &call->args[i]);
! 1377: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
! 1378: "%s%s", s, (i < call->nargs - 1) ? ", " : "");
! 1379: FREE(mtype, s);
! 1380: }
! 1381: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ")");
! 1382: return (STRDUP(mtype, buf));
! 1383: }
! 1384:
! 1385: /*
! 1386: * Returns a malloc'd string
! 1387: */
! 1388: static char *
! 1389: argstr(const char *mtype, const struct func_arg *arg)
! 1390: {
! 1391: char buf[BUFSIZE];
! 1392:
! 1393: if (arg->is_literal) {
! 1394: snprintf(buf, sizeof(buf), "\"%s\"", arg->u.literal);
! 1395: return (STRDUP(mtype, buf));
! 1396: } else
! 1397: return (funcstr(mtype, &arg->u.call));
! 1398: }
! 1399:
! 1400: static const char *
! 1401: typestr(const struct tmpl_elem *elem)
! 1402: {
! 1403: if (elem->text != NULL)
! 1404: return ("TEXT");
! 1405: switch (elem->call.type) {
! 1406: case TY_NORMAL:
! 1407: return (elem->call.handler != NULL ? "BUILTIN" : "USER");
! 1408: case TY_LOOP: return ("LOOP");
! 1409: case TY_ENDLOOP: return ("ENDLOOP");
! 1410: case TY_WHILE: return ("WHILE");
! 1411: case TY_ENDWHILE: return ("ENDWHILE");
! 1412: case TY_IF: return ("IF");
! 1413: case TY_ELSE: return ("ELSE");
! 1414: case TY_ELIF: return ("ELIF");
! 1415: case TY_ENDIF: return ("ENDIF");
! 1416: case TY_DEFINE: return ("DEFINE");
! 1417: case TY_ENDDEF: return ("ENDDEF");
! 1418: case TY_BREAK: return ("BREAK");
! 1419: case TY_CONTINUE: return ("CONTINUE");
! 1420: case TY_RETURN: return ("RETURN");
! 1421: }
! 1422: return ("???");
! 1423: }
! 1424: #endif /* TMPL_DEBUG */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>