From e2e3802987266c98df0efdf40ad5da4b07df0113 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 4 Jul 2021 01:25:34 +0200 Subject: awk: fix printf buffer overflow function old new delta awk_printf 468 546 +78 fmt_num 239 247 +8 getvar_s 125 111 -14 evaluate 3343 3329 -14 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/2 up/down: 86/-28) Total: 58 bytes Signed-off-by: Denys Vlasenko --- editors/awk.c | 94 ++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 55 insertions(+), 39 deletions(-) diff --git a/editors/awk.c b/editors/awk.c index cd135ef64..a440a6234 100644 --- a/editors/awk.c +++ b/editors/awk.c @@ -904,25 +904,23 @@ static double my_strtod(char **pp) /* -------- working with variables (set/get/copy/etc) -------- */ -static int fmt_num(char *b, int size, const char *format, double n, int int_as_int) +static void fmt_num(const char *format, double n) { - int r = 0; - char c; - const char *s = format; - - if (int_as_int && n == (long long)n) { - r = snprintf(b, size, "%lld", (long long)n); + if (n == (long long)n) { + snprintf(g_buf, MAXVARFMT, "%lld", (long long)n); } else { + const char *s = format; + char c; + do { c = *s; } while (c && *++s); if (strchr("diouxX", c)) { - r = snprintf(b, size, format, (int)n); + snprintf(g_buf, MAXVARFMT, format, (int)n); } else if (strchr("eEfFgGaA", c)) { - r = snprintf(b, size, format, n); + snprintf(g_buf, MAXVARFMT, format, n); } else { syntax_error(EMSG_INV_FMT); } } - return r; } static xhash *iamarray(var *a) @@ -999,7 +997,7 @@ static const char *getvar_s(var *v) { /* if v is numeric and has no cached string, convert it to string */ if ((v->type & (VF_NUMBER | VF_CACHED)) == VF_NUMBER) { - fmt_num(g_buf, MAXVARFMT, getvar_s(intvar[CONVFMT]), v->number, TRUE); + fmt_num(getvar_s(intvar[CONVFMT]), v->number); v->string = xstrdup(g_buf); v->type |= VF_CACHED; } @@ -2315,12 +2313,9 @@ static int awk_getline(rstream *rsm, var *v) #endif static char *awk_printf(node *n, int *len) { - char *b = NULL; - char *fmt, *s, *f; - const char *s1; - int i, j, incr, bsize; - char c, c1; - var *arg; + char *b; + char *fmt, *f; + int i; //tmpvar = nvalloc(1); #define TMPVAR (&G.awk_printf__tmpvar) @@ -2333,8 +2328,14 @@ static char *awk_printf(node *n, int *len) // to evaluate() potentially recursing into another awk_printf() can't // mangle the value. + b = NULL; i = 0; - while (*f) { + while (*f) { /* "print one format spec" loop */ + char *s; + char c; + char sv; + var *arg; + s = f; while (*f && (*f != '%' || *++f == '%')) f++; @@ -2343,40 +2344,55 @@ static char *awk_printf(node *n, int *len) syntax_error("%*x formats are not supported"); f++; } - - incr = (f - s) + MAXVARFMT; - b = qrealloc(b, incr + i, &bsize); c = *f; - if (c != '\0') - f++; - c1 = *f; + if (!c) { + /* Tail of fmt with no percent chars, + * or "....%" (percent seen, but no format specifier char found) + */ + goto tail; + } + sv = *++f; *f = '\0'; arg = evaluate(nextarg(&n), TMPVAR); - j = i; - if (c == 'c' || !c) { - i += sprintf(b+i, s, is_numeric(arg) ? + /* Result can be arbitrarily long. Example: + * printf "%99999s", "BOOM" + */ + if (c == 'c') { + s = xasprintf(s, is_numeric(arg) ? (char)getvar_i(arg) : *getvar_s(arg)); } else if (c == 's') { - s1 = getvar_s(arg); - b = qrealloc(b, incr+i+strlen(s1), &bsize); - i += sprintf(b+i, s, s1); + s = xasprintf(s, getvar_s(arg)); } else { - i += fmt_num(b+i, incr, s, getvar_i(arg), FALSE); + double d = getvar_i(arg); + if (strchr("diouxX", c)) { +//TODO: make it wider here (%x -> %llx etc)? + s = xasprintf(s, (int)d); + } else if (strchr("eEfFgGaA", c)) { + s = xasprintf(s, d); + } else { + syntax_error(EMSG_INV_FMT); + } } - *f = c1; + *f = sv; - /* if there was an error while sprintf, return value is negative */ - if (i < j) - i = j; + if (i == 0) { + b = s; + i = strlen(b); + continue; + } + tail: + b = xrealloc(b, i + strlen(s) + 1); + i = stpcpy(b + i, s) - b; + if (!c) /* tail? */ + break; + free(s); } free(fmt); //nvfree(tmpvar, 1); #undef TMPVAR - b = xrealloc(b, i + 1); - b[i] = '\0'; #if ENABLE_FEATURE_AWK_GNU_EXTENSIONS if (len) *len = i; @@ -2936,8 +2952,8 @@ static var *evaluate(node *op, var *res) for (;;) { var *v = evaluate(nextarg(&op1), TMPVAR0); if (v->type & VF_NUMBER) { - fmt_num(g_buf, MAXVARFMT, getvar_s(intvar[OFMT]), - getvar_i(v), TRUE); + fmt_num(getvar_s(intvar[OFMT]), + getvar_i(v)); fputs(g_buf, F); } else { fputs(getvar_s(v), F); -- cgit v1.2.3-55-g6feb From 49c3ce64f092fd5434fc67056f312bd32f82bae3 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 11 Jul 2021 11:46:21 +0200 Subject: awk: rollback_token() + chain_group() == chain_until_rbrace() function old new delta parse_program 336 332 -4 Signed-off-by: Denys Vlasenko --- editors/awk.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/editors/awk.c b/editors/awk.c index a440a6234..755e68fc7 100644 --- a/editors/awk.c +++ b/editors/awk.c @@ -1778,8 +1778,7 @@ static void parse_program(char *p) cn->l.n = parse_expr(TC_SEMICOL | TC_NEWLINE | TC_EOF | TC_LBRACE); if (t_tclass == TC_LBRACE) { debug_printf_parse("%s: TC_LBRACE\n", __func__); - rollback_token(); - chain_group(); + chain_until_rbrace(); } else { /* no action, assume default "{ print }" */ debug_printf_parse("%s: !TC_LBRACE\n", __func__); -- cgit v1.2.3-55-g6feb From 3d57a8490738d9febaa4496eba791e4fbfc91826 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 11 Jul 2021 12:00:31 +0200 Subject: awk: undo TI_PRINT, it introduced a bug (print with any redirect acting as printf) function old new delta evaluate 3329 3337 +8 Patch by Ron Yorston Signed-off-by: Denys Vlasenko --- editors/awk.c | 8 +++++--- testsuite/awk.tests | 5 +++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/editors/awk.c b/editors/awk.c index 755e68fc7..0aa7c0804 100644 --- a/editors/awk.c +++ b/editors/awk.c @@ -462,8 +462,7 @@ static const uint32_t tokeninfo[] ALIGN4 = { 0, 0, /* \n */ ST_IF, ST_DO, ST_FOR, OC_BREAK, -#define TI_PRINT OC_PRINT - OC_CONTINUE, OC_DELETE|Rx, TI_PRINT, + OC_CONTINUE, OC_DELETE|Rx, OC_PRINT, OC_PRINTF, OC_NEXT, OC_NEXTFILE, OC_RETURN|Vx, OC_EXIT|Nx, ST_WHILE, @@ -2944,7 +2943,10 @@ static var *evaluate(node *op, var *res) F = rsm->F; } - if (opinfo == TI_PRINT) { + /* Can't just check 'opinfo == OC_PRINT' here, parser ORs + * additional bits to opinfos of print/printf with redirects + */ + if ((opinfo & OPCLSMASK) == OC_PRINT) { if (!op1) { fputs(getvar_s(intvar[F0]), F); } else { diff --git a/testsuite/awk.tests b/testsuite/awk.tests index 770d8ffce..6b23b91cb 100755 --- a/testsuite/awk.tests +++ b/testsuite/awk.tests @@ -450,4 +450,9 @@ testing "awk exit N propagates through END's exit" \ "42\n" \ '' '' +testing "awk print + redirect" \ + "awk 'BEGIN { print \"STDERR %s\" >\"/dev/stderr\" }' 2>&1" \ + "STDERR %s\n" \ + '' '' + exit $FAILCOUNT -- cgit v1.2.3-55-g6feb From 4ef8841b21e27e7c7f58d3c9901c833b4fa5a862 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 11 Jul 2021 12:25:33 +0200 Subject: awk: unbreak "printf('%c') can output NUL" testcase function old new delta awk_printf 546 593 +47 Signed-off-by: Denys Vlasenko --- editors/awk.c | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/editors/awk.c b/editors/awk.c index 0aa7c0804..e765d3fcf 100644 --- a/editors/awk.c +++ b/editors/awk.c @@ -2309,11 +2309,11 @@ static int awk_getline(rstream *rsm, var *v) #if !ENABLE_FEATURE_AWK_GNU_EXTENSIONS # define awk_printf(a, b) awk_printf(a) #endif -static char *awk_printf(node *n, int *len) +static char *awk_printf(node *n, size_t *len) { char *b; char *fmt, *f; - int i; + size_t i; //tmpvar = nvalloc(1); #define TMPVAR (&G.awk_printf__tmpvar) @@ -2333,6 +2333,7 @@ static char *awk_printf(node *n, int *len) char c; char sv; var *arg; + size_t slen; s = f; while (*f && (*f != '%' || *++f == '%')) @@ -2347,6 +2348,7 @@ static char *awk_printf(node *n, int *len) /* Tail of fmt with no percent chars, * or "....%" (percent seen, but no format specifier char found) */ + slen = strlen(s); goto tail; } sv = *++f; @@ -2357,31 +2359,38 @@ static char *awk_printf(node *n, int *len) * printf "%99999s", "BOOM" */ if (c == 'c') { - s = xasprintf(s, is_numeric(arg) ? - (char)getvar_i(arg) : *getvar_s(arg)); - } else if (c == 's') { - s = xasprintf(s, getvar_s(arg)); + c = is_numeric(arg) ? getvar_i(arg) : *getvar_s(arg); + s = xasprintf(s, c); + /* + 1 if c == NUL: handle printf "%c" 0 case + * (and printf "%22c" 0 etc, but still fails for e.g. printf "%-22c" 0) */ + slen = strlen(s) + (c == '\0'); } else { - double d = getvar_i(arg); - if (strchr("diouxX", c)) { -//TODO: make it wider here (%x -> %llx etc)? - s = xasprintf(s, (int)d); - } else if (strchr("eEfFgGaA", c)) { - s = xasprintf(s, d); + if (c == 's') { + s = xasprintf(s, getvar_s(arg)); } else { - syntax_error(EMSG_INV_FMT); + double d = getvar_i(arg); + if (strchr("diouxX", c)) { +//TODO: make it wider here (%x -> %llx etc)? + s = xasprintf(s, (int)d); + } else if (strchr("eEfFgGaA", c)) { + s = xasprintf(s, d); + } else { + syntax_error(EMSG_INV_FMT); + } } + slen = strlen(s); } *f = sv; if (i == 0) { b = s; - i = strlen(b); + i = slen; continue; } tail: - b = xrealloc(b, i + strlen(s) + 1); - i = stpcpy(b + i, s) - b; + b = xrealloc(b, i + slen + 1); + strcpy(b + i, s); + i += slen; if (!c) /* tail? */ break; free(s); @@ -2926,7 +2935,6 @@ static var *evaluate(node *op, var *res) debug_printf_eval("PRINTF\n"); { FILE *F = stdout; - IF_FEATURE_AWK_GNU_EXTENSIONS(int len;) if (op->r.n) { rstream *rsm = newfile(R.s); @@ -2966,6 +2974,7 @@ static var *evaluate(node *op, var *res) } fputs(getvar_s(intvar[ORS]), F); } else { /* PRINTF */ + IF_FEATURE_AWK_GNU_EXTENSIONS(size_t len;) char *s = awk_printf(op1, &len); #if ENABLE_FEATURE_AWK_GNU_EXTENSIONS fwrite(s, len, 1, F); -- cgit v1.2.3-55-g6feb From 39aabfe8f033c9c62acf676b660dc979714d26a7 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 11 Jul 2021 12:51:43 +0200 Subject: awk: unbreak "cmd" | getline function old new delta evaluate 3337 3343 +6 Signed-off-by: Denys Vlasenko --- editors/awk.c | 3 ++- testsuite/awk.tests | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/editors/awk.c b/editors/awk.c index e765d3fcf..6c60a0615 100644 --- a/editors/awk.c +++ b/editors/awk.c @@ -3156,7 +3156,8 @@ static var *evaluate(node *op, var *res) if (op1) { rsm = newfile(L.s); if (!rsm->F) { - if (opinfo == TI_PGETLINE) { + /* NB: can't use "opinfo == TI_PGETLINE", would break "cmd" | getline */ + if ((opinfo & OPCLSMASK) == OC_PGETLINE) { rsm->F = popen(L.s, "r"); rsm->is_pipe = TRUE; } else { diff --git a/testsuite/awk.tests b/testsuite/awk.tests index 6b23b91cb..242c897d1 100755 --- a/testsuite/awk.tests +++ b/testsuite/awk.tests @@ -455,4 +455,9 @@ testing "awk print + redirect" \ "STDERR %s\n" \ '' '' +testing "awk \"cmd\" | getline" \ + "awk 'BEGIN { \"echo HELLO\" | getline; print }'" \ + "HELLO\n" \ + '' '' + exit $FAILCOUNT -- cgit v1.2.3-55-g6feb From f5f336e787fd8367d24de5fe3b341ebffb74c2c2 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 11 Jul 2021 14:50:04 +0200 Subject: tc: fix for parsing of "dev IFACE" and printing of "class list" First part (parsing fix) is from Steffen Nurpmeso function old new delta .rodata 104120 104138 +18 print_qdisc 469 464 -5 tc_main 980 969 -11 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/2 up/down: 18/-16) Total: 2 bytes Signed-off-by: Denys Vlasenko --- networking/tc.c | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/networking/tc.c b/networking/tc.c index 510684443..46ad23d8b 100644 --- a/networking/tc.c +++ b/networking/tc.c @@ -111,16 +111,14 @@ static char* print_tc_classid(uint32_t cid) #if 0 /* IMPOSSIBLE */ if (cid == TC_H_ROOT) return xasprintf("root"); - else #endif if (cid == TC_H_UNSPEC) return xasprintf("none"); - else if (TC_H_MAJ(cid) == 0) + if (TC_H_MAJ(cid) == 0) return xasprintf(":%x", TC_H_MIN(cid)); - else if (TC_H_MIN(cid) == 0) + if (TC_H_MIN(cid) == 0) return xasprintf("%x:", TC_H_MAJ(cid)>>16); - else - return xasprintf("%x:%x", TC_H_MAJ(cid)>>16, TC_H_MIN(cid)); + return xasprintf("%x:%x", TC_H_MAJ(cid)>>16, TC_H_MIN(cid)); } /* Get a qdisc handle. Return 0 on success, !0 otherwise. */ @@ -376,8 +374,10 @@ static FAST_FUNC int print_qdisc( prio_print_opt(tb[TCA_OPTIONS]); } else if (qqq == 1) { /* class based queuing */ cbq_print_opt(tb[TCA_OPTIONS]); - } else - bb_error_msg("unknown %s", name); + } else { + /* don't know how to print options for this qdisc */ + printf("(options for %s)", name); + } } bb_putchar('\n'); return 0; @@ -405,7 +405,7 @@ static FAST_FUNC int print_class( return -1; } /* not the desired interface? */ - if (filter_qdisc && TC_H_MAJ(msg->tcm_handle^filter_qdisc)) + if (filter_qdisc && TC_H_MAJ(msg->tcm_handle ^ filter_qdisc)) return 0; memset (tb, 0, sizeof(tb)); parse_rtattr(tb, TCA_MAX, TCA_RTA(msg), len); @@ -418,8 +418,8 @@ static FAST_FUNC int print_class( name = (char*)RTA_DATA(tb[TCA_KIND]); classid = !msg->tcm_handle ? NULL : print_tc_classid( - filter_qdisc ? TC_H_MIN(msg->tcm_parent) : msg->tcm_parent); - printf ("class %s %s", name, classid); + filter_qdisc ? TC_H_MIN(msg->tcm_handle) : msg->tcm_handle); + printf ("class %s %s ", name, classid); if (ENABLE_FEATURE_CLEAN_UP) free(classid); @@ -445,8 +445,10 @@ static FAST_FUNC int print_class( } else if (qqq == 1) { /* class based queuing */ /* cbq_print_copt() is identical to cbq_print_opt(). */ cbq_print_opt(tb[TCA_OPTIONS]); - } else - bb_error_msg("unknown %s", name); + } else { + /* don't know how to print options for this class */ + printf("(options for %s)", name); + } } bb_putchar('\n'); @@ -511,12 +513,11 @@ int tc_main(int argc UNUSED_PARAM, char **argv) ret = EXIT_SUCCESS; obj = index_in_substrings(objects, *argv++); - if (obj < 0) bb_show_usage(); - if (!*argv) - cmd = CMD_show; /* list is the default */ - else { + + cmd = CMD_show; /* list (aka show) is the default */ + if (*argv) { cmd = index_in_substrings(commands, *argv); if (cmd < 0) invarg_1_to_2(*argv, argv[-1]); @@ -538,16 +539,17 @@ int tc_main(int argc UNUSED_PARAM, char **argv) msg.tcm_ifindex = xll_name_to_index(dev); if (cmd >= CMD_show) filter_ifindex = msg.tcm_ifindex; - } else - if ((arg == ARG_qdisc && obj == OBJ_class && cmd >= CMD_show) - || (arg == ARG_handle && obj == OBJ_qdisc && cmd == CMD_change) + continue; + } + if ((arg == ARG_qdisc && obj == OBJ_class && cmd >= CMD_show) /* tc class show|list qdisc HANDLE */ + || (arg == ARG_handle && obj == OBJ_qdisc && cmd == CMD_change) /* tc qdisc change handle HANDLE */ ) { NEXT_ARG(); /* We don't care about duparg2("qdisc handle",*argv) for now */ if (get_qdisc_handle(&filter_qdisc, *argv)) invarg_1_to_2(*argv, "qdisc"); } else - if (obj != OBJ_qdisc + if (obj != OBJ_qdisc /* tc class|filter root|parent | tc filter preference|priority|protocol */ && (arg == ARG_root || arg == ARG_parent || (obj == OBJ_filter && arg >= ARG_pref) -- cgit v1.2.3-55-g6feb From caa93ecdd3a9b998a69dcbfafdddbc9c58887ec3 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 11 Jul 2021 18:16:10 +0200 Subject: awk: fix corner case in awk_printf Example where it wasn't working: awk 'BEGIN { printf "qwe %s rty %c uio\n", "a", 0, "c" }' - the NUL printing in %c caused premature stop of printing. function old new delta awk_printf 593 596 +3 Signed-off-by: Denys Vlasenko --- editors/awk.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/editors/awk.c b/editors/awk.c index 6c60a0615..465033f5f 100644 --- a/editors/awk.c +++ b/editors/awk.c @@ -2359,11 +2359,11 @@ static char *awk_printf(node *n, size_t *len) * printf "%99999s", "BOOM" */ if (c == 'c') { - c = is_numeric(arg) ? getvar_i(arg) : *getvar_s(arg); - s = xasprintf(s, c); - /* + 1 if c == NUL: handle printf "%c" 0 case + char cc = is_numeric(arg) ? getvar_i(arg) : *getvar_s(arg); + s = xasprintf(s, cc); + /* + 1 if cc == NUL: handle printf "%c" 0 case * (and printf "%22c" 0 etc, but still fails for e.g. printf "%-22c" 0) */ - slen = strlen(s) + (c == '\0'); + slen = strlen(s) + (cc == '\0'); } else { if (c == 's') { s = xasprintf(s, getvar_s(arg)); -- cgit v1.2.3-55-g6feb From 8d269ef85984f6476e7fdbec2c5a70f3b5c48a72 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 12 Jul 2021 11:27:11 +0200 Subject: awk: fix printf "%-10c", 0 function old new delta awk_printf 596 626 +30 Signed-off-by: Denys Vlasenko --- editors/awk.c | 9 +++++---- testsuite/awk.tests | 8 ++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/editors/awk.c b/editors/awk.c index 465033f5f..437d87ecf 100644 --- a/editors/awk.c +++ b/editors/awk.c @@ -2360,10 +2360,11 @@ static char *awk_printf(node *n, size_t *len) */ if (c == 'c') { char cc = is_numeric(arg) ? getvar_i(arg) : *getvar_s(arg); - s = xasprintf(s, cc); - /* + 1 if cc == NUL: handle printf "%c" 0 case - * (and printf "%22c" 0 etc, but still fails for e.g. printf "%-22c" 0) */ - slen = strlen(s) + (cc == '\0'); + char *r = xasprintf(s, cc ? cc : '^' /* else strlen will be wrong */); + slen = strlen(r); + if (cc == '\0') /* if cc is NUL, re-format the string with it */ + sprintf(r, s, cc); + s = r; } else { if (c == 's') { s = xasprintf(s, getvar_s(arg)); diff --git a/testsuite/awk.tests b/testsuite/awk.tests index 242c897d1..3cddb4dd4 100755 --- a/testsuite/awk.tests +++ b/testsuite/awk.tests @@ -415,6 +415,14 @@ testing "awk printf('%c') can output NUL" \ "awk '{printf(\"hello%c null\n\", 0)}'" "hello\0 null\n" "" "\n" SKIP= +optional FEATURE_AWK_GNU_EXTENSIONS +testing "awk printf('%-10c') can output NUL" \ + "awk 'BEGIN { printf \"[%-10c]\n\", 0 }' | od -tx1" "\ +0000000 5b 00 20 20 20 20 20 20 20 20 20 5d 0a +0000015 +" "" "" +SKIP= + # testing "description" "command" "result" "infile" "stdin" testing 'awk negative field access' \ 'awk 2>&1 -- '\''{ $(-1) }'\' \ -- cgit v1.2.3-55-g6feb From ab755e3717cefc06fd28ce8db56f0402412afaa3 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 12 Jul 2021 13:30:30 +0200 Subject: awk: in parsing, remove superfluous NEWLINE check; optimize builtin arg evaluation function old new delta exec_builtin 1149 1145 -4 Signed-off-by: Denys Vlasenko --- editors/awk.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/editors/awk.c b/editors/awk.c index 437d87ecf..7a282356d 100644 --- a/editors/awk.c +++ b/editors/awk.c @@ -1589,8 +1589,8 @@ static void chain_group(void) chain_until_rbrace(); return; } - if (tc & (TS_OPSEQ | TC_SEMICOL | TC_NEWLINE)) { - debug_printf_parse("%s: TS_OPSEQ | TC_SEMICOL | TC_NEWLINE\n", __func__); + if (tc & (TS_OPSEQ | TC_SEMICOL)) { + debug_printf_parse("%s: TS_OPSEQ | TC_SEMICOL\n", __func__); rollback_token(); chain_expr(OC_EXEC | Vx); return; @@ -2582,10 +2582,11 @@ static NOINLINE var *exec_builtin(node *op, var *res) av[2] = av[3] = NULL; for (i = 0; i < 4 && op; i++) { an[i] = nextarg(&op); - if (isr & 0x09000000) + if (isr & 0x09000000) { av[i] = evaluate(an[i], TMPVAR(i)); - if (isr & 0x08000000) - as[i] = getvar_s(av[i]); + if (isr & 0x08000000) + as[i] = getvar_s(av[i]); + } isr >>= 1; } -- cgit v1.2.3-55-g6feb From b50ac07cba558c370be66226ae0ad762157ee59a Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Wed, 7 Jul 2021 09:23:30 +0100 Subject: vi: allow 'gg' to specify a range Commit 7b93e317c (vi: enable 'dG' command. Closes 11801) allowed 'G' to be used as a range specifier for change/yank/delete operations. Add similar support for 'gg'. This requires setting the 'cmd_error' flag if 'g' is followed by any character other than another 'g'. function old new delta do_cmd 4852 4860 +8 .rodata 108179 108180 +1 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/0 up/down: 9/0) Total: 9 bytes Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- editors/vi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/editors/vi.c b/editors/vi.c index f0bbc9518..22b8f7cf1 100644 --- a/editors/vi.c +++ b/editors/vi.c @@ -3437,7 +3437,7 @@ static int find_range(char **start, char **stop, int cmd) // for non-change operations WS after NL is not part of word if (cmd != 'c' && dot != t && *dot != '\n') dot = t; - } else if (strchr("GHL+-jk'\r\n", c)) { + } else if (strchr("GHL+-gjk'\r\n", c)) { // these operate on whole lines buftype = WHOLE; do_cmd(c); // execute movement cmd @@ -4027,6 +4027,7 @@ static void do_cmd(int c) buf[1] = (c1 >= 0 ? c1 : '*'); buf[2] = '\0'; not_implemented(buf); + cmd_error = TRUE; break; } if (cmdcnt == 0) -- cgit v1.2.3-55-g6feb From 2916443ab650b10fd401ceb221d26fa58e2e99b3 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Fri, 2 Jul 2021 08:23:06 +0100 Subject: vi: use basic regular expressions for search Both traditional vi and vim use basic regular expressions for search. Also, they don't allow matches to extend across line endings. Thus with the file: 123 234 the search '/2.*4$' should find the second '2', not the first. Make BusyBox vi do the same. Whether or not VI_REGEX_SEARCH is enabled: function old new delta ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/0 up/down: 0/0) Total: 0 bytes Signed-off-by: Andrey Dobrovolsky Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- editors/vi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/editors/vi.c b/editors/vi.c index 22b8f7cf1..34d577e90 100644 --- a/editors/vi.c +++ b/editors/vi.c @@ -2378,9 +2378,9 @@ static char *char_search(char *p, const char *pat, int dir_and_range) char *q; int i, size, range, start; - re_syntax_options = RE_SYNTAX_POSIX_EXTENDED; + re_syntax_options = RE_SYNTAX_POSIX_BASIC & (~RE_DOT_NEWLINE); if (ignorecase) - re_syntax_options = RE_SYNTAX_POSIX_EXTENDED | RE_ICASE; + re_syntax_options |= RE_ICASE; memset(&preg, 0, sizeof(preg)); err = re_compile_pattern(pat, strlen(pat), &preg); -- cgit v1.2.3-55-g6feb From c76c78740a19ed3b1f9c5910313460221096536a Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Tue, 6 Jul 2021 07:43:57 +0100 Subject: vi: improve handling of anchored searches Suppose we search for a git conflict marker '<<<<<<< HEAD' using the command '/^<<<'. Using 'n' to go to the next match finds '<<<' on the current line, apparently ignoring the '^' anchor. Set a flag in the compiled regular expression to indicate that the start of the string should not be considered a beginning-of-line anchor. An exception has to be made when the search starts from the beginning of the file. Make a similar change for end-of-line anchors. This doesn't affect a default build with VI_REGEX_SEARCH disabled. When it's enabled: function old new delta char_search 247 285 +38 Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- editors/vi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/editors/vi.c b/editors/vi.c index 34d577e90..2941b8ae4 100644 --- a/editors/vi.c +++ b/editors/vi.c @@ -2384,6 +2384,8 @@ static char *char_search(char *p, const char *pat, int dir_and_range) memset(&preg, 0, sizeof(preg)); err = re_compile_pattern(pat, strlen(pat), &preg); + preg.not_bol = p != text; + preg.not_eol = p != end - 1; if (err != NULL) { status_line_bold("bad search pattern '%s': %s", pat, err); return p; -- cgit v1.2.3-55-g6feb From 95ac4a48f17c2fdd2a10524c0b399e3be72d8f42 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 13 Jul 2021 14:38:20 +0200 Subject: vi: allow regular expressions in ':s' commands BusyBox vi has never supported the use of regular expressions in search/replace (':s') commands. Implement this using GNU regex when VI_REGEX_SEARCH is enabled. The implementation: - uses basic regular expressions, to match those used in the search command; - only supports substitution of back references ('\0' - '\9') in the replacement string. Any other character following a backslash is treated as that literal character. VI_REGEX_SEARCH isn't enabled in the default build. In that case: function old new delta colon 4036 4033 -3 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-3) Total: -3 bytes When VI_REGEX_SEARCH is enabled: function old new delta colon 4036 4378 +342 .rodata 108207 108229 +22 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/0 up/down: 364/0) Total: 364 bytes v2: Rebase. Code shrink. Ensure empty replacement string is null terminated. Signed-off-by: Andrey Dobrovolsky Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- editors/vi.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 116 insertions(+), 15 deletions(-) diff --git a/editors/vi.c b/editors/vi.c index 2941b8ae4..070e0f55a 100644 --- a/editors/vi.c +++ b/editors/vi.c @@ -2677,6 +2677,59 @@ static char *expand_args(char *args) # endif #endif /* FEATURE_VI_COLON */ +#if ENABLE_FEATURE_VI_REGEX_SEARCH +# define MAX_SUBPATTERN 10 // subpatterns \0 .. \9 + +// If the return value is not NULL the caller should free R +static char *regex_search(char *q, regex_t *preg, const char *Rorig, + size_t *len_F, size_t *len_R, char **R) +{ + regmatch_t regmatch[MAX_SUBPATTERN], *cur_match; + char *found = NULL; + const char *t; + char *r; + + regmatch[0].rm_so = 0; + regmatch[0].rm_eo = end_line(q) - q; + if (regexec(preg, q, MAX_SUBPATTERN, regmatch, REG_STARTEND) != 0) + return found; + + found = q + regmatch[0].rm_so; + *len_F = regmatch[0].rm_eo - regmatch[0].rm_so; + *R = NULL; + + fill_result: + // first pass calculates len_R, second fills R + *len_R = 0; + for (t = Rorig, r = *R; *t; t++) { + size_t len = 1; // default is to copy one char from replace pattern + const char *from = t; + if (*t == '\\') { + from = ++t; // skip backslash + if (*t >= '0' && *t < '0' + MAX_SUBPATTERN) { + cur_match = regmatch + (*t - '0'); + if (cur_match->rm_so >= 0) { + len = cur_match->rm_eo - cur_match->rm_so; + from = q + cur_match->rm_so; + } + } + } + *len_R += len; + if (*R) { + memcpy(r, from, len); + r += len; + /* *r = '\0'; - xzalloc did it */ + } + } + if (*R == NULL) { + *R = xzalloc(*len_R + 1); + goto fill_result; + } + + return found; +} +#endif /* ENABLE_FEATURE_VI_REGEX_SEARCH */ + // buf must be no longer than MAX_INPUT_LEN! static void colon(char *buf) { @@ -3083,6 +3136,14 @@ static void colon(char *buf) int subs = 0; // number of substitutions # if ENABLE_FEATURE_VI_VERBOSE_STATUS int last_line = 0, lines = 0; +# endif +# if ENABLE_FEATURE_VI_REGEX_SEARCH + regex_t preg; + int cflags; + char *Rorig; +# if ENABLE_FEATURE_VI_UNDO + int undo = 0; +# endif # endif // F points to the "find" pattern @@ -3100,7 +3161,6 @@ static void colon(char *buf) *flags++ = '\0'; // terminate "replace" gflag = *flags; } - len_R = strlen(R); if (len_F) { // save "find" as last search pattern free(last_search_pattern); @@ -3122,31 +3182,68 @@ static void colon(char *buf) b = e; } +# if ENABLE_FEATURE_VI_REGEX_SEARCH + Rorig = R; + cflags = 0; + if (ignorecase) + cflags = REG_ICASE; + memset(&preg, 0, sizeof(preg)); + if (regcomp(&preg, F, cflags) != 0) { + status_line(":s bad search pattern"); + goto regex_search_end; + } +# else + len_R = strlen(R); +# endif + for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0 char *ls = q; // orig line start char *found; vc4: +# if ENABLE_FEATURE_VI_REGEX_SEARCH + found = regex_search(q, &preg, Rorig, &len_F, &len_R, &R); +# else found = char_search(q, F, (FORWARD << 1) | LIMITED); // search cur line only for "find" +# endif if (found) { uintptr_t bias; // we found the "find" pattern - delete it // For undo support, the first item should not be chained - text_hole_delete(found, found + len_F - 1, - subs ? ALLOW_UNDO_CHAIN: ALLOW_UNDO); - // can't do this above, no undo => no third argument - subs++; -# if ENABLE_FEATURE_VI_VERBOSE_STATUS - if (last_line != i) { - last_line = i; - ++lines; + // This needs to be handled differently depending on + // whether or not regex support is enabled. +# if ENABLE_FEATURE_VI_REGEX_SEARCH +# define TEST_LEN_F len_F // len_F may be zero +# define TEST_UNDO1 undo++ +# define TEST_UNDO2 undo++ +# else +# define TEST_LEN_F 1 // len_F is never zero +# define TEST_UNDO1 subs +# define TEST_UNDO2 1 +# endif + if (TEST_LEN_F) // match can be empty, no delete needed + text_hole_delete(found, found + len_F - 1, + TEST_UNDO1 ? ALLOW_UNDO_CHAIN: ALLOW_UNDO); + if (len_R) { // insert the "replace" pattern, if required + bias = string_insert(found, R, + TEST_UNDO2 ? ALLOW_UNDO_CHAIN: ALLOW_UNDO); + found += bias; + ls += bias; + dot = ls; + //q += bias; - recalculated anyway } +# if ENABLE_FEATURE_VI_REGEX_SEARCH + free(R); # endif - // insert the "replace" patern - bias = string_insert(found, R, ALLOW_UNDO_CHAIN); - found += bias; - ls += bias; - dot = ls; - //q += bias; - recalculated anyway + if (TEST_LEN_F || len_R) { + dot = ls; + subs++; +# if ENABLE_FEATURE_VI_VERBOSE_STATUS + if (last_line != i) { + last_line = i; + ++lines; + } +# endif + } // check for "global" :s/foo/bar/g if (gflag == 'g') { if ((found + len_R) < end_line(ls)) { @@ -3166,6 +3263,10 @@ static void colon(char *buf) status_line("%d substitutions on %d lines", subs, lines); # endif } +# if ENABLE_FEATURE_VI_REGEX_SEARCH + regex_search_end: + regfree(&preg); +# endif # endif /* FEATURE_VI_SEARCH */ } else if (strncmp(cmd, "version", i) == 0) { // show software version status_line(BB_VER); -- cgit v1.2.3-55-g6feb From 2759201401cf87a9a8621edd9e5f44dfe838407c Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Sat, 10 Jul 2021 11:00:04 +0100 Subject: vi: allow delimiter in ':s' to be escaped When regular expressions are allowed in search commands it becomes possible to escape the delimiter in search/replace commands. For example, this command will replace '/abc' with '/abc/': :s/\/abc/\/abc\//g The code to split the command into 'find' and 'replace' strings should allow for this possibility. VI_REGEX_SEARCH isn't enabled by default. When it is: function old new delta strchr_backslash - 38 +38 colon 4378 4373 -5 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 0/1 up/down: 38/-5) Total: 33 bytes Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- editors/vi.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/editors/vi.c b/editors/vi.c index 070e0f55a..c6bb74cfb 100644 --- a/editors/vi.c +++ b/editors/vi.c @@ -2680,6 +2680,19 @@ static char *expand_args(char *args) #if ENABLE_FEATURE_VI_REGEX_SEARCH # define MAX_SUBPATTERN 10 // subpatterns \0 .. \9 +// Like strchr() but skipping backslash-escaped characters +static char *strchr_backslash(const char *s, int c) +{ + for (; *s; ++s) { + if (*s == c) { + return (char *)s; + } else if (*s == '\\' && *++s == '\0') { + break; + } + } + return NULL; +} + // If the return value is not NULL the caller should free R static char *regex_search(char *q, regex_t *preg, const char *Rorig, size_t *len_F, size_t *len_R, char **R) @@ -2728,6 +2741,8 @@ static char *regex_search(char *q, regex_t *preg, const char *Rorig, return found; } +#else /* !ENABLE_FEATURE_VI_REGEX_SEARCH */ +# define strchr_backslash(s, c) strchr(s, c) #endif /* ENABLE_FEATURE_VI_REGEX_SEARCH */ // buf must be no longer than MAX_INPUT_LEN! @@ -3151,12 +3166,12 @@ static void colon(char *buf) // replace the cmd line delimiters "/" with NULs c = buf[1]; // what is the delimiter F = buf + 2; // start of "find" - R = strchr(F, c); // middle delimiter + R = strchr_backslash(F, c); // middle delimiter if (!R) goto colon_s_fail; len_F = R - F; *R++ = '\0'; // terminate "find" - flags = strchr(R, c); + flags = strchr_backslash(R, c); if (flags) { *flags++ = '\0'; // terminate "replace" gflag = *flags; -- cgit v1.2.3-55-g6feb From 36feb2682481d55ae49df899c38e16130227ba4a Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 13 Jul 2021 16:16:21 +0200 Subject: vi: somewhat more readable code, no logic changes Signed-off-by: Denys Vlasenko --- editors/vi.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/editors/vi.c b/editors/vi.c index c6bb74cfb..5c601c759 100644 --- a/editors/vi.c +++ b/editors/vi.c @@ -2683,12 +2683,13 @@ static char *expand_args(char *args) // Like strchr() but skipping backslash-escaped characters static char *strchr_backslash(const char *s, int c) { - for (; *s; ++s) { + while (*s) { if (*s == c) { return (char *)s; - } else if (*s == '\\' && *++s == '\0') { - break; - } + if (*s == '\\') + if (*++s == '\0') + break; + s++; } return NULL; } @@ -3237,10 +3238,10 @@ static void colon(char *buf) # endif if (TEST_LEN_F) // match can be empty, no delete needed text_hole_delete(found, found + len_F - 1, - TEST_UNDO1 ? ALLOW_UNDO_CHAIN: ALLOW_UNDO); - if (len_R) { // insert the "replace" pattern, if required + TEST_UNDO1 ? ALLOW_UNDO_CHAIN : ALLOW_UNDO); + if (len_R != 0) { // insert the "replace" pattern, if required bias = string_insert(found, R, - TEST_UNDO2 ? ALLOW_UNDO_CHAIN: ALLOW_UNDO); + TEST_UNDO2 ? ALLOW_UNDO_CHAIN : ALLOW_UNDO); found += bias; ls += bias; dot = ls; @@ -3249,7 +3250,7 @@ static void colon(char *buf) # if ENABLE_FEATURE_VI_REGEX_SEARCH free(R); # endif - if (TEST_LEN_F || len_R) { + if (TEST_LEN_F || len_R != 0) { dot = ls; subs++; # if ENABLE_FEATURE_VI_VERBOSE_STATUS -- cgit v1.2.3-55-g6feb From e6f4145f2961bfd500214ef1fcf07543ffacb603 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Tue, 13 Jul 2021 16:35:43 +0100 Subject: vi: fix regex search compilation error Building with FEATURE_VI_REGEX_SEARCH enabled fails. Signed-off-by: Ron Yorston Signed-off-by: Bernhard Reutner-Fischer --- editors/vi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editors/vi.c b/editors/vi.c index 5c601c759..a4b958734 100644 --- a/editors/vi.c +++ b/editors/vi.c @@ -2684,7 +2684,7 @@ static char *expand_args(char *args) static char *strchr_backslash(const char *s, int c) { while (*s) { - if (*s == c) { + if (*s == c) return (char *)s; if (*s == '\\') if (*++s == '\0') -- cgit v1.2.3-55-g6feb From d62627487a44d9175b05d49846aeef83fed97019 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 14 Jul 2021 14:25:07 +0200 Subject: awk: tighten parsing - disallow extra semicolons '; BEGIN {...}' and 'BEGIN {...} ;; {...}' are not accepted by gawk function old new delta parse_program 332 353 +21 Signed-off-by: Denys Vlasenko --- editors/awk.c | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/editors/awk.c b/editors/awk.c index 7a282356d..2f8a18c8e 100644 --- a/editors/awk.c +++ b/editors/awk.c @@ -1634,7 +1634,7 @@ static void chain_group(void) debug_printf_parse("%s: ST_FOR\n", __func__); next_token(TC_LPAREN); n2 = parse_expr(TC_SEMICOL | TC_RPAREN); - if (t_tclass & TC_RPAREN) { /* for-in */ + if (t_tclass & TC_RPAREN) { /* for (I in ARRAY) */ if (!n2 || n2->info != TI_IN) syntax_error(EMSG_UNEXP_TOKEN); n = chain_node(OC_WALKINIT | VV); @@ -1700,20 +1700,15 @@ static void parse_program(char *p) for (;;) { uint32_t tclass; - tclass = next_token(TC_EOF | TS_OPSEQ | TC_LBRACE | - TC_SEMICOL | TC_NEWLINE | TC_BEGIN | TC_END | TC_FUNCDECL); - + tclass = next_token(TS_OPSEQ | TC_LBRACE | TC_BEGIN | TC_END | TC_FUNCDECL + | TC_EOF | TC_NEWLINE /* but not TC_SEMICOL */); + got_tok: if (tclass == TC_EOF) { debug_printf_parse("%s: TC_EOF\n", __func__); break; } - if (tclass & (TC_SEMICOL | TC_NEWLINE)) { - debug_printf_parse("%s: TC_SEMICOL | TC_NEWLINE\n", __func__); -//NB: gawk allows many newlines, but does not allow more than one semicolon: -// BEGIN {...};; -//would complain "each rule must have a pattern or an action part". -//Same message for -// ; BEGIN {...} + if (tclass == TC_NEWLINE) { + debug_printf_parse("%s: TC_NEWLINE\n", __func__); continue; } if (tclass == TC_BEGIN) { @@ -1722,7 +1717,7 @@ static void parse_program(char *p) /* ensure there is no newline between BEGIN and { */ next_token(TC_LBRACE); chain_until_rbrace(); - continue; + goto next_tok; } if (tclass == TC_END) { debug_printf_parse("%s: TC_END\n", __func__); @@ -1730,7 +1725,7 @@ static void parse_program(char *p) /* ensure there is no newline between END and { */ next_token(TC_LBRACE); chain_until_rbrace(); - continue; + goto next_tok; } if (tclass == TC_FUNCDECL) { func *f; @@ -1765,7 +1760,7 @@ static void parse_program(char *p) continue; chain_until_rbrace(); hash_clear(ahash); - continue; + goto next_tok; } seq = &mainseq; if (tclass & TS_OPSEQ) { @@ -1784,12 +1779,25 @@ static void parse_program(char *p) chain_node(OC_PRINT); } cn->r.n = mainseq.last; - continue; + goto next_tok; } /* tclass == TC_LBRACE */ debug_printf_parse("%s: TC_LBRACE(?)\n", __func__); chain_until_rbrace(); - } + next_tok: + /* Same as next_token() at the top of the loop, + TC_SEMICOL */ + tclass = next_token(TS_OPSEQ | TC_LBRACE | TC_BEGIN | TC_END | TC_FUNCDECL + | TC_EOF | TC_NEWLINE | TC_SEMICOL); + /* gawk allows many newlines, but does not allow more than one semicolon: + * BEGIN {...};; + * would complain "each rule must have a pattern or an action part". + * Same message for + * ; BEGIN {...} + */ + if (tclass != TC_SEMICOL) + goto got_tok; /* use this token */ + /* else: loop back - ate the semicolon, get and use _next_ token */ + } /* for (;;) */ } -- cgit v1.2.3-55-g6feb From d3480dd58211d9d8c06ec7ef00089262603003ff Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 14 Jul 2021 14:33:37 +0200 Subject: awk: disallow break/continue outside of loops function old new delta .rodata 104139 104186 +47 chain_group 610 633 +23 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/0 up/down: 70/0) Total: 70 bytes Signed-off-by: Denys Vlasenko --- editors/awk.c | 6 ++++-- testsuite/awk.tests | 9 ++------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/editors/awk.c b/editors/awk.c index 2f8a18c8e..607d69487 100644 --- a/editors/awk.c +++ b/editors/awk.c @@ -1671,16 +1671,18 @@ static void chain_group(void) case OC_BREAK: debug_printf_parse("%s: OC_BREAK\n", __func__); n = chain_node(OC_EXEC); + if (!break_ptr) + syntax_error("'break' not in a loop"); n->a.n = break_ptr; -//TODO: if break_ptr is NULL, syntax error (not in the loop)? chain_expr(t_info); break; case OC_CONTINUE: debug_printf_parse("%s: OC_CONTINUE\n", __func__); n = chain_node(OC_EXEC); + if (!continue_ptr) + syntax_error("'continue' not in a loop"); n->a.n = continue_ptr; -//TODO: if continue_ptr is NULL, syntax error (not in the loop)? chain_expr(t_info); break; diff --git a/testsuite/awk.tests b/testsuite/awk.tests index 3cddb4dd4..f53b1efe2 100755 --- a/testsuite/awk.tests +++ b/testsuite/awk.tests @@ -379,19 +379,14 @@ testing "awk -e and ARGC" \ "" SKIP= -# The examples are in fact not valid awk programs (break/continue -# can only be used inside loops). -# But we do accept them outside of loops. -# We had a bug with misparsing "break ; else" sequence. -# Test that *that* bug is fixed, using simplest possible scripts: testing "awk break" \ "awk -f - 2>&1; echo \$?" \ - "0\n" \ + "awk: -:1: 'break' not in a loop\n1\n" \ "" \ 'BEGIN { if (1) break; else a = 1 }' testing "awk continue" \ "awk -f - 2>&1; echo \$?" \ - "0\n" \ + "awk: -:1: 'continue' not in a loop\n1\n" \ "" \ 'BEGIN { if (1) continue; else a = 1 }' -- cgit v1.2.3-55-g6feb From 95fffd8a7fd49637d7b6d9f25b69ac6d8c9a2fff Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 14 Jul 2021 16:28:43 +0200 Subject: vi: remove redundant assignment Signed-off-by: Denys Vlasenko --- editors/vi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/editors/vi.c b/editors/vi.c index a4b958734..23a44d597 100644 --- a/editors/vi.c +++ b/editors/vi.c @@ -3244,7 +3244,6 @@ static void colon(char *buf) TEST_UNDO2 ? ALLOW_UNDO_CHAIN : ALLOW_UNDO); found += bias; ls += bias; - dot = ls; //q += bias; - recalculated anyway } # if ENABLE_FEATURE_VI_REGEX_SEARCH -- cgit v1.2.3-55-g6feb From dabbeeb79356eef78528acd55e1f143ae80372f7 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 14 Jul 2021 16:58:05 +0200 Subject: awk: whitespace and debugging tweaks Signed-off-by: Denys Vlasenko --- editors/awk.c | 133 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 66 insertions(+), 67 deletions(-) diff --git a/editors/awk.c b/editors/awk.c index 607d69487..3adbca7aa 100644 --- a/editors/awk.c +++ b/editors/awk.c @@ -199,77 +199,78 @@ typedef struct tsplitter_s { /* simple token classes */ /* order and hex values are very important!!! See next_token() */ -#define TC_LPAREN (1 << 0) /* ( */ -#define TC_RPAREN (1 << 1) /* ) */ -#define TC_REGEXP (1 << 2) /* /.../ */ -#define TC_OUTRDR (1 << 3) /* | > >> */ -#define TC_UOPPOST (1 << 4) /* unary postfix operator ++ -- */ -#define TC_UOPPRE1 (1 << 5) /* unary prefix operator ++ -- $ */ -#define TC_BINOPX (1 << 6) /* two-opnd operator */ -#define TC_IN (1 << 7) /* 'in' */ -#define TC_COMMA (1 << 8) /* , */ -#define TC_PIPE (1 << 9) /* input redirection pipe | */ -#define TC_UOPPRE2 (1 << 10) /* unary prefix operator + - ! */ -#define TC_ARRTERM (1 << 11) /* ] */ -#define TC_LBRACE (1 << 12) /* { */ -#define TC_RBRACE (1 << 13) /* } */ -#define TC_SEMICOL (1 << 14) /* ; */ -#define TC_NEWLINE (1 << 15) -#define TC_STATX (1 << 16) /* ctl statement (for, next...) */ -#define TC_WHILE (1 << 17) /* 'while' */ -#define TC_ELSE (1 << 18) /* 'else' */ -#define TC_BUILTIN (1 << 19) +#define TC_LPAREN (1 << 0) /* ( */ +#define TC_RPAREN (1 << 1) /* ) */ +#define TC_REGEXP (1 << 2) /* /.../ */ +#define TC_OUTRDR (1 << 3) /* | > >> */ +#define TC_UOPPOST (1 << 4) /* unary postfix operator ++ -- */ +#define TC_UOPPRE1 (1 << 5) /* unary prefix operator ++ -- $ */ +#define TC_BINOPX (1 << 6) /* two-opnd operator */ +#define TC_IN (1 << 7) /* 'in' */ +#define TC_COMMA (1 << 8) /* , */ +#define TC_PIPE (1 << 9) /* input redirection pipe | */ +#define TC_UOPPRE2 (1 << 10) /* unary prefix operator + - ! */ +#define TC_ARRTERM (1 << 11) /* ] */ +#define TC_LBRACE (1 << 12) /* { */ +#define TC_RBRACE (1 << 13) /* } */ +#define TC_SEMICOL (1 << 14) /* ; */ +#define TC_NEWLINE (1 << 15) +#define TC_STATX (1 << 16) /* ctl statement (for, next...) */ +#define TC_WHILE (1 << 17) /* 'while' */ +#define TC_ELSE (1 << 18) /* 'else' */ +#define TC_BUILTIN (1 << 19) /* 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) /* 'length' */ -#define TC_GETLINE (1 << 21) /* 'getline' */ -#define TC_FUNCDECL (1 << 22) /* 'function' 'func' */ -#define TC_BEGIN (1 << 23) /* 'BEGIN' */ -#define TC_END (1 << 24) /* 'END' */ -#define TC_EOF (1 << 25) -#define TC_VARIABLE (1 << 26) /* name */ -#define TC_ARRAY (1 << 27) /* name[ */ -#define TC_FUNCTION (1 << 28) /* name( */ -#define TC_STRING (1 << 29) /* "..." */ -#define TC_NUMBER (1 << 30) +#define TC_LENGTH (1 << 20) /* 'length' */ +#define TC_GETLINE (1 << 21) /* 'getline' */ +#define TC_FUNCDECL (1 << 22) /* 'function' 'func' */ +#define TC_BEGIN (1 << 23) /* 'BEGIN' */ +#define TC_END (1 << 24) /* 'END' */ +#define TC_EOF (1 << 25) +#define TC_VARIABLE (1 << 26) /* name */ +#define TC_ARRAY (1 << 27) /* name[ */ +#define TC_FUNCTION (1 << 28) /* name( */ +#define TC_STRING (1 << 29) /* "..." */ +#define TC_NUMBER (1 << 30) #ifndef debug_parse_print_tc -#define debug_parse_print_tc(n) do { \ -if ((n) & TC_LPAREN ) debug_printf_parse(" LPAREN" ); \ -if ((n) & TC_RPAREN ) debug_printf_parse(" RPAREN" ); \ -if ((n) & TC_REGEXP ) debug_printf_parse(" REGEXP" ); \ -if ((n) & TC_OUTRDR ) debug_printf_parse(" OUTRDR" ); \ -if ((n) & TC_UOPPOST ) debug_printf_parse(" UOPPOST" ); \ -if ((n) & TC_UOPPRE1 ) debug_printf_parse(" UOPPRE1" ); \ -if ((n) & TC_BINOPX ) debug_printf_parse(" BINOPX" ); \ -if ((n) & TC_IN ) debug_printf_parse(" IN" ); \ -if ((n) & TC_COMMA ) debug_printf_parse(" COMMA" ); \ -if ((n) & TC_PIPE ) debug_printf_parse(" PIPE" ); \ -if ((n) & TC_UOPPRE2 ) debug_printf_parse(" UOPPRE2" ); \ -if ((n) & TC_ARRTERM ) debug_printf_parse(" ARRTERM" ); \ -if ((n) & TC_LBRACE ) debug_printf_parse(" LBRACE" ); \ -if ((n) & TC_RBRACE ) debug_printf_parse(" RBRACE" ); \ -if ((n) & TC_SEMICOL ) debug_printf_parse(" SEMICOL" ); \ -if ((n) & TC_NEWLINE ) debug_printf_parse(" NEWLINE" ); \ -if ((n) & TC_STATX ) debug_printf_parse(" STATX" ); \ -if ((n) & TC_WHILE ) debug_printf_parse(" WHILE" ); \ -if ((n) & TC_ELSE ) debug_printf_parse(" ELSE" ); \ -if ((n) & TC_BUILTIN ) debug_printf_parse(" BUILTIN" ); \ -if ((n) & TC_LENGTH ) debug_printf_parse(" LENGTH" ); \ -if ((n) & TC_GETLINE ) debug_printf_parse(" GETLINE" ); \ -if ((n) & TC_FUNCDECL) debug_printf_parse(" FUNCDECL"); \ -if ((n) & TC_BEGIN ) debug_printf_parse(" BEGIN" ); \ -if ((n) & TC_END ) debug_printf_parse(" END" ); \ -if ((n) & TC_EOF ) debug_printf_parse(" EOF" ); \ -if ((n) & TC_VARIABLE) debug_printf_parse(" VARIABLE"); \ -if ((n) & TC_ARRAY ) debug_printf_parse(" ARRAY" ); \ -if ((n) & TC_FUNCTION) debug_printf_parse(" FUNCTION"); \ -if ((n) & TC_STRING ) debug_printf_parse(" STRING" ); \ -if ((n) & TC_NUMBER ) debug_printf_parse(" NUMBER" ); \ -} while (0) +static void debug_parse_print_tc(uint32_t n) +{ + if (n & TC_LPAREN ) debug_printf_parse(" LPAREN" ); + if (n & TC_RPAREN ) debug_printf_parse(" RPAREN" ); + if (n & TC_REGEXP ) debug_printf_parse(" REGEXP" ); + if (n & TC_OUTRDR ) debug_printf_parse(" OUTRDR" ); + if (n & TC_UOPPOST ) debug_printf_parse(" UOPPOST" ); + if (n & TC_UOPPRE1 ) debug_printf_parse(" UOPPRE1" ); + if (n & TC_BINOPX ) debug_printf_parse(" BINOPX" ); + if (n & TC_IN ) debug_printf_parse(" IN" ); + if (n & TC_COMMA ) debug_printf_parse(" COMMA" ); + if (n & TC_PIPE ) debug_printf_parse(" PIPE" ); + if (n & TC_UOPPRE2 ) debug_printf_parse(" UOPPRE2" ); + if (n & TC_ARRTERM ) debug_printf_parse(" ARRTERM" ); + if (n & TC_LBRACE ) debug_printf_parse(" LBRACE" ); + if (n & TC_RBRACE ) debug_printf_parse(" RBRACE" ); + if (n & TC_SEMICOL ) debug_printf_parse(" SEMICOL" ); + if (n & TC_NEWLINE ) debug_printf_parse(" NEWLINE" ); + if (n & TC_STATX ) debug_printf_parse(" STATX" ); + if (n & TC_WHILE ) debug_printf_parse(" WHILE" ); + if (n & TC_ELSE ) debug_printf_parse(" ELSE" ); + if (n & TC_BUILTIN ) debug_printf_parse(" BUILTIN" ); + if (n & TC_LENGTH ) debug_printf_parse(" LENGTH" ); + if (n & TC_GETLINE ) debug_printf_parse(" GETLINE" ); + if (n & TC_FUNCDECL) debug_printf_parse(" FUNCDECL"); + if (n & TC_BEGIN ) debug_printf_parse(" BEGIN" ); + if (n & TC_END ) debug_printf_parse(" END" ); + if (n & TC_EOF ) debug_printf_parse(" EOF" ); + if (n & TC_VARIABLE) debug_printf_parse(" VARIABLE"); + if (n & TC_ARRAY ) debug_printf_parse(" ARRAY" ); + if (n & TC_FUNCTION) debug_printf_parse(" FUNCTION"); + if (n & TC_STRING ) debug_printf_parse(" STRING" ); + if (n & TC_NUMBER ) debug_printf_parse(" NUMBER" ); +} #endif /* combined token classes ("token [class] sets") */ @@ -417,7 +418,7 @@ static const char tokenlist[] ALIGN1 = "\5close" "\6system" "\6fflush" "\5atan2" "\3cos" "\3exp" "\3int" "\3log" "\4rand" "\3sin" "\4sqrt" "\5srand" - "\6gensub" "\4gsub" "\5index" /* "\6length" was here */ + "\6gensub" "\4gsub" "\5index" /* "\6length" was here */ "\5match" "\5split" "\7sprintf" "\3sub" "\6substr" "\7systime" "\10strftime" "\6mktime" "\7tolower" "\7toupper" NTC @@ -1802,7 +1803,6 @@ static void parse_program(char *p) } /* for (;;) */ } - /* -------- program execution part -------- */ /* temporary variables allocator */ @@ -3510,7 +3510,6 @@ static var *evaluate(node *op, var *res) #undef sreg } - /* -------- main & co. -------- */ static int awk_exit(void) -- cgit v1.2.3-55-g6feb