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 = ℞
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 = ℞
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