aboutsummaryrefslogtreecommitdiffstats
path: root/eval.c
diff options
context:
space:
mode:
Diffstat (limited to 'eval.c')
-rw-r--r--eval.c3917
1 files changed, 1907 insertions, 2010 deletions
diff --git a/eval.c b/eval.c
index ebf7f4c1..5279f245 100644
--- a/eval.c
+++ b/eval.c
@@ -1,5 +1,5 @@
/*
- * eval.c - gawk parse tree interpreter
+ * eval.c - gawk bytecode interpreter
*/
/*
@@ -25,31 +25,41 @@
#include "awk.h"
-extern double pow P((double x, double y));
-extern double modf P((double x, double *yp));
-extern double fmod P((double x, double y));
-
-static inline void make_scalar P((NODE *tree));
-static int eval_condition P((NODE *tree));
-static NODE *op_assign P((NODE *tree));
-static NODE *func_call P((NODE *tree));
-static NODE *match_op P((NODE *tree));
-static void pop_forloop P((void));
-static inline void pop_all_forloops P((void));
-static void push_forloop P((const char *varname, NODE **elems, size_t nelems));
-static void push_args P((int count, NODE *arglist, NODE **oldstack,
- const char *func_name, char **varnames));
-static inline void pop_fcall_stack P((void));
-static void pop_fcall P((void));
-static int comp_func P((const void *p1, const void *p2));
+extern void after_beginfile(IOBUF **curfile);
+extern double pow(double x, double y);
+extern double modf(double x, double *yp);
+extern double fmod(double x, double y);
+extern NODE **fcall_list;
+extern long fcall_count;
+IOBUF *curfile = NULL; /* current data file */
+int exiting = FALSE;
+
+#ifdef DEBUGGING
+extern int pre_execute(INSTRUCTION **, int inloop);
+extern void post_execute(INSTRUCTION *, int inloop);
+#else
+#define r_interpret interpret
+#endif
+
+/*
+ * Flag which executable this is; done here because eval.c is compiled
+ * differently for each of them.
+ */
+enum exe_mode which_gawk =
+#ifdef PROFILING
+ exe_profiling /* pgawk */
+#else
+# ifdef DEBUGGING
+ exe_debugging /* dgawk */
+# else
+ exe_normal /* normal gawk */
+# endif
+#endif
+ ; /* which_gawk */
#if __GNUC__ < 2
NODE *_t; /* used as a temporary in macros */
#endif
-#ifdef MSDOS
-double _msc51bug; /* to get around a bug in MSC 5.1 */
-#endif
-NODE *ret_node;
int OFSlen;
int ORSlen;
int OFMTidx;
@@ -62,20 +72,6 @@ int CONVFMTidx;
#define INCREMENT(n) /* nothing */
#endif
-/* Macros and variables to save and restore function and loop bindings */
-/*
- * the val variable allows return/continue/break-out-of-context to be
- * caught and diagnosed
- */
-#define PUSH_BINDING(stack, x, val) (memcpy((char *)(stack), (const char *)(x), sizeof(jmp_buf)), val++)
-#define RESTORE_BINDING(stack, x, val) (memcpy((char *)(x), (const char *)(stack), sizeof(jmp_buf)), val--)
-
-static jmp_buf loop_tag; /* always the current binding */
-static int loop_tag_valid = FALSE; /* nonzero when loop_tag valid */
-static int func_tag_valid = FALSE;
-static jmp_buf func_tag;
-extern int exiting, exit_val;
-
/* This rather ugly macro is for VMS C */
#ifdef C
#undef C
@@ -254,113 +250,155 @@ load_casetable(void)
* This table maps node types to strings for debugging.
* KEEP IN SYNC WITH awk.h!!!!
*/
+
static const char *const nodetypes[] = {
"Node_illegal",
- "Node_times",
- "Node_quotient",
- "Node_mod",
- "Node_plus",
- "Node_minus",
- "Node_cond_pair",
- "Node_subscript",
- "Node_concat",
- "Node_exp",
- "Node_preincrement",
- "Node_predecrement",
- "Node_postincrement",
- "Node_postdecrement",
- "Node_unary_minus",
- "Node_field_spec",
- "Node_assign",
- "Node_assign_times",
- "Node_assign_quotient",
- "Node_assign_mod",
- "Node_assign_plus",
- "Node_assign_minus",
- "Node_assign_exp",
- "Node_assign_concat",
- "Node_and",
- "Node_or",
- "Node_equal",
- "Node_notequal",
- "Node_less",
- "Node_greater",
- "Node_leq",
- "Node_geq",
- "Node_match",
- "Node_nomatch",
- "Node_not",
- "Node_rule_list",
- "Node_rule_node",
- "Node_statement_list",
- "Node_switch_body",
- "Node_case_list",
- "Node_if_branches",
- "Node_expression_list",
- "Node_param_list",
- "Node_K_if",
- "Node_K_switch",
- "Node_K_case",
- "Node_K_default",
- "Node_K_while",
- "Node_K_for",
- "Node_K_arrayfor",
- "Node_K_break",
- "Node_K_continue",
- "Node_K_print",
- "Node_K_print_rec",
- "Node_K_printf",
- "Node_K_next",
- "Node_K_exit",
- "Node_K_do",
- "Node_K_return",
- "Node_K_delete",
- "Node_K_delete_loop",
- "Node_K_getline",
- "Node_K_function",
- "Node_K_nextfile",
- "Node_redirect_output",
- "Node_redirect_append",
- "Node_redirect_pipe",
- "Node_redirect_pipein",
- "Node_redirect_input",
- "Node_redirect_twoway",
- "Node_var_new",
- "Node_var",
- "Node_var_array",
"Node_val",
- "Node_builtin",
- "Node_line_range",
- "Node_in_array",
- "Node_func",
- "Node_func_call",
- "Node_indirect_func_call",
- "Node_cond_exp",
"Node_regex",
"Node_dynregex",
+ "Node_var",
+ "Node_var_array",
+ "Node_var_new",
+ "Node_param_list",
+ "Node_func",
"Node_hashnode",
"Node_ahash",
"Node_array_ref",
- "Node_BINMODE",
- "Node_CONVFMT",
- "Node_FIELDWIDTHS",
- "Node_FNR",
- "Node_FPAT",
- "Node_FS",
- "Node_IGNORECASE",
- "Node_LINT",
- "Node_NF",
- "Node_NR",
- "Node_OFMT",
- "Node_OFS",
- "Node_ORS",
- "Node_RS",
- "Node_SUBSEP",
- "Node_TEXTDOMAIN",
+ "Node_arrayfor",
+ "Node_frame",
+ "Node_instruction",
"Node_final --- this should never appear",
NULL
};
+
+/*
+ * This table maps Op codes to strings.
+ * KEEP IN SYNC WITH awk.h!!!!
+ */
+
+static struct optypetab {
+ char *desc;
+ char *operator;
+} optypes[] = {
+ { "Op_illegal", NULL },
+ { "Op_times", " * " },
+ { "Op_times_i", " * " },
+ { "Op_quotient", " / " },
+ { "Op_quotient_i", " / " },
+ { "Op_mod", " % " },
+ { "Op_mod_i", " % " },
+ { "Op_plus", " + " },
+ { "Op_plus_i", " + " },
+ { "Op_minus", " - " },
+ { "Op_minus_i", " - " },
+ { "Op_exp", " ^ " },
+ { "Op_exp_i", " ^ " },
+ { "Op_concat", " " },
+ { "Op_line_range", NULL },
+ { "Op_cond_pair", ", " },
+ { "Op_subscript", "[]" },
+ { "Op_sub_array", "[]" },
+ { "Op_preincrement", "++" },
+ { "Op_predecrement", "--" },
+ { "Op_postincrement", "++" },
+ { "Op_postdecrement", "--" },
+ { "Op_unary_minus", "-" },
+ { "Op_field_spec", "$" },
+ { "Op_not", "! " },
+ { "Op_assign", " = " },
+ { "Op_store_var", " = " },
+ { "Op_store_sub", " = " },
+ { "Op_store_field", " = " },
+ { "Op_assign_times", " *= " },
+ { "Op_assign_quotient", " /= " },
+ { "Op_assign_mod", " %= " },
+ { "Op_assign_plus", " += " },
+ { "Op_assign_minus", " -= " },
+ { "Op_assign_exp", " ^= " },
+ { "Op_assign_concat", " " },
+ { "Op_and", " && " },
+ { "Op_and_final", NULL },
+ { "Op_or", " || " },
+ { "Op_or_final", NULL },
+ { "Op_equal", " == " },
+ { "Op_notequal", " != " },
+ { "Op_less", " < " },
+ { "Op_greater", " > " },
+ { "Op_leq", " <= " },
+ { "Op_geq", " >= " },
+ { "Op_match", " ~ " },
+ { "Op_match_rec", NULL },
+ { "Op_nomatch", " !~ " },
+ { "Op_rule", NULL },
+ { "Op_K_case", "case" },
+ { "Op_K_default", "default" },
+ { "Op_K_break", "break" },
+ { "Op_K_continue", "continue" },
+ { "Op_K_print", "print" },
+ { "Op_K_print_rec", "print" },
+ { "Op_K_printf", "printf" },
+ { "Op_K_next", "next" },
+ { "Op_K_exit", "exit" },
+ { "Op_K_return", "return" },
+ { "Op_K_delete", "delete" },
+ { "Op_K_delete_loop", NULL },
+ { "Op_K_getline_redir", "getline" },
+ { "Op_K_getline", "getline" },
+ { "Op_K_nextfile", "nextfile" },
+ { "Op_builtin", NULL },
+ { "Op_in_array", " in " },
+ { "Op_func_call", NULL },
+ { "Op_indirect_func_call", NULL },
+ { "Op_push", NULL },
+ { "Op_push_i", NULL },
+ { "Op_push_re", NULL },
+ { "Op_push_array", NULL },
+ { "Op_push_param", NULL },
+ { "Op_push_lhs", NULL },
+ { "Op_subscript_lhs", "[]" },
+ { "Op_field_spec_lhs", "$" },
+ { "Op_no_op", NULL },
+ { "Op_pop", NULL },
+ { "Op_jmp", NULL },
+ { "Op_jmp_true", NULL },
+ { "Op_jmp_false", NULL },
+ { "Op_push_loop", NULL },
+ { "Op_pop_loop", NULL },
+ { "Op_get_record", NULL },
+ { "Op_newfile", NULL },
+ { "Op_arrayfor_init", NULL },
+ { "Op_arrayfor_incr", NULL },
+ { "Op_arrayfor_final", NULL },
+ { "Op_var_update", NULL },
+ { "Op_var_assign", NULL },
+ { "Op_field_assign", NULL },
+ { "Op_after_beginfile", NULL },
+ { "Op_after_endfile", NULL },
+ { "Op_ext_func", NULL },
+ { "Op_func", NULL },
+ { "Op_exec_count", NULL },
+ { "Op_breakpoint", NULL },
+ { "Op_lint", NULL },
+ { "Op_atexit", NULL },
+ { "Op_stop", NULL },
+ { "Op_token", NULL },
+ { "Op_symbol", NULL },
+ { "Op_list", NULL },
+ { "Op_case_list", NULL },
+ { "Op_K_do", "do" },
+ { "Op_K_for", "for" },
+ { "Op_K_arrayfor", "for" },
+ { "Op_K_while", "while" },
+ { "Op_K_switch", "switch" },
+ { "Op_K_if", "if" },
+ { "Op_K_else", "else" },
+ { "Op_K_function", "function" },
+ { "Op_cond_exp", NULL },
+ { "Op_final --- this should never appear", NULL },
+ { NULL, NULL },
+};
+
/* nodetype2str --- convert a node type into a printable value */
const char *
@@ -375,6 +413,32 @@ nodetype2str(NODETYPE type)
return buf;
}
+/* opcode2str --- convert a opcode type into a printable value */
+
+const char *
+opcode2str(OPCODE op)
+{
+ if (op >= Op_illegal && op < Op_final)
+ return optypes[(int) op].desc;
+ fatal(_("unknown opcode %d"), (int) op);
+ return NULL;
+}
+
+const char *
+op2str(OPCODE op)
+{
+ if (op >= Op_illegal && op < Op_final) {
+ if (optypes[(int) op].operator != NULL)
+ return optypes[(int) op].operator;
+ else
+ fatal(_("opcode %s not an operator or keyword"),
+ optypes[(int) op].desc);
+ } else
+ fatal(_("unknown opcode %d"), (int) op);
+ return NULL;
+}
+
+
/* flags2str --- make a flags value readable */
const char *
@@ -382,7 +446,6 @@ flags2str(int flagval)
{
static const struct flagtab values[] = {
{ MALLOC, "MALLOC" },
- { TEMP, "TEMP" },
{ PERM, "PERM" },
{ STRING, "STRING" },
{ STRCUR, "STRCUR" },
@@ -393,8 +456,9 @@ flags2str(int flagval)
{ FUNC, "FUNC" },
{ FIELD, "FIELD" },
{ INTLSTR, "INTLSTR" },
+#ifdef WSTRCUR
{ WSTRCUR, "WSTRCUR" },
- { ASSIGNED, "ASSIGNED" },
+#endif
{ 0, NULL },
};
@@ -436,1075 +500,34 @@ genflags2str(int flagval, const struct flagtab *tab)
return buffer;
}
-/*
- * make_scalar --- make sure that tree is a scalar.
- *
- * tree is in a scalar context. If it is a variable, accomplish
- * what's needed; otherwise, do nothing.
- *
- * Notice that nodes of type Node_var_new have undefined value in var_value
- * (a.k.a. lnode)---even though awkgram.y:variable() initializes it,
- * push_args() doesn't. Thus we have to initialize it.
- */
-
-static inline void
-make_scalar(NODE *tree)
-{
- switch (tree->type) {
- case Node_var_array:
- fatal(_("attempt to use array `%s' in a scalar context"),
- array_vname(tree));
-
- case Node_array_ref:
- switch (tree->orig_array->type) {
- case Node_var_array:
- fatal(_("attempt to use array `%s' in a scalar context"),
- array_vname(tree));
- case Node_var_new:
- tree->orig_array->type = Node_var;
- tree->orig_array->var_value = Nnull_string;
- break;
- case Node_var:
- break;
- default:
- cant_happen();
- }
- /* fall through */
- case Node_var_new:
- tree->type = Node_var;
- tree->var_value = Nnull_string;
- default:
- /* shut up GCC */
- break;
- }
-}
-
-/*
- * interpret:
- * Tree is a bunch of rules to run. Returns zero if it hit an exit()
- * statement
- */
-int
-interpret(register NODE *volatile tree)
-{
- jmp_buf volatile loop_tag_stack; /* shallow binding stack for loop_tag */
- static jmp_buf rule_tag; /* tag the rule currently being run, for NEXT
- * and EXIT statements. It is static because
- * there are no nested rules */
- register NODE *volatile t = NULL; /* temporary */
- NODE **volatile lhs; /* lhs == Left Hand Side for assigns, etc */
- NODE *volatile stable_tree;
- int volatile traverse = TRUE; /* True => loop thru tree (Node_rule_list) */
-
- /* avoid false source indications */
- source = NULL;
- sourceline = 0;
-
- if (tree == NULL)
- return 1;
- sourceline = tree->source_line;
- source = tree->source_file;
- switch (tree->type) {
- case Node_rule_node:
- traverse = FALSE; /* False => one for-loop iteration only */
- /* FALL THROUGH */
- case Node_rule_list:
- for (t = tree; t != NULL; t = t->rnode) {
- if (traverse)
- tree = t->lnode;
- sourceline = tree->source_line;
- source = tree->source_file;
- INCREMENT(tree->exec_count);
- switch (setjmp(rule_tag)) {
- case 0: /* normal non-jump */
- /* test pattern, if any */
- if (tree->lnode == NULL ||
- eval_condition(tree->lnode)) {
- /* using the lnode exec_count is kludgey */
- if (tree->lnode != NULL)
- INCREMENT(tree->lnode->exec_count);
- (void) interpret(tree->rnode);
- }
- break;
- case TAG_CONTINUE: /* NEXT statement */
- pop_all_forloops();
- pop_fcall_stack();
- return 1;
- case TAG_BREAK: /* EXIT statement */
- pop_all_forloops();
- pop_fcall_stack();
- return 0;
- default:
- cant_happen();
- }
- if (! traverse) /* case Node_rule_node */
- break; /* don't loop */
- }
- break;
-
- case Node_statement_list:
- for (t = tree; t != NULL; t = t->rnode)
- (void) interpret(t->lnode);
- break;
-
- case Node_K_if:
- INCREMENT(tree->exec_count);
- if (eval_condition(tree->lnode)) {
- INCREMENT(tree->rnode->exec_count);
- (void) interpret(tree->rnode->lnode);
- } else {
- (void) interpret(tree->rnode->rnode);
- }
- break;
-
- case Node_K_switch:
- {
- NODE *switch_value;
- NODE *switch_body;
- NODE *case_list;
- NODE *default_list;
- NODE *case_stmt;
-
- int match_found = FALSE;
-
- PUSH_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
- INCREMENT(tree->exec_count);
- stable_tree = tree;
-
- switch_value = tree_eval(stable_tree->lnode);
- switch_body = stable_tree->rnode;
- case_list = switch_body->lnode;
- default_list = switch_body->rnode;
-
- for (; case_list != NULL; case_list = case_list->rnode) {
- case_stmt = case_list->lnode;
-
- /*
- * Once a match is found, all cases will be processed as they fall through,
- * so continue to execute statements until a break is reached.
- */
- if (! match_found) {
- if (case_stmt->type == Node_K_default)
- ; /* do nothing */
- else if (case_stmt->lnode->type == Node_regex) {
- NODE *t1;
- Regexp *rp;
- /* see comments in match_op() code about this. */
- int kludge_need_start = 0;
-
- t1 = force_string(switch_value);
- rp = re_update(case_stmt->lnode);
-
- if (avoid_dfa(tree, t1->stptr, t1->stlen))
- kludge_need_start = RE_NEED_START;
- match_found = (research(rp, t1->stptr, 0, t1->stlen, kludge_need_start) >= 0);
- if (t1 != switch_value)
- free_temp(t1);
- } else
- match_found = (cmp_nodes(switch_value, case_stmt->lnode) == 0);
- }
-
- /* If a match was found, execute the statements associated with the case. */
- if (match_found) {
- INCREMENT(case_stmt->exec_count);
- switch (setjmp(loop_tag)) {
- case 0: /* Normal non-jump */
- (void) interpret(case_stmt->rnode);
- break;
- case TAG_CONTINUE: /* continue statement */
- free_temp(switch_value);
- RESTORE_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
- longjmp(loop_tag, TAG_CONTINUE);
- break;
- case TAG_BREAK: /* break statement */
- free_temp(switch_value);
- RESTORE_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
- return 1;
- default:
- cant_happen();
- }
- }
-
- }
-
- free_temp(switch_value);
-
- /*
- * If a default section was found, execute the statements associated with it
- * and execute any trailing case statements if the default falls through.
- */
- if (! match_found && default_list != NULL) {
- for (case_list = default_list;
- case_list != NULL; case_list = case_list->rnode) {
- case_stmt = case_list->lnode;
-
- INCREMENT(case_stmt->exec_count);
- switch (setjmp(loop_tag)) {
- case 0: /* Normal non-jump */
- (void) interpret(case_stmt->rnode);
- break;
- case TAG_CONTINUE: /* continue statement */
- RESTORE_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
- longjmp(loop_tag, TAG_CONTINUE);
- break;
- case TAG_BREAK: /* break statement */
- RESTORE_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
- return 1;
- default:
- cant_happen();
- }
- }
- }
-
- RESTORE_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
- }
- break;
-
- case Node_K_while:
- PUSH_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
-
- stable_tree = tree;
- while (eval_condition(stable_tree->lnode)) {
- INCREMENT(stable_tree->exec_count);
- switch (setjmp(loop_tag)) {
- case 0: /* normal non-jump */
- (void) interpret(stable_tree->rnode);
- break;
- case TAG_CONTINUE: /* continue statement */
- break;
- case TAG_BREAK: /* break statement */
- RESTORE_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
- return 1;
- default:
- cant_happen();
- }
- }
- RESTORE_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
- break;
-
- case Node_K_do:
- PUSH_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
- stable_tree = tree;
- do {
- INCREMENT(stable_tree->exec_count);
- switch (setjmp(loop_tag)) {
- case 0: /* normal non-jump */
- (void) interpret(stable_tree->rnode);
- break;
- case TAG_CONTINUE: /* continue statement */
- break;
- case TAG_BREAK: /* break statement */
- RESTORE_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
- return 1;
- default:
- cant_happen();
- }
- } while (eval_condition(stable_tree->lnode));
- RESTORE_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
- break;
-
- case Node_K_for:
- PUSH_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
- (void) interpret(tree->forloop->init);
- stable_tree = tree;
- while (eval_condition(stable_tree->forloop->cond)) {
- INCREMENT(stable_tree->exec_count);
- switch (setjmp(loop_tag)) {
- case 0: /* normal non-jump */
- (void) interpret(stable_tree->lnode);
- /* fall through */
- case TAG_CONTINUE: /* continue statement */
- (void) interpret(stable_tree->forloop->incr);
- break;
- case TAG_BREAK: /* break statement */
- RESTORE_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
- return 1;
- default:
- cant_happen();
- }
- }
- RESTORE_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
- break;
-
- case Node_K_arrayfor:
- {
- Func_ptr after_assign = NULL;
- NODE **list = NULL;
- NODE *volatile array;
- NODE *volatile save_array;
- volatile size_t i, num_elems;
- size_t j;
- volatile int retval = 0;
- int sort_indices = whiny_users;
-
-#define hakvar forloop->init
-#define arrvar forloop->incr
- /* get the array */
- save_array = tree->arrvar;
- array = get_array(save_array);
-
- /* sanity: do nothing if empty */
- if (array->var_array == NULL || array->table_size == 0)
- break; /* from switch */
-
- /* allocate space for array */
- num_elems = array->table_size;
- emalloc(list, NODE **, num_elems * sizeof(NODE *), "for_loop");
-
- /* populate it */
- for (i = j = 0; i < array->array_size; i++) {
- NODE *t = array->var_array[i];
-
- if (t == NULL)
- continue;
-
- for (; t != NULL; t = t->ahnext) {
- list[j++] = dupnode(t);
- assert(list[j-1] == t);
- }
- }
-
-
- if (sort_indices)
- qsort(list, num_elems, sizeof(NODE *), comp_func); /* shazzam! */
-
- /* now we can run the loop */
- push_forloop(array->vname, list, num_elems);
- PUSH_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
-
- lhs = get_lhs(tree->hakvar, &after_assign, FALSE);
- stable_tree = tree;
- for (i = 0; i < num_elems; i++) {
- INCREMENT(stable_tree->exec_count);
- unref(*((NODE **) lhs));
- *lhs = make_string(list[i]->ahname_str, list[i]->ahname_len);
- if (after_assign)
- (*after_assign)();
- switch (setjmp(loop_tag)) {
- case 0:
- (void) interpret(stable_tree->lnode);
- case TAG_CONTINUE:
- break;
-
- case TAG_BREAK:
- retval = 1;
- goto done;
-
- default:
- cant_happen();
- }
- }
-
- done:
- RESTORE_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
- pop_forloop();
-
- if (do_lint && num_elems != array->table_size)
- lintwarn(_("for loop: array `%s' changed size from %ld to %ld during loop execution"),
- array_vname(save_array), (long) num_elems, (long) array->table_size);
-
- if (retval == 1)
- return 1;
- break;
- }
-#undef hakvar
-#undef arrvar
-
- case Node_K_break:
- INCREMENT(tree->exec_count);
- if (! loop_tag_valid) {
- /*
- * Old AT&T nawk treats break outside of loops like
- * next. New ones catch it at parse time. Allow it if
- * do_traditional is on, and complain if lint.
- */
- static short warned = FALSE;
-
- if (do_lint && ! warned) {
- lintwarn(_("`break' outside a loop is not portable"));
- warned = TRUE;
- }
- if (! do_traditional || do_posix)
- fatal(_("`break' outside a loop is not allowed"));
- longjmp(rule_tag, TAG_CONTINUE);
- } else
- longjmp(loop_tag, TAG_BREAK);
- break;
-
- case Node_K_continue:
- INCREMENT(tree->exec_count);
- if (! loop_tag_valid) {
- /*
- * Old AT&T nawk treats continue outside of loops like
- * next. New ones catch it at parse time. Allow it if
- * do_traditional is on, and complain if lint.
- */
- static short warned = FALSE;
-
- if (do_lint && ! warned) {
- lintwarn(_("`continue' outside a loop is not portable"));
- warned = TRUE;
- }
- if (! do_traditional || do_posix)
- fatal(_("`continue' outside a loop is not allowed"));
- longjmp(rule_tag, TAG_CONTINUE);
- } else
- longjmp(loop_tag, TAG_CONTINUE);
- break;
-
- case Node_K_print:
- INCREMENT(tree->exec_count);
- do_print(tree);
- break;
-
- case Node_K_print_rec:
- INCREMENT(tree->exec_count);
- do_print_rec(tree);
- break;
-
- case Node_K_printf:
- INCREMENT(tree->exec_count);
- do_printf(tree);
- break;
-
- case Node_K_delete:
- INCREMENT(tree->exec_count);
- do_delete(tree->lnode, tree->rnode);
- break;
-
- case Node_K_delete_loop:
- INCREMENT(tree->exec_count);
- do_delete_loop(tree->lnode, tree->rnode);
- break;
-
- case Node_K_next:
- INCREMENT(tree->exec_count);
- if (in_begin_rule)
- fatal(_("`next' cannot be called from a BEGIN rule"));
- else if (in_end_rule)
- fatal(_("`next' cannot be called from an END rule"));
- else if (in_beginfile_rule)
- fatal(_("`next' cannot be called from a BEGINFILE rule"));
- else if (in_endfile_rule)
- fatal(_("`next' cannot be called from an ENDFILE rule"));
-
- /* could add a lint check here for in a loop or function */
- longjmp(rule_tag, TAG_CONTINUE);
- break;
-
- case Node_K_nextfile:
- INCREMENT(tree->exec_count);
- if (in_begin_rule && ! in_beginfile_rule)
- fatal(_("`nextfile' cannot be called from a BEGIN rule"));
- else if (in_end_rule)
- fatal(_("`nextfile' cannot be called from an END rule"));
- /*
- else if (in_beginfile_rule)
- fatal(_("`nextfile' cannot be called from a BEGINFILE rule"));
- */
- else if (in_endfile_rule)
- fatal(_("`nextfile' cannot be called from an ENDFILE rule"));
-
- /* could add a lint check here for in a loop or function */
- /*
- * Have to do this cleanup here, since we don't longjump
- * back to the main awk rule loop (rule_tag).
- */
- pop_all_forloops();
- pop_fcall_stack();
-
- do_nextfile();
- break;
-
- case Node_K_exit:
- INCREMENT(tree->exec_count);
- /*
- * In A,K,&W, p. 49, it says that an exit statement "...
- * causes the program to behave as if the end of input had
- * occurred; no more input is read, and the END actions, if
- * any are executed." This implies that the rest of the rules
- * are not done. So we immediately break out of the main loop.
- */
- exiting = TRUE;
- if (tree->lnode != NULL) {
- t = tree_eval(tree->lnode);
- exit_val = (int) force_number(t);
-#ifdef VMS
- if (exit_val == 0)
- exit_val = EXIT_SUCCESS;
- else if (exit_val == 1)
- exit_val = EXIT_FAILURE;
- /* else
- just pass anything else on through */
-#endif
- free_temp(t);
- }
- longjmp(rule_tag, TAG_BREAK);
- break;
-
- case Node_K_return:
- INCREMENT(tree->exec_count);
- t = tree_eval(tree->lnode);
- if ((t->flags & (PERM|TEMP)) != 0)
- ret_node = t;
- else {
- ret_node = copynode(t); /* don't do a dupnode here */
- ret_node->flags |= TEMP;
- }
- longjmp(func_tag, TAG_RETURN);
- break;
-
- default:
- /*
- * Appears to be an expression statement. Throw away the
- * value.
- */
- if (do_lint && (tree->type == Node_var || tree->type == Node_var_new))
- lintwarn(_("statement has no effect"));
- INCREMENT(tree->exec_count);
- t = tree_eval(tree);
- if (t) /* stopme() returns NULL */
- free_temp(t);
- break;
- }
- return 1;
-}
-
-/*
- * calc_exp_posint --- calculate x^n for positive integral n,
- * using exponentiation by squaring without recursion.
- */
-
-static AWKNUM
-calc_exp_posint(AWKNUM x, long n)
-{
- AWKNUM mult = 1;
-
- while (n > 1) {
- if ((n % 2) == 1)
- mult *= x;
- x *= x;
- n /= 2;
- }
- return mult * x;
-}
-
-/* calc_exp --- calculate x1^x2 */
-
-AWKNUM
-calc_exp(AWKNUM x1, AWKNUM x2)
-{
- long lx;
-
- if ((lx = x2) == x2) { /* integer exponent */
- if (lx == 0)
- return 1;
- return (lx > 0) ? calc_exp_posint(x1, lx)
- : 1.0 / calc_exp_posint(x1, -lx);
- }
- return (AWKNUM) pow((double) x1, (double) x2);
-}
-
-/* r_tree_eval --- evaluate a subtree */
-
-NODE *
-r_tree_eval(register NODE *tree, int iscond)
-{
- register NODE *r, *t1, *t2; /* return value & temporary subtrees */
- register NODE **lhs;
- register int di;
- AWKNUM x, x1, x2;
-#ifdef _CRAY
- long lx2;
-#endif
-
-#ifndef TREE_EVAL_MACRO
- if (tree == NULL)
- cant_happen();
- if (tree->type == Node_val) {
- if (tree->stref <= 0)
- cant_happen();
- return ((tree->flags & INTLSTR) != 0
- ? r_force_string(tree)
- : tree);
- } else if (tree->type == Node_var) {
- if (tree->var_value->stref <= 0)
- cant_happen();
- if (! var_uninitialized(tree))
- return tree->var_value;
- }
-#endif
-
- if (tree->type == Node_param_list) {
- if ((tree->flags & FUNC) != 0)
- fatal(_("can't use function name `%s' as variable or array"),
- tree->vname);
-
- tree = stack_ptr[tree->param_cnt];
-
- if (tree == NULL) {
- if (do_lint)
- lintwarn(_("reference to uninitialized argument `%s'"),
- tree->vname);
- return Nnull_string;
- }
-
- if (do_lint && var_uninitialized(tree))
- lintwarn(_("reference to uninitialized argument `%s'"),
- tree->vname);
- }
-
- make_scalar(tree);
-
- switch (tree->type) {
- case Node_var:
- if (do_lint && var_uninitialized(tree))
- lintwarn(_("reference to uninitialized variable `%s'"),
- tree->vname);
- return tree->var_value;
-
- case Node_and:
- return tmp_number((AWKNUM) (eval_condition(tree->lnode)
- && eval_condition(tree->rnode)));
-
- case Node_or:
- return tmp_number((AWKNUM) (eval_condition(tree->lnode)
- || eval_condition(tree->rnode)));
-
- case Node_not:
- return tmp_number((AWKNUM) ! eval_condition(tree->lnode));
-
- /* Builtins */
- case Node_builtin:
- return (*tree->builtin)(tree->subnode);
-
- case Node_K_getline:
- return do_getline(tree);
-
- case Node_in_array:
- return tmp_number((AWKNUM) (in_array(tree->lnode, tree->rnode) != NULL));
-
- case Node_indirect_func_call:
- case Node_func_call:
- return func_call(tree);
-
- /* unary operations */
- case Node_NR:
- case Node_FNR:
- case Node_NF:
- case Node_FIELDWIDTHS:
- case Node_FPAT:
- case Node_FS:
- case Node_RS:
- case Node_field_spec:
- case Node_subscript:
- case Node_IGNORECASE:
- case Node_OFS:
- case Node_ORS:
- case Node_OFMT:
- case Node_CONVFMT:
- case Node_BINMODE:
- case Node_LINT:
- case Node_SUBSEP:
- case Node_TEXTDOMAIN:
- lhs = get_lhs(tree, (Func_ptr *) NULL, TRUE);
- return *lhs;
-
- case Node_unary_minus:
- t1 = tree_eval(tree->subnode);
- x = -force_number(t1);
- free_temp(t1);
- return tmp_number(x);
-
- case Node_cond_exp:
- if (eval_condition(tree->lnode))
- return tree_eval(tree->rnode->lnode);
- return tree_eval(tree->rnode->rnode);
-
- case Node_match:
- case Node_nomatch:
- case Node_regex:
- case Node_dynregex:
- return match_op(tree);
-
- case Node_concat:
- {
- NODE **treelist;
- NODE **strlist;
- NODE *save_tree;
- register NODE **treep;
- register NODE **strp;
- register size_t len;
- register size_t supposed_len;
- char *str;
- register char *dest;
- int alloc_count, str_count;
- int i;
-
- /*
- * This is an efficiency hack for multiple adjacent string
- * concatenations, to avoid recursion and string copies.
- *
- * Node_concat trees grow downward to the left, so
- * descend to lowest (first) node, accumulating nodes
- * to evaluate to strings as we go.
- */
-
- /*
- * But first, no arbitrary limits. Count the number of
- * nodes and malloc the treelist and strlist arrays.
- * There will be alloc_count + 1 items to concatenate. We
- * also leave room for an extra pointer at the end to
- * use as a sentinel. Thus, start alloc_count at 2.
- */
- save_tree = tree;
- for (alloc_count = 2; tree != NULL && tree->type == Node_concat;
- tree = tree->lnode)
- alloc_count++;
- tree = save_tree;
- emalloc(treelist, NODE **, sizeof(NODE *) * alloc_count, "tree_eval");
- emalloc(strlist, NODE **, sizeof(NODE *) * alloc_count, "tree_eval");
-
- /* Now, here we go. */
- treep = treelist;
- while (tree != NULL && tree->type == Node_concat) {
- *treep++ = tree->rnode;
- tree = tree->lnode;
- }
- *treep = tree;
- /*
- * Now, evaluate to strings in LIFO order, accumulating
- * the string length, so we can do a single malloc at the
- * end.
- *
- * Evaluate the expressions first, then get their
- * lengthes, in case one of the expressions has a
- * side effect that changes one of the others.
- * See test/nasty.awk.
- *
- * dupnode the results a la do_print, to give us
- * more predicable behavior; compare gawk 3.0.6 to
- * nawk/mawk on test/nasty.awk.
- */
- strp = strlist;
- supposed_len = len = 0;
- while (treep >= treelist) {
- NODE *n;
-
- /* Here lies the wumpus's brother. R.I.P. */
- n = force_string(tree_eval(*treep--));
- *strp = dupnode(n);
- free_temp(n);
- supposed_len += (*strp)->stlen;
- strp++;
- }
- *strp = NULL;
-
- str_count = strp - strlist;
- strp = strlist;
- for (i = 0; i < str_count; i++) {
- len += (*strp)->stlen;
- strp++;
- }
- if (do_lint && supposed_len != len)
- lintwarn(_("concatenation: side effects in one expression have changed the length of another!"));
- emalloc(str, char *, len+2, "tree_eval");
- str[len] = str[len+1] = '\0'; /* for good measure */
- dest = str;
- strp = strlist;
- while (*strp != NULL) {
- memcpy(dest, (*strp)->stptr, (*strp)->stlen);
- dest += (*strp)->stlen;
- unref(*strp);
- strp++;
- }
- r = make_str_node(str, len, ALREADY_MALLOCED);
- r->flags |= TEMP;
-
- free(strlist);
- free(treelist);
- }
- return r;
-
- /* assignments */
- case Node_assign_concat:
- {
- Func_ptr after_assign = NULL;
- NODE *l, *r, *t;
-
- /*
- * Note that something lovely like this:
- *
- * BEGIN { a = "a"; a = a (a = "b"); print a }
- *
- * is not defined. It could print `ab' or `bb'.
- * Gawk 3.1.3 prints `ab', so we do that too, simply
- * by evaluating the LHS first. Ugh.
- *
- * Thanks to mary1john@earthlink.net for pointing
- * out this issue.
- */
- lhs = get_lhs(tree->lnode, &after_assign, FALSE);
- *lhs = force_string(*lhs);
- l = *lhs;
-
- /*
- * This is a hack. We temporarily increase the reference count
- * on l in case evaluating r might change the original value
- * of l. We have to be careful about reducing it afterwards.
- * In particular, if the lhs changed during evaluation of the
- * rhs, we have to compensate.
- *
- * See test/nasty.awk.
- */
- t = dupnode(l);
- r = force_string(tree_eval(tree->rnode));
-
- if (l != *lhs) {
- /*
- * Something happened to the original
- * during the evaluation of the rhs.
- */
- unref(*lhs);
- *lhs = l;
- }
- else
- unref(t);
-
- /*
- * Don't clobber string constants!
- *
- * Also check stref; see test/strcat1.awk,
- * the test for l->stref == 1 can't be an
- * assertion.
- *
- * Thanks again to mary1john@earthlink.net for pointing
- * out this issue.
- */
- if (l != r && (l->flags & PERM) == 0 && l->stref == 1) {
- size_t nlen = l->stlen + r->stlen + 2;
-
- erealloc(l->stptr, char *, nlen, "interpret");
- memcpy(l->stptr + l->stlen, r->stptr, r->stlen);
- l->stlen += r->stlen;
- l->stptr[l->stlen] = '\0';
- free_wstr(l);
- } else {
- char *nval;
- size_t nlen = l->stlen + r->stlen + 2;
-
- emalloc(nval, char *, nlen, "interpret");
- memcpy(nval, l->stptr, l->stlen);
- memcpy(nval + l->stlen, r->stptr, r->stlen);
- unref(*lhs);
- *lhs = make_str_node(nval, l->stlen + r->stlen, ALREADY_MALLOCED);
- }
- (*lhs)->flags &= ~(NUMCUR|NUMBER);
- (*lhs)->flags |= ASSIGNED; /* for function pointers */
- free_temp(r);
-
- if (after_assign)
- (*after_assign)();
- return *lhs;
- }
- case Node_assign:
- {
- Func_ptr after_assign = NULL;
-
- if (do_lint && iscond)
- lintwarn(_("assignment used in conditional context"));
- r = tree_eval(tree->rnode);
- lhs = get_lhs(tree->lnode, &after_assign, FALSE);
- assign_val(lhs, r);
-
- if (tree->lnode->type == Node_var)
- tree->lnode->var_value->flags |= ASSIGNED; /* needed in handling of indirect function calls */
-
- if (after_assign)
- (*after_assign)();
- return *lhs;
- }
-
- /* other assignment types are easier because they are numeric */
- case Node_preincrement:
- case Node_predecrement:
- case Node_postincrement:
- case Node_postdecrement:
- case Node_assign_exp:
- case Node_assign_times:
- case Node_assign_quotient:
- case Node_assign_mod:
- case Node_assign_plus:
- case Node_assign_minus:
- return op_assign(tree);
- default:
- break; /* handled below */
- }
-
- /*
- * Evaluate subtrees in order to do binary operation, then keep going.
- * Use dupnode to make sure that these values don't disappear out
- * from under us during recursive subexpression evaluation.
- */
- t1 = dupnode(tree_eval(tree->lnode));
- t2 = dupnode(tree_eval(tree->rnode));
-
- switch (tree->type) {
- case Node_geq:
- case Node_leq:
- case Node_greater:
- case Node_less:
- case Node_notequal:
- case Node_equal:
- di = cmp_nodes(t1, t2);
- unref(t1);
- unref(t2);
- switch (tree->type) {
- case Node_equal:
- return tmp_number((AWKNUM) (di == 0));
- case Node_notequal:
- return tmp_number((AWKNUM) (di != 0));
- case Node_less:
- return tmp_number((AWKNUM) (di < 0));
- case Node_greater:
- return tmp_number((AWKNUM) (di > 0));
- case Node_leq:
- return tmp_number((AWKNUM) (di <= 0));
- case Node_geq:
- return tmp_number((AWKNUM) (di >= 0));
- default:
- cant_happen();
- }
- break;
- default:
- break; /* handled below */
- }
-
- x1 = force_number(t1);
- x2 = force_number(t2);
- unref(t1);
- unref(t2);
- switch (tree->type) {
- case Node_exp:
- return tmp_number(calc_exp(x1, x2));
-
- case Node_times:
- return tmp_number(x1 * x2);
-
- case Node_quotient:
- if (x2 == 0)
- fatal(_("division by zero attempted"));
-#ifdef _CRAY
- /* special case for integer division, put in for Cray */
- lx2 = x2;
- if (lx2 == 0)
- return tmp_number(x1 / x2);
- lx = (long) x1 / lx2;
- if (lx * x2 == x1)
- return tmp_number((AWKNUM) lx);
- else
-#endif
- return tmp_number(x1 / x2);
-
- case Node_mod:
- if (x2 == 0)
- fatal(_("division by zero attempted in `%%'"));
-#ifdef HAVE_FMOD
- return tmp_number(fmod(x1, x2));
-#else /* ! HAVE_FMOD */
- (void) modf(x1 / x2, &x);
- return tmp_number(x1 - x * x2);
-#endif /* ! HAVE_FMOD */
-
- case Node_plus:
- return tmp_number(x1 + x2);
-
- case Node_minus:
- return tmp_number(x1 - x2);
-
- default:
- fatal(_("illegal type (%s) in tree_eval"), nodetype2str(tree->type));
- }
- return (NODE *) 0;
-}
-
-/* eval_condition --- is TREE true or false? Returns 0==false, non-zero==true */
-
-static int
-eval_condition(register NODE *tree)
-{
- register NODE *t1;
- register int ret;
-
- if (tree == NULL) /* Null trees are the easiest kinds */
- return TRUE;
- if (tree->type == Node_line_range) {
- /*
- * Node_line_range is kind of like Node_match, EXCEPT: the
- * lnode field (more properly, the condpair field) is a node
- * of a Node_cond_pair; whether we evaluate the lnode of that
- * node or the rnode depends on the triggered word. More
- * precisely: if we are not yet triggered, we tree_eval the
- * lnode; if that returns true, we set the triggered word.
- * If we are triggered (not ELSE IF, note), we tree_eval the
- * rnode, clear triggered if it succeeds, and perform our
- * action (regardless of success or failure). We want to be
- * able to begin and end on a single input record, so this
- * isn't an ELSE IF, as noted above.
- */
- if (! tree->triggered) {
- if (! eval_condition(tree->condpair->lnode))
- return FALSE;
- else
- tree->triggered = TRUE;
- }
- /* Else we are triggered */
- if (eval_condition(tree->condpair->rnode))
- tree->triggered = FALSE;
- return TRUE;
- }
-
- /*
- * Could just be J.random expression. in which case, null and 0 are
- * false, anything else is true
- */
-
- t1 = m_tree_eval(tree, TRUE);
- if (t1->flags & MAYBE_NUM)
- (void) force_number(t1);
- if (t1->flags & NUMBER)
- ret = (t1->numbr != 0.0);
- else
- ret = (t1->stlen != 0);
- free_temp(t1);
- return ret;
-}
/* cmp_nodes --- compare two nodes, returning negative, 0, positive */
int
-cmp_nodes(register NODE *t1, register NODE *t2)
+cmp_nodes(NODE *t1, NODE *t2)
{
- register int ret;
- register size_t len1, len2;
- register int l;
- int ldiff;
+ int ret = 0;
+ size_t len1, len2;
+ int l, ldiff;
if (t1 == t2)
return 0;
+
if (t1->flags & MAYBE_NUM)
(void) force_number(t1);
if (t2->flags & MAYBE_NUM)
(void) force_number(t2);
if ((t1->flags & NUMBER) && (t2->flags & NUMBER)) {
if (t1->numbr == t2->numbr)
- return 0;
+ ret = 0;
/* don't subtract, in case one or both are infinite */
else if (t1->numbr < t2->numbr)
- return -1;
+ ret = -1;
else
- return 1;
+ ret = 1;
+ return ret;
}
+
(void) force_string(t1);
(void) force_string(t2);
len1 = t1->stlen;
@@ -1519,10 +542,8 @@ cmp_nodes(register NODE *t1, register NODE *t2)
#ifdef MBS_SUPPORT
if (gawk_mb_cur_max > 1) {
- mbstate_t mbs;
- memset(&mbs, 0, sizeof(mbstate_t));
- ret = strncasecmpmbs((const char *) cp1, mbs,
- (const char *) cp2, mbs, l);
+ ret = strncasecmpmbs((const char *) cp1,
+ (const char *) cp2, l);
} else
#endif
/* Could use tolower() here; see discussion above. */
@@ -1533,829 +554,79 @@ cmp_nodes(register NODE *t1, register NODE *t2)
return (ret == 0 ? ldiff : ret);
}
-/* op_assign --- do +=, -=, etc. */
-
-static NODE *
-op_assign(register NODE *tree)
-{
- AWKNUM rval, lval;
- NODE **lhs;
- NODE *tmp;
- Func_ptr after_assign = NULL;
- int post = FALSE;
-
- /*
- * For += etc, do the rhs first, since it can rearrange things,
- * and *then* get the lhs.
- */
- if (tree->rnode != NULL) {
- tmp = tree_eval(tree->rnode);
- rval = force_number(tmp);
- free_temp(tmp);
- } else
- rval = (AWKNUM) 1.0;
-
- lhs = get_lhs(tree->lnode, &after_assign, TRUE);
- lval = force_number(*lhs);
- unref(*lhs);
-
- switch(tree->type) {
- case Node_postincrement:
- post = TRUE;
- /* fall through */
- case Node_preincrement:
- case Node_assign_plus:
- *lhs = make_number(lval + rval);
- break;
-
- case Node_postdecrement:
- post = TRUE;
- /* fall through */
- case Node_predecrement:
- case Node_assign_minus:
- *lhs = make_number(lval - rval);
- break;
-
- case Node_assign_exp:
- *lhs = make_number(calc_exp(lval, rval));
- break;
-
- case Node_assign_times:
- *lhs = make_number(lval * rval);
- break;
-
- case Node_assign_quotient:
- if (rval == (AWKNUM) 0)
- fatal(_("division by zero attempted in `/='"));
- {
-#ifdef _CRAY
- long ltemp;
-
- /* special case for integer division, put in for Cray */
- ltemp = rval;
- if (ltemp == 0) {
- *lhs = make_number(lval / rval);
- break;
- }
- ltemp = (long) lval / ltemp;
- if (ltemp * lval == rval)
- *lhs = make_number((AWKNUM) ltemp);
- else
-#endif /* _CRAY */
- *lhs = make_number(lval / rval);
- }
- break;
-
- case Node_assign_mod:
- if (rval == (AWKNUM) 0)
- fatal(_("division by zero attempted in `%%='"));
-#ifdef HAVE_FMOD
- *lhs = make_number(fmod(lval, rval));
-#else /* ! HAVE_FMOD */
- {
- AWKNUM t1, t2;
-
- (void) modf(lval / rval, &t1);
- t2 = lval - rval * t1;
- *lhs = make_number(t2);
- }
-#endif /* ! HAVE_FMOD */
- break;
-
- default:
- cant_happen();
- }
-
- (*lhs)->flags |= ASSIGNED;
-
- if (after_assign)
- (*after_assign)();
-
- /* for postincrement or postdecrement, return the old value */
- return (post ? tmp_number(lval) : *lhs);
-}
-
-/*
- * Avoiding memory leaks is difficult. In paticular, any of `next',
- * `nextfile', `break' or `continue' (when not in a loop), can longjmp
- * out to the outermost level. This leaks memory if it happens in a
- * called function. It also leaks memory if it happens in a
- * `for (iggy in foo)' loop, since such loops malloc an array of the
- * current array indices to loop over, which provides stability.
- *
- * The following code takes care of these problems. First comes the
- * array-loop management code. This can be a stack of arrays being looped
- * on at any one time. This stack serves for both mainline code and
- * function body code. As each loop starts and finishes, it pushes its
- * info onto this stack and off of it; whether the loop is in a function
- * body or not isn't relevant.
- *
- * Since the list of indices is created using dupnode(), when popping
- * this stack it should be safe to unref() things, and then memory
- * will get finally released when the function call stack is popped.
- * This means that the loop_stack should be popped first upon a `next'.
- */
-
-static struct loop_info {
- const char *varname; /* variable name, for debugging */
- NODE **elems; /* list of indices */
- size_t nelems; /* how many there are */
-} *loop_stack = NULL;
-size_t nloops = 0; /* how many slots there are in the stack */
-size_t nloops_active = 0; /* how many loops are actively stacked */
-
-/* pop_forloop --- pop one for loop off the stack */
-
-static void
-pop_forloop()
-{
- int i, curloop;
- struct loop_info *loop;
-
- assert(nloops_active > 0);
-
- curloop = --nloops_active; /* 0-based indexing */
- loop = & loop_stack[curloop];
-
- for (i = 0; i < loop->nelems; i++)
- unref(loop->elems[i]);
-
- free(loop->elems);
-
- loop->elems = NULL;
- loop->varname = NULL;
- loop->nelems = 0;
-}
-
-/* pop_forloops --- pop the for loops stack all the way */
-
-static inline void
-pop_all_forloops()
-{
- while (nloops_active > 0)
- pop_forloop(); /* decrements nloops_active for us */
-}
-
-/* push_forloop --- add a single for loop to the stack */
+#if defined(PROFILING) || defined(DEBUGGING)
static void
-push_forloop(const char *varname, NODE **elems, size_t nelems)
+push_frame(NODE *f)
{
-#define NLOOPS 4 /* seems like a good guess */
- if (loop_stack == NULL) {
- /* allocate stack, set vars */
- nloops = NLOOPS;
- emalloc(loop_stack, struct loop_info *, nloops * sizeof(struct loop_info),
- "push_forloop");
- } else if (nloops_active == nloops) {
- /* grow stack, set vars */
- nloops *= 2;
- erealloc(loop_stack, struct loop_info *, nloops * sizeof(struct loop_info),
- "push_forloop");
- }
-
- loop_stack[nloops_active].varname = varname;
- loop_stack[nloops_active].elems = elems;
- loop_stack[nloops_active].nelems = nelems;
- nloops_active++;
-}
-
-/*
- * 2/2004:
- * N.B. The code that uses fcalls[] *always* uses indexing.
- * This avoids severe problems in case fcalls gets realloc()'ed
- * during recursive tree_eval()'s or whatever, so that we don't
- * have to carefully reassign pointers into the array. The
- * minor speed gain from using a pointer was offset too much
- * by the hassles to get the code right and commented.
- *
- * Thanks and a tip of the hatlo to Brian Kernighan.
- */
-
-static struct fcall {
- const char *fname; /* function name */
- size_t count; /* how many args */
- NODE *arglist; /* list thereof */
- NODE **prevstack; /* function stack frame of previous function */
- NODE **stack; /* function stack frame of current function */
-} *fcalls = NULL;
-
-static long fcall_list_size = 0;
-static long curfcall = -1;
-
-/*
- * get_curfunc_arg_count --- return number actual parameters
- *
- * This is for use by dynamically loaded C extension functions.
- */
-size_t
-get_curfunc_arg_count(void)
-{
- NODE *argp;
- size_t argc;
-
- assert(curfcall >= 0);
+ static long max_fcall;
- /* count the # of expressions in argument expression list */
- for (argc = 0, argp = fcalls[curfcall].arglist;
- argp != NULL; argp = argp->rnode)
- argc++;
+ /* NB: frame numbering scheme as in GDB. frame_ptr => frame #0. */
- return argc;
-}
-
-/* pop_fcall --- pop off a single function call */
-
-static void
-pop_fcall()
-{
- NODE *n, **sp;
- int count;
-
- assert(curfcall >= 0);
- stack_ptr = fcalls[curfcall].prevstack;
-
- sp = fcalls[curfcall].stack;
-
- for (count = fcalls[curfcall].count; count > 0; count--) {
- n = *sp++;
- /*
- * If, while setting the value of an argument in push_args,
- * the recursively evaluating code exits, this argument
- * could never have been set to a value. So check for NULL,
- * first.
- */
- if (n == NULL)
- continue;
- if (n->type == Node_var) /* local variable */
- unref(n->var_value);
- else if (n->type == Node_var_array) /* local array */
- assoc_clear(n);
- freenode(n);
- }
- if (fcalls[curfcall].stack) {
- free((char *) fcalls[curfcall].stack);
- fcalls[curfcall].stack = NULL;
+ fcall_count++;
+ if (fcall_list == NULL) {
+ max_fcall = 10;
+ emalloc(fcall_list, NODE **, (max_fcall + 1) * sizeof(NODE *), "push_frame");
+ } else if (fcall_count == max_fcall) {
+ max_fcall *= 2;
+ erealloc(fcall_list, NODE **, (max_fcall + 1) * sizeof(NODE *), "push_frame");
}
- curfcall--;
-}
-
-/* pop_fcall_stack --- pop off all function args, don't leak memory */
-static inline void
-pop_fcall_stack()
-{
- while (curfcall >= 0)
- pop_fcall();
+ if (fcall_count > 1)
+ memmove(fcall_list + 2, fcall_list + 1, (fcall_count - 1) * sizeof(NODE *));
+ fcall_list[1] = f;
}
-/* push_args --- push function arguments onto the stack */
-
static void
-push_args(int count,
- NODE *argp,
- NODE **oldstack,
- const char *func_name,
- char **varnames)
+pop_frame()
{
- NODE *arg, *r, **sp;
- int i;
-
- if (fcall_list_size == 0) { /* first time */
- emalloc(fcalls, struct fcall *, 10 * sizeof(struct fcall),
- "push_args");
- fcall_list_size = 10;
- }
-
- if (++curfcall >= fcall_list_size) {
- fcall_list_size *= 2;
- erealloc(fcalls, struct fcall *,
- fcall_list_size * sizeof(struct fcall), "push_args");
- }
-
- if (count > 0) {
- size_t nbytes = count * sizeof(NODE *);
-
- emalloc(fcalls[curfcall].stack, NODE **, nbytes, "push_args");
- memset(fcalls[curfcall].stack, 0, nbytes); /* Make sure these are all NULL pointers. */
- } else
- fcalls[curfcall].stack = NULL;
- fcalls[curfcall].count = count;
- fcalls[curfcall].fname = func_name; /* not used, for debugging, just in case */
- fcalls[curfcall].arglist = argp;
- fcalls[curfcall].prevstack = oldstack;
-
- sp = fcalls[curfcall].stack;
-
- /* for each calling arg. add NODE * on stack */
- for (i = 0; i < count; i++) {
- getnode(r);
- memset(r, 0, sizeof(*r));
- *sp++ = r;
- if (argp == NULL) {
- /* local variable */
- r->type = Node_var_new;
- r->var_value = Nnull_string;
- r->vname = varnames[i];
- r->rnode = NULL;
- continue;
- }
- arg = argp->lnode;
- /* call by reference for arrays; see below also */
- if (arg->type == Node_param_list)
- arg = fcalls[curfcall].prevstack[arg->param_cnt];
-
- if (arg->type == Node_var_array || arg->type == Node_var_new) {
- r->type = Node_array_ref;
- r->orig_array = arg;
- r->prev_array = arg;
- } else if (arg->type == Node_array_ref) {
- *r = *arg;
- r->prev_array = arg;
- } else {
- NODE *n = tree_eval(arg);
-
- r->type = Node_var;
- r->lnode = dupnode(n);
- r->rnode = (NODE *) NULL;
- r->var_value->flags |= ASSIGNED; /* For indirect function calls */
- free_temp(n);
- }
- r->vname = varnames[i];
- argp = argp->rnode;
- }
-
- if (argp != NULL) {
- /* Left over calling args. */
- warning(
- _("function `%s' called with more arguments than declared"),
- func_name);
- /* Evaluate them, they may have side effects: */
- do {
- arg = argp->lnode;
- if (arg->type == Node_param_list)
- arg = fcalls[curfcall].prevstack[arg->param_cnt];
- if (arg->type != Node_var_array &&
- arg->type != Node_array_ref &&
- arg->type != Node_var_new)
- free_temp(tree_eval(arg));
- } while ((argp = argp->rnode) != NULL);
- }
-
- stack_ptr = fcalls[curfcall].stack;
+#ifdef DEBUGGING
+ extern void frame_popped();
+#endif
+ if (fcall_count > 1)
+ memmove(fcall_list + 1, fcall_list + 2, (fcall_count - 1) * sizeof(NODE *));
+ fcall_count--;
+ assert(fcall_count >= 0);
+#ifdef DEBUGGING
+ frame_popped();
+#endif
}
-
-/* func_call --- call a function, call by reference for arrays */
-
-NODE **stack_ptr;
-
-static NODE *
-func_call(NODE *tree)
-{
- register NODE *r;
- NODE *name, *arg_list;
- NODE *f;
- jmp_buf volatile func_tag_stack;
- jmp_buf volatile loop_tag_stack;
- int volatile save_loop_tag_valid = FALSE;
- NODE *save_ret_node;
- extern NODE *ret_node;
- size_t current_nloops_active = 0;
-
- /* tree->rnode is a Node_val giving function name */
- /* tree->lnode is Node_expression_list of calling args. */
- name = tree->rnode;
- arg_list = tree->lnode;
-
- /*
- * After several attempts to both get the semantics right
- * and to avoid duplicate code, here is the cleanest code that
- * does the right thing.
- *
- * Pardon the gotos.
- */
-
- /* First, decide if we can use a cached funcbody */
- if (tree->type == Node_func_call) { /* direct function call */
- if (tree->funcbody != NULL) {
- f = tree->funcbody;
- goto out;
- }
-
- /* Get the function body, cache it */
- f = lookup(name->stptr);
- if (f == NULL)
- fatal(_("function `%s' not defined"), name->stptr);
- else if (f->type != Node_func)
- fatal(_("identifier `%s' is not a function"), name->stptr);
-
- tree->funcbody = f; /* save for next call */
- goto out;
- }
-
- /* Indirect function call */
-
- /* Check for parameters first, since they shadow globals */
- if (curfcall >= 0) {
- int n = fcalls[curfcall].count;
- NODE *parm;
- int i;
- int found = FALSE;
-
- for (i = 0; i < n; i++) {
- parm = fcalls[curfcall].stack[i];
- if (strcmp(parm->vname, name->stptr) == 0) {
- found = TRUE;
- break;
- }
- }
-
- if (! found)
- goto look_for_global_symbol;
-
- f = NULL;
- name = stack_ptr[i];
- if (name->type == Node_var) {
- if ((name->var_value->flags & ASSIGNED) == 0 && tree->funcbody) {
- /* Should be safe to use cached value */
- f = tree->funcbody;
- goto out;
- }
-
- force_string(name->var_value);
- f = lookup(name->var_value->stptr);
- }
-
- if (f != NULL) {
- if (f->type == Node_func) {
- tree->funcbody = f; /* save for next call */
- name->var_value->flags &= ~ASSIGNED;
- goto out;
- }
- }
-
- fatal(_("function parameter `%s' is not a scalar and cannot be used for indirect function call"),
- name->stptr);
- }
-
-look_for_global_symbol:
-
- /* not in a function call, or not a parameter, look it up globally */
- f = lookup(name->stptr);
- if (f != NULL) {
- if (f->type == Node_func) {
- tree->funcbody = f; /* save for next call */
- tree->type = Node_func_call; /* make it a direct call */
- /*
- * This may not be so silly; it allows a unified syntax which is good
- * if someone is generating code. So leave it alone for now.
- */
- if (0 && do_lint)
- lintwarn(_("indirect call of real function `%s' is silly"), name->stptr);
- goto out;
- } else if (f->type == Node_var) {
- char *fname;
- NODE *fvalue = f->var_value;
-
- if ((fvalue->flags & ASSIGNED) == 0 && tree->funcbody) {
- f = tree->funcbody;
- goto out;
- }
-
- force_string(f->var_value);
- fname = f->var_value->stptr;
- f = lookup(f->var_value->stptr);
- if (f != NULL && f->type == Node_func) {
- tree->funcbody = f; /* save for next call */
- fvalue->flags &= ~ASSIGNED;
- goto out;
- }
- else
- fatal(_("function `%s' called indirectly through `%s' does not exist"),
- fname, name->stptr);
- }
- }
-
- fatal(_("identifier `%s' cannot be used for indirect function call"), name->stptr);
-
-out:
-
-#ifdef FUNC_TRACE
- fprintf(stderr, "function `%s' called\n", name->stptr);
+#else /* not PROFILING or DEBUGGING */
+#define push_frame(p) /* nothing */
+#define pop_frame() /* nothing */
#endif
- push_args(f->lnode->param_cnt, arg_list, stack_ptr, name->stptr,
- f->parmlist);
- /*
- * Execute function body, saving context, as a return statement
- * will longjmp back here.
- *
- * Have to save and restore the loop_tag stuff so that a return
- * inside a loop in a function body doesn't scrog any loops going
- * on in the main program. We save the necessary info in variables
- * local to this function so that function nesting works OK.
- * We also only bother to save the loop stuff if we're in a loop
- * when the function is called.
- */
- if (loop_tag_valid) {
- int junk = 0;
-
- save_loop_tag_valid = (volatile int) loop_tag_valid;
- PUSH_BINDING(loop_tag_stack, loop_tag, junk);
- loop_tag_valid = FALSE;
- }
- PUSH_BINDING(func_tag_stack, func_tag, func_tag_valid);
- current_nloops_active = nloops_active;
- save_ret_node = ret_node;
- ret_node = Nnull_string; /* default return value */
- INCREMENT(f->exec_count); /* count function calls */
- if (setjmp(func_tag) == 0)
- (void) interpret(f->rnode);
-
- while (nloops_active > current_nloops_active)
- pop_forloop();
-
- r = ret_node;
- ret_node = (NODE *) save_ret_node;
- RESTORE_BINDING(func_tag_stack, func_tag, func_tag_valid);
- pop_fcall();
-
- /* Restore the loop_tag stuff if necessary. */
- if (save_loop_tag_valid) {
- int junk = 0;
-
- loop_tag_valid = (int) save_loop_tag_valid;
- RESTORE_BINDING(loop_tag_stack, loop_tag, junk);
- }
-
- return r;
-}
#ifdef PROFILING
+
/* dump_fcall_stack --- print a backtrace of the awk function calls */
void
dump_fcall_stack(FILE *fp)
{
- int i;
- if (curfcall < 0)
- return;
+ NODE *f, *func;
+ long i = 0;
+ if (fcall_count == 0)
+ return;
fprintf(fp, _("\n\t# Function Call Stack:\n\n"));
- for (i = curfcall; i >= 0; i--)
- fprintf(fp, "\t# %3d. %s\n", i+1, fcalls[i].fname);
- fprintf(fp, _("\t# -- main --\n"));
-}
-#endif /* PROFILING */
-/*
- * r_get_lhs:
- * This returns a POINTER to a node pointer. get_lhs(ptr) is the current
- * value of the var, or where to store the var's new value
- *
- * For the special variables, don't unref their current value if it's
- * the same as the internal copy; perhaps the current one is used in
- * a concatenation or some other expression somewhere higher up in the
- * call chain. Ouch.
- */
-
-NODE **
-r_get_lhs(register NODE *ptr, Func_ptr *assign, int reference)
-{
- register NODE **aptr = NULL;
- register NODE *n;
+ /* current frame */
+ func = frame_ptr->func_node;
+ fprintf(fp, "\t# %3ld. %s\n", i, func->lnode->param);
- if (assign)
- *assign = NULL; /* for safety */
- if (ptr->type == Node_param_list) {
- if ((ptr->flags & FUNC) != 0)
- fatal(_("can't use function name `%s' as variable or array"), ptr->vname);
- ptr = stack_ptr[ptr->param_cnt];
+ /* outer frames except main */
+ for (i = 1; i < fcall_count; i++) {
+ f = fcall_list[i];
+ func = f->func_node;
+ fprintf(fp, "\t# %3ld. %s\n", i, func->lnode->param);
}
- make_scalar(ptr);
-
- switch (ptr->type) {
- case Node_var:
- if (do_lint && reference && var_uninitialized(ptr))
- lintwarn(_("reference to uninitialized variable `%s'"),
- ptr->vname);
-
- aptr = &(ptr->var_value);
-#ifdef GAWKDEBUG
- if (ptr->var_value->stref <= 0)
- cant_happen();
-#endif
- break;
-
- case Node_FIELDWIDTHS:
- aptr = &(FIELDWIDTHS_node->var_value);
- if (assign != NULL)
- *assign = set_FIELDWIDTHS;
- break;
-
- case Node_RS:
- aptr = &(RS_node->var_value);
- if (assign != NULL)
- *assign = set_RS;
- break;
-
- case Node_FS:
- aptr = &(FS_node->var_value);
- if (assign != NULL)
- *assign = set_FS;
- break;
-
- case Node_FPAT:
- aptr = &(FPAT_node->var_value);
- if (assign != NULL)
- *assign = set_FPAT;
- break;
-
- case Node_FNR:
- if (FNR_node->var_value->numbr != FNR) {
- unref(FNR_node->var_value);
- FNR_node->var_value = make_number((AWKNUM) FNR);
- }
- aptr = &(FNR_node->var_value);
- if (assign != NULL)
- *assign = set_FNR;
- break;
-
- case Node_NR:
- if (NR_node->var_value->numbr != NR) {
- unref(NR_node->var_value);
- NR_node->var_value = make_number((AWKNUM) NR);
- }
- aptr = &(NR_node->var_value);
- if (assign != NULL)
- *assign = set_NR;
- break;
-
- case Node_NF:
- if (NF == -1 || NF_node->var_value->numbr != NF) {
- if (NF == -1)
- (void) get_field(UNLIMITED-1, assign); /* parse record */
- unref(NF_node->var_value);
- NF_node->var_value = make_number((AWKNUM) NF);
- }
- aptr = &(NF_node->var_value);
- if (assign != NULL)
- *assign = set_NF;
- break;
-
- case Node_IGNORECASE:
- aptr = &(IGNORECASE_node->var_value);
- if (assign != NULL)
- *assign = set_IGNORECASE;
- break;
-
- case Node_BINMODE:
- aptr = &(BINMODE_node->var_value);
- if (assign != NULL)
- *assign = set_BINMODE;
- break;
-
- case Node_LINT:
- aptr = &(LINT_node->var_value);
- if (assign != NULL)
- *assign = set_LINT;
- break;
-
- case Node_OFMT:
- aptr = &(OFMT_node->var_value);
- if (assign != NULL)
- *assign = set_OFMT;
- break;
-
- case Node_CONVFMT:
- aptr = &(CONVFMT_node->var_value);
- if (assign != NULL)
- *assign = set_CONVFMT;
- break;
-
- case Node_ORS:
- aptr = &(ORS_node->var_value);
- if (assign != NULL)
- *assign = set_ORS;
- break;
-
- case Node_OFS:
- aptr = &(OFS_node->var_value);
- if (assign != NULL)
- *assign = set_OFS;
- break;
-
- case Node_SUBSEP:
- aptr = &(SUBSEP_node->var_value);
- if (assign != NULL)
- *assign = set_SUBSEP;
- break;
-
- case Node_TEXTDOMAIN:
- aptr = &(TEXTDOMAIN_node->var_value);
- if (assign != NULL)
- *assign = set_TEXTDOMAIN;
- break;
-
- case Node_field_spec:
- {
- int field_num;
-
- n = tree_eval(ptr->lnode);
- if (do_lint) {
- if ((n->flags & NUMBER) == 0) {
- lintwarn(_("attempt to field reference from non-numeric value"));
- if (n->stlen == 0)
- lintwarn(_("attempt to reference from null string"));
- }
- }
- field_num = (int) force_number(n);
- free_temp(n);
- if (field_num < 0)
- fatal(_("attempt to access field %d"), field_num);
- if (field_num == 0 && field0_valid) { /* short circuit */
- aptr = &fields_arr[0];
- if (assign != NULL)
- *assign = reset_record;
- } else
- aptr = get_field(field_num, assign);
- if (do_lint && reference && (*aptr == Null_field || *aptr == Nnull_string))
- lintwarn(_("reference to uninitialized field `$%d'"),
- field_num);
- break;
- }
-
- case Node_subscript:
- n = get_array(ptr->lnode);
- aptr = assoc_lookup(n, concat_exp(ptr->rnode), reference);
- break;
-
- case Node_builtin:
-#if 1
- /* in gawk for a while */
- fatal(_("assignment is not allowed to result of builtin function"));
-#else
- /*
- * This is how Christos at Deshaw did it.
- * Does this buy us anything?
- */
- if (ptr->builtin == NULL)
- fatal(_("assignment is not allowed to result of builtin function"));
- ptr->callresult = (*ptr->builtin)(ptr->subnode);
- aptr = &ptr->callresult;
- break;
-#endif
-
- default:
- fprintf(stderr, "type = %s\n", nodetype2str(ptr->type));
- fflush(stderr);
- cant_happen();
- }
- return aptr;
+ fprintf(fp, "\t# %3ld. -- main --\n", fcall_count);
}
-/* match_op --- do ~ and !~ */
-
-static NODE *
-match_op(register NODE *tree)
-{
- register NODE *t1;
- register Regexp *rp;
- int i;
- int match = TRUE;
- int kludge_need_start = 0; /* FIXME: --- see below */
-
- if (tree->type == Node_nomatch)
- match = FALSE;
- if (tree->type == Node_regex)
- t1 = *get_field(0, (Func_ptr *) 0);
- else {
- t1 = force_string(tree_eval(tree->lnode));
- tree = tree->rnode;
- }
- rp = re_update(tree);
- /*
- * FIXME:
- *
- * Any place where research() is called with a last parameter of
- * zero, we need to use the avoid_dfa test. This appears here and
- * in the code for Node_K_switch.
- *
- * A new or improved dfa that distinguishes beginning/end of
- * string from beginning/end of line will allow us to get rid of
- * this temporary hack.
- *
- * The avoid_dfa() function is in re.c; it is not very smart.
- */
- if (avoid_dfa(tree, t1->stptr, t1->stlen))
- kludge_need_start = RE_NEED_START;
- i = research(rp, t1->stptr, 0, t1->stlen, kludge_need_start);
- i = (i == -1) ^ (match == TRUE);
- free_temp(t1);
- return tmp_number((AWKNUM) i);
-}
+#endif /* PROFILING */
/* set_IGNORECASE --- update IGNORECASE as appropriate */
@@ -2485,8 +756,8 @@ set_ORS()
/* fmt_ok --- is the conversion format a valid one? */
NODE **fmt_list = NULL;
-static int fmt_ok P((NODE *n));
-static int fmt_index P((NODE *n));
+static int fmt_ok(NODE *n);
+static int fmt_index(NODE *n);
static int
fmt_ok(NODE *n)
@@ -2528,7 +799,7 @@ fmt_ok(NODE *n)
static int
fmt_index(NODE *n)
{
- register int ix = 0;
+ int ix = 0;
static int fmt_num = 4;
static int fmt_hiwater = 0;
@@ -2641,27 +912,6 @@ set_TEXTDOMAIN()
*/
}
-/*
- * assign_val --- do mechanics of assignment, for calling from multiple
- * places.
- */
-
-NODE *
-assign_val(NODE **lhs_p, NODE *rhs)
-{
- if (rhs != *lhs_p) {
- /*
- * Since we know that the nodes are different,
- * we can do the unref() before the dupnode().
- */
- unref(*lhs_p);
- *lhs_p = dupnode(rhs);
- if ((*lhs_p)->type != Node_val)
- (*lhs_p)->funcbody = NULL;
- }
- return *lhs_p;
-}
-
/* update_ERRNO_saved --- update the value of ERRNO based on argument */
void
@@ -2686,9 +936,44 @@ update_ERRNO()
update_ERRNO_saved(errno);
}
+/* update_NR --- update the value of NR */
+
+void
+update_NR()
+{
+ if (NR_node->var_value->numbr != NR) {
+ unref(NR_node->var_value);
+ NR_node->var_value = make_number((AWKNUM) NR);
+ }
+}
+
+/* update_NF --- update the value of NF */
+
+void
+update_NF()
+{
+ if (NF == -1 || NF_node->var_value->numbr != NF) {
+ if (NF == -1)
+ (void) get_field(UNLIMITED - 1, NULL); /* parse record */
+ unref(NF_node->var_value);
+ NF_node->var_value = make_number((AWKNUM) NF);
+ }
+}
+
+/* update_FNR --- update the value of FNR */
+
+void
+update_FNR()
+{
+ if (FNR_node->var_value->numbr != FNR) {
+ unref(FNR_node->var_value);
+ FNR_node->var_value = make_number((AWKNUM) FNR);
+ }
+}
+
/* comp_func --- array index comparison function for qsort */
-static int
+int
comp_func(const void *p1, const void *p2)
{
size_t len1, len2;
@@ -2699,10 +984,6 @@ comp_func(const void *p1, const void *p2)
t1 = *((const NODE *const *) p1);
t2 = *((const NODE *const *) p2);
-/*
- t1 = force_string(t1);
- t2 = force_string(t2);
-*/
len1 = t1->ahname_len;
str1 = t1->ahname_str;
@@ -2715,3 +996,1619 @@ comp_func(const void *p1, const void *p2)
return (cmp1 != 0 ? cmp1 :
len1 < len2 ? -1 : (len1 > len2));
}
+
+
+NODE *frame_ptr; /* current frame */
+STACK_ITEM *stack_ptr = NULL;
+STACK_ITEM *stack_bottom;
+STACK_ITEM *stack_top;
+static unsigned long STACK_SIZE = 256; /* initial size of stack */
+int max_args = 0; /* maximum # of arguments to printf, print, sprintf,
+ * or # of array subscripts, or adjacent strings
+ * to be concatenated.
+ */
+NODE **args_array = NULL;
+
+/* grow_stack --- grow the size of runtime stack */
+
+/* N.B. stack_ptr points to the topmost occupied location
+ * on the stack, not the first free location.
+ */
+
+STACK_ITEM *
+grow_stack()
+{
+ if (stack_ptr == NULL) {
+ char *val;
+ if ((val = getenv("STACKSIZE")) != NULL) {
+ if (isdigit(*val)) {
+ unsigned long n = 0;
+ for (; *val && isdigit(*val); val++)
+ n = (n * 10) + *val - '0';
+ if (n >= 1)
+ STACK_SIZE = n;
+ }
+ }
+
+ emalloc(stack_bottom, STACK_ITEM *, STACK_SIZE * sizeof(STACK_ITEM), "grow_stack");
+ stack_ptr = stack_bottom - 1;
+ stack_top = stack_bottom + STACK_SIZE - 1;
+
+ /* initialize frame pointer */
+ getnode(frame_ptr);
+ frame_ptr->type = Node_frame;
+ frame_ptr->stack = NULL;
+ frame_ptr->func_node = NULL; /* in main */
+ frame_ptr->vname = NULL;
+ frame_ptr->loop_count = 0;
+ return stack_ptr;
+ }
+
+ STACK_SIZE *= 2;
+ erealloc(stack_bottom, STACK_ITEM *, STACK_SIZE * sizeof(STACK_ITEM), "grow_stack");
+ stack_top = stack_bottom + STACK_SIZE - 1;
+ stack_ptr = stack_bottom + STACK_SIZE / 2;
+ return stack_ptr;
+}
+
+/*
+ * r_get_lhs:
+ * This returns a POINTER to a node pointer (var's value).
+ * used to store the var's new value.
+ */
+
+NODE **
+r_get_lhs(NODE *n, int reference)
+{
+ int isparam = FALSE;
+
+ if (n->type == Node_param_list) {
+ if ((n->flags & FUNC) != 0)
+ fatal(_("can't use function name `%s' as variable or array"),
+ n->vname);
+ isparam = TRUE;
+ n = GET_PARAM(n->param_cnt);
+ }
+
+ switch (n->type) {
+ case Node_var_array:
+ fatal(_("attempt to use array `%s' in a scalar context"),
+ array_vname(n));
+ case Node_array_ref:
+ if (n->orig_array->type == Node_var_array)
+ fatal(_("attempt to use array `%s' in a scalar context"),
+ array_vname(n));
+ n->orig_array->type = Node_var;
+ n->orig_array->var_value = Nnull_string;
+ /* fall through */
+ case Node_var_new:
+ n->type = Node_var;
+ n->var_value = Nnull_string;
+ break;
+
+ case Node_var:
+ break;
+
+#if 0
+ case Node_builtin:
+ /* in gawk for a while */
+ fatal(_("assignment is not allowed to result of builtin function"));
+#endif
+
+ default:
+ cant_happen();
+ }
+
+ if (do_lint && reference && var_uninitialized(n))
+ lintwarn((isparam ?
+ _("reference to uninitialized argument `%s'") :
+ _("reference to uninitialized variable `%s'")),
+ n->vname);
+ return &n->var_value;
+}
+
+
+/* r_get_field --- get the address of a field node */
+
+static inline NODE **
+r_get_field(NODE *n, Func_ptr *assign, int reference)
+{
+ long field_num;
+ NODE **lhs;
+
+ if (assign)
+ *assign = NULL;
+ if (do_lint) {
+ if ((n->flags & NUMBER) == 0) {
+ lintwarn(_("attempt to field reference from non-numeric value"));
+ if (n->stlen == 0)
+ lintwarn(_("attempt to field reference from null string"));
+ }
+ }
+
+ field_num = (long) force_number(n);
+ if (field_num < 0)
+ fatal(_("attempt to access field %ld"), field_num);
+
+ if (field_num == 0 && field0_valid) { /* short circuit */
+ lhs = &fields_arr[0];
+ if (assign)
+ *assign = reset_record;
+ } else
+ lhs = get_field(field_num, assign);
+ if (do_lint && reference && (*lhs == Null_field || *lhs == Nnull_string))
+ lintwarn(_("reference to uninitialized field `$%ld'"),
+ field_num);
+ return lhs;
+}
+
+
+/*
+ * calc_exp_posint --- calculate x^n for positive integral n,
+ * using exponentiation by squaring without recursion.
+ */
+
+static AWKNUM
+calc_exp_posint(AWKNUM x, long n)
+{
+ AWKNUM mult = 1;
+
+ while (n > 1) {
+ if ((n % 2) == 1)
+ mult *= x;
+ x *= x;
+ n /= 2;
+ }
+ return mult * x;
+}
+
+/* calc_exp --- calculate x1^x2 */
+
+AWKNUM
+calc_exp(AWKNUM x1, AWKNUM x2)
+{
+ long lx;
+
+ if ((lx = x2) == x2) { /* integer exponent */
+ if (lx == 0)
+ return 1;
+ return (lx > 0) ? calc_exp_posint(x1, lx)
+ : 1.0 / calc_exp_posint(x1, -lx);
+ }
+ return (AWKNUM) pow((double) x1, (double) x2);
+}
+
+
+/* setup_frame --- setup new frame for function call */
+
+static void
+setup_frame(INSTRUCTION *pc)
+{
+ NODE *r = NULL;
+ NODE *m;
+ NODE *f;
+ NODE **sp = NULL;
+ char **varnames;
+ int pcount, arg_count, i;
+
+ f = pc->func_body;
+ pcount = f->lnode->param_cnt;
+ varnames = f->parmlist;
+ arg_count = (pc + 1)->expr_count;
+
+ /* check for extra args */
+ if (arg_count > pcount) {
+ warning(
+ _("function `%s' called with more arguments than declared"),
+ f->vname);
+ do {
+ r = POP();
+ if (r->type == Node_val)
+ DEREF(r);
+ } while (--arg_count > pcount);
+ }
+
+ if (pcount > 0) {
+ emalloc(sp, NODE **, pcount * sizeof(NODE *), "setup_frame");
+ memset(sp, 0, pcount * sizeof(NODE *));
+ }
+
+ for (i = 0; i < pcount; i++) {
+ getnode(r);
+ memset(r, 0, sizeof(NODE));
+ sp[i] = r;
+ if (i >= arg_count) {
+ /* local variable */
+ r->type = Node_var_new;
+ r->vname = varnames[i];
+ continue;
+ }
+
+ m = PEEK(arg_count - i - 1); /* arguments in reverse order on runtime stack */
+
+ if (m->type == Node_param_list)
+ m = GET_PARAM(m->param_cnt);
+
+ switch (m->type) {
+ case Node_var_new:
+ case Node_var_array:
+ r->type = Node_array_ref;
+ r->orig_array = r->prev_array = m;
+ break;
+
+ case Node_array_ref:
+ r->type = Node_array_ref;
+ r->orig_array = m->orig_array;
+ r->prev_array = m;
+ break;
+
+ case Node_val:
+ r->type = Node_var;
+ r->var_value = m;
+ break;
+
+ default:
+ cant_happen();
+ }
+ r->vname = varnames[i];
+ }
+ stack_adj(-arg_count); /* adjust stack pointer */
+
+ if (pc->opcode == Op_indirect_func_call) {
+ r = POP(); /* indirect var */
+ DEREF(r);
+ }
+
+ push_frame(frame_ptr);
+
+ /* save current frame in stack */
+ PUSH(frame_ptr);
+ /* setup new frame */
+ getnode(frame_ptr);
+ frame_ptr->type = Node_frame;
+ frame_ptr->stack = sp;
+ frame_ptr->func_node = f;
+ frame_ptr->loop_count = 0;
+ frame_ptr->vname = NULL;
+
+ frame_ptr->reti = (unsigned long) pc; /* on return execute pc->nexti */
+}
+
+
+/* restore_frame --- clean up the stack and update frame */
+
+static INSTRUCTION *
+restore_frame(NODE *fp)
+{
+ NODE *r;
+ NODE **sp;
+ int n;
+ NODE *func;
+ INSTRUCTION *ri;
+
+ func = frame_ptr->func_node;
+ n = func->lnode->param_cnt;
+ sp = frame_ptr->stack;
+
+ for (; n > 0; n--) {
+ r = *sp++;
+ if (r->type == Node_var) /* local variable */
+ DEREF(r->var_value);
+ else if (r->type == Node_var_array) /* local array */
+ assoc_clear(r);
+ freenode(r);
+ }
+ if (frame_ptr->stack != NULL)
+ efree(frame_ptr->stack);
+ ri = (INSTRUCTION *) frame_ptr->reti; /* execution in calling frame
+ * resumes from ri->nexti.
+ */
+ freenode(frame_ptr);
+ pop_frame();
+
+ frame_ptr = fp;
+ return ri->nexti;
+}
+
+
+/* free_arrayfor --- free 'for (var in array)' related data */
+
+static inline void
+free_arrayfor(NODE *r)
+{
+ if (r->var_array != NULL) {
+ size_t num_elems = r->table_size;
+ NODE **list = r->var_array;
+ while (num_elems > 0)
+ ahash_unref(list[--num_elems]);
+ efree(list);
+ }
+ freenode(r);
+}
+
+/* unwind_stack --- pop the runtime stack */
+
+void
+unwind_stack(STACK_ITEM *sp_bottom)
+{
+ NODE *r;
+
+ while (stack_ptr >= sp_bottom) {
+ r = POP();
+ switch (r->type) {
+ case Node_instruction:
+ freenode(r);
+ break;
+
+ case Node_frame:
+ (void) restore_frame(r);
+ source = frame_ptr->vname;
+ break;
+
+ case Node_arrayfor:
+ free_arrayfor(r);
+ break;
+
+ case Node_val:
+ DEREF(r);
+ break;
+
+ default:
+ if (get_context()->level == 0)
+ fatal(_("unwind_stack: unexpected type `%s'"),
+ nodetype2str(r->type));
+ /* else
+ * Node_var_array,
+ * Node_param_list,
+ * Node_var (e.g: trying to use scalar for array)
+ * Node_regex/Node_dynregex
+ * ?
+ */
+ break;
+ }
+ }
+}
+
+
+/*
+ * This generated compiler warnings from GCC 4.4. Who knows why.
+ *
+#define eval_condition(t) (((t)->flags & MAYBE_NUM) && force_number(t), \
+ ((t)->flags & NUMBER) ? ((t)->numbr != 0.0) : ((t)->stlen != 0))
+*/
+
+
+static inline int
+eval_condition(NODE *t)
+{
+ if ((t->flags & MAYBE_NUM) != 0)
+ force_number(t);
+
+ if ((t->flags & NUMBER) != 0)
+ return (t->numbr != 0.0);
+
+ return (t->stlen != 0);
+}
+
+/* PUSH_CODE --- push a code onto the runtime stack */
+
+void
+PUSH_CODE(INSTRUCTION *cp)
+{
+ NODE *r;
+ getnode(r);
+ r->type = Node_instruction;
+ r->code_ptr = cp;
+ PUSH(r);
+}
+
+/* POP_CODE --- pop a code off the runtime stack */
+
+INSTRUCTION *
+POP_CODE()
+{
+ NODE *r;
+ INSTRUCTION *cp;
+ r = POP();
+ cp = r->code_ptr;
+ freenode(r);
+ return cp;
+}
+
+
+/*
+ * r_interpret:
+ * code is a list of instructions to run. returns the exit value
+ * from the awk code.
+ */
+
+ /* N.B.:
+ * 1) reference counting done for both number and string values.
+ * 2) TEMP flag no longer needed (consequence of the above; valref = 0
+ * is the replacement).
+ * 3) Stack operations:
+ * Use REPLACE[_XX] if last stack operation was TOP[_XX],
+ * PUSH[_XX] if last operation was POP[_XX] instead.
+ * 4) UPREF and DREF -- see awk.h
+ */
+
+
+int
+r_interpret(INSTRUCTION *code)
+{
+ INSTRUCTION *pc; /* current instruction */
+ NODE *r = NULL;
+ NODE *m;
+ INSTRUCTION *ni;
+ NODE *t1, *t2;
+ NODE *f; /* function definition */
+ NODE **lhs;
+ AWKNUM x, x1, x2;
+ int di, pre = FALSE;
+#ifdef _CRAY
+ long lx;
+ long lx2;
+#endif
+ Regexp *rp;
+ int currule = 0;
+#if defined(GAWKDEBUG) || defined(ARRAYDEBUG)
+ int last_was_stopme = FALSE; /* builtin stopme() called ? */
+#endif
+ long in_loop = 0;
+ int stdio_problem = FALSE;
+
+ if (args_array == NULL)
+ emalloc(args_array, NODE **, (max_args + 2)*sizeof(NODE *), "r_interpret");
+ else
+ erealloc(args_array, NODE **, (max_args + 2)*sizeof(NODE *), "r_interpret");
+
+/* array subscript */
+#define mk_sub(n) (n == 1 ? POP_STRING() : concat_exp(n, TRUE))
+
+#ifdef DEBUGGING
+#define JUMPTO(x) do { post_execute(pc, in_loop); pc = (x); goto top; } while(FALSE)
+#else
+#define JUMPTO(x) do { pc = (x); goto top; } while(FALSE)
+#endif
+
+ pc = code;
+
+ /* N.B.: always use JUMPTO for next instruction, otherwise bad things
+ * may happen. DO NOT add a real loop (for/while) below to
+ * replace ' forever {'; this catches failure to use JUMPTO to execute
+ * next instruction (e.g. continue statement).
+ */
+
+ /* loop until hit Op_stop instruction */
+
+ /* forever { */
+top:
+ if (pc->source_line > 0)
+ sourceline = pc->source_line;
+
+#ifdef DEBUGGING
+ if (! pre_execute(&pc, in_loop))
+ goto top;
+#endif
+
+ switch (pc->opcode) {
+ case Op_rule:
+ currule = pc->in_rule; /* for sole use in Op_K_next, Op_K_nextfile */
+ /* fall through */
+ case Op_func:
+ case Op_ext_func:
+ source = pc->source_file;
+ break;
+
+ case Op_atexit:
+ /* avoid false source indications */
+ source = NULL;
+ sourceline = 0;
+ (void) nextfile(&curfile, TRUE); /* close input data file */
+ /*
+ * This used to be:
+ *
+ * if (close_io() != 0 && ! exiting && exit_val == 0)
+ * exit_val = 1;
+ *
+ * Other awks don't care about problems closing open files
+ * and pipes, in that it doesn't affect their exit status.
+ * So we no longer do either.
+ */
+ (void) close_io(& stdio_problem);
+ /*
+ * However, we do want to exit non-zero if there was a problem
+ * with stdout/stderr, so we reinstate a slightly different
+ * version of the above:
+ */
+ if (stdio_problem && ! exiting && exit_val == 0)
+ exit_val = 1;
+ break;
+
+ case Op_stop:
+ return 0;
+
+ case Op_push_i:
+ m = pc->memory;
+ PUSH((m->flags & INTLSTR) != 0 ? format_val(CONVFMT, CONVFMTidx, m): m);
+ break;
+
+ case Op_push:
+ {
+ NODE *save_symbol;
+ int isparam = FALSE;
+
+ save_symbol = m = pc->memory;
+ if (m->type == Node_param_list) {
+ if ((m->flags & FUNC) != 0)
+ fatal(_("can't use function name `%s' as variable or array"),
+ m->vname);
+ isparam = TRUE;
+ save_symbol = m = GET_PARAM(m->param_cnt);
+ if (m->type == Node_array_ref)
+ m = m->orig_array;
+ }
+
+ switch (m->type) {
+ case Node_var:
+ if (do_lint && var_uninitialized(m))
+ lintwarn(isparam ?
+ _("reference to uninitialized argument `%s'") :
+ _("reference to uninitialized variable `%s'"),
+ save_symbol->vname);
+ m = m->var_value;
+ UPREF(m);
+ PUSH(m);
+ break;
+
+ case Node_var_new:
+ m->type = Node_var;
+ m->var_value = Nnull_string;
+ if (do_lint)
+ lintwarn(isparam ?
+ _("reference to uninitialized argument `%s'") :
+ _("reference to uninitialized variable `%s'"),
+ save_symbol->vname);
+ PUSH(Nnull_string);
+ break;
+
+ case Node_var_array:
+ if (! do_posix
+ && pc->nexti->opcode == Op_builtin
+ && pc->nexti->builtin == do_length) /* length(array) */
+ PUSH(m);
+ else
+ fatal(_("attempt to use array `%s' in a scalar context"),
+ array_vname(save_symbol));
+ break;
+
+ default:
+ cant_happen();
+ }
+ }
+ break;
+
+ case Op_push_param: /* function argument */
+ m = pc->memory;
+ if (m->type == Node_param_list)
+ m = GET_PARAM(m->param_cnt);
+ if (m->type == Node_var) {
+ m = m->var_value;
+ UPREF(m);
+ PUSH(m);
+ break;
+ }
+ /* else
+ fall through */
+ case Op_push_array:
+ PUSH(pc->memory);
+ break;
+
+ case Op_push_lhs:
+ lhs = get_lhs(pc->memory, pc->do_reference);
+ PUSH_ADDRESS(lhs);
+ break;
+
+ case Op_subscript:
+ t2 = mk_sub(pc->sub_count);
+ t1 = POP_ARRAY();
+ r = *assoc_lookup(t1, t2, TRUE);
+ DEREF(t2);
+ if (r->type == Node_val)
+ UPREF(r);
+ PUSH(r);
+ break;
+
+ case Op_sub_array:
+ t2 = mk_sub(pc->sub_count);
+ t1 = POP_ARRAY();
+ r = in_array(t1, t2);
+ if (r == NULL) {
+ const char *arr_name = make_aname(t1, t2);
+ getnode(r);
+ r->type = Node_var_array;
+ r->var_array = NULL;
+ r->vname = estrdup(arr_name, strlen(arr_name));
+ *assoc_lookup(t1, t2, FALSE) = r;
+ } else if (r->type != Node_var_array) {
+ const char *arr_name = make_aname(t1, t2);
+ DEREF(t2);
+ fatal(_("attempt to use scalar `%s' as an array"), arr_name);
+ }
+ DEREF(t2);
+ PUSH(r);
+ break;
+
+ case Op_subscript_lhs:
+ t2 = mk_sub(pc->sub_count);
+ t1 = POP_ARRAY();
+ lhs = assoc_lookup(t1, t2, pc->do_reference);
+ if ((*lhs)->type == Node_var_array) {
+ const char *arr_name = make_aname(t1, t2);
+ DEREF(t2);
+ fatal(_("attempt to use array `%s' in a scalar context"), arr_name);
+ }
+ DEREF(t2);
+ PUSH_ADDRESS(lhs);
+ break;
+
+ case Op_field_spec:
+ t1 = TOP_SCALAR();
+ lhs = r_get_field(t1, (Func_ptr *) 0, TRUE);
+ decr_sp();
+ DEREF(t1);
+ /* This used to look like this:
+ PUSH(dupnode(*lhs));
+ but was changed to bypass an apparent bug in the z/OS C compiler.
+ Please do not remerge. */
+ r = dupnode(*lhs); /* can't use UPREF here */
+ PUSH(r);
+ break;
+
+ case Op_field_spec_lhs:
+ t1 = TOP_SCALAR();
+ lhs = r_get_field(t1, &pc->target_assign->field_assign, pc->do_reference);
+ decr_sp();
+ DEREF(t1);
+ PUSH_ADDRESS(lhs);
+ break;
+
+ case Op_lint:
+ if (do_lint) {
+ switch (pc->lint_type) {
+ case LINT_assign_in_cond:
+ lintwarn(_("assignment used in conditional context"));
+ break;
+
+ case LINT_no_effect:
+ lintwarn(_("statement has no effect"));
+ break;
+
+ default:
+ cant_happen();
+ }
+ }
+ break;
+
+ case Op_push_loop: /* for break/continue in loop, switch */
+ PUSH_CODE(pc);
+ in_loop++;
+ break;
+
+ case Op_pop_loop:
+ (void) POP_CODE();
+ in_loop--;
+ break;
+
+ case Op_jmp:
+ JUMPTO(pc->target_jmp);
+
+ case Op_jmp_false:
+ r = POP_SCALAR();
+ di = eval_condition(r);
+ DEREF(r);
+ if (! di)
+ JUMPTO(pc->target_jmp);
+ break;
+
+ case Op_jmp_true:
+ r = POP_SCALAR();
+ di = eval_condition(r);
+ DEREF(r);
+ if (di)
+ JUMPTO(pc->target_jmp);
+ break;
+
+ case Op_and:
+ case Op_or:
+ t1 = POP_SCALAR();
+ di = eval_condition(t1);
+ DEREF(t1);
+ if ((pc->opcode == Op_and && di)
+ || (pc->opcode == Op_or && ! di))
+ break;
+ r = make_number((AWKNUM) di);
+ PUSH(r);
+ ni = pc->target_jmp;
+ JUMPTO(ni->nexti);
+
+ case Op_and_final:
+ case Op_or_final:
+ t1 = TOP_SCALAR();
+ r = make_number((AWKNUM) eval_condition(t1));
+ DEREF(t1);
+ REPLACE(r);
+ break;
+
+ case Op_not:
+ t1 = TOP_SCALAR();
+ r = make_number((AWKNUM) ! eval_condition(t1));
+ DEREF(t1);
+ REPLACE(r);
+ break;
+
+ case Op_equal:
+
+/* compare two nodes on the stack */
+#define compare(X, Y) \
+t2 = POP_SCALAR(); \
+t1 = TOP_SCALAR(); \
+X = cmp_nodes(t1, t2); \
+DEREF(t1); \
+DEREF(t2); \
+r = make_number((AWKNUM) (Y)); \
+REPLACE(r);
+
+ compare(di, di == 0);
+ break;
+
+ case Op_notequal:
+ compare(di, di != 0);
+ break;
+
+ case Op_less:
+ compare(di, di < 0);
+ break;
+
+ case Op_greater:
+ compare(di, di > 0);
+ break;
+
+ case Op_leq:
+ compare(di, di <= 0);
+ break;
+
+ case Op_geq:
+ compare(di, di >= 0);
+ break;
+#undef compare
+
+ case Op_plus_i:
+ x2 = force_number(pc->memory);
+ goto plus;
+
+ case Op_plus:
+ POP_NUMBER(x2);
+plus:
+ TOP_NUMBER(x1);
+ r = make_number(x1 + x2);
+ REPLACE(r);
+ break;
+
+ case Op_minus_i:
+ x2 = force_number(pc->memory);
+ goto minus;
+
+ case Op_minus:
+ POP_NUMBER(x2);
+minus:
+ TOP_NUMBER(x1);
+ r = make_number(x1 - x2);
+ REPLACE(r);
+ break;
+
+ case Op_times_i:
+ x2 = force_number(pc->memory);
+ goto times;
+
+ case Op_times:
+ POP_NUMBER(x2);
+times:
+ TOP_NUMBER(x1);
+ r = make_number(x1 * x2);
+ REPLACE(r);
+ break;
+
+ case Op_exp_i:
+ x2 = force_number(pc->memory);
+ goto exponent;
+
+ case Op_exp:
+ POP_NUMBER(x2);
+exponent:
+ TOP_NUMBER(x1);
+ x = calc_exp(x1, x2);
+ r = make_number(x);
+ REPLACE(r);
+ break;
+
+ case Op_quotient_i:
+ x2 = force_number(pc->memory);
+ goto quotient;
+
+ case Op_quotient:
+ POP_NUMBER(x2);
+quotient:
+ TOP_NUMBER(x1);
+ if (x2 == 0) {
+ decr_sp();
+ fatal(_("division by zero attempted"));
+ }
+#ifdef _CRAY
+ /* special case for integer division, put in for Cray */
+ lx2 = x2;
+ if (lx2 == 0)
+ x = x1 / x2;
+ else {
+ lx = (long) x1 / lx2;
+ if (lx * x2 == x1)
+ x = lx;
+ else
+ x = x1 / x2;
+ }
+#else
+ x = x1 / x2;
+#endif
+ r = make_number(x);
+ REPLACE(r);
+ break;
+
+ case Op_mod_i:
+ x2 = force_number(pc->memory);
+ goto mod;
+
+ case Op_mod:
+ POP_NUMBER(x2);
+mod:
+ TOP_NUMBER(x1);
+
+ if (x2 == 0) {
+ decr_sp();
+ fatal(_("division by zero attempted in `%%'"));
+ }
+#ifdef HAVE_FMOD
+ x = fmod(x1, x2);
+#else /* ! HAVE_FMOD */
+ (void) modf(x1 / x2, &x);
+ x = x1 - x * x2;
+#endif /* ! HAVE_FMOD */
+ r = make_number(x);
+ REPLACE(r);
+ break;
+
+ case Op_preincrement:
+ pre = TRUE;
+ case Op_postincrement:
+ x2 = 1.0;
+post:
+ lhs = TOP_ADDRESS();
+ x1 = force_number(*lhs);
+ unref(*lhs);
+ r = *lhs = make_number(x1 + x2);
+ if (pre)
+ UPREF(r);
+ else
+ r = make_number(x1);
+ REPLACE(r);
+ pre = FALSE;
+ break;
+
+ case Op_predecrement:
+ pre = TRUE;
+ case Op_postdecrement:
+ x2 = -1.0;
+ goto post;
+
+ case Op_unary_minus:
+ TOP_NUMBER(x1);
+ r = make_number(-x1);
+ REPLACE(r);
+ break;
+
+ case Op_store_sub:
+ /* array[sub] assignment optimization,
+ * see awkgram.y (optimize_assignment)
+ */
+ t1 = get_array(pc->memory, TRUE); /* array */
+ t2 = mk_sub(pc->expr_count); /* subscript */
+ lhs = assoc_lookup(t1, t2, FALSE);
+ if ((*lhs)->type == Node_var_array) {
+ const char *arr_name = make_aname(t1, t2);
+ DEREF(t2);
+ fatal(_("attempt to use array `%s' in a scalar context"), arr_name);
+ }
+ DEREF(t2);
+ unref(*lhs);
+ *lhs = POP_SCALAR();
+ break;
+
+ case Op_store_var:
+ /* simple variable assignment optimization,
+ * see awkgram.y (optimize_assignment)
+ */
+
+ lhs = get_lhs(pc->memory, FALSE);
+ unref(*lhs);
+ *lhs = POP_SCALAR();
+ break;
+
+ case Op_store_field:
+ {
+ /* field assignment optimization,
+ * see awkgram.y (optimize_assignment)
+ */
+
+ Func_ptr assign;
+ t1 = TOP();
+ lhs = r_get_field(t1, &assign, FALSE);
+ decr_sp();
+ DEREF(t1);
+ unref(*lhs);
+ *lhs = POP_SCALAR();
+ assert(assign != NULL);
+ assign();
+ }
+ break;
+
+ case Op_assign_concat:
+ /* x = x ... string concatenation optimization */
+ lhs = get_lhs(pc->memory, FALSE);
+ t1 = force_string(*lhs);
+ t2 = POP_STRING();
+
+ free_wstr(*lhs);
+
+ if (t1 != t2 && t1->valref == 1 && (t1->flags & PERM) == 0) {
+ size_t nlen = t1->stlen + t2->stlen;
+ erealloc(t1->stptr, char *, nlen + 2, "interpret");
+ memcpy(t1->stptr + t1->stlen, t2->stptr, t2->stlen);
+ t1->stlen = nlen;
+ t1->stptr[nlen] = '\0';
+ } else {
+ size_t nlen = t1->stlen + t2->stlen;
+ char *p;
+
+ emalloc(p, char *, nlen + 2, "interpret");
+ memcpy(p, t1->stptr, t1->stlen);
+ memcpy(p + t1->stlen, t2->stptr, t2->stlen);
+ unref(*lhs);
+ t1 = *lhs = make_str_node(p, nlen, ALREADY_MALLOCED);
+ }
+ t1->flags &= ~(NUMCUR|NUMBER);
+ DEREF(t2);
+ break;
+
+ case Op_assign:
+ lhs = POP_ADDRESS();
+ r = TOP_SCALAR();
+ unref(*lhs);
+ *lhs = r;
+ UPREF(r);
+ REPLACE(r);
+ break;
+
+ case Op_assign_plus:
+#define assign_common(X, Y) \
+lhs = POP_ADDRESS(); \
+X = force_number(*lhs); \
+TOP_NUMBER(Y); \
+unref(*lhs)
+
+#define assign(X, Y, Z) \
+assign_common(X, Y); \
+r = *lhs = make_number(Z); \
+UPREF(r); \
+REPLACE(r)
+
+ assign(x1, x2, x1 + x2);
+ break;
+
+ case Op_assign_minus:
+ assign(x1, x2, x1 - x2);
+ break;
+
+ case Op_assign_times:
+ assign(x1, x2, x1 * x2);
+ break;
+
+ case Op_assign_quotient:
+ assign_common(x1, x2);
+ if (x2 == (AWKNUM) 0) {
+ decr_sp();
+ fatal(_("division by zero attempted in `/='"));
+ }
+#ifdef _CRAY
+ /* special case for integer division, put in for Cray */
+ lx = x2;
+ if (lx == 0) {
+ r = *lhs = make_number(x1 / x2);
+ UPREF(r);
+ REPLACE(r);
+ break;
+ }
+ lx = (long) x1 / lx;
+ if (lx * x1 == x2)
+ r = *lhs = make_number((AWKNUM) lx);
+ else
+#endif /* _CRAY */
+ r = *lhs = make_number(x1 / x2);
+ UPREF(r);
+ REPLACE(r);
+ break;
+
+ case Op_assign_mod:
+ assign_common(x1, x2);
+ if (x2 == (AWKNUM) 0) {
+ decr_sp();
+ fatal(_("division by zero attempted in `%%='"));
+ }
+#ifdef HAVE_FMOD
+ r = *lhs = make_number(fmod(x1, x2));
+#else /* ! HAVE_FMOD */
+ (void) modf(x1 / x2, &x);
+ x = x1 - x2 * x;
+ r = *lhs = make_number(x);
+#endif /* ! HAVE_FMOD */
+ UPREF(r);
+ REPLACE(r);
+ break;
+
+ case Op_assign_exp:
+ assign(x1, x2, (AWKNUM) calc_exp((double) x1, (double) x2));
+ break;
+
+#undef assign
+#undef assign_common
+
+ case Op_var_update: /* update value of NR, FNR or NF */
+ pc->memory->var_update();
+ break;
+
+ case Op_var_assign:
+ pc->memory->var_assign();
+ break;
+
+ case Op_field_assign:
+ pc->field_assign();
+ break;
+
+ case Op_concat:
+ r = concat_exp(pc->expr_count, pc->concat_flag & CSUBSEP);
+ PUSH(r);
+ break;
+
+ case Op_K_switch:
+ {
+ INSTRUCTION *curr;
+ int match_found = FALSE;
+
+ t1 = TOP_SCALAR(); /* switch expression */
+ for (curr = pc->case_val; curr != NULL; curr = curr->nexti) {
+ if (curr->opcode == Op_K_case) {
+ m = curr->memory;
+ if (m->type == Node_regex) {
+ (void) force_string(t1);
+ rp = re_update(m);
+ match_found = (research(rp, t1->stptr, 0, t1->stlen,
+ avoid_dfa(m, t1->stptr, t1->stlen)) >= 0);
+ } else
+ match_found = (cmp_nodes(t1, m) == 0);
+ if (match_found)
+ break;
+ }
+ }
+
+ if (! match_found)
+ curr = pc->switch_dflt;
+ decr_sp();
+ DEREF(t1);
+ JUMPTO(curr->target_stmt);
+ }
+
+ case Op_K_continue:
+ assert(in_loop >= 0);
+ while (in_loop) {
+ r = TOP();
+ ni = r->code_ptr;
+ /* assert(ip->opcode == Op_push_loop); */
+ if (ni->target_continue != NULL)
+ break;
+
+ /*
+ * This one is for continue in case statement;
+ * keep searching for one that corresponds
+ * to a loop.
+ */
+ (void) POP_CODE();
+ in_loop--;
+ }
+
+ if (in_loop)
+ JUMPTO(pc->target_jmp);
+ else
+ fatal(_("`continue' outside a loop is not allowed"));
+ break;
+
+ case Op_K_break:
+ assert(in_loop >= 0);
+ if (! in_loop)
+ fatal(_("`break' outside a loop is not allowed"));
+ else {
+ JUMPTO(pc->target_jmp);
+ }
+ break;
+
+ case Op_K_delete:
+ t1 = POP_ARRAY();
+ do_delete(t1, pc->expr_count);
+ stack_adj(-pc->expr_count);
+ break;
+
+ case Op_K_delete_loop:
+ t1 = POP_ARRAY();
+ lhs = POP_ADDRESS(); /* item */
+ do_delete_loop(t1, lhs);
+ break;
+
+ case Op_in_array:
+ t1 = POP_ARRAY();
+ t2 = mk_sub(pc->expr_count);
+ di = (in_array(t1, t2) != NULL);
+ DEREF(t2);
+ PUSH(make_number((AWKNUM) di));
+ break;
+
+ case Op_arrayfor_init:
+ {
+ NODE **list = NULL;
+ NODE *array;
+ size_t num_elems = 0;
+ size_t i, j;
+ int sort_indices = whiny_users;
+
+ /* get the array */
+ array = POP_ARRAY();
+
+ /* sanity: check if empty */
+ if (array->var_array == NULL || array->table_size == 0)
+ goto arrayfor;
+
+ /* allocate space for array */
+ num_elems = array->table_size;
+ emalloc(list, NODE **, (num_elems + 1) * sizeof(NODE *), "interpret");
+
+ /* populate it */
+ for (i = j = 0; i < array->array_size; i++) {
+ r = array->var_array[i];
+ if (r == NULL)
+ continue;
+ for (; r != NULL; r = r->ahnext) {
+ list[j++] = ahash_dupnode(r);
+ assert(list[j-1] == r);
+ }
+ }
+
+ if (sort_indices)
+ qsort(list, num_elems, sizeof(NODE *), comp_func); /* shazzam! */
+ list[num_elems] = array; /* actual array for use in
+ * lint warning in Op_arrayfor_incr
+ */
+
+arrayfor:
+ getnode(r);
+ r->type = Node_arrayfor;
+ r->var_array = list;
+ r->table_size = num_elems; /* # of elements in list */
+ r->array_size = -1; /* current index */
+ PUSH(r);
+
+ if (num_elems == 0)
+ JUMPTO(pc->target_jmp); /* Op_arrayfor_final */
+ }
+ break; /* next instruction is Op_push_loop */
+
+ case Op_arrayfor_incr:
+ r = PEEK(1); /* (break/continue) bytecode from Op_push_loop has
+ * an offset of 0.
+ */
+ /* assert(r->type == Node_arrayfor); */
+ if (++r->array_size == r->table_size) {
+ NODE *array;
+ array = r->var_array[r->table_size]; /* actual array */
+ if (do_lint && array->table_size != r->table_size)
+ lintwarn(_("for loop: array `%s' changed size from %ld to %ld during loop execution"),
+ array_vname(array), (long) r->table_size, (long) array->table_size);
+ JUMPTO(pc->target_jmp); /* Op_pop_loop */
+ }
+
+ t1 = r->var_array[r->array_size];
+ lhs = get_lhs(pc->array_var, FALSE);
+ unref(*lhs);
+ *lhs = make_string(t1->ahname_str, t1->ahname_len);
+ break;
+
+ case Op_arrayfor_final:
+ r = POP();
+ assert(r->type == Node_arrayfor);
+ free_arrayfor(r);
+ break;
+
+ case Op_builtin:
+ r = pc->builtin(pc->expr_count);
+#if defined(GAWKDEBUG) || defined(ARRAYDEBUG)
+ if (! r)
+ last_was_stopme = TRUE;
+ else
+#endif
+ PUSH(r);
+ break;
+
+ case Op_K_print:
+ do_print(pc->expr_count, pc->redir_type);
+ break;
+
+ case Op_K_printf:
+ do_printf(pc->expr_count, pc->redir_type);
+ break;
+
+ case Op_K_print_rec:
+ do_print_rec(pc->expr_count, pc->redir_type);
+ break;
+
+ case Op_push_re:
+ m = pc->memory;
+ if (m->type == Node_dynregex) {
+ r = POP_STRING();
+ unref(m->re_exp);
+ m->re_exp = r;
+ }
+ PUSH(m);
+ break;
+
+ case Op_match_rec:
+ m = pc->memory;
+ t1 = *get_field(0, (Func_ptr *) 0);
+match_re:
+ rp = re_update(m);
+ /*
+ * FIXME:
+ *
+ * Any place where research() is called with a last parameter of
+ * zero, we need to use the avoid_dfa test. This appears here and
+ * in the code for Op_K_switch.
+ *
+ * A new or improved dfa that distinguishes beginning/end of
+ * string from beginning/end of line will allow us to get rid of
+ * this temporary hack.
+ *
+ * The avoid_dfa() function is in re.c; it is not very smart.
+ */
+
+ di = research(rp, t1->stptr, 0, t1->stlen,
+ avoid_dfa(m, t1->stptr, t1->stlen));
+ di = (di == -1) ^ (pc->opcode != Op_nomatch);
+ if(pc->opcode != Op_match_rec) {
+ decr_sp();
+ DEREF(t1);
+ }
+ r = make_number((AWKNUM) di);
+ PUSH(r);
+ break;
+
+ case Op_nomatch:
+ /* fall through */
+ case Op_match:
+ m = pc->memory;
+ t1 = TOP_STRING();
+ if (m->type == Node_dynregex) {
+ unref(m->re_exp);
+ m->re_exp = t1;
+ decr_sp();
+ t1 = TOP_STRING();
+ }
+ goto match_re;
+ break;
+
+ case Op_indirect_func_call:
+ {
+ int arg_count;
+
+ f = NULL;
+ arg_count = (pc + 1)->expr_count;
+ t1 = PEEK(arg_count); /* indirect var */
+ assert(t1->type == Node_val); /* @a[1](p) not allowed in grammar */
+ (void) force_string(t1);
+ if (t1->stlen > 0) {
+ /* retrieve function definition node */
+ f = pc->func_body;
+ if (f != NULL
+ && STREQ(f->vname, t1->stptr) /* indirect var hasn't been reassigned */
+ )
+ goto func_call;
+ f = lookup(t1->stptr);
+ }
+
+ if (f == NULL || f->type != Node_func)
+ fatal(_("function called indirectly through `%s' does not exist"), pc->func_name);
+ pc->func_body = f; /* save for next call */
+
+ goto func_call;
+ }
+
+ case Op_func_call:
+ /* retrieve function definition node */
+ f = pc->func_body;
+ if (f == NULL) {
+ f = lookup(pc->func_name);
+ if (f == NULL || f->type != Node_func)
+ fatal(_("function `%s' not defined"), pc->func_name);
+ pc->func_body = f; /* save for next call */
+ }
+
+ /* save current frame along with source and loop count.
+ * NB: 'function fun() { break; } BEGIN { while (1) fun(); }'
+ * should be fatal.
+ */
+
+func_call:
+ frame_ptr->vname = source; /* save current source */
+ frame_ptr->loop_count = in_loop; /* save loop count */
+ setup_frame(pc);
+ in_loop = 0;
+
+ ni = f->code_ptr; /* function code */
+ if (ni->opcode == Op_ext_func) {
+ /* dynamically set source and line numbers for an extension builtin. */
+ ni->source_file = source;
+ ni->source_line = sourceline;
+ ni->nexti->source_line = sourceline; /* Op_builtin */
+ ni->nexti->nexti->source_line = sourceline; /* Op_K_return */
+ }
+
+ /* run the function instructions */
+ JUMPTO(ni); /* Op_func or Op_ext_func */
+
+ case Op_K_return:
+ m = POP_SCALAR(); /* return value */
+
+ r = POP();
+ while (r->type != Node_frame) {
+ switch (r->type) {
+ case Node_arrayfor:
+ free_arrayfor(r);
+ break;
+ case Node_val:
+ DEREF(r);
+ break;
+ case Node_instruction:
+ freenode(r);
+ break;
+ default:
+ break;
+ }
+ r = POP();
+ }
+
+ ni = restore_frame(r);
+ source = frame_ptr->vname;
+ in_loop = frame_ptr->loop_count;
+
+ /* put the return value back on stack */
+ PUSH(m);
+ JUMPTO(ni);
+
+ case Op_K_getline_redir:
+ r = do_getline_redir(pc->into_var, pc->redir_type);
+ PUSH(r);
+ break;
+
+ case Op_K_getline: /* no redirection */
+ do {
+ int ret;
+ ret = nextfile(&curfile, FALSE);
+ if (ret <= 0)
+ r = do_getline(pc->into_var, curfile);
+ else {
+ PUSH_CODE(pc);
+ if (curfile == NULL)
+ JUMPTO((pc + 1)->target_endfile);
+ else {
+ TOP()->loop_count = in_loop;
+ in_loop = 0;
+ JUMPTO((pc + 1)->target_beginfile);
+ }
+ }
+ } while (r == NULL); /* EOF */
+ PUSH(r);
+ break;
+
+ case Op_after_endfile:
+ ni = POP_CODE();
+ assert(ni->opcode == Op_newfile || ni->opcode == Op_K_getline);
+ JUMPTO(ni);
+
+ case Op_after_beginfile:
+ after_beginfile(&curfile);
+ in_loop = TOP()->loop_count;
+ ni = POP_CODE();
+ if (ni->opcode == Op_K_getline
+ || curfile == NULL /* skipping directory argument */
+ )
+ JUMPTO(ni);
+ PUSH_CODE(ni); /* for use in Op_K_nextfile and Op_get_record */
+ break; /* Op_get_record */
+
+ case Op_newfile:
+ {
+ int ret;
+ ret = nextfile(&curfile, FALSE);
+ if (ret < 0)
+ JUMPTO(pc->target_jmp); /* end block or Op_atexit */
+ else if (ret > 0) {
+ PUSH_CODE(pc);
+ if (curfile == NULL)
+ JUMPTO(pc->target_endfile);
+ TOP()->loop_count = in_loop;
+ in_loop = 0;
+ break; /* beginfile block */
+ } else
+ PUSH_CODE(pc);
+ /* fall through */
+ }
+
+ case Op_get_record:
+ if (curfile == NULL) { /* from getline without redirection */
+ ni = POP_CODE(); /* Op_newfile */
+ ni = ni->target_jmp; /* end_block or Op_atexit */
+ } else if (inrec(curfile) == 0)
+ break; /* prog block */
+ else
+ ni = POP_CODE(); /* Op_newfile */
+ JUMPTO(ni);
+
+ case Op_K_nextfile:
+ if (currule != Rule && currule != BEGINFILE)
+ fatal(_("`nextfile' cannot be called from a `%s' rule"),
+ ruletab[currule]);
+ (void) nextfile(&curfile, TRUE);
+ if (currule == BEGINFILE) {
+ while (TRUE) {
+ r = POP();
+ switch (r->type) {
+ case Node_instruction:
+ ni = r->code_ptr;
+ if (ni->opcode == Op_newfile
+ || ni->opcode == Op_K_getline
+ ) {
+ in_loop = r->loop_count;
+ freenode(r);
+ JUMPTO(ni);
+ }
+ freenode(r);
+ break;
+ case Node_frame:
+ (void) restore_frame(r);
+ source = frame_ptr->vname;
+ break;
+ case Node_arrayfor:
+ free_arrayfor(r);
+ break;
+ case Node_val:
+ DEREF(r);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ unwind_stack(stack_bottom + 1); /* don't pop Op_newfile */
+ in_loop = 0;
+ JUMPTO(pc->target_endfile); /* endfile block */
+
+ case Op_K_exit:
+ exiting = TRUE;
+ POP_NUMBER(x1);
+ exit_val = (int) x1;
+#ifdef VMS
+ if (exit_val == 0)
+ exit_val = EXIT_SUCCESS;
+ else if (exit_val == 1)
+ exit_val = EXIT_FAILURE;
+ /* else
+ just pass anything else on through */
+#endif
+ /* jump to either the first end_block instruction
+ * or to Op_atexit
+ */
+ unwind_stack(stack_bottom);
+ in_loop = 0;
+ JUMPTO(pc->target_jmp);
+
+ case Op_K_next:
+ if (currule != Rule)
+ fatal(_("`next' cannot be called from a `%s' rule"), ruletab[currule]);
+
+ /* jump to Op_get_record */
+ unwind_stack(stack_bottom + 1); /* don't pop Op_newfile */
+ in_loop = 0;
+ JUMPTO(pc->target_jmp);
+
+ case Op_pop:
+#if defined(GAWKDEBUG) || defined(ARRAYDEBUG)
+ if (last_was_stopme)
+ last_was_stopme = FALSE;
+ else
+#endif
+ {
+ r = POP_SCALAR();
+ DEREF(r);
+ }
+ break;
+
+ case Op_line_range:
+ if (pc->triggered) /* evaluate right expression */
+ JUMPTO(pc->target_jmp);
+ /* else
+ evaluate left expression */
+ break;
+
+ case Op_cond_pair:
+ {
+ int result;
+ INSTRUCTION *ip;
+
+ t1 = TOP_SCALAR(); /* from right hand side expression */
+ di = (eval_condition(t1) != 0);
+ DEREF(t1);
+
+ ip = pc->line_range; /* Op_line_range */
+
+ if (! ip->triggered && di) {
+ /* not already triggered and left expression is TRUE */
+ decr_sp();
+ ip->triggered = TRUE;
+ JUMPTO(ip->target_jmp); /* evaluate right expression */
+ }
+
+ result = ip->triggered || di;
+ ip->triggered ^= di; /* update triggered flag */
+ r = make_number((AWKNUM) result); /* final value of condition pair */
+ REPLACE(r);
+ JUMPTO(pc->target_jmp);
+ }
+
+ case Op_exec_count:
+ INCREMENT(pc->exec_count);
+ break;
+
+ case Op_no_op:
+ case Op_K_if:
+ case Op_K_else:
+ case Op_cond_exp:
+ break;
+
+ default:
+ fatal(_("Sorry, don't know how to interpret `%s'"), opcode2str(pc->opcode));
+ }
+
+ JUMPTO(pc->nexti);
+
+/* } forever */
+
+ /* not reached */
+ return 0;
+
+#undef mk_sub
+#undef JUMPTO
+}