diff options
Diffstat (limited to 'shell/ash.c')
-rw-r--r-- | shell/ash.c | 459 |
1 files changed, 219 insertions, 240 deletions
diff --git a/shell/ash.c b/shell/ash.c index 28b522d7c..6aaeecfac 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -241,6 +241,9 @@ | |||
241 | #include "shell_common.h" | 241 | #include "shell_common.h" |
242 | #if ENABLE_FEATURE_SH_MATH | 242 | #if ENABLE_FEATURE_SH_MATH |
243 | # include "math.h" | 243 | # include "math.h" |
244 | #else | ||
245 | typedef long arith_t; | ||
246 | # define ARITH_FMT "%ld" | ||
244 | #endif | 247 | #endif |
245 | #if ENABLE_ASH_RANDOM_SUPPORT | 248 | #if ENABLE_ASH_RANDOM_SUPPORT |
246 | # include "random.h" | 249 | # include "random.h" |
@@ -708,8 +711,8 @@ fmtstr(char *outbuf, size_t length, const char *fmt, ...) | |||
708 | va_list ap; | 711 | va_list ap; |
709 | int ret; | 712 | int ret; |
710 | 713 | ||
711 | va_start(ap, fmt); | ||
712 | INT_OFF; | 714 | INT_OFF; |
715 | va_start(ap, fmt); | ||
713 | ret = vsnprintf(outbuf, length, fmt, ap); | 716 | ret = vsnprintf(outbuf, length, fmt, ap); |
714 | va_end(ap); | 717 | va_end(ap); |
715 | INT_ON; | 718 | INT_ON; |
@@ -1334,7 +1337,6 @@ static struct parsefile basepf; /* top level input file */ | |||
1334 | static struct parsefile *g_parsefile = &basepf; /* current input file */ | 1337 | static struct parsefile *g_parsefile = &basepf; /* current input file */ |
1335 | static int startlinno; /* line # where last token started */ | 1338 | static int startlinno; /* line # where last token started */ |
1336 | static char *commandname; /* currently executing command */ | 1339 | static char *commandname; /* currently executing command */ |
1337 | static struct strlist *cmdenviron; /* environment for builtin command */ | ||
1338 | 1340 | ||
1339 | 1341 | ||
1340 | /* ============ Message printing */ | 1342 | /* ============ Message printing */ |
@@ -1391,6 +1393,18 @@ ash_msg_and_raise_error(const char *msg, ...) | |||
1391 | va_end(ap); | 1393 | va_end(ap); |
1392 | } | 1394 | } |
1393 | 1395 | ||
1396 | /* | ||
1397 | * Use '%m' to append error string on platforms that support it, '%s' and | ||
1398 | * strerror() on those that don't. | ||
1399 | * | ||
1400 | * 'fmt' must be a string literal. | ||
1401 | */ | ||
1402 | #ifdef HAVE_PRINTF_PERCENTM | ||
1403 | #define ash_msg_and_raise_perror(fmt, ...) ash_msg_and_raise_error(fmt ": %m", ##__VA_ARGS__) | ||
1404 | #else | ||
1405 | #define ash_msg_and_raise_perror(fmt, ...) ash_msg_and_raise_error(fmt ": %s", ##__VA_ARGS__, strerror(errno)) | ||
1406 | #endif | ||
1407 | |||
1394 | static void raise_error_syntax(const char *) NORETURN; | 1408 | static void raise_error_syntax(const char *) NORETURN; |
1395 | static void | 1409 | static void |
1396 | raise_error_syntax(const char *msg) | 1410 | raise_error_syntax(const char *msg) |
@@ -1875,9 +1889,6 @@ single_quote(const char *s) | |||
1875 | 1889 | ||
1876 | /* | 1890 | /* |
1877 | * Produce a possibly single quoted string suitable as input to the shell. | 1891 | * Produce a possibly single quoted string suitable as input to the shell. |
1878 | * If 'conditional' is nonzero, quoting is only done if the string contains | ||
1879 | * non-shellsafe characters, or is identical to a shell keyword (reserved | ||
1880 | * word); if it is zero, quoting is always done. | ||
1881 | * If quoting was done, the return string is allocated on the stack, | 1892 | * If quoting was done, the return string is allocated on the stack, |
1882 | * otherwise a pointer to the original string is returned. | 1893 | * otherwise a pointer to the original string is returned. |
1883 | */ | 1894 | */ |
@@ -2312,15 +2323,9 @@ reinit_unicode_for_ash(void) | |||
2312 | /* | 2323 | /* |
2313 | * Search the environment of a builtin command. | 2324 | * Search the environment of a builtin command. |
2314 | */ | 2325 | */ |
2315 | static const char * | 2326 | static ALWAYS_INLINE const char * |
2316 | bltinlookup(const char *name) | 2327 | bltinlookup(const char *name) |
2317 | { | 2328 | { |
2318 | struct strlist *sp; | ||
2319 | |||
2320 | for (sp = cmdenviron; sp; sp = sp->next) { | ||
2321 | if (varcmp(sp->text, name) == 0) | ||
2322 | return var_end(sp->text); | ||
2323 | } | ||
2324 | return lookupvar(name); | 2329 | return lookupvar(name); |
2325 | } | 2330 | } |
2326 | 2331 | ||
@@ -2331,14 +2336,15 @@ bltinlookup(const char *name) | |||
2331 | * will go away. | 2336 | * will go away. |
2332 | * Called with interrupts off. | 2337 | * Called with interrupts off. |
2333 | */ | 2338 | */ |
2334 | static void | 2339 | static struct var * |
2335 | setvareq(char *s, int flags) | 2340 | setvareq(char *s, int flags) |
2336 | { | 2341 | { |
2337 | struct var *vp, **vpp; | 2342 | struct var *vp, **vpp; |
2338 | 2343 | ||
2339 | vpp = hashvar(s); | 2344 | vpp = hashvar(s); |
2340 | flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); | 2345 | flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); |
2341 | vp = *findvar(vpp, s); | 2346 | vpp = findvar(vpp, s); |
2347 | vp = *vpp; | ||
2342 | if (vp) { | 2348 | if (vp) { |
2343 | if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) { | 2349 | if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) { |
2344 | const char *n; | 2350 | const char *n; |
@@ -2351,7 +2357,7 @@ setvareq(char *s, int flags) | |||
2351 | } | 2357 | } |
2352 | 2358 | ||
2353 | if (flags & VNOSET) | 2359 | if (flags & VNOSET) |
2354 | return; | 2360 | goto out; |
2355 | 2361 | ||
2356 | if (vp->var_func && !(flags & VNOFUNC)) | 2362 | if (vp->var_func && !(flags & VNOFUNC)) |
2357 | vp->var_func(var_end(s)); | 2363 | vp->var_func(var_end(s)); |
@@ -2359,11 +2365,22 @@ setvareq(char *s, int flags) | |||
2359 | if (!(vp->flags & (VTEXTFIXED|VSTACK))) | 2365 | if (!(vp->flags & (VTEXTFIXED|VSTACK))) |
2360 | free((char*)vp->var_text); | 2366 | free((char*)vp->var_text); |
2361 | 2367 | ||
2368 | if (((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) | (vp->flags & VSTRFIXED)) == VUNSET) { | ||
2369 | *vpp = vp->next; | ||
2370 | free(vp); | ||
2371 | out_free: | ||
2372 | if ((flags & (VTEXTFIXED|VSTACK|VNOSAVE)) == VNOSAVE) | ||
2373 | free(s); | ||
2374 | goto out; | ||
2375 | } | ||
2376 | |||
2362 | flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET); | 2377 | flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET); |
2363 | } else { | 2378 | } else { |
2364 | /* variable s is not found */ | 2379 | /* variable s is not found */ |
2365 | if (flags & VNOSET) | 2380 | if (flags & VNOSET) |
2366 | return; | 2381 | goto out; |
2382 | if ((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) == VUNSET) | ||
2383 | goto out_free; | ||
2367 | vp = ckzalloc(sizeof(*vp)); | 2384 | vp = ckzalloc(sizeof(*vp)); |
2368 | vp->next = *vpp; | 2385 | vp->next = *vpp; |
2369 | /*vp->func = NULL; - ckzalloc did it */ | 2386 | /*vp->func = NULL; - ckzalloc did it */ |
@@ -2373,13 +2390,16 @@ setvareq(char *s, int flags) | |||
2373 | s = ckstrdup(s); | 2390 | s = ckstrdup(s); |
2374 | vp->var_text = s; | 2391 | vp->var_text = s; |
2375 | vp->flags = flags; | 2392 | vp->flags = flags; |
2393 | |||
2394 | out: | ||
2395 | return vp; | ||
2376 | } | 2396 | } |
2377 | 2397 | ||
2378 | /* | 2398 | /* |
2379 | * Set the value of a variable. The flags argument is ored with the | 2399 | * Set the value of a variable. The flags argument is ored with the |
2380 | * flags of the variable. If val is NULL, the variable is unset. | 2400 | * flags of the variable. If val is NULL, the variable is unset. |
2381 | */ | 2401 | */ |
2382 | static void | 2402 | static struct var * |
2383 | setvar(const char *name, const char *val, int flags) | 2403 | setvar(const char *name, const char *val, int flags) |
2384 | { | 2404 | { |
2385 | const char *q; | 2405 | const char *q; |
@@ -2387,6 +2407,7 @@ setvar(const char *name, const char *val, int flags) | |||
2387 | char *nameeq; | 2407 | char *nameeq; |
2388 | size_t namelen; | 2408 | size_t namelen; |
2389 | size_t vallen; | 2409 | size_t vallen; |
2410 | struct var *vp; | ||
2390 | 2411 | ||
2391 | q = endofname(name); | 2412 | q = endofname(name); |
2392 | p = strchrnul(q, '='); | 2413 | p = strchrnul(q, '='); |
@@ -2408,8 +2429,10 @@ setvar(const char *name, const char *val, int flags) | |||
2408 | p = mempcpy(p, val, vallen); | 2429 | p = mempcpy(p, val, vallen); |
2409 | } | 2430 | } |
2410 | *p = '\0'; | 2431 | *p = '\0'; |
2411 | setvareq(nameeq, flags | VNOSAVE); | 2432 | vp = setvareq(nameeq, flags | VNOSAVE); |
2412 | INT_ON; | 2433 | INT_ON; |
2434 | |||
2435 | return vp; | ||
2413 | } | 2436 | } |
2414 | 2437 | ||
2415 | static void FAST_FUNC | 2438 | static void FAST_FUNC |
@@ -2421,43 +2444,10 @@ setvar0(const char *name, const char *val) | |||
2421 | /* | 2444 | /* |
2422 | * Unset the specified variable. | 2445 | * Unset the specified variable. |
2423 | */ | 2446 | */ |
2424 | static int | 2447 | static void |
2425 | unsetvar(const char *s) | 2448 | unsetvar(const char *s) |
2426 | { | 2449 | { |
2427 | struct var **vpp; | 2450 | setvar(s, NULL, 0); |
2428 | struct var *vp; | ||
2429 | int retval; | ||
2430 | |||
2431 | vpp = findvar(hashvar(s), s); | ||
2432 | vp = *vpp; | ||
2433 | retval = 2; | ||
2434 | if (vp) { | ||
2435 | int flags = vp->flags; | ||
2436 | |||
2437 | retval = 1; | ||
2438 | if (flags & VREADONLY) | ||
2439 | goto out; | ||
2440 | #if ENABLE_ASH_RANDOM_SUPPORT | ||
2441 | vp->flags &= ~VDYNAMIC; | ||
2442 | #endif | ||
2443 | if (flags & VUNSET) | ||
2444 | goto ok; | ||
2445 | if ((flags & VSTRFIXED) == 0) { | ||
2446 | INT_OFF; | ||
2447 | if ((flags & (VTEXTFIXED|VSTACK)) == 0) | ||
2448 | free((char*)vp->var_text); | ||
2449 | *vpp = vp->next; | ||
2450 | free(vp); | ||
2451 | INT_ON; | ||
2452 | } else { | ||
2453 | setvar0(s, NULL); | ||
2454 | vp->flags &= ~VEXPORT; | ||
2455 | } | ||
2456 | ok: | ||
2457 | retval = 0; | ||
2458 | } | ||
2459 | out: | ||
2460 | return retval; | ||
2461 | } | 2451 | } |
2462 | 2452 | ||
2463 | /* | 2453 | /* |
@@ -4067,7 +4057,7 @@ static void | |||
4067 | xtcsetpgrp(int fd, pid_t pgrp) | 4057 | xtcsetpgrp(int fd, pid_t pgrp) |
4068 | { | 4058 | { |
4069 | if (tcsetpgrp(fd, pgrp)) | 4059 | if (tcsetpgrp(fd, pgrp)) |
4070 | ash_msg_and_raise_error("can't set tty process group (%m)"); | 4060 | ash_msg_and_raise_perror("can't set tty process group"); |
4071 | } | 4061 | } |
4072 | 4062 | ||
4073 | /* | 4063 | /* |
@@ -5529,68 +5519,6 @@ stoppedjobs(void) | |||
5529 | #define CLOSED -3 /* marks a slot of previously-closed fd */ | 5519 | #define CLOSED -3 /* marks a slot of previously-closed fd */ |
5530 | 5520 | ||
5531 | /* | 5521 | /* |
5532 | * Open a file in noclobber mode. | ||
5533 | * The code was copied from bash. | ||
5534 | */ | ||
5535 | static int | ||
5536 | noclobberopen(const char *fname) | ||
5537 | { | ||
5538 | int r, fd; | ||
5539 | struct stat finfo, finfo2; | ||
5540 | |||
5541 | /* | ||
5542 | * If the file exists and is a regular file, return an error | ||
5543 | * immediately. | ||
5544 | */ | ||
5545 | r = stat(fname, &finfo); | ||
5546 | if (r == 0 && S_ISREG(finfo.st_mode)) { | ||
5547 | errno = EEXIST; | ||
5548 | return -1; | ||
5549 | } | ||
5550 | |||
5551 | /* | ||
5552 | * If the file was not present (r != 0), make sure we open it | ||
5553 | * exclusively so that if it is created before we open it, our open | ||
5554 | * will fail. Make sure that we do not truncate an existing file. | ||
5555 | * Note that we don't turn on O_EXCL unless the stat failed -- if the | ||
5556 | * file was not a regular file, we leave O_EXCL off. | ||
5557 | */ | ||
5558 | if (r != 0) | ||
5559 | return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666); | ||
5560 | fd = open(fname, O_WRONLY|O_CREAT, 0666); | ||
5561 | |||
5562 | /* If the open failed, return the file descriptor right away. */ | ||
5563 | if (fd < 0) | ||
5564 | return fd; | ||
5565 | |||
5566 | /* | ||
5567 | * OK, the open succeeded, but the file may have been changed from a | ||
5568 | * non-regular file to a regular file between the stat and the open. | ||
5569 | * We are assuming that the O_EXCL open handles the case where FILENAME | ||
5570 | * did not exist and is symlinked to an existing file between the stat | ||
5571 | * and open. | ||
5572 | */ | ||
5573 | |||
5574 | /* | ||
5575 | * If we can open it and fstat the file descriptor, and neither check | ||
5576 | * revealed that it was a regular file, and the file has not been | ||
5577 | * replaced, return the file descriptor. | ||
5578 | */ | ||
5579 | if (fstat(fd, &finfo2) == 0 | ||
5580 | && !S_ISREG(finfo2.st_mode) | ||
5581 | && finfo.st_dev == finfo2.st_dev | ||
5582 | && finfo.st_ino == finfo2.st_ino | ||
5583 | ) { | ||
5584 | return fd; | ||
5585 | } | ||
5586 | |||
5587 | /* The file has been replaced. badness. */ | ||
5588 | close(fd); | ||
5589 | errno = EEXIST; | ||
5590 | return -1; | ||
5591 | } | ||
5592 | |||
5593 | /* | ||
5594 | * Handle here documents. Normally we fork off a process to write the | 5522 | * Handle here documents. Normally we fork off a process to write the |
5595 | * data to a pipe. If the document is short, we can stuff the data in | 5523 | * data to a pipe. If the document is short, we can stuff the data in |
5596 | * the pipe without forking. | 5524 | * the pipe without forking. |
@@ -5645,6 +5573,7 @@ openhere(union node *redir) | |||
5645 | static int | 5573 | static int |
5646 | openredirect(union node *redir) | 5574 | openredirect(union node *redir) |
5647 | { | 5575 | { |
5576 | struct stat sb; | ||
5648 | char *fname; | 5577 | char *fname; |
5649 | int f; | 5578 | int f; |
5650 | 5579 | ||
@@ -5709,9 +5638,23 @@ openredirect(union node *redir) | |||
5709 | #endif | 5638 | #endif |
5710 | /* Take care of noclobber mode. */ | 5639 | /* Take care of noclobber mode. */ |
5711 | if (Cflag) { | 5640 | if (Cflag) { |
5712 | f = noclobberopen(fname); | 5641 | if (stat(fname, &sb) < 0) { |
5713 | if (f < 0) | 5642 | f = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666); |
5643 | if (f < 0) | ||
5644 | goto ecreate; | ||
5645 | } else if (!S_ISREG(sb.st_mode)) { | ||
5646 | f = open(fname, O_WRONLY, 0666); | ||
5647 | if (f < 0) | ||
5648 | goto ecreate; | ||
5649 | if (fstat(f, &sb) < 0 && S_ISREG(sb.st_mode)) { | ||
5650 | close(f); | ||
5651 | errno = EEXIST; | ||
5652 | goto ecreate; | ||
5653 | } | ||
5654 | } else { | ||
5655 | errno = EEXIST; | ||
5714 | goto ecreate; | 5656 | goto ecreate; |
5657 | } | ||
5715 | break; | 5658 | break; |
5716 | } | 5659 | } |
5717 | /* FALLTHROUGH */ | 5660 | /* FALLTHROUGH */ |
@@ -5750,7 +5693,7 @@ savefd(int from) | |||
5750 | err = newfd < 0 ? errno : 0; | 5693 | err = newfd < 0 ? errno : 0; |
5751 | if (err != EBADF) { | 5694 | if (err != EBADF) { |
5752 | if (err) | 5695 | if (err) |
5753 | ash_msg_and_raise_error("%d: %m", from); | 5696 | ash_msg_and_raise_perror("%d", from); |
5754 | close(from); | 5697 | close(from); |
5755 | fcntl(newfd, F_SETFD, FD_CLOEXEC); | 5698 | fcntl(newfd, F_SETFD, FD_CLOEXEC); |
5756 | } | 5699 | } |
@@ -5765,7 +5708,7 @@ dup2_or_raise(int from, int to) | |||
5765 | newfd = (from != to) ? dup2(from, to) : to; | 5708 | newfd = (from != to) ? dup2(from, to) : to; |
5766 | if (newfd < 0) { | 5709 | if (newfd < 0) { |
5767 | /* Happens when source fd is not open: try "echo >&99" */ | 5710 | /* Happens when source fd is not open: try "echo >&99" */ |
5768 | ash_msg_and_raise_error("%d: %m", from); | 5711 | ash_msg_and_raise_perror("%d", from); |
5769 | } | 5712 | } |
5770 | return newfd; | 5713 | return newfd; |
5771 | } | 5714 | } |
@@ -5896,7 +5839,7 @@ redirect(union node *redir, int flags) | |||
5896 | /* "echo >&10" and 10 is a fd opened to a sh script? */ | 5839 | /* "echo >&10" and 10 is a fd opened to a sh script? */ |
5897 | if (is_hidden_fd(sv, right_fd)) { | 5840 | if (is_hidden_fd(sv, right_fd)) { |
5898 | errno = EBADF; /* as if it is closed */ | 5841 | errno = EBADF; /* as if it is closed */ |
5899 | ash_msg_and_raise_error("%d: %m", right_fd); | 5842 | ash_msg_and_raise_perror("%d", right_fd); |
5900 | } | 5843 | } |
5901 | newfd = -1; | 5844 | newfd = -1; |
5902 | } else { | 5845 | } else { |
@@ -5930,7 +5873,7 @@ redirect(union node *redir, int flags) | |||
5930 | if (newfd >= 0) | 5873 | if (newfd >= 0) |
5931 | close(newfd); | 5874 | close(newfd); |
5932 | errno = i; | 5875 | errno = i; |
5933 | ash_msg_and_raise_error("%d: %m", fd); | 5876 | ash_msg_and_raise_perror("%d", fd); |
5934 | /* NOTREACHED */ | 5877 | /* NOTREACHED */ |
5935 | } | 5878 | } |
5936 | /* EBADF: it is not open - good, remember to close it */ | 5879 | /* EBADF: it is not open - good, remember to close it */ |
@@ -6104,7 +6047,7 @@ ash_arith(const char *s) | |||
6104 | #define RMESCAPE_SLASH 0x20 /* Stop globbing after slash */ | 6047 | #define RMESCAPE_SLASH 0x20 /* Stop globbing after slash */ |
6105 | 6048 | ||
6106 | /* Add CTLESC when necessary. */ | 6049 | /* Add CTLESC when necessary. */ |
6107 | #define QUOTES_ESC (EXP_FULL | EXP_CASE | EXP_QPAT | EXP_REDIR) | 6050 | #define QUOTES_ESC (EXP_FULL | EXP_CASE | EXP_QPAT) |
6108 | /* Do not skip NUL characters. */ | 6051 | /* Do not skip NUL characters. */ |
6109 | #define QUOTES_KEEPNUL EXP_TILDE | 6052 | #define QUOTES_KEEPNUL EXP_TILDE |
6110 | 6053 | ||
@@ -6137,19 +6080,20 @@ static struct arglist exparg; | |||
6137 | 6080 | ||
6138 | /* | 6081 | /* |
6139 | * Our own itoa(). | 6082 | * Our own itoa(). |
6083 | * cvtnum() is used even if math support is off (to prepare $? values and such). | ||
6140 | */ | 6084 | */ |
6141 | #if !ENABLE_FEATURE_SH_MATH | ||
6142 | /* cvtnum() is used even if math support is off (to prepare $? values and such) */ | ||
6143 | typedef long arith_t; | ||
6144 | # define ARITH_FMT "%ld" | ||
6145 | #endif | ||
6146 | static int | 6085 | static int |
6147 | cvtnum(arith_t num) | 6086 | cvtnum(arith_t num) |
6148 | { | 6087 | { |
6149 | int len; | 6088 | int len; |
6150 | 6089 | ||
6151 | expdest = makestrspace(sizeof(arith_t)*3 + 2, expdest); | 6090 | /* 32-bit and wider ints require buffer size of bytes*3 (or less) */ |
6152 | len = fmtstr(expdest, sizeof(arith_t)*3 + 2, ARITH_FMT, num); | 6091 | len = sizeof(arith_t) * 3; |
6092 | /* If narrower: worst case, 1-byte ints: need 5 bytes: "-127<NUL>" */ | ||
6093 | if (sizeof(arith_t) < 4) len += 2; | ||
6094 | |||
6095 | expdest = makestrspace(len, expdest); | ||
6096 | len = fmtstr(expdest, len, ARITH_FMT, num); | ||
6153 | STADJUST(len, expdest); | 6097 | STADJUST(len, expdest); |
6154 | return len; | 6098 | return len; |
6155 | } | 6099 | } |
@@ -6569,9 +6513,24 @@ struct backcmd { /* result of evalbackcmd */ | |||
6569 | }; | 6513 | }; |
6570 | 6514 | ||
6571 | /* These forward decls are needed to use "eval" code for backticks handling: */ | 6515 | /* These forward decls are needed to use "eval" code for backticks handling: */ |
6572 | #define EV_EXIT 01 /* exit after evaluating tree */ | 6516 | /* flags in argument to evaltree */ |
6517 | #define EV_EXIT 01 /* exit after evaluating tree */ | ||
6518 | #define EV_TESTED 02 /* exit status is checked; ignore -e flag */ | ||
6573 | static int evaltree(union node *, int); | 6519 | static int evaltree(union node *, int); |
6574 | 6520 | ||
6521 | /* An evaltree() which is known to never return. | ||
6522 | * Used to use an alias: | ||
6523 | * static int evaltreenr(union node *, int) __attribute__((alias("evaltree"),__noreturn__)); | ||
6524 | * but clang was reported to "transfer" noreturn-ness to evaltree() as well. | ||
6525 | */ | ||
6526 | static ALWAYS_INLINE NORETURN void | ||
6527 | evaltreenr(union node *n, int flags) | ||
6528 | { | ||
6529 | evaltree(n, flags); | ||
6530 | bb_unreachable(abort()); | ||
6531 | /* NOTREACHED */ | ||
6532 | } | ||
6533 | |||
6575 | static void FAST_FUNC | 6534 | static void FAST_FUNC |
6576 | evalbackcmd(union node *n, struct backcmd *result) | 6535 | evalbackcmd(union node *n, struct backcmd *result) |
6577 | { | 6536 | { |
@@ -6617,7 +6576,7 @@ evalbackcmd(union node *n, struct backcmd *result) | |||
6617 | */ | 6576 | */ |
6618 | eflag = 0; | 6577 | eflag = 0; |
6619 | ifsfree(); | 6578 | ifsfree(); |
6620 | evaltree(n, EV_EXIT); /* actually evaltreenr... */ | 6579 | evaltreenr(n, EV_EXIT); |
6621 | /* NOTREACHED */ | 6580 | /* NOTREACHED */ |
6622 | } | 6581 | } |
6623 | #endif | 6582 | #endif |
@@ -6750,19 +6709,15 @@ expari(int flag) | |||
6750 | #endif | 6709 | #endif |
6751 | 6710 | ||
6752 | /* argstr needs it */ | 6711 | /* argstr needs it */ |
6753 | static char *evalvar(char *p, int flags, struct strlist *var_str_list); | 6712 | static char *evalvar(char *p, int flags); |
6754 | 6713 | ||
6755 | /* | 6714 | /* |
6756 | * Perform variable and command substitution. If EXP_FULL is set, output CTLESC | 6715 | * Perform variable and command substitution. If EXP_FULL is set, output CTLESC |
6757 | * characters to allow for further processing. Otherwise treat | 6716 | * characters to allow for further processing. Otherwise treat |
6758 | * $@ like $* since no splitting will be performed. | 6717 | * $@ like $* since no splitting will be performed. |
6759 | * | ||
6760 | * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence | ||
6761 | * over shell variables. Needed for "A=a B=$A; echo $B" case - we use it | ||
6762 | * for correct expansion of "B=$A" word. | ||
6763 | */ | 6718 | */ |
6764 | static void | 6719 | static void |
6765 | argstr(char *p, int flags, struct strlist *var_str_list) | 6720 | argstr(char *p, int flags) |
6766 | { | 6721 | { |
6767 | static const char spclchars[] ALIGN1 = { | 6722 | static const char spclchars[] ALIGN1 = { |
6768 | '=', | 6723 | '=', |
@@ -6855,7 +6810,7 @@ argstr(char *p, int flags, struct strlist *var_str_list) | |||
6855 | inquotes ^= EXP_QUOTED; | 6810 | inquotes ^= EXP_QUOTED; |
6856 | /* "$@" syntax adherence hack */ | 6811 | /* "$@" syntax adherence hack */ |
6857 | if (inquotes && !memcmp(p, dolatstr + 1, DOLATSTRLEN - 1)) { | 6812 | if (inquotes && !memcmp(p, dolatstr + 1, DOLATSTRLEN - 1)) { |
6858 | p = evalvar(p + 1, flags | inquotes, /* var_str_list: */ NULL) + 1; | 6813 | p = evalvar(p + 1, flags | inquotes) + 1; |
6859 | goto start; | 6814 | goto start; |
6860 | } | 6815 | } |
6861 | addquote: | 6816 | addquote: |
@@ -6881,7 +6836,7 @@ argstr(char *p, int flags, struct strlist *var_str_list) | |||
6881 | goto addquote; | 6836 | goto addquote; |
6882 | case CTLVAR: | 6837 | case CTLVAR: |
6883 | TRACE(("argstr: evalvar('%s')\n", p)); | 6838 | TRACE(("argstr: evalvar('%s')\n", p)); |
6884 | p = evalvar(p, flags | inquotes, var_str_list); | 6839 | p = evalvar(p, flags | inquotes); |
6885 | TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock())); | 6840 | TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock())); |
6886 | goto start; | 6841 | goto start; |
6887 | case CTLBACKQ: | 6842 | case CTLBACKQ: |
@@ -7023,7 +6978,7 @@ varunset(const char *end, const char *var, const char *umsg, int varflags) | |||
7023 | 6978 | ||
7024 | static const char * | 6979 | static const char * |
7025 | subevalvar(char *p, char *varname, int strloc, int subtype, | 6980 | subevalvar(char *p, char *varname, int strloc, int subtype, |
7026 | int startloc, int varflags, int flag, struct strlist *var_str_list) | 6981 | int startloc, int varflags, int flag) |
7027 | { | 6982 | { |
7028 | struct nodelist *saveargbackq = argbackq; | 6983 | struct nodelist *saveargbackq = argbackq; |
7029 | int quotes = flag & QUOTES_ESC; | 6984 | int quotes = flag & QUOTES_ESC; |
@@ -7041,8 +6996,8 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
7041 | // p, varname, strloc, subtype, startloc, varflags, quotes); | 6996 | // p, varname, strloc, subtype, startloc, varflags, quotes); |
7042 | 6997 | ||
7043 | argstr(p, EXP_TILDE | (subtype != VSASSIGN && subtype != VSQUESTION ? | 6998 | argstr(p, EXP_TILDE | (subtype != VSASSIGN && subtype != VSQUESTION ? |
7044 | (flag & (EXP_QUOTED | EXP_QPAT) ? EXP_QPAT : EXP_CASE) : 0), | 6999 | (flag & (EXP_QUOTED | EXP_QPAT) ? EXP_QPAT : EXP_CASE) : 0) |
7045 | var_str_list); | 7000 | ); |
7046 | STPUTC('\0', expdest); | 7001 | STPUTC('\0', expdest); |
7047 | argbackq = saveargbackq; | 7002 | argbackq = saveargbackq; |
7048 | startp = (char *)stackblock() + startloc; | 7003 | startp = (char *)stackblock() + startloc; |
@@ -7319,7 +7274,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
7319 | * ash -c 'echo ${#1#}' name:'1=#' | 7274 | * ash -c 'echo ${#1#}' name:'1=#' |
7320 | */ | 7275 | */ |
7321 | static NOINLINE ssize_t | 7276 | static NOINLINE ssize_t |
7322 | varvalue(char *name, int varflags, int flags, struct strlist *var_str_list, int *quotedp) | 7277 | varvalue(char *name, int varflags, int flags, int *quotedp) |
7323 | { | 7278 | { |
7324 | const char *p; | 7279 | const char *p; |
7325 | int num; | 7280 | int num; |
@@ -7411,31 +7366,6 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list, int | |||
7411 | goto value; | 7366 | goto value; |
7412 | default: | 7367 | default: |
7413 | /* NB: name has form "VAR=..." */ | 7368 | /* NB: name has form "VAR=..." */ |
7414 | |||
7415 | /* "A=a B=$A" case: var_str_list is a list of "A=a" strings | ||
7416 | * which should be considered before we check variables. */ | ||
7417 | if (var_str_list) { | ||
7418 | unsigned name_len = (strchrnul(name, '=') - name) + 1; | ||
7419 | p = NULL; | ||
7420 | do { | ||
7421 | char *str, *eq; | ||
7422 | str = var_str_list->text; | ||
7423 | eq = strchr(str, '='); | ||
7424 | if (!eq) /* stop at first non-assignment */ | ||
7425 | break; | ||
7426 | eq++; | ||
7427 | if (name_len == (unsigned)(eq - str) | ||
7428 | && strncmp(str, name, name_len) == 0 | ||
7429 | ) { | ||
7430 | p = eq; | ||
7431 | /* goto value; - WRONG! */ | ||
7432 | /* think "A=1 A=2 B=$A" */ | ||
7433 | } | ||
7434 | var_str_list = var_str_list->next; | ||
7435 | } while (var_str_list); | ||
7436 | if (p) | ||
7437 | goto value; | ||
7438 | } | ||
7439 | p = lookupvar(name); | 7369 | p = lookupvar(name); |
7440 | value: | 7370 | value: |
7441 | if (!p) | 7371 | if (!p) |
@@ -7465,7 +7395,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list, int | |||
7465 | * input string. | 7395 | * input string. |
7466 | */ | 7396 | */ |
7467 | static char * | 7397 | static char * |
7468 | evalvar(char *p, int flag, struct strlist *var_str_list) | 7398 | evalvar(char *p, int flag) |
7469 | { | 7399 | { |
7470 | char varflags; | 7400 | char varflags; |
7471 | char subtype; | 7401 | char subtype; |
@@ -7489,7 +7419,7 @@ evalvar(char *p, int flag, struct strlist *var_str_list) | |||
7489 | p = strchr(p, '=') + 1; //TODO: use var_end(p)? | 7419 | p = strchr(p, '=') + 1; //TODO: use var_end(p)? |
7490 | 7420 | ||
7491 | again: | 7421 | again: |
7492 | varlen = varvalue(var, varflags, flag, var_str_list, "ed); | 7422 | varlen = varvalue(var, varflags, flag, "ed); |
7493 | if (varflags & VSNUL) | 7423 | if (varflags & VSNUL) |
7494 | varlen--; | 7424 | varlen--; |
7495 | 7425 | ||
@@ -7503,8 +7433,7 @@ evalvar(char *p, int flag, struct strlist *var_str_list) | |||
7503 | if (varlen < 0) { | 7433 | if (varlen < 0) { |
7504 | argstr( | 7434 | argstr( |
7505 | p, | 7435 | p, |
7506 | flag | EXP_TILDE | EXP_WORD, | 7436 | flag | EXP_TILDE | EXP_WORD |
7507 | var_str_list | ||
7508 | ); | 7437 | ); |
7509 | goto end; | 7438 | goto end; |
7510 | } | 7439 | } |
@@ -7516,7 +7445,7 @@ evalvar(char *p, int flag, struct strlist *var_str_list) | |||
7516 | goto record; | 7445 | goto record; |
7517 | 7446 | ||
7518 | subevalvar(p, var, 0, subtype, startloc, varflags, | 7447 | subevalvar(p, var, 0, subtype, startloc, varflags, |
7519 | flag & ~QUOTES_ESC, var_str_list); | 7448 | flag & ~QUOTES_ESC); |
7520 | varflags &= ~VSNUL; | 7449 | varflags &= ~VSNUL; |
7521 | /* | 7450 | /* |
7522 | * Remove any recorded regions beyond | 7451 | * Remove any recorded regions beyond |
@@ -7569,7 +7498,7 @@ evalvar(char *p, int flag, struct strlist *var_str_list) | |||
7569 | STPUTC('\0', expdest); | 7498 | STPUTC('\0', expdest); |
7570 | patloc = expdest - (char *)stackblock(); | 7499 | patloc = expdest - (char *)stackblock(); |
7571 | if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype, | 7500 | if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype, |
7572 | startloc, varflags, flag, var_str_list)) { | 7501 | startloc, varflags, flag)) { |
7573 | int amount = expdest - ( | 7502 | int amount = expdest - ( |
7574 | (char *)stackblock() + patloc - 1 | 7503 | (char *)stackblock() + patloc - 1 |
7575 | ); | 7504 | ); |
@@ -7993,8 +7922,7 @@ expandarg(union node *arg, struct arglist *arglist, int flag) | |||
7993 | argbackq = arg->narg.backquote; | 7922 | argbackq = arg->narg.backquote; |
7994 | STARTSTACKSTR(expdest); | 7923 | STARTSTACKSTR(expdest); |
7995 | TRACE(("expandarg: argstr('%s',flags:%x)\n", arg->narg.text, flag)); | 7924 | TRACE(("expandarg: argstr('%s',flags:%x)\n", arg->narg.text, flag)); |
7996 | argstr(arg->narg.text, flag, | 7925 | argstr(arg->narg.text, flag); |
7997 | /* var_str_list: */ arglist ? arglist->list : NULL); | ||
7998 | p = _STPUTC('\0', expdest); | 7926 | p = _STPUTC('\0', expdest); |
7999 | expdest = p - 1; | 7927 | expdest = p - 1; |
8000 | if (arglist == NULL) { | 7928 | if (arglist == NULL) { |
@@ -8013,10 +7941,6 @@ expandarg(union node *arg, struct arglist *arglist, int flag) | |||
8013 | exparg.lastp = &exparg.list; | 7941 | exparg.lastp = &exparg.list; |
8014 | expandmeta(exparg.list /*, flag*/); | 7942 | expandmeta(exparg.list /*, flag*/); |
8015 | } else { | 7943 | } else { |
8016 | if (flag & EXP_REDIR) { /*XXX - for now, just remove escapes */ | ||
8017 | rmescapes(p, 0); | ||
8018 | TRACE(("expandarg: rmescapes:'%s'\n", p)); | ||
8019 | } | ||
8020 | sp = stzalloc(sizeof(*sp)); | 7944 | sp = stzalloc(sizeof(*sp)); |
8021 | sp->text = p; | 7945 | sp->text = p; |
8022 | *exparg.lastp = sp; | 7946 | *exparg.lastp = sp; |
@@ -8065,8 +7989,7 @@ casematch(union node *pattern, char *val) | |||
8065 | setstackmark(&smark); | 7989 | setstackmark(&smark); |
8066 | argbackq = pattern->narg.backquote; | 7990 | argbackq = pattern->narg.backquote; |
8067 | STARTSTACKSTR(expdest); | 7991 | STARTSTACKSTR(expdest); |
8068 | argstr(pattern->narg.text, EXP_TILDE | EXP_CASE, | 7992 | argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); |
8069 | /* var_str_list: */ NULL); | ||
8070 | STACKSTRNUL(expdest); | 7993 | STACKSTRNUL(expdest); |
8071 | ifsfree(); | 7994 | ifsfree(); |
8072 | result = patmatch(stackblock(), val); | 7995 | result = patmatch(stackblock(), val); |
@@ -8144,7 +8067,7 @@ static int builtinloc = -1; /* index in path of %builtin, or -1 */ | |||
8144 | 8067 | ||
8145 | 8068 | ||
8146 | static void | 8069 | static void |
8147 | tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp) | 8070 | tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, char **envp) |
8148 | { | 8071 | { |
8149 | #if ENABLE_FEATURE_SH_STANDALONE | 8072 | #if ENABLE_FEATURE_SH_STANDALONE |
8150 | if (applet_no >= 0) { | 8073 | if (applet_no >= 0) { |
@@ -8152,6 +8075,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char ** | |||
8152 | clearenv(); | 8075 | clearenv(); |
8153 | while (*envp) | 8076 | while (*envp) |
8154 | putenv(*envp++); | 8077 | putenv(*envp++); |
8078 | popredir(/*drop:*/ 1, /*restore:*/ 0); | ||
8155 | run_applet_no_and_exit(applet_no, cmd, argv); | 8079 | run_applet_no_and_exit(applet_no, cmd, argv); |
8156 | } | 8080 | } |
8157 | /* re-exec ourselves with the new arguments */ | 8081 | /* re-exec ourselves with the new arguments */ |
@@ -8169,7 +8093,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char ** | |||
8169 | #else | 8093 | #else |
8170 | execve(cmd, argv, envp); | 8094 | execve(cmd, argv, envp); |
8171 | #endif | 8095 | #endif |
8172 | if (cmd != (char*) bb_busybox_exec_path && errno == ENOEXEC) { | 8096 | if (cmd != bb_busybox_exec_path && errno == ENOEXEC) { |
8173 | /* Run "cmd" as a shell script: | 8097 | /* Run "cmd" as a shell script: |
8174 | * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html | 8098 | * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html |
8175 | * "If the execve() function fails with ENOEXEC, the shell | 8099 | * "If the execve() function fails with ENOEXEC, the shell |
@@ -8186,8 +8110,8 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char ** | |||
8186 | * message and exit code 126. For one, this prevents attempts | 8110 | * message and exit code 126. For one, this prevents attempts |
8187 | * to interpret foreign ELF binaries as shell scripts. | 8111 | * to interpret foreign ELF binaries as shell scripts. |
8188 | */ | 8112 | */ |
8189 | argv[0] = cmd; | 8113 | argv[0] = (char*) cmd; |
8190 | cmd = (char*) bb_busybox_exec_path; | 8114 | cmd = bb_busybox_exec_path; |
8191 | /* NB: this is only possible because all callers of shellexec() | 8115 | /* NB: this is only possible because all callers of shellexec() |
8192 | * ensure that the argv[-1] slot exists! | 8116 | * ensure that the argv[-1] slot exists! |
8193 | */ | 8117 | */ |
@@ -8810,10 +8734,6 @@ static int nodeptrsize; | |||
8810 | static char **nodeptr; | 8734 | static char **nodeptr; |
8811 | #endif | 8735 | #endif |
8812 | 8736 | ||
8813 | /* flags in argument to evaltree */ | ||
8814 | #define EV_EXIT 01 /* exit after evaluating tree */ | ||
8815 | #define EV_TESTED 02 /* exit status is checked; ignore -e flag */ | ||
8816 | |||
8817 | static const uint8_t nodesize[N_NUMBER] ALIGN1 = { | 8737 | static const uint8_t nodesize[N_NUMBER] ALIGN1 = { |
8818 | [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)), | 8738 | [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)), |
8819 | [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)), | 8739 | [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)), |
@@ -9336,11 +9256,6 @@ evaltree(union node *n, int flags) | |||
9336 | return exitstatus; | 9256 | return exitstatus; |
9337 | } | 9257 | } |
9338 | 9258 | ||
9339 | #if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3) | ||
9340 | static | ||
9341 | #endif | ||
9342 | int evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__)); | ||
9343 | |||
9344 | static int | 9259 | static int |
9345 | skiploop(void) | 9260 | skiploop(void) |
9346 | { | 9261 | { |
@@ -9699,27 +9614,57 @@ optschanged(void) | |||
9699 | #endif | 9614 | #endif |
9700 | } | 9615 | } |
9701 | 9616 | ||
9702 | static struct localvar *localvars; | 9617 | struct localvar_list { |
9618 | struct localvar_list *next; | ||
9619 | struct localvar *lv; | ||
9620 | }; | ||
9621 | |||
9622 | static struct localvar_list *localvar_stack; | ||
9703 | 9623 | ||
9704 | /* | 9624 | /* |
9705 | * Called after a function returns. | 9625 | * Called after a function returns. |
9706 | * Interrupts must be off. | 9626 | * Interrupts must be off. |
9707 | */ | 9627 | */ |
9708 | static void | 9628 | static void |
9709 | poplocalvars(void) | 9629 | poplocalvars(int keep) |
9710 | { | 9630 | { |
9711 | struct localvar *lvp; | 9631 | struct localvar_list *ll; |
9632 | struct localvar *lvp, *next; | ||
9712 | struct var *vp; | 9633 | struct var *vp; |
9713 | 9634 | ||
9714 | while ((lvp = localvars) != NULL) { | 9635 | INT_OFF; |
9715 | localvars = lvp->next; | 9636 | ll = localvar_stack; |
9637 | localvar_stack = ll->next; | ||
9638 | |||
9639 | next = ll->lv; | ||
9640 | free(ll); | ||
9641 | |||
9642 | while ((lvp = next) != NULL) { | ||
9643 | next = lvp->next; | ||
9716 | vp = lvp->vp; | 9644 | vp = lvp->vp; |
9717 | TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-")); | 9645 | TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-")); |
9718 | if (vp == NULL) { /* $- saved */ | 9646 | if (keep) { |
9647 | int bits = VSTRFIXED; | ||
9648 | |||
9649 | if (lvp->flags != VUNSET) { | ||
9650 | if (vp->var_text == lvp->text) | ||
9651 | bits |= VTEXTFIXED; | ||
9652 | else if (!(lvp->flags & (VTEXTFIXED|VSTACK))) | ||
9653 | free((char*)lvp->text); | ||
9654 | } | ||
9655 | |||
9656 | vp->flags &= ~bits; | ||
9657 | vp->flags |= (lvp->flags & bits); | ||
9658 | |||
9659 | if ((vp->flags & | ||
9660 | (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) == VUNSET) | ||
9661 | unsetvar(vp->var_text); | ||
9662 | } else if (vp == NULL) { /* $- saved */ | ||
9719 | memcpy(optlist, lvp->text, sizeof(optlist)); | 9663 | memcpy(optlist, lvp->text, sizeof(optlist)); |
9720 | free((char*)lvp->text); | 9664 | free((char*)lvp->text); |
9721 | optschanged(); | 9665 | optschanged(); |
9722 | } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { | 9666 | } else if (lvp->flags == VUNSET) { |
9667 | vp->flags &= ~(VSTRFIXED|VREADONLY); | ||
9723 | unsetvar(vp->var_text); | 9668 | unsetvar(vp->var_text); |
9724 | } else { | 9669 | } else { |
9725 | if (vp->var_func) | 9670 | if (vp->var_func) |
@@ -9731,19 +9676,43 @@ poplocalvars(void) | |||
9731 | } | 9676 | } |
9732 | free(lvp); | 9677 | free(lvp); |
9733 | } | 9678 | } |
9679 | INT_ON; | ||
9680 | } | ||
9681 | |||
9682 | /* | ||
9683 | * Create a new localvar environment. | ||
9684 | */ | ||
9685 | static struct localvar_list * | ||
9686 | pushlocalvars(void) | ||
9687 | { | ||
9688 | struct localvar_list *ll; | ||
9689 | |||
9690 | INT_OFF; | ||
9691 | ll = ckzalloc(sizeof(*ll)); | ||
9692 | /*ll->lv = NULL; - zalloc did it */ | ||
9693 | ll->next = localvar_stack; | ||
9694 | localvar_stack = ll; | ||
9695 | INT_ON; | ||
9696 | |||
9697 | return ll->next; | ||
9698 | } | ||
9699 | |||
9700 | static void | ||
9701 | unwindlocalvars(struct localvar_list *stop) | ||
9702 | { | ||
9703 | while (localvar_stack != stop) | ||
9704 | poplocalvars(0); | ||
9734 | } | 9705 | } |
9735 | 9706 | ||
9736 | static int | 9707 | static int |
9737 | evalfun(struct funcnode *func, int argc, char **argv, int flags) | 9708 | evalfun(struct funcnode *func, int argc, char **argv, int flags) |
9738 | { | 9709 | { |
9739 | volatile struct shparam saveparam; | 9710 | volatile struct shparam saveparam; |
9740 | struct localvar *volatile savelocalvars; | ||
9741 | struct jmploc *volatile savehandler; | 9711 | struct jmploc *volatile savehandler; |
9742 | struct jmploc jmploc; | 9712 | struct jmploc jmploc; |
9743 | int e; | 9713 | int e; |
9744 | 9714 | ||
9745 | saveparam = shellparam; | 9715 | saveparam = shellparam; |
9746 | savelocalvars = localvars; | ||
9747 | savehandler = exception_handler; | 9716 | savehandler = exception_handler; |
9748 | e = setjmp(jmploc.loc); | 9717 | e = setjmp(jmploc.loc); |
9749 | if (e) { | 9718 | if (e) { |
@@ -9751,7 +9720,6 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) | |||
9751 | } | 9720 | } |
9752 | INT_OFF; | 9721 | INT_OFF; |
9753 | exception_handler = &jmploc; | 9722 | exception_handler = &jmploc; |
9754 | localvars = NULL; | ||
9755 | shellparam.malloced = 0; | 9723 | shellparam.malloced = 0; |
9756 | func->count++; | 9724 | func->count++; |
9757 | funcnest++; | 9725 | funcnest++; |
@@ -9762,13 +9730,13 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) | |||
9762 | shellparam.optind = 1; | 9730 | shellparam.optind = 1; |
9763 | shellparam.optoff = -1; | 9731 | shellparam.optoff = -1; |
9764 | #endif | 9732 | #endif |
9733 | pushlocalvars(); | ||
9765 | evaltree(func->n.narg.next, flags & EV_TESTED); | 9734 | evaltree(func->n.narg.next, flags & EV_TESTED); |
9735 | poplocalvars(0); | ||
9766 | funcdone: | 9736 | funcdone: |
9767 | INT_OFF; | 9737 | INT_OFF; |
9768 | funcnest--; | 9738 | funcnest--; |
9769 | freefunc(func); | 9739 | freefunc(func); |
9770 | poplocalvars(); | ||
9771 | localvars = savelocalvars; | ||
9772 | freeparam(&shellparam); | 9740 | freeparam(&shellparam); |
9773 | shellparam = saveparam; | 9741 | shellparam = saveparam; |
9774 | exception_handler = savehandler; | 9742 | exception_handler = savehandler; |
@@ -9797,7 +9765,7 @@ mklocal(char *name) | |||
9797 | * x=0; f() { local x=1; echo $x; local x; echo $x; }; f; echo $x | 9765 | * x=0; f() { local x=1; echo $x; local x; echo $x; }; f; echo $x |
9798 | * x=0; f() { local x=1; echo $x; local x=2; echo $x; }; f; echo $x | 9766 | * x=0; f() { local x=1; echo $x; local x=2; echo $x; }; f; echo $x |
9799 | */ | 9767 | */ |
9800 | lvp = localvars; | 9768 | lvp = localvar_stack->lv; |
9801 | while (lvp) { | 9769 | while (lvp) { |
9802 | if (lvp->vp && varcmp(lvp->vp->var_text, name) == 0) { | 9770 | if (lvp->vp && varcmp(lvp->vp->var_text, name) == 0) { |
9803 | if (eq) | 9771 | if (eq) |
@@ -9822,10 +9790,9 @@ mklocal(char *name) | |||
9822 | if (vp == NULL) { | 9790 | if (vp == NULL) { |
9823 | /* variable did not exist yet */ | 9791 | /* variable did not exist yet */ |
9824 | if (eq) | 9792 | if (eq) |
9825 | setvareq(name, VSTRFIXED); | 9793 | vp = setvareq(name, VSTRFIXED); |
9826 | else | 9794 | else |
9827 | setvar(name, NULL, VSTRFIXED); | 9795 | vp = setvar(name, NULL, VSTRFIXED); |
9828 | vp = *vpp; /* the new variable */ | ||
9829 | lvp->flags = VUNSET; | 9796 | lvp->flags = VUNSET; |
9830 | } else { | 9797 | } else { |
9831 | lvp->text = vp->var_text; | 9798 | lvp->text = vp->var_text; |
@@ -9842,8 +9809,8 @@ mklocal(char *name) | |||
9842 | } | 9809 | } |
9843 | } | 9810 | } |
9844 | lvp->vp = vp; | 9811 | lvp->vp = vp; |
9845 | lvp->next = localvars; | 9812 | lvp->next = localvar_stack->lv; |
9846 | localvars = lvp; | 9813 | localvar_stack->lv = lvp; |
9847 | ret: | 9814 | ret: |
9848 | INT_ON; | 9815 | INT_ON; |
9849 | } | 9816 | } |
@@ -9856,7 +9823,7 @@ localcmd(int argc UNUSED_PARAM, char **argv) | |||
9856 | { | 9823 | { |
9857 | char *name; | 9824 | char *name; |
9858 | 9825 | ||
9859 | if (!funcnest) | 9826 | if (!localvar_stack) |
9860 | ash_msg_and_raise_error("not in a function"); | 9827 | ash_msg_and_raise_error("not in a function"); |
9861 | 9828 | ||
9862 | argv = argptr; | 9829 | argv = argptr; |
@@ -10025,7 +9992,7 @@ static const struct builtincmd builtintab[] = { | |||
10025 | #if ENABLE_FEATURE_SH_MATH | 9992 | #if ENABLE_FEATURE_SH_MATH |
10026 | { BUILTIN_NOSPEC "let" , letcmd }, | 9993 | { BUILTIN_NOSPEC "let" , letcmd }, |
10027 | #endif | 9994 | #endif |
10028 | { BUILTIN_ASSIGN "local" , localcmd }, | 9995 | { BUILTIN_SPEC_REG_ASSG "local" , localcmd }, |
10029 | #if ENABLE_ASH_PRINTF | 9996 | #if ENABLE_ASH_PRINTF |
10030 | { BUILTIN_REGULAR "printf" , printfcmd }, | 9997 | { BUILTIN_REGULAR "printf" , printfcmd }, |
10031 | #endif | 9998 | #endif |
@@ -10115,6 +10082,7 @@ evalcommand(union node *cmd, int flags) | |||
10115 | static const struct builtincmd null_bltin = { | 10082 | static const struct builtincmd null_bltin = { |
10116 | "\0\0", bltincmd /* why three NULs? */ | 10083 | "\0\0", bltincmd /* why three NULs? */ |
10117 | }; | 10084 | }; |
10085 | struct localvar_list *localvar_stop; | ||
10118 | struct stackmark smark; | 10086 | struct stackmark smark; |
10119 | union node *argp; | 10087 | union node *argp; |
10120 | struct arglist arglist; | 10088 | struct arglist arglist; |
@@ -10136,6 +10104,7 @@ evalcommand(union node *cmd, int flags) | |||
10136 | /* First expand the arguments. */ | 10104 | /* First expand the arguments. */ |
10137 | TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); | 10105 | TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); |
10138 | setstackmark(&smark); | 10106 | setstackmark(&smark); |
10107 | localvar_stop = pushlocalvars(); | ||
10139 | back_exitstatus = 0; | 10108 | back_exitstatus = 0; |
10140 | 10109 | ||
10141 | cmdentry.cmdtype = CMDBUILTIN; | 10110 | cmdentry.cmdtype = CMDBUILTIN; |
@@ -10189,6 +10158,8 @@ evalcommand(union node *cmd, int flags) | |||
10189 | spp = varlist.lastp; | 10158 | spp = varlist.lastp; |
10190 | expandarg(argp, &varlist, EXP_VARTILDE); | 10159 | expandarg(argp, &varlist, EXP_VARTILDE); |
10191 | 10160 | ||
10161 | mklocal((*spp)->text); | ||
10162 | |||
10192 | /* | 10163 | /* |
10193 | * Modify the command lookup path, if a PATH= assignment | 10164 | * Modify the command lookup path, if a PATH= assignment |
10194 | * is present | 10165 | * is present |
@@ -10354,17 +10325,12 @@ evalcommand(union node *cmd, int flags) | |||
10354 | /* NOTREACHED */ | 10325 | /* NOTREACHED */ |
10355 | } /* default */ | 10326 | } /* default */ |
10356 | case CMDBUILTIN: | 10327 | case CMDBUILTIN: |
10357 | cmdenviron = varlist.list; | 10328 | if (spclbltin > 0 || argc == 0) { |
10358 | if (cmdenviron) { | 10329 | poplocalvars(1); |
10359 | struct strlist *list = cmdenviron; | 10330 | if (cmd_is_exec && argc > 1) |
10360 | int i = VNOSET; | 10331 | listsetvar(varlist.list, VEXPORT); |
10361 | if (spclbltin > 0 || argc == 0) { | ||
10362 | i = 0; | ||
10363 | if (cmd_is_exec && argc > 1) | ||
10364 | i = VEXPORT; | ||
10365 | } | ||
10366 | listsetvar(list, i); | ||
10367 | } | 10332 | } |
10333 | |||
10368 | /* Tight loop with builtins only: | 10334 | /* Tight loop with builtins only: |
10369 | * "while kill -0 $child; do true; done" | 10335 | * "while kill -0 $child; do true; done" |
10370 | * will never exit even if $child died, unless we do this | 10336 | * will never exit even if $child died, unless we do this |
@@ -10382,7 +10348,7 @@ evalcommand(union node *cmd, int flags) | |||
10382 | goto readstatus; | 10348 | goto readstatus; |
10383 | 10349 | ||
10384 | case CMDFUNCTION: | 10350 | case CMDFUNCTION: |
10385 | listsetvar(varlist.list, 0); | 10351 | poplocalvars(1); |
10386 | /* See above for the rationale */ | 10352 | /* See above for the rationale */ |
10387 | dowait(DOWAIT_NONBLOCK, NULL); | 10353 | dowait(DOWAIT_NONBLOCK, NULL); |
10388 | if (evalfun(cmdentry.u.func, argc, argv, flags)) | 10354 | if (evalfun(cmdentry.u.func, argc, argv, flags)) |
@@ -10395,6 +10361,7 @@ evalcommand(union node *cmd, int flags) | |||
10395 | out: | 10361 | out: |
10396 | if (cmd->ncmd.redirect) | 10362 | if (cmd->ncmd.redirect) |
10397 | popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec); | 10363 | popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec); |
10364 | unwindlocalvars(localvar_stop); | ||
10398 | if (lastarg) { | 10365 | if (lastarg) { |
10399 | /* dsl: I think this is intended to be used to support | 10366 | /* dsl: I think this is intended to be used to support |
10400 | * '_' in 'vi' command mode during line editing... | 10367 | * '_' in 'vi' command mode during line editing... |
@@ -13051,6 +13018,12 @@ expandstr(const char *ps) | |||
13051 | return stackblock(); | 13018 | return stackblock(); |
13052 | } | 13019 | } |
13053 | 13020 | ||
13021 | static inline int | ||
13022 | parser_eof(void) | ||
13023 | { | ||
13024 | return tokpushback && lasttoken == TEOF; | ||
13025 | } | ||
13026 | |||
13054 | /* | 13027 | /* |
13055 | * Execute a command or commands contained in a string. | 13028 | * Execute a command or commands contained in a string. |
13056 | */ | 13029 | */ |
@@ -13086,7 +13059,7 @@ evalstring(char *s, int flags) | |||
13086 | while ((n = parsecmd(0)) != NODE_EOF) { | 13059 | while ((n = parsecmd(0)) != NODE_EOF) { |
13087 | int i; | 13060 | int i; |
13088 | 13061 | ||
13089 | i = evaltree(n, flags); | 13062 | i = evaltree(n, flags & ~(parser_eof() ? 0 : EV_EXIT)); |
13090 | if (n) | 13063 | if (n) |
13091 | status = i; | 13064 | status = i; |
13092 | popstackmark(&smark); | 13065 | popstackmark(&smark); |
@@ -13237,11 +13210,12 @@ dotcmd(int argc_ UNUSED_PARAM, char **argv_ UNUSED_PARAM) | |||
13237 | char *fullname; | 13210 | char *fullname; |
13238 | char **argv; | 13211 | char **argv; |
13239 | char *args_need_save; | 13212 | char *args_need_save; |
13240 | struct strlist *sp; | ||
13241 | volatile struct shparam saveparam; | 13213 | volatile struct shparam saveparam; |
13242 | 13214 | ||
13243 | for (sp = cmdenviron; sp; sp = sp->next) | 13215 | //??? |
13244 | setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED); | 13216 | // struct strlist *sp; |
13217 | // for (sp = cmdenviron; sp; sp = sp->next) | ||
13218 | // setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED); | ||
13245 | 13219 | ||
13246 | nextopt(nullstr); /* handle possible "--" */ | 13220 | nextopt(nullstr); /* handle possible "--" */ |
13247 | argv = argptr; | 13221 | argv = argptr; |
@@ -13588,13 +13562,18 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
13588 | return 0; | 13562 | return 0; |
13589 | } | 13563 | } |
13590 | 13564 | ||
13565 | /* Why the second check? | ||
13566 | * "trap NUM [sig2]..." is the same as "trap - NUM [sig2]..." | ||
13567 | * In this case, NUM is signal no, not an action. | ||
13568 | */ | ||
13591 | action = NULL; | 13569 | action = NULL; |
13592 | if (ap[1]) | 13570 | if (ap[1] && !is_number(ap[0])) |
13593 | action = *ap++; | 13571 | action = *ap++; |
13572 | |||
13594 | exitcode = 0; | 13573 | exitcode = 0; |
13595 | while (*ap) { | 13574 | while (*ap) { |
13596 | signo = get_signum(*ap); | 13575 | signo = get_signum(*ap); |
13597 | if (signo < 0 || signo >= NSIG) { | 13576 | if (signo < 0) { |
13598 | /* Mimic bash message exactly */ | 13577 | /* Mimic bash message exactly */ |
13599 | ash_msg("%s: invalid signal specification", *ap); | 13578 | ash_msg("%s: invalid signal specification", *ap); |
13600 | exitcode = 1; | 13579 | exitcode = 1; |
@@ -13752,7 +13731,6 @@ unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
13752 | char **ap; | 13731 | char **ap; |
13753 | int i; | 13732 | int i; |
13754 | int flag = 0; | 13733 | int flag = 0; |
13755 | int ret = 0; | ||
13756 | 13734 | ||
13757 | while ((i = nextopt("vf")) != 0) { | 13735 | while ((i = nextopt("vf")) != 0) { |
13758 | flag = i; | 13736 | flag = i; |
@@ -13760,15 +13738,13 @@ unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
13760 | 13738 | ||
13761 | for (ap = argptr; *ap; ap++) { | 13739 | for (ap = argptr; *ap; ap++) { |
13762 | if (flag != 'f') { | 13740 | if (flag != 'f') { |
13763 | i = unsetvar(*ap); | 13741 | unsetvar(*ap); |
13764 | ret |= i; | 13742 | continue; |
13765 | if (!(i & 2)) | ||
13766 | continue; | ||
13767 | } | 13743 | } |
13768 | if (flag != 'v') | 13744 | if (flag != 'v') |
13769 | unsetfunc(*ap); | 13745 | unsetfunc(*ap); |
13770 | } | 13746 | } |
13771 | return ret & 1; | 13747 | return 0; |
13772 | } | 13748 | } |
13773 | 13749 | ||
13774 | static const unsigned char timescmd_str[] ALIGN1 = { | 13750 | static const unsigned char timescmd_str[] ALIGN1 = { |
@@ -14250,6 +14226,9 @@ reset(void) | |||
14250 | /* from redir.c: */ | 14226 | /* from redir.c: */ |
14251 | while (redirlist) | 14227 | while (redirlist) |
14252 | popredir(/*drop:*/ 0, /*restore:*/ 0); | 14228 | popredir(/*drop:*/ 0, /*restore:*/ 0); |
14229 | |||
14230 | /* from var.c: */ | ||
14231 | unwindlocalvars(NULL); | ||
14253 | } | 14232 | } |
14254 | 14233 | ||
14255 | #if PROFILE | 14234 | #if PROFILE |
@@ -14386,7 +14365,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv) | |||
14386 | // if (!sflag) g_parsefile->pf_fd = -1; | 14365 | // if (!sflag) g_parsefile->pf_fd = -1; |
14387 | // ^^ not necessary since now we special-case fd 0 | 14366 | // ^^ not necessary since now we special-case fd 0 |
14388 | // in is_hidden_fd() to not be considered "hidden fd" | 14367 | // in is_hidden_fd() to not be considered "hidden fd" |
14389 | evalstring(minusc, 0); | 14368 | evalstring(minusc, sflag ? 0 : EV_EXIT); |
14390 | } | 14369 | } |
14391 | 14370 | ||
14392 | if (sflag || minusc == NULL) { | 14371 | if (sflag || minusc == NULL) { |