(Note: this is a large patch. Patch included as mail attachment) All of the bash compatibility options will now depend on CONFIG_*_BASH_COMPAT, and CONFIG_*_BASH_COMPAT become an option that alone doesn't add any code.
Splitting these options allows more flexibility in configuration, and makes it self-documenting about what bash-compatible features we have. Signed-off-by: Kang-Che Sung <[email protected]> --- coreutils/test.c | 8 ++- shell/ash.c | 216 +++++++++++++++++++++++++++++++++++++------------------ shell/hush.c | 133 ++++++++++++++++++++++------------ 3 files changed, 239 insertions(+), 118 deletions(-)
From 655b1f7dd97c83c79502a0635fe9d195de47beaa Mon Sep 17 00:00:00 2001 From: Kang-Che Sung <[email protected]> Date: Wed, 11 Jan 2017 03:02:24 +0800 Subject: [PATCH 2/2] Split bash compatible extensions into separate options. All of the bash compatibility options will now depend on CONFIG_*_BASH_COMPAT, and CONFIG_*_BASH_COMPAT become an option that alone doesn't add any code. Splitting these options allows more flexibility in configuration, and makes it self-documenting about what bash-compatible features we have. Signed-off-by: Kang-Che Sung <[email protected]> --- coreutils/test.c | 8 ++- shell/ash.c | 216 +++++++++++++++++++++++++++++++++++++------------------ shell/hush.c | 133 ++++++++++++++++++++++------------ 3 files changed, 239 insertions(+), 118 deletions(-) diff --git a/coreutils/test.c b/coreutils/test.c index edc625f57..704255630 100644 --- a/coreutils/test.c +++ b/coreutils/test.c @@ -42,7 +42,7 @@ //config:config FEATURE_TEST_64 //config: bool "Extend test to 64 bit" //config: default y -//config: depends on TEST || TEST1 || TEST2 || ASH_TEST || HUSH_TEST +//config: depends on TEST || TEST1 || TEST2 || ASH_TEST || ASH_TEST2 || HUSH_TEST || HUSH_TEST2 //config: help //config: Enable 64-bit support in test. @@ -54,8 +54,10 @@ //kbuild:lib-$(CONFIG_TEST1) += test.o test_ptr_hack.o //kbuild:lib-$(CONFIG_TEST2) += test.o test_ptr_hack.o -//kbuild:lib-$(CONFIG_ASH_TEST) += test.o test_ptr_hack.o -//kbuild:lib-$(CONFIG_HUSH_TEST) += test.o test_ptr_hack.o +//kbuild:lib-$(CONFIG_ASH_TEST) += test.o test_ptr_hack.o +//kbuild:lib-$(CONFIG_ASH_TEST2) += test.o test_ptr_hack.o +//kbuild:lib-$(CONFIG_HUSH_TEST) += test.o test_ptr_hack.o +//kbuild:lib-$(CONFIG_HUSH_TEST2) += test.o test_ptr_hack.o /* "test --help" is special-cased to ignore --help */ //usage:#define test_trivial_usage NOUSAGE_STR diff --git a/shell/ash.c b/shell/ash.c index 866c7de05..d5f6b7d04 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -47,11 +47,6 @@ //config: Note that as of now (2017-01), uclibc and musl glob() both have bugs //config: which would break ash if you select N here. //config: -//config:config ASH_BASH_COMPAT -//config: bool "bash-compatible extensions" -//config: default y -//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH -//config: //config:config ASH_JOB_CONTROL //config: bool "Job control" //config: default y @@ -133,6 +128,76 @@ //config: you to run the specified command or builtin, //config: even when there is a function with the same name. //config: +//config:config ASH_BASH_COMPAT +//config: bool "bash-compatible extensions" +//config: default y +//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH +//config: +//config:config ASH_DOLLAR_SQUOTE +//config: bool "Dollar single quote $'...'" +//config: default y +//config: depends on ASH_BASH_COMPAT +//config: +//config:config ASH_SUBSTR_EXPANSION +//config: bool "Substring expansion ${var:position:length}" +//config: default y +//config: depends on ASH_BASH_COMPAT +//config: +//config:config ASH_PATTERN_SUBST +//config: bool "Pattern substitution ${var/pattern/replacement}" +//config: default y +//config: depends on ASH_BASH_COMPAT +//config: +//config:config ASH_REDIR_OUTPUT2 +//config: bool "Redirection operator &>file (equivalent to '>file 2>&1')" +//config: default y +//config: depends on ASH_BASH_COMPAT +//config: +//config:config ASH_FUNCTION2 +//config: bool "function keyword" +//config: default y +//config: depends on ASH_BASH_COMPAT +//config: help +//config: Enable the 'function' keyword to define a function in +//config: addition to the standard 'funcname() { commands; }' syntax. +//config: 'function' keyword is bash-specific. +//config: +//config:config ASH_SOURCE +//config: bool "source builtin" +//config: default y +//config: depends on ASH_BASH_COMPAT +//config: help +//config: Enable source builtin, which is equivalent to . (dot) builtin. +//config: The availabilty of . (dot) builtin is unaffected by this option. +//config: +//config:config ASH_TEST2 +//config: bool "test construct [[ EXPR ]]" +//config: default y +//config: depends on ASH_BASH_COMPAT +//config: help +//config: The test construct [[ EXPR ]] support in ash is currently +//config: limited to allowing unquoted && and || operators within and not +//config: treating them as command delimiters. +/* ^^^^^ TODO: update this if new feature gets implemented ^^^^^ */ +//config: +//config:config ASH_OPT_PIPEFAIL +//config: bool "pipefail option" +//config: default y +//config: depends on ASH_BASH_COMPAT +//config: help +//config: Allow setting the pipefail option ('ash -o pipefail' or +//config: 'set -o pipefail'). pipefail is disabled by default at runtime. +//config: +//config:config ASH_HOSTNAME_VAR +//config: bool "$HOSTNAME variable" +//config: default y +//config: depends on ASH_BASH_COMPAT +//config: +//config:config ASH_SHLVL_VAR +//config: bool "$SHLVL variable" +//config: default y +//config: depends on ASH_BASH_COMPAT +//config: //config:endif # ash options //applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP)) @@ -250,7 +315,7 @@ static const char *const optletters_optnames[] = { "b" "notify", "u" "nounset", "\0" "vi" -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_OPT_PIPEFAIL ,"\0" "pipefail" #endif #if DEBUG @@ -327,14 +392,14 @@ struct globals_misc { #define bflag optlist[11] #define uflag optlist[12] #define viflag optlist[13] -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_OPT_PIPEFAIL # define pipefail optlist[14] #else # define pipefail 0 #endif #if DEBUG -# define nolog optlist[14 + ENABLE_ASH_BASH_COMPAT] -# define debug optlist[15 + ENABLE_ASH_BASH_COMPAT] +# define nolog optlist[14 + ENABLE_ASH_OPT_PIPEFAIL] +# define debug optlist[15 + ENABLE_ASH_OPT_PIPEFAIL] #endif /* trap handler commands */ @@ -655,8 +720,10 @@ out2str(const char *p) #define VSTRIMLEFT 0x8 /* ${var#pattern} */ #define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */ #define VSLENGTH 0xa /* ${#var} */ -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_SUBSTR_EXPANSION #define VSSUBSTR 0xc /* ${var:position:length} */ +#endif +#if ENABLE_ASH_PATTERN_SUBST #define VSREPLACE 0xd /* ${var/pattern/replacement} */ #define VSREPLACEALL 0xe /* ${var//pattern/replacement} */ #endif @@ -683,7 +750,7 @@ static const char dolatstr[] ALIGN1 = { #define NDEFUN 14 #define NARG 15 #define NTO 16 -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_REDIR_OUTPUT2 #define NTO2 17 #endif #define NCLOBBER 18 @@ -1093,7 +1160,7 @@ shcmd(union node *cmd, FILE *fp) case NTO: s = ">>"+1; dftfd = 1; break; case NCLOBBER: s = ">|"; dftfd = 1; break; case NAPPEND: s = ">>"; dftfd = 1; break; -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_REDIR_OUTPUT2 case NTO2: #endif case NTOFD: s = ">&"; dftfd = 1; break; @@ -4455,7 +4522,8 @@ cmdputs(const char *s) static const char vstype[VSTYPE + 1][3] = { "", "}", "-", "+", "?", "=", "%", "%%", "#", "##" - IF_ASH_BASH_COMPAT(, ":", "/", "//") + IF_ASH_SUBSTR_EXPANSION(, ":") + IF_ASH_PATTERN_SUBST(, "/", "//") }; const char *p, *str; @@ -4682,7 +4750,7 @@ cmdtxt(union node *n) case NAPPEND: p = ">>"; goto redir; -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_REDIR_OUTPUT2 case NTO2: #endif case NTOFD: @@ -5209,7 +5277,7 @@ openredirect(union node *redir) goto ecreate; break; case NTO: -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_REDIR_OUTPUT2 case NTO2: #endif /* Take care of noclobber mode. */ @@ -5370,7 +5438,7 @@ redirect(union node *redir, int flags) union node *tmp = redir; do { sv_pos++; -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_REDIR_OUTPUT2 if (tmp->nfile.type == NTO2) sv_pos++; #endif @@ -5412,7 +5480,7 @@ redirect(union node *redir, int flags) continue; } } -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_REDIR_OUTPUT2 redirect_more: #endif if (need_to_remember(sv, fd)) { @@ -5465,12 +5533,12 @@ redirect(union node *redir, int flags) } } else if (fd != newfd) { /* move newfd to fd */ dup2_or_raise(newfd, fd); -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_REDIR_OUTPUT2 if (!(redir->nfile.type == NTO2 && fd == 2)) #endif close(newfd); } -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_REDIR_OUTPUT2 if (redir->nfile.type == NTO2 && fd == 1) { /* We already redirected it to fd 1, now copy it to 2 */ newfd = 1; @@ -5787,15 +5855,15 @@ static char * rmescapes(char *str, int flag) { static const char qchars[] ALIGN1 = { - IF_ASH_BASH_COMPAT('/',) CTLESC, CTLQUOTEMARK, '\0' }; + IF_ASH_PATTERN_SUBST('/',) CTLESC, CTLQUOTEMARK, '\0' }; char *p, *q, *r; unsigned inquotes; unsigned protect_against_glob; unsigned globbing; - IF_ASH_BASH_COMPAT(unsigned slash = flag & RMESCAPE_SLASH;) + IF_ASH_PATTERN_SUBST(unsigned slash = flag & RMESCAPE_SLASH;) - p = strpbrk(str, qchars IF_ASH_BASH_COMPAT(+ !slash)); + p = strpbrk(str, qchars IF_ASH_PATTERN_SUBST(+ !slash)); if (!p) return str; @@ -5847,7 +5915,7 @@ rmescapes(char *str, int flag) protect_against_glob = 0; goto copy; } -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_PATTERN_SUBST else if (*p == '/' && slash) { /* stop handling globbing and mark location of slash */ globbing = slash = 0; @@ -6494,10 +6562,10 @@ subevalvar(char *p, char *varname, int strloc, int subtype, char *loc; char *rmesc, *rmescend; char *str; - IF_ASH_BASH_COMPAT(char *repl = NULL;) - IF_ASH_BASH_COMPAT(int pos, len, orig_len;) + IF_ASH_SUBSTR_EXPANSION(int pos, len, orig_len;) int amount, resetloc; - IF_ASH_BASH_COMPAT(int workloc;) + IF_ASH_PATTERN_SUBST(int workloc;) + IF_ASH_PATTERN_SUBST(char *repl = NULL;) int zero; char *(*scan)(char*, char*, char*, char*, int, int); @@ -6522,7 +6590,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype, varunset(p, varname, startp, varflags); /* NOTREACHED */ -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_SUBSTR_EXPANSION case VSSUBSTR: //TODO: support more general format ${v:EXPR:EXPR}, // where EXPR follows $(()) rules @@ -6591,17 +6659,19 @@ subevalvar(char *p, char *varname, int strloc, int subtype, amount = loc - expdest; STADJUST(amount, expdest); return loc; -#endif +#endif /* ASH_SUBSTR_EXPANSION */ } resetloc = expdest - (char *)stackblock(); +#if ENABLE_ASH_PATTERN_SUBST /* We'll comeback here if we grow the stack while handling * a VSREPLACE or VSREPLACEALL, since our pointers into the * stack will need rebasing, and we'll need to remove our work * areas each time */ - IF_ASH_BASH_COMPAT(restart:) + restart: +#endif amount = expdest - ((char *)stackblock() + resetloc); STADJUST(-amount, expdest); @@ -6626,11 +6696,11 @@ subevalvar(char *p, char *varname, int strloc, int subtype, * RMESCAPE_SLASH causes preglob to work differently on the pattern * and string. It's only used on the first call. */ - preglob(str, IF_ASH_BASH_COMPAT( + preglob(str, IF_ASH_PATTERN_SUBST( (subtype == VSREPLACE || subtype == VSREPLACEALL) && !repl ? - RMESCAPE_SLASH :) 0); + RMESCAPE_SLASH : ) 0); -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_PATTERN_SUBST workloc = expdest - (char *)stackblock(); if (subtype == VSREPLACE || subtype == VSREPLACEALL) { char *idx, *end; @@ -6731,7 +6801,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype, STADJUST(-amount, expdest); return startp; } -#endif /* ENABLE_ASH_BASH_COMPAT */ +#endif /* ASH_PATTERN_SUBST */ subtype -= VSTRIMRIGHT; #if DEBUG @@ -6999,8 +7069,10 @@ evalvar(char *p, int flag, struct strlist *var_str_list) case VSTRIMLEFTMAX: case VSTRIMRIGHT: case VSTRIMRIGHTMAX: -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_SUBSTR_EXPANSION case VSSUBSTR: +#endif +#if ENABLE_ASH_PATTERN_SUBST case VSREPLACE: case VSREPLACEALL: #endif @@ -7924,7 +7996,7 @@ enum { TESAC, TFI, TFOR, -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_FUNCTION2 TFUNCTION, #endif TIF, @@ -7962,7 +8034,7 @@ enum { /* 19 */ | (1u << TESAC) /* 20 */ | (1u << TFI) /* 21 */ | (0u << TFOR) -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_FUNCTION2 /* 22 */ | (0u << TFUNCTION) #endif /* 23 */ | (0u << TIF) @@ -8000,7 +8072,7 @@ static const char *const tokname_array[] = { "esac", "fi", "for", -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_FUNCTION2 "function", #endif "if", @@ -8244,7 +8316,7 @@ static const uint8_t nodesize[N_NUMBER] ALIGN1 = { [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)), [NARG ] = SHELL_ALIGN(sizeof(struct narg)), [NTO ] = SHELL_ALIGN(sizeof(struct nfile)), -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_REDIR_OUTPUT2 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)), #endif [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)), @@ -8326,7 +8398,7 @@ calcsize(int funcblocksize, union node *n) funcblocksize = calcsize(funcblocksize, n->narg.next); break; case NTO: -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_REDIR_OUTPUT2 case NTO2: #endif case NCLOBBER: @@ -8440,7 +8512,7 @@ copynode(union node *n) new->narg.next = copynode(n->narg.next); break; case NTO: -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_REDIR_OUTPUT2 case NTO2: #endif case NCLOBBER: @@ -8873,14 +8945,14 @@ expredir(union node *n) case NFROMTO: case NFROM: case NTO: -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_REDIR_OUTPUT2 case NTO2: #endif case NCLOBBER: case NAPPEND: expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR); TRACE(("expredir expanded to '%s'\n", fn.list->text)); -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_REDIR_OUTPUT2 store_expfname: #endif #if 0 @@ -8902,7 +8974,7 @@ expredir(union node *n) expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE); if (fn.list == NULL) ash_msg_and_raise_error("redir error"); -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_REDIR_OUTPUT2 //FIXME: we used expandarg with different args! if (!isdigit_str9(fn.list->text)) { /* >&file, not >&fd */ @@ -9298,7 +9370,7 @@ static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, a #if ENABLE_ASH_PRINTF static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); } #endif -#if ENABLE_ASH_TEST +#if ENABLE_ASH_TEST || ENABLE_ASH_TEST2 static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); } #endif @@ -9308,9 +9380,9 @@ static const struct builtincmd builtintab[] = { { BUILTIN_SPEC_REG ":" , truecmd }, #if ENABLE_ASH_TEST { BUILTIN_REGULAR "[" , testcmd }, -# if ENABLE_ASH_BASH_COMPAT +#endif +#if ENABLE_ASH_TEST2 { BUILTIN_REGULAR "[[" , testcmd }, -# endif #endif #if ENABLE_ASH_ALIAS { BUILTIN_REG_ASSG "alias" , aliascmd }, @@ -9363,7 +9435,7 @@ static const struct builtincmd builtintab[] = { { BUILTIN_SPEC_REG "return" , returncmd }, { BUILTIN_SPEC_REG "set" , setcmd }, { BUILTIN_SPEC_REG "shift" , shiftcmd }, -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_SOURCE { BUILTIN_SPEC_REG "source" , dotcmd }, #endif #if ENABLE_ASH_TEST @@ -9386,7 +9458,7 @@ static const struct builtincmd builtintab[] = { #define COMMANDCMD (builtintab + \ /* . : */ 2 + \ /* [ */ 1 * ENABLE_ASH_TEST + \ - /* [[ */ 1 * ENABLE_ASH_TEST * ENABLE_ASH_BASH_COMPAT + \ + /* [[ */ 1 * ENABLE_ASH_TEST2 + \ /* alias */ 1 * ENABLE_ASH_ALIAS + \ /* bg */ 1 * ENABLE_ASH_JOB_CONTROL + \ /* break cd cddir */ 3) @@ -11008,10 +11080,8 @@ simplecmd(void) union node *vars, **vpp; union node **rpp, *redir; int savecheckkwd; -#if ENABLE_ASH_BASH_COMPAT - smallint double_brackets_flag = 0; - smallint function_flag = 0; -#endif + IF_ASH_TEST2(smallint double_brackets_flag = 0;) + IF_ASH_FUNCTION2(smallint function_flag = 0;) args = NULL; app = &args; @@ -11026,12 +11096,14 @@ simplecmd(void) checkkwd = savecheckkwd; t = readtoken(); switch (t) { -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_FUNCTION2 case TFUNCTION: if (peektoken() != TWORD) raise_error_unexpected_syntax(TWORD); function_flag = 1; break; +#endif +#if ENABLE_ASH_TEST2 case TAND: /* "&&" */ case TOR: /* "||" */ if (!double_brackets_flag) { @@ -11045,7 +11117,7 @@ simplecmd(void) n->type = NARG; /*n->narg.next = NULL; - stzalloc did it */ n->narg.text = wordtext; -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_TEST2 if (strcmp("[[", wordtext) == 0) double_brackets_flag = 1; else if (strcmp("]]", wordtext) == 0) @@ -11060,7 +11132,7 @@ simplecmd(void) app = &n->narg.next; savecheckkwd = 0; } -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_FUNCTION2 if (function_flag) { checkkwd = CHKNL | CHKKWD; switch (peektoken()) { @@ -11090,7 +11162,7 @@ simplecmd(void) parsefname(); /* read name of redirection file */ break; case TLP: - IF_ASH_BASH_COMPAT(do_func:) + IF_ASH_FUNCTION2(do_func:) if (args && app == &args->narg.next && !vars && !redir ) { @@ -11098,7 +11170,7 @@ simplecmd(void) const char *name; /* We have a function */ - if (IF_ASH_BASH_COMPAT(!function_flag &&) readtoken() != TRP) + if (IF_ASH_FUNCTION2(!function_flag &&) readtoken() != TRP) raise_error_unexpected_syntax(TRP); name = n->narg.text; if (!goodname(name) @@ -11111,7 +11183,7 @@ simplecmd(void) n->narg.next = parse_command(); return n; } - IF_ASH_BASH_COMPAT(function_flag = 0;) + IF_ASH_FUNCTION2(function_flag = 0;) /* fall through */ default: tokpushback = 1; @@ -11292,7 +11364,7 @@ parse_command(void) n1 = list(0); t = TEND; break; - IF_ASH_BASH_COMPAT(case TFUNCTION:) + IF_ASH_FUNCTION2(case TFUNCTION:) case TWORD: case TREDIR: tokpushback = 1; @@ -11325,7 +11397,7 @@ parse_command(void) return n1; } -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_DOLLAR_SQUOTE static int decode_dollar_squote(void) { @@ -11410,7 +11482,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) IF_FEATURE_SH_MATH(int parenlevel;) /* levels of parens in arithmetic */ int dqvarnest; /* levels of variables expansion within double quotes */ - IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;) + IF_ASH_DOLLAR_SQUOTE(smallint bash_dollar_squote = 0;) startlinno = g_parsefile->linno; bqlist = NULL; @@ -11445,7 +11517,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) USTPUTC(c, out); break; case CCTL: -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_DOLLAR_SQUOTE if (c == '\\' && bash_dollar_squote) { c = decode_dollar_squote(); if (c == '\0') { @@ -11506,7 +11578,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) dblquote = 1; goto quotemark; case CENDQUOTE: - IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;) + IF_ASH_DOLLAR_SQUOTE(bash_dollar_squote = 0;) if (eofmark != NULL && varnest == 0) { USTPUTC(c, out); } else { @@ -11565,7 +11637,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) break; default: if (varnest == 0) { -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_REDIR_OUTPUT2 if (c == '&') { //Can't call pgetc_eatbnl() here, this requires three-deep pungetc() if (pgetc() == '>') @@ -11597,7 +11669,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) len = out - (char *)stackblock(); out = stackblock(); if (eofmark == NULL) { - if ((c == '>' || c == '<' IF_ASH_BASH_COMPAT( || c == 0x100 + '>')) + if ((c == '>' || c == '<' IF_ASH_REDIR_OUTPUT2( || c == 0x100 + '>')) && quotef == 0 ) { if (isdigit_str9(out)) { @@ -11685,7 +11757,7 @@ parseredir: { pungetc(); } } -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_REDIR_OUTPUT2 else if (c == 0x100 + '>') { /* this flags &> redirection */ np->nfile.fd = 1; pgetc(); /* this is '>', no need to check */ @@ -11751,7 +11823,7 @@ parsesub: { if (c > 255 /* PEOA or PEOF */ || (c != '(' && c != '{' && !is_name(c) && !is_special(c)) ) { -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_DOLLAR_SQUOTE if (syntax != DQSYNTAX && c == '\'') bash_dollar_squote = 1; else @@ -11827,7 +11899,7 @@ parsesub: { switch (c) { case ':': c = pgetc_eatbnl(); -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_SUBSTR_EXPANSION /* This check is only needed to not misinterpret * ${VAR:-WORD}, ${VAR:+WORD}, ${VAR:=WORD}, ${VAR:?WORD} * constructs. @@ -11857,7 +11929,7 @@ parsesub: { subtype++; break; } -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_PATTERN_SUBST case '/': /* ${v/[/]pattern/repl} */ //TODO: encode pattern and repl separately. @@ -12112,7 +12184,7 @@ xxreadtoken(void) p += xxreadtoken_doubles + 1; } else { pungetc(); -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_REDIR_OUTPUT2 if (c == '&' && cc == '>') /* &> */ break; /* return readtoken1(...) */ #endif @@ -13274,9 +13346,11 @@ init(void) setvareq((char*)defoptindvar, VTEXTFIXED); setvar0("PPID", utoa(getppid())); -#if ENABLE_ASH_BASH_COMPAT +#if ENABLE_ASH_SHLVL_VAR p = lookupvar("SHLVL"); setvar("SHLVL", utoa((p ? atoi(p) : 0) + 1), VEXPORT); +#endif +#if ENABLE_ASH_HOSTNAME_VAR if (!lookupvar("HOSTNAME")) { struct utsname uts; uname(&uts); diff --git a/shell/hush.c b/shell/hush.c index 68b838378..be64202dc 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -95,18 +95,6 @@ //config: It does not handle select, aliases, tilde expansion, //config: &>file and >&file redirection of stdout+stderr. //config: -//config:config HUSH_BASH_COMPAT -//config: bool "bash-compatible extensions" -//config: default y -//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH -//config: -//config:config HUSH_BRACE_EXPANSION -//config: bool "Brace expansion" -//config: default y -//config: depends on HUSH_BASH_COMPAT -//config: help -//config: Enable {abc,def} extension. -//config: //config:config HUSH_INTERACTIVE //config: bool "Interactive mode" //config: default y @@ -269,6 +257,55 @@ //config: default n //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH //config: +//config:config HUSH_BASH_COMPAT +//config: bool "bash-compatible extensions" +//config: default y +//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH +//config: help +//config: Saying N to this will skip all options about bash-compatible +//config: extensions. This option alone does not add any code. +//config: +//config:config HUSH_BRACE_EXPANSION +//config: bool "Brace expansion" +//config: default y +//config: depends on HUSH_BASH_COMPAT +//config: help +//config: Enable {abc,def} extension. +//config: +//config:config HUSH_SUBSTR_EXPANSION +//config: bool "Substring expansion ${var:position:length}" +//config: default y +//config: depends on HUSH_BASH_COMPAT +//config: select FEATURE_SH_MATH # no-arithmetic version is not implemented +//config: +//config:config HUSH_PATTERN_SUBST +//config: bool "Pattern substitution ${var/pattern/replacement}" +//config: default y +//config: depends on HUSH_BASH_COMPAT +//config: +//config:config HUSH_SOURCE +//config: bool "source builtin" +//config: default y +//config: depends on HUSH_BASH_COMPAT +//config: help +//config: Enable source builtin, which is equivalent to . (dot) builtin. +//config: The availabilty of . (dot) builtin is unaffected by this option. +//config: +//config:config HUSH_TEST2 +//config: bool "test construct [[ EXPR ]]" +//config: default y +//config: depends on HUSH_BASH_COMPAT +//config: help +//config: The test construct [[ EXPR ]] support in hush is currently +//config: limited to supressing word splitting and filename expansion +//config: within brackets. +/* ^^^^^ TODO: update this if new feature gets implemented ^^^^^ */ +//config: +//config:config HUSH_HOSTNAME_VAR +//config: bool "$HOSTNAME variable" +//config: default y +//config: depends on HUSH_BASH_COMPAT +//config: //config:config MSH //config: bool "msh (deprecated: aliased to hush)" //config: default n @@ -411,7 +448,7 @@ #define _SPECIAL_VARS_STR "_*@$!?#" #define SPECIAL_VARS_STR ("_*@$!?#" + 1) #define NUMERIC_SPECVARS_STR ("_*@$!?#" + 3) -#if ENABLE_HUSH_BASH_COMPAT +#if ENABLE_HUSH_PATTERN_SUBST /* Support / and // replace ops */ /* Note that // is stored as \ in "encoded" string representation */ # define VAR_ENCODED_SUBST_OPS "\\/%#:-=+?" @@ -572,7 +609,7 @@ struct command { smallint cmd_type; /* CMD_xxx */ #define CMD_NORMAL 0 #define CMD_SUBSHELL 1 -#if ENABLE_HUSH_BASH_COMPAT +#if ENABLE_HUSH_TEST2 /* used for "[[ EXPR ]]" */ # define CMD_SINGLEWORD_NOGLOB 2 #endif @@ -947,7 +984,7 @@ static int builtin_set(char **argv) FAST_FUNC; #endif static int builtin_shift(char **argv) FAST_FUNC; static int builtin_source(char **argv) FAST_FUNC; -#if ENABLE_HUSH_TEST +#if ENABLE_HUSH_TEST || ENABLE_HUSH_TEST2 static int builtin_test(char **argv) FAST_FUNC; #endif #if ENABLE_HUSH_TRAP @@ -1044,7 +1081,7 @@ static const struct built_in_command bltins1[] = { BLTIN("set" , builtin_set , "Set positional parameters"), #endif BLTIN("shift" , builtin_shift , "Shift positional parameters"), -#if ENABLE_HUSH_BASH_COMPAT +#if ENABLE_HUSH_SOURCE BLTIN("source" , builtin_source , NULL), #endif #if ENABLE_HUSH_TRAP @@ -1073,9 +1110,9 @@ static const struct built_in_command bltins1[] = { static const struct built_in_command bltins2[] = { #if ENABLE_HUSH_TEST BLTIN("[" , builtin_test , NULL), -# if ENABLE_HUSH_BASH_COMPAT +#endif +#if ENABLE_HUSH_TEST2 BLTIN("[[" , builtin_test , NULL), -# endif #endif #if ENABLE_HUSH_ECHO BLTIN("echo" , builtin_echo , NULL), @@ -2177,7 +2214,7 @@ static void unset_vars(char **strings) free(strings); } -#if ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_BASH_COMPAT || ENABLE_HUSH_READ +#if ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_HOSTNAME_VAR || ENABLE_HUSH_READ static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) { char *var = xasprintf("%s=%s", name, val); @@ -3636,7 +3673,7 @@ static int done_word(o_string *word, struct parse_context *ctx) (ctx->ctx_res_w == RES_SNTX)); return (ctx->ctx_res_w == RES_SNTX); } -# if ENABLE_HUSH_BASH_COMPAT +# if ENABLE_HUSH_TEST2 if (strcmp(word->data, "[[") == 0) { command->cmd_type = CMD_SINGLEWORD_NOGLOB; } @@ -4234,7 +4271,7 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign { int ch; char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG; -# if ENABLE_HUSH_BASH_COMPAT +# if ENABLE_HUSH_SUBSTR_EXPANSION || ENABLE_HUSH_PATTERN_SUBST char end_char2 = end_ch >> 8; # endif end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1); @@ -4245,7 +4282,11 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign syntax_error_unterm_ch(end_ch); return 0; } - if (ch == end_ch IF_HUSH_BASH_COMPAT( || ch == end_char2)) { + if (ch == end_ch +# if ENABLE_HUSH_SUBSTR_EXPANSION || ENABLE_HUSH_PATTERN_SUBST + || ch == end_char2 +# endif + ) { if (!dbl) break; /* we look for closing )) of $((EXPR)) */ @@ -4398,14 +4439,14 @@ static int parse_dollar(o_string *as_string, /* Eat everything until closing '}' (or ':') */ end_ch = '}'; - if (ENABLE_HUSH_BASH_COMPAT + if (ENABLE_HUSH_SUBSTR_EXPANSION && ch == ':' && !strchr(MINUS_PLUS_EQUAL_QUESTION, i_peek(input)) ) { /* It's ${var:N[:M]} thing */ end_ch = '}' * 0x100 + ':'; } - if (ENABLE_HUSH_BASH_COMPAT + if (ENABLE_HUSH_PATTERN_SUBST && ch == '/' ) { /* It's ${var/[/]pattern[/repl]} thing */ @@ -4432,7 +4473,9 @@ static int parse_dollar(o_string *as_string, o_addchr(as_string, last_ch); } - if (ENABLE_HUSH_BASH_COMPAT && (end_ch & 0xff00)) { + if ((ENABLE_HUSH_SUBSTR_EXPANSION || ENABLE_HUSH_PATTERN_SUBST) + && (end_ch & 0xff00) + ) { /* close the first block: */ o_addchr(dest, SPECIAL_VAR_SYMBOL); /* while parsing N from ${var:N[:M]} @@ -4443,7 +4486,7 @@ static int parse_dollar(o_string *as_string, goto again; } /* got '}' */ - if (end_ch == '}' * 0x100 + ':') { + if (ENABLE_HUSH_SUBSTR_EXPANSION && end_ch == '}' * 0x100 + ':') { /* it's ${var:N} - emulate :999999999 */ o_addstr(dest, "999999999"); } /* else: it's ${var/[/]pattern} */ @@ -4518,7 +4561,7 @@ static int parse_dollar(o_string *as_string, } #if BB_MMU -# if ENABLE_HUSH_BASH_COMPAT +# if ENABLE_HUSH_PATTERN_SUBST #define encode_string(as_string, dest, input, dquote_end, process_bkslash) \ encode_string(dest, input, dquote_end, process_bkslash) # else @@ -4530,7 +4573,7 @@ static int parse_dollar(o_string *as_string, #else /* !MMU */ -# if ENABLE_HUSH_BASH_COMPAT +# if ENABLE_HUSH_PATTERN_SUBST /* all parameters are needed, no macro tricks */ # else #define encode_string(as_string, dest, input, dquote_end, process_bkslash) \ @@ -4543,7 +4586,7 @@ static int encode_string(o_string *as_string, int dquote_end, int process_bkslash) { -#if !ENABLE_HUSH_BASH_COMPAT +#if !ENABLE_HUSH_PATTERN_SUBST const int process_bkslash = 1; #endif int ch; @@ -5186,7 +5229,7 @@ static struct pipe *parse_stream(char **pstring, /*** Execution routines ***/ /* Expansion can recurse, need forward decls: */ -#if !ENABLE_HUSH_BASH_COMPAT +#if !ENABLE_HUSH_PATTERN_SUBST /* only ${var/pattern/repl} (its pattern part) needs additional mode */ #define expand_string_to_string(str, do_unbackslash) \ expand_string_to_string(str) @@ -5307,7 +5350,7 @@ static int expand_on_ifs(int *ended_with_ifs, o_string *output, int n, const cha * Returns malloced string. * As an optimization, we return NULL if expansion is not needed. */ -#if !ENABLE_HUSH_BASH_COMPAT +#if !ENABLE_HUSH_PATTERN_SUBST /* only ${var/pattern/repl} (its pattern part) needs additional mode */ #define encode_then_expand_string(str, process_bkslash, do_unbackslash) \ encode_then_expand_string(str) @@ -5361,7 +5404,7 @@ static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p) } #endif -#if ENABLE_HUSH_BASH_COMPAT +#if ENABLE_HUSH_PATTERN_SUBST /* ${var/[/]pattern[/repl]} helpers */ static char *strstr_pattern(char *val, const char *pattern, int *size) { @@ -5413,7 +5456,7 @@ static char *replace_pattern(char *val, const char *pattern, const char *repl, c debug_printf_varexp("result:'%s'\n", result); return result; } -#endif +#endif /* HUSH_PATTERN_SUBST */ /* Helper: * Handles <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct. @@ -5461,7 +5504,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha if (exp_op == ':') { exp_op = *exp_word++; //TODO: try ${var:} and ${var:bogus} in non-bash config - if (ENABLE_HUSH_BASH_COMPAT + if (ENABLE_HUSH_SUBSTR_EXPANSION && (!exp_op || !strchr(MINUS_PLUS_EQUAL_QUESTION, exp_op)) ) { /* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */ @@ -5543,7 +5586,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha } } } -#if ENABLE_HUSH_BASH_COMPAT +#if ENABLE_HUSH_PATTERN_SUBST else if (exp_op == '/' || exp_op == '\\') { /* It's ${var/[/]pattern[/repl]} thing. * Note that in encoded form it has TWO parts: @@ -5590,9 +5633,9 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha free(repl); } } -#endif +#endif /* HUSH_PATTERN_SUBST */ else if (exp_op == ':') { -#if ENABLE_HUSH_BASH_COMPAT && ENABLE_FEATURE_SH_MATH +#if ENABLE_HUSH_SUBSTR_EXPANSION && ENABLE_FEATURE_SH_MATH /* It's ${var:N[:M]} bashism. * Note that in encoded form it has TWO parts: * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL> @@ -5628,7 +5671,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha } debug_printf_varexp("val:'%s'\n", val); } else -#endif +#endif /* HUSH_SUBSTR_EXPANSION && FEATURE_SH_MATH */ { die_if_script("malformed ${%s:...}", var); val = NULL; @@ -5837,7 +5880,9 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) #endif default: val = expand_one_var(&to_be_freed, arg, &p); - IF_HUSH_TICK(store_val:) +#if ENABLE_HUSH_TICK + store_val: +#endif if (!(first_ch & 0x80)) { /* unquoted $VAR */ debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val, !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); @@ -5918,7 +5963,7 @@ static char **expand_strvec_to_strvec(char **argv) return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS); } -#if ENABLE_HUSH_BASH_COMPAT +#if ENABLE_HUSH_TEST2 static char **expand_strvec_to_strvec_singleword_noglob(char **argv) { return expand_variables(argv, EXP_FLAG_SINGLEWORD); @@ -5933,7 +5978,7 @@ static char **expand_strvec_to_strvec_singleword_noglob(char **argv) */ static char *expand_string_to_string(const char *str, int do_unbackslash) { -#if !ENABLE_HUSH_BASH_COMPAT +#if !ENABLE_HUSH_PATTERN_SUBST const int do_unbackslash = 1; #endif char *argv[2], **list; @@ -7655,7 +7700,7 @@ static NOINLINE int run_pipe(struct pipe *pi) } /* Expand the rest into (possibly) many strings each */ -#if ENABLE_HUSH_BASH_COMPAT +#if ENABLE_HUSH_TEST2 if (command->cmd_type == CMD_SINGLEWORD_NOGLOB) { argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt); } else @@ -8411,7 +8456,7 @@ int hush_main(int argc, char **argv) /* Export PWD */ set_pwd_var(/*exp:*/ 1); -#if ENABLE_HUSH_BASH_COMPAT +#if ENABLE_HUSH_HOSTNAME_VAR /* Set (but not export) HOSTNAME unless already set */ if (!get_local_var_value("HOSTNAME")) { struct utsname uts; @@ -8819,7 +8864,7 @@ static int run_applet_main(char **argv, int (*applet_main_func)(int argc, char * return applet_main_func(argc, argv - argc); } #endif -#if ENABLE_HUSH_TEST +#if ENABLE_HUSH_TEST || ENABLE_HUSH_TEST2 static int FAST_FUNC builtin_test(char **argv) { return run_applet_main(argv, test_main); -- 2.11.0
_______________________________________________ busybox mailing list [email protected] http://lists.busybox.net/mailman/listinfo/busybox
