diff options
Diffstat (limited to 'shell/ash.c')
-rw-r--r-- | shell/ash.c | 127 |
1 files changed, 74 insertions, 53 deletions
diff --git a/shell/ash.c b/shell/ash.c index 4bd1c2c9d..7131609e4 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -6272,7 +6272,7 @@ static int substr_atoi(const char *s) | |||
6272 | #define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ | 6272 | #define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ |
6273 | #define EXP_VARTILDE2 0x20 /* expand tildes after colons only */ | 6273 | #define EXP_VARTILDE2 0x20 /* expand tildes after colons only */ |
6274 | #define EXP_WORD 0x40 /* expand word in parameter expansion */ | 6274 | #define EXP_WORD 0x40 /* expand word in parameter expansion */ |
6275 | #define EXP_QUOTED 0x80 /* expand word in double quotes */ | 6275 | #define EXP_QUOTED 0x100 /* expand word in double quotes */ |
6276 | /* | 6276 | /* |
6277 | * rmescape() flags | 6277 | * rmescape() flags |
6278 | */ | 6278 | */ |
@@ -6606,9 +6606,7 @@ memtodest(const char *p, size_t len, int syntax, int quotes) | |||
6606 | if (quotes & QUOTES_ESC) { | 6606 | if (quotes & QUOTES_ESC) { |
6607 | int n = SIT(c, syntax); | 6607 | int n = SIT(c, syntax); |
6608 | if (n == CCTL | 6608 | if (n == CCTL |
6609 | || (((quotes & EXP_FULL) || syntax != BASESYNTAX) | 6609 | || (syntax != BASESYNTAX && n == CBACK) |
6610 | && n == CBACK | ||
6611 | ) | ||
6612 | ) { | 6610 | ) { |
6613 | USTPUTC(CTLESC, q); | 6611 | USTPUTC(CTLESC, q); |
6614 | } | 6612 | } |
@@ -7236,8 +7234,15 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
7236 | if (subtype == VSREPLACE || subtype == VSREPLACEALL) { | 7234 | if (subtype == VSREPLACE || subtype == VSREPLACEALL) { |
7237 | /* Find '/' and replace with NUL */ | 7235 | /* Find '/' and replace with NUL */ |
7238 | repl = p; | 7236 | repl = p; |
7237 | /* The pattern can't be empty. | ||
7238 | * IOW: if the first char after "${v//" is a slash, | ||
7239 | * it does not terminate the pattern - it's the first char of the pattern: | ||
7240 | * v=/dev/ram; echo ${v////-} prints -dev-ram (pattern is "/") | ||
7241 | * v=/dev/ram; echo ${v///r/-} prints /dev-am (pattern is "/r") | ||
7242 | */ | ||
7243 | if (*repl == '/') | ||
7244 | repl++; | ||
7239 | for (;;) { | 7245 | for (;;) { |
7240 | /* Handle escaped slashes, e.g. "${v/\//_}" (they are CTLESC'ed by this point) */ | ||
7241 | if (*repl == '\0') { | 7246 | if (*repl == '\0') { |
7242 | repl = NULL; | 7247 | repl = NULL; |
7243 | break; | 7248 | break; |
@@ -7246,6 +7251,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
7246 | *repl = '\0'; | 7251 | *repl = '\0'; |
7247 | break; | 7252 | break; |
7248 | } | 7253 | } |
7254 | /* Handle escaped slashes, e.g. "${v/\//_}" (they are CTLESC'ed by this point) */ | ||
7249 | if ((unsigned char)*repl == CTLESC && repl[1]) | 7255 | if ((unsigned char)*repl == CTLESC && repl[1]) |
7250 | repl++; | 7256 | repl++; |
7251 | repl++; | 7257 | repl++; |
@@ -7549,14 +7555,13 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
7549 | * ash -c 'echo ${#1#}' name:'1=#' | 7555 | * ash -c 'echo ${#1#}' name:'1=#' |
7550 | */ | 7556 | */ |
7551 | static NOINLINE ssize_t | 7557 | static NOINLINE ssize_t |
7552 | varvalue(char *name, int varflags, int flags, int *quotedp) | 7558 | varvalue(char *name, int varflags, int flags, int quoted) |
7553 | { | 7559 | { |
7554 | const char *p; | 7560 | const char *p; |
7555 | int num; | 7561 | int num; |
7556 | int i; | 7562 | int i; |
7557 | ssize_t len = 0; | 7563 | ssize_t len = 0; |
7558 | int sep; | 7564 | int sep; |
7559 | int quoted = *quotedp; | ||
7560 | int subtype = varflags & VSTYPE; | 7565 | int subtype = varflags & VSTYPE; |
7561 | int discard = subtype == VSPLUS || subtype == VSLENGTH; | 7566 | int discard = subtype == VSPLUS || subtype == VSLENGTH; |
7562 | int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL; | 7567 | int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL; |
@@ -7604,13 +7609,27 @@ varvalue(char *name, int varflags, int flags, int *quotedp) | |||
7604 | case '*': { | 7609 | case '*': { |
7605 | char **ap; | 7610 | char **ap; |
7606 | char sepc; | 7611 | char sepc; |
7612 | char c; | ||
7607 | 7613 | ||
7608 | if (quoted) | 7614 | /* We will set c to 0 or ~0 depending on whether |
7609 | sep = 0; | 7615 | * we're doing field splitting. We won't do field |
7610 | sep |= ifsset() ? ifsval()[0] : ' '; | 7616 | * splitting if either we're quoted or sep is zero. |
7617 | * | ||
7618 | * Instead of testing (quoted || !sep) the following | ||
7619 | * trick optimises away any branches by using the | ||
7620 | * fact that EXP_QUOTED (which is the only bit that | ||
7621 | * can be set in quoted) is the same as EXP_FULL << | ||
7622 | * CHAR_BIT (which is the only bit that can be set | ||
7623 | * in sep). | ||
7624 | */ | ||
7625 | #if EXP_QUOTED >> CHAR_BIT != EXP_FULL | ||
7626 | #error The following two lines expect EXP_QUOTED == EXP_FULL << CHAR_BIT | ||
7627 | #endif | ||
7628 | c = !((quoted | ~sep) & EXP_QUOTED) - 1; | ||
7629 | sep &= ~quoted; | ||
7630 | sep |= ifsset() ? (unsigned char)(c & ifsval()[0]) : ' '; | ||
7611 | param: | 7631 | param: |
7612 | sepc = sep; | 7632 | sepc = sep; |
7613 | *quotedp = !sepc; | ||
7614 | ap = shellparam.p; | 7633 | ap = shellparam.p; |
7615 | if (!ap) | 7634 | if (!ap) |
7616 | return -1; | 7635 | return -1; |
@@ -7675,7 +7694,6 @@ evalvar(char *p, int flag) | |||
7675 | char varflags; | 7694 | char varflags; |
7676 | char subtype; | 7695 | char subtype; |
7677 | int quoted; | 7696 | int quoted; |
7678 | char easy; | ||
7679 | char *var; | 7697 | char *var; |
7680 | int patloc; | 7698 | int patloc; |
7681 | int startloc; | 7699 | int startloc; |
@@ -7689,12 +7707,11 @@ evalvar(char *p, int flag) | |||
7689 | 7707 | ||
7690 | quoted = flag & EXP_QUOTED; | 7708 | quoted = flag & EXP_QUOTED; |
7691 | var = p; | 7709 | var = p; |
7692 | easy = (!quoted || (*var == '@' && shellparam.nparam)); | ||
7693 | startloc = expdest - (char *)stackblock(); | 7710 | startloc = expdest - (char *)stackblock(); |
7694 | p = strchr(p, '=') + 1; //TODO: use var_end(p)? | 7711 | p = strchr(p, '=') + 1; //TODO: use var_end(p)? |
7695 | 7712 | ||
7696 | again: | 7713 | again: |
7697 | varlen = varvalue(var, varflags, flag, "ed); | 7714 | varlen = varvalue(var, varflags, flag, quoted); |
7698 | if (varflags & VSNUL) | 7715 | if (varflags & VSNUL) |
7699 | varlen--; | 7716 | varlen--; |
7700 | 7717 | ||
@@ -7740,8 +7757,11 @@ evalvar(char *p, int flag) | |||
7740 | 7757 | ||
7741 | if (subtype == VSNORMAL) { | 7758 | if (subtype == VSNORMAL) { |
7742 | record: | 7759 | record: |
7743 | if (!easy) | 7760 | if (quoted) { |
7744 | goto end; | 7761 | quoted = *var == '@' && shellparam.nparam; |
7762 | if (!quoted) | ||
7763 | goto end; | ||
7764 | } | ||
7745 | recordregion(startloc, expdest - (char *)stackblock(), quoted); | 7765 | recordregion(startloc, expdest - (char *)stackblock(), quoted); |
7746 | goto end; | 7766 | goto end; |
7747 | } | 7767 | } |
@@ -7999,7 +8019,7 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len) | |||
7999 | } | 8019 | } |
8000 | } | 8020 | } |
8001 | } else { | 8021 | } else { |
8002 | if (*p == '\\') | 8022 | if (*p == '\\' && p[1]) |
8003 | esc++; | 8023 | esc++; |
8004 | if (p[esc] == '/') { | 8024 | if (p[esc] == '/') { |
8005 | if (metaflag) | 8025 | if (metaflag) |
@@ -8013,7 +8033,7 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len) | |||
8013 | return; | 8033 | return; |
8014 | p = name; | 8034 | p = name; |
8015 | do { | 8035 | do { |
8016 | if (*p == '\\') | 8036 | if (*p == '\\' && p[1]) |
8017 | p++; | 8037 | p++; |
8018 | *enddir++ = *p; | 8038 | *enddir++ = *p; |
8019 | } while (*p++); | 8039 | } while (*p++); |
@@ -8025,7 +8045,7 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len) | |||
8025 | if (name < start) { | 8045 | if (name < start) { |
8026 | p = name; | 8046 | p = name; |
8027 | do { | 8047 | do { |
8028 | if (*p == '\\') | 8048 | if (*p == '\\' && p[1]) |
8029 | p++; | 8049 | p++; |
8030 | *enddir++ = *p++; | 8050 | *enddir++ = *p++; |
8031 | } while (p < start); | 8051 | } while (p < start); |
@@ -8473,15 +8493,15 @@ static void shellexec(char *prog, char **argv, const char *path, int idx) | |||
8473 | 8493 | ||
8474 | /* Map to POSIX errors */ | 8494 | /* Map to POSIX errors */ |
8475 | switch (e) { | 8495 | switch (e) { |
8476 | case EACCES: | 8496 | default: |
8477 | exerrno = 126; | 8497 | exerrno = 126; |
8478 | break; | 8498 | break; |
8499 | case ELOOP: | ||
8500 | case ENAMETOOLONG: | ||
8479 | case ENOENT: | 8501 | case ENOENT: |
8502 | case ENOTDIR: | ||
8480 | exerrno = 127; | 8503 | exerrno = 127; |
8481 | break; | 8504 | break; |
8482 | default: | ||
8483 | exerrno = 2; | ||
8484 | break; | ||
8485 | } | 8505 | } |
8486 | exitstatus = exerrno; | 8506 | exitstatus = exerrno; |
8487 | TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n", | 8507 | TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n", |
@@ -10083,9 +10103,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) | |||
10083 | shellparam.optind = 1; | 10103 | shellparam.optind = 1; |
10084 | shellparam.optoff = -1; | 10104 | shellparam.optoff = -1; |
10085 | #endif | 10105 | #endif |
10086 | pushlocalvars(); | ||
10087 | evaltree(func->n.ndefun.body, flags & EV_TESTED); | 10106 | evaltree(func->n.ndefun.body, flags & EV_TESTED); |
10088 | poplocalvars(0); | ||
10089 | funcdone: | 10107 | funcdone: |
10090 | INT_OFF; | 10108 | INT_OFF; |
10091 | funcline = savefuncline; | 10109 | funcline = savefuncline; |
@@ -10413,6 +10431,7 @@ find_builtin(const char *name) | |||
10413 | /* | 10431 | /* |
10414 | * Execute a simple command. | 10432 | * Execute a simple command. |
10415 | */ | 10433 | */ |
10434 | static void unwindfiles(struct parsefile *stop); | ||
10416 | static int | 10435 | static int |
10417 | isassignment(const char *p) | 10436 | isassignment(const char *p) |
10418 | { | 10437 | { |
@@ -10436,6 +10455,7 @@ evalcommand(union node *cmd, int flags) | |||
10436 | "\0\0", bltincmd /* why three NULs? */ | 10455 | "\0\0", bltincmd /* why three NULs? */ |
10437 | }; | 10456 | }; |
10438 | struct localvar_list *localvar_stop; | 10457 | struct localvar_list *localvar_stop; |
10458 | struct parsefile *file_stop; | ||
10439 | struct redirtab *redir_stop; | 10459 | struct redirtab *redir_stop; |
10440 | struct stackmark smark; | 10460 | struct stackmark smark; |
10441 | union node *argp; | 10461 | union node *argp; |
@@ -10461,6 +10481,7 @@ evalcommand(union node *cmd, int flags) | |||
10461 | TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); | 10481 | TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); |
10462 | setstackmark(&smark); | 10482 | setstackmark(&smark); |
10463 | localvar_stop = pushlocalvars(); | 10483 | localvar_stop = pushlocalvars(); |
10484 | file_stop = g_parsefile; | ||
10464 | back_exitstatus = 0; | 10485 | back_exitstatus = 0; |
10465 | 10486 | ||
10466 | cmdentry.cmdtype = CMDBUILTIN; | 10487 | cmdentry.cmdtype = CMDBUILTIN; |
@@ -10742,7 +10763,6 @@ evalcommand(union node *cmd, int flags) | |||
10742 | goto readstatus; | 10763 | goto readstatus; |
10743 | 10764 | ||
10744 | case CMDFUNCTION: | 10765 | case CMDFUNCTION: |
10745 | poplocalvars(1); | ||
10746 | /* See above for the rationale */ | 10766 | /* See above for the rationale */ |
10747 | dowait(DOWAIT_NONBLOCK, NULL); | 10767 | dowait(DOWAIT_NONBLOCK, NULL); |
10748 | if (evalfun(cmdentry.u.func, argc, argv, flags)) | 10768 | if (evalfun(cmdentry.u.func, argc, argv, flags)) |
@@ -10756,6 +10776,7 @@ evalcommand(union node *cmd, int flags) | |||
10756 | if (cmd->ncmd.redirect) | 10776 | if (cmd->ncmd.redirect) |
10757 | popredir(/*drop:*/ cmd_is_exec); | 10777 | popredir(/*drop:*/ cmd_is_exec); |
10758 | unwindredir(redir_stop); | 10778 | unwindredir(redir_stop); |
10779 | unwindfiles(file_stop); | ||
10759 | unwindlocalvars(localvar_stop); | 10780 | unwindlocalvars(localvar_stop); |
10760 | if (lastarg) { | 10781 | if (lastarg) { |
10761 | /* dsl: I think this is intended to be used to support | 10782 | /* dsl: I think this is intended to be used to support |
@@ -11278,14 +11299,20 @@ popfile(void) | |||
11278 | INT_ON; | 11299 | INT_ON; |
11279 | } | 11300 | } |
11280 | 11301 | ||
11302 | static void | ||
11303 | unwindfiles(struct parsefile *stop) | ||
11304 | { | ||
11305 | while (g_parsefile != stop) | ||
11306 | popfile(); | ||
11307 | } | ||
11308 | |||
11281 | /* | 11309 | /* |
11282 | * Return to top level. | 11310 | * Return to top level. |
11283 | */ | 11311 | */ |
11284 | static void | 11312 | static void |
11285 | popallfiles(void) | 11313 | popallfiles(void) |
11286 | { | 11314 | { |
11287 | while (g_parsefile != &basepf) | 11315 | unwindfiles(&basepf); |
11288 | popfile(); | ||
11289 | } | 11316 | } |
11290 | 11317 | ||
11291 | #if !ENABLE_PLATFORM_MINGW32 | 11318 | #if !ENABLE_PLATFORM_MINGW32 |
@@ -12929,7 +12956,7 @@ parsesub: { | |||
12929 | STPUTC(c, out); | 12956 | STPUTC(c, out); |
12930 | c = pgetc_eatbnl(); | 12957 | c = pgetc_eatbnl(); |
12931 | } while (isdigit(c)); | 12958 | } while (isdigit(c)); |
12932 | } else { | 12959 | } else if (c != '}') { |
12933 | /* $[{[#]]<specialchar>[}] */ | 12960 | /* $[{[#]]<specialchar>[}] */ |
12934 | int cc = c; | 12961 | int cc = c; |
12935 | 12962 | ||
@@ -12955,7 +12982,8 @@ parsesub: { | |||
12955 | } | 12982 | } |
12956 | 12983 | ||
12957 | USTPUTC(cc, out); | 12984 | USTPUTC(cc, out); |
12958 | } | 12985 | } else |
12986 | goto badsub; | ||
12959 | 12987 | ||
12960 | if (c != '}' && subtype == VSLENGTH) { | 12988 | if (c != '}' && subtype == VSLENGTH) { |
12961 | /* ${#VAR didn't end with } */ | 12989 | /* ${#VAR didn't end with } */ |
@@ -14297,38 +14325,35 @@ letcmd(int argc UNUSED_PARAM, char **argv) | |||
14297 | static int FAST_FUNC | 14325 | static int FAST_FUNC |
14298 | readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | 14326 | readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
14299 | { | 14327 | { |
14300 | char *opt_n = NULL; | 14328 | struct builtin_read_params params; |
14301 | char *opt_p = NULL; | ||
14302 | char *opt_t = NULL; | ||
14303 | char *opt_u = NULL; | ||
14304 | char *opt_d = NULL; /* optimized out if !BASH */ | ||
14305 | int read_flags = 0; | ||
14306 | const char *r; | 14329 | const char *r; |
14307 | int i; | 14330 | int i; |
14308 | 14331 | ||
14332 | memset(¶ms, 0, sizeof(params)); | ||
14333 | |||
14309 | while ((i = nextopt("p:u:rt:n:sd:")) != '\0') { | 14334 | while ((i = nextopt("p:u:rt:n:sd:")) != '\0') { |
14310 | switch (i) { | 14335 | switch (i) { |
14311 | case 'p': | 14336 | case 'p': |
14312 | opt_p = optionarg; | 14337 | params.opt_p = optionarg; |
14313 | break; | 14338 | break; |
14314 | case 'n': | 14339 | case 'n': |
14315 | opt_n = optionarg; | 14340 | params.opt_n = optionarg; |
14316 | break; | 14341 | break; |
14317 | case 's': | 14342 | case 's': |
14318 | read_flags |= BUILTIN_READ_SILENT; | 14343 | params.read_flags |= BUILTIN_READ_SILENT; |
14319 | break; | 14344 | break; |
14320 | case 't': | 14345 | case 't': |
14321 | opt_t = optionarg; | 14346 | params.opt_t = optionarg; |
14322 | break; | 14347 | break; |
14323 | case 'r': | 14348 | case 'r': |
14324 | read_flags |= BUILTIN_READ_RAW; | 14349 | params.read_flags |= BUILTIN_READ_RAW; |
14325 | break; | 14350 | break; |
14326 | case 'u': | 14351 | case 'u': |
14327 | opt_u = optionarg; | 14352 | params.opt_u = optionarg; |
14328 | break; | 14353 | break; |
14329 | #if BASH_READ_D | 14354 | #if BASH_READ_D |
14330 | case 'd': | 14355 | case 'd': |
14331 | opt_d = optionarg; | 14356 | params.opt_d = optionarg; |
14332 | break; | 14357 | break; |
14333 | #endif | 14358 | #endif |
14334 | default: | 14359 | default: |
@@ -14336,21 +14361,16 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
14336 | } | 14361 | } |
14337 | } | 14362 | } |
14338 | 14363 | ||
14364 | params.argv = argptr; | ||
14365 | params.setvar = setvar0; | ||
14366 | params.ifs = bltinlookup("IFS"); /* can be NULL */ | ||
14367 | |||
14339 | /* "read -s" needs to save/restore termios, can't allow ^C | 14368 | /* "read -s" needs to save/restore termios, can't allow ^C |
14340 | * to jump out of it. | 14369 | * to jump out of it. |
14341 | */ | 14370 | */ |
14342 | again: | 14371 | again: |
14343 | INT_OFF; | 14372 | INT_OFF; |
14344 | r = shell_builtin_read(setvar0, | 14373 | r = shell_builtin_read(¶ms); |
14345 | argptr, | ||
14346 | bltinlookup("IFS"), /* can be NULL */ | ||
14347 | read_flags, | ||
14348 | opt_n, | ||
14349 | opt_p, | ||
14350 | opt_t, | ||
14351 | opt_u, | ||
14352 | opt_d | ||
14353 | ); | ||
14354 | INT_ON; | 14374 | INT_ON; |
14355 | 14375 | ||
14356 | if ((uintptr_t)r == 1 && errno == EINTR) { | 14376 | if ((uintptr_t)r == 1 && errno == EINTR) { |
@@ -14595,6 +14615,7 @@ init(void) | |||
14595 | } | 14615 | } |
14596 | } | 14616 | } |
14597 | 14617 | ||
14618 | setvareq((char*)defifsvar, VTEXTFIXED); | ||
14598 | setvareq((char*)defoptindvar, VTEXTFIXED); | 14619 | setvareq((char*)defoptindvar, VTEXTFIXED); |
14599 | 14620 | ||
14600 | setvar0("PPID", utoa(getppid())); | 14621 | setvar0("PPID", utoa(getppid())); |