diff options
Diffstat (limited to 'awk2.c')
-rw-r--r-- | awk2.c | 1129 |
1 files changed, 1129 insertions, 0 deletions
@@ -0,0 +1,1129 @@ +/* + * awk2 --- gawk parse tree interpreter + * + * Copyright (C) 1986 Free Software Foundation + * Written by Paul Rubin, August 1986 + * + */ + +/* +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 + +#include "awk.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; + + +#define min(a,b) ((a) < (b) ? (a) : (b)) + +/* 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? */ + extern struct obstack temp_strings; + + if(tree == NULL) { + DEBUG(("NULL",NULL)); + return Nnull_string; + } + switch (tree->type) { + /* trivial data */ + case Node_string: + case Node_number: + DEBUG(("DATA",tree)); + return tree; + + /* Builtins */ + case Node_builtin: + DEBUG(("builtin",tree)); + return ((*tree->proc)(tree->subnode)); + + /* 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 */ + 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; + } + /* 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; + + 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; + + 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); + + if (tree->type == Node_match || tree->type == Node_nomatch) { + t1=force_string(t1); + return (re_search (tree->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) /* make number positive */ + +/* 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 |