blob: d6dcd9578ceabda54407aa139fef8b2288b78641 [file] [log] [blame]
Glenn L McGrath545106f2002-11-11 06:21:00 +00001/* vi: set sw=4 ts=4: */
2/*
3 * awk implementation for busybox
4 *
5 * Copyright (C) 2002 by Dmitry Zakharov <dmit@crp.bank.gov.ua>
6 *
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +00007 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
Glenn L McGrath545106f2002-11-11 06:21:00 +00008 */
9
Glenn L McGrath545106f2002-11-11 06:21:00 +000010#include "busybox.h"
Rob Landleyd921b2e2006-08-03 15:41:12 +000011#include "xregex.h"
12#include <math.h>
Glenn L McGrath545106f2002-11-11 06:21:00 +000013
14
15#define MAXVARFMT 240
16#define MINNVBLOCK 64
17
18/* variable flags */
19#define VF_NUMBER 0x0001 /* 1 = primary type is number */
20#define VF_ARRAY 0x0002 /* 1 = it's an array */
21
22#define VF_CACHED 0x0100 /* 1 = num/str value has cached str/num eq */
23#define VF_USER 0x0200 /* 1 = user input (may be numeric string) */
24#define VF_SPECIAL 0x0400 /* 1 = requires extra handling when changed */
25#define VF_WALK 0x0800 /* 1 = variable has alloc'd x.walker list */
26#define VF_FSTR 0x1000 /* 1 = string points to fstring buffer */
27#define VF_CHILD 0x2000 /* 1 = function arg; x.parent points to source */
28#define VF_DIRTY 0x4000 /* 1 = variable was set explicitly */
29
30/* these flags are static, don't change them when value is changed */
31#define VF_DONTTOUCH (VF_ARRAY | VF_SPECIAL | VF_WALK | VF_CHILD | VF_DIRTY)
32
33/* Variable */
34typedef struct var_s {
35 unsigned short type; /* flags */
36 double number;
37 char *string;
38 union {
Mike Frysinger4b7b8a52006-04-16 05:55:15 +000039 int aidx; /* func arg idx (for compilation stage) */
Glenn L McGrath545106f2002-11-11 06:21:00 +000040 struct xhash_s *array; /* array ptr */
41 struct var_s *parent; /* for func args, ptr to actual parameter */
42 char **walker; /* list of array elements (for..in) */
43 } x;
44} var;
45
46/* Node chain (pattern-action chain, BEGIN, END, function bodies) */
47typedef struct chain_s {
48 struct node_s *first;
49 struct node_s *last;
50 char *programname;
51} chain;
52
53/* Function */
54typedef struct func_s {
55 unsigned short nargs;
56 struct chain_s body;
57} func;
58
59/* I/O stream */
60typedef struct rstream_s {
61 FILE *F;
62 char *buffer;
Glenn L McGrath00ed36f2003-10-30 13:36:39 +000063 int adv;
Glenn L McGrath545106f2002-11-11 06:21:00 +000064 int size;
65 int pos;
66 unsigned short is_pipe;
67} rstream;
68
69typedef struct hash_item_s {
70 union {
71 struct var_s v; /* variable/array hash */
72 struct rstream_s rs; /* redirect streams hash */
73 struct func_s f; /* functions hash */
74 } data;
75 struct hash_item_s *next; /* next in chain */
76 char name[1]; /* really it's longer */
77} hash_item;
78
79typedef struct xhash_s {
Denis Vlasenkof782f522007-01-01 23:51:30 +000080 unsigned nel; /* num of elements */
81 unsigned csize; /* current hash size */
82 unsigned nprime; /* next hash size in PRIMES[] */
83 unsigned glen; /* summary length of item names */
Glenn L McGrath545106f2002-11-11 06:21:00 +000084 struct hash_item_s **items;
85} xhash;
86
87/* Tree node */
88typedef struct node_s {
Mike Frysingerf87b3e32005-09-27 04:16:22 +000089 uint32_t info;
Glenn L McGrath545106f2002-11-11 06:21:00 +000090 unsigned short lineno;
91 union {
92 struct node_s *n;
93 var *v;
94 int i;
95 char *s;
96 regex_t *re;
97 } l;
98 union {
99 struct node_s *n;
100 regex_t *ire;
101 func *f;
102 int argno;
103 } r;
104 union {
105 struct node_s *n;
106 } a;
107} node;
108
109/* Block of temporary variables */
110typedef struct nvblock_s {
111 int size;
112 var *pos;
113 struct nvblock_s *prev;
114 struct nvblock_s *next;
115 var nv[0];
116} nvblock;
117
118typedef struct tsplitter_s {
119 node n;
120 regex_t re[2];
121} tsplitter;
122
123/* simple token classes */
124/* Order and hex values are very important!!! See next_token() */
125#define TC_SEQSTART 1 /* ( */
126#define TC_SEQTERM (1 << 1) /* ) */
127#define TC_REGEXP (1 << 2) /* /.../ */
128#define TC_OUTRDR (1 << 3) /* | > >> */
129#define TC_UOPPOST (1 << 4) /* unary postfix operator */
130#define TC_UOPPRE1 (1 << 5) /* unary prefix operator */
131#define TC_BINOPX (1 << 6) /* two-opnd operator */
132#define TC_IN (1 << 7)
133#define TC_COMMA (1 << 8)
134#define TC_PIPE (1 << 9) /* input redirection pipe */
135#define TC_UOPPRE2 (1 << 10) /* unary prefix operator */
136#define TC_ARRTERM (1 << 11) /* ] */
137#define TC_GRPSTART (1 << 12) /* { */
138#define TC_GRPTERM (1 << 13) /* } */
139#define TC_SEMICOL (1 << 14)
140#define TC_NEWLINE (1 << 15)
141#define TC_STATX (1 << 16) /* ctl statement (for, next...) */
142#define TC_WHILE (1 << 17)
143#define TC_ELSE (1 << 18)
144#define TC_BUILTIN (1 << 19)
145#define TC_GETLINE (1 << 20)
146#define TC_FUNCDECL (1 << 21) /* `function' `func' */
147#define TC_BEGIN (1 << 22)
148#define TC_END (1 << 23)
149#define TC_EOF (1 << 24)
150#define TC_VARIABLE (1 << 25)
151#define TC_ARRAY (1 << 26)
152#define TC_FUNCTION (1 << 27)
153#define TC_STRING (1 << 28)
154#define TC_NUMBER (1 << 29)
155
156#define TC_UOPPRE (TC_UOPPRE1 | TC_UOPPRE2)
157
158/* combined token classes */
159#define TC_BINOP (TC_BINOPX | TC_COMMA | TC_PIPE | TC_IN)
160#define TC_UNARYOP (TC_UOPPRE | TC_UOPPOST)
161#define TC_OPERAND (TC_VARIABLE | TC_ARRAY | TC_FUNCTION | \
162 TC_BUILTIN | TC_GETLINE | TC_SEQSTART | TC_STRING | TC_NUMBER)
163
164#define TC_STATEMNT (TC_STATX | TC_WHILE)
165#define TC_OPTERM (TC_SEMICOL | TC_NEWLINE)
166
167/* word tokens, cannot mean something else if not expected */
168#define TC_WORD (TC_IN | TC_STATEMNT | TC_ELSE | TC_BUILTIN | \
169 TC_GETLINE | TC_FUNCDECL | TC_BEGIN | TC_END)
170
171/* discard newlines after these */
172#define TC_NOTERM (TC_COMMA | TC_GRPSTART | TC_GRPTERM | \
173 TC_BINOP | TC_OPTERM)
174
175/* what can expression begin with */
176#define TC_OPSEQ (TC_OPERAND | TC_UOPPRE | TC_REGEXP)
177/* what can group begin with */
178#define TC_GRPSEQ (TC_OPSEQ | TC_OPTERM | TC_STATEMNT | TC_GRPSTART)
179
180/* if previous token class is CONCAT1 and next is CONCAT2, concatenation */
181/* operator is inserted between them */
182#define TC_CONCAT1 (TC_VARIABLE | TC_ARRTERM | TC_SEQTERM | \
183 TC_STRING | TC_NUMBER | TC_UOPPOST)
184#define TC_CONCAT2 (TC_OPERAND | TC_UOPPRE)
185
186#define OF_RES1 0x010000
187#define OF_RES2 0x020000
188#define OF_STR1 0x040000
189#define OF_STR2 0x080000
190#define OF_NUM1 0x100000
191#define OF_CHECKED 0x200000
192
193/* combined operator flags */
194#define xx 0
195#define xV OF_RES2
196#define xS (OF_RES2 | OF_STR2)
197#define Vx OF_RES1
198#define VV (OF_RES1 | OF_RES2)
199#define Nx (OF_RES1 | OF_NUM1)
200#define NV (OF_RES1 | OF_NUM1 | OF_RES2)
201#define Sx (OF_RES1 | OF_STR1)
202#define SV (OF_RES1 | OF_STR1 | OF_RES2)
203#define SS (OF_RES1 | OF_STR1 | OF_RES2 | OF_STR2)
204
205#define OPCLSMASK 0xFF00
206#define OPNMASK 0x007F
207
208/* operator priority is a highest byte (even: r->l, odd: l->r grouping)
209 * For builtins it has different meaning: n n s3 s2 s1 v3 v2 v1,
210 * n - min. number of args, vN - resolve Nth arg to var, sN - resolve to string
211 */
212#define P(x) (x << 24)
213#define PRIMASK 0x7F000000
214#define PRIMASK2 0x7E000000
215
216/* Operation classes */
217
218#define SHIFT_TIL_THIS 0x0600
219#define RECUR_FROM_THIS 0x1000
220
221enum {
222 OC_DELETE=0x0100, OC_EXEC=0x0200, OC_NEWSOURCE=0x0300,
223 OC_PRINT=0x0400, OC_PRINTF=0x0500, OC_WALKINIT=0x0600,
224
225 OC_BR=0x0700, OC_BREAK=0x0800, OC_CONTINUE=0x0900,
226 OC_EXIT=0x0a00, OC_NEXT=0x0b00, OC_NEXTFILE=0x0c00,
227 OC_TEST=0x0d00, OC_WALKNEXT=0x0e00,
228
229 OC_BINARY=0x1000, OC_BUILTIN=0x1100, OC_COLON=0x1200,
230 OC_COMMA=0x1300, OC_COMPARE=0x1400, OC_CONCAT=0x1500,
231 OC_FBLTIN=0x1600, OC_FIELD=0x1700, OC_FNARG=0x1800,
232 OC_FUNC=0x1900, OC_GETLINE=0x1a00, OC_IN=0x1b00,
233 OC_LAND=0x1c00, OC_LOR=0x1d00, OC_MATCH=0x1e00,
234 OC_MOVE=0x1f00, OC_PGETLINE=0x2000, OC_REGEXP=0x2100,
235 OC_REPLACE=0x2200, OC_RETURN=0x2300, OC_SPRINTF=0x2400,
236 OC_TERNARY=0x2500, OC_UNARY=0x2600, OC_VAR=0x2700,
237 OC_DONE=0x2800,
238
239 ST_IF=0x3000, ST_DO=0x3100, ST_FOR=0x3200,
240 ST_WHILE=0x3300
241};
242
243/* simple builtins */
244enum {
245 F_in=0, F_rn, F_co, F_ex, F_lg, F_si, F_sq, F_sr,
246 F_ti, F_le, F_sy, F_ff, F_cl
247};
248
249/* builtins */
250enum {
251 B_a2=0, B_ix, B_ma, B_sp, B_ss, B_ti, B_lo, B_up,
Denis Vlasenkoe175ff22006-09-26 17:41:00 +0000252 B_ge, B_gs, B_su,
253 B_an, B_co, B_ls, B_or, B_rs, B_xo,
Glenn L McGrath545106f2002-11-11 06:21:00 +0000254};
255
256/* tokens and their corresponding info values */
257
258#define NTC "\377" /* switch to next token class (tc<<1) */
259#define NTCC '\377'
260
261#define OC_B OC_BUILTIN
262
Denis Vlasenkof782f522007-01-01 23:51:30 +0000263static const char tokenlist[] =
264 "\1(" NTC
265 "\1)" NTC
266 "\1/" NTC /* REGEXP */
267 "\2>>" "\1>" "\1|" NTC /* OUTRDR */
268 "\2++" "\2--" NTC /* UOPPOST */
269 "\2++" "\2--" "\1$" NTC /* UOPPRE1 */
270 "\2==" "\1=" "\2+=" "\2-=" /* BINOPX */
271 "\2*=" "\2/=" "\2%=" "\2^="
272 "\1+" "\1-" "\3**=" "\2**"
273 "\1/" "\1%" "\1^" "\1*"
274 "\2!=" "\2>=" "\2<=" "\1>"
275 "\1<" "\2!~" "\1~" "\2&&"
276 "\2||" "\1?" "\1:" NTC
277 "\2in" NTC
278 "\1," NTC
279 "\1|" NTC
280 "\1+" "\1-" "\1!" NTC /* UOPPRE2 */
281 "\1]" NTC
282 "\1{" NTC
283 "\1}" NTC
284 "\1;" NTC
285 "\1\n" NTC
286 "\2if" "\2do" "\3for" "\5break" /* STATX */
287 "\10continue" "\6delete" "\5print"
288 "\6printf" "\4next" "\10nextfile"
289 "\6return" "\4exit" NTC
290 "\5while" NTC
291 "\4else" NTC
Glenn L McGrath545106f2002-11-11 06:21:00 +0000292
Denis Vlasenkof782f522007-01-01 23:51:30 +0000293 "\3and" "\5compl" "\6lshift" "\2or"
294 "\6rshift" "\3xor"
295 "\5close" "\6system" "\6fflush" "\5atan2" /* BUILTIN */
296 "\3cos" "\3exp" "\3int" "\3log"
297 "\4rand" "\3sin" "\4sqrt" "\5srand"
298 "\6gensub" "\4gsub" "\5index" "\6length"
299 "\5match" "\5split" "\7sprintf" "\3sub"
300 "\6substr" "\7systime" "\10strftime"
301 "\7tolower" "\7toupper" NTC
302 "\7getline" NTC
303 "\4func" "\10function" NTC
304 "\5BEGIN" NTC
305 "\3END" "\0"
Glenn L McGrath545106f2002-11-11 06:21:00 +0000306 ;
307
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +0000308static const uint32_t tokeninfo[] = {
Glenn L McGrath545106f2002-11-11 06:21:00 +0000309 0,
310 0,
311 OC_REGEXP,
Denis Vlasenkof782f522007-01-01 23:51:30 +0000312 xS|'a', xS|'w', xS|'|',
313 OC_UNARY|xV|P(9)|'p', OC_UNARY|xV|P(9)|'m',
314 OC_UNARY|xV|P(9)|'P', OC_UNARY|xV|P(9)|'M',
315 OC_FIELD|xV|P(5),
316 OC_COMPARE|VV|P(39)|5, OC_MOVE|VV|P(74),
317 OC_REPLACE|NV|P(74)|'+', OC_REPLACE|NV|P(74)|'-',
318 OC_REPLACE|NV|P(74)|'*', OC_REPLACE|NV|P(74)|'/',
319 OC_REPLACE|NV|P(74)|'%', OC_REPLACE|NV|P(74)|'&',
320 OC_BINARY|NV|P(29)|'+', OC_BINARY|NV|P(29)|'-',
321 OC_REPLACE|NV|P(74)|'&', OC_BINARY|NV|P(15)|'&',
322 OC_BINARY|NV|P(25)|'/', OC_BINARY|NV|P(25)|'%',
323 OC_BINARY|NV|P(15)|'&', OC_BINARY|NV|P(25)|'*',
324 OC_COMPARE|VV|P(39)|4, OC_COMPARE|VV|P(39)|3,
325 OC_COMPARE|VV|P(39)|0, OC_COMPARE|VV|P(39)|1,
326 OC_COMPARE|VV|P(39)|2, OC_MATCH|Sx|P(45)|'!',
327 OC_MATCH|Sx|P(45)|'~', OC_LAND|Vx|P(55),
328 OC_LOR|Vx|P(59), OC_TERNARY|Vx|P(64)|'?',
329 OC_COLON|xx|P(67)|':',
Glenn L McGrath545106f2002-11-11 06:21:00 +0000330 OC_IN|SV|P(49),
331 OC_COMMA|SS|P(80),
332 OC_PGETLINE|SV|P(37),
Denis Vlasenkof782f522007-01-01 23:51:30 +0000333 OC_UNARY|xV|P(19)|'+', OC_UNARY|xV|P(19)|'-',
334 OC_UNARY|xV|P(19)|'!',
Glenn L McGrath545106f2002-11-11 06:21:00 +0000335 0,
336 0,
337 0,
338 0,
339 0,
Denis Vlasenkof782f522007-01-01 23:51:30 +0000340 ST_IF, ST_DO, ST_FOR, OC_BREAK,
341 OC_CONTINUE, OC_DELETE|Vx, OC_PRINT,
342 OC_PRINTF, OC_NEXT, OC_NEXTFILE,
343 OC_RETURN|Vx, OC_EXIT|Nx,
Glenn L McGrath545106f2002-11-11 06:21:00 +0000344 ST_WHILE,
345 0,
346
Denis Vlasenkoe175ff22006-09-26 17:41:00 +0000347 OC_B|B_an|P(0x83), OC_B|B_co|P(0x41), OC_B|B_ls|P(0x83), OC_B|B_or|P(0x83),
348 OC_B|B_rs|P(0x83), OC_B|B_xo|P(0x83),
Glenn L McGrath545106f2002-11-11 06:21:00 +0000349 OC_FBLTIN|Sx|F_cl, OC_FBLTIN|Sx|F_sy, OC_FBLTIN|Sx|F_ff, OC_B|B_a2|P(0x83),
350 OC_FBLTIN|Nx|F_co, OC_FBLTIN|Nx|F_ex, OC_FBLTIN|Nx|F_in, OC_FBLTIN|Nx|F_lg,
351 OC_FBLTIN|F_rn, OC_FBLTIN|Nx|F_si, OC_FBLTIN|Nx|F_sq, OC_FBLTIN|Nx|F_sr,
352 OC_B|B_ge|P(0xd6), OC_B|B_gs|P(0xb6), OC_B|B_ix|P(0x9b), OC_FBLTIN|Sx|F_le,
353 OC_B|B_ma|P(0x89), OC_B|B_sp|P(0x8b), OC_SPRINTF, OC_B|B_su|P(0xb6),
354 OC_B|B_ss|P(0x8f), OC_FBLTIN|F_ti, OC_B|B_ti|P(0x0b),
355 OC_B|B_lo|P(0x49), OC_B|B_up|P(0x49),
356 OC_GETLINE|SV|P(0),
357 0, 0,
358 0,
359 0
360};
361
362/* internal variable names and their initial values */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000363/* asterisk marks SPECIAL vars; $ is just no-named Field0 */
Glenn L McGrath545106f2002-11-11 06:21:00 +0000364enum {
Denis Vlasenkof782f522007-01-01 23:51:30 +0000365 CONVFMT=0, OFMT, FS, OFS,
366 ORS, RS, RT, FILENAME,
367 SUBSEP, ARGIND, ARGC, ARGV,
368 ERRNO, FNR,
369 NR, NF, IGNORECASE,
370 ENVIRON, F0, _intvarcount_
Glenn L McGrath545106f2002-11-11 06:21:00 +0000371};
372
Denis Vlasenkof782f522007-01-01 23:51:30 +0000373static const char vNames[] =
374 "CONVFMT\0" "OFMT\0" "FS\0*" "OFS\0"
375 "ORS\0" "RS\0*" "RT\0" "FILENAME\0"
376 "SUBSEP\0" "ARGIND\0" "ARGC\0" "ARGV\0"
377 "ERRNO\0" "FNR\0"
378 "NR\0" "NF\0*" "IGNORECASE\0*"
379 "ENVIRON\0" "$\0*" "\0";
Glenn L McGrath545106f2002-11-11 06:21:00 +0000380
Denis Vlasenkof782f522007-01-01 23:51:30 +0000381static const char vValues[] =
382 "%.6g\0" "%.6g\0" " \0" " \0"
383 "\n\0" "\n\0" "\0" "\0"
Glenn L McGrath545106f2002-11-11 06:21:00 +0000384 "\034\0"
385 "\377";
386
387/* hash size may grow to these values */
388#define FIRST_PRIME 61;
Denis Vlasenkof782f522007-01-01 23:51:30 +0000389static const unsigned PRIMES[] = { 251, 1021, 4093, 16381, 65521 };
390enum { NPRIMES = sizeof(PRIMES) / sizeof(unsigned) };
Glenn L McGrath545106f2002-11-11 06:21:00 +0000391
392/* globals */
393
394extern char **environ;
395
396static var * V[_intvarcount_];
397static chain beginseq, mainseq, endseq, *seq;
398static int nextrec, nextfile;
399static node *break_ptr, *continue_ptr;
400static rstream *iF;
401static xhash *vhash, *ahash, *fdhash, *fnhash;
402static char *programname;
403static short lineno;
404static int is_f0_split;
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +0000405static int nfields;
406static var *Fields;
Glenn L McGrath545106f2002-11-11 06:21:00 +0000407static tsplitter fsplitter, rsplitter;
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +0000408static nvblock *cb;
Glenn L McGrath545106f2002-11-11 06:21:00 +0000409static char *pos;
410static char *buf;
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +0000411static int icase;
412static int exiting;
Glenn L McGrath545106f2002-11-11 06:21:00 +0000413
414static struct {
Mike Frysingerf87b3e32005-09-27 04:16:22 +0000415 uint32_t tclass;
416 uint32_t info;
Glenn L McGrath545106f2002-11-11 06:21:00 +0000417 char *string;
418 double number;
419 short lineno;
420 int rollback;
421} t;
422
423/* function prototypes */
Glenn L McGrath545106f2002-11-11 06:21:00 +0000424static void handle_special(var *);
Mike Frysingerf87b3e32005-09-27 04:16:22 +0000425static node *parse_expr(uint32_t);
Glenn L McGrath545106f2002-11-11 06:21:00 +0000426static void chain_group(void);
427static var *evaluate(node *, var *);
428static rstream *next_input_file(void);
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +0000429static int fmt_num(char *, int, const char *, double, int);
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +0000430static int awk_exit(int) ATTRIBUTE_NORETURN;
Glenn L McGrath545106f2002-11-11 06:21:00 +0000431
432/* ---- error handling ---- */
433
434static const char EMSG_INTERNAL_ERROR[] = "Internal error";
435static const char EMSG_UNEXP_EOS[] = "Unexpected end of string";
436static const char EMSG_UNEXP_TOKEN[] = "Unexpected token";
437static const char EMSG_DIV_BY_ZERO[] = "Division by zero";
438static const char EMSG_INV_FMT[] = "Invalid format specifier";
439static const char EMSG_TOO_FEW_ARGS[] = "Too few arguments for builtin";
440static const char EMSG_NOT_ARRAY[] = "Not an array";
441static const char EMSG_POSSIBLE_ERROR[] = "Possible syntax error";
442static const char EMSG_UNDEF_FUNC[] = "Call to undefined function";
Denis Vlasenkof782f522007-01-01 23:51:30 +0000443#if !ENABLE_FEATURE_AWK_MATH
Glenn L McGrath545106f2002-11-11 06:21:00 +0000444static const char EMSG_NO_MATH[] = "Math support is not compiled in";
445#endif
446
Denis Vlasenkof782f522007-01-01 23:51:30 +0000447static void zero_out_var(var * vp)
448{
449 memset(vp, 0, sizeof(*vp));
450}
451
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +0000452static void syntax_error(const char * const message) ATTRIBUTE_NORETURN;
Glenn L McGrathd4036f82002-11-28 09:30:40 +0000453static void syntax_error(const char * const message)
454{
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +0000455 bb_error_msg_and_die("%s:%i: %s", programname, lineno, message);
Glenn L McGrath545106f2002-11-11 06:21:00 +0000456}
457
458#define runtime_error(x) syntax_error(x)
459
460
461/* ---- hash stuff ---- */
462
Denis Vlasenkof782f522007-01-01 23:51:30 +0000463static unsigned hashidx(const char *name)
Mike Frysinger10a11e22005-09-27 02:23:02 +0000464{
Denis Vlasenkof782f522007-01-01 23:51:30 +0000465 unsigned idx = 0;
Glenn L McGrath545106f2002-11-11 06:21:00 +0000466
Denis Vlasenkof782f522007-01-01 23:51:30 +0000467 while (*name) idx = *name++ + (idx << 6) - idx;
Glenn L McGrath545106f2002-11-11 06:21:00 +0000468 return idx;
469}
470
471/* create new hash */
Mike Frysinger10a11e22005-09-27 02:23:02 +0000472static xhash *hash_init(void)
473{
Glenn L McGrath545106f2002-11-11 06:21:00 +0000474 xhash *newhash;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000475
Denis Vlasenko4cccc032006-12-22 18:37:07 +0000476 newhash = xzalloc(sizeof(xhash));
Glenn L McGrath545106f2002-11-11 06:21:00 +0000477 newhash->csize = FIRST_PRIME;
Denis Vlasenko4cccc032006-12-22 18:37:07 +0000478 newhash->items = xzalloc(newhash->csize * sizeof(hash_item *));
Glenn L McGrath545106f2002-11-11 06:21:00 +0000479
480 return newhash;
481}
482
483/* find item in hash, return ptr to data, NULL if not found */
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +0000484static void *hash_search(xhash *hash, const char *name)
Mike Frysinger10a11e22005-09-27 02:23:02 +0000485{
Glenn L McGrath545106f2002-11-11 06:21:00 +0000486 hash_item *hi;
487
488 hi = hash->items [ hashidx(name) % hash->csize ];
489 while (hi) {
490 if (strcmp(hi->name, name) == 0)
491 return &(hi->data);
492 hi = hi->next;
493 }
494 return NULL;
495}
496
497/* grow hash if it becomes too big */
Mike Frysinger10a11e22005-09-27 02:23:02 +0000498static void hash_rebuild(xhash *hash)
499{
Denis Vlasenkof782f522007-01-01 23:51:30 +0000500 unsigned newsize, i, idx;
Glenn L McGrath545106f2002-11-11 06:21:00 +0000501 hash_item **newitems, *hi, *thi;
502
503 if (hash->nprime == NPRIMES)
504 return;
505
506 newsize = PRIMES[hash->nprime++];
Denis Vlasenko4cccc032006-12-22 18:37:07 +0000507 newitems = xzalloc(newsize * sizeof(hash_item *));
Glenn L McGrath545106f2002-11-11 06:21:00 +0000508
509 for (i=0; i<hash->csize; i++) {
510 hi = hash->items[i];
511 while (hi) {
512 thi = hi;
513 hi = thi->next;
514 idx = hashidx(thi->name) % newsize;
515 thi->next = newitems[idx];
516 newitems[idx] = thi;
517 }
518 }
519
520 free(hash->items);
521 hash->csize = newsize;
522 hash->items = newitems;
523}
524
525/* find item in hash, add it if necessary. Return ptr to data */
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +0000526static void *hash_find(xhash *hash, const char *name)
Mike Frysinger10a11e22005-09-27 02:23:02 +0000527{
Glenn L McGrath545106f2002-11-11 06:21:00 +0000528 hash_item *hi;
Denis Vlasenkof782f522007-01-01 23:51:30 +0000529 unsigned idx;
Glenn L McGrath545106f2002-11-11 06:21:00 +0000530 int l;
531
532 hi = hash_search(hash, name);
533 if (! hi) {
534 if (++hash->nel / hash->csize > 10)
535 hash_rebuild(hash);
536
Rob Landleya3896512006-05-07 20:20:34 +0000537 l = strlen(name) + 1;
Rob Landley9ffd4232006-05-21 18:30:35 +0000538 hi = xzalloc(sizeof(hash_item) + l);
Glenn L McGrath545106f2002-11-11 06:21:00 +0000539 memcpy(hi->name, name, l);
540
541 idx = hashidx(name) % hash->csize;
542 hi->next = hash->items[idx];
543 hash->items[idx] = hi;
544 hash->glen += l;
545 }
546 return &(hi->data);
547}
548
Denis Vlasenkof782f522007-01-01 23:51:30 +0000549#define findvar(hash, name) ((var*) hash_find((hash) , (name)))
550#define newvar(name) ((var*) hash_find(vhash , (name)))
551#define newfile(name) ((rstream*)hash_find(fdhash ,(name)))
552#define newfunc(name) ((func*) hash_find(fnhash , (name)))
Glenn L McGrath545106f2002-11-11 06:21:00 +0000553
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +0000554static void hash_remove(xhash *hash, const char *name)
Mike Frysinger10a11e22005-09-27 02:23:02 +0000555{
Glenn L McGrath545106f2002-11-11 06:21:00 +0000556 hash_item *hi, **phi;
557
558 phi = &(hash->items[ hashidx(name) % hash->csize ]);
559 while (*phi) {
560 hi = *phi;
561 if (strcmp(hi->name, name) == 0) {
Rob Landleya3896512006-05-07 20:20:34 +0000562 hash->glen -= (strlen(name) + 1);
Glenn L McGrath545106f2002-11-11 06:21:00 +0000563 hash->nel--;
564 *phi = hi->next;
565 free(hi);
566 break;
567 }
568 phi = &(hi->next);
569 }
570}
571
572/* ------ some useful functions ------ */
573
Mike Frysinger10a11e22005-09-27 02:23:02 +0000574static void skip_spaces(char **s)
575{
"Robert P. J. Day"68229832006-07-01 13:08:46 +0000576 char *p = *s;
Glenn L McGrath545106f2002-11-11 06:21:00 +0000577
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000578 while (*p == ' ' || *p == '\t' ||
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +0000579 (*p == '\\' && *(p+1) == '\n' && (++p, ++t.lineno))) {
Mike Frysingerde2b9382005-09-27 03:18:00 +0000580 p++;
Glenn L McGrath545106f2002-11-11 06:21:00 +0000581 }
582 *s = p;
583}
584
Mike Frysinger10a11e22005-09-27 02:23:02 +0000585static char *nextword(char **s)
586{
"Robert P. J. Day"68229832006-07-01 13:08:46 +0000587 char *p = *s;
Glenn L McGrath545106f2002-11-11 06:21:00 +0000588
Denis Vlasenkof782f522007-01-01 23:51:30 +0000589 while (*(*s)++) /* */;
Glenn L McGrath545106f2002-11-11 06:21:00 +0000590
591 return p;
592}
593
Mike Frysinger10a11e22005-09-27 02:23:02 +0000594static char nextchar(char **s)
595{
"Robert P. J. Day"68229832006-07-01 13:08:46 +0000596 char c, *pps;
Glenn L McGrath545106f2002-11-11 06:21:00 +0000597
598 c = *((*s)++);
599 pps = *s;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000600 if (c == '\\') c = bb_process_escape_sequence((const char**)s);
Glenn L McGrath545106f2002-11-11 06:21:00 +0000601 if (c == '\\' && *s == pps) c = *((*s)++);
602 return c;
603}
604
Rob Landley88621d72006-08-29 19:41:06 +0000605static int ATTRIBUTE_ALWAYS_INLINE isalnum_(int c)
Mike Frysinger10a11e22005-09-27 02:23:02 +0000606{
Glenn L McGrath545106f2002-11-11 06:21:00 +0000607 return (isalnum(c) || c == '_');
608}
609
Mike Frysinger10a11e22005-09-27 02:23:02 +0000610static FILE *afopen(const char *path, const char *mode)
611{
Rob Landleyd921b2e2006-08-03 15:41:12 +0000612 return (*path == '-' && *(path+1) == '\0') ? stdin : xfopen(path, mode);
Glenn L McGrath545106f2002-11-11 06:21:00 +0000613}
614
615/* -------- working with variables (set/get/copy/etc) -------- */
616
Mike Frysinger10a11e22005-09-27 02:23:02 +0000617static xhash *iamarray(var *v)
618{
Glenn L McGrath545106f2002-11-11 06:21:00 +0000619 var *a = v;
620
621 while (a->type & VF_CHILD)
622 a = a->x.parent;
623
624 if (! (a->type & VF_ARRAY)) {
625 a->type |= VF_ARRAY;
626 a->x.array = hash_init();
627 }
628 return a->x.array;
629}
630
Mike Frysinger10a11e22005-09-27 02:23:02 +0000631static void clear_array(xhash *array)
632{
Denis Vlasenkof782f522007-01-01 23:51:30 +0000633 unsigned i;
Glenn L McGrath545106f2002-11-11 06:21:00 +0000634 hash_item *hi, *thi;
635
636 for (i=0; i<array->csize; i++) {
637 hi = array->items[i];
638 while (hi) {
639 thi = hi;
640 hi = hi->next;
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000641 free(thi->data.v.string);
Glenn L McGrath545106f2002-11-11 06:21:00 +0000642 free(thi);
643 }
644 array->items[i] = NULL;
645 }
646 array->glen = array->nel = 0;
647}
648
649/* clear a variable */
Mike Frysinger10a11e22005-09-27 02:23:02 +0000650static var *clrvar(var *v)
651{
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000652 if (!(v->type & VF_FSTR))
Glenn L McGrath545106f2002-11-11 06:21:00 +0000653 free(v->string);
654
655 v->type &= VF_DONTTOUCH;
656 v->type |= VF_DIRTY;
657 v->string = NULL;
658 return v;
659}
660
661/* assign string value to variable */
Mike Frysinger10a11e22005-09-27 02:23:02 +0000662static var *setvar_p(var *v, char *value)
663{
Glenn L McGrath545106f2002-11-11 06:21:00 +0000664 clrvar(v);
665 v->string = value;
666 handle_special(v);
667
668 return v;
669}
670
671/* same as setvar_p but make a copy of string */
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +0000672static var *setvar_s(var *v, const char *value)
Mike Frysinger10a11e22005-09-27 02:23:02 +0000673{
Rob Landleyd921b2e2006-08-03 15:41:12 +0000674 return setvar_p(v, (value && *value) ? xstrdup(value) : NULL);
Glenn L McGrath545106f2002-11-11 06:21:00 +0000675}
676
677/* same as setvar_s but set USER flag */
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +0000678static var *setvar_u(var *v, const char *value)
Mike Frysinger10a11e22005-09-27 02:23:02 +0000679{
Glenn L McGrath545106f2002-11-11 06:21:00 +0000680 setvar_s(v, value);
681 v->type |= VF_USER;
682 return v;
683}
684
685/* set array element to user string */
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +0000686static void setari_u(var *a, int idx, const char *s)
Mike Frysinger10a11e22005-09-27 02:23:02 +0000687{
"Robert P. J. Day"68229832006-07-01 13:08:46 +0000688 var *v;
Glenn L McGrath545106f2002-11-11 06:21:00 +0000689 static char sidx[12];
690
691 sprintf(sidx, "%d", idx);
692 v = findvar(iamarray(a), sidx);
693 setvar_u(v, s);
694}
695
696/* assign numeric value to variable */
Mike Frysinger10a11e22005-09-27 02:23:02 +0000697static var *setvar_i(var *v, double value)
698{
Glenn L McGrath545106f2002-11-11 06:21:00 +0000699 clrvar(v);
700 v->type |= VF_NUMBER;
701 v->number = value;
702 handle_special(v);
703 return v;
704}
705
Mike Frysinger10a11e22005-09-27 02:23:02 +0000706static char *getvar_s(var *v)
707{
Glenn L McGrath545106f2002-11-11 06:21:00 +0000708 /* if v is numeric and has no cached string, convert it to string */
709 if ((v->type & (VF_NUMBER | VF_CACHED)) == VF_NUMBER) {
710 fmt_num(buf, MAXVARFMT, getvar_s(V[CONVFMT]), v->number, TRUE);
Rob Landleyd921b2e2006-08-03 15:41:12 +0000711 v->string = xstrdup(buf);
Glenn L McGrath545106f2002-11-11 06:21:00 +0000712 v->type |= VF_CACHED;
713 }
714 return (v->string == NULL) ? "" : v->string;
715}
716
Mike Frysinger10a11e22005-09-27 02:23:02 +0000717static double getvar_i(var *v)
718{
Glenn L McGrath545106f2002-11-11 06:21:00 +0000719 char *s;
720
721 if ((v->type & (VF_NUMBER | VF_CACHED)) == 0) {
722 v->number = 0;
723 s = v->string;
724 if (s && *s) {
725 v->number = strtod(s, &s);
726 if (v->type & VF_USER) {
727 skip_spaces(&s);
728 if (*s != '\0')
729 v->type &= ~VF_USER;
730 }
731 } else {
732 v->type &= ~VF_USER;
733 }
734 v->type |= VF_CACHED;
735 }
736 return v->number;
737}
738
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +0000739static var *copyvar(var *dest, const var *src)
Mike Frysinger10a11e22005-09-27 02:23:02 +0000740{
Glenn L McGrath545106f2002-11-11 06:21:00 +0000741 if (dest != src) {
742 clrvar(dest);
743 dest->type |= (src->type & ~VF_DONTTOUCH);
744 dest->number = src->number;
745 if (src->string)
Rob Landleyd921b2e2006-08-03 15:41:12 +0000746 dest->string = xstrdup(src->string);
Glenn L McGrath545106f2002-11-11 06:21:00 +0000747 }
748 handle_special(dest);
749 return dest;
750}
751
Mike Frysinger10a11e22005-09-27 02:23:02 +0000752static var *incvar(var *v)
753{
Glenn L McGrath545106f2002-11-11 06:21:00 +0000754 return setvar_i(v, getvar_i(v)+1.);
755}
756
757/* return true if v is number or numeric string */
Mike Frysinger10a11e22005-09-27 02:23:02 +0000758static int is_numeric(var *v)
759{
Glenn L McGrath545106f2002-11-11 06:21:00 +0000760 getvar_i(v);
761 return ((v->type ^ VF_DIRTY) & (VF_NUMBER | VF_USER | VF_DIRTY));
762}
763
764/* return 1 when value of v corresponds to true, 0 otherwise */
Mike Frysinger10a11e22005-09-27 02:23:02 +0000765static int istrue(var *v)
766{
Glenn L McGrath545106f2002-11-11 06:21:00 +0000767 if (is_numeric(v))
768 return (v->number == 0) ? 0 : 1;
769 else
770 return (v->string && *(v->string)) ? 1 : 0;
771}
772
Eric Andersenaff114c2004-04-14 17:51:38 +0000773/* temporary variables allocator. Last allocated should be first freed */
Mike Frysinger10a11e22005-09-27 02:23:02 +0000774static var *nvalloc(int n)
775{
Glenn L McGrath545106f2002-11-11 06:21:00 +0000776 nvblock *pb = NULL;
777 var *v, *r;
778 int size;
779
780 while (cb) {
781 pb = cb;
782 if ((cb->pos - cb->nv) + n <= cb->size) break;
783 cb = cb->next;
784 }
785
786 if (! cb) {
787 size = (n <= MINNVBLOCK) ? MINNVBLOCK : n;
Denis Vlasenkob95636c2006-12-19 23:36:04 +0000788 cb = xmalloc(sizeof(nvblock) + size * sizeof(var));
Glenn L McGrath545106f2002-11-11 06:21:00 +0000789 cb->size = size;
790 cb->pos = cb->nv;
791 cb->prev = pb;
792 cb->next = NULL;
793 if (pb) pb->next = cb;
794 }
795
796 v = r = cb->pos;
797 cb->pos += n;
798
799 while (v < cb->pos) {
800 v->type = 0;
801 v->string = NULL;
802 v++;
803 }
804
805 return r;
806}
807
Mike Frysinger10a11e22005-09-27 02:23:02 +0000808static void nvfree(var *v)
809{
Glenn L McGrath545106f2002-11-11 06:21:00 +0000810 var *p;
811
812 if (v < cb->nv || v >= cb->pos)
813 runtime_error(EMSG_INTERNAL_ERROR);
814
815 for (p=v; p<cb->pos; p++) {
816 if ((p->type & (VF_ARRAY|VF_CHILD)) == VF_ARRAY) {
817 clear_array(iamarray(p));
818 free(p->x.array->items);
819 free(p->x.array);
820 }
821 if (p->type & VF_WALK)
822 free(p->x.walker);
823
824 clrvar(p);
825 }
826
827 cb->pos = v;
828 while (cb->prev && cb->pos == cb->nv) {
829 cb = cb->prev;
830 }
831}
832
833/* ------- awk program text parsing ------- */
834
835/* Parse next token pointed by global pos, place results into global t.
836 * If token isn't expected, give away. Return token class
837 */
Mike Frysingerf87b3e32005-09-27 04:16:22 +0000838static uint32_t next_token(uint32_t expected)
Mike Frysinger10a11e22005-09-27 02:23:02 +0000839{
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +0000840 static int concat_inserted;
Mike Frysingerf87b3e32005-09-27 04:16:22 +0000841 static uint32_t save_tclass, save_info;
842 static uint32_t ltclass = TC_OPTERM;
Glenn L McGrath545106f2002-11-11 06:21:00 +0000843
Denis Vlasenkof782f522007-01-01 23:51:30 +0000844 char *p, *pp, *s;
845 const char *tl;
846 uint32_t tc;
847 const uint32_t *ti;
848 int l;
849
Glenn L McGrath545106f2002-11-11 06:21:00 +0000850 if (t.rollback) {
Glenn L McGrath545106f2002-11-11 06:21:00 +0000851 t.rollback = FALSE;
852
853 } else if (concat_inserted) {
Glenn L McGrath545106f2002-11-11 06:21:00 +0000854 concat_inserted = FALSE;
855 t.tclass = save_tclass;
856 t.info = save_info;
857
858 } else {
Glenn L McGrath545106f2002-11-11 06:21:00 +0000859 p = pos;
Denis Vlasenkoe1d3e032007-01-01 23:53:52 +0000860 readnext:
Glenn L McGrath545106f2002-11-11 06:21:00 +0000861 skip_spaces(&p);
862 lineno = t.lineno;
863 if (*p == '#')
864 while (*p != '\n' && *p != '\0') p++;
865
866 if (*p == '\n')
867 t.lineno++;
868
869 if (*p == '\0') {
870 tc = TC_EOF;
871
872 } else if (*p == '\"') {
873 /* it's a string */
874 t.string = s = ++p;
875 while (*p != '\"') {
876 if (*p == '\0' || *p == '\n')
877 syntax_error(EMSG_UNEXP_EOS);
878 *(s++) = nextchar(&p);
879 }
880 p++;
881 *s = '\0';
882 tc = TC_STRING;
883
884 } else if ((expected & TC_REGEXP) && *p == '/') {
885 /* it's regexp */
886 t.string = s = ++p;
887 while (*p != '/') {
888 if (*p == '\0' || *p == '\n')
889 syntax_error(EMSG_UNEXP_EOS);
890 if ((*s++ = *p++) == '\\') {
891 pp = p;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000892 *(s-1) = bb_process_escape_sequence((const char **)&p);
Glenn L McGrath545106f2002-11-11 06:21:00 +0000893 if (*pp == '\\') *s++ = '\\';
894 if (p == pp) *s++ = *p++;
895 }
896 }
897 p++;
898 *s = '\0';
899 tc = TC_REGEXP;
900
901 } else if (*p == '.' || isdigit(*p)) {
902 /* it's a number */
903 t.number = strtod(p, &p);
904 if (*p == '.')
905 syntax_error(EMSG_UNEXP_TOKEN);
906 tc = TC_NUMBER;
907
908 } else {
909 /* search for something known */
910 tl = tokenlist;
911 tc = 0x00000001;
912 ti = tokeninfo;
913 while (*tl) {
914 l = *(tl++);
915 if (l == NTCC) {
916 tc <<= 1;
917 continue;
918 }
919 /* if token class is expected, token
920 * matches and it's not a longer word,
921 * then this is what we are looking for
922 */
923 if ((tc & (expected | TC_WORD | TC_NEWLINE)) &&
924 *tl == *p && strncmp(p, tl, l) == 0 &&
925 !((tc & TC_WORD) && isalnum_(*(p + l)))) {
926 t.info = *ti;
927 p += l;
928 break;
929 }
930 ti++;
931 tl += l;
932 }
933
Denis Vlasenkof782f522007-01-01 23:51:30 +0000934 if (!*tl) {
Glenn L McGrath545106f2002-11-11 06:21:00 +0000935 /* it's a name (var/array/function),
936 * otherwise it's something wrong
937 */
Denis Vlasenkoe1d3e032007-01-01 23:53:52 +0000938 if (!isalnum_(*p))
Glenn L McGrath545106f2002-11-11 06:21:00 +0000939 syntax_error(EMSG_UNEXP_TOKEN);
940
941 t.string = --p;
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000942 while (isalnum_(*(++p))) {
Glenn L McGrath545106f2002-11-11 06:21:00 +0000943 *(p-1) = *p;
944 }
945 *(p-1) = '\0';
946 tc = TC_VARIABLE;
Bernhard Reutner-Fischerbb204622005-10-17 14:21:06 +0000947 /* also consume whitespace between functionname and bracket */
Denis Vlasenkoe1d3e032007-01-01 23:53:52 +0000948 if (!(expected & TC_VARIABLE)) skip_spaces(&p);
Glenn L McGrath545106f2002-11-11 06:21:00 +0000949 if (*p == '(') {
950 tc = TC_FUNCTION;
951 } else {
Glenn L McGrath545106f2002-11-11 06:21:00 +0000952 if (*p == '[') {
953 p++;
954 tc = TC_ARRAY;
955 }
956 }
957 }
958 }
959 pos = p;
960
961 /* skipping newlines in some cases */
962 if ((ltclass & TC_NOTERM) && (tc & TC_NEWLINE))
963 goto readnext;
964
965 /* insert concatenation operator when needed */
966 if ((ltclass&TC_CONCAT1) && (tc&TC_CONCAT2) && (expected&TC_BINOP)) {
967 concat_inserted = TRUE;
968 save_tclass = tc;
969 save_info = t.info;
970 tc = TC_BINOP;
971 t.info = OC_CONCAT | SS | P(35);
972 }
973
974 t.tclass = tc;
975 }
976 ltclass = t.tclass;
977
978 /* Are we ready for this? */
979 if (! (ltclass & expected))
980 syntax_error((ltclass & (TC_NEWLINE | TC_EOF)) ?
981 EMSG_UNEXP_EOS : EMSG_UNEXP_TOKEN);
982
983 return ltclass;
984}
985
986static void rollback_token(void) { t.rollback = TRUE; }
987
Mike Frysingerf87b3e32005-09-27 04:16:22 +0000988static node *new_node(uint32_t info)
Mike Frysinger10a11e22005-09-27 02:23:02 +0000989{
"Robert P. J. Day"68229832006-07-01 13:08:46 +0000990 node *n;
Glenn L McGrath545106f2002-11-11 06:21:00 +0000991
Denis Vlasenko4cccc032006-12-22 18:37:07 +0000992 n = xzalloc(sizeof(node));
Glenn L McGrath545106f2002-11-11 06:21:00 +0000993 n->info = info;
994 n->lineno = lineno;
995 return n;
996}
997
Mike Frysinger10a11e22005-09-27 02:23:02 +0000998static node *mk_re_node(char *s, node *n, regex_t *re)
999{
Glenn L McGrath545106f2002-11-11 06:21:00 +00001000 n->info = OC_REGEXP;
1001 n->l.re = re;
1002 n->r.ire = re + 1;
1003 xregcomp(re, s, REG_EXTENDED);
1004 xregcomp(re+1, s, REG_EXTENDED | REG_ICASE);
1005
1006 return n;
1007}
1008
Mike Frysinger10a11e22005-09-27 02:23:02 +00001009static node *condition(void)
1010{
Glenn L McGrath545106f2002-11-11 06:21:00 +00001011 next_token(TC_SEQSTART);
1012 return parse_expr(TC_SEQTERM);
1013}
1014
1015/* parse expression terminated by given argument, return ptr
1016 * to built subtree. Terminator is eaten by parse_expr */
Mike Frysingerf87b3e32005-09-27 04:16:22 +00001017static node *parse_expr(uint32_t iexp)
Mike Frysinger10a11e22005-09-27 02:23:02 +00001018{
Glenn L McGrath545106f2002-11-11 06:21:00 +00001019 node sn;
1020 node *cn = &sn;
1021 node *vn, *glptr;
Mike Frysingerf87b3e32005-09-27 04:16:22 +00001022 uint32_t tc, xtc;
Glenn L McGrath545106f2002-11-11 06:21:00 +00001023 var *v;
1024
1025 sn.info = PRIMASK;
1026 sn.r.n = glptr = NULL;
1027 xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP | iexp;
1028
1029 while (! ((tc = next_token(xtc)) & iexp)) {
1030 if (glptr && (t.info == (OC_COMPARE|VV|P(39)|2))) {
1031 /* input redirection (<) attached to glptr node */
1032 cn = glptr->l.n = new_node(OC_CONCAT|SS|P(37));
Glenn L McGrath4bded582004-02-22 11:55:09 +00001033 cn->a.n = glptr;
Glenn L McGrath545106f2002-11-11 06:21:00 +00001034 xtc = TC_OPERAND | TC_UOPPRE;
1035 glptr = NULL;
1036
1037 } else if (tc & (TC_BINOP | TC_UOPPOST)) {
1038 /* for binary and postfix-unary operators, jump back over
1039 * previous operators with higher priority */
1040 vn = cn;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001041 while ( ((t.info & PRIMASK) > (vn->a.n->info & PRIMASK2)) ||
Glenn L McGrath545106f2002-11-11 06:21:00 +00001042 ((t.info == vn->info) && ((t.info & OPCLSMASK) == OC_COLON)) )
1043 vn = vn->a.n;
1044 if ((t.info & OPCLSMASK) == OC_TERNARY)
1045 t.info += P(6);
1046 cn = vn->a.n->r.n = new_node(t.info);
1047 cn->a.n = vn->a.n;
1048 if (tc & TC_BINOP) {
1049 cn->l.n = vn;
1050 xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP;
1051 if ((t.info & OPCLSMASK) == OC_PGETLINE) {
1052 /* it's a pipe */
1053 next_token(TC_GETLINE);
1054 /* give maximum priority to this pipe */
1055 cn->info &= ~PRIMASK;
1056 xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp;
1057 }
1058 } else {
1059 cn->r.n = vn;
1060 xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp;
1061 }
1062 vn->a.n = cn;
1063
1064 } else {
1065 /* for operands and prefix-unary operators, attach them
1066 * to last node */
1067 vn = cn;
Mike Frysingerde2b9382005-09-27 03:18:00 +00001068 cn = vn->r.n = new_node(t.info);
Glenn L McGrath545106f2002-11-11 06:21:00 +00001069 cn->a.n = vn;
1070 xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP;
1071 if (tc & (TC_OPERAND | TC_REGEXP)) {
Rob Landleyed830e82005-06-07 02:43:52 +00001072 xtc = TC_UOPPRE | TC_UOPPOST | TC_BINOP | TC_OPERAND | iexp;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001073 /* one should be very careful with switch on tclass -
Glenn L McGrath545106f2002-11-11 06:21:00 +00001074 * only simple tclasses should be used! */
1075 switch (tc) {
Denis Vlasenkof782f522007-01-01 23:51:30 +00001076 case TC_VARIABLE:
1077 case TC_ARRAY:
Glenn L McGrath545106f2002-11-11 06:21:00 +00001078 cn->info = OC_VAR;
Mike Frysingerde2b9382005-09-27 03:18:00 +00001079 if ((v = hash_search(ahash, t.string)) != NULL) {
Glenn L McGrath545106f2002-11-11 06:21:00 +00001080 cn->info = OC_FNARG;
1081 cn->l.i = v->x.aidx;
1082 } else {
Mike Frysingerde2b9382005-09-27 03:18:00 +00001083 cn->l.v = newvar(t.string);
Glenn L McGrath545106f2002-11-11 06:21:00 +00001084 }
1085 if (tc & TC_ARRAY) {
1086 cn->info |= xS;
1087 cn->r.n = parse_expr(TC_ARRTERM);
1088 }
Glenn L McGrath545106f2002-11-11 06:21:00 +00001089 break;
Mike Frysingerde2b9382005-09-27 03:18:00 +00001090
Denis Vlasenkof782f522007-01-01 23:51:30 +00001091 case TC_NUMBER:
1092 case TC_STRING:
Glenn L McGrath545106f2002-11-11 06:21:00 +00001093 cn->info = OC_VAR;
Rob Landley9ffd4232006-05-21 18:30:35 +00001094 v = cn->l.v = xzalloc(sizeof(var));
Glenn L McGrath545106f2002-11-11 06:21:00 +00001095 if (tc & TC_NUMBER)
1096 setvar_i(v, t.number);
1097 else
1098 setvar_s(v, t.string);
1099 break;
1100
Denis Vlasenkof782f522007-01-01 23:51:30 +00001101 case TC_REGEXP:
Denis Vlasenko4cccc032006-12-22 18:37:07 +00001102 mk_re_node(t.string, cn, xzalloc(sizeof(regex_t)*2));
Glenn L McGrath545106f2002-11-11 06:21:00 +00001103 break;
1104
Denis Vlasenkof782f522007-01-01 23:51:30 +00001105 case TC_FUNCTION:
Mike Frysingerde2b9382005-09-27 03:18:00 +00001106 cn->info = OC_FUNC;
Glenn L McGrath545106f2002-11-11 06:21:00 +00001107 cn->r.f = newfunc(t.string);
1108 cn->l.n = condition();
1109 break;
1110
Denis Vlasenkof782f522007-01-01 23:51:30 +00001111 case TC_SEQSTART:
Glenn L McGrath545106f2002-11-11 06:21:00 +00001112 cn = vn->r.n = parse_expr(TC_SEQTERM);
1113 cn->a.n = vn;
1114 break;
1115
Denis Vlasenkof782f522007-01-01 23:51:30 +00001116 case TC_GETLINE:
Glenn L McGrath545106f2002-11-11 06:21:00 +00001117 glptr = cn;
1118 xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp;
1119 break;
1120
Denis Vlasenkof782f522007-01-01 23:51:30 +00001121 case TC_BUILTIN:
Glenn L McGrath545106f2002-11-11 06:21:00 +00001122 cn->l.n = condition();
1123 break;
1124 }
1125 }
1126 }
1127 }
1128 return sn.r.n;
1129}
1130
1131/* add node to chain. Return ptr to alloc'd node */
Mike Frysingerf87b3e32005-09-27 04:16:22 +00001132static node *chain_node(uint32_t info)
Mike Frysinger10a11e22005-09-27 02:23:02 +00001133{
"Robert P. J. Day"68229832006-07-01 13:08:46 +00001134 node *n;
Glenn L McGrath545106f2002-11-11 06:21:00 +00001135
1136 if (! seq->first)
1137 seq->first = seq->last = new_node(0);
1138
1139 if (seq->programname != programname) {
1140 seq->programname = programname;
1141 n = chain_node(OC_NEWSOURCE);
Rob Landleyd921b2e2006-08-03 15:41:12 +00001142 n->l.s = xstrdup(programname);
Glenn L McGrath545106f2002-11-11 06:21:00 +00001143 }
1144
1145 n = seq->last;
1146 n->info = info;
1147 seq->last = n->a.n = new_node(OC_DONE);
1148
1149 return n;
1150}
1151
Mike Frysingerf87b3e32005-09-27 04:16:22 +00001152static void chain_expr(uint32_t info)
Mike Frysinger10a11e22005-09-27 02:23:02 +00001153{
Glenn L McGrath545106f2002-11-11 06:21:00 +00001154 node *n;
1155
1156 n = chain_node(info);
1157 n->l.n = parse_expr(TC_OPTERM | TC_GRPTERM);
1158 if (t.tclass & TC_GRPTERM)
1159 rollback_token();
1160}
1161
Mike Frysinger10a11e22005-09-27 02:23:02 +00001162static node *chain_loop(node *nn)
1163{
Glenn L McGrath545106f2002-11-11 06:21:00 +00001164 node *n, *n2, *save_brk, *save_cont;
1165
1166 save_brk = break_ptr;
1167 save_cont = continue_ptr;
1168
1169 n = chain_node(OC_BR | Vx);
1170 continue_ptr = new_node(OC_EXEC);
1171 break_ptr = new_node(OC_EXEC);
1172 chain_group();
1173 n2 = chain_node(OC_EXEC | Vx);
1174 n2->l.n = nn;
1175 n2->a.n = n;
1176 continue_ptr->a.n = n2;
1177 break_ptr->a.n = n->r.n = seq->last;
1178
1179 continue_ptr = save_cont;
1180 break_ptr = save_brk;
1181
1182 return n;
1183}
1184
1185/* parse group and attach it to chain */
Mike Frysinger10a11e22005-09-27 02:23:02 +00001186static void chain_group(void)
1187{
Mike Frysingerf87b3e32005-09-27 04:16:22 +00001188 uint32_t c;
Glenn L McGrath545106f2002-11-11 06:21:00 +00001189 node *n, *n2, *n3;
1190
1191 do {
1192 c = next_token(TC_GRPSEQ);
1193 } while (c & TC_NEWLINE);
1194
1195 if (c & TC_GRPSTART) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +00001196 while (next_token(TC_GRPSEQ | TC_GRPTERM) != TC_GRPTERM) {
Mike Frysingerde2b9382005-09-27 03:18:00 +00001197 if (t.tclass & TC_NEWLINE) continue;
Glenn L McGrath545106f2002-11-11 06:21:00 +00001198 rollback_token();
1199 chain_group();
1200 }
1201 } else if (c & (TC_OPSEQ | TC_OPTERM)) {
1202 rollback_token();
1203 chain_expr(OC_EXEC | Vx);
1204 } else { /* TC_STATEMNT */
1205 switch (t.info & OPCLSMASK) {
1206 case ST_IF:
1207 n = chain_node(OC_BR | Vx);
1208 n->l.n = condition();
1209 chain_group();
1210 n2 = chain_node(OC_EXEC);
1211 n->r.n = seq->last;
1212 if (next_token(TC_GRPSEQ | TC_GRPTERM | TC_ELSE)==TC_ELSE) {
1213 chain_group();
1214 n2->a.n = seq->last;
1215 } else {
1216 rollback_token();
1217 }
1218 break;
1219
1220 case ST_WHILE:
1221 n2 = condition();
1222 n = chain_loop(NULL);
1223 n->l.n = n2;
1224 break;
1225
1226 case ST_DO:
1227 n2 = chain_node(OC_EXEC);
1228 n = chain_loop(NULL);
1229 n2->a.n = n->a.n;
1230 next_token(TC_WHILE);
1231 n->l.n = condition();
1232 break;
1233
1234 case ST_FOR:
1235 next_token(TC_SEQSTART);
1236 n2 = parse_expr(TC_SEMICOL | TC_SEQTERM);
Denis Vlasenkobf0a2012006-12-26 10:42:51 +00001237 if (t.tclass & TC_SEQTERM) { /* for-in */
Glenn L McGrath545106f2002-11-11 06:21:00 +00001238 if ((n2->info & OPCLSMASK) != OC_IN)
1239 syntax_error(EMSG_UNEXP_TOKEN);
1240 n = chain_node(OC_WALKINIT | VV);
1241 n->l.n = n2->l.n;
1242 n->r.n = n2->r.n;
1243 n = chain_loop(NULL);
1244 n->info = OC_WALKNEXT | Vx;
1245 n->l.n = n2->l.n;
Denis Vlasenkobf0a2012006-12-26 10:42:51 +00001246 } else { /* for (;;) */
Glenn L McGrath545106f2002-11-11 06:21:00 +00001247 n = chain_node(OC_EXEC | Vx);
1248 n->l.n = n2;
1249 n2 = parse_expr(TC_SEMICOL);
1250 n3 = parse_expr(TC_SEQTERM);
1251 n = chain_loop(n3);
1252 n->l.n = n2;
1253 if (! n2)
1254 n->info = OC_EXEC;
1255 }
1256 break;
1257
1258 case OC_PRINT:
1259 case OC_PRINTF:
1260 n = chain_node(t.info);
1261 n->l.n = parse_expr(TC_OPTERM | TC_OUTRDR | TC_GRPTERM);
1262 if (t.tclass & TC_OUTRDR) {
1263 n->info |= t.info;
1264 n->r.n = parse_expr(TC_OPTERM | TC_GRPTERM);
1265 }
1266 if (t.tclass & TC_GRPTERM)
1267 rollback_token();
1268 break;
1269
1270 case OC_BREAK:
1271 n = chain_node(OC_EXEC);
1272 n->a.n = break_ptr;
1273 break;
1274
1275 case OC_CONTINUE:
1276 n = chain_node(OC_EXEC);
1277 n->a.n = continue_ptr;
1278 break;
1279
1280 /* delete, next, nextfile, return, exit */
1281 default:
1282 chain_expr(t.info);
Glenn L McGrath545106f2002-11-11 06:21:00 +00001283 }
1284 }
1285}
1286
Mike Frysinger10a11e22005-09-27 02:23:02 +00001287static void parse_program(char *p)
1288{
Mike Frysingerf87b3e32005-09-27 04:16:22 +00001289 uint32_t tclass;
Glenn L McGrath545106f2002-11-11 06:21:00 +00001290 node *cn;
1291 func *f;
1292 var *v;
1293
1294 pos = p;
1295 t.lineno = 1;
Denis Vlasenkobf0a2012006-12-26 10:42:51 +00001296 while ((tclass = next_token(TC_EOF | TC_OPSEQ | TC_GRPSTART |
Glenn L McGrath545106f2002-11-11 06:21:00 +00001297 TC_OPTERM | TC_BEGIN | TC_END | TC_FUNCDECL)) != TC_EOF) {
1298
1299 if (tclass & TC_OPTERM)
1300 continue;
1301
1302 seq = &mainseq;
1303 if (tclass & TC_BEGIN) {
1304 seq = &beginseq;
1305 chain_group();
1306
1307 } else if (tclass & TC_END) {
1308 seq = &endseq;
1309 chain_group();
1310
1311 } else if (tclass & TC_FUNCDECL) {
1312 next_token(TC_FUNCTION);
1313 pos++;
1314 f = newfunc(t.string);
1315 f->body.first = NULL;
1316 f->nargs = 0;
Denis Vlasenkobf0a2012006-12-26 10:42:51 +00001317 while (next_token(TC_VARIABLE | TC_SEQTERM) & TC_VARIABLE) {
Glenn L McGrath545106f2002-11-11 06:21:00 +00001318 v = findvar(ahash, t.string);
1319 v->x.aidx = (f->nargs)++;
1320
1321 if (next_token(TC_COMMA | TC_SEQTERM) & TC_SEQTERM)
1322 break;
1323 }
1324 seq = &(f->body);
1325 chain_group();
1326 clear_array(ahash);
1327
1328 } else if (tclass & TC_OPSEQ) {
1329 rollback_token();
1330 cn = chain_node(OC_TEST);
1331 cn->l.n = parse_expr(TC_OPTERM | TC_EOF | TC_GRPSTART);
1332 if (t.tclass & TC_GRPSTART) {
1333 rollback_token();
1334 chain_group();
1335 } else {
1336 chain_node(OC_PRINT);
1337 }
1338 cn->r.n = mainseq.last;
1339
1340 } else /* if (tclass & TC_GRPSTART) */ {
1341 rollback_token();
1342 chain_group();
1343 }
1344 }
1345}
1346
1347
1348/* -------- program execution part -------- */
1349
Mike Frysinger10a11e22005-09-27 02:23:02 +00001350static node *mk_splitter(char *s, tsplitter *spl)
1351{
"Robert P. J. Day"68229832006-07-01 13:08:46 +00001352 regex_t *re, *ire;
Glenn L McGrath545106f2002-11-11 06:21:00 +00001353 node *n;
1354
1355 re = &spl->re[0];
1356 ire = &spl->re[1];
1357 n = &spl->n;
Denis Vlasenko890ac9d2006-10-07 15:16:19 +00001358 if ((n->info & OPCLSMASK) == OC_REGEXP) {
Glenn L McGrath545106f2002-11-11 06:21:00 +00001359 regfree(re);
1360 regfree(ire);
1361 }
Rob Landleya3896512006-05-07 20:20:34 +00001362 if (strlen(s) > 1) {
Glenn L McGrath545106f2002-11-11 06:21:00 +00001363 mk_re_node(s, n, re);
1364 } else {
Mike Frysingerf87b3e32005-09-27 04:16:22 +00001365 n->info = (uint32_t) *s;
Glenn L McGrath545106f2002-11-11 06:21:00 +00001366 }
1367
1368 return n;
1369}
1370
1371/* use node as a regular expression. Supplied with node ptr and regex_t
Eric Andersenaff114c2004-04-14 17:51:38 +00001372 * storage space. Return ptr to regex (if result points to preg, it should
Glenn L McGrath545106f2002-11-11 06:21:00 +00001373 * be later regfree'd manually
1374 */
Mike Frysinger10a11e22005-09-27 02:23:02 +00001375static regex_t *as_regex(node *op, regex_t *preg)
1376{
Glenn L McGrath545106f2002-11-11 06:21:00 +00001377 var *v;
1378 char *s;
1379
1380 if ((op->info & OPCLSMASK) == OC_REGEXP) {
1381 return icase ? op->r.ire : op->l.re;
1382 } else {
1383 v = nvalloc(1);
1384 s = getvar_s(evaluate(op, v));
1385 xregcomp(preg, s, icase ? REG_EXTENDED | REG_ICASE : REG_EXTENDED);
1386 nvfree(v);
1387 return preg;
1388 }
1389}
1390
1391/* gradually increasing buffer */
Mike Frysinger10a11e22005-09-27 02:23:02 +00001392static void qrealloc(char **b, int n, int *size)
1393{
Denis Vlasenkoe1d3e032007-01-01 23:53:52 +00001394 if (!*b || n >= *size)
Glenn L McGrath545106f2002-11-11 06:21:00 +00001395 *b = xrealloc(*b, *size = n + (n>>1) + 80);
1396}
1397
1398/* resize field storage space */
Mike Frysinger10a11e22005-09-27 02:23:02 +00001399static void fsrealloc(int size)
1400{
Denis Vlasenkoe1d3e032007-01-01 23:53:52 +00001401 static int maxfields; /* = 0;*/
Glenn L McGrath545106f2002-11-11 06:21:00 +00001402 int i;
1403
1404 if (size >= maxfields) {
1405 i = maxfields;
1406 maxfields = size + 16;
Denis Vlasenkof782f522007-01-01 23:51:30 +00001407 Fields = xrealloc(Fields, maxfields * sizeof(var));
1408 for (; i < maxfields; i++) {
Glenn L McGrath545106f2002-11-11 06:21:00 +00001409 Fields[i].type = VF_SPECIAL;
1410 Fields[i].string = NULL;
1411 }
1412 }
1413
1414 if (size < nfields) {
Denis Vlasenkoe1d3e032007-01-01 23:53:52 +00001415 for (i = size; i < nfields; i++) {
1416 clrvar(Fields + i);
Glenn L McGrath545106f2002-11-11 06:21:00 +00001417 }
1418 }
1419 nfields = size;
1420}
1421
Mike Frysinger10a11e22005-09-27 02:23:02 +00001422static int awk_split(char *s, node *spl, char **slist)
1423{
Denis Vlasenkof782f522007-01-01 23:51:30 +00001424 int l, n = 0;
Glenn L McGrath545106f2002-11-11 06:21:00 +00001425 char c[4];
1426 char *s1;
1427 regmatch_t pmatch[2];
1428
1429 /* in worst case, each char would be a separate field */
Rob Landleyd921b2e2006-08-03 15:41:12 +00001430 *slist = s1 = xstrndup(s, strlen(s) * 2 + 3);
Glenn L McGrath545106f2002-11-11 06:21:00 +00001431
1432 c[0] = c[1] = (char)spl->info;
1433 c[2] = c[3] = '\0';
1434 if (*getvar_s(V[RS]) == '\0') c[2] = '\n';
1435
1436 if ((spl->info & OPCLSMASK) == OC_REGEXP) { /* regex split */
1437 while (*s) {
1438 l = strcspn(s, c+2);
1439 if (regexec(icase ? spl->r.ire : spl->l.re, s, 1, pmatch, 0) == 0 &&
1440 pmatch[0].rm_so <= l) {
1441 l = pmatch[0].rm_so;
1442 if (pmatch[0].rm_eo == 0) { l++; pmatch[0].rm_eo++; }
1443 } else {
1444 pmatch[0].rm_eo = l;
Denis Vlasenkof782f522007-01-01 23:51:30 +00001445 if (s[l]) pmatch[0].rm_eo++;
Glenn L McGrath545106f2002-11-11 06:21:00 +00001446 }
1447
1448 memcpy(s1, s, l);
Denis Vlasenkof782f522007-01-01 23:51:30 +00001449 s1[l] = '\0';
Glenn L McGrath545106f2002-11-11 06:21:00 +00001450 nextword(&s1);
1451 s += pmatch[0].rm_eo;
1452 n++;
1453 }
1454 } else if (c[0] == '\0') { /* null split */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +00001455 while (*s) {
Denis Vlasenkoe1d3e032007-01-01 23:53:52 +00001456 *s1++ = *s++;
1457 *s1++ = '\0';
Glenn L McGrath545106f2002-11-11 06:21:00 +00001458 n++;
1459 }
1460 } else if (c[0] != ' ') { /* single-character split */
1461 if (icase) {
1462 c[0] = toupper(c[0]);
1463 c[1] = tolower(c[1]);
1464 }
1465 if (*s1) n++;
1466 while ((s1 = strpbrk(s1, c))) {
Denis Vlasenkoe1d3e032007-01-01 23:53:52 +00001467 *s1++ = '\0';
Glenn L McGrath545106f2002-11-11 06:21:00 +00001468 n++;
1469 }
1470 } else { /* space split */
1471 while (*s) {
Denis Vlasenkod18a3a22006-10-25 12:46:03 +00001472 s = skip_whitespace(s);
Denis Vlasenkoe1d3e032007-01-01 23:53:52 +00001473 if (!*s) break;
Glenn L McGrath545106f2002-11-11 06:21:00 +00001474 n++;
1475 while (*s && !isspace(*s))
Denis Vlasenkoe1d3e032007-01-01 23:53:52 +00001476 *s1++ = *s++;
1477 *s1++ = '\0';
Glenn L McGrath545106f2002-11-11 06:21:00 +00001478 }
1479 }
1480 return n;
1481}
1482
Mike Frysinger10a11e22005-09-27 02:23:02 +00001483static void split_f0(void)
1484{
Glenn L McGrath545106f2002-11-11 06:21:00 +00001485 static char *fstrings = NULL;
1486 int i, n;
1487 char *s;
1488
1489 if (is_f0_split)
1490 return;
1491
1492 is_f0_split = TRUE;
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001493 free(fstrings);
Glenn L McGrath545106f2002-11-11 06:21:00 +00001494 fsrealloc(0);
1495 n = awk_split(getvar_s(V[F0]), &fsplitter.n, &fstrings);
1496 fsrealloc(n);
1497 s = fstrings;
Denis Vlasenkof782f522007-01-01 23:51:30 +00001498 for (i = 0; i < n; i++) {
Glenn L McGrath545106f2002-11-11 06:21:00 +00001499 Fields[i].string = nextword(&s);
1500 Fields[i].type |= (VF_FSTR | VF_USER | VF_DIRTY);
1501 }
1502
1503 /* set NF manually to avoid side effects */
1504 clrvar(V[NF]);
1505 V[NF]->type = VF_NUMBER | VF_SPECIAL;
1506 V[NF]->number = nfields;
1507}
1508
1509/* perform additional actions when some internal variables changed */
Mike Frysinger10a11e22005-09-27 02:23:02 +00001510static void handle_special(var *v)
1511{
Glenn L McGrath545106f2002-11-11 06:21:00 +00001512 int n;
1513 char *b, *sep, *s;
1514 int sl, l, len, i, bsize;
1515
Denis Vlasenkoe1d3e032007-01-01 23:53:52 +00001516 if (!(v->type & VF_SPECIAL))
Glenn L McGrath545106f2002-11-11 06:21:00 +00001517 return;
1518
1519 if (v == V[NF]) {
1520 n = (int)getvar_i(v);
1521 fsrealloc(n);
1522
1523 /* recalculate $0 */
1524 sep = getvar_s(V[OFS]);
Rob Landleya3896512006-05-07 20:20:34 +00001525 sl = strlen(sep);
Glenn L McGrath545106f2002-11-11 06:21:00 +00001526 b = NULL;
1527 len = 0;
1528 for (i=0; i<n; i++) {
1529 s = getvar_s(&Fields[i]);
Rob Landleya3896512006-05-07 20:20:34 +00001530 l = strlen(s);
Glenn L McGrath545106f2002-11-11 06:21:00 +00001531 if (b) {
1532 memcpy(b+len, sep, sl);
1533 len += sl;
1534 }
1535 qrealloc(&b, len+l+sl, &bsize);
1536 memcpy(b+len, s, l);
1537 len += l;
1538 }
Denis Vlasenkoe1d3e032007-01-01 23:53:52 +00001539 if (b)
1540 b[len] = '\0';
Glenn L McGrath545106f2002-11-11 06:21:00 +00001541 setvar_p(V[F0], b);
1542 is_f0_split = TRUE;
1543
1544 } else if (v == V[F0]) {
1545 is_f0_split = FALSE;
1546
1547 } else if (v == V[FS]) {
1548 mk_splitter(getvar_s(v), &fsplitter);
1549
1550 } else if (v == V[RS]) {
1551 mk_splitter(getvar_s(v), &rsplitter);
1552
1553 } else if (v == V[IGNORECASE]) {
1554 icase = istrue(v);
1555
Denis Vlasenkoe1d3e032007-01-01 23:53:52 +00001556 } else { /* $n */
Glenn L McGrath545106f2002-11-11 06:21:00 +00001557 n = getvar_i(V[NF]);
1558 setvar_i(V[NF], n > v-Fields ? n : v-Fields+1);
1559 /* right here v is invalid. Just to note... */
1560 }
1561}
1562
1563/* step through func/builtin/etc arguments */
Mike Frysinger10a11e22005-09-27 02:23:02 +00001564static node *nextarg(node **pn)
1565{
Glenn L McGrath545106f2002-11-11 06:21:00 +00001566 node *n;
1567
1568 n = *pn;
1569 if (n && (n->info & OPCLSMASK) == OC_COMMA) {
1570 *pn = n->r.n;
1571 n = n->l.n;
1572 } else {
1573 *pn = NULL;
1574 }
1575 return n;
1576}
1577
Mike Frysinger10a11e22005-09-27 02:23:02 +00001578static void hashwalk_init(var *v, xhash *array)
1579{
Glenn L McGrath545106f2002-11-11 06:21:00 +00001580 char **w;
1581 hash_item *hi;
1582 int i;
1583
1584 if (v->type & VF_WALK)
1585 free(v->x.walker);
1586
1587 v->type |= VF_WALK;
Denis Vlasenko4cccc032006-12-22 18:37:07 +00001588 w = v->x.walker = xzalloc(2 + 2*sizeof(char *) + array->glen);
Glenn L McGrath545106f2002-11-11 06:21:00 +00001589 *w = *(w+1) = (char *)(w + 2);
1590 for (i=0; i<array->csize; i++) {
1591 hi = array->items[i];
Denis Vlasenkobf0a2012006-12-26 10:42:51 +00001592 while (hi) {
Glenn L McGrath545106f2002-11-11 06:21:00 +00001593 strcpy(*w, hi->name);
1594 nextword(w);
1595 hi = hi->next;
1596 }
1597 }
1598}
1599
Mike Frysinger10a11e22005-09-27 02:23:02 +00001600static int hashwalk_next(var *v)
1601{
Glenn L McGrath545106f2002-11-11 06:21:00 +00001602 char **w;
1603
1604 w = v->x.walker;
1605 if (*(w+1) == *w)
1606 return FALSE;
1607
1608 setvar_s(v, nextword(w+1));
1609 return TRUE;
1610}
1611
1612/* evaluate node, return 1 when result is true, 0 otherwise */
Mike Frysinger10a11e22005-09-27 02:23:02 +00001613static int ptest(node *pattern)
1614{
Denis Vlasenkof782f522007-01-01 23:51:30 +00001615 static var v; /* static: to save stack space? */
1616
Glenn L McGrath545106f2002-11-11 06:21:00 +00001617 return istrue(evaluate(pattern, &v));
1618}
1619
1620/* read next record from stream rsm into a variable v */
Mike Frysinger10a11e22005-09-27 02:23:02 +00001621static int awk_getline(rstream *rsm, var *v)
1622{
Glenn L McGrath545106f2002-11-11 06:21:00 +00001623 char *b;
1624 regmatch_t pmatch[2];
Glenn L McGrath00ed36f2003-10-30 13:36:39 +00001625 int a, p, pp=0, size;
Glenn L McGrath545106f2002-11-11 06:21:00 +00001626 int fd, so, eo, r, rp;
Glenn L McGrath00ed36f2003-10-30 13:36:39 +00001627 char c, *m, *s;
Glenn L McGrath545106f2002-11-11 06:21:00 +00001628
1629 /* we're using our own buffer since we need access to accumulating
1630 * characters
1631 */
1632 fd = fileno(rsm->F);
Glenn L McGrath00ed36f2003-10-30 13:36:39 +00001633 m = rsm->buffer;
1634 a = rsm->adv;
Glenn L McGrath545106f2002-11-11 06:21:00 +00001635 p = rsm->pos;
1636 size = rsm->size;
1637 c = (char) rsplitter.n.info;
1638 rp = 0;
Glenn L McGrath00ed36f2003-10-30 13:36:39 +00001639
1640 if (! m) qrealloc(&m, 256, &size);
Glenn L McGrath545106f2002-11-11 06:21:00 +00001641 do {
Glenn L McGrath00ed36f2003-10-30 13:36:39 +00001642 b = m + a;
Glenn L McGrath545106f2002-11-11 06:21:00 +00001643 so = eo = p;
1644 r = 1;
1645 if (p > 0) {
1646 if ((rsplitter.n.info & OPCLSMASK) == OC_REGEXP) {
1647 if (regexec(icase ? rsplitter.n.r.ire : rsplitter.n.l.re,
1648 b, 1, pmatch, 0) == 0) {
1649 so = pmatch[0].rm_so;
1650 eo = pmatch[0].rm_eo;
1651 if (b[eo] != '\0')
1652 break;
1653 }
1654 } else if (c != '\0') {
1655 s = strchr(b+pp, c);
Rob Landley46e351d2006-02-14 16:05:32 +00001656 if (! s) s = memchr(b+pp, '\0', p - pp);
Glenn L McGrath545106f2002-11-11 06:21:00 +00001657 if (s) {
1658 so = eo = s-b;
1659 eo++;
1660 break;
1661 }
1662 } else {
1663 while (b[rp] == '\n')
1664 rp++;
1665 s = strstr(b+rp, "\n\n");
1666 if (s) {
1667 so = eo = s-b;
1668 while (b[eo] == '\n') eo++;
1669 if (b[eo] != '\0')
1670 break;
1671 }
1672 }
1673 }
1674
Glenn L McGrath00ed36f2003-10-30 13:36:39 +00001675 if (a > 0) {
1676 memmove(m, (const void *)(m+a), p+1);
1677 b = m;
1678 a = 0;
1679 }
1680
1681 qrealloc(&m, a+p+128, &size);
1682 b = m + a;
Glenn L McGrath545106f2002-11-11 06:21:00 +00001683 pp = p;
1684 p += safe_read(fd, b+p, size-p-1);
1685 if (p < pp) {
1686 p = 0;
1687 r = 0;
1688 setvar_i(V[ERRNO], errno);
1689 }
1690 b[p] = '\0';
1691
1692 } while (p > pp);
1693
1694 if (p == 0) {
1695 r--;
1696 } else {
1697 c = b[so]; b[so] = '\0';
1698 setvar_s(v, b+rp);
1699 v->type |= VF_USER;
1700 b[so] = c;
1701 c = b[eo]; b[eo] = '\0';
1702 setvar_s(V[RT], b+so);
1703 b[eo] = c;
1704 }
1705
Glenn L McGrath00ed36f2003-10-30 13:36:39 +00001706 rsm->buffer = m;
1707 rsm->adv = a + eo;
1708 rsm->pos = p - eo;
Glenn L McGrath545106f2002-11-11 06:21:00 +00001709 rsm->size = size;
1710
1711 return r;
1712}
1713
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +00001714static int fmt_num(char *b, int size, const char *format, double n, int int_as_int)
Mike Frysinger10a11e22005-09-27 02:23:02 +00001715{
Denis Vlasenkof782f522007-01-01 23:51:30 +00001716 int r = 0;
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +00001717 char c;
Denis Vlasenkof782f522007-01-01 23:51:30 +00001718 const char *s = format;
Glenn L McGrath545106f2002-11-11 06:21:00 +00001719
1720 if (int_as_int && n == (int)n) {
1721 r = snprintf(b, size, "%d", (int)n);
1722 } else {
Denis Vlasenkof782f522007-01-01 23:51:30 +00001723 do { c = *s; } while (c && *++s);
Glenn L McGrath545106f2002-11-11 06:21:00 +00001724 if (strchr("diouxX", c)) {
1725 r = snprintf(b, size, format, (int)n);
1726 } else if (strchr("eEfgG", c)) {
1727 r = snprintf(b, size, format, n);
1728 } else {
1729 runtime_error(EMSG_INV_FMT);
1730 }
1731 }
1732 return r;
1733}
1734
1735
1736/* formatted output into an allocated buffer, return ptr to buffer */
Mike Frysinger10a11e22005-09-27 02:23:02 +00001737static char *awk_printf(node *n)
1738{
Glenn L McGrath545106f2002-11-11 06:21:00 +00001739 char *b = NULL;
1740 char *fmt, *s, *s1, *f;
1741 int i, j, incr, bsize;
1742 char c, c1;
1743 var *v, *arg;
1744
1745 v = nvalloc(1);
Rob Landleyd921b2e2006-08-03 15:41:12 +00001746 fmt = f = xstrdup(getvar_s(evaluate(nextarg(&n), v)));
Glenn L McGrath545106f2002-11-11 06:21:00 +00001747
1748 i = 0;
1749 while (*f) {
1750 s = f;
1751 while (*f && (*f != '%' || *(++f) == '%'))
1752 f++;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001753 while (*f && !isalpha(*f))
Glenn L McGrath545106f2002-11-11 06:21:00 +00001754 f++;
1755
1756 incr = (f - s) + MAXVARFMT;
Denis Vlasenkof782f522007-01-01 23:51:30 +00001757 qrealloc(&b, incr + i, &bsize);
1758 c = *f;
1759 if (c != '\0') f++;
1760 c1 = *f;
1761 *f = '\0';
Glenn L McGrath545106f2002-11-11 06:21:00 +00001762 arg = evaluate(nextarg(&n), v);
1763
1764 j = i;
1765 if (c == 'c' || !c) {
Denis Vlasenkof782f522007-01-01 23:51:30 +00001766 i += sprintf(b+i, s, is_numeric(arg) ?
1767 (char)getvar_i(arg) : *getvar_s(arg));
Glenn L McGrath545106f2002-11-11 06:21:00 +00001768
1769 } else if (c == 's') {
Denis Vlasenko92758142006-10-03 19:56:34 +00001770 s1 = getvar_s(arg);
Rob Landleya3896512006-05-07 20:20:34 +00001771 qrealloc(&b, incr+i+strlen(s1), &bsize);
Glenn L McGrath545106f2002-11-11 06:21:00 +00001772 i += sprintf(b+i, s, s1);
1773
1774 } else {
1775 i += fmt_num(b+i, incr, s, getvar_i(arg), FALSE);
1776 }
1777 *f = c1;
1778
1779 /* if there was an error while sprintf, return value is negative */
1780 if (i < j) i = j;
Glenn L McGrath545106f2002-11-11 06:21:00 +00001781 }
1782
Denis Vlasenkof782f522007-01-01 23:51:30 +00001783 b = xrealloc(b, i + 1);
Glenn L McGrath545106f2002-11-11 06:21:00 +00001784 free(fmt);
1785 nvfree(v);
1786 b[i] = '\0';
1787 return b;
1788}
1789
1790/* common substitution routine
1791 * replace (nm) substring of (src) that match (n) with (repl), store
1792 * result into (dest), return number of substitutions. If nm=0, replace
1793 * all matches. If src or dst is NULL, use $0. If ex=TRUE, enable
1794 * subexpression matching (\1-\9)
1795 */
Mike Frysinger10a11e22005-09-27 02:23:02 +00001796static int awk_sub(node *rn, char *repl, int nm, var *src, var *dest, int ex)
1797{
Glenn L McGrath545106f2002-11-11 06:21:00 +00001798 char *ds = NULL;
1799 char *sp, *s;
1800 int c, i, j, di, rl, so, eo, nbs, n, dssize;
1801 regmatch_t pmatch[10];
1802 regex_t sreg, *re;
1803
1804 re = as_regex(rn, &sreg);
1805 if (! src) src = V[F0];
1806 if (! dest) dest = V[F0];
1807
1808 i = di = 0;
1809 sp = getvar_s(src);
Rob Landleya3896512006-05-07 20:20:34 +00001810 rl = strlen(repl);
Glenn L McGrath545106f2002-11-11 06:21:00 +00001811 while (regexec(re, sp, 10, pmatch, sp==getvar_s(src) ? 0:REG_NOTBOL) == 0) {
1812 so = pmatch[0].rm_so;
1813 eo = pmatch[0].rm_eo;
1814
1815 qrealloc(&ds, di + eo + rl, &dssize);
1816 memcpy(ds + di, sp, eo);
1817 di += eo;
1818 if (++i >= nm) {
1819 /* replace */
1820 di -= (eo - so);
1821 nbs = 0;
1822 for (s = repl; *s; s++) {
1823 ds[di++] = c = *s;
1824 if (c == '\\') {
1825 nbs++;
1826 continue;
1827 }
1828 if (c == '&' || (ex && c >= '0' && c <= '9')) {
1829 di -= ((nbs + 3) >> 1);
1830 j = 0;
1831 if (c != '&') {
1832 j = c - '0';
1833 nbs++;
1834 }
1835 if (nbs % 2) {
1836 ds[di++] = c;
1837 } else {
1838 n = pmatch[j].rm_eo - pmatch[j].rm_so;
1839 qrealloc(&ds, di + rl + n, &dssize);
1840 memcpy(ds + di, sp + pmatch[j].rm_so, n);
1841 di += n;
1842 }
1843 }
1844 nbs = 0;
1845 }
1846 }
1847
1848 sp += eo;
1849 if (i == nm) break;
1850 if (eo == so) {
1851 if (! (ds[di++] = *sp++)) break;
1852 }
1853 }
1854
1855 qrealloc(&ds, di + strlen(sp), &dssize);
1856 strcpy(ds + di, sp);
1857 setvar_p(dest, ds);
1858 if (re == &sreg) regfree(re);
1859 return i;
1860}
1861
Mike Frysinger10a11e22005-09-27 02:23:02 +00001862static var *exec_builtin(node *op, var *res)
1863{
Glenn L McGrath545106f2002-11-11 06:21:00 +00001864 int (*to_xxx)(int);
1865 var *tv;
1866 node *an[4];
1867 var *av[4];
1868 char *as[4];
1869 regmatch_t pmatch[2];
1870 regex_t sreg, *re;
1871 static tsplitter tspl;
1872 node *spl;
Mike Frysingerf87b3e32005-09-27 04:16:22 +00001873 uint32_t isr, info;
Glenn L McGrath545106f2002-11-11 06:21:00 +00001874 int nargs;
1875 time_t tt;
1876 char *s, *s1;
1877 int i, l, ll, n;
1878
1879 tv = nvalloc(4);
1880 isr = info = op->info;
1881 op = op->l.n;
1882
1883 av[2] = av[3] = NULL;
1884 for (i=0 ; i<4 && op ; i++) {
1885 an[i] = nextarg(&op);
1886 if (isr & 0x09000000) av[i] = evaluate(an[i], &tv[i]);
1887 if (isr & 0x08000000) as[i] = getvar_s(av[i]);
1888 isr >>= 1;
1889 }
1890
1891 nargs = i;
1892 if (nargs < (info >> 30))
1893 runtime_error(EMSG_TOO_FEW_ARGS);
1894
1895 switch (info & OPNMASK) {
1896
Denis Vlasenkof782f522007-01-01 23:51:30 +00001897 case B_a2:
1898#if ENABLE_FEATURE_AWK_MATH
Glenn L McGrath545106f2002-11-11 06:21:00 +00001899 setvar_i(res, atan2(getvar_i(av[i]), getvar_i(av[1])));
1900#else
1901 runtime_error(EMSG_NO_MATH);
1902#endif
1903 break;
1904
Denis Vlasenkof782f522007-01-01 23:51:30 +00001905 case B_sp:
Glenn L McGrath545106f2002-11-11 06:21:00 +00001906 if (nargs > 2) {
1907 spl = (an[2]->info & OPCLSMASK) == OC_REGEXP ?
1908 an[2] : mk_splitter(getvar_s(evaluate(an[2], &tv[2])), &tspl);
1909 } else {
1910 spl = &fsplitter.n;
1911 }
1912
1913 n = awk_split(as[0], spl, &s);
1914 s1 = s;
1915 clear_array(iamarray(av[1]));
1916 for (i=1; i<=n; i++)
1917 setari_u(av[1], i, nextword(&s1));
1918 free(s);
1919 setvar_i(res, n);
1920 break;
1921
Denis Vlasenkof782f522007-01-01 23:51:30 +00001922 case B_ss:
Rob Landleya3896512006-05-07 20:20:34 +00001923 l = strlen(as[0]);
Glenn L McGrath545106f2002-11-11 06:21:00 +00001924 i = getvar_i(av[1]) - 1;
1925 if (i>l) i=l; if (i<0) i=0;
1926 n = (nargs > 2) ? getvar_i(av[2]) : l-i;
1927 if (n<0) n=0;
1928 s = xmalloc(n+1);
1929 strncpy(s, as[0]+i, n);
1930 s[n] = '\0';
1931 setvar_p(res, s);
1932 break;
Denis Vlasenkof7996f32007-01-11 17:20:00 +00001933
Denis Vlasenkof782f522007-01-01 23:51:30 +00001934 case B_an:
Denis Vlasenkoe175ff22006-09-26 17:41:00 +00001935 setvar_i(res, (long)getvar_i(av[0]) & (long)getvar_i(av[1]));
1936 break;
Denis Vlasenkof7996f32007-01-11 17:20:00 +00001937
Denis Vlasenkof782f522007-01-01 23:51:30 +00001938 case B_co:
Denis Vlasenkoe175ff22006-09-26 17:41:00 +00001939 setvar_i(res, ~(long)getvar_i(av[0]));
1940 break;
1941
Denis Vlasenkof782f522007-01-01 23:51:30 +00001942 case B_ls:
Denis Vlasenkoe175ff22006-09-26 17:41:00 +00001943 setvar_i(res, (long)getvar_i(av[0]) << (long)getvar_i(av[1]));
1944 break;
1945
Denis Vlasenkof782f522007-01-01 23:51:30 +00001946 case B_or:
Denis Vlasenkoe175ff22006-09-26 17:41:00 +00001947 setvar_i(res, (long)getvar_i(av[0]) | (long)getvar_i(av[1]));
1948 break;
1949
Denis Vlasenkof782f522007-01-01 23:51:30 +00001950 case B_rs:
Denis Vlasenkoe175ff22006-09-26 17:41:00 +00001951 setvar_i(res, (long)((unsigned long)getvar_i(av[0]) >> (unsigned long)getvar_i(av[1])));
1952 break;
1953
Denis Vlasenkof782f522007-01-01 23:51:30 +00001954 case B_xo:
Denis Vlasenkoe175ff22006-09-26 17:41:00 +00001955 setvar_i(res, (long)getvar_i(av[0]) ^ (long)getvar_i(av[1]));
1956 break;
Glenn L McGrath545106f2002-11-11 06:21:00 +00001957
Denis Vlasenkof782f522007-01-01 23:51:30 +00001958 case B_lo:
Glenn L McGrath545106f2002-11-11 06:21:00 +00001959 to_xxx = tolower;
1960 goto lo_cont;
1961
Denis Vlasenkof782f522007-01-01 23:51:30 +00001962 case B_up:
Glenn L McGrath545106f2002-11-11 06:21:00 +00001963 to_xxx = toupper;
Denis Vlasenkoe1d3e032007-01-01 23:53:52 +00001964 lo_cont:
Rob Landleyd921b2e2006-08-03 15:41:12 +00001965 s1 = s = xstrdup(as[0]);
Glenn L McGrath545106f2002-11-11 06:21:00 +00001966 while (*s1) {
1967 *s1 = (*to_xxx)(*s1);
1968 s1++;
1969 }
1970 setvar_p(res, s);
1971 break;
1972
Denis Vlasenkof782f522007-01-01 23:51:30 +00001973 case B_ix:
Glenn L McGrath545106f2002-11-11 06:21:00 +00001974 n = 0;
Rob Landleya3896512006-05-07 20:20:34 +00001975 ll = strlen(as[1]);
1976 l = strlen(as[0]) - ll;
Glenn L McGrath545106f2002-11-11 06:21:00 +00001977 if (ll > 0 && l >= 0) {
1978 if (! icase) {
1979 s = strstr(as[0], as[1]);
1980 if (s) n = (s - as[0]) + 1;
1981 } else {
1982 /* this piece of code is terribly slow and
1983 * really should be rewritten
1984 */
1985 for (i=0; i<=l; i++) {
1986 if (strncasecmp(as[0]+i, as[1], ll) == 0) {
1987 n = i+1;
1988 break;
1989 }
1990 }
1991 }
1992 }
1993 setvar_i(res, n);
1994 break;
1995
Denis Vlasenkof782f522007-01-01 23:51:30 +00001996 case B_ti:
Glenn L McGrath545106f2002-11-11 06:21:00 +00001997 if (nargs > 1)
1998 tt = getvar_i(av[1]);
1999 else
2000 time(&tt);
2001 s = (nargs > 0) ? as[0] : "%a %b %d %H:%M:%S %Z %Y";
2002 i = strftime(buf, MAXVARFMT, s, localtime(&tt));
2003 buf[i] = '\0';
2004 setvar_s(res, buf);
2005 break;
2006
Denis Vlasenkof782f522007-01-01 23:51:30 +00002007 case B_ma:
Glenn L McGrath545106f2002-11-11 06:21:00 +00002008 re = as_regex(an[1], &sreg);
2009 n = regexec(re, as[0], 1, pmatch, 0);
2010 if (n == 0) {
2011 pmatch[0].rm_so++;
2012 pmatch[0].rm_eo++;
2013 } else {
2014 pmatch[0].rm_so = 0;
2015 pmatch[0].rm_eo = -1;
2016 }
2017 setvar_i(newvar("RSTART"), pmatch[0].rm_so);
2018 setvar_i(newvar("RLENGTH"), pmatch[0].rm_eo - pmatch[0].rm_so);
2019 setvar_i(res, pmatch[0].rm_so);
2020 if (re == &sreg) regfree(re);
2021 break;
2022
Denis Vlasenkof782f522007-01-01 23:51:30 +00002023 case B_ge:
Glenn L McGrath545106f2002-11-11 06:21:00 +00002024 awk_sub(an[0], as[1], getvar_i(av[2]), av[3], res, TRUE);
2025 break;
2026
Denis Vlasenkof782f522007-01-01 23:51:30 +00002027 case B_gs:
Glenn L McGrath545106f2002-11-11 06:21:00 +00002028 setvar_i(res, awk_sub(an[0], as[1], 0, av[2], av[2], FALSE));
2029 break;
2030
Denis Vlasenkof782f522007-01-01 23:51:30 +00002031 case B_su:
Glenn L McGrath545106f2002-11-11 06:21:00 +00002032 setvar_i(res, awk_sub(an[0], as[1], 1, av[2], av[2], FALSE));
2033 break;
2034 }
2035
2036 nvfree(tv);
2037 return res;
2038}
2039
2040/*
2041 * Evaluate node - the heart of the program. Supplied with subtree
2042 * and place where to store result. returns ptr to result.
2043 */
2044#define XC(n) ((n) >> 8)
2045
Mike Frysinger10a11e22005-09-27 02:23:02 +00002046static var *evaluate(node *op, var *res)
2047{
Mike Frysingerde2b9382005-09-27 03:18:00 +00002048 /* This procedure is recursive so we should count every byte */
Glenn L McGrath545106f2002-11-11 06:21:00 +00002049 static var *fnargs = NULL;
Denis Vlasenkof782f522007-01-01 23:51:30 +00002050 static unsigned seed = 1;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002051 static regex_t sreg;
Denis Vlasenkoe1d3e032007-01-01 23:53:52 +00002052
Glenn L McGrath545106f2002-11-11 06:21:00 +00002053 node *op1;
2054 var *v1;
2055 union {
2056 var *v;
2057 char *s;
2058 double d;
2059 int i;
2060 } L, R;
Mike Frysingerf87b3e32005-09-27 04:16:22 +00002061 uint32_t opinfo;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002062 short opn;
2063 union {
2064 char *s;
2065 rstream *rsm;
2066 FILE *F;
2067 var *v;
2068 regex_t *re;
Mike Frysingerf87b3e32005-09-27 04:16:22 +00002069 uint32_t info;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002070 } X;
2071
Denis Vlasenkoe1d3e032007-01-01 23:53:52 +00002072 if (!op)
Glenn L McGrath545106f2002-11-11 06:21:00 +00002073 return setvar_s(res, NULL);
2074
2075 v1 = nvalloc(2);
2076
2077 while (op) {
2078
2079 opinfo = op->info;
2080 opn = (short)(opinfo & OPNMASK);
2081 lineno = op->lineno;
2082
Mike Frysingerde2b9382005-09-27 03:18:00 +00002083 /* execute inevitable things */
Glenn L McGrath545106f2002-11-11 06:21:00 +00002084 op1 = op->l.n;
2085 if (opinfo & OF_RES1) X.v = L.v = evaluate(op1, v1);
2086 if (opinfo & OF_RES2) R.v = evaluate(op->r.n, v1+1);
2087 if (opinfo & OF_STR1) L.s = getvar_s(L.v);
2088 if (opinfo & OF_STR2) R.s = getvar_s(R.v);
2089 if (opinfo & OF_NUM1) L.d = getvar_i(L.v);
2090
2091 switch (XC(opinfo & OPCLSMASK)) {
2092
2093 /* -- iterative node type -- */
2094
2095 /* test pattern */
Denis Vlasenkof782f522007-01-01 23:51:30 +00002096 case XC( OC_TEST ):
Glenn L McGrath545106f2002-11-11 06:21:00 +00002097 if ((op1->info & OPCLSMASK) == OC_COMMA) {
2098 /* it's range pattern */
2099 if ((opinfo & OF_CHECKED) || ptest(op1->l.n)) {
2100 op->info |= OF_CHECKED;
2101 if (ptest(op1->r.n))
2102 op->info &= ~OF_CHECKED;
2103
2104 op = op->a.n;
2105 } else {
2106 op = op->r.n;
2107 }
2108 } else {
2109 op = (ptest(op1)) ? op->a.n : op->r.n;
2110 }
2111 break;
2112
2113 /* just evaluate an expression, also used as unconditional jump */
Denis Vlasenkof782f522007-01-01 23:51:30 +00002114 case XC( OC_EXEC ):
Mike Frysingerde2b9382005-09-27 03:18:00 +00002115 break;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002116
2117 /* branch, used in if-else and various loops */
Denis Vlasenkof782f522007-01-01 23:51:30 +00002118 case XC( OC_BR ):
Mike Frysingerde2b9382005-09-27 03:18:00 +00002119 op = istrue(L.v) ? op->a.n : op->r.n;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002120 break;
2121
2122 /* initialize for-in loop */
Denis Vlasenkof782f522007-01-01 23:51:30 +00002123 case XC( OC_WALKINIT ):
Mike Frysingerde2b9382005-09-27 03:18:00 +00002124 hashwalk_init(L.v, iamarray(R.v));
Glenn L McGrath545106f2002-11-11 06:21:00 +00002125 break;
2126
2127 /* get next array item */
Denis Vlasenkof782f522007-01-01 23:51:30 +00002128 case XC( OC_WALKNEXT ):
Glenn L McGrath545106f2002-11-11 06:21:00 +00002129 op = hashwalk_next(L.v) ? op->a.n : op->r.n;
2130 break;
2131
Denis Vlasenkof782f522007-01-01 23:51:30 +00002132 case XC( OC_PRINT ):
2133 case XC( OC_PRINTF ):
Glenn L McGrath545106f2002-11-11 06:21:00 +00002134 X.F = stdout;
Mike Frysingerde2b9382005-09-27 03:18:00 +00002135 if (op->r.n) {
Glenn L McGrath545106f2002-11-11 06:21:00 +00002136 X.rsm = newfile(R.s);
2137 if (! X.rsm->F) {
2138 if (opn == '|') {
2139 if((X.rsm->F = popen(R.s, "w")) == NULL)
Manuel Novoa III cad53642003-03-19 09:13:01 +00002140 bb_perror_msg_and_die("popen");
Glenn L McGrath545106f2002-11-11 06:21:00 +00002141 X.rsm->is_pipe = 1;
2142 } else {
Rob Landleyd921b2e2006-08-03 15:41:12 +00002143 X.rsm->F = xfopen(R.s, opn=='w' ? "w" : "a");
Glenn L McGrath545106f2002-11-11 06:21:00 +00002144 }
2145 }
2146 X.F = X.rsm->F;
2147 }
2148
2149 if ((opinfo & OPCLSMASK) == OC_PRINT) {
Mike Frysingerde2b9382005-09-27 03:18:00 +00002150 if (! op1) {
Glenn L McGrath545106f2002-11-11 06:21:00 +00002151 fputs(getvar_s(V[F0]), X.F);
2152 } else {
2153 while (op1) {
2154 L.v = evaluate(nextarg(&op1), v1);
2155 if (L.v->type & VF_NUMBER) {
2156 fmt_num(buf, MAXVARFMT, getvar_s(V[OFMT]),
Denis Vlasenkob54b2082006-10-27 09:05:40 +00002157 getvar_i(L.v), TRUE);
Glenn L McGrath545106f2002-11-11 06:21:00 +00002158 fputs(buf, X.F);
2159 } else {
2160 fputs(getvar_s(L.v), X.F);
2161 }
2162
2163 if (op1) fputs(getvar_s(V[OFS]), X.F);
2164 }
2165 }
2166 fputs(getvar_s(V[ORS]), X.F);
2167
2168 } else { /* OC_PRINTF */
2169 L.s = awk_printf(op1);
2170 fputs(L.s, X.F);
2171 free(L.s);
2172 }
2173 fflush(X.F);
2174 break;
2175
Denis Vlasenkof782f522007-01-01 23:51:30 +00002176 case XC( OC_DELETE ):
Mike Frysingerde2b9382005-09-27 03:18:00 +00002177 X.info = op1->info & OPCLSMASK;
2178 if (X.info == OC_VAR) {
Glenn L McGrath545106f2002-11-11 06:21:00 +00002179 R.v = op1->l.v;
2180 } else if (X.info == OC_FNARG) {
2181 R.v = &fnargs[op1->l.i];
2182 } else {
2183 runtime_error(EMSG_NOT_ARRAY);
2184 }
2185
Mike Frysingerde2b9382005-09-27 03:18:00 +00002186 if (op1->r.n) {
Glenn L McGrath545106f2002-11-11 06:21:00 +00002187 clrvar(L.v);
2188 L.s = getvar_s(evaluate(op1->r.n, v1));
2189 hash_remove(iamarray(R.v), L.s);
2190 } else {
2191 clear_array(iamarray(R.v));
2192 }
2193 break;
2194
Denis Vlasenkof782f522007-01-01 23:51:30 +00002195 case XC( OC_NEWSOURCE ):
Mike Frysingerde2b9382005-09-27 03:18:00 +00002196 programname = op->l.s;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002197 break;
2198
Denis Vlasenkof782f522007-01-01 23:51:30 +00002199 case XC( OC_RETURN ):
Glenn L McGrath545106f2002-11-11 06:21:00 +00002200 copyvar(res, L.v);
2201 break;
2202
Denis Vlasenkof782f522007-01-01 23:51:30 +00002203 case XC( OC_NEXTFILE ):
Mike Frysingerde2b9382005-09-27 03:18:00 +00002204 nextfile = TRUE;
Denis Vlasenkof782f522007-01-01 23:51:30 +00002205 case XC( OC_NEXT ):
Mike Frysingerde2b9382005-09-27 03:18:00 +00002206 nextrec = TRUE;
Denis Vlasenkof782f522007-01-01 23:51:30 +00002207 case XC( OC_DONE ):
Glenn L McGrath545106f2002-11-11 06:21:00 +00002208 clrvar(res);
2209 break;
2210
Denis Vlasenkof782f522007-01-01 23:51:30 +00002211 case XC( OC_EXIT ):
Mike Frysingerde2b9382005-09-27 03:18:00 +00002212 awk_exit(L.d);
Glenn L McGrath545106f2002-11-11 06:21:00 +00002213
2214 /* -- recursive node type -- */
2215
Denis Vlasenkof782f522007-01-01 23:51:30 +00002216 case XC( OC_VAR ):
Mike Frysingerde2b9382005-09-27 03:18:00 +00002217 L.v = op->l.v;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002218 if (L.v == V[NF])
2219 split_f0();
2220 goto v_cont;
2221
Denis Vlasenkof782f522007-01-01 23:51:30 +00002222 case XC( OC_FNARG ):
Mike Frysingerde2b9382005-09-27 03:18:00 +00002223 L.v = &fnargs[op->l.i];
Denis Vlasenkoe1d3e032007-01-01 23:53:52 +00002224 v_cont:
2225 res = op->r.n ? findvar(iamarray(L.v), R.s) : L.v;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002226 break;
2227
Denis Vlasenkof782f522007-01-01 23:51:30 +00002228 case XC( OC_IN ):
Glenn L McGrath545106f2002-11-11 06:21:00 +00002229 setvar_i(res, hash_search(iamarray(R.v), L.s) ? 1 : 0);
2230 break;
2231
Denis Vlasenkof782f522007-01-01 23:51:30 +00002232 case XC( OC_REGEXP ):
Mike Frysingerde2b9382005-09-27 03:18:00 +00002233 op1 = op;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002234 L.s = getvar_s(V[F0]);
2235 goto re_cont;
2236
Denis Vlasenkof782f522007-01-01 23:51:30 +00002237 case XC( OC_MATCH ):
Mike Frysingerde2b9382005-09-27 03:18:00 +00002238 op1 = op->r.n;
Denis Vlasenkoe1d3e032007-01-01 23:53:52 +00002239 re_cont:
Glenn L McGrath545106f2002-11-11 06:21:00 +00002240 X.re = as_regex(op1, &sreg);
2241 R.i = regexec(X.re, L.s, 0, NULL, 0);
2242 if (X.re == &sreg) regfree(X.re);
2243 setvar_i(res, (R.i == 0 ? 1 : 0) ^ (opn == '!' ? 1 : 0));
2244 break;
2245
Denis Vlasenkof782f522007-01-01 23:51:30 +00002246 case XC( OC_MOVE ):
Mike Frysingerde2b9382005-09-27 03:18:00 +00002247 /* if source is a temporary string, jusk relink it to dest */
Glenn L McGrath545106f2002-11-11 06:21:00 +00002248 if (R.v == v1+1 && R.v->string) {
2249 res = setvar_p(L.v, R.v->string);
2250 R.v->string = NULL;
2251 } else {
Mike Frysingerde2b9382005-09-27 03:18:00 +00002252 res = copyvar(L.v, R.v);
Glenn L McGrath545106f2002-11-11 06:21:00 +00002253 }
2254 break;
2255
Denis Vlasenkof782f522007-01-01 23:51:30 +00002256 case XC( OC_TERNARY ):
Mike Frysingerde2b9382005-09-27 03:18:00 +00002257 if ((op->r.n->info & OPCLSMASK) != OC_COLON)
Glenn L McGrath545106f2002-11-11 06:21:00 +00002258 runtime_error(EMSG_POSSIBLE_ERROR);
2259 res = evaluate(istrue(L.v) ? op->r.n->l.n : op->r.n->r.n, res);
2260 break;
2261
Denis Vlasenkof782f522007-01-01 23:51:30 +00002262 case XC( OC_FUNC ):
Mike Frysingerde2b9382005-09-27 03:18:00 +00002263 if (! op->r.f->body.first)
Glenn L McGrath545106f2002-11-11 06:21:00 +00002264 runtime_error(EMSG_UNDEF_FUNC);
2265
2266 X.v = R.v = nvalloc(op->r.f->nargs+1);
2267 while (op1) {
2268 L.v = evaluate(nextarg(&op1), v1);
2269 copyvar(R.v, L.v);
2270 R.v->type |= VF_CHILD;
2271 R.v->x.parent = L.v;
2272 if (++R.v - X.v >= op->r.f->nargs)
2273 break;
2274 }
2275
2276 R.v = fnargs;
2277 fnargs = X.v;
2278
2279 L.s = programname;
2280 res = evaluate(op->r.f->body.first, res);
2281 programname = L.s;
2282
2283 nvfree(fnargs);
2284 fnargs = R.v;
2285 break;
2286
Denis Vlasenkof782f522007-01-01 23:51:30 +00002287 case XC( OC_GETLINE ):
2288 case XC( OC_PGETLINE ):
Mike Frysingerde2b9382005-09-27 03:18:00 +00002289 if (op1) {
Glenn L McGrath545106f2002-11-11 06:21:00 +00002290 X.rsm = newfile(L.s);
2291 if (! X.rsm->F) {
2292 if ((opinfo & OPCLSMASK) == OC_PGETLINE) {
2293 X.rsm->F = popen(L.s, "r");
2294 X.rsm->is_pipe = TRUE;
2295 } else {
Rob Landleyd921b2e2006-08-03 15:41:12 +00002296 X.rsm->F = fopen(L.s, "r"); /* not xfopen! */
Glenn L McGrath545106f2002-11-11 06:21:00 +00002297 }
2298 }
2299 } else {
2300 if (! iF) iF = next_input_file();
2301 X.rsm = iF;
2302 }
2303
2304 if (! X.rsm->F) {
2305 setvar_i(V[ERRNO], errno);
2306 setvar_i(res, -1);
2307 break;
2308 }
2309
2310 if (! op->r.n)
2311 R.v = V[F0];
2312
2313 L.i = awk_getline(X.rsm, R.v);
2314 if (L.i > 0) {
2315 if (! op1) {
2316 incvar(V[FNR]);
2317 incvar(V[NR]);
2318 }
2319 }
2320 setvar_i(res, L.i);
2321 break;
2322
Mike Frysingerde2b9382005-09-27 03:18:00 +00002323 /* simple builtins */
Denis Vlasenkof782f522007-01-01 23:51:30 +00002324 case XC( OC_FBLTIN ):
Mike Frysingerde2b9382005-09-27 03:18:00 +00002325 switch (opn) {
Glenn L McGrath545106f2002-11-11 06:21:00 +00002326
Denis Vlasenkof782f522007-01-01 23:51:30 +00002327 case F_in:
Mike Frysingerde2b9382005-09-27 03:18:00 +00002328 R.d = (int)L.d;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002329 break;
2330
Denis Vlasenkof782f522007-01-01 23:51:30 +00002331 case F_rn:
2332 R.d = (double)rand() / (double)RAND_MAX;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002333 break;
2334
Denis Vlasenkof782f522007-01-01 23:51:30 +00002335#if ENABLE_FEATURE_AWK_MATH
2336 case F_co:
Mike Frysingerde2b9382005-09-27 03:18:00 +00002337 R.d = cos(L.d);
Glenn L McGrath545106f2002-11-11 06:21:00 +00002338 break;
2339
Denis Vlasenkof782f522007-01-01 23:51:30 +00002340 case F_ex:
Mike Frysingerde2b9382005-09-27 03:18:00 +00002341 R.d = exp(L.d);
Glenn L McGrath545106f2002-11-11 06:21:00 +00002342 break;
2343
Denis Vlasenkof782f522007-01-01 23:51:30 +00002344 case F_lg:
Mike Frysingerde2b9382005-09-27 03:18:00 +00002345 R.d = log(L.d);
Glenn L McGrath545106f2002-11-11 06:21:00 +00002346 break;
2347
Denis Vlasenkof782f522007-01-01 23:51:30 +00002348 case F_si:
Mike Frysingerde2b9382005-09-27 03:18:00 +00002349 R.d = sin(L.d);
Glenn L McGrath545106f2002-11-11 06:21:00 +00002350 break;
2351
Denis Vlasenkof782f522007-01-01 23:51:30 +00002352 case F_sq:
Mike Frysingerde2b9382005-09-27 03:18:00 +00002353 R.d = sqrt(L.d);
Glenn L McGrath545106f2002-11-11 06:21:00 +00002354 break;
2355#else
Denis Vlasenkof782f522007-01-01 23:51:30 +00002356 case F_co:
2357 case F_ex:
2358 case F_lg:
2359 case F_si:
2360 case F_sq:
Glenn L McGrath545106f2002-11-11 06:21:00 +00002361 runtime_error(EMSG_NO_MATH);
2362 break;
2363#endif
2364
Denis Vlasenkof782f522007-01-01 23:51:30 +00002365 case F_sr:
Glenn L McGrath545106f2002-11-11 06:21:00 +00002366 R.d = (double)seed;
Denis Vlasenkof782f522007-01-01 23:51:30 +00002367 seed = op1 ? (unsigned)L.d : (unsigned)time(NULL);
Glenn L McGrath545106f2002-11-11 06:21:00 +00002368 srand(seed);
2369 break;
2370
Denis Vlasenkof782f522007-01-01 23:51:30 +00002371 case F_ti:
Glenn L McGrath545106f2002-11-11 06:21:00 +00002372 R.d = time(NULL);
2373 break;
2374
Denis Vlasenkof782f522007-01-01 23:51:30 +00002375 case F_le:
Mike Frysingerde2b9382005-09-27 03:18:00 +00002376 if (! op1)
Glenn L McGrath545106f2002-11-11 06:21:00 +00002377 L.s = getvar_s(V[F0]);
Rob Landleya3896512006-05-07 20:20:34 +00002378 R.d = strlen(L.s);
Glenn L McGrath545106f2002-11-11 06:21:00 +00002379 break;
2380
Denis Vlasenkof782f522007-01-01 23:51:30 +00002381 case F_sy:
Glenn L McGrath545106f2002-11-11 06:21:00 +00002382 fflush(NULL);
Denis Vlasenko249fabf2006-12-19 00:29:22 +00002383 R.d = (ENABLE_FEATURE_ALLOW_EXEC && L.s && *L.s)
2384 ? (system(L.s) >> 8) : 0;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002385 break;
2386
Denis Vlasenkof782f522007-01-01 23:51:30 +00002387 case F_ff:
Glenn L McGrath545106f2002-11-11 06:21:00 +00002388 if (! op1)
2389 fflush(stdout);
2390 else {
2391 if (L.s && *L.s) {
2392 X.rsm = newfile(L.s);
2393 fflush(X.rsm->F);
2394 } else {
2395 fflush(NULL);
2396 }
2397 }
2398 break;
2399
Denis Vlasenkof782f522007-01-01 23:51:30 +00002400 case F_cl:
Glenn L McGrath545106f2002-11-11 06:21:00 +00002401 X.rsm = (rstream *)hash_search(fdhash, L.s);
2402 if (X.rsm) {
2403 R.i = X.rsm->is_pipe ? pclose(X.rsm->F) : fclose(X.rsm->F);
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002404 free(X.rsm->buffer);
Glenn L McGrath545106f2002-11-11 06:21:00 +00002405 hash_remove(fdhash, L.s);
2406 }
2407 if (R.i != 0)
2408 setvar_i(V[ERRNO], errno);
2409 R.d = (double)R.i;
2410 break;
2411 }
2412 setvar_i(res, R.d);
2413 break;
2414
Denis Vlasenkof782f522007-01-01 23:51:30 +00002415 case XC( OC_BUILTIN ):
Glenn L McGrath545106f2002-11-11 06:21:00 +00002416 res = exec_builtin(op, res);
2417 break;
2418
Denis Vlasenkof782f522007-01-01 23:51:30 +00002419 case XC( OC_SPRINTF ):
Mike Frysingerde2b9382005-09-27 03:18:00 +00002420 setvar_p(res, awk_printf(op1));
Glenn L McGrath545106f2002-11-11 06:21:00 +00002421 break;
2422
Denis Vlasenkof782f522007-01-01 23:51:30 +00002423 case XC( OC_UNARY ):
Mike Frysingerde2b9382005-09-27 03:18:00 +00002424 X.v = R.v;
2425 L.d = R.d = getvar_i(R.v);
2426 switch (opn) {
Denis Vlasenkof782f522007-01-01 23:51:30 +00002427 case 'P':
Mike Frysingerde2b9382005-09-27 03:18:00 +00002428 L.d = ++R.d;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002429 goto r_op_change;
Denis Vlasenkof782f522007-01-01 23:51:30 +00002430 case 'p':
Mike Frysingerde2b9382005-09-27 03:18:00 +00002431 R.d++;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002432 goto r_op_change;
Denis Vlasenkof782f522007-01-01 23:51:30 +00002433 case 'M':
Mike Frysingerde2b9382005-09-27 03:18:00 +00002434 L.d = --R.d;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002435 goto r_op_change;
Denis Vlasenkof782f522007-01-01 23:51:30 +00002436 case 'm':
Mike Frysingerde2b9382005-09-27 03:18:00 +00002437 R.d--;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002438 goto r_op_change;
Denis Vlasenkof782f522007-01-01 23:51:30 +00002439 case '!':
Denis Vlasenko92758142006-10-03 19:56:34 +00002440 L.d = istrue(X.v) ? 0 : 1;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002441 break;
Denis Vlasenkof782f522007-01-01 23:51:30 +00002442 case '-':
Mike Frysingerde2b9382005-09-27 03:18:00 +00002443 L.d = -R.d;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002444 break;
Denis Vlasenkof782f522007-01-01 23:51:30 +00002445 r_op_change:
Glenn L McGrath545106f2002-11-11 06:21:00 +00002446 setvar_i(X.v, R.d);
2447 }
2448 setvar_i(res, L.d);
2449 break;
2450
Denis Vlasenkof782f522007-01-01 23:51:30 +00002451 case XC( OC_FIELD ):
Mike Frysingerde2b9382005-09-27 03:18:00 +00002452 R.i = (int)getvar_i(R.v);
2453 if (R.i == 0) {
Glenn L McGrath545106f2002-11-11 06:21:00 +00002454 res = V[F0];
2455 } else {
2456 split_f0();
2457 if (R.i > nfields)
2458 fsrealloc(R.i);
2459
2460 res = &Fields[R.i-1];
2461 }
2462 break;
2463
2464 /* concatenation (" ") and index joining (",") */
Denis Vlasenkof782f522007-01-01 23:51:30 +00002465 case XC( OC_CONCAT ):
2466 case XC( OC_COMMA ):
Rob Landleya3896512006-05-07 20:20:34 +00002467 opn = strlen(L.s) + strlen(R.s) + 2;
Denis Vlasenkob95636c2006-12-19 23:36:04 +00002468 X.s = xmalloc(opn);
Glenn L McGrath545106f2002-11-11 06:21:00 +00002469 strcpy(X.s, L.s);
2470 if ((opinfo & OPCLSMASK) == OC_COMMA) {
2471 L.s = getvar_s(V[SUBSEP]);
Denis Vlasenkof782f522007-01-01 23:51:30 +00002472 X.s = xrealloc(X.s, opn + strlen(L.s));
Glenn L McGrath545106f2002-11-11 06:21:00 +00002473 strcat(X.s, L.s);
2474 }
2475 strcat(X.s, R.s);
2476 setvar_p(res, X.s);
2477 break;
2478
Denis Vlasenkof782f522007-01-01 23:51:30 +00002479 case XC( OC_LAND ):
Glenn L McGrath545106f2002-11-11 06:21:00 +00002480 setvar_i(res, istrue(L.v) ? ptest(op->r.n) : 0);
2481 break;
2482
Denis Vlasenkof782f522007-01-01 23:51:30 +00002483 case XC( OC_LOR ):
Glenn L McGrath545106f2002-11-11 06:21:00 +00002484 setvar_i(res, istrue(L.v) ? 1 : ptest(op->r.n));
2485 break;
2486
Denis Vlasenkof782f522007-01-01 23:51:30 +00002487 case XC( OC_BINARY ):
2488 case XC( OC_REPLACE ):
Mike Frysingerde2b9382005-09-27 03:18:00 +00002489 R.d = getvar_i(R.v);
Glenn L McGrath545106f2002-11-11 06:21:00 +00002490 switch (opn) {
Denis Vlasenkof782f522007-01-01 23:51:30 +00002491 case '+':
Mike Frysingerde2b9382005-09-27 03:18:00 +00002492 L.d += R.d;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002493 break;
Denis Vlasenkof782f522007-01-01 23:51:30 +00002494 case '-':
Mike Frysingerde2b9382005-09-27 03:18:00 +00002495 L.d -= R.d;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002496 break;
Denis Vlasenkof782f522007-01-01 23:51:30 +00002497 case '*':
Mike Frysingerde2b9382005-09-27 03:18:00 +00002498 L.d *= R.d;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002499 break;
Denis Vlasenkof782f522007-01-01 23:51:30 +00002500 case '/':
Mike Frysingerde2b9382005-09-27 03:18:00 +00002501 if (R.d == 0) runtime_error(EMSG_DIV_BY_ZERO);
2502 L.d /= R.d;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002503 break;
Denis Vlasenkof782f522007-01-01 23:51:30 +00002504 case '&':
2505#if ENABLE_FEATURE_AWK_MATH
Mike Frysingerde2b9382005-09-27 03:18:00 +00002506 L.d = pow(L.d, R.d);
Glenn L McGrath545106f2002-11-11 06:21:00 +00002507#else
2508 runtime_error(EMSG_NO_MATH);
2509#endif
2510 break;
Denis Vlasenkof782f522007-01-01 23:51:30 +00002511 case '%':
Mike Frysingerde2b9382005-09-27 03:18:00 +00002512 if (R.d == 0) runtime_error(EMSG_DIV_BY_ZERO);
2513 L.d -= (int)(L.d / R.d) * R.d;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002514 break;
2515 }
2516 res = setvar_i(((opinfo&OPCLSMASK) == OC_BINARY) ? res : X.v, L.d);
2517 break;
2518
Denis Vlasenkof782f522007-01-01 23:51:30 +00002519 case XC( OC_COMPARE ):
Glenn L McGrath545106f2002-11-11 06:21:00 +00002520 if (is_numeric(L.v) && is_numeric(R.v)) {
2521 L.d = getvar_i(L.v) - getvar_i(R.v);
2522 } else {
2523 L.s = getvar_s(L.v);
2524 R.s = getvar_s(R.v);
2525 L.d = icase ? strcasecmp(L.s, R.s) : strcmp(L.s, R.s);
2526 }
2527 switch (opn & 0xfe) {
Denis Vlasenkof782f522007-01-01 23:51:30 +00002528 case 0:
Mike Frysingerde2b9382005-09-27 03:18:00 +00002529 R.i = (L.d > 0);
Glenn L McGrath545106f2002-11-11 06:21:00 +00002530 break;
Denis Vlasenkof782f522007-01-01 23:51:30 +00002531 case 2:
Mike Frysingerde2b9382005-09-27 03:18:00 +00002532 R.i = (L.d >= 0);
Glenn L McGrath545106f2002-11-11 06:21:00 +00002533 break;
Denis Vlasenkof782f522007-01-01 23:51:30 +00002534 case 4:
Mike Frysingerde2b9382005-09-27 03:18:00 +00002535 R.i = (L.d == 0);
Glenn L McGrath545106f2002-11-11 06:21:00 +00002536 break;
2537 }
2538 setvar_i(res, (opn & 0x1 ? R.i : !R.i) ? 1 : 0);
2539 break;
2540
Denis Vlasenkof782f522007-01-01 23:51:30 +00002541 default:
Mike Frysingerde2b9382005-09-27 03:18:00 +00002542 runtime_error(EMSG_POSSIBLE_ERROR);
Glenn L McGrath545106f2002-11-11 06:21:00 +00002543 }
2544 if ((opinfo & OPCLSMASK) <= SHIFT_TIL_THIS)
2545 op = op->a.n;
2546 if ((opinfo & OPCLSMASK) >= RECUR_FROM_THIS)
2547 break;
2548 if (nextrec)
2549 break;
2550 }
2551 nvfree(v1);
2552 return res;
2553}
2554
2555
2556/* -------- main & co. -------- */
2557
Mike Frysinger10a11e22005-09-27 02:23:02 +00002558static int awk_exit(int r)
2559{
Denis Vlasenkof782f522007-01-01 23:51:30 +00002560 var tv;
2561 unsigned i;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002562 hash_item *hi;
Glenn L McGrath00ed36f2003-10-30 13:36:39 +00002563
Denis Vlasenkof782f522007-01-01 23:51:30 +00002564 zero_out_var(&tv);
2565
2566 if (!exiting) {
Glenn L McGrath00ed36f2003-10-30 13:36:39 +00002567 exiting = TRUE;
Glenn L McGrathca29ffc2004-09-24 09:24:27 +00002568 nextrec = FALSE;
Glenn L McGrath00ed36f2003-10-30 13:36:39 +00002569 evaluate(endseq.first, &tv);
2570 }
Glenn L McGrath545106f2002-11-11 06:21:00 +00002571
2572 /* waiting for children */
Denis Vlasenkof782f522007-01-01 23:51:30 +00002573 for (i = 0; i < fdhash->csize; i++) {
Glenn L McGrath545106f2002-11-11 06:21:00 +00002574 hi = fdhash->items[i];
Denis Vlasenkobf0a2012006-12-26 10:42:51 +00002575 while (hi) {
Glenn L McGrath545106f2002-11-11 06:21:00 +00002576 if (hi->data.rs.F && hi->data.rs.is_pipe)
2577 pclose(hi->data.rs.F);
2578 hi = hi->next;
2579 }
2580 }
2581
2582 exit(r);
2583}
2584
2585/* if expr looks like "var=value", perform assignment and return 1,
2586 * otherwise return 0 */
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +00002587static int is_assignment(const char *expr)
Mike Frysinger10a11e22005-09-27 02:23:02 +00002588{
Glenn L McGrath545106f2002-11-11 06:21:00 +00002589 char *exprc, *s, *s0, *s1;
2590
Rob Landleyd921b2e2006-08-03 15:41:12 +00002591 exprc = xstrdup(expr);
Glenn L McGrath545106f2002-11-11 06:21:00 +00002592 if (!isalnum_(*exprc) || (s = strchr(exprc, '=')) == NULL) {
2593 free(exprc);
2594 return FALSE;
2595 }
2596
2597 *(s++) = '\0';
2598 s0 = s1 = s;
2599 while (*s)
2600 *(s1++) = nextchar(&s);
2601
2602 *s1 = '\0';
2603 setvar_u(newvar(exprc), s0);
2604 free(exprc);
2605 return TRUE;
2606}
2607
2608/* switch to next input file */
Mike Frysinger10a11e22005-09-27 02:23:02 +00002609static rstream *next_input_file(void)
2610{
Glenn L McGrath545106f2002-11-11 06:21:00 +00002611 static rstream rsm;
2612 FILE *F = NULL;
2613 char *fname, *ind;
2614 static int files_happen = FALSE;
2615
2616 if (rsm.F) fclose(rsm.F);
2617 rsm.F = NULL;
Glenn L McGrath00ed36f2003-10-30 13:36:39 +00002618 rsm.pos = rsm.adv = 0;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002619
2620 do {
2621 if (getvar_i(V[ARGIND])+1 >= getvar_i(V[ARGC])) {
2622 if (files_happen)
2623 return NULL;
2624 fname = "-";
2625 F = stdin;
2626 } else {
2627 ind = getvar_s(incvar(V[ARGIND]));
2628 fname = getvar_s(findvar(iamarray(V[ARGV]), ind));
2629 if (fname && *fname && !is_assignment(fname))
2630 F = afopen(fname, "r");
2631 }
2632 } while (!F);
2633
2634 files_happen = TRUE;
2635 setvar_s(V[FILENAME], fname);
2636 rsm.F = F;
2637 return &rsm;
2638}
2639
Rob Landleydfba7412006-03-06 20:47:33 +00002640int awk_main(int argc, char **argv)
Mike Frysinger10a11e22005-09-27 02:23:02 +00002641{
Denis Vlasenko67b23e62006-10-03 21:00:06 +00002642 unsigned opt;
Denis Vlasenko099efbf2006-09-22 09:02:30 +00002643 char *opt_F, *opt_v, *opt_W;
Denis Vlasenkof782f522007-01-01 23:51:30 +00002644 int i, j, flen;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002645 var *v;
Denis Vlasenkof782f522007-01-01 23:51:30 +00002646 var tv;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002647 char **envp;
Denis Vlasenkof782f522007-01-01 23:51:30 +00002648 char *vnames = (char *)vNames; /* cheat */
2649 char *vvalues = (char *)vValues;
2650
Denis Vlasenko150f4022007-01-13 21:06:21 +00002651 /* Undo busybox.c, or else strtod may eat ','! This breaks parsing:
Denis Vlasenko6dc6ebb2007-01-01 23:53:12 +00002652 * $1,$2 == '$1,' '$2', NOT '$1' ',' '$2' */
2653 if (ENABLE_LOCALE_SUPPORT)
2654 setlocale(LC_NUMERIC, "C");
2655
Denis Vlasenkof782f522007-01-01 23:51:30 +00002656 zero_out_var(&tv);
Glenn L McGrath545106f2002-11-11 06:21:00 +00002657
2658 /* allocate global buffer */
Denis Vlasenkof782f522007-01-01 23:51:30 +00002659 buf = xmalloc(MAXVARFMT + 1);
Glenn L McGrath545106f2002-11-11 06:21:00 +00002660
2661 vhash = hash_init();
2662 ahash = hash_init();
2663 fdhash = hash_init();
2664 fnhash = hash_init();
2665
2666 /* initialize variables */
Denis Vlasenkof782f522007-01-01 23:51:30 +00002667 for (i = 0; *vnames; i++) {
2668 V[i] = v = newvar(nextword(&vnames));
2669 if (*vvalues != '\377')
2670 setvar_s(v, nextword(&vvalues));
Glenn L McGrath545106f2002-11-11 06:21:00 +00002671 else
2672 setvar_i(v, 0);
2673
Denis Vlasenkof782f522007-01-01 23:51:30 +00002674 if (*vnames == '*') {
Glenn L McGrath545106f2002-11-11 06:21:00 +00002675 v->type |= VF_SPECIAL;
Denis Vlasenkof782f522007-01-01 23:51:30 +00002676 vnames++;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002677 }
2678 }
2679
2680 handle_special(V[FS]);
2681 handle_special(V[RS]);
2682
Denis Vlasenkof782f522007-01-01 23:51:30 +00002683 newfile("/dev/stdin")->F = stdin;
2684 newfile("/dev/stdout")->F = stdout;
2685 newfile("/dev/stderr")->F = stderr;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002686
Denis Vlasenkof782f522007-01-01 23:51:30 +00002687 for (envp = environ; *envp; envp++) {
2688 char *s = xstrdup(*envp);
2689 char *s1 = strchr(s, '=');
2690 if (s1) {
2691 *s1++ = '\0';
2692 setvar_u(findvar(iamarray(V[ENVIRON]), s), s1);
Eric Andersen67776be2004-07-30 23:52:08 +00002693 }
Glenn L McGrath545106f2002-11-11 06:21:00 +00002694 free(s);
2695 }
2696
Denis Vlasenko67b23e62006-10-03 21:00:06 +00002697 opt = getopt32(argc, argv, "F:v:f:W:", &opt_F, &opt_v, &programname, &opt_W);
Denis Vlasenkof782f522007-01-01 23:51:30 +00002698 argv += optind;
2699 argc -= optind;
Denis Vlasenko099efbf2006-09-22 09:02:30 +00002700 if (opt & 0x1) setvar_s(V[FS], opt_F); // -F
2701 if (opt & 0x2) if (!is_assignment(opt_v)) bb_show_usage(); // -v
2702 if (opt & 0x4) { // -f
Denis Vlasenkof782f522007-01-01 23:51:30 +00002703 char *s = s; /* die, gcc, die */
2704 FILE *from_file = afopen(programname, "r");
Denis Vlasenko099efbf2006-09-22 09:02:30 +00002705 /* one byte is reserved for some trick in next_token */
Denis Vlasenkof782f522007-01-01 23:51:30 +00002706 if (fseek(from_file, 0, SEEK_END) == 0) {
2707 flen = ftell(from_file);
2708 s = xmalloc(flen + 4);
2709 fseek(from_file, 0, SEEK_SET);
2710 i = 1 + fread(s + 1, 1, flen, from_file);
Denis Vlasenko099efbf2006-09-22 09:02:30 +00002711 } else {
Denis Vlasenkof782f522007-01-01 23:51:30 +00002712 for (i = j = 1; j > 0; i += j) {
2713 s = xrealloc(s, i + 4096);
2714 j = fread(s + i, 1, 4094, from_file);
Denis Vlasenko099efbf2006-09-22 09:02:30 +00002715 }
Glenn L McGrath545106f2002-11-11 06:21:00 +00002716 }
Denis Vlasenko099efbf2006-09-22 09:02:30 +00002717 s[i] = '\0';
Denis Vlasenkof782f522007-01-01 23:51:30 +00002718 fclose(from_file);
2719 parse_program(s + 1);
Denis Vlasenko099efbf2006-09-22 09:02:30 +00002720 free(s);
Denis Vlasenkof782f522007-01-01 23:51:30 +00002721 } else { // no -f: take program from 1st parameter
2722 if (!argc)
2723 bb_show_usage();
2724 programname = "cmd. line";
2725 parse_program(*argv++);
2726 argc--;
Glenn L McGrath545106f2002-11-11 06:21:00 +00002727 }
Denis Vlasenko099efbf2006-09-22 09:02:30 +00002728 if (opt & 0x8) // -W
Denis Vlasenkoe1a0d482006-10-20 13:28:22 +00002729 bb_error_msg("warning: unrecognized option '-W %s' ignored", opt_W);
Glenn L McGrath545106f2002-11-11 06:21:00 +00002730
Glenn L McGrath545106f2002-11-11 06:21:00 +00002731 /* fill in ARGV array */
Denis Vlasenkof782f522007-01-01 23:51:30 +00002732 setvar_i(V[ARGC], argc + 1);
Glenn L McGrath545106f2002-11-11 06:21:00 +00002733 setari_u(V[ARGV], 0, "awk");
Denis Vlasenkof782f522007-01-01 23:51:30 +00002734 i = 0;
2735 while (*argv)
2736 setari_u(V[ARGV], ++i, *argv++);
Glenn L McGrath545106f2002-11-11 06:21:00 +00002737
2738 evaluate(beginseq.first, &tv);
Denis Vlasenkof782f522007-01-01 23:51:30 +00002739 if (!mainseq.first && !endseq.first)
Glenn L McGrath545106f2002-11-11 06:21:00 +00002740 awk_exit(EXIT_SUCCESS);
2741
2742 /* input file could already be opened in BEGIN block */
Denis Vlasenkof782f522007-01-01 23:51:30 +00002743 if (!iF) iF = next_input_file();
Glenn L McGrath545106f2002-11-11 06:21:00 +00002744
2745 /* passing through input files */
2746 while (iF) {
Glenn L McGrath545106f2002-11-11 06:21:00 +00002747 nextfile = FALSE;
2748 setvar_i(V[FNR], 0);
2749
Denis Vlasenkof782f522007-01-01 23:51:30 +00002750 while ((i = awk_getline(iF, V[F0])) > 0) {
Glenn L McGrath545106f2002-11-11 06:21:00 +00002751 nextrec = FALSE;
2752 incvar(V[NR]);
2753 incvar(V[FNR]);
2754 evaluate(mainseq.first, &tv);
2755
2756 if (nextfile)
2757 break;
2758 }
2759
Denis Vlasenkof782f522007-01-01 23:51:30 +00002760 if (i < 0)
Glenn L McGrath545106f2002-11-11 06:21:00 +00002761 runtime_error(strerror(errno));
2762
2763 iF = next_input_file();
Glenn L McGrath545106f2002-11-11 06:21:00 +00002764 }
2765
Glenn L McGrath545106f2002-11-11 06:21:00 +00002766 awk_exit(EXIT_SUCCESS);
Denis Vlasenkof782f522007-01-01 23:51:30 +00002767 /*return 0;*/
Glenn L McGrath545106f2002-11-11 06:21:00 +00002768}