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, "&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>