Home | History | Annotate | Download | only in cs
      1 /*
      2  * Copyright 2001-2004 Brandon Long
      3  * All Rights Reserved.
      4  *
      5  * ClearSilver Templating System
      6  *
      7  * This code is made available under the terms of the ClearSilver License.
      8  * http://www.clearsilver.net/license.hdf
      9  *
     10  */
     11 
     12 /*
     13  * TODO: there is some really ugly pseudo reference counting in here
     14  * for allocation of temporary strings (and passing references).  See the alloc
     15  * member of various structs for details.  We should move this to an arena
     16  * allocator so we can just allocate whenever we need to and just clean up
     17  * all the allocation at the end (may require two arenas: one for parese and
     18  * one for render)
     19  */
     20 
     21 #include "cs_config.h"
     22 
     23 #include <sys/types.h>
     24 #include <sys/stat.h>
     25 #include <fcntl.h>
     26 #include <unistd.h>
     27 #include <errno.h>
     28 #include <string.h>
     29 #include <stdlib.h>
     30 #include <stdio.h>
     31 #include <ctype.h>
     32 #include <limits.h>
     33 #include <stdarg.h>
     34 
     35 #ifdef ENABLE_GETTEXT
     36 #include <libintl.h>
     37 #endif
     38 
     39 #include "util/neo_misc.h"
     40 #include "util/neo_err.h"
     41 #include "util/neo_files.h"
     42 #include "util/neo_str.h"
     43 #include "util/ulist.h"
     44 #include "cs.h"
     45 
     46 /* turn on some debug output for expressions */
     47 #define DEBUG_EXPR_PARSE 0
     48 #define DEBUG_EXPR_EVAL 0
     49 
     50 typedef enum
     51 {
     52   ST_SAME = 0,
     53   ST_GLOBAL = 1<<0,
     54   ST_IF = 1<<1,
     55   ST_ELSE = 1<<2,
     56   ST_EACH = 1<<3,
     57   ST_WITH = 1<<4,
     58   ST_POP = 1<<5,
     59   ST_DEF = 1<<6,
     60   ST_LOOP =  1<<7,
     61   ST_ALT = 1<<8,
     62   ST_ESCAPE = 1<<9,
     63 } CS_STATE;
     64 
     65 #define ST_ANYWHERE (ST_EACH | ST_WITH | ST_ELSE | ST_IF | ST_GLOBAL | ST_DEF | ST_LOOP | ST_ALT | ST_ESCAPE)
     66 
     67 typedef struct _stack_entry
     68 {
     69   CS_STATE state;
     70   NEOS_ESCAPE escape;
     71   CSTREE *tree;
     72   CSTREE *next_tree;
     73   int num_local;
     74   int location;
     75 } STACK_ENTRY;
     76 
     77 static NEOERR *literal_parse (CSPARSE *parse, int cmd, char *arg);
     78 static NEOERR *literal_eval (CSPARSE *parse, CSTREE *node, CSTREE **next);
     79 static NEOERR *name_parse (CSPARSE *parse, int cmd, char *arg);
     80 static NEOERR *name_eval (CSPARSE *parse, CSTREE *node, CSTREE **next);
     81 static NEOERR *var_parse (CSPARSE *parse, int cmd, char *arg);
     82 static NEOERR *var_eval (CSPARSE *parse, CSTREE *node, CSTREE **next);
     83 static NEOERR *evar_parse (CSPARSE *parse, int cmd, char *arg);
     84 static NEOERR *lvar_parse (CSPARSE *parse, int cmd, char *arg);
     85 static NEOERR *lvar_eval (CSPARSE *parse, CSTREE *node, CSTREE **next);
     86 static NEOERR *if_parse (CSPARSE *parse, int cmd, char *arg);
     87 static NEOERR *if_eval (CSPARSE *parse, CSTREE *node, CSTREE **next);
     88 static NEOERR *else_parse (CSPARSE *parse, int cmd, char *arg);
     89 static NEOERR *elif_parse (CSPARSE *parse, int cmd, char *arg);
     90 static NEOERR *endif_parse (CSPARSE *parse, int cmd, char *arg);
     91 static NEOERR *each_with_parse (CSPARSE *parse, int cmd, char *arg);
     92 static NEOERR *each_eval (CSPARSE *parse, CSTREE *node, CSTREE **next);
     93 static NEOERR *with_eval (CSPARSE *parse, CSTREE *node, CSTREE **next);
     94 static NEOERR *end_parse (CSPARSE *parse, int cmd, char *arg);
     95 static NEOERR *include_parse (CSPARSE *parse, int cmd, char *arg);
     96 static NEOERR *linclude_parse (CSPARSE *parse, int cmd, char *arg);
     97 static NEOERR *linclude_eval (CSPARSE *parse, CSTREE *node, CSTREE **next);
     98 static NEOERR *def_parse (CSPARSE *parse, int cmd, char *arg);
     99 static NEOERR *skip_eval (CSPARSE *parse, CSTREE *node, CSTREE **next);
    100 static NEOERR *call_parse (CSPARSE *parse, int cmd, char *arg);
    101 static NEOERR *call_eval (CSPARSE *parse, CSTREE *node, CSTREE **next);
    102 static NEOERR *set_parse (CSPARSE *parse, int cmd, char *arg);
    103 static NEOERR *set_eval (CSPARSE *parse, CSTREE *node, CSTREE **next);
    104 static NEOERR *loop_parse (CSPARSE *parse, int cmd, char *arg);
    105 static NEOERR *loop_eval (CSPARSE *parse, CSTREE *node, CSTREE **next);
    106 static NEOERR *alt_parse (CSPARSE *parse, int cmd, char *arg);
    107 static NEOERR *alt_eval (CSPARSE *parse, CSTREE *node, CSTREE **next);
    108 static NEOERR *escape_parse (CSPARSE *parse, int cmd, char *arg);
    109 static NEOERR *escape_eval (CSPARSE *parse, CSTREE *node, CSTREE **next);
    110 
    111 static NEOERR *render_node (CSPARSE *parse, CSTREE *node);
    112 static NEOERR *cs_init_internal (CSPARSE **parse, HDF *hdf, CSPARSE *parent);
    113 static int rearrange_for_call(CSARG **args);
    114 
    115 typedef struct _cmds
    116 {
    117   char *cmd;
    118   int cmdlen;
    119   CS_STATE allowed_state;
    120   CS_STATE next_state;
    121   NEOERR* (*parse_handler)(CSPARSE *parse, int cmd, char *arg);
    122   NEOERR* (*eval_handler)(CSPARSE *parse, CSTREE *node, CSTREE **next);
    123   int has_arg;
    124 } CS_CMDS;
    125 
    126 CS_CMDS Commands[] = {
    127   {"literal", sizeof("literal")-1, ST_ANYWHERE,     ST_SAME,
    128     literal_parse, literal_eval, 0},
    129   {"name",     sizeof("name")-1,     ST_ANYWHERE,     ST_SAME,
    130     name_parse, name_eval,     1},
    131   {"var",     sizeof("var")-1,     ST_ANYWHERE,     ST_SAME,
    132     var_parse, var_eval,     1},
    133   {"uvar",     sizeof("uvar")-1,     ST_ANYWHERE,     ST_SAME,
    134     var_parse, var_eval,     1},
    135   {"evar",    sizeof("evar")-1,    ST_ANYWHERE,     ST_SAME,
    136     evar_parse, skip_eval,    1},
    137   {"lvar",    sizeof("lvar")-1,    ST_ANYWHERE,     ST_SAME,
    138     lvar_parse, lvar_eval,    1},
    139   {"if",      sizeof("if")-1,      ST_ANYWHERE,     ST_IF,
    140     if_parse, if_eval,      1},
    141   {"else",    sizeof("else")-1,    ST_IF,           ST_POP | ST_ELSE,
    142     else_parse, skip_eval,    0},
    143   {"elseif",  sizeof("elseif")-1,  ST_IF,           ST_SAME,
    144     elif_parse, if_eval,   1},
    145   {"elif",    sizeof("elif")-1,    ST_IF,           ST_SAME,
    146     elif_parse, if_eval,   1},
    147   {"/if",     sizeof("/if")-1,     ST_IF | ST_ELSE, ST_POP,
    148     endif_parse, skip_eval,   0},
    149   {"each",    sizeof("each")-1,    ST_ANYWHERE,     ST_EACH,
    150     each_with_parse, each_eval,    1},
    151   {"/each",   sizeof("/each")-1,   ST_EACH,         ST_POP,
    152     end_parse, skip_eval, 0},
    153   {"with",    sizeof("each")-1,    ST_ANYWHERE,     ST_WITH,
    154     each_with_parse, with_eval,    1},
    155   {"/with",   sizeof("/with")-1,   ST_WITH,         ST_POP,
    156     end_parse, skip_eval, 0},
    157   {"include", sizeof("include")-1, ST_ANYWHERE,     ST_SAME,
    158     include_parse, skip_eval, 1},
    159   {"linclude", sizeof("linclude")-1, ST_ANYWHERE,     ST_SAME,
    160     linclude_parse, linclude_eval, 1},
    161   {"def",     sizeof("def")-1,     ST_ANYWHERE,     ST_DEF,
    162     def_parse, skip_eval, 1},
    163   {"/def",    sizeof("/def")-1,    ST_DEF,          ST_POP,
    164     end_parse, skip_eval, 0},
    165   {"call",    sizeof("call")-1,    ST_ANYWHERE,     ST_SAME,
    166     call_parse, call_eval, 1},
    167   {"set",    sizeof("set")-1,    ST_ANYWHERE,     ST_SAME,
    168     set_parse, set_eval, 1},
    169   {"loop",    sizeof("loop")-1,    ST_ANYWHERE,     ST_LOOP,
    170     loop_parse, loop_eval, 1},
    171   {"/loop",    sizeof("/loop")-1,    ST_LOOP,     ST_POP,
    172     end_parse, skip_eval, 1},
    173   {"alt",    sizeof("alt")-1,    ST_ANYWHERE,     ST_ALT,
    174     alt_parse, alt_eval, 1},
    175   {"/alt",    sizeof("/alt")-1,    ST_ALT,     ST_POP,
    176     end_parse, skip_eval, 1},
    177   {"escape",    sizeof("escape")-1,    ST_ANYWHERE,     ST_ESCAPE,
    178     escape_parse, escape_eval, 1},
    179   {"/escape",    sizeof("/escape")-1,    ST_ESCAPE,     ST_POP,
    180     end_parse, skip_eval, 1},
    181   {NULL},
    182 };
    183 
    184 /* Possible Config.VarEscapeMode values */
    185 typedef struct _escape_modes
    186 {
    187   char *mode; /* Add space for NUL */
    188   NEOS_ESCAPE context; /* Context of the name */
    189 } CS_ESCAPE_MODES;
    190 
    191 CS_ESCAPE_MODES EscapeModes[] = {
    192   {"none", NEOS_ESCAPE_NONE},
    193   {"html", NEOS_ESCAPE_HTML},
    194   {"js",   NEOS_ESCAPE_SCRIPT},
    195   {"url",  NEOS_ESCAPE_URL},
    196   {NULL},
    197 };
    198 
    199 
    200 /* **** CS alloc/dealloc ******************************************** */
    201 
    202 static int NodeNumber = 0;
    203 
    204 static void init_node_pos(CSTREE *node, CSPARSE *parse)
    205 {
    206   CS_POSITION *pos = &parse->pos;
    207   char *data;
    208 
    209   if (parse->offset < pos->cur_offset) {
    210     /* Oops, we went backwards in file, is this an error? */
    211     node->linenum = -1;
    212     node->colnum = parse->offset;
    213     return;
    214   }
    215 
    216   /* Start counting from 1 not 0 */
    217   if (pos->line == 0) pos->line = 1;
    218   if (pos->col == 0) pos->col = 1;
    219 
    220   if (parse->context == NULL) {
    221     /* Not in a file */
    222     node->fname = NULL;
    223   }
    224   else {
    225     node->fname = strdup(parse->context);
    226     if (node->fname == NULL) {
    227       /* malloc error, cannot proceed */
    228       node->linenum = -1;
    229       return;
    230     }
    231   }
    232 
    233   data = parse->context_string;
    234   if (data == NULL) {
    235     node->linenum = -1;
    236     return;
    237   }
    238 
    239   while (pos->cur_offset < parse->offset) {
    240     if (data[pos->cur_offset] == '\n') {
    241       pos->line++;
    242       pos->col = 1;
    243     }
    244     else {
    245       pos->col++;
    246     }
    247 
    248     pos->cur_offset++;
    249   }
    250 
    251   node->linenum = pos->line;
    252   node->colnum = pos->col;
    253 
    254   return;
    255 
    256 }
    257 
    258 static NEOERR *alloc_node (CSTREE **node, CSPARSE *parse)
    259 {
    260   CSTREE *my_node;
    261 
    262   *node = NULL;
    263   my_node = (CSTREE *) calloc (1, sizeof (CSTREE));
    264   if (my_node == NULL)
    265     return nerr_raise (NERR_NOMEM, "Unable to allocate memory for node");
    266 
    267   my_node->cmd = 0;
    268   my_node->node_num = NodeNumber++;
    269 
    270   *node = my_node;
    271 
    272   if (parse->audit_mode) {
    273     init_node_pos(my_node, parse);
    274   }
    275   return STATUS_OK;
    276 }
    277 
    278 /* TODO: make these deallocations linear and not recursive */
    279 static void dealloc_arg (CSARG **arg)
    280 {
    281   CSARG *p;
    282 
    283   if (*arg == NULL) return;
    284   p = *arg;
    285   if (p->expr1) dealloc_arg (&(p->expr1));
    286   if (p->expr2) dealloc_arg (&(p->expr2));
    287   if (p->next) dealloc_arg (&(p->next));
    288 
    289   if (p->argexpr) free(p->argexpr);
    290 
    291   free(p);
    292   *arg = NULL;
    293 }
    294 
    295 static void dealloc_node (CSTREE **node)
    296 {
    297   CSTREE *my_node;
    298 
    299   if (*node == NULL) return;
    300   my_node = *node;
    301   if (my_node->case_0) dealloc_node (&(my_node->case_0));
    302   if (my_node->case_1) dealloc_node (&(my_node->case_1));
    303   if (my_node->next) dealloc_node (&(my_node->next));
    304   if (my_node->vargs) dealloc_arg (&(my_node->vargs));
    305   if (my_node->arg1.expr1) dealloc_arg (&(my_node->arg1.expr1));
    306   if (my_node->arg1.expr2) dealloc_arg (&(my_node->arg1.expr2));
    307   if (my_node->arg1.next) dealloc_arg (&(my_node->arg1.next));
    308   if (my_node->arg2.expr1) dealloc_arg (&(my_node->arg2.expr1));
    309   if (my_node->arg2.expr2) dealloc_arg (&(my_node->arg2.expr2));
    310   if (my_node->arg2.next) dealloc_arg (&(my_node->arg2.next));
    311 
    312   if (my_node->arg1.argexpr) free(my_node->arg1.argexpr);
    313   if (my_node->arg2.argexpr) free(my_node->arg2.argexpr);
    314   if (my_node->fname) free(my_node->fname);
    315 
    316   free(my_node);
    317   *node = NULL;
    318 }
    319 
    320 static void dealloc_macro (CS_MACRO **macro)
    321 {
    322   CS_MACRO *my_macro;
    323 
    324   if (*macro == NULL) return;
    325   my_macro = *macro;
    326   if (my_macro->name) free (my_macro->name);
    327   if (my_macro->args) dealloc_arg (&(my_macro->args));
    328   if (my_macro->next) dealloc_macro (&(my_macro->next));
    329   free (my_macro);
    330   *macro = NULL;
    331 }
    332 
    333 static void dealloc_function (CS_FUNCTION **csf)
    334 {
    335   CS_FUNCTION *my_csf;
    336 
    337   if (*csf == NULL) return;
    338   my_csf = *csf;
    339   if (my_csf->name) free (my_csf->name);
    340   if (my_csf->next) dealloc_function (&(my_csf->next));
    341   free (my_csf);
    342   *csf = NULL;
    343 }
    344 
    345 static int find_open_delim (CSPARSE *parse, char *buf, int x, int len)
    346 {
    347   char *p;
    348   int ws_index = 2+parse->taglen;
    349 
    350   while (x < len)
    351   {
    352     p = strchr (&(buf[x]), '<');
    353     if (p == NULL) return -1;
    354     if (p[1] == '?' && !strncasecmp(&p[2], parse->tag, parse->taglen) &&
    355 	(p[ws_index] == ' ' || p[ws_index] == '\n' || p[ws_index] == '\t' || p[ws_index] == '\r'))
    356       /*
    357     if (p[1] && p[1] == '?' &&
    358 	p[2] && (p[2] == 'C' || p[2] == 'c') &&
    359 	p[3] && (p[3] == 'S' || p[3] == 's') &&
    360 	p[4] && (p[4] == ' ' || p[4] == '\n' || p[4] == '\t' || p[4] == '\r'))
    361 	*/
    362     {
    363       return p - buf;
    364     }
    365     x = p - buf + 1;
    366   }
    367   return -1;
    368 }
    369 
    370 static NEOERR *_store_error (CSPARSE *parse, NEOERR *err)
    371 {
    372   CS_ERROR *ptr;
    373   CS_ERROR *node;
    374 
    375   node = (CS_ERROR *) calloc(1, sizeof(CS_ERROR));
    376   if (node == NULL)
    377   {
    378     return nerr_raise (NERR_NOMEM,
    379         "Unable to allocate memory for error entry");
    380   }
    381 
    382   node->err = err;
    383 
    384   if (parse->err_list == NULL)
    385   {
    386     parse->err_list = node;
    387     return STATUS_OK;
    388   }
    389 
    390   ptr = parse->err_list;
    391   while (ptr->next != NULL)
    392     ptr = ptr->next;
    393 
    394   ptr->next = node;
    395   return STATUS_OK;
    396 
    397 }
    398 
    399 NEOERR *cs_parse_file (CSPARSE *parse, const char *path)
    400 {
    401   NEOERR *err;
    402   char *ibuf;
    403   const char *save_context;
    404   int save_infile;
    405   char fpath[_POSIX_PATH_MAX];
    406   CS_POSITION pos;
    407 
    408   if (path == NULL)
    409     return nerr_raise (NERR_ASSERT, "path is NULL");
    410 
    411   if (parse->fileload)
    412   {
    413     err = parse->fileload(parse->fileload_ctx, parse->hdf, path, &ibuf);
    414   }
    415   else
    416   {
    417     if (path[0] != '/')
    418     {
    419       err = hdf_search_path (parse->hdf, path, fpath);
    420       if (parse->global_hdf && nerr_handle(&err, NERR_NOT_FOUND))
    421         err = hdf_search_path(parse->global_hdf, path, fpath);
    422       if (err != STATUS_OK) return nerr_pass(err);
    423       path = fpath;
    424     }
    425 
    426     err = ne_load_file (path, &ibuf);
    427   }
    428   if (err) return nerr_pass (err);
    429 
    430   save_context = parse->context;
    431   parse->context = path;
    432   save_infile = parse->in_file;
    433   parse->in_file = 1;
    434 
    435   if (parse->audit_mode) {
    436     /* Save previous position before parsing the new file */
    437     memcpy(&pos, &parse->pos, sizeof(CS_POSITION));
    438 
    439     parse->pos.line = 0;
    440     parse->pos.col = 0;
    441     parse->pos.cur_offset = 0;
    442   }
    443 
    444   err = cs_parse_string(parse, ibuf, strlen(ibuf));
    445 
    446   if (parse->audit_mode) {
    447     memcpy(&parse->pos, &pos, sizeof(CS_POSITION));
    448   }
    449 
    450   parse->in_file = save_infile;
    451   parse->context = save_context;
    452 
    453   return nerr_pass(err);
    454 }
    455 
    456 static char *find_context (CSPARSE *parse, int offset, char *buf, size_t blen)
    457 {
    458   FILE *fp;
    459   int dump_err = 1;
    460   char line[256];
    461   int count = 0;
    462   int lineno = 0;
    463   char *data;
    464 
    465   if (offset == -1) offset = parse->offset;
    466 
    467   do
    468   {
    469     if (parse->in_file && parse->context)
    470     {
    471       /* Open the file and find which line we're on */
    472 
    473       fp = fopen(parse->context, "r");
    474       if (fp == NULL) {
    475 	ne_warn("Unable to open context %s", parse->context);
    476 	break;
    477       }
    478       while (fgets(line, sizeof(line), fp) != NULL)
    479       {
    480 	count += strlen(line);
    481 	if (strchr(line, '\n') != NULL)
    482 	  lineno++;
    483 	if (count > offset) break;
    484       }
    485       fclose (fp);
    486       snprintf (buf, blen, "[%s:%d]", parse->context, lineno);
    487     }
    488     else
    489     {
    490       data = parse->context_string;
    491       if (data != NULL)
    492       {
    493 	lineno = 1;
    494 	while (count < offset)
    495 	{
    496 	  if (data[count++] == '\n') lineno++;
    497 	}
    498 	if (parse->context)
    499 	  snprintf (buf, blen, "[%s:~%d]", parse->context, lineno);
    500 	else
    501 	  snprintf (buf, blen, "[lineno:~%d]", lineno);
    502       }
    503       else
    504       {
    505 	if (parse->context)
    506 	  snprintf (buf, blen, "[%s:%d]", parse->context, offset);
    507 	else
    508 	  snprintf (buf, blen, "[offset:%d]", offset);
    509       }
    510     }
    511     dump_err = 0;
    512   } while (0);
    513   if (dump_err)
    514   {
    515     if (parse->context)
    516       snprintf (buf, blen, "[-E- %s:%d]", parse->context, offset);
    517     else
    518       snprintf (buf, blen, "[-E- offset:%d]", offset);
    519   }
    520 
    521   return buf;
    522 }
    523 
    524 static char *expand_state (CS_STATE state)
    525 {
    526   static char buf[256];
    527 
    528   if (state & ST_GLOBAL)
    529     return "GLOBAL";
    530   else if (state & ST_IF)
    531     return "IF";
    532   else if (state & ST_ELSE)
    533     return "ELSE";
    534   else if (state & ST_EACH)
    535     return "EACH";
    536   else if (state & ST_WITH)
    537     return "WITH";
    538   else if (state & ST_DEF)
    539     return "DEF";
    540   else if (state & ST_LOOP)
    541     return "LOOP";
    542   else if (state & ST_ALT)
    543     return "ALT";
    544   else if (state & ST_ESCAPE)
    545     return "ESCAPE";
    546 
    547   snprintf(buf, sizeof(buf), "Unknown state %d", state);
    548   return buf;
    549 }
    550 
    551 NEOERR *cs_parse_string (CSPARSE *parse, char *ibuf, size_t ibuf_len)
    552 {
    553   NEOERR *err = STATUS_OK;
    554   STACK_ENTRY *entry, *current_entry;
    555   char *p;
    556   char *token;
    557   int done = 0;
    558   int i, n;
    559   char *arg;
    560   int initial_stack_depth;
    561   int initial_offset;
    562   char *initial_context;
    563   char tmp[256];
    564 
    565   err = uListAppend(parse->alloc, ibuf);
    566   if (err)
    567   {
    568     free (ibuf);
    569     return nerr_pass (err);
    570   }
    571 
    572   initial_stack_depth = uListLength(parse->stack);
    573   initial_offset = parse->offset;
    574   initial_context = parse->context_string;
    575 
    576   parse->offset = 0;
    577   parse->context_string = ibuf;
    578   while (!done)
    579   {
    580     /* Stage 1: Find <?cs starter */
    581     i = find_open_delim (parse, ibuf, parse->offset, ibuf_len);
    582     if (i >= 0)
    583     {
    584       ibuf[i] = '\0';
    585       /* Create literal with data up until start delim */
    586       /* ne_warn ("literal -> %d-%d", parse->offset, i);  */
    587       err = (*(Commands[0].parse_handler))(parse, 0, &(ibuf[parse->offset]));
    588       /* skip delim */
    589       token = &(ibuf[i+3+parse->taglen]);
    590       while (*token && isspace(*token)) token++;
    591 
    592       p = strstr (token, "?>");
    593       if (p == NULL)
    594       {
    595 	return nerr_raise (NERR_PARSE, "%s Missing end ?> at %s",
    596 	    find_context(parse, i, tmp, sizeof(tmp)), &(ibuf[parse->offset]));
    597       }
    598       *p = '\0';
    599       if (strstr (token, "<?") != NULL)
    600       {
    601 	return nerr_raise (NERR_PARSE, "%s Missing end ?> at %s",
    602 	    find_context(parse, i, tmp, sizeof(tmp)),
    603 	    token);
    604       }
    605       parse->offset = p - ibuf + 2;
    606       if (token[0] != '#') /* handle comments */
    607       {
    608 	for (i = 1; Commands[i].cmd; i++)
    609 	{
    610 	  n = Commands[i].cmdlen;
    611 	  if (!strncasecmp(token, Commands[i].cmd, n))
    612 	  {
    613 	    if ((Commands[i].has_arg && ((token[n] == ':') || (token[n] == '!')))
    614 		|| (token[n] == ' ' || token[n] == '\0' || token[n] == '\r' || token[n] == '\n'))
    615 	    {
    616 	      err = uListGet (parse->stack, -1, (void *)&entry);
    617 	      if (err != STATUS_OK) goto cs_parse_done;
    618 	      if (!(Commands[i].allowed_state & entry->state))
    619 	      {
    620 		return nerr_raise (NERR_PARSE,
    621 		    "%s Command %s not allowed in %s", Commands[i].cmd,
    622 		    find_context(parse, -1, tmp, sizeof(tmp)),
    623 		    expand_state(entry->state));
    624 	      }
    625 	      if (Commands[i].has_arg)
    626 	      {
    627 		/* Need to parse out arg */
    628 		arg = &token[n];
    629 		err = (*(Commands[i].parse_handler))(parse, i, arg);
    630 	      }
    631 	      else
    632 	      {
    633 		err = (*(Commands[i].parse_handler))(parse, i, NULL);
    634 	      }
    635 	      if (err != STATUS_OK) goto cs_parse_done;
    636 	      if (Commands[i].next_state & ST_POP)
    637 	      {
    638                 void *ptr;
    639 		err = uListPop(parse->stack, &ptr);
    640 		if (err != STATUS_OK) goto cs_parse_done;
    641                 entry = (STACK_ENTRY *)ptr;
    642 		if (entry->next_tree)
    643 		  parse->current = entry->next_tree;
    644 		else
    645 		  parse->current = entry->tree;
    646 		free(entry);
    647 	      }
    648 	      if ((Commands[i].next_state & ~ST_POP) != ST_SAME)
    649 	      {
    650 		entry = (STACK_ENTRY *) calloc (1, sizeof (STACK_ENTRY));
    651 		if (entry == NULL)
    652 		  return nerr_raise (NERR_NOMEM,
    653 		      "%s Unable to allocate memory for stack entry",
    654 		      find_context(parse, -1, tmp, sizeof(tmp)));
    655 		entry->state = Commands[i].next_state;
    656 		entry->tree = parse->current;
    657 		entry->location = parse->offset;
    658 		/* Set the new stack escape context to the parent one */
    659 		err = uListGet (parse->stack, -1, (void *)&current_entry);
    660 		if (err != STATUS_OK) {
    661 		  free (entry);
    662 		  goto cs_parse_done;
    663 		}
    664 		entry->escape = current_entry->escape;
    665 		/* Get the future escape context from parse because when
    666 		 * we parse "escape", the new stack has not yet been established.
    667 		 */
    668 		entry->escape = parse->escaping.next_stack;
    669 		parse->escaping.next_stack = parse->escaping.global_ctx;
    670 		err = uListAppend(parse->stack, entry);
    671 		if (err != STATUS_OK) {
    672 		  free (entry);
    673 		  goto cs_parse_done;
    674 		}
    675 	      }
    676 	      break;
    677 	    }
    678 	  }
    679 	}
    680 	if (Commands[i].cmd == NULL)
    681 	{
    682 	  return nerr_raise (NERR_PARSE, "%s Unknown command %s",
    683 	      find_context(parse, -1, tmp, sizeof(tmp)), token);
    684 	}
    685       }
    686     }
    687     else
    688     {
    689       /* Create literal with all remaining data */
    690       err = (*(Commands[0].parse_handler))(parse, 0, &(ibuf[parse->offset]));
    691       done = 1;
    692     }
    693   }
    694   /* Should we check the parse stack here? */
    695   while (uListLength(parse->stack) > initial_stack_depth)
    696   {
    697     err = uListPop(parse->stack, (void *)&entry);
    698     if (err != STATUS_OK) goto cs_parse_done;
    699     if (entry->state & ~(ST_GLOBAL | ST_POP))
    700       return nerr_raise (NERR_PARSE, "%s Non-terminted %s clause",
    701 	  find_context(parse, entry->location, tmp, sizeof(tmp)),
    702           expand_state(entry->state));
    703   }
    704 
    705 cs_parse_done:
    706   parse->offset = initial_offset;
    707   parse->context_string = initial_context;
    708   parse->escaping.current = NEOS_ESCAPE_NONE;
    709   return nerr_pass(err);
    710 }
    711 
    712 static CS_LOCAL_MAP * lookup_map (CSPARSE *parse, char *name, char **rest)
    713 {
    714   CS_LOCAL_MAP *map;
    715   char *c;
    716 
    717   /* This shouldn't happen, but it did once... */
    718   if (name == NULL) return NULL;
    719   map = parse->locals;
    720   c = strchr (name, '.');
    721   if (c != NULL) *c = '\0';
    722   *rest = c;
    723   while (map != NULL)
    724   {
    725     if (!strcmp (map->name, name))
    726     {
    727       if (c != NULL) *c = '.';
    728       return map;
    729     }
    730     map = map->next;
    731   }
    732   if (c != NULL) *c = '.';
    733   return NULL;
    734 }
    735 
    736 static HDF *var_lookup_obj (CSPARSE *parse, char *name)
    737 {
    738   CS_LOCAL_MAP *map;
    739   char *c;
    740   HDF *ret_hdf;
    741 
    742   map = lookup_map (parse, name, &c);
    743   if (map && map->type == CS_TYPE_VAR)
    744   {
    745     if (c == NULL)
    746     {
    747       return map->h;
    748     }
    749     else
    750     {
    751       return hdf_get_obj (map->h, c+1);
    752     }
    753   }
    754   /* smarti:  Added support for global hdf under local hdf */
    755   /* return hdf_get_obj (parse->hdf, name); */
    756   ret_hdf = hdf_get_obj (parse->hdf, name);
    757   if (ret_hdf == NULL && parse->global_hdf != NULL) {
    758     ret_hdf = hdf_get_obj (parse->global_hdf, name);
    759   }
    760   return ret_hdf;
    761 }
    762 
    763 /* Ugh, I have to write the same walking code because I can't grab the
    764  * object for writing, as it might not exist... */
    765 static NEOERR *var_set_value (CSPARSE *parse, char *name, char *value)
    766 {
    767   CS_LOCAL_MAP *map;
    768   char *c;
    769 
    770   map = parse->locals;
    771   c = strchr (name, '.');
    772   if (c != NULL) *c = '\0';
    773   while (map != NULL)
    774   {
    775     if (!strcmp (map->name, name))
    776     {
    777       if (map->type == CS_TYPE_VAR)
    778       {
    779 	if (c == NULL)
    780 	{
    781           if (map->h == NULL) /* node didn't exist yet */
    782             return nerr_pass (hdf_set_value (parse->hdf, map->s, value));
    783           else
    784             return nerr_pass (hdf_set_value (map->h, NULL, value));
    785 	}
    786 	else
    787 	{
    788 	  *c = '.';
    789           if (map->h == NULL) /* node didn't exist yet */
    790           {
    791             NEOERR *err;
    792             char *mapped_name = sprintf_alloc("%s%s", map->s, c);
    793             if (mapped_name == NULL)
    794               return nerr_raise(NERR_NOMEM, "Unable to allocate memory to create mapped name");
    795             err = hdf_set_value(parse->hdf, mapped_name, value);
    796             free(mapped_name);
    797             return nerr_pass(err);
    798           }
    799 	  return nerr_pass (hdf_set_value (map->h, c+1, value));
    800 	}
    801       }
    802       else
    803       {
    804 	if (c == NULL)
    805 	{
    806 	  char *tmp = NULL;
    807 	  /* If this is a string, it might be what we're setting,
    808 	   * ie <?cs set:value = value ?>
    809 	   */
    810 	  if (map->type == CS_TYPE_STRING && map->map_alloc)
    811 	    tmp = map->s;
    812 	  map->type = CS_TYPE_STRING;
    813 	  map->map_alloc = 1;
    814 	  map->s = strdup(value);
    815 	  if (tmp != NULL) free(tmp);
    816 	  if (map->s == NULL && value != NULL)
    817 	    return nerr_raise(NERR_NOMEM,
    818 		"Unable to allocate memory to set var");
    819 
    820 	  return STATUS_OK;
    821 	}
    822 	else {
    823 	  ne_warn("WARNING!! Trying to set sub element '%s' of local variable '%s' which doesn't map to an HDF variable, ignoring", c+1, map->name);
    824 	  return STATUS_OK;
    825 	}
    826       }
    827     }
    828     map = map->next;
    829   }
    830   if (c != NULL) *c = '.';
    831   return nerr_pass (hdf_set_value (parse->hdf, name, value));
    832 }
    833 
    834 static char *var_lookup (CSPARSE *parse, char *name)
    835 {
    836   CS_LOCAL_MAP *map;
    837   char *c;
    838   char* retval;
    839 
    840   map = lookup_map (parse, name, &c);
    841   if (map)
    842   {
    843     if (map->type == CS_TYPE_VAR)
    844     {
    845       if (c == NULL)
    846       {
    847 	return hdf_obj_value (map->h);
    848       }
    849       else
    850       {
    851 	return hdf_get_value (map->h, c+1, NULL);
    852       }
    853     }
    854     /* Hmm, if c != NULL, they are asking for a sub member of something
    855      * which isn't a var... right now we ignore them, I don't know what
    856      * the right thing is */
    857     /* hmm, its possible now that they are getting a reference to a
    858      * string that will be deleted... where is it used? */
    859     else if (map->type == CS_TYPE_STRING)
    860     {
    861       return map->s;
    862     }
    863     else if (map->type == CS_TYPE_NUM)
    864     {
    865       char buf[40];
    866       if (map->s) return map->s;
    867       snprintf (buf, sizeof(buf), "%ld", map->n);
    868       map->s = strdup(buf);
    869       map->map_alloc = 1;
    870       return map->s;
    871     }
    872   }
    873   /* smarti:  Added support for global hdf under local hdf */
    874   /* return hdf_get_value (parse->hdf, name, NULL); */
    875   retval = hdf_get_value (parse->hdf, name, NULL);
    876   if (retval == NULL && parse->global_hdf != NULL) {
    877     retval = hdf_get_value (parse->global_hdf, name, NULL);
    878   }
    879   return retval;
    880 }
    881 
    882 long int var_int_lookup (CSPARSE *parse, char *name)
    883 {
    884   char *vs;
    885 
    886   vs = var_lookup (parse, name);
    887 
    888   if (vs == NULL)
    889     return 0;
    890   else
    891     return atoi(vs);
    892 }
    893 
    894 typedef struct _token
    895 {
    896   CSTOKEN_TYPE type;
    897   char *value;
    898   size_t len;
    899 } CSTOKEN;
    900 
    901 struct _simple_tokens
    902 {
    903   BOOL two_chars;
    904   char *token;
    905   CSTOKEN_TYPE type;
    906 } SimpleTokens[] = {
    907   { TRUE, "<=", CS_OP_LTE },
    908   { TRUE, ">=", CS_OP_GTE },
    909   { TRUE, "==", CS_OP_EQUAL },
    910   { TRUE, "!=", CS_OP_NEQUAL },
    911   { TRUE, "||", CS_OP_OR },
    912   { TRUE, "&&", CS_OP_AND },
    913   { FALSE, "!", CS_OP_NOT },
    914 /* For now, we are still treating this special instead of as an op
    915  * If we make this an op, then we'd have to determine how to handle
    916  * NUM types without doing something like #"5" */
    917 /*  { FALSE, "#", CS_OP_NUM }, */
    918   { FALSE, "?", CS_OP_EXISTS },
    919   { FALSE, "<", CS_OP_LT },
    920   { FALSE, ">", CS_OP_GT },
    921   { FALSE, "+", CS_OP_ADD },
    922   { FALSE, "-", CS_OP_SUB },
    923   { FALSE, "*", CS_OP_MULT },
    924   { FALSE, "/", CS_OP_DIV },
    925   { FALSE, "%", CS_OP_MOD },
    926   { FALSE, "(", CS_OP_LPAREN },
    927   { FALSE, ")", CS_OP_RPAREN },
    928   { FALSE, "[", CS_OP_LBRACKET },
    929   { FALSE, "]", CS_OP_RBRACKET },
    930   { FALSE, ".", CS_OP_DOT },
    931   { FALSE, ",", CS_OP_COMMA },
    932   { FALSE, NULL, 0 }
    933 };
    934 
    935 #define MAX_TOKENS 256
    936 
    937 static NEOERR *parse_tokens (CSPARSE *parse, char *arg, CSTOKEN *tokens,
    938     int *used_tokens)
    939 {
    940   char tmp[256];
    941   int ntokens = 0;
    942   int x;
    943   BOOL found;
    944   BOOL last_is_op = 1;
    945   char *p, *p2;
    946   char *expr = arg;
    947 
    948   while (arg && *arg != '\0')
    949   {
    950     while (*arg && isspace(*arg)) arg++;
    951     if (*arg == '\0') break;
    952     x = 0;
    953     found = FALSE;
    954 
    955     /* If we already saw an operator, and this is a +/-, assume its
    956      * a number */
    957     if (!(last_is_op && (*arg == '+' || *arg == '-')))
    958     {
    959       while ((found == FALSE) && SimpleTokens[x].token)
    960       {
    961 	if (((SimpleTokens[x].two_chars == TRUE) &&
    962 	      (*arg == SimpleTokens[x].token[0]) &&
    963 	      (*(arg + 1) == SimpleTokens[x].token[1])) ||
    964 	    ((SimpleTokens[x].two_chars == FALSE) &&
    965 	     (*arg == SimpleTokens[x].token[0])))
    966 	{
    967 	  tokens[ntokens++].type = SimpleTokens[x].type;
    968 	  found = TRUE;
    969 	  arg++;
    970 	  if (SimpleTokens[x].two_chars) arg++;
    971 	}
    972 	x++;
    973       }
    974       /* Another special case: RPAREN and RBRACKET can have another op
    975        * after it */
    976       if (found && !(tokens[ntokens-1].type == CS_OP_RPAREN || tokens[ntokens-1].type == CS_OP_RBRACKET))
    977 	last_is_op = 1;
    978     }
    979 
    980     if (found == FALSE)
    981     {
    982       if (*arg == '#')
    983       {
    984         /* TODO: make # an operator and not syntax */
    985 	arg++;
    986 	tokens[ntokens].type = CS_TYPE_NUM;
    987 	tokens[ntokens].value = arg;
    988 	strtol(arg, &p, 0);
    989 	if (p == arg)
    990 	{
    991 	  tokens[ntokens].type = CS_TYPE_VAR_NUM;
    992 	  p = strpbrk(arg, "\"?<>=!#-+|&,)*/%[]( \t\r\n");
    993 	  if (p == arg)
    994 	    return nerr_raise (NERR_PARSE, "%s Missing varname/number after #: %s",
    995 		find_context(parse, -1, tmp, sizeof(tmp)), arg);
    996 	}
    997 	if (p == NULL)
    998 	  tokens[ntokens].len = strlen(arg);
    999 	else
   1000 	  tokens[ntokens].len = p - arg;
   1001 	ntokens++;
   1002 	arg = p;
   1003       }
   1004       else if (*arg == '"')
   1005       {
   1006 	arg++;
   1007 	tokens[ntokens].type = CS_TYPE_STRING;
   1008 	tokens[ntokens].value = arg;
   1009 	p = strchr (arg, '"');
   1010 	if (p == NULL)
   1011 	  return nerr_raise (NERR_PARSE, "%s Missing end of string: %s",
   1012 	      find_context(parse, -1, tmp, sizeof(tmp)), arg);
   1013 	tokens[ntokens].len = p - arg;
   1014 	ntokens++;
   1015 	arg = p + 1;
   1016       }
   1017       else if (*arg == '\'')
   1018       {
   1019 	arg++;
   1020 	tokens[ntokens].type = CS_TYPE_STRING;
   1021 	tokens[ntokens].value = arg;
   1022 	p = strchr (arg, '\'');
   1023 	if (p == NULL)
   1024 	  return nerr_raise (NERR_PARSE, "%s Missing end of string: %s",
   1025 	      find_context(parse, -1, tmp, sizeof(tmp)), arg);
   1026 	tokens[ntokens].len = p - arg;
   1027 	ntokens++;
   1028 	arg = p + 1;
   1029       }
   1030       else if (*arg == '$')
   1031       {
   1032         /* TODO: make $ an operator and not syntax */
   1033 	arg++;
   1034 	tokens[ntokens].type = CS_TYPE_VAR;
   1035 	tokens[ntokens].value = arg;
   1036 	p = strpbrk(arg, "\"?<>=!#-+|&,)*/%[]( \t\r\n");
   1037 	if (p == arg)
   1038 	  return nerr_raise (NERR_PARSE, "%s Missing varname after $: %s",
   1039 	      find_context(parse, -1, tmp, sizeof(tmp)), arg);
   1040 	if (p == NULL)
   1041 	  tokens[ntokens].len = strlen(arg);
   1042 	else
   1043 	  tokens[ntokens].len = p - arg;
   1044 	ntokens++;
   1045 	arg = p;
   1046       }
   1047       else
   1048       {
   1049 	tokens[ntokens].type = CS_TYPE_VAR;
   1050 	tokens[ntokens].value = arg;
   1051 	/* Special case for Dave: If this is entirely a number, treat it
   1052 	 * as one */
   1053 	strtol(arg, &p2, 0);
   1054 	p = strpbrk(arg, "\"?<>=!#-+|&,)*/%[]( \t\r\n");
   1055 	/* This is complicated because +/- is valid in a number, but not
   1056 	 * in a varname */
   1057 	if (p2 != arg && (p <= p2 || (p == NULL && *p2 == '\0')))
   1058 	{
   1059 	  tokens[ntokens].type = CS_TYPE_NUM;
   1060 	  tokens[ntokens].len = p2 - arg;
   1061 	  arg = p2;
   1062 	}
   1063 	else
   1064 	{
   1065 	  if (p == arg)
   1066 	    return nerr_raise (NERR_PARSE,
   1067 		"%s Var arg specified with no varname: %s",
   1068 		find_context(parse, -1, tmp, sizeof(tmp)), arg);
   1069 	  if (p == NULL)
   1070 	    tokens[ntokens].len = strlen(arg);
   1071 	  else
   1072 	    tokens[ntokens].len = p - arg;
   1073 	  arg = p;
   1074 	}
   1075 	ntokens++;
   1076       }
   1077       last_is_op = 0;
   1078     }
   1079     if (ntokens >= MAX_TOKENS)
   1080 	return nerr_raise (NERR_PARSE,
   1081 	    "%s Expression exceeds maximum number of tokens of %d: %s",
   1082 	    find_context(parse, -1, tmp, sizeof(tmp)), MAX_TOKENS, expr);
   1083   }
   1084   *used_tokens = ntokens;
   1085   return STATUS_OK;
   1086 }
   1087 
   1088 CSTOKEN_TYPE OperatorOrder[] = {
   1089   CS_OP_COMMA,
   1090   CS_OP_OR,
   1091   CS_OP_AND,
   1092   CS_OP_EQUAL | CS_OP_NEQUAL,
   1093   CS_OP_GT | CS_OP_GTE | CS_OP_LT | CS_OP_LTE,
   1094   CS_OP_ADD | CS_OP_SUB,
   1095   CS_OP_MULT | CS_OP_DIV | CS_OP_MOD,
   1096   CS_OP_NOT | CS_OP_EXISTS,
   1097   CS_OP_LBRACKET | CS_OP_DOT | CS_OP_LPAREN,
   1098   0
   1099 };
   1100 
   1101 static char *expand_token_type(CSTOKEN_TYPE t_type, int more)
   1102 {
   1103   switch (t_type)
   1104   {
   1105     case CS_OP_EXISTS: return "?";
   1106     case CS_OP_NOT: return "!";
   1107     case CS_OP_NUM: return "#";
   1108     case CS_OP_EQUAL: return "==";
   1109     case CS_OP_NEQUAL: return "!=";
   1110     case CS_OP_LT: return "<";
   1111     case CS_OP_LTE: return "<=";
   1112     case CS_OP_GT: return ">";
   1113     case CS_OP_GTE: return ">=";
   1114     case CS_OP_AND: return "&&";
   1115     case CS_OP_OR: return "||";
   1116     case CS_OP_ADD: return "+";
   1117     case CS_OP_SUB: return "-";
   1118     case CS_OP_MULT: return "*";
   1119     case CS_OP_DIV: return "/";
   1120     case CS_OP_MOD: return "%";
   1121     case CS_OP_LPAREN: return "(";
   1122     case CS_OP_RPAREN: return ")";
   1123     case CS_OP_LBRACKET: return "[";
   1124     case CS_OP_RBRACKET: return "]";
   1125     case CS_OP_DOT : return ".";
   1126     case CS_OP_COMMA : return ",";
   1127     case CS_TYPE_STRING: return more ? "STRING" : "s";
   1128     case CS_TYPE_NUM: return more ? "NUM" : "n";
   1129     case CS_TYPE_VAR: return more ? "VAR" : "v";
   1130     case CS_TYPE_VAR_NUM: return more ? "VARNUM" : "vn";
   1131     case CS_TYPE_MACRO: return more ? "MACRO" : "m";
   1132     case CS_TYPE_FUNCTION: return more ? "FUNC" : "f";
   1133     default: return "u";
   1134   }
   1135   return "u";
   1136 }
   1137 
   1138 static char *token_list (CSTOKEN *tokens, int ntokens, char *buf, size_t buflen)
   1139 {
   1140   char *p = buf;
   1141   int i, t;
   1142   char save;
   1143 
   1144   for (i = 0; i < ntokens && buflen > 0; i++)
   1145   {
   1146     if (tokens[i].value)
   1147     {
   1148       save = tokens[i].value[tokens[i].len];
   1149       tokens[i].value[tokens[i].len] = '\0';
   1150       t = snprintf(p, buflen, "%s%d:%s:'%s'", i ? "  ":"", i, expand_token_type(tokens[i].type, 0), tokens[i].value);
   1151       tokens[i].value[tokens[i].len] = save;
   1152     }
   1153     else
   1154     {
   1155       t = snprintf(p, buflen, "%s%d:%s", i ? "  ":"", i, expand_token_type(tokens[i].type, 0));
   1156     }
   1157     if (t == -1 || t >= buflen) return buf;
   1158     buflen -= t;
   1159     p += t;
   1160   }
   1161   return buf;
   1162 }
   1163 
   1164 static NEOERR *parse_expr2 (CSPARSE *parse, CSTOKEN *tokens, int ntokens, int lvalue, CSARG *arg)
   1165 {
   1166   NEOERR *err = STATUS_OK;
   1167   char tmp[256];
   1168   char tmp2[256];
   1169   int x, op;
   1170   int m;
   1171 
   1172 #if DEBUG_EXPR_PARSE
   1173   fprintf(stderr, "%s\n", token_list(tokens, ntokens, tmp, sizeof(tmp)));
   1174   for (x = 0; x < ntokens; x++)
   1175   {
   1176     fprintf (stderr, "%s ", expand_token_type(tokens[x].type, 0));
   1177   }
   1178   fprintf(stderr, "\n");
   1179 #endif
   1180 
   1181   /* Not quite sure what to do with this case... */
   1182   if (ntokens == 0)
   1183   {
   1184     return nerr_raise (NERR_PARSE, "%s Bad Expression",
   1185 	find_context(parse, -1, tmp, sizeof(tmp)));
   1186   }
   1187   if (ntokens == 1)
   1188   {
   1189     x = 0;
   1190     if (tokens[0].type & CS_TYPES)
   1191     {
   1192       arg->s = tokens[0].value;
   1193       if (tokens[0].len >= 0)
   1194 	arg->s[tokens[0].len] = '\0';
   1195       arg->op_type = tokens[0].type;
   1196 
   1197       if (tokens[x].type == CS_TYPE_NUM)
   1198 	arg->n = strtol(arg->s, NULL, 0);
   1199       return STATUS_OK;
   1200     }
   1201     else
   1202     {
   1203       return nerr_raise (NERR_PARSE,
   1204 	  "%s Terminal token is not an argument, type is %s",
   1205 	  find_context(parse, -1, tmp, sizeof(tmp)), expand_token_type(tokens[0].type, 0));
   1206     }
   1207   }
   1208 
   1209   /*
   1210   if (ntokens == 2 && (tokens[0].type & CS_OPS_UNARY))
   1211   {
   1212     arg->op_type = tokens[0].type;
   1213     arg->expr1 = (CSARG *) calloc (1, sizeof (CSARG));
   1214     if (arg->expr1 == NULL)
   1215       return nerr_raise (NERR_NOMEM,
   1216 	  "%s Unable to allocate memory for expression",
   1217 	  find_context(parse, -1, tmp, sizeof(tmp)));
   1218     err = parse_expr2(parse, tokens + 1, 1, lvalue, arg->expr1);
   1219     return nerr_pass(err);
   1220   }
   1221   */
   1222 
   1223   op = 0;
   1224   while (OperatorOrder[op])
   1225   {
   1226     x = ntokens-1;
   1227     while (x >= 0)
   1228     {
   1229       /* handle associative ops by skipping through the entire set here,
   1230        * ie the whole thing is an expression that can't match a binary op */
   1231       if (tokens[x].type & CS_OP_RPAREN)
   1232       {
   1233 	m = 1;
   1234 	x--;
   1235 	while (x >= 0)
   1236 	{
   1237 	  if (tokens[x].type & CS_OP_RPAREN) m++;
   1238 	  if (tokens[x].type & CS_OP_LPAREN) m--;
   1239 	  if (m == 0) break;
   1240 	  x--;
   1241 	}
   1242 	if (m)
   1243 	  return nerr_raise (NERR_PARSE,
   1244 	      "%s Missing left parenthesis in expression",
   1245 	      find_context(parse, -1, tmp, sizeof(tmp)));
   1246 	/* if (x == 0) break; */
   1247 	/* x--; */
   1248 	/* we don't do an x-- here, because we are special casing the
   1249 	 * left bracket to be both an operator and an associative */
   1250       }
   1251       if (tokens[x].type & CS_OP_RBRACKET)
   1252       {
   1253 	m = 1;
   1254 	x--;
   1255 	while (x >= 0)
   1256 	{
   1257 	  if (tokens[x].type & CS_OP_RBRACKET) m++;
   1258 	  if (tokens[x].type & CS_OP_LBRACKET) m--;
   1259 	  if (m == 0) break;
   1260 	  x--;
   1261 	}
   1262 	if (m)
   1263 	  return nerr_raise (NERR_PARSE,
   1264 	      "%s Missing left bracket in expression",
   1265 	      find_context(parse, -1, tmp, sizeof(tmp)));
   1266 	if (x == 0) break;
   1267 	/* we don't do an x-- here, because we are special casing the
   1268 	 * left bracket to be both an operator and an associative */
   1269       }
   1270       if (lvalue && !(tokens[x].type & CS_OPS_LVALUE))
   1271       {
   1272 	return nerr_raise (NERR_PARSE,
   1273 	    "%s Invalid op '%s' in lvalue",
   1274 	    find_context(parse, -1, tmp, sizeof(tmp)),
   1275 	    expand_token_type(tokens[x].type, 0));
   1276       }
   1277       if (tokens[x].type & OperatorOrder[op])
   1278       {
   1279 	if (tokens[x].type & CS_OPS_UNARY)
   1280 	{
   1281 	  if (x == 0)
   1282 	  {
   1283 	    arg->op_type = tokens[x].type;
   1284 	    arg->expr1 = (CSARG *) calloc (1, sizeof (CSARG));
   1285 	    if (arg->expr1 == NULL)
   1286 	      return nerr_raise (NERR_NOMEM,
   1287 		  "%s Unable to allocate memory for expression",
   1288 		  find_context(parse, -1, tmp, sizeof(tmp)));
   1289             if (tokens[x].type & CS_OP_LPAREN)
   1290             {
   1291               if (!(tokens[ntokens-1].type & CS_OP_RPAREN))
   1292               {
   1293                 return nerr_raise (NERR_PARSE,
   1294                                    "%s Missing right parenthesis in expression",
   1295                                    find_context(parse, -1, tmp, sizeof(tmp)));
   1296               }
   1297               /* XXX: we might want to set lvalue to 0 here */
   1298               /* -2 since we strip the RPAREN as well */
   1299               err = parse_expr2(parse, tokens + 1, ntokens-2, lvalue, arg->expr1);
   1300             }
   1301             else
   1302             {
   1303               err = parse_expr2(parse, tokens + 1, ntokens-1, lvalue, arg->expr1);
   1304             }
   1305 	    return nerr_pass(err);
   1306 	  }
   1307 	}
   1308 	else if (tokens[x].type == CS_OP_COMMA)
   1309 	{
   1310 	  /* Technically, comma should be a left to right, not right to
   1311 	   * left, so we're going to build up the arguments in reverse
   1312 	   * order... */
   1313 	  arg->op_type = tokens[x].type;
   1314 	  /* The actual argument is expr1 */
   1315 	  arg->expr1 = (CSARG *) calloc (1, sizeof (CSARG));
   1316 	  /* The previous argument is next */
   1317 	  arg->next = (CSARG *) calloc (1, sizeof (CSARG));
   1318 	  if (arg->expr1 == NULL || arg->next == NULL)
   1319 	    return nerr_raise (NERR_NOMEM,
   1320 		"%s Unable to allocate memory for expression",
   1321 		find_context(parse, -1, tmp, sizeof(tmp)));
   1322 	  err = parse_expr2(parse, tokens + x + 1, ntokens-x-1, lvalue, arg->expr1);
   1323 	  if (err) return nerr_pass (err);
   1324 	  err = parse_expr2(parse, tokens, x, lvalue, arg->next);
   1325 	  if (err) return nerr_pass (err);
   1326 	  return STATUS_OK;
   1327 	}
   1328 	else
   1329 	{
   1330 	  arg->op_type = tokens[x].type;
   1331 	  arg->expr2 = (CSARG *) calloc (1, sizeof (CSARG));
   1332 	  arg->expr1 = (CSARG *) calloc (1, sizeof (CSARG));
   1333 	  if (arg->expr1 == NULL || arg->expr2 == NULL)
   1334 	    return nerr_raise (NERR_NOMEM,
   1335 		"%s Unable to allocate memory for expression",
   1336 		find_context(parse, -1, tmp, sizeof(tmp)));
   1337 	  if (tokens[x].type & CS_OP_LBRACKET)
   1338 	  {
   1339             if (!(tokens[ntokens-1].type & CS_OP_RBRACKET))
   1340             {
   1341               return nerr_raise (NERR_PARSE,
   1342                                  "%s Missing right bracket in expression",
   1343                                  find_context(parse, -1, tmp, sizeof(tmp)));
   1344             }
   1345 	    /* Inside of brackets, we don't limit to valid lvalue ops */
   1346             /* -2 since we strip the RBRACKET as well */
   1347 	    err = parse_expr2(parse, tokens + x + 1, ntokens-x-2, 0, arg->expr2);
   1348 	  }
   1349 	  else
   1350 	  {
   1351 	    err = parse_expr2(parse, tokens + x + 1, ntokens-x-1, lvalue, arg->expr2);
   1352 	  }
   1353 	  if (err) return nerr_pass (err);
   1354 	  err = parse_expr2(parse, tokens, x, lvalue, arg->expr1);
   1355 	  if (err) return nerr_pass (err);
   1356 	  return STATUS_OK;
   1357 	}
   1358       }
   1359       x--;
   1360     }
   1361     op++;
   1362   }
   1363 
   1364   /* Unary op against an entire expression */
   1365   if ((tokens[0].type & CS_OPS_UNARY) && tokens[1].type == CS_OP_LPAREN &&
   1366       tokens[ntokens-1].type == CS_OP_RPAREN)
   1367   {
   1368     arg->op_type = tokens[0].type;
   1369     arg->expr1 = (CSARG *) calloc (1, sizeof (CSARG));
   1370     if (arg->expr1 == NULL)
   1371       return nerr_raise (NERR_NOMEM,
   1372 	  "%s Unable to allocate memory for expression",
   1373 	  find_context(parse, -1, tmp, sizeof(tmp)));
   1374     err = parse_expr2(parse, tokens + 2, ntokens-3, lvalue, arg->expr1);
   1375     return nerr_pass(err);
   1376   }
   1377   if (tokens[0].type & CS_OPS_UNARY)
   1378   {
   1379     arg->op_type = tokens[0].type;
   1380     arg->expr1 = (CSARG *) calloc (1, sizeof (CSARG));
   1381     if (arg->expr1 == NULL)
   1382       return nerr_raise (NERR_NOMEM,
   1383 	  "%s Unable to allocate memory for expression",
   1384 	  find_context(parse, -1, tmp, sizeof(tmp)));
   1385     err = parse_expr2(parse, tokens + 1, ntokens-1, lvalue, arg->expr1);
   1386     return nerr_pass(err);
   1387   }
   1388 
   1389   /* function call */
   1390   if ((tokens[0].type & CS_TYPE_VAR) && tokens[1].type == CS_OP_LPAREN &&
   1391       tokens[ntokens-1].type == CS_OP_RPAREN)
   1392   {
   1393     CS_FUNCTION *csf;
   1394     int nargs;
   1395 
   1396     if (tokens[0].len >= 0)
   1397       tokens[0].value[tokens[0].len] = '\0';
   1398 
   1399     arg->op_type = CS_TYPE_FUNCTION;
   1400     csf = parse->functions;
   1401     while (csf != NULL)
   1402     {
   1403       if (!strcmp(tokens[0].value, csf->name))
   1404       {
   1405 	arg->function = csf;
   1406 	break;
   1407       }
   1408       csf = csf->next;
   1409     }
   1410     if (csf == NULL)
   1411     {
   1412       return nerr_raise (NERR_PARSE, "%s Unknown function %s called",
   1413 	  find_context(parse, -1, tmp, sizeof(tmp)), tokens[0].value);
   1414     }
   1415     arg->expr1 = (CSARG *) calloc (1, sizeof (CSARG));
   1416     if (arg->expr1 == NULL)
   1417       return nerr_raise (NERR_NOMEM,
   1418 	  "%s Unable to allocate memory for expression",
   1419 	  find_context(parse, -1, tmp, sizeof(tmp)));
   1420     if (ntokens-3 > 0) {
   1421       err = parse_expr2(parse, tokens + 2, ntokens-3, lvalue, arg->expr1);
   1422       if (err) return nerr_pass(err);
   1423     } else {
   1424       free(arg->expr1);
   1425       arg->expr1 = NULL;
   1426     }
   1427     nargs = rearrange_for_call(&(arg->expr1));
   1428     if (nargs != arg->function->n_args)
   1429     {
   1430       return nerr_raise (NERR_PARSE,
   1431 	  "%s Incorrect number of arguments in call to %s, expected %d, got %d",
   1432 	  find_context(parse, -1, tmp, sizeof(tmp)), tokens[0].value,
   1433 	  arg->function->n_args, nargs);
   1434     }
   1435     return nerr_pass(err);
   1436   }
   1437 
   1438   return nerr_raise (NERR_PARSE, "%s Bad Expression:%s",
   1439       find_context(parse, -1, tmp, sizeof(tmp)),
   1440       token_list(tokens, ntokens, tmp2, sizeof(tmp2)));
   1441 }
   1442 
   1443 static NEOERR *parse_expr (CSPARSE *parse, char *arg, int lvalue, CSARG *expr)
   1444 {
   1445   NEOERR *err;
   1446   CSTOKEN tokens[MAX_TOKENS];
   1447   int ntokens = 0;
   1448 
   1449   memset(tokens, 0, sizeof(CSTOKEN) * MAX_TOKENS);
   1450   err = parse_tokens (parse, arg, tokens, &ntokens);
   1451   if (err) return nerr_pass(err);
   1452 
   1453   if (parse->audit_mode) {
   1454     /* Save the complete expression string for future reference */
   1455     expr->argexpr = strdup(arg);
   1456   }
   1457 
   1458   err = parse_expr2 (parse, tokens, ntokens, lvalue, expr);
   1459   if (err) return nerr_pass(err);
   1460   return STATUS_OK;
   1461 }
   1462 
   1463 static NEOERR *literal_parse (CSPARSE *parse, int cmd, char *arg)
   1464 {
   1465   NEOERR *err;
   1466   CSTREE *node;
   1467 
   1468   /* ne_warn ("literal: %s", arg); */
   1469   err = alloc_node (&node, parse);
   1470   if (err) return nerr_pass(err);
   1471   node->cmd = cmd;
   1472   node->arg1.op_type = CS_TYPE_STRING;
   1473   node->arg1.s = arg;
   1474   *(parse->next) = node;
   1475   parse->next = &(node->next);
   1476   parse->current = node;
   1477 
   1478   return STATUS_OK;
   1479 }
   1480 
   1481 static NEOERR *literal_eval (CSPARSE *parse, CSTREE *node, CSTREE **next)
   1482 {
   1483   NEOERR *err = STATUS_OK;
   1484 
   1485   if (node->arg1.s != NULL)
   1486     err = parse->output_cb (parse->output_ctx, node->arg1.s);
   1487   *next = node->next;
   1488   return nerr_pass(err);
   1489 }
   1490 
   1491 static NEOERR *name_parse (CSPARSE *parse, int cmd, char *arg)
   1492 {
   1493   NEOERR *err;
   1494   CSTREE *node;
   1495   char *a, *s;
   1496   char tmp[256];
   1497 
   1498   /* ne_warn ("name: %s", arg); */
   1499   err = alloc_node (&node, parse);
   1500   if (err) return nerr_pass(err);
   1501   node->cmd = cmd;
   1502   if (arg[0] == '!')
   1503     node->flags |= CSF_REQUIRED;
   1504   arg++;
   1505   /* Validate arg is a var (regex /^[#" ]$/) */
   1506   a = neos_strip(arg);
   1507   s = strpbrk(a, "#\" <>");
   1508   if (s != NULL)
   1509   {
   1510     dealloc_node(&node);
   1511     return nerr_raise (NERR_PARSE, "%s Invalid character in var name %s: %c",
   1512 	find_context(parse, -1, tmp, sizeof(tmp)),
   1513 	a, s[0]);
   1514   }
   1515 
   1516   node->arg1.op_type = CS_TYPE_VAR;
   1517   node->arg1.s = a;
   1518   *(parse->next) = node;
   1519   parse->next = &(node->next);
   1520   parse->current = node;
   1521 
   1522   return STATUS_OK;
   1523 }
   1524 
   1525 static NEOERR *escape_parse (CSPARSE *parse, int cmd, char *arg)
   1526 {
   1527   NEOERR *err;
   1528   char *a = NULL;
   1529   char tmp[256];
   1530   CS_ESCAPE_MODES *esc_cursor;
   1531   CSTREE *node;
   1532 
   1533   /* ne_warn ("escape: %s", arg); */
   1534   err = alloc_node (&node, parse);
   1535   if (err) return nerr_pass(err);
   1536   node->cmd = cmd;
   1537   /* Since this throws an error always if there's a problem
   1538    * this flag seems pointless, but following convention,
   1539    * here it is. */
   1540   if (arg[0] == '!')
   1541     node->flags |= CSF_REQUIRED;
   1542   arg++; /* ignore colon, space, etc */
   1543 
   1544   /* Parse the arg - we're expecting a string */
   1545   err = parse_expr (parse, arg, 0, &(node->arg1));
   1546   if (err)
   1547   {
   1548     dealloc_node(&node);
   1549     return nerr_pass(err);
   1550   }
   1551   if (node->arg1.op_type != CS_TYPE_STRING)
   1552   {
   1553     dealloc_node(&node);
   1554     return nerr_raise (NERR_PARSE, "%s Invalid argument for escape: %s",
   1555       find_context(parse, -1, tmp, sizeof(tmp)), arg);
   1556   }
   1557 
   1558   a = neos_strip(node->arg1.s); /* Strip spaces for testing */
   1559 
   1560   /* Ensure the mode specified is allowed */
   1561   for (esc_cursor = &EscapeModes[0];
   1562        esc_cursor->mode != NULL;
   1563        esc_cursor++)
   1564     if (!strncasecmp(a, esc_cursor->mode, strlen(esc_cursor->mode)))
   1565     {
   1566       if (err != STATUS_OK) return nerr_pass(err);
   1567       parse->escaping.next_stack = esc_cursor->context;
   1568       break;
   1569     }
   1570   /* Didn't find an acceptable value we were looking for */
   1571   if (esc_cursor->mode == NULL)
   1572   {
   1573     dealloc_node(&node);
   1574     return nerr_raise (NERR_PARSE, "%s Invalid argument for escape: %s",
   1575       find_context(parse, -1, tmp, sizeof(tmp)), a);
   1576   }
   1577 
   1578   *(parse->next) = node;
   1579   parse->next = &(node->case_0);
   1580   parse->current = node;
   1581   return STATUS_OK;
   1582 }
   1583 
   1584 static NEOERR *name_eval (CSPARSE *parse, CSTREE *node, CSTREE **next)
   1585 {
   1586   NEOERR *err = STATUS_OK;
   1587   HDF *obj;
   1588   char *v;
   1589 
   1590   if (node->arg1.op_type == CS_TYPE_VAR && node->arg1.s != NULL)
   1591   {
   1592     obj = var_lookup_obj (parse, node->arg1.s);
   1593     if (obj != NULL)
   1594     {
   1595       v = hdf_obj_name(obj);
   1596       err = parse->output_cb (parse->output_ctx, v);
   1597     }
   1598   }
   1599   *next = node->next;
   1600   return nerr_pass(err);
   1601 }
   1602 
   1603 static NEOERR *var_parse (CSPARSE *parse, int cmd, char *arg)
   1604 {
   1605   NEOERR *err;
   1606   CSTREE *node;
   1607   STACK_ENTRY *entry;
   1608 
   1609   err = uListGet (parse->stack, -1, (void *)&entry);
   1610   if (err != STATUS_OK) return nerr_pass(err);
   1611 
   1612   /* ne_warn ("var: %s", arg); */
   1613   err = alloc_node (&node, parse);
   1614   if (err) return nerr_pass(err);
   1615   node->cmd = cmd;
   1616 
   1617   /* Default escape the variable based on
   1618    * current stack's escape context except for
   1619    * uvar:
   1620    */
   1621   if (!strcmp(Commands[cmd].cmd, "uvar"))
   1622     node->escape = NEOS_ESCAPE_NONE;
   1623   else
   1624     node->escape = entry->escape;
   1625 
   1626 
   1627   if (arg[0] == '!')
   1628     node->flags |= CSF_REQUIRED;
   1629   arg++;
   1630   /* Validate arg is a var (regex /^[#" ]$/) */
   1631   err = parse_expr (parse, arg, 0, &(node->arg1));
   1632   if (err)
   1633   {
   1634     dealloc_node(&node);
   1635     return nerr_pass(err);
   1636   }
   1637 
   1638   *(parse->next) = node;
   1639   parse->next = &(node->next);
   1640   parse->current = node;
   1641 
   1642   return STATUS_OK;
   1643 }
   1644 
   1645 static NEOERR *lvar_parse (CSPARSE *parse, int cmd, char *arg)
   1646 {
   1647   NEOERR *err;
   1648   CSTREE *node;
   1649 
   1650   /* ne_warn ("lvar: %s", arg); */
   1651   err = alloc_node (&node, parse);
   1652   if (err) return nerr_pass(err);
   1653   node->cmd = cmd;
   1654   if (arg[0] == '!')
   1655     node->flags |= CSF_REQUIRED;
   1656   arg++;
   1657   /* Validate arg is a var (regex /^[#" ]$/) */
   1658   err = parse_expr (parse, arg, 0, &(node->arg1));
   1659   if (err)
   1660   {
   1661     dealloc_node(&node);
   1662     return nerr_pass(err);
   1663   }
   1664 
   1665   *(parse->next) = node;
   1666   parse->next = &(node->next);
   1667   parse->current = node;
   1668 
   1669   return STATUS_OK;
   1670 }
   1671 
   1672 static NEOERR *linclude_parse (CSPARSE *parse, int cmd, char *arg)
   1673 {
   1674   NEOERR *err;
   1675   CSTREE *node;
   1676 
   1677   /* ne_warn ("linclude: %s", arg); */
   1678   err = alloc_node (&node, parse);
   1679   if (err) return nerr_pass(err);
   1680   node->cmd = cmd;
   1681   if (arg[0] == '!')
   1682     node->flags |= CSF_REQUIRED;
   1683   arg++;
   1684   /* Validate arg is a var (regex /^[#" ]$/) */
   1685   err = parse_expr (parse, arg, 0, &(node->arg1));
   1686   if (err)
   1687   {
   1688     dealloc_node(&node);
   1689     return nerr_pass(err);
   1690   }
   1691 
   1692   *(parse->next) = node;
   1693   parse->next = &(node->next);
   1694   parse->current = node;
   1695 
   1696   return STATUS_OK;
   1697 }
   1698 
   1699 static NEOERR *alt_parse (CSPARSE *parse, int cmd, char *arg)
   1700 {
   1701   NEOERR *err;
   1702   CSTREE *node;
   1703 
   1704   /* ne_warn ("var: %s", arg); */
   1705   err = alloc_node (&node, parse);
   1706   if (err) return nerr_pass(err);
   1707   node->cmd = cmd;
   1708   if (arg[0] == '!')
   1709     node->flags |= CSF_REQUIRED;
   1710   arg++;
   1711   /* Validate arg is a var (regex /^[#" ]$/) */
   1712   err = parse_expr (parse, arg, 0, &(node->arg1));
   1713   if (err)
   1714   {
   1715     dealloc_node(&node);
   1716     return nerr_pass(err);
   1717   }
   1718 
   1719   *(parse->next) = node;
   1720   parse->next = &(node->case_0);
   1721   parse->current = node;
   1722 
   1723   return STATUS_OK;
   1724 }
   1725 
   1726 static NEOERR *evar_parse (CSPARSE *parse, int cmd, char *arg)
   1727 {
   1728   NEOERR *err;
   1729   CSTREE *node;
   1730   char *a, *s;
   1731   const char *save_context;
   1732   int save_infile;
   1733   char tmp[256];
   1734 
   1735   /* ne_warn ("evar: %s", arg); */
   1736   err = alloc_node (&node, parse);
   1737   if (err) return nerr_pass(err);
   1738   node->cmd = cmd;
   1739   if (arg[0] == '!')
   1740     node->flags |= CSF_REQUIRED;
   1741   arg++;
   1742   /* Validate arg is a var (regex /^[#" ]$/) */
   1743   a = neos_strip(arg);
   1744   s = strpbrk(a, "#\" <>");
   1745   if (s != NULL)
   1746   {
   1747     dealloc_node(&node);
   1748     return nerr_raise (NERR_PARSE, "%s Invalid character in var name %s: %c",
   1749 	find_context(parse, -1, tmp, sizeof(tmp)),
   1750 	a, s[0]);
   1751   }
   1752 
   1753   err = hdf_get_copy (parse->hdf, a, &s, NULL);
   1754   if (err)
   1755   {
   1756     dealloc_node(&node);
   1757     return nerr_pass (err);
   1758   }
   1759   if (node->flags & CSF_REQUIRED && s == NULL)
   1760   {
   1761     dealloc_node(&node);
   1762     return nerr_raise (NERR_NOT_FOUND, "%s Unable to evar empty variable %s",
   1763 	find_context(parse, -1, tmp, sizeof(tmp)), a);
   1764   }
   1765 
   1766   node->arg1.op_type = CS_TYPE_VAR;
   1767   node->arg1.s = a;
   1768   *(parse->next) = node;
   1769   parse->next = &(node->next);
   1770   parse->current = node;
   1771 
   1772   save_context = parse->context;
   1773   save_infile = parse->in_file;
   1774   parse->context = a;
   1775   parse->in_file = 0;
   1776   if (s) err = cs_parse_string (parse, s, strlen(s));
   1777   parse->context = save_context;
   1778   parse->in_file = save_infile;
   1779 
   1780   return nerr_pass (err);
   1781 }
   1782 
   1783 static NEOERR *if_parse (CSPARSE *parse, int cmd, char *arg)
   1784 {
   1785   NEOERR *err;
   1786   CSTREE *node;
   1787 
   1788   /* ne_warn ("if: %s", arg); */
   1789   err = alloc_node (&node, parse);
   1790   if (err != STATUS_OK) return nerr_pass(err);
   1791   node->cmd = cmd;
   1792   arg++;
   1793 
   1794   err = parse_expr (parse, arg, 0, &(node->arg1));
   1795   if (err != STATUS_OK)
   1796   {
   1797     dealloc_node(&node);
   1798     return nerr_pass(err);
   1799   }
   1800 
   1801   *(parse->next) = node;
   1802   parse->next = &(node->case_0);
   1803   parse->current = node;
   1804 
   1805   return STATUS_OK;
   1806 }
   1807 
   1808 char *arg_eval (CSPARSE *parse, CSARG *arg)
   1809 {
   1810   switch ((arg->op_type & CS_TYPES))
   1811   {
   1812     case CS_TYPE_STRING:
   1813       return arg->s;
   1814     case CS_TYPE_VAR:
   1815       return var_lookup (parse, arg->s);
   1816     case CS_TYPE_NUM:
   1817     case CS_TYPE_VAR_NUM:
   1818     default:
   1819       ne_warn ("Unsupported type %s in arg_eval", expand_token_type(arg->op_type, 1));
   1820       return NULL;
   1821   }
   1822 }
   1823 
   1824 /* This coerces everything to numbers */
   1825 long int arg_eval_num (CSPARSE *parse, CSARG *arg)
   1826 {
   1827   long int v = 0;
   1828 
   1829   switch ((arg->op_type & CS_TYPES))
   1830   {
   1831     case CS_TYPE_STRING:
   1832       v = strtol(arg->s, NULL, 0);
   1833       break;
   1834     case CS_TYPE_NUM:
   1835       v = arg->n;
   1836       break;
   1837 
   1838     case CS_TYPE_VAR:
   1839     case CS_TYPE_VAR_NUM:
   1840       v = var_int_lookup (parse, arg->s);
   1841       break;
   1842     default:
   1843       ne_warn ("Unsupported type %s in arg_eval_num", expand_token_type(arg->op_type, 1));
   1844       v = 0;
   1845       break;
   1846   }
   1847   return v;
   1848 }
   1849 
   1850 /* This is different from arg_eval_num because we don't force strings to
   1851  * numbers, a string is either a number (if it is all numeric) or we're
   1852  * testing existance.  At least, that's what perl does and what dave
   1853  * wants */
   1854 long int arg_eval_bool (CSPARSE *parse, CSARG *arg)
   1855 {
   1856   long int v = 0;
   1857   char *s, *r;
   1858 
   1859   switch ((arg->op_type & CS_TYPES))
   1860   {
   1861     case CS_TYPE_STRING:
   1862     case CS_TYPE_VAR:
   1863       if (arg->op_type == CS_TYPE_VAR)
   1864 	s = var_lookup(parse, arg->s);
   1865       else
   1866 	s = arg->s;
   1867       if (!s || *s == '\0') return 0; /* non existance or empty is false(0) */
   1868       v = strtol(s, &r, 0);
   1869       if (*r == '\0') /* entire string converted, treat as number */
   1870 	return v;
   1871       /* if the entire string didn't convert, then its non-numeric and
   1872        * exists, so its true (1) */
   1873       return 1;
   1874     case CS_TYPE_NUM:
   1875       return arg->n;
   1876     case CS_TYPE_VAR_NUM: /* this implies forced numeric evaluation */
   1877       return var_int_lookup (parse, arg->s);
   1878       break;
   1879     default:
   1880       ne_warn ("Unsupported type %s in arg_eval_bool", expand_token_type(arg->op_type, 1));
   1881       v = 0;
   1882       break;
   1883   }
   1884   return v;
   1885 }
   1886 
   1887 char *arg_eval_str_alloc (CSPARSE *parse, CSARG *arg)
   1888 {
   1889   char *s = NULL;
   1890   char buf[256];
   1891   long int n_val;
   1892 
   1893   switch ((arg->op_type & CS_TYPES))
   1894   {
   1895     case CS_TYPE_STRING:
   1896       s = arg->s;
   1897       break;
   1898     case CS_TYPE_VAR:
   1899       s = var_lookup (parse, arg->s);
   1900       break;
   1901     case CS_TYPE_NUM:
   1902     case CS_TYPE_VAR_NUM:
   1903       s = buf;
   1904       n_val = arg_eval_num (parse, arg);
   1905       snprintf (buf, sizeof(buf), "%ld", n_val);
   1906       break;
   1907     default:
   1908       ne_warn ("Unsupported type %s in arg_eval_str_alloc",
   1909 	  expand_token_type(arg->op_type, 1));
   1910       s = NULL;
   1911       break;
   1912   }
   1913   if (s) return strdup(s);
   1914   return NULL;
   1915 }
   1916 
   1917 #if DEBUG_EXPR_EVAL
   1918 static void expand_arg (CSPARSE *parse, int depth, char *where, CSARG *arg)
   1919 {
   1920   int x;
   1921 
   1922   for (x=0; x<depth; x++)
   1923     fputc(' ', stderr);
   1924 
   1925   fprintf(stderr, "%s op: %s alloc: %d value: ", where, expand_token_type(arg->op_type, 0), arg->alloc);
   1926   if (arg->op_type & CS_OP_NOT)
   1927     fprintf(stderr, "!");
   1928   if (arg->op_type & CS_OP_NUM)
   1929     fprintf(stderr, "#");
   1930   if (arg->op_type & CS_OP_EXISTS)
   1931     fprintf(stderr, "?");
   1932   if (arg->op_type & (CS_TYPE_VAR_NUM | CS_TYPE_NUM))
   1933     fprintf(stderr, "#");
   1934   if (arg->op_type & CS_TYPE_NUM)
   1935     fprintf(stderr, "%ld\n", arg->n);
   1936   else if (arg->op_type & CS_TYPE_STRING)
   1937     fprintf(stderr, "'%s'\n", arg->s);
   1938   else if (arg->op_type & CS_TYPE_VAR)
   1939     fprintf(stderr, "%s = %s\n", arg->s, var_lookup(parse, arg->s));
   1940   else if (arg->op_type & CS_TYPE_VAR_NUM)
   1941     fprintf(stderr, "%s = %ld\n", arg->s, var_int_lookup(parse, arg->s));
   1942   else
   1943     fprintf(stderr, "\n");
   1944 }
   1945 #endif
   1946 
   1947 static NEOERR *eval_expr_string(CSPARSE *parse, CSARG *arg1, CSARG *arg2, CSTOKEN_TYPE op, CSARG *result)
   1948 {
   1949   char *s1, *s2;
   1950   int out;
   1951 
   1952   result->op_type = CS_TYPE_NUM;
   1953   s1 = arg_eval (parse, arg1);
   1954   s2 = arg_eval (parse, arg2);
   1955 
   1956   if ((s1 == NULL) || (s2 == NULL))
   1957   {
   1958     switch (op)
   1959     {
   1960       case CS_OP_EQUAL:
   1961 	result->n = (s1 == s2) ? 1 : 0;
   1962 	break;
   1963       case CS_OP_NEQUAL:
   1964 	result->n = (s1 != s2) ? 1 : 0;
   1965 	break;
   1966       case CS_OP_LT:
   1967 	result->n = ((s1 == NULL) && (s2 != NULL)) ? 1 : 0;
   1968 	break;
   1969       case CS_OP_LTE:
   1970 	result->n = (s1 == NULL) ? 1 : 0;
   1971 	break;
   1972       case CS_OP_GT:
   1973 	result->n = ((s1 != NULL) && (s2 == NULL)) ? 1 : 0;
   1974 	break;
   1975       case CS_OP_GTE:
   1976 	result->n = (s2 == NULL) ? 1 : 0;
   1977 	break;
   1978       case CS_OP_ADD:
   1979 	/* be sure to transfer ownership of the string here */
   1980 	result->op_type = CS_TYPE_STRING;
   1981 	if (s1 == NULL)
   1982 	{
   1983 	  result->s = s2;
   1984 	  result->alloc = arg2->alloc;
   1985 	  arg2->alloc = 0;
   1986 	}
   1987 	else
   1988 	{
   1989 	  result->s = s1;
   1990 	  result->alloc = arg1->alloc;
   1991 	  arg1->alloc = 0;
   1992 	}
   1993 	break;
   1994       default:
   1995 	ne_warn ("Unsupported op %s in eval_expr", expand_token_type(op, 1));
   1996 	break;
   1997     }
   1998   }
   1999   else
   2000   {
   2001     out = strcmp (s1, s2);
   2002     switch (op)
   2003     {
   2004       case CS_OP_EQUAL:
   2005 	result->n = (!out) ? 1 : 0;
   2006 	break;
   2007       case CS_OP_NEQUAL:
   2008 	result->n = (out) ? 1 : 0;
   2009 	break;
   2010       case CS_OP_LT:
   2011 	result->n = (out < 0) ? 1 : 0;
   2012 	break;
   2013       case CS_OP_LTE:
   2014 	result->n = (out <= 0) ? 1 : 0;
   2015 	break;
   2016       case CS_OP_GT:
   2017 	result->n = (out > 0) ? 1 : 0;
   2018 	break;
   2019       case CS_OP_GTE:
   2020 	result->n = (out >= 0) ? 1 : 0;
   2021 	break;
   2022       case CS_OP_ADD:
   2023 	result->op_type = CS_TYPE_STRING;
   2024 	result->alloc = 1;
   2025 	result->s = (char *) calloc ((strlen(s1) + strlen(s2) + 1), sizeof(char));
   2026 	if (result->s == NULL)
   2027 	  return nerr_raise (NERR_NOMEM, "Unable to allocate memory to concatenate strings in expression: %s + %s", s1, s2);
   2028 	strcpy(result->s, s1);
   2029 	strcat(result->s, s2);
   2030 	break;
   2031       default:
   2032 	ne_warn ("Unsupported op %s in eval_expr_string", expand_token_type(op, 1));
   2033 	break;
   2034     }
   2035   }
   2036   return STATUS_OK;
   2037 }
   2038 
   2039 static NEOERR *eval_expr_num(CSPARSE *parse, CSARG *arg1, CSARG *arg2, CSTOKEN_TYPE op, CSARG *result)
   2040 {
   2041   long int n1, n2;
   2042 
   2043   result->op_type = CS_TYPE_NUM;
   2044   n1 = arg_eval_num (parse, arg1);
   2045   n2 = arg_eval_num (parse, arg2);
   2046 
   2047   switch (op)
   2048   {
   2049     case CS_OP_EQUAL:
   2050       result->n = (n1 == n2) ? 1 : 0;
   2051       break;
   2052     case CS_OP_NEQUAL:
   2053       result->n = (n1 != n2) ? 1 : 0;
   2054       break;
   2055     case CS_OP_LT:
   2056       result->n = (n1 < n2) ? 1 : 0;
   2057       break;
   2058     case CS_OP_LTE:
   2059       result->n = (n1 <= n2) ? 1 : 0;
   2060       break;
   2061     case CS_OP_GT:
   2062       result->n = (n1 > n2) ? 1 : 0;
   2063       break;
   2064     case CS_OP_GTE:
   2065       result->n = (n1 >= n2) ? 1 : 0;
   2066       break;
   2067     case CS_OP_ADD:
   2068       result->n = (n1 + n2);
   2069       break;
   2070     case CS_OP_SUB:
   2071       result->n = (n1 - n2);
   2072       break;
   2073     case CS_OP_MULT:
   2074       result->n = (n1 * n2);
   2075       break;
   2076     case CS_OP_DIV:
   2077       if (n2 == 0) result->n = UINT_MAX;
   2078       else result->n = (n1 / n2);
   2079       break;
   2080     case CS_OP_MOD:
   2081       if (n2 == 0) result->n = 0;
   2082       else result->n = (n1 % n2);
   2083       break;
   2084     default:
   2085       ne_warn ("Unsupported op %s in eval_expr_num", expand_token_type(op, 1));
   2086       break;
   2087   }
   2088   return STATUS_OK;
   2089 }
   2090 
   2091 static NEOERR *eval_expr_bool(CSPARSE *parse, CSARG *arg1, CSARG *arg2, CSTOKEN_TYPE op, CSARG *result)
   2092 {
   2093   long int n1, n2;
   2094 
   2095   result->op_type = CS_TYPE_NUM;
   2096   n1 = arg_eval_bool (parse, arg1);
   2097   n2 = arg_eval_bool (parse, arg2);
   2098 
   2099   switch (op)
   2100   {
   2101     case CS_OP_AND:
   2102       result->n = (n1 && n2) ? 1 : 0;
   2103       break;
   2104     case CS_OP_OR:
   2105       result->n = (n1 || n2) ? 1 : 0;
   2106       break;
   2107     default:
   2108       ne_warn ("Unsupported op %s in eval_expr_bool", expand_token_type(op, 1));
   2109       break;
   2110   }
   2111   return STATUS_OK;
   2112 }
   2113 
   2114 #if DEBUG_EXPR_EVAL
   2115 static int _depth = 0;
   2116 #endif
   2117 
   2118 static NEOERR *eval_expr (CSPARSE *parse, CSARG *expr, CSARG *result)
   2119 {
   2120   NEOERR *err;
   2121 
   2122   if (expr == NULL)
   2123     return nerr_raise (NERR_ASSERT, "expr is NULL");
   2124   if (result == NULL)
   2125     return nerr_raise (NERR_ASSERT, "result is NULL");
   2126 
   2127 #if DEBUG_EXPR_EVAL
   2128   _depth++;
   2129   expand_arg(parse, _depth, "expr", expr);
   2130 #endif
   2131 
   2132   memset(result, 0, sizeof(CSARG));
   2133   if (expr->op_type & CS_TYPES)
   2134   {
   2135     *result = *expr;
   2136     /* we transfer ownership of the string here.. ugh */
   2137     if (expr->alloc) expr->alloc = 0;
   2138 #if DEBUG_EXPR_EVAL
   2139     expand_arg(parse, _depth, "result", result);
   2140     _depth--;
   2141 #endif
   2142     return STATUS_OK;
   2143   }
   2144 
   2145   if (expr->op_type & CS_OP_LPAREN)
   2146   {
   2147     /* lparen is a no-op, just skip */
   2148     return nerr_pass(eval_expr(parse, expr->expr1, result));
   2149   }
   2150   if (expr->op_type & CS_TYPE_FUNCTION)
   2151   {
   2152     if (expr->function == NULL || expr->function->function == NULL)
   2153       return nerr_raise(NERR_ASSERT,
   2154           "Function is NULL in attempt to evaluate function call %s",
   2155           (expr->function) ? expr->function->name : "");
   2156 
   2157     /* The function evaluates all the arguments, so don't pre-evaluate
   2158      * argument1 */
   2159     err = expr->function->function(parse, expr->function, expr->expr1, result);
   2160     if (err) return nerr_pass(err);
   2161     /* Indicate whether or not an explicit escape call was made by
   2162      * setting the mode (usually NONE or FUNCTION). This is ORed to
   2163      * ensure that escaping calls within other functions do not get
   2164      * double-escaped. E.g. slice(html_escape(foo), 10, 20) */
   2165     parse->escaping.current |= expr->function->escape;
   2166   }
   2167   else
   2168   {
   2169     CSARG arg1, arg2;
   2170     arg1.alloc = 0;
   2171     arg2.alloc = 0;
   2172 
   2173     err = eval_expr (parse, expr->expr1, &arg1);
   2174     if (err) return nerr_pass(err);
   2175 #if DEBUG_EXPR_EVAL
   2176     expand_arg(parse, _depth, "arg1", &arg1);
   2177 #endif
   2178     if (expr->op_type & CS_OPS_UNARY)
   2179     {
   2180       result->op_type = CS_TYPE_NUM;
   2181       switch (expr->op_type) {
   2182         case CS_OP_NOT:
   2183           result->n = arg_eval_bool(parse, &arg1) ? 0 : 1;
   2184           break;
   2185         case CS_OP_EXISTS:
   2186           if (arg1.op_type & (CS_TYPE_VAR | CS_TYPE_VAR_NUM))
   2187           {
   2188             if (arg_eval(parse, &arg1) == NULL)
   2189               result->n = 0;
   2190             else
   2191               result->n = 1;
   2192           }
   2193           else
   2194           {
   2195             /* All numbers/strings exist */
   2196             result->n = 1;
   2197           }
   2198           break;
   2199         case CS_OP_NUM:
   2200           result->n = arg_eval_num (parse, &arg1);
   2201           break;
   2202         case CS_OP_LPAREN:
   2203           return nerr_raise(NERR_ASSERT, "LPAREN should be handled above");
   2204         default:
   2205           result->n = 0;
   2206           ne_warn ("Unsupported op %s in eval_expr", expand_token_type(expr->op_type, 1));
   2207           break;
   2208       }
   2209     }
   2210     else if (expr->op_type == CS_OP_COMMA)
   2211     {
   2212       /* The comma operator, like in C, we return the value of the right
   2213        * most argument, in this case that's expr1, but we still need to
   2214        * evaluate the other stuff */
   2215       if (expr->next)
   2216       {
   2217         err = eval_expr (parse, expr->next, &arg2);
   2218 #if DEBUG_EXPR_EVAL
   2219         expand_arg(parse, _depth, "arg2", &arg2);
   2220 #endif
   2221         if (err) return nerr_pass(err);
   2222         if (arg2.alloc) free(arg2.s);
   2223       }
   2224       *result = arg1;
   2225       /* we transfer ownership of the string here.. ugh */
   2226       if (arg1.alloc) arg1.alloc = 0;
   2227 #if DEBUG_EXPR_EVAL
   2228       expand_arg(parse, _depth, "result", result);
   2229       _depth--;
   2230 #endif
   2231       return STATUS_OK;
   2232     }
   2233     else
   2234     {
   2235       err = eval_expr (parse, expr->expr2, &arg2);
   2236 #if DEBUG_EXPR_EVAL
   2237       expand_arg(parse, _depth, "arg2", &arg2);
   2238 #endif
   2239       if (err) return nerr_pass(err);
   2240 
   2241       if (expr->op_type == CS_OP_LBRACKET)
   2242       {
   2243         /* the bracket op is essentially hdf array lookups, which just
   2244          * means appending the value of arg2, .0 */
   2245         result->op_type = CS_TYPE_VAR;
   2246         result->alloc = 1;
   2247         if (arg2.op_type & (CS_TYPE_VAR_NUM | CS_TYPE_NUM))
   2248         {
   2249           long int n2 = arg_eval_num (parse, &arg2);
   2250           result->s = sprintf_alloc("%s.%ld", arg1.s, n2);
   2251           if (result->s == NULL)
   2252             return nerr_raise (NERR_NOMEM, "Unable to allocate memory to concatenate varnames in expression: %s + %ld", arg1.s, n2);
   2253         }
   2254         else
   2255         {
   2256           char *s2 = arg_eval (parse, &arg2);
   2257           if (s2 && s2[0])
   2258           {
   2259             result->s = sprintf_alloc("%s.%s", arg1.s, s2);
   2260             if (result->s == NULL)
   2261               return nerr_raise (NERR_NOMEM, "Unable to allocate memory to concatenate varnames in expression: %s + %s", arg1.s, s2);
   2262           }
   2263           else
   2264           {
   2265             /* if s2 doesn't match anything, then the whole thing is empty */
   2266             result->s = "";
   2267             result->alloc = 0;
   2268           }
   2269         }
   2270       }
   2271       else if (expr->op_type == CS_OP_DOT)
   2272       {
   2273         /* the dot op is essentially extending the hdf name, which just
   2274          * means appending the string .0 */
   2275         result->op_type = CS_TYPE_VAR;
   2276         result->alloc = 1;
   2277         if (arg2.op_type & CS_TYPES_VAR)
   2278         {
   2279           result->s = sprintf_alloc("%s.%s", arg1.s, arg2.s);
   2280           if (result->s == NULL)
   2281             return nerr_raise (NERR_NOMEM, "Unable to allocate memory to concatenate varnames in expression: %s + %s", arg1.s, arg2.s);
   2282         }
   2283         else
   2284         {
   2285           if (arg2.op_type & CS_TYPE_NUM)
   2286           {
   2287             long int n2 = arg_eval_num (parse, &arg2);
   2288             result->s = sprintf_alloc("%s.%ld", arg1.s, n2);
   2289             if (result->s == NULL)
   2290               return nerr_raise (NERR_NOMEM, "Unable to allocate memory to concatenate varnames in expression: %s + %ld", arg1.s, n2);
   2291           }
   2292           else
   2293           {
   2294             char *s2 = arg_eval (parse, &arg2);
   2295             if (s2 && s2[0])
   2296             {
   2297               result->s = sprintf_alloc("%s.%s", arg1.s, s2);
   2298               if (result->s == NULL)
   2299                 return nerr_raise (NERR_NOMEM, "Unable to allocate memory to concatenate varnames in expression: %s + %s", arg1.s, s2);
   2300             }
   2301             else
   2302             {
   2303               /* if s2 doesn't match anything, then the whole thing is empty */
   2304               result->s = "";
   2305               result->alloc = 0;
   2306             }
   2307           }
   2308         }
   2309       }
   2310       else if (expr->op_type & (CS_OP_AND | CS_OP_OR))
   2311       {
   2312         /* eval as bool */
   2313         err = eval_expr_bool (parse, &arg1, &arg2, expr->op_type, result);
   2314       }
   2315       else if ((arg1.op_type & (CS_TYPE_NUM | CS_TYPE_VAR_NUM)) ||
   2316                (arg2.op_type & (CS_TYPE_NUM | CS_TYPE_VAR_NUM)) ||
   2317                (expr->op_type & (CS_OP_AND | CS_OP_OR | CS_OP_SUB | CS_OP_MULT | CS_OP_DIV | CS_OP_MOD | CS_OP_GT | CS_OP_GTE | CS_OP_LT | CS_OP_LTE)))
   2318       {
   2319         /* eval as num */
   2320         err = eval_expr_num(parse, &arg1, &arg2, expr->op_type, result);
   2321       }
   2322       else /* eval as string */
   2323       {
   2324         err = eval_expr_string(parse, &arg1, &arg2, expr->op_type, result);
   2325       }
   2326     }
   2327     if (arg1.alloc) free(arg1.s);
   2328     if (arg2.alloc) free(arg2.s);
   2329   }
   2330 
   2331 #if DEBUG_EXPR_EVAL
   2332   expand_arg(parse, _depth, "result", result);
   2333   _depth--;
   2334 #endif
   2335   return STATUS_OK;
   2336 }
   2337 
   2338 static NEOERR *var_eval (CSPARSE *parse, CSTREE *node, CSTREE **next)
   2339 {
   2340   NEOERR *err = STATUS_OK;
   2341   CSARG val;
   2342 
   2343   parse->escaping.current = NEOS_ESCAPE_NONE;
   2344   err = eval_expr(parse, &(node->arg1), &val);
   2345   if (err) return nerr_pass(err);
   2346   if (val.op_type & (CS_TYPE_NUM | CS_TYPE_VAR_NUM))
   2347   {
   2348     char buf[256];
   2349     long int n_val;
   2350 
   2351     n_val = arg_eval_num (parse, &val);
   2352     snprintf (buf, sizeof(buf), "%ld", n_val);
   2353     err = parse->output_cb (parse->output_ctx, buf);
   2354   }
   2355   else
   2356   {
   2357     char *s = arg_eval (parse, &val);
   2358     /* Determine if the node has been escaped by an explicit function. If not
   2359      * call to escape. node->escape should contain the default escaping from
   2360      * Config.VarEscapeMode and parse->escaping.current will have a non-zero
   2361      * value if an explicit escape call was made sooooo.
   2362      */
   2363     if (s && parse->escaping.current == NEOS_ESCAPE_NONE) /* no explicit escape */
   2364     {
   2365       char *escaped = NULL;
   2366       /* Use default escape if escape is UNDEF */
   2367       if (node->escape == NEOS_ESCAPE_UNDEF)
   2368         err = neos_var_escape(parse->escaping.when_undef, s, &escaped);
   2369       else
   2370         err = neos_var_escape(node->escape, s, &escaped);
   2371 
   2372       if (escaped)
   2373       {
   2374         err = parse->output_cb (parse->output_ctx, escaped);
   2375         free(escaped);
   2376       }
   2377     }
   2378     else if (s)
   2379     { /* already explicitly escaped */
   2380       err = parse->output_cb (parse->output_ctx, s);
   2381     }
   2382     /* Do we set it to blank if s == NULL? */
   2383   }
   2384   if (val.alloc) free(val.s);
   2385 
   2386   *next = node->next;
   2387   return nerr_pass(err);
   2388 }
   2389 
   2390 static NEOERR *lvar_eval (CSPARSE *parse, CSTREE *node, CSTREE **next)
   2391 {
   2392   NEOERR *err = STATUS_OK;
   2393   CSARG val;
   2394 
   2395   err = eval_expr(parse, &(node->arg1), &val);
   2396   if (err) return nerr_pass(err);
   2397   if (val.op_type & (CS_TYPE_NUM | CS_TYPE_VAR_NUM))
   2398   {
   2399     char buf[256];
   2400     long int n_val;
   2401 
   2402     n_val = arg_eval_num (parse, &val);
   2403     snprintf (buf, sizeof(buf), "%ld", n_val);
   2404     err = parse->output_cb (parse->output_ctx, buf);
   2405   }
   2406   else
   2407   {
   2408     char *s = arg_eval (parse, &val);
   2409 
   2410     if (s)
   2411     {
   2412       CSPARSE *cs = NULL;
   2413 
   2414       /* Ok, we need our own copy of the string to pass to
   2415        * cs_parse_string... */
   2416       if (val.alloc && (val.op_type & CS_TYPE_STRING)) {
   2417 	val.alloc = 0;
   2418       }
   2419       else
   2420       {
   2421 	s = strdup(s);
   2422 	if (s == NULL)
   2423 	{
   2424 	  return nerr_raise(NERR_NOMEM, "Unable to allocate memory for lvar_eval");
   2425 	}
   2426       }
   2427 
   2428       do {
   2429 	err = cs_init_internal(&cs, parse->hdf, parse);
   2430 	if (err) break;
   2431 	err = cs_parse_string(cs, s, strlen(s));
   2432 	if (err) break;
   2433 	err = cs_render(cs, parse->output_ctx, parse->output_cb);
   2434 	if (err) break;
   2435       } while (0);
   2436       cs_destroy(&cs);
   2437     }
   2438   }
   2439   if (val.alloc) free(val.s);
   2440 
   2441   *next = node->next;
   2442   return nerr_pass(err);
   2443 }
   2444 
   2445 static NEOERR *linclude_eval (CSPARSE *parse, CSTREE *node, CSTREE **next)
   2446 {
   2447   NEOERR *err = STATUS_OK;
   2448   CSARG val;
   2449 
   2450   err = eval_expr(parse, &(node->arg1), &val);
   2451   if (err) return nerr_pass(err);
   2452   if (val.op_type & (CS_TYPE_NUM | CS_TYPE_VAR_NUM))
   2453   {
   2454     char buf[256];
   2455     long int n_val;
   2456 
   2457     n_val = arg_eval_num (parse, &val);
   2458     snprintf (buf, sizeof(buf), "%ld", n_val);
   2459     err = parse->output_cb (parse->output_ctx, buf);
   2460   }
   2461   else
   2462   {
   2463     char *s = arg_eval (parse, &val);
   2464 
   2465     if (s)
   2466     {
   2467       CSPARSE *cs = NULL;
   2468       do {
   2469 	err = cs_init_internal(&cs, parse->hdf, parse);
   2470 	if (err) break;
   2471 	err = cs_parse_file(cs, s);
   2472 	if (!(node->flags & CSF_REQUIRED))
   2473 	{
   2474 	  nerr_handle(&err, NERR_NOT_FOUND);
   2475 	}
   2476 	if (err) break;
   2477 	err = cs_render(cs, parse->output_ctx, parse->output_cb);
   2478 	if (err) break;
   2479       } while (0);
   2480       cs_destroy(&cs);
   2481     }
   2482   }
   2483   if (val.alloc) free(val.s);
   2484 
   2485   *next = node->next;
   2486   return nerr_pass(err);
   2487 }
   2488 
   2489 /* if the expr evaluates to true, display it, else render the alternate */
   2490 static NEOERR *alt_eval (CSPARSE *parse, CSTREE *node, CSTREE **next)
   2491 {
   2492   NEOERR *err = STATUS_OK;
   2493   CSARG val;
   2494   int eval_true = 1;
   2495 
   2496   err = eval_expr(parse, &(node->arg1), &val);
   2497   if (err) return nerr_pass(err);
   2498   eval_true = arg_eval_bool(parse, &val);
   2499   if (eval_true)
   2500   {
   2501     if (val.op_type & (CS_TYPE_NUM | CS_TYPE_VAR_NUM))
   2502     {
   2503       char buf[256];
   2504       long int n_val;
   2505 
   2506       n_val = arg_eval_num (parse, &val);
   2507       snprintf (buf, sizeof(buf), "%ld", n_val);
   2508       err = parse->output_cb (parse->output_ctx, buf);
   2509     }
   2510     else
   2511     {
   2512       char *s = arg_eval (parse, &val);
   2513       /* Do we set it to blank if s == NULL? */
   2514       if (s)
   2515       {
   2516 	err = parse->output_cb (parse->output_ctx, s);
   2517       }
   2518     }
   2519   }
   2520   if (val.alloc) free(val.s);
   2521 
   2522   if (eval_true == 0)
   2523   {
   2524     err = render_node (parse, node->case_0);
   2525   }
   2526 
   2527   *next = node->next;
   2528   return nerr_pass(err);
   2529 }
   2530 
   2531 /* just calls through to the child nodes */
   2532 static NEOERR *escape_eval (CSPARSE *parse, CSTREE *node, CSTREE **next)
   2533 {
   2534   NEOERR *err = STATUS_OK;
   2535   /* TODO(wad): Should I set a eval-time value here? */
   2536   err = render_node (parse, node->case_0);
   2537   *next = node->next;
   2538   return nerr_pass(err);
   2539 }
   2540 
   2541 
   2542 static NEOERR *if_eval (CSPARSE *parse, CSTREE *node, CSTREE **next)
   2543 {
   2544   NEOERR *err = STATUS_OK;
   2545   int eval_true = 0;
   2546   CSARG val;
   2547 
   2548   err = eval_expr(parse, &(node->arg1), &val);
   2549   if (err) return nerr_pass (err);
   2550   eval_true = arg_eval_bool(parse, &val);
   2551   if (val.alloc) free(val.s);
   2552 
   2553   if (eval_true)
   2554   {
   2555     err = render_node (parse, node->case_0);
   2556   }
   2557   else if (node->case_1 != NULL)
   2558   {
   2559     err = render_node (parse, node->case_1);
   2560   }
   2561   *next = node->next;
   2562   return nerr_pass (err);
   2563 }
   2564 
   2565 static NEOERR *else_parse (CSPARSE *parse, int cmd, char *arg)
   2566 {
   2567   NEOERR *err;
   2568   STACK_ENTRY *entry;
   2569 
   2570   /* ne_warn ("else"); */
   2571   err = uListGet (parse->stack, -1, (void *)&entry);
   2572   if (err != STATUS_OK) return nerr_pass(err);
   2573 
   2574   parse->next = &(entry->tree->case_1);
   2575   parse->current = entry->tree;
   2576   return STATUS_OK;
   2577 }
   2578 
   2579 static NEOERR *elif_parse (CSPARSE *parse, int cmd, char *arg)
   2580 {
   2581   NEOERR *err;
   2582   STACK_ENTRY *entry;
   2583 
   2584   /* ne_warn ("elif: %s", arg); */
   2585   err = uListGet (parse->stack, -1, (void *)&entry);
   2586   if (err != STATUS_OK) return nerr_pass(err);
   2587 
   2588   if (entry->next_tree == NULL)
   2589     entry->next_tree = entry->tree;
   2590 
   2591   parse->next = &(entry->tree->case_1);
   2592 
   2593   err = if_parse(parse, cmd, arg);
   2594   entry->tree = parse->current;
   2595   return nerr_pass(err);
   2596 }
   2597 
   2598 static NEOERR *endif_parse (CSPARSE *parse, int cmd, char *arg)
   2599 {
   2600   NEOERR *err;
   2601   STACK_ENTRY *entry;
   2602 
   2603   /* ne_warn ("endif"); */
   2604   err = uListGet (parse->stack, -1, (void *)&entry);
   2605   if (err != STATUS_OK) return nerr_pass(err);
   2606 
   2607   if (entry->next_tree)
   2608     parse->next = &(entry->next_tree->next);
   2609   else
   2610     parse->next = &(entry->tree->next);
   2611   parse->current = entry->tree;
   2612   return STATUS_OK;
   2613 }
   2614 
   2615 static NEOERR *each_with_parse (CSPARSE *parse, int cmd, char *arg)
   2616 {
   2617   NEOERR *err;
   2618   CSTREE *node;
   2619   char *lvar;
   2620   char *p;
   2621   char tmp[256];
   2622 
   2623   err = alloc_node (&node, parse);
   2624   if (err) return nerr_pass(err);
   2625   node->cmd = cmd;
   2626   if (arg[0] == '!')
   2627     node->flags |= CSF_REQUIRED;
   2628   arg++;
   2629 
   2630   p = lvar = neos_strip(arg);
   2631   while (*p && !isspace(*p) && *p != '=') p++;
   2632   if (*p == '\0')
   2633   {
   2634     dealloc_node(&node);
   2635     return nerr_raise (NERR_PARSE,
   2636 	"%s Improperly formatted %s directive: %s",
   2637 	find_context(parse, -1, tmp, sizeof(tmp)), Commands[cmd].cmd, arg);
   2638   }
   2639   if (*p != '=')
   2640   {
   2641     *p++ = '\0';
   2642     while (*p && *p != '=') p++;
   2643     if (*p == '\0')
   2644     {
   2645       dealloc_node(&node);
   2646       return nerr_raise (NERR_PARSE,
   2647 	  "%s Improperly formatted %s directive: %s",
   2648 	  find_context(parse, -1, tmp, sizeof(tmp)), Commands[cmd].cmd, arg);
   2649     }
   2650     p++;
   2651   }
   2652   else
   2653   {
   2654     *p++ = '\0';
   2655   }
   2656   while (*p && isspace(*p)) p++;
   2657   if (*p == '\0')
   2658   {
   2659     dealloc_node(&node);
   2660     return nerr_raise (NERR_PARSE,
   2661 	"%s Improperly formatted %s directive: %s",
   2662 	find_context(parse, -1, tmp, sizeof(tmp)), Commands[cmd].cmd, arg);
   2663   }
   2664   node->arg1.op_type = CS_TYPE_VAR;
   2665   node->arg1.s = lvar;
   2666 
   2667   err = parse_expr(parse, p, 0, &(node->arg2));
   2668   if (err)
   2669   {
   2670     dealloc_node(&node);
   2671     return nerr_pass(err);
   2672   }
   2673   /* ne_warn ("each %s %s", lvar, p); */
   2674 
   2675   *(parse->next) = node;
   2676   parse->next = &(node->case_0);
   2677   parse->current = node;
   2678 
   2679   return STATUS_OK;
   2680 }
   2681 
   2682 static NEOERR *each_eval (CSPARSE *parse, CSTREE *node, CSTREE **next)
   2683 {
   2684   NEOERR *err = STATUS_OK;
   2685   CS_LOCAL_MAP each_map;
   2686   CSARG val;
   2687   HDF *var, *child;
   2688 
   2689   memset(&each_map, 0, sizeof(each_map));
   2690 
   2691   err = eval_expr(parse, &(node->arg2), &val);
   2692   if (err) return nerr_pass(err);
   2693 
   2694   if (val.op_type == CS_TYPE_VAR)
   2695   {
   2696     var = var_lookup_obj (parse, val.s);
   2697 
   2698     if (var != NULL)
   2699     {
   2700       /* Init and install local map */
   2701       each_map.type = CS_TYPE_VAR;
   2702       each_map.name = node->arg1.s;
   2703       each_map.next = parse->locals;
   2704       each_map.first = 1;
   2705       each_map.last = 0;
   2706       parse->locals = &each_map;
   2707 
   2708       do
   2709       {
   2710 	child = hdf_obj_child (var);
   2711 	while (child != NULL)
   2712 	{
   2713           /* We don't explicitly set each_map.last here since checking
   2714            * requires a function call, so we move the check to _builtin_last
   2715            * so it only makes the call if last() is being used */
   2716 	  each_map.h = child;
   2717 	  err = render_node (parse, node->case_0);
   2718           if (each_map.map_alloc) {
   2719             free(each_map.s);
   2720             each_map.s = NULL;
   2721           }
   2722           if (each_map.first) each_map.first = 0;
   2723 	  if (err != STATUS_OK) break;
   2724 	  child = hdf_obj_next (child);
   2725 	}
   2726 
   2727       } while (0);
   2728 
   2729       /* Remove local map */
   2730       parse->locals = each_map.next;
   2731     }
   2732   } /* else WARNING */
   2733   if (val.alloc) free(val.s);
   2734 
   2735   *next = node->next;
   2736   return nerr_pass (err);
   2737 }
   2738 
   2739 static NEOERR *with_eval (CSPARSE *parse, CSTREE *node, CSTREE **next)
   2740 {
   2741   NEOERR *err = STATUS_OK;
   2742   CS_LOCAL_MAP with_map;
   2743   CSARG val;
   2744   HDF *var;
   2745 
   2746   memset(&with_map, 0, sizeof(with_map));
   2747 
   2748   err = eval_expr(parse, &(node->arg2), &val);
   2749   if (err) return nerr_pass(err);
   2750 
   2751   if (val.op_type == CS_TYPE_VAR)
   2752   {
   2753     var = var_lookup_obj (parse, val.s);
   2754 
   2755     if (var != NULL)
   2756     {
   2757       /* Init and install local map */
   2758       with_map.type = CS_TYPE_VAR;
   2759       with_map.name = node->arg1.s;
   2760       with_map.next = parse->locals;
   2761       with_map.h = var;
   2762       parse->locals = &with_map;
   2763       err = render_node (parse, node->case_0);
   2764       /* Remove local map */
   2765       if (with_map.map_alloc) free(with_map.s);
   2766       parse->locals = with_map.next;
   2767     }
   2768   }
   2769   else
   2770   {
   2771     /* else WARNING */
   2772     ne_warn("Invalid op_type for with: %s", expand_token_type(val.op_type, 1));
   2773   }
   2774   if (val.alloc) free(val.s);
   2775 
   2776   *next = node->next;
   2777   return nerr_pass (err);
   2778 }
   2779 static NEOERR *end_parse (CSPARSE *parse, int cmd, char *arg)
   2780 {
   2781   NEOERR *err;
   2782   STACK_ENTRY *entry;
   2783 
   2784   err = uListGet (parse->stack, -1, (void *)&entry);
   2785   if (err != STATUS_OK) return nerr_pass(err);
   2786 
   2787   parse->next = &(entry->tree->next);
   2788   parse->current = entry->tree;
   2789   return STATUS_OK;
   2790 }
   2791 
   2792 static NEOERR *include_parse (CSPARSE *parse, int cmd, char *arg)
   2793 {
   2794   NEOERR *err;
   2795   char *s;
   2796   int flags = 0;
   2797   CSARG arg1, val;
   2798 
   2799   memset(&arg1, 0, sizeof(CSARG));
   2800   if (arg[0] == '!')
   2801     flags |= CSF_REQUIRED;
   2802   arg++;
   2803   /* Validate arg is a var (regex /^[#" ]$/) */
   2804   err = parse_expr (parse, arg, 0, &arg1);
   2805   if (err) return nerr_pass(err);
   2806   /* ne_warn ("include: %s", a); */
   2807 
   2808   err = eval_expr(parse, &arg1, &val);
   2809   if (err) return nerr_pass(err);
   2810 
   2811   s = arg_eval (parse, &val);
   2812   if (s == NULL && !(flags & CSF_REQUIRED))
   2813     return STATUS_OK;
   2814   err = cs_parse_file(parse, s);
   2815   if (!(flags & CSF_REQUIRED))
   2816   {
   2817     nerr_handle(&err, NERR_NOT_FOUND);
   2818   }
   2819   if (val.alloc) free(val.s);
   2820 
   2821   return nerr_pass (err);
   2822 }
   2823 
   2824 static NEOERR *def_parse (CSPARSE *parse, int cmd, char *arg)
   2825 {
   2826   NEOERR *err;
   2827   CSTREE *node;
   2828   CS_MACRO *macro;
   2829   CSARG *carg, *larg = NULL;
   2830   char *a = NULL, *p = NULL, *s;
   2831   char tmp[256];
   2832   char name[256];
   2833   int x = 0;
   2834   BOOL last = FALSE;
   2835 
   2836   /* Since def doesn't get a new stack entry until after this is run,
   2837    * setup a dumb var on the parse object to hold the future setting.
   2838    */
   2839   parse->escaping.next_stack = NEOS_ESCAPE_UNDEF;
   2840 
   2841   err = alloc_node (&node, parse);
   2842   if (err) return nerr_pass(err);
   2843   node->cmd = cmd;
   2844   arg++;
   2845   s = arg;
   2846   while (*s && *s != ' ' && *s != '#' && *s != '(')
   2847   {
   2848     name[x++] = *s;
   2849     s++;
   2850   }
   2851   name[x] = '\0';
   2852   while (*s && isspace(*s)) s++;
   2853   if (*s == '\0' || *s != '(')
   2854   {
   2855     dealloc_node(&node);
   2856     return nerr_raise (NERR_PARSE,
   2857 	"%s Missing left paren in macro def %s",
   2858 	find_context(parse, -1, tmp, sizeof(tmp)), arg);
   2859   }
   2860   s++;
   2861   /* Check to see if this is a redefinition */
   2862   macro = parse->macros;
   2863   while (macro != NULL)
   2864   {
   2865     if (!strcmp(macro->name, name))
   2866     {
   2867       dealloc_node(&node);
   2868       return nerr_raise (NERR_PARSE,
   2869 	  "%s Duplicate macro def for %s",
   2870 	  find_context(parse, -1, tmp, sizeof(tmp)), arg);
   2871     }
   2872     macro = macro->next;
   2873   }
   2874 
   2875   macro = (CS_MACRO *) calloc (1, sizeof (CS_MACRO));
   2876   if (macro) macro->name = strdup(name);
   2877   if (macro == NULL || macro->name == NULL)
   2878   {
   2879     dealloc_node(&node);
   2880     dealloc_macro(&macro);
   2881     return nerr_raise (NERR_NOMEM,
   2882 	"%s Unable to allocate memory for CS_MACRO in def %s",
   2883 	find_context(parse, -1, tmp, sizeof(tmp)), arg);
   2884   }
   2885 
   2886   while (*s)
   2887   {
   2888     while (*s && isspace(*s)) s++;
   2889     a = strpbrk(s, ",)");
   2890     if (a == NULL)
   2891     {
   2892       err = nerr_raise (NERR_PARSE,
   2893 	  "%s Missing right paren in def %s",
   2894 	  find_context(parse, -1, tmp, sizeof(tmp)), arg);
   2895       break;
   2896     }
   2897     if (*a == ')') last = TRUE;
   2898     *a = '\0';
   2899     /* cut out ending whitespace */
   2900     p = strpbrk(s, " \t\r\n");
   2901     if (p != NULL) *p = '\0';
   2902     p = strpbrk(s, "\"?<>=!#-+|&,)*/%[]( \t\r\n");
   2903     if (p != NULL)
   2904     {
   2905       err = nerr_raise (NERR_PARSE,
   2906 	  "%s Invalid character in def %s argument: %c",
   2907 	  find_context(parse, -1, tmp, sizeof(tmp)), arg, *p);
   2908       break;
   2909     }
   2910     /* No argument case */
   2911     if (*s == '\0' && macro->n_args == 0) break;
   2912     if (*s == '\0')
   2913     {
   2914       err = nerr_raise (NERR_PARSE,
   2915 	  "%s Missing argument name or extra comma in def %s",
   2916 	  find_context(parse, -1, tmp, sizeof(tmp)), arg);
   2917       break;
   2918     }
   2919     carg = (CSARG *) calloc (1, sizeof(CSARG));
   2920     if (carg == NULL)
   2921     {
   2922       err = nerr_raise (NERR_NOMEM,
   2923 	  "%s Unable to allocate memory for CSARG in def %s",
   2924 	  find_context(parse, -1, tmp, sizeof(tmp)), arg);
   2925       break;
   2926     }
   2927     if (larg == NULL)
   2928     {
   2929       macro->args = carg;
   2930       larg = carg;
   2931     }
   2932     else
   2933     {
   2934       larg->next = carg;
   2935       larg = carg;
   2936     }
   2937     macro->n_args++;
   2938     carg->s = s;
   2939     if (last == TRUE) break;
   2940     s = a+1;
   2941   }
   2942   if (err)
   2943   {
   2944     dealloc_node(&node);
   2945     dealloc_macro(&macro);
   2946     return nerr_pass(err);
   2947   }
   2948 
   2949   macro->tree = node;
   2950   if (parse->macros)
   2951   {
   2952     macro->next = parse->macros;
   2953   }
   2954   parse->macros = macro;
   2955 
   2956   *(parse->next) = node;
   2957   parse->next = &(node->case_0);
   2958   parse->current = node;
   2959 
   2960   return STATUS_OK;
   2961 }
   2962 
   2963 static int rearrange_for_call(CSARG **args)
   2964 {
   2965   CSARG *larg = NULL;
   2966   CSARG *carg = *args;
   2967   CSARG *vargs = NULL;
   2968   int nargs = 0;
   2969 
   2970   /* multiple argument case, we have to walk the args and reverse
   2971    * them. Also handles single arg case since its the same as the
   2972    * last arg */
   2973   while (carg)
   2974   {
   2975     nargs++;
   2976     if (carg->op_type != CS_OP_COMMA)
   2977     {
   2978       /* last argument */
   2979       if (vargs)
   2980 	carg->next = vargs;
   2981       vargs = carg;
   2982       break;
   2983     }
   2984     if (vargs)
   2985       carg->expr1->next = vargs;
   2986     vargs = carg->expr1;
   2987     larg = carg;
   2988     carg = carg->next;
   2989     /* dealloc comma, but not its descendents */
   2990     larg->next = NULL;
   2991     larg->expr1 = NULL;
   2992     dealloc_arg(&larg);
   2993   }
   2994   *args = vargs;
   2995 
   2996   return nargs;
   2997 }
   2998 
   2999 static NEOERR *call_parse (CSPARSE *parse, int cmd, char *arg)
   3000 {
   3001   NEOERR *err;
   3002   CSTREE *node;
   3003   CS_MACRO *macro;
   3004   CSARG *carg;
   3005   char *s, *a = NULL;
   3006   char tmp[256];
   3007   char name[256];
   3008   int x = 0;
   3009   int nargs = 0;
   3010   STACK_ENTRY *entry;
   3011 
   3012   err = uListGet (parse->stack, -1, (void *)&entry);
   3013   if (err != STATUS_OK) return nerr_pass(err);
   3014 
   3015   err = alloc_node (&node, parse);
   3016   if (err) return nerr_pass(err);
   3017   node->cmd = cmd;
   3018   node->escape = entry->escape;
   3019   arg++;
   3020   s = arg;
   3021   while (x < sizeof(name) && *s && *s != ' ' && *s != '#' && *s != '(')
   3022   {
   3023     name[x++] = *s;
   3024     s++;
   3025   }
   3026   name[x] = '\0';
   3027   while (*s && isspace(*s)) s++;
   3028   if (*s == '\0' || *s != '(')
   3029   {
   3030     dealloc_node(&node);
   3031     return nerr_raise (NERR_PARSE,
   3032 	"%s Missing left paren in call %s",
   3033 	find_context(parse, -1, tmp, sizeof(tmp)), arg);
   3034   }
   3035   s++;
   3036   /* Check to see if this macro exists */
   3037   macro = parse->macros;
   3038   while (macro != NULL)
   3039   {
   3040     if (!strcmp(macro->name, name)) break;
   3041     macro = macro->next;
   3042   }
   3043   if (macro == NULL)
   3044   {
   3045     dealloc_node(&node);
   3046     err = nerr_raise (NERR_PARSE, "%s Undefined macro called: %s",
   3047           find_context(parse, -1, tmp, sizeof(tmp)), arg);
   3048     if (parse->audit_mode) {
   3049       /* Ignore macros that cannot be found */
   3050       return _store_error(parse, err);
   3051     }
   3052     else {
   3053       return err;
   3054     }
   3055   }
   3056   node->arg1.op_type = CS_TYPE_MACRO;
   3057   node->arg1.macro = macro;
   3058 
   3059   a = strrchr(s, ')');
   3060   if (a == NULL)
   3061   {
   3062     dealloc_node(&node);
   3063     return nerr_raise (NERR_PARSE,
   3064 	"%s Missing right paren in call %s",
   3065 	find_context(parse, -1, tmp, sizeof(tmp)), arg);
   3066   }
   3067   *a = '\0';
   3068 
   3069   while (*s && isspace(*s)) s++;
   3070   /* No arguments case */
   3071   if (*s == '\0')
   3072   {
   3073     nargs = 0;
   3074   }
   3075   else
   3076   {
   3077     /* Parse arguments case */
   3078     do
   3079     {
   3080       carg = (CSARG *) calloc (1, sizeof(CSARG));
   3081       if (carg == NULL)
   3082       {
   3083 	err = nerr_raise (NERR_NOMEM,
   3084 	    "%s Unable to allocate memory for CSARG in call %s",
   3085 	    find_context(parse, -1, tmp, sizeof(tmp)), arg);
   3086 	break;
   3087       }
   3088       err = parse_expr (parse, s, 0, carg);
   3089       if (err) break;
   3090       nargs = rearrange_for_call(&carg);
   3091       node->vargs = carg;
   3092     } while (0);
   3093   }
   3094   if (!err && nargs != macro->n_args)
   3095   {
   3096     err = nerr_raise (NERR_PARSE,
   3097 	"%s Incorrect number of arguments, expected %d, got %d in call to macro %s: %s",
   3098 	find_context(parse, -1, tmp, sizeof(tmp)), macro->n_args, nargs,
   3099 	macro->name, arg);
   3100   }
   3101   if (err)
   3102   {
   3103     dealloc_node(&node);
   3104     return nerr_pass(err);
   3105   }
   3106 
   3107   *(parse->next) = node;
   3108   parse->next = &(node->next);
   3109   parse->current = node;
   3110 
   3111   return STATUS_OK;
   3112 }
   3113 
   3114 static NEOERR *call_eval (CSPARSE *parse, CSTREE *node, CSTREE **next)
   3115 {
   3116   NEOERR *err = STATUS_OK;
   3117   CS_LOCAL_MAP *call_map, *map;
   3118   CS_MACRO *macro;
   3119   CSARG *carg, *darg;
   3120   HDF *var;
   3121   int x;
   3122 
   3123   /* Reset the value of when_undef for the coming call evaluation.
   3124    * This is only used here so it there's no need to reset its value after
   3125    * the call. If this call is nested (escape == NEOS_ESCAPE_UNDEF), then
   3126    * leave the when_undef variable alone. The parent call_eval should have
   3127    * already defined it.
   3128    */
   3129   if (node->escape != NEOS_ESCAPE_UNDEF)
   3130     parse->escaping.when_undef = node->escape;
   3131 
   3132   macro = node->arg1.macro;
   3133   if (macro->n_args)
   3134   {
   3135     call_map = (CS_LOCAL_MAP *) calloc (macro->n_args, sizeof(CS_LOCAL_MAP));
   3136     if (call_map == NULL)
   3137       return nerr_raise (NERR_NOMEM,
   3138                 "Unable to allocate memory for call_map in call_eval of %s",
   3139                          macro->name);
   3140   }
   3141   else
   3142   {
   3143     call_map = NULL;
   3144   }
   3145 
   3146   darg = macro->args;
   3147   carg = node->vargs;
   3148 
   3149   for (x = 0; x < macro->n_args; x++)
   3150   {
   3151     CSARG val;
   3152     map = &call_map[x];
   3153     if (x) call_map[x-1].next = map;
   3154 
   3155     map->name = darg->s;
   3156     err = eval_expr(parse, carg, &val);
   3157     if (err) break;
   3158     if (val.op_type & CS_TYPE_STRING)
   3159     {
   3160       map->s = val.s;
   3161       map->type = val.op_type;
   3162       map->map_alloc = val.alloc;
   3163       val.alloc = 0;
   3164     }
   3165     else if (val.op_type & CS_TYPE_NUM)
   3166     {
   3167       map->n = val.n;
   3168       map->type = CS_TYPE_NUM;
   3169     }
   3170     else if (val.op_type & (CS_TYPE_VAR | CS_TYPE_VAR_NUM))
   3171     {
   3172       CS_LOCAL_MAP *lmap;
   3173       char *c;
   3174       lmap = lookup_map (parse, val.s, &c);
   3175       if (lmap != NULL && (lmap->type != CS_TYPE_VAR && lmap->type != CS_TYPE_VAR_NUM))
   3176       {
   3177 	/* if we're referencing a local var which maps to a string or
   3178 	 * number... then copy  */
   3179 	if (lmap->type == CS_TYPE_NUM)
   3180 	{
   3181 	  map->n = lmap->n;
   3182 	  map->type = lmap->type;
   3183 	}
   3184 	else
   3185 	{
   3186 	  map->s = lmap->s;
   3187 	  map->type = lmap->type;
   3188 	}
   3189       }
   3190       else
   3191       {
   3192 	var = var_lookup_obj (parse, val.s);
   3193 	map->h = var;
   3194         map->type = CS_TYPE_VAR;
   3195         /* Bring across the name we're mapping to, in case h doesn't exist and
   3196          * we need to set it. */
   3197         map->s = val.s;
   3198         map->map_alloc = val.alloc;
   3199         val.alloc = 0;
   3200       }
   3201     }
   3202     else
   3203     {
   3204       ne_warn("Unsupported type %s in call_expr", expand_token_type(val.op_type, 1));
   3205     }
   3206     if (val.alloc) free(val.s);
   3207     map->next = parse->locals;
   3208 
   3209     darg = darg->next;
   3210     carg = carg->next;
   3211   }
   3212 
   3213   if (err == STATUS_OK)
   3214   {
   3215     map = parse->locals;
   3216     if (macro->n_args) parse->locals = call_map;
   3217     err = render_node (parse, macro->tree->case_0);
   3218     parse->locals = map;
   3219   }
   3220   for (x = 0; x < macro->n_args; x++)
   3221   {
   3222     if (call_map[x].map_alloc) free(call_map[x].s);
   3223   }
   3224   if (call_map) free (call_map);
   3225 
   3226   *next = node->next;
   3227   return nerr_pass(err);
   3228 }
   3229 
   3230 static NEOERR *set_parse (CSPARSE *parse, int cmd, char *arg)
   3231 {
   3232   NEOERR *err;
   3233   CSTREE *node;
   3234   char *s;
   3235   char tmp[256];
   3236 
   3237   err = alloc_node (&node, parse);
   3238   if (err) return nerr_pass(err);
   3239   node->cmd = cmd;
   3240   arg++;
   3241   s = arg;
   3242   while (*s && *s != '=') s++;
   3243   if (*s == '\0')
   3244   {
   3245     dealloc_node(&node);
   3246     return nerr_raise (NERR_PARSE,
   3247 	"%s Missing equals in set %s",
   3248 	find_context(parse, -1, tmp, sizeof(tmp)), arg);
   3249   }
   3250   *s = '\0';
   3251   s++;
   3252   err = parse_expr(parse, arg, 1, &(node->arg1));
   3253   if (err)
   3254   {
   3255     dealloc_node(&node);
   3256     return nerr_pass(err);
   3257   }
   3258 
   3259   err = parse_expr(parse, s, 0, &(node->arg2));
   3260   if (err)
   3261   {
   3262     dealloc_node(&node);
   3263     return nerr_pass(err);
   3264   }
   3265 
   3266   *(parse->next) = node;
   3267   parse->next = &(node->next);
   3268   parse->current = node;
   3269 
   3270   return STATUS_OK;
   3271 }
   3272 
   3273 static NEOERR *set_eval (CSPARSE *parse, CSTREE *node, CSTREE **next)
   3274 {
   3275   NEOERR *err = STATUS_OK;
   3276   CSARG val;
   3277   CSARG set;
   3278 
   3279   err = eval_expr(parse, &(node->arg1), &set);
   3280   if (err) return nerr_pass (err);
   3281   err = eval_expr(parse, &(node->arg2), &val);
   3282   if (err) {
   3283     if (set.alloc) free(set.s);
   3284     return nerr_pass (err);
   3285   }
   3286 
   3287   if (set.op_type != CS_TYPE_NUM)
   3288   {
   3289     /* this allow for a weirdness where set:"foo"="bar"
   3290      * actually sets the hdf var foo... */
   3291     if (val.op_type & (CS_TYPE_NUM | CS_TYPE_VAR_NUM))
   3292     {
   3293       char buf[256];
   3294       long int n_val;
   3295 
   3296       n_val = arg_eval_num (parse, &val);
   3297       snprintf (buf, sizeof(buf), "%ld", n_val);
   3298       if (set.s)
   3299       {
   3300 	err = var_set_value (parse, set.s, buf);
   3301       }
   3302       else
   3303       {
   3304 	err = nerr_raise(NERR_ASSERT,
   3305 	    "lvalue is NULL/empty in attempt to evaluate set to '%s'", buf);
   3306       }
   3307     }
   3308     else
   3309     {
   3310       char *s = arg_eval (parse, &val);
   3311       /* Do we set it to blank if s == NULL? */
   3312       if (set.s)
   3313       {
   3314 	err = var_set_value (parse, set.s, s);
   3315       }
   3316       else
   3317       {
   3318 	err = nerr_raise(NERR_ASSERT,
   3319 	    "lvalue is NULL/empty in attempt to evaluate set to '%s'",
   3320 	    (s) ? s : "");
   3321       }
   3322     }
   3323   } /* else WARNING */
   3324   if (set.alloc) free(set.s);
   3325   if (val.alloc) free(val.s);
   3326 
   3327   *next = node->next;
   3328   return nerr_pass (err);
   3329 }
   3330 
   3331 static NEOERR *loop_parse (CSPARSE *parse, int cmd, char *arg)
   3332 {
   3333   NEOERR *err;
   3334   CSTREE *node;
   3335   CSARG *carg, *larg = NULL;
   3336   BOOL last = FALSE;
   3337   char *lvar;
   3338   char *p, *a;
   3339   char tmp[256];
   3340   int x;
   3341 
   3342   err = alloc_node (&node, parse);
   3343   if (err) return nerr_pass(err);
   3344   node->cmd = cmd;
   3345   if (arg[0] == '!')
   3346     node->flags |= CSF_REQUIRED;
   3347   arg++;
   3348 
   3349   p = lvar = neos_strip(arg);
   3350   while (*p && !isspace(*p) && *p != '=') p++;
   3351   if (*p == '\0')
   3352   {
   3353     dealloc_node(&node);
   3354     return nerr_raise (NERR_PARSE,
   3355 	"%s Improperly formatted loop directive: %s",
   3356 	find_context(parse, -1, tmp, sizeof(tmp)), arg);
   3357   }
   3358   if (*p != '=')
   3359   {
   3360     *p++ = '\0';
   3361     while (*p && *p != '=') p++;
   3362     if (*p == '\0')
   3363     {
   3364       dealloc_node(&node);
   3365       return nerr_raise (NERR_PARSE,
   3366 	  "%s Improperly formatted loop directive: %s",
   3367 	  find_context(parse, -1, tmp, sizeof(tmp)), arg);
   3368     }
   3369     p++;
   3370   }
   3371   else
   3372   {
   3373     *p++ = '\0';
   3374   }
   3375   while (*p && isspace(*p)) p++;
   3376   if (*p == '\0')
   3377   {
   3378     dealloc_node(&node);
   3379     return nerr_raise (NERR_PARSE,
   3380 	"%s Improperly formatted loop directive: %s",
   3381 	find_context(parse, -1, tmp, sizeof(tmp)), arg);
   3382   }
   3383   node->arg1.op_type = CS_TYPE_VAR;
   3384   node->arg1.s = lvar;
   3385 
   3386   x = 0;
   3387   while (*p)
   3388   {
   3389     carg = (CSARG *) calloc (1, sizeof(CSARG));
   3390     if (carg == NULL)
   3391     {
   3392       err = nerr_raise (NERR_NOMEM,
   3393 	  "%s Unable to allocate memory for CSARG in loop %s",
   3394 	  find_context(parse, -1, tmp, sizeof(tmp)), arg);
   3395       break;
   3396     }
   3397     if (larg == NULL)
   3398     {
   3399       node->vargs = carg;
   3400       larg = carg;
   3401     }
   3402     else
   3403     {
   3404       larg->next = carg;
   3405       larg = carg;
   3406     }
   3407     x++;
   3408     a = strpbrk(p, ",");
   3409     if (a == NULL) last = TRUE;
   3410     else *a = '\0';
   3411     err = parse_expr (parse, p, 0, carg);
   3412     if (err) break;
   3413     if (last == TRUE) break;
   3414     p = a+1;
   3415   }
   3416   if (!err && ((x < 1) || (x > 3)))
   3417   {
   3418     err = nerr_raise (NERR_PARSE,
   3419 	"%s Incorrect number of arguments, expected 1, 2, or 3 got %d in loop: %s",
   3420 	find_context(parse, -1, tmp, sizeof(tmp)), x, arg);
   3421   }
   3422 
   3423   /* ne_warn ("loop %s %s", lvar, p); */
   3424 
   3425   *(parse->next) = node;
   3426   parse->next = &(node->case_0);
   3427   parse->current = node;
   3428 
   3429   return STATUS_OK;
   3430 }
   3431 
   3432 static NEOERR *loop_eval (CSPARSE *parse, CSTREE *node, CSTREE **next)
   3433 {
   3434   NEOERR *err = STATUS_OK;
   3435   CS_LOCAL_MAP each_map;
   3436   int var;
   3437   int start = 0, end = 0, step = 1;
   3438   int x, iter = 1;
   3439   CSARG *carg;
   3440   CSARG val;
   3441 
   3442   memset(&each_map, 0, sizeof(each_map));
   3443 
   3444   carg = node->vargs;
   3445   if (carg == NULL) return nerr_raise (NERR_ASSERT, "No arguments in loop eval?");
   3446   err = eval_expr(parse, carg, &val);
   3447   if (err) return nerr_pass(err);
   3448   end = arg_eval_num(parse, &val);
   3449   if (val.alloc) free(val.s);
   3450   if (carg->next)
   3451   {
   3452     start = end;
   3453     carg = carg->next;
   3454     err = eval_expr(parse, carg, &val);
   3455     if (err) return nerr_pass(err);
   3456     end = arg_eval_num(parse, &val);
   3457     if (val.alloc) free(val.s);
   3458     if (carg->next)
   3459     {
   3460       carg = carg->next;
   3461       err = eval_expr(parse, carg, &val);
   3462       if (err) return nerr_pass(err);
   3463       step = arg_eval_num(parse, &val);
   3464       if (val.alloc) free(val.s);
   3465     }
   3466   }
   3467   if (((step < 0) && (start < end)) ||
   3468       ((step > 0) && (end < start)))
   3469   {
   3470     iter = 0;
   3471   }
   3472   else if (step == 0)
   3473   {
   3474     iter = 0;
   3475   }
   3476   else
   3477   {
   3478     iter = abs((end - start) / step + 1);
   3479   }
   3480 
   3481   if (iter > 0)
   3482   {
   3483     /* Init and install local map */
   3484     each_map.type = CS_TYPE_NUM;
   3485     each_map.name = node->arg1.s;
   3486     each_map.next = parse->locals;
   3487     each_map.first = 1;
   3488     parse->locals = &each_map;
   3489 
   3490     var = start;
   3491     for (x = 0, var = start; x < iter; x++, var += step)
   3492     {
   3493       if (x == iter - 1) each_map.last = 1;
   3494       each_map.n = var;
   3495       err = render_node (parse, node->case_0);
   3496       if (each_map.map_alloc) {
   3497         free(each_map.s);
   3498         each_map.s = NULL;
   3499       }
   3500       if (each_map.first) each_map.first = 0;
   3501       if (err != STATUS_OK) break;
   3502     }
   3503 
   3504     /* Remove local map */
   3505     parse->locals = each_map.next;
   3506   }
   3507 
   3508   *next = node->next;
   3509   return nerr_pass (err);
   3510 }
   3511 
   3512 static NEOERR *skip_eval (CSPARSE *parse, CSTREE *node, CSTREE **next)
   3513 {
   3514   *next = node->next;
   3515   return STATUS_OK;
   3516 }
   3517 static NEOERR *render_node (CSPARSE *parse, CSTREE *node)
   3518 {
   3519   NEOERR *err = STATUS_OK;
   3520 
   3521   while (node != NULL)
   3522   {
   3523     /* ne_warn ("%s %08x", Commands[node->cmd].cmd, node); */
   3524     err = (*(Commands[node->cmd].eval_handler))(parse, node, &node);
   3525     if (err) break;
   3526   }
   3527   return nerr_pass(err);
   3528 }
   3529 
   3530 NEOERR *cs_render (CSPARSE *parse, void *ctx, CSOUTFUNC cb)
   3531 {
   3532   CSTREE *node;
   3533 
   3534   if (parse->tree == NULL)
   3535     return nerr_raise (NERR_ASSERT, "No parse tree exists");
   3536 
   3537   parse->output_ctx = ctx;
   3538   parse->output_cb = cb;
   3539 
   3540   node = parse->tree;
   3541   return nerr_pass (render_node(parse, node));
   3542 }
   3543 
   3544 /* **** Functions ******************************************** */
   3545 
   3546 NEOERR *cs_register_function(CSPARSE *parse, const char *funcname,
   3547                                   int n_args, CSFUNCTION function)
   3548 {
   3549   CS_FUNCTION *csf;
   3550 
   3551   /* Should we validate the parseability of the name? */
   3552 
   3553   csf = parse->functions;
   3554   while (csf != NULL)
   3555   {
   3556     if (!strcmp(csf->name, funcname) && csf->function != function)
   3557     {
   3558       return nerr_raise(NERR_DUPLICATE,
   3559 	  "Attempt to register duplicate function %s", funcname);
   3560     }
   3561     csf = csf->next;
   3562   }
   3563   csf = (CS_FUNCTION *) calloc (1, sizeof(CS_FUNCTION));
   3564   if (csf == NULL)
   3565     return nerr_raise(NERR_NOMEM,
   3566 	"Unable to allocate memory to register function %s", funcname);
   3567   csf->name = strdup(funcname);
   3568   if (csf->name == NULL)
   3569   {
   3570     free(csf);
   3571     return nerr_raise(NERR_NOMEM,
   3572 	"Unable to allocate memory to register function %s", funcname);
   3573   }
   3574   csf->function = function;
   3575   csf->n_args = n_args;
   3576   csf->escape = NEOS_ESCAPE_NONE;
   3577   csf->next = parse->functions;
   3578   parse->functions = csf;
   3579 
   3580   return STATUS_OK;
   3581 }
   3582 
   3583 /* This is similar to python's PyArg_ParseTuple, :
   3584  *   s - string (allocated)
   3585  *   i - int
   3586  *   A - arg ptr (maybe later)
   3587  */
   3588 NEOERR * cs_arg_parsev(CSPARSE *parse, CSARG *args, const char *fmt,
   3589                        va_list ap)
   3590 {
   3591   NEOERR *err = STATUS_OK;
   3592   char **s;
   3593   long int *i;
   3594   CSARG val;
   3595 
   3596   while (*fmt)
   3597   {
   3598     memset(&val, 0, sizeof(val));
   3599     err = eval_expr(parse, args, &val);
   3600     if (err) return nerr_pass(err);
   3601 
   3602     switch (*fmt)
   3603     {
   3604       case 's':
   3605 	s = va_arg(ap, char **);
   3606 	if (s == NULL)
   3607 	{
   3608 	  err = nerr_raise(NERR_ASSERT,
   3609 	      "Invalid number of arguments in call to cs_arg_parse");
   3610 	  break;
   3611 	}
   3612 	*s = arg_eval_str_alloc(parse, &val);
   3613 	break;
   3614       case 'i':
   3615 	i = va_arg(ap, long int *);
   3616 	if (i == NULL)
   3617 	{
   3618 	  err = nerr_raise(NERR_ASSERT,
   3619 	      "Invalid number of arguments in call to cs_arg_parse");
   3620 	  break;
   3621 	}
   3622 	*i = arg_eval_num(parse, &val);
   3623 	break;
   3624       default:
   3625 	break;
   3626     }
   3627     if (err) return nerr_pass(err);
   3628     fmt++;
   3629     args = args->next;
   3630     if (val.alloc) free(val.s);
   3631   }
   3632   if (err) return nerr_pass(err);
   3633   return STATUS_OK;
   3634 }
   3635 
   3636 NEOERR * cs_arg_parse(CSPARSE *parse, CSARG *args, const char *fmt, ...)
   3637 {
   3638   NEOERR *err;
   3639   va_list ap;
   3640 
   3641   va_start(ap, fmt);
   3642   err = cs_arg_parsev(parse, args, fmt, ap);
   3643   va_end(ap);
   3644   return nerr_pass(err);
   3645 }
   3646 
   3647 static NEOERR * _builtin_subcount(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result)
   3648 {
   3649   NEOERR *err;
   3650   HDF *obj;
   3651   int count = 0;
   3652   CSARG val;
   3653 
   3654   memset(&val, 0, sizeof(val));
   3655   err = eval_expr(parse, args, &val);
   3656   if (err) return nerr_pass(err);
   3657 
   3658   /* default for non-vars is 0 children */
   3659   result->op_type = CS_TYPE_NUM;
   3660   result->n = 0;
   3661 
   3662   if (val.op_type & CS_TYPE_VAR)
   3663   {
   3664     obj = var_lookup_obj (parse, val.s);
   3665     if (obj != NULL)
   3666     {
   3667       obj = hdf_obj_child(obj);
   3668       while (obj != NULL)
   3669       {
   3670 	count++;
   3671 	obj = hdf_obj_next(obj);
   3672       }
   3673     }
   3674     result->n = count;
   3675   }
   3676   if (val.alloc) free(val.s);
   3677 
   3678   return STATUS_OK;
   3679 }
   3680 
   3681 static NEOERR * _builtin_str_length(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result)
   3682 {
   3683   NEOERR *err;
   3684   CSARG val;
   3685 
   3686   memset(&val, 0, sizeof(val));
   3687   err = eval_expr(parse, args, &val);
   3688   if (err) return nerr_pass(err);
   3689 
   3690   /* non var/string objects have 0 length */
   3691   result->op_type = CS_TYPE_NUM;
   3692   result->n = 0;
   3693 
   3694   if (val.op_type & (CS_TYPE_VAR | CS_TYPE_STRING))
   3695   {
   3696     char *s = arg_eval(parse, &val);
   3697     if (s) result->n = strlen(s);
   3698   }
   3699   if (val.alloc) free(val.s);
   3700   return STATUS_OK;
   3701 }
   3702 
   3703 static NEOERR * _builtin_str_crc(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args,
   3704                                  CSARG *result)
   3705 {
   3706   NEOERR *err;
   3707   CSARG val;
   3708 
   3709   memset(&val, 0, sizeof(val));
   3710   err = eval_expr(parse, args, &val);
   3711   if (err) return nerr_pass(err);
   3712 
   3713   /* non var/string objects have 0 length */
   3714   result->op_type = CS_TYPE_NUM;
   3715   result->n = 0;
   3716 
   3717   if (val.op_type & (CS_TYPE_VAR | CS_TYPE_STRING))
   3718   {
   3719     char *s = arg_eval(parse, &val);
   3720     if (s) result->n = ne_crc((unsigned char *)s, strlen(s));
   3721   }
   3722   if (val.alloc) free(val.s);
   3723   return STATUS_OK;
   3724 }
   3725 
   3726 
   3727 static NEOERR * _builtin_str_find(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result)
   3728 {
   3729   NEOERR *err;
   3730   char *s = NULL;
   3731   char *substr = NULL;
   3732   char *pstr = NULL;
   3733 
   3734   result->op_type = CS_TYPE_NUM;
   3735   result->n = -1;
   3736 
   3737   err = cs_arg_parse(parse, args, "ss", &s, &substr);
   3738   if (err) return nerr_pass(err);
   3739   /* If null arguments, return -1 index */
   3740   if (s == NULL || substr == NULL) {
   3741     if (s) free(s);
   3742     if (substr) free(substr);
   3743     return STATUS_OK;
   3744   }
   3745   pstr = strstr(s, substr);
   3746   if (pstr != NULL) {
   3747     result->n = (pstr - s) / sizeof(char);
   3748   }
   3749   free(s);
   3750   free(substr);
   3751   return STATUS_OK;
   3752 }
   3753 
   3754 
   3755 static NEOERR * _builtin_name(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result)
   3756 {
   3757   NEOERR *err;
   3758   HDF *obj;
   3759   CSARG val;
   3760 
   3761   memset(&val, 0, sizeof(val));
   3762   err = eval_expr(parse, args, &val);
   3763   if (err) return nerr_pass(err);
   3764 
   3765   result->op_type = CS_TYPE_STRING;
   3766   result->s = "";
   3767 
   3768   if (val.op_type & CS_TYPE_VAR)
   3769   {
   3770     obj = var_lookup_obj (parse, val.s);
   3771     if (obj != NULL)
   3772       result->s = hdf_obj_name(obj);
   3773   }
   3774   else if (val.op_type & CS_TYPE_STRING)
   3775   {
   3776     result->s = val.s;
   3777     result->alloc = val.alloc;
   3778     val.alloc = 0;
   3779   }
   3780   if (val.alloc) free(val.s);
   3781   return STATUS_OK;
   3782 }
   3783 
   3784 /* Check to see if a local variable is the first in an each/loop sequence */
   3785 static NEOERR * _builtin_first(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args,
   3786                                CSARG *result)
   3787 {
   3788   NEOERR *err;
   3789   CS_LOCAL_MAP *map;
   3790   char *c;
   3791   CSARG val;
   3792 
   3793   memset(&val, 0, sizeof(val));
   3794   err = eval_expr(parse, args, &val);
   3795   if (err) return nerr_pass(err);
   3796 
   3797   /* default is "not first" */
   3798   result->op_type = CS_TYPE_NUM;
   3799   result->n = 0;
   3800 
   3801   /* Only applies to possible local vars */
   3802   if ((val.op_type & CS_TYPE_VAR) && !strchr(val.s, '.'))
   3803   {
   3804     map = lookup_map (parse, val.s, &c);
   3805     if (map && map->first)
   3806       result->n = 1;
   3807   }
   3808   if (val.alloc) free(val.s);
   3809   return STATUS_OK;
   3810 }
   3811 
   3812 /* Check to see if a local variable is the last in an each/loop sequence */
   3813 /* TODO: consider making this work on regular HDF vars */
   3814 static NEOERR * _builtin_last(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args,
   3815                                CSARG *result)
   3816 {
   3817   NEOERR *err;
   3818   CS_LOCAL_MAP *map;
   3819   char *c;
   3820   CSARG val;
   3821 
   3822   memset(&val, 0, sizeof(val));
   3823   err = eval_expr(parse, args, &val);
   3824   if (err) return nerr_pass(err);
   3825 
   3826   /* default is "not last" */
   3827   result->op_type = CS_TYPE_NUM;
   3828   result->n = 0;
   3829 
   3830   /* Only applies to possible local vars */
   3831   if ((val.op_type & CS_TYPE_VAR) && !strchr(val.s, '.'))
   3832   {
   3833     map = lookup_map (parse, val.s, &c);
   3834     if (map) {
   3835       if (map->last) {
   3836         result->n = 1;
   3837       } else if (map->type == CS_TYPE_VAR) {
   3838         if (hdf_obj_next(map->h) == NULL) {
   3839           result->n = 1;
   3840         }
   3841       }
   3842     }
   3843   }
   3844   if (val.alloc) free(val.s);
   3845   return STATUS_OK;
   3846 }
   3847 
   3848 /* returns the absolute value (ie, positive) of a number */
   3849 static NEOERR * _builtin_abs (CSPARSE *parse, CS_FUNCTION *csf, CSARG *args,
   3850                               CSARG *result)
   3851 {
   3852   NEOERR *err;
   3853   int n1 = 0;
   3854   CSARG val;
   3855 
   3856   memset(&val, 0, sizeof(val));
   3857   err = eval_expr(parse, args, &val);
   3858   if (err) return nerr_pass(err);
   3859 
   3860   result->op_type = CS_TYPE_NUM;
   3861   n1 = arg_eval_num(parse, &val);
   3862   result->n = abs(n1);
   3863 
   3864   if (val.alloc) free(val.s);
   3865   return STATUS_OK;
   3866 }
   3867 
   3868 /* returns the larger or two integers */
   3869 static NEOERR * _builtin_max (CSPARSE *parse, CS_FUNCTION *csf, CSARG *args,
   3870                               CSARG *result)
   3871 {
   3872   NEOERR *err;
   3873   long int n1 = 0;
   3874   long int n2 = 0;
   3875 
   3876   result->op_type = CS_TYPE_NUM;
   3877   result->n = 0;
   3878 
   3879   err = cs_arg_parse(parse, args, "ii", &n1, &n2);
   3880   if (err) return nerr_pass(err);
   3881   result->n = (n1 > n2) ? n1 : n2;
   3882 
   3883   return STATUS_OK;
   3884 }
   3885 
   3886 /* returns the smaller or two integers */
   3887 static NEOERR * _builtin_min (CSPARSE *parse, CS_FUNCTION *csf, CSARG *args,
   3888                               CSARG *result)
   3889 {
   3890   NEOERR *err;
   3891   long int n1 = 0;
   3892   long int n2 = 0;
   3893 
   3894   result->op_type = CS_TYPE_NUM;
   3895   result->n = 0;
   3896 
   3897   err = cs_arg_parse(parse, args, "ii", &n1, &n2);
   3898   if (err) return nerr_pass(err);
   3899   result->n = (n1 < n2) ? n1 : n2;
   3900 
   3901   return STATUS_OK;
   3902 }
   3903 
   3904 static NEOERR * _builtin_str_slice (CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result)
   3905 {
   3906   NEOERR *err;
   3907   char *s = NULL;
   3908   char *slice;
   3909   long int b = 0;
   3910   long int e = 0;
   3911   size_t len;
   3912 
   3913   result->op_type = CS_TYPE_STRING;
   3914   result->s = "";
   3915 
   3916   err = cs_arg_parse(parse, args, "sii", &s, &b, &e);
   3917   if (err) return nerr_pass(err);
   3918   /* If null, return empty string */
   3919   if (s == NULL) return STATUS_OK;
   3920   len = strlen(s);
   3921   if (b < 0 && e == 0) e = len;
   3922   if (b < 0) b += len;
   3923   if (e < 0) e += len;
   3924   if (e > len) e = len;
   3925   /* Its the whole string */
   3926   if (b == 0 && e == len)
   3927   {
   3928     result->s = s;
   3929     result->alloc = 1;
   3930     return STATUS_OK;
   3931   }
   3932   if (e < b) b = e;
   3933   if (b == e)
   3934   {
   3935     /* If null, return empty string */
   3936     free(s);
   3937     return STATUS_OK;
   3938   }
   3939   slice = (char *) malloc (sizeof(char) * (e-b+1));
   3940   if (slice == NULL)
   3941     return nerr_raise(NERR_NOMEM, "Unable to allocate memory for string slice");
   3942   strncpy(slice, s + b, e-b);
   3943   free(s);
   3944   slice[e-b] = '\0';
   3945 
   3946   result->s = slice;
   3947   result->alloc = 1;
   3948 
   3949   return STATUS_OK;
   3950 }
   3951 
   3952 #ifdef ENABLE_GETTEXT
   3953 static NEOERR * _builtin_gettext(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result)
   3954 {
   3955   NEOERR *err;
   3956   char *s;
   3957   CSARG val;
   3958 
   3959   memset(&val, 0, sizeof(val));
   3960   err = eval_expr(parse, args, &val);
   3961   if (err) return nerr_pass(err);
   3962 
   3963   result->op_type = CS_TYPE_STRING;
   3964   result->s = "";
   3965 
   3966   if (val.op_type & (CS_TYPE_VAR | CS_TYPE_STRING))
   3967   {
   3968     s = arg_eval(parse, &val);
   3969     if (s)
   3970     {
   3971       result->s = gettext(s);
   3972     }
   3973   }
   3974   if (val.alloc) free(val.s);
   3975   return STATUS_OK;
   3976 }
   3977 #endif
   3978 
   3979 static NEOERR * _str_func_wrapper (CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result)
   3980 {
   3981   NEOERR *err;
   3982   char *s;
   3983   CSARG val;
   3984 
   3985   memset(&val, 0, sizeof(val));
   3986   err = eval_expr(parse, args, &val);
   3987   if (err) return nerr_pass(err);
   3988 
   3989   if (val.op_type & (CS_TYPE_VAR | CS_TYPE_STRING))
   3990   {
   3991     result->op_type = CS_TYPE_STRING;
   3992     result->n = 0;
   3993 
   3994     s = arg_eval(parse, &val);
   3995     if (s)
   3996     {
   3997       err = csf->str_func(s, &(result->s));
   3998       if (err) return nerr_pass(err);
   3999       result->alloc = 1;
   4000     }
   4001   }
   4002   else
   4003   {
   4004     result->op_type = val.op_type;
   4005     result->n = val.n;
   4006     result->s = val.s;
   4007     result->alloc = val.alloc;
   4008     val.alloc = 0;
   4009   }
   4010   if (val.alloc) free(val.s);
   4011   return STATUS_OK;
   4012 }
   4013 
   4014 NEOERR *cs_register_strfunc(CSPARSE *parse, char *funcname, CSSTRFUNC str_func)
   4015 {
   4016   NEOERR *err;
   4017 
   4018   err = cs_register_function(parse, funcname, 1, _str_func_wrapper);
   4019   if (err) return nerr_pass(err);
   4020   parse->functions->str_func = str_func;
   4021 
   4022   return STATUS_OK;
   4023 }
   4024 
   4025 NEOERR *cs_register_esc_strfunc(CSPARSE *parse, char *funcname,
   4026                                 CSSTRFUNC str_func)
   4027 {
   4028   NEOERR *err;
   4029 
   4030   err = cs_register_strfunc(parse, funcname, str_func);
   4031   if (err) return nerr_pass(err);
   4032   parse->functions->escape = NEOS_ESCAPE_FUNCTION;
   4033 
   4034   return STATUS_OK;
   4035 }
   4036 
   4037 /* **** CS Initialize/Destroy ************************************ */
   4038 NEOERR *cs_init (CSPARSE **parse, HDF *hdf) {
   4039   return nerr_pass(cs_init_internal(parse, hdf, NULL));
   4040 }
   4041 
   4042 static NEOERR *cs_init_internal (CSPARSE **parse, HDF *hdf, CSPARSE *parent)
   4043 {
   4044   NEOERR *err = STATUS_OK;
   4045   CSPARSE *my_parse;
   4046   STACK_ENTRY *entry;
   4047   char *esc_value;
   4048   CS_ESCAPE_MODES *esc_cursor;
   4049 
   4050   err = nerr_init();
   4051   if (err != STATUS_OK) return nerr_pass (err);
   4052 
   4053   my_parse = (CSPARSE *) calloc (1, sizeof (CSPARSE));
   4054   if (my_parse == NULL)
   4055     return nerr_raise (NERR_NOMEM, "Unable to allocate memory for CSPARSE");
   4056 
   4057   err = uListInit (&(my_parse->stack), 10, 0);
   4058   if (err != STATUS_OK)
   4059   {
   4060     free(my_parse);
   4061     return nerr_pass(err);
   4062   }
   4063   err = uListInit (&(my_parse->alloc), 10, 0);
   4064   if (err != STATUS_OK)
   4065   {
   4066     free(my_parse);
   4067     return nerr_pass(err);
   4068   }
   4069   err = alloc_node (&(my_parse->tree), my_parse);
   4070   if (err != STATUS_OK)
   4071   {
   4072     cs_destroy (&my_parse);
   4073     return nerr_pass(err);
   4074   }
   4075   my_parse->current = my_parse->tree;
   4076   my_parse->next = &(my_parse->current->next);
   4077 
   4078   entry = (STACK_ENTRY *) calloc (1, sizeof (STACK_ENTRY));
   4079   if (entry == NULL)
   4080   {
   4081     cs_destroy (&my_parse);
   4082     return nerr_raise (NERR_NOMEM,
   4083 	"Unable to allocate memory for stack entry");
   4084   }
   4085   entry->state = ST_GLOBAL;
   4086   entry->tree = my_parse->current;
   4087   entry->location = 0;
   4088   entry->escape = NEOS_ESCAPE_NONE;
   4089   err = uListAppend(my_parse->stack, entry);
   4090   if (err != STATUS_OK) {
   4091     free (entry);
   4092     cs_destroy(&my_parse);
   4093     return nerr_pass(err);
   4094   }
   4095   my_parse->tag = hdf_get_value(hdf, "Config.TagStart", "cs");
   4096   my_parse->taglen = strlen(my_parse->tag);
   4097   my_parse->hdf = hdf;
   4098 
   4099   /* Let's set the default escape data */
   4100   my_parse->escaping.global_ctx = NEOS_ESCAPE_NONE;
   4101   my_parse->escaping.next_stack = NEOS_ESCAPE_NONE;
   4102   my_parse->escaping.when_undef = NEOS_ESCAPE_NONE;
   4103 
   4104   /* See CS_ESCAPE_MODES. 0 is "none" */
   4105   esc_value = hdf_get_value(hdf, "Config.VarEscapeMode", EscapeModes[0].mode);
   4106   /* Let's ensure the specified escape mode is valid and proceed */
   4107   for (esc_cursor = &EscapeModes[0];
   4108        esc_cursor->mode != NULL;
   4109        esc_cursor++)
   4110     if (!strcmp(esc_value, esc_cursor->mode))
   4111     {
   4112       my_parse->escaping.global_ctx = esc_cursor->context;
   4113       my_parse->escaping.next_stack = esc_cursor->context;
   4114       entry->escape = esc_cursor->context;
   4115       break;
   4116     }
   4117   /* Didn't find an acceptable value we were looking for */
   4118   if (esc_cursor->mode == NULL) {
   4119     cs_destroy (&my_parse);
   4120     return nerr_raise (NERR_OUTOFRANGE,
   4121       "Invalid HDF value for Config.VarEscapeMode (none,html,js,url): %s",
   4122       esc_value);
   4123   }
   4124 
   4125   /* Read configuration value to determine whether to enable audit mode */
   4126   my_parse->audit_mode = hdf_get_int_value(hdf, "Config.EnableAuditMode", 0);
   4127 
   4128   my_parse->err_list = NULL;
   4129 
   4130   if (parent == NULL)
   4131   {
   4132     static struct _builtin_functions {
   4133       const char *name;
   4134       int nargs;
   4135       CSFUNCTION function;
   4136     } Builtins[] = {
   4137       { "len", 1, _builtin_subcount },
   4138       { "subcount", 1, _builtin_subcount },
   4139       { "name", 1, _builtin_name },
   4140       { "first", 1, _builtin_first },
   4141       { "last", 1, _builtin_last },
   4142       { "abs", 1, _builtin_abs },
   4143       { "max", 2, _builtin_max },
   4144       { "min", 2, _builtin_min },
   4145       { "string.find", 2, _builtin_str_find },
   4146       { "string.slice", 3, _builtin_str_slice },
   4147       { "string.length", 1, _builtin_str_length },
   4148       { "string.crc", 1, _builtin_str_crc},
   4149 #ifdef ENABLE_GETTEXT
   4150       { "_", 1, _builtin_gettext },
   4151 #endif
   4152       { NULL, 0, NULL },
   4153     };
   4154     int x = 0;
   4155     while (Builtins[x].name != NULL) {
   4156       err = cs_register_function(my_parse, Builtins[x].name, Builtins[x].nargs,
   4157                                Builtins[x].function);
   4158       if (err)
   4159       {
   4160         cs_destroy(&my_parse);
   4161         return nerr_pass(err);
   4162       }
   4163       x++;
   4164     }
   4165     /* Set global_hdf to be null */
   4166     my_parse->global_hdf = NULL;
   4167     my_parse->parent = NULL;
   4168   }
   4169   else
   4170   {
   4171     /* TODO: macros and functions should actually not be duplicated, they
   4172      * should just be modified in lookup to walk the CS struct hierarchy we're
   4173      * creating here */
   4174     /* BUG: We currently can't copy the macros because they reference the parse
   4175      * tree, so if this sub-parse tree adds a macro, the macro reference will
   4176      * persist, but the parse tree it points to will be gone when the sub-parse
   4177      * is gone. */
   4178     my_parse->functions = parent->functions;
   4179     my_parse->global_hdf = parent->global_hdf;
   4180     my_parse->fileload = parent->fileload;
   4181     my_parse->fileload_ctx = parent->fileload_ctx;
   4182     // This should be safe since locals handling is done entirely local to the
   4183     // eval functions, not globally by the parse handling.  This should
   4184     // pass the locals down to the new parse context to make locals work with
   4185     // lvar
   4186     my_parse->locals = parent->locals;
   4187     my_parse->parent = parent;
   4188 
   4189     /* Copy the audit flag from parent */
   4190     my_parse->audit_mode = parent->audit_mode;
   4191   }
   4192 
   4193   *parse = my_parse;
   4194   return STATUS_OK;
   4195 }
   4196 
   4197 void cs_register_fileload(CSPARSE *parse, void *ctx, CSFILELOAD fileload) {
   4198   if (parse != NULL) {
   4199     parse->fileload_ctx = ctx;
   4200     parse->fileload = fileload;
   4201   }
   4202 }
   4203 
   4204 void cs_destroy (CSPARSE **parse)
   4205 {
   4206   CSPARSE *my_parse = *parse;
   4207 
   4208   if (my_parse == NULL)
   4209     return;
   4210 
   4211   uListDestroy (&(my_parse->stack), ULIST_FREE);
   4212   uListDestroy (&(my_parse->alloc), ULIST_FREE);
   4213 
   4214   dealloc_macro(&my_parse->macros);
   4215   dealloc_node(&(my_parse->tree));
   4216   if (my_parse->parent == NULL) {
   4217     dealloc_function(&(my_parse->functions));
   4218   }
   4219 
   4220   /* Free list of errors */
   4221   if (my_parse->err_list != NULL) {
   4222     CS_ERROR *ptr;
   4223 
   4224     while (my_parse->err_list) {
   4225       ptr = my_parse->err_list->next;
   4226       free(my_parse->err_list->err);
   4227       free(my_parse->err_list);
   4228       my_parse->err_list = ptr;
   4229     }
   4230   }
   4231 
   4232   free(my_parse);
   4233   *parse = NULL;
   4234 }
   4235 
   4236 /* **** CS Debug Dumps ******************************************** */
   4237 static NEOERR *dump_node (CSPARSE *parse, CSTREE *node, int depth, void *ctx,
   4238     CSOUTFUNC cb, char *buf, int blen)
   4239 {
   4240   NEOERR *err;
   4241 
   4242   while (node != NULL)
   4243   {
   4244     snprintf (buf, blen, "%*s %s ", depth, "", Commands[node->cmd].cmd);
   4245     err = cb (ctx, buf);
   4246     if (err) return nerr_pass (err);
   4247     if (node->cmd)
   4248     {
   4249       if (node->arg1.op_type)
   4250       {
   4251 	if (node->arg1.op_type == CS_TYPE_NUM)
   4252 	{
   4253 	  snprintf (buf, blen, "%ld ", node->arg1.n);
   4254 	}
   4255 	else if (node->arg1.op_type == CS_TYPE_MACRO)
   4256 	{
   4257 	  snprintf (buf, blen, "%s ", node->arg1.macro->name);
   4258 	}
   4259 	else
   4260 	{
   4261 	  snprintf (buf, blen, "%s ", node->arg1.s);
   4262 	}
   4263 	err = cb (ctx, buf);
   4264 	if (err) return nerr_pass (err);
   4265       }
   4266       if (node->arg2.op_type)
   4267       {
   4268 	if (node->arg2.op_type == CS_TYPE_NUM)
   4269 	{
   4270 	  snprintf (buf, blen, "%ld", node->arg2.n);
   4271 	}
   4272 	else
   4273 	{
   4274 	  snprintf (buf, blen, "%s", node->arg2.s);
   4275 	}
   4276 	err = cb (ctx, buf);
   4277 	if (err) return nerr_pass (err);
   4278       }
   4279       if (node->vargs)
   4280       {
   4281 	CSARG *arg;
   4282 	arg = node->vargs;
   4283 	while (arg)
   4284 	{
   4285 	  if (arg->op_type == CS_TYPE_NUM)
   4286 	  {
   4287 	    snprintf (buf, blen, "%ld ", arg->n);
   4288 	  }
   4289 	  else
   4290 	  {
   4291 	    snprintf (buf, blen, "%s ", arg->s);
   4292 	  }
   4293 	  err = cb (ctx, buf);
   4294 	  if (err) return nerr_pass (err);
   4295 	  arg = arg->next;
   4296 	}
   4297       }
   4298     }
   4299     err = cb (ctx, "\n");
   4300     if (err) return nerr_pass (err);
   4301     if (node->case_0)
   4302     {
   4303       snprintf (buf, blen, "%*s %s\n", depth, "", "Case 0");
   4304       err = cb (ctx, buf);
   4305       if (err) return nerr_pass (err);
   4306       err = dump_node (parse, node->case_0, depth+1, ctx, cb, buf, blen);
   4307       if (err) return nerr_pass (err);
   4308     }
   4309     if (node->case_1)
   4310     {
   4311       snprintf (buf, blen, "%*s %s\n", depth, "", "Case 1");
   4312       err = cb (ctx, buf);
   4313       if (err) return nerr_pass (err);
   4314       err = dump_node (parse, node->case_1, depth+1, ctx, cb, buf, blen);
   4315       if (err) return nerr_pass (err);
   4316     }
   4317     node = node->next;
   4318   }
   4319   return STATUS_OK;
   4320 }
   4321 
   4322 NEOERR *cs_dump (CSPARSE *parse, void *ctx, CSOUTFUNC cb)
   4323 {
   4324   CSTREE *node;
   4325   char buf[4096];
   4326 
   4327   if (parse->tree == NULL)
   4328     return nerr_raise (NERR_ASSERT, "No parse tree exists");
   4329 
   4330   node = parse->tree;
   4331   return nerr_pass (dump_node (parse, node, 0, ctx, cb, buf, sizeof(buf)));
   4332 }
   4333 
   4334 #if 0
   4335 static char *node_name (CSTREE *node)
   4336 {
   4337   static char buf[256];
   4338 
   4339   if (node == NULL)
   4340     snprintf (buf, sizeof(buf), "NULL");
   4341   else
   4342     snprintf (buf, sizeof(buf), "%s_%08x", Commands[node->cmd].cmd,
   4343 	node->node_num);
   4344 
   4345   return buf;
   4346 }
   4347 
   4348 static NEOERR *dump_node_pre_c (CSPARSE *parse, CSTREE *node, FILE *fp)
   4349 {
   4350   NEOERR *err;
   4351 
   4352   while (node != NULL)
   4353   {
   4354     fprintf (fp, "CSTREE %s;\n", node_name(node));
   4355     if (node->case_0)
   4356     {
   4357       err = dump_node_pre_c (parse, node->case_0, fp);
   4358       if (err != STATUS_OK) nerr_pass (err);
   4359     }
   4360     if (node->case_1)
   4361     {
   4362       err = dump_node_pre_c (parse, node->case_1, fp);
   4363       if (err != STATUS_OK) nerr_pass (err);
   4364     }
   4365     node = node->next;
   4366   }
   4367   return STATUS_OK;
   4368 }
   4369 
   4370 static NEOERR *dump_node_c (CSPARSE *parse, CSTREE *node, FILE *fp)
   4371 {
   4372   NEOERR *err;
   4373   char *s;
   4374 
   4375   while (node != NULL)
   4376   {
   4377     fprintf (fp, "CSTREE %s =\n\t{%d, %d, %d, ", node_name(node), node->node_num,
   4378 	node->cmd, node->flags);
   4379     s = repr_string_alloc (node->arg1.s);
   4380     if (s == NULL)
   4381       return nerr_raise(NERR_NOMEM, "Unable to allocate space for repr");
   4382     fprintf (fp, "\n\t  { %d, %s, %ld }, ", node->arg1.op_type, s, node->arg1.n);
   4383     free(s);
   4384     s = repr_string_alloc (node->arg2.s);
   4385     if (s == NULL)
   4386       return nerr_raise(NERR_NOMEM, "Unable to allocate space for repr");
   4387     fprintf (fp, "\n\t  { %d, %s, %ld }, ", node->arg2.op_type, s, node->arg2.n);
   4388     free(s);
   4389     if (node->case_0)
   4390       fprintf (fp, "\n\t%d, &%s, ", node->op, node_name(node->case_0));
   4391     else
   4392       fprintf (fp, "\n\t%d, NULL, ", node->op);
   4393     if (node->case_1)
   4394       fprintf (fp, "&%s, ", node_name(node->case_1));
   4395     else
   4396       fprintf (fp, "NULL, ");
   4397     if (node->next)
   4398       fprintf (fp, "&%s};\n\n", node_name(node->next));
   4399     else
   4400       fprintf (fp, "NULL};\n\n");
   4401     if (node->case_0)
   4402     {
   4403       err = dump_node_c (parse, node->case_0, fp);
   4404       if (err != STATUS_OK) nerr_pass (err);
   4405     }
   4406     if (node->case_1)
   4407     {
   4408       err = dump_node_c (parse, node->case_1, fp);
   4409       if (err != STATUS_OK) nerr_pass (err);
   4410     }
   4411     node = node->next;
   4412   }
   4413   return STATUS_OK;
   4414 }
   4415 
   4416 NEOERR *cs_dump_c (CSPARSE *parse, char *path)
   4417 {
   4418   CSTREE *node;
   4419   FILE *fp;
   4420   NEOERR *err;
   4421 
   4422   if (parse->tree == NULL)
   4423     return nerr_raise (NERR_ASSERT, "No parse tree exists");
   4424 
   4425   fp = fopen(path, "w");
   4426   if (fp == NULL)
   4427   {
   4428     return nerr_raise (NERR_SYSTEM,
   4429 	"Unable to open file %s for writing: [%d] %s", path, errno,
   4430 	strerror(errno));
   4431   }
   4432 
   4433   fprintf(fp, "/* Auto-generated file: DO NOT EDIT */\n");
   4434   fprintf(fp, "#include <stdlib.h>\n\n");
   4435   fprintf(fp, "#include \"cs.h\"\n");
   4436   node = parse->tree;
   4437   err = dump_node_pre_c (parse, node, fp);
   4438   fprintf(fp, "\n");
   4439   err = dump_node_c (parse, node, fp);
   4440   fclose(fp);
   4441   return nerr_pass (err);
   4442 }
   4443 #endif
   4444