home *** CD-ROM | disk | FTP | other *** search
- /**
- * $Revision: 1.1 $
- * $Log: C:/AWK/AWK2.C_V $
- *
- * Rev 1.1 09 Sep 1988 18:29:22 vince
- * MC 5.1 version
- *
- * Rev 1.0 09 Sep 1988 18:03:00 vince
- * Original source
- *
- *
- * awk2 --- gawk parse tree interpreter
- *
- * Copyright (C) 1986 Free Software Foundation
- * Written by Paul Rubin, August 1986
- *
- * Modifications by Andrew D. Estes, July 1988
- */
-
- /**
- * GAWK is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY. No author or distributor accepts responsibility to anyone
- * for the consequences of using it or for whether it serves any
- * particular purpose or works at all, unless he says so in writing.
- * Refer to the GAWK General Public License for full details.
- *
- * Everyone is granted permission to copy, modify and redistribute GAWK,
- * but only under the conditions described in the GAWK General Public
- * License. A copy of this license is supposed to have been given to you
- * along with GAWK so you can know your rights and responsibilities. It
- * should be in a file named COPYING. Among other things, the copyright
- * notice and this notice must be preserved on all copies.
- *
- * In other words, go ahead and share GAWK, but don't try to stop
- * anyone else from sharing it farther. Help stamp out software hoarding!
- */
-
- #include <setjmp.h>
- #include <stdio.h>
-
- #ifdef SYSV
- /* nasty nasty berkelixm */
- #define _setjmp setjmp
- #define _longjmp longjmp
- #endif
-
- #ifdef MSDOS
- /* nasty nasty berkelixm */
- #define _setjmp setjmp
- #define _longjmp longjmp
- #endif
-
- #include "awk.h"
- #include "regex.h"
-
- NODE ** get_lhs();
-
- extern NODE dumb[], *OFMT_node;
- /*
- * BEGIN and END blocks need special handling, because we are handed them as raw Node_statement_lists, not as Node_rule_lists (jfw)
- */
- extern NODE *begin_block, *end_block;
- NODE *do_sprintf();
- extern struct obstack other_stack;
-
- #ifdef min
- #undef min
- #define min(a,b) ((a) < (b) ? (a) : (b))
- #endif
-
- /* More of that debugging stuff */
- #ifdef FAST
- #define DEBUG(X)
- #else
- #define DEBUG(X) print_debug X
- #endif
-
- /* longjmp return codes, must be nonzero */
- /* Continue means either for loop/while continue, or next input record */
- #define TAG_CONTINUE 1
- /* Break means either for/while break, or stop reading input */
- #define TAG_BREAK 2
-
- /*
- * the loop_tag_valid variable allows continue/break-out-of-context to be caught and diagnosed (jfw)
- */
- #define PUSH_BINDING(stack, x) (bcopy ((char *)(x), (char *)(stack), sizeof (jmp_buf)), loop_tag_valid++)
- #define RESTORE_BINDING(stack, x) (bcopy ((char *)(stack), (char *)(x), sizeof (jmp_buf)), loop_tag_valid--)
-
- /* for "for(iggy in foo) {" */
- struct search
- {
- int numleft;
- AHASH **arr_ptr;
- AHASH *bucket;
- NODE *symbol;
- NODE *retval;
- };
-
- struct search *assoc_scan(), *assoc_next();
- /*
- * Tree is a bunch of rules to run. Returns zero if it hit an exit() statement
- */
- interpret(tree)
- NODE *tree;
- {
- register NODE *t; /* temporary */
-
- auto jmp_buf loop_tag_stack; /* shallow binding stack for loop_tag */
- static jmp_buf loop_tag; /* always the current binding */
- static int loop_tag_valid = 0; /* nonzero when loop_tag valid (jfw) */
-
- 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 **lhs; /* lhs == Left Hand Side for assigns, etc */
- register struct search *l; /* For array_for */
-
-
- extern struct obstack temp_strings;
- extern char *ob_dummy;
- NODE *do_printf();
-
- /*
- * clean up temporary strings created by evaluating expressions in previous recursive calls
- */
- obstack_free(&temp_strings, ob_dummy);
-
- if (tree == NULL)
- return 1;
- switch (tree->type)
- {
- #ifndef FAST
- /* Can't run these! */
- case Node_illegal:
- case Node_rule_node:
- case Node_if_branches:
- case Node_expression_list:
- case Node_K_BEGIN:
- case Node_K_END:
- case Node_redirect_output:
- case Node_redirect_append:
- case Node_redirect_pipe:
- case Node_var_array:
- abort();
- #endif
-
- case Node_rule_list:
- for (t = tree; t != NULL; t = t->rnode)
- {
- switch (_setjmp(rule_tag))
- {
- case 0: /* normal non-jump */
- if (eval_condition(t->lnode->lnode))
- {
- DEBUG(("Found a rule", t->lnode->rnode));
- if (t->lnode->rnode == NULL)
- {
- /*
- * special case: pattern with no action is equivalent to an action of {print} (jfw)
- */
- NODE printnode;
- printnode.type = Node_K_print;
- printnode.lnode = NULL;
- printnode.rnode = NULL;
- hack_print_node(&printnode);
- }
- else
- (void) interpret(t->lnode->rnode);
- }
- break;
- case TAG_CONTINUE: /* NEXT statement */
- return 1;
- case TAG_BREAK:
- return 0;
- }
- }
- break;
-
- case Node_statement_list:
- /* print_a_node(tree); */
- /*
- * because BEGIN and END do not have Node_rule_list nature, yet can
- * have exits and nexts, we special-case a setjmp of rule_tag here. (jfw)
- */
- if (tree == begin_block || tree == end_block)
- {
- switch (_setjmp(rule_tag))
- {
- case TAG_CONTINUE: /* next */
- panic("unexpected next");
- return 1;
- case TAG_BREAK:
- return 0;
- }
- }
- for (t = tree; t != NULL; t = t->rnode)
- {
- DEBUG(("Statements", t->lnode));
- (void) interpret(t->lnode);
- }
- break;
-
- case Node_K_if:
- DEBUG(("IF", tree->lnode));
- if (eval_condition(tree->lnode))
- {
- DEBUG(("True", tree->rnode->lnode));
- (void) interpret(tree->rnode->lnode);
- }
- else
- {
- DEBUG(("False", tree->rnode->rnode));
- (void) interpret(tree->rnode->rnode);
- }
- break;
-
- case Node_K_while:
- PUSH_BINDING(loop_tag_stack, loop_tag);
-
- DEBUG(("WHILE", tree->lnode));
- while (eval_condition(tree->lnode))
- {
- switch (_setjmp(loop_tag))
- {
- case 0: /* normal non-jump */
- DEBUG(("DO", tree->rnode));
- (void) interpret(tree->rnode);
- break;
- case TAG_CONTINUE: /* continue statement */
- break;
- case TAG_BREAK: /* break statement */
- RESTORE_BINDING(loop_tag_stack, loop_tag);
- return 1;
- #ifndef FAST
- default:
- abort(); /* never happens */
- #endif
- }
- }
- RESTORE_BINDING(loop_tag_stack, loop_tag);
- break;
-
- case Node_K_for:
- PUSH_BINDING(loop_tag_stack, loop_tag);
-
- DEBUG(("FOR", tree->forloop->init));
- (void) interpret(tree->forloop->init);
-
- DEBUG(("FOR.WHILE", tree->forloop->cond));
- while (eval_condition(tree->forloop->cond))
- {
- switch (_setjmp(loop_tag))
- {
- case 0: /* normal non-jump */
- DEBUG(("FOR.DO", tree->lnode));
- (void) interpret(tree->lnode);
- /* fall through */
- case TAG_CONTINUE: /* continue statement */
- DEBUG(("FOR.INCR", tree->forloop->incr));
- (void) interpret(tree->forloop->incr);
- break;
- case TAG_BREAK: /* break statement */
- RESTORE_BINDING(loop_tag_stack, loop_tag);
- return 1;
- #ifndef FAST
- default:
- abort(); /* never happens */
- #endif
- }
- }
- RESTORE_BINDING(loop_tag_stack, loop_tag);
- break;
-
- case Node_K_arrayfor:
- #define hakvar forloop->init
- #define arrvar forloop->incr
- PUSH_BINDING(loop_tag_stack, loop_tag);
- DEBUG(("AFOR.VAR", tree->hakvar));
- lhs = get_lhs(tree->hakvar);
- do_deref();
- for (l = assoc_scan(tree->arrvar); l; l = assoc_next(l))
- {
- *lhs = dupnode(l->retval);
- DEBUG(("AFOR.NEXTIS", *lhs));
- switch (_setjmp(loop_tag))
- {
- case 0:
- DEBUG(("AFOR.DO", tree->lnode));
- (void) interpret(tree->lnode);
- case TAG_CONTINUE:
- break;
-
- case TAG_BREAK:
- RESTORE_BINDING(loop_tag_stack, loop_tag);
- return 1;
- #ifndef FAST
- default:
- abort();
- #endif
- }
- }
- RESTORE_BINDING(loop_tag_stack, loop_tag);
- break;
-
- case Node_K_break:
- DEBUG(("BREAK", NULL));
- if (loop_tag_valid == 0) /* jfw */
- panic("unexpected break or continue");
- _longjmp(loop_tag, TAG_BREAK);
- break;
-
- case Node_K_continue:
- DEBUG(("CONTINUE", NULL));
- if (loop_tag_valid == 0) /* jfw */
- panic("unexpected break or continue");
- _longjmp(loop_tag, TAG_CONTINUE);
- break;
-
- case Node_K_print:
- DEBUG(("PRINT", tree));
- (void) hack_print_node(tree);
- break;
-
- case Node_K_printf:
- DEBUG(("PRINTF", tree));
- (void) do_printf(tree);
- break;
-
- case Node_K_next:
- DEBUG(("NEXT", NULL));
- _longjmp(rule_tag, TAG_CONTINUE);
- break;
-
- case Node_K_exit:
- /*
- * The unix awk doc says to skip the rest of the input. Does that mean
- * after performing all the rules on the current line?
- * Unix awk quits immediately, so this does too.
- */
- /* The UN*X exit can also take an optional arg return code. We don't */
- /* Well, we parse it, but never *DO* it */
- DEBUG(("EXIT", NULL));
- _longjmp(rule_tag, TAG_BREAK);
- break;
-
- default:
- /* Appears to be an expression statement. Throw away the value. */
- DEBUG(("E", NULL));
- (void) tree_eval(tree);
- break;
- }
- return 1;
- }
-
- /* evaluate a subtree, allocating strings on a temporary stack. */
- /*
- * This used to return a whole NODE, instead of a ptr to one, but that led to
- * lots of obnoxious copying. I got rid of it (JF)
- */
- NODE *tree_eval(tree)
- NODE *tree;
- {
- register NODE *r, *t1, *t2; /* return value and temporary subtrees */
- register NODE **lhs;
- static AWKNUM x; /* Why are these static? */
- NODE *do_getline(); /* for getline calls -ade- */
- double pow();
- extern struct obstack temp_strings;
-
- if (tree == NULL)
- {
- DEBUG(("NULL", NULL));
- return Nnull_string;
- }
- switch (tree->type)
- {
- /* trivial data */
- case Node_string:
- case Node_temp_string: /* ade */
- case Node_number:
- case Node_regex: /* ade */
- DEBUG(("DATA", tree));
- return tree;
-
- /* Builtins */
- case Node_builtin:
- DEBUG(("builtin", tree));
- return ((*tree->proc) (tree->subnode));
-
- case Node_K_getline: /* The getline function is overloaded; */
- DEBUG(("GETLINE", tree)); /* it requires special handling. -ade- */
- return (do_getline(tree));
- /* unary operations */
-
- case Node_var:
- case Node_subscript:
- case Node_field_spec:
- DEBUG(("var_type ref", tree));
- lhs = get_lhs(tree);
- return *lhs;
-
- case Node_preincrement:
- case Node_predecrement:
- DEBUG(("+-X", tree));
- lhs = get_lhs(tree->subnode);
- assign_number(lhs, force_number(*lhs) + (tree->type == Node_preincrement ? 1.0 : -1.0));
- return *lhs;
-
- case Node_postincrement:
- case Node_postdecrement:
- DEBUG(("X+-", tree));
- lhs = get_lhs(tree->subnode);
- x = force_number(*lhs);
- assign_number(lhs, x + (tree->type == Node_postincrement ? 1.0 : -1.0));
- return tmp_number(x);
-
- case Node_unary_minus:
- DEBUG(("UMINUS", tree));
- return tmp_number(-force_number(tree_eval(tree->subnode)));
-
- /* assignments */
- case Node_assign:
- DEBUG(("ASSIGN", tree));
- r = tree_eval(tree->rnode);
- lhs = get_lhs(tree->lnode);
- *lhs = dupnode(r);
- do_deref();
- /* FOO we have to regenerate $0 here! */
- if (tree->lnode->type == Node_field_spec)
- fix_fields();
- return r;
- /* other assignment types are easier because they are numeric */
- /* power functions added -ade- */
- case Node_assign_pow:
- r = tree_eval(tree->rnode);
- lhs = get_lhs(tree->lnode);
- assign_number(lhs, (AWKNUM) pow((double) force_number(*lhs),
- (double) force_number(r)));
- do_deref();
- return r;
-
- case Node_assign_times:
- r = tree_eval(tree->rnode);
- lhs = get_lhs(tree->lnode);
- assign_number(lhs, force_number(*lhs) * force_number(r));
- do_deref();
- return r;
-
- case Node_assign_quotient:
- r = tree_eval(tree->rnode);
- lhs = get_lhs(tree->lnode);
- assign_number(lhs, force_number(*lhs) / force_number(r));
- do_deref();
- return r;
-
- case Node_assign_mod:
- r = tree_eval(tree->rnode);
- lhs = get_lhs(tree->lnode);
- assign_number(lhs, (AWKNUM) (((int) force_number(*lhs)) % ((int) force_number(r))));
- do_deref();
- return r;
-
- case Node_assign_plus:
- r = tree_eval(tree->rnode);
- lhs = get_lhs(tree->lnode);
- assign_number(lhs, force_number(*lhs) + force_number(r));
- do_deref();
- return r;
-
- case Node_assign_minus:
- r = tree_eval(tree->rnode);
- lhs = get_lhs(tree->lnode);
- assign_number(lhs, force_number(*lhs) - force_number(r));
- do_deref();
- return r;
-
- /* conditional expression added -ade- */
- case Node_cond_exp:
- t1 = tree->rnode;
- return (eval_condition(tree->lnode) ? tree_eval(t1->lnode) :
- tree_eval(t1->rnode));
-
- }
- /*
- * Note that if TREE is invalid, gAWK will probably bomb in one of these tree_evals here.
- */
- /* evaluate subtrees in order to do binary operation, then keep going */
- t1 = tree_eval(tree->lnode);
- t2 = tree_eval(tree->rnode);
-
- switch (tree->type)
- {
-
- case Node_concat:
- t1 = force_string(t1);
- t2 = force_string(t2);
-
- r = (NODE *) obstack_alloc(&temp_strings, sizeof(NODE));
- r->type = Node_temp_string;
- r->stlen = t1->stlen + t2->stlen;
- r->stref = 1;
- r->stptr = (char *) obstack_alloc(&temp_strings, r->stlen + 1);
- bcopy(t1->stptr, r->stptr, t1->stlen);
- bcopy(t2->stptr, r->stptr + t1->stlen, t2->stlen);
- r->stptr[r->stlen] = '\0';
- return r;
-
- /* power functions added -ade- */
- case Node_pow:
- return tmp_number((AWKNUM) pow((double) force_number(t1),
- (double) force_number(t2)));
-
- case Node_times:
- return tmp_number(force_number(t1) * force_number(t2));
-
- case Node_quotient:
- x = force_number(t2);
- if (x == (AWKNUM) 0)
- return tmp_number((AWKNUM) 0);
- else
- return tmp_number(force_number(t1) / x);
-
- case Node_mod:
- x = force_number(t2);
- if (x == (AWKNUM) 0)
- return tmp_number((AWKNUM) 0);
- return tmp_number((AWKNUM) /* uggh... */
- (((int) force_number(t1)) % ((int) x)));
-
- case Node_plus:
- return tmp_number(force_number(t1) + force_number(t2));
-
- case Node_minus:
- return tmp_number(force_number(t1) - force_number(t2));
-
- #ifndef FAST
- default:
- fprintf(stderr, "internal error: illegal numeric operation\n");
- abort();
- #endif
- }
- return 0;
- }
-
- /*
- * We can't dereference a variable until after we've given it its new value. This variable points to the value we have to free up
- */
- NODE *deref;
-
- /*
- * 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
- */
-
- NODE **get_lhs(ptr)
- NODE *ptr;
- {
- register NODE *subexp;
- register NODE **aptr;
- register int num;
- extern NODE **fields_arr;
- extern f_arr_siz;
- NODE **assoc_lookup();
- extern char f_empty[]; /* jfw */
-
- #ifndef FAST
- if (ptr == NULL)
- abort();
- #endif
- deref = NULL;
- switch (ptr->type)
- {
- case Node_var:
- deref = ptr->var_value;
- return &(ptr->var_value);
-
- case Node_field_spec:
- num = (int) force_number(tree_eval(ptr->lnode));
- if (num < 0)
- num = 0; /* JF what should I do? */
- if (num > f_arr_siz)
- set_field(num, f_empty, 0); /* jfw: so blank_strings can be simpler */
- deref = NULL;
- return &fields_arr[num];
-
- case Node_subscript:
- subexp = tree_eval(ptr->rnode);
- aptr = assoc_lookup(ptr->lnode, subexp);
- deref = *aptr;
- return aptr;
- }
- #ifndef FAST
- abort();
- return 0;
- #endif
- }
-
- do_deref()
- {
- if (deref)
- {
- switch (deref->type)
- {
- case Node_string:
- if (deref != Nnull_string)
- FREE_ONE_REFERENCE(deref);
- break;
- case Node_number:
- free((char *) deref);
- break;
- #ifndef FAST
- default:
- abort();
- #endif
- }
- deref = 0;
- }
- }
-
- /*
- * This makes numeric operations slightly more efficient. Just change the value of a numeric node, if possible
- */
- assign_number(ptr, value)
- NODE **ptr;
- AWKNUM value;
- {
- switch ((*ptr)->type)
- {
- case Node_string:
- if (*ptr != Nnull_string)
- FREE_ONE_REFERENCE(*ptr);
- case Node_temp_string: /* jfw: dont crash if we say $2 += 4 */
- *ptr = make_number(value);
- return;
- case Node_number:
- (*ptr)->numbr = value;
- deref = 0;
- break;
- #ifndef FAST
- default:
- printf("assign_number nodetype %d\n", (*ptr)->type); /* jfw: add mesg. */
- abort();
- #endif
- }
- }
-
-
-
- /* Routines to deal with fields */
- #define ORIG_F 30
-
- NODE ** fields_arr;
- NODE *fields_nodes;
- int f_arr_siz;
- char f_empty[] = "";
-
- init_fields()
- {
- register NODE **tmp;
- register NODE *xtmp;
-
- f_arr_siz = ORIG_F;
- fields_arr = (NODE **) malloc(ORIG_F * sizeof(NODE *));
- fields_nodes = (NODE *) malloc(ORIG_F * sizeof(NODE));
- tmp = &fields_arr[f_arr_siz];
- xtmp = &fields_nodes[f_arr_siz];
- while (--tmp >= &fields_arr[0])
- {
- --xtmp;
- *tmp = xtmp;
- xtmp->type = Node_temp_string;
- xtmp->stlen = 0;
- xtmp->stref = 1;
- xtmp->stptr = f_empty;
- }
- }
-
- blank_fields()
- {
- register NODE **tmp;
- extern char *parse_end;
-
- tmp = &fields_arr[f_arr_siz];
- while (--tmp >= &fields_arr[0])
- {
- switch (tmp[0]->type)
- {
- case Node_number:
- free((char *) *tmp);
- *tmp = &fields_nodes[tmp - fields_arr];
- break;
- case Node_string:
- if (*tmp != Nnull_string)
- FREE_ONE_REFERENCE(*tmp);
- *tmp = &fields_nodes[tmp - fields_arr];
- break;
- case Node_temp_string:
- break;
- #ifndef FAST
- default:
- abort();
- #endif
- }
- if ((*tmp)->stptr != f_empty)
- { /* jfw */
- /* Then it was assigned a string with set_field */
- /* out of a private buffer to inrec, so don't free it */
- (*tmp)->stptr = f_empty;
- (*tmp)->stlen = 0;
- (*tmp)->stref = 1;
- }
- /* *tmp=Nnull_string; */
- }
- /* Free the strings */
- obstack_free(&other_stack, parse_end);
- }
-
- /*
- * Danger! Must only be called for fields we know have just been blanked, or fields we know don't exist yet.
- */
- set_field(n, str, len)
- char *str;
- {
- NODE *field_string();
-
- if (n > f_arr_siz)
- {
- int t;
-
- fields_arr = (NODE **) realloc((char *) fields_arr, (n + 1) * sizeof(NODE *));
- fields_nodes = (NODE *) realloc((char *) fields_nodes, (n + 1) * sizeof(NODE));
- for (t = f_arr_siz; t <= n; t++)
- {
- fields_arr[t] = &fields_nodes[t];
- fields_nodes[t].type = Node_temp_string;
- fields_nodes[t].stlen = 0;
- fields_nodes[t].stref = 1;
- fields_nodes[t].stptr = f_empty;
- }
- f_arr_siz = n + 1;
- }
- fields_nodes[n].stlen = len;
- if (n == 0)
- {
- fields_nodes[n].stptr = (char *) obstack_alloc(&other_stack, len + 1);
- bcopy(str, fields_nodes[n].stptr, len);
- fields_nodes[n].stptr[len] = '\0';
- }
- else
- {
- fields_nodes[n].stptr = str;
- str[len] = '\0';
- }
- }
-
- #ifdef DONTDEF
- /* Nodes created with this will go away when the next input line is read */
- NODE *field_string(s, len)
- char *s;
- {
- register NODE *r;
-
- r = (NODE *) obstack_alloc(&other_stack, sizeof(NODE));
- r->type = Node_temp_string;
- r->stref = 1;
- r->stlen = len;
- r->stptr = (char *) obstack_alloc(&other_stack, len + 1);
- bcopy(s, r->stptr, len);
- /*
- * r->stptr=s; r->stptr[len]='\0';
- */
-
- return r;
- }
- #endif
-
- /* Someone assigned a value to $(something). Fix up $0 to be right */
- fix_fields()
- {
- register int tlen;
- register NODE *tmp;
- NODE *ofs;
- char *ops;
- register char *cops;
- register NODE **ptr, **maxp;
- extern NODE *OFS_node;
-
- maxp = 0;
- tlen = 0;
- ofs = force_string(*get_lhs(OFS_node));
- ptr = &fields_arr[f_arr_siz];
- while (--ptr > &fields_arr[0])
- {
- tmp = force_string(*ptr);
- tlen += tmp->stlen;
- if (tmp->stlen && !maxp)
- maxp = ptr;
- }
- if (!maxp)
- {
- if (fields_arr[0] != fields_nodes)
- FREE_ONE_REFERENCE(fields_arr[0]);
- fields_arr[0] = Nnull_string;
- return;
- }
-
- tlen += ((maxp - fields_arr) - 1) * ofs->stlen;
- ops = (char *) malloc(tlen + 1);
- cops = ops;
- for (ptr = &fields_arr[1]; ptr <= maxp; ptr++)
- {
- tmp = force_string(*ptr);
- bcopy(tmp->stptr, cops, tmp->stlen);
- cops += tmp->stlen;
- if (ptr != maxp)
- {
- bcopy(ofs->stptr, cops, ofs->stlen);
- cops += ofs->stlen;
- }
- }
- tmp = newnode(Node_string);
- tmp->stptr = ops;
- tmp->stlen = tlen;
- tmp->stref = 1;
- tmp->stptr[tlen] = '\0';
- /* don't free unless it's new */
- if (fields_arr[0] != fields_nodes)
- FREE_ONE_REFERENCE(fields_arr[0]);
- fields_arr[0] = tmp;
- }
-
- /* Is TREE true or false? Returns 0==false, non-zero==true */
- int eval_condition(tree)
- NODE *tree;
- {
- register int di;
- register NODE *t1, *t2, *t3;
- struct re_pattern_buffer *rpb; /* ade */
-
- if (tree == NULL) /* Null trees are the easiest kinds */
- return 1;
- switch (tree->type)
- {
- /* Maybe it's easy; check and see. */
- /* BEGIN and END are always false */
- case Node_K_BEGIN:
- return 0;
- break;
-
- case Node_K_END:
- return 0;
- break;
-
- case Node_and:
- return eval_condition(tree->lnode)
- && eval_condition(tree->rnode);
-
- case Node_or:
- return eval_condition(tree->lnode)
- || eval_condition(tree->rnode);
-
- case Node_not:
- return !eval_condition(tree->lnode);
-
- /*
- * 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. This feature
- * was implemented by John Woods, jfw@eddie.mit.edu, during a rainy weekend.
- */
- case Node_line_range:
- if (!tree->triggered)
- if (!eval_condition(tree->condpair->lnode))
- return 0;
- else
- tree->triggered = 1;
- /* Else we are triggered */
- if (eval_condition(tree->condpair->rnode))
- tree->triggered = 0;
- return 1;
- }
-
- /*
- * Could just be J.random expression. in which case, null and 0 are false, anything else is true
- */
-
- switch (tree->type)
- {
- case Node_match:
- case Node_nomatch:
- case Node_equal:
- case Node_notequal:
- case Node_less:
- case Node_greater:
- case Node_leq:
- case Node_geq:
- break;
-
- default: /* This is so 'if(iggy)', etc, will work */
- /* Non-zero and non-empty are true */
- t1 = tree_eval(tree);
- switch (t1->type)
- {
- case Node_number:
- return t1->numbr != 0.0;
- case Node_string:
- case Node_temp_string:
- return t1->stlen != 0;
- #ifndef FAST
- default:
- abort();
- #endif
- }
- }
- /*
- * couldn't fob it off recursively, eval left subtree and see if it's a pattern match operation
- */
-
- t1 = tree_eval(tree->lnode);
-
- /*
- * special code added to allow an expression to be converted * into a regular expression -ade-
- */
-
- if (tree->type == Node_match || tree->type == Node_nomatch)
- {
- t2 = tree->rnode;
- if (t2->type == Node_expression_list)
- {
- rpb = make_regexp_n(force_string(tree_eval(t2->lnode)));
- t1 = force_string(t1);
- di = (re_search(rpb, t1->stptr, t1->stlen, 0, t1->stlen,
- NULL) == -1) ^ (tree->type == Node_match);
- free(rpb->buffer);
- return (di);
- }
- if (t2->type == Node_var)
- {
- rpb = make_regexp_n(force_string(tree_eval(t2->lnode)));
- t1 = force_string(t1);
- di = (re_search(rpb, t1->stptr, t1->stlen, 0, t1->stlen,
- NULL) == -1) ^ (tree->type == Node_match);
- free(rpb->buffer);
- return (di);
- }
- t1 = force_string(t1);
- return (re_search(t2->rereg, t1->stptr, t1->stlen, 0, t1->stlen, NULL) == -1)
- ^ (tree->type == Node_match);
- }
-
- /* still no luck--- eval the right subtree and try binary ops */
-
- t2 = tree_eval(tree->rnode);
-
- di = cmp_nodes(t1, t2);
-
- switch (tree->type)
- {
- case Node_equal:
- return di == 0;
- case Node_notequal:
- return di != 0;
- case Node_less:
- return di < 0;
- case Node_greater:
- return di > 0;
- case Node_leq:
- return di <= 0;
- case Node_geq:
- return di >= 0;
- #ifndef FAST
- default:
- fprintf(stderr, "Panic: unknown conditonal\n");
- abort();
- #endif
- }
- return 0;
- }
-
- /* FOO this doesn't properly compare "12.0" and 12.0 etc */
- /* or "1E1" and 10 etc */
- /* Perhaps someone should fix it. */
- /* Consider it fixed (jfw) */
-
- /*
- * strtod() would have been better, except (1) real awk is needlessly
- * restrictive in what strings it will consider to be numbers,
- * and (2) I couldn't find the public domain version anywhere handy.
- */
- is_a_number(str) /* does the string str have pure-numeric syntax? */
- char *str; /* don't convert it, assume that atof is better */
- {
- if (*str == 0)
- return 1; /* null string has numeric value of0 */
- /*
- * This is still a bug: in real awk, an explicit "" string is not treated as
- * a number. Perhaps it is only variables that, when empty, are also 0s.
- * This bug-lette here at least lets uninitialized variables to compare equal
- * to zero like they should.
- */
- if (*str == '-')
- str++;
- if (*str == 0)
- return 0;
- /* must be either . or digits (.4 is legal) */
- if (*str != '.' && !isdigit(*str))
- return 0;
- while (isdigit(*str))
- str++;
- if (*str == '.')
- {
- str++;
- while (isdigit(*str))
- str++;
- }
- /*
- * curiously, real awk DOESN'T consider "1E1" to be equal to 10! Or even
- * equal to 1E1 for that matter! For a laugh, try:
- * awk 'BEGIN {if ("1E1" == 1E1) print "eq"; else print "neq";exit}'
- * Since this behavior is QUITE curious, I include the code for the
- * adventurous. One might also feel like skipping leading whitespace
- * (awk doesn't) and allowing a leading + (awk doesn't).
- */
- #ifdef Allow_Exponents
- if (*str == 'e' || *str == 'E')
- {
- str++;
- if (*str == '+' || *str == '-')
- str++;
- if (!isdigit(*str))
- return 0;
- while (isdigit(*str))
- str++;
- }
- #endif
- /* if we have digested the whole string, we are successful */
- return (*str == 0);
- }
-
- cmp_nodes(t1, t2)
- NODE *t1, *t2;
- {
- register int di;
- register AWKNUM d;
-
- if (t1 == t2)
- {
- return 0;
- }
- #ifndef FAST
- if (!t1 || !t2)
- {
- abort();
- return t1 ? 1 : -1;
- }
-
- #endif
- if (t1->type == Node_number && t2->type == Node_number)
- {
- d = t1->numbr - t2->numbr;
- if (d < 0.0)
- return -1;
- if (d > 0.0)
- return 1;
- return 0;
- }
- t1 = force_string(t1);
- t2 = force_string(t2);
- /* "real" awk treats things as numbers if they both "look" like numbers. */
- if (*t1->stptr && *t2->stptr /* don't allow both to be empty strings(jfw) */
- && is_a_number(t1->stptr) && is_a_number(t2->stptr))
- {
- double atof();
- d = atof(t1->stptr) - atof(t2->stptr);
- if (d < 0.0)
- return -1;
- if (d > 0.0)
- return 1;
- return 0;
- }
- di = strncmp(t1->stptr, t2->stptr, min(t1->stlen, t2->stlen));
- if (di == 0)
- di = t1->stlen - t2->stlen;
- if (di > 0)
- return 1;
- if (di < 0)
- return -1;
- return 0;
- }
-
-
- #ifdef DONTDEF
- int primes[] = {31, 61, 127, 257, 509, 1021, 2053, 4099, 8191, 16381};
- #endif
-
- /*
- * routines for associative arrays. SYMBOL is the address of the node (or other pointer) being dereferenced. SUBS is a number or
- * string used as the subscript.
- */
-
- /* #define ASSOC_HASHSIZE 1009 /* prime */
- #define ASSOC_HASHSIZE 29
- #define STIR_BITS(n) ((n) << 5 | (((n) >> 27) & 0x1f))
- #define HASHSTEP(old, c) ((old << 1) + c)
- #define MAKE_POS(v) (v & ~0x80000000)
-
- /* static AHASH *assoc_table[ASSOC_HASHSIZE]; */
-
- /* Flush all the values in symbol[] before doing a split() */
- assoc_clear(symbol)
- NODE *symbol;
- {
- int i;
- AHASH *bucket, *next;
-
- if (symbol->var_array == 0)
- return;
- for (i = 0; i < ASSOC_HASHSIZE; i++)
- {
- for (bucket = symbol->var_array[i]; bucket; bucket = next)
- {
- next = bucket->next;
- deref = bucket->name;
- do_deref();
- deref = bucket->value;
- do_deref();
- free((void *) bucket);
- }
- symbol->var_array[i] = 0;
- }
- }
-
- /*
- * Find SYMBOL[SUBS] in the assoc array. Install it with value "" if it isn't there.
- */
- /* Returns a pointer ala get_lhs to where its value is stored */
- NODE **assoc_lookup(symbol, subs)
- NODE *symbol, *subs;
- {
- int hash1 = 0, hashf(), i;
- AHASH *bucket;
- NODETYPE ty;
-
- if (subs->type == Node_number)
- {
- hash1 = (int) subs->numbr;
- ty = Node_number;
- }
- else
- {
- ty = Node_string;
- subs = force_string(subs);
- for (i = 0; i < subs->stlen; i++)
- hash1 = HASHSTEP(hash1, subs->stptr[i]);
-
- /* hash1 ^= (int) STIR_BITS((int)symbol); */
- }
- hash1 = MAKE_POS(STIR_BITS((int) hash1)) % ASSOC_HASHSIZE;
-
- /* this table really should grow dynamically */
- if (symbol->var_array == 0)
- {
- symbol->var_array = (AHASH **) malloc(sizeof(AHASH *) * ASSOC_HASHSIZE);
- for (i = 0; i < ASSOC_HASHSIZE; i++)
- {
- symbol->var_array[i] = 0;
- }
- }
- else
- {
- for (bucket = symbol->var_array[hash1]; bucket; bucket = bucket->next)
- {
- if (bucket->name->type != ty || cmp_nodes(bucket->name, subs))
- continue;
- return &(bucket->value);
- }
- /* Didn't find it on first pass. Try again. */
- for (bucket = symbol->var_array[hash1]; bucket; bucket = bucket->next)
- {
- if (cmp_nodes(bucket->name, subs))
- continue;
- return &(bucket->value);
- }
- }
- bucket = (AHASH *) malloc(sizeof(AHASH));
- bucket->symbol = symbol;
- bucket->name = dupnode(subs);
- bucket->value = Nnull_string;
- bucket->next = symbol->var_array[hash1];
- symbol->var_array[hash1] = bucket;
- return &(bucket->value);
- }
-
- struct search *assoc_scan(symbol)
- NODE *symbol;
- {
- struct search *lookat;
-
- if (!symbol->var_array)
- return 0;
- lookat = (struct search *) obstack_alloc(&other_stack, sizeof(struct search));
- /* lookat->symbol=symbol; */
- lookat->numleft = ASSOC_HASHSIZE;
- lookat->arr_ptr = symbol->var_array;
- lookat->bucket = symbol->var_array[0];
- return assoc_next(lookat);
- }
-
- struct search *assoc_next(lookat)
- struct search *lookat;
- {
- for (; lookat->numleft; lookat->numleft--)
- {
- while (lookat->bucket != 0)
- {
- lookat->retval = lookat->bucket->name;
- lookat->bucket = lookat->bucket->next;
- return lookat;
- }
- lookat->bucket = *++(lookat->arr_ptr);
- }
- return 0;
- }
-
-
-
- #ifdef FAST
- NODE *strforce(n)
- NODE *n;
- {
- extern NODE dumb[], *OFMT_node;
- NODE *do_sprintf();
-
- dumb[1].lnode = n;
- if (OFMT_node->var_value->type != Node_string)
- panic("Insane value for OFMT detected.");
- return do_sprintf(&dumb[0]);
- }
-
- #else
- AWKNUM force_number(n)
- NODE *n;
- {
- double atof(); /* Forgetting this is bad */
-
- if (n == NULL)
- abort();
- switch (n->type)
- {
- case Node_number:
- return n->numbr;
- case Node_string:
- case Node_temp_string:
- return atof(n->stptr);
- default:
- abort();
- }
- return 0.0;
- }
-
- NODE *force_string(s)
- NODE *s;
- {
- if (s == NULL)
- abort();
- switch (s->type)
- {
- case Node_string:
- case Node_temp_string:
- return s;
- case Node_number:
- if ((*get_lhs(OFMT_node))->type != Node_string)
- panic("Insane value for OFMT!", 0);
- dumb[1].lnode = s;
- return do_sprintf(&dumb[0]);
- default:
- abort();
- }
- return NULL;
- }
- #endif