aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorNguyễn Thái Ngọc Duy <pclouds@gmail.com>2010-09-14 14:09:23 +1000
committerNguyễn Thái Ngọc Duy <pclouds@gmail.com>2010-09-14 14:09:23 +1000
commitee7c9b2c212fc7db80cce945e094fc2601092283 (patch)
tree24e51b27dbc3e9ab0b00c5839a6822604c02187c /shell
parentf28d4b20905b5b1f52ffa52060a0c6caf4b055ba (diff)
parent99862cbfad9c36b4f8f4378c3a7a9f077c239f20 (diff)
downloadbusybox-w32-ee7c9b2c212fc7db80cce945e094fc2601092283.tar.gz
busybox-w32-ee7c9b2c212fc7db80cce945e094fc2601092283.tar.bz2
busybox-w32-ee7c9b2c212fc7db80cce945e094fc2601092283.zip
Merge remote branch 'origin/master'
Diffstat (limited to 'shell')
-rw-r--r--shell/ash.c388
-rwxr-xr-xshell/ash_test/ash-vars/var_bash3.tests7
-rwxr-xr-xshell/ash_test/ash-vars/var_bash5.tests4
-rw-r--r--shell/bbsh.c223
-rw-r--r--shell/hush.c5151
-rw-r--r--[-rwxr-xr-x]shell/hush_test/hush-bugs/export_exp.tests.disabled (renamed from shell/hush_test/hush-bugs/export_exp.tests)3
-rw-r--r--shell/hush_test/hush-glob/glob2.right18
-rwxr-xr-xshell/hush_test/hush-glob/glob2.tests27
-rw-r--r--shell/hush_test/hush-misc/heredoc_backslash1.right43
-rwxr-xr-xshell/hush_test/hush-misc/heredoc_backslash1.tests70
-rw-r--r--shell/hush_test/hush-psubst/tick3.right2
-rwxr-xr-xshell/hush_test/hush-psubst/tick3.tests6
-rw-r--r--shell/hush_test/hush-trap/exit.right10
-rwxr-xr-xshell/hush_test/hush-trap/exit.tests31
-rwxr-xr-xshell/hush_test/hush-trap/subshell.tests11
-rw-r--r--shell/hush_test/hush-vars/var_bash1.right14
-rwxr-xr-xshell/hush_test/hush-vars/var_bash1.tests18
-rw-r--r--shell/hush_test/hush-vars/var_bash2.right10
-rwxr-xr-xshell/hush_test/hush-vars/var_bash2.tests24
-rw-r--r--shell/hush_test/hush-vars/var_bash3.right20
-rwxr-xr-xshell/hush_test/hush-vars/var_bash3.tests41
-rw-r--r--shell/hush_test/hush-vars/var_bash4.right40
-rwxr-xr-xshell/hush_test/hush-vars/var_bash4.tests81
-rw-r--r--shell/hush_test/hush-vars/var_bash5.right11
-rwxr-xr-xshell/hush_test/hush-vars/var_bash5.tests29
-rw-r--r--shell/hush_test/hush-vars/var_bash6.right5
-rwxr-xr-xshell/hush_test/hush-vars/var_bash6.tests9
-rw-r--r--shell/hush_test/hush-vars/var_unbackslash.right11
-rwxr-xr-xshell/hush_test/hush-vars/var_unbackslash.tests23
-rw-r--r--shell/match.c132
-rw-r--r--shell/match.h29
-rw-r--r--shell/math.c14
-rw-r--r--shell/math.h9
33 files changed, 3502 insertions, 3012 deletions
diff --git a/shell/ash.c b/shell/ash.c
index 6d67a1f8a..0ff5adb04 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -88,7 +88,7 @@
88//applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, _BB_DIR_BIN, _BB_SUID_DROP, sh)) 88//applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, _BB_DIR_BIN, _BB_SUID_DROP, sh))
89//applet:IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, _BB_DIR_BIN, _BB_SUID_DROP, bash)) 89//applet:IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, _BB_DIR_BIN, _BB_SUID_DROP, bash))
90 90
91//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o 91//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
92//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o 92//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
93 93
94//config:config ASH 94//config:config ASH
@@ -1002,7 +1002,8 @@ sharg(union node *arg, FILE *fp)
1002 for (p = arg->narg.text; *p; p++) { 1002 for (p = arg->narg.text; *p; p++) {
1003 switch ((unsigned char)*p) { 1003 switch ((unsigned char)*p) {
1004 case CTLESC: 1004 case CTLESC:
1005 putc(*++p, fp); 1005 p++;
1006 putc(*p, fp);
1006 break; 1007 break;
1007 case CTLVAR: 1008 case CTLVAR:
1008 putc('$', fp); 1009 putc('$', fp);
@@ -1011,8 +1012,10 @@ sharg(union node *arg, FILE *fp)
1011 if (subtype == VSLENGTH) 1012 if (subtype == VSLENGTH)
1012 putc('#', fp); 1013 putc('#', fp);
1013 1014
1014 while (*p != '=') 1015 while (*p != '=') {
1015 putc(*p++, fp); 1016 putc(*p, fp);
1017 p++;
1018 }
1016 1019
1017 if (subtype & VSNUL) 1020 if (subtype & VSNUL)
1018 putc(':', fp); 1021 putc(':', fp);
@@ -2031,10 +2034,6 @@ extern struct globals_var *const ash_ptr_to_globals_var;
2031# define optindval() (voptind.var_text + 7) 2034# define optindval() (voptind.var_text + 7)
2032#endif 2035#endif
2033 2036
2034
2035#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
2036#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
2037
2038#if ENABLE_ASH_GETOPTS 2037#if ENABLE_ASH_GETOPTS
2039static void FAST_FUNC 2038static void FAST_FUNC
2040getoptsreset(const char *value) 2039getoptsreset(const char *value)
@@ -2044,24 +2043,26 @@ getoptsreset(const char *value)
2044} 2043}
2045#endif 2044#endif
2046 2045
2046/* math.h has these, otherwise define our private copies */
2047#if !ENABLE_SH_MATH_SUPPORT
2048#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
2049#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
2047/* 2050/*
2048 * Return of a legal variable name (a letter or underscore followed by zero or 2051 * Return the pointer to the first char which is not part of a legal variable name
2049 * more letters, underscores, and digits). 2052 * (a letter or underscore followed by letters, underscores, and digits).
2050 */ 2053 */
2051static char* FAST_FUNC 2054static const char*
2052endofname(const char *name) 2055endofname(const char *name)
2053{ 2056{
2054 char *p; 2057 if (!is_name(*name))
2055 2058 return name;
2056 p = (char *) name; 2059 while (*++name) {
2057 if (!is_name(*p)) 2060 if (!is_in_name(*name))
2058 return p;
2059 while (*++p) {
2060 if (!is_in_name(*p))
2061 break; 2061 break;
2062 } 2062 }
2063 return p; 2063 return name;
2064} 2064}
2065#endif
2065 2066
2066/* 2067/*
2067 * Compares two strings up to the first = or '\0'. The first 2068 * Compares two strings up to the first = or '\0'. The first
@@ -2244,9 +2245,10 @@ setvareq(char *s, int flags)
2244static void 2245static void
2245setvar(const char *name, const char *val, int flags) 2246setvar(const char *name, const char *val, int flags)
2246{ 2247{
2247 char *p, *q; 2248 const char *q;
2248 size_t namelen; 2249 char *p;
2249 char *nameeq; 2250 char *nameeq;
2251 size_t namelen;
2250 size_t vallen; 2252 size_t vallen;
2251 2253
2252 q = endofname(name); 2254 q = endofname(name);
@@ -2260,12 +2262,13 @@ setvar(const char *name, const char *val, int flags)
2260 } else { 2262 } else {
2261 vallen = strlen(val); 2263 vallen = strlen(val);
2262 } 2264 }
2265
2263 INT_OFF; 2266 INT_OFF;
2264 nameeq = ckmalloc(namelen + vallen + 2); 2267 nameeq = ckmalloc(namelen + vallen + 2);
2265 p = (char *)memcpy(nameeq, name, namelen) + namelen; 2268 p = memcpy(nameeq, name, namelen) + namelen;
2266 if (val) { 2269 if (val) {
2267 *p++ = '='; 2270 *p++ = '=';
2268 p = (char *)memcpy(p, val, vallen) + vallen; 2271 p = memcpy(p, val, vallen) + vallen;
2269 } 2272 }
2270 *p = '\0'; 2273 *p = '\0';
2271 setvareq(nameeq, flags | VNOSAVE); 2274 setvareq(nameeq, flags | VNOSAVE);
@@ -2491,12 +2494,13 @@ static const char *expandstr(const char *ps);
2491#endif 2494#endif
2492 2495
2493static void 2496static void
2494setprompt(int whichprompt) 2497setprompt_if(smallint do_set, int whichprompt)
2495{ 2498{
2496 const char *prompt; 2499 const char *prompt;
2497#if ENABLE_ASH_EXPAND_PRMT 2500 IF_ASH_EXPAND_PRMT(struct stackmark smark;)
2498 struct stackmark smark; 2501
2499#endif 2502 if (!do_set)
2503 return;
2500 2504
2501 needprompt = 0; 2505 needprompt = 0;
2502 2506
@@ -5734,7 +5738,7 @@ ash_arith(const char *s)
5734 5738
5735 math_hooks.lookupvar = lookupvar; 5739 math_hooks.lookupvar = lookupvar;
5736 math_hooks.setvar = setvar2; 5740 math_hooks.setvar = setvar2;
5737 math_hooks.endofname = endofname; 5741 //math_hooks.endofname = endofname;
5738 5742
5739 INT_OFF; 5743 INT_OFF;
5740 result = arith(s, &errcode, &math_hooks); 5744 result = arith(s, &errcode, &math_hooks);
@@ -6326,7 +6330,7 @@ argstr(char *p, int flags, struct strlist *var_str_list)
6326 flags &= ~EXP_TILDE; 6330 flags &= ~EXP_TILDE;
6327 tilde: 6331 tilde:
6328 q = p; 6332 q = p;
6329 if (*q == CTLESC && (flags & EXP_QWORD)) 6333 if ((unsigned char)*q == CTLESC && (flags & EXP_QWORD))
6330 q++; 6334 q++;
6331 if (*q == '~') 6335 if (*q == '~')
6332 p = exptilde(p, q, flags); 6336 p = exptilde(p, q, flags);
@@ -6340,9 +6344,7 @@ argstr(char *p, int flags, struct strlist *var_str_list)
6340 c = p[length]; 6344 c = p[length];
6341 if (c) { 6345 if (c) {
6342 if (!(c & 0x80) 6346 if (!(c & 0x80)
6343#if ENABLE_SH_MATH_SUPPORT 6347 IF_SH_MATH_SUPPORT(|| c == CTLENDARI)
6344 || c == CTLENDARI
6345#endif
6346 ) { 6348 ) {
6347 /* c == '=' || c == ':' || c == CTLENDARI */ 6349 /* c == '=' || c == ':' || c == CTLENDARI */
6348 length++; 6350 length++;
@@ -6389,8 +6391,8 @@ argstr(char *p, int flags, struct strlist *var_str_list)
6389 /* "$@" syntax adherence hack */ 6391 /* "$@" syntax adherence hack */
6390 if (!inquotes 6392 if (!inquotes
6391 && memcmp(p, dolatstr, 4) == 0 6393 && memcmp(p, dolatstr, 4) == 0
6392 && ( p[4] == CTLQUOTEMARK 6394 && ( p[4] == (char)CTLQUOTEMARK
6393 || (p[4] == CTLENDVAR && p[5] == CTLQUOTEMARK) 6395 || (p[4] == (char)CTLENDVAR && p[5] == (char)CTLQUOTEMARK)
6394 ) 6396 )
6395 ) { 6397 ) {
6396 p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1; 6398 p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1;
@@ -6425,8 +6427,7 @@ argstr(char *p, int flags, struct strlist *var_str_list)
6425#endif 6427#endif
6426 } 6428 }
6427 } 6429 }
6428 breakloop: 6430 breakloop: ;
6429 ;
6430} 6431}
6431 6432
6432static char * 6433static char *
@@ -6611,8 +6612,8 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
6611 int zero; 6612 int zero;
6612 char *(*scan)(char*, char*, char*, char*, int, int); 6613 char *(*scan)(char*, char*, char*, char*, int, int);
6613 6614
6614 //bb_error_msg("subevalvar(p:'%s',varname:'%s',strloc:%d,subtype:%d,startloc:%d,varflags:%x,quotes:%d", 6615 //bb_error_msg("subevalvar(p:'%s',varname:'%s',strloc:%d,subtype:%d,startloc:%d,varflags:%x,quotes:%d)",
6615 // p, varname, strloc, subtype, startloc, varflags, quotes); 6616 // p, varname, strloc, subtype, startloc, varflags, quotes);
6616 6617
6617 herefd = -1; 6618 herefd = -1;
6618 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0, 6619 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
@@ -7045,8 +7046,8 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
7045 vsplus: 7046 vsplus:
7046 if (varlen < 0) { 7047 if (varlen < 0) {
7047 argstr( 7048 argstr(
7048 p, flags | EXP_TILDE | 7049 p,
7049 (quoted ? EXP_QWORD : EXP_WORD), 7050 flags | (quoted ? EXP_TILDE|EXP_QWORD : EXP_TILDE|EXP_WORD),
7050 var_str_list 7051 var_str_list
7051 ); 7052 );
7052 goto end; 7053 goto end;
@@ -9870,7 +9871,7 @@ evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9870static int 9871static int
9871goodname(const char *p) 9872goodname(const char *p)
9872{ 9873{
9873 return !*endofname(p); 9874 return endofname(p)[0] == '\0';
9874} 9875}
9875 9876
9876 9877
@@ -11501,7 +11502,6 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
11501 startlinno = g_parsefile->linno; 11502 startlinno = g_parsefile->linno;
11502 bqlist = NULL; 11503 bqlist = NULL;
11503 quotef = 0; 11504 quotef = 0;
11504 oldstyle = 0;
11505 prevsyntax = 0; 11505 prevsyntax = 0;
11506#if ENABLE_ASH_EXPAND_PRMT 11506#if ENABLE_ASH_EXPAND_PRMT
11507 pssyntax = (syntax == PSSYNTAX); 11507 pssyntax = (syntax == PSSYNTAX);
@@ -11517,159 +11517,156 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
11517 STARTSTACKSTR(out); 11517 STARTSTACKSTR(out);
11518 loop: 11518 loop:
11519 /* For each line, until end of word */ 11519 /* For each line, until end of word */
11520 { 11520 CHECKEND(); /* set c to PEOF if at end of here document */
11521 CHECKEND(); /* set c to PEOF if at end of here document */ 11521 for (;;) { /* until end of line or end of word */
11522 for (;;) { /* until end of line or end of word */ 11522 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
11523 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */ 11523 switch (SIT(c, syntax)) {
11524 switch (SIT(c, syntax)) { 11524 case CNL: /* '\n' */
11525 case CNL: /* '\n' */ 11525 if (syntax == BASESYNTAX)
11526 if (syntax == BASESYNTAX) 11526 goto endword; /* exit outer loop */
11527 goto endword; /* exit outer loop */ 11527 USTPUTC(c, out);
11528 USTPUTC(c, out); 11528 g_parsefile->linno++;
11529 g_parsefile->linno++; 11529 setprompt_if(doprompt, 2);
11530 if (doprompt) 11530 c = pgetc();
11531 setprompt(2); 11531 goto loop; /* continue outer loop */
11532 c = pgetc(); 11532 case CWORD:
11533 goto loop; /* continue outer loop */ 11533 USTPUTC(c, out);
11534 case CWORD: 11534 break;
11535 USTPUTC(c, out); 11535 case CCTL:
11536 break; 11536 if (eofmark == NULL || dblquote)
11537 case CCTL: 11537 USTPUTC(CTLESC, out);
11538 if (eofmark == NULL || dblquote)
11539 USTPUTC(CTLESC, out);
11540#if ENABLE_ASH_BASH_COMPAT 11538#if ENABLE_ASH_BASH_COMPAT
11541 if (c == '\\' && bash_dollar_squote) { 11539 if (c == '\\' && bash_dollar_squote) {
11542 c = decode_dollar_squote(); 11540 c = decode_dollar_squote();
11543 if (c & 0x100) { 11541 if (c & 0x100) {
11544 USTPUTC('\\', out); 11542 USTPUTC('\\', out);
11545 c = (unsigned char)c; 11543 c = (unsigned char)c;
11546 }
11547 } 11544 }
11545 }
11548#endif 11546#endif
11549 USTPUTC(c, out); 11547 USTPUTC(c, out);
11550 break; 11548 break;
11551 case CBACK: /* backslash */ 11549 case CBACK: /* backslash */
11552 c = pgetc_without_PEOA(); 11550 c = pgetc_without_PEOA();
11553 if (c == PEOF) { 11551 if (c == PEOF) {
11552 USTPUTC(CTLESC, out);
11553 USTPUTC('\\', out);
11554 pungetc();
11555 } else if (c == '\n') {
11556 setprompt_if(doprompt, 2);
11557 } else {
11558#if ENABLE_ASH_EXPAND_PRMT
11559 if (c == '$' && pssyntax) {
11554 USTPUTC(CTLESC, out); 11560 USTPUTC(CTLESC, out);
11555 USTPUTC('\\', out); 11561 USTPUTC('\\', out);
11556 pungetc(); 11562 }
11557 } else if (c == '\n') {
11558 if (doprompt)
11559 setprompt(2);
11560 } else {
11561#if ENABLE_ASH_EXPAND_PRMT
11562 if (c == '$' && pssyntax) {
11563 USTPUTC(CTLESC, out);
11564 USTPUTC('\\', out);
11565 }
11566#endif 11563#endif
11567 /* Backslash is retained if we are in "str" and next char isn't special */ 11564 /* Backslash is retained if we are in "str" and next char isn't special */
11568 if (dblquote 11565 if (dblquote
11569 && c != '\\' 11566 && c != '\\'
11570 && c != '`' 11567 && c != '`'
11571 && c != '$' 11568 && c != '$'
11572 && (c != '"' || eofmark != NULL) 11569 && (c != '"' || eofmark != NULL)
11573 ) { 11570 ) {
11574 USTPUTC(CTLESC, out); 11571 USTPUTC(CTLESC, out);
11575 USTPUTC('\\', out); 11572 USTPUTC('\\', out);
11576 }
11577 if (SIT(c, SQSYNTAX) == CCTL)
11578 USTPUTC(CTLESC, out);
11579 USTPUTC(c, out);
11580 quotef = 1;
11581 } 11573 }
11582 break; 11574 if (SIT(c, SQSYNTAX) == CCTL)
11583 case CSQUOTE: 11575 USTPUTC(CTLESC, out);
11584 syntax = SQSYNTAX; 11576 USTPUTC(c, out);
11577 quotef = 1;
11578 }
11579 break;
11580 case CSQUOTE:
11581 syntax = SQSYNTAX;
11585 quotemark: 11582 quotemark:
11586 if (eofmark == NULL) { 11583 if (eofmark == NULL) {
11587 USTPUTC(CTLQUOTEMARK, out); 11584 USTPUTC(CTLQUOTEMARK, out);
11585 }
11586 break;
11587 case CDQUOTE:
11588 syntax = DQSYNTAX;
11589 dblquote = 1;
11590 goto quotemark;
11591 case CENDQUOTE:
11592 IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
11593 if (eofmark != NULL && arinest == 0
11594 && varnest == 0
11595 ) {
11596 USTPUTC(c, out);
11597 } else {
11598 if (dqvarnest == 0) {
11599 syntax = BASESYNTAX;
11600 dblquote = 0;
11588 } 11601 }
11589 break; 11602 quotef = 1;
11590 case CDQUOTE:
11591 syntax = DQSYNTAX;
11592 dblquote = 1;
11593 goto quotemark; 11603 goto quotemark;
11594 case CENDQUOTE: 11604 }
11595 IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;) 11605 break;
11596 if (eofmark != NULL && arinest == 0 11606 case CVAR: /* '$' */
11597 && varnest == 0 11607 PARSESUB(); /* parse substitution */
11598 ) { 11608 break;
11599 USTPUTC(c, out); 11609 case CENDVAR: /* '}' */
11600 } else { 11610 if (varnest > 0) {
11601 if (dqvarnest == 0) { 11611 varnest--;
11602 syntax = BASESYNTAX; 11612 if (dqvarnest > 0) {
11603 dblquote = 0; 11613 dqvarnest--;
11604 }
11605 quotef = 1;
11606 goto quotemark;
11607 }
11608 break;
11609 case CVAR: /* '$' */
11610 PARSESUB(); /* parse substitution */
11611 break;
11612 case CENDVAR: /* '}' */
11613 if (varnest > 0) {
11614 varnest--;
11615 if (dqvarnest > 0) {
11616 dqvarnest--;
11617 }
11618 c = CTLENDVAR;
11619 } 11614 }
11620 USTPUTC(c, out); 11615 c = CTLENDVAR;
11621 break; 11616 }
11617 USTPUTC(c, out);
11618 break;
11622#if ENABLE_SH_MATH_SUPPORT 11619#if ENABLE_SH_MATH_SUPPORT
11623 case CLP: /* '(' in arithmetic */ 11620 case CLP: /* '(' in arithmetic */
11624 parenlevel++; 11621 parenlevel++;
11625 USTPUTC(c, out); 11622 USTPUTC(c, out);
11626 break; 11623 break;
11627 case CRP: /* ')' in arithmetic */ 11624 case CRP: /* ')' in arithmetic */
11628 if (parenlevel > 0) { 11625 if (parenlevel > 0) {
11629 parenlevel--; 11626 parenlevel--;
11630 } else { 11627 } else {
11631 if (pgetc() == ')') { 11628 if (pgetc() == ')') {
11632 if (--arinest == 0) { 11629 if (--arinest == 0) {
11633 syntax = prevsyntax; 11630 syntax = prevsyntax;
11634 dblquote = (syntax == DQSYNTAX); 11631 dblquote = (syntax == DQSYNTAX);
11635 c = CTLENDARI; 11632 c = CTLENDARI;
11636 }
11637 } else {
11638 /*
11639 * unbalanced parens
11640 * (don't 2nd guess - no error)
11641 */
11642 pungetc();
11643 } 11633 }
11634 } else {
11635 /*
11636 * unbalanced parens
11637 * (don't 2nd guess - no error)
11638 */
11639 pungetc();
11644 } 11640 }
11645 USTPUTC(c, out); 11641 }
11646 break; 11642 USTPUTC(c, out);
11643 break;
11647#endif 11644#endif
11648 case CBQUOTE: /* '`' */ 11645 case CBQUOTE: /* '`' */
11649 PARSEBACKQOLD(); 11646 PARSEBACKQOLD();
11650 break; 11647 break;
11651 case CENDFILE: 11648 case CENDFILE:
11652 goto endword; /* exit outer loop */ 11649 goto endword; /* exit outer loop */
11653 case CIGN: 11650 case CIGN:
11654 break; 11651 break;
11655 default: 11652 default:
11656 if (varnest == 0) { 11653 if (varnest == 0) {
11657#if ENABLE_ASH_BASH_COMPAT 11654#if ENABLE_ASH_BASH_COMPAT
11658 if (c == '&') { 11655 if (c == '&') {
11659 if (pgetc() == '>') 11656 if (pgetc() == '>')
11660 c = 0x100 + '>'; /* flag &> */ 11657 c = 0x100 + '>'; /* flag &> */
11661 pungetc(); 11658 pungetc();
11662 }
11663#endif
11664 goto endword; /* exit outer loop */
11665 } 11659 }
11666 IF_ASH_ALIAS(if (c != PEOA)) 11660#endif
11667 USTPUTC(c, out); 11661 goto endword; /* exit outer loop */
11668 } 11662 }
11669 c = pgetc_fast(); 11663 IF_ASH_ALIAS(if (c != PEOA))
11670 } /* for (;;) */ 11664 USTPUTC(c, out);
11671 } 11665 }
11666 c = pgetc_fast();
11667 } /* for (;;) */
11672 endword: 11668 endword:
11669
11673#if ENABLE_SH_MATH_SUPPORT 11670#if ENABLE_SH_MATH_SUPPORT
11674 if (syntax == ARISYNTAX) 11671 if (syntax == ARISYNTAX)
11675 raise_error_syntax("missing '))'"); 11672 raise_error_syntax("missing '))'");
@@ -11907,6 +11904,8 @@ parsesub: {
11907 c = pgetc(); 11904 c = pgetc();
11908#if ENABLE_ASH_BASH_COMPAT 11905#if ENABLE_ASH_BASH_COMPAT
11909 if (c == ':' || c == '$' || isdigit(c)) { 11906 if (c == ':' || c == '$' || isdigit(c)) {
11907//TODO: support more general format ${v:EXPR:EXPR},
11908// where EXPR follows $(()) rules
11910 subtype = VSSUBSTR; 11909 subtype = VSSUBSTR;
11911 pungetc(); 11910 pungetc();
11912 break; /* "goto do_pungetc" is bigger (!) */ 11911 break; /* "goto do_pungetc" is bigger (!) */
@@ -11934,6 +11933,9 @@ parsesub: {
11934 } 11933 }
11935#if ENABLE_ASH_BASH_COMPAT 11934#if ENABLE_ASH_BASH_COMPAT
11936 case '/': 11935 case '/':
11936 /* ${v/[/]pattern/repl} */
11937//TODO: encode pattern and repl separately.
11938// Currently ${v/$var_with_slash/repl} is horribly broken
11937 subtype = VSREPLACE; 11939 subtype = VSREPLACE;
11938 c = pgetc(); 11940 c = pgetc();
11939 if (c != '/') 11941 if (c != '/')
@@ -12000,16 +12002,14 @@ parsebackq: {
12000 treatment to some slashes, and then push the string and 12002 treatment to some slashes, and then push the string and
12001 reread it as input, interpreting it normally. */ 12003 reread it as input, interpreting it normally. */
12002 char *pout; 12004 char *pout;
12003 int pc;
12004 size_t psavelen; 12005 size_t psavelen;
12005 char *pstr; 12006 char *pstr;
12006 12007
12007
12008 STARTSTACKSTR(pout); 12008 STARTSTACKSTR(pout);
12009 for (;;) { 12009 for (;;) {
12010 if (needprompt) { 12010 int pc;
12011 setprompt(2); 12011
12012 } 12012 setprompt_if(needprompt, 2);
12013 pc = pgetc(); 12013 pc = pgetc();
12014 switch (pc) { 12014 switch (pc) {
12015 case '`': 12015 case '`':
@@ -12019,8 +12019,7 @@ parsebackq: {
12019 pc = pgetc(); 12019 pc = pgetc();
12020 if (pc == '\n') { 12020 if (pc == '\n') {
12021 g_parsefile->linno++; 12021 g_parsefile->linno++;
12022 if (doprompt) 12022 setprompt_if(doprompt, 2);
12023 setprompt(2);
12024 /* 12023 /*
12025 * If eating a newline, avoid putting 12024 * If eating a newline, avoid putting
12026 * the newline into the new character 12025 * the newline into the new character
@@ -12183,9 +12182,7 @@ xxreadtoken(void)
12183 tokpushback = 0; 12182 tokpushback = 0;
12184 return lasttoken; 12183 return lasttoken;
12185 } 12184 }
12186 if (needprompt) { 12185 setprompt_if(needprompt, 2);
12187 setprompt(2);
12188 }
12189 startlinno = g_parsefile->linno; 12186 startlinno = g_parsefile->linno;
12190 for (;;) { /* until token or start of word found */ 12187 for (;;) { /* until token or start of word found */
12191 c = pgetc_fast(); 12188 c = pgetc_fast();
@@ -12202,8 +12199,7 @@ xxreadtoken(void)
12202 break; /* return readtoken1(...) */ 12199 break; /* return readtoken1(...) */
12203 } 12200 }
12204 startlinno = ++g_parsefile->linno; 12201 startlinno = ++g_parsefile->linno;
12205 if (doprompt) 12202 setprompt_if(doprompt, 2);
12206 setprompt(2);
12207 } else { 12203 } else {
12208 const char *p; 12204 const char *p;
12209 12205
@@ -12249,9 +12245,7 @@ xxreadtoken(void)
12249 tokpushback = 0; 12245 tokpushback = 0;
12250 return lasttoken; 12246 return lasttoken;
12251 } 12247 }
12252 if (needprompt) { 12248 setprompt_if(needprompt, 2);
12253 setprompt(2);
12254 }
12255 startlinno = g_parsefile->linno; 12249 startlinno = g_parsefile->linno;
12256 for (;;) { /* until token or start of word found */ 12250 for (;;) { /* until token or start of word found */
12257 c = pgetc_fast(); 12251 c = pgetc_fast();
@@ -12267,8 +12261,7 @@ xxreadtoken(void)
12267 case '\\': 12261 case '\\':
12268 if (pgetc() == '\n') { 12262 if (pgetc() == '\n') {
12269 startlinno = ++g_parsefile->linno; 12263 startlinno = ++g_parsefile->linno;
12270 if (doprompt) 12264 setprompt_if(doprompt, 2);
12271 setprompt(2);
12272 continue; 12265 continue;
12273 } 12266 }
12274 pungetc(); 12267 pungetc();
@@ -12394,8 +12387,7 @@ parsecmd(int interact)
12394 12387
12395 tokpushback = 0; 12388 tokpushback = 0;
12396 doprompt = interact; 12389 doprompt = interact;
12397 if (doprompt) 12390 setprompt_if(doprompt, doprompt);
12398 setprompt(doprompt);
12399 needprompt = 0; 12391 needprompt = 0;
12400 t = readtoken(); 12392 t = readtoken();
12401 if (t == TEOF) 12393 if (t == TEOF)
@@ -12419,10 +12411,8 @@ parseheredoc(void)
12419 heredoclist = NULL; 12411 heredoclist = NULL;
12420 12412
12421 while (here) { 12413 while (here) {
12422 if (needprompt) { 12414 setprompt_if(needprompt, 2);
12423 setprompt(2); 12415 readtoken1(pgetc(), here->here->type == NHERE ? SQSYNTAX : DQSYNTAX,
12424 }
12425 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
12426 here->eofmark, here->striptabs); 12416 here->eofmark, here->striptabs);
12427 n = stzalloc(sizeof(struct narg)); 12417 n = stzalloc(sizeof(struct narg));
12428 n->narg.type = NARG; 12418 n->narg.type = NARG;
diff --git a/shell/ash_test/ash-vars/var_bash3.tests b/shell/ash_test/ash-vars/var_bash3.tests
index eca3318e2..146dbb6a5 100755
--- a/shell/ash_test/ash-vars/var_bash3.tests
+++ b/shell/ash_test/ash-vars/var_bash3.tests
@@ -3,13 +3,6 @@ r=${a//b/\041#}
3echo 1 $r 3echo 1 $r
4echo 2 ${a//b/\041#} 4echo 2 ${a//b/\041#}
5echo 3 "${a//b/\041#}" 5echo 3 "${a//b/\041#}"
6# --- var_bash3.xx
7# +++ var_bash3.right
8# -1 a\041#c
9# +1 a041#c
10# 2 a041#c
11# -3 a041#c
12# +3 a\041#c
13 6
14a='abc' 7a='abc'
15r=${a//b/\\041#} 8r=${a//b/\\041#}
diff --git a/shell/ash_test/ash-vars/var_bash5.tests b/shell/ash_test/ash-vars/var_bash5.tests
index 3f49321e1..7f482a554 100755
--- a/shell/ash_test/ash-vars/var_bash5.tests
+++ b/shell/ash_test/ash-vars/var_bash5.tests
@@ -1,5 +1,5 @@
1# This testcase checks whether slashes in ${v/a/b} are parsed before or after expansions 1# This testcase checks whether slashes in ${v/a/b} are parsed before
2# in a part 2# or after expansions
3 3
4v='a/b/c' 4v='a/b/c'
5s='b/c' 5s='b/c'
diff --git a/shell/bbsh.c b/shell/bbsh.c
deleted file mode 100644
index 8e25db458..000000000
--- a/shell/bbsh.c
+++ /dev/null
@@ -1,223 +0,0 @@
1/* vi: set ts=4 :
2 *
3 * bbsh - busybox shell
4 *
5 * Copyright 2006 Rob Landley <rob@landley.net>
6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 */
9
10// A section of code that gets repeatedly or conditionally executed is stored
11// as a string and parsed each time it's run.
12
13
14
15// Wheee, debugging.
16
17// Terminal control
18#define ENABLE_BBSH_TTY 0
19
20// &, fg, bg, jobs. (ctrl-z with tty.)
21#define ENABLE_BBSH_JOBCTL 0
22
23// Flow control (if, while, for, functions { })
24#define ENABLE_BBSH_FLOWCTL 0
25
26#define ENABLE_BBSH_ENVVARS 0 // Environment variable support
27
28// Local and synthetic variables, fancy prompts, set, $?, etc.
29#define ENABLE_BBSH_LOCALVARS 0
30
31// Pipes and redirects: | > < >> << && || & () ;
32#define ENABLE_BBSH_PIPES 0
33
34/* Fun:
35
36 echo `echo hello#comment " woot` and more
37*/
38
39#include "libbb.h"
40
41// A single executable, its arguments, and other information we know about it.
42#define BBSH_FLAG_EXIT 1
43#define BBSH_FLAG_SUSPEND 2
44#define BBSH_FLAG_PIPE 4
45#define BBSH_FLAG_AND 8
46#define BBSH_FLAG_OR 16
47#define BBSH_FLAG_AMP 32
48#define BBSH_FLAG_SEMI 64
49#define BBSH_FLAG_PAREN 128
50
51// What we know about a single process.
52struct command {
53 struct command *next;
54 int flags; // exit, suspend, && ||
55 int pid; // pid (or exit code)
56 int argc;
57 char *argv[];
58};
59
60// A collection of processes piped into/waiting on each other.
61struct pipeline {
62 struct pipeline *next;
63 int job_id;
64 struct command *cmd;
65 char *cmdline;
66 int cmdlinelen;
67};
68
69static void free_list(void *list, void (*freeit)(void *data))
70{
71 while (list) {
72 void **next = (void **)list;
73 void *list_next = *next;
74 freeit(list);
75 free(list);
76 list = list_next;
77 }
78}
79
80// Parse one word from the command line, appending one or more argv[] entries
81// to struct command. Handles environment variable substitution and
82// substrings. Returns pointer to next used byte, or NULL if it
83// hit an ending token.
84static char *parse_word(char *start, struct command **cmd)
85{
86 char *end;
87
88 // Detect end of line (and truncate line at comment)
89 if (ENABLE_BBSH_PIPES && strchr("><&|(;", *start)) return 0;
90
91 // Grab next word. (Add dequote and envvar logic here)
92 end = start;
93 end = skip_non_whitespace(end);
94 (*cmd)->argv[(*cmd)->argc++] = xstrndup(start, end-start);
95
96 // Allocate more space if there's no room for NULL terminator.
97
98 if (!((*cmd)->argc & 7))
99 *cmd = xrealloc(*cmd,
100 sizeof(struct command) + ((*cmd)->argc+8)*sizeof(char *));
101 (*cmd)->argv[(*cmd)->argc] = 0;
102 return end;
103}
104
105// Parse a line of text into a pipeline.
106// Returns a pointer to the next line.
107
108static char *parse_pipeline(char *cmdline, struct pipeline *line)
109{
110 struct command **cmd = &(line->cmd);
111 char *start = line->cmdline = cmdline;
112
113 if (!cmdline) return 0;
114
115 if (ENABLE_BBSH_JOBCTL) line->cmdline = cmdline;
116
117 // Parse command into argv[]
118 for (;;) {
119 char *end;
120
121 // Skip leading whitespace and detect end of line.
122 start = skip_whitespace(start);
123 if (!*start || *start=='#') {
124 if (ENABLE_BBSH_JOBCTL) line->cmdlinelen = start-cmdline;
125 return 0;
126 }
127
128 // Allocate next command structure if necessary
129 if (!*cmd) *cmd = xzalloc(sizeof(struct command)+8*sizeof(char *));
130
131 // Parse next argument and add the results to argv[]
132 end = parse_word(start, cmd);
133
134 // If we hit the end of this command, how did it end?
135 if (!end) {
136 if (ENABLE_BBSH_PIPES && *start) {
137 if (*start==';') {
138 start++;
139 break;
140 }
141 // handle | & < > >> << || &&
142 }
143 break;
144 }
145 start = end;
146 }
147
148 if (ENABLE_BBSH_JOBCTL) line->cmdlinelen = start-cmdline;
149
150 return start;
151}
152
153// Execute the commands in a pipeline
154static int run_pipeline(struct pipeline *line)
155{
156 struct command *cmd = line->cmd;
157 if (!cmd || !cmd->argc) return 0;
158
159 // Handle local commands. This is totally fake and plastic.
160 if (cmd->argc==2 && !strcmp(cmd->argv[0],"cd"))
161 chdir(cmd->argv[1]);
162 else if (!strcmp(cmd->argv[0],"exit"))
163 exit(cmd->argc>1 ? atoi(cmd->argv[1]) : 0);
164 else {
165 int status;
166 pid_t pid=fork();
167 if (!pid) {
168 run_applet_and_exit(cmd->argv[0],cmd->argc,cmd->argv);
169 execvp(cmd->argv[0],cmd->argv);
170 printf("No %s", cmd->argv[0]);
171 exit(EXIT_FAILURE);
172 } else waitpid(pid, &status, 0);
173 }
174
175 return 0;
176}
177
178static void free_cmd(void *data)
179{
180 struct command *cmd=(struct command *)data;
181
182 while (cmd->argc) free(cmd->argv[--cmd->argc]);
183}
184
185
186static void handle(char *command)
187{
188 struct pipeline line;
189 char *start = command;
190
191 for (;;) {
192 memset(&line,0,sizeof(struct pipeline));
193 start = parse_pipeline(start, &line);
194 if (!line.cmd) break;
195
196 run_pipeline(&line);
197 free_list(line.cmd, free_cmd);
198 }
199}
200
201int bbsh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
202int bbsh_main(int argc UNUSED_PARAM, char **argv)
203{
204 char *command=NULL;
205 FILE *f;
206
207 getopt32(argv, "c:", &command);
208
209 f = argv[optind] ? xfopen_for_read(argv[optind]) : NULL;
210 if (command) handle(command);
211 else {
212 unsigned cmdlen=0;
213 for (;;) {
214 if (!f) putchar('$');
215 if (1 > getline(&command, &cmdlen, f ? f : stdin)) break;
216
217 handle(command);
218 }
219 if (ENABLE_FEATURE_CLEAN_UP) free(command);
220 }
221
222 return 1;
223}
diff --git a/shell/hush.c b/shell/hush.c
index e8dfb2499..752efd0c8 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -50,7 +50,6 @@
50 * 50 *
51 * Bash compat TODO: 51 * Bash compat TODO:
52 * redirection of stdout+stderr: &> and >& 52 * redirection of stdout+stderr: &> and >&
53 * subst operator: ${var/[/]expr/expr}
54 * brace expansion: one/{two,three,four} 53 * brace expansion: one/{two,three,four}
55 * reserved words: function select 54 * reserved words: function select
56 * advanced test: [[ ]] 55 * advanced test: [[ ]]
@@ -103,7 +102,6 @@
103 102
104//applet:IF_HUSH(APPLET(hush, _BB_DIR_BIN, _BB_SUID_DROP)) 103//applet:IF_HUSH(APPLET(hush, _BB_DIR_BIN, _BB_SUID_DROP))
105//applet:IF_MSH(APPLET(msh, _BB_DIR_BIN, _BB_SUID_DROP)) 104//applet:IF_MSH(APPLET(msh, _BB_DIR_BIN, _BB_SUID_DROP))
106//applet:IF_LASH(APPLET(lash, _BB_DIR_BIN, _BB_SUID_DROP))
107//applet:IF_FEATURE_SH_IS_HUSH(APPLET_ODDNAME(sh, hush, _BB_DIR_BIN, _BB_SUID_DROP, sh)) 105//applet:IF_FEATURE_SH_IS_HUSH(APPLET_ODDNAME(sh, hush, _BB_DIR_BIN, _BB_SUID_DROP, sh))
108//applet:IF_FEATURE_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, _BB_DIR_BIN, _BB_SUID_DROP, bash)) 106//applet:IF_FEATURE_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, _BB_DIR_BIN, _BB_SUID_DROP, bash))
109 107
@@ -148,6 +146,13 @@
148//config: from stdin just like a shell script from a file. 146//config: from stdin just like a shell script from a file.
149//config: No prompt, no PS1/PS2 magic shell variables. 147//config: No prompt, no PS1/PS2 magic shell variables.
150//config: 148//config:
149//config:config HUSH_SAVEHISTORY
150//config: bool "Save command history to .hush_history"
151//config: default y
152//config: depends on HUSH_INTERACTIVE && FEATURE_EDITING_SAVEHISTORY
153//config: help
154//config: Enable history saving in hush.
155//config:
151//config:config HUSH_JOB 156//config:config HUSH_JOB
152//config: bool "Job control" 157//config: bool "Job control"
153//config: default y 158//config: default y
@@ -224,13 +229,6 @@
224//config: This instructs hush to print commands before execution. 229//config: This instructs hush to print commands before execution.
225//config: Adds ~300 bytes. 230//config: Adds ~300 bytes.
226//config: 231//config:
227//config:config LASH
228//config: bool "lash (deprecated: aliased to hush)"
229//config: default n
230//config: select HUSH
231//config: help
232//config: lash is deprecated and will be removed, please migrate to hush.
233//config:
234//config:config MSH 232//config:config MSH
235//config: bool "msh (deprecated: aliased to hush)" 233//config: bool "msh (deprecated: aliased to hush)"
236//config: default n 234//config: default n
@@ -241,10 +239,12 @@
241 239
242//usage:#define hush_trivial_usage NOUSAGE_STR 240//usage:#define hush_trivial_usage NOUSAGE_STR
243//usage:#define hush_full_usage "" 241//usage:#define hush_full_usage ""
244//usage:#define lash_trivial_usage NOUSAGE_STR
245//usage:#define lash_full_usage ""
246//usage:#define msh_trivial_usage NOUSAGE_STR 242//usage:#define msh_trivial_usage NOUSAGE_STR
247//usage:#define msh_full_usage "" 243//usage:#define msh_full_usage ""
244//usage:#define sh_trivial_usage NOUSAGE_STR
245//usage:#define sh_full_usage ""
246//usage:#define bash_trivial_usage NOUSAGE_STR
247//usage:#define bash_full_usage ""
248 248
249 249
250/* Build knobs */ 250/* Build knobs */
@@ -330,6 +330,17 @@
330#define _SPECIAL_VARS_STR "_*@$!?#" 330#define _SPECIAL_VARS_STR "_*@$!?#"
331#define SPECIAL_VARS_STR ("_*@$!?#" + 1) 331#define SPECIAL_VARS_STR ("_*@$!?#" + 1)
332#define NUMERIC_SPECVARS_STR ("_*@$!?#" + 3) 332#define NUMERIC_SPECVARS_STR ("_*@$!?#" + 3)
333#if ENABLE_HUSH_BASH_COMPAT
334/* Support / and // replace ops */
335/* Note that // is stored as \ in "encoded" string representation */
336# define VAR_ENCODED_SUBST_OPS "\\/%#:-=+?"
337# define VAR_SUBST_OPS ("\\/%#:-=+?" + 1)
338# define MINUS_PLUS_EQUAL_QUESTION ("\\/%#:-=+?" + 5)
339#else
340# define VAR_ENCODED_SUBST_OPS "%#:-=+?"
341# define VAR_SUBST_OPS "%#:-=+?"
342# define MINUS_PLUS_EQUAL_QUESTION ("%#:-=+?" + 3)
343#endif
333 344
334#define SPECIAL_VAR_SYMBOL 3 345#define SPECIAL_VAR_SYMBOL 3
335 346
@@ -349,7 +360,7 @@ typedef struct nommu_save_t {
349} nommu_save_t; 360} nommu_save_t;
350#endif 361#endif
351 362
352typedef enum reserved_style { 363enum {
353 RES_NONE = 0, 364 RES_NONE = 0,
354#if ENABLE_HUSH_IF 365#if ENABLE_HUSH_IF
355 RES_IF , 366 RES_IF ,
@@ -378,7 +389,13 @@ typedef enum reserved_style {
378#endif 389#endif
379 RES_XXXX , 390 RES_XXXX ,
380 RES_SNTX 391 RES_SNTX
381} reserved_style; 392};
393
394enum {
395 EXP_FLAG_GLOB = 0x200,
396 EXP_FLAG_ESC_GLOB_CHARS = 0x100,
397 EXP_FLAG_SINGLEWORD = 0x80, /* must be 0x80 */
398};
382 399
383typedef struct o_string { 400typedef struct o_string {
384 char *data; 401 char *data;
@@ -386,11 +403,10 @@ typedef struct o_string {
386 int maxlen; 403 int maxlen;
387 /* Protect newly added chars against globbing 404 /* Protect newly added chars against globbing
388 * (by prepending \ to *, ?, [, \) */ 405 * (by prepending \ to *, ?, [, \) */
389 smallint o_escape; 406 int o_expflags;
390 smallint o_glob;
391 /* At least some part of the string was inside '' or "", 407 /* At least some part of the string was inside '' or "",
392 * possibly empty one: word"", wo''rd etc. */ 408 * possibly empty one: word"", wo''rd etc. */
393 smallint o_quoted; 409 smallint has_quoted_part;
394 smallint has_empty_slot; 410 smallint has_empty_slot;
395 smallint o_assignment; /* 0:maybe, 1:yes, 2:no */ 411 smallint o_assignment; /* 0:maybe, 1:yes, 2:no */
396} o_string; 412} o_string;
@@ -475,20 +491,10 @@ struct command {
475 smallint cmd_type; /* CMD_xxx */ 491 smallint cmd_type; /* CMD_xxx */
476#define CMD_NORMAL 0 492#define CMD_NORMAL 0
477#define CMD_SUBSHELL 1 493#define CMD_SUBSHELL 1
478
479/* used for "[[ EXPR ]]" */
480#if ENABLE_HUSH_BASH_COMPAT 494#if ENABLE_HUSH_BASH_COMPAT
495/* used for "[[ EXPR ]]" */
481# define CMD_SINGLEWORD_NOGLOB 2 496# define CMD_SINGLEWORD_NOGLOB 2
482#endif 497#endif
483
484/* used for "export noglob=* glob* a=`echo a b`" */
485//#define CMD_SINGLEWORD_NOGLOB_COND 3
486// It is hard to implement correctly, it adds significant amounts of tricky code,
487// and all this is only useful for really obscure export statements
488// almost nobody would use anyway. #ifdef CMD_SINGLEWORD_NOGLOB_COND
489// guards the code which implements it, but I have doubts it works
490// in all cases (especially with mixed globbed/non-globbed arguments)
491
492#if ENABLE_HUSH_FUNCTIONS 498#if ENABLE_HUSH_FUNCTIONS
493# define CMD_FUNCDEF 3 499# define CMD_FUNCDEF 3
494#endif 500#endif
@@ -1354,11 +1360,15 @@ static void hush_exit(int exitcode)
1354 /* Prevent recursion: 1360 /* Prevent recursion:
1355 * trap "echo Hi; exit" EXIT; exit 1361 * trap "echo Hi; exit" EXIT; exit
1356 */ 1362 */
1357 char *argv[] = { NULL, G.traps[0], NULL }; 1363 char *argv[3];
1358 G.traps[0] = NULL; 1364 /* argv[0] is unused */
1359 G.exiting = 1; 1365 argv[1] = G.traps[0];
1366 argv[2] = NULL;
1367 G.exiting = 1; /* prevent EXIT trap recursion */
1368 /* Note: G.traps[0] is not cleared!
1369 * "trap" will still show it, if executed
1370 * in the handler */
1360 builtin_eval(argv); 1371 builtin_eval(argv);
1361 free(argv[1]);
1362 } 1372 }
1363 1373
1364#if ENABLE_HUSH_JOB 1374#if ENABLE_HUSH_JOB
@@ -1369,9 +1379,15 @@ static void hush_exit(int exitcode)
1369#endif 1379#endif
1370} 1380}
1371 1381
1382
1372static int check_and_run_traps(int sig) 1383static int check_and_run_traps(int sig)
1373{ 1384{
1374 static const struct timespec zero_timespec; 1385 /* I want it in rodata, not in bss.
1386 * gcc 4.2.1 puts it in rodata only if it has { 0, 0 }
1387 * initializer. But other compilers may still use bss.
1388 * TODO: find more portable solution.
1389 */
1390 static const struct timespec zero_timespec = { 0, 0 };
1375 smalluint save_rcode; 1391 smalluint save_rcode;
1376 int last_sig = 0; 1392 int last_sig = 0;
1377 1393
@@ -1386,10 +1402,12 @@ static int check_and_run_traps(int sig)
1386 if (G.traps && G.traps[sig]) { 1402 if (G.traps && G.traps[sig]) {
1387 if (G.traps[sig][0]) { 1403 if (G.traps[sig][0]) {
1388 /* We have user-defined handler */ 1404 /* We have user-defined handler */
1389 char *argv[] = { NULL, xstrdup(G.traps[sig]), NULL }; 1405 char *argv[3];
1406 /* argv[0] is unused */
1407 argv[1] = G.traps[sig];
1408 argv[2] = NULL;
1390 save_rcode = G.last_exitcode; 1409 save_rcode = G.last_exitcode;
1391 builtin_eval(argv); 1410 builtin_eval(argv);
1392 free(argv[1]);
1393 G.last_exitcode = save_rcode; 1411 G.last_exitcode = save_rcode;
1394 } /* else: "" trap, ignoring signal */ 1412 } /* else: "" trap, ignoring signal */
1395 continue; 1413 continue;
@@ -1449,13 +1467,11 @@ static const char *get_cwd(int force)
1449/* 1467/*
1450 * Shell and environment variable support 1468 * Shell and environment variable support
1451 */ 1469 */
1452static struct variable **get_ptr_to_local_var(const char *name) 1470static struct variable **get_ptr_to_local_var(const char *name, unsigned len)
1453{ 1471{
1454 struct variable **pp; 1472 struct variable **pp;
1455 struct variable *cur; 1473 struct variable *cur;
1456 int len;
1457 1474
1458 len = strlen(name);
1459 pp = &G.top_var; 1475 pp = &G.top_var;
1460 while ((cur = *pp) != NULL) { 1476 while ((cur = *pp) != NULL) {
1461 if (strncmp(cur->varstr, name, len) == 0 && cur->varstr[len] == '=') 1477 if (strncmp(cur->varstr, name, len) == 0 && cur->varstr[len] == '=')
@@ -1465,21 +1481,13 @@ static struct variable **get_ptr_to_local_var(const char *name)
1465 return NULL; 1481 return NULL;
1466} 1482}
1467 1483
1468static struct variable *get_local_var(const char *name)
1469{
1470 struct variable **pp = get_ptr_to_local_var(name);
1471 if (pp)
1472 return *pp;
1473 return NULL;
1474}
1475
1476static const char* FAST_FUNC get_local_var_value(const char *name) 1484static const char* FAST_FUNC get_local_var_value(const char *name)
1477{ 1485{
1478 struct variable **vpp; 1486 struct variable **vpp;
1487 unsigned len = strlen(name);
1479 1488
1480 if (G.expanded_assignments) { 1489 if (G.expanded_assignments) {
1481 char **cpp = G.expanded_assignments; 1490 char **cpp = G.expanded_assignments;
1482 int len = strlen(name);
1483 while (*cpp) { 1491 while (*cpp) {
1484 char *cp = *cpp; 1492 char *cp = *cpp;
1485 if (strncmp(cp, name, len) == 0 && cp[len] == '=') 1493 if (strncmp(cp, name, len) == 0 && cp[len] == '=')
@@ -1488,17 +1496,16 @@ static const char* FAST_FUNC get_local_var_value(const char *name)
1488 } 1496 }
1489 } 1497 }
1490 1498
1491 vpp = get_ptr_to_local_var(name); 1499 vpp = get_ptr_to_local_var(name, len);
1492 if (vpp) 1500 if (vpp)
1493 return strchr((*vpp)->varstr, '=') + 1; 1501 return (*vpp)->varstr + len + 1;
1494 1502
1495 if (strcmp(name, "PPID") == 0) 1503 if (strcmp(name, "PPID") == 0)
1496 return utoa(G.root_ppid); 1504 return utoa(G.root_ppid);
1497 // bash compat: UID? EUID? 1505 // bash compat: UID? EUID?
1498#if ENABLE_HUSH_RANDOM_SUPPORT 1506#if ENABLE_HUSH_RANDOM_SUPPORT
1499 if (strcmp(name, "RANDOM") == 0) { 1507 if (strcmp(name, "RANDOM") == 0)
1500 return utoa(next_random(&G.random_gen)); 1508 return utoa(next_random(&G.random_gen));
1501 }
1502#endif 1509#endif
1503 return NULL; 1510 return NULL;
1504} 1511}
@@ -1687,24 +1694,6 @@ static void unset_vars(char **strings)
1687 free(strings); 1694 free(strings);
1688} 1695}
1689 1696
1690#if ENABLE_SH_MATH_SUPPORT
1691# define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1692# define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1693static char* FAST_FUNC endofname(const char *name)
1694{
1695 char *p;
1696
1697 p = (char *) name;
1698 if (!is_name(*p))
1699 return p;
1700 while (*++p) {
1701 if (!is_in_name(*p))
1702 break;
1703 }
1704 return p;
1705}
1706#endif
1707
1708static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) 1697static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val)
1709{ 1698{
1710 char *var = xasprintf("%s=%s", name, val); 1699 char *var = xasprintf("%s=%s", name, val);
@@ -1748,9 +1737,7 @@ static struct variable *set_vars_and_save_old(char **strings)
1748 1737
1749 eq = strchr(*s, '='); 1738 eq = strchr(*s, '=');
1750 if (eq) { 1739 if (eq) {
1751 *eq = '\0'; 1740 var_pp = get_ptr_to_local_var(*s, eq - *s);
1752 var_pp = get_ptr_to_local_var(*s);
1753 *eq = '=';
1754 if (var_pp) { 1741 if (var_pp) {
1755 /* Remove variable from global linked list */ 1742 /* Remove variable from global linked list */
1756 var_p = *var_pp; 1743 var_p = *var_pp;
@@ -1802,7 +1789,7 @@ static void cmdedit_update_prompt(void)
1802 G.PS2 = "> "; 1789 G.PS2 = "> ";
1803} 1790}
1804 1791
1805static const char* setup_prompt_string(int promptmode) 1792static const char *setup_prompt_string(int promptmode)
1806{ 1793{
1807 const char *prompt_str; 1794 const char *prompt_str;
1808 debug_printf("setup_prompt_string %d ", promptmode); 1795 debug_printf("setup_prompt_string %d ", promptmode);
@@ -1953,7 +1940,7 @@ static void setup_string_in_str(struct in_str *i, const char *s)
1953static void o_reset_to_empty_unquoted(o_string *o) 1940static void o_reset_to_empty_unquoted(o_string *o)
1954{ 1941{
1955 o->length = 0; 1942 o->length = 0;
1956 o->o_quoted = 0; 1943 o->has_quoted_part = 0;
1957 if (o->data) 1944 if (o->data)
1958 o->data[0] = '\0'; 1945 o->data[0] = '\0';
1959} 1946}
@@ -2017,13 +2004,17 @@ static void o_addstr_with_NUL(o_string *o, const char *str)
2017static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len) 2004static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len)
2018{ 2005{
2019 while (len) { 2006 while (len) {
2007 len--;
2020 o_addchr(o, *str); 2008 o_addchr(o, *str);
2021 if (*str++ == '\\' 2009 if (*str++ == '\\') {
2022 && (*str != '*' && *str != '?' && *str != '[') 2010 /* \z -> \\\z; \<eol> -> \\<eol> */
2023 ) {
2024 o_addchr(o, '\\'); 2011 o_addchr(o, '\\');
2012 if (len) {
2013 len--;
2014 o_addchr(o, '\\');
2015 o_addchr(o, *str++);
2016 }
2025 } 2017 }
2026 len--;
2027 } 2018 }
2028} 2019}
2029 2020
@@ -2067,7 +2058,9 @@ static void o_addqchr(o_string *o, int ch)
2067static void o_addQchr(o_string *o, int ch) 2058static void o_addQchr(o_string *o, int ch)
2068{ 2059{
2069 int sz = 1; 2060 int sz = 1;
2070 if (o->o_escape && strchr("*?[\\" MAYBE_BRACES, ch)) { 2061 if ((o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)
2062 && strchr("*?[\\" MAYBE_BRACES, ch)
2063 ) {
2071 sz++; 2064 sz++;
2072 o->data[o->length] = '\\'; 2065 o->data[o->length] = '\\';
2073 o->length++; 2066 o->length++;
@@ -2078,12 +2071,8 @@ static void o_addQchr(o_string *o, int ch)
2078 o->data[o->length] = '\0'; 2071 o->data[o->length] = '\0';
2079} 2072}
2080 2073
2081static void o_addQstr(o_string *o, const char *str, int len) 2074static void o_addqblock(o_string *o, const char *str, int len)
2082{ 2075{
2083 if (!o->o_escape) {
2084 o_addblock(o, str, len);
2085 return;
2086 }
2087 while (len) { 2076 while (len) {
2088 char ch; 2077 char ch;
2089 int sz; 2078 int sz;
@@ -2110,6 +2099,20 @@ static void o_addQstr(o_string *o, const char *str, int len)
2110 } 2099 }
2111} 2100}
2112 2101
2102static void o_addQblock(o_string *o, const char *str, int len)
2103{
2104 if (!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)) {
2105 o_addblock(o, str, len);
2106 return;
2107 }
2108 o_addqblock(o, str, len);
2109}
2110
2111static void o_addQstr(o_string *o, const char *str)
2112{
2113 o_addQblock(o, str, strlen(str));
2114}
2115
2113/* A special kind of o_string for $VAR and `cmd` expansion. 2116/* A special kind of o_string for $VAR and `cmd` expansion.
2114 * It contains char* list[] at the beginning, which is grown in 16 element 2117 * It contains char* list[] at the beginning, which is grown in 16 element
2115 * increments. Actual string data starts at the next multiple of 16 * (char*). 2118 * increments. Actual string data starts at the next multiple of 16 * (char*).
@@ -2128,8 +2131,11 @@ static void debug_print_list(const char *prefix, o_string *o, int n)
2128 int i = 0; 2131 int i = 0;
2129 2132
2130 indent(); 2133 indent();
2131 fprintf(stderr, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d\n", 2134 fprintf(stderr, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d glob:%d quoted:%d escape:%d\n",
2132 prefix, list, n, string_start, o->length, o->maxlen); 2135 prefix, list, n, string_start, o->length, o->maxlen,
2136 !!(o->o_expflags & EXP_FLAG_GLOB),
2137 o->has_quoted_part,
2138 !!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
2133 while (i < n) { 2139 while (i < n) {
2134 indent(); 2140 indent();
2135 fprintf(stderr, " list[%d]=%d '%s' %p\n", i, (int)list[i], 2141 fprintf(stderr, " list[%d]=%d '%s' %p\n", i, (int)list[i],
@@ -2179,7 +2185,7 @@ static int o_save_ptr_helper(o_string *o, int n)
2179 n, string_len, string_start); 2185 n, string_len, string_start);
2180 o->has_empty_slot = 0; 2186 o->has_empty_slot = 0;
2181 } 2187 }
2182 list[n] = (char*)(ptrdiff_t)string_len; 2188 list[n] = (char*)(uintptr_t)string_len;
2183 return n + 1; 2189 return n + 1;
2184} 2190}
2185 2191
@@ -2189,7 +2195,7 @@ static int o_get_last_ptr(o_string *o, int n)
2189 char **list = (char**)o->data; 2195 char **list = (char**)o->data;
2190 int string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]); 2196 int string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
2191 2197
2192 return ((int)(ptrdiff_t)list[n-1]) + string_start; 2198 return ((int)(uintptr_t)list[n-1]) + string_start;
2193} 2199}
2194 2200
2195#ifdef HUSH_BRACE_EXP 2201#ifdef HUSH_BRACE_EXP
@@ -2227,9 +2233,9 @@ static const char *next_brace_sub(const char *cp)
2227 cp++; 2233 cp++;
2228 continue; 2234 continue;
2229 } 2235 }
2230 /*{*/ if ((*cp == '}' && depth-- == 0) || (*cp == ',' && depth == 0)) 2236 if ((*cp == '}' && depth-- == 0) || (*cp == ',' && depth == 0))
2231 break; 2237 break;
2232 if (*cp++ == '{') /*}*/ 2238 if (*cp++ == '{')
2233 depth++; 2239 depth++;
2234 } 2240 }
2235 2241
@@ -2251,7 +2257,7 @@ static int glob_brace(char *pattern, o_string *o, int n)
2251 while (1) { 2257 while (1) {
2252 if (*begin == '\0') 2258 if (*begin == '\0')
2253 goto simple_glob; 2259 goto simple_glob;
2254 if (*begin == '{') /*}*/ { 2260 if (*begin == '{') {
2255 /* Find the first sub-pattern and at the same time 2261 /* Find the first sub-pattern and at the same time
2256 * find the rest after the closing brace */ 2262 * find the rest after the closing brace */
2257 next = next_brace_sub(begin); 2263 next = next_brace_sub(begin);
@@ -2259,7 +2265,7 @@ static int glob_brace(char *pattern, o_string *o, int n)
2259 /* An illegal expression */ 2265 /* An illegal expression */
2260 goto simple_glob; 2266 goto simple_glob;
2261 } 2267 }
2262 /*{*/ if (*next == '}') { 2268 if (*next == '}') {
2263 /* "{abc}" with no commas - illegal 2269 /* "{abc}" with no commas - illegal
2264 * brace expr, disregard and skip it */ 2270 * brace expr, disregard and skip it */
2265 begin = next + 1; 2271 begin = next + 1;
@@ -2276,7 +2282,7 @@ static int glob_brace(char *pattern, o_string *o, int n)
2276 2282
2277 /* Now find the end of the whole brace expression */ 2283 /* Now find the end of the whole brace expression */
2278 rest = next; 2284 rest = next;
2279 /*{*/ while (*rest != '}') { 2285 while (*rest != '}') {
2280 rest = next_brace_sub(rest); 2286 rest = next_brace_sub(rest);
2281 if (rest == NULL) { 2287 if (rest == NULL) {
2282 /* An illegal expression */ 2288 /* An illegal expression */
@@ -2312,7 +2318,7 @@ static int glob_brace(char *pattern, o_string *o, int n)
2312 * That's why we re-copy prefix every time (1st memcpy above). 2318 * That's why we re-copy prefix every time (1st memcpy above).
2313 */ 2319 */
2314 n = glob_brace(new_pattern_buf, o, n); 2320 n = glob_brace(new_pattern_buf, o, n);
2315 /*{*/ if (*next == '}') { 2321 if (*next == '}') {
2316 /* We saw the last entry */ 2322 /* We saw the last entry */
2317 break; 2323 break;
2318 } 2324 }
@@ -2362,11 +2368,11 @@ static int glob_brace(char *pattern, o_string *o, int n)
2362/* Performs globbing on last list[], 2368/* Performs globbing on last list[],
2363 * saving each result as a new list[]. 2369 * saving each result as a new list[].
2364 */ 2370 */
2365static int o_glob(o_string *o, int n) 2371static int perform_glob(o_string *o, int n)
2366{ 2372{
2367 char *pattern, *copy; 2373 char *pattern, *copy;
2368 2374
2369 debug_printf_glob("start o_glob: n:%d o->data:%p\n", n, o->data); 2375 debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
2370 if (!o->data) 2376 if (!o->data)
2371 return o_save_ptr_helper(o, n); 2377 return o_save_ptr_helper(o, n);
2372 pattern = o->data + o_get_last_ptr(o, n); 2378 pattern = o->data + o_get_last_ptr(o, n);
@@ -2384,7 +2390,7 @@ static int o_glob(o_string *o, int n)
2384 n = glob_brace(copy, o, n); 2390 n = glob_brace(copy, o, n);
2385 free(copy); 2391 free(copy);
2386 if (DEBUG_GLOB) 2392 if (DEBUG_GLOB)
2387 debug_print_list("o_glob returning", o, n); 2393 debug_print_list("perform_glob returning", o, n);
2388 return n; 2394 return n;
2389} 2395}
2390 2396
@@ -2409,13 +2415,13 @@ static int glob_needed(const char *s)
2409/* Performs globbing on last list[], 2415/* Performs globbing on last list[],
2410 * saving each result as a new list[]. 2416 * saving each result as a new list[].
2411 */ 2417 */
2412static int o_glob(o_string *o, int n) 2418static int perform_glob(o_string *o, int n)
2413{ 2419{
2414 glob_t globdata; 2420 glob_t globdata;
2415 int gr; 2421 int gr;
2416 char *pattern; 2422 char *pattern;
2417 2423
2418 debug_printf_glob("start o_glob: n:%d o->data:%p\n", n, o->data); 2424 debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
2419 if (!o->data) 2425 if (!o->data)
2420 return o_save_ptr_helper(o, n); 2426 return o_save_ptr_helper(o, n);
2421 pattern = o->data + o_get_last_ptr(o, n); 2427 pattern = o->data + o_get_last_ptr(o, n);
@@ -2461,22 +2467,22 @@ static int o_glob(o_string *o, int n)
2461 } 2467 }
2462 globfree(&globdata); 2468 globfree(&globdata);
2463 if (DEBUG_GLOB) 2469 if (DEBUG_GLOB)
2464 debug_print_list("o_glob returning", o, n); 2470 debug_print_list("perform_glob returning", o, n);
2465 return n; 2471 return n;
2466} 2472}
2467 2473
2468#endif /* !HUSH_BRACE_EXP */ 2474#endif /* !HUSH_BRACE_EXP */
2469 2475
2470/* If o->o_glob == 1, glob the string so far remembered. 2476/* If o->o_expflags & EXP_FLAG_GLOB, glob the string so far remembered.
2471 * Otherwise, just finish current list[] and start new */ 2477 * Otherwise, just finish current list[] and start new */
2472static int o_save_ptr(o_string *o, int n) 2478static int o_save_ptr(o_string *o, int n)
2473{ 2479{
2474 if (o->o_glob) { /* if globbing is requested */ 2480 if (o->o_expflags & EXP_FLAG_GLOB) {
2475 /* If o->has_empty_slot, list[n] was already globbed 2481 /* If o->has_empty_slot, list[n] was already globbed
2476 * (if it was requested back then when it was filled) 2482 * (if it was requested back then when it was filled)
2477 * so don't do that again! */ 2483 * so don't do that again! */
2478 if (!o->has_empty_slot) 2484 if (!o->has_empty_slot)
2479 return o_glob(o, n); /* o_save_ptr_helper is inside */ 2485 return perform_glob(o, n); /* o_save_ptr_helper is inside */
2480 } 2486 }
2481 return o_save_ptr_helper(o, n); 2487 return o_save_ptr_helper(o, n);
2482} 2488}
@@ -2496,25 +2502,1882 @@ static char **o_finalize_list(o_string *o, int n)
2496 list[--n] = NULL; 2502 list[--n] = NULL;
2497 while (n) { 2503 while (n) {
2498 n--; 2504 n--;
2499 list[n] = o->data + (int)(ptrdiff_t)list[n] + string_start; 2505 list[n] = o->data + (int)(uintptr_t)list[n] + string_start;
2500 } 2506 }
2501 return list; 2507 return list;
2502} 2508}
2503 2509
2510static void free_pipe_list(struct pipe *pi);
2504 2511
2505/* Expansion can recurse */ 2512/* Returns pi->next - next pipe in the list */
2506#if ENABLE_HUSH_TICK 2513static struct pipe *free_pipe(struct pipe *pi)
2507static int process_command_subs(o_string *dest, const char *s); 2514{
2515 struct pipe *next;
2516 int i;
2517
2518 debug_printf_clean("free_pipe (pid %d)\n", getpid());
2519 for (i = 0; i < pi->num_cmds; i++) {
2520 struct command *command;
2521 struct redir_struct *r, *rnext;
2522
2523 command = &pi->cmds[i];
2524 debug_printf_clean(" command %d:\n", i);
2525 if (command->argv) {
2526 if (DEBUG_CLEAN) {
2527 int a;
2528 char **p;
2529 for (a = 0, p = command->argv; *p; a++, p++) {
2530 debug_printf_clean(" argv[%d] = %s\n", a, *p);
2531 }
2532 }
2533 free_strings(command->argv);
2534 //command->argv = NULL;
2535 }
2536 /* not "else if": on syntax error, we may have both! */
2537 if (command->group) {
2538 debug_printf_clean(" begin group (cmd_type:%d)\n",
2539 command->cmd_type);
2540 free_pipe_list(command->group);
2541 debug_printf_clean(" end group\n");
2542 //command->group = NULL;
2543 }
2544 /* else is crucial here.
2545 * If group != NULL, child_func is meaningless */
2546#if ENABLE_HUSH_FUNCTIONS
2547 else if (command->child_func) {
2548 debug_printf_exec("cmd %p releases child func at %p\n", command, command->child_func);
2549 command->child_func->parent_cmd = NULL;
2550 }
2551#endif
2552#if !BB_MMU
2553 free(command->group_as_string);
2554 //command->group_as_string = NULL;
2508#endif 2555#endif
2509static char *expand_string_to_string(const char *str); 2556 for (r = command->redirects; r; r = rnext) {
2557 debug_printf_clean(" redirect %d%s",
2558 r->rd_fd, redir_table[r->rd_type].descrip);
2559 /* guard against the case >$FOO, where foo is unset or blank */
2560 if (r->rd_filename) {
2561 debug_printf_clean(" fname:'%s'\n", r->rd_filename);
2562 free(r->rd_filename);
2563 //r->rd_filename = NULL;
2564 }
2565 debug_printf_clean(" rd_dup:%d\n", r->rd_dup);
2566 rnext = r->next;
2567 free(r);
2568 }
2569 //command->redirects = NULL;
2570 }
2571 free(pi->cmds); /* children are an array, they get freed all at once */
2572 //pi->cmds = NULL;
2573#if ENABLE_HUSH_JOB
2574 free(pi->cmdtext);
2575 //pi->cmdtext = NULL;
2576#endif
2577
2578 next = pi->next;
2579 free(pi);
2580 return next;
2581}
2582
2583static void free_pipe_list(struct pipe *pi)
2584{
2585 while (pi) {
2586#if HAS_KEYWORDS
2587 debug_printf_clean("pipe reserved word %d\n", pi->res_word);
2588#endif
2589 debug_printf_clean("pipe followup code %d\n", pi->followup);
2590 pi = free_pipe(pi);
2591 }
2592}
2593
2594
2595/*** Parsing routines ***/
2596
2597static struct pipe *new_pipe(void)
2598{
2599 struct pipe *pi;
2600 pi = xzalloc(sizeof(struct pipe));
2601 /*pi->followup = 0; - deliberately invalid value */
2602 /*pi->res_word = RES_NONE; - RES_NONE is 0 anyway */
2603 return pi;
2604}
2605
2606/* Command (member of a pipe) is complete, or we start a new pipe
2607 * if ctx->command is NULL.
2608 * No errors possible here.
2609 */
2610static int done_command(struct parse_context *ctx)
2611{
2612 /* The command is really already in the pipe structure, so
2613 * advance the pipe counter and make a new, null command. */
2614 struct pipe *pi = ctx->pipe;
2615 struct command *command = ctx->command;
2616
2617 if (command) {
2618 if (IS_NULL_CMD(command)) {
2619 debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds);
2620 goto clear_and_ret;
2621 }
2622 pi->num_cmds++;
2623 debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds);
2624 //debug_print_tree(ctx->list_head, 20);
2625 } else {
2626 debug_printf_parse("done_command: initializing, num_cmds=%d\n", pi->num_cmds);
2627 }
2628
2629 /* Only real trickiness here is that the uncommitted
2630 * command structure is not counted in pi->num_cmds. */
2631 pi->cmds = xrealloc(pi->cmds, sizeof(*pi->cmds) * (pi->num_cmds+1));
2632 ctx->command = command = &pi->cmds[pi->num_cmds];
2633 clear_and_ret:
2634 memset(command, 0, sizeof(*command));
2635 return pi->num_cmds; /* used only for 0/nonzero check */
2636}
2637
2638static void done_pipe(struct parse_context *ctx, pipe_style type)
2639{
2640 int not_null;
2641
2642 debug_printf_parse("done_pipe entered, followup %d\n", type);
2643 /* Close previous command */
2644 not_null = done_command(ctx);
2645 ctx->pipe->followup = type;
2646#if HAS_KEYWORDS
2647 ctx->pipe->pi_inverted = ctx->ctx_inverted;
2648 ctx->ctx_inverted = 0;
2649 ctx->pipe->res_word = ctx->ctx_res_w;
2650#endif
2651
2652 /* Without this check, even just <enter> on command line generates
2653 * tree of three NOPs (!). Which is harmless but annoying.
2654 * IOW: it is safe to do it unconditionally. */
2655 if (not_null
2656#if ENABLE_HUSH_IF
2657 || ctx->ctx_res_w == RES_FI
2658#endif
2659#if ENABLE_HUSH_LOOPS
2660 || ctx->ctx_res_w == RES_DONE
2661 || ctx->ctx_res_w == RES_FOR
2662 || ctx->ctx_res_w == RES_IN
2663#endif
2664#if ENABLE_HUSH_CASE
2665 || ctx->ctx_res_w == RES_ESAC
2666#endif
2667 ) {
2668 struct pipe *new_p;
2669 debug_printf_parse("done_pipe: adding new pipe: "
2670 "not_null:%d ctx->ctx_res_w:%d\n",
2671 not_null, ctx->ctx_res_w);
2672 new_p = new_pipe();
2673 ctx->pipe->next = new_p;
2674 ctx->pipe = new_p;
2675 /* RES_THEN, RES_DO etc are "sticky" -
2676 * they remain set for pipes inside if/while.
2677 * This is used to control execution.
2678 * RES_FOR and RES_IN are NOT sticky (needed to support
2679 * cases where variable or value happens to match a keyword):
2680 */
2681#if ENABLE_HUSH_LOOPS
2682 if (ctx->ctx_res_w == RES_FOR
2683 || ctx->ctx_res_w == RES_IN)
2684 ctx->ctx_res_w = RES_NONE;
2685#endif
2686#if ENABLE_HUSH_CASE
2687 if (ctx->ctx_res_w == RES_MATCH)
2688 ctx->ctx_res_w = RES_CASE_BODY;
2689 if (ctx->ctx_res_w == RES_CASE)
2690 ctx->ctx_res_w = RES_CASE_IN;
2691#endif
2692 ctx->command = NULL; /* trick done_command below */
2693 /* Create the memory for command, roughly:
2694 * ctx->pipe->cmds = new struct command;
2695 * ctx->command = &ctx->pipe->cmds[0];
2696 */
2697 done_command(ctx);
2698 //debug_print_tree(ctx->list_head, 10);
2699 }
2700 debug_printf_parse("done_pipe return\n");
2701}
2702
2703static void initialize_context(struct parse_context *ctx)
2704{
2705 memset(ctx, 0, sizeof(*ctx));
2706 ctx->pipe = ctx->list_head = new_pipe();
2707 /* Create the memory for command, roughly:
2708 * ctx->pipe->cmds = new struct command;
2709 * ctx->command = &ctx->pipe->cmds[0];
2710 */
2711 done_command(ctx);
2712}
2713
2714/* If a reserved word is found and processed, parse context is modified
2715 * and 1 is returned.
2716 */
2717#if HAS_KEYWORDS
2718struct reserved_combo {
2719 char literal[6];
2720 unsigned char res;
2721 unsigned char assignment_flag;
2722 int flag;
2723};
2724enum {
2725 FLAG_END = (1 << RES_NONE ),
2726# if ENABLE_HUSH_IF
2727 FLAG_IF = (1 << RES_IF ),
2728 FLAG_THEN = (1 << RES_THEN ),
2729 FLAG_ELIF = (1 << RES_ELIF ),
2730 FLAG_ELSE = (1 << RES_ELSE ),
2731 FLAG_FI = (1 << RES_FI ),
2732# endif
2733# if ENABLE_HUSH_LOOPS
2734 FLAG_FOR = (1 << RES_FOR ),
2735 FLAG_WHILE = (1 << RES_WHILE),
2736 FLAG_UNTIL = (1 << RES_UNTIL),
2737 FLAG_DO = (1 << RES_DO ),
2738 FLAG_DONE = (1 << RES_DONE ),
2739 FLAG_IN = (1 << RES_IN ),
2740# endif
2741# if ENABLE_HUSH_CASE
2742 FLAG_MATCH = (1 << RES_MATCH),
2743 FLAG_ESAC = (1 << RES_ESAC ),
2744# endif
2745 FLAG_START = (1 << RES_XXXX ),
2746};
2747
2748static const struct reserved_combo* match_reserved_word(o_string *word)
2749{
2750 /* Mostly a list of accepted follow-up reserved words.
2751 * FLAG_END means we are done with the sequence, and are ready
2752 * to turn the compound list into a command.
2753 * FLAG_START means the word must start a new compound list.
2754 */
2755 static const struct reserved_combo reserved_list[] = {
2756# if ENABLE_HUSH_IF
2757 { "!", RES_NONE, NOT_ASSIGNMENT , 0 },
2758 { "if", RES_IF, WORD_IS_KEYWORD, FLAG_THEN | FLAG_START },
2759 { "then", RES_THEN, WORD_IS_KEYWORD, FLAG_ELIF | FLAG_ELSE | FLAG_FI },
2760 { "elif", RES_ELIF, WORD_IS_KEYWORD, FLAG_THEN },
2761 { "else", RES_ELSE, WORD_IS_KEYWORD, FLAG_FI },
2762 { "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END },
2763# endif
2764# if ENABLE_HUSH_LOOPS
2765 { "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START },
2766 { "while", RES_WHILE, WORD_IS_KEYWORD, FLAG_DO | FLAG_START },
2767 { "until", RES_UNTIL, WORD_IS_KEYWORD, FLAG_DO | FLAG_START },
2768 { "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO },
2769 { "do", RES_DO, WORD_IS_KEYWORD, FLAG_DONE },
2770 { "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END },
2771# endif
2772# if ENABLE_HUSH_CASE
2773 { "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START },
2774 { "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END },
2775# endif
2776 };
2777 const struct reserved_combo *r;
2778
2779 for (r = reserved_list; r < reserved_list + ARRAY_SIZE(reserved_list); r++) {
2780 if (strcmp(word->data, r->literal) == 0)
2781 return r;
2782 }
2783 return NULL;
2784}
2785/* Return 0: not a keyword, 1: keyword
2786 */
2787static int reserved_word(o_string *word, struct parse_context *ctx)
2788{
2789# if ENABLE_HUSH_CASE
2790 static const struct reserved_combo reserved_match = {
2791 "", RES_MATCH, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_ESAC
2792 };
2793# endif
2794 const struct reserved_combo *r;
2795
2796 if (word->has_quoted_part)
2797 return 0;
2798 r = match_reserved_word(word);
2799 if (!r)
2800 return 0;
2801
2802 debug_printf("found reserved word %s, res %d\n", r->literal, r->res);
2803# if ENABLE_HUSH_CASE
2804 if (r->res == RES_IN && ctx->ctx_res_w == RES_CASE_IN) {
2805 /* "case word IN ..." - IN part starts first MATCH part */
2806 r = &reserved_match;
2807 } else
2808# endif
2809 if (r->flag == 0) { /* '!' */
2810 if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */
2811 syntax_error("! ! command");
2812 ctx->ctx_res_w = RES_SNTX;
2813 }
2814 ctx->ctx_inverted = 1;
2815 return 1;
2816 }
2817 if (r->flag & FLAG_START) {
2818 struct parse_context *old;
2819
2820 old = xmalloc(sizeof(*old));
2821 debug_printf_parse("push stack %p\n", old);
2822 *old = *ctx; /* physical copy */
2823 initialize_context(ctx);
2824 ctx->stack = old;
2825 } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) {
2826 syntax_error_at(word->data);
2827 ctx->ctx_res_w = RES_SNTX;
2828 return 1;
2829 } else {
2830 /* "{...} fi" is ok. "{...} if" is not
2831 * Example:
2832 * if { echo foo; } then { echo bar; } fi */
2833 if (ctx->command->group)
2834 done_pipe(ctx, PIPE_SEQ);
2835 }
2836
2837 ctx->ctx_res_w = r->res;
2838 ctx->old_flag = r->flag;
2839 word->o_assignment = r->assignment_flag;
2840
2841 if (ctx->old_flag & FLAG_END) {
2842 struct parse_context *old;
2843
2844 done_pipe(ctx, PIPE_SEQ);
2845 debug_printf_parse("pop stack %p\n", ctx->stack);
2846 old = ctx->stack;
2847 old->command->group = ctx->list_head;
2848 old->command->cmd_type = CMD_NORMAL;
2849# if !BB_MMU
2850 o_addstr(&old->as_string, ctx->as_string.data);
2851 o_free_unsafe(&ctx->as_string);
2852 old->command->group_as_string = xstrdup(old->as_string.data);
2853 debug_printf_parse("pop, remembering as:'%s'\n",
2854 old->command->group_as_string);
2855# endif
2856 *ctx = *old; /* physical copy */
2857 free(old);
2858 }
2859 return 1;
2860}
2861#endif /* HAS_KEYWORDS */
2862
2863/* Word is complete, look at it and update parsing context.
2864 * Normal return is 0. Syntax errors return 1.
2865 * Note: on return, word is reset, but not o_free'd!
2866 */
2867static int done_word(o_string *word, struct parse_context *ctx)
2868{
2869 struct command *command = ctx->command;
2870
2871 debug_printf_parse("done_word entered: '%s' %p\n", word->data, command);
2872 if (word->length == 0 && !word->has_quoted_part) {
2873 debug_printf_parse("done_word return 0: true null, ignored\n");
2874 return 0;
2875 }
2876
2877 if (ctx->pending_redirect) {
2878 /* We do not glob in e.g. >*.tmp case. bash seems to glob here
2879 * only if run as "bash", not "sh" */
2880 /* http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html
2881 * "2.7 Redirection
2882 * ...the word that follows the redirection operator
2883 * shall be subjected to tilde expansion, parameter expansion,
2884 * command substitution, arithmetic expansion, and quote
2885 * removal. Pathname expansion shall not be performed
2886 * on the word by a non-interactive shell; an interactive
2887 * shell may perform it, but shall do so only when
2888 * the expansion would result in one word."
2889 */
2890 ctx->pending_redirect->rd_filename = xstrdup(word->data);
2891 /* Cater for >\file case:
2892 * >\a creates file a; >\\a, >"\a", >"\\a" create file \a
2893 * Same with heredocs:
2894 * for <<\H delim is H; <<\\H, <<"\H", <<"\\H" - \H
2895 */
2896 if (ctx->pending_redirect->rd_type == REDIRECT_HEREDOC) {
2897 unbackslash(ctx->pending_redirect->rd_filename);
2898 /* Is it <<"HEREDOC"? */
2899 if (word->has_quoted_part) {
2900 ctx->pending_redirect->rd_dup |= HEREDOC_QUOTED;
2901 }
2902 }
2903 debug_printf_parse("word stored in rd_filename: '%s'\n", word->data);
2904 ctx->pending_redirect = NULL;
2905 } else {
2906 /* If this word wasn't an assignment, next ones definitely
2907 * can't be assignments. Even if they look like ones. */
2908 if (word->o_assignment != DEFINITELY_ASSIGNMENT
2909 && word->o_assignment != WORD_IS_KEYWORD
2910 ) {
2911 word->o_assignment = NOT_ASSIGNMENT;
2912 } else {
2913 if (word->o_assignment == DEFINITELY_ASSIGNMENT)
2914 command->assignment_cnt++;
2915 word->o_assignment = MAYBE_ASSIGNMENT;
2916 }
2917
2918#if HAS_KEYWORDS
2919# if ENABLE_HUSH_CASE
2920 if (ctx->ctx_dsemicolon
2921 && strcmp(word->data, "esac") != 0 /* not "... pattern) cmd;; esac" */
2922 ) {
2923 /* already done when ctx_dsemicolon was set to 1: */
2924 /* ctx->ctx_res_w = RES_MATCH; */
2925 ctx->ctx_dsemicolon = 0;
2926 } else
2927# endif
2928 if (!command->argv /* if it's the first word... */
2929# if ENABLE_HUSH_LOOPS
2930 && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */
2931 && ctx->ctx_res_w != RES_IN
2932# endif
2933# if ENABLE_HUSH_CASE
2934 && ctx->ctx_res_w != RES_CASE
2935# endif
2936 ) {
2937 debug_printf_parse("checking '%s' for reserved-ness\n", word->data);
2938 if (reserved_word(word, ctx)) {
2939 o_reset_to_empty_unquoted(word);
2940 debug_printf_parse("done_word return %d\n",
2941 (ctx->ctx_res_w == RES_SNTX));
2942 return (ctx->ctx_res_w == RES_SNTX);
2943 }
2944# if ENABLE_HUSH_BASH_COMPAT
2945 if (strcmp(word->data, "[[") == 0) {
2946 command->cmd_type = CMD_SINGLEWORD_NOGLOB;
2947 }
2948 /* fall through */
2949# endif
2950 }
2951#endif
2952 if (command->group) {
2953 /* "{ echo foo; } echo bar" - bad */
2954 syntax_error_at(word->data);
2955 debug_printf_parse("done_word return 1: syntax error, "
2956 "groups and arglists don't mix\n");
2957 return 1;
2958 }
2959 if (word->has_quoted_part
2960 /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */
2961 && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL)
2962 /* (otherwise it's known to be not empty and is already safe) */
2963 ) {
2964 /* exclude "$@" - it can expand to no word despite "" */
2965 char *p = word->data;
2966 while (p[0] == SPECIAL_VAR_SYMBOL
2967 && (p[1] & 0x7f) == '@'
2968 && p[2] == SPECIAL_VAR_SYMBOL
2969 ) {
2970 p += 3;
2971 }
2972 if (p == word->data || p[0] != '\0') {
2973 /* saw no "$@", or not only "$@" but some
2974 * real text is there too */
2975 /* insert "empty variable" reference, this makes
2976 * e.g. "", $empty"" etc to not disappear */
2977 o_addchr(word, SPECIAL_VAR_SYMBOL);
2978 o_addchr(word, SPECIAL_VAR_SYMBOL);
2979 }
2980 }
2981 command->argv = add_string_to_strings(command->argv, xstrdup(word->data));
2982 debug_print_strings("word appended to argv", command->argv);
2983 }
2984
2985#if ENABLE_HUSH_LOOPS
2986 if (ctx->ctx_res_w == RES_FOR) {
2987 if (word->has_quoted_part
2988 || !is_well_formed_var_name(command->argv[0], '\0')
2989 ) {
2990 /* bash says just "not a valid identifier" */
2991 syntax_error("not a valid identifier in for");
2992 return 1;
2993 }
2994 /* Force FOR to have just one word (variable name) */
2995 /* NB: basically, this makes hush see "for v in ..."
2996 * syntax as if it is "for v; in ...". FOR and IN become
2997 * two pipe structs in parse tree. */
2998 done_pipe(ctx, PIPE_SEQ);
2999 }
3000#endif
3001#if ENABLE_HUSH_CASE
3002 /* Force CASE to have just one word */
3003 if (ctx->ctx_res_w == RES_CASE) {
3004 done_pipe(ctx, PIPE_SEQ);
3005 }
3006#endif
3007
3008 o_reset_to_empty_unquoted(word);
3009
3010 debug_printf_parse("done_word return 0\n");
3011 return 0;
3012}
3013
3014
3015/* Peek ahead in the input to find out if we have a "&n" construct,
3016 * as in "2>&1", that represents duplicating a file descriptor.
3017 * Return:
3018 * REDIRFD_CLOSE if >&- "close fd" construct is seen,
3019 * REDIRFD_SYNTAX_ERR if syntax error,
3020 * REDIRFD_TO_FILE if no & was seen,
3021 * or the number found.
3022 */
3023#if BB_MMU
3024#define parse_redir_right_fd(as_string, input) \
3025 parse_redir_right_fd(input)
3026#endif
3027static int parse_redir_right_fd(o_string *as_string, struct in_str *input)
3028{
3029 int ch, d, ok;
3030
3031 ch = i_peek(input);
3032 if (ch != '&')
3033 return REDIRFD_TO_FILE;
3034
3035 ch = i_getch(input); /* get the & */
3036 nommu_addchr(as_string, ch);
3037 ch = i_peek(input);
3038 if (ch == '-') {
3039 ch = i_getch(input);
3040 nommu_addchr(as_string, ch);
3041 return REDIRFD_CLOSE;
3042 }
3043 d = 0;
3044 ok = 0;
3045 while (ch != EOF && isdigit(ch)) {
3046 d = d*10 + (ch-'0');
3047 ok = 1;
3048 ch = i_getch(input);
3049 nommu_addchr(as_string, ch);
3050 ch = i_peek(input);
3051 }
3052 if (ok) return d;
3053
3054//TODO: this is the place to catch ">&file" bashism (redirect both fd 1 and 2)
3055
3056 bb_error_msg("ambiguous redirect");
3057 return REDIRFD_SYNTAX_ERR;
3058}
3059
3060/* Return code is 0 normal, 1 if a syntax error is detected
3061 */
3062static int parse_redirect(struct parse_context *ctx,
3063 int fd,
3064 redir_type style,
3065 struct in_str *input)
3066{
3067 struct command *command = ctx->command;
3068 struct redir_struct *redir;
3069 struct redir_struct **redirp;
3070 int dup_num;
3071
3072 dup_num = REDIRFD_TO_FILE;
3073 if (style != REDIRECT_HEREDOC) {
3074 /* Check for a '>&1' type redirect */
3075 dup_num = parse_redir_right_fd(&ctx->as_string, input);
3076 if (dup_num == REDIRFD_SYNTAX_ERR)
3077 return 1;
3078 } else {
3079 int ch = i_peek(input);
3080 dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */
3081 if (dup_num) { /* <<-... */
3082 ch = i_getch(input);
3083 nommu_addchr(&ctx->as_string, ch);
3084 ch = i_peek(input);
3085 }
3086 }
3087
3088 if (style == REDIRECT_OVERWRITE && dup_num == REDIRFD_TO_FILE) {
3089 int ch = i_peek(input);
3090 if (ch == '|') {
3091 /* >|FILE redirect ("clobbering" >).
3092 * Since we do not support "set -o noclobber" yet,
3093 * >| and > are the same for now. Just eat |.
3094 */
3095 ch = i_getch(input);
3096 nommu_addchr(&ctx->as_string, ch);
3097 }
3098 }
3099
3100 /* Create a new redir_struct and append it to the linked list */
3101 redirp = &command->redirects;
3102 while ((redir = *redirp) != NULL) {
3103 redirp = &(redir->next);
3104 }
3105 *redirp = redir = xzalloc(sizeof(*redir));
3106 /* redir->next = NULL; */
3107 /* redir->rd_filename = NULL; */
3108 redir->rd_type = style;
3109 redir->rd_fd = (fd == -1) ? redir_table[style].default_fd : fd;
3110
3111 debug_printf_parse("redirect type %d %s\n", redir->rd_fd,
3112 redir_table[style].descrip);
3113
3114 redir->rd_dup = dup_num;
3115 if (style != REDIRECT_HEREDOC && dup_num != REDIRFD_TO_FILE) {
3116 /* Erik had a check here that the file descriptor in question
3117 * is legit; I postpone that to "run time"
3118 * A "-" representation of "close me" shows up as a -3 here */
3119 debug_printf_parse("duplicating redirect '%d>&%d'\n",
3120 redir->rd_fd, redir->rd_dup);
3121 } else {
3122 /* Set ctx->pending_redirect, so we know what to do at the
3123 * end of the next parsed word. */
3124 ctx->pending_redirect = redir;
3125 }
3126 return 0;
3127}
3128
3129/* If a redirect is immediately preceded by a number, that number is
3130 * supposed to tell which file descriptor to redirect. This routine
3131 * looks for such preceding numbers. In an ideal world this routine
3132 * needs to handle all the following classes of redirects...
3133 * echo 2>foo # redirects fd 2 to file "foo", nothing passed to echo
3134 * echo 49>foo # redirects fd 49 to file "foo", nothing passed to echo
3135 * echo -2>foo # redirects fd 1 to file "foo", "-2" passed to echo
3136 * echo 49x>foo # redirects fd 1 to file "foo", "49x" passed to echo
3137 *
3138 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html
3139 * "2.7 Redirection
3140 * ... If n is quoted, the number shall not be recognized as part of
3141 * the redirection expression. For example:
3142 * echo \2>a
3143 * writes the character 2 into file a"
3144 * We are getting it right by setting ->has_quoted_part on any \<char>
3145 *
3146 * A -1 return means no valid number was found,
3147 * the caller should use the appropriate default for this redirection.
3148 */
3149static int redirect_opt_num(o_string *o)
3150{
3151 int num;
3152
3153 if (o->data == NULL)
3154 return -1;
3155 num = bb_strtou(o->data, NULL, 10);
3156 if (errno || num < 0)
3157 return -1;
3158 o_reset_to_empty_unquoted(o);
3159 return num;
3160}
3161
3162#if BB_MMU
3163#define fetch_till_str(as_string, input, word, skip_tabs) \
3164 fetch_till_str(input, word, skip_tabs)
3165#endif
3166static char *fetch_till_str(o_string *as_string,
3167 struct in_str *input,
3168 const char *word,
3169 int heredoc_flags)
3170{
3171 o_string heredoc = NULL_O_STRING;
3172 unsigned past_EOL;
3173 int prev = 0; /* not \ */
3174 int ch;
3175
3176 goto jump_in;
3177 while (1) {
3178 ch = i_getch(input);
3179 if (ch != EOF)
3180 nommu_addchr(as_string, ch);
3181 if ((ch == '\n' || ch == EOF)
3182 && ((heredoc_flags & HEREDOC_QUOTED) || prev != '\\')
3183 ) {
3184 if (strcmp(heredoc.data + past_EOL, word) == 0) {
3185 heredoc.data[past_EOL] = '\0';
3186 debug_printf_parse("parsed heredoc '%s'\n", heredoc.data);
3187 return heredoc.data;
3188 }
3189 while (ch == '\n') {
3190 o_addchr(&heredoc, ch);
3191 prev = ch;
3192 jump_in:
3193 past_EOL = heredoc.length;
3194 do {
3195 ch = i_getch(input);
3196 if (ch != EOF)
3197 nommu_addchr(as_string, ch);
3198 } while ((heredoc_flags & HEREDOC_SKIPTABS) && ch == '\t');
3199 }
3200 }
3201 if (ch == EOF) {
3202 o_free_unsafe(&heredoc);
3203 return NULL;
3204 }
3205 o_addchr(&heredoc, ch);
3206 nommu_addchr(as_string, ch);
3207 if (prev == '\\' && ch == '\\')
3208 /* Correctly handle foo\\<eol> (not a line cont.) */
3209 prev = 0; /* not \ */
3210 else
3211 prev = ch;
3212 }
3213}
3214
3215/* Look at entire parse tree for not-yet-loaded REDIRECT_HEREDOCs
3216 * and load them all. There should be exactly heredoc_cnt of them.
3217 */
3218static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_str *input)
3219{
3220 struct pipe *pi = ctx->list_head;
3221
3222 while (pi && heredoc_cnt) {
3223 int i;
3224 struct command *cmd = pi->cmds;
3225
3226 debug_printf_parse("fetch_heredocs: num_cmds:%d cmd argv0:'%s'\n",
3227 pi->num_cmds,
3228 cmd->argv ? cmd->argv[0] : "NONE");
3229 for (i = 0; i < pi->num_cmds; i++) {
3230 struct redir_struct *redir = cmd->redirects;
3231
3232 debug_printf_parse("fetch_heredocs: %d cmd argv0:'%s'\n",
3233 i, cmd->argv ? cmd->argv[0] : "NONE");
3234 while (redir) {
3235 if (redir->rd_type == REDIRECT_HEREDOC) {
3236 char *p;
3237
3238 redir->rd_type = REDIRECT_HEREDOC2;
3239 /* redir->rd_dup is (ab)used to indicate <<- */
3240 p = fetch_till_str(&ctx->as_string, input,
3241 redir->rd_filename, redir->rd_dup);
3242 if (!p) {
3243 syntax_error("unexpected EOF in here document");
3244 return 1;
3245 }
3246 free(redir->rd_filename);
3247 redir->rd_filename = p;
3248 heredoc_cnt--;
3249 }
3250 redir = redir->next;
3251 }
3252 cmd++;
3253 }
3254 pi = pi->next;
3255 }
3256#if 0
3257 /* Should be 0. If it isn't, it's a parse error */
3258 if (heredoc_cnt)
3259 bb_error_msg_and_die("heredoc BUG 2");
3260#endif
3261 return 0;
3262}
3263
3264
3265static int run_list(struct pipe *pi);
3266#if BB_MMU
3267#define parse_stream(pstring, input, end_trigger) \
3268 parse_stream(input, end_trigger)
3269#endif
3270static struct pipe *parse_stream(char **pstring,
3271 struct in_str *input,
3272 int end_trigger);
3273
3274
3275#if !ENABLE_HUSH_FUNCTIONS
3276#define parse_group(dest, ctx, input, ch) \
3277 parse_group(ctx, input, ch)
3278#endif
3279static int parse_group(o_string *dest, struct parse_context *ctx,
3280 struct in_str *input, int ch)
3281{
3282 /* dest contains characters seen prior to ( or {.
3283 * Typically it's empty, but for function defs,
3284 * it contains function name (without '()'). */
3285 struct pipe *pipe_list;
3286 int endch;
3287 struct command *command = ctx->command;
3288
3289 debug_printf_parse("parse_group entered\n");
3290#if ENABLE_HUSH_FUNCTIONS
3291 if (ch == '(' && !dest->has_quoted_part) {
3292 if (dest->length)
3293 if (done_word(dest, ctx))
3294 return 1;
3295 if (!command->argv)
3296 goto skip; /* (... */
3297 if (command->argv[1]) { /* word word ... (... */
3298 syntax_error_unexpected_ch('(');
3299 return 1;
3300 }
3301 /* it is "word(..." or "word (..." */
3302 do
3303 ch = i_getch(input);
3304 while (ch == ' ' || ch == '\t');
3305 if (ch != ')') {
3306 syntax_error_unexpected_ch(ch);
3307 return 1;
3308 }
3309 nommu_addchr(&ctx->as_string, ch);
3310 do
3311 ch = i_getch(input);
3312 while (ch == ' ' || ch == '\t' || ch == '\n');
3313 if (ch != '{') {
3314 syntax_error_unexpected_ch(ch);
3315 return 1;
3316 }
3317 nommu_addchr(&ctx->as_string, ch);
3318 command->cmd_type = CMD_FUNCDEF;
3319 goto skip;
3320 }
3321#endif
3322
3323#if 0 /* Prevented by caller */
3324 if (command->argv /* word [word]{... */
3325 || dest->length /* word{... */
3326 || dest->has_quoted_part /* ""{... */
3327 ) {
3328 syntax_error(NULL);
3329 debug_printf_parse("parse_group return 1: "
3330 "syntax error, groups and arglists don't mix\n");
3331 return 1;
3332 }
3333#endif
3334
3335#if ENABLE_HUSH_FUNCTIONS
3336 skip:
3337#endif
3338 endch = '}';
3339 if (ch == '(') {
3340 endch = ')';
3341 command->cmd_type = CMD_SUBSHELL;
3342 } else {
3343 /* bash does not allow "{echo...", requires whitespace */
3344 ch = i_getch(input);
3345 if (ch != ' ' && ch != '\t' && ch != '\n') {
3346 syntax_error_unexpected_ch(ch);
3347 return 1;
3348 }
3349 nommu_addchr(&ctx->as_string, ch);
3350 }
3351
3352 {
2510#if BB_MMU 3353#if BB_MMU
2511#define parse_stream_dquoted(as_string, dest, input, dquote_end) \ 3354# define as_string NULL
2512 parse_stream_dquoted(dest, input, dquote_end) 3355#else
3356 char *as_string = NULL;
3357#endif
3358 pipe_list = parse_stream(&as_string, input, endch);
3359#if !BB_MMU
3360 if (as_string)
3361 o_addstr(&ctx->as_string, as_string);
2513#endif 3362#endif
2514static int parse_stream_dquoted(o_string *as_string, 3363 /* empty ()/{} or parse error? */
3364 if (!pipe_list || pipe_list == ERR_PTR) {
3365 /* parse_stream already emitted error msg */
3366 if (!BB_MMU)
3367 free(as_string);
3368 debug_printf_parse("parse_group return 1: "
3369 "parse_stream returned %p\n", pipe_list);
3370 return 1;
3371 }
3372 command->group = pipe_list;
3373#if !BB_MMU
3374 as_string[strlen(as_string) - 1] = '\0'; /* plink ')' or '}' */
3375 command->group_as_string = as_string;
3376 debug_printf_parse("end of group, remembering as:'%s'\n",
3377 command->group_as_string);
3378#endif
3379#undef as_string
3380 }
3381 debug_printf_parse("parse_group return 0\n");
3382 return 0;
3383 /* command remains "open", available for possible redirects */
3384}
3385
3386#if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS
3387/* Subroutines for copying $(...) and `...` things */
3388static void add_till_backquote(o_string *dest, struct in_str *input, int in_dquote);
3389/* '...' */
3390static void add_till_single_quote(o_string *dest, struct in_str *input)
3391{
3392 while (1) {
3393 int ch = i_getch(input);
3394 if (ch == EOF) {
3395 syntax_error_unterm_ch('\'');
3396 /*xfunc_die(); - redundant */
3397 }
3398 if (ch == '\'')
3399 return;
3400 o_addchr(dest, ch);
3401 }
3402}
3403/* "...\"...`..`...." - do we need to handle "...$(..)..." too? */
3404static void add_till_double_quote(o_string *dest, struct in_str *input)
3405{
3406 while (1) {
3407 int ch = i_getch(input);
3408 if (ch == EOF) {
3409 syntax_error_unterm_ch('"');
3410 /*xfunc_die(); - redundant */
3411 }
3412 if (ch == '"')
3413 return;
3414 if (ch == '\\') { /* \x. Copy both chars. */
3415 o_addchr(dest, ch);
3416 ch = i_getch(input);
3417 }
3418 o_addchr(dest, ch);
3419 if (ch == '`') {
3420 add_till_backquote(dest, input, /*in_dquote:*/ 1);
3421 o_addchr(dest, ch);
3422 continue;
3423 }
3424 //if (ch == '$') ...
3425 }
3426}
3427/* Process `cmd` - copy contents until "`" is seen. Complicated by
3428 * \` quoting.
3429 * "Within the backquoted style of command substitution, backslash
3430 * shall retain its literal meaning, except when followed by: '$', '`', or '\'.
3431 * The search for the matching backquote shall be satisfied by the first
3432 * backquote found without a preceding backslash; during this search,
3433 * if a non-escaped backquote is encountered within a shell comment,
3434 * a here-document, an embedded command substitution of the $(command)
3435 * form, or a quoted string, undefined results occur. A single-quoted
3436 * or double-quoted string that begins, but does not end, within the
3437 * "`...`" sequence produces undefined results."
3438 * Example Output
3439 * echo `echo '\'TEST\`echo ZZ\`BEST` \TESTZZBEST
3440 */
3441static void add_till_backquote(o_string *dest, struct in_str *input, int in_dquote)
3442{
3443 while (1) {
3444 int ch = i_getch(input);
3445 if (ch == '`')
3446 return;
3447 if (ch == '\\') {
3448 /* \x. Copy both unless it is \`, \$, \\ and maybe \" */
3449 ch = i_getch(input);
3450 if (ch != '`'
3451 && ch != '$'
3452 && ch != '\\'
3453 && (!in_dquote || ch != '"')
3454 ) {
3455 o_addchr(dest, '\\');
3456 }
3457 }
3458 if (ch == EOF) {
3459 syntax_error_unterm_ch('`');
3460 /*xfunc_die(); - redundant */
3461 }
3462 o_addchr(dest, ch);
3463 }
3464}
3465/* Process $(cmd) - copy contents until ")" is seen. Complicated by
3466 * quoting and nested ()s.
3467 * "With the $(command) style of command substitution, all characters
3468 * following the open parenthesis to the matching closing parenthesis
3469 * constitute the command. Any valid shell script can be used for command,
3470 * except a script consisting solely of redirections which produces
3471 * unspecified results."
3472 * Example Output
3473 * echo $(echo '(TEST)' BEST) (TEST) BEST
3474 * echo $(echo 'TEST)' BEST) TEST) BEST
3475 * echo $(echo \(\(TEST\) BEST) ((TEST) BEST
3476 *
3477 * Also adapted to eat ${var%...} and $((...)) constructs, since ... part
3478 * can contain arbitrary constructs, just like $(cmd).
3479 * In bash compat mode, it needs to also be able to stop on ':' or '/'
3480 * for ${var:N[:M]} and ${var/P[/R]} parsing.
3481 */
3482#define DOUBLE_CLOSE_CHAR_FLAG 0x80
3483static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsigned end_ch)
3484{
3485 int ch;
3486 char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG;
3487# if ENABLE_HUSH_BASH_COMPAT
3488 char end_char2 = end_ch >> 8;
3489# endif
3490 end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1);
3491
3492 while (1) {
3493 ch = i_getch(input);
3494 if (ch == EOF) {
3495 syntax_error_unterm_ch(end_ch);
3496 /*xfunc_die(); - redundant */
3497 }
3498 if (ch == end_ch IF_HUSH_BASH_COMPAT( || ch == end_char2)) {
3499 if (!dbl)
3500 break;
3501 /* we look for closing )) of $((EXPR)) */
3502 if (i_peek(input) == end_ch) {
3503 i_getch(input); /* eat second ')' */
3504 break;
3505 }
3506 }
3507 o_addchr(dest, ch);
3508 if (ch == '(' || ch == '{') {
3509 ch = (ch == '(' ? ')' : '}');
3510 add_till_closing_bracket(dest, input, ch);
3511 o_addchr(dest, ch);
3512 continue;
3513 }
3514 if (ch == '\'') {
3515 add_till_single_quote(dest, input);
3516 o_addchr(dest, ch);
3517 continue;
3518 }
3519 if (ch == '"') {
3520 add_till_double_quote(dest, input);
3521 o_addchr(dest, ch);
3522 continue;
3523 }
3524 if (ch == '`') {
3525 add_till_backquote(dest, input, /*in_dquote:*/ 0);
3526 o_addchr(dest, ch);
3527 continue;
3528 }
3529 if (ch == '\\') {
3530 /* \x. Copy verbatim. Important for \(, \) */
3531 ch = i_getch(input);
3532 if (ch == EOF) {
3533 syntax_error_unterm_ch(')');
3534 /*xfunc_die(); - redundant */
3535 }
3536 o_addchr(dest, ch);
3537 continue;
3538 }
3539 }
3540 return ch;
3541}
3542#endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS */
3543
3544/* Return code: 0 for OK, 1 for syntax error */
3545#if BB_MMU
3546#define parse_dollar(as_string, dest, input, quote_mask) \
3547 parse_dollar(dest, input, quote_mask)
3548#define as_string NULL
3549#endif
3550static int parse_dollar(o_string *as_string,
3551 o_string *dest,
3552 struct in_str *input, unsigned char quote_mask)
3553{
3554 int ch = i_peek(input); /* first character after the $ */
3555
3556 debug_printf_parse("parse_dollar entered: ch='%c'\n", ch);
3557 if (isalpha(ch)) {
3558 ch = i_getch(input);
3559 nommu_addchr(as_string, ch);
3560 make_var:
3561 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3562 while (1) {
3563 debug_printf_parse(": '%c'\n", ch);
3564 o_addchr(dest, ch | quote_mask);
3565 quote_mask = 0;
3566 ch = i_peek(input);
3567 if (!isalnum(ch) && ch != '_')
3568 break;
3569 ch = i_getch(input);
3570 nommu_addchr(as_string, ch);
3571 }
3572 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3573 } else if (isdigit(ch)) {
3574 make_one_char_var:
3575 ch = i_getch(input);
3576 nommu_addchr(as_string, ch);
3577 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3578 debug_printf_parse(": '%c'\n", ch);
3579 o_addchr(dest, ch | quote_mask);
3580 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3581 } else switch (ch) {
3582 case '$': /* pid */
3583 case '!': /* last bg pid */
3584 case '?': /* last exit code */
3585 case '#': /* number of args */
3586 case '*': /* args */
3587 case '@': /* args */
3588 goto make_one_char_var;
3589 case '{': {
3590 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3591
3592 ch = i_getch(input); /* eat '{' */
3593 nommu_addchr(as_string, ch);
3594
3595 ch = i_getch(input); /* first char after '{' */
3596 /* It should be ${?}, or ${#var},
3597 * or even ${?+subst} - operator acting on a special variable,
3598 * or the beginning of variable name.
3599 */
3600 if (ch == EOF
3601 || (!strchr(_SPECIAL_VARS_STR, ch) && !isalnum(ch)) /* not one of those */
3602 ) {
3603 bad_dollar_syntax:
3604 syntax_error_unterm_str("${name}");
3605 debug_printf_parse("parse_dollar return 1: unterminated ${name}\n");
3606 return 1;
3607 }
3608 nommu_addchr(as_string, ch);
3609 ch |= quote_mask;
3610
3611 /* It's possible to just call add_till_closing_bracket() at this point.
3612 * However, this regresses some of our testsuite cases
3613 * which check invalid constructs like ${%}.
3614 * Oh well... let's check that the var name part is fine... */
3615
3616 while (1) {
3617 unsigned pos;
3618
3619 o_addchr(dest, ch);
3620 debug_printf_parse(": '%c'\n", ch);
3621
3622 ch = i_getch(input);
3623 nommu_addchr(as_string, ch);
3624 if (ch == '}')
3625 break;
3626
3627 if (!isalnum(ch) && ch != '_') {
3628 unsigned end_ch;
3629 unsigned char last_ch;
3630 /* handle parameter expansions
3631 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02
3632 */
3633 if (!strchr(VAR_SUBST_OPS, ch)) /* ${var<bad_char>... */
3634 goto bad_dollar_syntax;
3635
3636 /* Eat everything until closing '}' (or ':') */
3637 end_ch = '}';
3638 if (ENABLE_HUSH_BASH_COMPAT
3639 && ch == ':'
3640 && !strchr(MINUS_PLUS_EQUAL_QUESTION, i_peek(input))
3641 ) {
3642 /* It's ${var:N[:M]} thing */
3643 end_ch = '}' * 0x100 + ':';
3644 }
3645 if (ENABLE_HUSH_BASH_COMPAT
3646 && ch == '/'
3647 ) {
3648 /* It's ${var/[/]pattern[/repl]} thing */
3649 if (i_peek(input) == '/') { /* ${var//pattern[/repl]}? */
3650 i_getch(input);
3651 nommu_addchr(as_string, '/');
3652 ch = '\\';
3653 }
3654 end_ch = '}' * 0x100 + '/';
3655 }
3656 o_addchr(dest, ch);
3657 again:
3658 if (!BB_MMU)
3659 pos = dest->length;
3660#if ENABLE_HUSH_DOLLAR_OPS
3661 last_ch = add_till_closing_bracket(dest, input, end_ch);
3662#else
3663#error Simple code to only allow ${var} is not implemented
3664#endif
3665 if (as_string) {
3666 o_addstr(as_string, dest->data + pos);
3667 o_addchr(as_string, last_ch);
3668 }
3669
3670 if (ENABLE_HUSH_BASH_COMPAT && (end_ch & 0xff00)) {
3671 /* close the first block: */
3672 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3673 /* while parsing N from ${var:N[:M]}
3674 * or pattern from ${var/[/]pattern[/repl]} */
3675 if ((end_ch & 0xff) == last_ch) {
3676 /* got ':' or '/'- parse the rest */
3677 end_ch = '}';
3678 goto again;
3679 }
3680 /* got '}' */
3681 if (end_ch == '}' * 0x100 + ':') {
3682 /* it's ${var:N} - emulate :999999999 */
3683 o_addstr(dest, "999999999");
3684 } /* else: it's ${var/[/]pattern} */
3685 }
3686 break;
3687 }
3688 }
3689 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3690 break;
3691 }
3692#if ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_TICK
3693 case '(': {
3694 unsigned pos;
3695
3696 ch = i_getch(input);
3697 nommu_addchr(as_string, ch);
3698# if ENABLE_SH_MATH_SUPPORT
3699 if (i_peek(input) == '(') {
3700 ch = i_getch(input);
3701 nommu_addchr(as_string, ch);
3702 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3703 o_addchr(dest, /*quote_mask |*/ '+');
3704 if (!BB_MMU)
3705 pos = dest->length;
3706 add_till_closing_bracket(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG);
3707 if (as_string) {
3708 o_addstr(as_string, dest->data + pos);
3709 o_addchr(as_string, ')');
3710 o_addchr(as_string, ')');
3711 }
3712 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3713 break;
3714 }
3715# endif
3716# if ENABLE_HUSH_TICK
3717 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3718 o_addchr(dest, quote_mask | '`');
3719 if (!BB_MMU)
3720 pos = dest->length;
3721 add_till_closing_bracket(dest, input, ')');
3722 if (as_string) {
3723 o_addstr(as_string, dest->data + pos);
3724 o_addchr(as_string, ')');
3725 }
3726 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3727# endif
3728 break;
3729 }
3730#endif
3731 case '_':
3732 ch = i_getch(input);
3733 nommu_addchr(as_string, ch);
3734 ch = i_peek(input);
3735 if (isalnum(ch)) { /* it's $_name or $_123 */
3736 ch = '_';
3737 goto make_var;
3738 }
3739 /* else: it's $_ */
3740 /* TODO: $_ and $-: */
3741 /* $_ Shell or shell script name; or last argument of last command
3742 * (if last command wasn't a pipe; if it was, bash sets $_ to "");
3743 * but in command's env, set to full pathname used to invoke it */
3744 /* $- Option flags set by set builtin or shell options (-i etc) */
3745 default:
3746 o_addQchr(dest, '$');
3747 }
3748 debug_printf_parse("parse_dollar return 0\n");
3749 return 0;
3750#undef as_string
3751}
3752
3753#if BB_MMU
3754# if ENABLE_HUSH_BASH_COMPAT
3755#define encode_string(as_string, dest, input, dquote_end, process_bkslash) \
3756 encode_string(dest, input, dquote_end, process_bkslash)
3757# else
3758/* only ${var/pattern/repl} (its pattern part) needs additional mode */
3759#define encode_string(as_string, dest, input, dquote_end, process_bkslash) \
3760 encode_string(dest, input, dquote_end)
3761# endif
3762#define as_string NULL
3763
3764#else /* !MMU */
3765
3766# if ENABLE_HUSH_BASH_COMPAT
3767/* all parameters are needed, no macro tricks */
3768# else
3769#define encode_string(as_string, dest, input, dquote_end, process_bkslash) \
3770 encode_string(as_string, dest, input, dquote_end)
3771# endif
3772#endif
3773static int encode_string(o_string *as_string,
2515 o_string *dest, 3774 o_string *dest,
2516 struct in_str *input, 3775 struct in_str *input,
2517 int dquote_end); 3776 int dquote_end,
3777 int process_bkslash)
3778{
3779#if !ENABLE_HUSH_BASH_COMPAT
3780 const int process_bkslash = 1;
3781#endif
3782 int ch;
3783 int next;
3784
3785 again:
3786 ch = i_getch(input);
3787 if (ch != EOF)
3788 nommu_addchr(as_string, ch);
3789 if (ch == dquote_end) { /* may be only '"' or EOF */
3790 debug_printf_parse("encode_string return 0\n");
3791 return 0;
3792 }
3793 /* note: can't move it above ch == dquote_end check! */
3794 if (ch == EOF) {
3795 syntax_error_unterm_ch('"');
3796 /*xfunc_die(); - redundant */
3797 }
3798 next = '\0';
3799 if (ch != '\n') {
3800 next = i_peek(input);
3801 }
3802 debug_printf_parse("\" ch=%c (%d) escape=%d\n",
3803 ch, ch, !!(dest->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
3804 if (process_bkslash && ch == '\\') {
3805 if (next == EOF) {
3806 syntax_error("\\<eof>");
3807 xfunc_die();
3808 }
3809 /* bash:
3810 * "The backslash retains its special meaning [in "..."]
3811 * only when followed by one of the following characters:
3812 * $, `, ", \, or <newline>. A double quote may be quoted
3813 * within double quotes by preceding it with a backslash."
3814 * NB: in (unquoted) heredoc, above does not apply to ",
3815 * therefore we check for it by "next == dquote_end" cond.
3816 */
3817 if (next == dquote_end || strchr("$`\\\n", next)) {
3818 ch = i_getch(input); /* eat next */
3819 if (ch == '\n')
3820 goto again; /* skip \<newline> */
3821 } /* else: ch remains == '\\', and we double it below: */
3822 o_addqchr(dest, ch); /* \c if c is a glob char, else just c */
3823 nommu_addchr(as_string, ch);
3824 goto again;
3825 }
3826 if (ch == '$') {
3827 if (parse_dollar(as_string, dest, input, /*quote_mask:*/ 0x80) != 0) {
3828 debug_printf_parse("encode_string return 1: "
3829 "parse_dollar returned non-0\n");
3830 return 1;
3831 }
3832 goto again;
3833 }
3834#if ENABLE_HUSH_TICK
3835 if (ch == '`') {
3836 //unsigned pos = dest->length;
3837 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3838 o_addchr(dest, 0x80 | '`');
3839 add_till_backquote(dest, input, /*in_dquote:*/ dquote_end == '"');
3840 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3841 //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
3842 goto again;
3843 }
3844#endif
3845 o_addQchr(dest, ch);
3846 goto again;
3847#undef as_string
3848}
3849
3850/*
3851 * Scan input until EOF or end_trigger char.
3852 * Return a list of pipes to execute, or NULL on EOF
3853 * or if end_trigger character is met.
3854 * On syntax error, exit is shell is not interactive,
3855 * reset parsing machinery and start parsing anew,
3856 * or return ERR_PTR.
3857 */
3858static struct pipe *parse_stream(char **pstring,
3859 struct in_str *input,
3860 int end_trigger)
3861{
3862 struct parse_context ctx;
3863 o_string dest = NULL_O_STRING;
3864 int heredoc_cnt;
3865
3866 /* Single-quote triggers a bypass of the main loop until its mate is
3867 * found. When recursing, quote state is passed in via dest->o_expflags.
3868 */
3869 debug_printf_parse("parse_stream entered, end_trigger='%c'\n",
3870 end_trigger ? end_trigger : 'X');
3871 debug_enter();
3872
3873 /* If very first arg is "" or '', dest.data may end up NULL.
3874 * Preventing this: */
3875 o_addchr(&dest, '\0');
3876 dest.length = 0;
3877
3878 /* We used to separate words on $IFS here. This was wrong.
3879 * $IFS is used only for word splitting when $var is expanded,
3880 * here we should use blank chars as separators, not $IFS
3881 */
3882
3883 reset: /* we come back here only on syntax errors in interactive shell */
3884
3885#if ENABLE_HUSH_INTERACTIVE
3886 input->promptmode = 0; /* PS1 */
3887#endif
3888 if (MAYBE_ASSIGNMENT != 0)
3889 dest.o_assignment = MAYBE_ASSIGNMENT;
3890 initialize_context(&ctx);
3891 heredoc_cnt = 0;
3892 while (1) {
3893 const char *is_blank;
3894 const char *is_special;
3895 int ch;
3896 int next;
3897 int redir_fd;
3898 redir_type redir_style;
3899
3900 ch = i_getch(input);
3901 debug_printf_parse(": ch=%c (%d) escape=%d\n",
3902 ch, ch, !!(dest.o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
3903 if (ch == EOF) {
3904 struct pipe *pi;
3905
3906 if (heredoc_cnt) {
3907 syntax_error_unterm_str("here document");
3908 goto parse_error;
3909 }
3910 /* end_trigger == '}' case errors out earlier,
3911 * checking only ')' */
3912 if (end_trigger == ')') {
3913 syntax_error_unterm_ch('('); /* exits */
3914 /* goto parse_error; */
3915 }
3916
3917 if (done_word(&dest, &ctx)) {
3918 goto parse_error;
3919 }
3920 o_free(&dest);
3921 done_pipe(&ctx, PIPE_SEQ);
3922 pi = ctx.list_head;
3923 /* If we got nothing... */
3924 /* (this makes bare "&" cmd a no-op.
3925 * bash says: "syntax error near unexpected token '&'") */
3926 if (pi->num_cmds == 0
3927 IF_HAS_KEYWORDS( && pi->res_word == RES_NONE)
3928 ) {
3929 free_pipe_list(pi);
3930 pi = NULL;
3931 }
3932#if !BB_MMU
3933 debug_printf_parse("as_string '%s'\n", ctx.as_string.data);
3934 if (pstring)
3935 *pstring = ctx.as_string.data;
3936 else
3937 o_free_unsafe(&ctx.as_string);
3938#endif
3939 debug_leave();
3940 debug_printf_parse("parse_stream return %p\n", pi);
3941 return pi;
3942 }
3943 nommu_addchr(&ctx.as_string, ch);
3944
3945 next = '\0';
3946 if (ch != '\n')
3947 next = i_peek(input);
3948
3949 is_special = "{}<>;&|()#'" /* special outside of "str" */
3950 "\\$\"" IF_HUSH_TICK("`"); /* always special */
3951 /* Are { and } special here? */
3952 if (ctx.command->argv /* word [word]{... - non-special */
3953 || dest.length /* word{... - non-special */
3954 || dest.has_quoted_part /* ""{... - non-special */
3955 || (next != ';' /* }; - special */
3956 && next != ')' /* }) - special */
3957 && next != '&' /* }& and }&& ... - special */
3958 && next != '|' /* }|| ... - special */
3959 && !strchr(defifs, next) /* {word - non-special */
3960 )
3961 ) {
3962 /* They are not special, skip "{}" */
3963 is_special += 2;
3964 }
3965 is_special = strchr(is_special, ch);
3966 is_blank = strchr(defifs, ch);
3967
3968 if (!is_special && !is_blank) { /* ordinary char */
3969 ordinary_char:
3970 o_addQchr(&dest, ch);
3971 if ((dest.o_assignment == MAYBE_ASSIGNMENT
3972 || dest.o_assignment == WORD_IS_KEYWORD)
3973 && ch == '='
3974 && is_well_formed_var_name(dest.data, '=')
3975 ) {
3976 dest.o_assignment = DEFINITELY_ASSIGNMENT;
3977 }
3978 continue;
3979 }
3980
3981 if (is_blank) {
3982 if (done_word(&dest, &ctx)) {
3983 goto parse_error;
3984 }
3985 if (ch == '\n') {
3986#if ENABLE_HUSH_CASE
3987 /* "case ... in <newline> word) ..." -
3988 * newlines are ignored (but ';' wouldn't be) */
3989 if (ctx.command->argv == NULL
3990 && ctx.ctx_res_w == RES_MATCH
3991 ) {
3992 continue;
3993 }
3994#endif
3995 /* Treat newline as a command separator. */
3996 done_pipe(&ctx, PIPE_SEQ);
3997 debug_printf_parse("heredoc_cnt:%d\n", heredoc_cnt);
3998 if (heredoc_cnt) {
3999 if (fetch_heredocs(heredoc_cnt, &ctx, input)) {
4000 goto parse_error;
4001 }
4002 heredoc_cnt = 0;
4003 }
4004 dest.o_assignment = MAYBE_ASSIGNMENT;
4005 ch = ';';
4006 /* note: if (is_blank) continue;
4007 * will still trigger for us */
4008 }
4009 }
4010
4011 /* "cmd}" or "cmd }..." without semicolon or &:
4012 * } is an ordinary char in this case, even inside { cmd; }
4013 * Pathological example: { ""}; } should exec "}" cmd
4014 */
4015 if (ch == '}') {
4016 if (!IS_NULL_CMD(ctx.command) /* cmd } */
4017 || dest.length != 0 /* word} */
4018 || dest.has_quoted_part /* ""} */
4019 ) {
4020 goto ordinary_char;
4021 }
4022 if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */
4023 goto skip_end_trigger;
4024 /* else: } does terminate a group */
4025 }
4026
4027 if (end_trigger && end_trigger == ch
4028 && (ch != ';' || heredoc_cnt == 0)
4029#if ENABLE_HUSH_CASE
4030 && (ch != ')'
4031 || ctx.ctx_res_w != RES_MATCH
4032 || (!dest.has_quoted_part && strcmp(dest.data, "esac") == 0)
4033 )
4034#endif
4035 ) {
4036 if (heredoc_cnt) {
4037 /* This is technically valid:
4038 * { cat <<HERE; }; echo Ok
4039 * heredoc
4040 * heredoc
4041 * HERE
4042 * but we don't support this.
4043 * We require heredoc to be in enclosing {}/(),
4044 * if any.
4045 */
4046 syntax_error_unterm_str("here document");
4047 goto parse_error;
4048 }
4049 if (done_word(&dest, &ctx)) {
4050 goto parse_error;
4051 }
4052 done_pipe(&ctx, PIPE_SEQ);
4053 dest.o_assignment = MAYBE_ASSIGNMENT;
4054 /* Do we sit outside of any if's, loops or case's? */
4055 if (!HAS_KEYWORDS
4056 IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0))
4057 ) {
4058 o_free(&dest);
4059#if !BB_MMU
4060 debug_printf_parse("as_string '%s'\n", ctx.as_string.data);
4061 if (pstring)
4062 *pstring = ctx.as_string.data;
4063 else
4064 o_free_unsafe(&ctx.as_string);
4065#endif
4066 debug_leave();
4067 debug_printf_parse("parse_stream return %p: "
4068 "end_trigger char found\n",
4069 ctx.list_head);
4070 return ctx.list_head;
4071 }
4072 }
4073 skip_end_trigger:
4074 if (is_blank)
4075 continue;
4076
4077 /* Catch <, > before deciding whether this word is
4078 * an assignment. a=1 2>z b=2: b=2 is still assignment */
4079 switch (ch) {
4080 case '>':
4081 redir_fd = redirect_opt_num(&dest);
4082 if (done_word(&dest, &ctx)) {
4083 goto parse_error;
4084 }
4085 redir_style = REDIRECT_OVERWRITE;
4086 if (next == '>') {
4087 redir_style = REDIRECT_APPEND;
4088 ch = i_getch(input);
4089 nommu_addchr(&ctx.as_string, ch);
4090 }
4091#if 0
4092 else if (next == '(') {
4093 syntax_error(">(process) not supported");
4094 goto parse_error;
4095 }
4096#endif
4097 if (parse_redirect(&ctx, redir_fd, redir_style, input))
4098 goto parse_error;
4099 continue; /* back to top of while (1) */
4100 case '<':
4101 redir_fd = redirect_opt_num(&dest);
4102 if (done_word(&dest, &ctx)) {
4103 goto parse_error;
4104 }
4105 redir_style = REDIRECT_INPUT;
4106 if (next == '<') {
4107 redir_style = REDIRECT_HEREDOC;
4108 heredoc_cnt++;
4109 debug_printf_parse("++heredoc_cnt=%d\n", heredoc_cnt);
4110 ch = i_getch(input);
4111 nommu_addchr(&ctx.as_string, ch);
4112 } else if (next == '>') {
4113 redir_style = REDIRECT_IO;
4114 ch = i_getch(input);
4115 nommu_addchr(&ctx.as_string, ch);
4116 }
4117#if 0
4118 else if (next == '(') {
4119 syntax_error("<(process) not supported");
4120 goto parse_error;
4121 }
4122#endif
4123 if (parse_redirect(&ctx, redir_fd, redir_style, input))
4124 goto parse_error;
4125 continue; /* back to top of while (1) */
4126 }
4127
4128 if (dest.o_assignment == MAYBE_ASSIGNMENT
4129 /* check that we are not in word in "a=1 2>word b=1": */
4130 && !ctx.pending_redirect
4131 ) {
4132 /* ch is a special char and thus this word
4133 * cannot be an assignment */
4134 dest.o_assignment = NOT_ASSIGNMENT;
4135 }
4136
4137 /* Note: nommu_addchr(&ctx.as_string, ch) is already done */
4138
4139 switch (ch) {
4140 case '#':
4141 if (dest.length == 0) {
4142 while (1) {
4143 ch = i_peek(input);
4144 if (ch == EOF || ch == '\n')
4145 break;
4146 i_getch(input);
4147 /* note: we do not add it to &ctx.as_string */
4148 }
4149 nommu_addchr(&ctx.as_string, '\n');
4150 } else {
4151 o_addQchr(&dest, ch);
4152 }
4153 break;
4154 case '\\':
4155 if (next == EOF) {
4156 syntax_error("\\<eof>");
4157 xfunc_die();
4158 }
4159 ch = i_getch(input);
4160 if (ch != '\n') {
4161 o_addchr(&dest, '\\');
4162 /*nommu_addchr(&ctx.as_string, '\\'); - already done */
4163 o_addchr(&dest, ch);
4164 nommu_addchr(&ctx.as_string, ch);
4165 /* Example: echo Hello \2>file
4166 * we need to know that word 2 is quoted */
4167 dest.has_quoted_part = 1;
4168 }
4169#if !BB_MMU
4170 else {
4171 /* It's "\<newline>". Remove trailing '\' from ctx.as_string */
4172 ctx.as_string.data[--ctx.as_string.length] = '\0';
4173 }
4174#endif
4175 break;
4176 case '$':
4177 if (parse_dollar(&ctx.as_string, &dest, input, /*quote_mask:*/ 0) != 0) {
4178 debug_printf_parse("parse_stream parse error: "
4179 "parse_dollar returned non-0\n");
4180 goto parse_error;
4181 }
4182 break;
4183 case '\'':
4184 dest.has_quoted_part = 1;
4185 while (1) {
4186 ch = i_getch(input);
4187 if (ch == EOF) {
4188 syntax_error_unterm_ch('\'');
4189 /*xfunc_die(); - redundant */
4190 }
4191 nommu_addchr(&ctx.as_string, ch);
4192 if (ch == '\'')
4193 break;
4194 o_addqchr(&dest, ch);
4195 }
4196 break;
4197 case '"':
4198 dest.has_quoted_part = 1;
4199 if (dest.o_assignment == NOT_ASSIGNMENT)
4200 dest.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS;
4201 if (encode_string(&ctx.as_string, &dest, input, '"', /*process_bkslash:*/ 1))
4202 goto parse_error;
4203 dest.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS;
4204 break;
4205#if ENABLE_HUSH_TICK
4206 case '`': {
4207 unsigned pos;
4208
4209 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
4210 o_addchr(&dest, '`');
4211 pos = dest.length;
4212 add_till_backquote(&dest, input, /*in_dquote:*/ 0);
4213# if !BB_MMU
4214 o_addstr(&ctx.as_string, dest.data + pos);
4215 o_addchr(&ctx.as_string, '`');
4216# endif
4217 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
4218 //debug_printf_subst("SUBST RES3 '%s'\n", dest.data + pos);
4219 break;
4220 }
4221#endif
4222 case ';':
4223#if ENABLE_HUSH_CASE
4224 case_semi:
4225#endif
4226 if (done_word(&dest, &ctx)) {
4227 goto parse_error;
4228 }
4229 done_pipe(&ctx, PIPE_SEQ);
4230#if ENABLE_HUSH_CASE
4231 /* Eat multiple semicolons, detect
4232 * whether it means something special */
4233 while (1) {
4234 ch = i_peek(input);
4235 if (ch != ';')
4236 break;
4237 ch = i_getch(input);
4238 nommu_addchr(&ctx.as_string, ch);
4239 if (ctx.ctx_res_w == RES_CASE_BODY) {
4240 ctx.ctx_dsemicolon = 1;
4241 ctx.ctx_res_w = RES_MATCH;
4242 break;
4243 }
4244 }
4245#endif
4246 new_cmd:
4247 /* We just finished a cmd. New one may start
4248 * with an assignment */
4249 dest.o_assignment = MAYBE_ASSIGNMENT;
4250 break;
4251 case '&':
4252 if (done_word(&dest, &ctx)) {
4253 goto parse_error;
4254 }
4255 if (next == '&') {
4256 ch = i_getch(input);
4257 nommu_addchr(&ctx.as_string, ch);
4258 done_pipe(&ctx, PIPE_AND);
4259 } else {
4260 done_pipe(&ctx, PIPE_BG);
4261 }
4262 goto new_cmd;
4263 case '|':
4264 if (done_word(&dest, &ctx)) {
4265 goto parse_error;
4266 }
4267#if ENABLE_HUSH_CASE
4268 if (ctx.ctx_res_w == RES_MATCH)
4269 break; /* we are in case's "word | word)" */
4270#endif
4271 if (next == '|') { /* || */
4272 ch = i_getch(input);
4273 nommu_addchr(&ctx.as_string, ch);
4274 done_pipe(&ctx, PIPE_OR);
4275 } else {
4276 /* we could pick up a file descriptor choice here
4277 * with redirect_opt_num(), but bash doesn't do it.
4278 * "echo foo 2| cat" yields "foo 2". */
4279 done_command(&ctx);
4280#if !BB_MMU
4281 o_reset_to_empty_unquoted(&ctx.as_string);
4282#endif
4283 }
4284 goto new_cmd;
4285 case '(':
4286#if ENABLE_HUSH_CASE
4287 /* "case... in [(]word)..." - skip '(' */
4288 if (ctx.ctx_res_w == RES_MATCH
4289 && ctx.command->argv == NULL /* not (word|(... */
4290 && dest.length == 0 /* not word(... */
4291 && dest.has_quoted_part == 0 /* not ""(... */
4292 ) {
4293 continue;
4294 }
4295#endif
4296 case '{':
4297 if (parse_group(&dest, &ctx, input, ch) != 0) {
4298 goto parse_error;
4299 }
4300 goto new_cmd;
4301 case ')':
4302#if ENABLE_HUSH_CASE
4303 if (ctx.ctx_res_w == RES_MATCH)
4304 goto case_semi;
4305#endif
4306 case '}':
4307 /* proper use of this character is caught by end_trigger:
4308 * if we see {, we call parse_group(..., end_trigger='}')
4309 * and it will match } earlier (not here). */
4310 syntax_error_unexpected_ch(ch);
4311 goto parse_error;
4312 default:
4313 if (HUSH_DEBUG)
4314 bb_error_msg_and_die("BUG: unexpected %c\n", ch);
4315 }
4316 } /* while (1) */
4317
4318 parse_error:
4319 {
4320 struct parse_context *pctx;
4321 IF_HAS_KEYWORDS(struct parse_context *p2;)
4322
4323 /* Clean up allocated tree.
4324 * Sample for finding leaks on syntax error recovery path.
4325 * Run it from interactive shell, watch pmap `pidof hush`.
4326 * while if false; then false; fi; do break; fi
4327 * Samples to catch leaks at execution:
4328 * while if (true | {true;}); then echo ok; fi; do break; done
4329 * while if (true | {true;}); then echo ok; fi; do (if echo ok; break; then :; fi) | cat; break; done
4330 */
4331 pctx = &ctx;
4332 do {
4333 /* Update pipe/command counts,
4334 * otherwise freeing may miss some */
4335 done_pipe(pctx, PIPE_SEQ);
4336 debug_printf_clean("freeing list %p from ctx %p\n",
4337 pctx->list_head, pctx);
4338 debug_print_tree(pctx->list_head, 0);
4339 free_pipe_list(pctx->list_head);
4340 debug_printf_clean("freed list %p\n", pctx->list_head);
4341#if !BB_MMU
4342 o_free_unsafe(&pctx->as_string);
4343#endif
4344 IF_HAS_KEYWORDS(p2 = pctx->stack;)
4345 if (pctx != &ctx) {
4346 free(pctx);
4347 }
4348 IF_HAS_KEYWORDS(pctx = p2;)
4349 } while (HAS_KEYWORDS && pctx);
4350 /* Free text, clear all dest fields */
4351 o_free(&dest);
4352 /* If we are not in top-level parse, we return,
4353 * our caller will propagate error.
4354 */
4355 if (end_trigger != ';') {
4356#if !BB_MMU
4357 if (pstring)
4358 *pstring = NULL;
4359#endif
4360 debug_leave();
4361 return ERR_PTR;
4362 }
4363 /* Discard cached input, force prompt */
4364 input->p = NULL;
4365 IF_HUSH_INTERACTIVE(input->promptme = 1;)
4366 goto reset;
4367 }
4368}
4369
4370
4371/*** Execution routines ***/
4372
4373/* Expansion can recurse, need forward decls: */
4374#if !ENABLE_HUSH_BASH_COMPAT
4375/* only ${var/pattern/repl} (its pattern part) needs additional mode */
4376#define expand_string_to_string(str, do_unbackslash) \
4377 expand_string_to_string(str)
4378#endif
4379static char *expand_string_to_string(const char *str, int do_unbackslash);
4380static int process_command_subs(o_string *dest, const char *s);
2518 4381
2519/* expand_strvec_to_strvec() takes a list of strings, expands 4382/* expand_strvec_to_strvec() takes a list of strings, expands
2520 * all variable references within and returns a pointer to 4383 * all variable references within and returns a pointer to
@@ -2522,7 +4385,7 @@ static int parse_stream_dquoted(o_string *as_string,
2522 * of strings. (Think VAR="a b"; echo $VAR). 4385 * of strings. (Think VAR="a b"; echo $VAR).
2523 * This new list is allocated as a single malloc block. 4386 * This new list is allocated as a single malloc block.
2524 * NULL-terminated list of char* pointers is at the beginning of it, 4387 * NULL-terminated list of char* pointers is at the beginning of it,
2525 * followed by strings themself. 4388 * followed by strings themselves.
2526 * Caller can deallocate entire list by single free(list). */ 4389 * Caller can deallocate entire list by single free(list). */
2527 4390
2528/* Store given string, finalizing the word and starting new one whenever 4391/* Store given string, finalizing the word and starting new one whenever
@@ -2533,10 +4396,18 @@ static int expand_on_ifs(o_string *output, int n, const char *str)
2533 while (1) { 4396 while (1) {
2534 int word_len = strcspn(str, G.ifs); 4397 int word_len = strcspn(str, G.ifs);
2535 if (word_len) { 4398 if (word_len) {
2536 if (output->o_escape || !output->o_glob) 4399 if (!(output->o_expflags & EXP_FLAG_GLOB))
2537 o_addQstr(output, str, word_len); 4400 o_addblock(output, str, word_len);
2538 else /* protect backslashes against globbing up :) */ 4401 else {
4402 /* Protect backslashes against globbing up :)
4403 * Example: "v='\*'; echo b$v" prints "b\*"
4404 * (and does not try to glob on "*")
4405 */
2539 o_addblock_duplicate_backslash(output, str, word_len); 4406 o_addblock_duplicate_backslash(output, str, word_len);
4407 /*/ Why can't we do it easier? */
4408 /*o_addblock(output, str, word_len); - WRONG: "v='\*'; echo Z$v" prints "Z*" instead of "Z\*" */
4409 /*o_addqblock(output, str, word_len); - WRONG: "v='*'; echo Z$v" prints "Z*" instead of Z* files */
4410 }
2540 str += word_len; 4411 str += word_len;
2541 } 4412 }
2542 if (!*str) /* EOL - do not finalize word */ 4413 if (!*str) /* EOL - do not finalize word */
@@ -2557,15 +4428,21 @@ static int expand_on_ifs(o_string *output, int n, const char *str)
2557 * Returns malloced string. 4428 * Returns malloced string.
2558 * As an optimization, we return NULL if expansion is not needed. 4429 * As an optimization, we return NULL if expansion is not needed.
2559 */ 4430 */
2560static char *expand_pseudo_dquoted(const char *str) 4431#if !ENABLE_HUSH_BASH_COMPAT
4432/* only ${var/pattern/repl} (its pattern part) needs additional mode */
4433#define encode_then_expand_string(str, process_bkslash, do_unbackslash) \
4434 encode_then_expand_string(str)
4435#endif
4436static char *encode_then_expand_string(const char *str, int process_bkslash, int do_unbackslash)
2561{ 4437{
2562 char *exp_str; 4438 char *exp_str;
2563 struct in_str input; 4439 struct in_str input;
2564 o_string dest = NULL_O_STRING; 4440 o_string dest = NULL_O_STRING;
2565 4441
2566 if (strchr(str, '$') == NULL 4442 if (!strchr(str, '$')
4443 && !strchr(str, '\\')
2567#if ENABLE_HUSH_TICK 4444#if ENABLE_HUSH_TICK
2568 && strchr(str, '`') == NULL 4445 && !strchr(str, '`')
2569#endif 4446#endif
2570 ) { 4447 ) {
2571 return NULL; 4448 return NULL;
@@ -2575,9 +4452,9 @@ static char *expand_pseudo_dquoted(const char *str)
2575 * echo $(($a + `echo 1`)) $((1 + $((2)) )) 4452 * echo $(($a + `echo 1`)) $((1 + $((2)) ))
2576 */ 4453 */
2577 setup_string_in_str(&input, str); 4454 setup_string_in_str(&input, str);
2578 parse_stream_dquoted(NULL, &dest, &input, EOF); 4455 encode_string(NULL, &dest, &input, EOF, process_bkslash);
2579 //bb_error_msg("'%s' -> '%s'", str, dest.data); 4456 //bb_error_msg("'%s' -> '%s'", str, dest.data);
2580 exp_str = expand_string_to_string(dest.data); 4457 exp_str = expand_string_to_string(dest.data, /*unbackslash:*/ do_unbackslash);
2581 //bb_error_msg("'%s' -> '%s'", dest.data, exp_str); 4458 //bb_error_msg("'%s' -> '%s'", dest.data, exp_str);
2582 o_free_unsafe(&dest); 4459 o_free_unsafe(&dest);
2583 return exp_str; 4460 return exp_str;
@@ -2592,38 +4469,369 @@ static arith_t expand_and_evaluate_arith(const char *arg, int *errcode_p)
2592 4469
2593 hooks.lookupvar = get_local_var_value; 4470 hooks.lookupvar = get_local_var_value;
2594 hooks.setvar = set_local_var_from_halves; 4471 hooks.setvar = set_local_var_from_halves;
2595 hooks.endofname = endofname; 4472 //hooks.endofname = endofname;
2596 exp_str = expand_pseudo_dquoted(arg); 4473 exp_str = encode_then_expand_string(arg, /*process_bkslash:*/ 1, /*unbackslash:*/ 1);
2597 res = arith(exp_str ? exp_str : arg, errcode_p, &hooks); 4474 res = arith(exp_str ? exp_str : arg, errcode_p, &hooks);
2598 free(exp_str); 4475 free(exp_str);
2599 return res; 4476 return res;
2600} 4477}
2601#endif 4478#endif
2602 4479
4480#if ENABLE_HUSH_BASH_COMPAT
4481/* ${var/[/]pattern[/repl]} helpers */
4482static char *strstr_pattern(char *val, const char *pattern, int *size)
4483{
4484 while (1) {
4485 char *end = scan_and_match(val, pattern, SCAN_MOVE_FROM_RIGHT + SCAN_MATCH_LEFT_HALF);
4486 debug_printf_varexp("val:'%s' pattern:'%s' end:'%s'\n", val, pattern, end);
4487 if (end) {
4488 *size = end - val;
4489 return val;
4490 }
4491 if (*val == '\0')
4492 return NULL;
4493 /* Optimization: if "*pat" did not match the start of "string",
4494 * we know that "tring", "ring" etc will not match too:
4495 */
4496 if (pattern[0] == '*')
4497 return NULL;
4498 val++;
4499 }
4500}
4501static char *replace_pattern(char *val, const char *pattern, const char *repl, char exp_op)
4502{
4503 char *result = NULL;
4504 unsigned res_len = 0;
4505 unsigned repl_len = strlen(repl);
4506
4507 while (1) {
4508 int size;
4509 char *s = strstr_pattern(val, pattern, &size);
4510 if (!s)
4511 break;
4512
4513 result = xrealloc(result, res_len + (s - val) + repl_len + 1);
4514 memcpy(result + res_len, val, s - val);
4515 res_len += s - val;
4516 strcpy(result + res_len, repl);
4517 res_len += repl_len;
4518 debug_printf_varexp("val:'%s' s:'%s' result:'%s'\n", val, s, result);
4519
4520 val = s + size;
4521 if (exp_op == '/')
4522 break;
4523 }
4524 if (val[0] && result) {
4525 result = xrealloc(result, res_len + strlen(val) + 1);
4526 strcpy(result + res_len, val);
4527 debug_printf_varexp("val:'%s' result:'%s'\n", val, result);
4528 }
4529 debug_printf_varexp("result:'%s'\n", result);
4530 return result;
4531}
4532#endif
4533
4534/* Helper:
4535 * Handles <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct.
4536 */
4537static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, char **pp)
4538{
4539 const char *val = NULL;
4540 char *to_be_freed = NULL;
4541 char *p = *pp;
4542 char *var;
4543 char first_char;
4544 char exp_op;
4545 char exp_save = exp_save; /* for compiler */
4546 char *exp_saveptr; /* points to expansion operator */
4547 char *exp_word = exp_word; /* for compiler */
4548 char arg0;
4549
4550 *p = '\0'; /* replace trailing SPECIAL_VAR_SYMBOL */
4551 var = arg;
4552 exp_saveptr = arg[1] ? strchr(VAR_ENCODED_SUBST_OPS, arg[1]) : NULL;
4553 arg0 = arg[0];
4554 first_char = arg[0] = arg0 & 0x7f;
4555 exp_op = 0;
4556
4557 if (first_char == '#' /* ${#... */
4558 && arg[1] && !exp_saveptr /* not ${#} and not ${#<op_char>...} */
4559 ) {
4560 /* It must be length operator: ${#var} */
4561 var++;
4562 exp_op = 'L';
4563 } else {
4564 /* Maybe handle parameter expansion */
4565 if (exp_saveptr /* if 2nd char is one of expansion operators */
4566 && strchr(NUMERIC_SPECVARS_STR, first_char) /* 1st char is special variable */
4567 ) {
4568 /* ${?:0}, ${#[:]%0} etc */
4569 exp_saveptr = var + 1;
4570 } else {
4571 /* ${?}, ${var}, ${var:0}, ${var[:]%0} etc */
4572 exp_saveptr = var+1 + strcspn(var+1, VAR_ENCODED_SUBST_OPS);
4573 }
4574 exp_op = exp_save = *exp_saveptr;
4575 if (exp_op) {
4576 exp_word = exp_saveptr + 1;
4577 if (exp_op == ':') {
4578 exp_op = *exp_word++;
4579//TODO: try ${var:} and ${var:bogus} in non-bash config
4580 if (ENABLE_HUSH_BASH_COMPAT
4581 && (!exp_op || !strchr(MINUS_PLUS_EQUAL_QUESTION, exp_op))
4582 ) {
4583 /* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */
4584 exp_op = ':';
4585 exp_word--;
4586 }
4587 }
4588 *exp_saveptr = '\0';
4589 } /* else: it's not an expansion op, but bare ${var} */
4590 }
4591
4592 /* Look up the variable in question */
4593 if (isdigit(var[0])) {
4594 /* parse_dollar should have vetted var for us */
4595 int n = xatoi_positive(var);
4596 if (n < G.global_argc)
4597 val = G.global_argv[n];
4598 /* else val remains NULL: $N with too big N */
4599 } else {
4600 switch (var[0]) {
4601 case '$': /* pid */
4602 val = utoa(G.root_pid);
4603 break;
4604 case '!': /* bg pid */
4605 val = G.last_bg_pid ? utoa(G.last_bg_pid) : "";
4606 break;
4607 case '?': /* exitcode */
4608 val = utoa(G.last_exitcode);
4609 break;
4610 case '#': /* argc */
4611 val = utoa(G.global_argc ? G.global_argc-1 : 0);
4612 break;
4613 default:
4614 val = get_local_var_value(var);
4615 }
4616 }
4617
4618 /* Handle any expansions */
4619 if (exp_op == 'L') {
4620 debug_printf_expand("expand: length(%s)=", val);
4621 val = utoa(val ? strlen(val) : 0);
4622 debug_printf_expand("%s\n", val);
4623 } else if (exp_op) {
4624 if (exp_op == '%' || exp_op == '#') {
4625 /* Standard-mandated substring removal ops:
4626 * ${parameter%word} - remove smallest suffix pattern
4627 * ${parameter%%word} - remove largest suffix pattern
4628 * ${parameter#word} - remove smallest prefix pattern
4629 * ${parameter##word} - remove largest prefix pattern
4630 *
4631 * Word is expanded to produce a glob pattern.
4632 * Then var's value is matched to it and matching part removed.
4633 */
4634 if (val && val[0]) {
4635 char *t;
4636 char *exp_exp_word;
4637 char *loc;
4638 unsigned scan_flags = pick_scan(exp_op, *exp_word);
4639 if (exp_op == *exp_word) /* ## or %% */
4640 exp_word++;
4641 exp_exp_word = encode_then_expand_string(exp_word, /*process_bkslash:*/ 1, /*unbackslash:*/ 1);
4642 if (exp_exp_word)
4643 exp_word = exp_exp_word;
4644 /* HACK ALERT. We depend here on the fact that
4645 * G.global_argv and results of utoa and get_local_var_value
4646 * are actually in writable memory:
4647 * scan_and_match momentarily stores NULs there. */
4648 t = (char*)val;
4649 loc = scan_and_match(t, exp_word, scan_flags);
4650 //bb_error_msg("op:%c str:'%s' pat:'%s' res:'%s'",
4651 // exp_op, t, exp_word, loc);
4652 free(exp_exp_word);
4653 if (loc) { /* match was found */
4654 if (scan_flags & SCAN_MATCH_LEFT_HALF) /* #[#] */
4655 val = loc; /* take right part */
4656 else /* %[%] */
4657 val = to_be_freed = xstrndup(val, loc - val); /* left */
4658 }
4659 }
4660 }
4661#if ENABLE_HUSH_BASH_COMPAT
4662 else if (exp_op == '/' || exp_op == '\\') {
4663 /* It's ${var/[/]pattern[/repl]} thing.
4664 * Note that in encoded form it has TWO parts:
4665 * var/pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL>
4666 * and if // is used, it is encoded as \:
4667 * var\pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL>
4668 */
4669 /* Empty variable always gives nothing: */
4670 // "v=''; echo ${v/*/w}" prints "", not "w"
4671 if (val && val[0]) {
4672 /* pattern uses non-standard expansion.
4673 * repl should be unbackslashed and globbed
4674 * by the usual expansion rules:
4675 * >az; >bz;
4676 * v='a bz'; echo "${v/a*z/a*z}" prints "a*z"
4677 * v='a bz'; echo "${v/a*z/\z}" prints "\z"
4678 * v='a bz'; echo ${v/a*z/a*z} prints "az"
4679 * v='a bz'; echo ${v/a*z/\z} prints "z"
4680 * (note that a*z _pattern_ is never globbed!)
4681 */
4682 char *pattern, *repl, *t;
4683 pattern = encode_then_expand_string(exp_word, /*process_bkslash:*/ 0, /*unbackslash:*/ 0);
4684 if (!pattern)
4685 pattern = xstrdup(exp_word);
4686 debug_printf_varexp("pattern:'%s'->'%s'\n", exp_word, pattern);
4687 *p++ = SPECIAL_VAR_SYMBOL;
4688 exp_word = p;
4689 p = strchr(p, SPECIAL_VAR_SYMBOL);
4690 *p = '\0';
4691 repl = encode_then_expand_string(exp_word, /*process_bkslash:*/ arg0 & 0x80, /*unbackslash:*/ 1);
4692 debug_printf_varexp("repl:'%s'->'%s'\n", exp_word, repl);
4693 /* HACK ALERT. We depend here on the fact that
4694 * G.global_argv and results of utoa and get_local_var_value
4695 * are actually in writable memory:
4696 * replace_pattern momentarily stores NULs there. */
4697 t = (char*)val;
4698 to_be_freed = replace_pattern(t,
4699 pattern,
4700 (repl ? repl : exp_word),
4701 exp_op);
4702 if (to_be_freed) /* at least one replace happened */
4703 val = to_be_freed;
4704 free(pattern);
4705 free(repl);
4706 }
4707 }
4708#endif
4709 else if (exp_op == ':') {
4710#if ENABLE_HUSH_BASH_COMPAT && ENABLE_SH_MATH_SUPPORT
4711 /* It's ${var:N[:M]} bashism.
4712 * Note that in encoded form it has TWO parts:
4713 * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL>
4714 */
4715 arith_t beg, len;
4716 int errcode = 0;
4717
4718 beg = expand_and_evaluate_arith(exp_word, &errcode);
4719 debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg);
4720 *p++ = SPECIAL_VAR_SYMBOL;
4721 exp_word = p;
4722 p = strchr(p, SPECIAL_VAR_SYMBOL);
4723 *p = '\0';
4724 len = expand_and_evaluate_arith(exp_word, &errcode);
4725 debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len);
4726
4727 if (errcode >= 0 && len >= 0) { /* bash compat: len < 0 is illegal */
4728 if (beg < 0) /* bash compat */
4729 beg = 0;
4730 debug_printf_varexp("from val:'%s'\n", val);
4731 if (len == 0 || !val || beg >= strlen(val))
4732 val = "";
4733 else {
4734 /* Paranoia. What if user entered 9999999999999
4735 * which fits in arith_t but not int? */
4736 if (len >= INT_MAX)
4737 len = INT_MAX;
4738 val = to_be_freed = xstrndup(val + beg, len);
4739 }
4740 debug_printf_varexp("val:'%s'\n", val);
4741 } else
4742#endif
4743 {
4744 die_if_script("malformed ${%s:...}", var);
4745 val = "";
4746 }
4747 } else { /* one of "-=+?" */
4748 /* Standard-mandated substitution ops:
4749 * ${var?word} - indicate error if unset
4750 * If var is unset, word (or a message indicating it is unset
4751 * if word is null) is written to standard error
4752 * and the shell exits with a non-zero exit status.
4753 * Otherwise, the value of var is substituted.
4754 * ${var-word} - use default value
4755 * If var is unset, word is substituted.
4756 * ${var=word} - assign and use default value
4757 * If var is unset, word is assigned to var.
4758 * In all cases, final value of var is substituted.
4759 * ${var+word} - use alternative value
4760 * If var is unset, null is substituted.
4761 * Otherwise, word is substituted.
4762 *
4763 * Word is subjected to tilde expansion, parameter expansion,
4764 * command substitution, and arithmetic expansion.
4765 * If word is not needed, it is not expanded.
4766 *
4767 * Colon forms (${var:-word}, ${var:=word} etc) do the same,
4768 * but also treat null var as if it is unset.
4769 */
4770 int use_word = (!val || ((exp_save == ':') && !val[0]));
4771 if (exp_op == '+')
4772 use_word = !use_word;
4773 debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op,
4774 (exp_save == ':') ? "true" : "false", use_word);
4775 if (use_word) {
4776 to_be_freed = encode_then_expand_string(exp_word, /*process_bkslash:*/ 1, /*unbackslash:*/ 1);
4777 if (to_be_freed)
4778 exp_word = to_be_freed;
4779 if (exp_op == '?') {
4780 /* mimic bash message */
4781 die_if_script("%s: %s",
4782 var,
4783 exp_word[0] ? exp_word : "parameter null or not set"
4784 );
4785//TODO: how interactive bash aborts expansion mid-command?
4786 } else {
4787 val = exp_word;
4788 }
4789
4790 if (exp_op == '=') {
4791 /* ${var=[word]} or ${var:=[word]} */
4792 if (isdigit(var[0]) || var[0] == '#') {
4793 /* mimic bash message */
4794 die_if_script("$%s: cannot assign in this way", var);
4795 val = NULL;
4796 } else {
4797 char *new_var = xasprintf("%s=%s", var, val);
4798 set_local_var(new_var, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
4799 }
4800 }
4801 }
4802 } /* one of "-=+?" */
4803
4804 *exp_saveptr = exp_save;
4805 } /* if (exp_op) */
4806
4807 arg[0] = arg0;
4808
4809 *pp = p;
4810 *to_be_freed_pp = to_be_freed;
4811 return val;
4812}
4813
2603/* Expand all variable references in given string, adding words to list[] 4814/* Expand all variable references in given string, adding words to list[]
2604 * at n, n+1,... positions. Return updated n (so that list[n] is next one 4815 * at n, n+1,... positions. Return updated n (so that list[n] is next one
2605 * to be filled). This routine is extremely tricky: has to deal with 4816 * to be filled). This routine is extremely tricky: has to deal with
2606 * variables/parameters with whitespace, $* and $@, and constructs like 4817 * variables/parameters with whitespace, $* and $@, and constructs like
2607 * 'echo -$*-'. If you play here, you must run testsuite afterwards! */ 4818 * 'echo -$*-'. If you play here, you must run testsuite afterwards! */
2608static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) 4819static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
2609{ 4820{
2610 /* or_mask is either 0 (normal case) or 0x80 - 4821 /* output->o_expflags & EXP_FLAG_SINGLEWORD (0x80) if we are in
2611 * expansion of right-hand side of assignment == 1-element expand. 4822 * expansion of right-hand side of assignment == 1-element expand.
2612 * It will also do no globbing, and thus we must not backslash-quote!
2613 */ 4823 */
2614 char ored_ch; 4824 char cant_be_null = 0; /* only bit 0x80 matters */
2615 char *p; 4825 char *p;
2616 4826
2617 ored_ch = 0; 4827 debug_printf_expand("expand_vars_to_list: arg:'%s' singleword:%x\n", arg,
2618 4828 !!(output->o_expflags & EXP_FLAG_SINGLEWORD));
2619 debug_printf_expand("expand_vars_to_list: arg:'%s' or_mask:%x\n", arg, or_mask);
2620 debug_print_list("expand_vars_to_list", output, n); 4829 debug_print_list("expand_vars_to_list", output, n);
2621 n = o_save_ptr(output, n); 4830 n = o_save_ptr(output, n);
2622 debug_print_list("expand_vars_to_list[0]", output, n); 4831 debug_print_list("expand_vars_to_list[0]", output, n);
2623 4832
2624 while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) { 4833 while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) {
2625 char first_ch; 4834 char first_ch;
2626 int i;
2627 char *to_be_freed = NULL; 4835 char *to_be_freed = NULL;
2628 const char *val = NULL; 4836 const char *val = NULL;
2629#if ENABLE_HUSH_TICK 4837#if ENABLE_HUSH_TICK
@@ -2637,24 +4845,28 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
2637 arg = ++p; 4845 arg = ++p;
2638 p = strchr(p, SPECIAL_VAR_SYMBOL); 4846 p = strchr(p, SPECIAL_VAR_SYMBOL);
2639 4847
2640 first_ch = arg[0] | or_mask; /* forced to "quoted" if or_mask = 0x80 */ 4848 /* Fetch special var name (if it is indeed one of them)
2641 /* "$@" is special. Even if quoted, it can still 4849 * and quote bit, force the bit on if singleword expansion -
2642 * expand to nothing (not even an empty string) */ 4850 * important for not getting v=$@ expand to many words. */
4851 first_ch = arg[0] | (output->o_expflags & EXP_FLAG_SINGLEWORD);
4852
4853 /* Is this variable quoted and thus expansion can't be null?
4854 * "$@" is special. Even if quoted, it can still
4855 * expand to nothing (not even an empty string),
4856 * thus it is excluded. */
2643 if ((first_ch & 0x7f) != '@') 4857 if ((first_ch & 0x7f) != '@')
2644 ored_ch |= first_ch; 4858 cant_be_null |= first_ch;
2645 4859
2646 switch (first_ch & 0x7f) { 4860 switch (first_ch & 0x7f) {
2647 /* Highest bit in first_ch indicates that var is double-quoted */ 4861 /* Highest bit in first_ch indicates that var is double-quoted */
2648 case '*': 4862 case '*':
2649 case '@': 4863 case '@': {
2650 i = 1; 4864 int i;
2651 if (!G.global_argv[i]) 4865 if (!G.global_argv[1])
2652 break; 4866 break;
2653 ored_ch |= first_ch; /* do it for "$@" _now_, when we know it's not empty */ 4867 i = 1;
4868 cant_be_null |= first_ch; /* do it for "$@" _now_, when we know it's not empty */
2654 if (!(first_ch & 0x80)) { /* unquoted $* or $@ */ 4869 if (!(first_ch & 0x80)) { /* unquoted $* or $@ */
2655 smallint sv = output->o_escape;
2656 /* unquoted var's contents should be globbed, so don't escape */
2657 output->o_escape = 0;
2658 while (G.global_argv[i]) { 4870 while (G.global_argv[i]) {
2659 n = expand_on_ifs(output, n, G.global_argv[i]); 4871 n = expand_on_ifs(output, n, G.global_argv[i]);
2660 debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, G.global_argc - 1); 4872 debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, G.global_argc - 1);
@@ -2667,22 +4879,23 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
2667 debug_print_list("expand_vars_to_list[3]", output, n); 4879 debug_print_list("expand_vars_to_list[3]", output, n);
2668 } 4880 }
2669 } 4881 }
2670 output->o_escape = sv;
2671 } else 4882 } else
2672 /* If or_mask is nonzero, we handle assignment 'a=....$@.....' 4883 /* If EXP_FLAG_SINGLEWORD, we handle assignment 'a=....$@.....'
2673 * and in this case should treat it like '$*' - see 'else...' below */ 4884 * and in this case should treat it like '$*' - see 'else...' below */
2674 if (first_ch == ('@'|0x80) && !or_mask) { /* quoted $@ */ 4885 if (first_ch == ('@'|0x80) /* quoted $@ */
4886 && !(output->o_expflags & EXP_FLAG_SINGLEWORD) /* not v="$@" case */
4887 ) {
2675 while (1) { 4888 while (1) {
2676 o_addQstr(output, G.global_argv[i], strlen(G.global_argv[i])); 4889 o_addQstr(output, G.global_argv[i]);
2677 if (++i >= G.global_argc) 4890 if (++i >= G.global_argc)
2678 break; 4891 break;
2679 o_addchr(output, '\0'); 4892 o_addchr(output, '\0');
2680 debug_print_list("expand_vars_to_list[4]", output, n); 4893 debug_print_list("expand_vars_to_list[4]", output, n);
2681 n = o_save_ptr(output, n); 4894 n = o_save_ptr(output, n);
2682 } 4895 }
2683 } else { /* quoted $*: add as one word */ 4896 } else { /* quoted $* (or v="$@" case): add as one word */
2684 while (1) { 4897 while (1) {
2685 o_addQstr(output, G.global_argv[i], strlen(G.global_argv[i])); 4898 o_addQstr(output, G.global_argv[i]);
2686 if (!G.global_argv[++i]) 4899 if (!G.global_argv[++i])
2687 break; 4900 break;
2688 if (G.ifs[0]) 4901 if (G.ifs[0])
@@ -2690,14 +4903,15 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
2690 } 4903 }
2691 } 4904 }
2692 break; 4905 break;
4906 }
2693 case SPECIAL_VAR_SYMBOL: /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_SYMBOL> */ 4907 case SPECIAL_VAR_SYMBOL: /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_SYMBOL> */
2694 /* "Empty variable", used to make "" etc to not disappear */ 4908 /* "Empty variable", used to make "" etc to not disappear */
2695 arg++; 4909 arg++;
2696 ored_ch = 0x80; 4910 cant_be_null = 0x80;
2697 break; 4911 break;
2698#if ENABLE_HUSH_TICK 4912#if ENABLE_HUSH_TICK
2699 case '`': /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */ 4913 case '`': /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */
2700 *p = '\0'; 4914 *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */
2701 arg++; 4915 arg++;
2702 /* Can't just stuff it into output o_string, 4916 /* Can't just stuff it into output o_string,
2703 * expanded result may need to be globbed 4917 * expanded result may need to be globbed
@@ -2739,240 +4953,31 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
2739 break; 4953 break;
2740 } 4954 }
2741#endif 4955#endif
2742 default: { /* <SPECIAL_VAR_SYMBOL>varname<SPECIAL_VAR_SYMBOL> */ 4956 default:
2743 char *var; 4957 val = expand_one_var(&to_be_freed, arg, &p);
2744 char first_char; 4958 IF_HUSH_TICK(store_val:)
2745 char exp_op;
2746 char exp_save = exp_save; /* for compiler */
2747 char *exp_saveptr; /* points to expansion operator */
2748 char *exp_word = exp_word; /* for compiler */
2749
2750 var = arg;
2751 *p = '\0';
2752 exp_saveptr = arg[1] ? strchr("%#:-=+?", arg[1]) : NULL;
2753 first_char = arg[0] = first_ch & 0x7f;
2754 exp_op = 0;
2755
2756 if (first_char == '#' && arg[1] && !exp_saveptr) {
2757 /* handle length expansion ${#var} */
2758 var++;
2759 exp_op = 'L';
2760 } else {
2761 /* maybe handle parameter expansion */
2762 if (exp_saveptr /* if 2nd char is one of expansion operators */
2763 && strchr(NUMERIC_SPECVARS_STR, first_char) /* 1st char is special variable */
2764 ) {
2765 /* ${?:0}, ${#[:]%0} etc */
2766 exp_saveptr = var + 1;
2767 } else {
2768 /* ${?}, ${var}, ${var:0}, ${var[:]%0} etc */
2769 exp_saveptr = var+1 + strcspn(var+1, "%#:-=+?");
2770 }
2771 exp_op = exp_save = *exp_saveptr;
2772 if (exp_op) {
2773 exp_word = exp_saveptr + 1;
2774 if (exp_op == ':') {
2775 exp_op = *exp_word++;
2776 if (ENABLE_HUSH_BASH_COMPAT
2777 && (exp_op == '\0' || !strchr("%#:-=+?"+3, exp_op))
2778 ) {
2779 /* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */
2780 exp_op = ':';
2781 exp_word--;
2782 }
2783 }
2784 *exp_saveptr = '\0';
2785 } /* else: it's not an expansion op, but bare ${var} */
2786 }
2787
2788 /* lookup the variable in question */
2789 if (isdigit(var[0])) {
2790 /* parse_dollar() should have vetted var for us */
2791 i = xatoi_positive(var);
2792 if (i < G.global_argc)
2793 val = G.global_argv[i];
2794 /* else val remains NULL: $N with too big N */
2795 } else {
2796 switch (var[0]) {
2797 case '$': /* pid */
2798 val = utoa(G.root_pid);
2799 break;
2800 case '!': /* bg pid */
2801 val = G.last_bg_pid ? utoa(G.last_bg_pid) : (char*)"";
2802 break;
2803 case '?': /* exitcode */
2804 val = utoa(G.last_exitcode);
2805 break;
2806 case '#': /* argc */
2807 val = utoa(G.global_argc ? G.global_argc-1 : 0);
2808 break;
2809 default:
2810 val = get_local_var_value(var);
2811 }
2812 }
2813
2814 /* handle any expansions */
2815 if (exp_op == 'L') {
2816 debug_printf_expand("expand: length(%s)=", val);
2817 val = utoa(val ? strlen(val) : 0);
2818 debug_printf_expand("%s\n", val);
2819 } else if (exp_op) {
2820 if (exp_op == '%' || exp_op == '#') {
2821 /* Standard-mandated substring removal ops:
2822 * ${parameter%word} - remove smallest suffix pattern
2823 * ${parameter%%word} - remove largest suffix pattern
2824 * ${parameter#word} - remove smallest prefix pattern
2825 * ${parameter##word} - remove largest prefix pattern
2826 *
2827 * Word is expanded to produce a glob pattern.
2828 * Then var's value is matched to it and matching part removed.
2829 */
2830 if (val) {
2831 bool match_at_left;
2832 char *loc;
2833 scan_t scan = pick_scan(exp_op, *exp_word, &match_at_left);
2834 if (exp_op == *exp_word) /* ## or %% */
2835 exp_word++;
2836 val = to_be_freed = xstrdup(val);
2837 {
2838 char *exp_exp_word = expand_pseudo_dquoted(exp_word);
2839 if (exp_exp_word)
2840 exp_word = exp_exp_word;
2841 loc = scan(to_be_freed, exp_word, match_at_left);
2842 //bb_error_msg("op:%c str:'%s' pat:'%s' res:'%s'",
2843 // exp_op, to_be_freed, exp_word, loc);
2844 free(exp_exp_word);
2845 }
2846 if (loc) { /* match was found */
2847 if (match_at_left) /* # or ## */
2848 val = loc;
2849 else /* % or %% */
2850 *loc = '\0';
2851 }
2852 }
2853 } else if (exp_op == ':') {
2854#if ENABLE_HUSH_BASH_COMPAT && ENABLE_SH_MATH_SUPPORT
2855 /* It's ${var:N[:M]} bashism.
2856 * Note that in encoded form it has TWO parts:
2857 * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL>
2858 */
2859 arith_t beg, len;
2860 int errcode = 0;
2861
2862 beg = expand_and_evaluate_arith(exp_word, &errcode);
2863 debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg);
2864 *p++ = SPECIAL_VAR_SYMBOL;
2865 exp_word = p;
2866 p = strchr(p, SPECIAL_VAR_SYMBOL);
2867 *p = '\0';
2868 len = expand_and_evaluate_arith(exp_word, &errcode);
2869 debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len);
2870
2871 if (errcode >= 0 && len >= 0) { /* bash compat: len < 0 is illegal */
2872 if (beg < 0) /* bash compat */
2873 beg = 0;
2874 debug_printf_varexp("from val:'%s'\n", val);
2875 if (len == 0 || !val || beg >= strlen(val))
2876 val = "";
2877 else {
2878 /* Paranoia. What if user entered 9999999999999
2879 * which fits in arith_t but not int? */
2880 if (len >= INT_MAX)
2881 len = INT_MAX;
2882 val = to_be_freed = xstrndup(val + beg, len);
2883 }
2884 debug_printf_varexp("val:'%s'\n", val);
2885 } else
2886#endif
2887 {
2888 die_if_script("malformed ${%s:...}", var);
2889 val = "";
2890 }
2891 } else { /* one of "-=+?" */
2892 /* Standard-mandated substitution ops:
2893 * ${var?word} - indicate error if unset
2894 * If var is unset, word (or a message indicating it is unset
2895 * if word is null) is written to standard error
2896 * and the shell exits with a non-zero exit status.
2897 * Otherwise, the value of var is substituted.
2898 * ${var-word} - use default value
2899 * If var is unset, word is substituted.
2900 * ${var=word} - assign and use default value
2901 * If var is unset, word is assigned to var.
2902 * In all cases, final value of var is substituted.
2903 * ${var+word} - use alternative value
2904 * If var is unset, null is substituted.
2905 * Otherwise, word is substituted.
2906 *
2907 * Word is subjected to tilde expansion, parameter expansion,
2908 * command substitution, and arithmetic expansion.
2909 * If word is not needed, it is not expanded.
2910 *
2911 * Colon forms (${var:-word}, ${var:=word} etc) do the same,
2912 * but also treat null var as if it is unset.
2913 */
2914 int use_word = (!val || ((exp_save == ':') && !val[0]));
2915 if (exp_op == '+')
2916 use_word = !use_word;
2917 debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op,
2918 (exp_save == ':') ? "true" : "false", use_word);
2919 if (use_word) {
2920 to_be_freed = expand_pseudo_dquoted(exp_word);
2921 if (to_be_freed)
2922 exp_word = to_be_freed;
2923 if (exp_op == '?') {
2924 /* mimic bash message */
2925 die_if_script("%s: %s",
2926 var,
2927 exp_word[0] ? exp_word : "parameter null or not set"
2928 );
2929//TODO: how interactive bash aborts expansion mid-command?
2930 } else {
2931 val = exp_word;
2932 }
2933
2934 if (exp_op == '=') {
2935 /* ${var=[word]} or ${var:=[word]} */
2936 if (isdigit(var[0]) || var[0] == '#') {
2937 /* mimic bash message */
2938 die_if_script("$%s: cannot assign in this way", var);
2939 val = NULL;
2940 } else {
2941 char *new_var = xasprintf("%s=%s", var, val);
2942 set_local_var(new_var, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
2943 }
2944 }
2945 }
2946 } /* one of "-=+?" */
2947
2948 *exp_saveptr = exp_save;
2949 } /* if (exp_op) */
2950
2951 arg[0] = first_ch;
2952#if ENABLE_HUSH_TICK
2953 store_val:
2954#endif
2955 if (!(first_ch & 0x80)) { /* unquoted $VAR */ 4959 if (!(first_ch & 0x80)) { /* unquoted $VAR */
2956 debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val, output->o_escape); 4960 debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val,
2957 if (val) { 4961 !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
2958 /* unquoted var's contents should be globbed, so don't escape */ 4962 if (val && val[0]) {
2959 smallint sv = output->o_escape;
2960 output->o_escape = 0;
2961 n = expand_on_ifs(output, n, val); 4963 n = expand_on_ifs(output, n, val);
2962 val = NULL; 4964 val = NULL;
2963 output->o_escape = sv;
2964 } 4965 }
2965 } else { /* quoted $VAR, val will be appended below */ 4966 } else { /* quoted $VAR, val will be appended below */
2966 debug_printf_expand("quoted '%s', output->o_escape:%d\n", val, output->o_escape); 4967 debug_printf_expand("quoted '%s', output->o_escape:%d\n", val,
4968 !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
2967 } 4969 }
2968 } /* default: */ 4970 break;
4971
2969 } /* switch (char after <SPECIAL_VAR_SYMBOL>) */ 4972 } /* switch (char after <SPECIAL_VAR_SYMBOL>) */
2970 4973
2971 if (val) { 4974 if (val && val[0]) {
2972 o_addQstr(output, val, strlen(val)); 4975 o_addQstr(output, val);
2973 } 4976 }
2974 free(to_be_freed); 4977 free(to_be_freed);
2975 /* Do the check to avoid writing to a const string */ 4978
4979 /* Restore NULL'ed SPECIAL_VAR_SYMBOL.
4980 * Do the check to avoid writing to a const string. */
2976 if (*p != SPECIAL_VAR_SYMBOL) 4981 if (*p != SPECIAL_VAR_SYMBOL)
2977 *p = SPECIAL_VAR_SYMBOL; 4982 *p = SPECIAL_VAR_SYMBOL;
2978 4983
@@ -2989,7 +4994,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
2989 o_addstr_with_NUL(output, arg); 4994 o_addstr_with_NUL(output, arg);
2990 debug_print_list("expand_vars_to_list[b]", output, n); 4995 debug_print_list("expand_vars_to_list[b]", output, n);
2991 } else if (output->length == o_get_last_ptr(output, n) /* expansion is empty */ 4996 } else if (output->length == o_get_last_ptr(output, n) /* expansion is empty */
2992 && !(ored_ch & 0x80) /* and all vars were not quoted. */ 4997 && !(cant_be_null & 0x80) /* and all vars were not quoted. */
2993 ) { 4998 ) {
2994 n--; 4999 n--;
2995 /* allow to reuse list[n] later without re-growth */ 5000 /* allow to reuse list[n] later without re-growth */
@@ -2997,27 +5002,22 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
2997 } else { 5002 } else {
2998 o_addchr(output, '\0'); 5003 o_addchr(output, '\0');
2999 } 5004 }
5005
3000 return n; 5006 return n;
3001} 5007}
3002 5008
3003static char **expand_variables(char **argv, int or_mask) 5009static char **expand_variables(char **argv, unsigned expflags)
3004{ 5010{
3005 int n; 5011 int n;
3006 char **list; 5012 char **list;
3007 char **v;
3008 o_string output = NULL_O_STRING; 5013 o_string output = NULL_O_STRING;
3009 5014
3010 if (or_mask & 0x100) { 5015 output.o_expflags = expflags;
3011 output.o_escape = 1; /* protect against globbing for "$var" */
3012 /* (unquoted $var will temporarily switch it off) */
3013 output.o_glob = 1;
3014 }
3015 5016
3016 n = 0; 5017 n = 0;
3017 v = argv; 5018 while (*argv) {
3018 while (*v) { 5019 n = expand_vars_to_list(&output, n, *argv);
3019 n = expand_vars_to_list(&output, n, *v, (unsigned char)or_mask); 5020 argv++;
3020 v++;
3021 } 5021 }
3022 debug_print_list("expand_variables", &output, n); 5022 debug_print_list("expand_variables", &output, n);
3023 5023
@@ -3029,68 +5029,54 @@ static char **expand_variables(char **argv, int or_mask)
3029 5029
3030static char **expand_strvec_to_strvec(char **argv) 5030static char **expand_strvec_to_strvec(char **argv)
3031{ 5031{
3032 return expand_variables(argv, 0x100); 5032 return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS);
3033} 5033}
3034 5034
3035#if ENABLE_HUSH_BASH_COMPAT 5035#if ENABLE_HUSH_BASH_COMPAT
3036static char **expand_strvec_to_strvec_singleword_noglob(char **argv) 5036static char **expand_strvec_to_strvec_singleword_noglob(char **argv)
3037{ 5037{
3038 return expand_variables(argv, 0x80); 5038 return expand_variables(argv, EXP_FLAG_SINGLEWORD);
3039} 5039}
3040#endif 5040#endif
3041 5041
3042#ifdef CMD_SINGLEWORD_NOGLOB_COND 5042/* Used for expansion of right hand of assignments,
3043static char **expand_strvec_to_strvec_singleword_noglob_cond(char **argv) 5043 * $((...)), heredocs, variable espansion parts.
5044 *
5045 * NB: should NOT do globbing!
5046 * "export v=/bin/c*; env | grep ^v=" outputs "v=/bin/c*"
5047 */
5048static char *expand_string_to_string(const char *str, int do_unbackslash)
3044{ 5049{
3045 int n; 5050#if !ENABLE_HUSH_BASH_COMPAT
3046 char **list; 5051 const int do_unbackslash = 1;
3047 char **v;
3048 o_string output = NULL_O_STRING;
3049
3050 n = 0;
3051 v = argv;
3052 while (*v) {
3053 int is_var = is_well_formed_var_name(*v, '=');
3054 /* is_var * 0x80: singleword expansion for vars */
3055 n = expand_vars_to_list(&output, n, *v, is_var * 0x80);
3056
3057 /* Subtle! expand_vars_to_list did not glob last word yet.
3058 * It does this only when fed with further data.
3059 * Therefore we set globbing flags AFTER it, not before:
3060 */
3061
3062 /* if it is not recognizably abc=...; then: */
3063 output.o_escape = !is_var; /* protect against globbing for "$var" */
3064 /* (unquoted $var will temporarily switch it off) */
3065 output.o_glob = !is_var; /* and indeed do globbing */
3066 v++;
3067 }
3068 debug_print_list("expand_cond", &output, n);
3069
3070 /* output.data (malloced in one block) gets returned in "list" */
3071 list = o_finalize_list(&output, n);
3072 debug_print_strings("expand_cond[1]", list);
3073 return list;
3074}
3075#endif 5052#endif
3076
3077/* Used for expansion of right hand of assignments */
3078/* NB: should NOT do globbing! "export v=/bin/c*; env | grep ^v=" outputs
3079 * "v=/bin/c*" */
3080static char *expand_string_to_string(const char *str)
3081{
3082 char *argv[2], **list; 5053 char *argv[2], **list;
3083 5054
5055 debug_printf_expand("string_to_string<='%s'\n", str);
5056 /* This is generally an optimization, but it also
5057 * handles "", which otherwise trips over !list[0] check below.
5058 * (is this ever happens that we actually get str="" here?)
5059 */
5060 if (!strchr(str, SPECIAL_VAR_SYMBOL) && !strchr(str, '\\')) {
5061 //TODO: Can use on strings with \ too, just unbackslash() them?
5062 debug_printf_expand("string_to_string(fast)=>'%s'\n", str);
5063 return xstrdup(str);
5064 }
5065
3084 argv[0] = (char*)str; 5066 argv[0] = (char*)str;
3085 argv[1] = NULL; 5067 argv[1] = NULL;
3086 list = expand_variables(argv, 0x80); /* 0x80: singleword expansion */ 5068 list = expand_variables(argv, do_unbackslash
5069 ? EXP_FLAG_ESC_GLOB_CHARS | EXP_FLAG_SINGLEWORD
5070 : EXP_FLAG_SINGLEWORD
5071 );
3087 if (HUSH_DEBUG) 5072 if (HUSH_DEBUG)
3088 if (!list[0] || list[1]) 5073 if (!list[0] || list[1])
3089 bb_error_msg_and_die("BUG in varexp2"); 5074 bb_error_msg_and_die("BUG in varexp2");
3090 /* actually, just move string 2*sizeof(char*) bytes back */ 5075 /* actually, just move string 2*sizeof(char*) bytes back */
3091 overlapping_strcpy((char*)list, list[0]); 5076 overlapping_strcpy((char*)list, list[0]);
3092 unbackslash((char*)list); 5077 if (do_unbackslash)
3093 debug_printf_expand("string_to_string='%s'\n", (char*)list); 5078 unbackslash((char*)list);
5079 debug_printf_expand("string_to_string=>'%s'\n", (char*)list);
3094 return (char*)list; 5080 return (char*)list;
3095} 5081}
3096 5082
@@ -3099,7 +5085,7 @@ static char* expand_strvec_to_string(char **argv)
3099{ 5085{
3100 char **list; 5086 char **list;
3101 5087
3102 list = expand_variables(argv, 0x80); 5088 list = expand_variables(argv, EXP_FLAG_SINGLEWORD);
3103 /* Convert all NULs to spaces */ 5089 /* Convert all NULs to spaces */
3104 if (list[0]) { 5090 if (list[0]) {
3105 int n = 1; 5091 int n = 1;
@@ -3125,7 +5111,7 @@ static char **expand_assignments(char **argv, int count)
3125 G.expanded_assignments = p = NULL; 5111 G.expanded_assignments = p = NULL;
3126 /* Expand assignments into one string each */ 5112 /* Expand assignments into one string each */
3127 for (i = 0; i < count; i++) { 5113 for (i = 0; i < count; i++) {
3128 G.expanded_assignments = p = add_string_to_strings(p, expand_string_to_string(argv[i])); 5114 G.expanded_assignments = p = add_string_to_strings(p, expand_string_to_string(argv[i], /*unbackslash:*/ 1));
3129 } 5115 }
3130 G.expanded_assignments = NULL; 5116 G.expanded_assignments = NULL;
3131 return p; 5117 return p;
@@ -3268,7 +5254,7 @@ static void re_execute_shell(char ***to_free, const char *s,
3268 *pp++ = (char *) G.argv0_for_re_execing; 5254 *pp++ = (char *) G.argv0_for_re_execing;
3269 *pp++ = param_buf; 5255 *pp++ = param_buf;
3270 for (cur = G.top_var; cur; cur = cur->next) { 5256 for (cur = G.top_var; cur; cur = cur->next) {
3271 if (cur->varstr == hush_version_str) 5257 if (strcmp(cur->varstr, hush_version_str) == 0)
3272 continue; 5258 continue;
3273 if (cur->flg_read_only) { 5259 if (cur->flg_read_only) {
3274 *pp++ = (char *) "-R"; 5260 *pp++ = (char *) "-R";
@@ -3332,6 +5318,194 @@ static void re_execute_shell(char ***to_free, const char *s,
3332#endif /* !BB_MMU */ 5318#endif /* !BB_MMU */
3333 5319
3334 5320
5321static int run_and_free_list(struct pipe *pi);
5322
5323/* Executing from string: eval, sh -c '...'
5324 * or from file: /etc/profile, . file, sh <script>, sh (intereactive)
5325 * end_trigger controls how often we stop parsing
5326 * NUL: parse all, execute, return
5327 * ';': parse till ';' or newline, execute, repeat till EOF
5328 */
5329static void parse_and_run_stream(struct in_str *inp, int end_trigger)
5330{
5331 /* Why we need empty flag?
5332 * An obscure corner case "false; ``; echo $?":
5333 * empty command in `` should still set $? to 0.
5334 * But we can't just set $? to 0 at the start,
5335 * this breaks "false; echo `echo $?`" case.
5336 */
5337 bool empty = 1;
5338 while (1) {
5339 struct pipe *pipe_list;
5340
5341 pipe_list = parse_stream(NULL, inp, end_trigger);
5342 if (!pipe_list) { /* EOF */
5343 if (empty)
5344 G.last_exitcode = 0;
5345 break;
5346 }
5347 debug_print_tree(pipe_list, 0);
5348 debug_printf_exec("parse_and_run_stream: run_and_free_list\n");
5349 run_and_free_list(pipe_list);
5350 empty = 0;
5351 }
5352}
5353
5354static void parse_and_run_string(const char *s)
5355{
5356 struct in_str input;
5357 setup_string_in_str(&input, s);
5358 parse_and_run_stream(&input, '\0');
5359}
5360
5361static void parse_and_run_file(FILE *f)
5362{
5363 struct in_str input;
5364 setup_file_in_str(&input, f);
5365 parse_and_run_stream(&input, ';');
5366}
5367
5368#if ENABLE_HUSH_TICK
5369static FILE *generate_stream_from_string(const char *s, pid_t *pid_p)
5370{
5371 pid_t pid;
5372 int channel[2];
5373# if !BB_MMU
5374 char **to_free = NULL;
5375# endif
5376
5377 xpipe(channel);
5378 pid = BB_MMU ? xfork() : xvfork();
5379 if (pid == 0) { /* child */
5380 disable_restore_tty_pgrp_on_exit();
5381 /* Process substitution is not considered to be usual
5382 * 'command execution'.
5383 * SUSv3 says ctrl-Z should be ignored, ctrl-C should not.
5384 */
5385 bb_signals(0
5386 + (1 << SIGTSTP)
5387 + (1 << SIGTTIN)
5388 + (1 << SIGTTOU)
5389 , SIG_IGN);
5390 CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */
5391 close(channel[0]); /* NB: close _first_, then move fd! */
5392 xmove_fd(channel[1], 1);
5393 /* Prevent it from trying to handle ctrl-z etc */
5394 IF_HUSH_JOB(G.run_list_level = 1;)
5395 /* Awful hack for `trap` or $(trap).
5396 *
5397 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
5398 * contains an example where "trap" is executed in a subshell:
5399 *
5400 * save_traps=$(trap)
5401 * ...
5402 * eval "$save_traps"
5403 *
5404 * Standard does not say that "trap" in subshell shall print
5405 * parent shell's traps. It only says that its output
5406 * must have suitable form, but then, in the above example
5407 * (which is not supposed to be normative), it implies that.
5408 *
5409 * bash (and probably other shell) does implement it
5410 * (traps are reset to defaults, but "trap" still shows them),
5411 * but as a result, "trap" logic is hopelessly messed up:
5412 *
5413 * # trap
5414 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
5415 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
5416 * # true | trap <--- trap is in subshell - no output (ditto)
5417 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
5418 * trap -- 'echo Ho' SIGWINCH
5419 * # echo `(trap)` <--- in subshell in subshell - output
5420 * trap -- 'echo Ho' SIGWINCH
5421 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
5422 * trap -- 'echo Ho' SIGWINCH
5423 *
5424 * The rules when to forget and when to not forget traps
5425 * get really complex and nonsensical.
5426 *
5427 * Our solution: ONLY bare $(trap) or `trap` is special.
5428 */
5429 s = skip_whitespace(s);
5430 if (strncmp(s, "trap", 4) == 0
5431 && skip_whitespace(s + 4)[0] == '\0'
5432 ) {
5433 static const char *const argv[] = { NULL, NULL };
5434 builtin_trap((char**)argv);
5435 exit(0); /* not _exit() - we need to fflush */
5436 }
5437# if BB_MMU
5438 reset_traps_to_defaults();
5439 parse_and_run_string(s);
5440 _exit(G.last_exitcode);
5441# else
5442 /* We re-execute after vfork on NOMMU. This makes this script safe:
5443 * yes "0123456789012345678901234567890" | dd bs=32 count=64k >BIG
5444 * huge=`cat BIG` # was blocking here forever
5445 * echo OK
5446 */
5447 re_execute_shell(&to_free,
5448 s,
5449 G.global_argv[0],
5450 G.global_argv + 1,
5451 NULL);
5452# endif
5453 }
5454
5455 /* parent */
5456 *pid_p = pid;
5457# if ENABLE_HUSH_FAST
5458 G.count_SIGCHLD++;
5459//bb_error_msg("[%d] fork in generate_stream_from_string:"
5460// " G.count_SIGCHLD:%d G.handled_SIGCHLD:%d",
5461// getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
5462# endif
5463 enable_restore_tty_pgrp_on_exit();
5464# if !BB_MMU
5465 free(to_free);
5466# endif
5467 close(channel[1]);
5468 close_on_exec_on(channel[0]);
5469 return xfdopen_for_read(channel[0]);
5470}
5471
5472/* Return code is exit status of the process that is run. */
5473static int process_command_subs(o_string *dest, const char *s)
5474{
5475 FILE *fp;
5476 struct in_str pipe_str;
5477 pid_t pid;
5478 int status, ch, eol_cnt;
5479
5480 fp = generate_stream_from_string(s, &pid);
5481
5482 /* Now send results of command back into original context */
5483 setup_file_in_str(&pipe_str, fp);
5484 eol_cnt = 0;
5485 while ((ch = i_getch(&pipe_str)) != EOF) {
5486 if (ch == '\n') {
5487 eol_cnt++;
5488 continue;
5489 }
5490 while (eol_cnt) {
5491 o_addchr(dest, '\n');
5492 eol_cnt--;
5493 }
5494 o_addQchr(dest, ch);
5495 }
5496
5497 debug_printf("done reading from `cmd` pipe, closing it\n");
5498 fclose(fp);
5499 /* We need to extract exitcode. Test case
5500 * "true; echo `sleep 1; false` $?"
5501 * should print 1 */
5502 safe_waitpid(pid, &status, 0);
5503 debug_printf("child exited. returning its exitcode:%d\n", WEXITSTATUS(status));
5504 return WEXITSTATUS(status);
5505}
5506#endif /* ENABLE_HUSH_TICK */
5507
5508
3335static void setup_heredoc(struct redir_struct *redir) 5509static void setup_heredoc(struct redir_struct *redir)
3336{ 5510{
3337 struct fd_pair pair; 5511 struct fd_pair pair;
@@ -3346,7 +5520,7 @@ static void setup_heredoc(struct redir_struct *redir)
3346 5520
3347 expanded = NULL; 5521 expanded = NULL;
3348 if (!(redir->rd_dup & HEREDOC_QUOTED)) { 5522 if (!(redir->rd_dup & HEREDOC_QUOTED)) {
3349 expanded = expand_pseudo_dquoted(heredoc); 5523 expanded = encode_then_expand_string(heredoc, /*process_bkslash:*/ 1, /*unbackslash:*/ 1);
3350 if (expanded) 5524 if (expanded)
3351 heredoc = expanded; 5525 heredoc = expanded;
3352 } 5526 }
@@ -3446,7 +5620,7 @@ static int setup_redirects(struct command *prog, int squirrel[])
3446 continue; 5620 continue;
3447 } 5621 }
3448 mode = redir_table[redir->rd_type].mode; 5622 mode = redir_table[redir->rd_type].mode;
3449 p = expand_string_to_string(redir->rd_filename); 5623 p = expand_string_to_string(redir->rd_filename, /*unbackslash:*/ 1);
3450 openfd = open_or_warn(p, mode); 5624 openfd = open_or_warn(p, mode);
3451 free(p); 5625 free(p);
3452 if (openfd < 0) { 5626 if (openfd < 0) {
@@ -3491,101 +5665,6 @@ static void restore_redirects(int squirrel[])
3491 } 5665 }
3492} 5666}
3493 5667
3494
3495static void free_pipe_list(struct pipe *head);
3496
3497/* Return code is the exit status of the pipe */
3498static void free_pipe(struct pipe *pi)
3499{
3500 char **p;
3501 struct command *command;
3502 struct redir_struct *r, *rnext;
3503 int a, i;
3504
3505 if (pi->stopped_cmds > 0) /* why? */
3506 return;
3507 debug_printf_clean("run pipe: (pid %d)\n", getpid());
3508 for (i = 0; i < pi->num_cmds; i++) {
3509 command = &pi->cmds[i];
3510 debug_printf_clean(" command %d:\n", i);
3511 if (command->argv) {
3512 for (a = 0, p = command->argv; *p; a++, p++) {
3513 debug_printf_clean(" argv[%d] = %s\n", a, *p);
3514 }
3515 free_strings(command->argv);
3516 command->argv = NULL;
3517 }
3518 /* not "else if": on syntax error, we may have both! */
3519 if (command->group) {
3520 debug_printf_clean(" begin group (cmd_type:%d)\n",
3521 command->cmd_type);
3522 free_pipe_list(command->group);
3523 debug_printf_clean(" end group\n");
3524 command->group = NULL;
3525 }
3526 /* else is crucial here.
3527 * If group != NULL, child_func is meaningless */
3528#if ENABLE_HUSH_FUNCTIONS
3529 else if (command->child_func) {
3530 debug_printf_exec("cmd %p releases child func at %p\n", command, command->child_func);
3531 command->child_func->parent_cmd = NULL;
3532 }
3533#endif
3534#if !BB_MMU
3535 free(command->group_as_string);
3536 command->group_as_string = NULL;
3537#endif
3538 for (r = command->redirects; r; r = rnext) {
3539 debug_printf_clean(" redirect %d%s",
3540 r->rd_fd, redir_table[r->rd_type].descrip);
3541 /* guard against the case >$FOO, where foo is unset or blank */
3542 if (r->rd_filename) {
3543 debug_printf_clean(" fname:'%s'\n", r->rd_filename);
3544 free(r->rd_filename);
3545 r->rd_filename = NULL;
3546 }
3547 debug_printf_clean(" rd_dup:%d\n", r->rd_dup);
3548 rnext = r->next;
3549 free(r);
3550 }
3551 command->redirects = NULL;
3552 }
3553 free(pi->cmds); /* children are an array, they get freed all at once */
3554 pi->cmds = NULL;
3555#if ENABLE_HUSH_JOB
3556 free(pi->cmdtext);
3557 pi->cmdtext = NULL;
3558#endif
3559}
3560
3561static void free_pipe_list(struct pipe *head)
3562{
3563 struct pipe *pi, *next;
3564
3565 for (pi = head; pi; pi = next) {
3566#if HAS_KEYWORDS
3567 debug_printf_clean(" pipe reserved word %d\n", pi->res_word);
3568#endif
3569 free_pipe(pi);
3570 debug_printf_clean("pipe followup code %d\n", pi->followup);
3571 next = pi->next;
3572 /*pi->next = NULL;*/
3573 free(pi);
3574 }
3575}
3576
3577
3578static int run_list(struct pipe *pi);
3579#if BB_MMU
3580#define parse_stream(pstring, input, end_trigger) \
3581 parse_stream(input, end_trigger)
3582#endif
3583static struct pipe *parse_stream(char **pstring,
3584 struct in_str *input,
3585 int end_trigger);
3586static void parse_and_run_string(const char *s);
3587
3588
3589static char *find_in_path(const char *arg) 5668static char *find_in_path(const char *arg)
3590{ 5669{
3591 char *ret = NULL; 5670 char *ret = NULL;
@@ -3619,7 +5698,7 @@ static char *find_in_path(const char *arg)
3619 return ret; 5698 return ret;
3620} 5699}
3621 5700
3622static const struct built_in_command* find_builtin_helper(const char *name, 5701static const struct built_in_command *find_builtin_helper(const char *name,
3623 const struct built_in_command *x, 5702 const struct built_in_command *x,
3624 const struct built_in_command *end) 5703 const struct built_in_command *end)
3625{ 5704{
@@ -3633,11 +5712,11 @@ static const struct built_in_command* find_builtin_helper(const char *name,
3633 } 5712 }
3634 return NULL; 5713 return NULL;
3635} 5714}
3636static const struct built_in_command* find_builtin1(const char *name) 5715static const struct built_in_command *find_builtin1(const char *name)
3637{ 5716{
3638 return find_builtin_helper(name, bltins1, &bltins1[ARRAY_SIZE(bltins1)]); 5717 return find_builtin_helper(name, bltins1, &bltins1[ARRAY_SIZE(bltins1)]);
3639} 5718}
3640static const struct built_in_command* find_builtin(const char *name) 5719static const struct built_in_command *find_builtin(const char *name)
3641{ 5720{
3642 const struct built_in_command *x = find_builtin1(name); 5721 const struct built_in_command *x = find_builtin1(name);
3643 if (x) 5722 if (x)
@@ -4133,15 +6212,13 @@ static void remove_bg_job(struct pipe *pi)
4133static void delete_finished_bg_job(struct pipe *pi) 6212static void delete_finished_bg_job(struct pipe *pi)
4134{ 6213{
4135 remove_bg_job(pi); 6214 remove_bg_job(pi);
4136 pi->stopped_cmds = 0;
4137 free_pipe(pi); 6215 free_pipe(pi);
4138 free(pi);
4139} 6216}
4140#endif /* JOB */ 6217#endif /* JOB */
4141 6218
4142/* Check to see if any processes have exited -- if they 6219/* Check to see if any processes have exited -- if they
4143 * have, figure out why and see if a job has completed */ 6220 * have, figure out why and see if a job has completed */
4144static int checkjobs(struct pipe* fg_pipe) 6221static int checkjobs(struct pipe *fg_pipe)
4145{ 6222{
4146 int attributes; 6223 int attributes;
4147 int status; 6224 int status;
@@ -4307,7 +6384,7 @@ static int checkjobs(struct pipe* fg_pipe)
4307} 6384}
4308 6385
4309#if ENABLE_HUSH_JOB 6386#if ENABLE_HUSH_JOB
4310static int checkjobs_and_fg_shell(struct pipe* fg_pipe) 6387static int checkjobs_and_fg_shell(struct pipe *fg_pipe)
4311{ 6388{
4312 pid_t p; 6389 pid_t p;
4313 int rcode = checkjobs(fg_pipe); 6390 int rcode = checkjobs(fg_pipe);
@@ -4351,7 +6428,11 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe)
4351#define redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel, char argv_expanded) \ 6428#define redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel, char argv_expanded) \
4352 redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel) 6429 redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel)
4353#endif 6430#endif
4354static int redirect_and_varexp_helper(char ***new_env_p, struct variable **old_vars_p, struct command *command, int squirrel[3], char **argv_expanded) 6431static int redirect_and_varexp_helper(char ***new_env_p,
6432 struct variable **old_vars_p,
6433 struct command *command,
6434 int squirrel[3],
6435 char **argv_expanded)
4355{ 6436{
4356 /* setup_redirects acts on file descriptors, not FILEs. 6437 /* setup_redirects acts on file descriptors, not FILEs.
4357 * This is perfect for work that comes after exec(). 6438 * This is perfect for work that comes after exec().
@@ -4384,6 +6465,13 @@ static NOINLINE int run_pipe(struct pipe *pi)
4384 debug_printf_exec("run_pipe start: members:%d\n", pi->num_cmds); 6465 debug_printf_exec("run_pipe start: members:%d\n", pi->num_cmds);
4385 debug_enter(); 6466 debug_enter();
4386 6467
6468 /* Testcase: set -- q w e; (IFS='' echo "$*"; IFS=''; echo "$*"); echo "$*"
6469 * Result should be 3 lines: q w e, qwe, q w e
6470 */
6471 G.ifs = get_local_var_value("IFS");
6472 if (!G.ifs)
6473 G.ifs = defifs;
6474
4387 IF_HUSH_JOB(pi->pgrp = -1;) 6475 IF_HUSH_JOB(pi->pgrp = -1;)
4388 pi->stopped_cmds = 0; 6476 pi->stopped_cmds = 0;
4389 command = &pi->cmds[0]; 6477 command = &pi->cmds[0];
@@ -4483,7 +6571,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
4483 if (G_x_mode) 6571 if (G_x_mode)
4484 bb_putchar_stderr('+'); 6572 bb_putchar_stderr('+');
4485 while (*argv) { 6573 while (*argv) {
4486 char *p = expand_string_to_string(*argv); 6574 char *p = expand_string_to_string(*argv, /*unbackslash:*/ 1);
4487 if (G_x_mode) 6575 if (G_x_mode)
4488 fprintf(stderr, " %s", p); 6576 fprintf(stderr, " %s", p);
4489 debug_printf_exec("set shell var:'%s'->'%s'\n", 6577 debug_printf_exec("set shell var:'%s'->'%s'\n",
@@ -4515,11 +6603,6 @@ static NOINLINE int run_pipe(struct pipe *pi)
4515 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt); 6603 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt);
4516 } 6604 }
4517#endif 6605#endif
4518#ifdef CMD_SINGLEWORD_NOGLOB_COND
4519 else if (command->cmd_type == CMD_SINGLEWORD_NOGLOB_COND) {
4520 argv_expanded = expand_strvec_to_strvec_singleword_noglob_cond(argv + command->assignment_cnt);
4521 }
4522#endif
4523 else { 6606 else {
4524 argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt); 6607 argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt);
4525 } 6608 }
@@ -4835,7 +6918,7 @@ static int run_list(struct pipe *pi)
4835 enum { cond_code = 0 }; 6918 enum { cond_code = 0 };
4836#endif 6919#endif
4837#if HAS_KEYWORDS 6920#if HAS_KEYWORDS
4838 smallint rword; /* enum reserved_style */ 6921 smallint rword; /* RES_foo */
4839 smallint last_rword; /* ditto */ 6922 smallint last_rword; /* ditto */
4840#endif 6923#endif
4841 6924
@@ -4992,7 +7075,7 @@ static int run_list(struct pipe *pi)
4992 /* all prev words didn't match, does this one match? */ 7075 /* all prev words didn't match, does this one match? */
4993 argv = pi->cmds->argv; 7076 argv = pi->cmds->argv;
4994 while (*argv) { 7077 while (*argv) {
4995 char *pattern = expand_string_to_string(*argv); 7078 char *pattern = expand_string_to_string(*argv, /*unbackslash:*/ 1);
4996 /* TODO: which FNM_xxx flags to use? */ 7079 /* TODO: which FNM_xxx flags to use? */
4997 cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0); 7080 cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0);
4998 free(pattern); 7081 free(pattern);
@@ -5160,1930 +7243,6 @@ static int run_and_free_list(struct pipe *pi)
5160} 7243}
5161 7244
5162 7245
5163static struct pipe *new_pipe(void)
5164{
5165 struct pipe *pi;
5166 pi = xzalloc(sizeof(struct pipe));
5167 /*pi->followup = 0; - deliberately invalid value */
5168 /*pi->res_word = RES_NONE; - RES_NONE is 0 anyway */
5169 return pi;
5170}
5171
5172/* Command (member of a pipe) is complete, or we start a new pipe
5173 * if ctx->command is NULL.
5174 * No errors possible here.
5175 */
5176static int done_command(struct parse_context *ctx)
5177{
5178 /* The command is really already in the pipe structure, so
5179 * advance the pipe counter and make a new, null command. */
5180 struct pipe *pi = ctx->pipe;
5181 struct command *command = ctx->command;
5182
5183 if (command) {
5184 if (IS_NULL_CMD(command)) {
5185 debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds);
5186 goto clear_and_ret;
5187 }
5188 pi->num_cmds++;
5189 debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds);
5190 //debug_print_tree(ctx->list_head, 20);
5191 } else {
5192 debug_printf_parse("done_command: initializing, num_cmds=%d\n", pi->num_cmds);
5193 }
5194
5195 /* Only real trickiness here is that the uncommitted
5196 * command structure is not counted in pi->num_cmds. */
5197 pi->cmds = xrealloc(pi->cmds, sizeof(*pi->cmds) * (pi->num_cmds+1));
5198 ctx->command = command = &pi->cmds[pi->num_cmds];
5199 clear_and_ret:
5200 memset(command, 0, sizeof(*command));
5201 return pi->num_cmds; /* used only for 0/nonzero check */
5202}
5203
5204static void done_pipe(struct parse_context *ctx, pipe_style type)
5205{
5206 int not_null;
5207
5208 debug_printf_parse("done_pipe entered, followup %d\n", type);
5209 /* Close previous command */
5210 not_null = done_command(ctx);
5211 ctx->pipe->followup = type;
5212#if HAS_KEYWORDS
5213 ctx->pipe->pi_inverted = ctx->ctx_inverted;
5214 ctx->ctx_inverted = 0;
5215 ctx->pipe->res_word = ctx->ctx_res_w;
5216#endif
5217
5218 /* Without this check, even just <enter> on command line generates
5219 * tree of three NOPs (!). Which is harmless but annoying.
5220 * IOW: it is safe to do it unconditionally. */
5221 if (not_null
5222#if ENABLE_HUSH_IF
5223 || ctx->ctx_res_w == RES_FI
5224#endif
5225#if ENABLE_HUSH_LOOPS
5226 || ctx->ctx_res_w == RES_DONE
5227 || ctx->ctx_res_w == RES_FOR
5228 || ctx->ctx_res_w == RES_IN
5229#endif
5230#if ENABLE_HUSH_CASE
5231 || ctx->ctx_res_w == RES_ESAC
5232#endif
5233 ) {
5234 struct pipe *new_p;
5235 debug_printf_parse("done_pipe: adding new pipe: "
5236 "not_null:%d ctx->ctx_res_w:%d\n",
5237 not_null, ctx->ctx_res_w);
5238 new_p = new_pipe();
5239 ctx->pipe->next = new_p;
5240 ctx->pipe = new_p;
5241 /* RES_THEN, RES_DO etc are "sticky" -
5242 * they remain set for pipes inside if/while.
5243 * This is used to control execution.
5244 * RES_FOR and RES_IN are NOT sticky (needed to support
5245 * cases where variable or value happens to match a keyword):
5246 */
5247#if ENABLE_HUSH_LOOPS
5248 if (ctx->ctx_res_w == RES_FOR
5249 || ctx->ctx_res_w == RES_IN)
5250 ctx->ctx_res_w = RES_NONE;
5251#endif
5252#if ENABLE_HUSH_CASE
5253 if (ctx->ctx_res_w == RES_MATCH)
5254 ctx->ctx_res_w = RES_CASE_BODY;
5255 if (ctx->ctx_res_w == RES_CASE)
5256 ctx->ctx_res_w = RES_CASE_IN;
5257#endif
5258 ctx->command = NULL; /* trick done_command below */
5259 /* Create the memory for command, roughly:
5260 * ctx->pipe->cmds = new struct command;
5261 * ctx->command = &ctx->pipe->cmds[0];
5262 */
5263 done_command(ctx);
5264 //debug_print_tree(ctx->list_head, 10);
5265 }
5266 debug_printf_parse("done_pipe return\n");
5267}
5268
5269static void initialize_context(struct parse_context *ctx)
5270{
5271 memset(ctx, 0, sizeof(*ctx));
5272 ctx->pipe = ctx->list_head = new_pipe();
5273 /* Create the memory for command, roughly:
5274 * ctx->pipe->cmds = new struct command;
5275 * ctx->command = &ctx->pipe->cmds[0];
5276 */
5277 done_command(ctx);
5278}
5279
5280/* If a reserved word is found and processed, parse context is modified
5281 * and 1 is returned.
5282 */
5283#if HAS_KEYWORDS
5284struct reserved_combo {
5285 char literal[6];
5286 unsigned char res;
5287 unsigned char assignment_flag;
5288 int flag;
5289};
5290enum {
5291 FLAG_END = (1 << RES_NONE ),
5292# if ENABLE_HUSH_IF
5293 FLAG_IF = (1 << RES_IF ),
5294 FLAG_THEN = (1 << RES_THEN ),
5295 FLAG_ELIF = (1 << RES_ELIF ),
5296 FLAG_ELSE = (1 << RES_ELSE ),
5297 FLAG_FI = (1 << RES_FI ),
5298# endif
5299# if ENABLE_HUSH_LOOPS
5300 FLAG_FOR = (1 << RES_FOR ),
5301 FLAG_WHILE = (1 << RES_WHILE),
5302 FLAG_UNTIL = (1 << RES_UNTIL),
5303 FLAG_DO = (1 << RES_DO ),
5304 FLAG_DONE = (1 << RES_DONE ),
5305 FLAG_IN = (1 << RES_IN ),
5306# endif
5307# if ENABLE_HUSH_CASE
5308 FLAG_MATCH = (1 << RES_MATCH),
5309 FLAG_ESAC = (1 << RES_ESAC ),
5310# endif
5311 FLAG_START = (1 << RES_XXXX ),
5312};
5313
5314static const struct reserved_combo* match_reserved_word(o_string *word)
5315{
5316 /* Mostly a list of accepted follow-up reserved words.
5317 * FLAG_END means we are done with the sequence, and are ready
5318 * to turn the compound list into a command.
5319 * FLAG_START means the word must start a new compound list.
5320 */
5321 static const struct reserved_combo reserved_list[] = {
5322# if ENABLE_HUSH_IF
5323 { "!", RES_NONE, NOT_ASSIGNMENT , 0 },
5324 { "if", RES_IF, WORD_IS_KEYWORD, FLAG_THEN | FLAG_START },
5325 { "then", RES_THEN, WORD_IS_KEYWORD, FLAG_ELIF | FLAG_ELSE | FLAG_FI },
5326 { "elif", RES_ELIF, WORD_IS_KEYWORD, FLAG_THEN },
5327 { "else", RES_ELSE, WORD_IS_KEYWORD, FLAG_FI },
5328 { "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END },
5329# endif
5330# if ENABLE_HUSH_LOOPS
5331 { "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START },
5332 { "while", RES_WHILE, WORD_IS_KEYWORD, FLAG_DO | FLAG_START },
5333 { "until", RES_UNTIL, WORD_IS_KEYWORD, FLAG_DO | FLAG_START },
5334 { "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO },
5335 { "do", RES_DO, WORD_IS_KEYWORD, FLAG_DONE },
5336 { "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END },
5337# endif
5338# if ENABLE_HUSH_CASE
5339 { "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START },
5340 { "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END },
5341# endif
5342 };
5343 const struct reserved_combo *r;
5344
5345 for (r = reserved_list; r < reserved_list + ARRAY_SIZE(reserved_list); r++) {
5346 if (strcmp(word->data, r->literal) == 0)
5347 return r;
5348 }
5349 return NULL;
5350}
5351/* Return 0: not a keyword, 1: keyword
5352 */
5353static int reserved_word(o_string *word, struct parse_context *ctx)
5354{
5355# if ENABLE_HUSH_CASE
5356 static const struct reserved_combo reserved_match = {
5357 "", RES_MATCH, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_ESAC
5358 };
5359# endif
5360 const struct reserved_combo *r;
5361
5362 if (word->o_quoted)
5363 return 0;
5364 r = match_reserved_word(word);
5365 if (!r)
5366 return 0;
5367
5368 debug_printf("found reserved word %s, res %d\n", r->literal, r->res);
5369# if ENABLE_HUSH_CASE
5370 if (r->res == RES_IN && ctx->ctx_res_w == RES_CASE_IN) {
5371 /* "case word IN ..." - IN part starts first MATCH part */
5372 r = &reserved_match;
5373 } else
5374# endif
5375 if (r->flag == 0) { /* '!' */
5376 if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */
5377 syntax_error("! ! command");
5378 ctx->ctx_res_w = RES_SNTX;
5379 }
5380 ctx->ctx_inverted = 1;
5381 return 1;
5382 }
5383 if (r->flag & FLAG_START) {
5384 struct parse_context *old;
5385
5386 old = xmalloc(sizeof(*old));
5387 debug_printf_parse("push stack %p\n", old);
5388 *old = *ctx; /* physical copy */
5389 initialize_context(ctx);
5390 ctx->stack = old;
5391 } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) {
5392 syntax_error_at(word->data);
5393 ctx->ctx_res_w = RES_SNTX;
5394 return 1;
5395 } else {
5396 /* "{...} fi" is ok. "{...} if" is not
5397 * Example:
5398 * if { echo foo; } then { echo bar; } fi */
5399 if (ctx->command->group)
5400 done_pipe(ctx, PIPE_SEQ);
5401 }
5402
5403 ctx->ctx_res_w = r->res;
5404 ctx->old_flag = r->flag;
5405 word->o_assignment = r->assignment_flag;
5406
5407 if (ctx->old_flag & FLAG_END) {
5408 struct parse_context *old;
5409
5410 done_pipe(ctx, PIPE_SEQ);
5411 debug_printf_parse("pop stack %p\n", ctx->stack);
5412 old = ctx->stack;
5413 old->command->group = ctx->list_head;
5414 old->command->cmd_type = CMD_NORMAL;
5415# if !BB_MMU
5416 o_addstr(&old->as_string, ctx->as_string.data);
5417 o_free_unsafe(&ctx->as_string);
5418 old->command->group_as_string = xstrdup(old->as_string.data);
5419 debug_printf_parse("pop, remembering as:'%s'\n",
5420 old->command->group_as_string);
5421# endif
5422 *ctx = *old; /* physical copy */
5423 free(old);
5424 }
5425 return 1;
5426}
5427#endif /* HAS_KEYWORDS */
5428
5429/* Word is complete, look at it and update parsing context.
5430 * Normal return is 0. Syntax errors return 1.
5431 * Note: on return, word is reset, but not o_free'd!
5432 */
5433static int done_word(o_string *word, struct parse_context *ctx)
5434{
5435 struct command *command = ctx->command;
5436
5437 debug_printf_parse("done_word entered: '%s' %p\n", word->data, command);
5438 if (word->length == 0 && word->o_quoted == 0) {
5439 debug_printf_parse("done_word return 0: true null, ignored\n");
5440 return 0;
5441 }
5442
5443 if (ctx->pending_redirect) {
5444 /* We do not glob in e.g. >*.tmp case. bash seems to glob here
5445 * only if run as "bash", not "sh" */
5446 /* http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html
5447 * "2.7 Redirection
5448 * ...the word that follows the redirection operator
5449 * shall be subjected to tilde expansion, parameter expansion,
5450 * command substitution, arithmetic expansion, and quote
5451 * removal. Pathname expansion shall not be performed
5452 * on the word by a non-interactive shell; an interactive
5453 * shell may perform it, but shall do so only when
5454 * the expansion would result in one word."
5455 */
5456 ctx->pending_redirect->rd_filename = xstrdup(word->data);
5457 /* Cater for >\file case:
5458 * >\a creates file a; >\\a, >"\a", >"\\a" create file \a
5459 * Same with heredocs:
5460 * for <<\H delim is H; <<\\H, <<"\H", <<"\\H" - \H
5461 */
5462 if (ctx->pending_redirect->rd_type == REDIRECT_HEREDOC) {
5463 unbackslash(ctx->pending_redirect->rd_filename);
5464 /* Is it <<"HEREDOC"? */
5465 if (word->o_quoted) {
5466 ctx->pending_redirect->rd_dup |= HEREDOC_QUOTED;
5467 }
5468 }
5469 debug_printf_parse("word stored in rd_filename: '%s'\n", word->data);
5470 ctx->pending_redirect = NULL;
5471 } else {
5472 /* If this word wasn't an assignment, next ones definitely
5473 * can't be assignments. Even if they look like ones. */
5474 if (word->o_assignment != DEFINITELY_ASSIGNMENT
5475 && word->o_assignment != WORD_IS_KEYWORD
5476 ) {
5477 word->o_assignment = NOT_ASSIGNMENT;
5478 } else {
5479 if (word->o_assignment == DEFINITELY_ASSIGNMENT)
5480 command->assignment_cnt++;
5481 word->o_assignment = MAYBE_ASSIGNMENT;
5482 }
5483
5484#if HAS_KEYWORDS
5485# if ENABLE_HUSH_CASE
5486 if (ctx->ctx_dsemicolon
5487 && strcmp(word->data, "esac") != 0 /* not "... pattern) cmd;; esac" */
5488 ) {
5489 /* already done when ctx_dsemicolon was set to 1: */
5490 /* ctx->ctx_res_w = RES_MATCH; */
5491 ctx->ctx_dsemicolon = 0;
5492 } else
5493# endif
5494 if (!command->argv /* if it's the first word... */
5495# if ENABLE_HUSH_LOOPS
5496 && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */
5497 && ctx->ctx_res_w != RES_IN
5498# endif
5499# if ENABLE_HUSH_CASE
5500 && ctx->ctx_res_w != RES_CASE
5501# endif
5502 ) {
5503 debug_printf_parse("checking '%s' for reserved-ness\n", word->data);
5504 if (reserved_word(word, ctx)) {
5505 o_reset_to_empty_unquoted(word);
5506 debug_printf_parse("done_word return %d\n",
5507 (ctx->ctx_res_w == RES_SNTX));
5508 return (ctx->ctx_res_w == RES_SNTX);
5509 }
5510# ifdef CMD_SINGLEWORD_NOGLOB_COND
5511 if (strcmp(word->data, "export") == 0
5512# if ENABLE_HUSH_LOCAL
5513 || strcmp(word->data, "local") == 0
5514# endif
5515 ) {
5516 command->cmd_type = CMD_SINGLEWORD_NOGLOB_COND;
5517 } else
5518# endif
5519# if ENABLE_HUSH_BASH_COMPAT
5520 if (strcmp(word->data, "[[") == 0) {
5521 command->cmd_type = CMD_SINGLEWORD_NOGLOB;
5522 }
5523 /* fall through */
5524# endif
5525 }
5526#endif
5527 if (command->group) {
5528 /* "{ echo foo; } echo bar" - bad */
5529 syntax_error_at(word->data);
5530 debug_printf_parse("done_word return 1: syntax error, "
5531 "groups and arglists don't mix\n");
5532 return 1;
5533 }
5534 if (word->o_quoted /* word had "xx" or 'xx' at least as part of it. */
5535 /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */
5536 && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL)
5537 /* (otherwise it's known to be not empty and is already safe) */
5538 ) {
5539 /* exclude "$@" - it can expand to no word despite "" */
5540 char *p = word->data;
5541 while (p[0] == SPECIAL_VAR_SYMBOL
5542 && (p[1] & 0x7f) == '@'
5543 && p[2] == SPECIAL_VAR_SYMBOL
5544 ) {
5545 p += 3;
5546 }
5547 if (p == word->data || p[0] != '\0') {
5548 /* saw no "$@", or not only "$@" but some
5549 * real text is there too */
5550 /* insert "empty variable" reference, this makes
5551 * e.g. "", $empty"" etc to not disappear */
5552 o_addchr(word, SPECIAL_VAR_SYMBOL);
5553 o_addchr(word, SPECIAL_VAR_SYMBOL);
5554 }
5555 }
5556 command->argv = add_string_to_strings(command->argv, xstrdup(word->data));
5557 debug_print_strings("word appended to argv", command->argv);
5558 }
5559
5560#if ENABLE_HUSH_LOOPS
5561 if (ctx->ctx_res_w == RES_FOR) {
5562 if (word->o_quoted
5563 || !is_well_formed_var_name(command->argv[0], '\0')
5564 ) {
5565 /* bash says just "not a valid identifier" */
5566 syntax_error("not a valid identifier in for");
5567 return 1;
5568 }
5569 /* Force FOR to have just one word (variable name) */
5570 /* NB: basically, this makes hush see "for v in ..."
5571 * syntax as if it is "for v; in ...". FOR and IN become
5572 * two pipe structs in parse tree. */
5573 done_pipe(ctx, PIPE_SEQ);
5574 }
5575#endif
5576#if ENABLE_HUSH_CASE
5577 /* Force CASE to have just one word */
5578 if (ctx->ctx_res_w == RES_CASE) {
5579 done_pipe(ctx, PIPE_SEQ);
5580 }
5581#endif
5582
5583 o_reset_to_empty_unquoted(word);
5584
5585 debug_printf_parse("done_word return 0\n");
5586 return 0;
5587}
5588
5589
5590/* Peek ahead in the input to find out if we have a "&n" construct,
5591 * as in "2>&1", that represents duplicating a file descriptor.
5592 * Return:
5593 * REDIRFD_CLOSE if >&- "close fd" construct is seen,
5594 * REDIRFD_SYNTAX_ERR if syntax error,
5595 * REDIRFD_TO_FILE if no & was seen,
5596 * or the number found.
5597 */
5598#if BB_MMU
5599#define parse_redir_right_fd(as_string, input) \
5600 parse_redir_right_fd(input)
5601#endif
5602static int parse_redir_right_fd(o_string *as_string, struct in_str *input)
5603{
5604 int ch, d, ok;
5605
5606 ch = i_peek(input);
5607 if (ch != '&')
5608 return REDIRFD_TO_FILE;
5609
5610 ch = i_getch(input); /* get the & */
5611 nommu_addchr(as_string, ch);
5612 ch = i_peek(input);
5613 if (ch == '-') {
5614 ch = i_getch(input);
5615 nommu_addchr(as_string, ch);
5616 return REDIRFD_CLOSE;
5617 }
5618 d = 0;
5619 ok = 0;
5620 while (ch != EOF && isdigit(ch)) {
5621 d = d*10 + (ch-'0');
5622 ok = 1;
5623 ch = i_getch(input);
5624 nommu_addchr(as_string, ch);
5625 ch = i_peek(input);
5626 }
5627 if (ok) return d;
5628
5629//TODO: this is the place to catch ">&file" bashism (redirect both fd 1 and 2)
5630
5631 bb_error_msg("ambiguous redirect");
5632 return REDIRFD_SYNTAX_ERR;
5633}
5634
5635/* Return code is 0 normal, 1 if a syntax error is detected
5636 */
5637static int parse_redirect(struct parse_context *ctx,
5638 int fd,
5639 redir_type style,
5640 struct in_str *input)
5641{
5642 struct command *command = ctx->command;
5643 struct redir_struct *redir;
5644 struct redir_struct **redirp;
5645 int dup_num;
5646
5647 dup_num = REDIRFD_TO_FILE;
5648 if (style != REDIRECT_HEREDOC) {
5649 /* Check for a '>&1' type redirect */
5650 dup_num = parse_redir_right_fd(&ctx->as_string, input);
5651 if (dup_num == REDIRFD_SYNTAX_ERR)
5652 return 1;
5653 } else {
5654 int ch = i_peek(input);
5655 dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */
5656 if (dup_num) { /* <<-... */
5657 ch = i_getch(input);
5658 nommu_addchr(&ctx->as_string, ch);
5659 ch = i_peek(input);
5660 }
5661 }
5662
5663 if (style == REDIRECT_OVERWRITE && dup_num == REDIRFD_TO_FILE) {
5664 int ch = i_peek(input);
5665 if (ch == '|') {
5666 /* >|FILE redirect ("clobbering" >).
5667 * Since we do not support "set -o noclobber" yet,
5668 * >| and > are the same for now. Just eat |.
5669 */
5670 ch = i_getch(input);
5671 nommu_addchr(&ctx->as_string, ch);
5672 }
5673 }
5674
5675 /* Create a new redir_struct and append it to the linked list */
5676 redirp = &command->redirects;
5677 while ((redir = *redirp) != NULL) {
5678 redirp = &(redir->next);
5679 }
5680 *redirp = redir = xzalloc(sizeof(*redir));
5681 /* redir->next = NULL; */
5682 /* redir->rd_filename = NULL; */
5683 redir->rd_type = style;
5684 redir->rd_fd = (fd == -1) ? redir_table[style].default_fd : fd;
5685
5686 debug_printf_parse("redirect type %d %s\n", redir->rd_fd,
5687 redir_table[style].descrip);
5688
5689 redir->rd_dup = dup_num;
5690 if (style != REDIRECT_HEREDOC && dup_num != REDIRFD_TO_FILE) {
5691 /* Erik had a check here that the file descriptor in question
5692 * is legit; I postpone that to "run time"
5693 * A "-" representation of "close me" shows up as a -3 here */
5694 debug_printf_parse("duplicating redirect '%d>&%d'\n",
5695 redir->rd_fd, redir->rd_dup);
5696 } else {
5697 /* Set ctx->pending_redirect, so we know what to do at the
5698 * end of the next parsed word. */
5699 ctx->pending_redirect = redir;
5700 }
5701 return 0;
5702}
5703
5704/* If a redirect is immediately preceded by a number, that number is
5705 * supposed to tell which file descriptor to redirect. This routine
5706 * looks for such preceding numbers. In an ideal world this routine
5707 * needs to handle all the following classes of redirects...
5708 * echo 2>foo # redirects fd 2 to file "foo", nothing passed to echo
5709 * echo 49>foo # redirects fd 49 to file "foo", nothing passed to echo
5710 * echo -2>foo # redirects fd 1 to file "foo", "-2" passed to echo
5711 * echo 49x>foo # redirects fd 1 to file "foo", "49x" passed to echo
5712 *
5713 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html
5714 * "2.7 Redirection
5715 * ... If n is quoted, the number shall not be recognized as part of
5716 * the redirection expression. For example:
5717 * echo \2>a
5718 * writes the character 2 into file a"
5719 * We are getting it right by setting ->o_quoted on any \<char>
5720 *
5721 * A -1 return means no valid number was found,
5722 * the caller should use the appropriate default for this redirection.
5723 */
5724static int redirect_opt_num(o_string *o)
5725{
5726 int num;
5727
5728 if (o->data == NULL)
5729 return -1;
5730 num = bb_strtou(o->data, NULL, 10);
5731 if (errno || num < 0)
5732 return -1;
5733 o_reset_to_empty_unquoted(o);
5734 return num;
5735}
5736
5737#if BB_MMU
5738#define fetch_till_str(as_string, input, word, skip_tabs) \
5739 fetch_till_str(input, word, skip_tabs)
5740#endif
5741static char *fetch_till_str(o_string *as_string,
5742 struct in_str *input,
5743 const char *word,
5744 int skip_tabs)
5745{
5746 o_string heredoc = NULL_O_STRING;
5747 int past_EOL = 0;
5748 int ch;
5749
5750 goto jump_in;
5751 while (1) {
5752 ch = i_getch(input);
5753 nommu_addchr(as_string, ch);
5754 if (ch == '\n') {
5755 if (strcmp(heredoc.data + past_EOL, word) == 0) {
5756 heredoc.data[past_EOL] = '\0';
5757 debug_printf_parse("parsed heredoc '%s'\n", heredoc.data);
5758 return heredoc.data;
5759 }
5760 do {
5761 o_addchr(&heredoc, ch);
5762 past_EOL = heredoc.length;
5763 jump_in:
5764 do {
5765 ch = i_getch(input);
5766 nommu_addchr(as_string, ch);
5767 } while (skip_tabs && ch == '\t');
5768 } while (ch == '\n');
5769 }
5770 if (ch == EOF) {
5771 o_free_unsafe(&heredoc);
5772 return NULL;
5773 }
5774 o_addchr(&heredoc, ch);
5775 nommu_addchr(as_string, ch);
5776 }
5777}
5778
5779/* Look at entire parse tree for not-yet-loaded REDIRECT_HEREDOCs
5780 * and load them all. There should be exactly heredoc_cnt of them.
5781 */
5782static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_str *input)
5783{
5784 struct pipe *pi = ctx->list_head;
5785
5786 while (pi && heredoc_cnt) {
5787 int i;
5788 struct command *cmd = pi->cmds;
5789
5790 debug_printf_parse("fetch_heredocs: num_cmds:%d cmd argv0:'%s'\n",
5791 pi->num_cmds,
5792 cmd->argv ? cmd->argv[0] : "NONE");
5793 for (i = 0; i < pi->num_cmds; i++) {
5794 struct redir_struct *redir = cmd->redirects;
5795
5796 debug_printf_parse("fetch_heredocs: %d cmd argv0:'%s'\n",
5797 i, cmd->argv ? cmd->argv[0] : "NONE");
5798 while (redir) {
5799 if (redir->rd_type == REDIRECT_HEREDOC) {
5800 char *p;
5801
5802 redir->rd_type = REDIRECT_HEREDOC2;
5803 /* redir->rd_dup is (ab)used to indicate <<- */
5804 p = fetch_till_str(&ctx->as_string, input,
5805 redir->rd_filename, redir->rd_dup & HEREDOC_SKIPTABS);
5806 if (!p) {
5807 syntax_error("unexpected EOF in here document");
5808 return 1;
5809 }
5810 free(redir->rd_filename);
5811 redir->rd_filename = p;
5812 heredoc_cnt--;
5813 }
5814 redir = redir->next;
5815 }
5816 cmd++;
5817 }
5818 pi = pi->next;
5819 }
5820#if 0
5821 /* Should be 0. If it isn't, it's a parse error */
5822 if (heredoc_cnt)
5823 bb_error_msg_and_die("heredoc BUG 2");
5824#endif
5825 return 0;
5826}
5827
5828
5829#if ENABLE_HUSH_TICK
5830static FILE *generate_stream_from_string(const char *s, pid_t *pid_p)
5831{
5832 pid_t pid;
5833 int channel[2];
5834# if !BB_MMU
5835 char **to_free = NULL;
5836# endif
5837
5838 xpipe(channel);
5839 pid = BB_MMU ? xfork() : xvfork();
5840 if (pid == 0) { /* child */
5841 disable_restore_tty_pgrp_on_exit();
5842 /* Process substitution is not considered to be usual
5843 * 'command execution'.
5844 * SUSv3 says ctrl-Z should be ignored, ctrl-C should not.
5845 */
5846 bb_signals(0
5847 + (1 << SIGTSTP)
5848 + (1 << SIGTTIN)
5849 + (1 << SIGTTOU)
5850 , SIG_IGN);
5851 CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */
5852 close(channel[0]); /* NB: close _first_, then move fd! */
5853 xmove_fd(channel[1], 1);
5854 /* Prevent it from trying to handle ctrl-z etc */
5855 IF_HUSH_JOB(G.run_list_level = 1;)
5856 /* Awful hack for `trap` or $(trap).
5857 *
5858 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
5859 * contains an example where "trap" is executed in a subshell:
5860 *
5861 * save_traps=$(trap)
5862 * ...
5863 * eval "$save_traps"
5864 *
5865 * Standard does not say that "trap" in subshell shall print
5866 * parent shell's traps. It only says that its output
5867 * must have suitable form, but then, in the above example
5868 * (which is not supposed to be normative), it implies that.
5869 *
5870 * bash (and probably other shell) does implement it
5871 * (traps are reset to defaults, but "trap" still shows them),
5872 * but as a result, "trap" logic is hopelessly messed up:
5873 *
5874 * # trap
5875 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
5876 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
5877 * # true | trap <--- trap is in subshell - no output (ditto)
5878 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
5879 * trap -- 'echo Ho' SIGWINCH
5880 * # echo `(trap)` <--- in subshell in subshell - output
5881 * trap -- 'echo Ho' SIGWINCH
5882 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
5883 * trap -- 'echo Ho' SIGWINCH
5884 *
5885 * The rules when to forget and when to not forget traps
5886 * get really complex and nonsensical.
5887 *
5888 * Our solution: ONLY bare $(trap) or `trap` is special.
5889 */
5890 s = skip_whitespace(s);
5891 if (strncmp(s, "trap", 4) == 0 && (*skip_whitespace(s + 4) == '\0'))
5892 {
5893 static const char *const argv[] = { NULL, NULL };
5894 builtin_trap((char**)argv);
5895 exit(0); /* not _exit() - we need to fflush */
5896 }
5897# if BB_MMU
5898 reset_traps_to_defaults();
5899 parse_and_run_string(s);
5900 _exit(G.last_exitcode);
5901# else
5902 /* We re-execute after vfork on NOMMU. This makes this script safe:
5903 * yes "0123456789012345678901234567890" | dd bs=32 count=64k >BIG
5904 * huge=`cat BIG` # was blocking here forever
5905 * echo OK
5906 */
5907 re_execute_shell(&to_free,
5908 s,
5909 G.global_argv[0],
5910 G.global_argv + 1,
5911 NULL);
5912# endif
5913 }
5914
5915 /* parent */
5916 *pid_p = pid;
5917# if ENABLE_HUSH_FAST
5918 G.count_SIGCHLD++;
5919//bb_error_msg("[%d] fork in generate_stream_from_string: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
5920# endif
5921 enable_restore_tty_pgrp_on_exit();
5922# if !BB_MMU
5923 free(to_free);
5924# endif
5925 close(channel[1]);
5926 close_on_exec_on(channel[0]);
5927 return xfdopen_for_read(channel[0]);
5928}
5929
5930/* Return code is exit status of the process that is run. */
5931static int process_command_subs(o_string *dest, const char *s)
5932{
5933 FILE *fp;
5934 struct in_str pipe_str;
5935 pid_t pid;
5936 int status, ch, eol_cnt;
5937
5938 fp = generate_stream_from_string(s, &pid);
5939
5940 /* Now send results of command back into original context */
5941 setup_file_in_str(&pipe_str, fp);
5942 eol_cnt = 0;
5943 while ((ch = i_getch(&pipe_str)) != EOF) {
5944 if (ch == '\n') {
5945 eol_cnt++;
5946 continue;
5947 }
5948 while (eol_cnt) {
5949 o_addchr(dest, '\n');
5950 eol_cnt--;
5951 }
5952 o_addQchr(dest, ch);
5953 }
5954
5955 debug_printf("done reading from `cmd` pipe, closing it\n");
5956 fclose(fp);
5957 /* We need to extract exitcode. Test case
5958 * "true; echo `sleep 1; false` $?"
5959 * should print 1 */
5960 safe_waitpid(pid, &status, 0);
5961 debug_printf("child exited. returning its exitcode:%d\n", WEXITSTATUS(status));
5962 return WEXITSTATUS(status);
5963}
5964#endif /* ENABLE_HUSH_TICK */
5965
5966#if !ENABLE_HUSH_FUNCTIONS
5967#define parse_group(dest, ctx, input, ch) \
5968 parse_group(ctx, input, ch)
5969#endif
5970static int parse_group(o_string *dest, struct parse_context *ctx,
5971 struct in_str *input, int ch)
5972{
5973 /* dest contains characters seen prior to ( or {.
5974 * Typically it's empty, but for function defs,
5975 * it contains function name (without '()'). */
5976 struct pipe *pipe_list;
5977 int endch;
5978 struct command *command = ctx->command;
5979
5980 debug_printf_parse("parse_group entered\n");
5981#if ENABLE_HUSH_FUNCTIONS
5982 if (ch == '(' && !dest->o_quoted) {
5983 if (dest->length)
5984 if (done_word(dest, ctx))
5985 return 1;
5986 if (!command->argv)
5987 goto skip; /* (... */
5988 if (command->argv[1]) { /* word word ... (... */
5989 syntax_error_unexpected_ch('(');
5990 return 1;
5991 }
5992 /* it is "word(..." or "word (..." */
5993 do
5994 ch = i_getch(input);
5995 while (ch == ' ' || ch == '\t');
5996 if (ch != ')') {
5997 syntax_error_unexpected_ch(ch);
5998 return 1;
5999 }
6000 nommu_addchr(&ctx->as_string, ch);
6001 do
6002 ch = i_getch(input);
6003 while (ch == ' ' || ch == '\t' || ch == '\n');
6004 if (ch != '{') {
6005 syntax_error_unexpected_ch(ch);
6006 return 1;
6007 }
6008 nommu_addchr(&ctx->as_string, ch);
6009 command->cmd_type = CMD_FUNCDEF;
6010 goto skip;
6011 }
6012#endif
6013
6014#if 0 /* Prevented by caller */
6015 if (command->argv /* word [word]{... */
6016 || dest->length /* word{... */
6017 || dest->o_quoted /* ""{... */
6018 ) {
6019 syntax_error(NULL);
6020 debug_printf_parse("parse_group return 1: "
6021 "syntax error, groups and arglists don't mix\n");
6022 return 1;
6023 }
6024#endif
6025
6026#if ENABLE_HUSH_FUNCTIONS
6027 skip:
6028#endif
6029 endch = '}';
6030 if (ch == '(') {
6031 endch = ')';
6032 command->cmd_type = CMD_SUBSHELL;
6033 } else {
6034 /* bash does not allow "{echo...", requires whitespace */
6035 ch = i_getch(input);
6036 if (ch != ' ' && ch != '\t' && ch != '\n') {
6037 syntax_error_unexpected_ch(ch);
6038 return 1;
6039 }
6040 nommu_addchr(&ctx->as_string, ch);
6041 }
6042
6043 {
6044#if BB_MMU
6045# define as_string NULL
6046#else
6047 char *as_string = NULL;
6048#endif
6049 pipe_list = parse_stream(&as_string, input, endch);
6050#if !BB_MMU
6051 if (as_string)
6052 o_addstr(&ctx->as_string, as_string);
6053#endif
6054 /* empty ()/{} or parse error? */
6055 if (!pipe_list || pipe_list == ERR_PTR) {
6056 /* parse_stream already emitted error msg */
6057 if (!BB_MMU)
6058 free(as_string);
6059 debug_printf_parse("parse_group return 1: "
6060 "parse_stream returned %p\n", pipe_list);
6061 return 1;
6062 }
6063 command->group = pipe_list;
6064#if !BB_MMU
6065 as_string[strlen(as_string) - 1] = '\0'; /* plink ')' or '}' */
6066 command->group_as_string = as_string;
6067 debug_printf_parse("end of group, remembering as:'%s'\n",
6068 command->group_as_string);
6069#endif
6070#undef as_string
6071 }
6072 debug_printf_parse("parse_group return 0\n");
6073 return 0;
6074 /* command remains "open", available for possible redirects */
6075}
6076
6077#if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS
6078/* Subroutines for copying $(...) and `...` things */
6079static void add_till_backquote(o_string *dest, struct in_str *input);
6080/* '...' */
6081static void add_till_single_quote(o_string *dest, struct in_str *input)
6082{
6083 while (1) {
6084 int ch = i_getch(input);
6085 if (ch == EOF) {
6086 syntax_error_unterm_ch('\'');
6087 /*xfunc_die(); - redundant */
6088 }
6089 if (ch == '\'')
6090 return;
6091 o_addchr(dest, ch);
6092 }
6093}
6094/* "...\"...`..`...." - do we need to handle "...$(..)..." too? */
6095static void add_till_double_quote(o_string *dest, struct in_str *input)
6096{
6097 while (1) {
6098 int ch = i_getch(input);
6099 if (ch == EOF) {
6100 syntax_error_unterm_ch('"');
6101 /*xfunc_die(); - redundant */
6102 }
6103 if (ch == '"')
6104 return;
6105 if (ch == '\\') { /* \x. Copy both chars. */
6106 o_addchr(dest, ch);
6107 ch = i_getch(input);
6108 }
6109 o_addchr(dest, ch);
6110 if (ch == '`') {
6111 add_till_backquote(dest, input);
6112 o_addchr(dest, ch);
6113 continue;
6114 }
6115 //if (ch == '$') ...
6116 }
6117}
6118/* Process `cmd` - copy contents until "`" is seen. Complicated by
6119 * \` quoting.
6120 * "Within the backquoted style of command substitution, backslash
6121 * shall retain its literal meaning, except when followed by: '$', '`', or '\'.
6122 * The search for the matching backquote shall be satisfied by the first
6123 * backquote found without a preceding backslash; during this search,
6124 * if a non-escaped backquote is encountered within a shell comment,
6125 * a here-document, an embedded command substitution of the $(command)
6126 * form, or a quoted string, undefined results occur. A single-quoted
6127 * or double-quoted string that begins, but does not end, within the
6128 * "`...`" sequence produces undefined results."
6129 * Example Output
6130 * echo `echo '\'TEST\`echo ZZ\`BEST` \TESTZZBEST
6131 */
6132static void add_till_backquote(o_string *dest, struct in_str *input)
6133{
6134 while (1) {
6135 int ch = i_getch(input);
6136 if (ch == EOF) {
6137 syntax_error_unterm_ch('`');
6138 /*xfunc_die(); - redundant */
6139 }
6140 if (ch == '`')
6141 return;
6142 if (ch == '\\') {
6143 /* \x. Copy both chars unless it is \` */
6144 int ch2 = i_getch(input);
6145 if (ch2 == EOF) {
6146 syntax_error_unterm_ch('`');
6147 /*xfunc_die(); - redundant */
6148 }
6149 if (ch2 != '`' && ch2 != '$' && ch2 != '\\')
6150 o_addchr(dest, ch);
6151 ch = ch2;
6152 }
6153 o_addchr(dest, ch);
6154 }
6155}
6156/* Process $(cmd) - copy contents until ")" is seen. Complicated by
6157 * quoting and nested ()s.
6158 * "With the $(command) style of command substitution, all characters
6159 * following the open parenthesis to the matching closing parenthesis
6160 * constitute the command. Any valid shell script can be used for command,
6161 * except a script consisting solely of redirections which produces
6162 * unspecified results."
6163 * Example Output
6164 * echo $(echo '(TEST)' BEST) (TEST) BEST
6165 * echo $(echo 'TEST)' BEST) TEST) BEST
6166 * echo $(echo \(\(TEST\) BEST) ((TEST) BEST
6167 *
6168 * Also adapted to eat ${var%...} and $((...)) constructs, since ... part
6169 * can contain arbitrary constructs, just like $(cmd).
6170 * In bash compat mode, it needs to also be able to stop on '}' or ':'
6171 * for ${var:N[:M]} parsing.
6172 */
6173#define DOUBLE_CLOSE_CHAR_FLAG 0x80
6174static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsigned end_ch)
6175{
6176 int ch;
6177 char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG;
6178# if ENABLE_HUSH_BASH_COMPAT
6179 char end_char2 = end_ch >> 8;
6180# endif
6181 end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1);
6182
6183 while (1) {
6184 ch = i_getch(input);
6185 if (ch == EOF) {
6186 syntax_error_unterm_ch(end_ch);
6187 /*xfunc_die(); - redundant */
6188 }
6189 if (ch == end_ch IF_HUSH_BASH_COMPAT( || ch == end_char2)) {
6190 if (!dbl)
6191 break;
6192 /* we look for closing )) of $((EXPR)) */
6193 if (i_peek(input) == end_ch) {
6194 i_getch(input); /* eat second ')' */
6195 break;
6196 }
6197 }
6198 o_addchr(dest, ch);
6199 if (ch == '(' || ch == '{') {
6200 ch = (ch == '(' ? ')' : '}');
6201 add_till_closing_bracket(dest, input, ch);
6202 o_addchr(dest, ch);
6203 continue;
6204 }
6205 if (ch == '\'') {
6206 add_till_single_quote(dest, input);
6207 o_addchr(dest, ch);
6208 continue;
6209 }
6210 if (ch == '"') {
6211 add_till_double_quote(dest, input);
6212 o_addchr(dest, ch);
6213 continue;
6214 }
6215 if (ch == '`') {
6216 add_till_backquote(dest, input);
6217 o_addchr(dest, ch);
6218 continue;
6219 }
6220 if (ch == '\\') {
6221 /* \x. Copy verbatim. Important for \(, \) */
6222 ch = i_getch(input);
6223 if (ch == EOF) {
6224 syntax_error_unterm_ch(')');
6225 /*xfunc_die(); - redundant */
6226 }
6227 o_addchr(dest, ch);
6228 continue;
6229 }
6230 }
6231 return ch;
6232}
6233#endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS */
6234
6235/* Return code: 0 for OK, 1 for syntax error */
6236#if BB_MMU
6237#define parse_dollar(as_string, dest, input) \
6238 parse_dollar(dest, input)
6239#define as_string NULL
6240#endif
6241static int parse_dollar(o_string *as_string,
6242 o_string *dest,
6243 struct in_str *input)
6244{
6245 int ch = i_peek(input); /* first character after the $ */
6246 unsigned char quote_mask = dest->o_escape ? 0x80 : 0;
6247
6248 debug_printf_parse("parse_dollar entered: ch='%c'\n", ch);
6249 if (isalpha(ch)) {
6250 ch = i_getch(input);
6251 nommu_addchr(as_string, ch);
6252 make_var:
6253 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6254 while (1) {
6255 debug_printf_parse(": '%c'\n", ch);
6256 o_addchr(dest, ch | quote_mask);
6257 quote_mask = 0;
6258 ch = i_peek(input);
6259 if (!isalnum(ch) && ch != '_')
6260 break;
6261 ch = i_getch(input);
6262 nommu_addchr(as_string, ch);
6263 }
6264 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6265 } else if (isdigit(ch)) {
6266 make_one_char_var:
6267 ch = i_getch(input);
6268 nommu_addchr(as_string, ch);
6269 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6270 debug_printf_parse(": '%c'\n", ch);
6271 o_addchr(dest, ch | quote_mask);
6272 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6273 } else switch (ch) {
6274 case '$': /* pid */
6275 case '!': /* last bg pid */
6276 case '?': /* last exit code */
6277 case '#': /* number of args */
6278 case '*': /* args */
6279 case '@': /* args */
6280 goto make_one_char_var;
6281 case '{': {
6282 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6283
6284 ch = i_getch(input); /* eat '{' */
6285 nommu_addchr(as_string, ch);
6286
6287 ch = i_getch(input); /* first char after '{' */
6288 nommu_addchr(as_string, ch);
6289 /* It should be ${?}, or ${#var},
6290 * or even ${?+subst} - operator acting on a special variable,
6291 * or the beginning of variable name.
6292 */
6293 if (!strchr(_SPECIAL_VARS_STR, ch) && !isalnum(ch)) { /* not one of those */
6294 bad_dollar_syntax:
6295 syntax_error_unterm_str("${name}");
6296 debug_printf_parse("parse_dollar return 1: unterminated ${name}\n");
6297 return 1;
6298 }
6299 ch |= quote_mask;
6300
6301 /* It's possible to just call add_till_closing_bracket() at this point.
6302 * However, this regresses some of our testsuite cases
6303 * which check invalid constructs like ${%}.
6304 * Oh well... let's check that the var name part is fine... */
6305
6306 while (1) {
6307 unsigned pos;
6308
6309 o_addchr(dest, ch);
6310 debug_printf_parse(": '%c'\n", ch);
6311
6312 ch = i_getch(input);
6313 nommu_addchr(as_string, ch);
6314 if (ch == '}')
6315 break;
6316
6317 if (!isalnum(ch) && ch != '_') {
6318 unsigned end_ch;
6319 unsigned char last_ch;
6320 /* handle parameter expansions
6321 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02
6322 */
6323 if (!strchr("%#:-=+?", ch)) /* ${var<bad_char>... */
6324 goto bad_dollar_syntax;
6325 o_addchr(dest, ch);
6326
6327 /* Eat everything until closing '}' (or ':') */
6328 end_ch = '}';
6329 if (ENABLE_HUSH_BASH_COMPAT
6330 && ch == ':'
6331 && !strchr("%#:-=+?"+3, i_peek(input))
6332 ) {
6333 /* It's ${var:N[:M]} thing */
6334 end_ch = '}' * 0x100 + ':';
6335 }
6336 again:
6337 if (!BB_MMU)
6338 pos = dest->length;
6339#if ENABLE_HUSH_DOLLAR_OPS
6340 last_ch = add_till_closing_bracket(dest, input, end_ch);
6341#else
6342#error Simple code to only allow ${var} is not implemented
6343#endif
6344 if (as_string) {
6345 o_addstr(as_string, dest->data + pos);
6346 o_addchr(as_string, last_ch);
6347 }
6348
6349 if (ENABLE_HUSH_BASH_COMPAT && (end_ch & 0xff00)) {
6350 /* close the first block: */
6351 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6352 /* while parsing N from ${var:N[:M]}... */
6353 if ((end_ch & 0xff) == last_ch) {
6354 /* ...got ':' - parse the rest */
6355 end_ch = '}';
6356 goto again;
6357 }
6358 /* ...got '}', not ':' - it's ${var:N}! emulate :999999999 */
6359 o_addstr(dest, "999999999");
6360 }
6361 break;
6362 }
6363 }
6364 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6365 break;
6366 }
6367#if ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_TICK
6368 case '(': {
6369 unsigned pos;
6370
6371 ch = i_getch(input);
6372 nommu_addchr(as_string, ch);
6373# if ENABLE_SH_MATH_SUPPORT
6374 if (i_peek(input) == '(') {
6375 ch = i_getch(input);
6376 nommu_addchr(as_string, ch);
6377 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6378 o_addchr(dest, /*quote_mask |*/ '+');
6379 if (!BB_MMU)
6380 pos = dest->length;
6381 add_till_closing_bracket(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG);
6382 if (as_string) {
6383 o_addstr(as_string, dest->data + pos);
6384 o_addchr(as_string, ')');
6385 o_addchr(as_string, ')');
6386 }
6387 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6388 break;
6389 }
6390# endif
6391# if ENABLE_HUSH_TICK
6392 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6393 o_addchr(dest, quote_mask | '`');
6394 if (!BB_MMU)
6395 pos = dest->length;
6396 add_till_closing_bracket(dest, input, ')');
6397 if (as_string) {
6398 o_addstr(as_string, dest->data + pos);
6399 o_addchr(as_string, ')');
6400 }
6401 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6402# endif
6403 break;
6404 }
6405#endif
6406 case '_':
6407 ch = i_getch(input);
6408 nommu_addchr(as_string, ch);
6409 ch = i_peek(input);
6410 if (isalnum(ch)) { /* it's $_name or $_123 */
6411 ch = '_';
6412 goto make_var;
6413 }
6414 /* else: it's $_ */
6415 /* TODO: $_ and $-: */
6416 /* $_ Shell or shell script name; or last argument of last command
6417 * (if last command wasn't a pipe; if it was, bash sets $_ to "");
6418 * but in command's env, set to full pathname used to invoke it */
6419 /* $- Option flags set by set builtin or shell options (-i etc) */
6420 default:
6421 o_addQchr(dest, '$');
6422 }
6423 debug_printf_parse("parse_dollar return 0\n");
6424 return 0;
6425#undef as_string
6426}
6427
6428#if BB_MMU
6429#define parse_stream_dquoted(as_string, dest, input, dquote_end) \
6430 parse_stream_dquoted(dest, input, dquote_end)
6431#define as_string NULL
6432#endif
6433static int parse_stream_dquoted(o_string *as_string,
6434 o_string *dest,
6435 struct in_str *input,
6436 int dquote_end)
6437{
6438 int ch;
6439 int next;
6440
6441 again:
6442 ch = i_getch(input);
6443 if (ch != EOF)
6444 nommu_addchr(as_string, ch);
6445 if (ch == dquote_end) { /* may be only '"' or EOF */
6446 if (dest->o_assignment == NOT_ASSIGNMENT)
6447 dest->o_escape ^= 1;
6448 debug_printf_parse("parse_stream_dquoted return 0\n");
6449 return 0;
6450 }
6451 /* note: can't move it above ch == dquote_end check! */
6452 if (ch == EOF) {
6453 syntax_error_unterm_ch('"');
6454 /*xfunc_die(); - redundant */
6455 }
6456 next = '\0';
6457 if (ch != '\n') {
6458 next = i_peek(input);
6459 }
6460 debug_printf_parse("\" ch=%c (%d) escape=%d\n",
6461 ch, ch, dest->o_escape);
6462 if (ch == '\\') {
6463 if (next == EOF) {
6464 syntax_error("\\<eof>");
6465 xfunc_die();
6466 }
6467 /* bash:
6468 * "The backslash retains its special meaning [in "..."]
6469 * only when followed by one of the following characters:
6470 * $, `, ", \, or <newline>. A double quote may be quoted
6471 * within double quotes by preceding it with a backslash."
6472 */
6473 if (strchr("$`\"\\\n", next) != NULL) {
6474 ch = i_getch(input);
6475 if (ch != '\n') {
6476 o_addqchr(dest, ch);
6477 nommu_addchr(as_string, ch);
6478 }
6479 } else {
6480 o_addqchr(dest, '\\');
6481 nommu_addchr(as_string, '\\');
6482 }
6483 goto again;
6484 }
6485 if (ch == '$') {
6486 if (parse_dollar(as_string, dest, input) != 0) {
6487 debug_printf_parse("parse_stream_dquoted return 1: "
6488 "parse_dollar returned non-0\n");
6489 return 1;
6490 }
6491 goto again;
6492 }
6493#if ENABLE_HUSH_TICK
6494 if (ch == '`') {
6495 //unsigned pos = dest->length;
6496 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6497 o_addchr(dest, 0x80 | '`');
6498 add_till_backquote(dest, input);
6499 o_addchr(dest, SPECIAL_VAR_SYMBOL);
6500 //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
6501 goto again;
6502 }
6503#endif
6504 o_addQchr(dest, ch);
6505 if (ch == '='
6506 && (dest->o_assignment == MAYBE_ASSIGNMENT
6507 || dest->o_assignment == WORD_IS_KEYWORD)
6508 && is_well_formed_var_name(dest->data, '=')
6509 ) {
6510 dest->o_assignment = DEFINITELY_ASSIGNMENT;
6511 }
6512 goto again;
6513#undef as_string
6514}
6515
6516/*
6517 * Scan input until EOF or end_trigger char.
6518 * Return a list of pipes to execute, or NULL on EOF
6519 * or if end_trigger character is met.
6520 * On syntax error, exit is shell is not interactive,
6521 * reset parsing machinery and start parsing anew,
6522 * or return ERR_PTR.
6523 */
6524static struct pipe *parse_stream(char **pstring,
6525 struct in_str *input,
6526 int end_trigger)
6527{
6528 struct parse_context ctx;
6529 o_string dest = NULL_O_STRING;
6530 int is_in_dquote;
6531 int heredoc_cnt;
6532
6533 /* Double-quote state is handled in the state variable is_in_dquote.
6534 * A single-quote triggers a bypass of the main loop until its mate is
6535 * found. When recursing, quote state is passed in via dest->o_escape.
6536 */
6537 debug_printf_parse("parse_stream entered, end_trigger='%c'\n",
6538 end_trigger ? end_trigger : 'X');
6539 debug_enter();
6540
6541 /* If very first arg is "" or '', dest.data may end up NULL.
6542 * Preventing this: */
6543 o_addchr(&dest, '\0');
6544 dest.length = 0;
6545
6546 G.ifs = get_local_var_value("IFS");
6547 if (G.ifs == NULL)
6548 G.ifs = defifs;
6549
6550 reset:
6551#if ENABLE_HUSH_INTERACTIVE
6552 input->promptmode = 0; /* PS1 */
6553#endif
6554 /* dest.o_assignment = MAYBE_ASSIGNMENT; - already is */
6555 initialize_context(&ctx);
6556 is_in_dquote = 0;
6557 heredoc_cnt = 0;
6558 while (1) {
6559 const char *is_ifs;
6560 const char *is_special;
6561 int ch;
6562 int next;
6563 int redir_fd;
6564 redir_type redir_style;
6565
6566 if (is_in_dquote) {
6567 /* dest.o_quoted = 1; - already is (see below) */
6568 if (parse_stream_dquoted(&ctx.as_string, &dest, input, '"')) {
6569 goto parse_error;
6570 }
6571 /* We reached closing '"' */
6572 is_in_dquote = 0;
6573 }
6574 ch = i_getch(input);
6575 debug_printf_parse(": ch=%c (%d) escape=%d\n",
6576 ch, ch, dest.o_escape);
6577 if (ch == EOF) {
6578 struct pipe *pi;
6579
6580 if (heredoc_cnt) {
6581 syntax_error_unterm_str("here document");
6582 goto parse_error;
6583 }
6584 /* end_trigger == '}' case errors out earlier,
6585 * checking only ')' */
6586 if (end_trigger == ')') {
6587 syntax_error_unterm_ch('('); /* exits */
6588 /* goto parse_error; */
6589 }
6590
6591 if (done_word(&dest, &ctx)) {
6592 goto parse_error;
6593 }
6594 o_free(&dest);
6595 done_pipe(&ctx, PIPE_SEQ);
6596 pi = ctx.list_head;
6597 /* If we got nothing... */
6598 /* (this makes bare "&" cmd a no-op.
6599 * bash says: "syntax error near unexpected token '&'") */
6600 if (pi->num_cmds == 0
6601 IF_HAS_KEYWORDS( && pi->res_word == RES_NONE)
6602 ) {
6603 free_pipe_list(pi);
6604 pi = NULL;
6605 }
6606#if !BB_MMU
6607 debug_printf_parse("as_string '%s'\n", ctx.as_string.data);
6608 if (pstring)
6609 *pstring = ctx.as_string.data;
6610 else
6611 o_free_unsafe(&ctx.as_string);
6612#endif
6613 debug_leave();
6614 debug_printf_parse("parse_stream return %p\n", pi);
6615 return pi;
6616 }
6617 nommu_addchr(&ctx.as_string, ch);
6618
6619 next = '\0';
6620 if (ch != '\n')
6621 next = i_peek(input);
6622
6623 is_special = "{}<>;&|()#'" /* special outside of "str" */
6624 "\\$\"" IF_HUSH_TICK("`"); /* always special */
6625 /* Are { and } special here? */
6626 if (ctx.command->argv /* word [word]{... - non-special */
6627 || dest.length /* word{... - non-special */
6628 || dest.o_quoted /* ""{... - non-special */
6629 || (next != ';' /* }; - special */
6630 && next != ')' /* }) - special */
6631 && next != '&' /* }& and }&& ... - special */
6632 && next != '|' /* }|| ... - special */
6633 && !strchr(G.ifs, next) /* {word - non-special */
6634 )
6635 ) {
6636 /* They are not special, skip "{}" */
6637 is_special += 2;
6638 }
6639 is_special = strchr(is_special, ch);
6640 is_ifs = strchr(G.ifs, ch);
6641
6642 if (!is_special && !is_ifs) { /* ordinary char */
6643 ordinary_char:
6644 o_addQchr(&dest, ch);
6645 if ((dest.o_assignment == MAYBE_ASSIGNMENT
6646 || dest.o_assignment == WORD_IS_KEYWORD)
6647 && ch == '='
6648 && is_well_formed_var_name(dest.data, '=')
6649 ) {
6650 dest.o_assignment = DEFINITELY_ASSIGNMENT;
6651 }
6652 continue;
6653 }
6654
6655 if (is_ifs) {
6656 if (done_word(&dest, &ctx)) {
6657 goto parse_error;
6658 }
6659 if (ch == '\n') {
6660#if ENABLE_HUSH_CASE
6661 /* "case ... in <newline> word) ..." -
6662 * newlines are ignored (but ';' wouldn't be) */
6663 if (ctx.command->argv == NULL
6664 && ctx.ctx_res_w == RES_MATCH
6665 ) {
6666 continue;
6667 }
6668#endif
6669 /* Treat newline as a command separator. */
6670 done_pipe(&ctx, PIPE_SEQ);
6671 debug_printf_parse("heredoc_cnt:%d\n", heredoc_cnt);
6672 if (heredoc_cnt) {
6673 if (fetch_heredocs(heredoc_cnt, &ctx, input)) {
6674 goto parse_error;
6675 }
6676 heredoc_cnt = 0;
6677 }
6678 dest.o_assignment = MAYBE_ASSIGNMENT;
6679 ch = ';';
6680 /* note: if (is_ifs) continue;
6681 * will still trigger for us */
6682 }
6683 }
6684
6685 /* "cmd}" or "cmd }..." without semicolon or &:
6686 * } is an ordinary char in this case, even inside { cmd; }
6687 * Pathological example: { ""}; } should exec "}" cmd
6688 */
6689 if (ch == '}') {
6690 if (!IS_NULL_CMD(ctx.command) /* cmd } */
6691 || dest.length != 0 /* word} */
6692 || dest.o_quoted /* ""} */
6693 ) {
6694 goto ordinary_char;
6695 }
6696 if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */
6697 goto skip_end_trigger;
6698 /* else: } does terminate a group */
6699 }
6700
6701 if (end_trigger && end_trigger == ch
6702 && (ch != ';' || heredoc_cnt == 0)
6703#if ENABLE_HUSH_CASE
6704 && (ch != ')'
6705 || ctx.ctx_res_w != RES_MATCH
6706 || (!dest.o_quoted && strcmp(dest.data, "esac") == 0)
6707 )
6708#endif
6709 ) {
6710 if (heredoc_cnt) {
6711 /* This is technically valid:
6712 * { cat <<HERE; }; echo Ok
6713 * heredoc
6714 * heredoc
6715 * HERE
6716 * but we don't support this.
6717 * We require heredoc to be in enclosing {}/(),
6718 * if any.
6719 */
6720 syntax_error_unterm_str("here document");
6721 goto parse_error;
6722 }
6723 if (done_word(&dest, &ctx)) {
6724 goto parse_error;
6725 }
6726 done_pipe(&ctx, PIPE_SEQ);
6727 dest.o_assignment = MAYBE_ASSIGNMENT;
6728 /* Do we sit outside of any if's, loops or case's? */
6729 if (!HAS_KEYWORDS
6730 IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0))
6731 ) {
6732 o_free(&dest);
6733#if !BB_MMU
6734 debug_printf_parse("as_string '%s'\n", ctx.as_string.data);
6735 if (pstring)
6736 *pstring = ctx.as_string.data;
6737 else
6738 o_free_unsafe(&ctx.as_string);
6739#endif
6740 debug_leave();
6741 debug_printf_parse("parse_stream return %p: "
6742 "end_trigger char found\n",
6743 ctx.list_head);
6744 return ctx.list_head;
6745 }
6746 }
6747 skip_end_trigger:
6748 if (is_ifs)
6749 continue;
6750
6751 /* Catch <, > before deciding whether this word is
6752 * an assignment. a=1 2>z b=2: b=2 is still assignment */
6753 switch (ch) {
6754 case '>':
6755 redir_fd = redirect_opt_num(&dest);
6756 if (done_word(&dest, &ctx)) {
6757 goto parse_error;
6758 }
6759 redir_style = REDIRECT_OVERWRITE;
6760 if (next == '>') {
6761 redir_style = REDIRECT_APPEND;
6762 ch = i_getch(input);
6763 nommu_addchr(&ctx.as_string, ch);
6764 }
6765#if 0
6766 else if (next == '(') {
6767 syntax_error(">(process) not supported");
6768 goto parse_error;
6769 }
6770#endif
6771 if (parse_redirect(&ctx, redir_fd, redir_style, input))
6772 goto parse_error;
6773 continue; /* back to top of while (1) */
6774 case '<':
6775 redir_fd = redirect_opt_num(&dest);
6776 if (done_word(&dest, &ctx)) {
6777 goto parse_error;
6778 }
6779 redir_style = REDIRECT_INPUT;
6780 if (next == '<') {
6781 redir_style = REDIRECT_HEREDOC;
6782 heredoc_cnt++;
6783 debug_printf_parse("++heredoc_cnt=%d\n", heredoc_cnt);
6784 ch = i_getch(input);
6785 nommu_addchr(&ctx.as_string, ch);
6786 } else if (next == '>') {
6787 redir_style = REDIRECT_IO;
6788 ch = i_getch(input);
6789 nommu_addchr(&ctx.as_string, ch);
6790 }
6791#if 0
6792 else if (next == '(') {
6793 syntax_error("<(process) not supported");
6794 goto parse_error;
6795 }
6796#endif
6797 if (parse_redirect(&ctx, redir_fd, redir_style, input))
6798 goto parse_error;
6799 continue; /* back to top of while (1) */
6800 }
6801
6802 if (dest.o_assignment == MAYBE_ASSIGNMENT
6803 /* check that we are not in word in "a=1 2>word b=1": */
6804 && !ctx.pending_redirect
6805 ) {
6806 /* ch is a special char and thus this word
6807 * cannot be an assignment */
6808 dest.o_assignment = NOT_ASSIGNMENT;
6809 }
6810
6811 /* Note: nommu_addchr(&ctx.as_string, ch) is already done */
6812
6813 switch (ch) {
6814 case '#':
6815 if (dest.length == 0) {
6816 while (1) {
6817 ch = i_peek(input);
6818 if (ch == EOF || ch == '\n')
6819 break;
6820 i_getch(input);
6821 /* note: we do not add it to &ctx.as_string */
6822 }
6823 nommu_addchr(&ctx.as_string, '\n');
6824 } else {
6825 o_addQchr(&dest, ch);
6826 }
6827 break;
6828 case '\\':
6829 if (next == EOF) {
6830 syntax_error("\\<eof>");
6831 xfunc_die();
6832 }
6833 ch = i_getch(input);
6834 if (ch != '\n') {
6835 o_addchr(&dest, '\\');
6836 /*nommu_addchr(&ctx.as_string, '\\'); - already done */
6837 o_addchr(&dest, ch);
6838 nommu_addchr(&ctx.as_string, ch);
6839 /* Example: echo Hello \2>file
6840 * we need to know that word 2 is quoted */
6841 dest.o_quoted = 1;
6842 }
6843#if !BB_MMU
6844 else {
6845 /* It's "\<newline>". Remove trailing '\' from ctx.as_string */
6846 ctx.as_string.data[--ctx.as_string.length] = '\0';
6847 }
6848#endif
6849 break;
6850 case '$':
6851 if (parse_dollar(&ctx.as_string, &dest, input) != 0) {
6852 debug_printf_parse("parse_stream parse error: "
6853 "parse_dollar returned non-0\n");
6854 goto parse_error;
6855 }
6856 break;
6857 case '\'':
6858 dest.o_quoted = 1;
6859 while (1) {
6860 ch = i_getch(input);
6861 if (ch == EOF) {
6862 syntax_error_unterm_ch('\'');
6863 /*xfunc_die(); - redundant */
6864 }
6865 nommu_addchr(&ctx.as_string, ch);
6866 if (ch == '\'')
6867 break;
6868 o_addqchr(&dest, ch);
6869 }
6870 break;
6871 case '"':
6872 dest.o_quoted = 1;
6873 is_in_dquote ^= 1; /* invert */
6874 if (dest.o_assignment == NOT_ASSIGNMENT)
6875 dest.o_escape ^= 1;
6876 break;
6877#if ENABLE_HUSH_TICK
6878 case '`': {
6879 unsigned pos;
6880
6881 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6882 o_addchr(&dest, '`');
6883 pos = dest.length;
6884 add_till_backquote(&dest, input);
6885# if !BB_MMU
6886 o_addstr(&ctx.as_string, dest.data + pos);
6887 o_addchr(&ctx.as_string, '`');
6888# endif
6889 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6890 //debug_printf_subst("SUBST RES3 '%s'\n", dest.data + pos);
6891 break;
6892 }
6893#endif
6894 case ';':
6895#if ENABLE_HUSH_CASE
6896 case_semi:
6897#endif
6898 if (done_word(&dest, &ctx)) {
6899 goto parse_error;
6900 }
6901 done_pipe(&ctx, PIPE_SEQ);
6902#if ENABLE_HUSH_CASE
6903 /* Eat multiple semicolons, detect
6904 * whether it means something special */
6905 while (1) {
6906 ch = i_peek(input);
6907 if (ch != ';')
6908 break;
6909 ch = i_getch(input);
6910 nommu_addchr(&ctx.as_string, ch);
6911 if (ctx.ctx_res_w == RES_CASE_BODY) {
6912 ctx.ctx_dsemicolon = 1;
6913 ctx.ctx_res_w = RES_MATCH;
6914 break;
6915 }
6916 }
6917#endif
6918 new_cmd:
6919 /* We just finished a cmd. New one may start
6920 * with an assignment */
6921 dest.o_assignment = MAYBE_ASSIGNMENT;
6922 break;
6923 case '&':
6924 if (done_word(&dest, &ctx)) {
6925 goto parse_error;
6926 }
6927 if (next == '&') {
6928 ch = i_getch(input);
6929 nommu_addchr(&ctx.as_string, ch);
6930 done_pipe(&ctx, PIPE_AND);
6931 } else {
6932 done_pipe(&ctx, PIPE_BG);
6933 }
6934 goto new_cmd;
6935 case '|':
6936 if (done_word(&dest, &ctx)) {
6937 goto parse_error;
6938 }
6939#if ENABLE_HUSH_CASE
6940 if (ctx.ctx_res_w == RES_MATCH)
6941 break; /* we are in case's "word | word)" */
6942#endif
6943 if (next == '|') { /* || */
6944 ch = i_getch(input);
6945 nommu_addchr(&ctx.as_string, ch);
6946 done_pipe(&ctx, PIPE_OR);
6947 } else {
6948 /* we could pick up a file descriptor choice here
6949 * with redirect_opt_num(), but bash doesn't do it.
6950 * "echo foo 2| cat" yields "foo 2". */
6951 done_command(&ctx);
6952#if !BB_MMU
6953 o_reset_to_empty_unquoted(&ctx.as_string);
6954#endif
6955 }
6956 goto new_cmd;
6957 case '(':
6958#if ENABLE_HUSH_CASE
6959 /* "case... in [(]word)..." - skip '(' */
6960 if (ctx.ctx_res_w == RES_MATCH
6961 && ctx.command->argv == NULL /* not (word|(... */
6962 && dest.length == 0 /* not word(... */
6963 && dest.o_quoted == 0 /* not ""(... */
6964 ) {
6965 continue;
6966 }
6967#endif
6968 case '{':
6969 if (parse_group(&dest, &ctx, input, ch) != 0) {
6970 goto parse_error;
6971 }
6972 goto new_cmd;
6973 case ')':
6974#if ENABLE_HUSH_CASE
6975 if (ctx.ctx_res_w == RES_MATCH)
6976 goto case_semi;
6977#endif
6978 case '}':
6979 /* proper use of this character is caught by end_trigger:
6980 * if we see {, we call parse_group(..., end_trigger='}')
6981 * and it will match } earlier (not here). */
6982 syntax_error_unexpected_ch(ch);
6983 goto parse_error;
6984 default:
6985 if (HUSH_DEBUG)
6986 bb_error_msg_and_die("BUG: unexpected %c\n", ch);
6987 }
6988 } /* while (1) */
6989
6990 parse_error:
6991 {
6992 struct parse_context *pctx;
6993 IF_HAS_KEYWORDS(struct parse_context *p2;)
6994
6995 /* Clean up allocated tree.
6996 * Sample for finding leaks on syntax error recovery path.
6997 * Run it from interactive shell, watch pmap `pidof hush`.
6998 * while if false; then false; fi; do break; fi
6999 * Samples to catch leaks at execution:
7000 * while if (true | {true;}); then echo ok; fi; do break; done
7001 * while if (true | {true;}); then echo ok; fi; do (if echo ok; break; then :; fi) | cat; break; done
7002 */
7003 pctx = &ctx;
7004 do {
7005 /* Update pipe/command counts,
7006 * otherwise freeing may miss some */
7007 done_pipe(pctx, PIPE_SEQ);
7008 debug_printf_clean("freeing list %p from ctx %p\n",
7009 pctx->list_head, pctx);
7010 debug_print_tree(pctx->list_head, 0);
7011 free_pipe_list(pctx->list_head);
7012 debug_printf_clean("freed list %p\n", pctx->list_head);
7013#if !BB_MMU
7014 o_free_unsafe(&pctx->as_string);
7015#endif
7016 IF_HAS_KEYWORDS(p2 = pctx->stack;)
7017 if (pctx != &ctx) {
7018 free(pctx);
7019 }
7020 IF_HAS_KEYWORDS(pctx = p2;)
7021 } while (HAS_KEYWORDS && pctx);
7022 /* Free text, clear all dest fields */
7023 o_free(&dest);
7024 /* If we are not in top-level parse, we return,
7025 * our caller will propagate error.
7026 */
7027 if (end_trigger != ';') {
7028#if !BB_MMU
7029 if (pstring)
7030 *pstring = NULL;
7031#endif
7032 debug_leave();
7033 return ERR_PTR;
7034 }
7035 /* Discard cached input, force prompt */
7036 input->p = NULL;
7037 IF_HUSH_INTERACTIVE(input->promptme = 1;)
7038 goto reset;
7039 }
7040}
7041
7042/* Executing from string: eval, sh -c '...'
7043 * or from file: /etc/profile, . file, sh <script>, sh (intereactive)
7044 * end_trigger controls how often we stop parsing
7045 * NUL: parse all, execute, return
7046 * ';': parse till ';' or newline, execute, repeat till EOF
7047 */
7048static void parse_and_run_stream(struct in_str *inp, int end_trigger)
7049{
7050 /* Why we need empty flag?
7051 * An obscure corner case "false; ``; echo $?":
7052 * empty command in `` should still set $? to 0.
7053 * But we can't just set $? to 0 at the start,
7054 * this breaks "false; echo `echo $?`" case.
7055 */
7056 bool empty = 1;
7057 while (1) {
7058 struct pipe *pipe_list;
7059
7060 pipe_list = parse_stream(NULL, inp, end_trigger);
7061 if (!pipe_list) { /* EOF */
7062 if (empty)
7063 G.last_exitcode = 0;
7064 break;
7065 }
7066 debug_print_tree(pipe_list, 0);
7067 debug_printf_exec("parse_and_run_stream: run_and_free_list\n");
7068 run_and_free_list(pipe_list);
7069 empty = 0;
7070 }
7071}
7072
7073static void parse_and_run_string(const char *s)
7074{
7075 struct in_str input;
7076 setup_string_in_str(&input, s);
7077 parse_and_run_stream(&input, '\0');
7078}
7079
7080static void parse_and_run_file(FILE *f)
7081{
7082 struct in_str input;
7083 setup_file_in_str(&input, f);
7084 parse_and_run_stream(&input, ';');
7085}
7086
7087/* Called a few times only (or even once if "sh -c") */ 7246/* Called a few times only (or even once if "sh -c") */
7088static void init_sigmasks(void) 7247static void init_sigmasks(void)
7089{ 7248{
@@ -7183,13 +7342,6 @@ static int set_mode(const char cstate, const char mode)
7183int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 7342int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
7184int hush_main(int argc, char **argv) 7343int hush_main(int argc, char **argv)
7185{ 7344{
7186 static const struct variable const_shell_ver = {
7187 .next = NULL,
7188 .varstr = (char*)hush_version_str,
7189 .max_len = 1, /* 0 can provoke free(name) */
7190 .flg_export = 1,
7191 .flg_read_only = 1,
7192 };
7193 int opt; 7345 int opt;
7194 unsigned builtin_argc; 7346 unsigned builtin_argc;
7195 char **e; 7347 char **e;
@@ -7202,12 +7354,16 @@ int hush_main(int argc, char **argv)
7202 G.argv0_for_re_execing = argv[0]; 7354 G.argv0_for_re_execing = argv[0];
7203#endif 7355#endif
7204 /* Deal with HUSH_VERSION */ 7356 /* Deal with HUSH_VERSION */
7205 G.shell_ver = const_shell_ver; /* copying struct here */ 7357 G.shell_ver.flg_export = 1;
7358 G.shell_ver.flg_read_only = 1;
7359 /* Code which handles ${var<op>...} needs writable values for all variables,
7360 * therefore we xstrdup: */
7361 G.shell_ver.varstr = xstrdup(hush_version_str),
7206 G.top_var = &G.shell_ver; 7362 G.top_var = &G.shell_ver;
7363 /* Create shell local variables from the values
7364 * currently living in the environment */
7207 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION"); 7365 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION");
7208 unsetenv("HUSH_VERSION"); /* in case it exists in initial env */ 7366 unsetenv("HUSH_VERSION"); /* in case it exists in initial env */
7209 /* Initialize our shell local variables with the values
7210 * currently living in the environment */
7211 cur_var = G.top_var; 7367 cur_var = G.top_var;
7212 e = environ; 7368 e = environ;
7213 if (e) while (*e) { 7369 if (e) while (*e) {
@@ -7221,9 +7377,9 @@ int hush_main(int argc, char **argv)
7221 } 7377 }
7222 e++; 7378 e++;
7223 } 7379 }
7224 /* reinstate HUSH_VERSION */ 7380 /* (Re)insert HUSH_VERSION into env (AFTER we scanned the env!) */
7225 debug_printf_env("putenv '%s'\n", hush_version_str); 7381 debug_printf_env("putenv '%s'\n", G.shell_ver.varstr);
7226 putenv((char *)hush_version_str); 7382 putenv(G.shell_ver.varstr);
7227 7383
7228 /* Export PWD */ 7384 /* Export PWD */
7229 set_pwd_var(/*exp:*/ 1); 7385 set_pwd_var(/*exp:*/ 1);
@@ -7266,7 +7422,20 @@ int hush_main(int argc, char **argv)
7266 7422
7267#if ENABLE_FEATURE_EDITING 7423#if ENABLE_FEATURE_EDITING
7268 G.line_input_state = new_line_input_t(FOR_SHELL); 7424 G.line_input_state = new_line_input_t(FOR_SHELL);
7425# if defined MAX_HISTORY && MAX_HISTORY > 0 && ENABLE_HUSH_SAVEHISTORY
7426 {
7427 const char *hp = get_local_var_value("HISTFILE");
7428 if (!hp) {
7429 hp = get_local_var_value("HOME");
7430 if (hp) {
7431 G.line_input_state->hist_file = concat_path_file(hp, ".hush_history");
7432 //set_local_var(xasprintf("HISTFILE=%s", ...));
7433 }
7434 }
7435 }
7436# endif
7269#endif 7437#endif
7438
7270 G.global_argc = argc; 7439 G.global_argc = argc;
7271 G.global_argv = argv; 7440 G.global_argv = argv;
7272 /* Initialize some more globals to non-zero values */ 7441 /* Initialize some more globals to non-zero values */
@@ -7586,15 +7755,6 @@ int hush_main(int argc, char **argv)
7586} 7755}
7587 7756
7588 7757
7589#if ENABLE_LASH
7590int lash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
7591int lash_main(int argc, char **argv)
7592{
7593 bb_error_msg("lash is deprecated, please use hush instead");
7594 return hush_main(argc, argv);
7595}
7596#endif
7597
7598#if ENABLE_MSH 7758#if ENABLE_MSH
7599int msh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 7759int msh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
7600int msh_main(int argc, char **argv) 7760int msh_main(int argc, char **argv)
@@ -7725,7 +7885,7 @@ static int FAST_FUNC builtin_exit(char **argv)
7725 * exit 7885 * exit
7726 # EEE (then bash exits) 7886 # EEE (then bash exits)
7727 * 7887 *
7728 * we can use G.exiting = -1 as indicator "last cmd was exit" 7888 * TODO: we can use G.exiting = -1 as indicator "last cmd was exit"
7729 */ 7889 */
7730 7890
7731 /* note: EXIT trap is run by hush_exit */ 7891 /* note: EXIT trap is run by hush_exit */
@@ -7765,13 +7925,16 @@ static void helper_export_local(char **argv, int exp, int lvl)
7765{ 7925{
7766 do { 7926 do {
7767 char *name = *argv; 7927 char *name = *argv;
7928 char *name_end = strchrnul(name, '=');
7768 7929
7769 /* So far we do not check that name is valid (TODO?) */ 7930 /* So far we do not check that name is valid (TODO?) */
7770 7931
7771 if (strchr(name, '=') == NULL) { 7932 if (*name_end == '\0') {
7772 struct variable *var; 7933 struct variable *var, **vpp;
7934
7935 vpp = get_ptr_to_local_var(name, name_end - name);
7936 var = vpp ? *vpp : NULL;
7773 7937
7774 var = get_local_var(name);
7775 if (exp == -1) { /* unexporting? */ 7938 if (exp == -1) { /* unexporting? */
7776 /* export -n NAME (without =VALUE) */ 7939 /* export -n NAME (without =VALUE) */
7777 if (var) { 7940 if (var) {
diff --git a/shell/hush_test/hush-bugs/export_exp.tests b/shell/hush_test/hush-bugs/export_exp.tests.disabled
index 91f57aa2c..0913fd3f2 100755..100644
--- a/shell/hush_test/hush-bugs/export_exp.tests
+++ b/shell/hush_test/hush-bugs/export_exp.tests.disabled
@@ -1,3 +1,6 @@
1# This test shows a very special handling of export and local
2# builtins by bash.
3
1v="a=aa0 b=bb0" 4v="a=aa0 b=bb0"
2# only 1st arg should be expanded in multiple words 5# only 1st arg should be expanded in multiple words
3export $v c=$v 6export $v c=$v
diff --git a/shell/hush_test/hush-glob/glob2.right b/shell/hush_test/hush-glob/glob2.right
new file mode 100644
index 000000000..7a70c2263
--- /dev/null
+++ b/shell/hush_test/hush-glob/glob2.right
@@ -0,0 +1,18 @@
1Expected Actual
2Z\* : Z\*
3Z* : Z*
4Z\f : Z\f
5Z\* : Z\*
6
7Z\z : Z\z
8Zz : Zz
9Z\z : Z\z
10Z\z : Z\z
11
12Z\ : Z\
13Z\ : Z\
14
15Z\f Zf : Z\f Zf
16Z\f Zf : Z\f Zf
17
18Done: 0
diff --git a/shell/hush_test/hush-glob/glob2.tests b/shell/hush_test/hush-glob/glob2.tests
new file mode 100755
index 000000000..4dbc92599
--- /dev/null
+++ b/shell/hush_test/hush-glob/glob2.tests
@@ -0,0 +1,27 @@
1# This test demonstrates that in unquoted $v, backslashes expand by this rule:
2# \z -> \\\z; \<eol> -> \\<eol> (for any z, special or not),
3# and subsequently globbing converts \\ to \ and treats \z as literal z
4# even if it is a special char.
5
6>'Zf'
7>'Z\f'
8 echo 'Expected' 'Actual'
9v='\*'; echo 'Z\* :' Z$v
10 echo 'Z* :' Z\*
11 echo 'Z\f :' Z\\*
12 echo 'Z\* :' Z\\\* # NB! only this matches Z$v output
13echo
14v='\z'; echo 'Z\z :' Z$v
15 echo 'Zz :' Z\z
16 echo 'Z\z :' Z\\z
17 echo 'Z\z :' Z\\\z
18echo
19v='\'; echo 'Z\ :' Z$v
20 echo 'Z\ :' Z\\
21echo
22v='*'; echo 'Z\f Zf :' Z$v
23 echo 'Z\f Zf :' Z*
24echo
25
26rm 'Z\f' 'Zf'
27echo Done: $?
diff --git a/shell/hush_test/hush-misc/heredoc_backslash1.right b/shell/hush_test/hush-misc/heredoc_backslash1.right
new file mode 100644
index 000000000..6a6114821
--- /dev/null
+++ b/shell/hush_test/hush-misc/heredoc_backslash1.right
@@ -0,0 +1,43 @@
1Quoted heredoc:
2a\
3 b
4a\\
5 b
6 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
7 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
8 123456 `echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-'`
9 123456 $(echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-')
10c\
11
12Unquoted heredoc:
13a b
14a\
15 b
16 123456 -qwerty-\t-\-\"-\'-`-\--\z-\*-\?-
17 -qwerty-\t-\-\"-\'-`-\--\z-\*-\?-
18 123456 v-$a-\t-\-\"-\x-`-\--\z-\*-\?-
19 123456 v-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-
20cEOF2
21
22Quoted -heredoc:
23a\
24b
25a\\
26b
27 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
28-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
29 123456 `echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-'`
30 123456 $(echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-')
31c\
32
33Unquoted -heredoc:
34a b
35a\
36b
37 123456 -qwerty-\t-\-\"-\'-`-\--\z-\*-\?-
38-qwerty-\t-\-\"-\'-`-\--\z-\*-\?-
39 123456 v-$a-\t-\-\"-\x-`-\--\z-\*-\?-
40 123456 v-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-
41cEOF4
42
43Done: 0
diff --git a/shell/hush_test/hush-misc/heredoc_backslash1.tests b/shell/hush_test/hush-misc/heredoc_backslash1.tests
new file mode 100755
index 000000000..501af5490
--- /dev/null
+++ b/shell/hush_test/hush-misc/heredoc_backslash1.tests
@@ -0,0 +1,70 @@
1# Test for correct handling of backslashes.
2# Note that some lines in each heredoc start with a tab.
3
4a=qwerty
5
6echo Quoted heredoc:
7cat <<"EOF1"
8a\
9 b
10a\\
11 b
12 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
13 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
14 123456 `echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-'`
15 123456 $(echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-')
16c\
17EOF1
18echo
19
20echo Unquoted heredoc:
21cat <<EOF2
22a\
23 b
24a\\
25 b
26 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
27 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
28 123456 `echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-'`
29 123456 $(echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-')
30c\
31EOF2
32EOF2
33echo
34
35echo Quoted -heredoc:
36cat <<-"EOF3"
37a\
38 b
39a\\
40 b
41 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
42 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
43 123456 `echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-'`
44 123456 $(echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-')
45c\
46 EOF3
47# In -heredoc case the marker is detected even if it is indented.
48echo
49
50echo Unquoted -heredoc:
51cat <<-EOF4
52a\
53 b
54a\\
55 b
56 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
57 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
58 123456 `echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-'`
59 123456 $(echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-')
60c\
61EOF4
62 EOF4
63# The marker is not detected if preceding line ends in backslash.
64# TODO: marker should be detected even if it is split by line continuation:
65# EOF\
66# 4
67# but currently hush doesn't do it. (Tab before "4" is not allowed, though.)
68echo
69
70echo "Done: $?"
diff --git a/shell/hush_test/hush-psubst/tick3.right b/shell/hush_test/hush-psubst/tick3.right
index dc84e9263..00f267ae5 100644
--- a/shell/hush_test/hush-psubst/tick3.right
+++ b/shell/hush_test/hush-psubst/tick3.right
@@ -2,5 +2,5 @@
2$TEST 2$TEST
3Q 3Q
4a\bc 4a\bc
5a"c 511-$a-\t-\-\"-`-\--\z-\*-\?-22 33-$a-\t-\-"-`-\--\z-\*-\?-44
6done:0 6done:0
diff --git a/shell/hush_test/hush-psubst/tick3.tests b/shell/hush_test/hush-psubst/tick3.tests
index 469c43c27..3aeb241c3 100755
--- a/shell/hush_test/hush-psubst/tick3.tests
+++ b/shell/hush_test/hush-psubst/tick3.tests
@@ -7,6 +7,8 @@ echo `echo '\'TEST\`echo ZZ\`BEST`
7echo `echo \\$TEST` 7echo `echo \\$TEST`
8echo `echo \$TEST` 8echo `echo \$TEST`
9echo a`echo \\\\b`c 9echo a`echo \\\\b`c
10# \" etc are NOT special (passed verbatim WITH \)! 10
11echo a`echo \"`c 11# \" is not special if in unquoted `cmd` (passed verbatim WITH \),
12# but is special in quoted one
13echo `echo 11'-$a-\t-\\-\"-\`-\--\z-\*-\?-'22` "`echo 33'-$a-\t-\\-\"-\`-\--\z-\*-\?-'44`"
12echo done:$? 14echo done:$?
diff --git a/shell/hush_test/hush-trap/exit.right b/shell/hush_test/hush-trap/exit.right
index b4932fb7f..3d0072564 100644
--- a/shell/hush_test/hush-trap/exit.right
+++ b/shell/hush_test/hush-trap/exit.right
@@ -1,2 +1,12 @@
1cow 1cow
2moo 2moo
3Traps1:
4trap -- 'exitfunc' EXIT
5Traps2:
6trap -- 'echo Should not run' EXIT
7Check1: 42
8Traps1:
9trap -- 'exitfunc' EXIT
10Traps2:
11trap -- 'echo Should not run' EXIT
12Check2: 42
diff --git a/shell/hush_test/hush-trap/exit.tests b/shell/hush_test/hush-trap/exit.tests
index 092543c25..2061105dd 100755
--- a/shell/hush_test/hush-trap/exit.tests
+++ b/shell/hush_test/hush-trap/exit.tests
@@ -1,3 +1,34 @@
1"$THIS_SH" -c 'trap "echo cow" 0' 1"$THIS_SH" -c 'trap "echo cow" 0'
2"$THIS_SH" -c 'trap "echo moo" EXIT' 2"$THIS_SH" -c 'trap "echo moo" EXIT'
3"$THIS_SH" -c 'trap "echo no" 0; trap 0' 3"$THIS_SH" -c 'trap "echo no" 0; trap 0'
4
5(
6exitfunc() {
7 echo "Traps1:"
8 trap
9 # EXIT trap is disabled after it is triggered,
10 # it can not be "re-armed" like this:
11 trap "echo Should not run" EXIT
12 echo "Traps2:"
13 trap
14}
15trap 'exitfunc' EXIT
16exit 42
17)
18echo Check1: $?
19
20(
21exitfunc() {
22 echo "Traps1:"
23 trap
24 # EXIT trap is disabled after it is triggered,
25 # it can not be "re-armed" like this:
26 trap "echo Should not run" EXIT
27 echo "Traps2:"
28 trap
29 exit 42
30}
31trap 'exitfunc' EXIT
32exit 66
33)
34echo Check2: $?
diff --git a/shell/hush_test/hush-trap/subshell.tests b/shell/hush_test/hush-trap/subshell.tests
index 045294bf4..d877f2b82 100755
--- a/shell/hush_test/hush-trap/subshell.tests
+++ b/shell/hush_test/hush-trap/subshell.tests
@@ -11,10 +11,9 @@ trap 'bad: caught WINCH' WINCH
11# With TERM we'll check whether it is reset 11# With TERM we'll check whether it is reset
12trap 'bad: caught TERM' TERM 12trap 'bad: caught TERM' TERM
13 13
14# using bash, because we don't have $PPID (yet) 14(trap; "$THIS_SH" -c 'kill -HUP $PPID'; echo Ok)
15(trap; bash -c 'kill -HUP $PPID'; echo Ok) 15(trap; "$THIS_SH" -c 'kill -QUIT $PPID'; echo Ok)
16(trap; bash -c 'kill -QUIT $PPID'; echo Ok) 16(trap; "$THIS_SH" -c 'kill -SYS $PPID'; echo Ok)
17(trap; bash -c 'kill -SYS $PPID'; echo Ok) 17(trap; "$THIS_SH" -c 'kill -WINCH $PPID'; echo Ok)
18(trap; bash -c 'kill -WINCH $PPID'; echo Ok) 18(trap; "$THIS_SH" -c 'kill -TERM $PPID'; echo Bad: TERM is not reset)
19(trap; bash -c 'kill -TERM $PPID'; echo Bad: TERM is not reset)
20echo Done 19echo Done
diff --git a/shell/hush_test/hush-vars/var_bash1.right b/shell/hush_test/hush-vars/var_bash1.right
new file mode 100644
index 000000000..c0a07699b
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash1.right
@@ -0,0 +1,14 @@
1
2
3f
4bcdef
5abcdef
6abcdef
7bcde
8abcd
9abcd
10abcdef
11bcdef
12abcdef
13abcdef
14abcdef
diff --git a/shell/hush_test/hush-vars/var_bash1.tests b/shell/hush_test/hush-vars/var_bash1.tests
new file mode 100755
index 000000000..24d3c9a00
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash1.tests
@@ -0,0 +1,18 @@
1var=abcdef
2
3echo ${var:7}
4echo ${var:6}
5echo ${var:5}
6echo ${var:1}
7echo ${var:0}
8echo ${var:-1}
9
10echo ${var:1:4}
11echo ${var:0:4}
12echo ${var::4}
13echo ${var:-1:4}
14
15echo ${var:1:7}
16echo ${var:0:7}
17echo ${var::7}
18echo ${var:-1:7}
diff --git a/shell/hush_test/hush-vars/var_bash2.right b/shell/hush_test/hush-vars/var_bash2.right
new file mode 100644
index 000000000..acba5c6fb
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash2.right
@@ -0,0 +1,10 @@
1abc123xcba123
2abx123dcba123
3abx123dxba123
4abcx23dcba123
5abcxxxdcbaxxx
6abx
7xba123
8abx23
9abc23dcba123
10abcdcba
diff --git a/shell/hush_test/hush-vars/var_bash2.tests b/shell/hush_test/hush-vars/var_bash2.tests
new file mode 100755
index 000000000..29c526cef
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash2.tests
@@ -0,0 +1,24 @@
1var=abc123dcba123
2
3echo ${var/d/x}
4echo ${var/c/x}
5echo ${var//c/x}
6echo ${var/[123]/x}
7echo ${var//[123]/x}
8echo ${var/c*/x}
9echo ${var/*c/x}
10
11# must match longest match: result is "abx23"
12echo ${var/c*1/x}
13
14# empty replacement - 2nd slash can be omitted
15echo ${var/[123]}
16echo ${var//[123]}
17
18### ash doesn't support
19### # match only at the beginning:
20### echo ${var/#a/x}
21### echo ${var/#b/x} # should not match
22### echo ${var//#b/x} # should not match
23### # match only at the end:
24### echo ${var/%3/x}
diff --git a/shell/hush_test/hush-vars/var_bash3.right b/shell/hush_test/hush-vars/var_bash3.right
new file mode 100644
index 000000000..a97c850ea
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash3.right
@@ -0,0 +1,20 @@
11 a041#c
22 a041#c
33 a\041#c
44 a\041#c
55 a\041#c
66 a\041#c
77 a\041#c
88 a\041#c
99 a\041#c
1010 a\c
1111 a\c
1212 a\c
1313 a\\c
1414 a\\c
1515 a\\c
1616 a\tc
1717 a\tc
1818 a\tc
1919 atc
2020 a\tc
diff --git a/shell/hush_test/hush-vars/var_bash3.tests b/shell/hush_test/hush-vars/var_bash3.tests
new file mode 100755
index 000000000..146dbb6a5
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash3.tests
@@ -0,0 +1,41 @@
1a='abc'
2r=${a//b/\041#}
3echo 1 $r
4echo 2 ${a//b/\041#}
5echo 3 "${a//b/\041#}"
6
7a='abc'
8r=${a//b/\\041#}
9echo 4 $r
10echo 5 ${a//b/\\041#}
11echo 6 "${a//b/\\041#}"
12
13a='abc'
14b='\041#'
15r=${a//b/$b}
16echo 7 $r
17echo 8 ${a//b/$b}
18echo 9 "${a//b/$b}"
19
20a='abc'
21b='\'
22r="${a//b/$b}"
23echo 10 $r
24echo 11 ${a//b/$b}
25echo 12 "${a//b/$b}"
26
27a='abc'
28b='\\'
29r="${a//b/$b}"
30echo 13 $r
31echo 14 ${a//b/$b}
32echo 15 "${a//b/$b}"
33
34a='abc'
35b='\t'
36r="${a//b/$b}"
37echo 16 $r
38echo 17 ${a//b/$b}
39echo 18 "${a//b/$b}"
40echo 19 ${a//b/\t}
41echo 20 "${a//b/\t}"
diff --git a/shell/hush_test/hush-vars/var_bash4.right b/shell/hush_test/hush-vars/var_bash4.right
new file mode 100644
index 000000000..0ef1bf661
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash4.right
@@ -0,0 +1,40 @@
1Source: a*b\*c
2Replace str: _\\_\z_
3Pattern: single backslash and star: "replace literal star"
4Unquoted: a_\_z_b\*c
5Unquoted =: a_\_z_b\*c
6Quoted: a_\_\z_b\*c
7Quoted =: a_\_\z_b\*c
8Pattern: double backslash and star: "replace backslash and everything after it"
9Unquoted: a*b_\_z_
10Unquoted =: a*b_\_z_
11Quoted: a*b_\_\z_
12Quoted =: a*b_\_\z_
13
14Source: a\bc
15Replace str: _\\_\z_
16Pattern: single backslash and b: "replace b"
17Unquoted: a\_\_z_c
18Unquoted =: a\_\_z_c
19Quoted: a\_\_\z_c
20Quoted =: a\_\_\z_c
21Pattern: double backslash and b: "replace backslash and b"
22Unquoted: a_\_z_c
23Unquoted =: a_\_z_c
24Quoted: a_\_\z_c
25Quoted =: a_\_\z_c
26
27Source: a\bc
28Replace str: _\\_\z_ (as variable $s)
29Pattern: single backslash and b: "replace b"
30Unquoted: a\_\\_\z_c
31Unquoted =: a\_\\_\z_c
32Quoted: a\_\\_\z_c
33Quoted =: a\_\\_\z_c
34Pattern: double backslash and b: "replace backslash and b"
35Unquoted: a_\\_\z_c
36Unquoted =: a_\\_\z_c
37Quoted: a_\\_\z_c
38Quoted =: a_\\_\z_c
39
40Done: 0
diff --git a/shell/hush_test/hush-vars/var_bash4.tests b/shell/hush_test/hush-vars/var_bash4.tests
new file mode 100755
index 000000000..32aa2b34c
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash4.tests
@@ -0,0 +1,81 @@
1# This testcase demonstrates that backslashes are treated differently
2# in 1st and 2nd parts of ${var/search/repl}:
3# if quoted ("${var/search/repl}"), and repl contains \a (a non-special char),
4# the backslash in repl stays; if unquoted, backslash is removed.
5# But search part does not act like that: \a is always converted to just a,
6# even in quotes.
7#
8# bash4 (and probably bash3 too): "Quoted:" results are different from
9# unquoted expansions - they have a backslash before z.
10#
11# The difference only exists if repl is a literal. If it is a variable:
12# ${v/.../$s}, then all backslashes are preserved in both cases.
13
14v='a*b\*c'
15echo 'Source: ' "$v"
16echo 'Replace str: ' '_\\_\z_'
17
18echo 'Pattern: ' 'single backslash and star: "replace literal star"'
19echo 'Unquoted: ' ${v/\*/_\\_\z_}
20r=${v/\*/_\\_\z_}
21echo 'Unquoted =: ' "$r"
22echo 'Quoted: ' "${v/\*/_\\_\z_}"
23r="${v/\*/_\\_\z_}"
24echo 'Quoted =: ' "$r"
25
26echo 'Pattern: ' 'double backslash and star: "replace backslash and everything after it"'
27echo 'Unquoted: ' ${v/\\*/_\\_\z_}
28r=${v/\\*/_\\_\z_}
29echo 'Unquoted =: ' "$r"
30echo 'Quoted: ' "${v/\\*/_\\_\z_}"
31r="${v/\\*/_\\_\z_}"
32echo 'Quoted =: ' "$r"
33
34echo
35
36v='a\bc'
37echo 'Source: ' "$v"
38echo 'Replace str: ' '_\\_\z_'
39
40echo 'Pattern: ' 'single backslash and b: "replace b"'
41echo 'Unquoted: ' ${v/\b/_\\_\z_}
42r=${v/\b/_\\_\z_}
43echo 'Unquoted =: ' "$r"
44echo 'Quoted: ' "${v/\b/_\\_\z_}"
45r="${v/\b/_\\_\z_}"
46echo 'Quoted =: ' "$r"
47
48echo 'Pattern: ' 'double backslash and b: "replace backslash and b"'
49echo 'Unquoted: ' ${v/\\b/_\\_\z_}
50r=${v/\\b/_\\_\z_}
51echo 'Unquoted =: ' "$r"
52echo 'Quoted: ' "${v/\\b/_\\_\z_}"
53r="${v/\\b/_\\_\z_}"
54echo 'Quoted =: ' "$r"
55
56echo
57
58v='a\bc'
59s='_\\_\z_'
60echo 'Source: ' "$v"
61echo 'Replace str: ' "$s" '(as variable $s)'
62
63echo 'Pattern: ' 'single backslash and b: "replace b"'
64echo 'Unquoted: ' ${v/\b/$s}
65r=${v/\b/$s}
66echo 'Unquoted =: ' "$r"
67echo 'Quoted: ' "${v/\b/$s}"
68r="${v/\b/$s}"
69echo 'Quoted =: ' "$r"
70
71echo 'Pattern: ' 'double backslash and b: "replace backslash and b"'
72echo 'Unquoted: ' ${v/\\b/$s}
73r=${v/\\b/$s}
74echo 'Unquoted =: ' "$r"
75echo 'Quoted: ' "${v/\\b/$s}"
76r="${v/\\b/$s}"
77echo 'Quoted =: ' "$r"
78
79echo
80
81echo Done: $?
diff --git a/shell/hush_test/hush-vars/var_bash5.right b/shell/hush_test/hush-vars/var_bash5.right
new file mode 100644
index 000000000..1990902b2
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash5.right
@@ -0,0 +1,11 @@
11 a/
22 a/d
33 a/e/f
44 a\
55 a\d
66 a\e\f
77 a\\
88 a\\d
99 a\\e\\f
10a ab
11Done: 0
diff --git a/shell/hush_test/hush-vars/var_bash5.tests b/shell/hush_test/hush-vars/var_bash5.tests
new file mode 100755
index 000000000..5748b4ac7
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash5.tests
@@ -0,0 +1,29 @@
1# This testcase checks whether slashes in ${v/a/b} are parsed before
2# or after expansions
3
4v='a/b/c'
5s='b/c'
6r='e/f'
7echo "1 ${v/$s}"
8echo "2 ${v/$s/d}"
9echo "3 ${v/$s/$r}"
10
11v='a\b\c'
12s='b\\c'
13r='e\f'
14echo "4 ${v/$s}"
15echo "5 ${v/$s/d}"
16echo "6 ${v/$s/$r}"
17
18v='a\\b\\c'
19s='b\\\\c'
20r='e\\f'
21echo "7 ${v/$s}"
22echo "8 ${v/$s/d}"
23echo "9 ${v/$s/$r}"
24
25v='a-$a-\t-\\-\"-\`-\--\z-\*-\?-b'
26s='-$a-\\t-\\\\-\\"-\\`-\\--\\z-\\\*-\\\?-'
27echo "a ${v/$s}"
28
29echo Done: $?
diff --git a/shell/hush_test/hush-vars/var_bash6.right b/shell/hush_test/hush-vars/var_bash6.right
new file mode 100644
index 000000000..63fc23df8
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash6.right
@@ -0,0 +1,5 @@
1Expected Actual
2a*z : a*z
3\z : \z
4a1z a2z: a1z a2z
5z : z
diff --git a/shell/hush_test/hush-vars/var_bash6.tests b/shell/hush_test/hush-vars/var_bash6.tests
new file mode 100755
index 000000000..cf2e4f020
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash6.tests
@@ -0,0 +1,9 @@
1# This testcase checks globbing correctness in ${v/a/b}
2
3>a1z; >a2z;
4 echo 'Expected' 'Actual'
5v='a bz'; echo 'a*z :' "${v/a*z/a*z}"
6v='a bz'; echo '\z :' "${v/a*z/\z}"
7v='a bz'; echo 'a1z a2z:' ${v/a*z/a*z}
8v='a bz'; echo 'z :' ${v/a*z/\z}
9rm a1z a2z
diff --git a/shell/hush_test/hush-vars/var_unbackslash.right b/shell/hush_test/hush-vars/var_unbackslash.right
new file mode 100644
index 000000000..8bc834711
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_unbackslash.right
@@ -0,0 +1,11 @@
1b1=-qwerty-t-\-"-`---z-*-?-
2b1=-qwerty-t-\-"-`---z-*-?-
3b2=-qwerty-\t-\-"-`-\--\z-\*-\?-
4b2=-qwerty-\t-\-"-`-\--\z-\*-\?-
5b3=-$a-\t-\\-\"-\`-\--\z-\*-\?-
6b3=-$a-\t-\\-\"-\`-\--\z-\*-\?-
7c=-$a-\t-\\-\"-\`-\--\z-\*-\?-
8c=-$a-\t-\\-\"-\`-\--\z-\*-\?-
9c=-$a-\t-\\-\"-\`-\--\z-\*-\?-
10c=-$a-\t-\\-\"-\`-\--\z-\*-\?-
11Done: 0
diff --git a/shell/hush_test/hush-vars/var_unbackslash.tests b/shell/hush_test/hush-vars/var_unbackslash.tests
new file mode 100755
index 000000000..bb52af3d0
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_unbackslash.tests
@@ -0,0 +1,23 @@
1# Test for correct handling of backslashes
2a=qwerty
3
4b=-$a-\t-\\-\"-\`-\--\z-\*-\?-
5echo b1=$b
6echo "b1=$b"
7b="-$a-\t-\\-\"-\`-\--\z-\*-\?-"
8echo b2=$b
9echo "b2=$b"
10b='-$a-\t-\\-\"-\`-\--\z-\*-\?-'
11echo b3=$b
12echo "b3=$b"
13
14c=$b
15echo "c=$c"
16c=${b}
17echo "c=$c"
18c="$b"
19echo "c=$c"
20c="${b}"
21echo "c=$c"
22
23echo "Done: $?"
diff --git a/shell/match.c b/shell/match.c
index 8b1ddacd5..fee3cf2a8 100644
--- a/shell/match.c
+++ b/shell/match.c
@@ -18,65 +18,78 @@
18# include <stdlib.h> 18# include <stdlib.h>
19# include <string.h> 19# include <string.h>
20# include <unistd.h> 20# include <unistd.h>
21# define FAST_FUNC /* nothing */
22# define PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN /* nothing */
23# define POP_SAVED_FUNCTION_VISIBILITY /* nothing */
21#else 24#else
22# include "libbb.h" 25# include "libbb.h"
23#endif 26#endif
24#include <fnmatch.h> 27#include <fnmatch.h>
25#include "match.h" 28#include "match.h"
26 29
27#define pmatch(a, b) !fnmatch((a), (b), 0) 30char* FAST_FUNC scan_and_match(char *string, const char *pattern, unsigned flags)
28
29char *scanleft(char *string, char *pattern, bool match_at_left)
30{ 31{
31 char c; 32 char *loc;
32 char *loc = string; 33 char *end;
33 34 unsigned len = strlen(string);
34 do { 35 int early_exit;
35 int match; 36
36 const char *s; 37 /* We can stop the scan early only if the string part
37 38 * we are matching against is shrinking, and the pattern has
38 c = *loc; 39 * an unquoted "star" at the corresponding end. There are two cases.
39 if (match_at_left) { 40 * Case 1:
40 *loc = '\0'; 41 * "qwerty" does not match against pattern "*zy",
41 s = string; 42 * no point in trying to match "werty", "erty" etc:
42 } else 43 */
43 s = loc; 44 early_exit = (flags == (SCAN_MOVE_FROM_LEFT + SCAN_MATCH_RIGHT_HALF) && pattern[0] == '*');
44 match = pmatch(pattern, s); 45
45 *loc = c; 46 if (flags & SCAN_MOVE_FROM_LEFT) {
46 47 loc = string;
47 if (match) 48 end = string + len + 1;
48 return loc; 49 } else {
49 50 loc = string + len;
50 loc++; 51 end = string - 1;
51 } while (c); 52 if (flags == (SCAN_MOVE_FROM_RIGHT + SCAN_MATCH_LEFT_HALF)) {
52 53 /* Case 2:
53 return NULL; 54 * "qwerty" does not match against pattern "qz*",
54} 55 * no point in trying to match "qwert", "qwer" etc:
55 56 */
56char *scanright(char *string, char *pattern, bool match_at_left) 57 const char *p = pattern + strlen(pattern);
57{ 58 if (--p >= pattern && *p == '*') {
58 char c; 59 early_exit = 1;
59 char *loc = string + strlen(string); 60 while (--p >= pattern && *p == '\\')
61 early_exit ^= 1;
62 }
63 }
64 }
60 65
61 while (loc >= string) { 66 while (loc != end) {
62 int match; 67 char c;
63 const char *s; 68 int r;
64 69
65 c = *loc; 70 c = *loc;
66 if (match_at_left) { 71 if (flags & SCAN_MATCH_LEFT_HALF) {
67 *loc = '\0'; 72 *loc = '\0';
68 s = string; 73 r = fnmatch(pattern, string, 0);
69 } else 74 *loc = c;
70 s = loc; 75 } else {
71 match = pmatch(pattern, s); 76 r = fnmatch(pattern, loc, 0);
72 *loc = c; 77 }
73 78 if (r == 0) /* match found */
74 if (match)
75 return loc; 79 return loc;
80 if (early_exit) {
81#ifdef STANDALONE
82 printf("(early exit) ");
83#endif
84 break;
85 }
76 86
77 loc--; 87 if (flags & SCAN_MOVE_FROM_LEFT) {
88 loc++;
89 } else {
90 loc--;
91 }
78 } 92 }
79
80 return NULL; 93 return NULL;
81} 94}
82 95
@@ -86,12 +99,11 @@ int main(int argc, char *argv[])
86 char *string; 99 char *string;
87 char *op; 100 char *op;
88 char *pattern; 101 char *pattern;
89 bool match_at_left;
90 char *loc; 102 char *loc;
91 103
92 int i; 104 setvbuf(stdout, NULL, _IONBF, 0);
93 105
94 if (argc == 1) { 106 if (!argv[1]) {
95 puts( 107 puts(
96 "Usage: match <test> [test...]\n\n" 108 "Usage: match <test> [test...]\n\n"
97 "Where a <test> is the form: <string><op><match>\n" 109 "Where a <test> is the form: <string><op><match>\n"
@@ -101,36 +113,34 @@ int main(int argc, char *argv[])
101 return 1; 113 return 1;
102 } 114 }
103 115
104 for (i = 1; i < argc; ++i) { 116 while (*++argv) {
105 size_t off; 117 size_t off;
106 scan_t scan; 118 unsigned scan_flags;
107
108 printf("'%s': ", argv[i]);
109 119
110 string = strdup(argv[i]); 120 string = *argv;
111 off = strcspn(string, "#%"); 121 off = strcspn(string, "#%");
112 if (!off) { 122 if (!off) {
113 printf("invalid format\n"); 123 printf("invalid format\n");
114 free(string);
115 continue; 124 continue;
116 } 125 }
117 op = string + off; 126 op = string + off;
118 scan = pick_scan(op[0], op[1], &match_at_left); 127 scan_flags = pick_scan(op[0], op[1]);
128
129 printf("'%s': flags:%x, ", string, scan_flags);
119 pattern = op + 1; 130 pattern = op + 1;
120 if (op[0] == op[1]) 131 if (op[0] == op[1])
121 op[1] = '\0', ++pattern; 132 pattern++;
122 op[0] = '\0'; 133 op[0] = '\0';
123 134
124 loc = scan(string, pattern, match_at_left); 135 loc = scan_and_match(string, pattern, scan_flags);
125 136
126 if (match_at_left) { 137 if (scan_flags & SCAN_MATCH_LEFT_HALF) {
127 printf("'%s'\n", loc); 138 printf("'%s'\n", loc);
128 } else { 139 } else {
129 *loc = '\0'; 140 if (loc)
141 *loc = '\0';
130 printf("'%s'\n", string); 142 printf("'%s'\n", string);
131 } 143 }
132
133 free(string);
134 } 144 }
135 145
136 return 0; 146 return 0;
diff --git a/shell/match.h b/shell/match.h
index c022ceb25..aa393ed1a 100644
--- a/shell/match.h
+++ b/shell/match.h
@@ -7,25 +7,26 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
7 7
8//TODO! Why ash.c still uses internal version?! 8//TODO! Why ash.c still uses internal version?!
9 9
10typedef char *(*scan_t)(char *string, char *match, bool match_at_left); 10enum {
11 SCAN_MOVE_FROM_LEFT = (1 << 0),
12 SCAN_MOVE_FROM_RIGHT = (1 << 1),
13 SCAN_MATCH_LEFT_HALF = (1 << 2),
14 SCAN_MATCH_RIGHT_HALF = (1 << 3),
15};
11 16
12char *scanleft(char *string, char *match, bool match_at_left); 17char* FAST_FUNC scan_and_match(char *string, const char *pattern, unsigned flags);
13char *scanright(char *string, char *match, bool match_at_left);
14 18
15static inline scan_t pick_scan(char op1, char op2, bool *match_at_left) 19static inline unsigned pick_scan(char op1, char op2)
16{ 20{
17 /* # - scanleft 21 unsigned scan_flags;
18 * ## - scanright
19 * % - scanright
20 * %% - scanleft
21 */
22 if (op1 == '#') { 22 if (op1 == '#') {
23 *match_at_left = true; 23 scan_flags = SCAN_MATCH_LEFT_HALF +
24 return op1 == op2 ? scanright : scanleft; 24 (op1 == op2 ? SCAN_MOVE_FROM_RIGHT : SCAN_MOVE_FROM_LEFT);
25 } else { 25 } else { /* % */
26 *match_at_left = false; 26 scan_flags = SCAN_MATCH_RIGHT_HALF +
27 return op1 == op2 ? scanleft : scanright; 27 (op1 == op2 ? SCAN_MOVE_FROM_LEFT : SCAN_MOVE_FROM_RIGHT);
28 } 28 }
29 return scan_flags;
29} 30}
30 31
31POP_SAVED_FUNCTION_VISIBILITY 32POP_SAVED_FUNCTION_VISIBILITY
diff --git a/shell/math.c b/shell/math.c
index f0cc2e35d..a4c55a4d0 100644
--- a/shell/math.c
+++ b/shell/math.c
@@ -122,7 +122,7 @@
122#define a_e_h_t arith_eval_hooks_t 122#define a_e_h_t arith_eval_hooks_t
123#define lookupvar (math_hooks->lookupvar) 123#define lookupvar (math_hooks->lookupvar)
124#define setvar (math_hooks->setvar ) 124#define setvar (math_hooks->setvar )
125#define endofname (math_hooks->endofname) 125//#define endofname (math_hooks->endofname)
126 126
127#define arith_isspace(arithval) \ 127#define arith_isspace(arithval) \
128 (arithval == ' ' || arithval == '\n' || arithval == '\t') 128 (arithval == ' ' || arithval == '\n' || arithval == '\t')
@@ -479,6 +479,18 @@ static const char op_tokens[] ALIGN1 = {
479/* ptr to ")" */ 479/* ptr to ")" */
480#define endexpression (&op_tokens[sizeof(op_tokens)-7]) 480#define endexpression (&op_tokens[sizeof(op_tokens)-7])
481 481
482const char* FAST_FUNC
483endofname(const char *name)
484{
485 if (!is_name(*name))
486 return name;
487 while (*++name) {
488 if (!is_in_name(*name))
489 break;
490 }
491 return name;
492}
493
482arith_t 494arith_t
483arith(const char *expr, int *perrcode, a_e_h_t *math_hooks) 495arith(const char *expr, int *perrcode, a_e_h_t *math_hooks)
484{ 496{
diff --git a/shell/math.h b/shell/math.h
index 2b0b2b89d..96088b4d2 100644
--- a/shell/math.h
+++ b/shell/math.h
@@ -87,14 +87,19 @@ typedef long arith_t;
87#define strto_arith_t strtoul 87#define strto_arith_t strtoul
88#endif 88#endif
89 89
90/* ash's and hush's endofname is the same, so... */
91# define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
92# define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
93const char* FAST_FUNC endofname(const char *name);
94
90typedef const char* FAST_FUNC (*arith_var_lookup_t)(const char *name); 95typedef const char* FAST_FUNC (*arith_var_lookup_t)(const char *name);
91typedef void FAST_FUNC (*arith_var_set_t)(const char *name, const char *val); 96typedef void FAST_FUNC (*arith_var_set_t)(const char *name, const char *val);
92typedef char* FAST_FUNC (*arith_var_endofname_t)(const char *name); 97//typedef const char* FAST_FUNC (*arith_var_endofname_t)(const char *name);
93 98
94typedef struct arith_eval_hooks { 99typedef struct arith_eval_hooks {
95 arith_var_lookup_t lookupvar; 100 arith_var_lookup_t lookupvar;
96 arith_var_set_t setvar; 101 arith_var_set_t setvar;
97 arith_var_endofname_t endofname; 102// arith_var_endofname_t endofname;
98} arith_eval_hooks_t; 103} arith_eval_hooks_t;
99 104
100arith_t arith(const char *expr, int *perrcode, arith_eval_hooks_t*); 105arith_t arith(const char *expr, int *perrcode, arith_eval_hooks_t*);