xref: /aosp_15_r20/external/toybox/toys/pending/awk.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
1*cf5a6c84SAndroid Build Coastguard Worker /* awk.c - An awk implementation.
2*cf5a6c84SAndroid Build Coastguard Worker  * vi: tabstop=2 softtabstop=2 shiftwidth=2
3*cf5a6c84SAndroid Build Coastguard Worker  *
4*cf5a6c84SAndroid Build Coastguard Worker  * Copyright 2024 Ray Gardner <[email protected]>
5*cf5a6c84SAndroid Build Coastguard Worker  *
6*cf5a6c84SAndroid Build Coastguard Worker  * See https://pubs.opengroup.org/onlinepubs/9799919799/utilities/awk.html
7*cf5a6c84SAndroid Build Coastguard Worker  *
8*cf5a6c84SAndroid Build Coastguard Worker  * Deviations from posix: Don't handle LANG, LC_ALL, etc.
9*cf5a6c84SAndroid Build Coastguard Worker  *   Accept regex for RS
10*cf5a6c84SAndroid Build Coastguard Worker  *   Bitwise functions (from gawk): and, or, xor, lshift, rshift
11*cf5a6c84SAndroid Build Coastguard Worker  *   Attempt to follow tradition (nawk, gawk) where it departs from posix
12*cf5a6c84SAndroid Build Coastguard Worker  *
13*cf5a6c84SAndroid Build Coastguard Worker  * TODO: Lazy field splitting; improve performance; more testing/debugging
14*cf5a6c84SAndroid Build Coastguard Worker 
15*cf5a6c84SAndroid Build Coastguard Worker USE_AWK(NEWTOY(awk, "F:v*f*bc", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LINEBUF))
16*cf5a6c84SAndroid Build Coastguard Worker 
17*cf5a6c84SAndroid Build Coastguard Worker config AWK
18*cf5a6c84SAndroid Build Coastguard Worker   bool "awk"
19*cf5a6c84SAndroid Build Coastguard Worker   default n
20*cf5a6c84SAndroid Build Coastguard Worker   help
21*cf5a6c84SAndroid Build Coastguard Worker     usage:  awk [-F sepstring] [-v assignment]... program [argument...]
22*cf5a6c84SAndroid Build Coastguard Worker       or:
23*cf5a6c84SAndroid Build Coastguard Worker             awk [-F sepstring] -f progfile [-f progfile]... [-v assignment]...
24*cf5a6c84SAndroid Build Coastguard Worker                   [argument...]
25*cf5a6c84SAndroid Build Coastguard Worker       also:
26*cf5a6c84SAndroid Build Coastguard Worker       -b : count bytes, not characters (experimental)
27*cf5a6c84SAndroid Build Coastguard Worker       -c : compile only, do not run
28*cf5a6c84SAndroid Build Coastguard Worker */
29*cf5a6c84SAndroid Build Coastguard Worker 
30*cf5a6c84SAndroid Build Coastguard Worker #define FOR_awk
31*cf5a6c84SAndroid Build Coastguard Worker #include "toys.h"
32*cf5a6c84SAndroid Build Coastguard Worker 
33*cf5a6c84SAndroid Build Coastguard Worker GLOBALS(
34*cf5a6c84SAndroid Build Coastguard Worker   struct arg_list *f;
35*cf5a6c84SAndroid Build Coastguard Worker   struct arg_list *v;
36*cf5a6c84SAndroid Build Coastguard Worker   char *F;
37*cf5a6c84SAndroid Build Coastguard Worker 
38*cf5a6c84SAndroid Build Coastguard Worker   struct scanner_state {
39*cf5a6c84SAndroid Build Coastguard Worker       char *p;
40*cf5a6c84SAndroid Build Coastguard Worker       char *progstring;
41*cf5a6c84SAndroid Build Coastguard Worker       struct arg_list *prog_args;
42*cf5a6c84SAndroid Build Coastguard Worker       char *filename;
43*cf5a6c84SAndroid Build Coastguard Worker       char *line;
44*cf5a6c84SAndroid Build Coastguard Worker       size_t line_size;
45*cf5a6c84SAndroid Build Coastguard Worker       ssize_t line_len;
46*cf5a6c84SAndroid Build Coastguard Worker       int line_num;
47*cf5a6c84SAndroid Build Coastguard Worker       int ch;
48*cf5a6c84SAndroid Build Coastguard Worker       FILE *fp;
49*cf5a6c84SAndroid Build Coastguard Worker       // state includes latest token seen
50*cf5a6c84SAndroid Build Coastguard Worker       int tok;
51*cf5a6c84SAndroid Build Coastguard Worker       int tokbuiltin;
52*cf5a6c84SAndroid Build Coastguard Worker       int toktype;
53*cf5a6c84SAndroid Build Coastguard Worker       char *tokstr;
54*cf5a6c84SAndroid Build Coastguard Worker       size_t maxtok;
55*cf5a6c84SAndroid Build Coastguard Worker       size_t toklen;
56*cf5a6c84SAndroid Build Coastguard Worker       double numval;
57*cf5a6c84SAndroid Build Coastguard Worker       int error;  // Set if lexical error.
58*cf5a6c84SAndroid Build Coastguard Worker   } *scs;
59*cf5a6c84SAndroid Build Coastguard Worker   char *tokstr;
60*cf5a6c84SAndroid Build Coastguard Worker   int prevtok;
61*cf5a6c84SAndroid Build Coastguard Worker 
62*cf5a6c84SAndroid Build Coastguard Worker   struct compiler_globals {
63*cf5a6c84SAndroid Build Coastguard Worker     int in_print_stmt;
64*cf5a6c84SAndroid Build Coastguard Worker     int paren_level;
65*cf5a6c84SAndroid Build Coastguard Worker     int in_function_body;
66*cf5a6c84SAndroid Build Coastguard Worker     int funcnum;
67*cf5a6c84SAndroid Build Coastguard Worker     int nparms;
68*cf5a6c84SAndroid Build Coastguard Worker     int compile_error_count;
69*cf5a6c84SAndroid Build Coastguard Worker     int first_begin;
70*cf5a6c84SAndroid Build Coastguard Worker     int last_begin;
71*cf5a6c84SAndroid Build Coastguard Worker     int first_end;
72*cf5a6c84SAndroid Build Coastguard Worker     int last_end;
73*cf5a6c84SAndroid Build Coastguard Worker     int first_recrule;
74*cf5a6c84SAndroid Build Coastguard Worker     int last_recrule;
75*cf5a6c84SAndroid Build Coastguard Worker     int break_dest;
76*cf5a6c84SAndroid Build Coastguard Worker     int continue_dest;
77*cf5a6c84SAndroid Build Coastguard Worker     int stack_offset_to_fix;  // fixup stack if return in for(e in a)
78*cf5a6c84SAndroid Build Coastguard Worker     int range_pattern_num;
79*cf5a6c84SAndroid Build Coastguard Worker     int rule_type;  // tkbegin, tkend, or 0
80*cf5a6c84SAndroid Build Coastguard Worker   } cgl;
81*cf5a6c84SAndroid Build Coastguard Worker 
82*cf5a6c84SAndroid Build Coastguard Worker   // zvalue: the main awk value type
83*cf5a6c84SAndroid Build Coastguard Worker   // Can be number or string or both, or else map (array) or regex
84*cf5a6c84SAndroid Build Coastguard Worker   struct zvalue {
85*cf5a6c84SAndroid Build Coastguard Worker     unsigned flags;
86*cf5a6c84SAndroid Build Coastguard Worker     double num;
87*cf5a6c84SAndroid Build Coastguard Worker     union { // anonymous union not in C99; not going to fix it now.
88*cf5a6c84SAndroid Build Coastguard Worker       struct zstring *vst;
89*cf5a6c84SAndroid Build Coastguard Worker       struct zmap *map;
90*cf5a6c84SAndroid Build Coastguard Worker       regex_t *rx;
91*cf5a6c84SAndroid Build Coastguard Worker     };
92*cf5a6c84SAndroid Build Coastguard Worker   } nozvalue;   // to shut up compiler warning TODO FIXME
93*cf5a6c84SAndroid Build Coastguard Worker 
94*cf5a6c84SAndroid Build Coastguard Worker   struct runtime_globals {
95*cf5a6c84SAndroid Build Coastguard Worker     struct zvalue cur_arg;
96*cf5a6c84SAndroid Build Coastguard Worker     FILE *fp;           // current data file
97*cf5a6c84SAndroid Build Coastguard Worker     int narg;           // cmdline arg index
98*cf5a6c84SAndroid Build Coastguard Worker     int nfiles;         // num of cmdline data file args processed
99*cf5a6c84SAndroid Build Coastguard Worker     int eof;            // all cmdline files (incl. stdin) read
100*cf5a6c84SAndroid Build Coastguard Worker     char *recptr;
101*cf5a6c84SAndroid Build Coastguard Worker     struct zstring *zspr;      // Global to receive sprintf() string value
102*cf5a6c84SAndroid Build Coastguard Worker   } rgl;
103*cf5a6c84SAndroid Build Coastguard Worker 
104*cf5a6c84SAndroid Build Coastguard Worker   // Expanding sequential list
105*cf5a6c84SAndroid Build Coastguard Worker   struct zlist {
106*cf5a6c84SAndroid Build Coastguard Worker     char *base, *limit, *avail;
107*cf5a6c84SAndroid Build Coastguard Worker     size_t size;
108*cf5a6c84SAndroid Build Coastguard Worker   } globals_table,  // global symbol table
109*cf5a6c84SAndroid Build Coastguard Worker     locals_table,     // local symbol table
110*cf5a6c84SAndroid Build Coastguard Worker     func_def_table;  // function symbol table
111*cf5a6c84SAndroid Build Coastguard Worker   // runtime lists
112*cf5a6c84SAndroid Build Coastguard Worker   struct zlist literals, fields, zcode, stack;
113*cf5a6c84SAndroid Build Coastguard Worker 
114*cf5a6c84SAndroid Build Coastguard Worker   char *progname;
115*cf5a6c84SAndroid Build Coastguard Worker 
116*cf5a6c84SAndroid Build Coastguard Worker   int spec_var_limit;
117*cf5a6c84SAndroid Build Coastguard Worker   int zcode_last;
118*cf5a6c84SAndroid Build Coastguard Worker   struct zvalue *stackp;  // top of stack ptr
119*cf5a6c84SAndroid Build Coastguard Worker 
120*cf5a6c84SAndroid Build Coastguard Worker   char *pbuf;   // Used for number formatting in num_to_zstring()
121*cf5a6c84SAndroid Build Coastguard Worker #define RS_MAX  64
122*cf5a6c84SAndroid Build Coastguard Worker   char rs_last[RS_MAX];
123*cf5a6c84SAndroid Build Coastguard Worker   regex_t rx_rs_default, rx_rs_last;
124*cf5a6c84SAndroid Build Coastguard Worker   regex_t rx_default, rx_last, rx_printf_fmt;
125*cf5a6c84SAndroid Build Coastguard Worker #define FS_MAX  64
126*cf5a6c84SAndroid Build Coastguard Worker   char fs_last[FS_MAX];
127*cf5a6c84SAndroid Build Coastguard Worker   char one_char_fs[4];
128*cf5a6c84SAndroid Build Coastguard Worker   int nf_internal;  // should match NF
129*cf5a6c84SAndroid Build Coastguard Worker   char range_sw[64];   // FIXME TODO quick and dirty set of range switches
130*cf5a6c84SAndroid Build Coastguard Worker   int file_cnt, std_file_cnt;
131*cf5a6c84SAndroid Build Coastguard Worker 
132*cf5a6c84SAndroid Build Coastguard Worker   struct zfile {
133*cf5a6c84SAndroid Build Coastguard Worker     struct zfile *next;
134*cf5a6c84SAndroid Build Coastguard Worker     char *fn;
135*cf5a6c84SAndroid Build Coastguard Worker     FILE *fp;
136*cf5a6c84SAndroid Build Coastguard Worker     char mode;  // w, a, or r
137*cf5a6c84SAndroid Build Coastguard Worker     char file_or_pipe;  // 1 if file, 0 if pipe
138*cf5a6c84SAndroid Build Coastguard Worker     char is_tty, is_std_file;
139*cf5a6c84SAndroid Build Coastguard Worker     char eof;
140*cf5a6c84SAndroid Build Coastguard Worker     int ro, lim, buflen;
141*cf5a6c84SAndroid Build Coastguard Worker     char *buf;
142*cf5a6c84SAndroid Build Coastguard Worker   } *zfiles, *cfile, *zstdout;
143*cf5a6c84SAndroid Build Coastguard Worker )
144*cf5a6c84SAndroid Build Coastguard Worker 
awk_exit(int status)145*cf5a6c84SAndroid Build Coastguard Worker static void awk_exit(int status)
146*cf5a6c84SAndroid Build Coastguard Worker {
147*cf5a6c84SAndroid Build Coastguard Worker   toys.exitval = status;
148*cf5a6c84SAndroid Build Coastguard Worker   xexit();
149*cf5a6c84SAndroid Build Coastguard Worker }
150*cf5a6c84SAndroid Build Coastguard Worker #ifdef __GNUC__
151*cf5a6c84SAndroid Build Coastguard Worker #define ATTR_FALLTHROUGH_INTENDED __attribute__ ((fallthrough))
152*cf5a6c84SAndroid Build Coastguard Worker #else
153*cf5a6c84SAndroid Build Coastguard Worker #define ATTR_FALLTHROUGH_INTENDED
154*cf5a6c84SAndroid Build Coastguard Worker #endif
155*cf5a6c84SAndroid Build Coastguard Worker 
156*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
157*cf5a6c84SAndroid Build Coastguard Worker ////   declarations
158*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
159*cf5a6c84SAndroid Build Coastguard Worker 
160*cf5a6c84SAndroid Build Coastguard Worker #define PBUFSIZE  512 // For num_to_zstring()
161*cf5a6c84SAndroid Build Coastguard Worker 
162*cf5a6c84SAndroid Build Coastguard Worker enum toktypes {
163*cf5a6c84SAndroid Build Coastguard Worker     // EOF (use -1 from stdio.h)
164*cf5a6c84SAndroid Build Coastguard Worker     ERROR = 2, NEWLINE, VAR, NUMBER, STRING, REGEX, USERFUNC, BUILTIN, TOKEN,
165*cf5a6c84SAndroid Build Coastguard Worker     KEYWORD
166*cf5a6c84SAndroid Build Coastguard Worker     };
167*cf5a6c84SAndroid Build Coastguard Worker 
168*cf5a6c84SAndroid Build Coastguard Worker // Must align with lbp_table[]
169*cf5a6c84SAndroid Build Coastguard Worker enum tokens {
170*cf5a6c84SAndroid Build Coastguard Worker     tkunusedtoken, tkeof, tkerr, tknl,
171*cf5a6c84SAndroid Build Coastguard Worker     tkvar, tknumber, tkstring, tkregex, tkfunc, tkbuiltin,
172*cf5a6c84SAndroid Build Coastguard Worker 
173*cf5a6c84SAndroid Build Coastguard Worker // static char *ops = " ;  ,  [  ]  (  )  {  }  $  ++ -- ^  !  *  /  %  +  -     "
174*cf5a6c84SAndroid Build Coastguard Worker //        "<  <= != == >  >= ~  !~ && || ?  :  ^= %= *= /= += -= =  >> |  ";
175*cf5a6c84SAndroid Build Coastguard Worker     tksemi, tkcomma, tklbracket, tkrbracket, tklparen, tkrparen, tklbrace,
176*cf5a6c84SAndroid Build Coastguard Worker     tkrbrace, tkfield, tkincr, tkdecr, tkpow, tknot, tkmul, tkdiv, tkmod,
177*cf5a6c84SAndroid Build Coastguard Worker     tkplus, tkminus,
178*cf5a6c84SAndroid Build Coastguard Worker     tkcat, // !!! Fake operator for concatenation (just adjacent string exprs)
179*cf5a6c84SAndroid Build Coastguard Worker     tklt, tkle, tkne, tkeq, tkgt, tkge, tkmatchop, tknotmatch, tkand, tkor,
180*cf5a6c84SAndroid Build Coastguard Worker     tkternif, tkternelse, tkpowasgn, tkmodasgn, tkmulasgn, tkdivasgn,
181*cf5a6c84SAndroid Build Coastguard Worker     tkaddasgn, tksubasgn, tkasgn, tkappend, tkpipe,
182*cf5a6c84SAndroid Build Coastguard Worker 
183*cf5a6c84SAndroid Build Coastguard Worker // static char *keywords = " in        BEGIN     END       if        else      "
184*cf5a6c84SAndroid Build Coastguard Worker //    "while     for       do        break     continue  exit      function  "
185*cf5a6c84SAndroid Build Coastguard Worker //    "return    next      nextfile  delete    print     printf    getline   ";
186*cf5a6c84SAndroid Build Coastguard Worker     tkin, tkbegin, tkend, tkif, tkelse,
187*cf5a6c84SAndroid Build Coastguard Worker     tkwhile, tkfor, tkdo, tkbreak, tkcontinue, tkexit, tkfunction,
188*cf5a6c84SAndroid Build Coastguard Worker     tkreturn, tknext, tknextfile, tkdelete, tkprint, tkprintf, tkgetline,
189*cf5a6c84SAndroid Build Coastguard Worker 
190*cf5a6c84SAndroid Build Coastguard Worker // static char *builtins = " atan2     cos       sin       exp       "
191*cf5a6c84SAndroid Build Coastguard Worker //    "log       sqrt      int       rand      srand     length    "
192*cf5a6c84SAndroid Build Coastguard Worker //    "tolower   toupper   system    fflush    "
193*cf5a6c84SAndroid Build Coastguard Worker //    "and       or        xor       lshift    rshift    ";
194*cf5a6c84SAndroid Build Coastguard Worker     tkatan2, tkcos, tksin, tkexp, tklog, tksqrt, tkint, tkrand, tksrand,
195*cf5a6c84SAndroid Build Coastguard Worker     tklength, tktolower, tktoupper, tksystem, tkfflush,
196*cf5a6c84SAndroid Build Coastguard Worker     tkband, tkbor, tkbxor, tklshift, tkrshift,
197*cf5a6c84SAndroid Build Coastguard Worker 
198*cf5a6c84SAndroid Build Coastguard Worker // static char *specialfuncs = " close     index     match     split     "
199*cf5a6c84SAndroid Build Coastguard Worker //    "sub       gsub      sprintf   substr    ";
200*cf5a6c84SAndroid Build Coastguard Worker     tkclose, tkindex, tkmatch, tksplit,
201*cf5a6c84SAndroid Build Coastguard Worker     tksub, tkgsub, tksprintf, tksubstr, tklasttk
202*cf5a6c84SAndroid Build Coastguard Worker     };
203*cf5a6c84SAndroid Build Coastguard Worker 
204*cf5a6c84SAndroid Build Coastguard Worker enum opcodes {
205*cf5a6c84SAndroid Build Coastguard Worker     opunusedop = tklasttk,
206*cf5a6c84SAndroid Build Coastguard Worker     opvarref, opmapref, opfldref, oppush, opdrop, opdrop_n, opnotnot,
207*cf5a6c84SAndroid Build Coastguard Worker     oppreincr, oppredecr, oppostincr, oppostdecr, opnegate, opjump, opjumptrue,
208*cf5a6c84SAndroid Build Coastguard Worker     opjumpfalse, opprepcall, opmap, opmapiternext, opmapdelete, opmatchrec,
209*cf5a6c84SAndroid Build Coastguard Worker     opquit, opprintrec, oprange1, oprange2, oprange3, oplastop
210*cf5a6c84SAndroid Build Coastguard Worker };
211*cf5a6c84SAndroid Build Coastguard Worker 
212*cf5a6c84SAndroid Build Coastguard Worker // Special variables (POSIX). Must align with char *spec_vars[]
213*cf5a6c84SAndroid Build Coastguard Worker enum spec_var_names { ARGC=1, ARGV, CONVFMT, ENVIRON, FILENAME, FNR, FS, NF,
214*cf5a6c84SAndroid Build Coastguard Worker     NR, OFMT, OFS, ORS, RLENGTH, RS, RSTART, SUBSEP };
215*cf5a6c84SAndroid Build Coastguard Worker 
216*cf5a6c84SAndroid Build Coastguard Worker struct symtab_slot {    // global symbol table entry
217*cf5a6c84SAndroid Build Coastguard Worker   unsigned flags;
218*cf5a6c84SAndroid Build Coastguard Worker   char *name;
219*cf5a6c84SAndroid Build Coastguard Worker };
220*cf5a6c84SAndroid Build Coastguard Worker 
221*cf5a6c84SAndroid Build Coastguard Worker // zstring: flexible string type.
222*cf5a6c84SAndroid Build Coastguard Worker // Capacity must be > size because we insert a NUL byte.
223*cf5a6c84SAndroid Build Coastguard Worker struct zstring {
224*cf5a6c84SAndroid Build Coastguard Worker   int refcnt;
225*cf5a6c84SAndroid Build Coastguard Worker   unsigned size;
226*cf5a6c84SAndroid Build Coastguard Worker   unsigned capacity;
227*cf5a6c84SAndroid Build Coastguard Worker   char str[];   // C99 flexible array member
228*cf5a6c84SAndroid Build Coastguard Worker };
229*cf5a6c84SAndroid Build Coastguard Worker 
230*cf5a6c84SAndroid Build Coastguard Worker // Flag bits for zvalue and symbol tables
231*cf5a6c84SAndroid Build Coastguard Worker #define ZF_MAYBEMAP (1u << 1)
232*cf5a6c84SAndroid Build Coastguard Worker #define ZF_MAP      (1u << 2)
233*cf5a6c84SAndroid Build Coastguard Worker #define ZF_SCALAR   (1u << 3)
234*cf5a6c84SAndroid Build Coastguard Worker #define ZF_NUM      (1u << 4)
235*cf5a6c84SAndroid Build Coastguard Worker #define ZF_RX       (1u << 5)
236*cf5a6c84SAndroid Build Coastguard Worker #define ZF_STR      (1u << 6)
237*cf5a6c84SAndroid Build Coastguard Worker #define ZF_NUMSTR   (1u << 7)   // "numeric string" per posix
238*cf5a6c84SAndroid Build Coastguard Worker #define ZF_REF      (1u << 9)   // for lvalues
239*cf5a6c84SAndroid Build Coastguard Worker #define ZF_MAPREF   (1u << 10)  // for lvalues
240*cf5a6c84SAndroid Build Coastguard Worker #define ZF_FIELDREF (1u << 11)  // for lvalues
241*cf5a6c84SAndroid Build Coastguard Worker #define ZF_EMPTY_RX (1u << 12)
242*cf5a6c84SAndroid Build Coastguard Worker #define ZF_ANYMAP   (ZF_MAP | ZF_MAYBEMAP)
243*cf5a6c84SAndroid Build Coastguard Worker 
244*cf5a6c84SAndroid Build Coastguard Worker // Macro to help facilitate possible future change in zvalue layout.
245*cf5a6c84SAndroid Build Coastguard Worker #define ZVINIT(flags, num, ptr) {(flags), (double)(num), {(ptr)}}
246*cf5a6c84SAndroid Build Coastguard Worker 
247*cf5a6c84SAndroid Build Coastguard Worker #define IS_STR(zvalp) ((zvalp)->flags & ZF_STR)
248*cf5a6c84SAndroid Build Coastguard Worker #define IS_RX(zvalp) ((zvalp)->flags & ZF_RX)
249*cf5a6c84SAndroid Build Coastguard Worker #define IS_NUM(zvalp) ((zvalp)->flags & ZF_NUM)
250*cf5a6c84SAndroid Build Coastguard Worker #define IS_MAP(zvalp) ((zvalp)->flags & ZF_MAP)
251*cf5a6c84SAndroid Build Coastguard Worker #define IS_EMPTY_RX(zvalp) ((zvalp)->flags & ZF_EMPTY_RX)
252*cf5a6c84SAndroid Build Coastguard Worker 
253*cf5a6c84SAndroid Build Coastguard Worker #define GLOBAL      ((struct symtab_slot *)TT.globals_table.base)
254*cf5a6c84SAndroid Build Coastguard Worker #define LOCAL       ((struct symtab_slot *)TT.locals_table.base)
255*cf5a6c84SAndroid Build Coastguard Worker #define FUNC_DEF    ((struct functab_slot *)TT.func_def_table.base)
256*cf5a6c84SAndroid Build Coastguard Worker 
257*cf5a6c84SAndroid Build Coastguard Worker #define LITERAL     ((struct zvalue *)TT.literals.base)
258*cf5a6c84SAndroid Build Coastguard Worker #define STACK       ((struct zvalue *)TT.stack.base)
259*cf5a6c84SAndroid Build Coastguard Worker #define FIELD       ((struct zvalue *)TT.fields.base)
260*cf5a6c84SAndroid Build Coastguard Worker 
261*cf5a6c84SAndroid Build Coastguard Worker #define ZCODE       ((int *)TT.zcode.base)
262*cf5a6c84SAndroid Build Coastguard Worker 
263*cf5a6c84SAndroid Build Coastguard Worker #define FUNC_DEFINED    (1u)
264*cf5a6c84SAndroid Build Coastguard Worker #define FUNC_CALLED     (2u)
265*cf5a6c84SAndroid Build Coastguard Worker 
266*cf5a6c84SAndroid Build Coastguard Worker #define MIN_STACK_LEFT 1024
267*cf5a6c84SAndroid Build Coastguard Worker 
268*cf5a6c84SAndroid Build Coastguard Worker struct functab_slot {    // function symbol table entry
269*cf5a6c84SAndroid Build Coastguard Worker   unsigned flags;
270*cf5a6c84SAndroid Build Coastguard Worker   char *name;
271*cf5a6c84SAndroid Build Coastguard Worker   struct zlist function_locals;
272*cf5a6c84SAndroid Build Coastguard Worker   int zcode_addr;
273*cf5a6c84SAndroid Build Coastguard Worker };
274*cf5a6c84SAndroid Build Coastguard Worker 
275*cf5a6c84SAndroid Build Coastguard Worker // Elements of the hash table (key/value pairs)
276*cf5a6c84SAndroid Build Coastguard Worker struct zmap_slot {
277*cf5a6c84SAndroid Build Coastguard Worker   int hash;       // store hash key to speed hash table expansion
278*cf5a6c84SAndroid Build Coastguard Worker   struct zstring *key;
279*cf5a6c84SAndroid Build Coastguard Worker   struct zvalue val;
280*cf5a6c84SAndroid Build Coastguard Worker };
281*cf5a6c84SAndroid Build Coastguard Worker #define ZMSLOTINIT(hash, key, val) {hash, key, val}
282*cf5a6c84SAndroid Build Coastguard Worker 
283*cf5a6c84SAndroid Build Coastguard Worker // zmap: Mapping data type for arrays; a hash table. Values in hash are either
284*cf5a6c84SAndroid Build Coastguard Worker // 0 (unused), -1 (marked deleted), or one plus the number of the zmap slot
285*cf5a6c84SAndroid Build Coastguard Worker // containing a key/value pair. The zlist slot entries are numbered from 0 to
286*cf5a6c84SAndroid Build Coastguard Worker // count-1, so need to add one to distinguish from unused.  The probe sequence
287*cf5a6c84SAndroid Build Coastguard Worker // is borrowed from Python dict, using the "perturb" idea to mix in upper bits
288*cf5a6c84SAndroid Build Coastguard Worker // of the original hash value.
289*cf5a6c84SAndroid Build Coastguard Worker struct zmap {
290*cf5a6c84SAndroid Build Coastguard Worker   unsigned mask;  // tablesize - 1; tablesize is 2 ** n
291*cf5a6c84SAndroid Build Coastguard Worker   int *hash;      // (mask + 1) elements
292*cf5a6c84SAndroid Build Coastguard Worker   int limit;      // 80% of table size ((mask+1)*8/10)
293*cf5a6c84SAndroid Build Coastguard Worker   int count;      // number of occupied slots in hash
294*cf5a6c84SAndroid Build Coastguard Worker   int deleted;    // number of deleted slots
295*cf5a6c84SAndroid Build Coastguard Worker   struct zlist slot;     // expanding list of zmap_slot elements
296*cf5a6c84SAndroid Build Coastguard Worker };
297*cf5a6c84SAndroid Build Coastguard Worker 
298*cf5a6c84SAndroid Build Coastguard Worker #define MAPSLOT    ((struct zmap_slot *)(m->slot).base)
299*cf5a6c84SAndroid Build Coastguard Worker #define FFATAL(format, ...) zzerr("$" format, __VA_ARGS__)
300*cf5a6c84SAndroid Build Coastguard Worker #define FATAL(...) zzerr("$%s\n", __VA_ARGS__)
301*cf5a6c84SAndroid Build Coastguard Worker #define XERR(format, ...) zzerr(format, __VA_ARGS__)
302*cf5a6c84SAndroid Build Coastguard Worker 
303*cf5a6c84SAndroid Build Coastguard Worker #define NO_EXIT_STATUS  (9999987)  // value unlikely to appear in exit stmt
304*cf5a6c84SAndroid Build Coastguard Worker 
305*cf5a6c84SAndroid Build Coastguard Worker 
306*cf5a6c84SAndroid Build Coastguard Worker 
307*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
308*cf5a6c84SAndroid Build Coastguard Worker //// lib
309*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
310*cf5a6c84SAndroid Build Coastguard Worker 
xfree(void * p)311*cf5a6c84SAndroid Build Coastguard Worker static void xfree(void *p)
312*cf5a6c84SAndroid Build Coastguard Worker {
313*cf5a6c84SAndroid Build Coastguard Worker   free(p);
314*cf5a6c84SAndroid Build Coastguard Worker }
315*cf5a6c84SAndroid Build Coastguard Worker 
hexval(int c)316*cf5a6c84SAndroid Build Coastguard Worker static int hexval(int c)
317*cf5a6c84SAndroid Build Coastguard Worker {
318*cf5a6c84SAndroid Build Coastguard Worker   // Assumes c is valid hex digit
319*cf5a6c84SAndroid Build Coastguard Worker   return isdigit(c) ? c - '0' : (c | 040) - 'a' + 10;
320*cf5a6c84SAndroid Build Coastguard Worker }
321*cf5a6c84SAndroid Build Coastguard Worker 
322*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
323*cf5a6c84SAndroid Build Coastguard Worker //// common defs
324*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
325*cf5a6c84SAndroid Build Coastguard Worker 
326*cf5a6c84SAndroid Build Coastguard Worker // These (ops, keywords, builtins) must align with enum tokens
327*cf5a6c84SAndroid Build Coastguard Worker static char *ops = " ;  ,  [  ]  (  )  {  }  $  ++ -- ^  !  *  /  %  +  -  .. "
328*cf5a6c84SAndroid Build Coastguard Worker         "<  <= != == >  >= ~  !~ && || ?  :  ^= %= *= /= += -= =  >> |  ";
329*cf5a6c84SAndroid Build Coastguard Worker 
330*cf5a6c84SAndroid Build Coastguard Worker static char *keywords = " in        BEGIN     END       if        else      "
331*cf5a6c84SAndroid Build Coastguard Worker     "while     for       do        break     continue  exit      function  "
332*cf5a6c84SAndroid Build Coastguard Worker     "return    next      nextfile  delete    print     printf    getline   ";
333*cf5a6c84SAndroid Build Coastguard Worker 
334*cf5a6c84SAndroid Build Coastguard Worker static char *builtins = " atan2     cos       sin       exp       log       "
335*cf5a6c84SAndroid Build Coastguard Worker     "sqrt      int       rand      srand     length    "
336*cf5a6c84SAndroid Build Coastguard Worker     "tolower   toupper   system    fflush    "
337*cf5a6c84SAndroid Build Coastguard Worker     "and       or        xor       lshift    rshift    "
338*cf5a6c84SAndroid Build Coastguard Worker     "close     index     match     split     "
339*cf5a6c84SAndroid Build Coastguard Worker     "sub       gsub      sprintf   substr    ";
340*cf5a6c84SAndroid Build Coastguard Worker 
zzerr(char * format,...)341*cf5a6c84SAndroid Build Coastguard Worker static void zzerr(char *format, ...)
342*cf5a6c84SAndroid Build Coastguard Worker {
343*cf5a6c84SAndroid Build Coastguard Worker   va_list args;
344*cf5a6c84SAndroid Build Coastguard Worker   int fatal_sw = 0;
345*cf5a6c84SAndroid Build Coastguard Worker   fprintf(stderr, "%s: ", TT.progname);
346*cf5a6c84SAndroid Build Coastguard Worker   if (format[0] == '$') {
347*cf5a6c84SAndroid Build Coastguard Worker     fprintf(stderr, "FATAL: ");
348*cf5a6c84SAndroid Build Coastguard Worker     format++;
349*cf5a6c84SAndroid Build Coastguard Worker     fatal_sw = 1;
350*cf5a6c84SAndroid Build Coastguard Worker   }
351*cf5a6c84SAndroid Build Coastguard Worker   fprintf(stderr, "file %s line %d: ", TT.scs->filename, TT.scs->line_num);
352*cf5a6c84SAndroid Build Coastguard Worker   va_start(args, format);
353*cf5a6c84SAndroid Build Coastguard Worker   vfprintf(stderr, format, args);
354*cf5a6c84SAndroid Build Coastguard Worker   va_end(args);
355*cf5a6c84SAndroid Build Coastguard Worker   if (format[strlen(format)-1] != '\n') fputc('\n', stderr); // TEMP FIXME !!!
356*cf5a6c84SAndroid Build Coastguard Worker   fflush(stderr);
357*cf5a6c84SAndroid Build Coastguard Worker   if (fatal_sw) awk_exit(2);
358*cf5a6c84SAndroid Build Coastguard Worker         // Don't bump error count for warnings
359*cf5a6c84SAndroid Build Coastguard Worker   else if (!strstr(format, "arning")) TT.cgl.compile_error_count++;
360*cf5a6c84SAndroid Build Coastguard Worker }
361*cf5a6c84SAndroid Build Coastguard Worker 
get_token_text(char * op,int tk)362*cf5a6c84SAndroid Build Coastguard Worker static void get_token_text(char *op, int tk)
363*cf5a6c84SAndroid Build Coastguard Worker {
364*cf5a6c84SAndroid Build Coastguard Worker   // This MUST ? be changed if ops string or tk... assignments change!
365*cf5a6c84SAndroid Build Coastguard Worker   memmove(op, ops + 3 * (tk - tksemi) + 1, 2);
366*cf5a6c84SAndroid Build Coastguard Worker   op[ op[1] == ' ' ? 1 : 2 ] = 0;
367*cf5a6c84SAndroid Build Coastguard Worker }
368*cf5a6c84SAndroid Build Coastguard Worker 
369*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
370*cf5a6c84SAndroid Build Coastguard Worker /// UTF-8
371*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
372*cf5a6c84SAndroid Build Coastguard Worker 
373*cf5a6c84SAndroid Build Coastguard Worker // Return number of bytes in 'cnt' utf8 codepoints
bytesinutf8(char * str,size_t len,size_t cnt)374*cf5a6c84SAndroid Build Coastguard Worker static int bytesinutf8(char *str, size_t len, size_t cnt)
375*cf5a6c84SAndroid Build Coastguard Worker {
376*cf5a6c84SAndroid Build Coastguard Worker   if (FLAG(b)) return cnt;
377*cf5a6c84SAndroid Build Coastguard Worker   unsigned wch;
378*cf5a6c84SAndroid Build Coastguard Worker   char *lim = str + len, *s0 = str;
379*cf5a6c84SAndroid Build Coastguard Worker   while (cnt-- && str < lim) {
380*cf5a6c84SAndroid Build Coastguard Worker     int r = utf8towc(&wch, str, lim - str);
381*cf5a6c84SAndroid Build Coastguard Worker     str += r > 0 ? r : 1;
382*cf5a6c84SAndroid Build Coastguard Worker   }
383*cf5a6c84SAndroid Build Coastguard Worker   return str - s0;
384*cf5a6c84SAndroid Build Coastguard Worker }
385*cf5a6c84SAndroid Build Coastguard Worker 
386*cf5a6c84SAndroid Build Coastguard Worker // Return number of utf8 codepoints in str
utf8cnt(char * str,size_t len)387*cf5a6c84SAndroid Build Coastguard Worker static int utf8cnt(char *str, size_t len)
388*cf5a6c84SAndroid Build Coastguard Worker {
389*cf5a6c84SAndroid Build Coastguard Worker   unsigned wch;
390*cf5a6c84SAndroid Build Coastguard Worker   int cnt = 0;
391*cf5a6c84SAndroid Build Coastguard Worker   char *lim;
392*cf5a6c84SAndroid Build Coastguard Worker   if (!len || FLAG(b)) return len;
393*cf5a6c84SAndroid Build Coastguard Worker   for (lim = str + len; str < lim; cnt++) {
394*cf5a6c84SAndroid Build Coastguard Worker     int r = utf8towc(&wch, str, lim - str);
395*cf5a6c84SAndroid Build Coastguard Worker     str += r > 0 ? r : 1;
396*cf5a6c84SAndroid Build Coastguard Worker   }
397*cf5a6c84SAndroid Build Coastguard Worker   return cnt;
398*cf5a6c84SAndroid Build Coastguard Worker }
399*cf5a6c84SAndroid Build Coastguard Worker 
400*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
401*cf5a6c84SAndroid Build Coastguard Worker ////   zlist
402*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
403*cf5a6c84SAndroid Build Coastguard Worker 
zlist_initx(struct zlist * p,size_t size,size_t count)404*cf5a6c84SAndroid Build Coastguard Worker static struct zlist *zlist_initx(struct zlist *p, size_t size, size_t count)
405*cf5a6c84SAndroid Build Coastguard Worker {
406*cf5a6c84SAndroid Build Coastguard Worker   p->base = p->avail = xzalloc(count * size);
407*cf5a6c84SAndroid Build Coastguard Worker   p->limit = p->base + size * count;
408*cf5a6c84SAndroid Build Coastguard Worker   p->size = size;
409*cf5a6c84SAndroid Build Coastguard Worker   return p;
410*cf5a6c84SAndroid Build Coastguard Worker }
411*cf5a6c84SAndroid Build Coastguard Worker 
zlist_init(struct zlist * p,size_t size)412*cf5a6c84SAndroid Build Coastguard Worker static struct zlist *zlist_init(struct zlist *p, size_t size)
413*cf5a6c84SAndroid Build Coastguard Worker {
414*cf5a6c84SAndroid Build Coastguard Worker #define SLIST_MAX_INIT_BYTES 128
415*cf5a6c84SAndroid Build Coastguard Worker   return zlist_initx(p, size, SLIST_MAX_INIT_BYTES / size);
416*cf5a6c84SAndroid Build Coastguard Worker }
417*cf5a6c84SAndroid Build Coastguard Worker 
418*cf5a6c84SAndroid Build Coastguard Worker // This is called from zlist_append() and add_stack() in run
zlist_expand(struct zlist * p)419*cf5a6c84SAndroid Build Coastguard Worker static void zlist_expand(struct zlist *p)
420*cf5a6c84SAndroid Build Coastguard Worker {
421*cf5a6c84SAndroid Build Coastguard Worker   size_t offset = p->avail - p->base;
422*cf5a6c84SAndroid Build Coastguard Worker   size_t cap = p->limit - p->base;
423*cf5a6c84SAndroid Build Coastguard Worker   size_t newcap = maxof(cap + p->size, ((cap / p->size) * 3 / 2) * p->size);
424*cf5a6c84SAndroid Build Coastguard Worker   if (newcap <= cap) error_exit("mem req error");
425*cf5a6c84SAndroid Build Coastguard Worker   char *base = xrealloc(p->base, newcap);
426*cf5a6c84SAndroid Build Coastguard Worker   p->base = base;
427*cf5a6c84SAndroid Build Coastguard Worker   p->limit = base + newcap;
428*cf5a6c84SAndroid Build Coastguard Worker   p->avail = base + offset;
429*cf5a6c84SAndroid Build Coastguard Worker }
430*cf5a6c84SAndroid Build Coastguard Worker 
zlist_append(struct zlist * p,void * obj)431*cf5a6c84SAndroid Build Coastguard Worker static size_t zlist_append(struct zlist *p, void *obj)
432*cf5a6c84SAndroid Build Coastguard Worker {
433*cf5a6c84SAndroid Build Coastguard Worker   // Insert obj (p->size bytes) at end of list, expand as needed.
434*cf5a6c84SAndroid Build Coastguard Worker   // Return scaled offset to newly inserted obj; i.e. the
435*cf5a6c84SAndroid Build Coastguard Worker   // "slot number" 0, 1, 2,...
436*cf5a6c84SAndroid Build Coastguard Worker   void *objtemp = 0;
437*cf5a6c84SAndroid Build Coastguard Worker   if (p->avail > p->limit - p->size) {
438*cf5a6c84SAndroid Build Coastguard Worker     objtemp = xmalloc(p->size);     // Copy obj in case it is in
439*cf5a6c84SAndroid Build Coastguard Worker     memmove(objtemp, obj, p->size); // the area realloc might free!
440*cf5a6c84SAndroid Build Coastguard Worker     obj = objtemp;
441*cf5a6c84SAndroid Build Coastguard Worker     zlist_expand(p);
442*cf5a6c84SAndroid Build Coastguard Worker   }
443*cf5a6c84SAndroid Build Coastguard Worker   memmove(p->avail, obj, p->size);
444*cf5a6c84SAndroid Build Coastguard Worker   if (objtemp) xfree(objtemp);
445*cf5a6c84SAndroid Build Coastguard Worker   p->avail += p->size;
446*cf5a6c84SAndroid Build Coastguard Worker   return (p->avail - p->base - p->size) / p->size;  // offset of updated slot
447*cf5a6c84SAndroid Build Coastguard Worker }
448*cf5a6c84SAndroid Build Coastguard Worker 
zlist_len(struct zlist * p)449*cf5a6c84SAndroid Build Coastguard Worker static int zlist_len(struct zlist *p)
450*cf5a6c84SAndroid Build Coastguard Worker {
451*cf5a6c84SAndroid Build Coastguard Worker   return (p->avail - p->base) / p->size;
452*cf5a6c84SAndroid Build Coastguard Worker }
453*cf5a6c84SAndroid Build Coastguard Worker 
454*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
455*cf5a6c84SAndroid Build Coastguard Worker ////   zstring
456*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
457*cf5a6c84SAndroid Build Coastguard Worker 
zstring_release(struct zstring ** s)458*cf5a6c84SAndroid Build Coastguard Worker static void zstring_release(struct zstring **s)
459*cf5a6c84SAndroid Build Coastguard Worker {
460*cf5a6c84SAndroid Build Coastguard Worker   if (*s && (**s).refcnt-- == 0) xfree(*s); //free_zstring(s);
461*cf5a6c84SAndroid Build Coastguard Worker   *s = 0;
462*cf5a6c84SAndroid Build Coastguard Worker }
463*cf5a6c84SAndroid Build Coastguard Worker 
zstring_incr_refcnt(struct zstring * s)464*cf5a6c84SAndroid Build Coastguard Worker static void zstring_incr_refcnt(struct zstring *s)
465*cf5a6c84SAndroid Build Coastguard Worker {
466*cf5a6c84SAndroid Build Coastguard Worker   if (s) s->refcnt++;
467*cf5a6c84SAndroid Build Coastguard Worker }
468*cf5a6c84SAndroid Build Coastguard Worker 
469*cf5a6c84SAndroid Build Coastguard Worker // !! Use only if 'to' is NULL or its refcnt is 0.
zstring_modify(struct zstring * to,size_t at,char * s,size_t n)470*cf5a6c84SAndroid Build Coastguard Worker static struct zstring *zstring_modify(struct zstring *to, size_t at, char *s, size_t n)
471*cf5a6c84SAndroid Build Coastguard Worker {
472*cf5a6c84SAndroid Build Coastguard Worker   size_t cap = at + n + 1;
473*cf5a6c84SAndroid Build Coastguard Worker   if (!to || to->capacity < cap) {
474*cf5a6c84SAndroid Build Coastguard Worker     to = xrealloc(to, sizeof(*to) + cap);
475*cf5a6c84SAndroid Build Coastguard Worker     to->capacity = cap;
476*cf5a6c84SAndroid Build Coastguard Worker     to->refcnt = 0;
477*cf5a6c84SAndroid Build Coastguard Worker   }
478*cf5a6c84SAndroid Build Coastguard Worker   memcpy(to->str + at, s, n);
479*cf5a6c84SAndroid Build Coastguard Worker   to->size = at + n;
480*cf5a6c84SAndroid Build Coastguard Worker   to->str[to->size] = '\0';
481*cf5a6c84SAndroid Build Coastguard Worker   return to;
482*cf5a6c84SAndroid Build Coastguard Worker }
483*cf5a6c84SAndroid Build Coastguard Worker 
484*cf5a6c84SAndroid Build Coastguard Worker // The 'to' pointer may move by realloc, so return (maybe updated) pointer.
485*cf5a6c84SAndroid Build Coastguard Worker // If refcnt is nonzero then there is another pointer to this zstring,
486*cf5a6c84SAndroid Build Coastguard Worker // so copy this one and release it. If refcnt is zero we can mutate this.
zstring_update(struct zstring * to,size_t at,char * s,size_t n)487*cf5a6c84SAndroid Build Coastguard Worker static struct zstring *zstring_update(struct zstring *to, size_t at, char *s, size_t n)
488*cf5a6c84SAndroid Build Coastguard Worker {
489*cf5a6c84SAndroid Build Coastguard Worker   if (to && to->refcnt) {
490*cf5a6c84SAndroid Build Coastguard Worker     struct zstring *to_before = to;
491*cf5a6c84SAndroid Build Coastguard Worker     to = zstring_modify(0, 0, to->str, to->size);
492*cf5a6c84SAndroid Build Coastguard Worker     zstring_release(&to_before);
493*cf5a6c84SAndroid Build Coastguard Worker   }
494*cf5a6c84SAndroid Build Coastguard Worker   return zstring_modify(to, at, s, n);
495*cf5a6c84SAndroid Build Coastguard Worker }
496*cf5a6c84SAndroid Build Coastguard Worker 
zstring_copy(struct zstring * to,struct zstring * from)497*cf5a6c84SAndroid Build Coastguard Worker static struct zstring *zstring_copy(struct zstring *to, struct zstring *from)
498*cf5a6c84SAndroid Build Coastguard Worker {
499*cf5a6c84SAndroid Build Coastguard Worker   return zstring_update(to, 0, from->str, from->size);
500*cf5a6c84SAndroid Build Coastguard Worker }
501*cf5a6c84SAndroid Build Coastguard Worker 
zstring_extend(struct zstring * to,struct zstring * from)502*cf5a6c84SAndroid Build Coastguard Worker static struct zstring *zstring_extend(struct zstring *to, struct zstring *from)
503*cf5a6c84SAndroid Build Coastguard Worker {
504*cf5a6c84SAndroid Build Coastguard Worker   return zstring_update(to, to->size, from->str, from->size);
505*cf5a6c84SAndroid Build Coastguard Worker }
506*cf5a6c84SAndroid Build Coastguard Worker 
new_zstring(char * s,size_t size)507*cf5a6c84SAndroid Build Coastguard Worker static struct zstring *new_zstring(char *s, size_t size)
508*cf5a6c84SAndroid Build Coastguard Worker {
509*cf5a6c84SAndroid Build Coastguard Worker   return zstring_modify(0, 0, s, size);
510*cf5a6c84SAndroid Build Coastguard Worker }
511*cf5a6c84SAndroid Build Coastguard Worker 
512*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
513*cf5a6c84SAndroid Build Coastguard Worker ////   zvalue
514*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
515*cf5a6c84SAndroid Build Coastguard Worker 
516*cf5a6c84SAndroid Build Coastguard Worker static struct zvalue uninit_zvalue = ZVINIT(0, 0.0, 0);
517*cf5a6c84SAndroid Build Coastguard Worker 
518*cf5a6c84SAndroid Build Coastguard Worker // This will be reassigned in init_globals() with an empty string.
519*cf5a6c84SAndroid Build Coastguard Worker // It's a special value used for "uninitialized" field vars
520*cf5a6c84SAndroid Build Coastguard Worker // referenced past $NF. See push_field().
521*cf5a6c84SAndroid Build Coastguard Worker static struct zvalue uninit_string_zvalue = ZVINIT(0, 0.0, 0);
522*cf5a6c84SAndroid Build Coastguard Worker 
new_str_val(char * s)523*cf5a6c84SAndroid Build Coastguard Worker static struct zvalue new_str_val(char *s)
524*cf5a6c84SAndroid Build Coastguard Worker {
525*cf5a6c84SAndroid Build Coastguard Worker   // Only if no nul inside string!
526*cf5a6c84SAndroid Build Coastguard Worker   struct zvalue v = ZVINIT(ZF_STR, 0.0, new_zstring(s, strlen(s)));
527*cf5a6c84SAndroid Build Coastguard Worker   return v;
528*cf5a6c84SAndroid Build Coastguard Worker }
529*cf5a6c84SAndroid Build Coastguard Worker 
zvalue_release_zstring(struct zvalue * v)530*cf5a6c84SAndroid Build Coastguard Worker static void zvalue_release_zstring(struct zvalue *v)
531*cf5a6c84SAndroid Build Coastguard Worker {
532*cf5a6c84SAndroid Build Coastguard Worker   if (v && ! (v->flags & (ZF_ANYMAP | ZF_RX))) zstring_release(&v->vst);
533*cf5a6c84SAndroid Build Coastguard Worker }
534*cf5a6c84SAndroid Build Coastguard Worker 
535*cf5a6c84SAndroid Build Coastguard Worker // push_val() is used for initializing globals (see init_compiler())
536*cf5a6c84SAndroid Build Coastguard Worker // but mostly used in runtime
537*cf5a6c84SAndroid Build Coastguard Worker // WARNING: push_val may change location of v, so do NOT depend on it after!
538*cf5a6c84SAndroid Build Coastguard Worker // Note the incr refcnt used to be after the zlist_append, but that caused a
539*cf5a6c84SAndroid Build Coastguard Worker // heap-use-after-free error when the zlist_append relocated the zvalue being
540*cf5a6c84SAndroid Build Coastguard Worker // pushed, invalidating the v pointer.
push_val(struct zvalue * v)541*cf5a6c84SAndroid Build Coastguard Worker static void push_val(struct zvalue *v)
542*cf5a6c84SAndroid Build Coastguard Worker {
543*cf5a6c84SAndroid Build Coastguard Worker   if (IS_STR(v) && v->vst) v->vst->refcnt++;  // inlined zstring_incr_refcnt()
544*cf5a6c84SAndroid Build Coastguard Worker   *++TT.stackp = *v;
545*cf5a6c84SAndroid Build Coastguard Worker }
546*cf5a6c84SAndroid Build Coastguard Worker 
zvalue_copy(struct zvalue * to,struct zvalue * from)547*cf5a6c84SAndroid Build Coastguard Worker static void zvalue_copy(struct zvalue *to, struct zvalue *from)
548*cf5a6c84SAndroid Build Coastguard Worker {
549*cf5a6c84SAndroid Build Coastguard Worker   if (IS_RX(from)) *to = *from;
550*cf5a6c84SAndroid Build Coastguard Worker   else {
551*cf5a6c84SAndroid Build Coastguard Worker     zvalue_release_zstring(to);
552*cf5a6c84SAndroid Build Coastguard Worker     *to = *from;
553*cf5a6c84SAndroid Build Coastguard Worker     zstring_incr_refcnt(to->vst);
554*cf5a6c84SAndroid Build Coastguard Worker   }
555*cf5a6c84SAndroid Build Coastguard Worker }
556*cf5a6c84SAndroid Build Coastguard Worker 
zvalue_dup_zstring(struct zvalue * v)557*cf5a6c84SAndroid Build Coastguard Worker static void zvalue_dup_zstring(struct zvalue *v)
558*cf5a6c84SAndroid Build Coastguard Worker {
559*cf5a6c84SAndroid Build Coastguard Worker   struct zstring *z = new_zstring(v->vst->str, v->vst->size);
560*cf5a6c84SAndroid Build Coastguard Worker   zstring_release(&v->vst);
561*cf5a6c84SAndroid Build Coastguard Worker   v->vst = z;
562*cf5a6c84SAndroid Build Coastguard Worker }
563*cf5a6c84SAndroid Build Coastguard Worker 
564*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
565*cf5a6c84SAndroid Build Coastguard Worker ////   zmap (array) implementation
566*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
567*cf5a6c84SAndroid Build Coastguard Worker 
zstring_match(struct zstring * a,struct zstring * b)568*cf5a6c84SAndroid Build Coastguard Worker static int zstring_match(struct zstring *a, struct zstring *b)
569*cf5a6c84SAndroid Build Coastguard Worker {
570*cf5a6c84SAndroid Build Coastguard Worker   return a->size == b->size && memcmp(a->str, b->str, a->size) == 0;
571*cf5a6c84SAndroid Build Coastguard Worker }
572*cf5a6c84SAndroid Build Coastguard Worker 
zstring_hash(struct zstring * s)573*cf5a6c84SAndroid Build Coastguard Worker static int zstring_hash(struct zstring *s)
574*cf5a6c84SAndroid Build Coastguard Worker {   // djb2 -- small, fast, good enough for this
575*cf5a6c84SAndroid Build Coastguard Worker   unsigned h = 5381;
576*cf5a6c84SAndroid Build Coastguard Worker   char *p = s->str, *lim = p + s->size;
577*cf5a6c84SAndroid Build Coastguard Worker   while (p < lim)
578*cf5a6c84SAndroid Build Coastguard Worker     h = (h << 5) + h + *p++;
579*cf5a6c84SAndroid Build Coastguard Worker   return h;
580*cf5a6c84SAndroid Build Coastguard Worker }
581*cf5a6c84SAndroid Build Coastguard Worker 
582*cf5a6c84SAndroid Build Coastguard Worker enum { PSHIFT = 5 };  // "perturb" shift -- see find_mapslot() below
583*cf5a6c84SAndroid Build Coastguard Worker 
find_mapslot(struct zmap * m,struct zstring * key,int * hash,int * probe)584*cf5a6c84SAndroid Build Coastguard Worker static struct zmap_slot *find_mapslot(struct zmap *m, struct zstring *key, int *hash, int *probe)
585*cf5a6c84SAndroid Build Coastguard Worker {
586*cf5a6c84SAndroid Build Coastguard Worker   struct zmap_slot *x = 0;
587*cf5a6c84SAndroid Build Coastguard Worker   unsigned perturb = *hash = zstring_hash(key);
588*cf5a6c84SAndroid Build Coastguard Worker   *probe = *hash & m->mask;
589*cf5a6c84SAndroid Build Coastguard Worker   int n, first_deleted = -1;
590*cf5a6c84SAndroid Build Coastguard Worker   while ((n = m->hash[*probe])) {
591*cf5a6c84SAndroid Build Coastguard Worker     if (n > 0) {
592*cf5a6c84SAndroid Build Coastguard Worker       x = &MAPSLOT[n-1];
593*cf5a6c84SAndroid Build Coastguard Worker       if (*hash == x->hash && zstring_match(key, x->key)) {
594*cf5a6c84SAndroid Build Coastguard Worker         return x;
595*cf5a6c84SAndroid Build Coastguard Worker       }
596*cf5a6c84SAndroid Build Coastguard Worker     } else if (first_deleted < 0) first_deleted = *probe;
597*cf5a6c84SAndroid Build Coastguard Worker     // Based on technique in Python dict implementation. Comment there
598*cf5a6c84SAndroid Build Coastguard Worker     // (https://github.com/python/cpython/blob/3.10/Objects/dictobject.c)
599*cf5a6c84SAndroid Build Coastguard Worker     // says
600*cf5a6c84SAndroid Build Coastguard Worker     //
601*cf5a6c84SAndroid Build Coastguard Worker     // j = ((5*j) + 1) mod 2**i
602*cf5a6c84SAndroid Build Coastguard Worker     // For any initial j in range(2**i), repeating that 2**i times generates
603*cf5a6c84SAndroid Build Coastguard Worker     // each int in range(2**i) exactly once (see any text on random-number
604*cf5a6c84SAndroid Build Coastguard Worker     // generation for proof).
605*cf5a6c84SAndroid Build Coastguard Worker     //
606*cf5a6c84SAndroid Build Coastguard Worker     // The addition of 'perturb' greatly improves the probe sequence. See
607*cf5a6c84SAndroid Build Coastguard Worker     // the Python dict implementation for more details.
608*cf5a6c84SAndroid Build Coastguard Worker     *probe = (*probe * 5 + 1 + (perturb >>= PSHIFT)) & m->mask;
609*cf5a6c84SAndroid Build Coastguard Worker   }
610*cf5a6c84SAndroid Build Coastguard Worker   if (first_deleted >= 0) *probe = first_deleted;
611*cf5a6c84SAndroid Build Coastguard Worker   return 0;
612*cf5a6c84SAndroid Build Coastguard Worker }
613*cf5a6c84SAndroid Build Coastguard Worker 
zmap_find(struct zmap * m,struct zstring * key)614*cf5a6c84SAndroid Build Coastguard Worker static struct zvalue *zmap_find(struct zmap *m, struct zstring *key)
615*cf5a6c84SAndroid Build Coastguard Worker {
616*cf5a6c84SAndroid Build Coastguard Worker   int hash, probe;
617*cf5a6c84SAndroid Build Coastguard Worker   struct zmap_slot *x = find_mapslot(m, key, &hash, &probe);
618*cf5a6c84SAndroid Build Coastguard Worker   return x ? &x->val : 0;
619*cf5a6c84SAndroid Build Coastguard Worker }
620*cf5a6c84SAndroid Build Coastguard Worker 
zmap_init(struct zmap * m)621*cf5a6c84SAndroid Build Coastguard Worker static void zmap_init(struct zmap *m)
622*cf5a6c84SAndroid Build Coastguard Worker {
623*cf5a6c84SAndroid Build Coastguard Worker   enum {INIT_SIZE = 8};
624*cf5a6c84SAndroid Build Coastguard Worker   m->mask = INIT_SIZE - 1;
625*cf5a6c84SAndroid Build Coastguard Worker   m->hash = xzalloc(INIT_SIZE * sizeof(*m->hash));
626*cf5a6c84SAndroid Build Coastguard Worker   m->limit = INIT_SIZE * 8 / 10;
627*cf5a6c84SAndroid Build Coastguard Worker   m->count = 0;
628*cf5a6c84SAndroid Build Coastguard Worker   m->deleted = 0;
629*cf5a6c84SAndroid Build Coastguard Worker   zlist_init(&m->slot, sizeof(struct zmap_slot));
630*cf5a6c84SAndroid Build Coastguard Worker }
631*cf5a6c84SAndroid Build Coastguard Worker 
zvalue_map_init(struct zvalue * v)632*cf5a6c84SAndroid Build Coastguard Worker static void zvalue_map_init(struct zvalue *v)
633*cf5a6c84SAndroid Build Coastguard Worker {
634*cf5a6c84SAndroid Build Coastguard Worker   struct zmap *m = xmalloc(sizeof(*m));
635*cf5a6c84SAndroid Build Coastguard Worker   zmap_init(m);
636*cf5a6c84SAndroid Build Coastguard Worker   v->map = m;
637*cf5a6c84SAndroid Build Coastguard Worker   v->flags |= ZF_MAP;
638*cf5a6c84SAndroid Build Coastguard Worker }
639*cf5a6c84SAndroid Build Coastguard Worker 
zmap_delete_map_incl_slotdata(struct zmap * m)640*cf5a6c84SAndroid Build Coastguard Worker static void zmap_delete_map_incl_slotdata(struct zmap *m)
641*cf5a6c84SAndroid Build Coastguard Worker {
642*cf5a6c84SAndroid Build Coastguard Worker   for (struct zmap_slot *p = &MAPSLOT[0]; p < &MAPSLOT[zlist_len(&m->slot)]; p++) {
643*cf5a6c84SAndroid Build Coastguard Worker     if (p->key) zstring_release(&p->key);
644*cf5a6c84SAndroid Build Coastguard Worker     if (p->val.vst) zstring_release(&p->val.vst);
645*cf5a6c84SAndroid Build Coastguard Worker   }
646*cf5a6c84SAndroid Build Coastguard Worker   xfree(m->slot.base);
647*cf5a6c84SAndroid Build Coastguard Worker   xfree(m->hash);
648*cf5a6c84SAndroid Build Coastguard Worker }
649*cf5a6c84SAndroid Build Coastguard Worker 
zmap_delete_map(struct zmap * m)650*cf5a6c84SAndroid Build Coastguard Worker static void zmap_delete_map(struct zmap *m)
651*cf5a6c84SAndroid Build Coastguard Worker {
652*cf5a6c84SAndroid Build Coastguard Worker   zmap_delete_map_incl_slotdata(m);
653*cf5a6c84SAndroid Build Coastguard Worker   zmap_init(m);
654*cf5a6c84SAndroid Build Coastguard Worker }
655*cf5a6c84SAndroid Build Coastguard Worker 
zmap_rehash(struct zmap * m)656*cf5a6c84SAndroid Build Coastguard Worker static void zmap_rehash(struct zmap *m)
657*cf5a6c84SAndroid Build Coastguard Worker {
658*cf5a6c84SAndroid Build Coastguard Worker   // New table is twice the size of old.
659*cf5a6c84SAndroid Build Coastguard Worker   int size = m->mask + 1;
660*cf5a6c84SAndroid Build Coastguard Worker   unsigned mask = 2 * size - 1;
661*cf5a6c84SAndroid Build Coastguard Worker   int *h = xzalloc(2 * size * sizeof(*m->hash));
662*cf5a6c84SAndroid Build Coastguard Worker   // Step through the old hash table, set up location in new table.
663*cf5a6c84SAndroid Build Coastguard Worker   for (int i = 0; i < size; i++) {
664*cf5a6c84SAndroid Build Coastguard Worker     int n = m->hash[i];
665*cf5a6c84SAndroid Build Coastguard Worker     if (n > 0) {
666*cf5a6c84SAndroid Build Coastguard Worker       int hash = MAPSLOT[n-1].hash;
667*cf5a6c84SAndroid Build Coastguard Worker       unsigned perturb = hash;
668*cf5a6c84SAndroid Build Coastguard Worker       int p = hash & mask;
669*cf5a6c84SAndroid Build Coastguard Worker       while (h[p]) {
670*cf5a6c84SAndroid Build Coastguard Worker         p = (p * 5 + 1 + (perturb >>= PSHIFT)) & mask;
671*cf5a6c84SAndroid Build Coastguard Worker       }
672*cf5a6c84SAndroid Build Coastguard Worker       h[p] = n;
673*cf5a6c84SAndroid Build Coastguard Worker     }
674*cf5a6c84SAndroid Build Coastguard Worker   }
675*cf5a6c84SAndroid Build Coastguard Worker   m->mask = mask;
676*cf5a6c84SAndroid Build Coastguard Worker   xfree(m->hash);
677*cf5a6c84SAndroid Build Coastguard Worker   m->hash = h;
678*cf5a6c84SAndroid Build Coastguard Worker   m->limit = 2 * size * 8 / 10;
679*cf5a6c84SAndroid Build Coastguard Worker }
680*cf5a6c84SAndroid Build Coastguard Worker 
zmap_find_or_insert_key(struct zmap * m,struct zstring * key)681*cf5a6c84SAndroid Build Coastguard Worker static struct zmap_slot *zmap_find_or_insert_key(struct zmap *m, struct zstring *key)
682*cf5a6c84SAndroid Build Coastguard Worker {
683*cf5a6c84SAndroid Build Coastguard Worker   int hash, probe;
684*cf5a6c84SAndroid Build Coastguard Worker   struct zmap_slot *x = find_mapslot(m, key, &hash, &probe);
685*cf5a6c84SAndroid Build Coastguard Worker   if (x) return x;
686*cf5a6c84SAndroid Build Coastguard Worker   // not found; insert it.
687*cf5a6c84SAndroid Build Coastguard Worker   if (m->count == m->limit) {
688*cf5a6c84SAndroid Build Coastguard Worker     zmap_rehash(m);         // rehash if getting too full.
689*cf5a6c84SAndroid Build Coastguard Worker     // rerun find_mapslot to get new probe index
690*cf5a6c84SAndroid Build Coastguard Worker     x = find_mapslot(m, key, &hash, &probe);
691*cf5a6c84SAndroid Build Coastguard Worker   }
692*cf5a6c84SAndroid Build Coastguard Worker   // Assign key to new slot entry and bump refcnt.
693*cf5a6c84SAndroid Build Coastguard Worker   struct zmap_slot zs = ZMSLOTINIT(hash, key, (struct zvalue)ZVINIT(0, 0.0, 0));
694*cf5a6c84SAndroid Build Coastguard Worker   zstring_incr_refcnt(key);
695*cf5a6c84SAndroid Build Coastguard Worker   int n = zlist_append(&m->slot, &zs);
696*cf5a6c84SAndroid Build Coastguard Worker   m->count++;
697*cf5a6c84SAndroid Build Coastguard Worker   m->hash[probe] = n + 1;
698*cf5a6c84SAndroid Build Coastguard Worker   return &MAPSLOT[n];
699*cf5a6c84SAndroid Build Coastguard Worker }
700*cf5a6c84SAndroid Build Coastguard Worker 
zmap_delete(struct zmap * m,struct zstring * key)701*cf5a6c84SAndroid Build Coastguard Worker static void zmap_delete(struct zmap *m, struct zstring *key)
702*cf5a6c84SAndroid Build Coastguard Worker {
703*cf5a6c84SAndroid Build Coastguard Worker   int hash, probe;
704*cf5a6c84SAndroid Build Coastguard Worker   struct zmap_slot *x = find_mapslot(m, key, &hash, &probe);
705*cf5a6c84SAndroid Build Coastguard Worker   if (!x) return;
706*cf5a6c84SAndroid Build Coastguard Worker   zstring_release(&MAPSLOT[m->hash[probe] - 1].key);
707*cf5a6c84SAndroid Build Coastguard Worker   m->hash[probe] = -1;
708*cf5a6c84SAndroid Build Coastguard Worker   m->deleted++;
709*cf5a6c84SAndroid Build Coastguard Worker }
710*cf5a6c84SAndroid Build Coastguard Worker 
711*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
712*cf5a6c84SAndroid Build Coastguard Worker //// scan (lexical analyzer)
713*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
714*cf5a6c84SAndroid Build Coastguard Worker 
715*cf5a6c84SAndroid Build Coastguard Worker // TODO:
716*cf5a6c84SAndroid Build Coastguard Worker // IS line_num getting incr correctly? Newline counts as start of line!?
717*cf5a6c84SAndroid Build Coastguard Worker // Handle nuls in file better.
718*cf5a6c84SAndroid Build Coastguard Worker // Open files "rb" and handle CRs in program.
719*cf5a6c84SAndroid Build Coastguard Worker // Roll gch() into get_char() ?
720*cf5a6c84SAndroid Build Coastguard Worker // Deal with signed char (at EOF? elsewhere?)
721*cf5a6c84SAndroid Build Coastguard Worker //
722*cf5a6c84SAndroid Build Coastguard Worker // 2023-01-11: Allow nul bytes inside strings? regexes?
723*cf5a6c84SAndroid Build Coastguard Worker 
progfile_open(void)724*cf5a6c84SAndroid Build Coastguard Worker static void progfile_open(void)
725*cf5a6c84SAndroid Build Coastguard Worker {
726*cf5a6c84SAndroid Build Coastguard Worker   TT.scs->filename = TT.scs->prog_args->arg;
727*cf5a6c84SAndroid Build Coastguard Worker   TT.scs->prog_args = TT.scs->prog_args->next;
728*cf5a6c84SAndroid Build Coastguard Worker   TT.scs->fp = stdin;
729*cf5a6c84SAndroid Build Coastguard Worker   if (strcmp(TT.scs->filename, "-")) TT.scs->fp = fopen(TT.scs->filename, "r");
730*cf5a6c84SAndroid Build Coastguard Worker   if (!TT.scs->fp) error_exit("Can't open %s", TT.scs->filename);
731*cf5a6c84SAndroid Build Coastguard Worker   TT.scs->line_num = 0;
732*cf5a6c84SAndroid Build Coastguard Worker }
733*cf5a6c84SAndroid Build Coastguard Worker 
get_char(void)734*cf5a6c84SAndroid Build Coastguard Worker static int get_char(void)
735*cf5a6c84SAndroid Build Coastguard Worker {
736*cf5a6c84SAndroid Build Coastguard Worker   static char *nl = "\n";
737*cf5a6c84SAndroid Build Coastguard Worker   // On first entry, TT.scs->p points to progstring if any, or null string.
738*cf5a6c84SAndroid Build Coastguard Worker   for (;;) {
739*cf5a6c84SAndroid Build Coastguard Worker     int c = *(TT.scs->p)++;
740*cf5a6c84SAndroid Build Coastguard Worker     if (c) {
741*cf5a6c84SAndroid Build Coastguard Worker       return c;
742*cf5a6c84SAndroid Build Coastguard Worker     }
743*cf5a6c84SAndroid Build Coastguard Worker     if (TT.scs->progstring) {  // Fake newline at end of progstring.
744*cf5a6c84SAndroid Build Coastguard Worker       if (TT.scs->progstring == nl) return EOF;
745*cf5a6c84SAndroid Build Coastguard Worker       TT.scs->p = TT.scs->progstring = nl;
746*cf5a6c84SAndroid Build Coastguard Worker       continue;
747*cf5a6c84SAndroid Build Coastguard Worker     }
748*cf5a6c84SAndroid Build Coastguard Worker     // Here if getting from progfile(s).
749*cf5a6c84SAndroid Build Coastguard Worker     if (TT.scs->line == nl) return EOF;
750*cf5a6c84SAndroid Build Coastguard Worker     if (!TT.scs->fp) {
751*cf5a6c84SAndroid Build Coastguard Worker       progfile_open();
752*cf5a6c84SAndroid Build Coastguard Worker     }
753*cf5a6c84SAndroid Build Coastguard Worker     // Save last char to allow faking final newline.
754*cf5a6c84SAndroid Build Coastguard Worker     int lastchar = (TT.scs->p)[-2];
755*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->line_len = getline(&TT.scs->line, &TT.scs->line_size, TT.scs->fp);
756*cf5a6c84SAndroid Build Coastguard Worker     if (TT.scs->line_len > 0) {
757*cf5a6c84SAndroid Build Coastguard Worker       TT.scs->line_num++;
758*cf5a6c84SAndroid Build Coastguard Worker       TT.scs->p = TT.scs->line;
759*cf5a6c84SAndroid Build Coastguard Worker       continue;
760*cf5a6c84SAndroid Build Coastguard Worker     }
761*cf5a6c84SAndroid Build Coastguard Worker     // EOF
762*cf5a6c84SAndroid Build Coastguard Worker     // FIXME TODO or check for error? feof() vs. ferror()
763*cf5a6c84SAndroid Build Coastguard Worker     fclose(TT.scs->fp);
764*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->fp = 0;
765*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->p = "  " + 2;
766*cf5a6c84SAndroid Build Coastguard Worker     if (!TT.scs->prog_args) {
767*cf5a6c84SAndroid Build Coastguard Worker       xfree(TT.scs->line);
768*cf5a6c84SAndroid Build Coastguard Worker       if (lastchar == '\n') return EOF;
769*cf5a6c84SAndroid Build Coastguard Worker       // Fake final newline
770*cf5a6c84SAndroid Build Coastguard Worker       TT.scs->line = TT.scs->p = nl;
771*cf5a6c84SAndroid Build Coastguard Worker     }
772*cf5a6c84SAndroid Build Coastguard Worker   }
773*cf5a6c84SAndroid Build Coastguard Worker }
774*cf5a6c84SAndroid Build Coastguard Worker 
append_this_char(int c)775*cf5a6c84SAndroid Build Coastguard Worker static void append_this_char(int c)
776*cf5a6c84SAndroid Build Coastguard Worker {
777*cf5a6c84SAndroid Build Coastguard Worker   if (TT.scs->toklen == TT.scs->maxtok - 1) {
778*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->maxtok *= 2;
779*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->tokstr = xrealloc(TT.scs->tokstr, TT.scs->maxtok);
780*cf5a6c84SAndroid Build Coastguard Worker   }
781*cf5a6c84SAndroid Build Coastguard Worker   TT.scs->tokstr[TT.scs->toklen++] = c;
782*cf5a6c84SAndroid Build Coastguard Worker   TT.scs->tokstr[TT.scs->toklen] = 0;
783*cf5a6c84SAndroid Build Coastguard Worker }
784*cf5a6c84SAndroid Build Coastguard Worker 
gch(void)785*cf5a6c84SAndroid Build Coastguard Worker static void gch(void)
786*cf5a6c84SAndroid Build Coastguard Worker {
787*cf5a6c84SAndroid Build Coastguard Worker   // FIXME probably not right place to skip CRs.
788*cf5a6c84SAndroid Build Coastguard Worker   do {
789*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->ch = get_char();
790*cf5a6c84SAndroid Build Coastguard Worker   } while (TT.scs->ch == '\r');
791*cf5a6c84SAndroid Build Coastguard Worker }
792*cf5a6c84SAndroid Build Coastguard Worker 
append_char(void)793*cf5a6c84SAndroid Build Coastguard Worker static void append_char(void)
794*cf5a6c84SAndroid Build Coastguard Worker {
795*cf5a6c84SAndroid Build Coastguard Worker   append_this_char(TT.scs->ch);
796*cf5a6c84SAndroid Build Coastguard Worker   gch();
797*cf5a6c84SAndroid Build Coastguard Worker }
798*cf5a6c84SAndroid Build Coastguard Worker 
find_keyword_or_builtin(char * table,int first_tok_in_table)799*cf5a6c84SAndroid Build Coastguard Worker static int find_keyword_or_builtin(char *table,
800*cf5a6c84SAndroid Build Coastguard Worker     int first_tok_in_table)
801*cf5a6c84SAndroid Build Coastguard Worker {
802*cf5a6c84SAndroid Build Coastguard Worker   char s[16] = " ", *p;
803*cf5a6c84SAndroid Build Coastguard Worker   // keywords and builtin functions are spaced 10 apart for strstr() lookup,
804*cf5a6c84SAndroid Build Coastguard Worker   // so must be less than that long.
805*cf5a6c84SAndroid Build Coastguard Worker   if (TT.scs->toklen >= 10) return 0;
806*cf5a6c84SAndroid Build Coastguard Worker   strcat(s, TT.scs->tokstr);
807*cf5a6c84SAndroid Build Coastguard Worker   strcat(s, " ");
808*cf5a6c84SAndroid Build Coastguard Worker   p = strstr(table, s);
809*cf5a6c84SAndroid Build Coastguard Worker   if (!p) return 0;
810*cf5a6c84SAndroid Build Coastguard Worker   return first_tok_in_table + (p - table) / 10;
811*cf5a6c84SAndroid Build Coastguard Worker }
812*cf5a6c84SAndroid Build Coastguard Worker 
find_token(void)813*cf5a6c84SAndroid Build Coastguard Worker static int find_token(void)
814*cf5a6c84SAndroid Build Coastguard Worker {
815*cf5a6c84SAndroid Build Coastguard Worker   char s[6] = " ", *p;
816*cf5a6c84SAndroid Build Coastguard Worker   // tokens are spaced 3 apart for strstr() lookup, so must be less than
817*cf5a6c84SAndroid Build Coastguard Worker   // that long.
818*cf5a6c84SAndroid Build Coastguard Worker   strcat(s, TT.scs->tokstr);
819*cf5a6c84SAndroid Build Coastguard Worker   strcat(s, " ");
820*cf5a6c84SAndroid Build Coastguard Worker   p = strstr(ops, s);
821*cf5a6c84SAndroid Build Coastguard Worker   if (!p) return 0;
822*cf5a6c84SAndroid Build Coastguard Worker   return tksemi + (p - ops) / 3;
823*cf5a6c84SAndroid Build Coastguard Worker }
824*cf5a6c84SAndroid Build Coastguard Worker 
find_keyword(void)825*cf5a6c84SAndroid Build Coastguard Worker static int find_keyword(void)
826*cf5a6c84SAndroid Build Coastguard Worker {
827*cf5a6c84SAndroid Build Coastguard Worker   return find_keyword_or_builtin(keywords, tkin);
828*cf5a6c84SAndroid Build Coastguard Worker }
829*cf5a6c84SAndroid Build Coastguard Worker 
find_builtin(void)830*cf5a6c84SAndroid Build Coastguard Worker static int find_builtin(void)
831*cf5a6c84SAndroid Build Coastguard Worker {
832*cf5a6c84SAndroid Build Coastguard Worker   return find_keyword_or_builtin(builtins, tkatan2);
833*cf5a6c84SAndroid Build Coastguard Worker }
834*cf5a6c84SAndroid Build Coastguard Worker 
get_number(void)835*cf5a6c84SAndroid Build Coastguard Worker static void get_number(void)
836*cf5a6c84SAndroid Build Coastguard Worker {
837*cf5a6c84SAndroid Build Coastguard Worker   // Assumes TT.scs->ch is digit or dot on entry.
838*cf5a6c84SAndroid Build Coastguard Worker   // TT.scs->p points to the following character.
839*cf5a6c84SAndroid Build Coastguard Worker   // OK formats: 1 1. 1.2 1.2E3 1.2E+3 1.2E-3 1.E2 1.E+2 1.E-2 1E2 .1 .1E2
840*cf5a6c84SAndroid Build Coastguard Worker   // .1E+2 .1E-2
841*cf5a6c84SAndroid Build Coastguard Worker   // NOT OK: . .E .E1 .E+ .E+1 ; 1E .1E 1.E 1.E+ 1.E- parse as number
842*cf5a6c84SAndroid Build Coastguard Worker   // followed by variable E.
843*cf5a6c84SAndroid Build Coastguard Worker   // gawk accepts 12.E+ and 12.E- as 12; nawk & mawk say syntax error.
844*cf5a6c84SAndroid Build Coastguard Worker   char *leftover;
845*cf5a6c84SAndroid Build Coastguard Worker   int len;
846*cf5a6c84SAndroid Build Coastguard Worker   TT.scs->numval = strtod(TT.scs->p - 1, &leftover);
847*cf5a6c84SAndroid Build Coastguard Worker   len = leftover - TT.scs->p + 1;
848*cf5a6c84SAndroid Build Coastguard Worker   if (len == 0) {
849*cf5a6c84SAndroid Build Coastguard Worker     append_char();
850*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->toktype = ERROR;
851*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->tok = tkerr;
852*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->error = 1;
853*cf5a6c84SAndroid Build Coastguard Worker     FFATAL("Unexpected token '%s'\n", TT.scs->tokstr);
854*cf5a6c84SAndroid Build Coastguard Worker     return;
855*cf5a6c84SAndroid Build Coastguard Worker   }
856*cf5a6c84SAndroid Build Coastguard Worker   while (len--)
857*cf5a6c84SAndroid Build Coastguard Worker     append_char();
858*cf5a6c84SAndroid Build Coastguard Worker }
859*cf5a6c84SAndroid Build Coastguard Worker 
get_string_or_regex(int endchar)860*cf5a6c84SAndroid Build Coastguard Worker static void get_string_or_regex(int endchar)
861*cf5a6c84SAndroid Build Coastguard Worker {
862*cf5a6c84SAndroid Build Coastguard Worker   gch();
863*cf5a6c84SAndroid Build Coastguard Worker   while (TT.scs->ch != endchar) {
864*cf5a6c84SAndroid Build Coastguard Worker     if (TT.scs->ch == '\n') {
865*cf5a6c84SAndroid Build Coastguard Worker       // FIXME Handle unterminated string or regex. Is this OK?
866*cf5a6c84SAndroid Build Coastguard Worker       // FIXME TODO better diagnostic here?
867*cf5a6c84SAndroid Build Coastguard Worker       XERR("%s\n", "unterminated string or regex");
868*cf5a6c84SAndroid Build Coastguard Worker       break;
869*cf5a6c84SAndroid Build Coastguard Worker     } else if (TT.scs->ch == '\\') {
870*cf5a6c84SAndroid Build Coastguard Worker       // \\ \a \b \f \n \r \t \v \" \/ \ddd
871*cf5a6c84SAndroid Build Coastguard Worker       char *p, *escapes = "\\abfnrtv\"/";
872*cf5a6c84SAndroid Build Coastguard Worker       gch();
873*cf5a6c84SAndroid Build Coastguard Worker       if (TT.scs->ch == '\n') {  // backslash newline is continuation
874*cf5a6c84SAndroid Build Coastguard Worker         gch();
875*cf5a6c84SAndroid Build Coastguard Worker         continue;
876*cf5a6c84SAndroid Build Coastguard Worker       } else if ((p = strchr(escapes, TT.scs->ch))) {
877*cf5a6c84SAndroid Build Coastguard Worker         // posix regex does not use these escapes,
878*cf5a6c84SAndroid Build Coastguard Worker         // but awk does, so do them.
879*cf5a6c84SAndroid Build Coastguard Worker         int c = "\\\a\b\f\n\r\t\v\"/"[p-escapes];
880*cf5a6c84SAndroid Build Coastguard Worker         append_this_char(c);
881*cf5a6c84SAndroid Build Coastguard Worker         // Need to double up \ inside literal regex
882*cf5a6c84SAndroid Build Coastguard Worker         if (endchar == '/' && c == '\\') append_this_char('\\');
883*cf5a6c84SAndroid Build Coastguard Worker         gch();
884*cf5a6c84SAndroid Build Coastguard Worker       } else if (TT.scs->ch == 'x') {
885*cf5a6c84SAndroid Build Coastguard Worker         gch();
886*cf5a6c84SAndroid Build Coastguard Worker         if (isxdigit(TT.scs->ch)) {
887*cf5a6c84SAndroid Build Coastguard Worker           int c = hexval(TT.scs->ch);
888*cf5a6c84SAndroid Build Coastguard Worker           gch();
889*cf5a6c84SAndroid Build Coastguard Worker           if (isxdigit(TT.scs->ch)) {
890*cf5a6c84SAndroid Build Coastguard Worker             c = c * 16 + hexval(TT.scs->ch);
891*cf5a6c84SAndroid Build Coastguard Worker             gch();
892*cf5a6c84SAndroid Build Coastguard Worker           }
893*cf5a6c84SAndroid Build Coastguard Worker           append_this_char(c);
894*cf5a6c84SAndroid Build Coastguard Worker         } else append_this_char('x');
895*cf5a6c84SAndroid Build Coastguard Worker       } else if (TT.scs->ch == 'u') {
896*cf5a6c84SAndroid Build Coastguard Worker         gch();
897*cf5a6c84SAndroid Build Coastguard Worker         if (isxdigit(TT.scs->ch)) {
898*cf5a6c84SAndroid Build Coastguard Worker           int i = 0, j = 0, c = 0;
899*cf5a6c84SAndroid Build Coastguard Worker           char codep[9] = {0};
900*cf5a6c84SAndroid Build Coastguard Worker           do {
901*cf5a6c84SAndroid Build Coastguard Worker             codep[j++] = TT.scs->ch;
902*cf5a6c84SAndroid Build Coastguard Worker             gch();
903*cf5a6c84SAndroid Build Coastguard Worker           } while (j < 8 && isxdigit(TT.scs->ch));
904*cf5a6c84SAndroid Build Coastguard Worker           c = strtol(codep, 0, 16);
905*cf5a6c84SAndroid Build Coastguard Worker           for (i = wctoutf8(codep, c), j = 0; j < i; j++)
906*cf5a6c84SAndroid Build Coastguard Worker             append_this_char(codep[j]);
907*cf5a6c84SAndroid Build Coastguard Worker         } else append_this_char('u');
908*cf5a6c84SAndroid Build Coastguard Worker       } else if (isdigit(TT.scs->ch)) {
909*cf5a6c84SAndroid Build Coastguard Worker         if (TT.scs->ch < '8') {
910*cf5a6c84SAndroid Build Coastguard Worker           int k, c = 0;
911*cf5a6c84SAndroid Build Coastguard Worker           for (k = 0; k < 3; k++) {
912*cf5a6c84SAndroid Build Coastguard Worker             if (isdigit(TT.scs->ch) && TT.scs->ch < '8') {
913*cf5a6c84SAndroid Build Coastguard Worker               c = c * 8 + TT.scs->ch - '0';
914*cf5a6c84SAndroid Build Coastguard Worker               gch();
915*cf5a6c84SAndroid Build Coastguard Worker             } else
916*cf5a6c84SAndroid Build Coastguard Worker               break;
917*cf5a6c84SAndroid Build Coastguard Worker           }
918*cf5a6c84SAndroid Build Coastguard Worker           append_this_char(c);
919*cf5a6c84SAndroid Build Coastguard Worker         } else {
920*cf5a6c84SAndroid Build Coastguard Worker           append_char();
921*cf5a6c84SAndroid Build Coastguard Worker         }
922*cf5a6c84SAndroid Build Coastguard Worker       } else {
923*cf5a6c84SAndroid Build Coastguard Worker         if (endchar == '/') {
924*cf5a6c84SAndroid Build Coastguard Worker           // pass \ unmolested if not awk escape,
925*cf5a6c84SAndroid Build Coastguard Worker           // so that regex routines can see it.
926*cf5a6c84SAndroid Build Coastguard Worker           if (!strchr(".[]()*+?{}|^$-", TT.scs->ch)) {
927*cf5a6c84SAndroid Build Coastguard Worker             XERR("warning: '\\%c' -- unknown regex escape\n", TT.scs->ch);
928*cf5a6c84SAndroid Build Coastguard Worker           }
929*cf5a6c84SAndroid Build Coastguard Worker           append_this_char('\\');
930*cf5a6c84SAndroid Build Coastguard Worker         } else {
931*cf5a6c84SAndroid Build Coastguard Worker           XERR("warning: '\\%c' treated as plain '%c'\n", TT.scs->ch, TT.scs->ch);
932*cf5a6c84SAndroid Build Coastguard Worker         }
933*cf5a6c84SAndroid Build Coastguard Worker       }
934*cf5a6c84SAndroid Build Coastguard Worker     } else if (TT.scs->ch == EOF) {
935*cf5a6c84SAndroid Build Coastguard Worker       FATAL("EOF in string or regex\n");
936*cf5a6c84SAndroid Build Coastguard Worker     } else {
937*cf5a6c84SAndroid Build Coastguard Worker       append_char();
938*cf5a6c84SAndroid Build Coastguard Worker     }
939*cf5a6c84SAndroid Build Coastguard Worker   }
940*cf5a6c84SAndroid Build Coastguard Worker   gch();
941*cf5a6c84SAndroid Build Coastguard Worker }
942*cf5a6c84SAndroid Build Coastguard Worker 
ascan_opt_div(int div_op_allowed_here)943*cf5a6c84SAndroid Build Coastguard Worker static void ascan_opt_div(int div_op_allowed_here)
944*cf5a6c84SAndroid Build Coastguard Worker {
945*cf5a6c84SAndroid Build Coastguard Worker   int n;
946*cf5a6c84SAndroid Build Coastguard Worker   for (;;) {
947*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->tokbuiltin = 0;
948*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->toklen = 0;
949*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->tokstr[0] = 0;
950*cf5a6c84SAndroid Build Coastguard Worker     while (TT.scs->ch == ' ' || TT.scs->ch == '\t')
951*cf5a6c84SAndroid Build Coastguard Worker       gch();
952*cf5a6c84SAndroid Build Coastguard Worker     if (TT.scs->ch == '\\') {
953*cf5a6c84SAndroid Build Coastguard Worker       append_char();
954*cf5a6c84SAndroid Build Coastguard Worker       if (TT.scs->ch == '\n') {
955*cf5a6c84SAndroid Build Coastguard Worker         gch();
956*cf5a6c84SAndroid Build Coastguard Worker         continue;
957*cf5a6c84SAndroid Build Coastguard Worker       }
958*cf5a6c84SAndroid Build Coastguard Worker       TT.scs->toktype = ERROR;   // \ not last char in line.
959*cf5a6c84SAndroid Build Coastguard Worker       TT.scs->tok = tkerr;
960*cf5a6c84SAndroid Build Coastguard Worker       TT.scs->error = 3;
961*cf5a6c84SAndroid Build Coastguard Worker       FATAL("backslash not last char in line\n");
962*cf5a6c84SAndroid Build Coastguard Worker       return;
963*cf5a6c84SAndroid Build Coastguard Worker     }
964*cf5a6c84SAndroid Build Coastguard Worker     break;
965*cf5a6c84SAndroid Build Coastguard Worker   }
966*cf5a6c84SAndroid Build Coastguard Worker   // Note \<NEWLINE> in comment does not continue it.
967*cf5a6c84SAndroid Build Coastguard Worker   if (TT.scs->ch == '#') {
968*cf5a6c84SAndroid Build Coastguard Worker     gch();
969*cf5a6c84SAndroid Build Coastguard Worker     while (TT.scs->ch != '\n')
970*cf5a6c84SAndroid Build Coastguard Worker       gch();
971*cf5a6c84SAndroid Build Coastguard Worker     // Need to fall through here to pick up newline.
972*cf5a6c84SAndroid Build Coastguard Worker   }
973*cf5a6c84SAndroid Build Coastguard Worker   if (TT.scs->ch == '\n') {
974*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->toktype = NEWLINE;
975*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->tok = tknl;
976*cf5a6c84SAndroid Build Coastguard Worker     append_char();
977*cf5a6c84SAndroid Build Coastguard Worker   } else if (isalpha(TT.scs->ch) || TT.scs->ch == '_') {
978*cf5a6c84SAndroid Build Coastguard Worker     append_char();
979*cf5a6c84SAndroid Build Coastguard Worker     while (isalnum(TT.scs->ch) || TT.scs->ch == '_') {
980*cf5a6c84SAndroid Build Coastguard Worker       append_char();
981*cf5a6c84SAndroid Build Coastguard Worker     }
982*cf5a6c84SAndroid Build Coastguard Worker     if ((n = find_keyword()) != 0) {
983*cf5a6c84SAndroid Build Coastguard Worker       TT.scs->toktype = KEYWORD;
984*cf5a6c84SAndroid Build Coastguard Worker       TT.scs->tok = n;
985*cf5a6c84SAndroid Build Coastguard Worker     } else if ((n = find_builtin()) != 0) {
986*cf5a6c84SAndroid Build Coastguard Worker       TT.scs->toktype = BUILTIN;
987*cf5a6c84SAndroid Build Coastguard Worker       TT.scs->tok = tkbuiltin;
988*cf5a6c84SAndroid Build Coastguard Worker       TT.scs->tokbuiltin = n;
989*cf5a6c84SAndroid Build Coastguard Worker     } else if (TT.scs->ch == '(') {
990*cf5a6c84SAndroid Build Coastguard Worker       TT.scs->toktype = USERFUNC;
991*cf5a6c84SAndroid Build Coastguard Worker       TT.scs->tok = tkfunc;
992*cf5a6c84SAndroid Build Coastguard Worker     } else {
993*cf5a6c84SAndroid Build Coastguard Worker       TT.scs->toktype = VAR;
994*cf5a6c84SAndroid Build Coastguard Worker       TT.scs->tok = tkvar;
995*cf5a6c84SAndroid Build Coastguard Worker       // skip whitespace to be able to check for , or )
996*cf5a6c84SAndroid Build Coastguard Worker       while (TT.scs->ch == ' ' || TT.scs->ch == '\t')
997*cf5a6c84SAndroid Build Coastguard Worker         gch();
998*cf5a6c84SAndroid Build Coastguard Worker     }
999*cf5a6c84SAndroid Build Coastguard Worker     return;
1000*cf5a6c84SAndroid Build Coastguard Worker   } else if (TT.scs->ch == '"') {
1001*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->toktype = STRING;
1002*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->tok = tkstring;
1003*cf5a6c84SAndroid Build Coastguard Worker     get_string_or_regex('"');
1004*cf5a6c84SAndroid Build Coastguard Worker   } else if (isdigit(TT.scs->ch) || TT.scs->ch == '.') {
1005*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->toktype = NUMBER;
1006*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->tok = tknumber;
1007*cf5a6c84SAndroid Build Coastguard Worker     get_number();
1008*cf5a6c84SAndroid Build Coastguard Worker   } else if (TT.scs->ch == '/' && ! div_op_allowed_here) {
1009*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->toktype = REGEX;
1010*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->tok = tkregex;
1011*cf5a6c84SAndroid Build Coastguard Worker     get_string_or_regex('/');
1012*cf5a6c84SAndroid Build Coastguard Worker   } else if (TT.scs->ch == EOF) {
1013*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->toktype = EOF;
1014*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->tok = tkeof;
1015*cf5a6c84SAndroid Build Coastguard Worker   } else if (TT.scs->ch == '\0') {
1016*cf5a6c84SAndroid Build Coastguard Worker     append_char();
1017*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->toktype = ERROR;
1018*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->tok = tkerr;
1019*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->error = 5;
1020*cf5a6c84SAndroid Build Coastguard Worker     FATAL("null char\n");
1021*cf5a6c84SAndroid Build Coastguard Worker   } else {
1022*cf5a6c84SAndroid Build Coastguard Worker     // All other tokens.
1023*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->toktype = TT.scs->ch;
1024*cf5a6c84SAndroid Build Coastguard Worker     append_char();
1025*cf5a6c84SAndroid Build Coastguard Worker     // Special case for **= and ** tokens
1026*cf5a6c84SAndroid Build Coastguard Worker     if (TT.scs->toktype == '*' && TT.scs->ch == '*') {
1027*cf5a6c84SAndroid Build Coastguard Worker       append_char();
1028*cf5a6c84SAndroid Build Coastguard Worker       if (TT.scs->ch == '=') {
1029*cf5a6c84SAndroid Build Coastguard Worker         append_char();
1030*cf5a6c84SAndroid Build Coastguard Worker         TT.scs->tok = tkpowasgn;
1031*cf5a6c84SAndroid Build Coastguard Worker       } else TT.scs->tok = tkpow;
1032*cf5a6c84SAndroid Build Coastguard Worker       TT.scs->toktype = TT.scs->tok + 200;
1033*cf5a6c84SAndroid Build Coastguard Worker       return;
1034*cf5a6c84SAndroid Build Coastguard Worker     }
1035*cf5a6c84SAndroid Build Coastguard Worker     // Is it a 2-character token?
1036*cf5a6c84SAndroid Build Coastguard Worker     if (TT.scs->ch != ' ' && TT.scs->ch != '\n') {
1037*cf5a6c84SAndroid Build Coastguard Worker       append_this_char(TT.scs->ch);
1038*cf5a6c84SAndroid Build Coastguard Worker       if (find_token()) {
1039*cf5a6c84SAndroid Build Coastguard Worker         TT.scs->tok = find_token();
1040*cf5a6c84SAndroid Build Coastguard Worker         TT.scs->toktype = TT.scs->tok + 200;
1041*cf5a6c84SAndroid Build Coastguard Worker         gch();  // Eat second char of token.
1042*cf5a6c84SAndroid Build Coastguard Worker         return;
1043*cf5a6c84SAndroid Build Coastguard Worker       }
1044*cf5a6c84SAndroid Build Coastguard Worker       TT.scs->toklen--;  // Not 2-character token; back off.
1045*cf5a6c84SAndroid Build Coastguard Worker       TT.scs->tokstr[TT.scs->toklen] = 0;
1046*cf5a6c84SAndroid Build Coastguard Worker     }
1047*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->tok = find_token();
1048*cf5a6c84SAndroid Build Coastguard Worker     if (TT.scs->tok) return;
1049*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->toktype = ERROR;
1050*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->tok = tkerr;
1051*cf5a6c84SAndroid Build Coastguard Worker     TT.scs->error = 4;
1052*cf5a6c84SAndroid Build Coastguard Worker     FFATAL("Unexpected token '%s'\n", TT.scs->tokstr);
1053*cf5a6c84SAndroid Build Coastguard Worker   }
1054*cf5a6c84SAndroid Build Coastguard Worker }
1055*cf5a6c84SAndroid Build Coastguard Worker 
scan_opt_div(int div_op_allowed_here)1056*cf5a6c84SAndroid Build Coastguard Worker static void scan_opt_div(int div_op_allowed_here)
1057*cf5a6c84SAndroid Build Coastguard Worker {
1058*cf5a6c84SAndroid Build Coastguard Worker   // TODO FIXME need better diags for bad tokens!
1059*cf5a6c84SAndroid Build Coastguard Worker   // TODO Also set global syntax error flag.
1060*cf5a6c84SAndroid Build Coastguard Worker   do ascan_opt_div(div_op_allowed_here); while (TT.scs->tok == tkerr);
1061*cf5a6c84SAndroid Build Coastguard Worker }
1062*cf5a6c84SAndroid Build Coastguard Worker 
init_scanner(void)1063*cf5a6c84SAndroid Build Coastguard Worker static void init_scanner(void)
1064*cf5a6c84SAndroid Build Coastguard Worker {
1065*cf5a6c84SAndroid Build Coastguard Worker   TT.prevtok = tkeof;
1066*cf5a6c84SAndroid Build Coastguard Worker   gch();
1067*cf5a6c84SAndroid Build Coastguard Worker }
1068*cf5a6c84SAndroid Build Coastguard Worker 
1069*cf5a6c84SAndroid Build Coastguard Worker // POSIX says '/' does not begin a regex wherever '/' or '/=' can mean divide.
1070*cf5a6c84SAndroid Build Coastguard Worker // Pretty sure if / or /= comes after these, it means divide:
1071*cf5a6c84SAndroid Build Coastguard Worker static char div_preceders[] = {tknumber, tkstring, tkvar, tkgetline, tkrparen, tkrbracket, tkincr, tkdecr, 0};
1072*cf5a6c84SAndroid Build Coastguard Worker 
1073*cf5a6c84SAndroid Build Coastguard Worker // For checking end of prev statement for termination and if '/' can come next
1074*cf5a6c84SAndroid Build Coastguard Worker 
scan(void)1075*cf5a6c84SAndroid Build Coastguard Worker static void scan(void)
1076*cf5a6c84SAndroid Build Coastguard Worker {
1077*cf5a6c84SAndroid Build Coastguard Worker   TT.prevtok = TT.scs->tok;
1078*cf5a6c84SAndroid Build Coastguard Worker   if (TT.prevtok && strchr(div_preceders, TT.prevtok)) scan_opt_div(1);
1079*cf5a6c84SAndroid Build Coastguard Worker   else scan_opt_div(0);
1080*cf5a6c84SAndroid Build Coastguard Worker   TT.tokstr = TT.scs->tokstr;
1081*cf5a6c84SAndroid Build Coastguard Worker }
1082*cf5a6c84SAndroid Build Coastguard Worker 
1083*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
1084*cf5a6c84SAndroid Build Coastguard Worker //// compile
1085*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
1086*cf5a6c84SAndroid Build Coastguard Worker 
1087*cf5a6c84SAndroid Build Coastguard Worker //  NOTES:
1088*cf5a6c84SAndroid Build Coastguard Worker //  NL ok after , { && || do else OR after right paren after if/while/for
1089*cf5a6c84SAndroid Build Coastguard Worker //  TODO:
1090*cf5a6c84SAndroid Build Coastguard Worker //    see case tkgetline -- test more
1091*cf5a6c84SAndroid Build Coastguard Worker //    case tkmatchop, tknotmatch -- fix ~ (/re/)
1092*cf5a6c84SAndroid Build Coastguard Worker 
1093*cf5a6c84SAndroid Build Coastguard Worker // Forward declarations -- for mutually recursive parsing functions
1094*cf5a6c84SAndroid Build Coastguard Worker static int expr(int rbp);
1095*cf5a6c84SAndroid Build Coastguard Worker static void lvalue(void);
1096*cf5a6c84SAndroid Build Coastguard Worker static int primary(void);
1097*cf5a6c84SAndroid Build Coastguard Worker static void stmt(void);
1098*cf5a6c84SAndroid Build Coastguard Worker static void action(int action_type);
1099*cf5a6c84SAndroid Build Coastguard Worker 
1100*cf5a6c84SAndroid Build Coastguard Worker #define CURTOK() (TT.scs->tok)
1101*cf5a6c84SAndroid Build Coastguard Worker #define ISTOK(toknum) (TT.scs->tok == (toknum))
1102*cf5a6c84SAndroid Build Coastguard Worker 
havetok(int tk)1103*cf5a6c84SAndroid Build Coastguard Worker static int havetok(int tk)
1104*cf5a6c84SAndroid Build Coastguard Worker {
1105*cf5a6c84SAndroid Build Coastguard Worker   if (!ISTOK(tk)) return 0;
1106*cf5a6c84SAndroid Build Coastguard Worker   scan();
1107*cf5a6c84SAndroid Build Coastguard Worker   return 1;
1108*cf5a6c84SAndroid Build Coastguard Worker }
1109*cf5a6c84SAndroid Build Coastguard Worker 
1110*cf5a6c84SAndroid Build Coastguard Worker //// code and "literal" emitters
gencd(int op)1111*cf5a6c84SAndroid Build Coastguard Worker static void gencd(int op)
1112*cf5a6c84SAndroid Build Coastguard Worker {
1113*cf5a6c84SAndroid Build Coastguard Worker   TT.zcode_last = zlist_append(&TT.zcode, &op);
1114*cf5a6c84SAndroid Build Coastguard Worker }
1115*cf5a6c84SAndroid Build Coastguard Worker 
gen2cd(int op,int n)1116*cf5a6c84SAndroid Build Coastguard Worker static void gen2cd(int op, int n)
1117*cf5a6c84SAndroid Build Coastguard Worker {
1118*cf5a6c84SAndroid Build Coastguard Worker   gencd(op);
1119*cf5a6c84SAndroid Build Coastguard Worker   gencd(n);
1120*cf5a6c84SAndroid Build Coastguard Worker }
1121*cf5a6c84SAndroid Build Coastguard Worker 
make_literal_str_val(char * s)1122*cf5a6c84SAndroid Build Coastguard Worker static int make_literal_str_val(char *s)
1123*cf5a6c84SAndroid Build Coastguard Worker {
1124*cf5a6c84SAndroid Build Coastguard Worker   // Only if no nul inside string!
1125*cf5a6c84SAndroid Build Coastguard Worker   struct zvalue v = new_str_val(s);
1126*cf5a6c84SAndroid Build Coastguard Worker   return zlist_append(&TT.literals, &v);
1127*cf5a6c84SAndroid Build Coastguard Worker }
1128*cf5a6c84SAndroid Build Coastguard Worker 
make_literal_regex_val(char * s)1129*cf5a6c84SAndroid Build Coastguard Worker static int make_literal_regex_val(char *s)
1130*cf5a6c84SAndroid Build Coastguard Worker {
1131*cf5a6c84SAndroid Build Coastguard Worker   regex_t *rx;
1132*cf5a6c84SAndroid Build Coastguard Worker   rx = xmalloc(sizeof(*rx));
1133*cf5a6c84SAndroid Build Coastguard Worker   xregcomp(rx, s, REG_EXTENDED);
1134*cf5a6c84SAndroid Build Coastguard Worker   struct zvalue v = ZVINIT(ZF_RX, 0, 0);
1135*cf5a6c84SAndroid Build Coastguard Worker   v.rx = rx;
1136*cf5a6c84SAndroid Build Coastguard Worker   // Flag empty rx to make it easy to identify for split() special case
1137*cf5a6c84SAndroid Build Coastguard Worker   if (!*s) v.flags |= ZF_EMPTY_RX;
1138*cf5a6c84SAndroid Build Coastguard Worker   return zlist_append(&TT.literals, &v);
1139*cf5a6c84SAndroid Build Coastguard Worker }
1140*cf5a6c84SAndroid Build Coastguard Worker 
make_literal_num_val(double num)1141*cf5a6c84SAndroid Build Coastguard Worker static int make_literal_num_val(double num)
1142*cf5a6c84SAndroid Build Coastguard Worker {
1143*cf5a6c84SAndroid Build Coastguard Worker   struct zvalue v = ZVINIT(ZF_NUM, num, 0);
1144*cf5a6c84SAndroid Build Coastguard Worker   return zlist_append(&TT.literals, &v);
1145*cf5a6c84SAndroid Build Coastguard Worker }
1146*cf5a6c84SAndroid Build Coastguard Worker 
make_uninit_val(void)1147*cf5a6c84SAndroid Build Coastguard Worker static int make_uninit_val(void)
1148*cf5a6c84SAndroid Build Coastguard Worker {
1149*cf5a6c84SAndroid Build Coastguard Worker   return zlist_append(&TT.literals, &uninit_zvalue);
1150*cf5a6c84SAndroid Build Coastguard Worker }
1151*cf5a6c84SAndroid Build Coastguard Worker //// END code and "literal" emitters
1152*cf5a6c84SAndroid Build Coastguard Worker 
1153*cf5a6c84SAndroid Build Coastguard Worker //// Symbol tables functions
find_func_def_entry(char * s)1154*cf5a6c84SAndroid Build Coastguard Worker static int find_func_def_entry(char *s)
1155*cf5a6c84SAndroid Build Coastguard Worker {
1156*cf5a6c84SAndroid Build Coastguard Worker   for (int k = 1; k < zlist_len(&TT.func_def_table); k++)
1157*cf5a6c84SAndroid Build Coastguard Worker     if (!strcmp(s, FUNC_DEF[k].name)) return k;
1158*cf5a6c84SAndroid Build Coastguard Worker   return 0;
1159*cf5a6c84SAndroid Build Coastguard Worker }
1160*cf5a6c84SAndroid Build Coastguard Worker 
add_func_def_entry(char * s)1161*cf5a6c84SAndroid Build Coastguard Worker static int add_func_def_entry(char *s)
1162*cf5a6c84SAndroid Build Coastguard Worker {
1163*cf5a6c84SAndroid Build Coastguard Worker   struct functab_slot ent = {0, 0, {0, 0, 0, 0}, 0};
1164*cf5a6c84SAndroid Build Coastguard Worker   ent.name = xstrdup(s);
1165*cf5a6c84SAndroid Build Coastguard Worker   int slotnum = zlist_append(&TT.func_def_table, &ent);
1166*cf5a6c84SAndroid Build Coastguard Worker   return slotnum;
1167*cf5a6c84SAndroid Build Coastguard Worker }
1168*cf5a6c84SAndroid Build Coastguard Worker 
find_global(char * s)1169*cf5a6c84SAndroid Build Coastguard Worker static int find_global(char *s)
1170*cf5a6c84SAndroid Build Coastguard Worker {
1171*cf5a6c84SAndroid Build Coastguard Worker   for (int k = 1; k < zlist_len(&TT.globals_table); k++)
1172*cf5a6c84SAndroid Build Coastguard Worker     if (!strcmp(s, GLOBAL[k].name)) return k;
1173*cf5a6c84SAndroid Build Coastguard Worker   return 0;
1174*cf5a6c84SAndroid Build Coastguard Worker }
1175*cf5a6c84SAndroid Build Coastguard Worker 
add_global(char * s)1176*cf5a6c84SAndroid Build Coastguard Worker static int add_global(char *s)
1177*cf5a6c84SAndroid Build Coastguard Worker {
1178*cf5a6c84SAndroid Build Coastguard Worker   struct symtab_slot ent = {0, 0};
1179*cf5a6c84SAndroid Build Coastguard Worker   ent.name = xstrdup(s);
1180*cf5a6c84SAndroid Build Coastguard Worker   int slotnum = zlist_append(&TT.globals_table, &ent);
1181*cf5a6c84SAndroid Build Coastguard Worker   return slotnum;
1182*cf5a6c84SAndroid Build Coastguard Worker }
1183*cf5a6c84SAndroid Build Coastguard Worker 
find_local_entry(char * s)1184*cf5a6c84SAndroid Build Coastguard Worker static int find_local_entry(char *s)
1185*cf5a6c84SAndroid Build Coastguard Worker {
1186*cf5a6c84SAndroid Build Coastguard Worker   for (int k = 1; k < zlist_len(&TT.locals_table); k++)
1187*cf5a6c84SAndroid Build Coastguard Worker     if (!strcmp(s, LOCAL[k].name)) return k;
1188*cf5a6c84SAndroid Build Coastguard Worker   return 0;
1189*cf5a6c84SAndroid Build Coastguard Worker }
1190*cf5a6c84SAndroid Build Coastguard Worker 
add_local_entry(char * s)1191*cf5a6c84SAndroid Build Coastguard Worker static int add_local_entry(char *s)
1192*cf5a6c84SAndroid Build Coastguard Worker {
1193*cf5a6c84SAndroid Build Coastguard Worker   struct symtab_slot ent = {0, 0};
1194*cf5a6c84SAndroid Build Coastguard Worker   ent.name = xstrdup(s);
1195*cf5a6c84SAndroid Build Coastguard Worker   int slotnum = zlist_append(&TT.locals_table, &ent);
1196*cf5a6c84SAndroid Build Coastguard Worker   return slotnum;
1197*cf5a6c84SAndroid Build Coastguard Worker }
1198*cf5a6c84SAndroid Build Coastguard Worker 
find_or_add_var_name(void)1199*cf5a6c84SAndroid Build Coastguard Worker static int find_or_add_var_name(void)
1200*cf5a6c84SAndroid Build Coastguard Worker {
1201*cf5a6c84SAndroid Build Coastguard Worker   int slotnum = 0;    // + means global; - means local to function
1202*cf5a6c84SAndroid Build Coastguard Worker   int globals_ent = 0;
1203*cf5a6c84SAndroid Build Coastguard Worker   int locals_ent = find_local_entry(TT.tokstr);   // in local symbol table?
1204*cf5a6c84SAndroid Build Coastguard Worker   if (locals_ent) {
1205*cf5a6c84SAndroid Build Coastguard Worker     slotnum = -locals_ent;
1206*cf5a6c84SAndroid Build Coastguard Worker   } else {
1207*cf5a6c84SAndroid Build Coastguard Worker     globals_ent = find_global(TT.tokstr);
1208*cf5a6c84SAndroid Build Coastguard Worker     if (!globals_ent) globals_ent = add_global(TT.tokstr);
1209*cf5a6c84SAndroid Build Coastguard Worker     slotnum = globals_ent;
1210*cf5a6c84SAndroid Build Coastguard Worker     if (find_func_def_entry(TT.tokstr))
1211*cf5a6c84SAndroid Build Coastguard Worker       // POSIX: The same name shall not be used both as a variable name
1212*cf5a6c84SAndroid Build Coastguard Worker       // with global scope and as the name of a function.
1213*cf5a6c84SAndroid Build Coastguard Worker       XERR("var '%s' used as function name\n", TT.tokstr);
1214*cf5a6c84SAndroid Build Coastguard Worker   }
1215*cf5a6c84SAndroid Build Coastguard Worker   return slotnum;
1216*cf5a6c84SAndroid Build Coastguard Worker }
1217*cf5a6c84SAndroid Build Coastguard Worker 
1218*cf5a6c84SAndroid Build Coastguard Worker //// END Symbol tables functions
1219*cf5a6c84SAndroid Build Coastguard Worker 
1220*cf5a6c84SAndroid Build Coastguard Worker //// Initialization
init_locals_table(void)1221*cf5a6c84SAndroid Build Coastguard Worker static void init_locals_table(void)
1222*cf5a6c84SAndroid Build Coastguard Worker {
1223*cf5a6c84SAndroid Build Coastguard Worker   static struct symtab_slot locals_ent;
1224*cf5a6c84SAndroid Build Coastguard Worker   zlist_init(&TT.locals_table, sizeof(struct symtab_slot));
1225*cf5a6c84SAndroid Build Coastguard Worker   zlist_append(&TT.locals_table, &locals_ent);
1226*cf5a6c84SAndroid Build Coastguard Worker }
1227*cf5a6c84SAndroid Build Coastguard Worker 
init_tables(void)1228*cf5a6c84SAndroid Build Coastguard Worker static void init_tables(void)
1229*cf5a6c84SAndroid Build Coastguard Worker {
1230*cf5a6c84SAndroid Build Coastguard Worker   static struct symtab_slot global_ent;
1231*cf5a6c84SAndroid Build Coastguard Worker   static struct functab_slot func_ent;
1232*cf5a6c84SAndroid Build Coastguard Worker 
1233*cf5a6c84SAndroid Build Coastguard Worker   // Append dummy elements in lists to force valid offsets nonzero.
1234*cf5a6c84SAndroid Build Coastguard Worker   zlist_init(&TT.globals_table, sizeof(struct symtab_slot));
1235*cf5a6c84SAndroid Build Coastguard Worker   zlist_append(&TT.globals_table, &global_ent);
1236*cf5a6c84SAndroid Build Coastguard Worker   zlist_init(&TT.func_def_table, sizeof(struct functab_slot));
1237*cf5a6c84SAndroid Build Coastguard Worker   zlist_append(&TT.func_def_table, &func_ent);
1238*cf5a6c84SAndroid Build Coastguard Worker   init_locals_table();
1239*cf5a6c84SAndroid Build Coastguard Worker   zlist_init(&TT.zcode, sizeof(int));
1240*cf5a6c84SAndroid Build Coastguard Worker   gencd(tkeof);   // to ensure zcode offsets are non-zero
1241*cf5a6c84SAndroid Build Coastguard Worker   zlist_init(&TT.literals, sizeof(struct zvalue));
1242*cf5a6c84SAndroid Build Coastguard Worker   // Init stack size at twice MIN_STACK_LEFT. MIN_STACK_LEFT is at least as
1243*cf5a6c84SAndroid Build Coastguard Worker   // many entries as any statement may ever take.  Currently there is no diag
1244*cf5a6c84SAndroid Build Coastguard Worker   // if this is exceeded; prog. will probably crash. 1024 should be plenty?
1245*cf5a6c84SAndroid Build Coastguard Worker   zlist_initx(&TT.stack, sizeof(struct zvalue), 2 * MIN_STACK_LEFT);
1246*cf5a6c84SAndroid Build Coastguard Worker   TT.stackp = (struct zvalue *)TT.stack.base;
1247*cf5a6c84SAndroid Build Coastguard Worker   zlist_init(&TT.fields, sizeof(struct zvalue));
1248*cf5a6c84SAndroid Build Coastguard Worker   zlist_append(&TT.literals, &uninit_zvalue);
1249*cf5a6c84SAndroid Build Coastguard Worker   zlist_append(&TT.stack, &uninit_zvalue);
1250*cf5a6c84SAndroid Build Coastguard Worker   zlist_append(&TT.fields, &uninit_zvalue);
1251*cf5a6c84SAndroid Build Coastguard Worker   FIELD[0].vst = new_zstring("", 0);
1252*cf5a6c84SAndroid Build Coastguard Worker }
1253*cf5a6c84SAndroid Build Coastguard Worker 
init_compiler(void)1254*cf5a6c84SAndroid Build Coastguard Worker static void init_compiler(void)
1255*cf5a6c84SAndroid Build Coastguard Worker {
1256*cf5a6c84SAndroid Build Coastguard Worker   // Special variables (POSIX). Must align with enum spec_var_names
1257*cf5a6c84SAndroid Build Coastguard Worker   static char *spec_vars[] = { "ARGC", "ARGV", "CONVFMT", "ENVIRON", "FILENAME",
1258*cf5a6c84SAndroid Build Coastguard Worker       "FNR", "FS", "NF", "NR", "OFMT", "OFS", "ORS", "RLENGTH", "RS", "RSTART",
1259*cf5a6c84SAndroid Build Coastguard Worker       "SUBSEP", 0};
1260*cf5a6c84SAndroid Build Coastguard Worker 
1261*cf5a6c84SAndroid Build Coastguard Worker   init_tables();
1262*cf5a6c84SAndroid Build Coastguard Worker   for (int k = 0; spec_vars[k]; k++) {
1263*cf5a6c84SAndroid Build Coastguard Worker     TT.spec_var_limit = add_global(spec_vars[k]);
1264*cf5a6c84SAndroid Build Coastguard Worker     GLOBAL[TT.spec_var_limit++].flags |= (k == 1 || k == 3) ? ZF_MAP : ZF_SCALAR;
1265*cf5a6c84SAndroid Build Coastguard Worker     push_val(&uninit_zvalue);
1266*cf5a6c84SAndroid Build Coastguard Worker   }
1267*cf5a6c84SAndroid Build Coastguard Worker }
1268*cf5a6c84SAndroid Build Coastguard Worker //// END Initialization
1269*cf5a6c84SAndroid Build Coastguard Worker 
1270*cf5a6c84SAndroid Build Coastguard Worker //// Parsing and compiling to TT.zcode
1271*cf5a6c84SAndroid Build Coastguard Worker // Left binding powers
1272*cf5a6c84SAndroid Build Coastguard Worker static int lbp_table[] = {  // Must align with enum Toks
1273*cf5a6c84SAndroid Build Coastguard Worker   0, 0, 0, 0,     // tkunusedtoken, tkeof, tkerr, tknl,
1274*cf5a6c84SAndroid Build Coastguard Worker   250, 250, 250,  // tkvar, tknumber, tkstring,
1275*cf5a6c84SAndroid Build Coastguard Worker   250, 250, 250,  // tkregex, tkfunc, tkbuiltin,
1276*cf5a6c84SAndroid Build Coastguard Worker   0, 0, 210, 0, // tksemi, tkcomma, tklbracket, tkrbracket,
1277*cf5a6c84SAndroid Build Coastguard Worker   200, 0, 0, 0, // tklparen, tkrparen, tklbrace, tkrbrace,
1278*cf5a6c84SAndroid Build Coastguard Worker   190, 180, 180, 170, 160, // tkfield, tkincr, tkdecr, tkpow, tknot,
1279*cf5a6c84SAndroid Build Coastguard Worker   150, 150, 150, 140, 140, // tkmul, tkdiv, tkmod, tkplus, tkminus,
1280*cf5a6c84SAndroid Build Coastguard Worker   130, // tkcat, // FAKE (?) optor for concatenation (adjacent string exprs)
1281*cf5a6c84SAndroid Build Coastguard Worker   110, 110, 110, 110, 110, 110, // tklt, tkle, tkne, tkeq, tkgt, tkge,
1282*cf5a6c84SAndroid Build Coastguard Worker   100, 100, // tkmatchop, tknotmatch,
1283*cf5a6c84SAndroid Build Coastguard Worker   80, 70, // tkand, tkor,
1284*cf5a6c84SAndroid Build Coastguard Worker   60, 0, // tkternif, tkternelse,
1285*cf5a6c84SAndroid Build Coastguard Worker   50, 50, 50, 50,   // tkpowasgn, tkmodasgn, tkmulasgn, tkdivasgn,
1286*cf5a6c84SAndroid Build Coastguard Worker   50, 50, 50, // tkaddasgn, tksubasgn, tkasgn,
1287*cf5a6c84SAndroid Build Coastguard Worker   0, 120, // tkappend, tkpipe,
1288*cf5a6c84SAndroid Build Coastguard Worker   90 // tkin
1289*cf5a6c84SAndroid Build Coastguard Worker };
1290*cf5a6c84SAndroid Build Coastguard Worker 
getlbp(int tok)1291*cf5a6c84SAndroid Build Coastguard Worker static int getlbp(int tok)
1292*cf5a6c84SAndroid Build Coastguard Worker {
1293*cf5a6c84SAndroid Build Coastguard Worker   // FIXME: should tkappend be here too? is tkpipe needed?
1294*cf5a6c84SAndroid Build Coastguard Worker   // In print statement outside parens: make '>' end an expression
1295*cf5a6c84SAndroid Build Coastguard Worker   if (TT.cgl.in_print_stmt && ! TT.cgl.paren_level && (tok == tkgt || tok == tkpipe))
1296*cf5a6c84SAndroid Build Coastguard Worker     return 0;
1297*cf5a6c84SAndroid Build Coastguard Worker   return (0 <= tok && tok <= tkin) ? lbp_table[tok] :
1298*cf5a6c84SAndroid Build Coastguard Worker     // getline is special, not a normal builtin.
1299*cf5a6c84SAndroid Build Coastguard Worker     // close, index, match, split, sub, gsub, sprintf, substr
1300*cf5a6c84SAndroid Build Coastguard Worker     // are really builtin functions though bwk treats them as keywords.
1301*cf5a6c84SAndroid Build Coastguard Worker     (tkgetline <= tok && tok <= tksubstr) ? 240 : 0;     // FIXME 240 is temp?
1302*cf5a6c84SAndroid Build Coastguard Worker }
1303*cf5a6c84SAndroid Build Coastguard Worker 
1304*cf5a6c84SAndroid Build Coastguard Worker // Get right binding power. Same as left except for right associative optors
getrbp(int tok)1305*cf5a6c84SAndroid Build Coastguard Worker static int getrbp(int tok)
1306*cf5a6c84SAndroid Build Coastguard Worker {
1307*cf5a6c84SAndroid Build Coastguard Worker   int lbp = getlbp(tok);
1308*cf5a6c84SAndroid Build Coastguard Worker   // ternary (?:), assignment, power ops are right associative
1309*cf5a6c84SAndroid Build Coastguard Worker   return (lbp <= 60 || lbp == 170) ? lbp - 1 : lbp;
1310*cf5a6c84SAndroid Build Coastguard Worker }
1311*cf5a6c84SAndroid Build Coastguard Worker 
unexpected_eof(void)1312*cf5a6c84SAndroid Build Coastguard Worker static void unexpected_eof(void)
1313*cf5a6c84SAndroid Build Coastguard Worker {
1314*cf5a6c84SAndroid Build Coastguard Worker   error_exit("terminated with error(s)");
1315*cf5a6c84SAndroid Build Coastguard Worker }
1316*cf5a6c84SAndroid Build Coastguard Worker 
1317*cf5a6c84SAndroid Build Coastguard Worker //// syntax error diagnostic and recovery (Turner's method)
1318*cf5a6c84SAndroid Build Coastguard Worker // D.A. Turner, Error diagnosis and recovery in one pass compilers,
1319*cf5a6c84SAndroid Build Coastguard Worker // Information Processing Letters, Volume 6, Issue 4, 1977, Pages 113-115
1320*cf5a6c84SAndroid Build Coastguard Worker static int recovering = 0;
1321*cf5a6c84SAndroid Build Coastguard Worker 
complain(int tk)1322*cf5a6c84SAndroid Build Coastguard Worker static void complain(int tk)
1323*cf5a6c84SAndroid Build Coastguard Worker {
1324*cf5a6c84SAndroid Build Coastguard Worker   char op[3], tkstr[10];
1325*cf5a6c84SAndroid Build Coastguard Worker   if (recovering) return;
1326*cf5a6c84SAndroid Build Coastguard Worker   recovering = 1;
1327*cf5a6c84SAndroid Build Coastguard Worker   if (!strcmp(TT.tokstr, "\n")) TT.tokstr = "<newline>";
1328*cf5a6c84SAndroid Build Coastguard Worker   if (tksemi <= tk && tk <= tkpipe) {
1329*cf5a6c84SAndroid Build Coastguard Worker     get_token_text(op, tk);
1330*cf5a6c84SAndroid Build Coastguard Worker     XERR("syntax near '%s' -- '%s' expected\n", TT.tokstr, op);
1331*cf5a6c84SAndroid Build Coastguard Worker   } else if (tk >= tkin && tk <= tksubstr) {
1332*cf5a6c84SAndroid Build Coastguard Worker     if (tk < tkatan2) memmove(tkstr, keywords + 1 + 10 * (tk - tkin), 10);
1333*cf5a6c84SAndroid Build Coastguard Worker     else memmove(tkstr, builtins + 1 + 10 * (tk - tkatan2), 10);
1334*cf5a6c84SAndroid Build Coastguard Worker     *strchr(tkstr, ' ') = 0;
1335*cf5a6c84SAndroid Build Coastguard Worker     XERR("syntax near '%s' -- '%s' expected\n", TT.tokstr, tkstr);
1336*cf5a6c84SAndroid Build Coastguard Worker   } else XERR("syntax near '%s'\n", TT.tokstr);
1337*cf5a6c84SAndroid Build Coastguard Worker }
1338*cf5a6c84SAndroid Build Coastguard Worker 
expect(int tk)1339*cf5a6c84SAndroid Build Coastguard Worker static void expect(int tk)
1340*cf5a6c84SAndroid Build Coastguard Worker {
1341*cf5a6c84SAndroid Build Coastguard Worker   if (recovering) {
1342*cf5a6c84SAndroid Build Coastguard Worker     while (!ISTOK(tkeof) && !ISTOK(tk))
1343*cf5a6c84SAndroid Build Coastguard Worker       scan();
1344*cf5a6c84SAndroid Build Coastguard Worker     if (ISTOK(tkeof)) unexpected_eof();
1345*cf5a6c84SAndroid Build Coastguard Worker     scan(); // consume expected token
1346*cf5a6c84SAndroid Build Coastguard Worker     recovering = 0;
1347*cf5a6c84SAndroid Build Coastguard Worker   } else if (!havetok(tk)) complain(tk);
1348*cf5a6c84SAndroid Build Coastguard Worker }
1349*cf5a6c84SAndroid Build Coastguard Worker 
skip_to(char * tklist)1350*cf5a6c84SAndroid Build Coastguard Worker static void skip_to(char *tklist)
1351*cf5a6c84SAndroid Build Coastguard Worker {
1352*cf5a6c84SAndroid Build Coastguard Worker   do scan(); while (!ISTOK(tkeof) && !strchr(tklist, CURTOK()));
1353*cf5a6c84SAndroid Build Coastguard Worker   if (ISTOK(tkeof)) unexpected_eof();
1354*cf5a6c84SAndroid Build Coastguard Worker }
1355*cf5a6c84SAndroid Build Coastguard Worker 
1356*cf5a6c84SAndroid Build Coastguard Worker //// END syntax error diagnostic and recovery (Turner's method)
1357*cf5a6c84SAndroid Build Coastguard Worker 
optional_nl_or_semi(void)1358*cf5a6c84SAndroid Build Coastguard Worker static void optional_nl_or_semi(void)
1359*cf5a6c84SAndroid Build Coastguard Worker {
1360*cf5a6c84SAndroid Build Coastguard Worker   while (havetok(tknl) || havetok(tksemi))
1361*cf5a6c84SAndroid Build Coastguard Worker     ;
1362*cf5a6c84SAndroid Build Coastguard Worker }
1363*cf5a6c84SAndroid Build Coastguard Worker 
optional_nl(void)1364*cf5a6c84SAndroid Build Coastguard Worker static void optional_nl(void)
1365*cf5a6c84SAndroid Build Coastguard Worker {
1366*cf5a6c84SAndroid Build Coastguard Worker   while (havetok(tknl))
1367*cf5a6c84SAndroid Build Coastguard Worker     ;
1368*cf5a6c84SAndroid Build Coastguard Worker }
1369*cf5a6c84SAndroid Build Coastguard Worker 
rparen(void)1370*cf5a6c84SAndroid Build Coastguard Worker static void rparen(void)
1371*cf5a6c84SAndroid Build Coastguard Worker {
1372*cf5a6c84SAndroid Build Coastguard Worker   expect(tkrparen);
1373*cf5a6c84SAndroid Build Coastguard Worker   optional_nl();
1374*cf5a6c84SAndroid Build Coastguard Worker }
1375*cf5a6c84SAndroid Build Coastguard Worker 
have_comma(void)1376*cf5a6c84SAndroid Build Coastguard Worker static int have_comma(void)
1377*cf5a6c84SAndroid Build Coastguard Worker {
1378*cf5a6c84SAndroid Build Coastguard Worker   if (!havetok(tkcomma)) return 0;
1379*cf5a6c84SAndroid Build Coastguard Worker   optional_nl();
1380*cf5a6c84SAndroid Build Coastguard Worker   return 1;
1381*cf5a6c84SAndroid Build Coastguard Worker }
1382*cf5a6c84SAndroid Build Coastguard Worker 
check_set_map(int slotnum)1383*cf5a6c84SAndroid Build Coastguard Worker static void check_set_map(int slotnum)
1384*cf5a6c84SAndroid Build Coastguard Worker {
1385*cf5a6c84SAndroid Build Coastguard Worker   // POSIX: The same name shall not be used within the same scope both as
1386*cf5a6c84SAndroid Build Coastguard Worker   // a scalar variable and as an array.
1387*cf5a6c84SAndroid Build Coastguard Worker   if (slotnum < 0 && LOCAL[-slotnum].flags & ZF_SCALAR)
1388*cf5a6c84SAndroid Build Coastguard Worker     XERR("scalar param '%s' used as array\n", LOCAL[-slotnum].name);
1389*cf5a6c84SAndroid Build Coastguard Worker   if (slotnum > 0 && GLOBAL[slotnum].flags & ZF_SCALAR)
1390*cf5a6c84SAndroid Build Coastguard Worker     XERR("scalar var '%s' used as array\n", GLOBAL[slotnum].name);
1391*cf5a6c84SAndroid Build Coastguard Worker   if (slotnum < 0) LOCAL[-slotnum].flags |= ZF_MAP;
1392*cf5a6c84SAndroid Build Coastguard Worker   if (slotnum > 0) GLOBAL[slotnum].flags |= ZF_MAP;
1393*cf5a6c84SAndroid Build Coastguard Worker }
1394*cf5a6c84SAndroid Build Coastguard Worker 
check_set_scalar(int slotnum)1395*cf5a6c84SAndroid Build Coastguard Worker static void check_set_scalar(int slotnum)
1396*cf5a6c84SAndroid Build Coastguard Worker {
1397*cf5a6c84SAndroid Build Coastguard Worker   if (slotnum < 0 && LOCAL[-slotnum].flags & ZF_MAP)
1398*cf5a6c84SAndroid Build Coastguard Worker     XERR("array param '%s' used as scalar\n", LOCAL[-slotnum].name);
1399*cf5a6c84SAndroid Build Coastguard Worker   if (slotnum > 0 && GLOBAL[slotnum].flags & ZF_MAP)
1400*cf5a6c84SAndroid Build Coastguard Worker     XERR("array var '%s' used as scalar\n", GLOBAL[slotnum].name);
1401*cf5a6c84SAndroid Build Coastguard Worker   if (slotnum < 0) LOCAL[-slotnum].flags |= ZF_SCALAR;
1402*cf5a6c84SAndroid Build Coastguard Worker   if (slotnum > 0) GLOBAL[slotnum].flags |= ZF_SCALAR;
1403*cf5a6c84SAndroid Build Coastguard Worker }
1404*cf5a6c84SAndroid Build Coastguard Worker 
map_name(void)1405*cf5a6c84SAndroid Build Coastguard Worker static void map_name(void)
1406*cf5a6c84SAndroid Build Coastguard Worker {
1407*cf5a6c84SAndroid Build Coastguard Worker   int slotnum;
1408*cf5a6c84SAndroid Build Coastguard Worker   check_set_map(slotnum = find_or_add_var_name());
1409*cf5a6c84SAndroid Build Coastguard Worker   gen2cd(tkvar, slotnum);
1410*cf5a6c84SAndroid Build Coastguard Worker }
1411*cf5a6c84SAndroid Build Coastguard Worker 
check_builtin_arg_counts(int tk,int num_args,char * fname)1412*cf5a6c84SAndroid Build Coastguard Worker static void check_builtin_arg_counts(int tk, int num_args, char *fname)
1413*cf5a6c84SAndroid Build Coastguard Worker {
1414*cf5a6c84SAndroid Build Coastguard Worker   static char builtin_1_arg[] = { tkcos, tksin, tkexp, tklog, tksqrt, tkint,
1415*cf5a6c84SAndroid Build Coastguard Worker                                   tktolower, tktoupper, tkclose, tksystem, 0};
1416*cf5a6c84SAndroid Build Coastguard Worker   static char builtin_2_arg[] = { tkatan2, tkmatch, tkindex, tklshift, tkrshift, 0};
1417*cf5a6c84SAndroid Build Coastguard Worker   static char builtin_al_2_arg[] = { tkband, tkbor, tkbxor, 0};
1418*cf5a6c84SAndroid Build Coastguard Worker   static char builtin_2_3_arg[] = { tksub, tkgsub, tksplit, tksubstr, 0};
1419*cf5a6c84SAndroid Build Coastguard Worker   static char builtin_0_1_arg[] = { tksrand, tklength, tkfflush, 0};
1420*cf5a6c84SAndroid Build Coastguard Worker 
1421*cf5a6c84SAndroid Build Coastguard Worker   if (tk == tkrand && num_args)
1422*cf5a6c84SAndroid Build Coastguard Worker     XERR("function '%s' expected no args, got %d\n", fname, num_args);
1423*cf5a6c84SAndroid Build Coastguard Worker   else if (strchr(builtin_1_arg, tk) && num_args != 1)
1424*cf5a6c84SAndroid Build Coastguard Worker     XERR("function '%s' expected 1 arg, got %d\n", fname, num_args);
1425*cf5a6c84SAndroid Build Coastguard Worker   else if (strchr(builtin_2_arg, tk) && num_args != 2)
1426*cf5a6c84SAndroid Build Coastguard Worker     XERR("function '%s' expected 2 args, got %d\n", fname, num_args);
1427*cf5a6c84SAndroid Build Coastguard Worker   else if (strchr(builtin_al_2_arg, tk) && num_args < 2)
1428*cf5a6c84SAndroid Build Coastguard Worker     XERR("function '%s' expected at least 2 args, got %d\n", fname, num_args);
1429*cf5a6c84SAndroid Build Coastguard Worker   else if (strchr(builtin_2_3_arg, tk) && num_args != 2 && num_args != 3)
1430*cf5a6c84SAndroid Build Coastguard Worker     XERR("function '%s' expected 2 or 3 args, got %d\n", fname, num_args);
1431*cf5a6c84SAndroid Build Coastguard Worker   else if (strchr(builtin_0_1_arg, tk) && num_args != 0 && num_args != 1)
1432*cf5a6c84SAndroid Build Coastguard Worker     XERR("function '%s' expected no arg or 1 arg, got %d\n", fname, num_args);
1433*cf5a6c84SAndroid Build Coastguard Worker }
1434*cf5a6c84SAndroid Build Coastguard Worker 
builtin_call(int tk,char * builtin_name)1435*cf5a6c84SAndroid Build Coastguard Worker static void builtin_call(int tk, char *builtin_name)
1436*cf5a6c84SAndroid Build Coastguard Worker {
1437*cf5a6c84SAndroid Build Coastguard Worker   int num_args = 0;
1438*cf5a6c84SAndroid Build Coastguard Worker   expect(tklparen);
1439*cf5a6c84SAndroid Build Coastguard Worker   TT.cgl.paren_level++;
1440*cf5a6c84SAndroid Build Coastguard Worker   switch (tk) {
1441*cf5a6c84SAndroid Build Coastguard Worker     case tksub:
1442*cf5a6c84SAndroid Build Coastguard Worker     case tkgsub:
1443*cf5a6c84SAndroid Build Coastguard Worker       if (ISTOK(tkregex)) {
1444*cf5a6c84SAndroid Build Coastguard Worker         gen2cd(tkregex, make_literal_regex_val(TT.tokstr));
1445*cf5a6c84SAndroid Build Coastguard Worker         scan();
1446*cf5a6c84SAndroid Build Coastguard Worker       } else expr(0);
1447*cf5a6c84SAndroid Build Coastguard Worker       expect(tkcomma);
1448*cf5a6c84SAndroid Build Coastguard Worker       optional_nl();
1449*cf5a6c84SAndroid Build Coastguard Worker       expr(0);
1450*cf5a6c84SAndroid Build Coastguard Worker       if (have_comma()) {
1451*cf5a6c84SAndroid Build Coastguard Worker         lvalue();
1452*cf5a6c84SAndroid Build Coastguard Worker       } else {
1453*cf5a6c84SAndroid Build Coastguard Worker         gen2cd(tknumber, make_literal_num_val(0));
1454*cf5a6c84SAndroid Build Coastguard Worker         gen2cd(opfldref, tkeof);
1455*cf5a6c84SAndroid Build Coastguard Worker       }
1456*cf5a6c84SAndroid Build Coastguard Worker       num_args = 3;
1457*cf5a6c84SAndroid Build Coastguard Worker       break;
1458*cf5a6c84SAndroid Build Coastguard Worker 
1459*cf5a6c84SAndroid Build Coastguard Worker     case tkmatch:
1460*cf5a6c84SAndroid Build Coastguard Worker       expr(0);
1461*cf5a6c84SAndroid Build Coastguard Worker       expect(tkcomma);
1462*cf5a6c84SAndroid Build Coastguard Worker       optional_nl();
1463*cf5a6c84SAndroid Build Coastguard Worker       if (ISTOK(tkregex)) {
1464*cf5a6c84SAndroid Build Coastguard Worker         gen2cd(tkregex, make_literal_regex_val(TT.tokstr));
1465*cf5a6c84SAndroid Build Coastguard Worker         scan();
1466*cf5a6c84SAndroid Build Coastguard Worker       } else expr(0);
1467*cf5a6c84SAndroid Build Coastguard Worker       num_args = 2;
1468*cf5a6c84SAndroid Build Coastguard Worker       break;
1469*cf5a6c84SAndroid Build Coastguard Worker 
1470*cf5a6c84SAndroid Build Coastguard Worker     case tksplit:
1471*cf5a6c84SAndroid Build Coastguard Worker       expr(0);
1472*cf5a6c84SAndroid Build Coastguard Worker       expect(tkcomma);
1473*cf5a6c84SAndroid Build Coastguard Worker       optional_nl();
1474*cf5a6c84SAndroid Build Coastguard Worker       if (ISTOK(tkvar) && (TT.scs->ch == ',' || TT.scs->ch == ')')) {
1475*cf5a6c84SAndroid Build Coastguard Worker         map_name();
1476*cf5a6c84SAndroid Build Coastguard Worker         scan();
1477*cf5a6c84SAndroid Build Coastguard Worker       } else {
1478*cf5a6c84SAndroid Build Coastguard Worker         XERR("%s\n", "expected array name as split() 2nd arg");
1479*cf5a6c84SAndroid Build Coastguard Worker         expr(0);
1480*cf5a6c84SAndroid Build Coastguard Worker       }
1481*cf5a6c84SAndroid Build Coastguard Worker       // FIXME some recovery needed here!?
1482*cf5a6c84SAndroid Build Coastguard Worker       num_args = 2;
1483*cf5a6c84SAndroid Build Coastguard Worker       if (have_comma()) {
1484*cf5a6c84SAndroid Build Coastguard Worker         if (ISTOK(tkregex)) {
1485*cf5a6c84SAndroid Build Coastguard Worker           gen2cd(tkregex, make_literal_regex_val(TT.tokstr));
1486*cf5a6c84SAndroid Build Coastguard Worker           scan();
1487*cf5a6c84SAndroid Build Coastguard Worker         } else expr(0);
1488*cf5a6c84SAndroid Build Coastguard Worker         num_args++;
1489*cf5a6c84SAndroid Build Coastguard Worker       }
1490*cf5a6c84SAndroid Build Coastguard Worker       break;
1491*cf5a6c84SAndroid Build Coastguard Worker 
1492*cf5a6c84SAndroid Build Coastguard Worker     case tklength:
1493*cf5a6c84SAndroid Build Coastguard Worker       if (ISTOK(tkvar) && (TT.scs->ch == ',' || TT.scs->ch == ')')) {
1494*cf5a6c84SAndroid Build Coastguard Worker         gen2cd(tkvar, find_or_add_var_name());
1495*cf5a6c84SAndroid Build Coastguard Worker         scan();
1496*cf5a6c84SAndroid Build Coastguard Worker         num_args++;
1497*cf5a6c84SAndroid Build Coastguard Worker       }
1498*cf5a6c84SAndroid Build Coastguard Worker       ATTR_FALLTHROUGH_INTENDED;
1499*cf5a6c84SAndroid Build Coastguard Worker 
1500*cf5a6c84SAndroid Build Coastguard Worker     default:
1501*cf5a6c84SAndroid Build Coastguard Worker       if (ISTOK(tkrparen)) break;
1502*cf5a6c84SAndroid Build Coastguard Worker       do {
1503*cf5a6c84SAndroid Build Coastguard Worker         expr(0);
1504*cf5a6c84SAndroid Build Coastguard Worker         num_args++;
1505*cf5a6c84SAndroid Build Coastguard Worker       } while (have_comma());
1506*cf5a6c84SAndroid Build Coastguard Worker       break;
1507*cf5a6c84SAndroid Build Coastguard Worker   }
1508*cf5a6c84SAndroid Build Coastguard Worker   expect(tkrparen);
1509*cf5a6c84SAndroid Build Coastguard Worker   TT.cgl.paren_level--;
1510*cf5a6c84SAndroid Build Coastguard Worker 
1511*cf5a6c84SAndroid Build Coastguard Worker   check_builtin_arg_counts(tk, num_args, builtin_name);
1512*cf5a6c84SAndroid Build Coastguard Worker 
1513*cf5a6c84SAndroid Build Coastguard Worker   gen2cd(tk, num_args);
1514*cf5a6c84SAndroid Build Coastguard Worker }
1515*cf5a6c84SAndroid Build Coastguard Worker 
function_call(void)1516*cf5a6c84SAndroid Build Coastguard Worker static void function_call(void)
1517*cf5a6c84SAndroid Build Coastguard Worker {
1518*cf5a6c84SAndroid Build Coastguard Worker   // Function call: generate TT.zcode to:
1519*cf5a6c84SAndroid Build Coastguard Worker   //  push placeholder for return value, push placeholder for return addr,
1520*cf5a6c84SAndroid Build Coastguard Worker   //  push args, then push number of args, then:
1521*cf5a6c84SAndroid Build Coastguard Worker   //      for builtins: gen opcode (e.g. tkgsub)
1522*cf5a6c84SAndroid Build Coastguard Worker   //      for user func: gen (tkfunc, number-of-args)
1523*cf5a6c84SAndroid Build Coastguard Worker   int functk = 0, funcnum = 0;
1524*cf5a6c84SAndroid Build Coastguard Worker   char builtin_name[16];  // be sure it's long enough for all builtins
1525*cf5a6c84SAndroid Build Coastguard Worker   if (ISTOK(tkbuiltin)) {
1526*cf5a6c84SAndroid Build Coastguard Worker     functk = TT.scs->tokbuiltin;
1527*cf5a6c84SAndroid Build Coastguard Worker     strcpy(builtin_name, TT.tokstr);
1528*cf5a6c84SAndroid Build Coastguard Worker   } else if (ISTOK(tkfunc)) { // user function
1529*cf5a6c84SAndroid Build Coastguard Worker     funcnum = find_func_def_entry(TT.tokstr);
1530*cf5a6c84SAndroid Build Coastguard Worker     if (!funcnum) funcnum = add_func_def_entry(TT.tokstr);
1531*cf5a6c84SAndroid Build Coastguard Worker     FUNC_DEF[funcnum].flags |= FUNC_CALLED;
1532*cf5a6c84SAndroid Build Coastguard Worker     gen2cd(opprepcall, funcnum);
1533*cf5a6c84SAndroid Build Coastguard Worker   } else error_exit("bad function %s!", TT.tokstr);
1534*cf5a6c84SAndroid Build Coastguard Worker   scan();
1535*cf5a6c84SAndroid Build Coastguard Worker   // length() can appear without parens
1536*cf5a6c84SAndroid Build Coastguard Worker   int num_args = 0;
1537*cf5a6c84SAndroid Build Coastguard Worker   if (functk == tklength && !ISTOK(tklparen)) {
1538*cf5a6c84SAndroid Build Coastguard Worker     gen2cd(functk, 0);
1539*cf5a6c84SAndroid Build Coastguard Worker     return;
1540*cf5a6c84SAndroid Build Coastguard Worker   }
1541*cf5a6c84SAndroid Build Coastguard Worker   if (functk) {   // builtin
1542*cf5a6c84SAndroid Build Coastguard Worker     builtin_call(functk, builtin_name);
1543*cf5a6c84SAndroid Build Coastguard Worker     return;
1544*cf5a6c84SAndroid Build Coastguard Worker   }
1545*cf5a6c84SAndroid Build Coastguard Worker   expect(tklparen);
1546*cf5a6c84SAndroid Build Coastguard Worker   TT.cgl.paren_level++;
1547*cf5a6c84SAndroid Build Coastguard Worker   if (ISTOK(tkrparen)) {
1548*cf5a6c84SAndroid Build Coastguard Worker     scan();
1549*cf5a6c84SAndroid Build Coastguard Worker   } else {
1550*cf5a6c84SAndroid Build Coastguard Worker     do {
1551*cf5a6c84SAndroid Build Coastguard Worker       if (ISTOK(tkvar) && (TT.scs->ch == ',' || TT.scs->ch == ')')) {
1552*cf5a6c84SAndroid Build Coastguard Worker         // Function call arg that is a lone variable. Cannot tell in this
1553*cf5a6c84SAndroid Build Coastguard Worker         // context if it is a scalar or map. Just add it to symbol table.
1554*cf5a6c84SAndroid Build Coastguard Worker         gen2cd(tkvar, find_or_add_var_name());
1555*cf5a6c84SAndroid Build Coastguard Worker         scan();
1556*cf5a6c84SAndroid Build Coastguard Worker       } else expr(0);
1557*cf5a6c84SAndroid Build Coastguard Worker       num_args++;
1558*cf5a6c84SAndroid Build Coastguard Worker     } while (have_comma());
1559*cf5a6c84SAndroid Build Coastguard Worker     expect(tkrparen);
1560*cf5a6c84SAndroid Build Coastguard Worker   }
1561*cf5a6c84SAndroid Build Coastguard Worker   TT.cgl.paren_level--;
1562*cf5a6c84SAndroid Build Coastguard Worker   gen2cd(tkfunc, num_args);
1563*cf5a6c84SAndroid Build Coastguard Worker }
1564*cf5a6c84SAndroid Build Coastguard Worker 
var(void)1565*cf5a6c84SAndroid Build Coastguard Worker static void var(void)
1566*cf5a6c84SAndroid Build Coastguard Worker {
1567*cf5a6c84SAndroid Build Coastguard Worker   // var name is in TT.tokstr
1568*cf5a6c84SAndroid Build Coastguard Worker   // slotnum: + means global; - means local to function
1569*cf5a6c84SAndroid Build Coastguard Worker   int slotnum = find_or_add_var_name();
1570*cf5a6c84SAndroid Build Coastguard Worker   scan();
1571*cf5a6c84SAndroid Build Coastguard Worker   if (havetok(tklbracket)) {
1572*cf5a6c84SAndroid Build Coastguard Worker     check_set_map(slotnum);
1573*cf5a6c84SAndroid Build Coastguard Worker     int num_subscripts = 0;
1574*cf5a6c84SAndroid Build Coastguard Worker     do {
1575*cf5a6c84SAndroid Build Coastguard Worker       expr(0);
1576*cf5a6c84SAndroid Build Coastguard Worker       num_subscripts++;
1577*cf5a6c84SAndroid Build Coastguard Worker     } while (have_comma());
1578*cf5a6c84SAndroid Build Coastguard Worker     expect(tkrbracket);
1579*cf5a6c84SAndroid Build Coastguard Worker     if (num_subscripts > 1) gen2cd(tkrbracket, num_subscripts);
1580*cf5a6c84SAndroid Build Coastguard Worker     gen2cd(opmap, slotnum);
1581*cf5a6c84SAndroid Build Coastguard Worker   } else {
1582*cf5a6c84SAndroid Build Coastguard Worker     check_set_scalar(slotnum);
1583*cf5a6c84SAndroid Build Coastguard Worker     gen2cd(tkvar, slotnum);
1584*cf5a6c84SAndroid Build Coastguard Worker   }
1585*cf5a6c84SAndroid Build Coastguard Worker }
1586*cf5a6c84SAndroid Build Coastguard Worker 
1587*cf5a6c84SAndroid Build Coastguard Worker //   Dollar $ tkfield can be followed by "any" expresson, but
1588*cf5a6c84SAndroid Build Coastguard Worker //   the way it binds varies.
1589*cf5a6c84SAndroid Build Coastguard Worker //   The following are valid lvalues:
1590*cf5a6c84SAndroid Build Coastguard Worker //   $ ( expr )
1591*cf5a6c84SAndroid Build Coastguard Worker //   $ tkvar $ tknumber $ tkstring $ tkregex
1592*cf5a6c84SAndroid Build Coastguard Worker //   $ tkfunc(...)
1593*cf5a6c84SAndroid Build Coastguard Worker //   $ tkbuiltin(...)
1594*cf5a6c84SAndroid Build Coastguard Worker //   $ length   # with no parens after
1595*cf5a6c84SAndroid Build Coastguard Worker //   $ tkclose(), ... $ tksubstr
1596*cf5a6c84SAndroid Build Coastguard Worker //   $ tkgetline FIXME TODO TEST THIS
1597*cf5a6c84SAndroid Build Coastguard Worker //   $ ++ lvalue
1598*cf5a6c84SAndroid Build Coastguard Worker //   $ -- lvalue
1599*cf5a6c84SAndroid Build Coastguard Worker //   $ + expression_up_to_exponentiation (also -, ! prefix ops)
1600*cf5a6c84SAndroid Build Coastguard Worker //   $ $ whatever_can_follow_and_bind_to_dollar
1601*cf5a6c84SAndroid Build Coastguard Worker //
1602*cf5a6c84SAndroid Build Coastguard Worker //     tkvar, tknumber, tkstring, tkregex, tkfunc, tkbuiltin, tkfield, tkminus,
1603*cf5a6c84SAndroid Build Coastguard Worker //     tkplus, tknot, tkincr, tkdecr, tklparen, tkgetline,
1604*cf5a6c84SAndroid Build Coastguard Worker //     tkclose, tkindex, tkmatch, tksplit, tksub, tkgsub, tksprintf, tksubstr
1605*cf5a6c84SAndroid Build Coastguard Worker //
1606*cf5a6c84SAndroid Build Coastguard Worker // ray@radon:~$ awk 'BEGIN { $0 = "7 9 5 8"; k=2; print $k*k }'
1607*cf5a6c84SAndroid Build Coastguard Worker // 18
1608*cf5a6c84SAndroid Build Coastguard Worker // ray@radon:~$ awk 'BEGIN { $0 = "7 9 5 8"; k=2; print $+k*k }'
1609*cf5a6c84SAndroid Build Coastguard Worker // 18
1610*cf5a6c84SAndroid Build Coastguard Worker // ray@radon:~$ awk 'BEGIN { $0 = "7 9 5 8"; k=2; print $k^k }'
1611*cf5a6c84SAndroid Build Coastguard Worker // 81
1612*cf5a6c84SAndroid Build Coastguard Worker // ray@radon:~$ awk 'BEGIN { $0 = "7 9 5 8"; k=2; print $+k^k }'
1613*cf5a6c84SAndroid Build Coastguard Worker // 8
1614*cf5a6c84SAndroid Build Coastguard Worker 
field_op(void)1615*cf5a6c84SAndroid Build Coastguard Worker static void field_op(void)
1616*cf5a6c84SAndroid Build Coastguard Worker {
1617*cf5a6c84SAndroid Build Coastguard Worker   // CURTOK() must be $ here.
1618*cf5a6c84SAndroid Build Coastguard Worker   expect(tkfield);
1619*cf5a6c84SAndroid Build Coastguard Worker   // tkvar, tknumber, tkstring, tkregex, tkfunc, tkbuiltin, tkfield, tkminus,
1620*cf5a6c84SAndroid Build Coastguard Worker   // tkplus, tknot, tkincr, tkdecr, tklparen, tkgetline, tkclose, tkindex,
1621*cf5a6c84SAndroid Build Coastguard Worker   // tkmatch, tksplit, tksub, tkgsub, tksprintf, tksubstr
1622*cf5a6c84SAndroid Build Coastguard Worker   if (ISTOK(tkfield)) field_op();
1623*cf5a6c84SAndroid Build Coastguard Worker   else if (ISTOK(tkvar)) var();
1624*cf5a6c84SAndroid Build Coastguard Worker   else primary();
1625*cf5a6c84SAndroid Build Coastguard Worker   // tkfield op has "dummy" 2nd word so that convert_push_to_reference(void)
1626*cf5a6c84SAndroid Build Coastguard Worker   // can find either tkfield or tkvar at same place (ZCODE[TT.zcode_last-1]).
1627*cf5a6c84SAndroid Build Coastguard Worker   gen2cd(tkfield, tkeof);
1628*cf5a6c84SAndroid Build Coastguard Worker }
1629*cf5a6c84SAndroid Build Coastguard Worker 
1630*cf5a6c84SAndroid Build Coastguard Worker // Tokens that can start expression
1631*cf5a6c84SAndroid Build Coastguard Worker static char exprstartsy[] = {tkvar, tknumber, tkstring, tkregex, tkfunc,
1632*cf5a6c84SAndroid Build Coastguard Worker   tkbuiltin, tkfield, tkminus, tkplus, tknot, tkincr, tkdecr, tklparen,
1633*cf5a6c84SAndroid Build Coastguard Worker   tkgetline, tkclose, tkindex, tkmatch, tksplit, tksub, tkgsub, tksprintf,
1634*cf5a6c84SAndroid Build Coastguard Worker   tksubstr, tkband, tkbor, tkbxor, tkrshift, tklshift, 0};
1635*cf5a6c84SAndroid Build Coastguard Worker 
1636*cf5a6c84SAndroid Build Coastguard Worker // Tokens that can end statement
1637*cf5a6c84SAndroid Build Coastguard Worker static char stmtendsy[] = {tknl, tksemi, tkrbrace, 0};
1638*cf5a6c84SAndroid Build Coastguard Worker 
1639*cf5a6c84SAndroid Build Coastguard Worker // Tokens that can follow expressions of a print statement
1640*cf5a6c84SAndroid Build Coastguard Worker static char printexprendsy[] = {tkgt, tkappend, tkpipe, tknl, tksemi, tkrbrace, 0};
1641*cf5a6c84SAndroid Build Coastguard Worker 
1642*cf5a6c84SAndroid Build Coastguard Worker // !! Ensure this:
1643*cf5a6c84SAndroid Build Coastguard Worker // ternary op is right associative, so
1644*cf5a6c84SAndroid Build Coastguard Worker // a ? b : c ? d : e        evaluates as
1645*cf5a6c84SAndroid Build Coastguard Worker // a ? b : (c ? d : e)      not as
1646*cf5a6c84SAndroid Build Coastguard Worker // (a ? b : c) ? d : e
1647*cf5a6c84SAndroid Build Coastguard Worker 
convert_push_to_reference(void)1648*cf5a6c84SAndroid Build Coastguard Worker static void convert_push_to_reference(void)
1649*cf5a6c84SAndroid Build Coastguard Worker {
1650*cf5a6c84SAndroid Build Coastguard Worker   if (ZCODE[TT.zcode_last - 1] == tkvar) ZCODE[TT.zcode_last-1] = opvarref;
1651*cf5a6c84SAndroid Build Coastguard Worker   else if (ZCODE[TT.zcode_last - 1] == opmap) ZCODE[TT.zcode_last - 1] = opmapref;
1652*cf5a6c84SAndroid Build Coastguard Worker   else if (ZCODE[TT.zcode_last - 1] == tkfield) ZCODE[TT.zcode_last - 1] = opfldref;
1653*cf5a6c84SAndroid Build Coastguard Worker   else error_exit("bad lvalue?");
1654*cf5a6c84SAndroid Build Coastguard Worker }
1655*cf5a6c84SAndroid Build Coastguard Worker 
lvalue(void)1656*cf5a6c84SAndroid Build Coastguard Worker static void lvalue(void)
1657*cf5a6c84SAndroid Build Coastguard Worker {
1658*cf5a6c84SAndroid Build Coastguard Worker   if (ISTOK(tkfield)) {
1659*cf5a6c84SAndroid Build Coastguard Worker     field_op();
1660*cf5a6c84SAndroid Build Coastguard Worker     convert_push_to_reference();
1661*cf5a6c84SAndroid Build Coastguard Worker   } else if (ISTOK(tkvar)) {
1662*cf5a6c84SAndroid Build Coastguard Worker     var();
1663*cf5a6c84SAndroid Build Coastguard Worker     convert_push_to_reference();
1664*cf5a6c84SAndroid Build Coastguard Worker   } else {
1665*cf5a6c84SAndroid Build Coastguard Worker     XERR("syntax near '%s' (bad lvalue)\n", TT.tokstr);
1666*cf5a6c84SAndroid Build Coastguard Worker   }
1667*cf5a6c84SAndroid Build Coastguard Worker }
1668*cf5a6c84SAndroid Build Coastguard Worker 
primary(void)1669*cf5a6c84SAndroid Build Coastguard Worker static int primary(void)
1670*cf5a6c84SAndroid Build Coastguard Worker {
1671*cf5a6c84SAndroid Build Coastguard Worker   //  On entry: CURTOK() is first token of expression
1672*cf5a6c84SAndroid Build Coastguard Worker   //  On exit: CURTOK() is infix operator (for binary_op() to handle) or next
1673*cf5a6c84SAndroid Build Coastguard Worker   //   token after end of expression.
1674*cf5a6c84SAndroid Build Coastguard Worker   //  return -1 for field or var (potential lvalue);
1675*cf5a6c84SAndroid Build Coastguard Worker   //      2 or more for comma-separated expr list
1676*cf5a6c84SAndroid Build Coastguard Worker   //          as in "multiple subscript expression in array"
1677*cf5a6c84SAndroid Build Coastguard Worker   //          e.g. (1, 2) in array_name, or a print/printf list;
1678*cf5a6c84SAndroid Build Coastguard Worker   //      otherwise return 0
1679*cf5a6c84SAndroid Build Coastguard Worker   //
1680*cf5a6c84SAndroid Build Coastguard Worker   //  expr can start with:
1681*cf5a6c84SAndroid Build Coastguard Worker   //      tkvar, tknumber, tkstring, tkregex, tkfunc, tkbuiltin, tkfield, tkminus,
1682*cf5a6c84SAndroid Build Coastguard Worker   //      tkplus, tknot, tkincr, tkdecr, tklparen, tkgetline, tkclose, tkindex,
1683*cf5a6c84SAndroid Build Coastguard Worker   //      tkmatch, tksplit, tksub, tkgsub, tksprintf, tksubstr
1684*cf5a6c84SAndroid Build Coastguard Worker   //
1685*cf5a6c84SAndroid Build Coastguard Worker   //  bwk treats these as keywords, not builtins: close index match split sub gsub
1686*cf5a6c84SAndroid Build Coastguard Worker   //      sprintf substr
1687*cf5a6c84SAndroid Build Coastguard Worker   //
1688*cf5a6c84SAndroid Build Coastguard Worker   //  bwk builtins are: atan2 cos sin exp log sqrt int rand srand length tolower
1689*cf5a6c84SAndroid Build Coastguard Worker   //      toupper system fflush
1690*cf5a6c84SAndroid Build Coastguard Worker   //  NOTE: fflush() is NOT in POSIX awk
1691*cf5a6c84SAndroid Build Coastguard Worker   //
1692*cf5a6c84SAndroid Build Coastguard Worker   //  primary() must consume prefix and postfix operators as well as
1693*cf5a6c84SAndroid Build Coastguard Worker   //      num, string, regex, var, var with subscripts, and function calls
1694*cf5a6c84SAndroid Build Coastguard Worker 
1695*cf5a6c84SAndroid Build Coastguard Worker   int num_exprs = 0;
1696*cf5a6c84SAndroid Build Coastguard Worker   int nargs, modifier;
1697*cf5a6c84SAndroid Build Coastguard Worker   int tok = CURTOK();
1698*cf5a6c84SAndroid Build Coastguard Worker   switch (tok) {
1699*cf5a6c84SAndroid Build Coastguard Worker     case tkvar:
1700*cf5a6c84SAndroid Build Coastguard Worker     case tkfield:
1701*cf5a6c84SAndroid Build Coastguard Worker       if (ISTOK(tkvar)) var();
1702*cf5a6c84SAndroid Build Coastguard Worker       else field_op();
1703*cf5a6c84SAndroid Build Coastguard Worker       if (ISTOK(tkincr) || ISTOK(tkdecr)) {
1704*cf5a6c84SAndroid Build Coastguard Worker         convert_push_to_reference();
1705*cf5a6c84SAndroid Build Coastguard Worker         gencd(CURTOK());
1706*cf5a6c84SAndroid Build Coastguard Worker         scan();
1707*cf5a6c84SAndroid Build Coastguard Worker       } else return -1;
1708*cf5a6c84SAndroid Build Coastguard Worker       break;
1709*cf5a6c84SAndroid Build Coastguard Worker 
1710*cf5a6c84SAndroid Build Coastguard Worker     case tknumber:
1711*cf5a6c84SAndroid Build Coastguard Worker       gen2cd(tknumber, make_literal_num_val(TT.scs->numval));
1712*cf5a6c84SAndroid Build Coastguard Worker       scan();
1713*cf5a6c84SAndroid Build Coastguard Worker       break;
1714*cf5a6c84SAndroid Build Coastguard Worker 
1715*cf5a6c84SAndroid Build Coastguard Worker     case tkstring:
1716*cf5a6c84SAndroid Build Coastguard Worker       gen2cd(tkstring, make_literal_str_val(TT.tokstr));
1717*cf5a6c84SAndroid Build Coastguard Worker       scan();
1718*cf5a6c84SAndroid Build Coastguard Worker       break;
1719*cf5a6c84SAndroid Build Coastguard Worker 
1720*cf5a6c84SAndroid Build Coastguard Worker     case tkregex:
1721*cf5a6c84SAndroid Build Coastguard Worker       // When an ERE token appears as an expression in any context other
1722*cf5a6c84SAndroid Build Coastguard Worker       // than as the right-hand of the '~' or "!~" operator or as one of
1723*cf5a6c84SAndroid Build Coastguard Worker       // the built-in function arguments described below, the value of
1724*cf5a6c84SAndroid Build Coastguard Worker       // the resulting expression shall be the equivalent of: $0 ~ /ere/
1725*cf5a6c84SAndroid Build Coastguard Worker       // FIXME TODO
1726*cf5a6c84SAndroid Build Coastguard Worker       gen2cd(opmatchrec, make_literal_regex_val(TT.tokstr));
1727*cf5a6c84SAndroid Build Coastguard Worker       scan();
1728*cf5a6c84SAndroid Build Coastguard Worker       break;
1729*cf5a6c84SAndroid Build Coastguard Worker 
1730*cf5a6c84SAndroid Build Coastguard Worker     case tkbuiltin: // various builtins
1731*cf5a6c84SAndroid Build Coastguard Worker     case tkfunc:    // user-defined function
1732*cf5a6c84SAndroid Build Coastguard Worker       function_call();
1733*cf5a6c84SAndroid Build Coastguard Worker       break;
1734*cf5a6c84SAndroid Build Coastguard Worker 
1735*cf5a6c84SAndroid Build Coastguard Worker     // Unary prefix ! + -
1736*cf5a6c84SAndroid Build Coastguard Worker     case tknot:
1737*cf5a6c84SAndroid Build Coastguard Worker     case tkminus:
1738*cf5a6c84SAndroid Build Coastguard Worker     case tkplus:
1739*cf5a6c84SAndroid Build Coastguard Worker       scan();
1740*cf5a6c84SAndroid Build Coastguard Worker       expr(getlbp(tknot));   // unary +/- same precedence as !
1741*cf5a6c84SAndroid Build Coastguard Worker       if (tok == tknot) gencd(tknot);
1742*cf5a6c84SAndroid Build Coastguard Worker       else gencd(opnegate);               // forces to number
1743*cf5a6c84SAndroid Build Coastguard Worker       if (tok == tkplus) gencd(opnegate); // forces to number
1744*cf5a6c84SAndroid Build Coastguard Worker       break;
1745*cf5a6c84SAndroid Build Coastguard Worker 
1746*cf5a6c84SAndroid Build Coastguard Worker       // Unary prefix ++ -- MUST take lvalue
1747*cf5a6c84SAndroid Build Coastguard Worker     case tkincr:
1748*cf5a6c84SAndroid Build Coastguard Worker     case tkdecr:
1749*cf5a6c84SAndroid Build Coastguard Worker       scan();
1750*cf5a6c84SAndroid Build Coastguard Worker       lvalue();
1751*cf5a6c84SAndroid Build Coastguard Worker       if (tok == tkincr) gencd(oppreincr);
1752*cf5a6c84SAndroid Build Coastguard Worker       else gencd(oppredecr);
1753*cf5a6c84SAndroid Build Coastguard Worker       break;
1754*cf5a6c84SAndroid Build Coastguard Worker 
1755*cf5a6c84SAndroid Build Coastguard Worker     case tklparen:
1756*cf5a6c84SAndroid Build Coastguard Worker       scan();
1757*cf5a6c84SAndroid Build Coastguard Worker       TT.cgl.paren_level++;
1758*cf5a6c84SAndroid Build Coastguard Worker       num_exprs = 0;
1759*cf5a6c84SAndroid Build Coastguard Worker       do {
1760*cf5a6c84SAndroid Build Coastguard Worker         expr(0);
1761*cf5a6c84SAndroid Build Coastguard Worker         num_exprs++;
1762*cf5a6c84SAndroid Build Coastguard Worker       } while (have_comma());
1763*cf5a6c84SAndroid Build Coastguard Worker       expect(tkrparen);
1764*cf5a6c84SAndroid Build Coastguard Worker       TT.cgl.paren_level--;
1765*cf5a6c84SAndroid Build Coastguard Worker       if (num_exprs > 1) return num_exprs;
1766*cf5a6c84SAndroid Build Coastguard Worker       break;
1767*cf5a6c84SAndroid Build Coastguard Worker 
1768*cf5a6c84SAndroid Build Coastguard Worker     case tkgetline:
1769*cf5a6c84SAndroid Build Coastguard Worker       // getline may be (according to awk book):
1770*cf5a6c84SAndroid Build Coastguard Worker       // getline [var [<file]]
1771*cf5a6c84SAndroid Build Coastguard Worker       // getline <file
1772*cf5a6c84SAndroid Build Coastguard Worker       // cmd | getline [var]
1773*cf5a6c84SAndroid Build Coastguard Worker       // var must be lvalue (can be any lvalue?)
1774*cf5a6c84SAndroid Build Coastguard Worker       scan();
1775*cf5a6c84SAndroid Build Coastguard Worker       nargs = 0;
1776*cf5a6c84SAndroid Build Coastguard Worker       modifier = tkeof;
1777*cf5a6c84SAndroid Build Coastguard Worker       if (ISTOK(tkfield) || ISTOK(tkvar)) {
1778*cf5a6c84SAndroid Build Coastguard Worker         lvalue();
1779*cf5a6c84SAndroid Build Coastguard Worker         nargs++;
1780*cf5a6c84SAndroid Build Coastguard Worker       }
1781*cf5a6c84SAndroid Build Coastguard Worker       if (havetok(tklt)) {
1782*cf5a6c84SAndroid Build Coastguard Worker         expr(getrbp(tkcat));   // bwk "historical practice" precedence
1783*cf5a6c84SAndroid Build Coastguard Worker         nargs++;
1784*cf5a6c84SAndroid Build Coastguard Worker         modifier = tklt;
1785*cf5a6c84SAndroid Build Coastguard Worker       }
1786*cf5a6c84SAndroid Build Coastguard Worker       gen2cd(tkgetline, nargs);
1787*cf5a6c84SAndroid Build Coastguard Worker       gencd(modifier);
1788*cf5a6c84SAndroid Build Coastguard Worker       break;
1789*cf5a6c84SAndroid Build Coastguard Worker 
1790*cf5a6c84SAndroid Build Coastguard Worker     default:
1791*cf5a6c84SAndroid Build Coastguard Worker       XERR("syntax near '%s'\n", TT.tokstr[0] == '\n' ? "\\n" : TT.tokstr);
1792*cf5a6c84SAndroid Build Coastguard Worker       skip_to(stmtendsy);
1793*cf5a6c84SAndroid Build Coastguard Worker       break;
1794*cf5a6c84SAndroid Build Coastguard Worker   }
1795*cf5a6c84SAndroid Build Coastguard Worker   return 0;
1796*cf5a6c84SAndroid Build Coastguard Worker }
1797*cf5a6c84SAndroid Build Coastguard Worker 
binary_op(int optor)1798*cf5a6c84SAndroid Build Coastguard Worker static void binary_op(int optor)  // Also for ternary ?: optor.
1799*cf5a6c84SAndroid Build Coastguard Worker {
1800*cf5a6c84SAndroid Build Coastguard Worker   int nargs, cdx = 0;  // index in TT.zcode list
1801*cf5a6c84SAndroid Build Coastguard Worker   int rbp = getrbp(optor);
1802*cf5a6c84SAndroid Build Coastguard Worker   if (optor != tkcat) scan();
1803*cf5a6c84SAndroid Build Coastguard Worker   // CURTOK() holds first token of right operand.
1804*cf5a6c84SAndroid Build Coastguard Worker   switch (optor) {
1805*cf5a6c84SAndroid Build Coastguard Worker     case tkin:
1806*cf5a6c84SAndroid Build Coastguard Worker       // right side of 'in' must be (only) an array name
1807*cf5a6c84SAndroid Build Coastguard Worker       map_name();
1808*cf5a6c84SAndroid Build Coastguard Worker       gencd(tkin);
1809*cf5a6c84SAndroid Build Coastguard Worker       scan();
1810*cf5a6c84SAndroid Build Coastguard Worker       // FIXME TODO 20230109 x = y in a && 2 works OK?
1811*cf5a6c84SAndroid Build Coastguard Worker       // x = y in a + 2 does not; it's parsed as x = (y in a) + 2
1812*cf5a6c84SAndroid Build Coastguard Worker       // The +2 is not cat'ed with (y in a) as in bwk's OTA.
1813*cf5a6c84SAndroid Build Coastguard Worker       // Other awks see y in a + 2 as a syntax error. They (may)
1814*cf5a6c84SAndroid Build Coastguard Worker       // not want anything after y in a except a lower binding operator
1815*cf5a6c84SAndroid Build Coastguard Worker       // (&& || ?:) or end of expression, i.e. ')' ';' '}'
1816*cf5a6c84SAndroid Build Coastguard Worker       break;
1817*cf5a6c84SAndroid Build Coastguard Worker 
1818*cf5a6c84SAndroid Build Coastguard Worker   case tkpipe:
1819*cf5a6c84SAndroid Build Coastguard Worker       expect(tkgetline);
1820*cf5a6c84SAndroid Build Coastguard Worker       nargs = 1;
1821*cf5a6c84SAndroid Build Coastguard Worker       if (ISTOK(tkfield) || ISTOK(tkvar)) {
1822*cf5a6c84SAndroid Build Coastguard Worker         lvalue();
1823*cf5a6c84SAndroid Build Coastguard Worker         nargs++;
1824*cf5a6c84SAndroid Build Coastguard Worker       }
1825*cf5a6c84SAndroid Build Coastguard Worker       gen2cd(tkgetline, nargs);
1826*cf5a6c84SAndroid Build Coastguard Worker       gencd(tkpipe);
1827*cf5a6c84SAndroid Build Coastguard Worker       break;
1828*cf5a6c84SAndroid Build Coastguard Worker 
1829*cf5a6c84SAndroid Build Coastguard Worker   case tkand:
1830*cf5a6c84SAndroid Build Coastguard Worker   case tkor:
1831*cf5a6c84SAndroid Build Coastguard Worker       optional_nl();
1832*cf5a6c84SAndroid Build Coastguard Worker       gen2cd(optor, -1);  // tkand: jump if false, else drop
1833*cf5a6c84SAndroid Build Coastguard Worker       cdx = TT.zcode_last;   // tkor:  jump if true, else drop
1834*cf5a6c84SAndroid Build Coastguard Worker       expr(rbp);
1835*cf5a6c84SAndroid Build Coastguard Worker       gencd(opnotnot);    // replace TT.stack top with truth value
1836*cf5a6c84SAndroid Build Coastguard Worker       ZCODE[cdx] = TT.zcode_last - cdx;
1837*cf5a6c84SAndroid Build Coastguard Worker       break;
1838*cf5a6c84SAndroid Build Coastguard Worker 
1839*cf5a6c84SAndroid Build Coastguard Worker   case tkternif:
1840*cf5a6c84SAndroid Build Coastguard Worker       gen2cd(optor, -1);
1841*cf5a6c84SAndroid Build Coastguard Worker       cdx = TT.zcode_last;
1842*cf5a6c84SAndroid Build Coastguard Worker       expr(0);
1843*cf5a6c84SAndroid Build Coastguard Worker       expect(tkternelse);
1844*cf5a6c84SAndroid Build Coastguard Worker       gen2cd(tkternelse, -1);
1845*cf5a6c84SAndroid Build Coastguard Worker       ZCODE[cdx] = TT.zcode_last - cdx;
1846*cf5a6c84SAndroid Build Coastguard Worker       cdx = TT.zcode_last;
1847*cf5a6c84SAndroid Build Coastguard Worker       expr(rbp);
1848*cf5a6c84SAndroid Build Coastguard Worker       ZCODE[cdx] = TT.zcode_last - cdx;
1849*cf5a6c84SAndroid Build Coastguard Worker       break;
1850*cf5a6c84SAndroid Build Coastguard Worker 
1851*cf5a6c84SAndroid Build Coastguard Worker   case tkmatchop:
1852*cf5a6c84SAndroid Build Coastguard Worker   case tknotmatch:
1853*cf5a6c84SAndroid Build Coastguard Worker       expr(rbp);
1854*cf5a6c84SAndroid Build Coastguard Worker       if (ZCODE[TT.zcode_last - 1] == opmatchrec) ZCODE[TT.zcode_last - 1] = tkregex;
1855*cf5a6c84SAndroid Build Coastguard Worker       gencd(optor);
1856*cf5a6c84SAndroid Build Coastguard Worker       break;
1857*cf5a6c84SAndroid Build Coastguard Worker 
1858*cf5a6c84SAndroid Build Coastguard Worker   default:
1859*cf5a6c84SAndroid Build Coastguard Worker       expr(rbp);
1860*cf5a6c84SAndroid Build Coastguard Worker       gencd(optor);
1861*cf5a6c84SAndroid Build Coastguard Worker   }
1862*cf5a6c84SAndroid Build Coastguard Worker }
1863*cf5a6c84SAndroid Build Coastguard Worker 
cat_start_concated_expr(int tok)1864*cf5a6c84SAndroid Build Coastguard Worker static int cat_start_concated_expr(int tok)
1865*cf5a6c84SAndroid Build Coastguard Worker {
1866*cf5a6c84SAndroid Build Coastguard Worker   // concat'ed expr can start w/ var number string func builtin $ ! ( (or ++ if prev was not lvalue)
1867*cf5a6c84SAndroid Build Coastguard Worker   static char exprstarttermsy[] = {tkvar, tknumber, tkstring, tkregex, tkfunc, tkbuiltin,
1868*cf5a6c84SAndroid Build Coastguard Worker     tkfield, tknot, tkincr, tkdecr, tklparen, tkgetline, 0};
1869*cf5a6c84SAndroid Build Coastguard Worker 
1870*cf5a6c84SAndroid Build Coastguard Worker   // NOTE this depends on builtins (close etc) being >= tkgetline
1871*cf5a6c84SAndroid Build Coastguard Worker   return !! strchr(exprstarttermsy, tok) || tok >= tkgetline;
1872*cf5a6c84SAndroid Build Coastguard Worker }
1873*cf5a6c84SAndroid Build Coastguard Worker 
1874*cf5a6c84SAndroid Build Coastguard Worker #define CALLED_BY_PRINT 99987 // Arbitrary, different from any real rbp value
1875*cf5a6c84SAndroid Build Coastguard Worker 
expr(int rbp)1876*cf5a6c84SAndroid Build Coastguard Worker static int expr(int rbp)
1877*cf5a6c84SAndroid Build Coastguard Worker {
1878*cf5a6c84SAndroid Build Coastguard Worker   // On entry: TT.scs has first symbol of expression, e.g. var, number, string,
1879*cf5a6c84SAndroid Build Coastguard Worker   // regex, func, getline, left paren, prefix op ($ ++ -- ! unary + or -) etc.
1880*cf5a6c84SAndroid Build Coastguard Worker   static char asgnops[] = {tkpowasgn, tkmodasgn, tkmulasgn, tkdivasgn,
1881*cf5a6c84SAndroid Build Coastguard Worker     tkaddasgn, tksubasgn, tkasgn, 0};
1882*cf5a6c84SAndroid Build Coastguard Worker   int prim_st = primary();
1883*cf5a6c84SAndroid Build Coastguard Worker   // If called directly by print_stmt(), and found a parenthesized expression list
1884*cf5a6c84SAndroid Build Coastguard Worker   //    followed by an end of print statement: any of > >> | ; } <newline>
1885*cf5a6c84SAndroid Build Coastguard Worker   //    Then: return the count of expressions in list
1886*cf5a6c84SAndroid Build Coastguard Worker   //    Else: continue parsing an expression
1887*cf5a6c84SAndroid Build Coastguard Worker   if (rbp == CALLED_BY_PRINT) {
1888*cf5a6c84SAndroid Build Coastguard Worker     if (prim_st > 0 && strchr(printexprendsy, CURTOK())) return prim_st;
1889*cf5a6c84SAndroid Build Coastguard Worker     else rbp = 0;
1890*cf5a6c84SAndroid Build Coastguard Worker   }
1891*cf5a6c84SAndroid Build Coastguard Worker 
1892*cf5a6c84SAndroid Build Coastguard Worker   // mult_expr_list in parens must be followed by 'in' unless it
1893*cf5a6c84SAndroid Build Coastguard Worker   // immediately follows print or printf, where it may still be followed
1894*cf5a6c84SAndroid Build Coastguard Worker   // by 'in' ... unless at end of statement
1895*cf5a6c84SAndroid Build Coastguard Worker   if (prim_st > 0 && ! ISTOK(tkin))
1896*cf5a6c84SAndroid Build Coastguard Worker     XERR("syntax near '%s'; expected 'in'\n", TT.tokstr);
1897*cf5a6c84SAndroid Build Coastguard Worker   if (prim_st > 0) gen2cd(tkrbracket, prim_st);
1898*cf5a6c84SAndroid Build Coastguard Worker   // primary() has eaten subscripts, function args, postfix ops.
1899*cf5a6c84SAndroid Build Coastguard Worker   // CURTOK() should be a binary op.
1900*cf5a6c84SAndroid Build Coastguard Worker   int optor = CURTOK();
1901*cf5a6c84SAndroid Build Coastguard Worker   if (strchr(asgnops, optor)) {
1902*cf5a6c84SAndroid Build Coastguard Worker 
1903*cf5a6c84SAndroid Build Coastguard Worker     // TODO FIXME ?  NOT SURE IF THIS WORKS RIGHT!
1904*cf5a6c84SAndroid Build Coastguard Worker     // awk does not parse according to POSIX spec in some odd cases.
1905*cf5a6c84SAndroid Build Coastguard Worker     // When an assignment (lvalue =) is on the right of certain operators,
1906*cf5a6c84SAndroid Build Coastguard Worker     // it is not treated as a bad lvalue (as it is in C).
1907*cf5a6c84SAndroid Build Coastguard Worker     // Example: (1 && a=2) # no error; the assignment is performed.
1908*cf5a6c84SAndroid Build Coastguard Worker     // This happens for ?: || && ~ !~ < <= ~= == > >=
1909*cf5a6c84SAndroid Build Coastguard Worker     //
1910*cf5a6c84SAndroid Build Coastguard Worker     static char odd_assignment_rbp[] = {59, 60, 70, 80, 100, 110, 0};
1911*cf5a6c84SAndroid Build Coastguard Worker     if (prim_st < 0 && (rbp <= getrbp(optor) || strchr(odd_assignment_rbp, rbp))) {
1912*cf5a6c84SAndroid Build Coastguard Worker       convert_push_to_reference();
1913*cf5a6c84SAndroid Build Coastguard Worker       scan();
1914*cf5a6c84SAndroid Build Coastguard Worker       expr(getrbp(optor));
1915*cf5a6c84SAndroid Build Coastguard Worker       gencd(optor);
1916*cf5a6c84SAndroid Build Coastguard Worker       return 0;
1917*cf5a6c84SAndroid Build Coastguard Worker     }
1918*cf5a6c84SAndroid Build Coastguard Worker     XERR("syntax near '%s'\n", TT.tokstr[0] == '\n' ? "\\n" : TT.tokstr);
1919*cf5a6c84SAndroid Build Coastguard Worker     skip_to(stmtendsy);
1920*cf5a6c84SAndroid Build Coastguard Worker   }
1921*cf5a6c84SAndroid Build Coastguard Worker   if (cat_start_concated_expr(optor)) optor = tkcat;
1922*cf5a6c84SAndroid Build Coastguard Worker   while (rbp < getlbp(optor)) {
1923*cf5a6c84SAndroid Build Coastguard Worker     binary_op(optor);
1924*cf5a6c84SAndroid Build Coastguard Worker     // HERE tok s/b an operator or expression terminator ( ; etc.).
1925*cf5a6c84SAndroid Build Coastguard Worker     optor = CURTOK();
1926*cf5a6c84SAndroid Build Coastguard Worker     if (cat_start_concated_expr(optor)) optor = tkcat;
1927*cf5a6c84SAndroid Build Coastguard Worker   }
1928*cf5a6c84SAndroid Build Coastguard Worker   return 0;
1929*cf5a6c84SAndroid Build Coastguard Worker }
1930*cf5a6c84SAndroid Build Coastguard Worker 
print_stmt(int tk)1931*cf5a6c84SAndroid Build Coastguard Worker static void print_stmt(int tk)
1932*cf5a6c84SAndroid Build Coastguard Worker {
1933*cf5a6c84SAndroid Build Coastguard Worker   static char outmodes[] = {tkgt, tkappend, tkpipe, 0};
1934*cf5a6c84SAndroid Build Coastguard Worker   int num_exprs = 0, outmode;
1935*cf5a6c84SAndroid Build Coastguard Worker   TT.cgl.in_print_stmt = 1;
1936*cf5a6c84SAndroid Build Coastguard Worker   expect(tk); // tkprint or tkprintf
1937*cf5a6c84SAndroid Build Coastguard Worker   if ((tk == tkprintf) || !strchr(printexprendsy, CURTOK())) {
1938*cf5a6c84SAndroid Build Coastguard Worker     // printf always needs expression
1939*cf5a6c84SAndroid Build Coastguard Worker     // print non-empty statement needs expression
1940*cf5a6c84SAndroid Build Coastguard Worker     num_exprs = expr(CALLED_BY_PRINT);
1941*cf5a6c84SAndroid Build Coastguard Worker     if (num_exprs > 0 && !strchr(printexprendsy, CURTOK())) FATAL("print stmt bug");
1942*cf5a6c84SAndroid Build Coastguard Worker     if (!num_exprs) {
1943*cf5a6c84SAndroid Build Coastguard Worker       for (num_exprs++; have_comma(); num_exprs++)
1944*cf5a6c84SAndroid Build Coastguard Worker         expr(0);
1945*cf5a6c84SAndroid Build Coastguard Worker     }
1946*cf5a6c84SAndroid Build Coastguard Worker   }
1947*cf5a6c84SAndroid Build Coastguard Worker   outmode = CURTOK();
1948*cf5a6c84SAndroid Build Coastguard Worker   if (strchr(outmodes, outmode)) {
1949*cf5a6c84SAndroid Build Coastguard Worker     scan();
1950*cf5a6c84SAndroid Build Coastguard Worker     expr(0); // FIXME s/b only bwk term? check POSIX
1951*cf5a6c84SAndroid Build Coastguard Worker     num_exprs++;
1952*cf5a6c84SAndroid Build Coastguard Worker   } else outmode = 0;
1953*cf5a6c84SAndroid Build Coastguard Worker   gen2cd(tk, num_exprs);
1954*cf5a6c84SAndroid Build Coastguard Worker   gencd(outmode);
1955*cf5a6c84SAndroid Build Coastguard Worker   TT.cgl.in_print_stmt = 0;
1956*cf5a6c84SAndroid Build Coastguard Worker }
1957*cf5a6c84SAndroid Build Coastguard Worker 
delete_stmt(void)1958*cf5a6c84SAndroid Build Coastguard Worker static void delete_stmt(void)
1959*cf5a6c84SAndroid Build Coastguard Worker {
1960*cf5a6c84SAndroid Build Coastguard Worker   expect(tkdelete);
1961*cf5a6c84SAndroid Build Coastguard Worker   if (ISTOK(tkvar)) {
1962*cf5a6c84SAndroid Build Coastguard Worker     int slotnum = find_or_add_var_name();
1963*cf5a6c84SAndroid Build Coastguard Worker     check_set_map(slotnum);
1964*cf5a6c84SAndroid Build Coastguard Worker     scan();
1965*cf5a6c84SAndroid Build Coastguard Worker     if (havetok(tklbracket)) {
1966*cf5a6c84SAndroid Build Coastguard Worker       int num_subscripts = 0;
1967*cf5a6c84SAndroid Build Coastguard Worker       do {
1968*cf5a6c84SAndroid Build Coastguard Worker         expr(0);
1969*cf5a6c84SAndroid Build Coastguard Worker         num_subscripts++;
1970*cf5a6c84SAndroid Build Coastguard Worker       } while (have_comma());
1971*cf5a6c84SAndroid Build Coastguard Worker       expect(tkrbracket);
1972*cf5a6c84SAndroid Build Coastguard Worker       if (num_subscripts > 1) gen2cd(tkrbracket, num_subscripts);
1973*cf5a6c84SAndroid Build Coastguard Worker       gen2cd(opmapref, slotnum);
1974*cf5a6c84SAndroid Build Coastguard Worker       gencd(tkdelete);
1975*cf5a6c84SAndroid Build Coastguard Worker     } else {
1976*cf5a6c84SAndroid Build Coastguard Worker       // delete entire map (elements only; var is still a map)
1977*cf5a6c84SAndroid Build Coastguard Worker       gen2cd(opmapref, slotnum);
1978*cf5a6c84SAndroid Build Coastguard Worker       gencd(opmapdelete);
1979*cf5a6c84SAndroid Build Coastguard Worker     }
1980*cf5a6c84SAndroid Build Coastguard Worker   } else expect(tkvar);
1981*cf5a6c84SAndroid Build Coastguard Worker }
1982*cf5a6c84SAndroid Build Coastguard Worker 
simple_stmt(void)1983*cf5a6c84SAndroid Build Coastguard Worker static void simple_stmt(void)
1984*cf5a6c84SAndroid Build Coastguard Worker {
1985*cf5a6c84SAndroid Build Coastguard Worker   if (strchr(exprstartsy, CURTOK())) {
1986*cf5a6c84SAndroid Build Coastguard Worker     expr(0);
1987*cf5a6c84SAndroid Build Coastguard Worker     gencd(opdrop);
1988*cf5a6c84SAndroid Build Coastguard Worker     return;
1989*cf5a6c84SAndroid Build Coastguard Worker   }
1990*cf5a6c84SAndroid Build Coastguard Worker   switch (CURTOK()) {
1991*cf5a6c84SAndroid Build Coastguard Worker     case tkprint:
1992*cf5a6c84SAndroid Build Coastguard Worker     case tkprintf:
1993*cf5a6c84SAndroid Build Coastguard Worker       print_stmt(CURTOK());
1994*cf5a6c84SAndroid Build Coastguard Worker       break;
1995*cf5a6c84SAndroid Build Coastguard Worker 
1996*cf5a6c84SAndroid Build Coastguard Worker     case tkdelete:
1997*cf5a6c84SAndroid Build Coastguard Worker       delete_stmt();
1998*cf5a6c84SAndroid Build Coastguard Worker       break;
1999*cf5a6c84SAndroid Build Coastguard Worker 
2000*cf5a6c84SAndroid Build Coastguard Worker     default:
2001*cf5a6c84SAndroid Build Coastguard Worker       XERR("syntax near '%s'\n", TT.tokstr[0] == '\n' ? "\\n" : TT.tokstr);
2002*cf5a6c84SAndroid Build Coastguard Worker       skip_to(stmtendsy);
2003*cf5a6c84SAndroid Build Coastguard Worker   }
2004*cf5a6c84SAndroid Build Coastguard Worker }
2005*cf5a6c84SAndroid Build Coastguard Worker 
prev_was_terminated(void)2006*cf5a6c84SAndroid Build Coastguard Worker static int prev_was_terminated(void)
2007*cf5a6c84SAndroid Build Coastguard Worker {
2008*cf5a6c84SAndroid Build Coastguard Worker   return !!strchr(stmtendsy, TT.prevtok);
2009*cf5a6c84SAndroid Build Coastguard Worker }
2010*cf5a6c84SAndroid Build Coastguard Worker 
is_nl_semi(void)2011*cf5a6c84SAndroid Build Coastguard Worker static int is_nl_semi(void)
2012*cf5a6c84SAndroid Build Coastguard Worker {
2013*cf5a6c84SAndroid Build Coastguard Worker   return ISTOK(tknl) || ISTOK(tksemi);
2014*cf5a6c84SAndroid Build Coastguard Worker }
2015*cf5a6c84SAndroid Build Coastguard Worker 
if_stmt(void)2016*cf5a6c84SAndroid Build Coastguard Worker static void if_stmt(void)
2017*cf5a6c84SAndroid Build Coastguard Worker {
2018*cf5a6c84SAndroid Build Coastguard Worker   expect(tkif);
2019*cf5a6c84SAndroid Build Coastguard Worker   expect(tklparen);
2020*cf5a6c84SAndroid Build Coastguard Worker   expr(0);
2021*cf5a6c84SAndroid Build Coastguard Worker   rparen();
2022*cf5a6c84SAndroid Build Coastguard Worker   gen2cd(tkif, -1);
2023*cf5a6c84SAndroid Build Coastguard Worker   int cdx = TT.zcode_last;
2024*cf5a6c84SAndroid Build Coastguard Worker   stmt();
2025*cf5a6c84SAndroid Build Coastguard Worker   if (!prev_was_terminated() && is_nl_semi()) {
2026*cf5a6c84SAndroid Build Coastguard Worker     scan();
2027*cf5a6c84SAndroid Build Coastguard Worker     optional_nl();
2028*cf5a6c84SAndroid Build Coastguard Worker   }
2029*cf5a6c84SAndroid Build Coastguard Worker   if (prev_was_terminated()) {
2030*cf5a6c84SAndroid Build Coastguard Worker     optional_nl();
2031*cf5a6c84SAndroid Build Coastguard Worker     if (havetok(tkelse)) {
2032*cf5a6c84SAndroid Build Coastguard Worker       gen2cd(tkelse, -1);
2033*cf5a6c84SAndroid Build Coastguard Worker       ZCODE[cdx] = TT.zcode_last - cdx;
2034*cf5a6c84SAndroid Build Coastguard Worker       cdx = TT.zcode_last;
2035*cf5a6c84SAndroid Build Coastguard Worker       optional_nl();
2036*cf5a6c84SAndroid Build Coastguard Worker       stmt();
2037*cf5a6c84SAndroid Build Coastguard Worker     }
2038*cf5a6c84SAndroid Build Coastguard Worker   }
2039*cf5a6c84SAndroid Build Coastguard Worker   ZCODE[cdx] = TT.zcode_last - cdx;
2040*cf5a6c84SAndroid Build Coastguard Worker }
2041*cf5a6c84SAndroid Build Coastguard Worker 
save_break_continue(int * brk,int * cont)2042*cf5a6c84SAndroid Build Coastguard Worker static void save_break_continue(int *brk, int *cont)
2043*cf5a6c84SAndroid Build Coastguard Worker {
2044*cf5a6c84SAndroid Build Coastguard Worker   *brk = TT.cgl.break_dest;
2045*cf5a6c84SAndroid Build Coastguard Worker   *cont = TT.cgl.continue_dest;
2046*cf5a6c84SAndroid Build Coastguard Worker }
2047*cf5a6c84SAndroid Build Coastguard Worker 
restore_break_continue(int * brk,int * cont)2048*cf5a6c84SAndroid Build Coastguard Worker static void restore_break_continue(int *brk, int *cont)
2049*cf5a6c84SAndroid Build Coastguard Worker {
2050*cf5a6c84SAndroid Build Coastguard Worker   TT.cgl.break_dest = *brk;
2051*cf5a6c84SAndroid Build Coastguard Worker   TT.cgl.continue_dest = *cont;
2052*cf5a6c84SAndroid Build Coastguard Worker }
2053*cf5a6c84SAndroid Build Coastguard Worker 
while_stmt(void)2054*cf5a6c84SAndroid Build Coastguard Worker static void while_stmt(void)
2055*cf5a6c84SAndroid Build Coastguard Worker {
2056*cf5a6c84SAndroid Build Coastguard Worker   int brk, cont;
2057*cf5a6c84SAndroid Build Coastguard Worker   save_break_continue(&brk, &cont);
2058*cf5a6c84SAndroid Build Coastguard Worker   expect(tkwhile);
2059*cf5a6c84SAndroid Build Coastguard Worker   expect(tklparen);
2060*cf5a6c84SAndroid Build Coastguard Worker   TT.cgl.continue_dest = TT.zcode_last + 1;
2061*cf5a6c84SAndroid Build Coastguard Worker   expr(0);
2062*cf5a6c84SAndroid Build Coastguard Worker   rparen();
2063*cf5a6c84SAndroid Build Coastguard Worker   gen2cd(tkwhile, 2);    // drop, jump if true
2064*cf5a6c84SAndroid Build Coastguard Worker   TT.cgl.break_dest = TT.zcode_last + 1;
2065*cf5a6c84SAndroid Build Coastguard Worker   gen2cd(opjump, -1);     // jump here to break
2066*cf5a6c84SAndroid Build Coastguard Worker   stmt();
2067*cf5a6c84SAndroid Build Coastguard Worker   gen2cd(opjump, -1);     // jump to continue
2068*cf5a6c84SAndroid Build Coastguard Worker   ZCODE[TT.zcode_last] = TT.cgl.continue_dest - TT.zcode_last - 1;
2069*cf5a6c84SAndroid Build Coastguard Worker   ZCODE[TT.cgl.break_dest + 1] = TT.zcode_last - TT.cgl.break_dest - 1;
2070*cf5a6c84SAndroid Build Coastguard Worker   restore_break_continue(&brk, &cont);
2071*cf5a6c84SAndroid Build Coastguard Worker }
2072*cf5a6c84SAndroid Build Coastguard Worker 
do_stmt(void)2073*cf5a6c84SAndroid Build Coastguard Worker static void do_stmt(void)
2074*cf5a6c84SAndroid Build Coastguard Worker {
2075*cf5a6c84SAndroid Build Coastguard Worker   int brk, cont;
2076*cf5a6c84SAndroid Build Coastguard Worker   save_break_continue(&brk, &cont);
2077*cf5a6c84SAndroid Build Coastguard Worker   expect(tkdo);
2078*cf5a6c84SAndroid Build Coastguard Worker   optional_nl();
2079*cf5a6c84SAndroid Build Coastguard Worker   gen2cd(opjump, 4);   // jump over jumps, to statement
2080*cf5a6c84SAndroid Build Coastguard Worker   TT.cgl.continue_dest = TT.zcode_last + 1;
2081*cf5a6c84SAndroid Build Coastguard Worker   gen2cd(opjump, -1);   // here on continue
2082*cf5a6c84SAndroid Build Coastguard Worker   TT.cgl.break_dest = TT.zcode_last + 1;
2083*cf5a6c84SAndroid Build Coastguard Worker   gen2cd(opjump, -1);   // here on break
2084*cf5a6c84SAndroid Build Coastguard Worker   stmt();
2085*cf5a6c84SAndroid Build Coastguard Worker   if (!prev_was_terminated()) {
2086*cf5a6c84SAndroid Build Coastguard Worker     if (is_nl_semi()) {
2087*cf5a6c84SAndroid Build Coastguard Worker       scan();
2088*cf5a6c84SAndroid Build Coastguard Worker       optional_nl();
2089*cf5a6c84SAndroid Build Coastguard Worker     } else {
2090*cf5a6c84SAndroid Build Coastguard Worker       XERR("syntax near '%s' -- ';' or newline expected\n", TT.tokstr);
2091*cf5a6c84SAndroid Build Coastguard Worker       // FIXME
2092*cf5a6c84SAndroid Build Coastguard Worker     }
2093*cf5a6c84SAndroid Build Coastguard Worker   }
2094*cf5a6c84SAndroid Build Coastguard Worker   ZCODE[TT.cgl.continue_dest + 1] = TT.zcode_last - TT.cgl.continue_dest - 1;
2095*cf5a6c84SAndroid Build Coastguard Worker   optional_nl();
2096*cf5a6c84SAndroid Build Coastguard Worker   expect(tkwhile);
2097*cf5a6c84SAndroid Build Coastguard Worker   expect(tklparen);
2098*cf5a6c84SAndroid Build Coastguard Worker   expr(0);
2099*cf5a6c84SAndroid Build Coastguard Worker   rparen();
2100*cf5a6c84SAndroid Build Coastguard Worker   gen2cd(tkwhile, TT.cgl.break_dest - TT.zcode_last - 1);
2101*cf5a6c84SAndroid Build Coastguard Worker   ZCODE[TT.cgl.break_dest + 1] = TT.zcode_last - TT.cgl.break_dest - 1;
2102*cf5a6c84SAndroid Build Coastguard Worker   restore_break_continue(&brk, &cont);
2103*cf5a6c84SAndroid Build Coastguard Worker }
2104*cf5a6c84SAndroid Build Coastguard Worker 
for_not_map_iter(void)2105*cf5a6c84SAndroid Build Coastguard Worker static void for_not_map_iter(void)
2106*cf5a6c84SAndroid Build Coastguard Worker {
2107*cf5a6c84SAndroid Build Coastguard Worker   // Here after loop initialization, if any; loop condition
2108*cf5a6c84SAndroid Build Coastguard Worker   int condition_loc = TT.zcode_last + 1;
2109*cf5a6c84SAndroid Build Coastguard Worker   if (havetok(tksemi)) {
2110*cf5a6c84SAndroid Build Coastguard Worker     // "endless" loop variant; no condition
2111*cf5a6c84SAndroid Build Coastguard Worker     // no NL allowed here in OTA
2112*cf5a6c84SAndroid Build Coastguard Worker     gen2cd(opjump, -1);     // jump to statement
2113*cf5a6c84SAndroid Build Coastguard Worker   } else {
2114*cf5a6c84SAndroid Build Coastguard Worker     optional_nl();                // NOT posix or awk book; in OTA
2115*cf5a6c84SAndroid Build Coastguard Worker     expr(0);                 // loop while true
2116*cf5a6c84SAndroid Build Coastguard Worker     expect(tksemi);
2117*cf5a6c84SAndroid Build Coastguard Worker     gen2cd(tkwhile, -1);    // drop, jump to statement if true
2118*cf5a6c84SAndroid Build Coastguard Worker   }
2119*cf5a6c84SAndroid Build Coastguard Worker   optional_nl();                    // NOT posix or awk book; in OTA
2120*cf5a6c84SAndroid Build Coastguard Worker   TT.cgl.break_dest = TT.zcode_last + 1;
2121*cf5a6c84SAndroid Build Coastguard Worker   gen2cd(opjump, -1);
2122*cf5a6c84SAndroid Build Coastguard Worker   TT.cgl.continue_dest = TT.zcode_last + 1;
2123*cf5a6c84SAndroid Build Coastguard Worker   if (!ISTOK(tkrparen)) simple_stmt();  // "increment"
2124*cf5a6c84SAndroid Build Coastguard Worker   gen2cd(opjump, condition_loc - TT.zcode_last - 3);
2125*cf5a6c84SAndroid Build Coastguard Worker   rparen();
2126*cf5a6c84SAndroid Build Coastguard Worker   ZCODE[TT.cgl.break_dest - 1] = TT.zcode_last - TT.cgl.break_dest + 1;
2127*cf5a6c84SAndroid Build Coastguard Worker   stmt();
2128*cf5a6c84SAndroid Build Coastguard Worker   gen2cd(opjump, TT.cgl.continue_dest - TT.zcode_last - 3);
2129*cf5a6c84SAndroid Build Coastguard Worker   ZCODE[TT.cgl.break_dest + 1] = TT.zcode_last - TT.cgl.break_dest - 1;
2130*cf5a6c84SAndroid Build Coastguard Worker }
2131*cf5a6c84SAndroid Build Coastguard Worker 
valid_for_array_iteration(int first,int last)2132*cf5a6c84SAndroid Build Coastguard Worker static int valid_for_array_iteration(int first, int last)
2133*cf5a6c84SAndroid Build Coastguard Worker {
2134*cf5a6c84SAndroid Build Coastguard Worker   return ZCODE[first] == tkvar && ZCODE[first + 2] == tkvar
2135*cf5a6c84SAndroid Build Coastguard Worker       && ZCODE[first + 4] == tkin && ZCODE[first + 5] == opdrop
2136*cf5a6c84SAndroid Build Coastguard Worker       && first + 5 == last;
2137*cf5a6c84SAndroid Build Coastguard Worker }
2138*cf5a6c84SAndroid Build Coastguard Worker 
for_stmt(void)2139*cf5a6c84SAndroid Build Coastguard Worker static void for_stmt(void)
2140*cf5a6c84SAndroid Build Coastguard Worker {
2141*cf5a6c84SAndroid Build Coastguard Worker   int brk, cont;
2142*cf5a6c84SAndroid Build Coastguard Worker   save_break_continue(&brk, &cont);
2143*cf5a6c84SAndroid Build Coastguard Worker   expect(tkfor);
2144*cf5a6c84SAndroid Build Coastguard Worker   expect(tklparen);
2145*cf5a6c84SAndroid Build Coastguard Worker   if (havetok(tksemi)) {
2146*cf5a6c84SAndroid Build Coastguard Worker     // No "initialization" part
2147*cf5a6c84SAndroid Build Coastguard Worker     for_not_map_iter();
2148*cf5a6c84SAndroid Build Coastguard Worker   } else {
2149*cf5a6c84SAndroid Build Coastguard Worker     int loop_start_loc = TT.zcode_last + 1;
2150*cf5a6c84SAndroid Build Coastguard Worker     simple_stmt();  // initializaton part, OR varname in arrayname form
2151*cf5a6c84SAndroid Build Coastguard Worker     if (!havetok(tkrparen)) {
2152*cf5a6c84SAndroid Build Coastguard Worker       expect(tksemi);
2153*cf5a6c84SAndroid Build Coastguard Worker       for_not_map_iter();
2154*cf5a6c84SAndroid Build Coastguard Worker     } else {
2155*cf5a6c84SAndroid Build Coastguard Worker       // Must be map iteration
2156*cf5a6c84SAndroid Build Coastguard Worker       // Check here for varname in varname!
2157*cf5a6c84SAndroid Build Coastguard Worker       // FIXME TODO must examine generated TT.zcode for var in array?
2158*cf5a6c84SAndroid Build Coastguard Worker       if (!valid_for_array_iteration(loop_start_loc, TT.zcode_last))
2159*cf5a6c84SAndroid Build Coastguard Worker         XERR("%s", "bad 'for (var in array)' loop\n");
2160*cf5a6c84SAndroid Build Coastguard Worker       else {
2161*cf5a6c84SAndroid Build Coastguard Worker         ZCODE[TT.zcode_last-5] = opvarref;
2162*cf5a6c84SAndroid Build Coastguard Worker         ZCODE[TT.zcode_last-1] = tknumber;
2163*cf5a6c84SAndroid Build Coastguard Worker         ZCODE[TT.zcode_last] = make_literal_num_val(-1);
2164*cf5a6c84SAndroid Build Coastguard Worker         TT.cgl.continue_dest = TT.zcode_last + 1;
2165*cf5a6c84SAndroid Build Coastguard Worker         gen2cd(opmapiternext, 2);
2166*cf5a6c84SAndroid Build Coastguard Worker         TT.cgl.break_dest = TT.zcode_last + 1;
2167*cf5a6c84SAndroid Build Coastguard Worker         gen2cd(opjump, -1);   // fill in with loc after stmt
2168*cf5a6c84SAndroid Build Coastguard Worker       }
2169*cf5a6c84SAndroid Build Coastguard Worker       optional_nl();
2170*cf5a6c84SAndroid Build Coastguard Worker       // fixup TT.stack if return or exit inside for (var in array)
2171*cf5a6c84SAndroid Build Coastguard Worker       TT.cgl.stack_offset_to_fix += 3;
2172*cf5a6c84SAndroid Build Coastguard Worker       stmt();
2173*cf5a6c84SAndroid Build Coastguard Worker       TT.cgl.stack_offset_to_fix -= 3;
2174*cf5a6c84SAndroid Build Coastguard Worker       gen2cd(opjump, TT.cgl.continue_dest - TT.zcode_last - 3);
2175*cf5a6c84SAndroid Build Coastguard Worker       ZCODE[TT.cgl.break_dest + 1] = TT.zcode_last - TT.cgl.break_dest - 1;
2176*cf5a6c84SAndroid Build Coastguard Worker       gencd(opdrop);
2177*cf5a6c84SAndroid Build Coastguard Worker       gencd(opdrop);
2178*cf5a6c84SAndroid Build Coastguard Worker       gencd(opdrop);
2179*cf5a6c84SAndroid Build Coastguard Worker     }
2180*cf5a6c84SAndroid Build Coastguard Worker   }
2181*cf5a6c84SAndroid Build Coastguard Worker   restore_break_continue(&brk, &cont);
2182*cf5a6c84SAndroid Build Coastguard Worker }
2183*cf5a6c84SAndroid Build Coastguard Worker 
stmt(void)2184*cf5a6c84SAndroid Build Coastguard Worker static void stmt(void)
2185*cf5a6c84SAndroid Build Coastguard Worker {
2186*cf5a6c84SAndroid Build Coastguard Worker   switch (CURTOK()) {
2187*cf5a6c84SAndroid Build Coastguard Worker     case tkeof:
2188*cf5a6c84SAndroid Build Coastguard Worker       break;     // FIXME ERROR?
2189*cf5a6c84SAndroid Build Coastguard Worker 
2190*cf5a6c84SAndroid Build Coastguard Worker     case tkbreak:
2191*cf5a6c84SAndroid Build Coastguard Worker       scan();
2192*cf5a6c84SAndroid Build Coastguard Worker       if (TT.cgl.break_dest) gen2cd(tkbreak, TT.cgl.break_dest - TT.zcode_last - 3);
2193*cf5a6c84SAndroid Build Coastguard Worker       else XERR("%s", "break not in a loop\n");
2194*cf5a6c84SAndroid Build Coastguard Worker       break;
2195*cf5a6c84SAndroid Build Coastguard Worker 
2196*cf5a6c84SAndroid Build Coastguard Worker     case tkcontinue:
2197*cf5a6c84SAndroid Build Coastguard Worker       scan();
2198*cf5a6c84SAndroid Build Coastguard Worker       if (TT.cgl.continue_dest)
2199*cf5a6c84SAndroid Build Coastguard Worker         gen2cd(tkcontinue, TT.cgl.continue_dest - TT.zcode_last - 3);
2200*cf5a6c84SAndroid Build Coastguard Worker       else XERR("%s", "continue not in a loop\n");
2201*cf5a6c84SAndroid Build Coastguard Worker       break;
2202*cf5a6c84SAndroid Build Coastguard Worker 
2203*cf5a6c84SAndroid Build Coastguard Worker     case tknext:
2204*cf5a6c84SAndroid Build Coastguard Worker       scan();
2205*cf5a6c84SAndroid Build Coastguard Worker       gencd(tknext);
2206*cf5a6c84SAndroid Build Coastguard Worker       if (TT.cgl.rule_type) XERR("%s", "next inside BEGIN or END\n");
2207*cf5a6c84SAndroid Build Coastguard Worker       if (TT.cgl.in_function_body) XERR("%s", "next inside function def\n");
2208*cf5a6c84SAndroid Build Coastguard Worker       break;
2209*cf5a6c84SAndroid Build Coastguard Worker 
2210*cf5a6c84SAndroid Build Coastguard Worker     case tknextfile:
2211*cf5a6c84SAndroid Build Coastguard Worker       scan();
2212*cf5a6c84SAndroid Build Coastguard Worker       gencd(tknextfile);
2213*cf5a6c84SAndroid Build Coastguard Worker       if (TT.cgl.rule_type) XERR("%s", "nextfile inside BEGIN or END\n");
2214*cf5a6c84SAndroid Build Coastguard Worker       if (TT.cgl.in_function_body) XERR("%s", "nextfile inside function def\n");
2215*cf5a6c84SAndroid Build Coastguard Worker       break;
2216*cf5a6c84SAndroid Build Coastguard Worker 
2217*cf5a6c84SAndroid Build Coastguard Worker     case tkexit:
2218*cf5a6c84SAndroid Build Coastguard Worker       scan();
2219*cf5a6c84SAndroid Build Coastguard Worker       if (strchr(exprstartsy, CURTOK())) {
2220*cf5a6c84SAndroid Build Coastguard Worker         expr(0);
2221*cf5a6c84SAndroid Build Coastguard Worker       } else gen2cd(tknumber, make_literal_num_val(NO_EXIT_STATUS));
2222*cf5a6c84SAndroid Build Coastguard Worker       gencd(tkexit);
2223*cf5a6c84SAndroid Build Coastguard Worker       break;
2224*cf5a6c84SAndroid Build Coastguard Worker 
2225*cf5a6c84SAndroid Build Coastguard Worker     case tkreturn:
2226*cf5a6c84SAndroid Build Coastguard Worker       scan();
2227*cf5a6c84SAndroid Build Coastguard Worker       if (TT.cgl.stack_offset_to_fix) gen2cd(opdrop_n, TT.cgl.stack_offset_to_fix);
2228*cf5a6c84SAndroid Build Coastguard Worker       if (strchr(exprstartsy, CURTOK())) {
2229*cf5a6c84SAndroid Build Coastguard Worker         expr(0);
2230*cf5a6c84SAndroid Build Coastguard Worker       } else gen2cd(tknumber, make_literal_num_val(0.0));
2231*cf5a6c84SAndroid Build Coastguard Worker       gen2cd(tkreturn, TT.cgl.nparms);
2232*cf5a6c84SAndroid Build Coastguard Worker       if (!TT.cgl.in_function_body) XERR("%s", "return outside function def\n");
2233*cf5a6c84SAndroid Build Coastguard Worker       break;
2234*cf5a6c84SAndroid Build Coastguard Worker 
2235*cf5a6c84SAndroid Build Coastguard Worker     case tklbrace:
2236*cf5a6c84SAndroid Build Coastguard Worker       action(tklbrace);
2237*cf5a6c84SAndroid Build Coastguard Worker       break;
2238*cf5a6c84SAndroid Build Coastguard Worker 
2239*cf5a6c84SAndroid Build Coastguard Worker     case tkif:
2240*cf5a6c84SAndroid Build Coastguard Worker       if_stmt();
2241*cf5a6c84SAndroid Build Coastguard Worker       break;
2242*cf5a6c84SAndroid Build Coastguard Worker 
2243*cf5a6c84SAndroid Build Coastguard Worker     case tkwhile:
2244*cf5a6c84SAndroid Build Coastguard Worker       while_stmt();
2245*cf5a6c84SAndroid Build Coastguard Worker       break;
2246*cf5a6c84SAndroid Build Coastguard Worker 
2247*cf5a6c84SAndroid Build Coastguard Worker     case tkdo:
2248*cf5a6c84SAndroid Build Coastguard Worker       do_stmt();
2249*cf5a6c84SAndroid Build Coastguard Worker       break;
2250*cf5a6c84SAndroid Build Coastguard Worker 
2251*cf5a6c84SAndroid Build Coastguard Worker     case tkfor:
2252*cf5a6c84SAndroid Build Coastguard Worker       for_stmt();
2253*cf5a6c84SAndroid Build Coastguard Worker       break;
2254*cf5a6c84SAndroid Build Coastguard Worker 
2255*cf5a6c84SAndroid Build Coastguard Worker     case tksemi:
2256*cf5a6c84SAndroid Build Coastguard Worker       scan();
2257*cf5a6c84SAndroid Build Coastguard Worker       break;
2258*cf5a6c84SAndroid Build Coastguard Worker     default:
2259*cf5a6c84SAndroid Build Coastguard Worker       simple_stmt();      // expression print printf delete
2260*cf5a6c84SAndroid Build Coastguard Worker   }
2261*cf5a6c84SAndroid Build Coastguard Worker }
2262*cf5a6c84SAndroid Build Coastguard Worker 
add_param(int funcnum,char * s)2263*cf5a6c84SAndroid Build Coastguard Worker static void add_param(int funcnum, char *s)
2264*cf5a6c84SAndroid Build Coastguard Worker {
2265*cf5a6c84SAndroid Build Coastguard Worker   if (!find_local_entry(s)) add_local_entry(s);
2266*cf5a6c84SAndroid Build Coastguard Worker   else XERR("function '%s' dup param '%s'\n", FUNC_DEF[funcnum].name, s);
2267*cf5a6c84SAndroid Build Coastguard Worker   TT.cgl.nparms++;
2268*cf5a6c84SAndroid Build Coastguard Worker 
2269*cf5a6c84SAndroid Build Coastguard Worker   // POSIX: The same name shall not be used as both a function parameter name
2270*cf5a6c84SAndroid Build Coastguard Worker   // and as the name of a function or a special awk variable.
2271*cf5a6c84SAndroid Build Coastguard Worker   // !!! NOTE seems implementations exc. mawk only compare param names with
2272*cf5a6c84SAndroid Build Coastguard Worker   // builtin funcs; use same name as userfunc is OK!
2273*cf5a6c84SAndroid Build Coastguard Worker   if (!strcmp(s, FUNC_DEF[funcnum].name))
2274*cf5a6c84SAndroid Build Coastguard Worker     XERR("function '%s' param '%s' matches func name\n",
2275*cf5a6c84SAndroid Build Coastguard Worker         FUNC_DEF[funcnum].name, s);
2276*cf5a6c84SAndroid Build Coastguard Worker   if (find_global(s) && find_global(s) < TT.spec_var_limit)
2277*cf5a6c84SAndroid Build Coastguard Worker     XERR("function '%s' param '%s' matches special var\n",
2278*cf5a6c84SAndroid Build Coastguard Worker         FUNC_DEF[funcnum].name, s);
2279*cf5a6c84SAndroid Build Coastguard Worker }
2280*cf5a6c84SAndroid Build Coastguard Worker 
function_def(void)2281*cf5a6c84SAndroid Build Coastguard Worker static void function_def(void)
2282*cf5a6c84SAndroid Build Coastguard Worker {
2283*cf5a6c84SAndroid Build Coastguard Worker   expect(tkfunction);
2284*cf5a6c84SAndroid Build Coastguard Worker   int funcnum = find_func_def_entry(TT.tokstr);
2285*cf5a6c84SAndroid Build Coastguard Worker   if (!funcnum) {
2286*cf5a6c84SAndroid Build Coastguard Worker     funcnum = add_func_def_entry(TT.tokstr);
2287*cf5a6c84SAndroid Build Coastguard Worker   } else if (FUNC_DEF[funcnum].flags & FUNC_DEFINED) {
2288*cf5a6c84SAndroid Build Coastguard Worker     XERR("dup defined function '%s'\n", TT.tokstr);
2289*cf5a6c84SAndroid Build Coastguard Worker   }
2290*cf5a6c84SAndroid Build Coastguard Worker   FUNC_DEF[funcnum].flags |= FUNC_DEFINED;
2291*cf5a6c84SAndroid Build Coastguard Worker   if (find_global(TT.tokstr)) {
2292*cf5a6c84SAndroid Build Coastguard Worker     // POSIX: The same name shall not be used both as a variable name with
2293*cf5a6c84SAndroid Build Coastguard Worker     // global scope and as the name of a function.
2294*cf5a6c84SAndroid Build Coastguard Worker     XERR("function name '%s' previously defined\n", TT.tokstr);
2295*cf5a6c84SAndroid Build Coastguard Worker   }
2296*cf5a6c84SAndroid Build Coastguard Worker 
2297*cf5a6c84SAndroid Build Coastguard Worker   gen2cd(tkfunction, funcnum);
2298*cf5a6c84SAndroid Build Coastguard Worker   FUNC_DEF[funcnum].zcode_addr = TT.zcode_last - 1;
2299*cf5a6c84SAndroid Build Coastguard Worker   TT.cgl.funcnum = funcnum;
2300*cf5a6c84SAndroid Build Coastguard Worker   TT.cgl.nparms = 0;
2301*cf5a6c84SAndroid Build Coastguard Worker   if (ISTOK(tkfunc)) expect(tkfunc); // func name with no space before (
2302*cf5a6c84SAndroid Build Coastguard Worker   else expect(tkvar);  // func name with space before (
2303*cf5a6c84SAndroid Build Coastguard Worker   expect(tklparen);
2304*cf5a6c84SAndroid Build Coastguard Worker   if (ISTOK(tkvar)) {
2305*cf5a6c84SAndroid Build Coastguard Worker     add_param(funcnum, TT.tokstr);
2306*cf5a6c84SAndroid Build Coastguard Worker     scan();
2307*cf5a6c84SAndroid Build Coastguard Worker     // FIXME is the the best way? what if TT.tokstr not a tkvar?
2308*cf5a6c84SAndroid Build Coastguard Worker     while (have_comma()) {
2309*cf5a6c84SAndroid Build Coastguard Worker       add_param(funcnum, TT.tokstr);
2310*cf5a6c84SAndroid Build Coastguard Worker       expect(tkvar);
2311*cf5a6c84SAndroid Build Coastguard Worker     }
2312*cf5a6c84SAndroid Build Coastguard Worker   }
2313*cf5a6c84SAndroid Build Coastguard Worker   rparen();
2314*cf5a6c84SAndroid Build Coastguard Worker   if (ISTOK(tklbrace)) {
2315*cf5a6c84SAndroid Build Coastguard Worker     TT.cgl.in_function_body = 1;
2316*cf5a6c84SAndroid Build Coastguard Worker     action(tkfunc);
2317*cf5a6c84SAndroid Build Coastguard Worker     TT.cgl.in_function_body = 0;
2318*cf5a6c84SAndroid Build Coastguard Worker     // Need to return uninit value if falling off end of function.
2319*cf5a6c84SAndroid Build Coastguard Worker     gen2cd(tknumber, make_uninit_val());
2320*cf5a6c84SAndroid Build Coastguard Worker     gen2cd(tkreturn, TT.cgl.nparms);
2321*cf5a6c84SAndroid Build Coastguard Worker   } else {
2322*cf5a6c84SAndroid Build Coastguard Worker     XERR("syntax near '%s'\n", TT.tokstr);
2323*cf5a6c84SAndroid Build Coastguard Worker     // FIXME some recovery needed here!?
2324*cf5a6c84SAndroid Build Coastguard Worker   }
2325*cf5a6c84SAndroid Build Coastguard Worker   // Do not re-init locals table for dup function.
2326*cf5a6c84SAndroid Build Coastguard Worker   // Avoids memory leak detected by LeakSanitizer.
2327*cf5a6c84SAndroid Build Coastguard Worker   if (!FUNC_DEF[funcnum].function_locals.base) {
2328*cf5a6c84SAndroid Build Coastguard Worker     FUNC_DEF[funcnum].function_locals = TT.locals_table;
2329*cf5a6c84SAndroid Build Coastguard Worker     init_locals_table();
2330*cf5a6c84SAndroid Build Coastguard Worker   }
2331*cf5a6c84SAndroid Build Coastguard Worker }
2332*cf5a6c84SAndroid Build Coastguard Worker 
action(int action_type)2333*cf5a6c84SAndroid Build Coastguard Worker static void action(int action_type)
2334*cf5a6c84SAndroid Build Coastguard Worker {
2335*cf5a6c84SAndroid Build Coastguard Worker (void)action_type;
2336*cf5a6c84SAndroid Build Coastguard Worker   // action_type is tkbegin, tkend, tkdo (every line), tkif (if pattern),
2337*cf5a6c84SAndroid Build Coastguard Worker   //                  tkfunc (function body), tklbrace (compound statement)
2338*cf5a6c84SAndroid Build Coastguard Worker   // Should have lbrace on entry.
2339*cf5a6c84SAndroid Build Coastguard Worker   expect(tklbrace);
2340*cf5a6c84SAndroid Build Coastguard Worker   for (;;) {
2341*cf5a6c84SAndroid Build Coastguard Worker     if (ISTOK(tkeof)) unexpected_eof();
2342*cf5a6c84SAndroid Build Coastguard Worker     optional_nl_or_semi();
2343*cf5a6c84SAndroid Build Coastguard Worker     if (havetok(tkrbrace)) {
2344*cf5a6c84SAndroid Build Coastguard Worker       break;
2345*cf5a6c84SAndroid Build Coastguard Worker     }
2346*cf5a6c84SAndroid Build Coastguard Worker     stmt();
2347*cf5a6c84SAndroid Build Coastguard Worker     // stmt() is normally unterminated here, but may be terminated if we
2348*cf5a6c84SAndroid Build Coastguard Worker     // have if with no else (had to consume terminator looking for else)
2349*cf5a6c84SAndroid Build Coastguard Worker     //   !!!   if (ISTOK(tkrbrace) || prev_was_terminated())
2350*cf5a6c84SAndroid Build Coastguard Worker     if (prev_was_terminated()) continue;
2351*cf5a6c84SAndroid Build Coastguard Worker     if (!is_nl_semi() && !ISTOK(tkrbrace)) {
2352*cf5a6c84SAndroid Build Coastguard Worker       XERR("syntax near '%s' -- newline, ';', or '}' expected\n", TT.tokstr);
2353*cf5a6c84SAndroid Build Coastguard Worker       while (!is_nl_semi() && !ISTOK(tkrbrace) && !ISTOK(tkeof)) scan();
2354*cf5a6c84SAndroid Build Coastguard Worker       if (ISTOK(tkeof)) unexpected_eof();
2355*cf5a6c84SAndroid Build Coastguard Worker     }
2356*cf5a6c84SAndroid Build Coastguard Worker     if (havetok(tkrbrace)) break;
2357*cf5a6c84SAndroid Build Coastguard Worker     // Must be semicolon or newline
2358*cf5a6c84SAndroid Build Coastguard Worker     scan();
2359*cf5a6c84SAndroid Build Coastguard Worker   }
2360*cf5a6c84SAndroid Build Coastguard Worker }
2361*cf5a6c84SAndroid Build Coastguard Worker 
rule(void)2362*cf5a6c84SAndroid Build Coastguard Worker static void rule(void)
2363*cf5a6c84SAndroid Build Coastguard Worker {
2364*cf5a6c84SAndroid Build Coastguard Worker   //       pa_pat
2365*cf5a6c84SAndroid Build Coastguard Worker   //     | pa_pat lbrace stmtlist '}'
2366*cf5a6c84SAndroid Build Coastguard Worker   //     | pa_pat ',' opt_nl pa_pat
2367*cf5a6c84SAndroid Build Coastguard Worker   //     | pa_pat ',' opt_nl pa_pat lbrace stmtlist '}'
2368*cf5a6c84SAndroid Build Coastguard Worker   //     | lbrace stmtlist '}'
2369*cf5a6c84SAndroid Build Coastguard Worker   //     | XBEGIN lbrace stmtlist '}'
2370*cf5a6c84SAndroid Build Coastguard Worker   //     | XEND lbrace stmtlist '}'
2371*cf5a6c84SAndroid Build Coastguard Worker   //     | FUNC funcname '(' varlist rparen  lbrace stmtlist '}'
2372*cf5a6c84SAndroid Build Coastguard Worker 
2373*cf5a6c84SAndroid Build Coastguard Worker   switch (CURTOK()) {
2374*cf5a6c84SAndroid Build Coastguard Worker     case tkbegin:
2375*cf5a6c84SAndroid Build Coastguard Worker       scan();
2376*cf5a6c84SAndroid Build Coastguard Worker       if (TT.cgl.last_begin) ZCODE[TT.cgl.last_begin] = TT.zcode_last - TT.cgl.last_begin;
2377*cf5a6c84SAndroid Build Coastguard Worker       else TT.cgl.first_begin = TT.zcode_last + 1;
2378*cf5a6c84SAndroid Build Coastguard Worker 
2379*cf5a6c84SAndroid Build Coastguard Worker       TT.cgl.rule_type = tkbegin;
2380*cf5a6c84SAndroid Build Coastguard Worker       action(tkbegin);
2381*cf5a6c84SAndroid Build Coastguard Worker       TT.cgl.rule_type = 0;
2382*cf5a6c84SAndroid Build Coastguard Worker       gen2cd(opjump, -1);
2383*cf5a6c84SAndroid Build Coastguard Worker       TT.cgl.last_begin = TT.zcode_last;
2384*cf5a6c84SAndroid Build Coastguard Worker       break;
2385*cf5a6c84SAndroid Build Coastguard Worker 
2386*cf5a6c84SAndroid Build Coastguard Worker     case tkend:
2387*cf5a6c84SAndroid Build Coastguard Worker       scan();
2388*cf5a6c84SAndroid Build Coastguard Worker       if (TT.cgl.last_end) ZCODE[TT.cgl.last_end] = TT.zcode_last - TT.cgl.last_end;
2389*cf5a6c84SAndroid Build Coastguard Worker       else TT.cgl.first_end = TT.zcode_last + 1;
2390*cf5a6c84SAndroid Build Coastguard Worker 
2391*cf5a6c84SAndroid Build Coastguard Worker       TT.cgl.rule_type = tkbegin;
2392*cf5a6c84SAndroid Build Coastguard Worker       action(tkend);
2393*cf5a6c84SAndroid Build Coastguard Worker       TT.cgl.rule_type = 0;
2394*cf5a6c84SAndroid Build Coastguard Worker       gen2cd(opjump, -1);
2395*cf5a6c84SAndroid Build Coastguard Worker       TT.cgl.last_end = TT.zcode_last;
2396*cf5a6c84SAndroid Build Coastguard Worker       break;
2397*cf5a6c84SAndroid Build Coastguard Worker 
2398*cf5a6c84SAndroid Build Coastguard Worker     case tklbrace:
2399*cf5a6c84SAndroid Build Coastguard Worker       if (TT.cgl.last_recrule)
2400*cf5a6c84SAndroid Build Coastguard Worker         ZCODE[TT.cgl.last_recrule] = TT.zcode_last - TT.cgl.last_recrule;
2401*cf5a6c84SAndroid Build Coastguard Worker       else TT.cgl.first_recrule = TT.zcode_last + 1;
2402*cf5a6c84SAndroid Build Coastguard Worker       action(tkdo);
2403*cf5a6c84SAndroid Build Coastguard Worker       gen2cd(opjump, -1);
2404*cf5a6c84SAndroid Build Coastguard Worker       TT.cgl.last_recrule = TT.zcode_last;
2405*cf5a6c84SAndroid Build Coastguard Worker       break;
2406*cf5a6c84SAndroid Build Coastguard Worker 
2407*cf5a6c84SAndroid Build Coastguard Worker     case tkfunction:
2408*cf5a6c84SAndroid Build Coastguard Worker       function_def();
2409*cf5a6c84SAndroid Build Coastguard Worker       break;
2410*cf5a6c84SAndroid Build Coastguard Worker     default:
2411*cf5a6c84SAndroid Build Coastguard Worker       if (TT.cgl.last_recrule)
2412*cf5a6c84SAndroid Build Coastguard Worker         ZCODE[TT.cgl.last_recrule] = TT.zcode_last - TT.cgl.last_recrule;
2413*cf5a6c84SAndroid Build Coastguard Worker       else TT.cgl.first_recrule = TT.zcode_last + 1;
2414*cf5a6c84SAndroid Build Coastguard Worker       gen2cd(opjump, 1);
2415*cf5a6c84SAndroid Build Coastguard Worker       gencd(tkeof);
2416*cf5a6c84SAndroid Build Coastguard Worker       int cdx = 0, saveloc = TT.zcode_last;
2417*cf5a6c84SAndroid Build Coastguard Worker       expr(0);
2418*cf5a6c84SAndroid Build Coastguard Worker       if (!have_comma()) {
2419*cf5a6c84SAndroid Build Coastguard Worker         gen2cd(tkif, -1);
2420*cf5a6c84SAndroid Build Coastguard Worker         cdx = TT.zcode_last;
2421*cf5a6c84SAndroid Build Coastguard Worker       } else {
2422*cf5a6c84SAndroid Build Coastguard Worker         gen2cd(oprange2, ++TT.cgl.range_pattern_num);
2423*cf5a6c84SAndroid Build Coastguard Worker         gencd(-1);
2424*cf5a6c84SAndroid Build Coastguard Worker         cdx = TT.zcode_last;
2425*cf5a6c84SAndroid Build Coastguard Worker         ZCODE[saveloc-2] = oprange1;
2426*cf5a6c84SAndroid Build Coastguard Worker         ZCODE[saveloc-1] = TT.cgl.range_pattern_num;
2427*cf5a6c84SAndroid Build Coastguard Worker         ZCODE[saveloc] = TT.zcode_last - saveloc;
2428*cf5a6c84SAndroid Build Coastguard Worker         expr(0);
2429*cf5a6c84SAndroid Build Coastguard Worker         gen2cd(oprange3, TT.cgl.range_pattern_num);
2430*cf5a6c84SAndroid Build Coastguard Worker       }
2431*cf5a6c84SAndroid Build Coastguard Worker       if (ISTOK(tklbrace)) {
2432*cf5a6c84SAndroid Build Coastguard Worker         action(tkif);
2433*cf5a6c84SAndroid Build Coastguard Worker         ZCODE[cdx] = TT.zcode_last - cdx;
2434*cf5a6c84SAndroid Build Coastguard Worker       } else {
2435*cf5a6c84SAndroid Build Coastguard Worker         gencd(opprintrec);   // print $0 ?
2436*cf5a6c84SAndroid Build Coastguard Worker         ZCODE[cdx] = TT.zcode_last - cdx;
2437*cf5a6c84SAndroid Build Coastguard Worker       }
2438*cf5a6c84SAndroid Build Coastguard Worker       gen2cd(opjump, -1);
2439*cf5a6c84SAndroid Build Coastguard Worker       TT.cgl.last_recrule = TT.zcode_last;
2440*cf5a6c84SAndroid Build Coastguard Worker   }
2441*cf5a6c84SAndroid Build Coastguard Worker }
2442*cf5a6c84SAndroid Build Coastguard Worker 
diag_func_def_ref(void)2443*cf5a6c84SAndroid Build Coastguard Worker static void diag_func_def_ref(void)
2444*cf5a6c84SAndroid Build Coastguard Worker {
2445*cf5a6c84SAndroid Build Coastguard Worker   int n = zlist_len(&TT.func_def_table);
2446*cf5a6c84SAndroid Build Coastguard Worker   for (int k = 1; k < n; k++) {
2447*cf5a6c84SAndroid Build Coastguard Worker     if ((FUNC_DEF[k].flags & FUNC_CALLED) &&
2448*cf5a6c84SAndroid Build Coastguard Worker             !(FUNC_DEF[k].flags & FUNC_DEFINED)) {
2449*cf5a6c84SAndroid Build Coastguard Worker       // Sorry, we can't tell where this was called from, for now at least.
2450*cf5a6c84SAndroid Build Coastguard Worker       XERR("Undefined function '%s'", FUNC_DEF[k].name);
2451*cf5a6c84SAndroid Build Coastguard Worker     }
2452*cf5a6c84SAndroid Build Coastguard Worker   }
2453*cf5a6c84SAndroid Build Coastguard Worker }
2454*cf5a6c84SAndroid Build Coastguard Worker 
compile(void)2455*cf5a6c84SAndroid Build Coastguard Worker static void compile(void)
2456*cf5a6c84SAndroid Build Coastguard Worker {
2457*cf5a6c84SAndroid Build Coastguard Worker   init_compiler();
2458*cf5a6c84SAndroid Build Coastguard Worker   init_scanner();
2459*cf5a6c84SAndroid Build Coastguard Worker   scan();
2460*cf5a6c84SAndroid Build Coastguard Worker   optional_nl_or_semi();        // Does posix allow NL or ; before first rule?
2461*cf5a6c84SAndroid Build Coastguard Worker   while (! ISTOK(tkeof)) {
2462*cf5a6c84SAndroid Build Coastguard Worker     rule();
2463*cf5a6c84SAndroid Build Coastguard Worker     optional_nl_or_semi();        // NOT POSIX
2464*cf5a6c84SAndroid Build Coastguard Worker   }
2465*cf5a6c84SAndroid Build Coastguard Worker 
2466*cf5a6c84SAndroid Build Coastguard Worker 
2467*cf5a6c84SAndroid Build Coastguard Worker   if (TT.cgl.last_begin) ZCODE[TT.cgl.last_begin-1] = opquit;
2468*cf5a6c84SAndroid Build Coastguard Worker   if (TT.cgl.last_end) ZCODE[TT.cgl.last_end-1] = opquit;
2469*cf5a6c84SAndroid Build Coastguard Worker   if (TT.cgl.last_recrule) ZCODE[TT.cgl.last_recrule-1] = opquit;
2470*cf5a6c84SAndroid Build Coastguard Worker 
2471*cf5a6c84SAndroid Build Coastguard Worker   gen2cd(tknumber, make_literal_num_val(0.0));
2472*cf5a6c84SAndroid Build Coastguard Worker   gencd(tkexit);
2473*cf5a6c84SAndroid Build Coastguard Worker   gencd(opquit);
2474*cf5a6c84SAndroid Build Coastguard Worker   // If there are only BEGIN and END or only END actions, generate actions to
2475*cf5a6c84SAndroid Build Coastguard Worker   // read all input before END.
2476*cf5a6c84SAndroid Build Coastguard Worker   if (TT.cgl.first_end && !TT.cgl.first_recrule) {
2477*cf5a6c84SAndroid Build Coastguard Worker     gencd(opquit);
2478*cf5a6c84SAndroid Build Coastguard Worker     TT.cgl.first_recrule = TT.zcode_last;
2479*cf5a6c84SAndroid Build Coastguard Worker   }
2480*cf5a6c84SAndroid Build Coastguard Worker   gencd(opquit);  // One more opcode to keep ip in bounds in run code.
2481*cf5a6c84SAndroid Build Coastguard Worker   diag_func_def_ref();
2482*cf5a6c84SAndroid Build Coastguard Worker }
2483*cf5a6c84SAndroid Build Coastguard Worker 
2484*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
2485*cf5a6c84SAndroid Build Coastguard Worker //// runtime
2486*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
2487*cf5a6c84SAndroid Build Coastguard Worker 
check_numeric_string(struct zvalue * v)2488*cf5a6c84SAndroid Build Coastguard Worker static void check_numeric_string(struct zvalue *v)
2489*cf5a6c84SAndroid Build Coastguard Worker {
2490*cf5a6c84SAndroid Build Coastguard Worker   if (v->vst) {
2491*cf5a6c84SAndroid Build Coastguard Worker     char *end, *s = v->vst->str;
2492*cf5a6c84SAndroid Build Coastguard Worker     // Significant speed gain with this test:
2493*cf5a6c84SAndroid Build Coastguard Worker     // num string must begin space, +, -, ., or digit.
2494*cf5a6c84SAndroid Build Coastguard Worker     if (strchr("+-.1234567890 ", *s)) {
2495*cf5a6c84SAndroid Build Coastguard Worker       double num = strtod(s, &end);
2496*cf5a6c84SAndroid Build Coastguard Worker       if (s == end || end[strspn(end, " ")]) return;
2497*cf5a6c84SAndroid Build Coastguard Worker       v->num = num;
2498*cf5a6c84SAndroid Build Coastguard Worker       v->flags |= ZF_NUM | ZF_STR | ZF_NUMSTR;
2499*cf5a6c84SAndroid Build Coastguard Worker     }
2500*cf5a6c84SAndroid Build Coastguard Worker   }
2501*cf5a6c84SAndroid Build Coastguard Worker }
2502*cf5a6c84SAndroid Build Coastguard Worker 
num_to_zstring(double n,char * fmt)2503*cf5a6c84SAndroid Build Coastguard Worker static struct zstring *num_to_zstring(double n, char *fmt)
2504*cf5a6c84SAndroid Build Coastguard Worker {
2505*cf5a6c84SAndroid Build Coastguard Worker   int k;
2506*cf5a6c84SAndroid Build Coastguard Worker   if (n == (long long)n) k = snprintf(TT.pbuf, PBUFSIZE, "%lld", (long long)n);
2507*cf5a6c84SAndroid Build Coastguard Worker   else k = snprintf(TT.pbuf, PBUFSIZE, fmt, n);
2508*cf5a6c84SAndroid Build Coastguard Worker   if (k < 0 || k >= PBUFSIZE) FFATAL("error encoding %f via '%s'", n, fmt);
2509*cf5a6c84SAndroid Build Coastguard Worker   return new_zstring(TT.pbuf, k);
2510*cf5a6c84SAndroid Build Coastguard Worker }
2511*cf5a6c84SAndroid Build Coastguard Worker 
2512*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
2513*cf5a6c84SAndroid Build Coastguard Worker //// regex routines
2514*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
2515*cf5a6c84SAndroid Build Coastguard Worker 
escape_str(char * s,int is_regex)2516*cf5a6c84SAndroid Build Coastguard Worker static char *escape_str(char *s, int is_regex)
2517*cf5a6c84SAndroid Build Coastguard Worker {
2518*cf5a6c84SAndroid Build Coastguard Worker   char *p, *escapes = is_regex ? "abfnrtv\"/" : "\\abfnrtv\"/";
2519*cf5a6c84SAndroid Build Coastguard Worker   // FIXME TODO should / be in there?
2520*cf5a6c84SAndroid Build Coastguard Worker   char *s0 = s, *to = s;
2521*cf5a6c84SAndroid Build Coastguard Worker   while ((*to = *s)) {
2522*cf5a6c84SAndroid Build Coastguard Worker     if (*s != '\\') { to++, s++;
2523*cf5a6c84SAndroid Build Coastguard Worker     } else if ((p = strchr(escapes, *++s))) {
2524*cf5a6c84SAndroid Build Coastguard Worker       // checking char after \ for known escapes
2525*cf5a6c84SAndroid Build Coastguard Worker       int c = (is_regex?"\a\b\f\n\r\t\v\"/":"\\\a\b\f\n\r\t\v\"/")[p-escapes];
2526*cf5a6c84SAndroid Build Coastguard Worker       if (c) *to = c, s++;  // else final backslash
2527*cf5a6c84SAndroid Build Coastguard Worker       to++;
2528*cf5a6c84SAndroid Build Coastguard Worker     } else if ('0' <= *s && *s <= '9') {
2529*cf5a6c84SAndroid Build Coastguard Worker       int k, c = *s++ - '0';
2530*cf5a6c84SAndroid Build Coastguard Worker       for (k = 0; k < 2 && '0' <= *s && *s <= '9'; k++)
2531*cf5a6c84SAndroid Build Coastguard Worker         c = c * 8 + *s++ - '0';
2532*cf5a6c84SAndroid Build Coastguard Worker       *to++ = c;
2533*cf5a6c84SAndroid Build Coastguard Worker     } else if (*s == 'x') {
2534*cf5a6c84SAndroid Build Coastguard Worker       if (isxdigit(s[1])) {
2535*cf5a6c84SAndroid Build Coastguard Worker         int c = hexval(*++s);
2536*cf5a6c84SAndroid Build Coastguard Worker         if (isxdigit(s[1])) c = c * 16 + hexval(*++s);
2537*cf5a6c84SAndroid Build Coastguard Worker         *to++ = c, s++;
2538*cf5a6c84SAndroid Build Coastguard Worker       }
2539*cf5a6c84SAndroid Build Coastguard Worker     } else {
2540*cf5a6c84SAndroid Build Coastguard Worker       if (is_regex) *to++ = '\\';
2541*cf5a6c84SAndroid Build Coastguard Worker       *to++ = *s++;
2542*cf5a6c84SAndroid Build Coastguard Worker     }
2543*cf5a6c84SAndroid Build Coastguard Worker   }
2544*cf5a6c84SAndroid Build Coastguard Worker   return s0;
2545*cf5a6c84SAndroid Build Coastguard Worker }
2546*cf5a6c84SAndroid Build Coastguard Worker 
force_maybemap_to_scalar(struct zvalue * v)2547*cf5a6c84SAndroid Build Coastguard Worker static void force_maybemap_to_scalar(struct zvalue *v)
2548*cf5a6c84SAndroid Build Coastguard Worker {
2549*cf5a6c84SAndroid Build Coastguard Worker   if (!(v->flags & ZF_ANYMAP)) return;
2550*cf5a6c84SAndroid Build Coastguard Worker   if (v->flags & ZF_MAP || v->map->count)
2551*cf5a6c84SAndroid Build Coastguard Worker     FATAL("array in scalar context");
2552*cf5a6c84SAndroid Build Coastguard Worker   v->flags = 0;
2553*cf5a6c84SAndroid Build Coastguard Worker   v->map = 0; // v->flags = v->map = 0 gets warning
2554*cf5a6c84SAndroid Build Coastguard Worker }
2555*cf5a6c84SAndroid Build Coastguard Worker 
force_maybemap_to_map(struct zvalue * v)2556*cf5a6c84SAndroid Build Coastguard Worker static void force_maybemap_to_map(struct zvalue *v)
2557*cf5a6c84SAndroid Build Coastguard Worker {
2558*cf5a6c84SAndroid Build Coastguard Worker   if (v->flags & ZF_MAYBEMAP) v->flags = ZF_MAP;
2559*cf5a6c84SAndroid Build Coastguard Worker }
2560*cf5a6c84SAndroid Build Coastguard Worker 
2561*cf5a6c84SAndroid Build Coastguard Worker // fmt_offs is either CONVFMT or OFMT (offset in stack to zvalue)
to_str_fmt(struct zvalue * v,int fmt_offs)2562*cf5a6c84SAndroid Build Coastguard Worker static struct zvalue *to_str_fmt(struct zvalue *v, int fmt_offs)
2563*cf5a6c84SAndroid Build Coastguard Worker {
2564*cf5a6c84SAndroid Build Coastguard Worker   force_maybemap_to_scalar(v);
2565*cf5a6c84SAndroid Build Coastguard Worker   // TODO: consider handling numstring differently
2566*cf5a6c84SAndroid Build Coastguard Worker   if (v->flags & ZF_NUMSTR) v->flags = ZF_STR;
2567*cf5a6c84SAndroid Build Coastguard Worker   if (IS_STR(v)) return v;
2568*cf5a6c84SAndroid Build Coastguard Worker   else if (!v->flags) { // uninitialized
2569*cf5a6c84SAndroid Build Coastguard Worker     v->vst = new_zstring("", 0);
2570*cf5a6c84SAndroid Build Coastguard Worker   } else if (IS_NUM(v)) {
2571*cf5a6c84SAndroid Build Coastguard Worker     zvalue_release_zstring(v);
2572*cf5a6c84SAndroid Build Coastguard Worker     if (!IS_STR(&STACK[fmt_offs])) {
2573*cf5a6c84SAndroid Build Coastguard Worker       zstring_release(&STACK[fmt_offs].vst);
2574*cf5a6c84SAndroid Build Coastguard Worker       STACK[fmt_offs].vst = num_to_zstring(STACK[fmt_offs].num, "%.6g");
2575*cf5a6c84SAndroid Build Coastguard Worker       STACK[fmt_offs].flags = ZF_STR;
2576*cf5a6c84SAndroid Build Coastguard Worker     }
2577*cf5a6c84SAndroid Build Coastguard Worker     v->vst = num_to_zstring(v->num, STACK[fmt_offs].vst->str);
2578*cf5a6c84SAndroid Build Coastguard Worker   } else {
2579*cf5a6c84SAndroid Build Coastguard Worker     FATAL("Wrong or unknown type in to_str_fmt\n");
2580*cf5a6c84SAndroid Build Coastguard Worker   }
2581*cf5a6c84SAndroid Build Coastguard Worker   v->flags = ZF_STR;
2582*cf5a6c84SAndroid Build Coastguard Worker   return v;
2583*cf5a6c84SAndroid Build Coastguard Worker }
2584*cf5a6c84SAndroid Build Coastguard Worker 
to_str(struct zvalue * v)2585*cf5a6c84SAndroid Build Coastguard Worker static struct zvalue *to_str(struct zvalue *v)
2586*cf5a6c84SAndroid Build Coastguard Worker {
2587*cf5a6c84SAndroid Build Coastguard Worker   return to_str_fmt(v, CONVFMT);
2588*cf5a6c84SAndroid Build Coastguard Worker }
2589*cf5a6c84SAndroid Build Coastguard Worker 
2590*cf5a6c84SAndroid Build Coastguard Worker // TODO FIXME Is this needed? (YES -- investigate) Just use to_str()?
2591*cf5a6c84SAndroid Build Coastguard Worker #define ENSURE_STR(v) (IS_STR(v) ? (v) : to_str(v))
2592*cf5a6c84SAndroid Build Coastguard Worker 
rx_zvalue_compile(regex_t ** rx,struct zvalue * pat)2593*cf5a6c84SAndroid Build Coastguard Worker static void rx_zvalue_compile(regex_t **rx, struct zvalue *pat)
2594*cf5a6c84SAndroid Build Coastguard Worker {
2595*cf5a6c84SAndroid Build Coastguard Worker   if (IS_RX(pat)) *rx = pat->rx;
2596*cf5a6c84SAndroid Build Coastguard Worker   else {
2597*cf5a6c84SAndroid Build Coastguard Worker     zvalue_dup_zstring(to_str(pat));
2598*cf5a6c84SAndroid Build Coastguard Worker     escape_str(pat->vst->str, 1);
2599*cf5a6c84SAndroid Build Coastguard Worker     xregcomp(*rx, pat->vst->str, REG_EXTENDED);
2600*cf5a6c84SAndroid Build Coastguard Worker   }
2601*cf5a6c84SAndroid Build Coastguard Worker }
2602*cf5a6c84SAndroid Build Coastguard Worker 
rx_zvalue_free(regex_t * rx,struct zvalue * pat)2603*cf5a6c84SAndroid Build Coastguard Worker static void rx_zvalue_free(regex_t *rx, struct zvalue *pat)
2604*cf5a6c84SAndroid Build Coastguard Worker {
2605*cf5a6c84SAndroid Build Coastguard Worker   if (!IS_RX(pat) || rx != pat->rx) regfree(rx);
2606*cf5a6c84SAndroid Build Coastguard Worker }
2607*cf5a6c84SAndroid Build Coastguard Worker 
2608*cf5a6c84SAndroid Build Coastguard Worker // Used by the match/not match ops (~ !~) and implicit $0 match (/regex/)
match(struct zvalue * zvsubject,struct zvalue * zvpat)2609*cf5a6c84SAndroid Build Coastguard Worker static int match(struct zvalue *zvsubject, struct zvalue *zvpat)
2610*cf5a6c84SAndroid Build Coastguard Worker {
2611*cf5a6c84SAndroid Build Coastguard Worker   int r;
2612*cf5a6c84SAndroid Build Coastguard Worker   regex_t rx, *rxp = &rx;
2613*cf5a6c84SAndroid Build Coastguard Worker   rx_zvalue_compile(&rxp, zvpat);
2614*cf5a6c84SAndroid Build Coastguard Worker   if ((r = regexec(rxp, to_str(zvsubject)->vst->str, 0, 0, 0)) != 0) {
2615*cf5a6c84SAndroid Build Coastguard Worker     if (r != REG_NOMATCH) {
2616*cf5a6c84SAndroid Build Coastguard Worker       char errbuf[256];
2617*cf5a6c84SAndroid Build Coastguard Worker       regerror(r, &rx, errbuf, sizeof(errbuf));
2618*cf5a6c84SAndroid Build Coastguard Worker       // FIXME TODO better diagnostic here
2619*cf5a6c84SAndroid Build Coastguard Worker       error_exit("regex match error %d: %s", r, errbuf);
2620*cf5a6c84SAndroid Build Coastguard Worker     }
2621*cf5a6c84SAndroid Build Coastguard Worker     rx_zvalue_free(rxp, zvpat);
2622*cf5a6c84SAndroid Build Coastguard Worker     return 1;
2623*cf5a6c84SAndroid Build Coastguard Worker   }
2624*cf5a6c84SAndroid Build Coastguard Worker   rx_zvalue_free(rxp, zvpat);
2625*cf5a6c84SAndroid Build Coastguard Worker   return 0;
2626*cf5a6c84SAndroid Build Coastguard Worker }
2627*cf5a6c84SAndroid Build Coastguard Worker 
rx_find(regex_t * rx,char * s,regoff_t * start,regoff_t * end,int eflags)2628*cf5a6c84SAndroid Build Coastguard Worker static int rx_find(regex_t *rx, char *s, regoff_t *start, regoff_t *end, int eflags)
2629*cf5a6c84SAndroid Build Coastguard Worker {
2630*cf5a6c84SAndroid Build Coastguard Worker   regmatch_t matches[1];
2631*cf5a6c84SAndroid Build Coastguard Worker   int r = regexec(rx, s, 1, matches, eflags);
2632*cf5a6c84SAndroid Build Coastguard Worker   if (r == REG_NOMATCH) return r;
2633*cf5a6c84SAndroid Build Coastguard Worker   if (r) FATAL("regexec error");  // TODO ? use regerr() to meaningful msg
2634*cf5a6c84SAndroid Build Coastguard Worker   *start = matches[0].rm_so;
2635*cf5a6c84SAndroid Build Coastguard Worker   *end = matches[0].rm_eo;
2636*cf5a6c84SAndroid Build Coastguard Worker   return 0;
2637*cf5a6c84SAndroid Build Coastguard Worker }
2638*cf5a6c84SAndroid Build Coastguard Worker 
2639*cf5a6c84SAndroid Build Coastguard Worker // Differs from rx_find() in that FS cannot match null (empty) string.
2640*cf5a6c84SAndroid Build Coastguard Worker // See https://www.austingroupbugs.net/view.php?id=1468.
rx_find_FS(regex_t * rx,char * s,regoff_t * start,regoff_t * end,int eflags)2641*cf5a6c84SAndroid Build Coastguard Worker static int rx_find_FS(regex_t *rx, char *s, regoff_t *start, regoff_t *end, int eflags)
2642*cf5a6c84SAndroid Build Coastguard Worker {
2643*cf5a6c84SAndroid Build Coastguard Worker   int r = rx_find(rx, s, start, end, eflags);
2644*cf5a6c84SAndroid Build Coastguard Worker   if (r || *start != *end) return r;  // not found, or found non-empty match
2645*cf5a6c84SAndroid Build Coastguard Worker   // Found empty match, retry starting past the match
2646*cf5a6c84SAndroid Build Coastguard Worker   char *p = s + *end;
2647*cf5a6c84SAndroid Build Coastguard Worker   if (!*p) return REG_NOMATCH;  // End of string, no non-empty match found
2648*cf5a6c84SAndroid Build Coastguard Worker   // Empty match not at EOS, move ahead and try again
2649*cf5a6c84SAndroid Build Coastguard Worker   while (!r && *start == *end && *++p)
2650*cf5a6c84SAndroid Build Coastguard Worker     r = rx_find(rx, p, start, end, eflags);
2651*cf5a6c84SAndroid Build Coastguard Worker   if (r || !*p) return REG_NOMATCH;  // no non-empty match found
2652*cf5a6c84SAndroid Build Coastguard Worker   *start += p - s;  // offsets from original string
2653*cf5a6c84SAndroid Build Coastguard Worker   *end += p - s;
2654*cf5a6c84SAndroid Build Coastguard Worker   return 0;
2655*cf5a6c84SAndroid Build Coastguard Worker }
2656*cf5a6c84SAndroid Build Coastguard Worker 
2657*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
2658*cf5a6c84SAndroid Build Coastguard Worker ////   fields
2659*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
2660*cf5a6c84SAndroid Build Coastguard Worker 
2661*cf5a6c84SAndroid Build Coastguard Worker #define FIELDS_MAX  102400 // Was 1024; need more for toybox awk test
2662*cf5a6c84SAndroid Build Coastguard Worker #define THIS_MEANS_SET_NF 999999999
2663*cf5a6c84SAndroid Build Coastguard Worker 
get_int_val(struct zvalue * v)2664*cf5a6c84SAndroid Build Coastguard Worker static int get_int_val(struct zvalue *v)
2665*cf5a6c84SAndroid Build Coastguard Worker {
2666*cf5a6c84SAndroid Build Coastguard Worker   if (IS_NUM(v)) return (int)v->num;
2667*cf5a6c84SAndroid Build Coastguard Worker   if (IS_STR(v) && v->vst) return (int)atof(v->vst->str);
2668*cf5a6c84SAndroid Build Coastguard Worker   return 0;
2669*cf5a6c84SAndroid Build Coastguard Worker }
2670*cf5a6c84SAndroid Build Coastguard Worker 
2671*cf5a6c84SAndroid Build Coastguard Worker // A single-char FS is never a regex, so make it a [<char>] regex to
2672*cf5a6c84SAndroid Build Coastguard Worker // match only that one char in case FS is a regex metachar.
2673*cf5a6c84SAndroid Build Coastguard Worker // If regex FS is needed, must use > 1 char. If a '.' regex
2674*cf5a6c84SAndroid Build Coastguard Worker // is needed, use e.g. '.|.' (unlikely case).
fmt_one_char_fs(char * fs)2675*cf5a6c84SAndroid Build Coastguard Worker static char *fmt_one_char_fs(char *fs)
2676*cf5a6c84SAndroid Build Coastguard Worker {
2677*cf5a6c84SAndroid Build Coastguard Worker   if (strlen(fs) != 1) return fs;
2678*cf5a6c84SAndroid Build Coastguard Worker   snprintf(TT.one_char_fs, sizeof(TT.one_char_fs), "[%c]", fs[0]);
2679*cf5a6c84SAndroid Build Coastguard Worker   return TT.one_char_fs;
2680*cf5a6c84SAndroid Build Coastguard Worker }
2681*cf5a6c84SAndroid Build Coastguard Worker 
rx_fs_prep(char * fs)2682*cf5a6c84SAndroid Build Coastguard Worker static regex_t *rx_fs_prep(char *fs)
2683*cf5a6c84SAndroid Build Coastguard Worker {
2684*cf5a6c84SAndroid Build Coastguard Worker   if (!strcmp(fs, " ")) return &TT.rx_default;
2685*cf5a6c84SAndroid Build Coastguard Worker   if (!strcmp(fs, TT.fs_last)) return &TT.rx_last;
2686*cf5a6c84SAndroid Build Coastguard Worker   if (strlen(fs) >= FS_MAX) FATAL("FS too long");
2687*cf5a6c84SAndroid Build Coastguard Worker   strcpy(TT.fs_last, fs);
2688*cf5a6c84SAndroid Build Coastguard Worker   regfree(&TT.rx_last);
2689*cf5a6c84SAndroid Build Coastguard Worker   xregcomp(&TT.rx_last, fmt_one_char_fs(fs), REG_EXTENDED);
2690*cf5a6c84SAndroid Build Coastguard Worker   return &TT.rx_last;
2691*cf5a6c84SAndroid Build Coastguard Worker }
2692*cf5a6c84SAndroid Build Coastguard Worker 
2693*cf5a6c84SAndroid Build Coastguard Worker // Only for use by split() builtin
set_map_element(struct zmap * m,int k,char * val,size_t len)2694*cf5a6c84SAndroid Build Coastguard Worker static void set_map_element(struct zmap *m, int k, char *val, size_t len)
2695*cf5a6c84SAndroid Build Coastguard Worker {
2696*cf5a6c84SAndroid Build Coastguard Worker   // Do not need format here b/c k is integer, uses "%lld" format.
2697*cf5a6c84SAndroid Build Coastguard Worker   struct zstring *key = num_to_zstring(k, "");// "" vs 0 format avoids warning
2698*cf5a6c84SAndroid Build Coastguard Worker   struct zmap_slot *zs = zmap_find_or_insert_key(m, key);
2699*cf5a6c84SAndroid Build Coastguard Worker   zstring_release(&key);
2700*cf5a6c84SAndroid Build Coastguard Worker   zs->val.vst = zstring_update(zs->val.vst, 0, val, len);
2701*cf5a6c84SAndroid Build Coastguard Worker   zs->val.flags = ZF_STR;
2702*cf5a6c84SAndroid Build Coastguard Worker   check_numeric_string(&zs->val);
2703*cf5a6c84SAndroid Build Coastguard Worker }
2704*cf5a6c84SAndroid Build Coastguard Worker 
set_zvalue_str(struct zvalue * v,char * s,size_t size)2705*cf5a6c84SAndroid Build Coastguard Worker static void set_zvalue_str(struct zvalue *v, char *s, size_t size)
2706*cf5a6c84SAndroid Build Coastguard Worker {
2707*cf5a6c84SAndroid Build Coastguard Worker   v->vst = zstring_update(v->vst, 0, s, size);
2708*cf5a6c84SAndroid Build Coastguard Worker   v->flags = ZF_STR;
2709*cf5a6c84SAndroid Build Coastguard Worker }
2710*cf5a6c84SAndroid Build Coastguard Worker 
2711*cf5a6c84SAndroid Build Coastguard Worker // All changes to NF go through here!
set_nf(int nf)2712*cf5a6c84SAndroid Build Coastguard Worker static void set_nf(int nf)
2713*cf5a6c84SAndroid Build Coastguard Worker {
2714*cf5a6c84SAndroid Build Coastguard Worker   if (nf < 0) FATAL("NF set negative");
2715*cf5a6c84SAndroid Build Coastguard Worker   STACK[NF].num = TT.nf_internal = nf;
2716*cf5a6c84SAndroid Build Coastguard Worker   STACK[NF].flags = ZF_NUM;
2717*cf5a6c84SAndroid Build Coastguard Worker }
2718*cf5a6c84SAndroid Build Coastguard Worker 
set_field(struct zmap * unused,int fnum,char * s,size_t size)2719*cf5a6c84SAndroid Build Coastguard Worker static void set_field(struct zmap *unused, int fnum, char *s, size_t size)
2720*cf5a6c84SAndroid Build Coastguard Worker { (void)unused;
2721*cf5a6c84SAndroid Build Coastguard Worker   if (fnum < 0 || fnum > FIELDS_MAX) FFATAL("bad field num %d\n", fnum);
2722*cf5a6c84SAndroid Build Coastguard Worker   int nfields = zlist_len(&TT.fields);
2723*cf5a6c84SAndroid Build Coastguard Worker   // Need nfields to be > fnum b/c e.g. fnum==1 implies 2 TT.fields
2724*cf5a6c84SAndroid Build Coastguard Worker   while (nfields <= fnum)
2725*cf5a6c84SAndroid Build Coastguard Worker     nfields = zlist_append(&TT.fields, &uninit_zvalue) + 1;
2726*cf5a6c84SAndroid Build Coastguard Worker   set_zvalue_str(&FIELD[fnum], s, size);
2727*cf5a6c84SAndroid Build Coastguard Worker   set_nf(fnum);
2728*cf5a6c84SAndroid Build Coastguard Worker   check_numeric_string(&FIELD[fnum]);
2729*cf5a6c84SAndroid Build Coastguard Worker }
2730*cf5a6c84SAndroid Build Coastguard Worker 
2731*cf5a6c84SAndroid Build Coastguard Worker // Split s via fs, using setter; return number of TT.fields.
2732*cf5a6c84SAndroid Build Coastguard Worker // This is used to split TT.fields and also for split() builtin.
splitter(void (* setter)(struct zmap *,int,char *,size_t),struct zmap * m,char * s,struct zvalue * zvfs)2733*cf5a6c84SAndroid Build Coastguard Worker static int splitter(void (*setter)(struct zmap *, int, char *, size_t), struct zmap *m, char *s, struct zvalue *zvfs)
2734*cf5a6c84SAndroid Build Coastguard Worker {
2735*cf5a6c84SAndroid Build Coastguard Worker   regex_t *rx;
2736*cf5a6c84SAndroid Build Coastguard Worker   regoff_t offs, end;
2737*cf5a6c84SAndroid Build Coastguard Worker   int multiline_null_rs = !ENSURE_STR(&STACK[RS])->vst->str[0];
2738*cf5a6c84SAndroid Build Coastguard Worker   int nf = 0, r = 0, eflag = 0;
2739*cf5a6c84SAndroid Build Coastguard Worker   int one_char_fs = 0;
2740*cf5a6c84SAndroid Build Coastguard Worker   char *s0 = s, *fs = "";
2741*cf5a6c84SAndroid Build Coastguard Worker   if (!IS_RX(zvfs)) {
2742*cf5a6c84SAndroid Build Coastguard Worker     to_str(zvfs);
2743*cf5a6c84SAndroid Build Coastguard Worker     fs = zvfs->vst->str;
2744*cf5a6c84SAndroid Build Coastguard Worker     one_char_fs = utf8cnt(zvfs->vst->str, zvfs->vst->size) == 1;
2745*cf5a6c84SAndroid Build Coastguard Worker   }
2746*cf5a6c84SAndroid Build Coastguard Worker   // Empty string or empty fs (regex).
2747*cf5a6c84SAndroid Build Coastguard Worker   // Need to include !*s b/c empty string, otherwise
2748*cf5a6c84SAndroid Build Coastguard Worker   // split("", a, "x") splits to a 1-element (empty element) array
2749*cf5a6c84SAndroid Build Coastguard Worker   if (!*s || (IS_STR(zvfs) && !*fs) || IS_EMPTY_RX(zvfs)) {
2750*cf5a6c84SAndroid Build Coastguard Worker     while (*s) {
2751*cf5a6c84SAndroid Build Coastguard Worker       if (*s < 128) setter(m, ++nf, s++, 1);
2752*cf5a6c84SAndroid Build Coastguard Worker       else {        // Handle UTF-8
2753*cf5a6c84SAndroid Build Coastguard Worker         char cbuf[8];
2754*cf5a6c84SAndroid Build Coastguard Worker         unsigned wc;
2755*cf5a6c84SAndroid Build Coastguard Worker         int nc = utf8towc(&wc, s, strlen(s));
2756*cf5a6c84SAndroid Build Coastguard Worker         if (nc < 2) FFATAL("bad string for split: \"%s\"\n", s0);
2757*cf5a6c84SAndroid Build Coastguard Worker         s += nc;
2758*cf5a6c84SAndroid Build Coastguard Worker         nc = wctoutf8(cbuf, wc);
2759*cf5a6c84SAndroid Build Coastguard Worker         setter(m, ++nf, cbuf, nc);
2760*cf5a6c84SAndroid Build Coastguard Worker       }
2761*cf5a6c84SAndroid Build Coastguard Worker     }
2762*cf5a6c84SAndroid Build Coastguard Worker     return nf;
2763*cf5a6c84SAndroid Build Coastguard Worker   }
2764*cf5a6c84SAndroid Build Coastguard Worker   if (IS_RX(zvfs)) rx = zvfs->rx;
2765*cf5a6c84SAndroid Build Coastguard Worker   else rx = rx_fs_prep(fs);
2766*cf5a6c84SAndroid Build Coastguard Worker   while (*s) {
2767*cf5a6c84SAndroid Build Coastguard Worker     // Find the next occurrence of FS.
2768*cf5a6c84SAndroid Build Coastguard Worker     // rx_find_FS() returns 0 if found. If nonzero, the field will
2769*cf5a6c84SAndroid Build Coastguard Worker     // be the rest of the record (all of it if first time through).
2770*cf5a6c84SAndroid Build Coastguard Worker     if ((r = rx_find_FS(rx, s, &offs, &end, eflag))) offs = end = strlen(s);
2771*cf5a6c84SAndroid Build Coastguard Worker     if (setter == set_field && multiline_null_rs && one_char_fs) {
2772*cf5a6c84SAndroid Build Coastguard Worker       // Contra POSIX, if RS=="" then newline is always also a
2773*cf5a6c84SAndroid Build Coastguard Worker       // field separator only if FS is a single char (see gawk manual)
2774*cf5a6c84SAndroid Build Coastguard Worker       int k = strcspn(s, "\n");
2775*cf5a6c84SAndroid Build Coastguard Worker       if (k < offs) offs = k, end = k + 1;
2776*cf5a6c84SAndroid Build Coastguard Worker     }
2777*cf5a6c84SAndroid Build Coastguard Worker     eflag |= REG_NOTBOL;
2778*cf5a6c84SAndroid Build Coastguard Worker 
2779*cf5a6c84SAndroid Build Coastguard Worker     // Field will be s up to (not including) the offset. If offset
2780*cf5a6c84SAndroid Build Coastguard Worker     // is zero and FS is found and FS is ' ' (TT.rx_default "[ \t]+"),
2781*cf5a6c84SAndroid Build Coastguard Worker     // then the find is the leading or trailing spaces and/or tabs.
2782*cf5a6c84SAndroid Build Coastguard Worker     // If so, skip this (empty) field, otherwise set field, length is offs.
2783*cf5a6c84SAndroid Build Coastguard Worker     if (offs || r || rx != &TT.rx_default) setter(m, ++nf, s, offs);
2784*cf5a6c84SAndroid Build Coastguard Worker     s += end;
2785*cf5a6c84SAndroid Build Coastguard Worker   }
2786*cf5a6c84SAndroid Build Coastguard Worker   if (!r && rx != &TT.rx_default) setter(m, ++nf, "", 0);
2787*cf5a6c84SAndroid Build Coastguard Worker   return nf;
2788*cf5a6c84SAndroid Build Coastguard Worker }
2789*cf5a6c84SAndroid Build Coastguard Worker 
build_fields(void)2790*cf5a6c84SAndroid Build Coastguard Worker static void build_fields(void)
2791*cf5a6c84SAndroid Build Coastguard Worker {
2792*cf5a6c84SAndroid Build Coastguard Worker   char *rec = FIELD[0].vst->str;
2793*cf5a6c84SAndroid Build Coastguard Worker   // TODO test this -- why did I not want to split empty $0?
2794*cf5a6c84SAndroid Build Coastguard Worker   // Maybe don't split empty $0 b/c non-default FS gets NF==1 with splitter()?
2795*cf5a6c84SAndroid Build Coastguard Worker   set_nf(*rec ? splitter(set_field, 0, rec, to_str(&STACK[FS])) : 0);
2796*cf5a6c84SAndroid Build Coastguard Worker }
2797*cf5a6c84SAndroid Build Coastguard Worker 
rebuild_field0(void)2798*cf5a6c84SAndroid Build Coastguard Worker static void rebuild_field0(void)
2799*cf5a6c84SAndroid Build Coastguard Worker {
2800*cf5a6c84SAndroid Build Coastguard Worker   struct zstring *s = FIELD[0].vst;
2801*cf5a6c84SAndroid Build Coastguard Worker   int nf = TT.nf_internal;
2802*cf5a6c84SAndroid Build Coastguard Worker   if (!nf) {
2803*cf5a6c84SAndroid Build Coastguard Worker     zvalue_copy(&FIELD[0], &uninit_string_zvalue);
2804*cf5a6c84SAndroid Build Coastguard Worker     return;
2805*cf5a6c84SAndroid Build Coastguard Worker   }
2806*cf5a6c84SAndroid Build Coastguard Worker   // uninit value needed for eventual reference to .vst in zstring_release()
2807*cf5a6c84SAndroid Build Coastguard Worker   struct zvalue tempv = uninit_zvalue;
2808*cf5a6c84SAndroid Build Coastguard Worker   zvalue_copy(&tempv, to_str(&STACK[OFS]));
2809*cf5a6c84SAndroid Build Coastguard Worker   for (int i = 1; i <= nf; i++) {
2810*cf5a6c84SAndroid Build Coastguard Worker     if (i > 1) {
2811*cf5a6c84SAndroid Build Coastguard Worker       s = s ? zstring_extend(s, tempv.vst) : zstring_copy(s, tempv.vst);
2812*cf5a6c84SAndroid Build Coastguard Worker     }
2813*cf5a6c84SAndroid Build Coastguard Worker     if (FIELD[i].flags) to_str(&FIELD[i]);
2814*cf5a6c84SAndroid Build Coastguard Worker     if (FIELD[i].vst) {
2815*cf5a6c84SAndroid Build Coastguard Worker       if (i > 1) s = zstring_extend(s, FIELD[i].vst);
2816*cf5a6c84SAndroid Build Coastguard Worker       else s = zstring_copy(s, FIELD[i].vst);
2817*cf5a6c84SAndroid Build Coastguard Worker     }
2818*cf5a6c84SAndroid Build Coastguard Worker   }
2819*cf5a6c84SAndroid Build Coastguard Worker   FIELD[0].vst = s;
2820*cf5a6c84SAndroid Build Coastguard Worker   FIELD[0].flags |= ZF_STR;
2821*cf5a6c84SAndroid Build Coastguard Worker   zvalue_release_zstring(&tempv);
2822*cf5a6c84SAndroid Build Coastguard Worker }
2823*cf5a6c84SAndroid Build Coastguard Worker 
2824*cf5a6c84SAndroid Build Coastguard Worker // get field ref (lvalue ref) in prep for assignment to field.
2825*cf5a6c84SAndroid Build Coastguard Worker // [... assigning to a nonexistent field (for example, $(NF+2)=5) shall
2826*cf5a6c84SAndroid Build Coastguard Worker // increase the value of NF; create any intervening TT.fields with the
2827*cf5a6c84SAndroid Build Coastguard Worker // uninitialized value; and cause the value of $0 to be recomputed, with the
2828*cf5a6c84SAndroid Build Coastguard Worker // TT.fields being separated by the value of OFS.]
2829*cf5a6c84SAndroid Build Coastguard Worker // Called by setup_lvalue()
get_field_ref(int fnum)2830*cf5a6c84SAndroid Build Coastguard Worker static struct zvalue *get_field_ref(int fnum)
2831*cf5a6c84SAndroid Build Coastguard Worker {
2832*cf5a6c84SAndroid Build Coastguard Worker   if (fnum < 0 || fnum > FIELDS_MAX) error_exit("bad field num %d", fnum);
2833*cf5a6c84SAndroid Build Coastguard Worker   if (fnum > TT.nf_internal) {
2834*cf5a6c84SAndroid Build Coastguard Worker     // Ensure TT.fields list is large enough for fnum
2835*cf5a6c84SAndroid Build Coastguard Worker     // Need len of TT.fields to be > fnum b/c e.g. fnum==1 implies 2 TT.fields
2836*cf5a6c84SAndroid Build Coastguard Worker     for (int i = TT.nf_internal + 1; i <= fnum; i++) {
2837*cf5a6c84SAndroid Build Coastguard Worker       if (i == zlist_len(&TT.fields)) zlist_append(&TT.fields, &uninit_zvalue);
2838*cf5a6c84SAndroid Build Coastguard Worker       zvalue_copy(&FIELD[i], &uninit_string_zvalue);
2839*cf5a6c84SAndroid Build Coastguard Worker     }
2840*cf5a6c84SAndroid Build Coastguard Worker     set_nf(fnum);
2841*cf5a6c84SAndroid Build Coastguard Worker   }
2842*cf5a6c84SAndroid Build Coastguard Worker   return &FIELD[fnum];
2843*cf5a6c84SAndroid Build Coastguard Worker }
2844*cf5a6c84SAndroid Build Coastguard Worker 
2845*cf5a6c84SAndroid Build Coastguard Worker // Called by tksplit op
split(struct zstring * s,struct zvalue * a,struct zvalue * fs)2846*cf5a6c84SAndroid Build Coastguard Worker static int split(struct zstring *s, struct zvalue *a, struct zvalue *fs)
2847*cf5a6c84SAndroid Build Coastguard Worker {
2848*cf5a6c84SAndroid Build Coastguard Worker   return splitter(set_map_element, a->map, s->str, fs);
2849*cf5a6c84SAndroid Build Coastguard Worker }
2850*cf5a6c84SAndroid Build Coastguard Worker 
2851*cf5a6c84SAndroid Build Coastguard Worker // Called by getrec_f0_f() and getrec_f0()
copy_to_field0(char * buf,size_t k)2852*cf5a6c84SAndroid Build Coastguard Worker static void copy_to_field0(char *buf, size_t k)
2853*cf5a6c84SAndroid Build Coastguard Worker {
2854*cf5a6c84SAndroid Build Coastguard Worker   set_zvalue_str(&FIELD[0], buf, k);
2855*cf5a6c84SAndroid Build Coastguard Worker   check_numeric_string(&FIELD[0]);
2856*cf5a6c84SAndroid Build Coastguard Worker   build_fields();
2857*cf5a6c84SAndroid Build Coastguard Worker }
2858*cf5a6c84SAndroid Build Coastguard Worker 
2859*cf5a6c84SAndroid Build Coastguard Worker // After changing $0, must rebuild TT.fields & reset NF
2860*cf5a6c84SAndroid Build Coastguard Worker // Changing other field must rebuild $0
2861*cf5a6c84SAndroid Build Coastguard Worker // Called by gsub() and assignment ops.
fixup_fields(int fnum)2862*cf5a6c84SAndroid Build Coastguard Worker static void fixup_fields(int fnum)
2863*cf5a6c84SAndroid Build Coastguard Worker {
2864*cf5a6c84SAndroid Build Coastguard Worker   if (fnum == THIS_MEANS_SET_NF) {  // NF was assigned to
2865*cf5a6c84SAndroid Build Coastguard Worker     int new_nf = get_int_val(&STACK[NF]);
2866*cf5a6c84SAndroid Build Coastguard Worker     // Ensure TT.fields list is large enough for fnum
2867*cf5a6c84SAndroid Build Coastguard Worker     // Need len of TT.fields to be > fnum b/c e.g. fnum==1 implies 2 TT.fields
2868*cf5a6c84SAndroid Build Coastguard Worker     for (int i = TT.nf_internal + 1; i <= new_nf; i++) {
2869*cf5a6c84SAndroid Build Coastguard Worker       if (i == zlist_len(&TT.fields)) zlist_append(&TT.fields, &uninit_zvalue);
2870*cf5a6c84SAndroid Build Coastguard Worker       zvalue_copy(&FIELD[i], &uninit_string_zvalue);
2871*cf5a6c84SAndroid Build Coastguard Worker     }
2872*cf5a6c84SAndroid Build Coastguard Worker     set_nf(TT.nf_internal = STACK[NF].num);
2873*cf5a6c84SAndroid Build Coastguard Worker     rebuild_field0();
2874*cf5a6c84SAndroid Build Coastguard Worker     return;
2875*cf5a6c84SAndroid Build Coastguard Worker   }
2876*cf5a6c84SAndroid Build Coastguard Worker   // fnum is # of field that was just updated.
2877*cf5a6c84SAndroid Build Coastguard Worker   // If it's 0, need to rebuild the TT.fields 1... n.
2878*cf5a6c84SAndroid Build Coastguard Worker   // If it's non-0, need to rebuild field 0.
2879*cf5a6c84SAndroid Build Coastguard Worker   to_str(&FIELD[fnum]);
2880*cf5a6c84SAndroid Build Coastguard Worker   if (fnum) check_numeric_string(&FIELD[fnum]);
2881*cf5a6c84SAndroid Build Coastguard Worker   if (fnum) rebuild_field0();
2882*cf5a6c84SAndroid Build Coastguard Worker   else build_fields();
2883*cf5a6c84SAndroid Build Coastguard Worker }
2884*cf5a6c84SAndroid Build Coastguard Worker 
2885*cf5a6c84SAndroid Build Coastguard Worker // Fetching non-existent field gets uninit string value; no change to NF!
2886*cf5a6c84SAndroid Build Coastguard Worker // Called by tkfield op       // TODO inline it?
push_field(int fnum)2887*cf5a6c84SAndroid Build Coastguard Worker static void push_field(int fnum)
2888*cf5a6c84SAndroid Build Coastguard Worker {
2889*cf5a6c84SAndroid Build Coastguard Worker   if (fnum < 0 || fnum > FIELDS_MAX) error_exit("bad field num %d", fnum);
2890*cf5a6c84SAndroid Build Coastguard Worker   // Contrary to posix, awk evaluates TT.fields beyond $NF as empty strings.
2891*cf5a6c84SAndroid Build Coastguard Worker   if (fnum > TT.nf_internal) push_val(&uninit_string_zvalue);
2892*cf5a6c84SAndroid Build Coastguard Worker   else push_val(&FIELD[fnum]);
2893*cf5a6c84SAndroid Build Coastguard Worker }
2894*cf5a6c84SAndroid Build Coastguard Worker 
2895*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
2896*cf5a6c84SAndroid Build Coastguard Worker ////   END fields
2897*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
2898*cf5a6c84SAndroid Build Coastguard Worker 
2899*cf5a6c84SAndroid Build Coastguard Worker #define STKP    TT.stackp   // pointer to top of stack
2900*cf5a6c84SAndroid Build Coastguard Worker 
seedrand(double seed)2901*cf5a6c84SAndroid Build Coastguard Worker static double seedrand(double seed)
2902*cf5a6c84SAndroid Build Coastguard Worker {
2903*cf5a6c84SAndroid Build Coastguard Worker   static double prev_seed;
2904*cf5a6c84SAndroid Build Coastguard Worker   double r = prev_seed;
2905*cf5a6c84SAndroid Build Coastguard Worker   srandom(trunc(prev_seed = seed));
2906*cf5a6c84SAndroid Build Coastguard Worker   return r;
2907*cf5a6c84SAndroid Build Coastguard Worker }
2908*cf5a6c84SAndroid Build Coastguard Worker 
popnumval(void)2909*cf5a6c84SAndroid Build Coastguard Worker static int popnumval(void)
2910*cf5a6c84SAndroid Build Coastguard Worker {
2911*cf5a6c84SAndroid Build Coastguard Worker   return STKP-- -> num;
2912*cf5a6c84SAndroid Build Coastguard Worker }
2913*cf5a6c84SAndroid Build Coastguard Worker 
drop(void)2914*cf5a6c84SAndroid Build Coastguard Worker static void drop(void)
2915*cf5a6c84SAndroid Build Coastguard Worker {
2916*cf5a6c84SAndroid Build Coastguard Worker   if (!(STKP->flags & (ZF_ANYMAP | ZF_RX))) zstring_release(&STKP->vst);
2917*cf5a6c84SAndroid Build Coastguard Worker   STKP--;
2918*cf5a6c84SAndroid Build Coastguard Worker }
2919*cf5a6c84SAndroid Build Coastguard Worker 
drop_n(int n)2920*cf5a6c84SAndroid Build Coastguard Worker static void drop_n(int n)
2921*cf5a6c84SAndroid Build Coastguard Worker {
2922*cf5a6c84SAndroid Build Coastguard Worker   while (n--) drop();
2923*cf5a6c84SAndroid Build Coastguard Worker }
2924*cf5a6c84SAndroid Build Coastguard Worker 
swap(void)2925*cf5a6c84SAndroid Build Coastguard Worker static void swap(void)
2926*cf5a6c84SAndroid Build Coastguard Worker {
2927*cf5a6c84SAndroid Build Coastguard Worker   struct zvalue tmp = STKP[-1];
2928*cf5a6c84SAndroid Build Coastguard Worker   STKP[-1] = STKP[0];
2929*cf5a6c84SAndroid Build Coastguard Worker   STKP[0] = tmp;
2930*cf5a6c84SAndroid Build Coastguard Worker }
2931*cf5a6c84SAndroid Build Coastguard Worker 
2932*cf5a6c84SAndroid Build Coastguard Worker // Set and return logical (0/1) val of top TT.stack value; flag value as NUM.
get_set_logical(void)2933*cf5a6c84SAndroid Build Coastguard Worker static int get_set_logical(void)
2934*cf5a6c84SAndroid Build Coastguard Worker {
2935*cf5a6c84SAndroid Build Coastguard Worker   struct zvalue *v = STKP;
2936*cf5a6c84SAndroid Build Coastguard Worker   force_maybemap_to_scalar(v);
2937*cf5a6c84SAndroid Build Coastguard Worker   int r = 0;
2938*cf5a6c84SAndroid Build Coastguard Worker   if (IS_NUM(v)) r = !! v->num;
2939*cf5a6c84SAndroid Build Coastguard Worker   else if (IS_STR(v)) r = (v->vst && v->vst->str[0]);
2940*cf5a6c84SAndroid Build Coastguard Worker   zvalue_release_zstring(v);
2941*cf5a6c84SAndroid Build Coastguard Worker   v->num = r;
2942*cf5a6c84SAndroid Build Coastguard Worker   v->flags = ZF_NUM;
2943*cf5a6c84SAndroid Build Coastguard Worker   return r;
2944*cf5a6c84SAndroid Build Coastguard Worker }
2945*cf5a6c84SAndroid Build Coastguard Worker 
2946*cf5a6c84SAndroid Build Coastguard Worker 
to_num(struct zvalue * v)2947*cf5a6c84SAndroid Build Coastguard Worker static double to_num(struct zvalue *v)
2948*cf5a6c84SAndroid Build Coastguard Worker {
2949*cf5a6c84SAndroid Build Coastguard Worker   force_maybemap_to_scalar(v);
2950*cf5a6c84SAndroid Build Coastguard Worker   if (v->flags & ZF_NUMSTR) zvalue_release_zstring(v);
2951*cf5a6c84SAndroid Build Coastguard Worker   else if (!IS_NUM(v)) {
2952*cf5a6c84SAndroid Build Coastguard Worker     v->num = 0.0;
2953*cf5a6c84SAndroid Build Coastguard Worker     if (IS_STR(v) && v->vst) v->num = atof(v->vst->str);
2954*cf5a6c84SAndroid Build Coastguard Worker     zvalue_release_zstring(v);
2955*cf5a6c84SAndroid Build Coastguard Worker   }
2956*cf5a6c84SAndroid Build Coastguard Worker   v->flags = ZF_NUM;
2957*cf5a6c84SAndroid Build Coastguard Worker   return v->num;
2958*cf5a6c84SAndroid Build Coastguard Worker }
2959*cf5a6c84SAndroid Build Coastguard Worker 
set_num(struct zvalue * v,double n)2960*cf5a6c84SAndroid Build Coastguard Worker static void set_num(struct zvalue *v, double n)
2961*cf5a6c84SAndroid Build Coastguard Worker {
2962*cf5a6c84SAndroid Build Coastguard Worker   zstring_release(&v->vst);
2963*cf5a6c84SAndroid Build Coastguard Worker   v->num = n;
2964*cf5a6c84SAndroid Build Coastguard Worker   v->flags = ZF_NUM;
2965*cf5a6c84SAndroid Build Coastguard Worker }
2966*cf5a6c84SAndroid Build Coastguard Worker 
incr_zvalue(struct zvalue * v)2967*cf5a6c84SAndroid Build Coastguard Worker static void incr_zvalue(struct zvalue *v)
2968*cf5a6c84SAndroid Build Coastguard Worker {
2969*cf5a6c84SAndroid Build Coastguard Worker   v->num = trunc(to_num(v)) + 1;
2970*cf5a6c84SAndroid Build Coastguard Worker }
2971*cf5a6c84SAndroid Build Coastguard Worker 
push_int_val(ptrdiff_t n)2972*cf5a6c84SAndroid Build Coastguard Worker static void push_int_val(ptrdiff_t n)
2973*cf5a6c84SAndroid Build Coastguard Worker {
2974*cf5a6c84SAndroid Build Coastguard Worker   struct zvalue v = ZVINIT(ZF_NUM, n, 0);
2975*cf5a6c84SAndroid Build Coastguard Worker   push_val(&v);
2976*cf5a6c84SAndroid Build Coastguard Worker }
2977*cf5a6c84SAndroid Build Coastguard Worker 
get_map_val(struct zvalue * v,struct zvalue * key)2978*cf5a6c84SAndroid Build Coastguard Worker static struct zvalue *get_map_val(struct zvalue *v, struct zvalue *key)
2979*cf5a6c84SAndroid Build Coastguard Worker {
2980*cf5a6c84SAndroid Build Coastguard Worker   struct zmap_slot *x = zmap_find_or_insert_key(v->map, to_str(key)->vst);
2981*cf5a6c84SAndroid Build Coastguard Worker   return &x->val;
2982*cf5a6c84SAndroid Build Coastguard Worker }
2983*cf5a6c84SAndroid Build Coastguard Worker 
setup_lvalue(int ref_stack_ptr,int parmbase,int * field_num)2984*cf5a6c84SAndroid Build Coastguard Worker static struct zvalue *setup_lvalue(int ref_stack_ptr, int parmbase, int *field_num)
2985*cf5a6c84SAndroid Build Coastguard Worker {
2986*cf5a6c84SAndroid Build Coastguard Worker   // ref_stack_ptr is number of slots down in stack the ref is
2987*cf5a6c84SAndroid Build Coastguard Worker   // for +=, *=, etc
2988*cf5a6c84SAndroid Build Coastguard Worker   // Stack is: ... scalar_ref value_to_op_by
2989*cf5a6c84SAndroid Build Coastguard Worker   // or ... subscript_val map_ref value_to_op_by
2990*cf5a6c84SAndroid Build Coastguard Worker   // or ... fieldref value_to_op_by
2991*cf5a6c84SAndroid Build Coastguard Worker   // for =, ++, --
2992*cf5a6c84SAndroid Build Coastguard Worker   // Stack is: ... scalar_ref
2993*cf5a6c84SAndroid Build Coastguard Worker   // or ... subscript_val map_ref
2994*cf5a6c84SAndroid Build Coastguard Worker   // or ... fieldnum fieldref
2995*cf5a6c84SAndroid Build Coastguard Worker   int k;
2996*cf5a6c84SAndroid Build Coastguard Worker   struct zvalue *ref, *v = 0; // init v to mute "may be uninit" warning
2997*cf5a6c84SAndroid Build Coastguard Worker   *field_num = -1;
2998*cf5a6c84SAndroid Build Coastguard Worker   ref = STKP - ref_stack_ptr;
2999*cf5a6c84SAndroid Build Coastguard Worker   if (ref->flags & ZF_FIELDREF) return get_field_ref(*field_num = ref->num);
3000*cf5a6c84SAndroid Build Coastguard Worker   k = ref->num >= 0 ? ref->num : parmbase - ref->num;
3001*cf5a6c84SAndroid Build Coastguard Worker   if (k == NF) *field_num = THIS_MEANS_SET_NF;
3002*cf5a6c84SAndroid Build Coastguard Worker   v = &STACK[k];
3003*cf5a6c84SAndroid Build Coastguard Worker   if (ref->flags & ZF_REF) {
3004*cf5a6c84SAndroid Build Coastguard Worker     force_maybemap_to_scalar(v);
3005*cf5a6c84SAndroid Build Coastguard Worker   } else if (ref->flags & ZF_MAPREF) {
3006*cf5a6c84SAndroid Build Coastguard Worker     force_maybemap_to_map(v);
3007*cf5a6c84SAndroid Build Coastguard Worker     if (!IS_MAP(v)) FATAL("scalar in array context");
3008*cf5a6c84SAndroid Build Coastguard Worker     v = get_map_val(v, STKP - ref_stack_ptr - 1);
3009*cf5a6c84SAndroid Build Coastguard Worker     swap();
3010*cf5a6c84SAndroid Build Coastguard Worker     drop();
3011*cf5a6c84SAndroid Build Coastguard Worker   } else FATAL("assignment to bad lvalue");
3012*cf5a6c84SAndroid Build Coastguard Worker   return v; // order FATAL() and return to mute warning
3013*cf5a6c84SAndroid Build Coastguard Worker }
3014*cf5a6c84SAndroid Build Coastguard Worker 
new_file(char * fn,FILE * fp,char mode,char file_or_pipe,char is_std_file)3015*cf5a6c84SAndroid Build Coastguard Worker static struct zfile *new_file(char *fn, FILE *fp, char mode, char file_or_pipe,
3016*cf5a6c84SAndroid Build Coastguard Worker                               char is_std_file)
3017*cf5a6c84SAndroid Build Coastguard Worker {
3018*cf5a6c84SAndroid Build Coastguard Worker   struct zfile *f = xzalloc(sizeof(struct zfile));
3019*cf5a6c84SAndroid Build Coastguard Worker   *f = (struct zfile){TT.zfiles, xstrdup(fn), fp, mode, file_or_pipe,
3020*cf5a6c84SAndroid Build Coastguard Worker                 isatty(fileno(fp)), is_std_file, 0, 0, 0, 0, 0};
3021*cf5a6c84SAndroid Build Coastguard Worker   return TT.zfiles = f;
3022*cf5a6c84SAndroid Build Coastguard Worker }
3023*cf5a6c84SAndroid Build Coastguard Worker 
fflush_all(void)3024*cf5a6c84SAndroid Build Coastguard Worker static int fflush_all(void)
3025*cf5a6c84SAndroid Build Coastguard Worker {
3026*cf5a6c84SAndroid Build Coastguard Worker   int ret = 0;
3027*cf5a6c84SAndroid Build Coastguard Worker   for (struct zfile *p = TT.zfiles; p; p = p->next)
3028*cf5a6c84SAndroid Build Coastguard Worker     if (fflush(p->fp)) ret = -1;
3029*cf5a6c84SAndroid Build Coastguard Worker   return ret;
3030*cf5a6c84SAndroid Build Coastguard Worker }
3031*cf5a6c84SAndroid Build Coastguard Worker 
fflush_file(int nargs)3032*cf5a6c84SAndroid Build Coastguard Worker static int fflush_file(int nargs)
3033*cf5a6c84SAndroid Build Coastguard Worker {
3034*cf5a6c84SAndroid Build Coastguard Worker   if (!nargs) return fflush_all();
3035*cf5a6c84SAndroid Build Coastguard Worker 
3036*cf5a6c84SAndroid Build Coastguard Worker   to_str(STKP);   // filename at top of TT.stack
3037*cf5a6c84SAndroid Build Coastguard Worker   // Null string means flush all
3038*cf5a6c84SAndroid Build Coastguard Worker   if (!STKP[0].vst->str[0]) return fflush_all();
3039*cf5a6c84SAndroid Build Coastguard Worker 
3040*cf5a6c84SAndroid Build Coastguard Worker   // is it open in file table?
3041*cf5a6c84SAndroid Build Coastguard Worker   for (struct zfile *p = TT.zfiles; p; p = p->next)
3042*cf5a6c84SAndroid Build Coastguard Worker     if (!strcmp(STKP[0].vst->str, p->fn))
3043*cf5a6c84SAndroid Build Coastguard Worker       if (!fflush(p->fp)) return 0;
3044*cf5a6c84SAndroid Build Coastguard Worker   return -1;    // error, or file not found in table
3045*cf5a6c84SAndroid Build Coastguard Worker }
close_file(char * fn)3046*cf5a6c84SAndroid Build Coastguard Worker static int close_file(char *fn)
3047*cf5a6c84SAndroid Build Coastguard Worker {
3048*cf5a6c84SAndroid Build Coastguard Worker   // !fn (null ptr) means close all (exc. stdin/stdout/stderr)
3049*cf5a6c84SAndroid Build Coastguard Worker   int r = 0;
3050*cf5a6c84SAndroid Build Coastguard Worker   struct zfile *np, **pp = &TT.zfiles;
3051*cf5a6c84SAndroid Build Coastguard Worker   for (struct zfile *p = TT.zfiles; p; p = np) {
3052*cf5a6c84SAndroid Build Coastguard Worker     np = p->next;   // save in case unlinking file (invalidates p->next)
3053*cf5a6c84SAndroid Build Coastguard Worker     // Don't close std files -- wrecks print/printf (can be fixed though TODO)
3054*cf5a6c84SAndroid Build Coastguard Worker     if ((!p->is_std_file) && (!fn || !strcmp(fn, p->fn))) {
3055*cf5a6c84SAndroid Build Coastguard Worker       xfree(p->buf);
3056*cf5a6c84SAndroid Build Coastguard Worker       xfree(p->fn);
3057*cf5a6c84SAndroid Build Coastguard Worker       r = (p->fp) ? (p->file_or_pipe ? fclose : pclose)(p->fp) : -1;
3058*cf5a6c84SAndroid Build Coastguard Worker       *pp = p->next;
3059*cf5a6c84SAndroid Build Coastguard Worker       xfree(p);
3060*cf5a6c84SAndroid Build Coastguard Worker       if (fn) return r;
3061*cf5a6c84SAndroid Build Coastguard Worker     } else pp = &p->next; // only if not unlinking zfile
3062*cf5a6c84SAndroid Build Coastguard Worker   }
3063*cf5a6c84SAndroid Build Coastguard Worker   return -1;  // file not in table, or closed all files
3064*cf5a6c84SAndroid Build Coastguard Worker }
3065*cf5a6c84SAndroid Build Coastguard Worker 
3066*cf5a6c84SAndroid Build Coastguard Worker static struct zfile badfile_obj, *badfile = &badfile_obj;
3067*cf5a6c84SAndroid Build Coastguard Worker 
3068*cf5a6c84SAndroid Build Coastguard Worker // FIXME TODO check if file/pipe/mode matches what's in the table already.
3069*cf5a6c84SAndroid Build Coastguard Worker // Apparently gawk/mawk/nawk are OK with different mode, but just use the file
3070*cf5a6c84SAndroid Build Coastguard Worker // in whatever mode it's already in; i.e. > after >> still appends.
setup_file(char file_or_pipe,char * mode)3071*cf5a6c84SAndroid Build Coastguard Worker static struct zfile *setup_file(char file_or_pipe, char *mode)
3072*cf5a6c84SAndroid Build Coastguard Worker {
3073*cf5a6c84SAndroid Build Coastguard Worker   to_str(STKP);   // filename at top of TT.stack
3074*cf5a6c84SAndroid Build Coastguard Worker   char *fn = STKP[0].vst->str;
3075*cf5a6c84SAndroid Build Coastguard Worker   // is it already open in file table?
3076*cf5a6c84SAndroid Build Coastguard Worker   for (struct zfile *p = TT.zfiles; p; p = p->next)
3077*cf5a6c84SAndroid Build Coastguard Worker     if (!strcmp(fn, p->fn)) {
3078*cf5a6c84SAndroid Build Coastguard Worker       drop();
3079*cf5a6c84SAndroid Build Coastguard Worker       return p;   // open; return it
3080*cf5a6c84SAndroid Build Coastguard Worker     }
3081*cf5a6c84SAndroid Build Coastguard Worker   FILE *fp = (file_or_pipe ? fopen : popen)(fn, mode);
3082*cf5a6c84SAndroid Build Coastguard Worker   if (fp) {
3083*cf5a6c84SAndroid Build Coastguard Worker     struct zfile *p = new_file(fn, fp, *mode, file_or_pipe, 0);
3084*cf5a6c84SAndroid Build Coastguard Worker     drop();
3085*cf5a6c84SAndroid Build Coastguard Worker     return p;
3086*cf5a6c84SAndroid Build Coastguard Worker   }
3087*cf5a6c84SAndroid Build Coastguard Worker   if (*mode != 'r') FFATAL("cannot open '%s'\n", fn);
3088*cf5a6c84SAndroid Build Coastguard Worker   drop();
3089*cf5a6c84SAndroid Build Coastguard Worker   return badfile;
3090*cf5a6c84SAndroid Build Coastguard Worker }
3091*cf5a6c84SAndroid Build Coastguard Worker 
3092*cf5a6c84SAndroid Build Coastguard Worker // TODO FIXME should be a function?
3093*cf5a6c84SAndroid Build Coastguard Worker #define stkn(n) ((int)(TT.stackp - (n) - (struct zvalue *)TT.stack.base))
3094*cf5a6c84SAndroid Build Coastguard Worker 
getcnt(int k)3095*cf5a6c84SAndroid Build Coastguard Worker static int getcnt(int k)
3096*cf5a6c84SAndroid Build Coastguard Worker {
3097*cf5a6c84SAndroid Build Coastguard Worker   if (k >= stkn(0)) FATAL("too few args for printf\n");
3098*cf5a6c84SAndroid Build Coastguard Worker   return (int)to_num(&STACK[k]);
3099*cf5a6c84SAndroid Build Coastguard Worker }
3100*cf5a6c84SAndroid Build Coastguard Worker 
fsprintf(FILE * ignored,const char * fmt,...)3101*cf5a6c84SAndroid Build Coastguard Worker static int fsprintf(FILE *ignored, const char *fmt, ...)
3102*cf5a6c84SAndroid Build Coastguard Worker {
3103*cf5a6c84SAndroid Build Coastguard Worker   (void)ignored;
3104*cf5a6c84SAndroid Build Coastguard Worker   va_list args, args2;
3105*cf5a6c84SAndroid Build Coastguard Worker   va_start(args, fmt);
3106*cf5a6c84SAndroid Build Coastguard Worker   va_copy(args2, args);
3107*cf5a6c84SAndroid Build Coastguard Worker   int len = vsnprintf(0, 0, fmt, args); // size needed
3108*cf5a6c84SAndroid Build Coastguard Worker   va_end(args);
3109*cf5a6c84SAndroid Build Coastguard Worker   if (len < 0) FATAL("Bad sprintf format");
3110*cf5a6c84SAndroid Build Coastguard Worker   // Unfortunately we have to mess with zstring internals here.
3111*cf5a6c84SAndroid Build Coastguard Worker   if (TT.rgl.zspr->size + len + 1 > TT.rgl.zspr->capacity) {
3112*cf5a6c84SAndroid Build Coastguard Worker       // This should always work b/c capacity > size
3113*cf5a6c84SAndroid Build Coastguard Worker       unsigned cap = 2 * TT.rgl.zspr->capacity + len;
3114*cf5a6c84SAndroid Build Coastguard Worker       TT.rgl.zspr = xrealloc(TT.rgl.zspr, sizeof(*TT.rgl.zspr) + cap);
3115*cf5a6c84SAndroid Build Coastguard Worker       TT.rgl.zspr->capacity = cap;
3116*cf5a6c84SAndroid Build Coastguard Worker     }
3117*cf5a6c84SAndroid Build Coastguard Worker   vsnprintf(TT.rgl.zspr->str + TT.rgl.zspr->size, len+1, fmt, args2);
3118*cf5a6c84SAndroid Build Coastguard Worker   TT.rgl.zspr->size += len;
3119*cf5a6c84SAndroid Build Coastguard Worker   TT.rgl.zspr->str[TT.rgl.zspr->size] = 0;
3120*cf5a6c84SAndroid Build Coastguard Worker   va_end(args2);
3121*cf5a6c84SAndroid Build Coastguard Worker   return 0;
3122*cf5a6c84SAndroid Build Coastguard Worker }
3123*cf5a6c84SAndroid Build Coastguard Worker 
varprint(int (* fpvar)(FILE *,const char *,...),FILE * outfp,int nargs)3124*cf5a6c84SAndroid Build Coastguard Worker static void varprint(int(*fpvar)(FILE *, const char *, ...), FILE *outfp, int nargs)
3125*cf5a6c84SAndroid Build Coastguard Worker {
3126*cf5a6c84SAndroid Build Coastguard Worker   int k, nn, nnc, fmtc, holdc, cnt1 = 0, cnt2 = 0;
3127*cf5a6c84SAndroid Build Coastguard Worker   char *s = 0;  // to shut up spurious warning
3128*cf5a6c84SAndroid Build Coastguard Worker   regoff_t offs = -1, e = -1;
3129*cf5a6c84SAndroid Build Coastguard Worker   char *pfmt, *fmt = to_str(STKP-nargs+1)->vst->str;
3130*cf5a6c84SAndroid Build Coastguard Worker   k = stkn(nargs - 2);
3131*cf5a6c84SAndroid Build Coastguard Worker   while (*fmt) {
3132*cf5a6c84SAndroid Build Coastguard Worker     double n = 0;
3133*cf5a6c84SAndroid Build Coastguard Worker     nn = strcspn(fmt, "%");
3134*cf5a6c84SAndroid Build Coastguard Worker     if (nn) {
3135*cf5a6c84SAndroid Build Coastguard Worker       holdc = fmt[nn];
3136*cf5a6c84SAndroid Build Coastguard Worker       fmt[nn] = 0;
3137*cf5a6c84SAndroid Build Coastguard Worker       fpvar(outfp, "%s", fmt);
3138*cf5a6c84SAndroid Build Coastguard Worker       fmt[nn] = holdc;
3139*cf5a6c84SAndroid Build Coastguard Worker     }
3140*cf5a6c84SAndroid Build Coastguard Worker     fmt += nn;
3141*cf5a6c84SAndroid Build Coastguard Worker     if (!*(pfmt = fmt)) break;
3142*cf5a6c84SAndroid Build Coastguard Worker     nnc = strcspn(fmt+1, "aAdiouxXfFeEgGcs%");
3143*cf5a6c84SAndroid Build Coastguard Worker     fmtc = fmt[nnc+1];
3144*cf5a6c84SAndroid Build Coastguard Worker     if (!fmtc) FFATAL("bad printf format '%s'", fmt);
3145*cf5a6c84SAndroid Build Coastguard Worker     holdc = fmt[nnc+2];
3146*cf5a6c84SAndroid Build Coastguard Worker     fmt[nnc+2] = 0;
3147*cf5a6c84SAndroid Build Coastguard Worker     if (rx_find(&TT.rx_printf_fmt, fmt, &offs, &e, 0))
3148*cf5a6c84SAndroid Build Coastguard Worker       FFATAL("bad printf format <%s>\n", fmt);
3149*cf5a6c84SAndroid Build Coastguard Worker     int nargsneeded = 1;
3150*cf5a6c84SAndroid Build Coastguard Worker     for (char *p = strchr(fmt, '*'); p; p = strchr(p+1, '*'))
3151*cf5a6c84SAndroid Build Coastguard Worker       nargsneeded++;
3152*cf5a6c84SAndroid Build Coastguard Worker     nargsneeded -= fmtc == '%';
3153*cf5a6c84SAndroid Build Coastguard Worker 
3154*cf5a6c84SAndroid Build Coastguard Worker     switch (nargsneeded) {
3155*cf5a6c84SAndroid Build Coastguard Worker       case 0:
3156*cf5a6c84SAndroid Build Coastguard Worker         fpvar(outfp, fmt);
3157*cf5a6c84SAndroid Build Coastguard Worker         break;
3158*cf5a6c84SAndroid Build Coastguard Worker       case 3:
3159*cf5a6c84SAndroid Build Coastguard Worker         cnt1 = getcnt(k++);
3160*cf5a6c84SAndroid Build Coastguard Worker         ATTR_FALLTHROUGH_INTENDED;
3161*cf5a6c84SAndroid Build Coastguard Worker       case 2:
3162*cf5a6c84SAndroid Build Coastguard Worker         cnt2 = getcnt(k++);
3163*cf5a6c84SAndroid Build Coastguard Worker         ATTR_FALLTHROUGH_INTENDED;
3164*cf5a6c84SAndroid Build Coastguard Worker       case 1:
3165*cf5a6c84SAndroid Build Coastguard Worker         if (k > stkn(0)) FATAL("too few args for printf\n");
3166*cf5a6c84SAndroid Build Coastguard Worker         if (fmtc == 's') {
3167*cf5a6c84SAndroid Build Coastguard Worker           s = to_str(&STACK[k++])->vst->str;
3168*cf5a6c84SAndroid Build Coastguard Worker         } else if (fmtc == 'c' && !IS_NUM(&STACK[k])) {
3169*cf5a6c84SAndroid Build Coastguard Worker           unsigned wch;
3170*cf5a6c84SAndroid Build Coastguard Worker           struct zvalue *z = &STACK[k++];
3171*cf5a6c84SAndroid Build Coastguard Worker           if (z->vst && z->vst->str[0])
3172*cf5a6c84SAndroid Build Coastguard Worker             n = utf8towc(&wch, z->vst->str, z->vst->size) < 1 ? 0xfffd : wch;
3173*cf5a6c84SAndroid Build Coastguard Worker         } else {
3174*cf5a6c84SAndroid Build Coastguard Worker           n = to_num(&STACK[k++]);
3175*cf5a6c84SAndroid Build Coastguard Worker         }
3176*cf5a6c84SAndroid Build Coastguard Worker         if (strchr("cdiouxX", fmtc)) {
3177*cf5a6c84SAndroid Build Coastguard Worker           pfmt = strcpy(TT.pbuf, fmt);
3178*cf5a6c84SAndroid Build Coastguard Worker           if (pfmt[nnc] != 'l') {
3179*cf5a6c84SAndroid Build Coastguard Worker             strcpy(pfmt+nnc+1, "l_");
3180*cf5a6c84SAndroid Build Coastguard Worker             pfmt[nnc+2] = fmtc;
3181*cf5a6c84SAndroid Build Coastguard Worker           }
3182*cf5a6c84SAndroid Build Coastguard Worker         }
3183*cf5a6c84SAndroid Build Coastguard Worker         if (fmtc == 'c' && n > 0x10ffff) n = 0xfffd;  // musl won't take larger "wchar"
3184*cf5a6c84SAndroid Build Coastguard Worker         switch (nargsneeded) {
3185*cf5a6c84SAndroid Build Coastguard Worker           case 1:
3186*cf5a6c84SAndroid Build Coastguard Worker             if (fmtc == 's') fpvar(outfp, pfmt, s);
3187*cf5a6c84SAndroid Build Coastguard Worker             else if (fmtc == 'c') fpvar(outfp, pfmt, (wint_t)n);
3188*cf5a6c84SAndroid Build Coastguard Worker             else if (strchr("di", fmtc)) fpvar(outfp, pfmt, (long)n);
3189*cf5a6c84SAndroid Build Coastguard Worker             else if (strchr("ouxX", fmtc)) fpvar(outfp, pfmt, (unsigned long)n);
3190*cf5a6c84SAndroid Build Coastguard Worker             else fpvar(outfp, pfmt, n);
3191*cf5a6c84SAndroid Build Coastguard Worker             break;
3192*cf5a6c84SAndroid Build Coastguard Worker           case 2:
3193*cf5a6c84SAndroid Build Coastguard Worker             if (fmtc == 's') fpvar(outfp, pfmt, cnt2, s);
3194*cf5a6c84SAndroid Build Coastguard Worker             else if (fmtc == 'c') fpvar(outfp, pfmt, cnt2, (wint_t)n);
3195*cf5a6c84SAndroid Build Coastguard Worker             else if (strchr("di", fmtc)) fpvar(outfp, pfmt, cnt2, (long)n);
3196*cf5a6c84SAndroid Build Coastguard Worker             else if (strchr("ouxX", fmtc)) fpvar(outfp, pfmt, cnt2, (unsigned long)n);
3197*cf5a6c84SAndroid Build Coastguard Worker             else fpvar(outfp, pfmt, cnt2, n);
3198*cf5a6c84SAndroid Build Coastguard Worker             break;
3199*cf5a6c84SAndroid Build Coastguard Worker           case 3:
3200*cf5a6c84SAndroid Build Coastguard Worker             if (fmtc == 's') fpvar(outfp, pfmt, cnt1, cnt2, s);
3201*cf5a6c84SAndroid Build Coastguard Worker             else if (fmtc == 'c') fpvar(outfp, pfmt, cnt1, cnt2, (wint_t)n);
3202*cf5a6c84SAndroid Build Coastguard Worker             else if (strchr("di", fmtc)) fpvar(outfp, pfmt, cnt1, cnt2, (long)n);
3203*cf5a6c84SAndroid Build Coastguard Worker             else if (strchr("ouxX", fmtc)) fpvar(outfp, pfmt, cnt1, cnt2, (unsigned long)n);
3204*cf5a6c84SAndroid Build Coastguard Worker             else fpvar(outfp, pfmt, cnt1, cnt2, n);
3205*cf5a6c84SAndroid Build Coastguard Worker             break;
3206*cf5a6c84SAndroid Build Coastguard Worker         }
3207*cf5a6c84SAndroid Build Coastguard Worker         break;
3208*cf5a6c84SAndroid Build Coastguard Worker       default:
3209*cf5a6c84SAndroid Build Coastguard Worker         FATAL("bad printf format\n");
3210*cf5a6c84SAndroid Build Coastguard Worker     }
3211*cf5a6c84SAndroid Build Coastguard Worker     fmt += nnc + 2;
3212*cf5a6c84SAndroid Build Coastguard Worker     *fmt = holdc;
3213*cf5a6c84SAndroid Build Coastguard Worker   }
3214*cf5a6c84SAndroid Build Coastguard Worker }
3215*cf5a6c84SAndroid Build Coastguard Worker 
is_ok_varname(char * v)3216*cf5a6c84SAndroid Build Coastguard Worker static int is_ok_varname(char *v)
3217*cf5a6c84SAndroid Build Coastguard Worker {
3218*cf5a6c84SAndroid Build Coastguard Worker   char *ok = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
3219*cf5a6c84SAndroid Build Coastguard Worker   if (!*v) return 0;
3220*cf5a6c84SAndroid Build Coastguard Worker   for (int i = 0; v[i]; i++)
3221*cf5a6c84SAndroid Build Coastguard Worker     if (i ? !strchr(ok, v[i]) : !strchr(ok + 10, v[i])) return 0;
3222*cf5a6c84SAndroid Build Coastguard Worker   return 1;
3223*cf5a6c84SAndroid Build Coastguard Worker }
3224*cf5a6c84SAndroid Build Coastguard Worker 
3225*cf5a6c84SAndroid Build Coastguard Worker // FIXME TODO return value never used. What if assign to var not in globals?
assign_global(char * var,char * value)3226*cf5a6c84SAndroid Build Coastguard Worker static int assign_global(char *var, char *value)
3227*cf5a6c84SAndroid Build Coastguard Worker {
3228*cf5a6c84SAndroid Build Coastguard Worker   if (!is_ok_varname(var)) FFATAL("Invalid variable name '%s'\n", var);
3229*cf5a6c84SAndroid Build Coastguard Worker   int globals_ent = find_global(var);
3230*cf5a6c84SAndroid Build Coastguard Worker   if (globals_ent) {
3231*cf5a6c84SAndroid Build Coastguard Worker     struct zvalue *v = &STACK[globals_ent];
3232*cf5a6c84SAndroid Build Coastguard Worker     if (IS_MAP(v)) error_exit("-v assignment to array");  // Maybe not needed?
3233*cf5a6c84SAndroid Build Coastguard Worker 
3234*cf5a6c84SAndroid Build Coastguard Worker // The compile phase may insert a var in global table with flag of zero.  Then
3235*cf5a6c84SAndroid Build Coastguard Worker // init_globals() will assign a ZF_MAYBEMAP flag to it. If it is then assigned
3236*cf5a6c84SAndroid Build Coastguard Worker // via -v option or by assignment_arg() it will here be assigned a string value.
3237*cf5a6c84SAndroid Build Coastguard Worker // So first, remove all map data to prevent memory leak. BUG FIX // 2024-02-13.
3238*cf5a6c84SAndroid Build Coastguard Worker     if (v->flags & ZF_ANYMAP) {
3239*cf5a6c84SAndroid Build Coastguard Worker       zmap_delete_map_incl_slotdata(v->map);
3240*cf5a6c84SAndroid Build Coastguard Worker       xfree(v->map);
3241*cf5a6c84SAndroid Build Coastguard Worker       v->map = 0;
3242*cf5a6c84SAndroid Build Coastguard Worker       v->flags &= ~ZF_ANYMAP;
3243*cf5a6c84SAndroid Build Coastguard Worker     }
3244*cf5a6c84SAndroid Build Coastguard Worker 
3245*cf5a6c84SAndroid Build Coastguard Worker     zvalue_release_zstring(v);
3246*cf5a6c84SAndroid Build Coastguard Worker     value = xstrdup(value);
3247*cf5a6c84SAndroid Build Coastguard Worker     *v = new_str_val(escape_str(value, 0));
3248*cf5a6c84SAndroid Build Coastguard Worker     xfree(value);
3249*cf5a6c84SAndroid Build Coastguard Worker     check_numeric_string(v);
3250*cf5a6c84SAndroid Build Coastguard Worker     return 1;
3251*cf5a6c84SAndroid Build Coastguard Worker   }
3252*cf5a6c84SAndroid Build Coastguard Worker   return 0;
3253*cf5a6c84SAndroid Build Coastguard Worker }
3254*cf5a6c84SAndroid Build Coastguard Worker 
3255*cf5a6c84SAndroid Build Coastguard Worker // If valid assignment arg, assign the global and return 1;
3256*cf5a6c84SAndroid Build Coastguard Worker // otherwise return 0.
3257*cf5a6c84SAndroid Build Coastguard Worker // TODO FIXME This does not check the format of the variable per posix.
3258*cf5a6c84SAndroid Build Coastguard Worker // Needs to start w/ _A-Za-z then _A-Za-z0-9
3259*cf5a6c84SAndroid Build Coastguard Worker // If not valid assignment form, then nextfilearg needs to treat as filename.
assignment_arg(char * arg)3260*cf5a6c84SAndroid Build Coastguard Worker static int assignment_arg(char *arg)
3261*cf5a6c84SAndroid Build Coastguard Worker {
3262*cf5a6c84SAndroid Build Coastguard Worker   char *val = strchr(arg, '=');
3263*cf5a6c84SAndroid Build Coastguard Worker   if (val) {
3264*cf5a6c84SAndroid Build Coastguard Worker     *val++ = 0;
3265*cf5a6c84SAndroid Build Coastguard Worker     if (!is_ok_varname(arg)) {
3266*cf5a6c84SAndroid Build Coastguard Worker       *--val = '=';
3267*cf5a6c84SAndroid Build Coastguard Worker       return 0;
3268*cf5a6c84SAndroid Build Coastguard Worker     }
3269*cf5a6c84SAndroid Build Coastguard Worker     assign_global(arg, val);
3270*cf5a6c84SAndroid Build Coastguard Worker     *--val = '=';
3271*cf5a6c84SAndroid Build Coastguard Worker     return 1;
3272*cf5a6c84SAndroid Build Coastguard Worker   } else return 0;
3273*cf5a6c84SAndroid Build Coastguard Worker }
3274*cf5a6c84SAndroid Build Coastguard Worker 
nextfilearg(void)3275*cf5a6c84SAndroid Build Coastguard Worker static char *nextfilearg(void)
3276*cf5a6c84SAndroid Build Coastguard Worker {
3277*cf5a6c84SAndroid Build Coastguard Worker   char *arg;
3278*cf5a6c84SAndroid Build Coastguard Worker   do {
3279*cf5a6c84SAndroid Build Coastguard Worker     if (++TT.rgl.narg >= (int)to_num(&STACK[ARGC])) return 0;
3280*cf5a6c84SAndroid Build Coastguard Worker     struct zvalue *v = &STACK[ARGV];
3281*cf5a6c84SAndroid Build Coastguard Worker     struct zvalue zkey = ZVINIT(ZF_STR, 0,
3282*cf5a6c84SAndroid Build Coastguard Worker         num_to_zstring(TT.rgl.narg, to_str(&STACK[CONVFMT])->vst->str));
3283*cf5a6c84SAndroid Build Coastguard Worker     arg = "";
3284*cf5a6c84SAndroid Build Coastguard Worker     if (zmap_find(v->map, zkey.vst)) {
3285*cf5a6c84SAndroid Build Coastguard Worker       zvalue_copy(&TT.rgl.cur_arg, to_str(get_map_val(v, &zkey)));
3286*cf5a6c84SAndroid Build Coastguard Worker       arg = TT.rgl.cur_arg.vst->str;
3287*cf5a6c84SAndroid Build Coastguard Worker     }
3288*cf5a6c84SAndroid Build Coastguard Worker     zvalue_release_zstring(&zkey);
3289*cf5a6c84SAndroid Build Coastguard Worker   } while (!*arg || assignment_arg(arg));
3290*cf5a6c84SAndroid Build Coastguard Worker   TT.rgl.nfiles++;
3291*cf5a6c84SAndroid Build Coastguard Worker   return arg;
3292*cf5a6c84SAndroid Build Coastguard Worker }
3293*cf5a6c84SAndroid Build Coastguard Worker 
next_fp(void)3294*cf5a6c84SAndroid Build Coastguard Worker static int next_fp(void)
3295*cf5a6c84SAndroid Build Coastguard Worker {
3296*cf5a6c84SAndroid Build Coastguard Worker   char *fn = nextfilearg();
3297*cf5a6c84SAndroid Build Coastguard Worker   if (TT.cfile->fp && TT.cfile->fp != stdin) fclose(TT.cfile->fp);
3298*cf5a6c84SAndroid Build Coastguard Worker   if ((!fn && !TT.rgl.nfiles && TT.cfile->fp != stdin) || (fn && !strcmp(fn, "-"))) {
3299*cf5a6c84SAndroid Build Coastguard Worker     xfree(TT.cfile->buf);
3300*cf5a6c84SAndroid Build Coastguard Worker     *TT.cfile = (struct zfile){0};
3301*cf5a6c84SAndroid Build Coastguard Worker     TT.cfile->fp = stdin;
3302*cf5a6c84SAndroid Build Coastguard Worker     TT.cfile->fn = "-";
3303*cf5a6c84SAndroid Build Coastguard Worker     zvalue_release_zstring(&STACK[FILENAME]);
3304*cf5a6c84SAndroid Build Coastguard Worker     STACK[FILENAME].vst = new_zstring("-", 1);
3305*cf5a6c84SAndroid Build Coastguard Worker   } else if (fn) {
3306*cf5a6c84SAndroid Build Coastguard Worker     xfree(TT.cfile->buf);
3307*cf5a6c84SAndroid Build Coastguard Worker     *TT.cfile = (struct zfile){0};
3308*cf5a6c84SAndroid Build Coastguard Worker     if (!(TT.cfile->fp = fopen(fn, "r"))) FFATAL("can't open %s\n", fn);
3309*cf5a6c84SAndroid Build Coastguard Worker     TT.cfile->fn = fn;
3310*cf5a6c84SAndroid Build Coastguard Worker     zvalue_copy(&STACK[FILENAME], &TT.rgl.cur_arg);
3311*cf5a6c84SAndroid Build Coastguard Worker   } else {
3312*cf5a6c84SAndroid Build Coastguard Worker     TT.rgl.eof = 1;
3313*cf5a6c84SAndroid Build Coastguard Worker     return 0;
3314*cf5a6c84SAndroid Build Coastguard Worker   }
3315*cf5a6c84SAndroid Build Coastguard Worker   set_num(&STACK[FNR], 0);
3316*cf5a6c84SAndroid Build Coastguard Worker   TT.cfile->is_tty = isatty(fileno(TT.cfile->fp));
3317*cf5a6c84SAndroid Build Coastguard Worker   return 1;
3318*cf5a6c84SAndroid Build Coastguard Worker }
3319*cf5a6c84SAndroid Build Coastguard Worker 
rx_find_rs(regex_t * rx,char * s,long len,regoff_t * start,regoff_t * end,int one_byte_rs)3320*cf5a6c84SAndroid Build Coastguard Worker static int rx_find_rs(regex_t *rx, char *s, long len,
3321*cf5a6c84SAndroid Build Coastguard Worker                       regoff_t *start, regoff_t *end, int one_byte_rs)
3322*cf5a6c84SAndroid Build Coastguard Worker {
3323*cf5a6c84SAndroid Build Coastguard Worker   regmatch_t matches[1];
3324*cf5a6c84SAndroid Build Coastguard Worker   if (one_byte_rs) {
3325*cf5a6c84SAndroid Build Coastguard Worker     char *p = memchr(s, one_byte_rs, len);
3326*cf5a6c84SAndroid Build Coastguard Worker     if (!p) return REG_NOMATCH;
3327*cf5a6c84SAndroid Build Coastguard Worker     *start = p - s;
3328*cf5a6c84SAndroid Build Coastguard Worker     *end = *start + 1;
3329*cf5a6c84SAndroid Build Coastguard Worker   } else {
3330*cf5a6c84SAndroid Build Coastguard Worker     int r = regexec0(rx, s, len, 1, matches, 0);
3331*cf5a6c84SAndroid Build Coastguard Worker     if (r == REG_NOMATCH) return r;
3332*cf5a6c84SAndroid Build Coastguard Worker     if (r) FATAL("regexec error");  // TODO ? use regerr() to meaningful msg
3333*cf5a6c84SAndroid Build Coastguard Worker     *start = matches[0].rm_so;
3334*cf5a6c84SAndroid Build Coastguard Worker     *end = matches[0].rm_eo;
3335*cf5a6c84SAndroid Build Coastguard Worker   }
3336*cf5a6c84SAndroid Build Coastguard Worker   return 0;
3337*cf5a6c84SAndroid Build Coastguard Worker }
3338*cf5a6c84SAndroid Build Coastguard Worker 
3339*cf5a6c84SAndroid Build Coastguard Worker // get a record; return length, or -1 at EOF
3340*cf5a6c84SAndroid Build Coastguard Worker // Does work for getrec_f() for regular RS or multiline
getr(struct zfile * zfp,int rs_mode)3341*cf5a6c84SAndroid Build Coastguard Worker static ssize_t getr(struct zfile *zfp, int rs_mode)
3342*cf5a6c84SAndroid Build Coastguard Worker {
3343*cf5a6c84SAndroid Build Coastguard Worker   // zfp->buf (initially null) points to record buffer
3344*cf5a6c84SAndroid Build Coastguard Worker   // zfp->buflen -- size of allocated buf
3345*cf5a6c84SAndroid Build Coastguard Worker   // TT.rgl.recptr -- points to where record is being / has been read into
3346*cf5a6c84SAndroid Build Coastguard Worker   // zfp->ro -- offset in buf to record data
3347*cf5a6c84SAndroid Build Coastguard Worker   // zfp->lim -- offset to 1+last byte read in buffer
3348*cf5a6c84SAndroid Build Coastguard Worker   // rs_mode nonzero iff multiline mode; reused for one-byte RS
3349*cf5a6c84SAndroid Build Coastguard Worker 
3350*cf5a6c84SAndroid Build Coastguard Worker   regex_t rsrx; // FIXME Need to cache and avoid rx compile on every record?
3351*cf5a6c84SAndroid Build Coastguard Worker   long ret = -1;
3352*cf5a6c84SAndroid Build Coastguard Worker   int r = -REG_NOMATCH;   // r cannot have this value after rx_findx() below
3353*cf5a6c84SAndroid Build Coastguard Worker   regoff_t so = 0, eo = 0;
3354*cf5a6c84SAndroid Build Coastguard Worker   size_t m = 0, n = 0;
3355*cf5a6c84SAndroid Build Coastguard Worker 
3356*cf5a6c84SAndroid Build Coastguard Worker   xregcomp(&rsrx, rs_mode ? "\n\n+" : fmt_one_char_fs(STACK[RS].vst->str),
3357*cf5a6c84SAndroid Build Coastguard Worker       REG_EXTENDED);
3358*cf5a6c84SAndroid Build Coastguard Worker   rs_mode = strlen(STACK[RS].vst->str) == 1 ? STACK[RS].vst->str[0] : 0;
3359*cf5a6c84SAndroid Build Coastguard Worker   for ( ;; ) {
3360*cf5a6c84SAndroid Build Coastguard Worker     if (zfp->ro == zfp->lim && zfp->eof) break; // EOF & last record; return -1
3361*cf5a6c84SAndroid Build Coastguard Worker 
3362*cf5a6c84SAndroid Build Coastguard Worker     // Allocate initial buffer, and expand iff buffer holds one
3363*cf5a6c84SAndroid Build Coastguard Worker     //   possibly (probably) incomplete record.
3364*cf5a6c84SAndroid Build Coastguard Worker     if (zfp->ro == 0 && zfp->lim == zfp->buflen)
3365*cf5a6c84SAndroid Build Coastguard Worker       zfp->buf = xrealloc(zfp->buf,
3366*cf5a6c84SAndroid Build Coastguard Worker           (zfp->buflen = maxof(512, zfp->buflen * 2)) + 1);
3367*cf5a6c84SAndroid Build Coastguard Worker 
3368*cf5a6c84SAndroid Build Coastguard Worker     if ((m = zfp->buflen - zfp->lim) && !zfp->eof) {
3369*cf5a6c84SAndroid Build Coastguard Worker       // Read iff space left in buffer
3370*cf5a6c84SAndroid Build Coastguard Worker       if (zfp->is_tty) m = 1;
3371*cf5a6c84SAndroid Build Coastguard Worker       n = fread(zfp->buf + zfp->lim, 1, m, zfp->fp);
3372*cf5a6c84SAndroid Build Coastguard Worker       if (n < m) {
3373*cf5a6c84SAndroid Build Coastguard Worker         if (ferror(zfp->fp)) FFATAL("i/o error %d on %s!", errno, zfp->fn);
3374*cf5a6c84SAndroid Build Coastguard Worker         zfp->eof = 1;
3375*cf5a6c84SAndroid Build Coastguard Worker         if (!n && r == -REG_NOMATCH) break; // catch empty file here
3376*cf5a6c84SAndroid Build Coastguard Worker       }
3377*cf5a6c84SAndroid Build Coastguard Worker       zfp->lim += n;
3378*cf5a6c84SAndroid Build Coastguard Worker       zfp->buf[zfp->lim] = 0;
3379*cf5a6c84SAndroid Build Coastguard Worker     }
3380*cf5a6c84SAndroid Build Coastguard Worker     TT.rgl.recptr = zfp->buf + zfp->ro;
3381*cf5a6c84SAndroid Build Coastguard Worker     r = rx_find_rs(&rsrx, TT.rgl.recptr, zfp->lim - zfp->ro, &so, &eo, rs_mode);
3382*cf5a6c84SAndroid Build Coastguard Worker     if (!r && so == eo) r = 1;  // RS was empty, so fake not found
3383*cf5a6c84SAndroid Build Coastguard Worker 
3384*cf5a6c84SAndroid Build Coastguard Worker     if (!zfp->eof && (r
3385*cf5a6c84SAndroid Build Coastguard Worker           || (zfp->lim - (zfp->ro + eo)) < zfp->buflen / 4) && !zfp->is_tty) {
3386*cf5a6c84SAndroid Build Coastguard Worker       // RS not found, or found near lim. Slide up and try to get more data
3387*cf5a6c84SAndroid Build Coastguard Worker       // If recptr at start of buf and RS not found then expand buffer
3388*cf5a6c84SAndroid Build Coastguard Worker       memmove(zfp->buf, TT.rgl.recptr, zfp->lim - zfp->ro);
3389*cf5a6c84SAndroid Build Coastguard Worker       zfp->lim -= zfp->ro;
3390*cf5a6c84SAndroid Build Coastguard Worker       zfp->ro = 0;
3391*cf5a6c84SAndroid Build Coastguard Worker       continue;
3392*cf5a6c84SAndroid Build Coastguard Worker     }
3393*cf5a6c84SAndroid Build Coastguard Worker     ret = so;   // If RS found, then 'so' is rec length
3394*cf5a6c84SAndroid Build Coastguard Worker     if (zfp->eof) {
3395*cf5a6c84SAndroid Build Coastguard Worker       if (r) {  // EOF and RS not found; rec is all data left in buf
3396*cf5a6c84SAndroid Build Coastguard Worker         ret = zfp->lim - zfp->ro;
3397*cf5a6c84SAndroid Build Coastguard Worker         zfp->ro = zfp->lim; // set ro for -1 return on next call
3398*cf5a6c84SAndroid Build Coastguard Worker       } else zfp->ro += eo; // RS found; advance ro
3399*cf5a6c84SAndroid Build Coastguard Worker     } else zfp->ro += eo; // Here only if RS found not near lim
3400*cf5a6c84SAndroid Build Coastguard Worker 
3401*cf5a6c84SAndroid Build Coastguard Worker     if (!r || !zfp->is_tty) {
3402*cf5a6c84SAndroid Build Coastguard Worker       // If is_tty then RS found; reset buffer pointers;
3403*cf5a6c84SAndroid Build Coastguard Worker       // is_tty uses one rec per buffer load
3404*cf5a6c84SAndroid Build Coastguard Worker       if (zfp->is_tty) zfp->ro = zfp->lim = 0;
3405*cf5a6c84SAndroid Build Coastguard Worker       break;
3406*cf5a6c84SAndroid Build Coastguard Worker     } // RS not found AND is_tty; loop to keep reading
3407*cf5a6c84SAndroid Build Coastguard Worker   }
3408*cf5a6c84SAndroid Build Coastguard Worker   regfree(&rsrx);
3409*cf5a6c84SAndroid Build Coastguard Worker   return ret;
3410*cf5a6c84SAndroid Build Coastguard Worker }
3411*cf5a6c84SAndroid Build Coastguard Worker 
3412*cf5a6c84SAndroid Build Coastguard Worker // get a record; return length, or -1 at EOF
getrec_f(struct zfile * zfp)3413*cf5a6c84SAndroid Build Coastguard Worker static ssize_t getrec_f(struct zfile *zfp)
3414*cf5a6c84SAndroid Build Coastguard Worker {
3415*cf5a6c84SAndroid Build Coastguard Worker   int k;
3416*cf5a6c84SAndroid Build Coastguard Worker   if (ENSURE_STR(&STACK[RS])->vst->str[0]) return getr(zfp, 0);
3417*cf5a6c84SAndroid Build Coastguard Worker   // RS == "" so multiline read
3418*cf5a6c84SAndroid Build Coastguard Worker   // Passing 1 to getr() forces multiline mode, which uses regex "\n\n+" to
3419*cf5a6c84SAndroid Build Coastguard Worker   // split on sequences of 2 or more newlines. But that's not the same as
3420*cf5a6c84SAndroid Build Coastguard Worker   // multiline mode, which never returns empty records or records with leading
3421*cf5a6c84SAndroid Build Coastguard Worker   // or trailing newlines, which can occur with RS="\n\n+". So here we loop and
3422*cf5a6c84SAndroid Build Coastguard Worker   // strip leading/trailing newlines and discard empty lines. See gawk manual,
3423*cf5a6c84SAndroid Build Coastguard Worker   // "4.9 Multiple-Line Records" for info on this difference.
3424*cf5a6c84SAndroid Build Coastguard Worker   do {
3425*cf5a6c84SAndroid Build Coastguard Worker     k = getr(zfp, 1);
3426*cf5a6c84SAndroid Build Coastguard Worker     if (k < 0) break;
3427*cf5a6c84SAndroid Build Coastguard Worker     while (k && TT.rgl.recptr[k-1] == '\n') k--;
3428*cf5a6c84SAndroid Build Coastguard Worker     while (k && TT.rgl.recptr[0] == '\n') k--, TT.rgl.recptr++;
3429*cf5a6c84SAndroid Build Coastguard Worker   } while (!k);
3430*cf5a6c84SAndroid Build Coastguard Worker   return k;
3431*cf5a6c84SAndroid Build Coastguard Worker }
3432*cf5a6c84SAndroid Build Coastguard Worker 
getrec(void)3433*cf5a6c84SAndroid Build Coastguard Worker static ssize_t getrec(void)
3434*cf5a6c84SAndroid Build Coastguard Worker {
3435*cf5a6c84SAndroid Build Coastguard Worker   ssize_t k;
3436*cf5a6c84SAndroid Build Coastguard Worker   if (TT.rgl.eof) return -1;
3437*cf5a6c84SAndroid Build Coastguard Worker   if (!TT.cfile->fp) next_fp();
3438*cf5a6c84SAndroid Build Coastguard Worker   do {
3439*cf5a6c84SAndroid Build Coastguard Worker     if ((k = getrec_f(TT.cfile)) >= 0) return k;
3440*cf5a6c84SAndroid Build Coastguard Worker   } while (next_fp());
3441*cf5a6c84SAndroid Build Coastguard Worker   return -1;
3442*cf5a6c84SAndroid Build Coastguard Worker }
3443*cf5a6c84SAndroid Build Coastguard Worker 
getrec_f0_f(struct zfile * zfp)3444*cf5a6c84SAndroid Build Coastguard Worker static ssize_t getrec_f0_f(struct zfile *zfp)
3445*cf5a6c84SAndroid Build Coastguard Worker {
3446*cf5a6c84SAndroid Build Coastguard Worker   ssize_t k = getrec_f(zfp);
3447*cf5a6c84SAndroid Build Coastguard Worker   if (k >= 0) {
3448*cf5a6c84SAndroid Build Coastguard Worker     copy_to_field0(TT.rgl.recptr, k);
3449*cf5a6c84SAndroid Build Coastguard Worker   }
3450*cf5a6c84SAndroid Build Coastguard Worker   return k;
3451*cf5a6c84SAndroid Build Coastguard Worker }
3452*cf5a6c84SAndroid Build Coastguard Worker 
getrec_f0(void)3453*cf5a6c84SAndroid Build Coastguard Worker static ssize_t getrec_f0(void)
3454*cf5a6c84SAndroid Build Coastguard Worker {
3455*cf5a6c84SAndroid Build Coastguard Worker   ssize_t k = getrec();
3456*cf5a6c84SAndroid Build Coastguard Worker   if (k >= 0) {
3457*cf5a6c84SAndroid Build Coastguard Worker     copy_to_field0(TT.rgl.recptr, k);
3458*cf5a6c84SAndroid Build Coastguard Worker     incr_zvalue(&STACK[NR]);
3459*cf5a6c84SAndroid Build Coastguard Worker     incr_zvalue(&STACK[FNR]);
3460*cf5a6c84SAndroid Build Coastguard Worker   }
3461*cf5a6c84SAndroid Build Coastguard Worker   return k;
3462*cf5a6c84SAndroid Build Coastguard Worker }
3463*cf5a6c84SAndroid Build Coastguard Worker 
3464*cf5a6c84SAndroid Build Coastguard Worker // source is tkeof (no pipe/file), tklt (file), or tkpipe (pipe)
3465*cf5a6c84SAndroid Build Coastguard Worker // fp is file or pipe (is NULL if file/pipe could not be opened)
3466*cf5a6c84SAndroid Build Coastguard Worker // FIXME TODO should -1 return be replaced by test at caller?
3467*cf5a6c84SAndroid Build Coastguard Worker // v is NULL or an lvalue ref
awk_getline(int source,struct zfile * zfp,struct zvalue * v)3468*cf5a6c84SAndroid Build Coastguard Worker static int awk_getline(int source, struct zfile *zfp, struct zvalue *v)
3469*cf5a6c84SAndroid Build Coastguard Worker {
3470*cf5a6c84SAndroid Build Coastguard Worker   ssize_t k;
3471*cf5a6c84SAndroid Build Coastguard Worker   int is_stream = source != tkeof;
3472*cf5a6c84SAndroid Build Coastguard Worker   if (is_stream && !zfp->fp) return -1;
3473*cf5a6c84SAndroid Build Coastguard Worker   if (v) {
3474*cf5a6c84SAndroid Build Coastguard Worker     if ((k = is_stream ? getrec_f(zfp) : getrec()) < 0) return 0;
3475*cf5a6c84SAndroid Build Coastguard Worker     zstring_release(&v->vst);
3476*cf5a6c84SAndroid Build Coastguard Worker     v->vst = new_zstring(TT.rgl.recptr, k);
3477*cf5a6c84SAndroid Build Coastguard Worker     v->flags = ZF_STR;
3478*cf5a6c84SAndroid Build Coastguard Worker     check_numeric_string(v);    // bug fix 20240514
3479*cf5a6c84SAndroid Build Coastguard Worker     if (!is_stream) {
3480*cf5a6c84SAndroid Build Coastguard Worker       incr_zvalue(&STACK[NR]);
3481*cf5a6c84SAndroid Build Coastguard Worker       incr_zvalue(&STACK[FNR]);
3482*cf5a6c84SAndroid Build Coastguard Worker     }
3483*cf5a6c84SAndroid Build Coastguard Worker   } else k = is_stream ? getrec_f0_f(zfp) : getrec_f0();
3484*cf5a6c84SAndroid Build Coastguard Worker   return k < 0 ? 0 : 1;
3485*cf5a6c84SAndroid Build Coastguard Worker }
3486*cf5a6c84SAndroid Build Coastguard Worker 
3487*cf5a6c84SAndroid Build Coastguard Worker // Define GAWK_SUB to get the same behavior with sub()/gsub() replacement text
3488*cf5a6c84SAndroid Build Coastguard Worker // as with gawk, goawk, and recent bwk awk (nawk) versions. Undefine GAWK_SUB
3489*cf5a6c84SAndroid Build Coastguard Worker // to get the simpler POSIX behavior, but I think most users will prefer the
3490*cf5a6c84SAndroid Build Coastguard Worker // gawk behavior. See the gawk (GNU Awk) manual,
3491*cf5a6c84SAndroid Build Coastguard Worker // sec. 9.1.4.1 // More about '\' and '&' with sub(), gsub(), and gensub()
3492*cf5a6c84SAndroid Build Coastguard Worker // for details on the differences.
3493*cf5a6c84SAndroid Build Coastguard Worker //
3494*cf5a6c84SAndroid Build Coastguard Worker #undef GAWK_SUB
3495*cf5a6c84SAndroid Build Coastguard Worker #define GAWK_SUB
3496*cf5a6c84SAndroid Build Coastguard Worker 
3497*cf5a6c84SAndroid Build Coastguard Worker // sub(ere, repl[, in]) Substitute the string repl in place of the
3498*cf5a6c84SAndroid Build Coastguard Worker // first instance of the extended regular expression ERE in string 'in'
3499*cf5a6c84SAndroid Build Coastguard Worker // and return the number of substitutions.  An <ampersand> ( '&' )
3500*cf5a6c84SAndroid Build Coastguard Worker // appearing in the string repl shall be replaced by the string from in
3501*cf5a6c84SAndroid Build Coastguard Worker // that matches the ERE. (partial spec... there's more)
gsub(int opcode,int nargs,int parmbase)3502*cf5a6c84SAndroid Build Coastguard Worker static void gsub(int opcode, int nargs, int parmbase)
3503*cf5a6c84SAndroid Build Coastguard Worker { (void)nargs;
3504*cf5a6c84SAndroid Build Coastguard Worker   int field_num = -1;
3505*cf5a6c84SAndroid Build Coastguard Worker   // compile ensures 3 args
3506*cf5a6c84SAndroid Build Coastguard Worker   struct zvalue *v = setup_lvalue(0, parmbase, &field_num);
3507*cf5a6c84SAndroid Build Coastguard Worker   struct zvalue *ere = STKP-2;
3508*cf5a6c84SAndroid Build Coastguard Worker   struct zvalue *repl = STKP-1;
3509*cf5a6c84SAndroid Build Coastguard Worker   regex_t rx, *rxp = &rx;
3510*cf5a6c84SAndroid Build Coastguard Worker   rx_zvalue_compile(&rxp, ere);
3511*cf5a6c84SAndroid Build Coastguard Worker   to_str(repl);
3512*cf5a6c84SAndroid Build Coastguard Worker   to_str(v);
3513*cf5a6c84SAndroid Build Coastguard Worker 
3514*cf5a6c84SAndroid Build Coastguard Worker #define SLEN(zvalp) ((zvalp)->vst->size)
3515*cf5a6c84SAndroid Build Coastguard Worker   char *p, *rp0 = repl->vst->str, *rp = rp0, *s = v->vst->str;
3516*cf5a6c84SAndroid Build Coastguard Worker   int namps = 0, nhits = 0, is_sub = (opcode == tksub), eflags = 0;
3517*cf5a6c84SAndroid Build Coastguard Worker   regoff_t so = -1, eo;
3518*cf5a6c84SAndroid Build Coastguard Worker   // Count ampersands in repl string; may be overcount due to \& escapes.
3519*cf5a6c84SAndroid Build Coastguard Worker   for (rp = rp0; *rp; rp++) namps += *rp == '&';
3520*cf5a6c84SAndroid Build Coastguard Worker   p = s;
3521*cf5a6c84SAndroid Build Coastguard Worker   regoff_t need = SLEN(v) + 1;  // capacity needed for result string
3522*cf5a6c84SAndroid Build Coastguard Worker   // A pass just to determine needed destination (result) string size.
3523*cf5a6c84SAndroid Build Coastguard Worker   while(!rx_find(rxp, p, &so, &eo, eflags)) {
3524*cf5a6c84SAndroid Build Coastguard Worker     need += SLEN(repl) + (eo - so) * (namps - 1);
3525*cf5a6c84SAndroid Build Coastguard Worker     if (!*p) break;
3526*cf5a6c84SAndroid Build Coastguard Worker     p += eo ? eo : 1; // ensure progress if empty hit at start
3527*cf5a6c84SAndroid Build Coastguard Worker     if (is_sub) break;
3528*cf5a6c84SAndroid Build Coastguard Worker     eflags |= REG_NOTBOL;
3529*cf5a6c84SAndroid Build Coastguard Worker   }
3530*cf5a6c84SAndroid Build Coastguard Worker 
3531*cf5a6c84SAndroid Build Coastguard Worker   if (so >= 0) {  // at least one hit
3532*cf5a6c84SAndroid Build Coastguard Worker     struct zstring *z = xzalloc(sizeof(*z) + need);
3533*cf5a6c84SAndroid Build Coastguard Worker     z->capacity = need;
3534*cf5a6c84SAndroid Build Coastguard Worker 
3535*cf5a6c84SAndroid Build Coastguard Worker     char *e = z->str; // result destination pointer
3536*cf5a6c84SAndroid Build Coastguard Worker     p = s;
3537*cf5a6c84SAndroid Build Coastguard Worker     eflags = 0;
3538*cf5a6c84SAndroid Build Coastguard Worker     char *ep0 = p, *sp, *ep;
3539*cf5a6c84SAndroid Build Coastguard Worker     while(!rx_find(rxp, p, &so, &eo, eflags)) {
3540*cf5a6c84SAndroid Build Coastguard Worker       sp = p + so;
3541*cf5a6c84SAndroid Build Coastguard Worker       ep = p + eo;
3542*cf5a6c84SAndroid Build Coastguard Worker       memmove(e, ep0, sp - ep0);  // copy unchanged part
3543*cf5a6c84SAndroid Build Coastguard Worker       e += sp - ep0;
3544*cf5a6c84SAndroid Build Coastguard Worker       // Skip match if not at start and just after prev match and this is empty
3545*cf5a6c84SAndroid Build Coastguard Worker       if (p == s || sp - ep0 || eo - so) {
3546*cf5a6c84SAndroid Build Coastguard Worker         nhits++;
3547*cf5a6c84SAndroid Build Coastguard Worker         for (rp = rp0; *rp; rp++) { // copy replacement
3548*cf5a6c84SAndroid Build Coastguard Worker           if (*rp == '&') {
3549*cf5a6c84SAndroid Build Coastguard Worker             memmove(e, sp, eo - so);  //copy match
3550*cf5a6c84SAndroid Build Coastguard Worker             e += eo - so;
3551*cf5a6c84SAndroid Build Coastguard Worker           } else if (*rp == '\\') {
3552*cf5a6c84SAndroid Build Coastguard Worker             if (rp[1] == '&') *e++ = *++rp;
3553*cf5a6c84SAndroid Build Coastguard Worker             else if (rp[1] != '\\') *e++ = *rp;
3554*cf5a6c84SAndroid Build Coastguard Worker             else {
3555*cf5a6c84SAndroid Build Coastguard Worker #ifdef GAWK_SUB
3556*cf5a6c84SAndroid Build Coastguard Worker               if (rp[2] == '\\' && rp[3] == '&') {
3557*cf5a6c84SAndroid Build Coastguard Worker                 rp += 2;
3558*cf5a6c84SAndroid Build Coastguard Worker                 *e++ = *rp;
3559*cf5a6c84SAndroid Build Coastguard Worker               } else if (rp[2] != '&') *e++ = '\\';
3560*cf5a6c84SAndroid Build Coastguard Worker #endif
3561*cf5a6c84SAndroid Build Coastguard Worker               *e++ = *++rp;
3562*cf5a6c84SAndroid Build Coastguard Worker             }
3563*cf5a6c84SAndroid Build Coastguard Worker           } else *e++ = *rp;
3564*cf5a6c84SAndroid Build Coastguard Worker         }
3565*cf5a6c84SAndroid Build Coastguard Worker       }
3566*cf5a6c84SAndroid Build Coastguard Worker       ep0 = ep;
3567*cf5a6c84SAndroid Build Coastguard Worker       if (!*p) break;
3568*cf5a6c84SAndroid Build Coastguard Worker       p += eo ? eo : 1; // ensure progress if empty hit at start
3569*cf5a6c84SAndroid Build Coastguard Worker       if (is_sub) break;
3570*cf5a6c84SAndroid Build Coastguard Worker       eflags |= REG_NOTBOL;
3571*cf5a6c84SAndroid Build Coastguard Worker     }
3572*cf5a6c84SAndroid Build Coastguard Worker     // copy remaining subject string
3573*cf5a6c84SAndroid Build Coastguard Worker     memmove(e, ep0, s + SLEN(v) - ep0);
3574*cf5a6c84SAndroid Build Coastguard Worker     e += s + SLEN(v) - ep0;
3575*cf5a6c84SAndroid Build Coastguard Worker     *e = 0;
3576*cf5a6c84SAndroid Build Coastguard Worker     z->size = e - z->str;
3577*cf5a6c84SAndroid Build Coastguard Worker     zstring_release(&v->vst);
3578*cf5a6c84SAndroid Build Coastguard Worker     v->vst = z;
3579*cf5a6c84SAndroid Build Coastguard Worker   }
3580*cf5a6c84SAndroid Build Coastguard Worker   rx_zvalue_free(rxp, ere);
3581*cf5a6c84SAndroid Build Coastguard Worker   if (!IS_RX(STKP-2)) zstring_release(&STKP[-2].vst);
3582*cf5a6c84SAndroid Build Coastguard Worker   drop_n(3);
3583*cf5a6c84SAndroid Build Coastguard Worker   push_int_val(nhits);
3584*cf5a6c84SAndroid Build Coastguard Worker   if (field_num >= 0) fixup_fields(field_num);
3585*cf5a6c84SAndroid Build Coastguard Worker }
3586*cf5a6c84SAndroid Build Coastguard Worker 
3587*cf5a6c84SAndroid Build Coastguard Worker // Initially set stackp_needmore at MIN_STACK_LEFT before limit.
3588*cf5a6c84SAndroid Build Coastguard Worker // When stackp > stackp_needmore, then expand and reset stackp_needmore
add_stack(struct zvalue ** stackp_needmore)3589*cf5a6c84SAndroid Build Coastguard Worker static void add_stack(struct zvalue **stackp_needmore)
3590*cf5a6c84SAndroid Build Coastguard Worker {
3591*cf5a6c84SAndroid Build Coastguard Worker   int k = stkn(0);  // stack elements in use
3592*cf5a6c84SAndroid Build Coastguard Worker   zlist_expand(&TT.stack);
3593*cf5a6c84SAndroid Build Coastguard Worker   STKP = (struct zvalue *)TT.stack.base + k;
3594*cf5a6c84SAndroid Build Coastguard Worker   *stackp_needmore = (struct zvalue *)TT.stack.limit - MIN_STACK_LEFT;
3595*cf5a6c84SAndroid Build Coastguard Worker }
3596*cf5a6c84SAndroid Build Coastguard Worker 
3597*cf5a6c84SAndroid Build Coastguard Worker #define CLAMP(x, lo, hi) ((x) < (lo) ? (lo) : (x) > (hi) ? (hi) : (x))
3598*cf5a6c84SAndroid Build Coastguard Worker 
3599*cf5a6c84SAndroid Build Coastguard Worker // Main loop of interpreter. Run this once for all BEGIN rules (which
3600*cf5a6c84SAndroid Build Coastguard Worker // have had their instructions chained in compile), all END rules (also
3601*cf5a6c84SAndroid Build Coastguard Worker // chained in compile), and once for each record of the data file(s).
interpx(int start,int * status)3602*cf5a6c84SAndroid Build Coastguard Worker static int interpx(int start, int *status)
3603*cf5a6c84SAndroid Build Coastguard Worker {
3604*cf5a6c84SAndroid Build Coastguard Worker   int *ip = &ZCODE[start];
3605*cf5a6c84SAndroid Build Coastguard Worker   int opcode, op2, k, r, nargs, nsubscrs, range_num, parmbase = 0;
3606*cf5a6c84SAndroid Build Coastguard Worker   int field_num;
3607*cf5a6c84SAndroid Build Coastguard Worker   double nleft, nright, d;
3608*cf5a6c84SAndroid Build Coastguard Worker   double (*mathfunc[])(double) = {cos, sin, exp, log, sqrt, trunc};
3609*cf5a6c84SAndroid Build Coastguard Worker   struct zvalue *v, vv,
3610*cf5a6c84SAndroid Build Coastguard Worker         *stackp_needmore = (struct zvalue*)TT.stack.limit - MIN_STACK_LEFT;
3611*cf5a6c84SAndroid Build Coastguard Worker   while ((opcode = *ip++)) {
3612*cf5a6c84SAndroid Build Coastguard Worker 
3613*cf5a6c84SAndroid Build Coastguard Worker     switch (opcode) {
3614*cf5a6c84SAndroid Build Coastguard Worker       case opquit:
3615*cf5a6c84SAndroid Build Coastguard Worker         return opquit;
3616*cf5a6c84SAndroid Build Coastguard Worker 
3617*cf5a6c84SAndroid Build Coastguard Worker       case tknot:
3618*cf5a6c84SAndroid Build Coastguard Worker         (STKP)->num = ! get_set_logical();
3619*cf5a6c84SAndroid Build Coastguard Worker         break;
3620*cf5a6c84SAndroid Build Coastguard Worker 
3621*cf5a6c84SAndroid Build Coastguard Worker       case opnotnot:
3622*cf5a6c84SAndroid Build Coastguard Worker         get_set_logical();
3623*cf5a6c84SAndroid Build Coastguard Worker         break;
3624*cf5a6c84SAndroid Build Coastguard Worker 
3625*cf5a6c84SAndroid Build Coastguard Worker       case opnegate:
3626*cf5a6c84SAndroid Build Coastguard Worker         STKP->num = -to_num(STKP);
3627*cf5a6c84SAndroid Build Coastguard Worker         break;
3628*cf5a6c84SAndroid Build Coastguard Worker 
3629*cf5a6c84SAndroid Build Coastguard Worker       case tkpow:         // FALLTHROUGH intentional here
3630*cf5a6c84SAndroid Build Coastguard Worker       case tkmul:         // FALLTHROUGH intentional here
3631*cf5a6c84SAndroid Build Coastguard Worker       case tkdiv:         // FALLTHROUGH intentional here
3632*cf5a6c84SAndroid Build Coastguard Worker       case tkmod:         // FALLTHROUGH intentional here
3633*cf5a6c84SAndroid Build Coastguard Worker       case tkplus:        // FALLTHROUGH intentional here
3634*cf5a6c84SAndroid Build Coastguard Worker       case tkminus:
3635*cf5a6c84SAndroid Build Coastguard Worker         nleft = to_num(STKP-1);
3636*cf5a6c84SAndroid Build Coastguard Worker         nright = to_num(STKP);
3637*cf5a6c84SAndroid Build Coastguard Worker         switch (opcode) {
3638*cf5a6c84SAndroid Build Coastguard Worker           case tkpow: nleft = pow(nleft, nright); break;
3639*cf5a6c84SAndroid Build Coastguard Worker           case tkmul: nleft *= nright; break;
3640*cf5a6c84SAndroid Build Coastguard Worker           case tkdiv: nleft /= nright; break;
3641*cf5a6c84SAndroid Build Coastguard Worker           case tkmod: nleft = fmod(nleft, nright); break;
3642*cf5a6c84SAndroid Build Coastguard Worker           case tkplus: nleft += nright; break;
3643*cf5a6c84SAndroid Build Coastguard Worker           case tkminus: nleft -= nright; break;
3644*cf5a6c84SAndroid Build Coastguard Worker         }
3645*cf5a6c84SAndroid Build Coastguard Worker         drop();
3646*cf5a6c84SAndroid Build Coastguard Worker         STKP->num = nleft;
3647*cf5a6c84SAndroid Build Coastguard Worker         break;
3648*cf5a6c84SAndroid Build Coastguard Worker 
3649*cf5a6c84SAndroid Build Coastguard Worker       // FIXME REDO REDO ?
3650*cf5a6c84SAndroid Build Coastguard Worker       case tkcat:
3651*cf5a6c84SAndroid Build Coastguard Worker         to_str(STKP-1);
3652*cf5a6c84SAndroid Build Coastguard Worker         to_str(STKP);
3653*cf5a6c84SAndroid Build Coastguard Worker         STKP[-1].vst = zstring_extend(STKP[-1].vst, STKP[0].vst);
3654*cf5a6c84SAndroid Build Coastguard Worker         drop();
3655*cf5a6c84SAndroid Build Coastguard Worker         break;
3656*cf5a6c84SAndroid Build Coastguard Worker 
3657*cf5a6c84SAndroid Build Coastguard Worker         // Comparisons (with the '<', "<=", "!=", "==", '>', and ">="
3658*cf5a6c84SAndroid Build Coastguard Worker         // operators) shall be made numerically:
3659*cf5a6c84SAndroid Build Coastguard Worker         // * if both operands are numeric,
3660*cf5a6c84SAndroid Build Coastguard Worker         // * if one is numeric and the other has a string value that is a
3661*cf5a6c84SAndroid Build Coastguard Worker         //   numeric string,
3662*cf5a6c84SAndroid Build Coastguard Worker         // * if both have string values that are numeric strings, or
3663*cf5a6c84SAndroid Build Coastguard Worker         // * if one is numeric and the other has the uninitialized value.
3664*cf5a6c84SAndroid Build Coastguard Worker         //
3665*cf5a6c84SAndroid Build Coastguard Worker         // Otherwise, operands shall be converted to strings as required and a
3666*cf5a6c84SAndroid Build Coastguard Worker         // string comparison shall be made as follows:
3667*cf5a6c84SAndroid Build Coastguard Worker         // * For the "!=" and "==" operators, the strings shall be compared to
3668*cf5a6c84SAndroid Build Coastguard Worker         //   check if they are identical (not to check if they collate equally).
3669*cf5a6c84SAndroid Build Coastguard Worker         // * For the other operators, the strings shall be compared using the
3670*cf5a6c84SAndroid Build Coastguard Worker         //   locale-specific collation sequence.
3671*cf5a6c84SAndroid Build Coastguard Worker         //
3672*cf5a6c84SAndroid Build Coastguard Worker         // The value of the comparison expression shall be 1 if the relation is
3673*cf5a6c84SAndroid Build Coastguard Worker         // true, or 0 if the relation is false.
3674*cf5a6c84SAndroid Build Coastguard Worker       case tklt:          // FALLTHROUGH intentional here
3675*cf5a6c84SAndroid Build Coastguard Worker       case tkle:          // FALLTHROUGH intentional here
3676*cf5a6c84SAndroid Build Coastguard Worker       case tkne:          // FALLTHROUGH intentional here
3677*cf5a6c84SAndroid Build Coastguard Worker       case tkeq:          // FALLTHROUGH intentional here
3678*cf5a6c84SAndroid Build Coastguard Worker       case tkgt:          // FALLTHROUGH intentional here
3679*cf5a6c84SAndroid Build Coastguard Worker       case tkge:
3680*cf5a6c84SAndroid Build Coastguard Worker         ; int cmp = 31416;
3681*cf5a6c84SAndroid Build Coastguard Worker 
3682*cf5a6c84SAndroid Build Coastguard Worker         if (  (IS_NUM(&STKP[-1]) &&
3683*cf5a6c84SAndroid Build Coastguard Worker               (STKP[0].flags & (ZF_NUM | ZF_NUMSTR) || !STKP[0].flags)) ||
3684*cf5a6c84SAndroid Build Coastguard Worker               (IS_NUM(&STKP[0]) &&
3685*cf5a6c84SAndroid Build Coastguard Worker               (STKP[-1].flags & (ZF_NUM | ZF_NUMSTR) || !STKP[-1].flags))) {
3686*cf5a6c84SAndroid Build Coastguard Worker           switch (opcode) {
3687*cf5a6c84SAndroid Build Coastguard Worker             case tklt: cmp = STKP[-1].num < STKP[0].num; break;
3688*cf5a6c84SAndroid Build Coastguard Worker             case tkle: cmp = STKP[-1].num <= STKP[0].num; break;
3689*cf5a6c84SAndroid Build Coastguard Worker             case tkne: cmp = STKP[-1].num != STKP[0].num; break;
3690*cf5a6c84SAndroid Build Coastguard Worker             case tkeq: cmp = STKP[-1].num == STKP[0].num; break;
3691*cf5a6c84SAndroid Build Coastguard Worker             case tkgt: cmp = STKP[-1].num > STKP[0].num; break;
3692*cf5a6c84SAndroid Build Coastguard Worker             case tkge: cmp = STKP[-1].num >= STKP[0].num; break;
3693*cf5a6c84SAndroid Build Coastguard Worker           }
3694*cf5a6c84SAndroid Build Coastguard Worker         } else {
3695*cf5a6c84SAndroid Build Coastguard Worker           cmp = strcmp(to_str(STKP-1)->vst->str, to_str(STKP)->vst->str);
3696*cf5a6c84SAndroid Build Coastguard Worker           switch (opcode) {
3697*cf5a6c84SAndroid Build Coastguard Worker             case tklt: cmp = cmp < 0; break;
3698*cf5a6c84SAndroid Build Coastguard Worker             case tkle: cmp = cmp <= 0; break;
3699*cf5a6c84SAndroid Build Coastguard Worker             case tkne: cmp = cmp != 0; break;
3700*cf5a6c84SAndroid Build Coastguard Worker             case tkeq: cmp = cmp == 0; break;
3701*cf5a6c84SAndroid Build Coastguard Worker             case tkgt: cmp = cmp > 0; break;
3702*cf5a6c84SAndroid Build Coastguard Worker             case tkge: cmp = cmp >= 0; break;
3703*cf5a6c84SAndroid Build Coastguard Worker           }
3704*cf5a6c84SAndroid Build Coastguard Worker         }
3705*cf5a6c84SAndroid Build Coastguard Worker         drop();
3706*cf5a6c84SAndroid Build Coastguard Worker         drop();
3707*cf5a6c84SAndroid Build Coastguard Worker         push_int_val(cmp);
3708*cf5a6c84SAndroid Build Coastguard Worker         break;
3709*cf5a6c84SAndroid Build Coastguard Worker 
3710*cf5a6c84SAndroid Build Coastguard Worker       case opmatchrec:
3711*cf5a6c84SAndroid Build Coastguard Worker         op2 = *ip++;
3712*cf5a6c84SAndroid Build Coastguard Worker         int mret = match(&FIELD[0], &LITERAL[op2]);
3713*cf5a6c84SAndroid Build Coastguard Worker         push_int_val(!mret);
3714*cf5a6c84SAndroid Build Coastguard Worker         break;
3715*cf5a6c84SAndroid Build Coastguard Worker 
3716*cf5a6c84SAndroid Build Coastguard Worker       case tkmatchop:
3717*cf5a6c84SAndroid Build Coastguard Worker       case tknotmatch:
3718*cf5a6c84SAndroid Build Coastguard Worker         mret = match(STKP-1, STKP); // mret == 0 if match
3719*cf5a6c84SAndroid Build Coastguard Worker         drop();
3720*cf5a6c84SAndroid Build Coastguard Worker         drop();
3721*cf5a6c84SAndroid Build Coastguard Worker         push_int_val(!mret == (opcode == tkmatchop));
3722*cf5a6c84SAndroid Build Coastguard Worker         break;
3723*cf5a6c84SAndroid Build Coastguard Worker 
3724*cf5a6c84SAndroid Build Coastguard Worker       case tkpowasgn:     // FALLTHROUGH intentional here
3725*cf5a6c84SAndroid Build Coastguard Worker       case tkmodasgn:     // FALLTHROUGH intentional here
3726*cf5a6c84SAndroid Build Coastguard Worker       case tkmulasgn:     // FALLTHROUGH intentional here
3727*cf5a6c84SAndroid Build Coastguard Worker       case tkdivasgn:     // FALLTHROUGH intentional here
3728*cf5a6c84SAndroid Build Coastguard Worker       case tkaddasgn:     // FALLTHROUGH intentional here
3729*cf5a6c84SAndroid Build Coastguard Worker       case tksubasgn:
3730*cf5a6c84SAndroid Build Coastguard Worker         // Stack is: ... scalar_ref value_to_op_by
3731*cf5a6c84SAndroid Build Coastguard Worker         // or ... subscript_val map_ref value_to_op_by
3732*cf5a6c84SAndroid Build Coastguard Worker         // or ... fieldref value_to_op_by
3733*cf5a6c84SAndroid Build Coastguard Worker         v = setup_lvalue(1, parmbase, &field_num);
3734*cf5a6c84SAndroid Build Coastguard Worker         to_num(v);
3735*cf5a6c84SAndroid Build Coastguard Worker         to_num(STKP);
3736*cf5a6c84SAndroid Build Coastguard Worker         switch (opcode) {
3737*cf5a6c84SAndroid Build Coastguard Worker           case tkpowasgn:
3738*cf5a6c84SAndroid Build Coastguard Worker             // TODO
3739*cf5a6c84SAndroid Build Coastguard Worker             v->num = pow(v->num, STKP->num);
3740*cf5a6c84SAndroid Build Coastguard Worker             break;
3741*cf5a6c84SAndroid Build Coastguard Worker           case tkmodasgn:
3742*cf5a6c84SAndroid Build Coastguard Worker             // TODO
3743*cf5a6c84SAndroid Build Coastguard Worker             v->num = fmod(v->num, STKP->num);
3744*cf5a6c84SAndroid Build Coastguard Worker             break;
3745*cf5a6c84SAndroid Build Coastguard Worker           case tkmulasgn:
3746*cf5a6c84SAndroid Build Coastguard Worker             v->num *= STKP->num;
3747*cf5a6c84SAndroid Build Coastguard Worker             break;
3748*cf5a6c84SAndroid Build Coastguard Worker           case tkdivasgn:
3749*cf5a6c84SAndroid Build Coastguard Worker             v->num /= STKP->num;
3750*cf5a6c84SAndroid Build Coastguard Worker             break;
3751*cf5a6c84SAndroid Build Coastguard Worker           case tkaddasgn:
3752*cf5a6c84SAndroid Build Coastguard Worker             v->num += STKP->num;
3753*cf5a6c84SAndroid Build Coastguard Worker             break;
3754*cf5a6c84SAndroid Build Coastguard Worker           case tksubasgn:
3755*cf5a6c84SAndroid Build Coastguard Worker             v->num -= STKP->num;
3756*cf5a6c84SAndroid Build Coastguard Worker             break;
3757*cf5a6c84SAndroid Build Coastguard Worker         }
3758*cf5a6c84SAndroid Build Coastguard Worker 
3759*cf5a6c84SAndroid Build Coastguard Worker         drop_n(2);
3760*cf5a6c84SAndroid Build Coastguard Worker         v->flags = ZF_NUM;
3761*cf5a6c84SAndroid Build Coastguard Worker         push_val(v);
3762*cf5a6c84SAndroid Build Coastguard Worker         if (field_num >= 0) fixup_fields(field_num);
3763*cf5a6c84SAndroid Build Coastguard Worker         break;
3764*cf5a6c84SAndroid Build Coastguard Worker 
3765*cf5a6c84SAndroid Build Coastguard Worker       case tkasgn:
3766*cf5a6c84SAndroid Build Coastguard Worker         // Stack is: ... scalar_ref value_to_assign
3767*cf5a6c84SAndroid Build Coastguard Worker         // or ... subscript_val map_ref value_to_assign
3768*cf5a6c84SAndroid Build Coastguard Worker         // or ... fieldref value_to_assign
3769*cf5a6c84SAndroid Build Coastguard Worker         v = setup_lvalue(1, parmbase, &field_num);
3770*cf5a6c84SAndroid Build Coastguard Worker         force_maybemap_to_scalar(STKP);
3771*cf5a6c84SAndroid Build Coastguard Worker         zvalue_copy(v, STKP);
3772*cf5a6c84SAndroid Build Coastguard Worker         swap();
3773*cf5a6c84SAndroid Build Coastguard Worker         drop();
3774*cf5a6c84SAndroid Build Coastguard Worker         if (field_num >= 0) fixup_fields(field_num);
3775*cf5a6c84SAndroid Build Coastguard Worker         break;
3776*cf5a6c84SAndroid Build Coastguard Worker 
3777*cf5a6c84SAndroid Build Coastguard Worker       case tkincr:        // FALLTHROUGH intentional here
3778*cf5a6c84SAndroid Build Coastguard Worker       case tkdecr:        // FALLTHROUGH intentional here
3779*cf5a6c84SAndroid Build Coastguard Worker       case oppreincr:     // FALLTHROUGH intentional here
3780*cf5a6c84SAndroid Build Coastguard Worker       case oppredecr:
3781*cf5a6c84SAndroid Build Coastguard Worker         // Stack is: ... scalar_ref
3782*cf5a6c84SAndroid Build Coastguard Worker         // or ... subscript_val map_ref
3783*cf5a6c84SAndroid Build Coastguard Worker         // or ... fieldnum fieldref
3784*cf5a6c84SAndroid Build Coastguard Worker         v = setup_lvalue(0, parmbase, &field_num);
3785*cf5a6c84SAndroid Build Coastguard Worker         to_num(v);
3786*cf5a6c84SAndroid Build Coastguard Worker         switch (opcode) {
3787*cf5a6c84SAndroid Build Coastguard Worker           case tkincr: case tkdecr:
3788*cf5a6c84SAndroid Build Coastguard Worker             // Must be done in this order because push_val(v) may move v,
3789*cf5a6c84SAndroid Build Coastguard Worker             // invalidating the pointer.
3790*cf5a6c84SAndroid Build Coastguard Worker             v->num += (opcode == tkincr) ? 1 : -1;
3791*cf5a6c84SAndroid Build Coastguard Worker             push_val(v);
3792*cf5a6c84SAndroid Build Coastguard Worker             // Now reverse the incr/decr on the top TT.stack val.
3793*cf5a6c84SAndroid Build Coastguard Worker             STKP->num -= (opcode == tkincr) ? 1 : -1;
3794*cf5a6c84SAndroid Build Coastguard Worker             break;
3795*cf5a6c84SAndroid Build Coastguard Worker           case oppreincr: case oppredecr:
3796*cf5a6c84SAndroid Build Coastguard Worker             v->num += (opcode == oppreincr) ? 1 : -1;
3797*cf5a6c84SAndroid Build Coastguard Worker             push_val(v);
3798*cf5a6c84SAndroid Build Coastguard Worker             break;
3799*cf5a6c84SAndroid Build Coastguard Worker         }
3800*cf5a6c84SAndroid Build Coastguard Worker         swap();
3801*cf5a6c84SAndroid Build Coastguard Worker         drop();
3802*cf5a6c84SAndroid Build Coastguard Worker         if (field_num >= 0) fixup_fields(field_num);
3803*cf5a6c84SAndroid Build Coastguard Worker         break;
3804*cf5a6c84SAndroid Build Coastguard Worker 
3805*cf5a6c84SAndroid Build Coastguard Worker       case tknumber:      // FALLTHROUGH intentional here
3806*cf5a6c84SAndroid Build Coastguard Worker       case tkstring:      // FALLTHROUGH intentional here
3807*cf5a6c84SAndroid Build Coastguard Worker       case tkregex:
3808*cf5a6c84SAndroid Build Coastguard Worker         push_val(&LITERAL[*ip++]);
3809*cf5a6c84SAndroid Build Coastguard Worker         break;
3810*cf5a6c84SAndroid Build Coastguard Worker 
3811*cf5a6c84SAndroid Build Coastguard Worker       case tkprint:
3812*cf5a6c84SAndroid Build Coastguard Worker       case tkprintf:
3813*cf5a6c84SAndroid Build Coastguard Worker         nargs = *ip++;
3814*cf5a6c84SAndroid Build Coastguard Worker         int outmode = *ip++;
3815*cf5a6c84SAndroid Build Coastguard Worker         struct zfile *outfp = TT.zstdout;
3816*cf5a6c84SAndroid Build Coastguard Worker         switch (outmode) {
3817*cf5a6c84SAndroid Build Coastguard Worker           case tkgt: outfp = setup_file(1, "w"); break;     // file
3818*cf5a6c84SAndroid Build Coastguard Worker           case tkappend: outfp = setup_file(1, "a"); break; // file
3819*cf5a6c84SAndroid Build Coastguard Worker           case tkpipe: outfp = setup_file(0, "w"); break;   // pipe
3820*cf5a6c84SAndroid Build Coastguard Worker           default: nargs++; break;
3821*cf5a6c84SAndroid Build Coastguard Worker         }
3822*cf5a6c84SAndroid Build Coastguard Worker         nargs--;
3823*cf5a6c84SAndroid Build Coastguard Worker         if (opcode == tkprintf) {
3824*cf5a6c84SAndroid Build Coastguard Worker           varprint(fprintf, outfp->fp, nargs);
3825*cf5a6c84SAndroid Build Coastguard Worker           drop_n(nargs);
3826*cf5a6c84SAndroid Build Coastguard Worker           break;
3827*cf5a6c84SAndroid Build Coastguard Worker         }
3828*cf5a6c84SAndroid Build Coastguard Worker         if (!nargs) {
3829*cf5a6c84SAndroid Build Coastguard Worker           fprintf(outfp->fp, "%s", to_str(&FIELD[0])->vst->str);
3830*cf5a6c84SAndroid Build Coastguard Worker         } else {
3831*cf5a6c84SAndroid Build Coastguard Worker           struct zvalue tempv = uninit_zvalue;
3832*cf5a6c84SAndroid Build Coastguard Worker           zvalue_copy(&tempv, &STACK[OFS]);
3833*cf5a6c84SAndroid Build Coastguard Worker           to_str(&tempv);
3834*cf5a6c84SAndroid Build Coastguard Worker           for (int k = 0; k < nargs; k++) {
3835*cf5a6c84SAndroid Build Coastguard Worker             if (k) fprintf(outfp->fp, "%s", tempv.vst->str);
3836*cf5a6c84SAndroid Build Coastguard Worker             int sp = stkn(nargs - 1 - k);
3837*cf5a6c84SAndroid Build Coastguard Worker             ////// FIXME refcnt -- prob. don't need to copy from TT.stack?
3838*cf5a6c84SAndroid Build Coastguard Worker             v = &STACK[sp];
3839*cf5a6c84SAndroid Build Coastguard Worker             to_str_fmt(v, OFMT);
3840*cf5a6c84SAndroid Build Coastguard Worker             struct zstring *zs = v->vst;
3841*cf5a6c84SAndroid Build Coastguard Worker             fprintf(outfp->fp, "%s", zs ? zs->str : "");
3842*cf5a6c84SAndroid Build Coastguard Worker           }
3843*cf5a6c84SAndroid Build Coastguard Worker           zvalue_release_zstring(&tempv);
3844*cf5a6c84SAndroid Build Coastguard Worker           drop_n(nargs);
3845*cf5a6c84SAndroid Build Coastguard Worker         }
3846*cf5a6c84SAndroid Build Coastguard Worker         fputs(ENSURE_STR(&STACK[ORS])->vst->str, outfp->fp);
3847*cf5a6c84SAndroid Build Coastguard Worker         break;
3848*cf5a6c84SAndroid Build Coastguard Worker 
3849*cf5a6c84SAndroid Build Coastguard Worker       case opdrop:
3850*cf5a6c84SAndroid Build Coastguard Worker         drop();
3851*cf5a6c84SAndroid Build Coastguard Worker         break;
3852*cf5a6c84SAndroid Build Coastguard Worker 
3853*cf5a6c84SAndroid Build Coastguard Worker       case opdrop_n:
3854*cf5a6c84SAndroid Build Coastguard Worker         drop_n(*ip++);
3855*cf5a6c84SAndroid Build Coastguard Worker         break;
3856*cf5a6c84SAndroid Build Coastguard Worker 
3857*cf5a6c84SAndroid Build Coastguard Worker         // Stack frame layout relative to parmbase:
3858*cf5a6c84SAndroid Build Coastguard Worker #define RETURN_VALUE    -4
3859*cf5a6c84SAndroid Build Coastguard Worker #define RETURN_ADDR     -3
3860*cf5a6c84SAndroid Build Coastguard Worker #define PREV_PARMBASE   -2
3861*cf5a6c84SAndroid Build Coastguard Worker #define ARG_CNT         -1
3862*cf5a6c84SAndroid Build Coastguard Worker #define FUNCTION_NUM    0
3863*cf5a6c84SAndroid Build Coastguard Worker         // Actual args follow, starting at parmbase + 1
3864*cf5a6c84SAndroid Build Coastguard Worker       case tkfunction:    // function definition
3865*cf5a6c84SAndroid Build Coastguard Worker         op2 = *ip++;    // func table num
3866*cf5a6c84SAndroid Build Coastguard Worker         struct functab_slot *pfdef = &FUNC_DEF[op2];
3867*cf5a6c84SAndroid Build Coastguard Worker         struct zlist *loctab = &pfdef->function_locals;
3868*cf5a6c84SAndroid Build Coastguard Worker         int nparms = zlist_len(loctab)-1;
3869*cf5a6c84SAndroid Build Coastguard Worker 
3870*cf5a6c84SAndroid Build Coastguard Worker         nargs = popnumval();
3871*cf5a6c84SAndroid Build Coastguard Worker         int newparmbase = stkn(nargs);
3872*cf5a6c84SAndroid Build Coastguard Worker         STACK[newparmbase + PREV_PARMBASE].num = parmbase;
3873*cf5a6c84SAndroid Build Coastguard Worker         parmbase = newparmbase;
3874*cf5a6c84SAndroid Build Coastguard Worker         for ( ;nargs > nparms; nargs--)
3875*cf5a6c84SAndroid Build Coastguard Worker           drop();
3876*cf5a6c84SAndroid Build Coastguard Worker         for ( ;nargs < nparms; nargs++) {
3877*cf5a6c84SAndroid Build Coastguard Worker           // Push additional "args" that were not passed by the caller, to
3878*cf5a6c84SAndroid Build Coastguard Worker           // match the formal parameters (parms) defined in the function
3879*cf5a6c84SAndroid Build Coastguard Worker           // definition. In the local var table we may have the type as scalar
3880*cf5a6c84SAndroid Build Coastguard Worker           // or map if it is used as such within the function. In that case we
3881*cf5a6c84SAndroid Build Coastguard Worker           // init the pushed arg from the type of the locals table.
3882*cf5a6c84SAndroid Build Coastguard Worker           // But if a var appears only as a bare arg in a function call it will
3883*cf5a6c84SAndroid Build Coastguard Worker           // not be typed in the locals table. In that case we can only say it
3884*cf5a6c84SAndroid Build Coastguard Worker           // "may be" a map, but we have to assume the possibility and attach a
3885*cf5a6c84SAndroid Build Coastguard Worker           // map to the var. When/if the var is used as a map or scalar in the
3886*cf5a6c84SAndroid Build Coastguard Worker           // called function it will be converted to a map or scalar as
3887*cf5a6c84SAndroid Build Coastguard Worker           // required.
3888*cf5a6c84SAndroid Build Coastguard Worker           // See force_maybemap_to_scalar().
3889*cf5a6c84SAndroid Build Coastguard Worker           struct symtab_slot *q = &((struct symtab_slot *)loctab->base)[nargs+1];
3890*cf5a6c84SAndroid Build Coastguard Worker           vv = (struct zvalue)ZVINIT(q->flags, 0, 0);
3891*cf5a6c84SAndroid Build Coastguard Worker           if (vv.flags == 0) {
3892*cf5a6c84SAndroid Build Coastguard Worker             zvalue_map_init(&vv);
3893*cf5a6c84SAndroid Build Coastguard Worker             vv.flags = ZF_MAYBEMAP;
3894*cf5a6c84SAndroid Build Coastguard Worker           } else if (IS_MAP(&vv)) {
3895*cf5a6c84SAndroid Build Coastguard Worker             zvalue_map_init(&vv);
3896*cf5a6c84SAndroid Build Coastguard Worker           } else {
3897*cf5a6c84SAndroid Build Coastguard Worker             vv.flags = 0;
3898*cf5a6c84SAndroid Build Coastguard Worker           }
3899*cf5a6c84SAndroid Build Coastguard Worker           push_val(&vv);
3900*cf5a6c84SAndroid Build Coastguard Worker         }
3901*cf5a6c84SAndroid Build Coastguard Worker         break;
3902*cf5a6c84SAndroid Build Coastguard Worker 
3903*cf5a6c84SAndroid Build Coastguard Worker       case tkreturn:
3904*cf5a6c84SAndroid Build Coastguard Worker         nparms = *ip++;
3905*cf5a6c84SAndroid Build Coastguard Worker         nargs = STACK[parmbase+ARG_CNT].num;
3906*cf5a6c84SAndroid Build Coastguard Worker         force_maybemap_to_scalar(STKP); // Unneeded?
3907*cf5a6c84SAndroid Build Coastguard Worker         zvalue_copy(&STACK[parmbase+RETURN_VALUE], STKP);
3908*cf5a6c84SAndroid Build Coastguard Worker         drop();
3909*cf5a6c84SAndroid Build Coastguard Worker         // Remove the local args (not supplied by caller) from TT.stack, check to
3910*cf5a6c84SAndroid Build Coastguard Worker         // release any map data created.
3911*cf5a6c84SAndroid Build Coastguard Worker         while (stkn(0) > parmbase + nargs) {
3912*cf5a6c84SAndroid Build Coastguard Worker           if ((STKP)->flags & ZF_ANYMAP) {
3913*cf5a6c84SAndroid Build Coastguard Worker             zmap_delete_map_incl_slotdata((STKP)->map);
3914*cf5a6c84SAndroid Build Coastguard Worker             xfree((STKP)->map);
3915*cf5a6c84SAndroid Build Coastguard Worker           }
3916*cf5a6c84SAndroid Build Coastguard Worker           drop();
3917*cf5a6c84SAndroid Build Coastguard Worker         }
3918*cf5a6c84SAndroid Build Coastguard Worker         while (stkn(0) > parmbase + RETURN_VALUE)
3919*cf5a6c84SAndroid Build Coastguard Worker           drop();
3920*cf5a6c84SAndroid Build Coastguard Worker         ip = &ZCODE[(int)STACK[parmbase+RETURN_ADDR].num];
3921*cf5a6c84SAndroid Build Coastguard Worker         parmbase = STACK[parmbase+PREV_PARMBASE].num;
3922*cf5a6c84SAndroid Build Coastguard Worker         break;
3923*cf5a6c84SAndroid Build Coastguard Worker 
3924*cf5a6c84SAndroid Build Coastguard Worker       case opprepcall:    // function call prep
3925*cf5a6c84SAndroid Build Coastguard Worker         if (STKP > stackp_needmore) add_stack(&stackp_needmore);
3926*cf5a6c84SAndroid Build Coastguard Worker         push_int_val(0);      // return value placeholder
3927*cf5a6c84SAndroid Build Coastguard Worker         push_int_val(0);      // return addr
3928*cf5a6c84SAndroid Build Coastguard Worker         push_int_val(0);      // parmbase
3929*cf5a6c84SAndroid Build Coastguard Worker         push_int_val(0);      // arg count
3930*cf5a6c84SAndroid Build Coastguard Worker         push_int_val(*ip++);  // function tbl ref
3931*cf5a6c84SAndroid Build Coastguard Worker         break;
3932*cf5a6c84SAndroid Build Coastguard Worker 
3933*cf5a6c84SAndroid Build Coastguard Worker       case tkfunc:        // function call
3934*cf5a6c84SAndroid Build Coastguard Worker         nargs = *ip++;
3935*cf5a6c84SAndroid Build Coastguard Worker         newparmbase = stkn(nargs);
3936*cf5a6c84SAndroid Build Coastguard Worker         STACK[newparmbase+RETURN_ADDR].num = ip - &ZCODE[0];
3937*cf5a6c84SAndroid Build Coastguard Worker         STACK[newparmbase+ARG_CNT].num = nargs;
3938*cf5a6c84SAndroid Build Coastguard Worker         push_int_val(nargs);      // FIXME TODO pass this in a zregister?
3939*cf5a6c84SAndroid Build Coastguard Worker         ip = &ZCODE[FUNC_DEF[(int)STACK[newparmbase+FUNCTION_NUM].num].zcode_addr];
3940*cf5a6c84SAndroid Build Coastguard Worker         break;
3941*cf5a6c84SAndroid Build Coastguard Worker 
3942*cf5a6c84SAndroid Build Coastguard Worker       case tkrbracket:    // concat multiple map subscripts
3943*cf5a6c84SAndroid Build Coastguard Worker         nsubscrs = *ip++;
3944*cf5a6c84SAndroid Build Coastguard Worker         while (--nsubscrs) {
3945*cf5a6c84SAndroid Build Coastguard Worker           swap();
3946*cf5a6c84SAndroid Build Coastguard Worker           to_str(STKP);
3947*cf5a6c84SAndroid Build Coastguard Worker           push_val(&STACK[SUBSEP]);
3948*cf5a6c84SAndroid Build Coastguard Worker           to_str(STKP);
3949*cf5a6c84SAndroid Build Coastguard Worker           STKP[-1].vst = zstring_extend(STKP[-1].vst, STKP->vst);
3950*cf5a6c84SAndroid Build Coastguard Worker           drop();
3951*cf5a6c84SAndroid Build Coastguard Worker           swap();
3952*cf5a6c84SAndroid Build Coastguard Worker           to_str(STKP);
3953*cf5a6c84SAndroid Build Coastguard Worker           STKP[-1].vst = zstring_extend(STKP[-1].vst, STKP->vst);
3954*cf5a6c84SAndroid Build Coastguard Worker           drop();
3955*cf5a6c84SAndroid Build Coastguard Worker         }
3956*cf5a6c84SAndroid Build Coastguard Worker         break;
3957*cf5a6c84SAndroid Build Coastguard Worker 
3958*cf5a6c84SAndroid Build Coastguard Worker       case opmapdelete:
3959*cf5a6c84SAndroid Build Coastguard Worker       case tkdelete:
3960*cf5a6c84SAndroid Build Coastguard Worker         k = STKP->num;
3961*cf5a6c84SAndroid Build Coastguard Worker         if (k < 0) k = parmbase - k;    // loc of var on TT.stack
3962*cf5a6c84SAndroid Build Coastguard Worker         v = &STACK[k];
3963*cf5a6c84SAndroid Build Coastguard Worker         force_maybemap_to_map(v);
3964*cf5a6c84SAndroid Build Coastguard Worker         if (opcode == opmapdelete) {
3965*cf5a6c84SAndroid Build Coastguard Worker           zmap_delete_map(v->map);
3966*cf5a6c84SAndroid Build Coastguard Worker         } else {
3967*cf5a6c84SAndroid Build Coastguard Worker           drop();
3968*cf5a6c84SAndroid Build Coastguard Worker           zmap_delete(v->map, to_str(STKP)->vst);
3969*cf5a6c84SAndroid Build Coastguard Worker         }
3970*cf5a6c84SAndroid Build Coastguard Worker         drop();
3971*cf5a6c84SAndroid Build Coastguard Worker         break;
3972*cf5a6c84SAndroid Build Coastguard Worker 
3973*cf5a6c84SAndroid Build Coastguard Worker       case opmap:
3974*cf5a6c84SAndroid Build Coastguard Worker         op2 = *ip++;
3975*cf5a6c84SAndroid Build Coastguard Worker         k = op2 < 0 ? parmbase - op2 : op2;
3976*cf5a6c84SAndroid Build Coastguard Worker         v = &STACK[k];
3977*cf5a6c84SAndroid Build Coastguard Worker         force_maybemap_to_map(v);
3978*cf5a6c84SAndroid Build Coastguard Worker         if (!IS_MAP(v)) FATAL("scalar in array context");
3979*cf5a6c84SAndroid Build Coastguard Worker         v = get_map_val(v, STKP);
3980*cf5a6c84SAndroid Build Coastguard Worker         drop();     // drop subscript
3981*cf5a6c84SAndroid Build Coastguard Worker         push_val(v);
3982*cf5a6c84SAndroid Build Coastguard Worker         break;
3983*cf5a6c84SAndroid Build Coastguard Worker 
3984*cf5a6c84SAndroid Build Coastguard Worker       case tkin:
3985*cf5a6c84SAndroid Build Coastguard Worker         if (!(STKP->flags & ZF_ANYMAP)) FATAL("scalar in array context");
3986*cf5a6c84SAndroid Build Coastguard Worker         v = zmap_find(STKP->map, to_str(STKP-1)->vst);
3987*cf5a6c84SAndroid Build Coastguard Worker         drop();
3988*cf5a6c84SAndroid Build Coastguard Worker         drop();
3989*cf5a6c84SAndroid Build Coastguard Worker         push_int_val(v ? 1 : 0);
3990*cf5a6c84SAndroid Build Coastguard Worker         break;
3991*cf5a6c84SAndroid Build Coastguard Worker 
3992*cf5a6c84SAndroid Build Coastguard Worker       case opmapiternext:
3993*cf5a6c84SAndroid Build Coastguard Worker         op2 = *ip++;
3994*cf5a6c84SAndroid Build Coastguard Worker         v = STKP-1;
3995*cf5a6c84SAndroid Build Coastguard Worker         force_maybemap_to_map(v);
3996*cf5a6c84SAndroid Build Coastguard Worker         if (!IS_MAP(v)) FATAL("scalar in array context");
3997*cf5a6c84SAndroid Build Coastguard Worker         struct zmap *m = v->map;   // Need for MAPSLOT macro
3998*cf5a6c84SAndroid Build Coastguard Worker         int zlen = zlist_len(&m->slot);
3999*cf5a6c84SAndroid Build Coastguard Worker         int kk = STKP->num + 1;
4000*cf5a6c84SAndroid Build Coastguard Worker         while (kk < zlen && !(MAPSLOT[kk].key)) // skip deleted slots
4001*cf5a6c84SAndroid Build Coastguard Worker           kk++;
4002*cf5a6c84SAndroid Build Coastguard Worker         STKP->num = kk; // save index for next iteration
4003*cf5a6c84SAndroid Build Coastguard Worker         if (kk < zlen) {
4004*cf5a6c84SAndroid Build Coastguard Worker           struct zvalue *var = setup_lvalue(2, parmbase, &field_num);
4005*cf5a6c84SAndroid Build Coastguard Worker           var->flags = ZF_STR;
4006*cf5a6c84SAndroid Build Coastguard Worker           zstring_release(&var->vst);
4007*cf5a6c84SAndroid Build Coastguard Worker           var->vst = MAPSLOT[kk].key;
4008*cf5a6c84SAndroid Build Coastguard Worker           zstring_incr_refcnt(var->vst);
4009*cf5a6c84SAndroid Build Coastguard Worker           ip += op2;
4010*cf5a6c84SAndroid Build Coastguard Worker         }
4011*cf5a6c84SAndroid Build Coastguard Worker         break;
4012*cf5a6c84SAndroid Build Coastguard Worker 
4013*cf5a6c84SAndroid Build Coastguard Worker       case tkvar:
4014*cf5a6c84SAndroid Build Coastguard Worker         op2 = *ip++;
4015*cf5a6c84SAndroid Build Coastguard Worker         k = op2 < 0 ? parmbase - op2 : op2;
4016*cf5a6c84SAndroid Build Coastguard Worker         v = &STACK[k];
4017*cf5a6c84SAndroid Build Coastguard Worker         push_val(v);
4018*cf5a6c84SAndroid Build Coastguard Worker         break;
4019*cf5a6c84SAndroid Build Coastguard Worker 
4020*cf5a6c84SAndroid Build Coastguard Worker       case tkfield:
4021*cf5a6c84SAndroid Build Coastguard Worker         // tkfield op has "dummy" 2nd word so that convert_push_to_reference(void)
4022*cf5a6c84SAndroid Build Coastguard Worker         // can find either tkfield or tkvar at same place (ZCODE[TT.zcode_last-1]).
4023*cf5a6c84SAndroid Build Coastguard Worker         ip++; // skip dummy "operand" instruction field
4024*cf5a6c84SAndroid Build Coastguard Worker         push_field((int)(to_num(STKP)));
4025*cf5a6c84SAndroid Build Coastguard Worker 
4026*cf5a6c84SAndroid Build Coastguard Worker         swap();
4027*cf5a6c84SAndroid Build Coastguard Worker         drop();
4028*cf5a6c84SAndroid Build Coastguard Worker         break;
4029*cf5a6c84SAndroid Build Coastguard Worker 
4030*cf5a6c84SAndroid Build Coastguard Worker       case oppush:
4031*cf5a6c84SAndroid Build Coastguard Worker         push_int_val(*ip++);
4032*cf5a6c84SAndroid Build Coastguard Worker         break;
4033*cf5a6c84SAndroid Build Coastguard Worker 
4034*cf5a6c84SAndroid Build Coastguard Worker       case tkand:
4035*cf5a6c84SAndroid Build Coastguard Worker         op2 = *ip++;
4036*cf5a6c84SAndroid Build Coastguard Worker         if (get_set_logical()) drop();
4037*cf5a6c84SAndroid Build Coastguard Worker         else ip += op2;
4038*cf5a6c84SAndroid Build Coastguard Worker         break;
4039*cf5a6c84SAndroid Build Coastguard Worker 
4040*cf5a6c84SAndroid Build Coastguard Worker       case tkor:
4041*cf5a6c84SAndroid Build Coastguard Worker         op2 = *ip++;
4042*cf5a6c84SAndroid Build Coastguard Worker         if (!get_set_logical()) drop();
4043*cf5a6c84SAndroid Build Coastguard Worker         else ip += op2;
4044*cf5a6c84SAndroid Build Coastguard Worker         break;
4045*cf5a6c84SAndroid Build Coastguard Worker 
4046*cf5a6c84SAndroid Build Coastguard Worker       case tkwhile:
4047*cf5a6c84SAndroid Build Coastguard Worker         (STKP)->num = ! get_set_logical();
4048*cf5a6c84SAndroid Build Coastguard Worker         ATTR_FALLTHROUGH_INTENDED;
4049*cf5a6c84SAndroid Build Coastguard Worker         // FALLTHROUGH to tkternif
4050*cf5a6c84SAndroid Build Coastguard Worker       case tkif:
4051*cf5a6c84SAndroid Build Coastguard Worker         // FALLTHROUGH to tkternif
4052*cf5a6c84SAndroid Build Coastguard Worker       case tkternif:
4053*cf5a6c84SAndroid Build Coastguard Worker         op2 = *ip++;
4054*cf5a6c84SAndroid Build Coastguard Worker         int t = get_set_logical();  // FIXME only need to get, not set
4055*cf5a6c84SAndroid Build Coastguard Worker         drop();
4056*cf5a6c84SAndroid Build Coastguard Worker         if (!t) ip += op2;
4057*cf5a6c84SAndroid Build Coastguard Worker         break;
4058*cf5a6c84SAndroid Build Coastguard Worker 
4059*cf5a6c84SAndroid Build Coastguard Worker       case tkelse:        // FALLTHROUGH intentional here
4060*cf5a6c84SAndroid Build Coastguard Worker       case tkternelse:    // FALLTHROUGH intentional here
4061*cf5a6c84SAndroid Build Coastguard Worker       case tkbreak:       // FALLTHROUGH intentional here
4062*cf5a6c84SAndroid Build Coastguard Worker       case tkcontinue:    // FALLTHROUGH intentional here
4063*cf5a6c84SAndroid Build Coastguard Worker       case opjump:
4064*cf5a6c84SAndroid Build Coastguard Worker         op2 = *ip++;
4065*cf5a6c84SAndroid Build Coastguard Worker         ip += op2;
4066*cf5a6c84SAndroid Build Coastguard Worker         break;
4067*cf5a6c84SAndroid Build Coastguard Worker 
4068*cf5a6c84SAndroid Build Coastguard Worker       case opvarref:
4069*cf5a6c84SAndroid Build Coastguard Worker         op2 = *ip++;
4070*cf5a6c84SAndroid Build Coastguard Worker         vv = (struct zvalue)ZVINIT(ZF_REF, op2, 0);
4071*cf5a6c84SAndroid Build Coastguard Worker         push_val(&vv);
4072*cf5a6c84SAndroid Build Coastguard Worker         break;
4073*cf5a6c84SAndroid Build Coastguard Worker 
4074*cf5a6c84SAndroid Build Coastguard Worker       case opmapref:
4075*cf5a6c84SAndroid Build Coastguard Worker         op2 = *ip++;
4076*cf5a6c84SAndroid Build Coastguard Worker         vv = (struct zvalue)ZVINIT(ZF_MAPREF, op2, 0);
4077*cf5a6c84SAndroid Build Coastguard Worker         push_val(&vv);
4078*cf5a6c84SAndroid Build Coastguard Worker         break;
4079*cf5a6c84SAndroid Build Coastguard Worker 
4080*cf5a6c84SAndroid Build Coastguard Worker       case opfldref:
4081*cf5a6c84SAndroid Build Coastguard Worker         to_num(STKP);
4082*cf5a6c84SAndroid Build Coastguard Worker         (STKP)->flags |= ZF_FIELDREF;
4083*cf5a6c84SAndroid Build Coastguard Worker         ip++; // skip dummy "operand" instruction field
4084*cf5a6c84SAndroid Build Coastguard Worker         break;
4085*cf5a6c84SAndroid Build Coastguard Worker 
4086*cf5a6c84SAndroid Build Coastguard Worker       case opprintrec:
4087*cf5a6c84SAndroid Build Coastguard Worker         puts(to_str(&FIELD[0])->vst->str);
4088*cf5a6c84SAndroid Build Coastguard Worker         break;
4089*cf5a6c84SAndroid Build Coastguard Worker 
4090*cf5a6c84SAndroid Build Coastguard Worker       case oprange1:
4091*cf5a6c84SAndroid Build Coastguard Worker         range_num = *ip++;
4092*cf5a6c84SAndroid Build Coastguard Worker         op2 = *ip++;
4093*cf5a6c84SAndroid Build Coastguard Worker         if (TT.range_sw[range_num]) ip += op2;
4094*cf5a6c84SAndroid Build Coastguard Worker         break;
4095*cf5a6c84SAndroid Build Coastguard Worker 
4096*cf5a6c84SAndroid Build Coastguard Worker       case oprange2:
4097*cf5a6c84SAndroid Build Coastguard Worker         range_num = *ip++;
4098*cf5a6c84SAndroid Build Coastguard Worker         op2 = *ip++;
4099*cf5a6c84SAndroid Build Coastguard Worker         t = get_set_logical();  // FIXME only need to get, not set
4100*cf5a6c84SAndroid Build Coastguard Worker         drop();
4101*cf5a6c84SAndroid Build Coastguard Worker         if (t) TT.range_sw[range_num] = 1;
4102*cf5a6c84SAndroid Build Coastguard Worker         else ip += op2;
4103*cf5a6c84SAndroid Build Coastguard Worker         break;
4104*cf5a6c84SAndroid Build Coastguard Worker 
4105*cf5a6c84SAndroid Build Coastguard Worker       case oprange3:
4106*cf5a6c84SAndroid Build Coastguard Worker         range_num = *ip++;
4107*cf5a6c84SAndroid Build Coastguard Worker         t = get_set_logical();  // FIXME only need to get, not set
4108*cf5a6c84SAndroid Build Coastguard Worker         drop();
4109*cf5a6c84SAndroid Build Coastguard Worker         if (t) TT.range_sw[range_num] = 0;
4110*cf5a6c84SAndroid Build Coastguard Worker         break;
4111*cf5a6c84SAndroid Build Coastguard Worker 
4112*cf5a6c84SAndroid Build Coastguard Worker       case tkexit:
4113*cf5a6c84SAndroid Build Coastguard Worker         r = popnumval();
4114*cf5a6c84SAndroid Build Coastguard Worker         if (r != NO_EXIT_STATUS) *status = (int)r & 255;
4115*cf5a6c84SAndroid Build Coastguard Worker         // TODO FIXME do we need NO_EXIT_STATUS at all? Just use 0?
4116*cf5a6c84SAndroid Build Coastguard Worker         ATTR_FALLTHROUGH_INTENDED;
4117*cf5a6c84SAndroid Build Coastguard Worker       case tknext:
4118*cf5a6c84SAndroid Build Coastguard Worker       case tknextfile:
4119*cf5a6c84SAndroid Build Coastguard Worker         return opcode;
4120*cf5a6c84SAndroid Build Coastguard Worker 
4121*cf5a6c84SAndroid Build Coastguard Worker       case tkgetline:
4122*cf5a6c84SAndroid Build Coastguard Worker         nargs = *ip++;
4123*cf5a6c84SAndroid Build Coastguard Worker         int source = *ip++;
4124*cf5a6c84SAndroid Build Coastguard Worker         // TT.stack is:
4125*cf5a6c84SAndroid Build Coastguard Worker         // if tkgetline 0 tkeof:   (nothing stacked; plain getline)
4126*cf5a6c84SAndroid Build Coastguard Worker         // if tkgetline 1 tkeof:   (lvalue)
4127*cf5a6c84SAndroid Build Coastguard Worker         // if tkgetline 1 tklt:    (filename_string)
4128*cf5a6c84SAndroid Build Coastguard Worker         // if tkgetline 2 tklt:    (lvalue) (filename_string)
4129*cf5a6c84SAndroid Build Coastguard Worker         // if tkgetline 1 tkpipe:  (pipe_command_string)
4130*cf5a6c84SAndroid Build Coastguard Worker         // if tkgetline 2 tkpipe:  (pipe_command_string) (lvalue)
4131*cf5a6c84SAndroid Build Coastguard Worker         // effect is to set:
4132*cf5a6c84SAndroid Build Coastguard Worker         // if tkgetline 0 tkeof:   $0 NF NR FNR
4133*cf5a6c84SAndroid Build Coastguard Worker         // if tkgetline 1 tkeof:   var NR FNR
4134*cf5a6c84SAndroid Build Coastguard Worker         // if tkgetline 1 tklt:    $0 NF
4135*cf5a6c84SAndroid Build Coastguard Worker         // if tkgetline 2 tklt:    var
4136*cf5a6c84SAndroid Build Coastguard Worker         // if tkgetline 1 tkpipe:  $0 NF
4137*cf5a6c84SAndroid Build Coastguard Worker         // if tkgetline 2 tkpipe:  var
4138*cf5a6c84SAndroid Build Coastguard Worker         // Ensure pipe cmd on top
4139*cf5a6c84SAndroid Build Coastguard Worker         if (nargs == 2 && source == tkpipe) swap();
4140*cf5a6c84SAndroid Build Coastguard Worker         struct zfile *zfp = 0;
4141*cf5a6c84SAndroid Build Coastguard Worker         if (source == tklt || source == tkpipe) {
4142*cf5a6c84SAndroid Build Coastguard Worker           zfp = setup_file(source == tklt, "r");
4143*cf5a6c84SAndroid Build Coastguard Worker           nargs--;
4144*cf5a6c84SAndroid Build Coastguard Worker         }
4145*cf5a6c84SAndroid Build Coastguard Worker         // now cases are:
4146*cf5a6c84SAndroid Build Coastguard Worker         // nargs source  TT.stack
4147*cf5a6c84SAndroid Build Coastguard Worker         //  0 tkeof:   (nothing; plain getline) from current data file
4148*cf5a6c84SAndroid Build Coastguard Worker         //  1 tkeof:   (lvalue)  from current data file
4149*cf5a6c84SAndroid Build Coastguard Worker         //  0 tklt:    (nothing) from named file in 'stream'
4150*cf5a6c84SAndroid Build Coastguard Worker         //  1 tklt:    (lvalue)  from  named file in 'stream'
4151*cf5a6c84SAndroid Build Coastguard Worker         //  0 tkpipe:  (nothing) from piped command in 'stream'
4152*cf5a6c84SAndroid Build Coastguard Worker         //  1 tkpipe:  (lvalue)  from piped command in 'stream'
4153*cf5a6c84SAndroid Build Coastguard Worker         v = nargs ? setup_lvalue(0, parmbase, &field_num) : 0;
4154*cf5a6c84SAndroid Build Coastguard Worker         if (v) drop();
4155*cf5a6c84SAndroid Build Coastguard Worker         // source is tkeof (no pipe/file), tklt (file), or tkpipe (pipe)
4156*cf5a6c84SAndroid Build Coastguard Worker         // stream is name of file or pipe
4157*cf5a6c84SAndroid Build Coastguard Worker         // v is NULL or an lvalue ref
4158*cf5a6c84SAndroid Build Coastguard Worker         if (zfp != badfile) push_int_val(awk_getline(source, zfp, v));
4159*cf5a6c84SAndroid Build Coastguard Worker         else push_int_val(-1);
4160*cf5a6c84SAndroid Build Coastguard Worker 
4161*cf5a6c84SAndroid Build Coastguard Worker         // fake return value for now
4162*cf5a6c84SAndroid Build Coastguard Worker         break;
4163*cf5a6c84SAndroid Build Coastguard Worker 
4164*cf5a6c84SAndroid Build Coastguard Worker         ////// builtin functions ///////
4165*cf5a6c84SAndroid Build Coastguard Worker 
4166*cf5a6c84SAndroid Build Coastguard Worker       case tksplit:
4167*cf5a6c84SAndroid Build Coastguard Worker         nargs = *ip++;
4168*cf5a6c84SAndroid Build Coastguard Worker         if (nargs == 2) push_val(&STACK[FS]);
4169*cf5a6c84SAndroid Build Coastguard Worker         struct zstring *s = to_str(STKP-2)->vst;
4170*cf5a6c84SAndroid Build Coastguard Worker         force_maybemap_to_map(STKP-1);
4171*cf5a6c84SAndroid Build Coastguard Worker         struct zvalue *a = STKP-1;
4172*cf5a6c84SAndroid Build Coastguard Worker         struct zvalue *fs = STKP;
4173*cf5a6c84SAndroid Build Coastguard Worker         zmap_delete_map(a->map);
4174*cf5a6c84SAndroid Build Coastguard Worker         k = split(s, a, fs);
4175*cf5a6c84SAndroid Build Coastguard Worker         drop_n(3);
4176*cf5a6c84SAndroid Build Coastguard Worker         push_int_val(k);
4177*cf5a6c84SAndroid Build Coastguard Worker         break;
4178*cf5a6c84SAndroid Build Coastguard Worker 
4179*cf5a6c84SAndroid Build Coastguard Worker       case tkmatch:
4180*cf5a6c84SAndroid Build Coastguard Worker         nargs = *ip++;
4181*cf5a6c84SAndroid Build Coastguard Worker         if (!IS_RX(STKP)) to_str(STKP);
4182*cf5a6c84SAndroid Build Coastguard Worker         regex_t rx_pat, *rxp = &rx_pat;
4183*cf5a6c84SAndroid Build Coastguard Worker         rx_zvalue_compile(&rxp, STKP);
4184*cf5a6c84SAndroid Build Coastguard Worker         regoff_t rso = 0, reo = 0;  // shut up warning (may be uninit)
4185*cf5a6c84SAndroid Build Coastguard Worker         k = rx_find(rxp, to_str(STKP-1)->vst->str, &rso, &reo, 0);
4186*cf5a6c84SAndroid Build Coastguard Worker         rx_zvalue_free(rxp, STKP);
4187*cf5a6c84SAndroid Build Coastguard Worker         // Force these to num before setting.
4188*cf5a6c84SAndroid Build Coastguard Worker         to_num(&STACK[RSTART]);
4189*cf5a6c84SAndroid Build Coastguard Worker         to_num(&STACK[RLENGTH]);
4190*cf5a6c84SAndroid Build Coastguard Worker         if (k) STACK[RSTART].num = 0, STACK[RLENGTH].num = -1;
4191*cf5a6c84SAndroid Build Coastguard Worker         else {
4192*cf5a6c84SAndroid Build Coastguard Worker           reo = utf8cnt(STKP[-1].vst->str, reo);
4193*cf5a6c84SAndroid Build Coastguard Worker           rso = utf8cnt(STKP[-1].vst->str, rso);
4194*cf5a6c84SAndroid Build Coastguard Worker           STACK[RSTART].num = rso + 1, STACK[RLENGTH].num = reo - rso;
4195*cf5a6c84SAndroid Build Coastguard Worker         }
4196*cf5a6c84SAndroid Build Coastguard Worker         drop();
4197*cf5a6c84SAndroid Build Coastguard Worker         drop();
4198*cf5a6c84SAndroid Build Coastguard Worker         push_int_val(k ? 0 : rso + 1);
4199*cf5a6c84SAndroid Build Coastguard Worker         break;
4200*cf5a6c84SAndroid Build Coastguard Worker 
4201*cf5a6c84SAndroid Build Coastguard Worker       case tksub:
4202*cf5a6c84SAndroid Build Coastguard Worker       case tkgsub:
4203*cf5a6c84SAndroid Build Coastguard Worker         gsub(opcode, *ip++, parmbase);  // tksub/tkgsub, args
4204*cf5a6c84SAndroid Build Coastguard Worker         break;
4205*cf5a6c84SAndroid Build Coastguard Worker 
4206*cf5a6c84SAndroid Build Coastguard Worker       case tksubstr:
4207*cf5a6c84SAndroid Build Coastguard Worker         nargs = *ip++;
4208*cf5a6c84SAndroid Build Coastguard Worker         struct zstring *zz = to_str(STKP - nargs + 1)->vst;
4209*cf5a6c84SAndroid Build Coastguard Worker         int nchars = utf8cnt(zz->str, zz->size);  // number of utf8 codepoints
4210*cf5a6c84SAndroid Build Coastguard Worker         // Offset of start of string (in chars not bytes); convert 1-based to 0-based
4211*cf5a6c84SAndroid Build Coastguard Worker         ssize_t mm = CLAMP(trunc(to_num(STKP - nargs + 2)) - 1, 0, nchars);
4212*cf5a6c84SAndroid Build Coastguard Worker         ssize_t nn = nchars - mm;   // max possible substring length (chars)
4213*cf5a6c84SAndroid Build Coastguard Worker         if (nargs == 3) nn = CLAMP(trunc(to_num(STKP)), 0, nn);
4214*cf5a6c84SAndroid Build Coastguard Worker         mm = bytesinutf8(zz->str, zz->size, mm);
4215*cf5a6c84SAndroid Build Coastguard Worker         nn = bytesinutf8(zz->str + mm, zz->size - mm, nn);
4216*cf5a6c84SAndroid Build Coastguard Worker         struct zstring *zzz = new_zstring(zz->str + mm, nn);
4217*cf5a6c84SAndroid Build Coastguard Worker         zstring_release(&(STKP - nargs + 1)->vst);
4218*cf5a6c84SAndroid Build Coastguard Worker         (STKP - nargs + 1)->vst = zzz;
4219*cf5a6c84SAndroid Build Coastguard Worker         drop_n(nargs - 1);
4220*cf5a6c84SAndroid Build Coastguard Worker         break;
4221*cf5a6c84SAndroid Build Coastguard Worker 
4222*cf5a6c84SAndroid Build Coastguard Worker       case tkindex:
4223*cf5a6c84SAndroid Build Coastguard Worker         nargs = *ip++;
4224*cf5a6c84SAndroid Build Coastguard Worker         char *s1 = to_str(STKP-1)->vst->str;
4225*cf5a6c84SAndroid Build Coastguard Worker         char *s3 = strstr(s1, to_str(STKP)->vst->str);
4226*cf5a6c84SAndroid Build Coastguard Worker         ptrdiff_t offs = s3 ? utf8cnt(s1, s3 - s1) + 1 : 0;
4227*cf5a6c84SAndroid Build Coastguard Worker         drop();
4228*cf5a6c84SAndroid Build Coastguard Worker         drop();
4229*cf5a6c84SAndroid Build Coastguard Worker         push_int_val(offs);
4230*cf5a6c84SAndroid Build Coastguard Worker         break;
4231*cf5a6c84SAndroid Build Coastguard Worker 
4232*cf5a6c84SAndroid Build Coastguard Worker       case tkband:
4233*cf5a6c84SAndroid Build Coastguard Worker       case tkbor:
4234*cf5a6c84SAndroid Build Coastguard Worker       case tkbxor:
4235*cf5a6c84SAndroid Build Coastguard Worker       case tklshift:
4236*cf5a6c84SAndroid Build Coastguard Worker       case tkrshift:
4237*cf5a6c84SAndroid Build Coastguard Worker         ; size_t acc = to_num(STKP);
4238*cf5a6c84SAndroid Build Coastguard Worker         nargs = *ip++;
4239*cf5a6c84SAndroid Build Coastguard Worker         for (int i = 1; i < nargs; i++) switch (opcode) {
4240*cf5a6c84SAndroid Build Coastguard Worker           case tkband: acc &= (size_t)to_num(STKP-i); break;
4241*cf5a6c84SAndroid Build Coastguard Worker           case tkbor:  acc |= (size_t)to_num(STKP-i); break;
4242*cf5a6c84SAndroid Build Coastguard Worker           case tkbxor: acc ^= (size_t)to_num(STKP-i); break;
4243*cf5a6c84SAndroid Build Coastguard Worker           case tklshift: acc = (size_t)to_num(STKP-i) << acc; break;
4244*cf5a6c84SAndroid Build Coastguard Worker           case tkrshift: acc = (size_t)to_num(STKP-i) >> acc; break;
4245*cf5a6c84SAndroid Build Coastguard Worker         }
4246*cf5a6c84SAndroid Build Coastguard Worker         drop_n(nargs);
4247*cf5a6c84SAndroid Build Coastguard Worker         push_int_val(acc);
4248*cf5a6c84SAndroid Build Coastguard Worker         break;
4249*cf5a6c84SAndroid Build Coastguard Worker 
4250*cf5a6c84SAndroid Build Coastguard Worker       case tktolower:
4251*cf5a6c84SAndroid Build Coastguard Worker       case tktoupper:
4252*cf5a6c84SAndroid Build Coastguard Worker         nargs = *ip++;
4253*cf5a6c84SAndroid Build Coastguard Worker         struct zstring *z = to_str(STKP)->vst;
4254*cf5a6c84SAndroid Build Coastguard Worker         unsigned zzlen = z->size + 4; // Allow for expansion
4255*cf5a6c84SAndroid Build Coastguard Worker         zz = zstring_update(0, zzlen, "", 0);
4256*cf5a6c84SAndroid Build Coastguard Worker         char *p = z->str, *e = z->str + z->size, *q = zz->str;
4257*cf5a6c84SAndroid Build Coastguard Worker         // Similar logic to toybox strlower(), but fixed.
4258*cf5a6c84SAndroid Build Coastguard Worker         while (p < e) {
4259*cf5a6c84SAndroid Build Coastguard Worker           unsigned wch;
4260*cf5a6c84SAndroid Build Coastguard Worker           int len = utf8towc(&wch, p, e-p);
4261*cf5a6c84SAndroid Build Coastguard Worker           if (len < 1) {  // nul byte, error, or truncated code
4262*cf5a6c84SAndroid Build Coastguard Worker             *q++ = *p++;
4263*cf5a6c84SAndroid Build Coastguard Worker             continue;
4264*cf5a6c84SAndroid Build Coastguard Worker           }
4265*cf5a6c84SAndroid Build Coastguard Worker           p += len;
4266*cf5a6c84SAndroid Build Coastguard Worker           wch = (opcode == tktolower ? towlower : towupper)(wch);
4267*cf5a6c84SAndroid Build Coastguard Worker           len = wctoutf8(q, wch);
4268*cf5a6c84SAndroid Build Coastguard Worker           q += len;
4269*cf5a6c84SAndroid Build Coastguard Worker           // Need realloc here if overflow possible
4270*cf5a6c84SAndroid Build Coastguard Worker           if ((len = q - zz->str) + 4 < (int)zzlen) continue;
4271*cf5a6c84SAndroid Build Coastguard Worker           zz = zstring_update(zz, zzlen = len + 16, "", 0);
4272*cf5a6c84SAndroid Build Coastguard Worker           q = zz->str + len;
4273*cf5a6c84SAndroid Build Coastguard Worker         }
4274*cf5a6c84SAndroid Build Coastguard Worker         *q = 0;
4275*cf5a6c84SAndroid Build Coastguard Worker         zz->size = q - zz->str;
4276*cf5a6c84SAndroid Build Coastguard Worker         zstring_release(&z);
4277*cf5a6c84SAndroid Build Coastguard Worker         STKP->vst = zz;
4278*cf5a6c84SAndroid Build Coastguard Worker         break;
4279*cf5a6c84SAndroid Build Coastguard Worker 
4280*cf5a6c84SAndroid Build Coastguard Worker       case tklength:
4281*cf5a6c84SAndroid Build Coastguard Worker         nargs = *ip++;
4282*cf5a6c84SAndroid Build Coastguard Worker         v = nargs ? STKP : &FIELD[0];
4283*cf5a6c84SAndroid Build Coastguard Worker         force_maybemap_to_map(v);
4284*cf5a6c84SAndroid Build Coastguard Worker         if (IS_MAP(v)) k = v->map->count - v->map->deleted;
4285*cf5a6c84SAndroid Build Coastguard Worker         else {
4286*cf5a6c84SAndroid Build Coastguard Worker           to_str(v);
4287*cf5a6c84SAndroid Build Coastguard Worker           k = utf8cnt(v->vst->str, v->vst->size);
4288*cf5a6c84SAndroid Build Coastguard Worker         }
4289*cf5a6c84SAndroid Build Coastguard Worker         if (nargs) drop();
4290*cf5a6c84SAndroid Build Coastguard Worker         push_int_val(k);
4291*cf5a6c84SAndroid Build Coastguard Worker         break;
4292*cf5a6c84SAndroid Build Coastguard Worker 
4293*cf5a6c84SAndroid Build Coastguard Worker       case tksystem:
4294*cf5a6c84SAndroid Build Coastguard Worker         nargs = *ip++;
4295*cf5a6c84SAndroid Build Coastguard Worker         fflush(stdout);
4296*cf5a6c84SAndroid Build Coastguard Worker         fflush(stderr);
4297*cf5a6c84SAndroid Build Coastguard Worker         r = system(to_str(STKP)->vst->str);
4298*cf5a6c84SAndroid Build Coastguard Worker #ifdef WEXITSTATUS
4299*cf5a6c84SAndroid Build Coastguard Worker         // WEXITSTATUS is in sys/wait.h, but I'm not including that.
4300*cf5a6c84SAndroid Build Coastguard Worker         // It seems to also be in stdlib.h in gcc and musl-gcc.
4301*cf5a6c84SAndroid Build Coastguard Worker         // No idea how portable this is!
4302*cf5a6c84SAndroid Build Coastguard Worker         if (WIFEXITED(r)) r = WEXITSTATUS(r);
4303*cf5a6c84SAndroid Build Coastguard Worker #endif
4304*cf5a6c84SAndroid Build Coastguard Worker         drop();
4305*cf5a6c84SAndroid Build Coastguard Worker         push_int_val(r);
4306*cf5a6c84SAndroid Build Coastguard Worker         break;
4307*cf5a6c84SAndroid Build Coastguard Worker 
4308*cf5a6c84SAndroid Build Coastguard Worker       case tkfflush:
4309*cf5a6c84SAndroid Build Coastguard Worker         nargs = *ip++;
4310*cf5a6c84SAndroid Build Coastguard Worker         r = fflush_file(nargs);
4311*cf5a6c84SAndroid Build Coastguard Worker         if (nargs) drop();
4312*cf5a6c84SAndroid Build Coastguard Worker         push_int_val(r);
4313*cf5a6c84SAndroid Build Coastguard Worker         break;
4314*cf5a6c84SAndroid Build Coastguard Worker 
4315*cf5a6c84SAndroid Build Coastguard Worker       case tkclose:
4316*cf5a6c84SAndroid Build Coastguard Worker         nargs = *ip++;
4317*cf5a6c84SAndroid Build Coastguard Worker         r = close_file(to_str(STKP)->vst->str);
4318*cf5a6c84SAndroid Build Coastguard Worker         drop();
4319*cf5a6c84SAndroid Build Coastguard Worker         push_int_val(r);
4320*cf5a6c84SAndroid Build Coastguard Worker         break;
4321*cf5a6c84SAndroid Build Coastguard Worker 
4322*cf5a6c84SAndroid Build Coastguard Worker       case tksprintf:
4323*cf5a6c84SAndroid Build Coastguard Worker         nargs = *ip++;
4324*cf5a6c84SAndroid Build Coastguard Worker         zstring_release(&TT.rgl.zspr);
4325*cf5a6c84SAndroid Build Coastguard Worker         TT.rgl.zspr = new_zstring("", 0);
4326*cf5a6c84SAndroid Build Coastguard Worker         varprint(fsprintf, 0, nargs);
4327*cf5a6c84SAndroid Build Coastguard Worker         drop_n(nargs);
4328*cf5a6c84SAndroid Build Coastguard Worker         vv = (struct zvalue)ZVINIT(ZF_STR, 0, TT.rgl.zspr);
4329*cf5a6c84SAndroid Build Coastguard Worker         push_val(&vv);
4330*cf5a6c84SAndroid Build Coastguard Worker         break;
4331*cf5a6c84SAndroid Build Coastguard Worker 
4332*cf5a6c84SAndroid Build Coastguard Worker       // Math builtins -- move here (per Oliver Webb suggestion)
4333*cf5a6c84SAndroid Build Coastguard Worker       case tkatan2:
4334*cf5a6c84SAndroid Build Coastguard Worker         nargs = *ip++;
4335*cf5a6c84SAndroid Build Coastguard Worker         d = atan2(to_num(STKP-1), to_num(STKP));
4336*cf5a6c84SAndroid Build Coastguard Worker         drop();
4337*cf5a6c84SAndroid Build Coastguard Worker         STKP->num = d;
4338*cf5a6c84SAndroid Build Coastguard Worker         break;
4339*cf5a6c84SAndroid Build Coastguard Worker       case tkrand:
4340*cf5a6c84SAndroid Build Coastguard Worker         nargs = *ip++;
4341*cf5a6c84SAndroid Build Coastguard Worker         push_int_val(0);
4342*cf5a6c84SAndroid Build Coastguard Worker         // Get all 53 mantissa bits in play:
4343*cf5a6c84SAndroid Build Coastguard Worker         // (upper 26 bits * 2^27 + upper 27 bits) / 2^53
4344*cf5a6c84SAndroid Build Coastguard Worker         STKP->num =
4345*cf5a6c84SAndroid Build Coastguard Worker           ((random() >> 5) * 134217728.0 + (random() >> 4)) / 9007199254740992.0;
4346*cf5a6c84SAndroid Build Coastguard Worker         break;
4347*cf5a6c84SAndroid Build Coastguard Worker       case tksrand:
4348*cf5a6c84SAndroid Build Coastguard Worker         nargs = *ip++;
4349*cf5a6c84SAndroid Build Coastguard Worker         if (nargs == 1) {
4350*cf5a6c84SAndroid Build Coastguard Worker           STKP->num = seedrand(to_num(STKP));
4351*cf5a6c84SAndroid Build Coastguard Worker         } else push_int_val(seedrand(time(0)));
4352*cf5a6c84SAndroid Build Coastguard Worker         break;
4353*cf5a6c84SAndroid Build Coastguard Worker       case tkcos: case tksin: case tkexp: case tklog: case tksqrt: case tkint:
4354*cf5a6c84SAndroid Build Coastguard Worker         nargs = *ip++;
4355*cf5a6c84SAndroid Build Coastguard Worker         STKP->num = mathfunc[opcode-tkcos](to_num(STKP));
4356*cf5a6c84SAndroid Build Coastguard Worker         break;
4357*cf5a6c84SAndroid Build Coastguard Worker 
4358*cf5a6c84SAndroid Build Coastguard Worker       default:
4359*cf5a6c84SAndroid Build Coastguard Worker         // This should never happen:
4360*cf5a6c84SAndroid Build Coastguard Worker         error_exit("!!! Unimplemented opcode %d", opcode);
4361*cf5a6c84SAndroid Build Coastguard Worker     }
4362*cf5a6c84SAndroid Build Coastguard Worker   }
4363*cf5a6c84SAndroid Build Coastguard Worker   return opquit;
4364*cf5a6c84SAndroid Build Coastguard Worker }
4365*cf5a6c84SAndroid Build Coastguard Worker 
4366*cf5a6c84SAndroid Build Coastguard Worker // interp() wraps the main interpreter loop interpx(). The main purpose
4367*cf5a6c84SAndroid Build Coastguard Worker // is to allow the TT.stack to be readjusted after an 'exit' from a function.
4368*cf5a6c84SAndroid Build Coastguard Worker // Also catches errors, as the normal operation should leave the TT.stack
4369*cf5a6c84SAndroid Build Coastguard Worker // depth unchanged after each run through the rules.
interp(int start,int * status)4370*cf5a6c84SAndroid Build Coastguard Worker static int interp(int start, int *status)
4371*cf5a6c84SAndroid Build Coastguard Worker {
4372*cf5a6c84SAndroid Build Coastguard Worker   int stkptrbefore = stkn(0);
4373*cf5a6c84SAndroid Build Coastguard Worker   int r = interpx(start, status);
4374*cf5a6c84SAndroid Build Coastguard Worker   // If exit from function, TT.stack will be loaded with args etc. Clean it.
4375*cf5a6c84SAndroid Build Coastguard Worker   if (r == tkexit) {
4376*cf5a6c84SAndroid Build Coastguard Worker     // TODO FIXME is this safe? Just remove extra entries?
4377*cf5a6c84SAndroid Build Coastguard Worker     STKP = &STACK[stkptrbefore];
4378*cf5a6c84SAndroid Build Coastguard Worker   }
4379*cf5a6c84SAndroid Build Coastguard Worker   if (stkn(0) - stkptrbefore)
4380*cf5a6c84SAndroid Build Coastguard Worker     error_exit("!!AWK BUG stack pointer offset: %d", stkn(0) - stkptrbefore);
4381*cf5a6c84SAndroid Build Coastguard Worker   return r;
4382*cf5a6c84SAndroid Build Coastguard Worker }
4383*cf5a6c84SAndroid Build Coastguard Worker 
insert_argv_map(struct zvalue * map,int key,char * value)4384*cf5a6c84SAndroid Build Coastguard Worker static void insert_argv_map(struct zvalue *map, int key, char *value)
4385*cf5a6c84SAndroid Build Coastguard Worker {
4386*cf5a6c84SAndroid Build Coastguard Worker   struct zvalue zkey = ZVINIT(ZF_STR, 0, num_to_zstring(key, ENSURE_STR(&STACK[CONVFMT])->vst->str));
4387*cf5a6c84SAndroid Build Coastguard Worker   struct zvalue *v = get_map_val(map, &zkey);
4388*cf5a6c84SAndroid Build Coastguard Worker   zvalue_release_zstring(&zkey);
4389*cf5a6c84SAndroid Build Coastguard Worker   zvalue_release_zstring(v);
4390*cf5a6c84SAndroid Build Coastguard Worker   *v = new_str_val(value);
4391*cf5a6c84SAndroid Build Coastguard Worker   check_numeric_string(v);
4392*cf5a6c84SAndroid Build Coastguard Worker }
4393*cf5a6c84SAndroid Build Coastguard Worker 
init_globals(int optind,int argc,char ** argv,char * sepstring,struct arg_list * assign_args)4394*cf5a6c84SAndroid Build Coastguard Worker static void init_globals(int optind, int argc, char **argv, char *sepstring,
4395*cf5a6c84SAndroid Build Coastguard Worker     struct arg_list *assign_args)
4396*cf5a6c84SAndroid Build Coastguard Worker {
4397*cf5a6c84SAndroid Build Coastguard Worker   // Global variables reside at the bottom of the TT.stack. Start with the awk
4398*cf5a6c84SAndroid Build Coastguard Worker   // "special variables":  ARGC, ARGV, CONVFMT, ENVIRON, FILENAME, FNR, FS, NF,
4399*cf5a6c84SAndroid Build Coastguard Worker   // NR, OFMT, OFS, ORS, RLENGTH, RS, RSTART, SUBSEP
4400*cf5a6c84SAndroid Build Coastguard Worker 
4401*cf5a6c84SAndroid Build Coastguard Worker   STACK[CONVFMT] = new_str_val("%.6g");
4402*cf5a6c84SAndroid Build Coastguard Worker   // Init ENVIRON map.
4403*cf5a6c84SAndroid Build Coastguard Worker   struct zvalue m = ZVINIT(ZF_MAP, 0, 0);
4404*cf5a6c84SAndroid Build Coastguard Worker   zvalue_map_init(&m);
4405*cf5a6c84SAndroid Build Coastguard Worker   STACK[ENVIRON] = m;
4406*cf5a6c84SAndroid Build Coastguard Worker   for (char **pkey = environ; *pkey; pkey++) {
4407*cf5a6c84SAndroid Build Coastguard Worker     char *pval = strchr(*pkey, '=');
4408*cf5a6c84SAndroid Build Coastguard Worker     if (!pval) continue;
4409*cf5a6c84SAndroid Build Coastguard Worker     struct zvalue zkey = ZVINIT(ZF_STR, 0, new_zstring(*pkey, pval - *pkey));
4410*cf5a6c84SAndroid Build Coastguard Worker     struct zvalue *v = get_map_val(&m, &zkey);
4411*cf5a6c84SAndroid Build Coastguard Worker     zstring_release(&zkey.vst);
4412*cf5a6c84SAndroid Build Coastguard Worker     if (v->vst) FFATAL("env var dup? (%s)", pkey);
4413*cf5a6c84SAndroid Build Coastguard Worker     *v = new_str_val(++pval);    // FIXME refcnt
4414*cf5a6c84SAndroid Build Coastguard Worker     check_numeric_string(v);
4415*cf5a6c84SAndroid Build Coastguard Worker   }
4416*cf5a6c84SAndroid Build Coastguard Worker 
4417*cf5a6c84SAndroid Build Coastguard Worker   // Init ARGV map.
4418*cf5a6c84SAndroid Build Coastguard Worker   m = (struct zvalue)ZVINIT(ZF_MAP, 0, 0);
4419*cf5a6c84SAndroid Build Coastguard Worker   zvalue_map_init(&m);
4420*cf5a6c84SAndroid Build Coastguard Worker   STACK[ARGV] = m;
4421*cf5a6c84SAndroid Build Coastguard Worker   insert_argv_map(&m, 0, TT.progname);
4422*cf5a6c84SAndroid Build Coastguard Worker   int nargc = 1;
4423*cf5a6c84SAndroid Build Coastguard Worker   for (int k = optind; k < argc; k++) {
4424*cf5a6c84SAndroid Build Coastguard Worker     insert_argv_map(&m, nargc, argv[k]);
4425*cf5a6c84SAndroid Build Coastguard Worker     nargc++;
4426*cf5a6c84SAndroid Build Coastguard Worker   }
4427*cf5a6c84SAndroid Build Coastguard Worker 
4428*cf5a6c84SAndroid Build Coastguard Worker   // Init rest of the awk special variables.
4429*cf5a6c84SAndroid Build Coastguard Worker   STACK[ARGC] = (struct zvalue)ZVINIT(ZF_NUM, nargc, 0);
4430*cf5a6c84SAndroid Build Coastguard Worker   STACK[FILENAME] = new_str_val("");
4431*cf5a6c84SAndroid Build Coastguard Worker   STACK[FNR] = (struct zvalue)ZVINIT(ZF_NUM, 0, 0);
4432*cf5a6c84SAndroid Build Coastguard Worker   STACK[FS] = new_str_val(sepstring);
4433*cf5a6c84SAndroid Build Coastguard Worker   STACK[NF] = (struct zvalue)ZVINIT(ZF_NUM, 0, 0);
4434*cf5a6c84SAndroid Build Coastguard Worker   STACK[NR] = (struct zvalue)ZVINIT(ZF_NUM, 0, 0);
4435*cf5a6c84SAndroid Build Coastguard Worker   STACK[OFMT] = new_str_val("%.6g");
4436*cf5a6c84SAndroid Build Coastguard Worker   STACK[OFS] = new_str_val(" ");
4437*cf5a6c84SAndroid Build Coastguard Worker   STACK[ORS] = new_str_val("\n");
4438*cf5a6c84SAndroid Build Coastguard Worker   STACK[RLENGTH] = (struct zvalue)ZVINIT(ZF_NUM, 0, 0);
4439*cf5a6c84SAndroid Build Coastguard Worker   STACK[RS] = new_str_val("\n");
4440*cf5a6c84SAndroid Build Coastguard Worker   STACK[RSTART] = (struct zvalue)ZVINIT(ZF_NUM, 0, 0);
4441*cf5a6c84SAndroid Build Coastguard Worker   STACK[SUBSEP] = new_str_val("\034");
4442*cf5a6c84SAndroid Build Coastguard Worker 
4443*cf5a6c84SAndroid Build Coastguard Worker   // Init program globals.
4444*cf5a6c84SAndroid Build Coastguard Worker   //
4445*cf5a6c84SAndroid Build Coastguard Worker   // Push global variables on the TT.stack at offsets matching their index in the
4446*cf5a6c84SAndroid Build Coastguard Worker   // global var table.  In the global var table we may have the type as scalar
4447*cf5a6c84SAndroid Build Coastguard Worker   // or map if it is used as such in the program. In that case we init the
4448*cf5a6c84SAndroid Build Coastguard Worker   // pushed arg from the type of the globals table.
4449*cf5a6c84SAndroid Build Coastguard Worker   // But if a global var appears only as a bare arg in a function call it will
4450*cf5a6c84SAndroid Build Coastguard Worker   // not be typed in the globals table. In that case we can only say it "may be"
4451*cf5a6c84SAndroid Build Coastguard Worker   // a map, but we have to assume the possibility and attach a map to the
4452*cf5a6c84SAndroid Build Coastguard Worker   // var. When/if the var is used as a map or scalar in the called function it
4453*cf5a6c84SAndroid Build Coastguard Worker   // will be converted to a map or scalar as required.
4454*cf5a6c84SAndroid Build Coastguard Worker   // See force_maybemap_to_scalar(), and the similar comment in
4455*cf5a6c84SAndroid Build Coastguard Worker   // 'case tkfunction:' above.
4456*cf5a6c84SAndroid Build Coastguard Worker   //
4457*cf5a6c84SAndroid Build Coastguard Worker   int gstx, len = zlist_len(&TT.globals_table);
4458*cf5a6c84SAndroid Build Coastguard Worker   for (gstx = TT.spec_var_limit; gstx < len; gstx++) {
4459*cf5a6c84SAndroid Build Coastguard Worker     struct symtab_slot gs = GLOBAL[gstx];
4460*cf5a6c84SAndroid Build Coastguard Worker     struct zvalue v = ZVINIT(gs.flags, 0, 0);
4461*cf5a6c84SAndroid Build Coastguard Worker     if (v.flags == 0) {
4462*cf5a6c84SAndroid Build Coastguard Worker       zvalue_map_init(&v);
4463*cf5a6c84SAndroid Build Coastguard Worker       v.flags = ZF_MAYBEMAP;
4464*cf5a6c84SAndroid Build Coastguard Worker     } else if (IS_MAP(&v)) {
4465*cf5a6c84SAndroid Build Coastguard Worker       zvalue_map_init(&v);
4466*cf5a6c84SAndroid Build Coastguard Worker     } else {
4467*cf5a6c84SAndroid Build Coastguard Worker       // Set SCALAR flag 0 to create "uninitialized" scalar.
4468*cf5a6c84SAndroid Build Coastguard Worker       v.flags = 0;
4469*cf5a6c84SAndroid Build Coastguard Worker     }
4470*cf5a6c84SAndroid Build Coastguard Worker     push_val(&v);
4471*cf5a6c84SAndroid Build Coastguard Worker   }
4472*cf5a6c84SAndroid Build Coastguard Worker 
4473*cf5a6c84SAndroid Build Coastguard Worker   // Init -v assignment options.
4474*cf5a6c84SAndroid Build Coastguard Worker   for (struct arg_list *p = assign_args; p; p = p->next) {
4475*cf5a6c84SAndroid Build Coastguard Worker     char *asgn = p->arg;
4476*cf5a6c84SAndroid Build Coastguard Worker     char *val = strchr(asgn, '=');
4477*cf5a6c84SAndroid Build Coastguard Worker     if (!val) error_exit("bad -v assignment format");
4478*cf5a6c84SAndroid Build Coastguard Worker     *val++ = 0;
4479*cf5a6c84SAndroid Build Coastguard Worker     assign_global(asgn, val);
4480*cf5a6c84SAndroid Build Coastguard Worker   }
4481*cf5a6c84SAndroid Build Coastguard Worker 
4482*cf5a6c84SAndroid Build Coastguard Worker   TT.rgl.cur_arg = new_str_val("<cmdline>");
4483*cf5a6c84SAndroid Build Coastguard Worker   uninit_string_zvalue = new_str_val("");
4484*cf5a6c84SAndroid Build Coastguard Worker   zvalue_copy(&FIELD[0], &uninit_string_zvalue);
4485*cf5a6c84SAndroid Build Coastguard Worker }
4486*cf5a6c84SAndroid Build Coastguard Worker 
run_files(int * status)4487*cf5a6c84SAndroid Build Coastguard Worker static void run_files(int *status)
4488*cf5a6c84SAndroid Build Coastguard Worker {
4489*cf5a6c84SAndroid Build Coastguard Worker   int r = 0;
4490*cf5a6c84SAndroid Build Coastguard Worker   while (r != tkexit && *status < 0 && getrec_f0() >= 0)
4491*cf5a6c84SAndroid Build Coastguard Worker     if ((r = interp(TT.cgl.first_recrule, status)) == tknextfile) next_fp();
4492*cf5a6c84SAndroid Build Coastguard Worker }
4493*cf5a6c84SAndroid Build Coastguard Worker 
free_literal_regex(void)4494*cf5a6c84SAndroid Build Coastguard Worker static void free_literal_regex(void)
4495*cf5a6c84SAndroid Build Coastguard Worker {
4496*cf5a6c84SAndroid Build Coastguard Worker   int len = zlist_len(&TT.literals);
4497*cf5a6c84SAndroid Build Coastguard Worker   for (int k = 1; k < len; k++)
4498*cf5a6c84SAndroid Build Coastguard Worker     if (IS_RX(&LITERAL[k])) regfree(LITERAL[k].rx);
4499*cf5a6c84SAndroid Build Coastguard Worker }
4500*cf5a6c84SAndroid Build Coastguard Worker 
run(int optind,int argc,char ** argv,char * sepstring,struct arg_list * assign_args)4501*cf5a6c84SAndroid Build Coastguard Worker static void run(int optind, int argc, char **argv, char *sepstring,
4502*cf5a6c84SAndroid Build Coastguard Worker     struct arg_list *assign_args)
4503*cf5a6c84SAndroid Build Coastguard Worker {
4504*cf5a6c84SAndroid Build Coastguard Worker   char *printf_fmt_rx = "%[-+ #0']*([*]|[0-9]*)([.]([*]|[0-9]*))?l?[aAdiouxXfFeEgGcs%]";
4505*cf5a6c84SAndroid Build Coastguard Worker   init_globals(optind, argc, argv, sepstring, assign_args);
4506*cf5a6c84SAndroid Build Coastguard Worker   TT.cfile = xzalloc(sizeof(struct zfile));
4507*cf5a6c84SAndroid Build Coastguard Worker   xregcomp(&TT.rx_default, "[ \t\n]+", REG_EXTENDED);
4508*cf5a6c84SAndroid Build Coastguard Worker   xregcomp(&TT.rx_last, "[ \t\n]+", REG_EXTENDED);
4509*cf5a6c84SAndroid Build Coastguard Worker   xregcomp(&TT.rx_printf_fmt, printf_fmt_rx, REG_EXTENDED);
4510*cf5a6c84SAndroid Build Coastguard Worker   new_file("-", stdin, 'r', 1, 1);
4511*cf5a6c84SAndroid Build Coastguard Worker   new_file("/dev/stdin", stdin, 'r', 1, 1);
4512*cf5a6c84SAndroid Build Coastguard Worker   new_file("/dev/stdout", stdout, 'w', 1, 1);
4513*cf5a6c84SAndroid Build Coastguard Worker   TT.zstdout = TT.zfiles;
4514*cf5a6c84SAndroid Build Coastguard Worker   new_file("/dev/stderr", stderr, 'w', 1, 1);
4515*cf5a6c84SAndroid Build Coastguard Worker   seedrand(1);
4516*cf5a6c84SAndroid Build Coastguard Worker   int status = -1, r = 0;
4517*cf5a6c84SAndroid Build Coastguard Worker   if (TT.cgl.first_begin) r = interp(TT.cgl.first_begin, &status);
4518*cf5a6c84SAndroid Build Coastguard Worker   if (r != tkexit)
4519*cf5a6c84SAndroid Build Coastguard Worker     if (TT.cgl.first_recrule) run_files(&status);
4520*cf5a6c84SAndroid Build Coastguard Worker   if (TT.cgl.first_end) r = interp(TT.cgl.first_end, &status);
4521*cf5a6c84SAndroid Build Coastguard Worker   regfree(&TT.rx_printf_fmt);
4522*cf5a6c84SAndroid Build Coastguard Worker   regfree(&TT.rx_default);
4523*cf5a6c84SAndroid Build Coastguard Worker   regfree(&TT.rx_last);
4524*cf5a6c84SAndroid Build Coastguard Worker   free_literal_regex();
4525*cf5a6c84SAndroid Build Coastguard Worker   close_file(0);    // close all files
4526*cf5a6c84SAndroid Build Coastguard Worker   if (status >= 0) awk_exit(status);
4527*cf5a6c84SAndroid Build Coastguard Worker }
4528*cf5a6c84SAndroid Build Coastguard Worker 
4529*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
4530*cf5a6c84SAndroid Build Coastguard Worker //// main
4531*cf5a6c84SAndroid Build Coastguard Worker ////////////////////
4532*cf5a6c84SAndroid Build Coastguard Worker 
progfiles_init(char * progstring,struct arg_list * prog_args)4533*cf5a6c84SAndroid Build Coastguard Worker static void progfiles_init(char *progstring, struct arg_list *prog_args)
4534*cf5a6c84SAndroid Build Coastguard Worker {
4535*cf5a6c84SAndroid Build Coastguard Worker   TT.scs->p = progstring ? progstring : "  " + 2;
4536*cf5a6c84SAndroid Build Coastguard Worker   TT.scs->progstring = progstring;
4537*cf5a6c84SAndroid Build Coastguard Worker   TT.scs->prog_args = prog_args;
4538*cf5a6c84SAndroid Build Coastguard Worker   TT.scs->filename = "(cmdline)";
4539*cf5a6c84SAndroid Build Coastguard Worker   TT.scs->maxtok = 256;
4540*cf5a6c84SAndroid Build Coastguard Worker   TT.scs->tokstr = xzalloc(TT.scs->maxtok);
4541*cf5a6c84SAndroid Build Coastguard Worker }
4542*cf5a6c84SAndroid Build Coastguard Worker 
awk(char * sepstring,char * progstring,struct arg_list * prog_args,struct arg_list * assign_args,int optind,int argc,char ** argv,int opt_run_prog)4543*cf5a6c84SAndroid Build Coastguard Worker static int awk(char *sepstring, char *progstring, struct arg_list *prog_args,
4544*cf5a6c84SAndroid Build Coastguard Worker     struct arg_list *assign_args, int optind, int argc, char **argv,
4545*cf5a6c84SAndroid Build Coastguard Worker     int opt_run_prog)
4546*cf5a6c84SAndroid Build Coastguard Worker {
4547*cf5a6c84SAndroid Build Coastguard Worker   struct scanner_state ss = {0};
4548*cf5a6c84SAndroid Build Coastguard Worker   TT.scs = &ss;
4549*cf5a6c84SAndroid Build Coastguard Worker 
4550*cf5a6c84SAndroid Build Coastguard Worker   setlocale(LC_NUMERIC, "");
4551*cf5a6c84SAndroid Build Coastguard Worker   progfiles_init(progstring, prog_args);
4552*cf5a6c84SAndroid Build Coastguard Worker   compile();
4553*cf5a6c84SAndroid Build Coastguard Worker 
4554*cf5a6c84SAndroid Build Coastguard Worker   if (TT.cgl.compile_error_count)
4555*cf5a6c84SAndroid Build Coastguard Worker     error_exit("%d syntax error(s)", TT.cgl.compile_error_count);
4556*cf5a6c84SAndroid Build Coastguard Worker   else {
4557*cf5a6c84SAndroid Build Coastguard Worker     if (opt_run_prog)
4558*cf5a6c84SAndroid Build Coastguard Worker       run(optind, argc, argv, sepstring, assign_args);
4559*cf5a6c84SAndroid Build Coastguard Worker   }
4560*cf5a6c84SAndroid Build Coastguard Worker 
4561*cf5a6c84SAndroid Build Coastguard Worker   return TT.cgl.compile_error_count;
4562*cf5a6c84SAndroid Build Coastguard Worker }
4563*cf5a6c84SAndroid Build Coastguard Worker 
awk_main(void)4564*cf5a6c84SAndroid Build Coastguard Worker void awk_main(void)
4565*cf5a6c84SAndroid Build Coastguard Worker {
4566*cf5a6c84SAndroid Build Coastguard Worker   char *sepstring = TT.F ? escape_str(TT.F, 0) : " ";
4567*cf5a6c84SAndroid Build Coastguard Worker   int optind = 0;
4568*cf5a6c84SAndroid Build Coastguard Worker   char *progstring = NULL;
4569*cf5a6c84SAndroid Build Coastguard Worker 
4570*cf5a6c84SAndroid Build Coastguard Worker   TT.pbuf = toybuf;
4571*cf5a6c84SAndroid Build Coastguard Worker   toys.exitval = 2;
4572*cf5a6c84SAndroid Build Coastguard Worker   if (!TT.f) {
4573*cf5a6c84SAndroid Build Coastguard Worker     if (*toys.optargs) progstring = toys.optargs[optind++];
4574*cf5a6c84SAndroid Build Coastguard Worker     else error_exit("No program string\n");
4575*cf5a6c84SAndroid Build Coastguard Worker   }
4576*cf5a6c84SAndroid Build Coastguard Worker   TT.progname = toys.which->name;
4577*cf5a6c84SAndroid Build Coastguard Worker   toys.exitval = awk(sepstring, progstring, TT.f, TT.v,
4578*cf5a6c84SAndroid Build Coastguard Worker       optind, toys.optc, toys.optargs, !FLAG(c));
4579*cf5a6c84SAndroid Build Coastguard Worker }
4580