X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=editors%2Fawk.c;h=bafc9ba1d382b6bbb2651469aefd12b1161625b5;hb=2454e678cb7d241097ee9ce4b2114a136f1b57ec;hp=8848d94a5b67f6d627cc147013fb8392060c924e;hpb=7985bc109e0d738644094f391d08d9848a2f2b50;p=oweals%2Fbusybox.git diff --git a/editors/awk.c b/editors/awk.c index 8848d94a5..bafc9ba1d 100644 --- a/editors/awk.c +++ b/editors/awk.c @@ -6,6 +6,34 @@ * * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ +//config:config AWK +//config: bool "awk (22 kb)" +//config: default y +//config: help +//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 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: Example: awk -f mylib.awk -e '{print myfunction($1);}' ... + +//applet:IF_AWK(APPLET_NOEXEC(awk, awk, BB_DIR_USR_BIN, BB_SUID_DROP, awk)) + +//kbuild:lib-$(CONFIG_AWK) += awk.o //usage:#define awk_trivial_usage //usage: "[OPTIONS] [AWK_PROGRAM] [FILE]..." @@ -13,6 +41,9 @@ //usage: " -v VAR=VAL Set variable" //usage: "\n -F SEP Use SEP as field separator" //usage: "\n -f FILE Read program from FILE" +//usage: IF_FEATURE_AWK_GNU_EXTENSIONS( +//usage: "\n -e AWK_PROGRAM" +//usage: ) #include "libbb.h" #include "xregex.h" @@ -38,6 +69,26 @@ #endif +/* "+": stop on first non-option: + * $ awk 'BEGIN { for(i=1; i >> */ @@ -175,16 +226,22 @@ typedef struct tsplitter_s { #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) @@ -192,14 +249,16 @@ typedef struct tsplitter_s { #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 \ @@ -294,54 +353,54 @@ enum { #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, @@ -356,7 +415,7 @@ static const uint32_t tokeninfo[] = { 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)|'!', @@ -371,20 +430,20 @@ static const uint32_t tokeninfo[] = { 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 */ @@ -539,6 +598,7 @@ static const char EMSG_NOT_ARRAY[] ALIGN1 = "Not an array"; 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) { @@ -1008,12 +1068,10 @@ static uint32_t next_token(uint32_t expected) if (t_rollback) { t_rollback = FALSE; - } else if (concat_inserted) { concat_inserted = FALSE; t_tclass = save_tclass; t_info = save_info; - } else { p = g_pos; readnext: @@ -1029,7 +1087,6 @@ static uint32_t next_token(uint32_t expected) if (*p == '\0') { tc = TC_EOF; debug_printf_parse("%s: token found: TC_EOF\n", __func__); - } else if (*p == '\"') { /* it's a string */ t_string = s = ++p; @@ -1045,7 +1102,6 @@ static uint32_t next_token(uint32_t expected) *s = '\0'; tc = TC_STRING; debug_printf_parse("%s: token found:'%s' TC_STRING\n", __func__, t_string); - } else if ((expected & TC_REGEXP) && *p == '/') { /* it's regexp */ t_string = s = ++p; @@ -1078,7 +1134,6 @@ static uint32_t next_token(uint32_t expected) syntax_error(EMSG_UNEXP_TOKEN); tc = TC_NUMBER; debug_printf_parse("%s: token found:%f TC_NUMBER\n", __func__, t_double); - } else { /* search for something known */ tl = tokenlist; @@ -1155,9 +1210,10 @@ static uint32_t next_token(uint32_t expected) 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 @@ -1324,6 +1380,16 @@ static node *parse_expr(uint32_t iexp) 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; } } } @@ -1451,7 +1517,7 @@ static void chain_group(void) 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; @@ -1488,12 +1554,14 @@ static void chain_group(void) debug_printf_parse("%s: OC_BREAK\n", __func__); n = chain_node(OC_EXEC); n->a.n = break_ptr; + chain_expr(t_info); break; case OC_CONTINUE: debug_printf_parse("%s: OC_CONTINUE\n", __func__); n = chain_node(OC_EXEC); n->a.n = continue_ptr; + chain_expr(t_info); break; /* delete, next, nextfile, return, exit */ @@ -1526,12 +1594,10 @@ static void parse_program(char *p) debug_printf_parse("%s: TC_BEGIN\n", __func__); seq = &beginseq; chain_group(); - } else if (tclass & TC_END) { debug_printf_parse("%s: TC_END\n", __func__); seq = &endseq; chain_group(); - } else if (tclass & TC_FUNCDECL) { debug_printf_parse("%s: TC_FUNCDECL\n", __func__); next_token(TC_FUNCTION); @@ -1549,7 +1615,6 @@ static void parse_program(char *p) seq = &f->body; chain_group(); clear_array(ahash); - } else if (tclass & TC_OPSEQ) { debug_printf_parse("%s: TC_OPSEQ\n", __func__); rollback_token(); @@ -1564,7 +1629,6 @@ static void parse_program(char *p) chain_node(OC_PRINT); } cn->r.n = mainseq.last; - } else /* if (tclass & TC_GRPSTART) */ { debug_printf_parse("%s: TC_GRPSTART(?)\n", __func__); rollback_token(); @@ -1825,13 +1889,10 @@ static void handle_special(var *v) split_f0(); mk_splitter(getvar_s(v), &fsplitter); - } else if (v == intvar[RS]) { mk_splitter(getvar_s(v), &rsplitter); - } else if (v == intvar[IGNORECASE]) { icase = istrue(v); - } else { /* $n */ n = getvar_i(intvar[NF]); setvar_i(intvar[NF], n > v-Fields ? n : v-Fields+1); @@ -2454,6 +2515,32 @@ static var *evaluate(node *op, var *res) 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); @@ -2561,28 +2648,7 @@ static var *evaluate(node *op, var *res) 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; @@ -2606,12 +2672,14 @@ static var *evaluate(node *op, var *res) /* -- 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; @@ -2882,6 +2950,8 @@ static var *evaluate(node *op, var *res) 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 { @@ -2975,7 +3045,8 @@ static var *evaluate(node *op, var *res) default: syntax_error(EMSG_POSSIBLE_ERROR); - } + } /* switch */ + next: if ((opinfo & OPCLSMASK) <= SHIFT_TIL_THIS) op = op->a.n; if ((opinfo & OPCLSMASK) >= RECUR_FROM_THIS) @@ -3081,12 +3152,15 @@ static rstream *next_input_file(void) } 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; llist_t *list_v = NULL; llist_t *list_f = NULL; +#if ENABLE_FEATURE_AWK_GNU_EXTENSIONS + llist_t *list_e = NULL; +#endif int i, j; var *v; var tv; @@ -3145,51 +3219,53 @@ int awk_main(int argc, char **argv) *s1 = '='; } } - opt_complementary = "v::f::"; /* -v and -f can occur multiple times */ - opt = getopt32(argv, "F:v:f:W:", &opt_F, &list_v, &list_f, NULL); + opt = getopt32(argv, OPTSTR_AWK, &opt_F, &list_v, &list_f, IF_FEATURE_AWK_GNU_EXTENSIONS(&list_e,) NULL); argv += optind; - argc -= optind; - if (opt & 0x1) { /* -F */ + //argc -= optind; + if (opt & OPT_W) + bb_error_msg("warning: option -W is ignored"); + if (opt & OPT_F) { unescape_string_in_place(opt_F); setvar_s(intvar[FS], opt_F); } - while (list_v) { /* -v */ + while (list_v) { if (!is_assignment(llist_pop(&list_v))) bb_show_usage(); } - if (list_f) { /* -f */ - do { - char *s = NULL; - FILE *from_file; - - g_progname = llist_pop(&list_f); - from_file = xfopen_stdin(g_progname); - /* one byte is reserved for some trick in next_token */ - for (i = j = 1; j > 0; i += j) { - s = xrealloc(s, i + 4096); - j = fread(s + i, 1, 4094, from_file); - } - s[i] = '\0'; - fclose(from_file); - parse_program(s + 1); - free(s); - } while (list_f); - argc++; - } else { // no -f: take program from 1st parameter - if (!argc) + while (list_f) { + char *s = NULL; + FILE *from_file; + + g_progname = llist_pop(&list_f); + from_file = xfopen_stdin(g_progname); + /* one byte is reserved for some trick in next_token */ + for (i = j = 1; j > 0; i += j) { + s = xrealloc(s, i + 4096); + j = fread(s + i, 1, 4094, from_file); + } + s[i] = '\0'; + fclose(from_file); + parse_program(s + 1); + free(s); + } + g_progname = "cmd. line"; +#if ENABLE_FEATURE_AWK_GNU_EXTENSIONS + while (list_e) { + parse_program(llist_pop(&list_e)); + } +#endif + if (!(opt & (OPT_f | OPT_e))) { + if (!*argv) bb_show_usage(); - g_progname = "cmd. line"; parse_program(*argv++); } - if (opt & 0x8) // -W - bb_error_msg("warning: option -W is ignored"); /* fill in ARGV array */ - setvar_i(intvar[ARGC], argc); 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)