diff options
Diffstat (limited to 'shell/ash.c')
-rw-r--r-- | shell/ash.c | 251 |
1 files changed, 147 insertions, 104 deletions
diff --git a/shell/ash.c b/shell/ash.c index 5ec035043..35438a887 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -204,7 +204,6 @@ | |||
204 | 204 | ||
205 | #define JOBS ENABLE_ASH_JOB_CONTROL | 205 | #define JOBS ENABLE_ASH_JOB_CONTROL |
206 | 206 | ||
207 | #include <setjmp.h> | ||
208 | #include <fnmatch.h> | 207 | #include <fnmatch.h> |
209 | #include <sys/times.h> | 208 | #include <sys/times.h> |
210 | #include <sys/utsname.h> /* for setting $HOSTNAME */ | 209 | #include <sys/utsname.h> /* for setting $HOSTNAME */ |
@@ -5755,7 +5754,7 @@ openredirect(union node *redir) | |||
5755 | f = open(fname, O_WRONLY, 0666); | 5754 | f = open(fname, O_WRONLY, 0666); |
5756 | if (f < 0) | 5755 | if (f < 0) |
5757 | goto ecreate; | 5756 | goto ecreate; |
5758 | if (fstat(f, &sb) < 0 && S_ISREG(sb.st_mode)) { | 5757 | if (!fstat(f, &sb) && S_ISREG(sb.st_mode)) { |
5759 | close(f); | 5758 | close(f); |
5760 | errno = EEXIST; | 5759 | errno = EEXIST; |
5761 | goto ecreate; | 5760 | goto ecreate; |
@@ -6255,10 +6254,9 @@ static int substr_atoi(const char *s) | |||
6255 | * performs globbing, and thus diverges from what we do). | 6254 | * performs globbing, and thus diverges from what we do). |
6256 | */ | 6255 | */ |
6257 | #define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ | 6256 | #define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ |
6258 | #define EXP_QPAT 0x20 /* pattern in quoted parameter expansion */ | 6257 | #define EXP_VARTILDE2 0x20 /* expand tildes after colons only */ |
6259 | #define EXP_VARTILDE2 0x40 /* expand tildes after colons only */ | 6258 | #define EXP_WORD 0x40 /* expand word in parameter expansion */ |
6260 | #define EXP_WORD 0x80 /* expand word in parameter expansion */ | 6259 | #define EXP_QUOTED 0x80 /* expand word in double quotes */ |
6261 | #define EXP_QUOTED 0x100 /* expand word in double quotes */ | ||
6262 | /* | 6260 | /* |
6263 | * rmescape() flags | 6261 | * rmescape() flags |
6264 | */ | 6262 | */ |
@@ -6268,7 +6266,7 @@ static int substr_atoi(const char *s) | |||
6268 | #define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */ | 6266 | #define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */ |
6269 | 6267 | ||
6270 | /* Add CTLESC when necessary. */ | 6268 | /* Add CTLESC when necessary. */ |
6271 | #define QUOTES_ESC (EXP_FULL | EXP_CASE | EXP_QPAT) | 6269 | #define QUOTES_ESC (EXP_FULL | EXP_CASE) |
6272 | /* Do not skip NUL characters. */ | 6270 | /* Do not skip NUL characters. */ |
6273 | #define QUOTES_KEEPNUL EXP_TILDE | 6271 | #define QUOTES_KEEPNUL EXP_TILDE |
6274 | 6272 | ||
@@ -6343,7 +6341,10 @@ ifsbreakup(char *string, struct arglist *arglist) | |||
6343 | realifs = ifsset() ? ifsval() : defifs; | 6341 | realifs = ifsset() ? ifsval() : defifs; |
6344 | ifsp = &ifsfirst; | 6342 | ifsp = &ifsfirst; |
6345 | do { | 6343 | do { |
6344 | int afternul; | ||
6345 | |||
6346 | p = string + ifsp->begoff; | 6346 | p = string + ifsp->begoff; |
6347 | afternul = nulonly; | ||
6347 | nulonly = ifsp->nulonly; | 6348 | nulonly = ifsp->nulonly; |
6348 | ifs = nulonly ? nullstr : realifs; | 6349 | ifs = nulonly ? nullstr : realifs; |
6349 | ifsspc = 0; | 6350 | ifsspc = 0; |
@@ -6355,7 +6356,7 @@ ifsbreakup(char *string, struct arglist *arglist) | |||
6355 | p++; | 6356 | p++; |
6356 | continue; | 6357 | continue; |
6357 | } | 6358 | } |
6358 | if (!nulonly) | 6359 | if (!(afternul || nulonly)) |
6359 | ifsspc = (strchr(defifs, *p) != NULL); | 6360 | ifsspc = (strchr(defifs, *p) != NULL); |
6360 | /* Ignore IFS whitespace at start */ | 6361 | /* Ignore IFS whitespace at start */ |
6361 | if (q == start && ifsspc) { | 6362 | if (q == start && ifsspc) { |
@@ -6457,7 +6458,6 @@ rmescapes(char *str, int flag, int *slash_position) | |||
6457 | IF_BASH_PATTERN_SUBST('/',) CTLESC, CTLQUOTEMARK, '\0' }; | 6458 | IF_BASH_PATTERN_SUBST('/',) CTLESC, CTLQUOTEMARK, '\0' }; |
6458 | 6459 | ||
6459 | char *p, *q, *r; | 6460 | char *p, *q, *r; |
6460 | unsigned inquotes; | ||
6461 | unsigned protect_against_glob; | 6461 | unsigned protect_against_glob; |
6462 | unsigned globbing; | 6462 | unsigned globbing; |
6463 | 6463 | ||
@@ -6488,18 +6488,21 @@ rmescapes(char *str, int flag, int *slash_position) | |||
6488 | } | 6488 | } |
6489 | } | 6489 | } |
6490 | 6490 | ||
6491 | inquotes = 0; | ||
6492 | globbing = flag & RMESCAPE_GLOB; | 6491 | globbing = flag & RMESCAPE_GLOB; |
6493 | protect_against_glob = globbing; | 6492 | protect_against_glob = globbing; |
6494 | while (*p) { | 6493 | while (*p) { |
6495 | if ((unsigned char)*p == CTLQUOTEMARK) { | 6494 | if ((unsigned char)*p == CTLQUOTEMARK) { |
6496 | // Note: both inquotes and protect_against_glob only affect whether | 6495 | // Note: protect_against_glob only affect whether |
6497 | // CTLESC,<ch> gets converted to <ch> or to \<ch> | 6496 | // CTLESC,<ch> gets converted to <ch> or to \<ch> |
6498 | inquotes = ~inquotes; | ||
6499 | p++; | 6497 | p++; |
6500 | protect_against_glob = globbing; | 6498 | protect_against_glob = globbing; |
6501 | continue; | 6499 | continue; |
6502 | } | 6500 | } |
6501 | if (*p == '\\') { | ||
6502 | /* naked back slash */ | ||
6503 | protect_against_glob = 0; | ||
6504 | goto copy; | ||
6505 | } | ||
6503 | if ((unsigned char)*p == CTLESC) { | 6506 | if ((unsigned char)*p == CTLESC) { |
6504 | p++; | 6507 | p++; |
6505 | #if DEBUG | 6508 | #if DEBUG |
@@ -6535,10 +6538,6 @@ rmescapes(char *str, int flag, int *slash_position) | |||
6535 | *q++ = '\\'; | 6538 | *q++ = '\\'; |
6536 | } | 6539 | } |
6537 | } | 6540 | } |
6538 | } else if (*p == '\\' && !inquotes) { | ||
6539 | /* naked back slash */ | ||
6540 | protect_against_glob = 0; | ||
6541 | goto copy; | ||
6542 | } | 6541 | } |
6543 | #if BASH_PATTERN_SUBST | 6542 | #if BASH_PATTERN_SUBST |
6544 | else if (slash_position && p == str + *slash_position) { | 6543 | else if (slash_position && p == str + *slash_position) { |
@@ -7032,12 +7031,12 @@ argstr(char *p, int flags) | |||
7032 | case CTLENDVAR: /* ??? */ | 7031 | case CTLENDVAR: /* ??? */ |
7033 | goto breakloop; | 7032 | goto breakloop; |
7034 | case CTLQUOTEMARK: | 7033 | case CTLQUOTEMARK: |
7035 | inquotes ^= EXP_QUOTED; | ||
7036 | /* "$@" syntax adherence hack */ | 7034 | /* "$@" syntax adherence hack */ |
7037 | if (inquotes && !memcmp(p, dolatstr + 1, DOLATSTRLEN - 1)) { | 7035 | if (!inquotes && !memcmp(p, dolatstr + 1, DOLATSTRLEN - 1)) { |
7038 | p = evalvar(p + 1, flags | inquotes) + 1; | 7036 | p = evalvar(p + 1, flags | EXP_QUOTED) + 1; |
7039 | goto start; | 7037 | goto start; |
7040 | } | 7038 | } |
7039 | inquotes ^= EXP_QUOTED; | ||
7041 | addquote: | 7040 | addquote: |
7042 | if (flags & QUOTES_ESC) { | 7041 | if (flags & QUOTES_ESC) { |
7043 | p--; | 7042 | p--; |
@@ -7048,16 +7047,6 @@ argstr(char *p, int flags) | |||
7048 | case CTLESC: | 7047 | case CTLESC: |
7049 | startloc++; | 7048 | startloc++; |
7050 | length++; | 7049 | length++; |
7051 | |||
7052 | /* | ||
7053 | * Quoted parameter expansion pattern: remove quote | ||
7054 | * unless inside inner quotes or we have a literal | ||
7055 | * backslash. | ||
7056 | */ | ||
7057 | if (((flags | inquotes) & (EXP_QPAT | EXP_QUOTED)) == | ||
7058 | EXP_QPAT && *p != '\\') | ||
7059 | break; | ||
7060 | |||
7061 | goto addquote; | 7050 | goto addquote; |
7062 | case CTLVAR: | 7051 | case CTLVAR: |
7063 | TRACE(("argstr: evalvar('%s')\n", p)); | 7052 | TRACE(("argstr: evalvar('%s')\n", p)); |
@@ -7248,15 +7237,24 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
7248 | } | 7237 | } |
7249 | #endif | 7238 | #endif |
7250 | argstr_flags = EXP_TILDE; | 7239 | argstr_flags = EXP_TILDE; |
7251 | if (subtype != VSASSIGN && subtype != VSQUESTION) | 7240 | if (subtype != VSASSIGN |
7252 | argstr_flags |= (flag & (EXP_QUOTED | EXP_QPAT) ? EXP_QPAT : EXP_CASE); | 7241 | && subtype != VSQUESTION |
7242 | #if BASH_SUBSTR | ||
7243 | && subtype != VSSUBSTR | ||
7244 | #endif | ||
7245 | ) { | ||
7246 | /* EXP_CASE keeps CTLESC's */ | ||
7247 | argstr_flags = EXP_TILDE | EXP_CASE; | ||
7248 | } | ||
7253 | argstr(p, argstr_flags); | 7249 | argstr(p, argstr_flags); |
7250 | //bb_error_msg("str0:'%s'", (char *)stackblock() + strloc); | ||
7254 | #if BASH_PATTERN_SUBST | 7251 | #if BASH_PATTERN_SUBST |
7255 | slash_pos = -1; | 7252 | slash_pos = -1; |
7256 | if (repl) { | 7253 | if (repl) { |
7257 | slash_pos = expdest - ((char *)stackblock() + strloc); | 7254 | slash_pos = expdest - ((char *)stackblock() + strloc); |
7258 | STPUTC('/', expdest); | 7255 | STPUTC('/', expdest); |
7259 | argstr(repl + 1, argstr_flags); | 7256 | //bb_error_msg("repl+1:'%s'", repl + 1); |
7257 | argstr(repl + 1, EXP_TILDE); /* EXP_TILDE: echo "${v/x/~}" expands ~ ! */ | ||
7260 | *repl = '/'; | 7258 | *repl = '/'; |
7261 | } | 7259 | } |
7262 | #endif | 7260 | #endif |
@@ -11182,6 +11180,34 @@ pgetc_eatbnl(void) | |||
11182 | return c; | 11180 | return c; |
11183 | } | 11181 | } |
11184 | 11182 | ||
11183 | struct synstack { | ||
11184 | smalluint syntax; | ||
11185 | uint8_t innerdq :1; | ||
11186 | uint8_t varpushed :1; | ||
11187 | uint8_t dblquote :1; | ||
11188 | int varnest; /* levels of variables expansion */ | ||
11189 | int dqvarnest; /* levels of variables expansion within double quotes */ | ||
11190 | int parenlevel; /* levels of parens in arithmetic */ | ||
11191 | struct synstack *prev; | ||
11192 | struct synstack *next; | ||
11193 | }; | ||
11194 | |||
11195 | static void | ||
11196 | synstack_push(struct synstack **stack, struct synstack *next, int syntax) | ||
11197 | { | ||
11198 | memset(next, 0, sizeof(*next)); | ||
11199 | next->syntax = syntax; | ||
11200 | next->next = *stack; | ||
11201 | (*stack)->prev = next; | ||
11202 | *stack = next; | ||
11203 | } | ||
11204 | |||
11205 | static ALWAYS_INLINE void | ||
11206 | synstack_pop(struct synstack **stack) | ||
11207 | { | ||
11208 | *stack = (*stack)->next; | ||
11209 | } | ||
11210 | |||
11185 | /* | 11211 | /* |
11186 | * To handle the "." command, a stack of input files is used. Pushfile | 11212 | * To handle the "." command, a stack of input files is used. Pushfile |
11187 | * adds a new entry to the stack and popfile restores the previous level. | 11213 | * adds a new entry to the stack and popfile restores the previous level. |
@@ -12443,19 +12469,13 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
12443 | size_t len; | 12469 | size_t len; |
12444 | struct nodelist *bqlist; | 12470 | struct nodelist *bqlist; |
12445 | smallint quotef; | 12471 | smallint quotef; |
12446 | smallint dblquote; | ||
12447 | smallint oldstyle; | 12472 | smallint oldstyle; |
12448 | IF_FEATURE_SH_MATH(smallint prevsyntax;) /* syntax before arithmetic */ | ||
12449 | smallint pssyntax; /* we are expanding a prompt string */ | 12473 | smallint pssyntax; /* we are expanding a prompt string */ |
12450 | int varnest; /* levels of variables expansion */ | ||
12451 | IF_FEATURE_SH_MATH(int arinest;) /* levels of arithmetic expansion */ | ||
12452 | IF_FEATURE_SH_MATH(int parenlevel;) /* levels of parens in arithmetic */ | ||
12453 | int dqvarnest; /* levels of variables expansion within double quotes */ | ||
12454 | IF_BASH_DOLLAR_SQUOTE(smallint bash_dollar_squote = 0;) | 12474 | IF_BASH_DOLLAR_SQUOTE(smallint bash_dollar_squote = 0;) |
12475 | /* syntax stack */ | ||
12476 | struct synstack synbase = { }; | ||
12477 | struct synstack *synstack = &synbase; | ||
12455 | 12478 | ||
12456 | bqlist = NULL; | ||
12457 | quotef = 0; | ||
12458 | IF_FEATURE_SH_MATH(prevsyntax = 0;) | ||
12459 | #if ENABLE_ASH_EXPAND_PRMT | 12479 | #if ENABLE_ASH_EXPAND_PRMT |
12460 | pssyntax = (syntax == PSSYNTAX); | 12480 | pssyntax = (syntax == PSSYNTAX); |
12461 | if (pssyntax) | 12481 | if (pssyntax) |
@@ -12463,11 +12483,12 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
12463 | #else | 12483 | #else |
12464 | pssyntax = 0; /* constant */ | 12484 | pssyntax = 0; /* constant */ |
12465 | #endif | 12485 | #endif |
12466 | dblquote = (syntax == DQSYNTAX); | 12486 | synstack->syntax = syntax; |
12467 | varnest = 0; | 12487 | |
12468 | IF_FEATURE_SH_MATH(arinest = 0;) | 12488 | if (syntax == DQSYNTAX) |
12469 | IF_FEATURE_SH_MATH(parenlevel = 0;) | 12489 | synstack->dblquote = 1; |
12470 | dqvarnest = 0; | 12490 | quotef = 0; |
12491 | bqlist = NULL; | ||
12471 | 12492 | ||
12472 | STARTSTACKSTR(out); | 12493 | STARTSTACKSTR(out); |
12473 | loop: | 12494 | loop: |
@@ -12475,9 +12496,9 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
12475 | CHECKEND(); /* set c to PEOF if at end of here document */ | 12496 | CHECKEND(); /* set c to PEOF if at end of here document */ |
12476 | for (;;) { /* until end of line or end of word */ | 12497 | for (;;) { /* until end of line or end of word */ |
12477 | CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */ | 12498 | CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */ |
12478 | switch (SIT(c, syntax)) { | 12499 | switch (SIT(c, synstack->syntax)) { |
12479 | case CNL: /* '\n' */ | 12500 | case CNL: /* '\n' */ |
12480 | if (syntax == BASESYNTAX) | 12501 | if (synstack->syntax == BASESYNTAX) |
12481 | goto endword; /* exit outer loop */ | 12502 | goto endword; /* exit outer loop */ |
12482 | USTPUTC(c, out); | 12503 | USTPUTC(c, out); |
12483 | nlprompt(); | 12504 | nlprompt(); |
@@ -12497,13 +12518,13 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
12497 | if (c & 0x100) { | 12518 | if (c & 0x100) { |
12498 | /* Unknown escape. Encode as '\z' */ | 12519 | /* Unknown escape. Encode as '\z' */ |
12499 | c = (unsigned char)c; | 12520 | c = (unsigned char)c; |
12500 | if (eofmark == NULL || dblquote) | 12521 | if (eofmark == NULL || synstack->dblquote) |
12501 | USTPUTC(CTLESC, out); | 12522 | USTPUTC(CTLESC, out); |
12502 | USTPUTC('\\', out); | 12523 | USTPUTC('\\', out); |
12503 | } | 12524 | } |
12504 | } | 12525 | } |
12505 | #endif | 12526 | #endif |
12506 | if (eofmark == NULL || dblquote) | 12527 | if (!eofmark || synstack->dblquote || synstack->varnest) |
12507 | USTPUTC(CTLESC, out); | 12528 | USTPUTC(CTLESC, out); |
12508 | USTPUTC(c, out); | 12529 | USTPUTC(c, out); |
12509 | break; | 12530 | break; |
@@ -12523,20 +12544,13 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
12523 | /* Backslash is retained if we are in "str" | 12544 | /* Backslash is retained if we are in "str" |
12524 | * and next char isn't dquote-special. | 12545 | * and next char isn't dquote-special. |
12525 | */ | 12546 | */ |
12526 | if (dblquote | 12547 | if (synstack->dblquote |
12527 | && c != '\\' | 12548 | && c != '\\' |
12528 | && c != '`' | 12549 | && c != '`' |
12529 | && c != '$' | 12550 | && c != '$' |
12530 | && (c != '"' || eofmark != NULL) | 12551 | && (c != '"' || (eofmark != NULL && !synstack->varnest)) |
12552 | && (c != '}' || !synstack->varnest) | ||
12531 | ) { | 12553 | ) { |
12532 | //dash survives not doing USTPUTC(CTLESC), but merely by chance: | ||
12533 | //Example: "\z" gets encoded as "\<CTLESC>z". | ||
12534 | //rmescapes() then emits "\", "\z", protecting z from globbing. | ||
12535 | //But it's wrong, should protect _both_ from globbing: | ||
12536 | //everything in double quotes is not globbed. | ||
12537 | //Unlike dash, we have a fix in rmescapes() which emits bare "z" | ||
12538 | //for "<CTLESC>z" since "z" is not glob-special (else unicode may break), | ||
12539 | //and glob would see "\z" and eat "\". Thus: | ||
12540 | USTPUTC(CTLESC, out); /* protect '\' from glob */ | 12554 | USTPUTC(CTLESC, out); /* protect '\' from glob */ |
12541 | USTPUTC('\\', out); | 12555 | USTPUTC('\\', out); |
12542 | } | 12556 | } |
@@ -12546,56 +12560,62 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
12546 | } | 12560 | } |
12547 | break; | 12561 | break; |
12548 | case CSQUOTE: | 12562 | case CSQUOTE: |
12549 | syntax = SQSYNTAX; | 12563 | synstack->syntax = SQSYNTAX; |
12550 | quotemark: | 12564 | quotemark: |
12551 | if (eofmark == NULL) { | 12565 | if (eofmark == NULL) { |
12552 | USTPUTC(CTLQUOTEMARK, out); | 12566 | USTPUTC(CTLQUOTEMARK, out); |
12553 | } | 12567 | } |
12554 | break; | 12568 | break; |
12555 | case CDQUOTE: | 12569 | case CDQUOTE: |
12556 | syntax = DQSYNTAX; | 12570 | synstack->syntax = DQSYNTAX; |
12557 | dblquote = 1; | 12571 | synstack->dblquote = 1; |
12572 | toggledq: | ||
12573 | if (synstack->varnest) | ||
12574 | synstack->innerdq ^= 1; | ||
12558 | goto quotemark; | 12575 | goto quotemark; |
12559 | case CENDQUOTE: | 12576 | case CENDQUOTE: |
12560 | IF_BASH_DOLLAR_SQUOTE(bash_dollar_squote = 0;) | 12577 | IF_BASH_DOLLAR_SQUOTE(bash_dollar_squote = 0;) |
12561 | if (eofmark != NULL && varnest == 0) { | 12578 | if (eofmark != NULL && synstack->varnest == 0) { |
12562 | USTPUTC(c, out); | 12579 | USTPUTC(c, out); |
12563 | } else { | 12580 | break; |
12564 | if (dqvarnest == 0) { | ||
12565 | syntax = BASESYNTAX; | ||
12566 | dblquote = 0; | ||
12567 | } | ||
12568 | quotef = 1; | ||
12569 | goto quotemark; | ||
12570 | } | 12581 | } |
12571 | break; | 12582 | |
12583 | if (synstack->dqvarnest == 0) { | ||
12584 | synstack->syntax = BASESYNTAX; | ||
12585 | synstack->dblquote = 0; | ||
12586 | } | ||
12587 | |||
12588 | quotef = 1; | ||
12589 | |||
12590 | if (c == '"') | ||
12591 | goto toggledq; | ||
12592 | |||
12593 | goto quotemark; | ||
12572 | case CVAR: /* '$' */ | 12594 | case CVAR: /* '$' */ |
12573 | PARSESUB(); /* parse substitution */ | 12595 | PARSESUB(); /* parse substitution */ |
12574 | break; | 12596 | break; |
12575 | case CENDVAR: /* '}' */ | 12597 | case CENDVAR: /* '}' */ |
12576 | if (varnest > 0) { | 12598 | if (!synstack->innerdq && synstack->varnest > 0) { |
12577 | varnest--; | 12599 | if (!--synstack->varnest && synstack->varpushed) |
12578 | if (dqvarnest > 0) { | 12600 | synstack_pop(&synstack); |
12579 | dqvarnest--; | 12601 | else if (synstack->dqvarnest > 0) |
12580 | } | 12602 | synstack->dqvarnest--; |
12581 | c = CTLENDVAR; | 12603 | c = CTLENDVAR; |
12582 | } | 12604 | } |
12583 | USTPUTC(c, out); | 12605 | USTPUTC(c, out); |
12584 | break; | 12606 | break; |
12585 | #if ENABLE_FEATURE_SH_MATH | 12607 | #if ENABLE_FEATURE_SH_MATH |
12586 | case CLP: /* '(' in arithmetic */ | 12608 | case CLP: /* '(' in arithmetic */ |
12587 | parenlevel++; | 12609 | synstack->parenlevel++; |
12588 | USTPUTC(c, out); | 12610 | USTPUTC(c, out); |
12589 | break; | 12611 | break; |
12590 | case CRP: /* ')' in arithmetic */ | 12612 | case CRP: /* ')' in arithmetic */ |
12591 | if (parenlevel > 0) { | 12613 | if (synstack->parenlevel > 0) { |
12592 | parenlevel--; | 12614 | synstack->parenlevel--; |
12593 | } else { | 12615 | } else { |
12594 | if (pgetc_eatbnl() == ')') { | 12616 | if (pgetc_eatbnl() == ')') { |
12595 | c = CTLENDARI; | 12617 | c = CTLENDARI; |
12596 | if (--arinest == 0) { | 12618 | synstack_pop(&synstack); |
12597 | syntax = prevsyntax; | ||
12598 | } | ||
12599 | } else { | 12619 | } else { |
12600 | /* | 12620 | /* |
12601 | * unbalanced parens | 12621 | * unbalanced parens |
@@ -12621,7 +12641,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
12621 | case CIGN: | 12641 | case CIGN: |
12622 | break; | 12642 | break; |
12623 | default: | 12643 | default: |
12624 | if (varnest == 0) { | 12644 | if (synstack->varnest == 0) { |
12625 | #if BASH_REDIR_OUTPUT | 12645 | #if BASH_REDIR_OUTPUT |
12626 | if (c == '&') { | 12646 | if (c == '&') { |
12627 | //Can't call pgetc_eatbnl() here, this requires three-deep pungetc() | 12647 | //Can't call pgetc_eatbnl() here, this requires three-deep pungetc() |
@@ -12640,12 +12660,12 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
12640 | endword: | 12660 | endword: |
12641 | 12661 | ||
12642 | #if ENABLE_FEATURE_SH_MATH | 12662 | #if ENABLE_FEATURE_SH_MATH |
12643 | if (syntax == ARISYNTAX) | 12663 | if (synstack->syntax == ARISYNTAX) |
12644 | raise_error_syntax("missing '))'"); | 12664 | raise_error_syntax("missing '))'"); |
12645 | #endif | 12665 | #endif |
12646 | if (syntax != BASESYNTAX && eofmark == NULL) | 12666 | if (synstack->syntax != BASESYNTAX && eofmark == NULL) |
12647 | raise_error_syntax("unterminated quoted string"); | 12667 | raise_error_syntax("unterminated quoted string"); |
12648 | if (varnest != 0) { | 12668 | if (synstack->varnest != 0) { |
12649 | /* { */ | 12669 | /* { */ |
12650 | raise_error_syntax("missing '}'"); | 12670 | raise_error_syntax("missing '}'"); |
12651 | } | 12671 | } |
@@ -12827,7 +12847,7 @@ parsesub: { | |||
12827 | || (c != '(' && c != '{' && !is_name(c) && !is_special(c)) | 12847 | || (c != '(' && c != '{' && !is_name(c) && !is_special(c)) |
12828 | ) { | 12848 | ) { |
12829 | #if BASH_DOLLAR_SQUOTE | 12849 | #if BASH_DOLLAR_SQUOTE |
12830 | if (syntax != DQSYNTAX && c == '\'') | 12850 | if (synstack->syntax != DQSYNTAX && c == '\'') |
12831 | bash_dollar_squote = 1; | 12851 | bash_dollar_squote = 1; |
12832 | else | 12852 | else |
12833 | #endif | 12853 | #endif |
@@ -12847,6 +12867,8 @@ parsesub: { | |||
12847 | } | 12867 | } |
12848 | } else { | 12868 | } else { |
12849 | /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */ | 12869 | /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */ |
12870 | smalluint newsyn = synstack->syntax; | ||
12871 | |||
12850 | USTPUTC(CTLVAR, out); | 12872 | USTPUTC(CTLVAR, out); |
12851 | typeloc = out - (char *)stackblock(); | 12873 | typeloc = out - (char *)stackblock(); |
12852 | STADJUST(1, out); | 12874 | STADJUST(1, out); |
@@ -12905,6 +12927,8 @@ parsesub: { | |||
12905 | static const char types[] ALIGN1 = "}-+?="; | 12927 | static const char types[] ALIGN1 = "}-+?="; |
12906 | /* ${VAR...} but not $VAR or ${#VAR} */ | 12928 | /* ${VAR...} but not $VAR or ${#VAR} */ |
12907 | /* c == first char after VAR */ | 12929 | /* c == first char after VAR */ |
12930 | int cc = c; | ||
12931 | |||
12908 | switch (c) { | 12932 | switch (c) { |
12909 | case ':': | 12933 | case ':': |
12910 | c = pgetc_eatbnl(); | 12934 | c = pgetc_eatbnl(); |
@@ -12929,21 +12953,24 @@ parsesub: { | |||
12929 | break; | 12953 | break; |
12930 | } | 12954 | } |
12931 | case '%': | 12955 | case '%': |
12932 | case '#': { | 12956 | case '#': |
12933 | int cc = c; | ||
12934 | subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT); | 12957 | subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT); |
12935 | c = pgetc_eatbnl(); | 12958 | c = pgetc_eatbnl(); |
12936 | if (c != cc) | 12959 | if (c == cc) |
12937 | goto badsub; | 12960 | subtype++; |
12938 | subtype++; | 12961 | else |
12962 | pungetc(); | ||
12963 | |||
12964 | newsyn = BASESYNTAX; | ||
12939 | break; | 12965 | break; |
12940 | } | ||
12941 | #if BASH_PATTERN_SUBST | 12966 | #if BASH_PATTERN_SUBST |
12942 | case '/': | 12967 | case '/': |
12943 | /* ${v/[/]pattern/repl} */ | 12968 | /* ${v/[/]pattern/repl} */ |
12944 | //TODO: encode pattern and repl separately. | 12969 | //TODO: encode pattern and repl separately. |
12945 | // Currently ${v/$var_with_slash/repl} is horribly broken | 12970 | // Currently cases like: v=1;echo ${v/$((1/1))/ONE} |
12971 | // are broken (should print "ONE") | ||
12946 | subtype = VSREPLACE; | 12972 | subtype = VSREPLACE; |
12973 | newsyn = BASESYNTAX; | ||
12947 | c = pgetc_eatbnl(); | 12974 | c = pgetc_eatbnl(); |
12948 | if (c != '/') | 12975 | if (c != '/') |
12949 | goto badsub; | 12976 | goto badsub; |
@@ -12955,11 +12982,26 @@ parsesub: { | |||
12955 | badsub: | 12982 | badsub: |
12956 | pungetc(); | 12983 | pungetc(); |
12957 | } | 12984 | } |
12985 | |||
12986 | if (newsyn == ARISYNTAX) | ||
12987 | newsyn = DQSYNTAX; | ||
12988 | |||
12989 | if ((newsyn != synstack->syntax || synstack->innerdq) | ||
12990 | && subtype != VSNORMAL | ||
12991 | ) { | ||
12992 | synstack_push(&synstack, | ||
12993 | synstack->prev ?: alloca(sizeof(*synstack)), | ||
12994 | newsyn); | ||
12995 | |||
12996 | synstack->varpushed = 1; | ||
12997 | synstack->dblquote = newsyn != BASESYNTAX; | ||
12998 | } | ||
12999 | |||
12958 | ((unsigned char *)stackblock())[typeloc] = subtype; | 13000 | ((unsigned char *)stackblock())[typeloc] = subtype; |
12959 | if (subtype != VSNORMAL) { | 13001 | if (subtype != VSNORMAL) { |
12960 | varnest++; | 13002 | synstack->varnest++; |
12961 | if (dblquote) | 13003 | if (synstack->dblquote) |
12962 | dqvarnest++; | 13004 | synstack->dqvarnest++; |
12963 | } | 13005 | } |
12964 | STPUTC('=', out); | 13006 | STPUTC('=', out); |
12965 | } | 13007 | } |
@@ -13016,7 +13058,7 @@ parsebackq: { | |||
13016 | case '\\': | 13058 | case '\\': |
13017 | pc = pgetc(); /* or pgetc_eatbnl()? why (example)? */ | 13059 | pc = pgetc(); /* or pgetc_eatbnl()? why (example)? */ |
13018 | if (pc != '\\' && pc != '`' && pc != '$' | 13060 | if (pc != '\\' && pc != '`' && pc != '$' |
13019 | && (!dblquote || pc != '"') | 13061 | && (!synstack->dblquote || pc != '"') |
13020 | ) { | 13062 | ) { |
13021 | STPUTC('\\', pout); | 13063 | STPUTC('\\', pout); |
13022 | } | 13064 | } |
@@ -13091,10 +13133,11 @@ parsebackq: { | |||
13091 | * Parse an arithmetic expansion (indicate start of one and set state) | 13133 | * Parse an arithmetic expansion (indicate start of one and set state) |
13092 | */ | 13134 | */ |
13093 | parsearith: { | 13135 | parsearith: { |
13094 | if (++arinest == 1) { | 13136 | |
13095 | prevsyntax = syntax; | 13137 | synstack_push(&synstack, |
13096 | syntax = ARISYNTAX; | 13138 | synstack->prev ?: alloca(sizeof(*synstack)), |
13097 | } | 13139 | ARISYNTAX); |
13140 | synstack->dblquote = 1; | ||
13098 | USTPUTC(CTLARI, out); | 13141 | USTPUTC(CTLARI, out); |
13099 | goto parsearith_return; | 13142 | goto parsearith_return; |
13100 | } | 13143 | } |
@@ -14542,7 +14585,7 @@ init(void) | |||
14542 | 14585 | ||
14543 | 14586 | ||
14544 | //usage:#define ash_trivial_usage | 14587 | //usage:#define ash_trivial_usage |
14545 | //usage: "[-/+OPTIONS] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]" | 14588 | //usage: "[-/+OPTIONS] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS] / -s [ARGS]]" |
14546 | //usage:#define ash_full_usage "\n\n" | 14589 | //usage:#define ash_full_usage "\n\n" |
14547 | //usage: "Unix shell interpreter" | 14590 | //usage: "Unix shell interpreter" |
14548 | 14591 | ||