Annotation of embedaddon/libpdel/tmpl/tmpl_parse.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: #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, "&lt;");
                   1199:                        break;
                   1200:                case '>':
                   1201:                        t += sprintf(t, "&gt;");
                   1202:                        break;
                   1203:                case '"':
                   1204:                        t += sprintf(t, "&quot;");
                   1205:                        break;
                   1206:                case '&':
                   1207:                        t += sprintf(t, "&amp;");
                   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>