From 558ef4962dd3adb9e63b4c0787f8d4902fc909d4 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 7 Aug 2025 00:00:03 +0200 Subject: hush: optimization: do not glob words "[" and "[[" function old new delta glob_needed 71 86 +15 Signed-off-by: Denys Vlasenko --- shell/hush.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index 09ab6ebc0..6129a087b 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -115,6 +115,11 @@ //config:# It's only needed to get "nice" menuconfig indenting. //config:if SHELL_HUSH || HUSH || SH_IS_HUSH || BASH_IS_HUSH //config: +//config:config HUSH_NEED_FOR_SPEED +//config: bool "Faster, but larger code" +//config: default y +//config: depends on SHELL_HUSH +//config: //config:config HUSH_BASH_COMPAT //config: bool "bash-compatible extensions" //config: default y @@ -3381,6 +3386,24 @@ static int o_get_last_ptr(o_string *o, int n) /* Helper */ static int glob_needed(const char *s) { +# if ENABLE_HUSH_NEED_FOR_SPEED + char c = *s; + if (c == '[' /*|| c == '{'*/) { + /* Special-case words consisting entirely of [. + * Optimization to avoid glob() + * on "[ COND ]" and "[[ COND ]]": + * strace hush -c 'i=0; while [ $((++i)) != 50000 ]; do :; done' + * shouldn't be doing 50000 stat("["). + * (Can do it for "{" too, but it's not a common case). + */ + const char *p = s; + while (*++p == c) + continue; + if (*p == '\0') + return 0; + } +# endif + while (*s) { if (*s == '\\') { if (!s[1]) @@ -3572,6 +3595,23 @@ static int perform_glob(o_string *o, int n) /* Helper */ static int glob_needed(const char *s) { +# if ENABLE_HUSH_NEED_FOR_SPEED + char c = *s; + if (c == '[') { + /* Special-case words consisting entirely of [. + * Optimization to avoid glob() + * on "[ COND ]" and "[[ COND ]]": + * strace hush -c 'i=0; while [ $((++i)) != 50000 ]; do :; done' + * shouldn't be doing 50000 stat("["). + */ + const char *p = s; + while (*++p == c) + continue; + if (*p == '\0') + return 0; + } +# endif + while (*s) { if (*s == '\\') { if (!s[1]) -- cgit v1.2.3-55-g6feb From 7648dc721e390bba5049e798d73e70c6180b366b Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 7 Aug 2025 01:30:57 +0200 Subject: ash,hush: fix corner cases with backslash-newlines in heredocs function old new delta fetch_heredocs 477 485 +8 Signed-off-by: Denys Vlasenko --- shell/ash.c | 7 ++++--- shell/ash_test/ash-heredoc/heredoc_bkslash_newline2a.right | 1 + shell/ash_test/ash-heredoc/heredoc_bkslash_newline2a.tests | 4 ++++ shell/ash_test/ash-heredoc/heredoc_bkslash_newline3.right | 1 + shell/ash_test/ash-heredoc/heredoc_bkslash_newline3.tests | 4 ++++ shell/ash_test/ash-heredoc/heredoc_bkslash_newline3a.right | 1 + shell/ash_test/ash-heredoc/heredoc_bkslash_newline3a.tests | 4 ++++ shell/hush.c | 12 ++++++++++-- shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.right | 1 + shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.tests | 4 ++++ shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.right | 1 + shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.tests | 4 ++++ shell/hush_test/hush-heredoc/heredoc_bkslash_newline3a.right | 1 + shell/hush_test/hush-heredoc/heredoc_bkslash_newline3a.tests | 4 ++++ 14 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 shell/ash_test/ash-heredoc/heredoc_bkslash_newline2a.right create mode 100755 shell/ash_test/ash-heredoc/heredoc_bkslash_newline2a.tests create mode 100644 shell/ash_test/ash-heredoc/heredoc_bkslash_newline3.right create mode 100755 shell/ash_test/ash-heredoc/heredoc_bkslash_newline3.tests create mode 100644 shell/ash_test/ash-heredoc/heredoc_bkslash_newline3a.right create mode 100755 shell/ash_test/ash-heredoc/heredoc_bkslash_newline3a.tests create mode 100644 shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.right create mode 100755 shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.tests create mode 100644 shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.right create mode 100755 shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.tests create mode 100644 shell/hush_test/hush-heredoc/heredoc_bkslash_newline3a.right create mode 100755 shell/hush_test/hush-heredoc/heredoc_bkslash_newline3a.tests (limited to 'shell') diff --git a/shell/ash.c b/shell/ash.c index 9cacdff64..bfd9aa53e 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -12735,21 +12735,22 @@ checkend: { if (striptabs) { while (c == '\t') - c = pgetc(); + c = pgetc_eatbnl(); /* dash does pgetc() */ + /* (see heredoc_bkslash_newline3a.tests) */ } markloc = out - (char *)stackblock(); for (p = eofmark; STPUTC(c, out), *p; p++) { if (c != *p) goto more_heredoc; - /* FIXME: fails for backslash-newlined terminator: + /* dash still has this not fixed (as of 2025-08) * cat <\ + * EOF + */ + ch = i_getch_and_eat_bkslash_nl(input); + } if (ch != EOF) nommu_addchr(as_string, ch); } while ((heredoc_flags & HEREDOC_SKIPTABS) && ch == '\t'); @@ -4665,7 +4673,7 @@ static char *fetch_till_str(o_string *as_string, prev = 0; /* not '\' */ continue; } - } + } /* if (\n or EOF) */ if (ch == EOF) { o_free(&heredoc); return NULL; /* error */ diff --git a/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.right b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.right new file mode 100644 index 000000000..3d79316d7 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.right @@ -0,0 +1 @@ +Ok1 diff --git a/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.tests b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.tests new file mode 100755 index 000000000..eb2223031 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline2a.tests @@ -0,0 +1,4 @@ +cat <<-EOF + Ok1 + EO\ +F diff --git a/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.right b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.right new file mode 100644 index 000000000..3d79316d7 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.right @@ -0,0 +1 @@ +Ok1 diff --git a/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.tests b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.tests new file mode 100755 index 000000000..de21132d1 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline3.tests @@ -0,0 +1,4 @@ +cat < Date: Thu, 7 Aug 2025 02:26:25 +0200 Subject: ash: fix heredoc.tests broken by last commit function old new delta readtoken1 3053 3095 +42 xxreadtoken 215 212 -3 expandstr 255 252 -3 parseheredoc 148 127 -21 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/3 up/down: 42/-27) Total: 15 bytes Signed-off-by: Denys Vlasenko --- shell/ash.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) (limited to 'shell') diff --git a/shell/ash.c b/shell/ash.c index bfd9aa53e..4b235d2c9 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -12454,12 +12454,12 @@ decode_dollar_squote(void) #endif /* Used by expandstr to get here-doc like behaviour. */ -#define FAKEEOFMARK ((char*)(uintptr_t)1) +#define FAKEEOFMARK ((struct heredoc*)(uintptr_t)1) static ALWAYS_INLINE int -realeofmark(const char *eofmark) +realeofmark(struct heredoc *here) { - return eofmark && eofmark != FAKEEOFMARK; + return here && here != FAKEEOFMARK; } /* @@ -12481,7 +12481,7 @@ realeofmark(const char *eofmark) #define PARSEPROCSUB() {style = PSUB; goto parsebackq; parsebackq_psreturn:;} #define PARSEARITH() {goto parsearith; parsearith_return:;} static int -readtoken1(int c, int syntax, char *eofmark, int striptabs) +readtoken1(int c, int syntax, struct heredoc *eofmark) { /* NB: syntax parameter fits into smallint */ /* c parameter is an unsigned char or PEOF */ @@ -12733,14 +12733,17 @@ checkend: { int markloc; char *p; - if (striptabs) { + if (eofmark->striptabs) { while (c == '\t') - c = pgetc_eatbnl(); /* dash does pgetc() */ + if (eofmark->here->type == NHERE) + c = pgetc(); /* dash always does pgetc() */ + else /* NXHERE */ + c = pgetc_eatbnl(); /* (see heredoc_bkslash_newline3a.tests) */ } markloc = out - (char *)stackblock(); - for (p = eofmark; STPUTC(c, out), *p; p++) { + for (p = eofmark->eofmark; STPUTC(c, out), *p; p++) { if (c != *p) goto more_heredoc; /* dash still has this not fixed (as of 2025-08) @@ -12750,7 +12753,10 @@ checkend: { * F * (see heredoc_bkslash_newline2.tests) */ - c = pgetc_eatbnl(); /* dash does pgetc() */ + if (eofmark->here->type == NHERE) + c = pgetc(); /* dash always does pgetc() */ + else /* NXHERE */ + c = pgetc_eatbnl(); } if (c == '\n' || c == PEOF) { @@ -12760,7 +12766,6 @@ checkend: { needprompt = doprompt; } else { int len_here; - more_heredoc: p = (char *)stackblock() + markloc + 1; len_here = out - p; @@ -13269,7 +13274,7 @@ xxreadtoken(void) } } /* for (;;) */ - return readtoken1(c, BASESYNTAX, (char *) NULL, 0); + return readtoken1(c, BASESYNTAX, NULL); } #else /* old xxreadtoken */ #define RETURN(token) return lasttoken = token @@ -13320,7 +13325,7 @@ xxreadtoken(void) } break; } - return readtoken1(c, BASESYNTAX, (char *)NULL, 0); + return readtoken1(c, BASESYNTAX, NULL); #undef RETURN } #endif /* old xxreadtoken */ @@ -13426,9 +13431,9 @@ parseheredoc(void) tokpushback = 0; setprompt_if(needprompt, 2); if (here->here->type == NHERE) - readtoken1(pgetc(), SQSYNTAX, here->eofmark, here->striptabs); + readtoken1(pgetc(), SQSYNTAX, here); else - readtoken1(pgetc_eatbnl(), DQSYNTAX, here->eofmark, here->striptabs); + readtoken1(pgetc_eatbnl(), DQSYNTAX, here); n = stzalloc(sizeof(struct narg)); n->narg.type = NARG; /*n->narg.next = NULL; - stzalloc did it */ @@ -13471,8 +13476,7 @@ expandstr(const char *ps, int syntax_type) * PS1='$(date "+%H:%M:%S) > ' */ exception_handler = &jmploc; - readtoken1(pgetc_eatbnl(), syntax_type, FAKEEOFMARK, 0); - + readtoken1(pgetc_eatbnl(), syntax_type, FAKEEOFMARK); n.narg.type = NARG; n.narg.next = NULL; n.narg.text = wordtext; -- cgit v1.2.3-55-g6feb From 34948f18587979bee89c13267f45b2333721ca6e Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 7 Aug 2025 02:46:04 +0200 Subject: hush: smarter optimization for not-globbing [ and [[ function old new delta o_save_ptr 176 167 -9 Signed-off-by: Denys Vlasenko --- shell/hush.c | 60 +++++++++++++++++++++++------------------------------------- 1 file changed, 23 insertions(+), 37 deletions(-) (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index 0b8da5e2d..2cceef25a 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -3386,24 +3386,6 @@ static int o_get_last_ptr(o_string *o, int n) /* Helper */ static int glob_needed(const char *s) { -# if ENABLE_HUSH_NEED_FOR_SPEED - char c = *s; - if (c == '[' /*|| c == '{'*/) { - /* Special-case words consisting entirely of [. - * Optimization to avoid glob() - * on "[ COND ]" and "[[ COND ]]": - * strace hush -c 'i=0; while [ $((++i)) != 50000 ]; do :; done' - * shouldn't be doing 50000 stat("["). - * (Can do it for "{" too, but it's not a common case). - */ - const char *p = s; - while (*++p == c) - continue; - if (*p == '\0') - return 0; - } -# endif - while (*s) { if (*s == '\\') { if (!s[1]) @@ -3411,7 +3393,19 @@ static int glob_needed(const char *s) s += 2; continue; } - if (*s == '*' || *s == '[' || *s == '?' || *s == '{') + if (*s == '*' || *s == '?') + return 1; + /* Only force glob if "..[..].." detected. + * Not merely "[", "[[", "][" etc. + * Optimization to avoid glob() + * on "[ COND ]" and "[[ COND ]]": + * strace hush -c 'i=0; while [ $((++i)) != 50000 ]; do :; done' + * shouldn't be doing 50000 stat("["). + * (Can do it for "{" too, but it's not a common case). + */ + if (*s == '[' && strchr(s+1, ']')) + return 1; + if (*s == '{' /* && strchr(s+1, '}')*/) return 1; s++; } @@ -3595,23 +3589,6 @@ static int perform_glob(o_string *o, int n) /* Helper */ static int glob_needed(const char *s) { -# if ENABLE_HUSH_NEED_FOR_SPEED - char c = *s; - if (c == '[') { - /* Special-case words consisting entirely of [. - * Optimization to avoid glob() - * on "[ COND ]" and "[[ COND ]]": - * strace hush -c 'i=0; while [ $((++i)) != 50000 ]; do :; done' - * shouldn't be doing 50000 stat("["). - */ - const char *p = s; - while (*++p == c) - continue; - if (*p == '\0') - return 0; - } -# endif - while (*s) { if (*s == '\\') { if (!s[1]) @@ -3619,7 +3596,16 @@ static int glob_needed(const char *s) s += 2; continue; } - if (*s == '*' || *s == '[' || *s == '?') + if (*s == '*' || *s == '?') + return 1; + /* Only force glob if "..[..].." detected. + * Not merely "[", "[[", "][" etc. + * Optimization to avoid glob() + * on "[ COND ]" and "[[ COND ]]": + * strace hush -c 'i=0; while [ $((++i)) != 50000 ]; do :; done' + * shouldn't be doing 50000 stat("["). + */ + if (*s == '[' && strchr(s+1, ']')) return 1; s++; } -- cgit v1.2.3-55-g6feb From 422f4ede6f6c83e1d0693c43e5da1d1b070588aa Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 7 Aug 2025 23:41:35 +0200 Subject: ash: fix cmdputs - was showing some variable forms incorrectly function old new delta cmdputs 402 418 +16 static.vstype 48 42 -6 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/1 up/down: 16/-6) Total: 10 bytes Signed-off-by: Denys Vlasenko --- shell/ash.c | 55 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 20 deletions(-) (limited to 'shell') diff --git a/shell/ash.c b/shell/ash.c index 4b235d2c9..0f8c2a66a 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -867,6 +867,7 @@ out2str(const char *p) #define VSTRIMLEFT 0x8 /* ${var#pattern} */ #define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */ #define VSLENGTH 0xa /* ${#var} */ +// unused yet 0xb /* unused */ #if BASH_SUBSTR #define VSSUBSTR 0xc /* ${var:position:length} */ #endif @@ -4829,13 +4830,25 @@ static char *cmdnextc; static void cmdputs(const char *s) { - static const char vstype[VSTYPE + 1][3] ALIGN1 = { - "", "}", "-", "+", "?", "=", - "%", "%%", "#", "##" - IF_BASH_SUBSTR(, ":") - IF_BASH_PATTERN_SUBST(, "/", "//") + static const char vstype[][3] ALIGN1 = { + [VSNORMAL - VSNORMAL] = "}", // $var or ${var} + [VSMINUS - VSNORMAL] = "-", // ${var-text} + [VSPLUS - VSNORMAL] = "+", // ${var+text} + [VSQUESTION - VSNORMAL] = "?", // ${var?message} + [VSASSIGN - VSNORMAL] = "=", // ${var=text} + [VSTRIMRIGHT - VSNORMAL] = "%", // ${var%pattern} + [VSTRIMRIGHTMAX - VSNORMAL] = "%%",// ${var%%pattern} + [VSTRIMLEFT - VSNORMAL] = "#", // ${var#pattern} + [VSTRIMLEFTMAX - VSNORMAL] = "##",// ${var##pattern} + [VSLENGTH - VSNORMAL] = "", // ${#var} +#if BASH_SUBSTR + [VSSUBSTR - VSNORMAL] = ":", // ${var:position:length} +#endif +#if BASH_PATTERN_SUBST + [VSREPLACE - VSNORMAL] = "/", // ${var/pattern/replacement} + [VSREPLACEALL - VSNORMAL] = "//",// ${var//pattern/replacement} +#endif }; - const char *p, *str; char cc[2]; char *nextc; @@ -4898,32 +4911,34 @@ cmdputs(const char *s) case '=': if (subtype == 0) break; + /* We are in variable name */ if ((subtype & VSTYPE) != VSNORMAL) quoted <<= 1; - str = vstype[subtype & VSTYPE]; - if (subtype & VSNUL) - c = ':'; - else - goto checkstr; + str = vstype[(subtype & VSTYPE) - VSNORMAL]; + if (!(subtype & VSNUL)) + goto dostr; + c = ':'; break; - case '\'': + case '$': + /* Can happen inside quotes, or in variable name $$ */ + if (subtype != 0) + // Testcase: + // $ true $$ & + // $ + // [1]+ Done true ${$} // shows ${\$} without "if (subtype)" check + break; + /* Not in variable name - show as \$ */ + case '\'': /* These can only happen inside quotes */ case '\\': case '"': - case '$': - /* These can only happen inside quotes */ cc[0] = c; str = cc; -//FIXME: -// $ true $$ & -// $ -// [1]+ Done true ${\$} <<=== BUG: ${\$} is not a valid way to write $$ (${$} would be ok) c = '\\'; break; default: break; } USTPUTC(c, nextc); - checkstr: if (!str) continue; dostr: @@ -4935,7 +4950,7 @@ cmdputs(const char *s) if (quoted & 1) { USTPUTC('"', nextc); } - *nextc = 0; + *nextc = '\0'; cmdnextc = nextc; } -- cgit v1.2.3-55-g6feb From 5b3405594a8925d4590c21e02adeabf85d34d93e Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 8 Aug 2025 12:40:44 +0200 Subject: ash: reuse vstype_suffix[] in debug code, shrink it function old new delta vstype_suffix - 39 +39 static.vstype 42 - -42 ------------------------------------------------------------------------------ (add/remove: 1/1 grow/shrink: 0/0 up/down: 39/-42) Total: -3 bytes Signed-off-by: Denys Vlasenko --- shell/ash.c | 72 +++++++++++++++++++++++++++++++------------------------------ 1 file changed, 37 insertions(+), 35 deletions(-) (limited to 'shell') diff --git a/shell/ash.c b/shell/ash.c index 0f8c2a66a..0752629ee 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -852,29 +852,47 @@ out2str(const char *p) # define CTL_LAST CTLFROMPROC #endif -/* variable substitution byte (follows CTLVAR) */ -#define VSTYPE 0x0f /* type of variable substitution */ -#define VSNUL 0x10 /* colon--treat the empty string as unset */ - -/* values of VSTYPE field */ -#define VSNORMAL 0x1 /* normal variable: $var or ${var} */ -#define VSMINUS 0x2 /* ${var-text} */ -#define VSPLUS 0x3 /* ${var+text} */ -#define VSQUESTION 0x4 /* ${var?message} */ -#define VSASSIGN 0x5 /* ${var=text} */ +/* ${VAR[ops]} encoding is CTLVAR,,"VARNAME=",,CTLENDVAR */ +/* variable type byte (follows CTLVAR) */ +#define VSTYPE 0x0f /* type of variable substitution */ +#define VSNUL 0x10 /* colon: the op is one of :- :+ :? := */ +/* values of VSTYPE field. The first 5 must be in this order, "}-+?=" string is used elsewhere to index into them */ +#define VSNORMAL 0x1 /* $var or ${var} */ +#define VSMINUS 0x2 /* ${var[:]-text} */ +#define VSPLUS 0x3 /* ${var[:]+text} */ +#define VSQUESTION 0x4 /* ${var[:]?message} */ +#define VSASSIGN 0x5 /* ${var[:]=text} */ #define VSTRIMRIGHT 0x6 /* ${var%pattern} */ #define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */ #define VSTRIMLEFT 0x8 /* ${var#pattern} */ #define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */ #define VSLENGTH 0xa /* ${#var} */ -// unused yet 0xb /* unused */ #if BASH_SUBSTR -#define VSSUBSTR 0xc /* ${var:position:length} */ +#define VSSUBSTR 0xb /* ${var:position:length} */ #endif #if BASH_PATTERN_SUBST -#define VSREPLACE 0xd /* ${var/pattern/replacement} */ -#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */ +#define VSREPLACE 0xc /* ${var/pattern/replacement} */ +#define VSREPLACEALL 0xd /* ${var//pattern/replacement} */ #endif +static const char vstype_suffix[][3] ALIGN1 = { + [VSNORMAL - VSNORMAL] = "}", // $var or ${var} + [VSMINUS - VSNORMAL] = "-", // ${var-text} + [VSPLUS - VSNORMAL] = "+", // ${var+text} + [VSQUESTION - VSNORMAL] = "?", // ${var?message} + [VSASSIGN - VSNORMAL] = "=", // ${var=text} + [VSTRIMRIGHT - VSNORMAL] = "%", // ${var%pattern} + [VSTRIMRIGHTMAX - VSNORMAL] = "%%",// ${var%%pattern} + [VSTRIMLEFT - VSNORMAL] = "#", // ${var#pattern} + [VSTRIMLEFTMAX - VSNORMAL] = "##",// ${var##pattern} + [VSLENGTH - VSNORMAL] = "", // ${#var} +#if BASH_SUBSTR + [VSSUBSTR - VSNORMAL] = ":", // ${var:position:length} +#endif +#if BASH_PATTERN_SUBST + [VSREPLACE - VSNORMAL] = "/", // ${var/pattern/replacement} + [VSREPLACEALL - VSNORMAL] = "//",// ${var//pattern/replacement} +#endif +}; static const char dolatstr[] ALIGN1 = { CTLQUOTEMARK, CTLVAR, VSNORMAL, '@', '=', CTLQUOTEMARK, '\0' @@ -1248,7 +1266,9 @@ sharg(union node *arg, FILE *fp) if (subtype & VSNUL) putc(':', fp); - +#if 1 + fputs(vstype_suffix[(subtype & VSTYPE) - VSNORMAL], fp); +#else switch (subtype & VSTYPE) { case VSNORMAL: putc('}', fp); @@ -1284,6 +1304,7 @@ sharg(union node *arg, FILE *fp) default: out1fmt("", subtype); } +#endif break; case CTLENDVAR: putc('}', fp); @@ -4830,25 +4851,6 @@ static char *cmdnextc; static void cmdputs(const char *s) { - static const char vstype[][3] ALIGN1 = { - [VSNORMAL - VSNORMAL] = "}", // $var or ${var} - [VSMINUS - VSNORMAL] = "-", // ${var-text} - [VSPLUS - VSNORMAL] = "+", // ${var+text} - [VSQUESTION - VSNORMAL] = "?", // ${var?message} - [VSASSIGN - VSNORMAL] = "=", // ${var=text} - [VSTRIMRIGHT - VSNORMAL] = "%", // ${var%pattern} - [VSTRIMRIGHTMAX - VSNORMAL] = "%%",// ${var%%pattern} - [VSTRIMLEFT - VSNORMAL] = "#", // ${var#pattern} - [VSTRIMLEFTMAX - VSNORMAL] = "##",// ${var##pattern} - [VSLENGTH - VSNORMAL] = "", // ${#var} -#if BASH_SUBSTR - [VSSUBSTR - VSNORMAL] = ":", // ${var:position:length} -#endif -#if BASH_PATTERN_SUBST - [VSREPLACE - VSNORMAL] = "/", // ${var/pattern/replacement} - [VSREPLACEALL - VSNORMAL] = "//",// ${var//pattern/replacement} -#endif - }; const char *p, *str; char cc[2]; char *nextc; @@ -4914,7 +4916,7 @@ cmdputs(const char *s) /* We are in variable name */ if ((subtype & VSTYPE) != VSNORMAL) quoted <<= 1; - str = vstype[(subtype & VSTYPE) - VSNORMAL]; + str = vstype_suffix[(subtype & VSTYPE) - VSNORMAL]; if (!(subtype & VSNUL)) goto dostr; c = ':'; -- cgit v1.2.3-55-g6feb From fa82ef1a2ec558bb8e624461851a0d1fcbc0a0b6 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 9 Aug 2025 01:48:12 +0200 Subject: shells: add testsuite item Signed-off-by: Denys Vlasenko --- shell/ash_test/ash-vars/var_backslash1.right | 25 +++++++++++++++++ shell/ash_test/ash-vars/var_backslash1.tests | 37 ++++++++++++++++++++++++++ shell/hush_test/hush-bugs/var_backslash1.right | 25 +++++++++++++++++ shell/hush_test/hush-bugs/var_backslash1.tests | 37 ++++++++++++++++++++++++++ 4 files changed, 124 insertions(+) create mode 100644 shell/ash_test/ash-vars/var_backslash1.right create mode 100755 shell/ash_test/ash-vars/var_backslash1.tests create mode 100644 shell/hush_test/hush-bugs/var_backslash1.right create mode 100755 shell/hush_test/hush-bugs/var_backslash1.tests (limited to 'shell') diff --git a/shell/ash_test/ash-vars/var_backslash1.right b/shell/ash_test/ash-vars/var_backslash1.right new file mode 100644 index 000000000..066294838 --- /dev/null +++ b/shell/ash_test/ash-vars/var_backslash1.right @@ -0,0 +1,25 @@ +a is '\*bc' +b is '\' +c is '*' +${a##?*} removes everything: || +${a##?"*"} removes \*: |bc| - matches one char, then * +${a##\*} removes nothing: |\*bc| - first char is not * +${a##\\*} removes everything: || - matches \, then all +${a##\\\*} removes \*: || - matches \, then * +${a##?$c} removes everything: || - matches one char, then all +${a##?"$c"} removes \*: |bc| - matches one char, then * +${a##\\$c} removes everything: || - matches \, then all +${a##\\"$c"} removes \*: |bc| - matches \, then * +${a##$b} removes \: |*bc| - matches \ +${a##"$b"} removes \: |*bc| - matches \ + +${a##"$b"?} removes \*: |bc| - matches \, then one char +${a##"$b"*} removes everything: || - matches \, then all +${a##"$b""?"} removes nothing: |\*bc| - second char is not ? +${a##"$b""*"} removes \*: |bc| - matches \, then * +${a##"$b"\*} removes \*: |bc| - matches \, then * +${a##"$b"$c} removes everything:|| - matches \, then all +${a##"$b""$c"} removes \*: |bc| - matches \, then * +${a##"$b?"} removes nothing: |\*bc| - second char is not ? +${a##"$b*"} removes \*: |bc| - matches \, then * +${a##"$b$c"} removes \*: |bc| - matches \, then * diff --git a/shell/ash_test/ash-vars/var_backslash1.tests b/shell/ash_test/ash-vars/var_backslash1.tests new file mode 100755 index 000000000..7d865c929 --- /dev/null +++ b/shell/ash_test/ash-vars/var_backslash1.tests @@ -0,0 +1,37 @@ +a='\*bc' +b='\' +c='*' +echo "a is '$a'" +echo "b is '$b'" +echo "c is '$c'" +echo '${a##?*} removes everything: '"|${a##?*}|" +echo '${a##?"*"} removes \*: '"|${a##?"*"}|"' - matches one char, then *' +echo '${a##\*} removes nothing: '"|${a##\*}|"' - first char is not *' +echo '${a##\\*} removes everything: '"|${a##\\*}|"' - matches \, then all' +echo '${a##\\\*} removes \*: '"|${a##\\*}|"' - matches \, then *' +echo '${a##?$c} removes everything: '"|${a##?$c}|"' - matches one char, then all' +echo '${a##?"$c"} removes \*: '"|${a##?"$c"}|"' - matches one char, then *' +echo '${a##\\$c} removes everything: '"|${a##\\$c}|"' - matches \, then all' +echo '${a##\\"$c"} removes \*: '"|${a##\\"$c"}|"' - matches \, then *' +echo '${a##$b} removes \: '"|${a##$b}|"' - matches \' +echo '${a##"$b"} removes \: '"|${a##"$b"}|"' - matches \' +echo +## In bash, this isn't working as expected +#echo '${a##$b?} removes \*: '"|${a##$b?}|"' - matches \, then one char' # bash prints |\*bc| +#echo '${a##$b*} removes everything: '"|${a##$b*}|"' - matches \, then all' # bash prints |\*bc| +#echo '${a##$b$c} removes everything: '"|${a##$b$c}|"' - matches \, then all' # bash prints |\*bc| +#echo '${a##$b"$c"} removes \*: '"|${a##$b"$c"}|"' - matches \, then *' # bash prints |\*bc| +## the cause seems to be that $b emits backslash that "glues" onto next character if there is one: +## a='\*bc'; b='\'; c='*'; echo "|${a##?$b*}|" # bash prints |bc| - the $b* works as \* (matches literal *) +## a='\*bc'; b='\'; c='*'; echo "|${a##\\$b*}|" # bash prints |bc| +#echo +echo '${a##"$b"?} removes \*: '"|${a##"$b"?}|"' - matches \, then one char' +echo '${a##"$b"*} removes everything: '"|${a##"$b"*}|"' - matches \, then all' +echo '${a##"$b""?"} removes nothing: '"|${a##"$b""?"}|"' - second char is not ?' # bash prints |bc| +echo '${a##"$b""*"} removes \*: '"|${a##"$b""*"}|"' - matches \, then *' +echo '${a##"$b"\*} removes \*: '"|${a##"$b"\*}|"' - matches \, then *' +echo '${a##"$b"$c} removes everything:'"|${a##"$b"$c}|"' - matches \, then all' +echo '${a##"$b""$c"} removes \*: '"|${a##"$b""$c"}|"' - matches \, then *' +echo '${a##"$b?"} removes nothing: '"|${a##"$b?"}|"' - second char is not ?' # bash prints |bc| +echo '${a##"$b*"} removes \*: '"|${a##"$b*"}|"' - matches \, then *' # bash prints || +echo '${a##"$b$c"} removes \*: '"|${a##"$b$c"}|"' - matches \, then *' diff --git a/shell/hush_test/hush-bugs/var_backslash1.right b/shell/hush_test/hush-bugs/var_backslash1.right new file mode 100644 index 000000000..066294838 --- /dev/null +++ b/shell/hush_test/hush-bugs/var_backslash1.right @@ -0,0 +1,25 @@ +a is '\*bc' +b is '\' +c is '*' +${a##?*} removes everything: || +${a##?"*"} removes \*: |bc| - matches one char, then * +${a##\*} removes nothing: |\*bc| - first char is not * +${a##\\*} removes everything: || - matches \, then all +${a##\\\*} removes \*: || - matches \, then * +${a##?$c} removes everything: || - matches one char, then all +${a##?"$c"} removes \*: |bc| - matches one char, then * +${a##\\$c} removes everything: || - matches \, then all +${a##\\"$c"} removes \*: |bc| - matches \, then * +${a##$b} removes \: |*bc| - matches \ +${a##"$b"} removes \: |*bc| - matches \ + +${a##"$b"?} removes \*: |bc| - matches \, then one char +${a##"$b"*} removes everything: || - matches \, then all +${a##"$b""?"} removes nothing: |\*bc| - second char is not ? +${a##"$b""*"} removes \*: |bc| - matches \, then * +${a##"$b"\*} removes \*: |bc| - matches \, then * +${a##"$b"$c} removes everything:|| - matches \, then all +${a##"$b""$c"} removes \*: |bc| - matches \, then * +${a##"$b?"} removes nothing: |\*bc| - second char is not ? +${a##"$b*"} removes \*: |bc| - matches \, then * +${a##"$b$c"} removes \*: |bc| - matches \, then * diff --git a/shell/hush_test/hush-bugs/var_backslash1.tests b/shell/hush_test/hush-bugs/var_backslash1.tests new file mode 100755 index 000000000..7d865c929 --- /dev/null +++ b/shell/hush_test/hush-bugs/var_backslash1.tests @@ -0,0 +1,37 @@ +a='\*bc' +b='\' +c='*' +echo "a is '$a'" +echo "b is '$b'" +echo "c is '$c'" +echo '${a##?*} removes everything: '"|${a##?*}|" +echo '${a##?"*"} removes \*: '"|${a##?"*"}|"' - matches one char, then *' +echo '${a##\*} removes nothing: '"|${a##\*}|"' - first char is not *' +echo '${a##\\*} removes everything: '"|${a##\\*}|"' - matches \, then all' +echo '${a##\\\*} removes \*: '"|${a##\\*}|"' - matches \, then *' +echo '${a##?$c} removes everything: '"|${a##?$c}|"' - matches one char, then all' +echo '${a##?"$c"} removes \*: '"|${a##?"$c"}|"' - matches one char, then *' +echo '${a##\\$c} removes everything: '"|${a##\\$c}|"' - matches \, then all' +echo '${a##\\"$c"} removes \*: '"|${a##\\"$c"}|"' - matches \, then *' +echo '${a##$b} removes \: '"|${a##$b}|"' - matches \' +echo '${a##"$b"} removes \: '"|${a##"$b"}|"' - matches \' +echo +## In bash, this isn't working as expected +#echo '${a##$b?} removes \*: '"|${a##$b?}|"' - matches \, then one char' # bash prints |\*bc| +#echo '${a##$b*} removes everything: '"|${a##$b*}|"' - matches \, then all' # bash prints |\*bc| +#echo '${a##$b$c} removes everything: '"|${a##$b$c}|"' - matches \, then all' # bash prints |\*bc| +#echo '${a##$b"$c"} removes \*: '"|${a##$b"$c"}|"' - matches \, then *' # bash prints |\*bc| +## the cause seems to be that $b emits backslash that "glues" onto next character if there is one: +## a='\*bc'; b='\'; c='*'; echo "|${a##?$b*}|" # bash prints |bc| - the $b* works as \* (matches literal *) +## a='\*bc'; b='\'; c='*'; echo "|${a##\\$b*}|" # bash prints |bc| +#echo +echo '${a##"$b"?} removes \*: '"|${a##"$b"?}|"' - matches \, then one char' +echo '${a##"$b"*} removes everything: '"|${a##"$b"*}|"' - matches \, then all' +echo '${a##"$b""?"} removes nothing: '"|${a##"$b""?"}|"' - second char is not ?' # bash prints |bc| +echo '${a##"$b""*"} removes \*: '"|${a##"$b""*"}|"' - matches \, then *' +echo '${a##"$b"\*} removes \*: '"|${a##"$b"\*}|"' - matches \, then *' +echo '${a##"$b"$c} removes everything:'"|${a##"$b"$c}|"' - matches \, then all' +echo '${a##"$b""$c"} removes \*: '"|${a##"$b""$c"}|"' - matches \, then *' +echo '${a##"$b?"} removes nothing: '"|${a##"$b?"}|"' - second char is not ?' # bash prints |bc| +echo '${a##"$b*"} removes \*: '"|${a##"$b*"}|"' - matches \, then *' # bash prints || +echo '${a##"$b$c"} removes \*: '"|${a##"$b$c"}|"' - matches \, then *' -- cgit v1.2.3-55-g6feb From 98b1a000c98cc6e91bae702c2f2f3824bbd7420a Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 9 Aug 2025 02:02:04 +0200 Subject: ash: jobs: drop unused node parameter in makejob() Signed-off-by: Denys Vlasenko --- shell/ash.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'shell') diff --git a/shell/ash.c b/shell/ash.c index 0752629ee..e8ac07cf0 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -3664,7 +3664,7 @@ struct job { struct job *prev_job; /* previous job */ }; -static struct job *makejob(/*union node *,*/ int); +static struct job *makejob(int); static int forkshell(struct job *, union node *, int); static int waitforjob(struct job *); @@ -4801,7 +4801,7 @@ growjobtab(void) * Called with interrupts off. */ static struct job * -makejob(/*union node *node,*/ int nprocs) +makejob(int nprocs) { int i; struct job *jp; @@ -6628,7 +6628,7 @@ evalbackcmd(union node *n, struct backcmd *result if (pipe(pip) < 0) ash_msg_and_raise_perror("can't create pipe"); /* process substitution uses NULL job, like openhere() */ - jp = (ctl == CTLBACKQ) ? makejob(/*n,*/ 1) : NULL; + jp = (ctl == CTLBACKQ) ? makejob(1) : NULL; if (forkshell(jp, n, FORK_NOJOB) == 0) { /* child */ FORCE_INT_ON; @@ -9608,7 +9608,7 @@ evalsubshell(union node *n, int flags) INT_OFF; if (backgnd == FORK_FG) get_tty_state(); - jp = makejob(/*n,*/ 1); + jp = makejob(1); if (forkshell(jp, n, backgnd) == 0) { /* child */ INT_ON; @@ -9715,7 +9715,7 @@ evalpipe(union node *n, int flags) INT_OFF; if (n->npipe.pipe_backgnd == 0) get_tty_state(); - jp = makejob(/*n,*/ pipelen); + jp = makejob(pipelen); prevfd = -1; for (lp = n->npipe.cmdlist; lp; lp = lp->next) { prehash(lp->n); @@ -10618,7 +10618,7 @@ evalcommand(union node *cmd, int flags) /* No, forking off a child is necessary */ INT_OFF; get_tty_state(); - jp = makejob(/*cmd,*/ 1); + jp = makejob(1); if (forkshell(jp, cmd, FORK_FG) != 0) { /* parent */ break; -- cgit v1.2.3-55-g6feb From 6d4476258993d82a14ec060c53e41199d2f5eef5 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 9 Aug 2025 02:24:56 +0200 Subject: ash: options: Do not set commandname in procargs Upstream commit: Date: Mon Feb 25 12:49:20 2019 +0800 options: Do not set commandname in procargs We set commandname in procargs when we don't have to. This results in a duplicated output of arg0 when an error occurs. function old new delta ash_main 1256 1236 -20 Signed-off-by: Denys Vlasenko --- shell/ash.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'shell') diff --git a/shell/ash.c b/shell/ash.c index e8ac07cf0..aaea641a9 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -1447,7 +1447,7 @@ ash_vmsg(const char *msg, va_list ap) { fprintf(stderr, "%s: ", arg0); if (commandname) { - if (strcmp(arg0, commandname)) + if (strcmp(arg0, commandname) != 0) fprintf(stderr, "%s: ", commandname); if (!iflag || g_parsefile->pf_fd > 0) fprintf(stderr, "line %d: ", errlinno); @@ -14659,7 +14659,7 @@ procargs(char **argv) int login_sh; xargv = argv; - login_sh = xargv[0] && xargv[0][0] == '-'; + login_sh = /*xargv[0] &&*/ xargv[0][0] == '-'; #if NUM_SCRIPTS > 0 if (minusc) goto setarg0; @@ -14705,7 +14705,6 @@ procargs(char **argv) setinputfile(*xargv, 0); setarg0: arg0 = *xargv++; - commandname = arg0; } shellparam.p = xargv; -- cgit v1.2.3-55-g6feb From 4eb4141b225e229b7263ded195307dd9d228c2af Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 9 Aug 2025 12:23:21 +0200 Subject: ash: eval: Always set exitstatus in evaltree Upstream commit: Date: Tue, 6 Dec 2022 16:49:14 +0800 eval: Always set exitstatus in evaltree There is no harm in setting exitstatus unconditionally in evaltree. Signed-off-by: Denys Vlasenko --- shell/ash.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'shell') diff --git a/shell/ash.c b/shell/ash.c index aaea641a9..f1339cf3c 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -9357,7 +9357,7 @@ evaltree(union node *n, int flags) #endif case NNOT: status = !evaltree(n->nnot.com, EV_TESTED); - goto setstatus; + break; case NREDIR: errlinno = lineno = n->nredir.linno; expredir(n->nredir.redirect); @@ -9368,7 +9368,7 @@ evaltree(union node *n, int flags) } if (n->nredir.redirect) popredir(/*drop:*/ 0); - goto setstatus; + break; case NCMD: evalfn = evalcommand; checkexit: @@ -9412,7 +9412,7 @@ evaltree(union node *n, int flags) evalfn = evaltree; calleval: status = evalfn(n, flags); - goto setstatus; + break; } case NIF: status = evaltree(n->nif.test, EV_TESTED); @@ -9426,17 +9426,18 @@ evaltree(union node *n, int flags) goto evaln; } status = 0; - goto setstatus; + break; case NDEFUN: defun(n); /* Not necessary. To test it: * "false; f() { qwerty; }; echo $?" should print 0. */ /* status = 0; */ - setstatus: - exitstatus = status; break; } + + exitstatus = status; + out: /* Order of checks below is important: * signal handlers trigger before exit caused by "set -e". -- cgit v1.2.3-55-g6feb From c92755133b310461171cd6050237caba6dce1264 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 9 Aug 2025 13:00:55 +0200 Subject: shells: fix a typo in var_backslash1.tests, expand it while at it Signed-off-by: Denys Vlasenko --- shell/ash_test/ash-vars/var_backslash1.right | 5 +++-- shell/ash_test/ash-vars/var_backslash1.tests | 5 +++-- shell/hush_test/hush-bugs/var_backslash1.right | 5 +++-- shell/hush_test/hush-bugs/var_backslash1.tests | 5 +++-- 4 files changed, 12 insertions(+), 8 deletions(-) (limited to 'shell') diff --git a/shell/ash_test/ash-vars/var_backslash1.right b/shell/ash_test/ash-vars/var_backslash1.right index 066294838..f384da1f5 100644 --- a/shell/ash_test/ash-vars/var_backslash1.right +++ b/shell/ash_test/ash-vars/var_backslash1.right @@ -1,14 +1,15 @@ a is '\*bc' b is '\' c is '*' -${a##?*} removes everything: || +${a##?*} removes everything: || - matches one char, then all ${a##?"*"} removes \*: |bc| - matches one char, then * ${a##\*} removes nothing: |\*bc| - first char is not * ${a##\\*} removes everything: || - matches \, then all -${a##\\\*} removes \*: || - matches \, then * +${a##\\\*} removes \*: |bc| - matches \, then * ${a##?$c} removes everything: || - matches one char, then all ${a##?"$c"} removes \*: |bc| - matches one char, then * ${a##\\$c} removes everything: || - matches \, then all +${a##\\\$c} removes nothing: |\*bc| - matches \, but then second char is not $ ${a##\\"$c"} removes \*: |bc| - matches \, then * ${a##$b} removes \: |*bc| - matches \ ${a##"$b"} removes \: |*bc| - matches \ diff --git a/shell/ash_test/ash-vars/var_backslash1.tests b/shell/ash_test/ash-vars/var_backslash1.tests index 7d865c929..7eea3cc19 100755 --- a/shell/ash_test/ash-vars/var_backslash1.tests +++ b/shell/ash_test/ash-vars/var_backslash1.tests @@ -4,14 +4,15 @@ c='*' echo "a is '$a'" echo "b is '$b'" echo "c is '$c'" -echo '${a##?*} removes everything: '"|${a##?*}|" +echo '${a##?*} removes everything: '"|${a##?*}|"' - matches one char, then all' echo '${a##?"*"} removes \*: '"|${a##?"*"}|"' - matches one char, then *' echo '${a##\*} removes nothing: '"|${a##\*}|"' - first char is not *' echo '${a##\\*} removes everything: '"|${a##\\*}|"' - matches \, then all' -echo '${a##\\\*} removes \*: '"|${a##\\*}|"' - matches \, then *' +echo '${a##\\\*} removes \*: '"|${a##\\\*}|"' - matches \, then *' echo '${a##?$c} removes everything: '"|${a##?$c}|"' - matches one char, then all' echo '${a##?"$c"} removes \*: '"|${a##?"$c"}|"' - matches one char, then *' echo '${a##\\$c} removes everything: '"|${a##\\$c}|"' - matches \, then all' +echo '${a##\\\$c} removes nothing: '"|${a##\\\$c}|"' - matches \, but then second char is not $' echo '${a##\\"$c"} removes \*: '"|${a##\\"$c"}|"' - matches \, then *' echo '${a##$b} removes \: '"|${a##$b}|"' - matches \' echo '${a##"$b"} removes \: '"|${a##"$b"}|"' - matches \' diff --git a/shell/hush_test/hush-bugs/var_backslash1.right b/shell/hush_test/hush-bugs/var_backslash1.right index 066294838..f384da1f5 100644 --- a/shell/hush_test/hush-bugs/var_backslash1.right +++ b/shell/hush_test/hush-bugs/var_backslash1.right @@ -1,14 +1,15 @@ a is '\*bc' b is '\' c is '*' -${a##?*} removes everything: || +${a##?*} removes everything: || - matches one char, then all ${a##?"*"} removes \*: |bc| - matches one char, then * ${a##\*} removes nothing: |\*bc| - first char is not * ${a##\\*} removes everything: || - matches \, then all -${a##\\\*} removes \*: || - matches \, then * +${a##\\\*} removes \*: |bc| - matches \, then * ${a##?$c} removes everything: || - matches one char, then all ${a##?"$c"} removes \*: |bc| - matches one char, then * ${a##\\$c} removes everything: || - matches \, then all +${a##\\\$c} removes nothing: |\*bc| - matches \, but then second char is not $ ${a##\\"$c"} removes \*: |bc| - matches \, then * ${a##$b} removes \: |*bc| - matches \ ${a##"$b"} removes \: |*bc| - matches \ diff --git a/shell/hush_test/hush-bugs/var_backslash1.tests b/shell/hush_test/hush-bugs/var_backslash1.tests index 7d865c929..7eea3cc19 100755 --- a/shell/hush_test/hush-bugs/var_backslash1.tests +++ b/shell/hush_test/hush-bugs/var_backslash1.tests @@ -4,14 +4,15 @@ c='*' echo "a is '$a'" echo "b is '$b'" echo "c is '$c'" -echo '${a##?*} removes everything: '"|${a##?*}|" +echo '${a##?*} removes everything: '"|${a##?*}|"' - matches one char, then all' echo '${a##?"*"} removes \*: '"|${a##?"*"}|"' - matches one char, then *' echo '${a##\*} removes nothing: '"|${a##\*}|"' - first char is not *' echo '${a##\\*} removes everything: '"|${a##\\*}|"' - matches \, then all' -echo '${a##\\\*} removes \*: '"|${a##\\*}|"' - matches \, then *' +echo '${a##\\\*} removes \*: '"|${a##\\\*}|"' - matches \, then *' echo '${a##?$c} removes everything: '"|${a##?$c}|"' - matches one char, then all' echo '${a##?"$c"} removes \*: '"|${a##?"$c"}|"' - matches one char, then *' echo '${a##\\$c} removes everything: '"|${a##\\$c}|"' - matches \, then all' +echo '${a##\\\$c} removes nothing: '"|${a##\\\$c}|"' - matches \, but then second char is not $' echo '${a##\\"$c"} removes \*: '"|${a##\\"$c"}|"' - matches \, then *' echo '${a##$b} removes \: '"|${a##$b}|"' - matches \' echo '${a##"$b"} removes \: '"|${a##"$b"}|"' - matches \' -- cgit v1.2.3-55-g6feb From 53b3854e8141f4fc5fad10f180fc4fac2feee954 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 9 Aug 2025 17:04:16 +0200 Subject: ash: fix fallout of no-more-set commandname Testsuite reports lots of message mismatches, fix that Signed-off-by: Denys Vlasenko --- shell/ash.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'shell') diff --git a/shell/ash.c b/shell/ash.c index f1339cf3c..3e393715b 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -1445,13 +1445,14 @@ showtree(union node *n) static void ash_vmsg(const char *msg, va_list ap) { +//In dash, the order/format is different: +// arg0: LINENO: [commandname:] MSG +//If you fix it, change testsuite to match fprintf(stderr, "%s: ", arg0); - if (commandname) { - if (strcmp(arg0, commandname) != 0) - fprintf(stderr, "%s: ", commandname); - if (!iflag || g_parsefile->pf_fd > 0) - fprintf(stderr, "line %d: ", errlinno); - } + if (commandname && strcmp(arg0, commandname) != 0) + fprintf(stderr, "%s: ", commandname); + if (!iflag || g_parsefile->pf_fd > 0) + fprintf(stderr, "line %d: ", errlinno); vfprintf(stderr, msg, ap); newline_and_flush(stderr); } -- cgit v1.2.3-55-g6feb