*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-
//config:config AWK
-//config: bool "awk"
+//config: bool "awk (22 kb)"
//config: default y
//config: help
-//config: Awk is used as a pattern scanning and processing language. This is
-//config: the BusyBox implementation of that programming language.
+//config: Awk is used as a pattern scanning and processing language.
//config:
//config:config FEATURE_AWK_LIBM
//config: bool "Enable math functions (requires libm)"
//config: default y
//config: depends on AWK
//config: help
-//config: Enable math functions of the Awk programming language.
-//config: NOTE: This will require libm to be present for linking.
+//config: Enable math functions of the Awk programming language.
+//config: NOTE: This requires libm to be present for linking.
//config:
//config:config FEATURE_AWK_GNU_EXTENSIONS
//config: bool "Enable a few GNU extensions"
//config: default y
//config: depends on AWK
//config: help
-//config: Enable a few features from gawk:
-//config: * command line option -e AWK_PROGRAM
-//config: * simultaneous use of -f and -e on the command line.
-//config: This enables the use of awk library files.
-//config: Ex: awk -f mylib.awk -e '{print myfunction($1);}' ...
+//config: Enable a few features from gawk:
+//config: * command line option -e AWK_PROGRAM
+//config: * simultaneous use of -f and -e on the command line.
+//config: This enables the use of awk library files.
+//config: Example: awk -f mylib.awk -e '{print myfunction($1);}' ...
//applet:IF_AWK(APPLET_NOEXEC(awk, awk, BB_DIR_USR_BIN, BB_SUID_DROP, awk))
#endif
-#define OPTSTR_AWK \
- "F:v:f:" \
- IF_FEATURE_AWK_GNU_EXTENSIONS("e:") \
+/* "+": stop on first non-option:
+ * $ awk 'BEGIN { for(i=1; i<ARGC; ++i) { print i ": " ARGV[i] }}' -argz
+ * 1: -argz
+ */
+#define OPTSTR_AWK "+" \
+ "F:v:*f:*" \
+ IF_FEATURE_AWK_GNU_EXTENSIONS("e:*") \
"W:"
-#define OPTCOMPLSTR_AWK \
- "v::f::" \
- IF_FEATURE_AWK_GNU_EXTENSIONS("e::")
enum {
OPTBIT_F, /* define field separator */
OPTBIT_v, /* define variable */
/* simple token classes */
/* Order and hex values are very important!!! See next_token() */
-#define TC_SEQSTART 1 /* ( */
+#define TC_SEQSTART (1 << 0) /* ( */
#define TC_SEQTERM (1 << 1) /* ) */
#define TC_REGEXP (1 << 2) /* /.../ */
#define TC_OUTRDR (1 << 3) /* | > >> */
#define TC_WHILE (1 << 17)
#define TC_ELSE (1 << 18)
#define TC_BUILTIN (1 << 19)
-#define TC_GETLINE (1 << 20)
-#define TC_FUNCDECL (1 << 21) /* `function' `func' */
-#define TC_BEGIN (1 << 22)
-#define TC_END (1 << 23)
-#define TC_EOF (1 << 24)
-#define TC_VARIABLE (1 << 25)
-#define TC_ARRAY (1 << 26)
-#define TC_FUNCTION (1 << 27)
-#define TC_STRING (1 << 28)
-#define TC_NUMBER (1 << 29)
+/* This costs ~50 bytes of code.
+ * A separate class to support deprecated "length" form. If we don't need that
+ * (i.e. if we demand that only "length()" with () is valid), then TC_LENGTH
+ * can be merged with TC_BUILTIN:
+ */
+#define TC_LENGTH (1 << 20)
+#define TC_GETLINE (1 << 21)
+#define TC_FUNCDECL (1 << 22) /* 'function' 'func' */
+#define TC_BEGIN (1 << 23)
+#define TC_END (1 << 24)
+#define TC_EOF (1 << 25)
+#define TC_VARIABLE (1 << 26)
+#define TC_ARRAY (1 << 27)
+#define TC_FUNCTION (1 << 28)
+#define TC_STRING (1 << 29)
+#define TC_NUMBER (1 << 30)
#define TC_UOPPRE (TC_UOPPRE1 | TC_UOPPRE2)
#define TC_BINOP (TC_BINOPX | TC_COMMA | TC_PIPE | TC_IN)
//#define TC_UNARYOP (TC_UOPPRE | TC_UOPPOST)
#define TC_OPERAND (TC_VARIABLE | TC_ARRAY | TC_FUNCTION \
- | TC_BUILTIN | TC_GETLINE | TC_SEQSTART | TC_STRING | TC_NUMBER)
+ | TC_BUILTIN | TC_LENGTH | TC_GETLINE \
+ | TC_SEQSTART | TC_STRING | TC_NUMBER)
#define TC_STATEMNT (TC_STATX | TC_WHILE)
#define TC_OPTERM (TC_SEMICOL | TC_NEWLINE)
/* word tokens, cannot mean something else if not expected */
-#define TC_WORD (TC_IN | TC_STATEMNT | TC_ELSE | TC_BUILTIN \
- | TC_GETLINE | TC_FUNCDECL | TC_BEGIN | TC_END)
+#define TC_WORD (TC_IN | TC_STATEMNT | TC_ELSE \
+ | TC_BUILTIN | TC_LENGTH | TC_GETLINE \
+ | TC_FUNCDECL | TC_BEGIN | TC_END)
/* discard newlines after these */
#define TC_NOTERM (TC_COMMA | TC_GRPSTART | TC_GRPTERM \
#define NTC "\377" /* switch to next token class (tc<<1) */
#define NTCC '\377'
-#define OC_B OC_BUILTIN
-
static const char tokenlist[] ALIGN1 =
- "\1(" NTC
- "\1)" NTC
- "\1/" NTC /* REGEXP */
- "\2>>" "\1>" "\1|" NTC /* OUTRDR */
- "\2++" "\2--" NTC /* UOPPOST */
- "\2++" "\2--" "\1$" NTC /* UOPPRE1 */
- "\2==" "\1=" "\2+=" "\2-=" /* BINOPX */
+ "\1(" NTC /* TC_SEQSTART */
+ "\1)" NTC /* TC_SEQTERM */
+ "\1/" NTC /* TC_REGEXP */
+ "\2>>" "\1>" "\1|" NTC /* TC_OUTRDR */
+ "\2++" "\2--" NTC /* TC_UOPPOST */
+ "\2++" "\2--" "\1$" NTC /* TC_UOPPRE1 */
+ "\2==" "\1=" "\2+=" "\2-=" /* TC_BINOPX */
"\2*=" "\2/=" "\2%=" "\2^="
"\1+" "\1-" "\3**=" "\2**"
"\1/" "\1%" "\1^" "\1*"
"\2!=" "\2>=" "\2<=" "\1>"
"\1<" "\2!~" "\1~" "\2&&"
"\2||" "\1?" "\1:" NTC
- "\2in" NTC
- "\1," NTC
- "\1|" NTC
- "\1+" "\1-" "\1!" NTC /* UOPPRE2 */
- "\1]" NTC
- "\1{" NTC
- "\1}" NTC
- "\1;" NTC
- "\1\n" NTC
- "\2if" "\2do" "\3for" "\5break" /* STATX */
+ "\2in" NTC /* TC_IN */
+ "\1," NTC /* TC_COMMA */
+ "\1|" NTC /* TC_PIPE */
+ "\1+" "\1-" "\1!" NTC /* TC_UOPPRE2 */
+ "\1]" NTC /* TC_ARRTERM */
+ "\1{" NTC /* TC_GRPSTART */
+ "\1}" NTC /* TC_GRPTERM */
+ "\1;" NTC /* TC_SEMICOL */
+ "\1\n" NTC /* TC_NEWLINE */
+ "\2if" "\2do" "\3for" "\5break" /* TC_STATX */
"\10continue" "\6delete" "\5print"
"\6printf" "\4next" "\10nextfile"
"\6return" "\4exit" NTC
- "\5while" NTC
- "\4else" NTC
-
- "\3and" "\5compl" "\6lshift" "\2or"
+ "\5while" NTC /* TC_WHILE */
+ "\4else" NTC /* TC_ELSE */
+ "\3and" "\5compl" "\6lshift" "\2or" /* TC_BUILTIN */
"\6rshift" "\3xor"
- "\5close" "\6system" "\6fflush" "\5atan2" /* BUILTIN */
+ "\5close" "\6system" "\6fflush" "\5atan2"
"\3cos" "\3exp" "\3int" "\3log"
"\4rand" "\3sin" "\4sqrt" "\5srand"
- "\6gensub" "\4gsub" "\5index" "\6length"
+ "\6gensub" "\4gsub" "\5index" /* "\6length" was here */
"\5match" "\5split" "\7sprintf" "\3sub"
"\6substr" "\7systime" "\10strftime" "\6mktime"
"\7tolower" "\7toupper" NTC
- "\7getline" NTC
- "\4func" "\10function" NTC
- "\5BEGIN" NTC
- "\3END"
+ "\6length" NTC /* TC_LENGTH */
+ "\7getline" NTC /* TC_GETLINE */
+ "\4func" "\10function" NTC /* TC_FUNCDECL */
+ "\5BEGIN" NTC /* TC_BEGIN */
+ "\3END" /* TC_END */
/* compiler adds trailing "\0" */
;
+#define OC_B OC_BUILTIN
+
static const uint32_t tokeninfo[] = {
0,
0,
OC_COMPARE|VV|P(39)|4, OC_COMPARE|VV|P(39)|3, OC_COMPARE|VV|P(39)|0, OC_COMPARE|VV|P(39)|1,
OC_COMPARE|VV|P(39)|2, OC_MATCH|Sx|P(45)|'!', OC_MATCH|Sx|P(45)|'~', OC_LAND|Vx|P(55),
OC_LOR|Vx|P(59), OC_TERNARY|Vx|P(64)|'?', OC_COLON|xx|P(67)|':',
- OC_IN|SV|P(49), /* in */
+ OC_IN|SV|P(49), /* TC_IN */
OC_COMMA|SS|P(80),
OC_PGETLINE|SV|P(37),
OC_UNARY|xV|P(19)|'+', OC_UNARY|xV|P(19)|'-', OC_UNARY|xV|P(19)|'!',
OC_RETURN|Vx, OC_EXIT|Nx,
ST_WHILE,
0, /* else */
-
OC_B|B_an|P(0x83), OC_B|B_co|P(0x41), OC_B|B_ls|P(0x83), OC_B|B_or|P(0x83),
OC_B|B_rs|P(0x83), OC_B|B_xo|P(0x83),
OC_FBLTIN|Sx|F_cl, OC_FBLTIN|Sx|F_sy, OC_FBLTIN|Sx|F_ff, OC_B|B_a2|P(0x83),
OC_FBLTIN|Nx|F_co, OC_FBLTIN|Nx|F_ex, OC_FBLTIN|Nx|F_in, OC_FBLTIN|Nx|F_lg,
OC_FBLTIN|F_rn, OC_FBLTIN|Nx|F_si, OC_FBLTIN|Nx|F_sq, OC_FBLTIN|Nx|F_sr,
- OC_B|B_ge|P(0xd6), OC_B|B_gs|P(0xb6), OC_B|B_ix|P(0x9b), OC_FBLTIN|Sx|F_le,
+ OC_B|B_ge|P(0xd6), OC_B|B_gs|P(0xb6), OC_B|B_ix|P(0x9b), /* OC_FBLTIN|Sx|F_le, was here */
OC_B|B_ma|P(0x89), OC_B|B_sp|P(0x8b), OC_SPRINTF, OC_B|B_su|P(0xb6),
OC_B|B_ss|P(0x8f), OC_FBLTIN|F_ti, OC_B|B_ti|P(0x0b), OC_B|B_mt|P(0x0b),
OC_B|B_lo|P(0x49), OC_B|B_up|P(0x49),
+ OC_FBLTIN|Sx|F_le, /* TC_LENGTH */
OC_GETLINE|SV|P(0),
0, 0,
0,
- 0 /* END */
+ 0 /* TC_END */
};
/* internal variable names and their initial values */
static const char EMSG_POSSIBLE_ERROR[] ALIGN1 = "Possible syntax error";
static const char EMSG_UNDEF_FUNC[] ALIGN1 = "Call to undefined function";
static const char EMSG_NO_MATH[] ALIGN1 = "Math support is not compiled in";
+static const char EMSG_NEGATIVE_FIELD[] ALIGN1 = "Access to negative field";
static void zero_out_var(var *vp)
{
ltclass = t_tclass;
/* Are we ready for this? */
- if (!(ltclass & expected))
+ if (!(ltclass & expected)) {
syntax_error((ltclass & (TC_NEWLINE | TC_EOF)) ?
EMSG_UNEXP_EOS : EMSG_UNEXP_TOKEN);
+ }
return ltclass;
#undef concat_inserted
debug_printf_parse("%s: TC_BUILTIN\n", __func__);
cn->l.n = condition();
break;
+
+ case TC_LENGTH:
+ debug_printf_parse("%s: TC_LENGTH\n", __func__);
+ next_token(TC_SEQSTART | TC_OPTERM | TC_GRPTERM);
+ rollback_token();
+ if (t_tclass & TC_SEQSTART) {
+ /* It was a "(" token. Handle just like TC_BUILTIN */
+ cn->l.n = condition();
+ }
+ break;
}
}
}
next_token(TC_SEQSTART);
n2 = parse_expr(TC_SEMICOL | TC_SEQTERM);
if (t_tclass & TC_SEQTERM) { /* for-in */
- if ((n2->info & OPCLSMASK) != OC_IN)
+ if (!n2 || (n2->info & OPCLSMASK) != OC_IN)
syntax_error(EMSG_UNEXP_TOKEN);
n = chain_node(OC_WALKINIT | VV);
n->l.n = n2->l.n;
op1 = op->l.n;
debug_printf_eval("opinfo:%08x opn:%08x\n", opinfo, opn);
+ /* "delete" is special:
+ * "delete array[var--]" must evaluate index expr only once,
+ * must not evaluate it in "execute inevitable things" part.
+ */
+ if (XC(opinfo & OPCLSMASK) == XC(OC_DELETE)) {
+ uint32_t info = op1->info & OPCLSMASK;
+ var *v;
+
+ debug_printf_eval("DELETE\n");
+ if (info == OC_VAR) {
+ v = op1->l.v;
+ } else if (info == OC_FNARG) {
+ v = &fnargs[op1->l.aidx];
+ } else {
+ syntax_error(EMSG_NOT_ARRAY);
+ }
+ if (op1->r.n) { /* array ref? */
+ const char *s;
+ s = getvar_s(evaluate(op1->r.n, v1));
+ hash_remove(iamarray(v), s);
+ } else {
+ clear_array(iamarray(v));
+ }
+ goto next;
+ }
+
/* execute inevitable things */
if (opinfo & OF_RES1)
L.v = evaluate(op1, v1);
break;
}
- case XC( OC_DELETE ): {
- uint32_t info = op1->info & OPCLSMASK;
- var *v;
-
- if (info == OC_VAR) {
- v = op1->l.v;
- } else if (info == OC_FNARG) {
- v = &fnargs[op1->l.aidx];
- } else {
- syntax_error(EMSG_NOT_ARRAY);
- }
-
- if (op1->r.n) {
- const char *s;
- clrvar(L.v);
- s = getvar_s(evaluate(op1->r.n, v1));
- hash_remove(iamarray(v), s);
- } else {
- clear_array(iamarray(v));
- }
- break;
- }
+ /* case XC( OC_DELETE ): - moved to happen before arg evaluation */
case XC( OC_NEWSOURCE ):
g_progname = op->l.new_progname;
/* -- recursive node type -- */
case XC( OC_VAR ):
+ debug_printf_eval("VAR\n");
L.v = op->l.v;
if (L.v == intvar[NF])
split_f0();
goto v_cont;
case XC( OC_FNARG ):
+ debug_printf_eval("FNARG[%d]\n", op->l.aidx);
L.v = &fnargs[op->l.aidx];
v_cont:
res = op->r.n ? findvar(iamarray(L.v), R.s) : L.v;
case XC( OC_FIELD ): {
int i = (int)getvar_i(R.v);
+ if (i < 0)
+ syntax_error(EMSG_NEGATIVE_FIELD);
if (i == 0) {
res = intvar[F0];
} else {
default:
syntax_error(EMSG_POSSIBLE_ERROR);
- }
+ } /* switch */
+ next:
if ((opinfo & OPCLSMASK) <= SHIFT_TIL_THIS)
op = op->a.n;
if ((opinfo & OPCLSMASK) >= RECUR_FROM_THIS)
}
int awk_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int awk_main(int argc, char **argv)
+int awk_main(int argc UNUSED_PARAM, char **argv)
{
unsigned opt;
char *opt_F;
*s1 = '=';
}
}
- opt_complementary = OPTCOMPLSTR_AWK;
opt = getopt32(argv, OPTSTR_AWK, &opt_F, &list_v, &list_f, IF_FEATURE_AWK_GNU_EXTENSIONS(&list_e,) NULL);
argv += optind;
- argc -= optind;
+ //argc -= optind;
if (opt & OPT_W)
bb_error_msg("warning: option -W is ignored");
if (opt & OPT_F) {
if (!*argv)
bb_show_usage();
parse_program(*argv++);
- argc--;
}
/* fill in ARGV array */
- setvar_i(intvar[ARGC], argc + 1);
setari_u(intvar[ARGV], 0, "awk");
i = 0;
while (*argv)
setari_u(intvar[ARGV], ++i, *argv++);
+ setvar_i(intvar[ARGC], i + 1);
evaluate(beginseq.first, &tv);
if (!mainseq.first && !endseq.first)