/* * eval.c - gawk bytecode interpreter */ /* * Copyright (C) 1986, 1988, 1989, 1991-2019, 2021, 2022, 2023, * the Free Software Foundation, Inc. * * This file is part of GAWK, the GNU implementation of the * AWK Programming 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 3 of the License, 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 this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "awk.h" #include NODE **fcall_list = NULL; long fcall_count = 0; int currule = 0; IOBUF *curfile = NULL; /* current data file */ bool exiting = false; int (*interpret)(INSTRUCTION *); #define MAX_EXEC_HOOKS 10 static int num_exec_hook = 0; static Func_pre_exec pre_execute[MAX_EXEC_HOOKS]; static Func_post_exec post_execute = NULL; static double fix_nan_sign(double left, double right, double result); extern void frame_popped(); int OFSlen; int ORSlen; int OFMTidx; int CONVFMTidx; static NODE *node_Boolean[2]; /* This rather ugly macro is for VMS C */ #ifdef C #undef C #endif #define C(c) ((char)c) /* * This table is used by the regexp routines to do case independent * matching. Basically, every ascii character maps to itself, except * uppercase letters map to lower case ones. This table has 256 * entries, for ISO 8859-1. 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. * * 6/2004: * This table is also used for IGNORECASE for == and !=, and index(). * Although with GLIBC, we could use tolower() everywhere and RE_ICASE * for the regex matcher, precomputing this table once gives us a * performance improvement. I also think it's better for portability * to non-GLIBC systems. All the world is not (yet :-) GNU/Linux. */ #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', /* Latin 1: */ /* * 4/2019: This is now overridden; in single byte locales * we call load_casetable from main and it fills in the values * based on the current locale. In particular, we want LC_ALL=C * to work correctly for values >= 0200. */ C('\200'), C('\201'), C('\202'), C('\203'), C('\204'), C('\205'), C('\206'), C('\207'), C('\210'), C('\211'), C('\212'), C('\213'), C('\214'), C('\215'), C('\216'), C('\217'), C('\220'), C('\221'), C('\222'), C('\223'), C('\224'), C('\225'), C('\226'), C('\227'), C('\230'), C('\231'), C('\232'), C('\233'), C('\234'), C('\235'), C('\236'), C('\237'), C('\240'), C('\241'), C('\242'), C('\243'), C('\244'), C('\245'), C('\246'), C('\247'), C('\250'), C('\251'), C('\252'), C('\253'), C('\254'), C('\255'), C('\256'), C('\257'), C('\260'), C('\261'), C('\262'), C('\263'), C('\264'), C('\265'), C('\266'), C('\267'), C('\270'), C('\271'), C('\272'), C('\273'), C('\274'), C('\275'), C('\276'), C('\277'), C('\340'), C('\341'), C('\342'), C('\343'), C('\344'), C('\345'), C('\346'), C('\347'), C('\350'), C('\351'), C('\352'), C('\353'), C('\354'), C('\355'), C('\356'), C('\357'), C('\360'), C('\361'), C('\362'), C('\363'), C('\364'), C('\365'), C('\366'), C('\327'), C('\370'), C('\371'), C('\372'), C('\373'), C('\374'), C('\375'), C('\376'), C('\337'), C('\340'), C('\341'), C('\342'), C('\343'), C('\344'), C('\345'), C('\346'), C('\347'), C('\350'), C('\351'), C('\352'), C('\353'), C('\354'), C('\355'), C('\356'), C('\357'), C('\360'), C('\361'), C('\362'), C('\363'), C('\364'), C('\365'), C('\366'), C('\367'), C('\370'), C('\371'), C('\372'), C('\373'), C('\374'), C('\375'), C('\376'), C('\377'), }; #elif defined(USE_EBCDIC) char casetable[] = { /*00 NU SH SX EX PF HT LC DL */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /*08 SM VT FF CR SO SI */ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, /*10 DE D1 D2 TM RS NL BS IL */ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /*18 CN EM CC C1 FS GS RS US */ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, /*20 DS SS FS BP LF EB EC */ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /*28 SM C2 EQ AK BL */ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, /*30 SY PN RS UC ET */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /*38 C3 D4 NK SU */ 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, /*40 SP */ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /*48 CENT . < ( + | */ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, /*50 & */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /*58 ! $ * ) ; ^ */ 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, /*60 - / */ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /*68 | , % _ > ? */ 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, /*70 */ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /*78 ` : # @ ' = " */ 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, /*80 a b c d e f g */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /*88 h i { */ 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, /*90 j k l m n o p */ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /*98 q r } */ 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, /*A0 ~ s t u v w x */ 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, /*A8 y z [ */ 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, /*B0 */ 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, /*B8 ] */ 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, /*C0 { A B C D E F G */ 0xC0, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /*C8 H I */ 0x88, 0x89, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, /*D0 } J K L M N O P */ 0xD0, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /*D8 Q R */ 0x98, 0x99, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, /*E0 \ S T U V W X */ 0xE0, 0xE1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, /*E8 Y Z */ 0xA8, 0xA9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, /*F0 0 1 2 3 4 5 6 7 */ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, /*F8 8 9 */ 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF }; #else #include "You lose. You will need a translation table for your character set." #endif #undef C /* load_casetable --- for a non-ASCII locale, redo the table */ void load_casetable(void) { #if defined(LC_CTYPE) int i; static bool loaded = false; if (loaded || do_traditional) return; loaded = true; #ifndef USE_EBCDIC /* use of isalpha is ok here (see is_alpha in awkgram.y) */ for (i = 0200; i <= 0377; i++) { if (isalpha(i) && islower(i) && i != toupper(i)) casetable[i] = toupper(i); else casetable[i] = i; } #endif #endif } /* * This table maps node types to strings for debugging. * KEEP IN SYNC WITH awk.h!!!! */ static const char *const nodetypes[] = { "Node_illegal", "Node_val", "Node_regex", "Node_dynregex", "Node_var", "Node_var_array", "Node_var_new", "Node_elem_new", "Node_param_list", "Node_func", "Node_ext_func", "Node_builtin_func", "Node_array_ref", "Node_array_tree", "Node_array_leaf", "Node_dump_array", "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_unary_plus", "+" }, { "Op_field_spec", "$" }, { "Op_not", "! " }, { "Op_assign", " = " }, { "Op_store_var", " = " }, { "Op_store_sub", " = " }, { "Op_store_field", " = " }, { "Op_store_field_exp", " = " }, { "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_return_from_eval", "return" }, { "Op_K_delete", "delete" }, { "Op_K_delete_loop", NULL }, { "Op_K_getline_redir", "getline" }, { "Op_K_getline", "getline" }, { "Op_K_nextfile", "nextfile" }, { "Op_K_namespace", "@namespace" }, { "Op_builtin", NULL }, { "Op_sub_builtin", NULL }, { "Op_ext_builtin", NULL }, { "Op_in_array", " in " }, { "Op_func_call", NULL }, { "Op_indirect_func_call", NULL }, { "Op_push", NULL }, { "Op_push_arg", NULL }, { "Op_push_arg_untyped", 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_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_subscript_assign", NULL }, { "Op_after_beginfile", NULL }, { "Op_after_endfile", NULL }, { "Op_func", NULL }, { "Op_comment", NULL }, { "Op_exec_count", NULL }, { "Op_breakpoint", NULL }, { "Op_lint", NULL }, { "Op_lint_plus", NULL }, { "Op_atexit", NULL }, { "Op_stop", NULL }, { "Op_token", NULL }, { "Op_symbol", NULL }, { "Op_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_parens", NULL }, { "Op_final --- this should never appear", NULL }, { NULL, NULL }, }; /* nodetype2str --- convert a node type into a printable value */ const char * nodetype2str(NODETYPE type) { static char buf[40]; if (type >= Node_illegal && type <= Node_final) return nodetypes[(int) type]; sprintf(buf, _("unknown nodetype %d"), (int) type); return buf; } /* opcode2str --- convert an 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; } /* op2str --- convert an opcode type to corresponding operator or keyword */ 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 * flags2str(int flagval) { static const struct flagtab values[] = { { MALLOC, "MALLOC" }, { STRING, "STRING" }, { STRCUR, "STRCUR" }, { NUMCUR, "NUMCUR" }, { NUMBER, "NUMBER" }, { USER_INPUT, "USER_INPUT" }, { BOOLVAL, "BOOL" }, { INTLSTR, "INTLSTR" }, { NUMINT, "NUMINT" }, { INTIND, "INTIND" }, { WSTRCUR, "WSTRCUR" }, { MPFN, "MPFN" }, { MPZN, "MPZN" }, { NO_EXT_SET, "NO_EXT_SET" }, { NULL_FIELD, "NULL_FIELD" }, { ARRAYMAXED, "ARRAYMAXED" }, { HALFHAT, "HALFHAT" }, { XARRAY, "XARRAY" }, { NUMCONSTSTR, "NUMCONSTSTR" }, { REGEX, "REGEX" }, { 0, NULL }, }; return genflags2str(flagval, values); } /* genflags2str --- general routine to convert a flag value to a string */ const char * genflags2str(int flagval, const struct flagtab *tab) { static char buffer[BUFSIZ]; char *sp; int i, space_left, space_needed; sp = buffer; space_left = BUFSIZ; for (i = 0; tab[i].name != NULL; i++) { if ((flagval & tab[i].val) != 0) { /* * note the trick, we want 1 or 0 for whether we need * the '|' character. */ space_needed = (strlen(tab[i].name) + (sp != buffer)); if (space_left <= space_needed) fatal(_("buffer overflow in genflags2str")); if (sp != buffer) { *sp++ = '|'; space_left--; } strcpy(sp, tab[i].name); /* note ordering! */ space_left -= strlen(sp); sp += strlen(sp); } } *sp = '\0'; return buffer; } /* posix_compare --- compare strings using strcoll */ static int posix_compare(NODE *s1, NODE *s2) { int ret; if (gawk_mb_cur_max == 1) { char save1, save2; const char *p1, *p2; save1 = s1->stptr[s1->stlen]; s1->stptr[s1->stlen] = '\0'; save2 = s2->stptr[s2->stlen]; s2->stptr[s2->stlen] = '\0'; p1 = s1->stptr; p2 = s2->stptr; for (;;) { size_t len; ret = strcoll(p1, p2); if (ret != 0) break; len = strlen(p1); p1 += len + 1; p2 += len + 1; if (p1 == s1->stptr + s1->stlen + 1) { if (p2 != s2->stptr + s2->stlen + 1) ret = -1; break; } if (p2 == s2->stptr + s2->stlen + 1) { ret = 1; break; } } s1->stptr[s1->stlen] = save1; s2->stptr[s2->stlen] = save2; } else { /* Similar logic, using wide characters */ const wchar_t *p1, *p2; (void) force_wstring(s1); (void) force_wstring(s2); p1 = s1->wstptr; p2 = s2->wstptr; for (;;) { size_t len; ret = wcscoll(p1, p2); if (ret != 0) break; len = wcslen(p1); p1 += len + 1; p2 += len + 1; if (p1 == s1->wstptr + s1->wstlen + 1) { if (p2 != s2->wstptr + s2->wstlen + 1) ret = -1; break; } if (p2 == s2->wstptr + s2->wstlen + 1) { ret = 1; break; } } } return ret; } /* cmp_nodes --- compare two nodes, returning negative, 0, positive */ int cmp_nodes(NODE *t1, NODE *t2, bool use_strcmp) { int ret = 0; size_t len1, len2; int l, ldiff; if (t1 == t2) return 0; (void) fixtype(t1); (void) fixtype(t2); if ((t1->flags & NUMBER) != 0 && (t2->flags & NUMBER) != 0) return cmp_numbers(t1, t2); (void) force_string(t1); (void) force_string(t2); len1 = t1->stlen; len2 = t2->stlen; ldiff = len1 - len2; if (len1 == 0 || len2 == 0) return ldiff; if (do_posix && ! use_strcmp) return posix_compare(t1, t2); l = (ldiff <= 0 ? len1 : len2); if (IGNORECASE) { const unsigned char *cp1 = (const unsigned char *) t1->stptr; const unsigned char *cp2 = (const unsigned char *) t2->stptr; char save1 = t1->stptr[t1->stlen]; char save2 = t2->stptr[t2->stlen]; if (gawk_mb_cur_max > 1) { t1->stptr[t1->stlen] = t2->stptr[t2->stlen] = '\0'; ret = strncasecmpmbs((const unsigned char *) cp1, (const unsigned char *) cp2, l); t1->stptr[t1->stlen] = save1; t2->stptr[t2->stlen] = save2; } else { /* Could use tolower() here; see discussion above. */ for (ret = 0; l-- > 0 && ret == 0; cp1++, cp2++) ret = casetable[*cp1] - casetable[*cp2]; } } else ret = memcmp(t1->stptr, t2->stptr, l); ret = ret == 0 ? ldiff : ret; return ret; } /* push_frame --- push a frame NODE onto stack */ static void push_frame(NODE *f) { static long max_fcall; /* NB: frame numbering scheme as in GDB. frame_ptr => frame #0. */ 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"); } if (fcall_count > 1) memmove(fcall_list + 2, fcall_list + 1, (fcall_count - 1) * sizeof(NODE *)); fcall_list[1] = f; } /* pop_frame --- pop off a frame NODE*/ static void pop_frame() { if (fcall_count > 1) memmove(fcall_list + 1, fcall_list + 2, (fcall_count - 1) * sizeof(NODE *)); fcall_count--; assert(fcall_count >= 0); if (do_debug) frame_popped(); } /* dump_fcall_stack --- print a backtrace of the awk function calls */ void dump_fcall_stack(FILE *fp) { NODE *f, *func; long i = 0, k = 0; if (fcall_count == 0) return; fprintf(fp, _("\n\t# Function Call Stack:\n\n")); /* current frame */ func = frame_ptr->func_node; fprintf(fp, "\t# %3ld. %s\n", k++, func->vname); /* 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", k++, func->vname); } fprintf(fp, "\t# %3ld. -- main --\n", k); } /* set_IGNORECASE --- update IGNORECASE as appropriate */ void set_IGNORECASE() { static bool warned = false; if ((do_lint_extensions || do_traditional) && ! warned) { warned = true; lintwarn(_("`IGNORECASE' is a gawk extension")); } if (do_traditional) IGNORECASE = false; else IGNORECASE = boolval(IGNORECASE_node->var_value); set_RS(); /* set_RS() calls set_FS() if need be, for us */ } /* set_BINMODE --- set translation mode (OS/2, DOS, others) */ void set_BINMODE() { static bool warned = false; char *p; NODE *v = fixtype(BINMODE_node->var_value); if ((do_lint_extensions || do_traditional) && ! warned) { warned = true; lintwarn(_("`BINMODE' is a gawk extension")); } if (do_traditional) BINMODE = TEXT_TRANSLATE; else if ((v->flags & NUMBER) != 0) { BINMODE = get_number_si(v); /* Make sure the value is rational. */ if (BINMODE < TEXT_TRANSLATE) BINMODE = TEXT_TRANSLATE; else if (BINMODE > BINMODE_BOTH) BINMODE = BINMODE_BOTH; } else if ((v->flags & STRING) != 0) { p = v->stptr; /* * Allow only one of the following: * "0", "1", "2", "3", * "r", "w", "rw", "wr" * ANYTHING ELSE goes to 3. So there. */ switch (v->stlen) { case 1: switch (p[0]) { case '0': case '1': case '2': case '3': BINMODE = p[0] - '0'; break; case 'r': BINMODE = BINMODE_INPUT; break; case 'w': BINMODE = BINMODE_OUTPUT; break; default: BINMODE = BINMODE_BOTH; goto bad_value; break; } break; case 2: switch (p[0]) { case 'r': BINMODE = BINMODE_BOTH; if (p[1] != 'w') goto bad_value; break; case 'w': BINMODE = BINMODE_BOTH; if (p[1] != 'r') goto bad_value; break; } break; default: bad_value: lintwarn(_("BINMODE value `%s' is invalid, treated as 3"), p); break; } } else BINMODE = 3; /* shouldn't happen */ } /* set_OFS --- update OFS related variables when OFS assigned to */ void set_OFS() { static bool first = true; size_t new_ofs_len; if (first) /* true when called from init_vars() in main() */ first = false; else { /* rebuild $0 using OFS that was current when $0 changed */ if (! field0_valid) { get_field(UNLIMITED - 1, NULL); rebuild_record(); } } /* * Save OFS value for use in building record and in printing. * Can't just have OFS point into the OFS_node since it's * already updated when we come into this routine, and we need * the old value to rebuild the record (see above). */ OFS_node->var_value = force_string(OFS_node->var_value); new_ofs_len = OFS_node->var_value->stlen; if (OFS == NULL) emalloc(OFS, char *, new_ofs_len + 1, "set_OFS"); else if (OFSlen < new_ofs_len) erealloc(OFS, char *, new_ofs_len + 1, "set_OFS"); memcpy(OFS, OFS_node->var_value->stptr, OFS_node->var_value->stlen); OFSlen = new_ofs_len; OFS[OFSlen] = '\0'; } /* set_ORS --- update ORS related variables when ORS assigned to */ void set_ORS() { ORS_node->var_value = force_string(ORS_node->var_value); ORS = ORS_node->var_value->stptr; ORSlen = ORS_node->var_value->stlen; } /* fmt_ok --- is the conversion format a valid one? */ NODE **fmt_list = NULL; static int fmt_ok(NODE *n); static int fmt_index(NODE *n); static int fmt_ok(NODE *n) { NODE *tmp = force_string(n); const char *p = tmp->stptr; #if ! defined(PRINTF_HAS_F_FORMAT) || PRINTF_HAS_F_FORMAT != 1 static const char float_formats[] = "efgEG"; #else static const char float_formats[] = "efgEFG"; #endif #if defined(HAVE_LOCALE_H) static const char flags[] = " +-#'"; #else static const char flags[] = " +-#"; #endif // We rely on the caller to zero-terminate n->stptr. if (*p++ != '%') return 0; while (*p && strchr(flags, *p) != NULL) /* flags */ p++; while (*p && isdigit((unsigned char) *p)) /* width - %*.*g is NOT allowed */ p++; if (*p == '\0' || (*p != '.' && ! isdigit((unsigned char) *p))) return 0; if (*p == '.') p++; while (*p && isdigit((unsigned char) *p)) /* precision */ p++; if (*p == '\0' || strchr(float_formats, *p) == NULL) return 0; if (*++p != '\0') return 0; return 1; } /* fmt_index --- track values of OFMT and CONVFMT to keep semantics correct */ static int fmt_index(NODE *n) { int ix = 0; static int fmt_num = 4; static int fmt_hiwater = 0; char save; if (fmt_list == NULL) emalloc(fmt_list, NODE **, fmt_num*sizeof(*fmt_list), "fmt_index"); n = force_string(n); save = n->stptr[n->stlen]; n->stptr[n->stlen] = '\0'; while (ix < fmt_hiwater) { if (cmp_nodes(fmt_list[ix], n, true) == 0) return ix; ix++; } /* not found */ if (do_lint && ! fmt_ok(n)) lintwarn(_("bad `%sFMT' specification `%s'"), n == CONVFMT_node->var_value ? "CONV" : n == OFMT_node->var_value ? "O" : "", n->stptr); n->stptr[n->stlen] = save; if (fmt_hiwater >= fmt_num) { fmt_num *= 2; erealloc(fmt_list, NODE **, fmt_num * sizeof(*fmt_list), "fmt_index"); } fmt_list[fmt_hiwater] = dupnode(n); return fmt_hiwater++; } /* set_OFMT --- track OFMT correctly */ void set_OFMT() { OFMTidx = fmt_index(OFMT_node->var_value); OFMT = fmt_list[OFMTidx]->stptr; } /* set_CONVFMT --- track CONVFMT correctly */ void set_CONVFMT() { CONVFMTidx = fmt_index(CONVFMT_node->var_value); CONVFMT = fmt_list[CONVFMTidx]->stptr; } /* set_LINT --- update LINT as appropriate */ void set_LINT() { #ifndef NO_LINT int old_lint = do_lint; NODE *n = fixtype(LINT_node->var_value); /* start with clean defaults */ lintfunc = r_warning; do_flags &= ~(DO_LINT_ALL|DO_LINT_INVALID); if ((n->flags & STRING) != 0) { const char *lintval; size_t lintlen; lintval = n->stptr; lintlen = n->stlen; if (lintlen > 0) { if (lintlen == 7 && strncmp(lintval, "invalid", 7) == 0) do_flags |= DO_LINT_INVALID; else if (lintlen == 6 && strncmp(lintval, "no-ext", 6) == 0) do_flags &= ~DO_LINT_EXTENSIONS; else { do_flags |= DO_LINT_ALL; if (lintlen == 5 && strncmp(lintval, "fatal", 5) == 0) lintfunc = r_fatal; } } } else { if (! is_zero(n)) do_flags |= DO_LINT_ALL; } /* explicitly use warning() here, in case lintfunc == r_fatal */ if (old_lint != do_lint && old_lint && ! do_lint) warning(_("turning off `--lint' due to assignment to `LINT'")); /* inform plug-in api of change */ update_ext_api(); #endif /* ! NO_LINT */ } /* set_TEXTDOMAIN --- update TEXTDOMAIN variable when TEXTDOMAIN assigned to */ void set_TEXTDOMAIN() { NODE *tmp; tmp = TEXTDOMAIN_node->var_value = force_string(TEXTDOMAIN_node->var_value); TEXTDOMAIN = tmp->stptr; /* * Note: don't call textdomain(); this value is for * the awk program, not for gawk itself. */ } /* update_ERRNO_int --- update the value of ERRNO based on argument */ void update_ERRNO_int(int errcode) { const char *cp; update_PROCINFO_num("errno", errcode); if (errcode) { cp = strerror(errcode); cp = gettext(cp); } else cp = ""; unref(ERRNO_node->var_value); ERRNO_node->var_value = make_string(cp, strlen(cp)); } /* update_ERRNO_string --- update ERRNO */ void update_ERRNO_string(const char *string) { update_PROCINFO_num("errno", 0); unref(ERRNO_node->var_value); size_t len = strlen(string); #if defined(USE_EBCDIC) && defined(ELIDE_IBM_ERROR_CODE) // skip over leading IBM error code // N.B. This code is untested if (isupper(string[0]) && isupper(string[1])) { while (*string && *string != ' ') string++; while (*string && *string == ' ') string++; len = strlen(string); if (string[len-1] == '.') len--; // remove the final '.' } #endif ERRNO_node->var_value = make_string(string, len); } /* unset_ERRNO --- eliminate the value of ERRNO */ void unset_ERRNO(void) { update_PROCINFO_num("errno", 0); unref(ERRNO_node->var_value); ERRNO_node->var_value = dupnode(Nnull_string); } /* update_NR --- update the value of NR */ void update_NR() { #ifdef HAVE_MPFR if (is_mpg_number(NR_node->var_value)) (void) mpg_update_var(NR_node); else #endif if (NR_node->var_value->numbr != NR) { unref(NR_node->var_value); NR_node->var_value = make_number(NR); } } /* update_NF --- update the value of NF */ void update_NF() { long l; l = get_number_si(NF_node->var_value); if (NF == -1 || l != NF) { if (NF == -1) (void) get_field(UNLIMITED - 1, NULL); /* parse record */ unref(NF_node->var_value); NF_node->var_value = make_number(NF); } } /* update_FNR --- update the value of FNR */ void update_FNR() { #ifdef HAVE_MPFR if (is_mpg_number(FNR_node->var_value)) (void) mpg_update_var(FNR_node); else #endif if (FNR_node->var_value->numbr != FNR) { unref(FNR_node->var_value); FNR_node->var_value = make_number(FNR); } } 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() { 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, bool reference) { bool isparam = false; if (n->type == Node_param_list) { 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)); return NULL; // silence compiler warnings 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)); if (n->orig_array->type != Node_var) { n->orig_array->type = Node_var; n->orig_array->var_value = dupnode(Nnull_string); } /* fall through */ case Node_var_new: n->type = Node_var; n->var_value = dupnode(Nnull_string); break; case Node_elem_new: efree(n->stptr); n->stptr = NULL; n->stlen = 0; n->type = Node_var; n->var_value = dupnode(Nnull_string); break; case Node_var: break; default: cant_happen("unexpected variable type %s", nodetype2str(n->type)); } 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 */ NODE ** r_get_field(NODE *n, Func_ptr *assign, bool reference) { long field_num; NODE **lhs; if (assign) *assign = NULL; if (do_lint) { if ((fixtype(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")); } } (void) force_number(n); field_num = get_number_si(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)->flags & NULL_FIELD) != 0) 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 INSTRUCTION * setup_frame(INSTRUCTION *pc) { NODE *r = NULL; NODE *m, *f, *fp; NODE **sp = NULL; int pcount, arg_count, i, j; f = pc->func_body; pcount = f->param_cnt; fp = f->fparms; arg_count = (pc + 1)->expr_count; if (pcount > 0) { ezalloc(sp, NODE **, pcount * sizeof(NODE *), "setup_frame"); } /* 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); } for (i = 0, j = arg_count - 1; i < pcount; i++, j--) { getnode(r); memset(r, 0, sizeof(NODE)); sp[i] = r; if (i >= arg_count) { /* local variable */ r->type = Node_var_new; r->vname = fp[i].param; continue; } m = PEEK(j); /* arguments in reverse order on runtime stack */ if (m->type == Node_param_list) m = GET_PARAM(m->param_cnt); /* $0 needs to be passed by value to a function */ if (m == fields_arr[0]) { DEREF(m); m = dupnode(m); } switch (m->type) { case Node_var_new: case Node_var_array: case Node_elem_new: 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_var: /* Untyped (Node_var_new) variable as param became a * scalar during evaluation of expression for a * subsequent param. */ r->type = Node_var; r->var_value = dupnode(Nnull_string); break; case Node_val: r->type = Node_var; r->var_value = m; break; case Node_func: case Node_builtin_func: case Node_ext_func: r->type = Node_var; r->var_value = make_string(m->vname, strlen(m->vname)); break; default: cant_happen("unexpected parameter type %s", nodetype2str(m->type)); } r->vname = fp[i].param; } stack_adj(-arg_count); /* adjust stack pointer */ if (pc->opcode == Op_indirect_func_call) { r = POP(); /* indirect var */ DEREF(r); } frame_ptr->vname = source; /* save current source */ if (do_profile || do_debug) 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->prev_frame_size = (stack_ptr - stack_bottom); /* size of the previous stack frame */ frame_ptr->func_node = f; frame_ptr->vname = NULL; frame_ptr->reti = pc; /* on return execute pc->nexti */ return f->code_ptr; } /* 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->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 = frame_ptr->reti; /* execution in calling frame * resumes from ri->nexti. */ freenode(frame_ptr); if (do_profile || do_debug) pop_frame(); /* restore frame */ frame_ptr = fp; /* restore source */ source = fp->vname; fp->vname = NULL; return ri->nexti; } /* free_arrayfor --- free 'for (var in array)' related data */ static inline void free_arrayfor(NODE *r) { if (r->for_list != NULL) { NODE *n; size_t num_elems = r->for_list_size; NODE **list = r->for_list; while (num_elems > 0) { n = list[--num_elems]; unref(n); } efree(list); } freenode(r); } /* * unwind_stack --- pop items off the run-time stack; * 'n' is the # of items left in the stack. */ INSTRUCTION * unwind_stack(long n) { NODE *r; INSTRUCTION *cp = NULL; STACK_ITEM *sp; if (stack_empty()) return NULL; sp = stack_bottom + n; if (stack_ptr < sp) return NULL; while ((r = POP()) != NULL) { switch (r->type) { case Node_frame: cp = restore_frame(r); break; case Node_arrayfor: free_arrayfor(r); break; case Node_val: DEREF(r); break; case Node_instruction: freenode(r); break; default: /* * Check `exiting' and don't produce an error for * cases like: * func _fn0() { exit } * BEGIN { ARRAY[_fn0()] } */ if (in_main_context() && ! exiting) 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; } if (stack_ptr < sp) break; } return cp; } /* pop_fcall --- pop off the innermost frame */ #define pop_fcall() unwind_stack(frame_ptr->prev_frame_size) /* pop the run-time stack */ #define pop_stack() (void) unwind_stack(0) static inline bool eval_condition(NODE *t) { if (t == node_Boolean[false]) return false; if (t == node_Boolean[true]) return true; return boolval(t); } static bool cmp_doubles(const NODE *t1, const NODE *t2, scalar_cmp_t comparison_type); extern bool mpg_cmp_as_numbers(const NODE *t1, const NODE *t2, scalar_cmp_t comparison_type); /* cmp_scalars -- compare two nodes on the stack */ static bool cmp_scalars(scalar_cmp_t comparison_type) { NODE *t1, *t2; int di; bool ret; t2 = POP_SCALAR(); t1 = TOP(); t1 = elem_new_to_scalar(t1); t2 = elem_new_to_scalar(t2); if (t1->type == Node_var_array) { DEREF(t2); fatal(_("attempt to use array `%s' in a scalar context"), array_vname(t1)); } if ((t1->flags & STRING) != 0 || (t2->flags & STRING) != 0) { bool use_strcmp = (comparison_type == SCALAR_EQ || comparison_type == SCALAR_NEQ); di = cmp_nodes(t1, t2, use_strcmp); switch (comparison_type) { case SCALAR_EQ: ret = (di == 0); break; case SCALAR_NEQ: ret = (di != 0); break; case SCALAR_LT: ret = (di < 0); break; case SCALAR_LE: ret = (di <= 0); break; case SCALAR_GT: ret = (di > 0); break; case SCALAR_GE: ret = (di >= 0); break; } } else { fixtype(t1); fixtype(t2); #ifdef HAVE_MPFR if (do_mpfr) ret = mpg_cmp_as_numbers(t1, t2, comparison_type); else #endif ret = cmp_doubles(t1, t2, comparison_type); } DEREF(t1); DEREF(t2); return ret; } /* cmp_doubles --- compare two doubles */ static bool cmp_doubles(const NODE *t1, const NODE *t2, scalar_cmp_t comparison_type) { /* * This routine provides numeric comparisons that should work * the same as in C. It should NOT be used for sorting. */ bool t1_nan = isnan(t1->numbr); bool t2_nan = isnan(t2->numbr); int ret; if ((t1_nan || t2_nan) && comparison_type != SCALAR_NEQ) return false; switch (comparison_type) { case SCALAR_EQ: ret = (t1->numbr == t2->numbr); break; case SCALAR_NEQ: ret = (t1->numbr != t2->numbr); break; case SCALAR_LT: ret = (t1->numbr < t2->numbr); break; case SCALAR_LE: ret = (t1->numbr <= t2->numbr); break; case SCALAR_GT: ret = (t1->numbr > t2->numbr); break; case SCALAR_GE: ret = (t1->numbr >= t2->numbr); break; } return ret; } /* op_assign --- assignment operators excluding = */ static void op_assign(OPCODE op) { NODE **lhs; NODE *t1, *t2; AWKNUM x = 0.0, x1, x2; lhs = POP_ADDRESS(); t1 = *lhs; x1 = force_number(t1)->numbr; t2 = TOP_SCALAR(); x2 = force_number(t2)->numbr; DEREF(t2); switch (op) { case Op_assign_plus: x = x1 + x2; break; case Op_assign_minus: x = x1 - x2; break; case Op_assign_times: x = x1 * x2; break; case Op_assign_quotient: if (x2 == (AWKNUM) 0) { decr_sp(); fatal(_("division by zero attempted in `/='")); } x = x1 / x2; break; case Op_assign_mod: if (x2 == (AWKNUM) 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 - x2 * x; #endif /* ! HAVE_FMOD */ break; case Op_assign_exp: x = calc_exp((double) x1, (double) x2); break; default: break; } if (t1->valref == 1 && t1->flags == (MALLOC|NUMCUR|NUMBER)) { /* optimization */ t1->numbr = x; } else { unref(t1); t1 = *lhs = make_number(x); } UPREF(t1); REPLACE(t1); } /* 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; } /* * Implementation of BEGINFILE and ENDFILE requires saving an execution * state and the ability to return to that state. The state is * defined by the instruction triggering the BEGINFILE/ENDFILE rule, the * run-time stack, the rule and the source file. The source line is available in * the instruction and hence is not considered a part of the execution state. */ typedef struct exec_state { struct exec_state *next; INSTRUCTION *cptr; /* either getline (Op_K_getline) or the * implicit "open-file, read-record" loop (Op_newfile). */ int rule; /* rule for the INSTRUCTION */ long stack_size; /* For this particular usage, it is sufficient to save * only the size of the call stack. We do not * store the actual stack pointer to avoid problems * in case the stack gets realloc-ed. */ const char *source; /* source file for the INSTRUCTION */ } EXEC_STATE; static EXEC_STATE exec_state_stack; /* push_exec_state --- save an execution state on stack */ static void push_exec_state(INSTRUCTION *cp, int rule, char *src, STACK_ITEM *sp) { EXEC_STATE *es; emalloc(es, EXEC_STATE *, sizeof(EXEC_STATE), "push_exec_state"); es->rule = rule; es->cptr = cp; es->stack_size = (sp - stack_bottom) + 1; es->source = src; es->next = exec_state_stack.next; exec_state_stack.next = es; } /* pop_exec_state --- pop one execution state off the stack */ static INSTRUCTION * pop_exec_state(int *rule, char **src, long *sz) { INSTRUCTION *cp; EXEC_STATE *es; es = exec_state_stack.next; if (es == NULL) return NULL; cp = es->cptr; if (rule != NULL) *rule = es->rule; if (src != NULL) *src = (char *) es->source; if (sz != NULL) *sz = es->stack_size; exec_state_stack.next = es->next; efree(es); return cp; } /* register_exec_hook --- add exec hooks in the interpreter. */ int register_exec_hook(Func_pre_exec preh, Func_post_exec posth) { int pos = 0; /* * multiple post-exec hooks aren't supported. post-exec hook is mainly * for use by the debugger. */ if (! preh || (post_execute && posth)) return false; if (num_exec_hook == MAX_EXEC_HOOKS) return false; /* * Add to the beginning of the array but do not displace the * debugger hook if it exists. */ if (num_exec_hook > 0) { pos = !! do_debug; if (num_exec_hook > pos) memmove(pre_execute + pos + 1, pre_execute + pos, (num_exec_hook - pos) * sizeof (preh)); } pre_execute[pos] = preh; num_exec_hook++; if (posth) post_execute = posth; return true; } /* interpreter routine when not debugging */ #include "interpret.h" /* interpreter routine with exec hook(s). Used when debugging and/or with MPFR. */ #define r_interpret h_interpret #define EXEC_HOOK 1 #include "interpret.h" #undef EXEC_HOOK #undef r_interpret void init_interpret() { long newval; if ((newval = getenv_long("GAWK_STACKSIZE")) > 0) STACK_SIZE = newval; 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; /* initialize true and false nodes */ node_Boolean[false] = make_number(0.0); node_Boolean[true] = make_number(1.0); if (! is_mpg_number(node_Boolean[false])) { node_Boolean[false]->flags |= NUMINT; node_Boolean[true]->flags |= NUMINT; } /* * Select the interpreter routine. The version without * any exec hook support (r_interpret) is faster by about * 5%, or more depending on the opcodes. */ if (num_exec_hook > 0) interpret = h_interpret; else interpret = r_interpret; } /* elem_new_to_scalar --- convert Node_elem_new to untyped scalar */ NODE * elem_new_to_scalar(NODE *n) { if (n->type != Node_elem_new) return n; if (n->valref > 1) { unref(n); return dupnode(Nnull_string); } n->type = Node_val; return n; } /* fix_nan_sign --- fix NaN sign on RiscV */ // See the thread starting at // https://lists.gnu.org/archive/html/bug-gawk/2022-09/msg00005.html // for why we need this function. static double fix_nan_sign(double left, double right, double result) { if (isnan(left) && signbit(left)) return copysign(result, -1.0); else if (isnan(right) && signbit(right)) return copysign(result, -1.0); else return result; }