home *** CD-ROM | disk | FTP | other *** search
- /*
- * awk2 --- gawk parse tree interpreter
- *
- * $Log: awk2.c,v $
- * Revision 1.51 89/03/31 13:25:34 david
- * GNU license; MSDOS support
- *
- * Revision 1.50 89/03/30 20:57:19 david
- * allow for Node_rule_node (a single rule)
- * avoid calling eval_condition for no pattern
- * plug a memory leak in assign_number
- *
- * Revision 1.49 89/03/29 14:15:07 david
- * delinting
- *
- * Revision 1.48 89/03/26 17:58:53 david
- * changed return of do_printf to void
- *
- * Revision 1.47 89/03/22 22:09:50 david
- * a cleaner way to handle assignment to $n where n > 0
- *
- * Revision 1.46 89/03/22 21:01:14 david
- * delete some obsolete code
- *
- * Revision 1.45 89/03/21 19:25:37 david
- * minor cleanup
- *
- * Revision 1.44 89/03/21 18:24:02 david
- * bug fix in cmp_nodes: strings in which one was a prefix of the other compared equal
- *
- * Revision 1.43 89/03/21 10:55:55 david
- * cleanup and fix of string comparison (0 length was wrong)
- *
- * Revision 1.42 89/03/15 22:01:17 david
- * old case stuff removed
- * new case stuff added
- * fixed % operator
- * strings with embedded \0 can now be compared
- *
- * Revision 1.41 89/03/15 21:32:50 david
- * try to free up memory in as many places as possible
- *
- * Revision 1.40 88/12/15 12:57:31 david
- * make casetable static
- *
- * Revision 1.39 88/12/14 10:50:51 david
- * dupnode() the return from a function
- *
- * Revision 1.38 88/12/13 22:27:04 david
- * macro-front-end tree_eval and other optimizations
- *
- * Revision 1.36 88/12/08 10:51:37 david
- * small correction to source file code
- *
- * Revision 1.35 88/12/07 20:00:35 david
- * changes for incorporating source filename into error messages
- *
- * Revision 1.34 88/12/01 15:04:48 david
- * cleanup and additions for source line number printing in error messages
- *
- * Revision 1.33 88/11/30 15:16:10 david
- * merge FREE_ONE_REFERENCE into do_deref()
- * free more in do_deref
- * in for (i in array) loops, make sure value of i gets freed on each iteration
- *
- * Revision 1.32 88/11/29 09:55:04 david
- * corrections to code that tracks value of NF -- this needs cleanup
- *
- * Revision 1.31 88/11/23 21:40:47 david
- * Arnold: comment cleanup
- *
- * Revision 1.30 88/11/22 13:49:09 david
- * Arnold: changes for case-insensitive matching
- *
- * Revision 1.29 88/11/15 10:22:42 david
- * Arnold: cleanup of comments and #include's
- *
- * Revision 1.28 88/11/14 21:55:38 david
- * Arnold: misc. cleanup and error message on bad regexp
- *
- * Revision 1.27 88/11/14 21:26:52 david
- * update NF on assignment to a field greater than current NF
- *
- * Revision 1.26 88/11/03 15:26:20 david
- * simplify call to in_array(); extensive revision of cmp_nodes and is_a_number
- *
- * Revision 1.25 88/11/01 12:11:57 david
- * DEBUG macro becomes DBG_P; added some debugging code; moved all the
- * compound assignments (+= etc.) into op_assign()
- *
- * Revision 1.24 88/10/25 10:43:05 david
- * intermediate state: more code movement; Node_string et al. -> Node_val;
- * add more debugging code; improve cmp_nodes
- *
- * Revision 1.22 88/10/19 21:57:41 david
- * replace malloc and realloc with error checking versions
- * start to change handling of $0
- *
- * Revision 1.21 88/10/17 20:56:13 david
- * Arnold: better error messages for use of a function in the wrong context
- *
- * Revision 1.20 88/10/13 21:56:41 david
- * cleanup of previous changes
- * change panic() to fatal()
- * detect and bomb on function call with space between name and opening (
- *
- * Revision 1.19 88/10/11 22:19:20 david
- * cleanup
- *
- * Revision 1.18 88/10/04 21:31:33 david
- * minor cleanup
- *
- * Revision 1.17 88/08/22 14:01:19 david
- * fix to set_field() from Jay Finlayson
- *
- * Revision 1.16 88/08/09 14:51:34 david
- * removed bad call to obstack_free() -- there is a lot of memory that is
- * not being properly freed -- this area needs major work
- * changed semantics in eval_condition -- if(expr) should test true if
- * expr is a non-null string, even if the num,erical value is zero -- counter-
- * intuitive but that's what's in the book
- *
- * Revision 1.15 88/06/13 18:02:58 david
- * separate exit value from fact that exit has been called [from Arnold]
- *
- * Revision 1.14 88/06/07 23:39:48 david
- * insubstantial changes
- *
- * Revision 1.13 88/06/06 11:26:39 david
- * get rid of some obsolete code
- * change interface of set_field()
- *
- * Revision 1.12 88/06/05 22:21:36 david
- * local variables are now kept on a stack
- *
- * Revision 1.11 88/06/01 22:06:50 david
- * make sure incases of Node_param_list that the variable is looked up
- *
- * Revision 1.10 88/05/31 09:29:47 david
- * expunge Node_local_var
- *
- * Revision 1.9 88/05/30 09:52:55 david
- * be prepared for NULL return from make_regexp()
- * fix fatal() call
- *
- * Revision 1.8 88/05/26 22:48:48 david
- * fixed regexp matching code
- *
- * Revision 1.7 88/05/16 21:27:09 david
- * comment out obstack_free in interpret() -- it is done in do_file() anyway
- * and was definitely free'ing stuff it shouldn't have
- * change call of func_call() a bit
- * allow get_lhs to be called with other Node types -- return 0; used in
- * do_sub()
- *
- * Revision 1.6 88/05/13 22:00:03 david
- * generalized *_BINDING macros and moved them to awk.h
- * changes to function calling (mostly elsewhere)
- * put into use the Node_var_array type
- *
- * Revision 1.5 88/05/09 21:22:27 david
- * finally (I hope) got the code right in assign_number
- *
- * Revision 1.4 88/05/04 12:23:30 david
- * fflush(stdout) on prints if FAST not def'ed
- * all the assign_* cases were returning the wrong thing
- * fixed Node_in_array code
- * code in assign_number was freeing memory it shouldn't have
- *
- * Revision 1.3 88/04/15 13:12:38 david
- * additional error message
- *
- * Revision 1.2 88/04/12 16:03:24 david
- * fixed previously intoduced bug: all matches succeeded
- *
- * Revision 1.1 88/04/08 15:15:01 david
- * Initial revision
- * Revision 1.7 88/04/08 14:48:33 david changes from
- * Arnold Robbins
- *
- * Revision 1.6 88/03/28 14:13:50 david *** empty log message ***
- *
- * Revision 1.5 88/03/23 22:17:37 david mostly delinting -- a couple of bug
- * fixes
- *
- * Revision 1.4 88/03/18 21:00:10 david Baseline -- hoefully all the
- * functionality of the new awk added. Just debugging and tuning to do.
- *
- * Revision 1.3 87/11/14 15:16:21 david added user-defined functions with
- * return and do-while loops
- *
- * Revision 1.2 87/10/29 21:45:44 david added support for array membership
- * test, as in: if ("yes" in answers) ... this involved one more case: for
- * Node_in_array and rearrangment of the code in assoc_lookup, so that the
- * element can be located without being created
- *
- * Revision 1.1 87/10/27 15:23:28 david Initial revision
- *
- */
-
- /*
- * Copyright (C) 1986, 1988, 1989 the Free Software Foundation, Inc.
- *
- * This file is part of GAWK, the GNU implementation of the
- * AWK Progamming Language.
- *
- * GAWK is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 1, or (at your option)
- * any later version.
- *
- * GAWK is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GAWK; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
- #include "awk.h"
-
- extern void do_print();
- extern void do_printf();
- extern NODE *func_call();
- extern NODE *do_match();
- extern NODE *do_sub();
- extern NODE *do_getline();
- extern int in_array();
- extern void do_delete();
-
- extern double pow();
-
- static int eval_condition();
- static int is_a_number();
- static NODE *op_assign();
-
- NODE *_t; /* used as a temporary in macros */
- #ifdef MSDOS
- double _msc51bug; /* to get around a bug in MSC 5.1 */
- #endif
- NODE *_result; /* holds result of tree_eval, for possible freeing */
- NODE *ret_node;
-
- /* More of that debugging stuff */
- #ifdef DEBUG
- #define DBG_P(X) print_debug X
- #else
- #define DBG_P(X)
- #endif
-
- extern jmp_buf func_tag;
-
- /*
- * This table is used by the regexp routines to do case independant
- * matching. Basically, every ascii character maps to itself, except
- * uppercase letters map to lower case ones. This table has 256
- * entries, which may be overkill. Note also that if the system this
- * is compiled on doesn't use 7-bit ascii, casetable[] should not be
- * defined to the linker, so gawk should not load.
- *
- * Do NOT make this array static, it is used in several spots, not
- * just in this file.
- */
- #if 'a' == 97 /* it's ascii */
- char casetable[] = {
- '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
- '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
- '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
- '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
- /* ' ' '!' '"' '#' '$' '%' '&' ''' */
- '\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047',
- /* '(' ')' '*' '+' ',' '-' '.' '/' */
- '\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057',
- /* '0' '1' '2' '3' '4' '5' '6' '7' */
- '\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067',
- /* '8' '9' ':' ';' '<' '=' '>' '?' */
- '\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077',
- /* '@' 'A' 'B' 'C' 'D' 'E' 'F' 'G' */
- '\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
- /* 'H' 'I' 'J' 'K' 'L' 'M' 'N' 'O' */
- '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
- /* 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W' */
- '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
- /* 'X' 'Y' 'Z' '[' '\' ']' '^' '_' */
- '\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137',
- /* '`' 'a' 'b' 'c' 'd' 'e' 'f' 'g' */
- '\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
- /* 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' */
- '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
- /* 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' */
- '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
- /* 'x' 'y' 'z' '{' '|' '}' '~' */
- '\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177',
- '\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207',
- '\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217',
- '\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227',
- '\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237',
- '\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247',
- '\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257',
- '\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267',
- '\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277',
- '\300', '\301', '\302', '\303', '\304', '\305', '\306', '\307',
- '\310', '\311', '\312', '\313', '\314', '\315', '\316', '\317',
- '\320', '\321', '\322', '\323', '\324', '\325', '\326', '\327',
- '\330', '\331', '\332', '\333', '\334', '\335', '\336', '\337',
- '\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
- '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
- '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
- '\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377',
- };
- #else
- #include "You lose. You will need a translation table for your character set."
- #endif
-
- /*
- * Tree is a bunch of rules to run. Returns zero if it hit an exit()
- * statement
- */
- interpret(tree)
- NODE *tree;
- {
- register NODE *t = NULL; /* 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 */
-
- 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 NODE **fields_arr;
- extern int exiting, exit_val;
-
- /*
- * clean up temporary strings created by evaluating expressions in
- * previous recursive calls
- */
-
- if (tree == NULL)
- return 1;
- sourceline = tree->source_line;
- source = tree->source_file;
- switch (tree->type) {
- case Node_rule_list:
- for (t = tree; t != NULL; t = t->rnode) {
- tree = t->lnode;
- /* FALL THROUGH */
- case Node_rule_node:
- switch (_setjmp(rule_tag)) {
- case 0: /* normal non-jump */
- /* test pattern, if any */
- if (tree->lnode == NULL
- || eval_condition(tree->lnode)) {
- DBG_P(("Found a rule", tree->rnode));
- if (tree->rnode == NULL) {
- /*
- * special case: pattern with
- * no action is equivalent to
- * an action of {print}
- */
- NODE printnode;
-
- printnode.type = Node_K_print;
- printnode.lnode = NULL;
- printnode.rnode = NULL;
- do_print(&printnode);
- } else if (tree->rnode->type == Node_illegal) {
- /*
- * An empty statement
- * (``{ }'') is different
- * from a missing statement.
- * A missing statement is
- * equal to ``{ print }'' as
- * above, but an empty
- * statement is as in C, do
- * nothing.
- */
- } else
- (void) interpret(tree->rnode);
- }
- break;
- case TAG_CONTINUE: /* NEXT statement */
- return 1;
- case TAG_BREAK:
- return 0;
- }
- if (t == NULL)
- break;
- }
- break;
-
- case Node_statement_list:
- for (t = tree; t != NULL; t = t->rnode) {
- DBG_P(("Statements", t->lnode));
- (void) interpret(t->lnode);
- }
- break;
-
- case Node_K_if:
- DBG_P(("IF", tree->lnode));
- if (eval_condition(tree->lnode)) {
- DBG_P(("True", tree->rnode->lnode));
- (void) interpret(tree->rnode->lnode);
- } else {
- DBG_P(("False", tree->rnode->rnode));
- (void) interpret(tree->rnode->rnode);
- }
- break;
-
- case Node_K_while:
- PUSH_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
-
- DBG_P(("WHILE", tree->lnode));
- while (eval_condition(tree->lnode)) {
- switch (_setjmp(loop_tag)) {
- case 0: /* normal non-jump */
- DBG_P(("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, 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);
-
- do {
- switch (_setjmp(loop_tag)) {
- case 0: /* normal non-jump */
- DBG_P(("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, loop_tag_valid);
- return 1;
- default:
- cant_happen();
- }
- DBG_P(("WHILE", tree->lnode));
- } while (eval_condition(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);
-
- DBG_P(("FOR", tree->forloop->init));
- (void) interpret(tree->forloop->init);
-
- DBG_P(("FOR.WHILE", tree->forloop->cond));
- while (eval_condition(tree->forloop->cond)) {
- switch (_setjmp(loop_tag)) {
- case 0: /* normal non-jump */
- DBG_P(("FOR.DO", tree->lnode));
- (void) interpret(tree->lnode);
- /* fall through */
- case TAG_CONTINUE: /* continue statement */
- DBG_P(("FOR.INCR", tree->forloop->incr));
- (void) interpret(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:
- #define hakvar forloop->init
- #define arrvar forloop->incr
- PUSH_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
- DBG_P(("AFOR.VAR", tree->hakvar));
- lhs = get_lhs(tree->hakvar, 1);
- t = tree->arrvar;
- if (tree->arrvar->type == Node_param_list)
- t = stack_ptr[tree->arrvar->param_cnt];
- for (l = assoc_scan(t); l; l = assoc_next(l)) {
- deref = *lhs;
- do_deref();
- *lhs = dupnode(l->retval);
- if (field_num == 0)
- set_record(fields_arr[0]->stptr,
- fields_arr[0]->stlen);
- DBG_P(("AFOR.NEXTIS", *lhs));
- switch (_setjmp(loop_tag)) {
- case 0:
- DBG_P(("AFOR.DO", tree->lnode));
- (void) interpret(tree->lnode);
- case TAG_CONTINUE:
- break;
-
- case TAG_BREAK:
- RESTORE_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
- field_num = -1;
- return 1;
- default:
- cant_happen();
- }
- }
- field_num = -1;
- RESTORE_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
- break;
-
- case Node_K_break:
- DBG_P(("BREAK", NULL));
- if (loop_tag_valid == 0)
- fatal("unexpected break");
- _longjmp(loop_tag, TAG_BREAK);
- break;
-
- case Node_K_continue:
- DBG_P(("CONTINUE", NULL));
- if (loop_tag_valid == 0)
- fatal("unexpected continue");
- _longjmp(loop_tag, TAG_CONTINUE);
- break;
-
- case Node_K_print:
- DBG_P(("PRINT", tree));
- do_print(tree);
- break;
-
- case Node_K_printf:
- DBG_P(("PRINTF", tree));
- do_printf(tree);
- break;
-
- case Node_K_next:
- DBG_P(("NEXT", NULL));
- _longjmp(rule_tag, TAG_CONTINUE);
- break;
-
- case Node_K_exit:
- /*
- * 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.
- */
- DBG_P(("EXIT", NULL));
- exiting = 1;
- if (tree)
- exit_val = (int) force_number(tree_eval(tree->lnode));
- free_result();
- _longjmp(rule_tag, TAG_BREAK);
- break;
-
- case Node_K_return:
- DBG_P(("RETURN", NULL));
- ret_node = dupnode(tree_eval(tree->lnode));
- ret_node->flags |= TEMP;
- _longjmp(func_tag, TAG_RETURN);
- break;
-
- default:
- /*
- * Appears to be an expression statement. Throw away the
- * value.
- */
- DBG_P(("E", NULL));
- (void) tree_eval(tree);
- free_result();
- break;
- }
- return 1;
- }
-
- /* evaluate a subtree, allocating strings on a temporary stack. */
-
- NODE *
- r_tree_eval(tree)
- NODE *tree;
- {
- register NODE *r, *t1, *t2; /* return value & temporary subtrees */
- int i;
- register NODE **lhs;
- int di;
- AWKNUM x, x2;
- long lx;
- struct re_pattern_buffer *rp;
- extern NODE **fields_arr;
-
- if (tree->type != Node_var)
- source = tree->source_file;
- sourceline = tree->source_line;
- switch (tree->type) {
- case Node_and:
- DBG_P(("AND", tree));
- return tmp_number((AWKNUM) (eval_condition(tree->lnode)
- && eval_condition(tree->rnode)));
-
- case Node_or:
- DBG_P(("OR", tree));
- return tmp_number((AWKNUM) (eval_condition(tree->lnode)
- || eval_condition(tree->rnode)));
-
- case Node_not:
- DBG_P(("NOT", tree));
- return tmp_number((AWKNUM) ! eval_condition(tree->lnode));
-
- /* Builtins */
- case Node_builtin:
- DBG_P(("builtin", tree));
- return ((*tree->proc) (tree->subnode));
-
- case Node_K_getline:
- DBG_P(("GETLINE", tree));
- return (do_getline(tree));
-
- case Node_in_array:
- DBG_P(("IN_ARRAY", tree));
- return tmp_number((AWKNUM) in_array(tree->lnode, tree->rnode));
-
- case Node_K_match:
- DBG_P(("MATCH", tree));
- return do_match(tree);
-
- case Node_sub:
- case Node_gsub:
- DBG_P(("SUB", tree));
- return do_sub(tree);
-
- case Node_func_call:
- DBG_P(("func_call", tree));
- return func_call(tree->rnode, tree->lnode);
-
- case Node_K_delete:
- DBG_P(("DELETE", tree));
- do_delete(tree->lnode, tree->rnode);
- return Nnull_string;
-
- /* unary operations */
-
- case Node_var:
- case Node_var_array:
- case Node_param_list:
- case Node_subscript:
- case Node_field_spec:
- DBG_P(("var_type ref", tree));
- lhs = get_lhs(tree, 0);
- field_num = -1;
- deref = 0;
- return *lhs;
-
- case Node_unary_minus:
- DBG_P(("UMINUS", tree));
- x = -force_number(tree_eval(tree->subnode));
- free_result();
- return tmp_number(x);
-
- case Node_cond_exp:
- DBG_P(("?:", tree));
- if (eval_condition(tree->lnode)) {
- DBG_P(("True", tree->rnode->lnode));
- return tree_eval(tree->rnode->lnode);
- }
- DBG_P(("False", tree->rnode->rnode));
- return tree_eval(tree->rnode->rnode);
-
- case Node_match:
- case Node_nomatch:
- DBG_P(("ASSIGN_[no]match", tree));
- t1 = force_string(tree_eval(tree->lnode));
- if (tree->rnode->type == Node_regex) {
- rp = tree->rnode->rereg;
- if (!strict && ((IGNORECASE_node->var_value->numbr != 0)
- ^ (tree->rnode->re_case != 0))) {
- /* recompile since case sensitivity differs */
- rp = tree->rnode->rereg =
- mk_re_parse(tree->rnode->re_text,
- (IGNORECASE_node->var_value->numbr != 0));
- tree->rnode->re_case = (IGNORECASE_node->var_value->numbr != 0);
- }
- } else {
- rp = make_regexp(force_string(tree_eval(tree->rnode)),
- (IGNORECASE_node->var_value->numbr != 0));
- if (rp == NULL)
- cant_happen();
- }
- i = re_search(rp, t1->stptr, t1->stlen, 0, t1->stlen,
- (struct re_registers *) NULL);
- i = (i == -1) ^ (tree->type == Node_match);
- free_temp(t1);
- return tmp_number((AWKNUM) i);
-
- case Node_func:
- fatal("function `%s' called with space between name and (,\n%s",
- tree->lnode->param,
- "or used in other expression context");
-
- /* assignments */
- case Node_assign:
- DBG_P(("ASSIGN", tree));
- r = tree_eval(tree->rnode);
- lhs = get_lhs(tree->lnode, 1);
- *lhs = dupnode(r);
- do_deref();
- if (field_num == 0)
- set_record(fields_arr[0]->stptr, fields_arr[0]->stlen);
- field_num = -1;
- 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);
- }
-
- /*
- * 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:
- DBG_P(("CONCAT", tree));
- t1 = force_string(t1);
- t2 = force_string(t2);
-
- r = newnode(Node_val);
- r->flags = (STR|TEMP);
- r->stlen = t1->stlen + t2->stlen;
- r->stref = 1;
- emalloc(r->stptr, char *, r->stlen + 1, "tree_eval");
- bcopy(t1->stptr, r->stptr, t1->stlen);
- bcopy(t2->stptr, r->stptr + t1->stlen, t2->stlen);
- r->stptr[r->stlen] = '\0';
- free_temp(t1);
- free_temp(t2);
- return r;
-
- case Node_geq:
- case Node_leq:
- case Node_greater:
- case Node_less:
- case Node_notequal:
- case Node_equal:
- di = cmp_nodes(t1, t2);
- free_temp(t1);
- free_temp(t2);
- switch (tree->type) {
- case Node_equal:
- DBG_P(("EQUAL", tree));
- return tmp_number((AWKNUM) (di == 0));
- case Node_notequal:
- DBG_P(("NOT_EQUAL", tree));
- return tmp_number((AWKNUM) (di != 0));
- case Node_less:
- DBG_P(("LESS_THAN", tree));
- return tmp_number((AWKNUM) (di < 0));
- case Node_greater:
- DBG_P(("GREATER_THAN", tree));
- return tmp_number((AWKNUM) (di > 0));
- case Node_leq:
- DBG_P(("LESS_THAN_EQUAL", tree));
- return tmp_number((AWKNUM) (di <= 0));
- case Node_geq:
- DBG_P(("GREATER_THAN_EQUAL", tree));
- return tmp_number((AWKNUM) (di >= 0));
- }
- break;
- }
-
- (void) force_number(t1);
- (void) force_number(t2);
-
- switch (tree->type) {
- case Node_exp:
- DBG_P(("EXPONENT", tree));
- x = pow((double) t1->numbr, (double) t2->numbr);
- free_temp(t1);
- free_temp(t2);
- return tmp_number(x);
-
- case Node_times:
- DBG_P(("MULT", tree));
- x = t1->numbr * t2->numbr;
- free_temp(t1);
- free_temp(t2);
- return tmp_number(x);
-
- case Node_quotient:
- DBG_P(("DIVIDE", tree));
- x = t2->numbr;
- free_temp(t2);
- if (x == (AWKNUM) 0) {
- free_temp(t1);
- return tmp_number((AWKNUM) 0);
- } else {
- x = t1->numbr / x;
- free_temp(t1);
- return tmp_number(x);
- }
-
- case Node_mod:
- DBG_P(("MODULUS", tree));
- x = t2->numbr;
- free_temp(t2);
- if (x == (AWKNUM) 0) {
- free_temp(t1);
- return tmp_number((AWKNUM) 0);
- }
- lx = t1->numbr / x; /* assignment to long truncates */
- x2 = lx * x;
- x = t1->numbr - x2;
- free_temp(t1);
- return tmp_number(x);
-
- case Node_plus:
- DBG_P(("PLUS", tree));
- x = t1->numbr + t2->numbr;
- free_temp(t1);
- free_temp(t2);
- return tmp_number(x);
-
- case Node_minus:
- DBG_P(("MINUS", tree));
- x = t1->numbr - t2->numbr;
- free_temp(t1);
- free_temp(t2);
- return tmp_number(x);
-
- default:
- fatal("illegal type (%d) in tree_eval", tree->type);
- }
- return 0;
- }
-
- /*
- * This makes numeric operations slightly more efficient. Just change the
- * value of a numeric node, if possible
- */
- void
- assign_number(ptr, value)
- NODE **ptr;
- AWKNUM value;
- {
- extern NODE *deref;
- register NODE *n = *ptr;
-
- #ifdef DEBUG
- if (n->type != Node_val)
- cant_happen();
- #endif
- if (n == Nnull_string) {
- *ptr = make_number(value);
- deref = 0;
- return;
- }
- if (n->stref > 1) {
- *ptr = make_number(value);
- return;
- }
- if ((n->flags & STR) && (n->flags & (MALLOC|TEMP)))
- free(n->stptr);
- n->numbr = value;
- n->flags |= NUM;
- n->flags &= ~STR;
- n->stref = 0;
- deref = 0;
- }
-
-
- /* Is TREE true or false? Returns 0==false, non-zero==true */
- static int
- eval_condition(tree)
- NODE *tree;
- {
- register NODE *t1;
- int ret;
-
- if (tree == NULL) /* Null trees are the easiest kinds */
- return 1;
- 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 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
- */
-
- t1 = tree_eval(tree);
- if (t1->flags & STR)
- ret = t1->stlen != 0;
- else
- ret = t1->numbr != 0.0;
- free_temp(t1);
- return ret;
- }
-
- /*
- * 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.
- */
- static int
- 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 0; /* null string is not equal to 0 */
-
- 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);
- }
-
- int
- cmp_nodes(t1, t2)
- NODE *t1, *t2;
- {
- AWKNUM d;
- int ret;
- int len1, len2;
-
- if (t1 == t2)
- return 0;
- if ((t1->flags & NUM)) {
- if ((t2->flags & NUM))
- d = t1->numbr - t2->numbr;
- else if (is_a_number(t2->stptr))
- d = t1->numbr - force_number(t2);
- else {
- t1 = force_string(t1);
- goto strings;
- }
- if (d == 0.0) /* from profiling, this is most common */
- return 0;
- if (d > 0.0)
- return 1;
- return -1;
- }
- if ((t2->flags & NUM)) {
- if (is_a_number(t1->stptr))
- d = force_number(t1) - t2->numbr;
- else {
- t2 = force_string(t2);
- goto strings;
- }
- if (d == 0.0) /* from profiling, this is most common */
- return 0;
- if (d > 0.0)
- return 1;
- return -1;
- }
- if (is_a_number(t1->stptr) && is_a_number(t2->stptr)) {
- /*
- * following two statements are this way because force_number
- * is a macro
- */
- d = force_number(t1);
- d = d - force_number(t2);
- if (d == 0.0) /* from profiling, this is most common */
- return 0;
- if (d > 0.0)
- return 1;
- return -1;
- }
-
- strings:
- len1 = t1->stlen;
- len2 = t2->stlen;
- if (len1 == 0) {
- if (len2 == 0)
- return 0;
- else
- return -1;
- } else if (len2 == 0)
- return 1;
- ret = memcmp(t1->stptr, t2->stptr, len1 <= len2 ? len1 : len2);
- if (ret == 0 && len1 != len2)
- return len1 < len2 ? -1: 1;
- return ret;
- }
-
- #ifdef NOMEMCMP
- /*
- * memcmp --- compare strings.
- *
- * We use our own routine since it has to act like strcmp() for return
- * value, and the BSD manual says bcmp() only returns zero/non-zero.
- */
-
- static int
- memcmp (s1, s2, l)
- register char *s1, *s2;
- register int l;
- {
- for (; l--; s1++, s2++) {
- if (*s1 != *s2)
- return (*s1 - *s2);
- }
- return (*--s1 - *--s2);
- }
- #endif
-
- static NODE *
- op_assign(tree)
- NODE *tree;
- {
- AWKNUM rval, lval;
- NODE **lhs;
- AWKNUM t1, t2;
- long ltemp;
-
- lhs = get_lhs(tree->lnode, 1);
- lval = force_number(*lhs);
-
- switch(tree->type) {
- case Node_preincrement:
- case Node_predecrement:
- DBG_P(("+-X", tree));
- assign_number(lhs,
- lval + (tree->type == Node_preincrement ? 1.0 : -1.0));
- do_deref();
- if (field_num == 0)
- set_record(fields_arr[0]->stptr, fields_arr[0]->stlen);
- field_num = -1;
- return *lhs;
-
- case Node_postincrement:
- case Node_postdecrement:
- DBG_P(("X+-", tree));
- assign_number(lhs,
- lval + (tree->type == Node_postincrement ? 1.0 : -1.0));
- do_deref();
- if (field_num == 0)
- set_record(fields_arr[0]->stptr, fields_arr[0]->stlen);
- field_num = -1;
- return tmp_number(lval);
- }
-
- rval = force_number(tree_eval(tree->rnode));
- free_result();
- switch(tree->type) {
- case Node_assign_exp:
- DBG_P(("ASSIGN_exp", tree));
- assign_number(lhs, (AWKNUM) pow((double) lval, (double) rval));
- break;
-
- case Node_assign_times:
- DBG_P(("ASSIGN_times", tree));
- assign_number(lhs, lval * rval);
- break;
-
- case Node_assign_quotient:
- DBG_P(("ASSIGN_quotient", tree));
- assign_number(lhs, lval / rval);
- break;
-
- case Node_assign_mod:
- DBG_P(("ASSIGN_mod", tree));
- ltemp = lval / rval; /* assignment to long truncates */
- t1 = ltemp * rval;
- t2 = lval - t1;
- assign_number(lhs, t2);
- break;
-
- case Node_assign_plus:
- DBG_P(("ASSIGN_plus", tree));
- assign_number(lhs, lval + rval);
- break;
-
- case Node_assign_minus:
- DBG_P(("ASSIGN_minus", tree));
- assign_number(lhs, lval - rval);
- break;
- }
- do_deref();
- if (field_num == 0)
- set_record(fields_arr[0]->stptr, fields_arr[0]->stlen);
- field_num = -1;
- return *lhs;
- }
-
-