| /* |
| * Copyright 2001-2004 Brandon Long |
| * All Rights Reserved. |
| * |
| * ClearSilver Templating System |
| * |
| * This code is made available under the terms of the ClearSilver License. |
| * http://www.clearsilver.net/license.hdf |
| * |
| */ |
| |
| /* |
| * TODO: there is some really ugly pseudo reference counting in here |
| * for allocation of temporary strings (and passing references). See the alloc |
| * member of various structs for details. We should move this to an arena |
| * allocator so we can just allocate whenever we need to and just clean up |
| * all the allocation at the end (may require two arenas: one for parese and |
| * one for render) |
| */ |
| |
| #include "cs_config.h" |
| |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <ctype.h> |
| #include <limits.h> |
| #include <stdarg.h> |
| |
| #ifdef ENABLE_GETTEXT |
| #include <libintl.h> |
| #endif |
| |
| #include "util/neo_misc.h" |
| #include "util/neo_err.h" |
| #include "util/neo_files.h" |
| #include "util/neo_str.h" |
| #include "util/ulist.h" |
| #include "cs.h" |
| |
| /* turn on some debug output for expressions */ |
| #define DEBUG_EXPR_PARSE 0 |
| #define DEBUG_EXPR_EVAL 0 |
| |
| typedef enum |
| { |
| ST_SAME = 0, |
| ST_GLOBAL = 1<<0, |
| ST_IF = 1<<1, |
| ST_ELSE = 1<<2, |
| ST_EACH = 1<<3, |
| ST_WITH = 1<<4, |
| ST_POP = 1<<5, |
| ST_DEF = 1<<6, |
| ST_LOOP = 1<<7, |
| ST_ALT = 1<<8, |
| ST_ESCAPE = 1<<9, |
| } CS_STATE; |
| |
| #define ST_ANYWHERE (ST_EACH | ST_WITH | ST_ELSE | ST_IF | ST_GLOBAL | ST_DEF | ST_LOOP | ST_ALT | ST_ESCAPE) |
| |
| typedef struct _stack_entry |
| { |
| CS_STATE state; |
| NEOS_ESCAPE escape; |
| CSTREE *tree; |
| CSTREE *next_tree; |
| int num_local; |
| int location; |
| } STACK_ENTRY; |
| |
| static NEOERR *literal_parse (CSPARSE *parse, int cmd, char *arg); |
| static NEOERR *literal_eval (CSPARSE *parse, CSTREE *node, CSTREE **next); |
| static NEOERR *name_parse (CSPARSE *parse, int cmd, char *arg); |
| static NEOERR *name_eval (CSPARSE *parse, CSTREE *node, CSTREE **next); |
| static NEOERR *var_parse (CSPARSE *parse, int cmd, char *arg); |
| static NEOERR *var_eval (CSPARSE *parse, CSTREE *node, CSTREE **next); |
| static NEOERR *evar_parse (CSPARSE *parse, int cmd, char *arg); |
| static NEOERR *lvar_parse (CSPARSE *parse, int cmd, char *arg); |
| static NEOERR *lvar_eval (CSPARSE *parse, CSTREE *node, CSTREE **next); |
| static NEOERR *if_parse (CSPARSE *parse, int cmd, char *arg); |
| static NEOERR *if_eval (CSPARSE *parse, CSTREE *node, CSTREE **next); |
| static NEOERR *else_parse (CSPARSE *parse, int cmd, char *arg); |
| static NEOERR *elif_parse (CSPARSE *parse, int cmd, char *arg); |
| static NEOERR *endif_parse (CSPARSE *parse, int cmd, char *arg); |
| static NEOERR *each_with_parse (CSPARSE *parse, int cmd, char *arg); |
| static NEOERR *each_eval (CSPARSE *parse, CSTREE *node, CSTREE **next); |
| static NEOERR *with_eval (CSPARSE *parse, CSTREE *node, CSTREE **next); |
| static NEOERR *end_parse (CSPARSE *parse, int cmd, char *arg); |
| static NEOERR *include_parse (CSPARSE *parse, int cmd, char *arg); |
| static NEOERR *linclude_parse (CSPARSE *parse, int cmd, char *arg); |
| static NEOERR *linclude_eval (CSPARSE *parse, CSTREE *node, CSTREE **next); |
| static NEOERR *def_parse (CSPARSE *parse, int cmd, char *arg); |
| static NEOERR *skip_eval (CSPARSE *parse, CSTREE *node, CSTREE **next); |
| static NEOERR *call_parse (CSPARSE *parse, int cmd, char *arg); |
| static NEOERR *call_eval (CSPARSE *parse, CSTREE *node, CSTREE **next); |
| static NEOERR *set_parse (CSPARSE *parse, int cmd, char *arg); |
| static NEOERR *set_eval (CSPARSE *parse, CSTREE *node, CSTREE **next); |
| static NEOERR *loop_parse (CSPARSE *parse, int cmd, char *arg); |
| static NEOERR *loop_eval (CSPARSE *parse, CSTREE *node, CSTREE **next); |
| static NEOERR *alt_parse (CSPARSE *parse, int cmd, char *arg); |
| static NEOERR *alt_eval (CSPARSE *parse, CSTREE *node, CSTREE **next); |
| static NEOERR *escape_parse (CSPARSE *parse, int cmd, char *arg); |
| static NEOERR *escape_eval (CSPARSE *parse, CSTREE *node, CSTREE **next); |
| |
| static NEOERR *render_node (CSPARSE *parse, CSTREE *node); |
| static NEOERR *cs_init_internal (CSPARSE **parse, HDF *hdf, CSPARSE *parent); |
| static int rearrange_for_call(CSARG **args); |
| |
| typedef struct _cmds |
| { |
| char *cmd; |
| int cmdlen; |
| CS_STATE allowed_state; |
| CS_STATE next_state; |
| NEOERR* (*parse_handler)(CSPARSE *parse, int cmd, char *arg); |
| NEOERR* (*eval_handler)(CSPARSE *parse, CSTREE *node, CSTREE **next); |
| int has_arg; |
| } CS_CMDS; |
| |
| CS_CMDS Commands[] = { |
| {"literal", sizeof("literal")-1, ST_ANYWHERE, ST_SAME, |
| literal_parse, literal_eval, 0}, |
| {"name", sizeof("name")-1, ST_ANYWHERE, ST_SAME, |
| name_parse, name_eval, 1}, |
| {"var", sizeof("var")-1, ST_ANYWHERE, ST_SAME, |
| var_parse, var_eval, 1}, |
| {"uvar", sizeof("uvar")-1, ST_ANYWHERE, ST_SAME, |
| var_parse, var_eval, 1}, |
| {"evar", sizeof("evar")-1, ST_ANYWHERE, ST_SAME, |
| evar_parse, skip_eval, 1}, |
| {"lvar", sizeof("lvar")-1, ST_ANYWHERE, ST_SAME, |
| lvar_parse, lvar_eval, 1}, |
| {"if", sizeof("if")-1, ST_ANYWHERE, ST_IF, |
| if_parse, if_eval, 1}, |
| {"else", sizeof("else")-1, ST_IF, ST_POP | ST_ELSE, |
| else_parse, skip_eval, 0}, |
| {"elseif", sizeof("elseif")-1, ST_IF, ST_SAME, |
| elif_parse, if_eval, 1}, |
| {"elif", sizeof("elif")-1, ST_IF, ST_SAME, |
| elif_parse, if_eval, 1}, |
| {"/if", sizeof("/if")-1, ST_IF | ST_ELSE, ST_POP, |
| endif_parse, skip_eval, 0}, |
| {"each", sizeof("each")-1, ST_ANYWHERE, ST_EACH, |
| each_with_parse, each_eval, 1}, |
| {"/each", sizeof("/each")-1, ST_EACH, ST_POP, |
| end_parse, skip_eval, 0}, |
| {"with", sizeof("each")-1, ST_ANYWHERE, ST_WITH, |
| each_with_parse, with_eval, 1}, |
| {"/with", sizeof("/with")-1, ST_WITH, ST_POP, |
| end_parse, skip_eval, 0}, |
| {"include", sizeof("include")-1, ST_ANYWHERE, ST_SAME, |
| include_parse, skip_eval, 1}, |
| {"linclude", sizeof("linclude")-1, ST_ANYWHERE, ST_SAME, |
| linclude_parse, linclude_eval, 1}, |
| {"def", sizeof("def")-1, ST_ANYWHERE, ST_DEF, |
| def_parse, skip_eval, 1}, |
| {"/def", sizeof("/def")-1, ST_DEF, ST_POP, |
| end_parse, skip_eval, 0}, |
| {"call", sizeof("call")-1, ST_ANYWHERE, ST_SAME, |
| call_parse, call_eval, 1}, |
| {"set", sizeof("set")-1, ST_ANYWHERE, ST_SAME, |
| set_parse, set_eval, 1}, |
| {"loop", sizeof("loop")-1, ST_ANYWHERE, ST_LOOP, |
| loop_parse, loop_eval, 1}, |
| {"/loop", sizeof("/loop")-1, ST_LOOP, ST_POP, |
| end_parse, skip_eval, 1}, |
| {"alt", sizeof("alt")-1, ST_ANYWHERE, ST_ALT, |
| alt_parse, alt_eval, 1}, |
| {"/alt", sizeof("/alt")-1, ST_ALT, ST_POP, |
| end_parse, skip_eval, 1}, |
| {"escape", sizeof("escape")-1, ST_ANYWHERE, ST_ESCAPE, |
| escape_parse, escape_eval, 1}, |
| {"/escape", sizeof("/escape")-1, ST_ESCAPE, ST_POP, |
| end_parse, skip_eval, 1}, |
| {NULL}, |
| }; |
| |
| /* Possible Config.VarEscapeMode values */ |
| typedef struct _escape_modes |
| { |
| char *mode; /* Add space for NUL */ |
| NEOS_ESCAPE context; /* Context of the name */ |
| } CS_ESCAPE_MODES; |
| |
| CS_ESCAPE_MODES EscapeModes[] = { |
| {"none", NEOS_ESCAPE_NONE}, |
| {"html", NEOS_ESCAPE_HTML}, |
| {"js", NEOS_ESCAPE_SCRIPT}, |
| {"url", NEOS_ESCAPE_URL}, |
| {NULL}, |
| }; |
| |
| |
| /* **** CS alloc/dealloc ******************************************** */ |
| |
| static int NodeNumber = 0; |
| |
| static void init_node_pos(CSTREE *node, CSPARSE *parse) |
| { |
| CS_POSITION *pos = &parse->pos; |
| char *data; |
| |
| if (parse->offset < pos->cur_offset) { |
| /* Oops, we went backwards in file, is this an error? */ |
| node->linenum = -1; |
| node->colnum = parse->offset; |
| return; |
| } |
| |
| /* Start counting from 1 not 0 */ |
| if (pos->line == 0) pos->line = 1; |
| if (pos->col == 0) pos->col = 1; |
| |
| if (parse->context == NULL) { |
| /* Not in a file */ |
| node->fname = NULL; |
| } |
| else { |
| node->fname = strdup(parse->context); |
| if (node->fname == NULL) { |
| /* malloc error, cannot proceed */ |
| node->linenum = -1; |
| return; |
| } |
| } |
| |
| data = parse->context_string; |
| if (data == NULL) { |
| node->linenum = -1; |
| return; |
| } |
| |
| while (pos->cur_offset < parse->offset) { |
| if (data[pos->cur_offset] == '\n') { |
| pos->line++; |
| pos->col = 1; |
| } |
| else { |
| pos->col++; |
| } |
| |
| pos->cur_offset++; |
| } |
| |
| node->linenum = pos->line; |
| node->colnum = pos->col; |
| |
| return; |
| |
| } |
| |
| static NEOERR *alloc_node (CSTREE **node, CSPARSE *parse) |
| { |
| CSTREE *my_node; |
| |
| *node = NULL; |
| my_node = (CSTREE *) calloc (1, sizeof (CSTREE)); |
| if (my_node == NULL) |
| return nerr_raise (NERR_NOMEM, "Unable to allocate memory for node"); |
| |
| my_node->cmd = 0; |
| my_node->node_num = NodeNumber++; |
| |
| *node = my_node; |
| |
| if (parse->audit_mode) { |
| init_node_pos(my_node, parse); |
| } |
| return STATUS_OK; |
| } |
| |
| /* TODO: make these deallocations linear and not recursive */ |
| static void dealloc_arg (CSARG **arg) |
| { |
| CSARG *p; |
| |
| if (*arg == NULL) return; |
| p = *arg; |
| if (p->expr1) dealloc_arg (&(p->expr1)); |
| if (p->expr2) dealloc_arg (&(p->expr2)); |
| if (p->next) dealloc_arg (&(p->next)); |
| |
| if (p->argexpr) free(p->argexpr); |
| |
| free(p); |
| *arg = NULL; |
| } |
| |
| static void dealloc_node (CSTREE **node) |
| { |
| CSTREE *my_node; |
| |
| if (*node == NULL) return; |
| my_node = *node; |
| if (my_node->case_0) dealloc_node (&(my_node->case_0)); |
| if (my_node->case_1) dealloc_node (&(my_node->case_1)); |
| if (my_node->next) dealloc_node (&(my_node->next)); |
| if (my_node->vargs) dealloc_arg (&(my_node->vargs)); |
| if (my_node->arg1.expr1) dealloc_arg (&(my_node->arg1.expr1)); |
| if (my_node->arg1.expr2) dealloc_arg (&(my_node->arg1.expr2)); |
| if (my_node->arg1.next) dealloc_arg (&(my_node->arg1.next)); |
| if (my_node->arg2.expr1) dealloc_arg (&(my_node->arg2.expr1)); |
| if (my_node->arg2.expr2) dealloc_arg (&(my_node->arg2.expr2)); |
| if (my_node->arg2.next) dealloc_arg (&(my_node->arg2.next)); |
| |
| if (my_node->arg1.argexpr) free(my_node->arg1.argexpr); |
| if (my_node->arg2.argexpr) free(my_node->arg2.argexpr); |
| if (my_node->fname) free(my_node->fname); |
| |
| free(my_node); |
| *node = NULL; |
| } |
| |
| static void dealloc_macro (CS_MACRO **macro) |
| { |
| CS_MACRO *my_macro; |
| |
| if (*macro == NULL) return; |
| my_macro = *macro; |
| if (my_macro->name) free (my_macro->name); |
| if (my_macro->args) dealloc_arg (&(my_macro->args)); |
| if (my_macro->next) dealloc_macro (&(my_macro->next)); |
| free (my_macro); |
| *macro = NULL; |
| } |
| |
| static void dealloc_function (CS_FUNCTION **csf) |
| { |
| CS_FUNCTION *my_csf; |
| |
| if (*csf == NULL) return; |
| my_csf = *csf; |
| if (my_csf->name) free (my_csf->name); |
| if (my_csf->next) dealloc_function (&(my_csf->next)); |
| free (my_csf); |
| *csf = NULL; |
| } |
| |
| static int find_open_delim (CSPARSE *parse, char *buf, int x, int len) |
| { |
| char *p; |
| int ws_index = 2+parse->taglen; |
| |
| while (x < len) |
| { |
| p = strchr (&(buf[x]), '<'); |
| if (p == NULL) return -1; |
| if (p[1] == '?' && !strncasecmp(&p[2], parse->tag, parse->taglen) && |
| (p[ws_index] == ' ' || p[ws_index] == '\n' || p[ws_index] == '\t' || p[ws_index] == '\r')) |
| /* |
| if (p[1] && p[1] == '?' && |
| p[2] && (p[2] == 'C' || p[2] == 'c') && |
| p[3] && (p[3] == 'S' || p[3] == 's') && |
| p[4] && (p[4] == ' ' || p[4] == '\n' || p[4] == '\t' || p[4] == '\r')) |
| */ |
| { |
| return p - buf; |
| } |
| x = p - buf + 1; |
| } |
| return -1; |
| } |
| |
| static NEOERR *_store_error (CSPARSE *parse, NEOERR *err) |
| { |
| CS_ERROR *ptr; |
| CS_ERROR *node; |
| |
| node = (CS_ERROR *) calloc(1, sizeof(CS_ERROR)); |
| if (node == NULL) |
| { |
| return nerr_raise (NERR_NOMEM, |
| "Unable to allocate memory for error entry"); |
| } |
| |
| node->err = err; |
| |
| if (parse->err_list == NULL) |
| { |
| parse->err_list = node; |
| return STATUS_OK; |
| } |
| |
| ptr = parse->err_list; |
| while (ptr->next != NULL) |
| ptr = ptr->next; |
| |
| ptr->next = node; |
| return STATUS_OK; |
| |
| } |
| |
| NEOERR *cs_parse_file (CSPARSE *parse, const char *path) |
| { |
| NEOERR *err; |
| char *ibuf; |
| const char *save_context; |
| int save_infile; |
| char fpath[_POSIX_PATH_MAX]; |
| CS_POSITION pos; |
| |
| if (path == NULL) |
| return nerr_raise (NERR_ASSERT, "path is NULL"); |
| |
| if (parse->fileload) |
| { |
| err = parse->fileload(parse->fileload_ctx, parse->hdf, path, &ibuf); |
| } |
| else |
| { |
| if (path[0] != '/') |
| { |
| err = hdf_search_path (parse->hdf, path, fpath); |
| if (parse->global_hdf && nerr_handle(&err, NERR_NOT_FOUND)) |
| err = hdf_search_path(parse->global_hdf, path, fpath); |
| if (err != STATUS_OK) return nerr_pass(err); |
| path = fpath; |
| } |
| |
| err = ne_load_file (path, &ibuf); |
| } |
| if (err) return nerr_pass (err); |
| |
| save_context = parse->context; |
| parse->context = path; |
| save_infile = parse->in_file; |
| parse->in_file = 1; |
| |
| if (parse->audit_mode) { |
| /* Save previous position before parsing the new file */ |
| memcpy(&pos, &parse->pos, sizeof(CS_POSITION)); |
| |
| parse->pos.line = 0; |
| parse->pos.col = 0; |
| parse->pos.cur_offset = 0; |
| } |
| |
| err = cs_parse_string(parse, ibuf, strlen(ibuf)); |
| |
| if (parse->audit_mode) { |
| memcpy(&parse->pos, &pos, sizeof(CS_POSITION)); |
| } |
| |
| parse->in_file = save_infile; |
| parse->context = save_context; |
| |
| return nerr_pass(err); |
| } |
| |
| static char *find_context (CSPARSE *parse, int offset, char *buf, size_t blen) |
| { |
| FILE *fp; |
| int dump_err = 1; |
| char line[256]; |
| int count = 0; |
| int lineno = 0; |
| char *data; |
| |
| if (offset == -1) offset = parse->offset; |
| |
| do |
| { |
| if (parse->in_file && parse->context) |
| { |
| /* Open the file and find which line we're on */ |
| |
| fp = fopen(parse->context, "r"); |
| if (fp == NULL) { |
| ne_warn("Unable to open context %s", parse->context); |
| break; |
| } |
| while (fgets(line, sizeof(line), fp) != NULL) |
| { |
| count += strlen(line); |
| if (strchr(line, '\n') != NULL) |
| lineno++; |
| if (count > offset) break; |
| } |
| fclose (fp); |
| snprintf (buf, blen, "[%s:%d]", parse->context, lineno); |
| } |
| else |
| { |
| data = parse->context_string; |
| if (data != NULL) |
| { |
| lineno = 1; |
| while (count < offset) |
| { |
| if (data[count++] == '\n') lineno++; |
| } |
| if (parse->context) |
| snprintf (buf, blen, "[%s:~%d]", parse->context, lineno); |
| else |
| snprintf (buf, blen, "[lineno:~%d]", lineno); |
| } |
| else |
| { |
| if (parse->context) |
| snprintf (buf, blen, "[%s:%d]", parse->context, offset); |
| else |
| snprintf (buf, blen, "[offset:%d]", offset); |
| } |
| } |
| dump_err = 0; |
| } while (0); |
| if (dump_err) |
| { |
| if (parse->context) |
| snprintf (buf, blen, "[-E- %s:%d]", parse->context, offset); |
| else |
| snprintf (buf, blen, "[-E- offset:%d]", offset); |
| } |
| |
| return buf; |
| } |
| |
| static char *expand_state (CS_STATE state) |
| { |
| static char buf[256]; |
| |
| if (state & ST_GLOBAL) |
| return "GLOBAL"; |
| else if (state & ST_IF) |
| return "IF"; |
| else if (state & ST_ELSE) |
| return "ELSE"; |
| else if (state & ST_EACH) |
| return "EACH"; |
| else if (state & ST_WITH) |
| return "WITH"; |
| else if (state & ST_DEF) |
| return "DEF"; |
| else if (state & ST_LOOP) |
| return "LOOP"; |
| else if (state & ST_ALT) |
| return "ALT"; |
| else if (state & ST_ESCAPE) |
| return "ESCAPE"; |
| |
| snprintf(buf, sizeof(buf), "Unknown state %d", state); |
| return buf; |
| } |
| |
| NEOERR *cs_parse_string (CSPARSE *parse, char *ibuf, size_t ibuf_len) |
| { |
| NEOERR *err = STATUS_OK; |
| STACK_ENTRY *entry, *current_entry; |
| char *p; |
| char *token; |
| int done = 0; |
| int i, n; |
| char *arg; |
| int initial_stack_depth; |
| int initial_offset; |
| char *initial_context; |
| char tmp[256]; |
| |
| err = uListAppend(parse->alloc, ibuf); |
| if (err) |
| { |
| free (ibuf); |
| return nerr_pass (err); |
| } |
| |
| initial_stack_depth = uListLength(parse->stack); |
| initial_offset = parse->offset; |
| initial_context = parse->context_string; |
| |
| parse->offset = 0; |
| parse->context_string = ibuf; |
| while (!done) |
| { |
| /* Stage 1: Find <?cs starter */ |
| i = find_open_delim (parse, ibuf, parse->offset, ibuf_len); |
| if (i >= 0) |
| { |
| ibuf[i] = '\0'; |
| /* Create literal with data up until start delim */ |
| /* ne_warn ("literal -> %d-%d", parse->offset, i); */ |
| err = (*(Commands[0].parse_handler))(parse, 0, &(ibuf[parse->offset])); |
| /* skip delim */ |
| token = &(ibuf[i+3+parse->taglen]); |
| while (*token && isspace(*token)) token++; |
| |
| p = strstr (token, "?>"); |
| if (p == NULL) |
| { |
| return nerr_raise (NERR_PARSE, "%s Missing end ?> at %s", |
| find_context(parse, i, tmp, sizeof(tmp)), &(ibuf[parse->offset])); |
| } |
| *p = '\0'; |
| if (strstr (token, "<?") != NULL) |
| { |
| return nerr_raise (NERR_PARSE, "%s Missing end ?> at %s", |
| find_context(parse, i, tmp, sizeof(tmp)), |
| token); |
| } |
| parse->offset = p - ibuf + 2; |
| if (token[0] != '#') /* handle comments */ |
| { |
| for (i = 1; Commands[i].cmd; i++) |
| { |
| n = Commands[i].cmdlen; |
| if (!strncasecmp(token, Commands[i].cmd, n)) |
| { |
| if ((Commands[i].has_arg && ((token[n] == ':') || (token[n] == '!'))) |
| || (token[n] == ' ' || token[n] == '\0' || token[n] == '\r' || token[n] == '\n')) |
| { |
| err = uListGet (parse->stack, -1, (void *)&entry); |
| if (err != STATUS_OK) goto cs_parse_done; |
| if (!(Commands[i].allowed_state & entry->state)) |
| { |
| return nerr_raise (NERR_PARSE, |
| "%s Command %s not allowed in %s", Commands[i].cmd, |
| find_context(parse, -1, tmp, sizeof(tmp)), |
| expand_state(entry->state)); |
| } |
| if (Commands[i].has_arg) |
| { |
| /* Need to parse out arg */ |
| arg = &token[n]; |
| err = (*(Commands[i].parse_handler))(parse, i, arg); |
| } |
| else |
| { |
| err = (*(Commands[i].parse_handler))(parse, i, NULL); |
| } |
| if (err != STATUS_OK) goto cs_parse_done; |
| if (Commands[i].next_state & ST_POP) |
| { |
| void *ptr; |
| err = uListPop(parse->stack, &ptr); |
| if (err != STATUS_OK) goto cs_parse_done; |
| entry = (STACK_ENTRY *)ptr; |
| if (entry->next_tree) |
| parse->current = entry->next_tree; |
| else |
| parse->current = entry->tree; |
| free(entry); |
| } |
| if ((Commands[i].next_state & ~ST_POP) != ST_SAME) |
| { |
| entry = (STACK_ENTRY *) calloc (1, sizeof (STACK_ENTRY)); |
| if (entry == NULL) |
| return nerr_raise (NERR_NOMEM, |
| "%s Unable to allocate memory for stack entry", |
| find_context(parse, -1, tmp, sizeof(tmp))); |
| entry->state = Commands[i].next_state; |
| entry->tree = parse->current; |
| entry->location = parse->offset; |
| /* Set the new stack escape context to the parent one */ |
| err = uListGet (parse->stack, -1, (void *)¤t_entry); |
| if (err != STATUS_OK) { |
| free (entry); |
| goto cs_parse_done; |
| } |
| entry->escape = current_entry->escape; |
| /* Get the future escape context from parse because when |
| * we parse "escape", the new stack has not yet been established. |
| */ |
| entry->escape = parse->escaping.next_stack; |
| parse->escaping.next_stack = parse->escaping.global_ctx; |
| err = uListAppend(parse->stack, entry); |
| if (err != STATUS_OK) { |
| free (entry); |
| goto cs_parse_done; |
| } |
| } |
| break; |
| } |
| } |
| } |
| if (Commands[i].cmd == NULL) |
| { |
| return nerr_raise (NERR_PARSE, "%s Unknown command %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), token); |
| } |
| } |
| } |
| else |
| { |
| /* Create literal with all remaining data */ |
| err = (*(Commands[0].parse_handler))(parse, 0, &(ibuf[parse->offset])); |
| done = 1; |
| } |
| } |
| /* Should we check the parse stack here? */ |
| while (uListLength(parse->stack) > initial_stack_depth) |
| { |
| err = uListPop(parse->stack, (void *)&entry); |
| if (err != STATUS_OK) goto cs_parse_done; |
| if (entry->state & ~(ST_GLOBAL | ST_POP)) |
| return nerr_raise (NERR_PARSE, "%s Non-terminted %s clause", |
| find_context(parse, entry->location, tmp, sizeof(tmp)), |
| expand_state(entry->state)); |
| } |
| |
| cs_parse_done: |
| parse->offset = initial_offset; |
| parse->context_string = initial_context; |
| parse->escaping.current = NEOS_ESCAPE_NONE; |
| return nerr_pass(err); |
| } |
| |
| static CS_LOCAL_MAP * lookup_map (CSPARSE *parse, char *name, char **rest) |
| { |
| CS_LOCAL_MAP *map; |
| char *c; |
| |
| /* This shouldn't happen, but it did once... */ |
| if (name == NULL) return NULL; |
| map = parse->locals; |
| c = strchr (name, '.'); |
| if (c != NULL) *c = '\0'; |
| *rest = c; |
| while (map != NULL) |
| { |
| if (!strcmp (map->name, name)) |
| { |
| if (c != NULL) *c = '.'; |
| return map; |
| } |
| map = map->next; |
| } |
| if (c != NULL) *c = '.'; |
| return NULL; |
| } |
| |
| static HDF *var_lookup_obj (CSPARSE *parse, char *name) |
| { |
| CS_LOCAL_MAP *map; |
| char *c; |
| HDF *ret_hdf; |
| |
| map = lookup_map (parse, name, &c); |
| if (map && map->type == CS_TYPE_VAR) |
| { |
| if (c == NULL) |
| { |
| return map->h; |
| } |
| else |
| { |
| return hdf_get_obj (map->h, c+1); |
| } |
| } |
| /* smarti: Added support for global hdf under local hdf */ |
| /* return hdf_get_obj (parse->hdf, name); */ |
| ret_hdf = hdf_get_obj (parse->hdf, name); |
| if (ret_hdf == NULL && parse->global_hdf != NULL) { |
| ret_hdf = hdf_get_obj (parse->global_hdf, name); |
| } |
| return ret_hdf; |
| } |
| |
| /* Ugh, I have to write the same walking code because I can't grab the |
| * object for writing, as it might not exist... */ |
| static NEOERR *var_set_value (CSPARSE *parse, char *name, char *value) |
| { |
| CS_LOCAL_MAP *map; |
| char *c; |
| |
| map = parse->locals; |
| c = strchr (name, '.'); |
| if (c != NULL) *c = '\0'; |
| while (map != NULL) |
| { |
| if (!strcmp (map->name, name)) |
| { |
| if (map->type == CS_TYPE_VAR) |
| { |
| if (c == NULL) |
| { |
| if (map->h == NULL) /* node didn't exist yet */ |
| return nerr_pass (hdf_set_value (parse->hdf, map->s, value)); |
| else |
| return nerr_pass (hdf_set_value (map->h, NULL, value)); |
| } |
| else |
| { |
| *c = '.'; |
| if (map->h == NULL) /* node didn't exist yet */ |
| { |
| NEOERR *err; |
| char *mapped_name = sprintf_alloc("%s%s", map->s, c); |
| if (mapped_name == NULL) |
| return nerr_raise(NERR_NOMEM, "Unable to allocate memory to create mapped name"); |
| err = hdf_set_value(parse->hdf, mapped_name, value); |
| free(mapped_name); |
| return nerr_pass(err); |
| } |
| return nerr_pass (hdf_set_value (map->h, c+1, value)); |
| } |
| } |
| else |
| { |
| if (c == NULL) |
| { |
| char *tmp = NULL; |
| /* If this is a string, it might be what we're setting, |
| * ie <?cs set:value = value ?> |
| */ |
| if (map->type == CS_TYPE_STRING && map->map_alloc) |
| tmp = map->s; |
| map->type = CS_TYPE_STRING; |
| map->map_alloc = 1; |
| map->s = strdup(value); |
| if (tmp != NULL) free(tmp); |
| if (map->s == NULL && value != NULL) |
| return nerr_raise(NERR_NOMEM, |
| "Unable to allocate memory to set var"); |
| |
| return STATUS_OK; |
| } |
| else { |
| 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); |
| return STATUS_OK; |
| } |
| } |
| } |
| map = map->next; |
| } |
| if (c != NULL) *c = '.'; |
| return nerr_pass (hdf_set_value (parse->hdf, name, value)); |
| } |
| |
| static char *var_lookup (CSPARSE *parse, char *name) |
| { |
| CS_LOCAL_MAP *map; |
| char *c; |
| char* retval; |
| |
| map = lookup_map (parse, name, &c); |
| if (map) |
| { |
| if (map->type == CS_TYPE_VAR) |
| { |
| if (c == NULL) |
| { |
| return hdf_obj_value (map->h); |
| } |
| else |
| { |
| return hdf_get_value (map->h, c+1, NULL); |
| } |
| } |
| /* Hmm, if c != NULL, they are asking for a sub member of something |
| * which isn't a var... right now we ignore them, I don't know what |
| * the right thing is */ |
| /* hmm, its possible now that they are getting a reference to a |
| * string that will be deleted... where is it used? */ |
| else if (map->type == CS_TYPE_STRING) |
| { |
| return map->s; |
| } |
| else if (map->type == CS_TYPE_NUM) |
| { |
| char buf[40]; |
| if (map->s) return map->s; |
| snprintf (buf, sizeof(buf), "%ld", map->n); |
| map->s = strdup(buf); |
| map->map_alloc = 1; |
| return map->s; |
| } |
| } |
| /* smarti: Added support for global hdf under local hdf */ |
| /* return hdf_get_value (parse->hdf, name, NULL); */ |
| retval = hdf_get_value (parse->hdf, name, NULL); |
| if (retval == NULL && parse->global_hdf != NULL) { |
| retval = hdf_get_value (parse->global_hdf, name, NULL); |
| } |
| return retval; |
| } |
| |
| long int var_int_lookup (CSPARSE *parse, char *name) |
| { |
| char *vs; |
| |
| vs = var_lookup (parse, name); |
| |
| if (vs == NULL) |
| return 0; |
| else |
| return atoi(vs); |
| } |
| |
| typedef struct _token |
| { |
| CSTOKEN_TYPE type; |
| char *value; |
| size_t len; |
| } CSTOKEN; |
| |
| struct _simple_tokens |
| { |
| BOOL two_chars; |
| char *token; |
| CSTOKEN_TYPE type; |
| } SimpleTokens[] = { |
| { TRUE, "<=", CS_OP_LTE }, |
| { TRUE, ">=", CS_OP_GTE }, |
| { TRUE, "==", CS_OP_EQUAL }, |
| { TRUE, "!=", CS_OP_NEQUAL }, |
| { TRUE, "||", CS_OP_OR }, |
| { TRUE, "&&", CS_OP_AND }, |
| { FALSE, "!", CS_OP_NOT }, |
| /* For now, we are still treating this special instead of as an op |
| * If we make this an op, then we'd have to determine how to handle |
| * NUM types without doing something like #"5" */ |
| /* { FALSE, "#", CS_OP_NUM }, */ |
| { FALSE, "?", CS_OP_EXISTS }, |
| { FALSE, "<", CS_OP_LT }, |
| { FALSE, ">", CS_OP_GT }, |
| { FALSE, "+", CS_OP_ADD }, |
| { FALSE, "-", CS_OP_SUB }, |
| { FALSE, "*", CS_OP_MULT }, |
| { FALSE, "/", CS_OP_DIV }, |
| { FALSE, "%", CS_OP_MOD }, |
| { FALSE, "(", CS_OP_LPAREN }, |
| { FALSE, ")", CS_OP_RPAREN }, |
| { FALSE, "[", CS_OP_LBRACKET }, |
| { FALSE, "]", CS_OP_RBRACKET }, |
| { FALSE, ".", CS_OP_DOT }, |
| { FALSE, ",", CS_OP_COMMA }, |
| { FALSE, NULL, 0 } |
| }; |
| |
| #define MAX_TOKENS 256 |
| |
| static NEOERR *parse_tokens (CSPARSE *parse, char *arg, CSTOKEN *tokens, |
| int *used_tokens) |
| { |
| char tmp[256]; |
| int ntokens = 0; |
| int x; |
| BOOL found; |
| BOOL last_is_op = 1; |
| char *p, *p2; |
| char *expr = arg; |
| |
| while (arg && *arg != '\0') |
| { |
| while (*arg && isspace(*arg)) arg++; |
| if (*arg == '\0') break; |
| x = 0; |
| found = FALSE; |
| |
| /* If we already saw an operator, and this is a +/-, assume its |
| * a number */ |
| if (!(last_is_op && (*arg == '+' || *arg == '-'))) |
| { |
| while ((found == FALSE) && SimpleTokens[x].token) |
| { |
| if (((SimpleTokens[x].two_chars == TRUE) && |
| (*arg == SimpleTokens[x].token[0]) && |
| (*(arg + 1) == SimpleTokens[x].token[1])) || |
| ((SimpleTokens[x].two_chars == FALSE) && |
| (*arg == SimpleTokens[x].token[0]))) |
| { |
| tokens[ntokens++].type = SimpleTokens[x].type; |
| found = TRUE; |
| arg++; |
| if (SimpleTokens[x].two_chars) arg++; |
| } |
| x++; |
| } |
| /* Another special case: RPAREN and RBRACKET can have another op |
| * after it */ |
| if (found && !(tokens[ntokens-1].type == CS_OP_RPAREN || tokens[ntokens-1].type == CS_OP_RBRACKET)) |
| last_is_op = 1; |
| } |
| |
| if (found == FALSE) |
| { |
| if (*arg == '#') |
| { |
| /* TODO: make # an operator and not syntax */ |
| arg++; |
| tokens[ntokens].type = CS_TYPE_NUM; |
| tokens[ntokens].value = arg; |
| strtol(arg, &p, 0); |
| if (p == arg) |
| { |
| tokens[ntokens].type = CS_TYPE_VAR_NUM; |
| p = strpbrk(arg, "\"?<>=!#-+|&,)*/%[]( \t\r\n"); |
| if (p == arg) |
| return nerr_raise (NERR_PARSE, "%s Missing varname/number after #: %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), arg); |
| } |
| if (p == NULL) |
| tokens[ntokens].len = strlen(arg); |
| else |
| tokens[ntokens].len = p - arg; |
| ntokens++; |
| arg = p; |
| } |
| else if (*arg == '"') |
| { |
| arg++; |
| tokens[ntokens].type = CS_TYPE_STRING; |
| tokens[ntokens].value = arg; |
| p = strchr (arg, '"'); |
| if (p == NULL) |
| return nerr_raise (NERR_PARSE, "%s Missing end of string: %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), arg); |
| tokens[ntokens].len = p - arg; |
| ntokens++; |
| arg = p + 1; |
| } |
| else if (*arg == '\'') |
| { |
| arg++; |
| tokens[ntokens].type = CS_TYPE_STRING; |
| tokens[ntokens].value = arg; |
| p = strchr (arg, '\''); |
| if (p == NULL) |
| return nerr_raise (NERR_PARSE, "%s Missing end of string: %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), arg); |
| tokens[ntokens].len = p - arg; |
| ntokens++; |
| arg = p + 1; |
| } |
| else if (*arg == '$') |
| { |
| /* TODO: make $ an operator and not syntax */ |
| arg++; |
| tokens[ntokens].type = CS_TYPE_VAR; |
| tokens[ntokens].value = arg; |
| p = strpbrk(arg, "\"?<>=!#-+|&,)*/%[]( \t\r\n"); |
| if (p == arg) |
| return nerr_raise (NERR_PARSE, "%s Missing varname after $: %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), arg); |
| if (p == NULL) |
| tokens[ntokens].len = strlen(arg); |
| else |
| tokens[ntokens].len = p - arg; |
| ntokens++; |
| arg = p; |
| } |
| else |
| { |
| tokens[ntokens].type = CS_TYPE_VAR; |
| tokens[ntokens].value = arg; |
| /* Special case for Dave: If this is entirely a number, treat it |
| * as one */ |
| strtol(arg, &p2, 0); |
| p = strpbrk(arg, "\"?<>=!#-+|&,)*/%[]( \t\r\n"); |
| /* This is complicated because +/- is valid in a number, but not |
| * in a varname */ |
| if (p2 != arg && (p <= p2 || (p == NULL && *p2 == '\0'))) |
| { |
| tokens[ntokens].type = CS_TYPE_NUM; |
| tokens[ntokens].len = p2 - arg; |
| arg = p2; |
| } |
| else |
| { |
| if (p == arg) |
| return nerr_raise (NERR_PARSE, |
| "%s Var arg specified with no varname: %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), arg); |
| if (p == NULL) |
| tokens[ntokens].len = strlen(arg); |
| else |
| tokens[ntokens].len = p - arg; |
| arg = p; |
| } |
| ntokens++; |
| } |
| last_is_op = 0; |
| } |
| if (ntokens >= MAX_TOKENS) |
| return nerr_raise (NERR_PARSE, |
| "%s Expression exceeds maximum number of tokens of %d: %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), MAX_TOKENS, expr); |
| } |
| *used_tokens = ntokens; |
| return STATUS_OK; |
| } |
| |
| CSTOKEN_TYPE OperatorOrder[] = { |
| CS_OP_COMMA, |
| CS_OP_OR, |
| CS_OP_AND, |
| CS_OP_EQUAL | CS_OP_NEQUAL, |
| CS_OP_GT | CS_OP_GTE | CS_OP_LT | CS_OP_LTE, |
| CS_OP_ADD | CS_OP_SUB, |
| CS_OP_MULT | CS_OP_DIV | CS_OP_MOD, |
| CS_OP_NOT | CS_OP_EXISTS, |
| CS_OP_LBRACKET | CS_OP_DOT | CS_OP_LPAREN, |
| 0 |
| }; |
| |
| static char *expand_token_type(CSTOKEN_TYPE t_type, int more) |
| { |
| switch (t_type) |
| { |
| case CS_OP_EXISTS: return "?"; |
| case CS_OP_NOT: return "!"; |
| case CS_OP_NUM: return "#"; |
| case CS_OP_EQUAL: return "=="; |
| case CS_OP_NEQUAL: return "!="; |
| case CS_OP_LT: return "<"; |
| case CS_OP_LTE: return "<="; |
| case CS_OP_GT: return ">"; |
| case CS_OP_GTE: return ">="; |
| case CS_OP_AND: return "&&"; |
| case CS_OP_OR: return "||"; |
| case CS_OP_ADD: return "+"; |
| case CS_OP_SUB: return "-"; |
| case CS_OP_MULT: return "*"; |
| case CS_OP_DIV: return "/"; |
| case CS_OP_MOD: return "%"; |
| case CS_OP_LPAREN: return "("; |
| case CS_OP_RPAREN: return ")"; |
| case CS_OP_LBRACKET: return "["; |
| case CS_OP_RBRACKET: return "]"; |
| case CS_OP_DOT : return "."; |
| case CS_OP_COMMA : return ","; |
| case CS_TYPE_STRING: return more ? "STRING" : "s"; |
| case CS_TYPE_NUM: return more ? "NUM" : "n"; |
| case CS_TYPE_VAR: return more ? "VAR" : "v"; |
| case CS_TYPE_VAR_NUM: return more ? "VARNUM" : "vn"; |
| case CS_TYPE_MACRO: return more ? "MACRO" : "m"; |
| case CS_TYPE_FUNCTION: return more ? "FUNC" : "f"; |
| default: return "u"; |
| } |
| return "u"; |
| } |
| |
| static char *token_list (CSTOKEN *tokens, int ntokens, char *buf, size_t buflen) |
| { |
| char *p = buf; |
| int i, t; |
| char save; |
| |
| for (i = 0; i < ntokens && buflen > 0; i++) |
| { |
| if (tokens[i].value) |
| { |
| save = tokens[i].value[tokens[i].len]; |
| tokens[i].value[tokens[i].len] = '\0'; |
| t = snprintf(p, buflen, "%s%d:%s:'%s'", i ? " ":"", i, expand_token_type(tokens[i].type, 0), tokens[i].value); |
| tokens[i].value[tokens[i].len] = save; |
| } |
| else |
| { |
| t = snprintf(p, buflen, "%s%d:%s", i ? " ":"", i, expand_token_type(tokens[i].type, 0)); |
| } |
| if (t == -1 || t >= buflen) return buf; |
| buflen -= t; |
| p += t; |
| } |
| return buf; |
| } |
| |
| static NEOERR *parse_expr2 (CSPARSE *parse, CSTOKEN *tokens, int ntokens, int lvalue, CSARG *arg) |
| { |
| NEOERR *err = STATUS_OK; |
| char tmp[256]; |
| char tmp2[256]; |
| int x, op; |
| int m; |
| |
| #if DEBUG_EXPR_PARSE |
| fprintf(stderr, "%s\n", token_list(tokens, ntokens, tmp, sizeof(tmp))); |
| for (x = 0; x < ntokens; x++) |
| { |
| fprintf (stderr, "%s ", expand_token_type(tokens[x].type, 0)); |
| } |
| fprintf(stderr, "\n"); |
| #endif |
| |
| /* Not quite sure what to do with this case... */ |
| if (ntokens == 0) |
| { |
| return nerr_raise (NERR_PARSE, "%s Bad Expression", |
| find_context(parse, -1, tmp, sizeof(tmp))); |
| } |
| if (ntokens == 1) |
| { |
| x = 0; |
| if (tokens[0].type & CS_TYPES) |
| { |
| arg->s = tokens[0].value; |
| if (tokens[0].len >= 0) |
| arg->s[tokens[0].len] = '\0'; |
| arg->op_type = tokens[0].type; |
| |
| if (tokens[x].type == CS_TYPE_NUM) |
| arg->n = strtol(arg->s, NULL, 0); |
| return STATUS_OK; |
| } |
| else |
| { |
| return nerr_raise (NERR_PARSE, |
| "%s Terminal token is not an argument, type is %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), expand_token_type(tokens[0].type, 0)); |
| } |
| } |
| |
| /* |
| if (ntokens == 2 && (tokens[0].type & CS_OPS_UNARY)) |
| { |
| arg->op_type = tokens[0].type; |
| arg->expr1 = (CSARG *) calloc (1, sizeof (CSARG)); |
| if (arg->expr1 == NULL) |
| return nerr_raise (NERR_NOMEM, |
| "%s Unable to allocate memory for expression", |
| find_context(parse, -1, tmp, sizeof(tmp))); |
| err = parse_expr2(parse, tokens + 1, 1, lvalue, arg->expr1); |
| return nerr_pass(err); |
| } |
| */ |
| |
| op = 0; |
| while (OperatorOrder[op]) |
| { |
| x = ntokens-1; |
| while (x >= 0) |
| { |
| /* handle associative ops by skipping through the entire set here, |
| * ie the whole thing is an expression that can't match a binary op */ |
| if (tokens[x].type & CS_OP_RPAREN) |
| { |
| m = 1; |
| x--; |
| while (x >= 0) |
| { |
| if (tokens[x].type & CS_OP_RPAREN) m++; |
| if (tokens[x].type & CS_OP_LPAREN) m--; |
| if (m == 0) break; |
| x--; |
| } |
| if (m) |
| return nerr_raise (NERR_PARSE, |
| "%s Missing left parenthesis in expression", |
| find_context(parse, -1, tmp, sizeof(tmp))); |
| /* if (x == 0) break; */ |
| /* x--; */ |
| /* we don't do an x-- here, because we are special casing the |
| * left bracket to be both an operator and an associative */ |
| } |
| if (tokens[x].type & CS_OP_RBRACKET) |
| { |
| m = 1; |
| x--; |
| while (x >= 0) |
| { |
| if (tokens[x].type & CS_OP_RBRACKET) m++; |
| if (tokens[x].type & CS_OP_LBRACKET) m--; |
| if (m == 0) break; |
| x--; |
| } |
| if (m) |
| return nerr_raise (NERR_PARSE, |
| "%s Missing left bracket in expression", |
| find_context(parse, -1, tmp, sizeof(tmp))); |
| if (x == 0) break; |
| /* we don't do an x-- here, because we are special casing the |
| * left bracket to be both an operator and an associative */ |
| } |
| if (lvalue && !(tokens[x].type & CS_OPS_LVALUE)) |
| { |
| return nerr_raise (NERR_PARSE, |
| "%s Invalid op '%s' in lvalue", |
| find_context(parse, -1, tmp, sizeof(tmp)), |
| expand_token_type(tokens[x].type, 0)); |
| } |
| if (tokens[x].type & OperatorOrder[op]) |
| { |
| if (tokens[x].type & CS_OPS_UNARY) |
| { |
| if (x == 0) |
| { |
| arg->op_type = tokens[x].type; |
| arg->expr1 = (CSARG *) calloc (1, sizeof (CSARG)); |
| if (arg->expr1 == NULL) |
| return nerr_raise (NERR_NOMEM, |
| "%s Unable to allocate memory for expression", |
| find_context(parse, -1, tmp, sizeof(tmp))); |
| if (tokens[x].type & CS_OP_LPAREN) |
| { |
| if (!(tokens[ntokens-1].type & CS_OP_RPAREN)) |
| { |
| return nerr_raise (NERR_PARSE, |
| "%s Missing right parenthesis in expression", |
| find_context(parse, -1, tmp, sizeof(tmp))); |
| } |
| /* XXX: we might want to set lvalue to 0 here */ |
| /* -2 since we strip the RPAREN as well */ |
| err = parse_expr2(parse, tokens + 1, ntokens-2, lvalue, arg->expr1); |
| } |
| else |
| { |
| err = parse_expr2(parse, tokens + 1, ntokens-1, lvalue, arg->expr1); |
| } |
| return nerr_pass(err); |
| } |
| } |
| else if (tokens[x].type == CS_OP_COMMA) |
| { |
| /* Technically, comma should be a left to right, not right to |
| * left, so we're going to build up the arguments in reverse |
| * order... */ |
| arg->op_type = tokens[x].type; |
| /* The actual argument is expr1 */ |
| arg->expr1 = (CSARG *) calloc (1, sizeof (CSARG)); |
| /* The previous argument is next */ |
| arg->next = (CSARG *) calloc (1, sizeof (CSARG)); |
| if (arg->expr1 == NULL || arg->next == NULL) |
| return nerr_raise (NERR_NOMEM, |
| "%s Unable to allocate memory for expression", |
| find_context(parse, -1, tmp, sizeof(tmp))); |
| err = parse_expr2(parse, tokens + x + 1, ntokens-x-1, lvalue, arg->expr1); |
| if (err) return nerr_pass (err); |
| err = parse_expr2(parse, tokens, x, lvalue, arg->next); |
| if (err) return nerr_pass (err); |
| return STATUS_OK; |
| } |
| else |
| { |
| arg->op_type = tokens[x].type; |
| arg->expr2 = (CSARG *) calloc (1, sizeof (CSARG)); |
| arg->expr1 = (CSARG *) calloc (1, sizeof (CSARG)); |
| if (arg->expr1 == NULL || arg->expr2 == NULL) |
| return nerr_raise (NERR_NOMEM, |
| "%s Unable to allocate memory for expression", |
| find_context(parse, -1, tmp, sizeof(tmp))); |
| if (tokens[x].type & CS_OP_LBRACKET) |
| { |
| if (!(tokens[ntokens-1].type & CS_OP_RBRACKET)) |
| { |
| return nerr_raise (NERR_PARSE, |
| "%s Missing right bracket in expression", |
| find_context(parse, -1, tmp, sizeof(tmp))); |
| } |
| /* Inside of brackets, we don't limit to valid lvalue ops */ |
| /* -2 since we strip the RBRACKET as well */ |
| err = parse_expr2(parse, tokens + x + 1, ntokens-x-2, 0, arg->expr2); |
| } |
| else |
| { |
| err = parse_expr2(parse, tokens + x + 1, ntokens-x-1, lvalue, arg->expr2); |
| } |
| if (err) return nerr_pass (err); |
| err = parse_expr2(parse, tokens, x, lvalue, arg->expr1); |
| if (err) return nerr_pass (err); |
| return STATUS_OK; |
| } |
| } |
| x--; |
| } |
| op++; |
| } |
| |
| /* Unary op against an entire expression */ |
| if ((tokens[0].type & CS_OPS_UNARY) && tokens[1].type == CS_OP_LPAREN && |
| tokens[ntokens-1].type == CS_OP_RPAREN) |
| { |
| arg->op_type = tokens[0].type; |
| arg->expr1 = (CSARG *) calloc (1, sizeof (CSARG)); |
| if (arg->expr1 == NULL) |
| return nerr_raise (NERR_NOMEM, |
| "%s Unable to allocate memory for expression", |
| find_context(parse, -1, tmp, sizeof(tmp))); |
| err = parse_expr2(parse, tokens + 2, ntokens-3, lvalue, arg->expr1); |
| return nerr_pass(err); |
| } |
| if (tokens[0].type & CS_OPS_UNARY) |
| { |
| arg->op_type = tokens[0].type; |
| arg->expr1 = (CSARG *) calloc (1, sizeof (CSARG)); |
| if (arg->expr1 == NULL) |
| return nerr_raise (NERR_NOMEM, |
| "%s Unable to allocate memory for expression", |
| find_context(parse, -1, tmp, sizeof(tmp))); |
| err = parse_expr2(parse, tokens + 1, ntokens-1, lvalue, arg->expr1); |
| return nerr_pass(err); |
| } |
| |
| /* function call */ |
| if ((tokens[0].type & CS_TYPE_VAR) && tokens[1].type == CS_OP_LPAREN && |
| tokens[ntokens-1].type == CS_OP_RPAREN) |
| { |
| CS_FUNCTION *csf; |
| int nargs; |
| |
| if (tokens[0].len >= 0) |
| tokens[0].value[tokens[0].len] = '\0'; |
| |
| arg->op_type = CS_TYPE_FUNCTION; |
| csf = parse->functions; |
| while (csf != NULL) |
| { |
| if (!strcmp(tokens[0].value, csf->name)) |
| { |
| arg->function = csf; |
| break; |
| } |
| csf = csf->next; |
| } |
| if (csf == NULL) |
| { |
| return nerr_raise (NERR_PARSE, "%s Unknown function %s called", |
| find_context(parse, -1, tmp, sizeof(tmp)), tokens[0].value); |
| } |
| arg->expr1 = (CSARG *) calloc (1, sizeof (CSARG)); |
| if (arg->expr1 == NULL) |
| return nerr_raise (NERR_NOMEM, |
| "%s Unable to allocate memory for expression", |
| find_context(parse, -1, tmp, sizeof(tmp))); |
| if (ntokens-3 > 0) { |
| err = parse_expr2(parse, tokens + 2, ntokens-3, lvalue, arg->expr1); |
| if (err) return nerr_pass(err); |
| } else { |
| free(arg->expr1); |
| arg->expr1 = NULL; |
| } |
| nargs = rearrange_for_call(&(arg->expr1)); |
| if (nargs != arg->function->n_args) |
| { |
| return nerr_raise (NERR_PARSE, |
| "%s Incorrect number of arguments in call to %s, expected %d, got %d", |
| find_context(parse, -1, tmp, sizeof(tmp)), tokens[0].value, |
| arg->function->n_args, nargs); |
| } |
| return nerr_pass(err); |
| } |
| |
| return nerr_raise (NERR_PARSE, "%s Bad Expression:%s", |
| find_context(parse, -1, tmp, sizeof(tmp)), |
| token_list(tokens, ntokens, tmp2, sizeof(tmp2))); |
| } |
| |
| static NEOERR *parse_expr (CSPARSE *parse, char *arg, int lvalue, CSARG *expr) |
| { |
| NEOERR *err; |
| CSTOKEN tokens[MAX_TOKENS]; |
| int ntokens = 0; |
| |
| memset(tokens, 0, sizeof(CSTOKEN) * MAX_TOKENS); |
| err = parse_tokens (parse, arg, tokens, &ntokens); |
| if (err) return nerr_pass(err); |
| |
| if (parse->audit_mode) { |
| /* Save the complete expression string for future reference */ |
| expr->argexpr = strdup(arg); |
| } |
| |
| err = parse_expr2 (parse, tokens, ntokens, lvalue, expr); |
| if (err) return nerr_pass(err); |
| return STATUS_OK; |
| } |
| |
| static NEOERR *literal_parse (CSPARSE *parse, int cmd, char *arg) |
| { |
| NEOERR *err; |
| CSTREE *node; |
| |
| /* ne_warn ("literal: %s", arg); */ |
| err = alloc_node (&node, parse); |
| if (err) return nerr_pass(err); |
| node->cmd = cmd; |
| node->arg1.op_type = CS_TYPE_STRING; |
| node->arg1.s = arg; |
| *(parse->next) = node; |
| parse->next = &(node->next); |
| parse->current = node; |
| |
| return STATUS_OK; |
| } |
| |
| static NEOERR *literal_eval (CSPARSE *parse, CSTREE *node, CSTREE **next) |
| { |
| NEOERR *err = STATUS_OK; |
| |
| if (node->arg1.s != NULL) |
| err = parse->output_cb (parse->output_ctx, node->arg1.s); |
| *next = node->next; |
| return nerr_pass(err); |
| } |
| |
| static NEOERR *name_parse (CSPARSE *parse, int cmd, char *arg) |
| { |
| NEOERR *err; |
| CSTREE *node; |
| char *a, *s; |
| char tmp[256]; |
| |
| /* ne_warn ("name: %s", arg); */ |
| err = alloc_node (&node, parse); |
| if (err) return nerr_pass(err); |
| node->cmd = cmd; |
| if (arg[0] == '!') |
| node->flags |= CSF_REQUIRED; |
| arg++; |
| /* Validate arg is a var (regex /^[#" ]$/) */ |
| a = neos_strip(arg); |
| s = strpbrk(a, "#\" <>"); |
| if (s != NULL) |
| { |
| dealloc_node(&node); |
| return nerr_raise (NERR_PARSE, "%s Invalid character in var name %s: %c", |
| find_context(parse, -1, tmp, sizeof(tmp)), |
| a, s[0]); |
| } |
| |
| node->arg1.op_type = CS_TYPE_VAR; |
| node->arg1.s = a; |
| *(parse->next) = node; |
| parse->next = &(node->next); |
| parse->current = node; |
| |
| return STATUS_OK; |
| } |
| |
| static NEOERR *escape_parse (CSPARSE *parse, int cmd, char *arg) |
| { |
| NEOERR *err; |
| char *a = NULL; |
| char tmp[256]; |
| CS_ESCAPE_MODES *esc_cursor; |
| CSTREE *node; |
| |
| /* ne_warn ("escape: %s", arg); */ |
| err = alloc_node (&node, parse); |
| if (err) return nerr_pass(err); |
| node->cmd = cmd; |
| /* Since this throws an error always if there's a problem |
| * this flag seems pointless, but following convention, |
| * here it is. */ |
| if (arg[0] == '!') |
| node->flags |= CSF_REQUIRED; |
| arg++; /* ignore colon, space, etc */ |
| |
| /* Parse the arg - we're expecting a string */ |
| err = parse_expr (parse, arg, 0, &(node->arg1)); |
| if (err) |
| { |
| dealloc_node(&node); |
| return nerr_pass(err); |
| } |
| if (node->arg1.op_type != CS_TYPE_STRING) |
| { |
| dealloc_node(&node); |
| return nerr_raise (NERR_PARSE, "%s Invalid argument for escape: %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), arg); |
| } |
| |
| a = neos_strip(node->arg1.s); /* Strip spaces for testing */ |
| |
| /* Ensure the mode specified is allowed */ |
| for (esc_cursor = &EscapeModes[0]; |
| esc_cursor->mode != NULL; |
| esc_cursor++) |
| if (!strncasecmp(a, esc_cursor->mode, strlen(esc_cursor->mode))) |
| { |
| if (err != STATUS_OK) return nerr_pass(err); |
| parse->escaping.next_stack = esc_cursor->context; |
| break; |
| } |
| /* Didn't find an acceptable value we were looking for */ |
| if (esc_cursor->mode == NULL) |
| { |
| dealloc_node(&node); |
| return nerr_raise (NERR_PARSE, "%s Invalid argument for escape: %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), a); |
| } |
| |
| *(parse->next) = node; |
| parse->next = &(node->case_0); |
| parse->current = node; |
| return STATUS_OK; |
| } |
| |
| static NEOERR *name_eval (CSPARSE *parse, CSTREE *node, CSTREE **next) |
| { |
| NEOERR *err = STATUS_OK; |
| HDF *obj; |
| char *v; |
| |
| if (node->arg1.op_type == CS_TYPE_VAR && node->arg1.s != NULL) |
| { |
| obj = var_lookup_obj (parse, node->arg1.s); |
| if (obj != NULL) |
| { |
| v = hdf_obj_name(obj); |
| err = parse->output_cb (parse->output_ctx, v); |
| } |
| } |
| *next = node->next; |
| return nerr_pass(err); |
| } |
| |
| static NEOERR *var_parse (CSPARSE *parse, int cmd, char *arg) |
| { |
| NEOERR *err; |
| CSTREE *node; |
| STACK_ENTRY *entry; |
| |
| err = uListGet (parse->stack, -1, (void *)&entry); |
| if (err != STATUS_OK) return nerr_pass(err); |
| |
| /* ne_warn ("var: %s", arg); */ |
| err = alloc_node (&node, parse); |
| if (err) return nerr_pass(err); |
| node->cmd = cmd; |
| |
| /* Default escape the variable based on |
| * current stack's escape context except for |
| * uvar: |
| */ |
| if (!strcmp(Commands[cmd].cmd, "uvar")) |
| node->escape = NEOS_ESCAPE_NONE; |
| else |
| node->escape = entry->escape; |
| |
| |
| if (arg[0] == '!') |
| node->flags |= CSF_REQUIRED; |
| arg++; |
| /* Validate arg is a var (regex /^[#" ]$/) */ |
| err = parse_expr (parse, arg, 0, &(node->arg1)); |
| if (err) |
| { |
| dealloc_node(&node); |
| return nerr_pass(err); |
| } |
| |
| *(parse->next) = node; |
| parse->next = &(node->next); |
| parse->current = node; |
| |
| return STATUS_OK; |
| } |
| |
| static NEOERR *lvar_parse (CSPARSE *parse, int cmd, char *arg) |
| { |
| NEOERR *err; |
| CSTREE *node; |
| |
| /* ne_warn ("lvar: %s", arg); */ |
| err = alloc_node (&node, parse); |
| if (err) return nerr_pass(err); |
| node->cmd = cmd; |
| if (arg[0] == '!') |
| node->flags |= CSF_REQUIRED; |
| arg++; |
| /* Validate arg is a var (regex /^[#" ]$/) */ |
| err = parse_expr (parse, arg, 0, &(node->arg1)); |
| if (err) |
| { |
| dealloc_node(&node); |
| return nerr_pass(err); |
| } |
| |
| *(parse->next) = node; |
| parse->next = &(node->next); |
| parse->current = node; |
| |
| return STATUS_OK; |
| } |
| |
| static NEOERR *linclude_parse (CSPARSE *parse, int cmd, char *arg) |
| { |
| NEOERR *err; |
| CSTREE *node; |
| |
| /* ne_warn ("linclude: %s", arg); */ |
| err = alloc_node (&node, parse); |
| if (err) return nerr_pass(err); |
| node->cmd = cmd; |
| if (arg[0] == '!') |
| node->flags |= CSF_REQUIRED; |
| arg++; |
| /* Validate arg is a var (regex /^[#" ]$/) */ |
| err = parse_expr (parse, arg, 0, &(node->arg1)); |
| if (err) |
| { |
| dealloc_node(&node); |
| return nerr_pass(err); |
| } |
| |
| *(parse->next) = node; |
| parse->next = &(node->next); |
| parse->current = node; |
| |
| return STATUS_OK; |
| } |
| |
| static NEOERR *alt_parse (CSPARSE *parse, int cmd, char *arg) |
| { |
| NEOERR *err; |
| CSTREE *node; |
| |
| /* ne_warn ("var: %s", arg); */ |
| err = alloc_node (&node, parse); |
| if (err) return nerr_pass(err); |
| node->cmd = cmd; |
| if (arg[0] == '!') |
| node->flags |= CSF_REQUIRED; |
| arg++; |
| /* Validate arg is a var (regex /^[#" ]$/) */ |
| err = parse_expr (parse, arg, 0, &(node->arg1)); |
| if (err) |
| { |
| dealloc_node(&node); |
| return nerr_pass(err); |
| } |
| |
| *(parse->next) = node; |
| parse->next = &(node->case_0); |
| parse->current = node; |
| |
| return STATUS_OK; |
| } |
| |
| static NEOERR *evar_parse (CSPARSE *parse, int cmd, char *arg) |
| { |
| NEOERR *err; |
| CSTREE *node; |
| char *a, *s; |
| const char *save_context; |
| int save_infile; |
| char tmp[256]; |
| |
| /* ne_warn ("evar: %s", arg); */ |
| err = alloc_node (&node, parse); |
| if (err) return nerr_pass(err); |
| node->cmd = cmd; |
| if (arg[0] == '!') |
| node->flags |= CSF_REQUIRED; |
| arg++; |
| /* Validate arg is a var (regex /^[#" ]$/) */ |
| a = neos_strip(arg); |
| s = strpbrk(a, "#\" <>"); |
| if (s != NULL) |
| { |
| dealloc_node(&node); |
| return nerr_raise (NERR_PARSE, "%s Invalid character in var name %s: %c", |
| find_context(parse, -1, tmp, sizeof(tmp)), |
| a, s[0]); |
| } |
| |
| err = hdf_get_copy (parse->hdf, a, &s, NULL); |
| if (err) |
| { |
| dealloc_node(&node); |
| return nerr_pass (err); |
| } |
| if (node->flags & CSF_REQUIRED && s == NULL) |
| { |
| dealloc_node(&node); |
| return nerr_raise (NERR_NOT_FOUND, "%s Unable to evar empty variable %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), a); |
| } |
| |
| node->arg1.op_type = CS_TYPE_VAR; |
| node->arg1.s = a; |
| *(parse->next) = node; |
| parse->next = &(node->next); |
| parse->current = node; |
| |
| save_context = parse->context; |
| save_infile = parse->in_file; |
| parse->context = a; |
| parse->in_file = 0; |
| if (s) err = cs_parse_string (parse, s, strlen(s)); |
| parse->context = save_context; |
| parse->in_file = save_infile; |
| |
| return nerr_pass (err); |
| } |
| |
| static NEOERR *if_parse (CSPARSE *parse, int cmd, char *arg) |
| { |
| NEOERR *err; |
| CSTREE *node; |
| |
| /* ne_warn ("if: %s", arg); */ |
| err = alloc_node (&node, parse); |
| if (err != STATUS_OK) return nerr_pass(err); |
| node->cmd = cmd; |
| arg++; |
| |
| err = parse_expr (parse, arg, 0, &(node->arg1)); |
| if (err != STATUS_OK) |
| { |
| dealloc_node(&node); |
| return nerr_pass(err); |
| } |
| |
| *(parse->next) = node; |
| parse->next = &(node->case_0); |
| parse->current = node; |
| |
| return STATUS_OK; |
| } |
| |
| char *arg_eval (CSPARSE *parse, CSARG *arg) |
| { |
| switch ((arg->op_type & CS_TYPES)) |
| { |
| case CS_TYPE_STRING: |
| return arg->s; |
| case CS_TYPE_VAR: |
| return var_lookup (parse, arg->s); |
| case CS_TYPE_NUM: |
| case CS_TYPE_VAR_NUM: |
| default: |
| ne_warn ("Unsupported type %s in arg_eval", expand_token_type(arg->op_type, 1)); |
| return NULL; |
| } |
| } |
| |
| /* This coerces everything to numbers */ |
| long int arg_eval_num (CSPARSE *parse, CSARG *arg) |
| { |
| long int v = 0; |
| |
| switch ((arg->op_type & CS_TYPES)) |
| { |
| case CS_TYPE_STRING: |
| v = strtol(arg->s, NULL, 0); |
| break; |
| case CS_TYPE_NUM: |
| v = arg->n; |
| break; |
| |
| case CS_TYPE_VAR: |
| case CS_TYPE_VAR_NUM: |
| v = var_int_lookup (parse, arg->s); |
| break; |
| default: |
| ne_warn ("Unsupported type %s in arg_eval_num", expand_token_type(arg->op_type, 1)); |
| v = 0; |
| break; |
| } |
| return v; |
| } |
| |
| /* This is different from arg_eval_num because we don't force strings to |
| * numbers, a string is either a number (if it is all numeric) or we're |
| * testing existance. At least, that's what perl does and what dave |
| * wants */ |
| long int arg_eval_bool (CSPARSE *parse, CSARG *arg) |
| { |
| long int v = 0; |
| char *s, *r; |
| |
| switch ((arg->op_type & CS_TYPES)) |
| { |
| case CS_TYPE_STRING: |
| case CS_TYPE_VAR: |
| if (arg->op_type == CS_TYPE_VAR) |
| s = var_lookup(parse, arg->s); |
| else |
| s = arg->s; |
| if (!s || *s == '\0') return 0; /* non existance or empty is false(0) */ |
| v = strtol(s, &r, 0); |
| if (*r == '\0') /* entire string converted, treat as number */ |
| return v; |
| /* if the entire string didn't convert, then its non-numeric and |
| * exists, so its true (1) */ |
| return 1; |
| case CS_TYPE_NUM: |
| return arg->n; |
| case CS_TYPE_VAR_NUM: /* this implies forced numeric evaluation */ |
| return var_int_lookup (parse, arg->s); |
| break; |
| default: |
| ne_warn ("Unsupported type %s in arg_eval_bool", expand_token_type(arg->op_type, 1)); |
| v = 0; |
| break; |
| } |
| return v; |
| } |
| |
| char *arg_eval_str_alloc (CSPARSE *parse, CSARG *arg) |
| { |
| char *s = NULL; |
| char buf[256]; |
| long int n_val; |
| |
| switch ((arg->op_type & CS_TYPES)) |
| { |
| case CS_TYPE_STRING: |
| s = arg->s; |
| break; |
| case CS_TYPE_VAR: |
| s = var_lookup (parse, arg->s); |
| break; |
| case CS_TYPE_NUM: |
| case CS_TYPE_VAR_NUM: |
| s = buf; |
| n_val = arg_eval_num (parse, arg); |
| snprintf (buf, sizeof(buf), "%ld", n_val); |
| break; |
| default: |
| ne_warn ("Unsupported type %s in arg_eval_str_alloc", |
| expand_token_type(arg->op_type, 1)); |
| s = NULL; |
| break; |
| } |
| if (s) return strdup(s); |
| return NULL; |
| } |
| |
| #if DEBUG_EXPR_EVAL |
| static void expand_arg (CSPARSE *parse, int depth, char *where, CSARG *arg) |
| { |
| int x; |
| |
| for (x=0; x<depth; x++) |
| fputc(' ', stderr); |
| |
| fprintf(stderr, "%s op: %s alloc: %d value: ", where, expand_token_type(arg->op_type, 0), arg->alloc); |
| if (arg->op_type & CS_OP_NOT) |
| fprintf(stderr, "!"); |
| if (arg->op_type & CS_OP_NUM) |
| fprintf(stderr, "#"); |
| if (arg->op_type & CS_OP_EXISTS) |
| fprintf(stderr, "?"); |
| if (arg->op_type & (CS_TYPE_VAR_NUM | CS_TYPE_NUM)) |
| fprintf(stderr, "#"); |
| if (arg->op_type & CS_TYPE_NUM) |
| fprintf(stderr, "%ld\n", arg->n); |
| else if (arg->op_type & CS_TYPE_STRING) |
| fprintf(stderr, "'%s'\n", arg->s); |
| else if (arg->op_type & CS_TYPE_VAR) |
| fprintf(stderr, "%s = %s\n", arg->s, var_lookup(parse, arg->s)); |
| else if (arg->op_type & CS_TYPE_VAR_NUM) |
| fprintf(stderr, "%s = %ld\n", arg->s, var_int_lookup(parse, arg->s)); |
| else |
| fprintf(stderr, "\n"); |
| } |
| #endif |
| |
| static NEOERR *eval_expr_string(CSPARSE *parse, CSARG *arg1, CSARG *arg2, CSTOKEN_TYPE op, CSARG *result) |
| { |
| char *s1, *s2; |
| int out; |
| |
| result->op_type = CS_TYPE_NUM; |
| s1 = arg_eval (parse, arg1); |
| s2 = arg_eval (parse, arg2); |
| |
| if ((s1 == NULL) || (s2 == NULL)) |
| { |
| switch (op) |
| { |
| case CS_OP_EQUAL: |
| result->n = (s1 == s2) ? 1 : 0; |
| break; |
| case CS_OP_NEQUAL: |
| result->n = (s1 != s2) ? 1 : 0; |
| break; |
| case CS_OP_LT: |
| result->n = ((s1 == NULL) && (s2 != NULL)) ? 1 : 0; |
| break; |
| case CS_OP_LTE: |
| result->n = (s1 == NULL) ? 1 : 0; |
| break; |
| case CS_OP_GT: |
| result->n = ((s1 != NULL) && (s2 == NULL)) ? 1 : 0; |
| break; |
| case CS_OP_GTE: |
| result->n = (s2 == NULL) ? 1 : 0; |
| break; |
| case CS_OP_ADD: |
| /* be sure to transfer ownership of the string here */ |
| result->op_type = CS_TYPE_STRING; |
| if (s1 == NULL) |
| { |
| result->s = s2; |
| result->alloc = arg2->alloc; |
| arg2->alloc = 0; |
| } |
| else |
| { |
| result->s = s1; |
| result->alloc = arg1->alloc; |
| arg1->alloc = 0; |
| } |
| break; |
| default: |
| ne_warn ("Unsupported op %s in eval_expr", expand_token_type(op, 1)); |
| break; |
| } |
| } |
| else |
| { |
| out = strcmp (s1, s2); |
| switch (op) |
| { |
| case CS_OP_EQUAL: |
| result->n = (!out) ? 1 : 0; |
| break; |
| case CS_OP_NEQUAL: |
| result->n = (out) ? 1 : 0; |
| break; |
| case CS_OP_LT: |
| result->n = (out < 0) ? 1 : 0; |
| break; |
| case CS_OP_LTE: |
| result->n = (out <= 0) ? 1 : 0; |
| break; |
| case CS_OP_GT: |
| result->n = (out > 0) ? 1 : 0; |
| break; |
| case CS_OP_GTE: |
| result->n = (out >= 0) ? 1 : 0; |
| break; |
| case CS_OP_ADD: |
| result->op_type = CS_TYPE_STRING; |
| result->alloc = 1; |
| result->s = (char *) calloc ((strlen(s1) + strlen(s2) + 1), sizeof(char)); |
| if (result->s == NULL) |
| return nerr_raise (NERR_NOMEM, "Unable to allocate memory to concatenate strings in expression: %s + %s", s1, s2); |
| strcpy(result->s, s1); |
| strcat(result->s, s2); |
| break; |
| default: |
| ne_warn ("Unsupported op %s in eval_expr_string", expand_token_type(op, 1)); |
| break; |
| } |
| } |
| return STATUS_OK; |
| } |
| |
| static NEOERR *eval_expr_num(CSPARSE *parse, CSARG *arg1, CSARG *arg2, CSTOKEN_TYPE op, CSARG *result) |
| { |
| long int n1, n2; |
| |
| result->op_type = CS_TYPE_NUM; |
| n1 = arg_eval_num (parse, arg1); |
| n2 = arg_eval_num (parse, arg2); |
| |
| switch (op) |
| { |
| case CS_OP_EQUAL: |
| result->n = (n1 == n2) ? 1 : 0; |
| break; |
| case CS_OP_NEQUAL: |
| result->n = (n1 != n2) ? 1 : 0; |
| break; |
| case CS_OP_LT: |
| result->n = (n1 < n2) ? 1 : 0; |
| break; |
| case CS_OP_LTE: |
| result->n = (n1 <= n2) ? 1 : 0; |
| break; |
| case CS_OP_GT: |
| result->n = (n1 > n2) ? 1 : 0; |
| break; |
| case CS_OP_GTE: |
| result->n = (n1 >= n2) ? 1 : 0; |
| break; |
| case CS_OP_ADD: |
| result->n = (n1 + n2); |
| break; |
| case CS_OP_SUB: |
| result->n = (n1 - n2); |
| break; |
| case CS_OP_MULT: |
| result->n = (n1 * n2); |
| break; |
| case CS_OP_DIV: |
| if (n2 == 0) result->n = UINT_MAX; |
| else result->n = (n1 / n2); |
| break; |
| case CS_OP_MOD: |
| if (n2 == 0) result->n = 0; |
| else result->n = (n1 % n2); |
| break; |
| default: |
| ne_warn ("Unsupported op %s in eval_expr_num", expand_token_type(op, 1)); |
| break; |
| } |
| return STATUS_OK; |
| } |
| |
| static NEOERR *eval_expr_bool(CSPARSE *parse, CSARG *arg1, CSARG *arg2, CSTOKEN_TYPE op, CSARG *result) |
| { |
| long int n1, n2; |
| |
| result->op_type = CS_TYPE_NUM; |
| n1 = arg_eval_bool (parse, arg1); |
| n2 = arg_eval_bool (parse, arg2); |
| |
| switch (op) |
| { |
| case CS_OP_AND: |
| result->n = (n1 && n2) ? 1 : 0; |
| break; |
| case CS_OP_OR: |
| result->n = (n1 || n2) ? 1 : 0; |
| break; |
| default: |
| ne_warn ("Unsupported op %s in eval_expr_bool", expand_token_type(op, 1)); |
| break; |
| } |
| return STATUS_OK; |
| } |
| |
| #if DEBUG_EXPR_EVAL |
| static int _depth = 0; |
| #endif |
| |
| static NEOERR *eval_expr (CSPARSE *parse, CSARG *expr, CSARG *result) |
| { |
| NEOERR *err; |
| |
| if (expr == NULL) |
| return nerr_raise (NERR_ASSERT, "expr is NULL"); |
| if (result == NULL) |
| return nerr_raise (NERR_ASSERT, "result is NULL"); |
| |
| #if DEBUG_EXPR_EVAL |
| _depth++; |
| expand_arg(parse, _depth, "expr", expr); |
| #endif |
| |
| memset(result, 0, sizeof(CSARG)); |
| if (expr->op_type & CS_TYPES) |
| { |
| *result = *expr; |
| /* we transfer ownership of the string here.. ugh */ |
| if (expr->alloc) expr->alloc = 0; |
| #if DEBUG_EXPR_EVAL |
| expand_arg(parse, _depth, "result", result); |
| _depth--; |
| #endif |
| return STATUS_OK; |
| } |
| |
| if (expr->op_type & CS_OP_LPAREN) |
| { |
| /* lparen is a no-op, just skip */ |
| return nerr_pass(eval_expr(parse, expr->expr1, result)); |
| } |
| if (expr->op_type & CS_TYPE_FUNCTION) |
| { |
| if (expr->function == NULL || expr->function->function == NULL) |
| return nerr_raise(NERR_ASSERT, |
| "Function is NULL in attempt to evaluate function call %s", |
| (expr->function) ? expr->function->name : ""); |
| |
| /* The function evaluates all the arguments, so don't pre-evaluate |
| * argument1 */ |
| err = expr->function->function(parse, expr->function, expr->expr1, result); |
| if (err) return nerr_pass(err); |
| /* Indicate whether or not an explicit escape call was made by |
| * setting the mode (usually NONE or FUNCTION). This is ORed to |
| * ensure that escaping calls within other functions do not get |
| * double-escaped. E.g. slice(html_escape(foo), 10, 20) */ |
| parse->escaping.current |= expr->function->escape; |
| } |
| else |
| { |
| CSARG arg1, arg2; |
| arg1.alloc = 0; |
| arg2.alloc = 0; |
| |
| err = eval_expr (parse, expr->expr1, &arg1); |
| if (err) return nerr_pass(err); |
| #if DEBUG_EXPR_EVAL |
| expand_arg(parse, _depth, "arg1", &arg1); |
| #endif |
| if (expr->op_type & CS_OPS_UNARY) |
| { |
| result->op_type = CS_TYPE_NUM; |
| switch (expr->op_type) { |
| case CS_OP_NOT: |
| result->n = arg_eval_bool(parse, &arg1) ? 0 : 1; |
| break; |
| case CS_OP_EXISTS: |
| if (arg1.op_type & (CS_TYPE_VAR | CS_TYPE_VAR_NUM)) |
| { |
| if (arg_eval(parse, &arg1) == NULL) |
| result->n = 0; |
| else |
| result->n = 1; |
| } |
| else |
| { |
| /* All numbers/strings exist */ |
| result->n = 1; |
| } |
| break; |
| case CS_OP_NUM: |
| result->n = arg_eval_num (parse, &arg1); |
| break; |
| case CS_OP_LPAREN: |
| return nerr_raise(NERR_ASSERT, "LPAREN should be handled above"); |
| default: |
| result->n = 0; |
| ne_warn ("Unsupported op %s in eval_expr", expand_token_type(expr->op_type, 1)); |
| break; |
| } |
| } |
| else if (expr->op_type == CS_OP_COMMA) |
| { |
| /* The comma operator, like in C, we return the value of the right |
| * most argument, in this case that's expr1, but we still need to |
| * evaluate the other stuff */ |
| if (expr->next) |
| { |
| err = eval_expr (parse, expr->next, &arg2); |
| #if DEBUG_EXPR_EVAL |
| expand_arg(parse, _depth, "arg2", &arg2); |
| #endif |
| if (err) return nerr_pass(err); |
| if (arg2.alloc) free(arg2.s); |
| } |
| *result = arg1; |
| /* we transfer ownership of the string here.. ugh */ |
| if (arg1.alloc) arg1.alloc = 0; |
| #if DEBUG_EXPR_EVAL |
| expand_arg(parse, _depth, "result", result); |
| _depth--; |
| #endif |
| return STATUS_OK; |
| } |
| else |
| { |
| err = eval_expr (parse, expr->expr2, &arg2); |
| #if DEBUG_EXPR_EVAL |
| expand_arg(parse, _depth, "arg2", &arg2); |
| #endif |
| if (err) return nerr_pass(err); |
| |
| if (expr->op_type == CS_OP_LBRACKET) |
| { |
| /* the bracket op is essentially hdf array lookups, which just |
| * means appending the value of arg2, .0 */ |
| result->op_type = CS_TYPE_VAR; |
| result->alloc = 1; |
| if (arg2.op_type & (CS_TYPE_VAR_NUM | CS_TYPE_NUM)) |
| { |
| long int n2 = arg_eval_num (parse, &arg2); |
| result->s = sprintf_alloc("%s.%ld", arg1.s, n2); |
| if (result->s == NULL) |
| return nerr_raise (NERR_NOMEM, "Unable to allocate memory to concatenate varnames in expression: %s + %ld", arg1.s, n2); |
| } |
| else |
| { |
| char *s2 = arg_eval (parse, &arg2); |
| if (s2 && s2[0]) |
| { |
| result->s = sprintf_alloc("%s.%s", arg1.s, s2); |
| if (result->s == NULL) |
| return nerr_raise (NERR_NOMEM, "Unable to allocate memory to concatenate varnames in expression: %s + %s", arg1.s, s2); |
| } |
| else |
| { |
| /* if s2 doesn't match anything, then the whole thing is empty */ |
| result->s = ""; |
| result->alloc = 0; |
| } |
| } |
| } |
| else if (expr->op_type == CS_OP_DOT) |
| { |
| /* the dot op is essentially extending the hdf name, which just |
| * means appending the string .0 */ |
| result->op_type = CS_TYPE_VAR; |
| result->alloc = 1; |
| if (arg2.op_type & CS_TYPES_VAR) |
| { |
| result->s = sprintf_alloc("%s.%s", arg1.s, arg2.s); |
| if (result->s == NULL) |
| return nerr_raise (NERR_NOMEM, "Unable to allocate memory to concatenate varnames in expression: %s + %s", arg1.s, arg2.s); |
| } |
| else |
| { |
| if (arg2.op_type & CS_TYPE_NUM) |
| { |
| long int n2 = arg_eval_num (parse, &arg2); |
| result->s = sprintf_alloc("%s.%ld", arg1.s, n2); |
| if (result->s == NULL) |
| return nerr_raise (NERR_NOMEM, "Unable to allocate memory to concatenate varnames in expression: %s + %ld", arg1.s, n2); |
| } |
| else |
| { |
| char *s2 = arg_eval (parse, &arg2); |
| if (s2 && s2[0]) |
| { |
| result->s = sprintf_alloc("%s.%s", arg1.s, s2); |
| if (result->s == NULL) |
| return nerr_raise (NERR_NOMEM, "Unable to allocate memory to concatenate varnames in expression: %s + %s", arg1.s, s2); |
| } |
| else |
| { |
| /* if s2 doesn't match anything, then the whole thing is empty */ |
| result->s = ""; |
| result->alloc = 0; |
| } |
| } |
| } |
| } |
| else if (expr->op_type & (CS_OP_AND | CS_OP_OR)) |
| { |
| /* eval as bool */ |
| err = eval_expr_bool (parse, &arg1, &arg2, expr->op_type, result); |
| } |
| else if ((arg1.op_type & (CS_TYPE_NUM | CS_TYPE_VAR_NUM)) || |
| (arg2.op_type & (CS_TYPE_NUM | CS_TYPE_VAR_NUM)) || |
| (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))) |
| { |
| /* eval as num */ |
| err = eval_expr_num(parse, &arg1, &arg2, expr->op_type, result); |
| } |
| else /* eval as string */ |
| { |
| err = eval_expr_string(parse, &arg1, &arg2, expr->op_type, result); |
| } |
| } |
| if (arg1.alloc) free(arg1.s); |
| if (arg2.alloc) free(arg2.s); |
| } |
| |
| #if DEBUG_EXPR_EVAL |
| expand_arg(parse, _depth, "result", result); |
| _depth--; |
| #endif |
| return STATUS_OK; |
| } |
| |
| static NEOERR *var_eval (CSPARSE *parse, CSTREE *node, CSTREE **next) |
| { |
| NEOERR *err = STATUS_OK; |
| CSARG val; |
| |
| parse->escaping.current = NEOS_ESCAPE_NONE; |
| err = eval_expr(parse, &(node->arg1), &val); |
| if (err) return nerr_pass(err); |
| if (val.op_type & (CS_TYPE_NUM | CS_TYPE_VAR_NUM)) |
| { |
| char buf[256]; |
| long int n_val; |
| |
| n_val = arg_eval_num (parse, &val); |
| snprintf (buf, sizeof(buf), "%ld", n_val); |
| err = parse->output_cb (parse->output_ctx, buf); |
| } |
| else |
| { |
| char *s = arg_eval (parse, &val); |
| /* Determine if the node has been escaped by an explicit function. If not |
| * call to escape. node->escape should contain the default escaping from |
| * Config.VarEscapeMode and parse->escaping.current will have a non-zero |
| * value if an explicit escape call was made sooooo. |
| */ |
| if (s && parse->escaping.current == NEOS_ESCAPE_NONE) /* no explicit escape */ |
| { |
| char *escaped = NULL; |
| /* Use default escape if escape is UNDEF */ |
| if (node->escape == NEOS_ESCAPE_UNDEF) |
| err = neos_var_escape(parse->escaping.when_undef, s, &escaped); |
| else |
| err = neos_var_escape(node->escape, s, &escaped); |
| |
| if (escaped) |
| { |
| err = parse->output_cb (parse->output_ctx, escaped); |
| free(escaped); |
| } |
| } |
| else if (s) |
| { /* already explicitly escaped */ |
| err = parse->output_cb (parse->output_ctx, s); |
| } |
| /* Do we set it to blank if s == NULL? */ |
| } |
| if (val.alloc) free(val.s); |
| |
| *next = node->next; |
| return nerr_pass(err); |
| } |
| |
| static NEOERR *lvar_eval (CSPARSE *parse, CSTREE *node, CSTREE **next) |
| { |
| NEOERR *err = STATUS_OK; |
| CSARG val; |
| |
| err = eval_expr(parse, &(node->arg1), &val); |
| if (err) return nerr_pass(err); |
| if (val.op_type & (CS_TYPE_NUM | CS_TYPE_VAR_NUM)) |
| { |
| char buf[256]; |
| long int n_val; |
| |
| n_val = arg_eval_num (parse, &val); |
| snprintf (buf, sizeof(buf), "%ld", n_val); |
| err = parse->output_cb (parse->output_ctx, buf); |
| } |
| else |
| { |
| char *s = arg_eval (parse, &val); |
| |
| if (s) |
| { |
| CSPARSE *cs = NULL; |
| |
| /* Ok, we need our own copy of the string to pass to |
| * cs_parse_string... */ |
| if (val.alloc && (val.op_type & CS_TYPE_STRING)) { |
| val.alloc = 0; |
| } |
| else |
| { |
| s = strdup(s); |
| if (s == NULL) |
| { |
| return nerr_raise(NERR_NOMEM, "Unable to allocate memory for lvar_eval"); |
| } |
| } |
| |
| do { |
| err = cs_init_internal(&cs, parse->hdf, parse); |
| if (err) break; |
| err = cs_parse_string(cs, s, strlen(s)); |
| if (err) break; |
| err = cs_render(cs, parse->output_ctx, parse->output_cb); |
| if (err) break; |
| } while (0); |
| cs_destroy(&cs); |
| } |
| } |
| if (val.alloc) free(val.s); |
| |
| *next = node->next; |
| return nerr_pass(err); |
| } |
| |
| static NEOERR *linclude_eval (CSPARSE *parse, CSTREE *node, CSTREE **next) |
| { |
| NEOERR *err = STATUS_OK; |
| CSARG val; |
| |
| err = eval_expr(parse, &(node->arg1), &val); |
| if (err) return nerr_pass(err); |
| if (val.op_type & (CS_TYPE_NUM | CS_TYPE_VAR_NUM)) |
| { |
| char buf[256]; |
| long int n_val; |
| |
| n_val = arg_eval_num (parse, &val); |
| snprintf (buf, sizeof(buf), "%ld", n_val); |
| err = parse->output_cb (parse->output_ctx, buf); |
| } |
| else |
| { |
| char *s = arg_eval (parse, &val); |
| |
| if (s) |
| { |
| CSPARSE *cs = NULL; |
| do { |
| err = cs_init_internal(&cs, parse->hdf, parse); |
| if (err) break; |
| err = cs_parse_file(cs, s); |
| if (!(node->flags & CSF_REQUIRED)) |
| { |
| nerr_handle(&err, NERR_NOT_FOUND); |
| } |
| if (err) break; |
| err = cs_render(cs, parse->output_ctx, parse->output_cb); |
| if (err) break; |
| } while (0); |
| cs_destroy(&cs); |
| } |
| } |
| if (val.alloc) free(val.s); |
| |
| *next = node->next; |
| return nerr_pass(err); |
| } |
| |
| /* if the expr evaluates to true, display it, else render the alternate */ |
| static NEOERR *alt_eval (CSPARSE *parse, CSTREE *node, CSTREE **next) |
| { |
| NEOERR *err = STATUS_OK; |
| CSARG val; |
| int eval_true = 1; |
| |
| err = eval_expr(parse, &(node->arg1), &val); |
| if (err) return nerr_pass(err); |
| eval_true = arg_eval_bool(parse, &val); |
| if (eval_true) |
| { |
| if (val.op_type & (CS_TYPE_NUM | CS_TYPE_VAR_NUM)) |
| { |
| char buf[256]; |
| long int n_val; |
| |
| n_val = arg_eval_num (parse, &val); |
| snprintf (buf, sizeof(buf), "%ld", n_val); |
| err = parse->output_cb (parse->output_ctx, buf); |
| } |
| else |
| { |
| char *s = arg_eval (parse, &val); |
| /* Do we set it to blank if s == NULL? */ |
| if (s) |
| { |
| err = parse->output_cb (parse->output_ctx, s); |
| } |
| } |
| } |
| if (val.alloc) free(val.s); |
| |
| if (eval_true == 0) |
| { |
| err = render_node (parse, node->case_0); |
| } |
| |
| *next = node->next; |
| return nerr_pass(err); |
| } |
| |
| /* just calls through to the child nodes */ |
| static NEOERR *escape_eval (CSPARSE *parse, CSTREE *node, CSTREE **next) |
| { |
| NEOERR *err = STATUS_OK; |
| /* TODO(wad): Should I set a eval-time value here? */ |
| err = render_node (parse, node->case_0); |
| *next = node->next; |
| return nerr_pass(err); |
| } |
| |
| |
| static NEOERR *if_eval (CSPARSE *parse, CSTREE *node, CSTREE **next) |
| { |
| NEOERR *err = STATUS_OK; |
| int eval_true = 0; |
| CSARG val; |
| |
| err = eval_expr(parse, &(node->arg1), &val); |
| if (err) return nerr_pass (err); |
| eval_true = arg_eval_bool(parse, &val); |
| if (val.alloc) free(val.s); |
| |
| if (eval_true) |
| { |
| err = render_node (parse, node->case_0); |
| } |
| else if (node->case_1 != NULL) |
| { |
| err = render_node (parse, node->case_1); |
| } |
| *next = node->next; |
| return nerr_pass (err); |
| } |
| |
| static NEOERR *else_parse (CSPARSE *parse, int cmd, char *arg) |
| { |
| NEOERR *err; |
| STACK_ENTRY *entry; |
| |
| /* ne_warn ("else"); */ |
| err = uListGet (parse->stack, -1, (void *)&entry); |
| if (err != STATUS_OK) return nerr_pass(err); |
| |
| parse->next = &(entry->tree->case_1); |
| parse->current = entry->tree; |
| return STATUS_OK; |
| } |
| |
| static NEOERR *elif_parse (CSPARSE *parse, int cmd, char *arg) |
| { |
| NEOERR *err; |
| STACK_ENTRY *entry; |
| |
| /* ne_warn ("elif: %s", arg); */ |
| err = uListGet (parse->stack, -1, (void *)&entry); |
| if (err != STATUS_OK) return nerr_pass(err); |
| |
| if (entry->next_tree == NULL) |
| entry->next_tree = entry->tree; |
| |
| parse->next = &(entry->tree->case_1); |
| |
| err = if_parse(parse, cmd, arg); |
| entry->tree = parse->current; |
| return nerr_pass(err); |
| } |
| |
| static NEOERR *endif_parse (CSPARSE *parse, int cmd, char *arg) |
| { |
| NEOERR *err; |
| STACK_ENTRY *entry; |
| |
| /* ne_warn ("endif"); */ |
| err = uListGet (parse->stack, -1, (void *)&entry); |
| if (err != STATUS_OK) return nerr_pass(err); |
| |
| if (entry->next_tree) |
| parse->next = &(entry->next_tree->next); |
| else |
| parse->next = &(entry->tree->next); |
| parse->current = entry->tree; |
| return STATUS_OK; |
| } |
| |
| static NEOERR *each_with_parse (CSPARSE *parse, int cmd, char *arg) |
| { |
| NEOERR *err; |
| CSTREE *node; |
| char *lvar; |
| char *p; |
| char tmp[256]; |
| |
| err = alloc_node (&node, parse); |
| if (err) return nerr_pass(err); |
| node->cmd = cmd; |
| if (arg[0] == '!') |
| node->flags |= CSF_REQUIRED; |
| arg++; |
| |
| p = lvar = neos_strip(arg); |
| while (*p && !isspace(*p) && *p != '=') p++; |
| if (*p == '\0') |
| { |
| dealloc_node(&node); |
| return nerr_raise (NERR_PARSE, |
| "%s Improperly formatted %s directive: %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), Commands[cmd].cmd, arg); |
| } |
| if (*p != '=') |
| { |
| *p++ = '\0'; |
| while (*p && *p != '=') p++; |
| if (*p == '\0') |
| { |
| dealloc_node(&node); |
| return nerr_raise (NERR_PARSE, |
| "%s Improperly formatted %s directive: %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), Commands[cmd].cmd, arg); |
| } |
| p++; |
| } |
| else |
| { |
| *p++ = '\0'; |
| } |
| while (*p && isspace(*p)) p++; |
| if (*p == '\0') |
| { |
| dealloc_node(&node); |
| return nerr_raise (NERR_PARSE, |
| "%s Improperly formatted %s directive: %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), Commands[cmd].cmd, arg); |
| } |
| node->arg1.op_type = CS_TYPE_VAR; |
| node->arg1.s = lvar; |
| |
| err = parse_expr(parse, p, 0, &(node->arg2)); |
| if (err) |
| { |
| dealloc_node(&node); |
| return nerr_pass(err); |
| } |
| /* ne_warn ("each %s %s", lvar, p); */ |
| |
| *(parse->next) = node; |
| parse->next = &(node->case_0); |
| parse->current = node; |
| |
| return STATUS_OK; |
| } |
| |
| static NEOERR *each_eval (CSPARSE *parse, CSTREE *node, CSTREE **next) |
| { |
| NEOERR *err = STATUS_OK; |
| CS_LOCAL_MAP each_map; |
| CSARG val; |
| HDF *var, *child; |
| |
| memset(&each_map, 0, sizeof(each_map)); |
| |
| err = eval_expr(parse, &(node->arg2), &val); |
| if (err) return nerr_pass(err); |
| |
| if (val.op_type == CS_TYPE_VAR) |
| { |
| var = var_lookup_obj (parse, val.s); |
| |
| if (var != NULL) |
| { |
| /* Init and install local map */ |
| each_map.type = CS_TYPE_VAR; |
| each_map.name = node->arg1.s; |
| each_map.next = parse->locals; |
| each_map.first = 1; |
| each_map.last = 0; |
| parse->locals = &each_map; |
| |
| do |
| { |
| child = hdf_obj_child (var); |
| while (child != NULL) |
| { |
| /* We don't explicitly set each_map.last here since checking |
| * requires a function call, so we move the check to _builtin_last |
| * so it only makes the call if last() is being used */ |
| each_map.h = child; |
| err = render_node (parse, node->case_0); |
| if (each_map.map_alloc) { |
| free(each_map.s); |
| each_map.s = NULL; |
| } |
| if (each_map.first) each_map.first = 0; |
| if (err != STATUS_OK) break; |
| child = hdf_obj_next (child); |
| } |
| |
| } while (0); |
| |
| /* Remove local map */ |
| parse->locals = each_map.next; |
| } |
| } /* else WARNING */ |
| if (val.alloc) free(val.s); |
| |
| *next = node->next; |
| return nerr_pass (err); |
| } |
| |
| static NEOERR *with_eval (CSPARSE *parse, CSTREE *node, CSTREE **next) |
| { |
| NEOERR *err = STATUS_OK; |
| CS_LOCAL_MAP with_map; |
| CSARG val; |
| HDF *var; |
| |
| memset(&with_map, 0, sizeof(with_map)); |
| |
| err = eval_expr(parse, &(node->arg2), &val); |
| if (err) return nerr_pass(err); |
| |
| if (val.op_type == CS_TYPE_VAR) |
| { |
| var = var_lookup_obj (parse, val.s); |
| |
| if (var != NULL) |
| { |
| /* Init and install local map */ |
| with_map.type = CS_TYPE_VAR; |
| with_map.name = node->arg1.s; |
| with_map.next = parse->locals; |
| with_map.h = var; |
| parse->locals = &with_map; |
| err = render_node (parse, node->case_0); |
| /* Remove local map */ |
| if (with_map.map_alloc) free(with_map.s); |
| parse->locals = with_map.next; |
| } |
| } |
| else |
| { |
| /* else WARNING */ |
| ne_warn("Invalid op_type for with: %s", expand_token_type(val.op_type, 1)); |
| } |
| if (val.alloc) free(val.s); |
| |
| *next = node->next; |
| return nerr_pass (err); |
| } |
| static NEOERR *end_parse (CSPARSE *parse, int cmd, char *arg) |
| { |
| NEOERR *err; |
| STACK_ENTRY *entry; |
| |
| err = uListGet (parse->stack, -1, (void *)&entry); |
| if (err != STATUS_OK) return nerr_pass(err); |
| |
| parse->next = &(entry->tree->next); |
| parse->current = entry->tree; |
| return STATUS_OK; |
| } |
| |
| static NEOERR *include_parse (CSPARSE *parse, int cmd, char *arg) |
| { |
| NEOERR *err; |
| char *s; |
| int flags = 0; |
| CSARG arg1, val; |
| |
| memset(&arg1, 0, sizeof(CSARG)); |
| if (arg[0] == '!') |
| flags |= CSF_REQUIRED; |
| arg++; |
| /* Validate arg is a var (regex /^[#" ]$/) */ |
| err = parse_expr (parse, arg, 0, &arg1); |
| if (err) return nerr_pass(err); |
| /* ne_warn ("include: %s", a); */ |
| |
| err = eval_expr(parse, &arg1, &val); |
| if (err) return nerr_pass(err); |
| |
| s = arg_eval (parse, &val); |
| if (s == NULL && !(flags & CSF_REQUIRED)) |
| return STATUS_OK; |
| err = cs_parse_file(parse, s); |
| if (!(flags & CSF_REQUIRED)) |
| { |
| nerr_handle(&err, NERR_NOT_FOUND); |
| } |
| if (val.alloc) free(val.s); |
| |
| return nerr_pass (err); |
| } |
| |
| static NEOERR *def_parse (CSPARSE *parse, int cmd, char *arg) |
| { |
| NEOERR *err; |
| CSTREE *node; |
| CS_MACRO *macro; |
| CSARG *carg, *larg = NULL; |
| char *a = NULL, *p = NULL, *s; |
| char tmp[256]; |
| char name[256]; |
| int x = 0; |
| BOOL last = FALSE; |
| |
| /* Since def doesn't get a new stack entry until after this is run, |
| * setup a dumb var on the parse object to hold the future setting. |
| */ |
| parse->escaping.next_stack = NEOS_ESCAPE_UNDEF; |
| |
| err = alloc_node (&node, parse); |
| if (err) return nerr_pass(err); |
| node->cmd = cmd; |
| arg++; |
| s = arg; |
| while (*s && *s != ' ' && *s != '#' && *s != '(') |
| { |
| name[x++] = *s; |
| s++; |
| } |
| name[x] = '\0'; |
| while (*s && isspace(*s)) s++; |
| if (*s == '\0' || *s != '(') |
| { |
| dealloc_node(&node); |
| return nerr_raise (NERR_PARSE, |
| "%s Missing left paren in macro def %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), arg); |
| } |
| s++; |
| /* Check to see if this is a redefinition */ |
| macro = parse->macros; |
| while (macro != NULL) |
| { |
| if (!strcmp(macro->name, name)) |
| { |
| dealloc_node(&node); |
| return nerr_raise (NERR_PARSE, |
| "%s Duplicate macro def for %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), arg); |
| } |
| macro = macro->next; |
| } |
| |
| macro = (CS_MACRO *) calloc (1, sizeof (CS_MACRO)); |
| if (macro) macro->name = strdup(name); |
| if (macro == NULL || macro->name == NULL) |
| { |
| dealloc_node(&node); |
| dealloc_macro(¯o); |
| return nerr_raise (NERR_NOMEM, |
| "%s Unable to allocate memory for CS_MACRO in def %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), arg); |
| } |
| |
| while (*s) |
| { |
| while (*s && isspace(*s)) s++; |
| a = strpbrk(s, ",)"); |
| if (a == NULL) |
| { |
| err = nerr_raise (NERR_PARSE, |
| "%s Missing right paren in def %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), arg); |
| break; |
| } |
| if (*a == ')') last = TRUE; |
| *a = '\0'; |
| /* cut out ending whitespace */ |
| p = strpbrk(s, " \t\r\n"); |
| if (p != NULL) *p = '\0'; |
| p = strpbrk(s, "\"?<>=!#-+|&,)*/%[]( \t\r\n"); |
| if (p != NULL) |
| { |
| err = nerr_raise (NERR_PARSE, |
| "%s Invalid character in def %s argument: %c", |
| find_context(parse, -1, tmp, sizeof(tmp)), arg, *p); |
| break; |
| } |
| /* No argument case */ |
| if (*s == '\0' && macro->n_args == 0) break; |
| if (*s == '\0') |
| { |
| err = nerr_raise (NERR_PARSE, |
| "%s Missing argument name or extra comma in def %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), arg); |
| break; |
| } |
| carg = (CSARG *) calloc (1, sizeof(CSARG)); |
| if (carg == NULL) |
| { |
| err = nerr_raise (NERR_NOMEM, |
| "%s Unable to allocate memory for CSARG in def %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), arg); |
| break; |
| } |
| if (larg == NULL) |
| { |
| macro->args = carg; |
| larg = carg; |
| } |
| else |
| { |
| larg->next = carg; |
| larg = carg; |
| } |
| macro->n_args++; |
| carg->s = s; |
| if (last == TRUE) break; |
| s = a+1; |
| } |
| if (err) |
| { |
| dealloc_node(&node); |
| dealloc_macro(¯o); |
| return nerr_pass(err); |
| } |
| |
| macro->tree = node; |
| if (parse->macros) |
| { |
| macro->next = parse->macros; |
| } |
| parse->macros = macro; |
| |
| *(parse->next) = node; |
| parse->next = &(node->case_0); |
| parse->current = node; |
| |
| return STATUS_OK; |
| } |
| |
| static int rearrange_for_call(CSARG **args) |
| { |
| CSARG *larg = NULL; |
| CSARG *carg = *args; |
| CSARG *vargs = NULL; |
| int nargs = 0; |
| |
| /* multiple argument case, we have to walk the args and reverse |
| * them. Also handles single arg case since its the same as the |
| * last arg */ |
| while (carg) |
| { |
| nargs++; |
| if (carg->op_type != CS_OP_COMMA) |
| { |
| /* last argument */ |
| if (vargs) |
| carg->next = vargs; |
| vargs = carg; |
| break; |
| } |
| if (vargs) |
| carg->expr1->next = vargs; |
| vargs = carg->expr1; |
| larg = carg; |
| carg = carg->next; |
| /* dealloc comma, but not its descendents */ |
| larg->next = NULL; |
| larg->expr1 = NULL; |
| dealloc_arg(&larg); |
| } |
| *args = vargs; |
| |
| return nargs; |
| } |
| |
| static NEOERR *call_parse (CSPARSE *parse, int cmd, char *arg) |
| { |
| NEOERR *err; |
| CSTREE *node; |
| CS_MACRO *macro; |
| CSARG *carg; |
| char *s, *a = NULL; |
| char tmp[256]; |
| char name[256]; |
| int x = 0; |
| int nargs = 0; |
| STACK_ENTRY *entry; |
| |
| err = uListGet (parse->stack, -1, (void *)&entry); |
| if (err != STATUS_OK) return nerr_pass(err); |
| |
| err = alloc_node (&node, parse); |
| if (err) return nerr_pass(err); |
| node->cmd = cmd; |
| node->escape = entry->escape; |
| arg++; |
| s = arg; |
| while (x < sizeof(name) && *s && *s != ' ' && *s != '#' && *s != '(') |
| { |
| name[x++] = *s; |
| s++; |
| } |
| name[x] = '\0'; |
| while (*s && isspace(*s)) s++; |
| if (*s == '\0' || *s != '(') |
| { |
| dealloc_node(&node); |
| return nerr_raise (NERR_PARSE, |
| "%s Missing left paren in call %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), arg); |
| } |
| s++; |
| /* Check to see if this macro exists */ |
| macro = parse->macros; |
| while (macro != NULL) |
| { |
| if (!strcmp(macro->name, name)) break; |
| macro = macro->next; |
| } |
| if (macro == NULL) |
| { |
| dealloc_node(&node); |
| err = nerr_raise (NERR_PARSE, "%s Undefined macro called: %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), arg); |
| if (parse->audit_mode) { |
| /* Ignore macros that cannot be found */ |
| return _store_error(parse, err); |
| } |
| else { |
| return err; |
| } |
| } |
| node->arg1.op_type = CS_TYPE_MACRO; |
| node->arg1.macro = macro; |
| |
| a = strrchr(s, ')'); |
| if (a == NULL) |
| { |
| dealloc_node(&node); |
| return nerr_raise (NERR_PARSE, |
| "%s Missing right paren in call %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), arg); |
| } |
| *a = '\0'; |
| |
| while (*s && isspace(*s)) s++; |
| /* No arguments case */ |
| if (*s == '\0') |
| { |
| nargs = 0; |
| } |
| else |
| { |
| /* Parse arguments case */ |
| do |
| { |
| carg = (CSARG *) calloc (1, sizeof(CSARG)); |
| if (carg == NULL) |
| { |
| err = nerr_raise (NERR_NOMEM, |
| "%s Unable to allocate memory for CSARG in call %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), arg); |
| break; |
| } |
| err = parse_expr (parse, s, 0, carg); |
| if (err) break; |
| nargs = rearrange_for_call(&carg); |
| node->vargs = carg; |
| } while (0); |
| } |
| if (!err && nargs != macro->n_args) |
| { |
| err = nerr_raise (NERR_PARSE, |
| "%s Incorrect number of arguments, expected %d, got %d in call to macro %s: %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), macro->n_args, nargs, |
| macro->name, arg); |
| } |
| if (err) |
| { |
| dealloc_node(&node); |
| return nerr_pass(err); |
| } |
| |
| *(parse->next) = node; |
| parse->next = &(node->next); |
| parse->current = node; |
| |
| return STATUS_OK; |
| } |
| |
| static NEOERR *call_eval (CSPARSE *parse, CSTREE *node, CSTREE **next) |
| { |
| NEOERR *err = STATUS_OK; |
| CS_LOCAL_MAP *call_map, *map; |
| CS_MACRO *macro; |
| CSARG *carg, *darg; |
| HDF *var; |
| int x; |
| |
| /* Reset the value of when_undef for the coming call evaluation. |
| * This is only used here so it there's no need to reset its value after |
| * the call. If this call is nested (escape == NEOS_ESCAPE_UNDEF), then |
| * leave the when_undef variable alone. The parent call_eval should have |
| * already defined it. |
| */ |
| if (node->escape != NEOS_ESCAPE_UNDEF) |
| parse->escaping.when_undef = node->escape; |
| |
| macro = node->arg1.macro; |
| if (macro->n_args) |
| { |
| call_map = (CS_LOCAL_MAP *) calloc (macro->n_args, sizeof(CS_LOCAL_MAP)); |
| if (call_map == NULL) |
| return nerr_raise (NERR_NOMEM, |
| "Unable to allocate memory for call_map in call_eval of %s", |
| macro->name); |
| } |
| else |
| { |
| call_map = NULL; |
| } |
| |
| darg = macro->args; |
| carg = node->vargs; |
| |
| for (x = 0; x < macro->n_args; x++) |
| { |
| CSARG val; |
| map = &call_map[x]; |
| if (x) call_map[x-1].next = map; |
| |
| map->name = darg->s; |
| err = eval_expr(parse, carg, &val); |
| if (err) break; |
| if (val.op_type & CS_TYPE_STRING) |
| { |
| map->s = val.s; |
| map->type = val.op_type; |
| map->map_alloc = val.alloc; |
| val.alloc = 0; |
| } |
| else if (val.op_type & CS_TYPE_NUM) |
| { |
| map->n = val.n; |
| map->type = CS_TYPE_NUM; |
| } |
| else if (val.op_type & (CS_TYPE_VAR | CS_TYPE_VAR_NUM)) |
| { |
| CS_LOCAL_MAP *lmap; |
| char *c; |
| lmap = lookup_map (parse, val.s, &c); |
| if (lmap != NULL && (lmap->type != CS_TYPE_VAR && lmap->type != CS_TYPE_VAR_NUM)) |
| { |
| /* if we're referencing a local var which maps to a string or |
| * number... then copy */ |
| if (lmap->type == CS_TYPE_NUM) |
| { |
| map->n = lmap->n; |
| map->type = lmap->type; |
| } |
| else |
| { |
| map->s = lmap->s; |
| map->type = lmap->type; |
| } |
| } |
| else |
| { |
| var = var_lookup_obj (parse, val.s); |
| map->h = var; |
| map->type = CS_TYPE_VAR; |
| /* Bring across the name we're mapping to, in case h doesn't exist and |
| * we need to set it. */ |
| map->s = val.s; |
| map->map_alloc = val.alloc; |
| val.alloc = 0; |
| } |
| } |
| else |
| { |
| ne_warn("Unsupported type %s in call_expr", expand_token_type(val.op_type, 1)); |
| } |
| if (val.alloc) free(val.s); |
| map->next = parse->locals; |
| |
| darg = darg->next; |
| carg = carg->next; |
| } |
| |
| if (err == STATUS_OK) |
| { |
| map = parse->locals; |
| if (macro->n_args) parse->locals = call_map; |
| err = render_node (parse, macro->tree->case_0); |
| parse->locals = map; |
| } |
| for (x = 0; x < macro->n_args; x++) |
| { |
| if (call_map[x].map_alloc) free(call_map[x].s); |
| } |
| if (call_map) free (call_map); |
| |
| *next = node->next; |
| return nerr_pass(err); |
| } |
| |
| static NEOERR *set_parse (CSPARSE *parse, int cmd, char *arg) |
| { |
| NEOERR *err; |
| CSTREE *node; |
| char *s; |
| char tmp[256]; |
| |
| err = alloc_node (&node, parse); |
| if (err) return nerr_pass(err); |
| node->cmd = cmd; |
| arg++; |
| s = arg; |
| while (*s && *s != '=') s++; |
| if (*s == '\0') |
| { |
| dealloc_node(&node); |
| return nerr_raise (NERR_PARSE, |
| "%s Missing equals in set %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), arg); |
| } |
| *s = '\0'; |
| s++; |
| err = parse_expr(parse, arg, 1, &(node->arg1)); |
| if (err) |
| { |
| dealloc_node(&node); |
| return nerr_pass(err); |
| } |
| |
| err = parse_expr(parse, s, 0, &(node->arg2)); |
| if (err) |
| { |
| dealloc_node(&node); |
| return nerr_pass(err); |
| } |
| |
| *(parse->next) = node; |
| parse->next = &(node->next); |
| parse->current = node; |
| |
| return STATUS_OK; |
| } |
| |
| static NEOERR *set_eval (CSPARSE *parse, CSTREE *node, CSTREE **next) |
| { |
| NEOERR *err = STATUS_OK; |
| CSARG val; |
| CSARG set; |
| |
| err = eval_expr(parse, &(node->arg1), &set); |
| if (err) return nerr_pass (err); |
| err = eval_expr(parse, &(node->arg2), &val); |
| if (err) { |
| if (set.alloc) free(set.s); |
| return nerr_pass (err); |
| } |
| |
| if (set.op_type != CS_TYPE_NUM) |
| { |
| /* this allow for a weirdness where set:"foo"="bar" |
| * actually sets the hdf var foo... */ |
| if (val.op_type & (CS_TYPE_NUM | CS_TYPE_VAR_NUM)) |
| { |
| char buf[256]; |
| long int n_val; |
| |
| n_val = arg_eval_num (parse, &val); |
| snprintf (buf, sizeof(buf), "%ld", n_val); |
| if (set.s) |
| { |
| err = var_set_value (parse, set.s, buf); |
| } |
| else |
| { |
| err = nerr_raise(NERR_ASSERT, |
| "lvalue is NULL/empty in attempt to evaluate set to '%s'", buf); |
| } |
| } |
| else |
| { |
| char *s = arg_eval (parse, &val); |
| /* Do we set it to blank if s == NULL? */ |
| if (set.s) |
| { |
| err = var_set_value (parse, set.s, s); |
| } |
| else |
| { |
| err = nerr_raise(NERR_ASSERT, |
| "lvalue is NULL/empty in attempt to evaluate set to '%s'", |
| (s) ? s : ""); |
| } |
| } |
| } /* else WARNING */ |
| if (set.alloc) free(set.s); |
| if (val.alloc) free(val.s); |
| |
| *next = node->next; |
| return nerr_pass (err); |
| } |
| |
| static NEOERR *loop_parse (CSPARSE *parse, int cmd, char *arg) |
| { |
| NEOERR *err; |
| CSTREE *node; |
| CSARG *carg, *larg = NULL; |
| BOOL last = FALSE; |
| char *lvar; |
| char *p, *a; |
| char tmp[256]; |
| int x; |
| |
| err = alloc_node (&node, parse); |
| if (err) return nerr_pass(err); |
| node->cmd = cmd; |
| if (arg[0] == '!') |
| node->flags |= CSF_REQUIRED; |
| arg++; |
| |
| p = lvar = neos_strip(arg); |
| while (*p && !isspace(*p) && *p != '=') p++; |
| if (*p == '\0') |
| { |
| dealloc_node(&node); |
| return nerr_raise (NERR_PARSE, |
| "%s Improperly formatted loop directive: %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), arg); |
| } |
| if (*p != '=') |
| { |
| *p++ = '\0'; |
| while (*p && *p != '=') p++; |
| if (*p == '\0') |
| { |
| dealloc_node(&node); |
| return nerr_raise (NERR_PARSE, |
| "%s Improperly formatted loop directive: %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), arg); |
| } |
| p++; |
| } |
| else |
| { |
| *p++ = '\0'; |
| } |
| while (*p && isspace(*p)) p++; |
| if (*p == '\0') |
| { |
| dealloc_node(&node); |
| return nerr_raise (NERR_PARSE, |
| "%s Improperly formatted loop directive: %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), arg); |
| } |
| node->arg1.op_type = CS_TYPE_VAR; |
| node->arg1.s = lvar; |
| |
| x = 0; |
| while (*p) |
| { |
| carg = (CSARG *) calloc (1, sizeof(CSARG)); |
| if (carg == NULL) |
| { |
| err = nerr_raise (NERR_NOMEM, |
| "%s Unable to allocate memory for CSARG in loop %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), arg); |
| break; |
| } |
| if (larg == NULL) |
| { |
| node->vargs = carg; |
| larg = carg; |
| } |
| else |
| { |
| larg->next = carg; |
| larg = carg; |
| } |
| x++; |
| a = strpbrk(p, ","); |
| if (a == NULL) last = TRUE; |
| else *a = '\0'; |
| err = parse_expr (parse, p, 0, carg); |
| if (err) break; |
| if (last == TRUE) break; |
| p = a+1; |
| } |
| if (!err && ((x < 1) || (x > 3))) |
| { |
| err = nerr_raise (NERR_PARSE, |
| "%s Incorrect number of arguments, expected 1, 2, or 3 got %d in loop: %s", |
| find_context(parse, -1, tmp, sizeof(tmp)), x, arg); |
| } |
| |
| /* ne_warn ("loop %s %s", lvar, p); */ |
| |
| *(parse->next) = node; |
| parse->next = &(node->case_0); |
| parse->current = node; |
| |
| return STATUS_OK; |
| } |
| |
| static NEOERR *loop_eval (CSPARSE *parse, CSTREE *node, CSTREE **next) |
| { |
| NEOERR *err = STATUS_OK; |
| CS_LOCAL_MAP each_map; |
| int var; |
| int start = 0, end = 0, step = 1; |
| int x, iter = 1; |
| CSARG *carg; |
| CSARG val; |
| |
| memset(&each_map, 0, sizeof(each_map)); |
| |
| carg = node->vargs; |
| if (carg == NULL) return nerr_raise (NERR_ASSERT, "No arguments in loop eval?"); |
| err = eval_expr(parse, carg, &val); |
| if (err) return nerr_pass(err); |
| end = arg_eval_num(parse, &val); |
| if (val.alloc) free(val.s); |
| if (carg->next) |
| { |
| start = end; |
| carg = carg->next; |
| err = eval_expr(parse, carg, &val); |
| if (err) return nerr_pass(err); |
| end = arg_eval_num(parse, &val); |
| if (val.alloc) free(val.s); |
| if (carg->next) |
| { |
| carg = carg->next; |
| err = eval_expr(parse, carg, &val); |
| if (err) return nerr_pass(err); |
| step = arg_eval_num(parse, &val); |
| if (val.alloc) free(val.s); |
| } |
| } |
| if (((step < 0) && (start < end)) || |
| ((step > 0) && (end < start))) |
| { |
| iter = 0; |
| } |
| else if (step == 0) |
| { |
| iter = 0; |
| } |
| else |
| { |
| iter = abs((end - start) / step + 1); |
| } |
| |
| if (iter > 0) |
| { |
| /* Init and install local map */ |
| each_map.type = CS_TYPE_NUM; |
| each_map.name = node->arg1.s; |
| each_map.next = parse->locals; |
| each_map.first = 1; |
| parse->locals = &each_map; |
| |
| var = start; |
| for (x = 0, var = start; x < iter; x++, var += step) |
| { |
| if (x == iter - 1) each_map.last = 1; |
| each_map.n = var; |
| err = render_node (parse, node->case_0); |
| if (each_map.map_alloc) { |
| free(each_map.s); |
| each_map.s = NULL; |
| } |
| if (each_map.first) each_map.first = 0; |
| if (err != STATUS_OK) break; |
| } |
| |
| /* Remove local map */ |
| parse->locals = each_map.next; |
| } |
| |
| *next = node->next; |
| return nerr_pass (err); |
| } |
| |
| static NEOERR *skip_eval (CSPARSE *parse, CSTREE *node, CSTREE **next) |
| { |
| *next = node->next; |
| return STATUS_OK; |
| } |
| static NEOERR *render_node (CSPARSE *parse, CSTREE *node) |
| { |
| NEOERR *err = STATUS_OK; |
| |
| while (node != NULL) |
| { |
| /* ne_warn ("%s %08x", Commands[node->cmd].cmd, node); */ |
| err = (*(Commands[node->cmd].eval_handler))(parse, node, &node); |
| if (err) break; |
| } |
| return nerr_pass(err); |
| } |
| |
| NEOERR *cs_render (CSPARSE *parse, void *ctx, CSOUTFUNC cb) |
| { |
| CSTREE *node; |
| |
| if (parse->tree == NULL) |
| return nerr_raise (NERR_ASSERT, "No parse tree exists"); |
| |
| parse->output_ctx = ctx; |
| parse->output_cb = cb; |
| |
| node = parse->tree; |
| return nerr_pass (render_node(parse, node)); |
| } |
| |
| /* **** Functions ******************************************** */ |
| |
| NEOERR *cs_register_function(CSPARSE *parse, const char *funcname, |
| int n_args, CSFUNCTION function) |
| { |
| CS_FUNCTION *csf; |
| |
| /* Should we validate the parseability of the name? */ |
| |
| csf = parse->functions; |
| while (csf != NULL) |
| { |
| if (!strcmp(csf->name, funcname) && csf->function != function) |
| { |
| return nerr_raise(NERR_DUPLICATE, |
| "Attempt to register duplicate function %s", funcname); |
| } |
| csf = csf->next; |
| } |
| csf = (CS_FUNCTION *) calloc (1, sizeof(CS_FUNCTION)); |
| if (csf == NULL) |
| return nerr_raise(NERR_NOMEM, |
| "Unable to allocate memory to register function %s", funcname); |
| csf->name = strdup(funcname); |
| if (csf->name == NULL) |
| { |
| free(csf); |
| return nerr_raise(NERR_NOMEM, |
| "Unable to allocate memory to register function %s", funcname); |
| } |
| csf->function = function; |
| csf->n_args = n_args; |
| csf->escape = NEOS_ESCAPE_NONE; |
| csf->next = parse->functions; |
| parse->functions = csf; |
| |
| return STATUS_OK; |
| } |
| |
| /* This is similar to python's PyArg_ParseTuple, : |
| * s - string (allocated) |
| * i - int |
| * A - arg ptr (maybe later) |
| */ |
| NEOERR * cs_arg_parsev(CSPARSE *parse, CSARG *args, const char *fmt, |
| va_list ap) |
| { |
| NEOERR *err = STATUS_OK; |
| char **s; |
| long int *i; |
| CSARG val; |
| |
| while (*fmt) |
| { |
| memset(&val, 0, sizeof(val)); |
| err = eval_expr(parse, args, &val); |
| if (err) return nerr_pass(err); |
| |
| switch (*fmt) |
| { |
| case 's': |
| s = va_arg(ap, char **); |
| if (s == NULL) |
| { |
| err = nerr_raise(NERR_ASSERT, |
| "Invalid number of arguments in call to cs_arg_parse"); |
| break; |
| } |
| *s = arg_eval_str_alloc(parse, &val); |
| break; |
| case 'i': |
| i = va_arg(ap, long int *); |
| if (i == NULL) |
| { |
| err = nerr_raise(NERR_ASSERT, |
| "Invalid number of arguments in call to cs_arg_parse"); |
| break; |
| } |
| *i = arg_eval_num(parse, &val); |
| break; |
| default: |
| break; |
| } |
| if (err) return nerr_pass(err); |
| fmt++; |
| args = args->next; |
| if (val.alloc) free(val.s); |
| } |
| if (err) return nerr_pass(err); |
| return STATUS_OK; |
| } |
| |
| NEOERR * cs_arg_parse(CSPARSE *parse, CSARG *args, const char *fmt, ...) |
| { |
| NEOERR *err; |
| va_list ap; |
| |
| va_start(ap, fmt); |
| err = cs_arg_parsev(parse, args, fmt, ap); |
| va_end(ap); |
| return nerr_pass(err); |
| } |
| |
| static NEOERR * _builtin_subcount(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result) |
| { |
| NEOERR *err; |
| HDF *obj; |
| int count = 0; |
| CSARG val; |
| |
| memset(&val, 0, sizeof(val)); |
| err = eval_expr(parse, args, &val); |
| if (err) return nerr_pass(err); |
| |
| /* default for non-vars is 0 children */ |
| result->op_type = CS_TYPE_NUM; |
| result->n = 0; |
| |
| if (val.op_type & CS_TYPE_VAR) |
| { |
| obj = var_lookup_obj (parse, val.s); |
| if (obj != NULL) |
| { |
| obj = hdf_obj_child(obj); |
| while (obj != NULL) |
| { |
| count++; |
| obj = hdf_obj_next(obj); |
| } |
| } |
| result->n = count; |
| } |
| if (val.alloc) free(val.s); |
| |
| return STATUS_OK; |
| } |
| |
| static NEOERR * _builtin_str_length(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result) |
| { |
| NEOERR *err; |
| CSARG val; |
| |
| memset(&val, 0, sizeof(val)); |
| err = eval_expr(parse, args, &val); |
| if (err) return nerr_pass(err); |
| |
| /* non var/string objects have 0 length */ |
| result->op_type = CS_TYPE_NUM; |
| result->n = 0; |
| |
| if (val.op_type & (CS_TYPE_VAR | CS_TYPE_STRING)) |
| { |
| char *s = arg_eval(parse, &val); |
| if (s) result->n = strlen(s); |
| } |
| if (val.alloc) free(val.s); |
| return STATUS_OK; |
| } |
| |
| static NEOERR * _builtin_str_crc(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, |
| CSARG *result) |
| { |
| NEOERR *err; |
| CSARG val; |
| |
| memset(&val, 0, sizeof(val)); |
| err = eval_expr(parse, args, &val); |
| if (err) return nerr_pass(err); |
| |
| /* non var/string objects have 0 length */ |
| result->op_type = CS_TYPE_NUM; |
| result->n = 0; |
| |
| if (val.op_type & (CS_TYPE_VAR | CS_TYPE_STRING)) |
| { |
| char *s = arg_eval(parse, &val); |
| if (s) result->n = ne_crc((unsigned char *)s, strlen(s)); |
| } |
| if (val.alloc) free(val.s); |
| return STATUS_OK; |
| } |
| |
| |
| static NEOERR * _builtin_str_find(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result) |
| { |
| NEOERR *err; |
| char *s = NULL; |
| char *substr = NULL; |
| char *pstr = NULL; |
| |
| result->op_type = CS_TYPE_NUM; |
| result->n = -1; |
| |
| err = cs_arg_parse(parse, args, "ss", &s, &substr); |
| if (err) return nerr_pass(err); |
| /* If null arguments, return -1 index */ |
| if (s == NULL || substr == NULL) { |
| if (s) free(s); |
| if (substr) free(substr); |
| return STATUS_OK; |
| } |
| pstr = strstr(s, substr); |
| if (pstr != NULL) { |
| result->n = (pstr - s) / sizeof(char); |
| } |
| free(s); |
| free(substr); |
| return STATUS_OK; |
| } |
| |
| |
| static NEOERR * _builtin_name(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result) |
| { |
| NEOERR *err; |
| HDF *obj; |
| CSARG val; |
| |
| memset(&val, 0, sizeof(val)); |
| err = eval_expr(parse, args, &val); |
| if (err) return nerr_pass(err); |
| |
| result->op_type = CS_TYPE_STRING; |
| result->s = ""; |
| |
| if (val.op_type & CS_TYPE_VAR) |
| { |
| obj = var_lookup_obj (parse, val.s); |
| if (obj != NULL) |
| result->s = hdf_obj_name(obj); |
| } |
| else if (val.op_type & CS_TYPE_STRING) |
| { |
| result->s = val.s; |
| result->alloc = val.alloc; |
| val.alloc = 0; |
| } |
| if (val.alloc) free(val.s); |
| return STATUS_OK; |
| } |
| |
| /* Check to see if a local variable is the first in an each/loop sequence */ |
| static NEOERR * _builtin_first(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, |
| CSARG *result) |
| { |
| NEOERR *err; |
| CS_LOCAL_MAP *map; |
| char *c; |
| CSARG val; |
| |
| memset(&val, 0, sizeof(val)); |
| err = eval_expr(parse, args, &val); |
| if (err) return nerr_pass(err); |
| |
| /* default is "not first" */ |
| result->op_type = CS_TYPE_NUM; |
| result->n = 0; |
| |
| /* Only applies to possible local vars */ |
| if ((val.op_type & CS_TYPE_VAR) && !strchr(val.s, '.')) |
| { |
| map = lookup_map (parse, val.s, &c); |
| if (map && map->first) |
| result->n = 1; |
| } |
| if (val.alloc) free(val.s); |
| return STATUS_OK; |
| } |
| |
| /* Check to see if a local variable is the last in an each/loop sequence */ |
| /* TODO: consider making this work on regular HDF vars */ |
| static NEOERR * _builtin_last(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, |
| CSARG *result) |
| { |
| NEOERR *err; |
| CS_LOCAL_MAP *map; |
| char *c; |
| CSARG val; |
| |
| memset(&val, 0, sizeof(val)); |
| err = eval_expr(parse, args, &val); |
| if (err) return nerr_pass(err); |
| |
| /* default is "not last" */ |
| result->op_type = CS_TYPE_NUM; |
| result->n = 0; |
| |
| /* Only applies to possible local vars */ |
| if ((val.op_type & CS_TYPE_VAR) && !strchr(val.s, '.')) |
| { |
| map = lookup_map (parse, val.s, &c); |
| if (map) { |
| if (map->last) { |
| result->n = 1; |
| } else if (map->type == CS_TYPE_VAR) { |
| if (hdf_obj_next(map->h) == NULL) { |
| result->n = 1; |
| } |
| } |
| } |
| } |
| if (val.alloc) free(val.s); |
| return STATUS_OK; |
| } |
| |
| /* returns the absolute value (ie, positive) of a number */ |
| static NEOERR * _builtin_abs (CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, |
| CSARG *result) |
| { |
| NEOERR *err; |
| int n1 = 0; |
| CSARG val; |
| |
| memset(&val, 0, sizeof(val)); |
| err = eval_expr(parse, args, &val); |
| if (err) return nerr_pass(err); |
| |
| result->op_type = CS_TYPE_NUM; |
| n1 = arg_eval_num(parse, &val); |
| result->n = abs(n1); |
| |
| if (val.alloc) free(val.s); |
| return STATUS_OK; |
| } |
| |
| /* returns the larger or two integers */ |
| static NEOERR * _builtin_max (CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, |
| CSARG *result) |
| { |
| NEOERR *err; |
| long int n1 = 0; |
| long int n2 = 0; |
| |
| result->op_type = CS_TYPE_NUM; |
| result->n = 0; |
| |
| err = cs_arg_parse(parse, args, "ii", &n1, &n2); |
| if (err) return nerr_pass(err); |
| result->n = (n1 > n2) ? n1 : n2; |
| |
| return STATUS_OK; |
| } |
| |
| /* returns the smaller or two integers */ |
| static NEOERR * _builtin_min (CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, |
| CSARG *result) |
| { |
| NEOERR *err; |
| long int n1 = 0; |
| long int n2 = 0; |
| |
| result->op_type = CS_TYPE_NUM; |
| result->n = 0; |
| |
| err = cs_arg_parse(parse, args, "ii", &n1, &n2); |
| if (err) return nerr_pass(err); |
| result->n = (n1 < n2) ? n1 : n2; |
| |
| return STATUS_OK; |
| } |
| |
| static NEOERR * _builtin_str_slice (CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result) |
| { |
| NEOERR *err; |
| char *s = NULL; |
| char *slice; |
| long int b = 0; |
| long int e = 0; |
| size_t len; |
| |
| result->op_type = CS_TYPE_STRING; |
| result->s = ""; |
| |
| err = cs_arg_parse(parse, args, "sii", &s, &b, &e); |
| if (err) return nerr_pass(err); |
| /* If null, return empty string */ |
| if (s == NULL) return STATUS_OK; |
| len = strlen(s); |
| if (b < 0 && e == 0) e = len; |
| if (b < 0) b += len; |
| if (e < 0) e += len; |
| if (e > len) e = len; |
| /* Its the whole string */ |
| if (b == 0 && e == len) |
| { |
| result->s = s; |
| result->alloc = 1; |
| return STATUS_OK; |
| } |
| if (e < b) b = e; |
| if (b == e) |
| { |
| /* If null, return empty string */ |
| free(s); |
| return STATUS_OK; |
| } |
| slice = (char *) malloc (sizeof(char) * (e-b+1)); |
| if (slice == NULL) |
| return nerr_raise(NERR_NOMEM, "Unable to allocate memory for string slice"); |
| strncpy(slice, s + b, e-b); |
| free(s); |
| slice[e-b] = '\0'; |
| |
| result->s = slice; |
| result->alloc = 1; |
| |
| return STATUS_OK; |
| } |
| |
| #ifdef ENABLE_GETTEXT |
| static NEOERR * _builtin_gettext(CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result) |
| { |
| NEOERR *err; |
| char *s; |
| CSARG val; |
| |
| memset(&val, 0, sizeof(val)); |
| err = eval_expr(parse, args, &val); |
| if (err) return nerr_pass(err); |
| |
| result->op_type = CS_TYPE_STRING; |
| result->s = ""; |
| |
| if (val.op_type & (CS_TYPE_VAR | CS_TYPE_STRING)) |
| { |
| s = arg_eval(parse, &val); |
| if (s) |
| { |
| result->s = gettext(s); |
| } |
| } |
| if (val.alloc) free(val.s); |
| return STATUS_OK; |
| } |
| #endif |
| |
| static NEOERR * _str_func_wrapper (CSPARSE *parse, CS_FUNCTION *csf, CSARG *args, CSARG *result) |
| { |
| NEOERR *err; |
| char *s; |
| CSARG val; |
| |
| memset(&val, 0, sizeof(val)); |
| err = eval_expr(parse, args, &val); |
| if (err) return nerr_pass(err); |
| |
| if (val.op_type & (CS_TYPE_VAR | CS_TYPE_STRING)) |
| { |
| result->op_type = CS_TYPE_STRING; |
| result->n = 0; |
| |
| s = arg_eval(parse, &val); |
| if (s) |
| { |
| err = csf->str_func(s, &(result->s)); |
| if (err) return nerr_pass(err); |
| result->alloc = 1; |
| } |
| } |
| else |
| { |
| result->op_type = val.op_type; |
| result->n = val.n; |
| result->s = val.s; |
| result->alloc = val.alloc; |
| val.alloc = 0; |
| } |
| if (val.alloc) free(val.s); |
| return STATUS_OK; |
| } |
| |
| NEOERR *cs_register_strfunc(CSPARSE *parse, char *funcname, CSSTRFUNC str_func) |
| { |
| NEOERR *err; |
| |
| err = cs_register_function(parse, funcname, 1, _str_func_wrapper); |
| if (err) return nerr_pass(err); |
| parse->functions->str_func = str_func; |
| |
| return STATUS_OK; |
| } |
| |
| NEOERR *cs_register_esc_strfunc(CSPARSE *parse, char *funcname, |
| CSSTRFUNC str_func) |
| { |
| NEOERR *err; |
| |
| err = cs_register_strfunc(parse, funcname, str_func); |
| if (err) return nerr_pass(err); |
| parse->functions->escape = NEOS_ESCAPE_FUNCTION; |
| |
| return STATUS_OK; |
| } |
| |
| /* **** CS Initialize/Destroy ************************************ */ |
| NEOERR *cs_init (CSPARSE **parse, HDF *hdf) { |
| return nerr_pass(cs_init_internal(parse, hdf, NULL)); |
| } |
| |
| static NEOERR *cs_init_internal (CSPARSE **parse, HDF *hdf, CSPARSE *parent) |
| { |
| NEOERR *err = STATUS_OK; |
| CSPARSE *my_parse; |
| STACK_ENTRY *entry; |
| char *esc_value; |
| CS_ESCAPE_MODES *esc_cursor; |
| |
| err = nerr_init(); |
| if (err != STATUS_OK) return nerr_pass (err); |
| |
| my_parse = (CSPARSE *) calloc (1, sizeof (CSPARSE)); |
| if (my_parse == NULL) |
| return nerr_raise (NERR_NOMEM, "Unable to allocate memory for CSPARSE"); |
| |
| err = uListInit (&(my_parse->stack), 10, 0); |
| if (err != STATUS_OK) |
| { |
| free(my_parse); |
| return nerr_pass(err); |
| } |
| err = uListInit (&(my_parse->alloc), 10, 0); |
| if (err != STATUS_OK) |
| { |
| free(my_parse); |
| return nerr_pass(err); |
| } |
| err = alloc_node (&(my_parse->tree), my_parse); |
| if (err != STATUS_OK) |
| { |
| cs_destroy (&my_parse); |
| return nerr_pass(err); |
| } |
| my_parse->current = my_parse->tree; |
| my_parse->next = &(my_parse->current->next); |
| |
| entry = (STACK_ENTRY *) calloc (1, sizeof (STACK_ENTRY)); |
| if (entry == NULL) |
| { |
| cs_destroy (&my_parse); |
| return nerr_raise (NERR_NOMEM, |
| "Unable to allocate memory for stack entry"); |
| } |
| entry->state = ST_GLOBAL; |
| entry->tree = my_parse->current; |
| entry->location = 0; |
| entry->escape = NEOS_ESCAPE_NONE; |
| err = uListAppend(my_parse->stack, entry); |
| if (err != STATUS_OK) { |
| free (entry); |
| cs_destroy(&my_parse); |
| return nerr_pass(err); |
| } |
| my_parse->tag = hdf_get_value(hdf, "Config.TagStart", "cs"); |
| my_parse->taglen = strlen(my_parse->tag); |
| my_parse->hdf = hdf; |
| |
| /* Let's set the default escape data */ |
| my_parse->escaping.global_ctx = NEOS_ESCAPE_NONE; |
| my_parse->escaping.next_stack = NEOS_ESCAPE_NONE; |
| my_parse->escaping.when_undef = NEOS_ESCAPE_NONE; |
| |
| /* See CS_ESCAPE_MODES. 0 is "none" */ |
| esc_value = hdf_get_value(hdf, "Config.VarEscapeMode", EscapeModes[0].mode); |
| /* Let's ensure the specified escape mode is valid and proceed */ |
| for (esc_cursor = &EscapeModes[0]; |
| esc_cursor->mode != NULL; |
| esc_cursor++) |
| if (!strcmp(esc_value, esc_cursor->mode)) |
| { |
| my_parse->escaping.global_ctx = esc_cursor->context; |
| my_parse->escaping.next_stack = esc_cursor->context; |
| entry->escape = esc_cursor->context; |
| break; |
| } |
| /* Didn't find an acceptable value we were looking for */ |
| if (esc_cursor->mode == NULL) { |
| cs_destroy (&my_parse); |
| return nerr_raise (NERR_OUTOFRANGE, |
| "Invalid HDF value for Config.VarEscapeMode (none,html,js,url): %s", |
| esc_value); |
| } |
| |
| /* Read configuration value to determine whether to enable audit mode */ |
| my_parse->audit_mode = hdf_get_int_value(hdf, "Config.EnableAuditMode", 0); |
| |
| my_parse->err_list = NULL; |
| |
| if (parent == NULL) |
| { |
| static struct _builtin_functions { |
| const char *name; |
| int nargs; |
| CSFUNCTION function; |
| } Builtins[] = { |
| { "len", 1, _builtin_subcount }, |
| { "subcount", 1, _builtin_subcount }, |
| { "name", 1, _builtin_name }, |
| { "first", 1, _builtin_first }, |
| { "last", 1, _builtin_last }, |
| { "abs", 1, _builtin_abs }, |
| { "max", 2, _builtin_max }, |
| { "min", 2, _builtin_min }, |
| { "string.find", 2, _builtin_str_find }, |
| { "string.slice", 3, _builtin_str_slice }, |
| { "string.length", 1, _builtin_str_length }, |
| { "string.crc", 1, _builtin_str_crc}, |
| #ifdef ENABLE_GETTEXT |
| { "_", 1, _builtin_gettext }, |
| #endif |
| { NULL, 0, NULL }, |
| }; |
| int x = 0; |
| while (Builtins[x].name != NULL) { |
| err = cs_register_function(my_parse, Builtins[x].name, Builtins[x].nargs, |
| Builtins[x].function); |
| if (err) |
| { |
| cs_destroy(&my_parse); |
| return nerr_pass(err); |
| } |
| x++; |
| } |
| /* Set global_hdf to be null */ |
| my_parse->global_hdf = NULL; |
| my_parse->parent = NULL; |
| } |
| else |
| { |
| /* TODO: macros and functions should actually not be duplicated, they |
| * should just be modified in lookup to walk the CS struct hierarchy we're |
| * creating here */ |
| /* BUG: We currently can't copy the macros because they reference the parse |
| * tree, so if this sub-parse tree adds a macro, the macro reference will |
| * persist, but the parse tree it points to will be gone when the sub-parse |
| * is gone. */ |
| my_parse->functions = parent->functions; |
| my_parse->global_hdf = parent->global_hdf; |
| my_parse->fileload = parent->fileload; |
| my_parse->fileload_ctx = parent->fileload_ctx; |
| // This should be safe since locals handling is done entirely local to the |
| // eval functions, not globally by the parse handling. This should |
| // pass the locals down to the new parse context to make locals work with |
| // lvar |
| my_parse->locals = parent->locals; |
| my_parse->parent = parent; |
| |
| /* Copy the audit flag from parent */ |
| my_parse->audit_mode = parent->audit_mode; |
| } |
| |
| *parse = my_parse; |
| return STATUS_OK; |
| } |
| |
| void cs_register_fileload(CSPARSE *parse, void *ctx, CSFILELOAD fileload) { |
| if (parse != NULL) { |
| parse->fileload_ctx = ctx; |
| parse->fileload = fileload; |
| } |
| } |
| |
| void cs_destroy (CSPARSE **parse) |
| { |
| CSPARSE *my_parse = *parse; |
| |
| if (my_parse == NULL) |
| return; |
| |
| uListDestroy (&(my_parse->stack), ULIST_FREE); |
| uListDestroy (&(my_parse->alloc), ULIST_FREE); |
| |
| dealloc_macro(&my_parse->macros); |
| dealloc_node(&(my_parse->tree)); |
| if (my_parse->parent == NULL) { |
| dealloc_function(&(my_parse->functions)); |
| } |
| |
| /* Free list of errors */ |
| if (my_parse->err_list != NULL) { |
| CS_ERROR *ptr; |
| |
| while (my_parse->err_list) { |
| ptr = my_parse->err_list->next; |
| free(my_parse->err_list->err); |
| free(my_parse->err_list); |
| my_parse->err_list = ptr; |
| } |
| } |
| |
| free(my_parse); |
| *parse = NULL; |
| } |
| |
| /* **** CS Debug Dumps ******************************************** */ |
| static NEOERR *dump_node (CSPARSE *parse, CSTREE *node, int depth, void *ctx, |
| CSOUTFUNC cb, char *buf, int blen) |
| { |
| NEOERR *err; |
| |
| while (node != NULL) |
| { |
| snprintf (buf, blen, "%*s %s ", depth, "", Commands[node->cmd].cmd); |
| err = cb (ctx, buf); |
| if (err) return nerr_pass (err); |
| if (node->cmd) |
| { |
| if (node->arg1.op_type) |
| { |
| if (node->arg1.op_type == CS_TYPE_NUM) |
| { |
| snprintf (buf, blen, "%ld ", node->arg1.n); |
| } |
| else if (node->arg1.op_type == CS_TYPE_MACRO) |
| { |
| snprintf (buf, blen, "%s ", node->arg1.macro->name); |
| } |
| else |
| { |
| snprintf (buf, blen, "%s ", node->arg1.s); |
| } |
| err = cb (ctx, buf); |
| if (err) return nerr_pass (err); |
| } |
| if (node->arg2.op_type) |
| { |
| if (node->arg2.op_type == CS_TYPE_NUM) |
| { |
| snprintf (buf, blen, "%ld", node->arg2.n); |
| } |
| else |
| { |
| snprintf (buf, blen, "%s", node->arg2.s); |
| } |
| err = cb (ctx, buf); |
| if (err) return nerr_pass (err); |
| } |
| if (node->vargs) |
| { |
| CSARG *arg; |
| arg = node->vargs; |
| while (arg) |
| { |
| if (arg->op_type == CS_TYPE_NUM) |
| { |
| snprintf (buf, blen, "%ld ", arg->n); |
| } |
| else |
| { |
| snprintf (buf, blen, "%s ", arg->s); |
| } |
| err = cb (ctx, buf); |
| if (err) return nerr_pass (err); |
| arg = arg->next; |
| } |
| } |
| } |
| err = cb (ctx, "\n"); |
| if (err) return nerr_pass (err); |
| if (node->case_0) |
| { |
| snprintf (buf, blen, "%*s %s\n", depth, "", "Case 0"); |
| err = cb (ctx, buf); |
| if (err) return nerr_pass (err); |
| err = dump_node (parse, node->case_0, depth+1, ctx, cb, buf, blen); |
| if (err) return nerr_pass (err); |
| } |
| if (node->case_1) |
| { |
| snprintf (buf, blen, "%*s %s\n", depth, "", "Case 1"); |
| err = cb (ctx, buf); |
| if (err) return nerr_pass (err); |
| err = dump_node (parse, node->case_1, depth+1, ctx, cb, buf, blen); |
| if (err) return nerr_pass (err); |
| } |
| node = node->next; |
| } |
| return STATUS_OK; |
| } |
| |
| NEOERR *cs_dump (CSPARSE *parse, void *ctx, CSOUTFUNC cb) |
| { |
| CSTREE *node; |
| char buf[4096]; |
| |
| if (parse->tree == NULL) |
| return nerr_raise (NERR_ASSERT, "No parse tree exists"); |
| |
| node = parse->tree; |
| return nerr_pass (dump_node (parse, node, 0, ctx, cb, buf, sizeof(buf))); |
| } |
| |
| #if 0 |
| static char *node_name (CSTREE *node) |
| { |
| static char buf[256]; |
| |
| if (node == NULL) |
| snprintf (buf, sizeof(buf), "NULL"); |
| else |
| snprintf (buf, sizeof(buf), "%s_%08x", Commands[node->cmd].cmd, |
| node->node_num); |
| |
| return buf; |
| } |
| |
| static NEOERR *dump_node_pre_c (CSPARSE *parse, CSTREE *node, FILE *fp) |
| { |
| NEOERR *err; |
| |
| while (node != NULL) |
| { |
| fprintf (fp, "CSTREE %s;\n", node_name(node)); |
| if (node->case_0) |
| { |
| err = dump_node_pre_c (parse, node->case_0, fp); |
| if (err != STATUS_OK) nerr_pass (err); |
| } |
| if (node->case_1) |
| { |
| err = dump_node_pre_c (parse, node->case_1, fp); |
| if (err != STATUS_OK) nerr_pass (err); |
| } |
| node = node->next; |
| } |
| return STATUS_OK; |
| } |
| |
| static NEOERR *dump_node_c (CSPARSE *parse, CSTREE *node, FILE *fp) |
| { |
| NEOERR *err; |
| char *s; |
| |
| while (node != NULL) |
| { |
| fprintf (fp, "CSTREE %s =\n\t{%d, %d, %d, ", node_name(node), node->node_num, |
| node->cmd, node->flags); |
| s = repr_string_alloc (node->arg1.s); |
| if (s == NULL) |
| return nerr_raise(NERR_NOMEM, "Unable to allocate space for repr"); |
| fprintf (fp, "\n\t { %d, %s, %ld }, ", node->arg1.op_type, s, node->arg1.n); |
| free(s); |
| s = repr_string_alloc (node->arg2.s); |
| if (s == NULL) |
| return nerr_raise(NERR_NOMEM, "Unable to allocate space for repr"); |
| fprintf (fp, "\n\t { %d, %s, %ld }, ", node->arg2.op_type, s, node->arg2.n); |
| free(s); |
| if (node->case_0) |
| fprintf (fp, "\n\t%d, &%s, ", node->op, node_name(node->case_0)); |
| else |
| fprintf (fp, "\n\t%d, NULL, ", node->op); |
| if (node->case_1) |
| fprintf (fp, "&%s, ", node_name(node->case_1)); |
| else |
| fprintf (fp, "NULL, "); |
| if (node->next) |
| fprintf (fp, "&%s};\n\n", node_name(node->next)); |
| else |
| fprintf (fp, "NULL};\n\n"); |
| if (node->case_0) |
| { |
| err = dump_node_c (parse, node->case_0, fp); |
| if (err != STATUS_OK) nerr_pass (err); |
| } |
| if (node->case_1) |
| { |
| err = dump_node_c (parse, node->case_1, fp); |
| if (err != STATUS_OK) nerr_pass (err); |
| } |
| node = node->next; |
| } |
| return STATUS_OK; |
| } |
| |
| NEOERR *cs_dump_c (CSPARSE *parse, char *path) |
| { |
| CSTREE *node; |
| FILE *fp; |
| NEOERR *err; |
| |
| if (parse->tree == NULL) |
| return nerr_raise (NERR_ASSERT, "No parse tree exists"); |
| |
| fp = fopen(path, "w"); |
| if (fp == NULL) |
| { |
| return nerr_raise (NERR_SYSTEM, |
| "Unable to open file %s for writing: [%d] %s", path, errno, |
| strerror(errno)); |
| } |
| |
| fprintf(fp, "/* Auto-generated file: DO NOT EDIT */\n"); |
| fprintf(fp, "#include <stdlib.h>\n\n"); |
| fprintf(fp, "#include \"cs.h\"\n"); |
| node = parse->tree; |
| err = dump_node_pre_c (parse, node, fp); |
| fprintf(fp, "\n"); |
| err = dump_node_c (parse, node, fp); |
| fclose(fp); |
| return nerr_pass (err); |
| } |
| #endif |