diff options
Diffstat (limited to 'shell')
55 files changed, 1061 insertions, 288 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) { |
diff --git a/shell/ash_test/ash-misc/assignment2.right b/shell/ash_test/ash-misc/assignment2.right new file mode 100644 index 000000000..179c71c5a --- /dev/null +++ b/shell/ash_test/ash-misc/assignment2.right | |||
@@ -0,0 +1,2 @@ | |||
1 | ./assignment2.tests: line 2: a=b: not found | ||
2 | 127 | ||
diff --git a/shell/ash_test/ash-misc/assignment2.tests b/shell/ash_test/ash-misc/assignment2.tests new file mode 100755 index 000000000..f6938434c --- /dev/null +++ b/shell/ash_test/ash-misc/assignment2.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | # This must not be interpreted as an assignment | ||
2 | a''=b true | ||
3 | echo $? | ||
diff --git a/shell/ash_test/ash-misc/empty_args.right b/shell/ash_test/ash-misc/empty_args.right new file mode 100644 index 000000000..968b5a4d9 --- /dev/null +++ b/shell/ash_test/ash-misc/empty_args.right | |||
@@ -0,0 +1,6 @@ | |||
1 | Null 0th arg: | ||
2 | ./empty_args.tests: line 2: : Permission denied | ||
3 | 127 | ||
4 | Null 1st arg: | ||
5 | 0 | ||
6 | Null arg in exec: | ||
diff --git a/shell/ash_test/ash-misc/empty_args.tests b/shell/ash_test/ash-misc/empty_args.tests new file mode 100755 index 000000000..efce5494a --- /dev/null +++ b/shell/ash_test/ash-misc/empty_args.tests | |||
@@ -0,0 +1,9 @@ | |||
1 | echo Null 0th arg: | ||
2 | "" | ||
3 | echo $? | ||
4 | echo Null 1st arg: | ||
5 | # printf without args would print usage info | ||
6 | printf "" | ||
7 | echo $? | ||
8 | echo Null arg in exec: | ||
9 | exec printf "" | ||
diff --git a/shell/ash_test/ash-misc/env_and_func.right b/shell/ash_test/ash-misc/env_and_func.right new file mode 100644 index 000000000..5fc3488ae --- /dev/null +++ b/shell/ash_test/ash-misc/env_and_func.right | |||
@@ -0,0 +1,2 @@ | |||
1 | var=val | ||
2 | var=val | ||
diff --git a/shell/ash_test/ash-misc/env_and_func.tests b/shell/ash_test/ash-misc/env_and_func.tests new file mode 100755 index 000000000..3efef1a41 --- /dev/null +++ b/shell/ash_test/ash-misc/env_and_func.tests | |||
@@ -0,0 +1,8 @@ | |||
1 | var=old | ||
2 | f() { echo "var=$var"; } | ||
3 | # bash: POSIXLY_CORRECT behavior is to "leak" new variable values | ||
4 | # out of function invocations (similar to "special builtins" behavior); | ||
5 | # but in "bash mode", they don't leak. | ||
6 | # hush does not "leak" values. ash does. | ||
7 | var=val f | ||
8 | echo "var=$var" | ||
diff --git a/shell/ash_test/ash-psubst/emptytick.right b/shell/ash_test/ash-psubst/emptytick.right new file mode 100644 index 000000000..7629deba6 --- /dev/null +++ b/shell/ash_test/ash-psubst/emptytick.right | |||
@@ -0,0 +1,17 @@ | |||
1 | 0 | ||
2 | 0 | ||
3 | ./emptytick.tests: line 3: : Permission denied | ||
4 | 127 | ||
5 | ./emptytick.tests: line 4: : Permission denied | ||
6 | 127 | ||
7 | 0 | ||
8 | 0 | ||
9 | 0 | ||
10 | 0 | ||
11 | ./emptytick.tests: line 10: : Permission denied | ||
12 | 127 | ||
13 | ./emptytick.tests: line 11: : Permission denied | ||
14 | 127 | ||
15 | 0 | ||
16 | 0 | ||
17 | ./emptytick.tests: exec: line 15: : Permission denied | ||
diff --git a/shell/ash_test/ash-psubst/emptytick.tests b/shell/ash_test/ash-psubst/emptytick.tests new file mode 100755 index 000000000..eaffafb22 --- /dev/null +++ b/shell/ash_test/ash-psubst/emptytick.tests | |||
@@ -0,0 +1,16 @@ | |||
1 | true; ``; echo $? | ||
2 | false; ``; echo $? | ||
3 | true; `""`; echo $? | ||
4 | false; `""`; echo $? | ||
5 | true; ` `; echo $? | ||
6 | false; ` `; echo $? | ||
7 | |||
8 | true; $(); echo $? | ||
9 | false; $(); echo $? | ||
10 | true; $(""); echo $? | ||
11 | false; $(""); echo $? | ||
12 | true; $( ); echo $? | ||
13 | false; $( ); echo $? | ||
14 | |||
15 | exec ''; echo $? | ||
16 | echo Not reached | ||
diff --git a/shell/ash_test/ash-psubst/tick.right b/shell/ash_test/ash-psubst/tick.right new file mode 100644 index 000000000..6ed281c75 --- /dev/null +++ b/shell/ash_test/ash-psubst/tick.right | |||
@@ -0,0 +1,2 @@ | |||
1 | 1 | ||
2 | 1 | ||
diff --git a/shell/ash_test/ash-psubst/tick.tests b/shell/ash_test/ash-psubst/tick.tests new file mode 100755 index 000000000..1f749a9cd --- /dev/null +++ b/shell/ash_test/ash-psubst/tick.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | true | ||
2 | false; echo `echo $?` | ||
3 | true | ||
4 | { false; echo `echo $?`; } | ||
diff --git a/shell/ash_test/ash-psubst/tick2.right b/shell/ash_test/ash-psubst/tick2.right new file mode 100644 index 000000000..216c883b8 --- /dev/null +++ b/shell/ash_test/ash-psubst/tick2.right | |||
@@ -0,0 +1 @@ | |||
BAZ | |||
diff --git a/shell/ash_test/ash-psubst/tick2.tests b/shell/ash_test/ash-psubst/tick2.tests new file mode 100755 index 000000000..db4e944fe --- /dev/null +++ b/shell/ash_test/ash-psubst/tick2.tests | |||
@@ -0,0 +1,5 @@ | |||
1 | if false; then | ||
2 | echo "FOO" | ||
3 | tmp=`echo BAR >&2` | ||
4 | fi | ||
5 | echo BAZ | ||
diff --git a/shell/ash_test/ash-psubst/tick3.right b/shell/ash_test/ash-psubst/tick3.right new file mode 100644 index 000000000..00f267ae5 --- /dev/null +++ b/shell/ash_test/ash-psubst/tick3.right | |||
@@ -0,0 +1,6 @@ | |||
1 | \TESTZZBEST | ||
2 | $TEST | ||
3 | Q | ||
4 | a\bc | ||
5 | 11-$a-\t-\-\"-`-\--\z-\*-\?-22 33-$a-\t-\-"-`-\--\z-\*-\?-44 | ||
6 | done:0 | ||
diff --git a/shell/ash_test/ash-psubst/tick3.tests b/shell/ash_test/ash-psubst/tick3.tests new file mode 100755 index 000000000..3aeb241c3 --- /dev/null +++ b/shell/ash_test/ash-psubst/tick3.tests | |||
@@ -0,0 +1,14 @@ | |||
1 | test "$CONFIG_FEATURE_FANCY_ECHO" = "y" || exit 77 | ||
2 | |||
3 | TEST=Q | ||
4 | # \` is special | ||
5 | echo `echo '\'TEST\`echo ZZ\`BEST` | ||
6 | # \$ and \\ are special | ||
7 | echo `echo \\$TEST` | ||
8 | echo `echo \$TEST` | ||
9 | echo a`echo \\\\b`c | ||
10 | |||
11 | # \" is not special if in unquoted `cmd` (passed verbatim WITH \), | ||
12 | # but is special in quoted one | ||
13 | echo `echo 11'-$a-\t-\\-\"-\`-\--\z-\*-\?-'22` "`echo 33'-$a-\t-\\-\"-\`-\--\z-\*-\?-'44`" | ||
14 | echo done:$? | ||
diff --git a/shell/ash_test/ash-psubst/tick4.right b/shell/ash_test/ash-psubst/tick4.right new file mode 100644 index 000000000..d8030eafd --- /dev/null +++ b/shell/ash_test/ash-psubst/tick4.right | |||
@@ -0,0 +1,7 @@ | |||
1 | (TEST) BEST | ||
2 | TEST) BEST | ||
3 | ((TEST) BEST | ||
4 | ) | ||
5 | abc | ||
6 | a)c | ||
7 | OK: 0 | ||
diff --git a/shell/ash_test/ash-psubst/tick4.tests b/shell/ash_test/ash-psubst/tick4.tests new file mode 100755 index 000000000..f2305fb3d --- /dev/null +++ b/shell/ash_test/ash-psubst/tick4.tests | |||
@@ -0,0 +1,7 @@ | |||
1 | echo $(echo '(TEST)' BEST) | ||
2 | echo $(echo 'TEST)' BEST) | ||
3 | echo $(echo \(\(TEST\) BEST) | ||
4 | echo $(echo \)) | ||
5 | echo $(echo a"`echo "b"`"c ) | ||
6 | echo $(echo a"`echo ")"`"c ) | ||
7 | echo OK: $? | ||
diff --git a/shell/ash_test/ash-psubst/tick_huge.right b/shell/ash_test/ash-psubst/tick_huge.right new file mode 100644 index 000000000..11740f674 --- /dev/null +++ b/shell/ash_test/ash-psubst/tick_huge.right | |||
@@ -0,0 +1,3 @@ | |||
1 | 546ed3f5c81c780d3ab86ada14824237 - | ||
2 | 546ed3f5c81c780d3ab86ada14824237 - | ||
3 | End | ||
diff --git a/shell/ash_test/ash-psubst/tick_huge.tests b/shell/ash_test/ash-psubst/tick_huge.tests new file mode 100755 index 000000000..acce92fb2 --- /dev/null +++ b/shell/ash_test/ash-psubst/tick_huge.tests | |||
@@ -0,0 +1,7 @@ | |||
1 | # This creates 120k file | ||
2 | yes "123456789 123456789 123456789 123456789" | head -3000 >>"$0.tmp" | ||
3 | |||
4 | echo "`cat $0.tmp`" | md5sum | ||
5 | rm "$0.tmp" | ||
6 | yes "123456789 123456789 123456789 123456789" | head -3000 | md5sum | ||
7 | echo End | ||
diff --git a/shell/ash_test/ash-signals/catch.right b/shell/ash_test/ash-signals/catch.right new file mode 100644 index 000000000..68530c6e7 --- /dev/null +++ b/shell/ash_test/ash-signals/catch.right | |||
@@ -0,0 +1,5 @@ | |||
1 | sending USR2 | ||
2 | caught | ||
3 | sending USR2 | ||
4 | sending USR2 | ||
5 | User defined signal 2 | ||
diff --git a/shell/ash_test/ash-signals/catch.tests b/shell/ash_test/ash-signals/catch.tests new file mode 100755 index 000000000..d2a21d17e --- /dev/null +++ b/shell/ash_test/ash-signals/catch.tests | |||
@@ -0,0 +1,20 @@ | |||
1 | # avoid ugly warnings about signals not being caught | ||
2 | trap ":" USR1 USR2 | ||
3 | |||
4 | "$THIS_SH" -c ' | ||
5 | trap "echo caught" USR2 | ||
6 | echo "sending USR2" | ||
7 | kill -USR2 $$ | ||
8 | |||
9 | trap "" USR2 | ||
10 | echo "sending USR2" | ||
11 | kill -USR2 $$ | ||
12 | |||
13 | trap "-" USR2 | ||
14 | echo "sending USR2" | ||
15 | kill -USR2 $$ | ||
16 | |||
17 | echo "not reached" | ||
18 | ' | ||
19 | |||
20 | trap "-" USR1 USR2 | ||
diff --git a/shell/ash_test/ash-signals/signal_read2.right b/shell/ash_test/ash-signals/signal_read2.right new file mode 100644 index 000000000..87d8da304 --- /dev/null +++ b/shell/ash_test/ash-signals/signal_read2.right | |||
@@ -0,0 +1,2 @@ | |||
1 | Hangup | ||
2 | Done:129 | ||
diff --git a/shell/ash_test/ash-signals/signal_read2.tests b/shell/ash_test/ash-signals/signal_read2.tests new file mode 100755 index 000000000..eab5b9b5b --- /dev/null +++ b/shell/ash_test/ash-signals/signal_read2.tests | |||
@@ -0,0 +1,7 @@ | |||
1 | $THIS_SH -c ' | ||
2 | (sleep 1; kill -HUP $$) & | ||
3 | while true; do | ||
4 | read ignored | ||
5 | done | ||
6 | ' | ||
7 | echo "Done:$?" | ||
diff --git a/shell/ash_test/ash-signals/subshell.right b/shell/ash_test/ash-signals/subshell.right new file mode 100644 index 000000000..248fcc41a --- /dev/null +++ b/shell/ash_test/ash-signals/subshell.right | |||
@@ -0,0 +1,21 @@ | |||
1 | trap -- '' HUP | ||
2 | trap -- '' QUIT | ||
3 | trap -- '' SYS | ||
4 | Ok | ||
5 | trap -- '' HUP | ||
6 | trap -- '' QUIT | ||
7 | trap -- '' SYS | ||
8 | Ok | ||
9 | trap -- '' HUP | ||
10 | trap -- '' QUIT | ||
11 | trap -- '' SYS | ||
12 | Ok | ||
13 | trap -- '' HUP | ||
14 | trap -- '' QUIT | ||
15 | trap -- '' SYS | ||
16 | Ok | ||
17 | trap -- '' HUP | ||
18 | trap -- '' QUIT | ||
19 | trap -- '' SYS | ||
20 | Terminated | ||
21 | Done | ||
diff --git a/shell/ash_test/ash-signals/subshell.tests b/shell/ash_test/ash-signals/subshell.tests new file mode 100755 index 000000000..d877f2b82 --- /dev/null +++ b/shell/ash_test/ash-signals/subshell.tests | |||
@@ -0,0 +1,19 @@ | |||
1 | # Non-empty traps should be reset in subshell | ||
2 | |||
3 | # HUP is special in interactive shells | ||
4 | trap '' HUP | ||
5 | # QUIT is always special | ||
6 | trap '' QUIT | ||
7 | # SYS is not special | ||
8 | trap '' SYS | ||
9 | # WINCH is harmless | ||
10 | trap 'bad: caught WINCH' WINCH | ||
11 | # With TERM we'll check whether it is reset | ||
12 | trap 'bad: caught TERM' TERM | ||
13 | |||
14 | (trap; "$THIS_SH" -c 'kill -HUP $PPID'; echo Ok) | ||
15 | (trap; "$THIS_SH" -c 'kill -QUIT $PPID'; echo Ok) | ||
16 | (trap; "$THIS_SH" -c 'kill -SYS $PPID'; echo Ok) | ||
17 | (trap; "$THIS_SH" -c 'kill -WINCH $PPID'; echo Ok) | ||
18 | (trap; "$THIS_SH" -c 'kill -TERM $PPID'; echo Bad: TERM is not reset) | ||
19 | echo Done | ||
diff --git a/shell/ash_test/ash-vars/param_expand_alt.right b/shell/ash_test/ash-vars/param_expand_alt.right new file mode 100644 index 000000000..1303f8064 --- /dev/null +++ b/shell/ash_test/ash-vars/param_expand_alt.right | |||
@@ -0,0 +1,9 @@ | |||
1 | SHELL: line 1: syntax error: bad substitution | ||
2 | SHELL: line 1: syntax error: bad substitution | ||
3 | __ | ||
4 | _z_ _z_ | ||
5 | _ _ _ _ _ | ||
6 | _aaaa _ _ _word _word | ||
7 | _ _ _ _ _ | ||
8 | _ _ _ _word _ | ||
9 | _fff _ _ _word _word | ||
diff --git a/shell/ash_test/ash-vars/param_expand_alt.tests b/shell/ash_test/ash-vars/param_expand_alt.tests new file mode 100755 index 000000000..23e9a26be --- /dev/null +++ b/shell/ash_test/ash-vars/param_expand_alt.tests | |||
@@ -0,0 +1,33 @@ | |||
1 | # First try some invalid patterns. Do in subshell due to parsing error. | ||
2 | # (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages) | ||
3 | "$THIS_SH" -c 'echo ${+} ; echo moo' SHELL | ||
4 | "$THIS_SH" -c 'echo ${:+} ; echo moo' SHELL | ||
5 | |||
6 | # now some funky ones. | ||
7 | # ${V+word} "if V unset, then substitute nothing, else substitute word" | ||
8 | # ${V:+word} "if V unset or '', then substitute nothing, else substitute word" | ||
9 | # | ||
10 | # ${#:+} is a :+ op on $#, but ${#+} (and any other ${#c}) is "length of $c", | ||
11 | # not + op on $#. | ||
12 | # bash and dash do not accept ${#+}. it's possible for some shell to skip | ||
13 | # the check that c is valid and interpret ${#+} as "len of $+". Not testing it. | ||
14 | # echo _${#+}_ | ||
15 | echo _${#:+}_ | ||
16 | # Forms with non-empty word work as expected in both ash and bash. | ||
17 | echo _${#+z}_ _${#:+z}_ | ||
18 | |||
19 | # now some valid ones | ||
20 | set -- | ||
21 | echo _$1 _${1+} _${1:+} _${1+word} _${1:+word} | ||
22 | |||
23 | set -- aaaa | ||
24 | echo _$1 _${1+} _${1:+} _${1+word} _${1:+word} | ||
25 | |||
26 | unset f | ||
27 | echo _$f _${f+} _${f:+} _${f+word} _${f:+word} | ||
28 | |||
29 | f= | ||
30 | echo _$f _${f+} _${f:+} _${f+word} _${f:+word} | ||
31 | |||
32 | f=fff | ||
33 | echo _$f _${f+} _${f:+} _${f+word} _${f:+word} | ||
diff --git a/shell/ash_test/ash-vars/param_expand_assign.right b/shell/ash_test/ash-vars/param_expand_assign.right new file mode 100644 index 000000000..9b07d8cd4 --- /dev/null +++ b/shell/ash_test/ash-vars/param_expand_assign.right | |||
@@ -0,0 +1,27 @@ | |||
1 | SHELL: line 1: syntax error: bad substitution | ||
2 | SHELL: line 1: syntax error: bad substitution | ||
3 | 0 | ||
4 | 0 | ||
5 | SHELL: line 1: 1: bad variable name | ||
6 | SHELL: line 1: 1: bad variable name | ||
7 | SHELL: line 1: 1: bad variable name | ||
8 | SHELL: line 1: 1: bad variable name | ||
9 | _aa | ||
10 | _aa | ||
11 | _aa | ||
12 | _aa | ||
13 | _ | ||
14 | _ | ||
15 | _ | ||
16 | _word | ||
17 | _word | ||
18 | _ | ||
19 | _ | ||
20 | _ | ||
21 | _ | ||
22 | _word | ||
23 | _fff | ||
24 | _fff | ||
25 | _fff | ||
26 | _fff | ||
27 | _fff | ||
diff --git a/shell/ash_test/ash-vars/param_expand_assign.tests b/shell/ash_test/ash-vars/param_expand_assign.tests new file mode 100755 index 000000000..79de95613 --- /dev/null +++ b/shell/ash_test/ash-vars/param_expand_assign.tests | |||
@@ -0,0 +1,39 @@ | |||
1 | # First try some invalid patterns. Do in subshell due to parsing error. | ||
2 | # (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages) | ||
3 | "$THIS_SH" -c 'echo ${=}' SHELL | ||
4 | "$THIS_SH" -c 'echo ${:=}' SHELL | ||
5 | |||
6 | # now some funky ones | ||
7 | "$THIS_SH" -c 'echo ${#=}' SHELL | ||
8 | "$THIS_SH" -c 'echo ${#:=}' SHELL | ||
9 | |||
10 | # should error out | ||
11 | "$THIS_SH" -c 'set --; echo _${1=}' SHELL | ||
12 | "$THIS_SH" -c 'set --; echo _${1:=}' SHELL | ||
13 | "$THIS_SH" -c 'set --; echo _${1=word}' SHELL | ||
14 | "$THIS_SH" -c 'set --; echo _${1:=word}' SHELL | ||
15 | |||
16 | # should not error | ||
17 | "$THIS_SH" -c 'set aa; echo _${1=}' SHELL | ||
18 | "$THIS_SH" -c 'set aa; echo _${1:=}' SHELL | ||
19 | "$THIS_SH" -c 'set aa; echo _${1=word}' SHELL | ||
20 | "$THIS_SH" -c 'set aa; echo _${1:=word}' SHELL | ||
21 | |||
22 | # should work fine | ||
23 | unset f; echo _$f | ||
24 | unset f; echo _${f=} | ||
25 | unset f; echo _${f:=} | ||
26 | unset f; echo _${f=word} | ||
27 | unset f; echo _${f:=word} | ||
28 | |||
29 | f=; echo _$f | ||
30 | f=; echo _${f=} | ||
31 | f=; echo _${f:=} | ||
32 | f=; echo _${f=word} | ||
33 | f=; echo _${f:=word} | ||
34 | |||
35 | f=fff; echo _$f | ||
36 | f=fff; echo _${f=} | ||
37 | f=fff; echo _${f:=} | ||
38 | f=fff; echo _${f=word} | ||
39 | f=fff; echo _${f:=word} | ||
diff --git a/shell/ash_test/ash-vars/param_expand_bash_substring.right b/shell/ash_test/ash-vars/param_expand_bash_substring.right new file mode 100644 index 000000000..9ad6dbcad --- /dev/null +++ b/shell/ash_test/ash-vars/param_expand_bash_substring.right | |||
@@ -0,0 +1,64 @@ | |||
1 | SHELL: line 1: syntax error: bad substitution | ||
2 | SHELL: line 1: syntax error: bad substitution | ||
3 | SHELL: line 1: syntax error: bad substitution | ||
4 | SHELL: line 1: syntax error: bad substitution | ||
5 | SHELL: line 1: syntax error: missing '}' | ||
6 | 1 =|| | ||
7 | 1:1 =|| | ||
8 | 1:1:2=|| | ||
9 | 1::2 =|| | ||
10 | 1:1: =|| | ||
11 | 1:: =|| | ||
12 | 1 =|0123| | ||
13 | 1:1 =|123| | ||
14 | 1:1:2=|12| | ||
15 | 1::2 =|01| | ||
16 | 1:1: =|| | ||
17 | 1:: =|| | ||
18 | f =|| | ||
19 | f:1 =|| | ||
20 | f:1:2=|| | ||
21 | f::2 =|| | ||
22 | f:1: =|| | ||
23 | f:: =|| | ||
24 | f =|| | ||
25 | f:1 =|| | ||
26 | f:1:2=|| | ||
27 | f::2 =|| | ||
28 | f:1: =|| | ||
29 | f:: =|| | ||
30 | f =|a| | ||
31 | f:1 =|| | ||
32 | f:1:2=|| | ||
33 | f::2 =|a| | ||
34 | f:1: =|| | ||
35 | f:: =|| | ||
36 | f =|0123456789| | ||
37 | f:1 =|123456789| | ||
38 | f:1:2=|12| | ||
39 | f::2 =|01| | ||
40 | f:1: =|| | ||
41 | f:: =|| | ||
42 | Substrings from special vars | ||
43 | ? =|0| | ||
44 | ?:1 =|| | ||
45 | ?:1:2=|| | ||
46 | ?::2 =|0| | ||
47 | ?:1: =|| | ||
48 | ?:: =|| | ||
49 | # =|11| | ||
50 | #:1 =|1| | ||
51 | #:1:2=|1| | ||
52 | #::2 =|11| | ||
53 | #:1: =|| | ||
54 | #:: =|| | ||
55 | Substrings with expressions | ||
56 | f =|01234567| | ||
57 | f:1+1:2+2 =|2345| | ||
58 | f:-1:2+2 =|01234567| | ||
59 | f:1:f =|1234567| | ||
60 | f:1:$f =|1234567| | ||
61 | f:1:${f} =|1234567| | ||
62 | f:1:${f:3:1} =|123| | ||
63 | f:1:1`echo 1`=|1| | ||
64 | Done | ||
diff --git a/shell/ash_test/ash-vars/param_expand_bash_substring.tests b/shell/ash_test/ash-vars/param_expand_bash_substring.tests new file mode 100755 index 000000000..cce9f123e --- /dev/null +++ b/shell/ash_test/ash-vars/param_expand_bash_substring.tests | |||
@@ -0,0 +1,84 @@ | |||
1 | # first try some invalid patterns | ||
2 | # do all of these in subshells since it's supposed to error out | ||
3 | # (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages) | ||
4 | export var=0123456789 | ||
5 | "$THIS_SH" -c 'echo ${:}' SHELL | ||
6 | "$THIS_SH" -c 'echo ${::}' SHELL | ||
7 | "$THIS_SH" -c 'echo ${:1}' SHELL | ||
8 | "$THIS_SH" -c 'echo ${::1}' SHELL | ||
9 | |||
10 | #this also is not valid in bash, hush accepts it: | ||
11 | "$THIS_SH" -c 'echo ${var:}' SHELL | ||
12 | |||
13 | # then some funky ones | ||
14 | # UNFIXED BUG: this should work: "$THIS_SH" -c 'echo ${?:0}' | ||
15 | |||
16 | # now some valid ones | ||
17 | set --; echo "1 =|${1}|" | ||
18 | set --; echo "1:1 =|${1:1}|" | ||
19 | set --; echo "1:1:2=|${1:1:2}|" | ||
20 | set --; echo "1::2 =|${1::2}|" | ||
21 | set --; echo "1:1: =|${1:1:}|" | ||
22 | set --; echo "1:: =|${1::}|" | ||
23 | |||
24 | set -- 0123; echo "1 =|${1}|" | ||
25 | set -- 0123; echo "1:1 =|${1:1}|" | ||
26 | set -- 0123; echo "1:1:2=|${1:1:2}|" | ||
27 | set -- 0123; echo "1::2 =|${1::2}|" | ||
28 | set -- 0123; echo "1:1: =|${1:1:}|" | ||
29 | set -- 0123; echo "1:: =|${1::}|" | ||
30 | |||
31 | unset f; echo "f =|$f|" | ||
32 | unset f; echo "f:1 =|${f:1}|" | ||
33 | unset f; echo "f:1:2=|${f:1:2}|" | ||
34 | unset f; echo "f::2 =|${f::2}|" | ||
35 | unset f; echo "f:1: =|${f:1:}|" | ||
36 | unset f; echo "f:: =|${f::}|" | ||
37 | |||
38 | f=; echo "f =|$f|" | ||
39 | f=; echo "f:1 =|${f:1}|" | ||
40 | f=; echo "f:1:2=|${f:1:2}|" | ||
41 | f=; echo "f::2 =|${f::2}|" | ||
42 | f=; echo "f:1: =|${f:1:}|" | ||
43 | f=; echo "f:: =|${f::}|" | ||
44 | |||
45 | f=a; echo "f =|$f|" | ||
46 | f=a; echo "f:1 =|${f:1}|" | ||
47 | f=a; echo "f:1:2=|${f:1:2}|" | ||
48 | f=a; echo "f::2 =|${f::2}|" | ||
49 | f=a; echo "f:1: =|${f:1:}|" | ||
50 | f=a; echo "f:: =|${f::}|" | ||
51 | |||
52 | f=0123456789; echo "f =|$f|" | ||
53 | f=0123456789; echo "f:1 =|${f:1}|" | ||
54 | f=0123456789; echo "f:1:2=|${f:1:2}|" | ||
55 | f=0123456789; echo "f::2 =|${f::2}|" | ||
56 | f=0123456789; echo "f:1: =|${f:1:}|" | ||
57 | f=0123456789; echo "f:: =|${f::}|" | ||
58 | |||
59 | echo "Substrings from special vars" | ||
60 | echo '? '"=|$?|" | ||
61 | echo '?:1 '"=|${?:1}|" | ||
62 | echo '?:1:2'"=|${?:1:2}|" | ||
63 | echo '?::2 '"=|${?::2}|" | ||
64 | echo '?:1: '"=|${?:1:}|" | ||
65 | echo '?:: '"=|${?::}|" | ||
66 | set -- 1 2 3 4 5 6 7 8 9 10 11 | ||
67 | echo '# '"=|$#|" | ||
68 | echo '#:1 '"=|${#:1}|" | ||
69 | echo '#:1:2'"=|${#:1:2}|" | ||
70 | echo '#::2 '"=|${#::2}|" | ||
71 | echo '#:1: '"=|${#:1:}|" | ||
72 | echo '#:: '"=|${#::}|" | ||
73 | |||
74 | echo "Substrings with expressions" | ||
75 | f=01234567; echo 'f '"=|$f|" | ||
76 | f=01234567; echo 'f:1+1:2+2 '"=|${f:1+1:2+2}|" | ||
77 | f=01234567; echo 'f:-1:2+2 '"=|${f:-1:2+2}|" | ||
78 | f=01234567; echo 'f:1:f '"=|${f:1:f}|" | ||
79 | f=01234567; echo 'f:1:$f '"=|${f:1:$f}|" | ||
80 | f=01234567; echo 'f:1:${f} '"=|${f:1:${f}}|" | ||
81 | f=01234567; echo 'f:1:${f:3:1} '"=|${f:1:${f:3:1}}|" | ||
82 | f=01234567; echo 'f:1:1`echo 1`'"=|${f:1:`echo 1`}|" | ||
83 | |||
84 | echo Done | ||
diff --git a/shell/ash_test/ash-vars/param_expand_default.right b/shell/ash_test/ash-vars/param_expand_default.right new file mode 100644 index 000000000..3eecd1375 --- /dev/null +++ b/shell/ash_test/ash-vars/param_expand_default.right | |||
@@ -0,0 +1,7 @@ | |||
1 | SHELL: line 1: syntax error: bad substitution | ||
2 | _0 _0 | ||
3 | _ _ _ _word _word | ||
4 | _aaaa _aaaa _aaaa _aaaa _aaaa | ||
5 | _ _ _ _word _word | ||
6 | _ _ _ _ _word | ||
7 | _fff _fff _fff _fff _fff | ||
diff --git a/shell/ash_test/ash-vars/param_expand_default.tests b/shell/ash_test/ash-vars/param_expand_default.tests new file mode 100755 index 000000000..5e42d30e3 --- /dev/null +++ b/shell/ash_test/ash-vars/param_expand_default.tests | |||
@@ -0,0 +1,23 @@ | |||
1 | # first try some invalid patterns (do in subshell due to parsing error) | ||
2 | # (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages) | ||
3 | # valid in bash and ash (same as $-): "$THIS_SH" -c 'echo ${-}' SHELL | ||
4 | "$THIS_SH" -c 'echo ${:-}' SHELL | ||
5 | |||
6 | # now some funky ones | ||
7 | echo _${#-} _${#:-} | ||
8 | |||
9 | # now some valid ones | ||
10 | set -- | ||
11 | echo _$1 _${1-} _${1:-} _${1-word} _${1:-word} | ||
12 | |||
13 | set -- aaaa | ||
14 | echo _$1 _${1-} _${1:-} _${1-word} _${1:-word} | ||
15 | |||
16 | unset f | ||
17 | echo _$f _${f-} _${f:-} _${f-word} _${f:-word} | ||
18 | |||
19 | f= | ||
20 | echo _$f _${f-} _${f:-} _${f-word} _${f:-word} | ||
21 | |||
22 | f=fff | ||
23 | echo _$f _${f-} _${f:-} _${f-word} _${f:-word} | ||
diff --git a/shell/ash_test/ash-vars/param_expand_indicate_error.right b/shell/ash_test/ash-vars/param_expand_indicate_error.right new file mode 100644 index 000000000..33afacee0 --- /dev/null +++ b/shell/ash_test/ash-vars/param_expand_indicate_error.right | |||
@@ -0,0 +1,43 @@ | |||
1 | SHELL: line 1: syntax error: bad substitution | ||
2 | 1 | ||
3 | 0 | ||
4 | ==== | ||
5 | _ | ||
6 | SHELL: line 1: 1: parameter not set | ||
7 | SHELL: line 1: 1: parameter not set or null | ||
8 | SHELL: line 1: 1: message1 | ||
9 | SHELL: line 1: 1: message1 | ||
10 | SHELL: line 1: 1: unset! | ||
11 | SHELL: line 1: 1: null or unset! | ||
12 | ==== | ||
13 | _aaaa | ||
14 | _aaaa | ||
15 | _aaaa | ||
16 | _aaaa | ||
17 | _aaaa | ||
18 | _aaaa | ||
19 | _aaaa | ||
20 | ==== | ||
21 | _ | ||
22 | SHELL: line 1: f: parameter not set | ||
23 | SHELL: line 1: f: parameter not set or null | ||
24 | SHELL: line 1: f: message3 | ||
25 | SHELL: line 1: f: message3 | ||
26 | SHELL: line 1: f: unset! | ||
27 | SHELL: line 1: f: null or unset! | ||
28 | ==== | ||
29 | _ | ||
30 | _ | ||
31 | SHELL: line 1: f: parameter not set or null | ||
32 | _ | ||
33 | SHELL: line 1: f: message4 | ||
34 | _ | ||
35 | SHELL: line 1: f: null or unset! | ||
36 | ==== | ||
37 | _fff | ||
38 | _fff | ||
39 | _fff | ||
40 | _fff | ||
41 | _fff | ||
42 | _fff | ||
43 | _fff | ||
diff --git a/shell/ash_test/ash-vars/param_expand_indicate_error.tests b/shell/ash_test/ash-vars/param_expand_indicate_error.tests new file mode 100755 index 000000000..0f3061949 --- /dev/null +++ b/shell/ash_test/ash-vars/param_expand_indicate_error.tests | |||
@@ -0,0 +1,61 @@ | |||
1 | # do all of these in subshells since it's supposed to error out | ||
2 | # (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages) | ||
3 | |||
4 | # first try some invalid patterns | ||
5 | #"$THIS_SH" -c 'echo ${?}' SHELL -- this is valid as it's the same as $? | ||
6 | "$THIS_SH" -c 'echo ${:?}' SHELL | ||
7 | |||
8 | # then some funky ones | ||
9 | # note: bash prints 1 - treats it as "length of $#" | ||
10 | "$THIS_SH" -c 'echo ${#?}' SHELL | ||
11 | # bash prints 0 | ||
12 | "$THIS_SH" -c 'echo ${#:?}' SHELL | ||
13 | |||
14 | # now some valid ones | ||
15 | export msg_unset="unset!" | ||
16 | export msg_null_or_unset="null or unset!" | ||
17 | |||
18 | echo ==== | ||
19 | "$THIS_SH" -c 'set --; echo _$1' SHELL | ||
20 | "$THIS_SH" -c 'set --; echo _${1?}' SHELL | ||
21 | "$THIS_SH" -c 'set --; echo _${1:?}' SHELL | ||
22 | "$THIS_SH" -c 'set --; echo _${1?message1}' SHELL | ||
23 | "$THIS_SH" -c 'set --; echo _${1:?message1}' SHELL | ||
24 | "$THIS_SH" -c 'set --; echo _${1?$msg_unset}' SHELL | ||
25 | "$THIS_SH" -c 'set --; echo _${1:?$msg_null_or_unset}' SHELL | ||
26 | |||
27 | echo ==== | ||
28 | "$THIS_SH" -c 'set -- aaaa; echo _$1' SHELL | ||
29 | "$THIS_SH" -c 'set -- aaaa; echo _${1?}' SHELL | ||
30 | "$THIS_SH" -c 'set -- aaaa; echo _${1:?}' SHELL | ||
31 | "$THIS_SH" -c 'set -- aaaa; echo _${1?word}' SHELL | ||
32 | "$THIS_SH" -c 'set -- aaaa; echo _${1:?word}' SHELL | ||
33 | "$THIS_SH" -c 'set -- aaaa; echo _${1?$msg_unset}' SHELL | ||
34 | "$THIS_SH" -c 'set -- aaaa; echo _${1:?$msg_null_or_unset}' SHELL | ||
35 | |||
36 | echo ==== | ||
37 | "$THIS_SH" -c 'unset f; echo _$f' SHELL | ||
38 | "$THIS_SH" -c 'unset f; echo _${f?}' SHELL | ||
39 | "$THIS_SH" -c 'unset f; echo _${f:?}' SHELL | ||
40 | "$THIS_SH" -c 'unset f; echo _${f?message3}' SHELL | ||
41 | "$THIS_SH" -c 'unset f; echo _${f:?message3}' SHELL | ||
42 | "$THIS_SH" -c 'unset f; echo _${f?$msg_unset}' SHELL | ||
43 | "$THIS_SH" -c 'unset f; echo _${f:?$msg_null_or_unset}' SHELL | ||
44 | |||
45 | echo ==== | ||
46 | "$THIS_SH" -c 'f=; echo _$f' SHELL | ||
47 | "$THIS_SH" -c 'f=; echo _${f?}' SHELL | ||
48 | "$THIS_SH" -c 'f=; echo _${f:?}' SHELL | ||
49 | "$THIS_SH" -c 'f=; echo _${f?word}' SHELL | ||
50 | "$THIS_SH" -c 'f=; echo _${f:?message4}' SHELL | ||
51 | "$THIS_SH" -c 'f=; echo _${f?$msg_unset}' SHELL | ||
52 | "$THIS_SH" -c 'f=; echo _${f:?$msg_null_or_unset}' SHELL | ||
53 | |||
54 | echo ==== | ||
55 | "$THIS_SH" -c 'f=fff; echo _$f' SHELL | ||
56 | "$THIS_SH" -c 'f=fff; echo _${f?}' SHELL | ||
57 | "$THIS_SH" -c 'f=fff; echo _${f:?}' SHELL | ||
58 | "$THIS_SH" -c 'f=fff; echo _${f?word}' SHELL | ||
59 | "$THIS_SH" -c 'f=fff; echo _${f:?word}' SHELL | ||
60 | "$THIS_SH" -c 'f=fff; echo _${f?$msg_unset}' SHELL | ||
61 | "$THIS_SH" -c 'f=fff; echo _${f:?$msg_null_or_unset}' SHELL | ||
diff --git a/shell/ash_test/ash-vars/param_expand_len1.right b/shell/ash_test/ash-vars/param_expand_len1.right new file mode 100644 index 000000000..dff3c7bb1 --- /dev/null +++ b/shell/ash_test/ash-vars/param_expand_len1.right | |||
@@ -0,0 +1,11 @@ | |||
1 | One:1 | ||
2 | Two:2 | ||
3 | Three:3 | ||
4 | |||
5 | One:1 | ||
6 | Two:2 | ||
7 | Three:3 | ||
8 | |||
9 | Ok ${#$}: 0 | ||
10 | |||
11 | Ok ${#!}: 0 | ||
diff --git a/shell/ash_test/ash-vars/param_expand_len1.tests b/shell/ash_test/ash-vars/param_expand_len1.tests new file mode 100755 index 000000000..e1beab320 --- /dev/null +++ b/shell/ash_test/ash-vars/param_expand_len1.tests | |||
@@ -0,0 +1,31 @@ | |||
1 | # ${#c} for any single char c means "length of $c", including all special vars | ||
2 | |||
3 | false | ||
4 | echo One:${#?} | ||
5 | (exit 10) | ||
6 | echo Two:${#?} | ||
7 | (exit 100) | ||
8 | echo Three:${#?} | ||
9 | |||
10 | echo | ||
11 | echo One:${##} | ||
12 | set -- 1 2 3 4 5 6 7 8 9 0 | ||
13 | echo Two:${##} | ||
14 | set -- 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 \ | ||
15 | 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 \ | ||
16 | 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 \ | ||
17 | 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 | ||
18 | echo Three:${##} | ||
19 | |||
20 | echo | ||
21 | v=$$ | ||
22 | test "${#v}" = "${#$}" | ||
23 | echo 'Ok ${#$}:' $? | ||
24 | |||
25 | echo | ||
26 | sleep 0 & | ||
27 | v=$! | ||
28 | test "${#v}" = "${#!}" | ||
29 | echo 'Ok ${#!}:' $? | ||
30 | |||
31 | # TODO: ${#-} ${#_} | ||
diff --git a/shell/ash_test/ash-vars/readonly0.right b/shell/ash_test/ash-vars/readonly0.right index f3a6bde9e..ecc4054f8 100644 --- a/shell/ash_test/ash-vars/readonly0.right +++ b/shell/ash_test/ash-vars/readonly0.right | |||
@@ -10,4 +10,4 @@ Fail:2 | |||
10 | ./readonly0.tests: export: line 27: a: is read only | 10 | ./readonly0.tests: export: line 27: a: is read only |
11 | Fail:2 | 11 | Fail:2 |
12 | 12 | ||
13 | Fail:1 | 13 | ./readonly0.tests: unset: line 44: a: is read only |
diff --git a/shell/ash_test/ash-vars/unset.right b/shell/ash_test/ash-vars/unset.right new file mode 100644 index 000000000..77d5abe9e --- /dev/null +++ b/shell/ash_test/ash-vars/unset.right | |||
@@ -0,0 +1,17 @@ | |||
1 | ./unset.tests: unset: line 3: -: bad variable name | ||
2 | 2 | ||
3 | ./unset.tests: unset: line 5: illegal option -m | ||
4 | 2 | ||
5 | 0 | ||
6 | ___ | ||
7 | 0 f g | ||
8 | 0 g | ||
9 | 0 | ||
10 | ___ | ||
11 | 0 f g | ||
12 | 0 | ||
13 | 0 f g | ||
14 | 0 | ||
15 | ___ | ||
16 | ./unset.tests: unset: line 36: VAR_RO: is read only | ||
17 | 2 f g | ||
diff --git a/shell/ash_test/ash-vars/unset.tests b/shell/ash_test/ash-vars/unset.tests new file mode 100755 index 000000000..11b392744 --- /dev/null +++ b/shell/ash_test/ash-vars/unset.tests | |||
@@ -0,0 +1,40 @@ | |||
1 | # check invalid options are rejected | ||
2 | # bash: in posix mode, aborts if non-interactive; using subshell to avoid that | ||
3 | (unset -) | ||
4 | echo $? | ||
5 | (unset -m a b c) | ||
6 | echo $? | ||
7 | |||
8 | # check funky usage | ||
9 | unset | ||
10 | echo $? | ||
11 | |||
12 | # check normal usage | ||
13 | echo ___ | ||
14 | f=f g=g | ||
15 | echo $? $f $g | ||
16 | unset f | ||
17 | echo $? $f $g | ||
18 | unset g | ||
19 | echo $? $f $g | ||
20 | |||
21 | echo ___ | ||
22 | f=f g=g | ||
23 | echo $? $f $g | ||
24 | unset f g | ||
25 | echo $? $f $g | ||
26 | f=f g=g | ||
27 | echo $? $f $g | ||
28 | unset -v f g | ||
29 | echo $? $f $g | ||
30 | |||
31 | # check read only vars | ||
32 | echo ___ | ||
33 | f=f g=g | ||
34 | VAR_RO=1 | ||
35 | readonly VAR_RO | ||
36 | (unset VAR_RO) | ||
37 | echo $? $f $g | ||
38 | # not testing "do variables survive error halfway through unset" since unset aborts | ||
39 | # unset f VAR_RO g | ||
40 | #echo $? $f $g | ||
diff --git a/shell/hush.c b/shell/hush.c index 309ed2139..d0225edb9 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -4466,6 +4466,8 @@ static int parse_dollar(o_string *as_string, | |||
4466 | case '@': /* args */ | 4466 | case '@': /* args */ |
4467 | goto make_one_char_var; | 4467 | goto make_one_char_var; |
4468 | case '{': { | 4468 | case '{': { |
4469 | char len_single_ch; | ||
4470 | |||
4469 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | 4471 | o_addchr(dest, SPECIAL_VAR_SYMBOL); |
4470 | 4472 | ||
4471 | ch = i_getch(input); /* eat '{' */ | 4473 | ch = i_getch(input); /* eat '{' */ |
@@ -4485,6 +4487,7 @@ static int parse_dollar(o_string *as_string, | |||
4485 | return 0; | 4487 | return 0; |
4486 | } | 4488 | } |
4487 | nommu_addchr(as_string, ch); | 4489 | nommu_addchr(as_string, ch); |
4490 | len_single_ch = ch; | ||
4488 | ch |= quote_mask; | 4491 | ch |= quote_mask; |
4489 | 4492 | ||
4490 | /* It's possible to just call add_till_closing_bracket() at this point. | 4493 | /* It's possible to just call add_till_closing_bracket() at this point. |
@@ -4509,9 +4512,18 @@ static int parse_dollar(o_string *as_string, | |||
4509 | /* handle parameter expansions | 4512 | /* handle parameter expansions |
4510 | * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02 | 4513 | * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02 |
4511 | */ | 4514 | */ |
4512 | if (!strchr(VAR_SUBST_OPS, ch)) /* ${var<bad_char>... */ | 4515 | if (!strchr(VAR_SUBST_OPS, ch)) { /* ${var<bad_char>... */ |
4513 | goto bad_dollar_syntax; | 4516 | if (len_single_ch != '#' |
4514 | 4517 | /*|| !strchr(SPECIAL_VARS_STR, ch) - disallow errors like ${#+} ? */ | |
4518 | || i_peek(input) != '}' | ||
4519 | ) { | ||
4520 | goto bad_dollar_syntax; | ||
4521 | } | ||
4522 | /* else: it's "length of C" ${#C} op, | ||
4523 | * where C is a single char | ||
4524 | * special var name, e.g. ${#!}. | ||
4525 | */ | ||
4526 | } | ||
4515 | /* Eat everything until closing '}' (or ':') */ | 4527 | /* Eat everything until closing '}' (or ':') */ |
4516 | end_ch = '}'; | 4528 | end_ch = '}'; |
4517 | if (BASH_SUBSTR | 4529 | if (BASH_SUBSTR |
@@ -4568,6 +4580,7 @@ static int parse_dollar(o_string *as_string, | |||
4568 | } | 4580 | } |
4569 | break; | 4581 | break; |
4570 | } | 4582 | } |
4583 | len_single_ch = 0; /* it can't be ${#C} op */ | ||
4571 | } | 4584 | } |
4572 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | 4585 | o_addchr(dest, SPECIAL_VAR_SYMBOL); |
4573 | break; | 4586 | break; |
@@ -5559,8 +5572,10 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
5559 | first_char = arg[0] = arg0 & 0x7f; | 5572 | first_char = arg[0] = arg0 & 0x7f; |
5560 | exp_op = 0; | 5573 | exp_op = 0; |
5561 | 5574 | ||
5562 | if (first_char == '#' /* ${#... */ | 5575 | if (first_char == '#' && arg[1] /* ${#...} but not ${#} */ |
5563 | && arg[1] && !exp_saveptr /* not ${#} and not ${#<op_char>...} */ | 5576 | && (!exp_saveptr /* and ( not(${#<op_char>...}) */ |
5577 | || (arg[2] == '\0' && strchr(SPECIAL_VARS_STR, arg[1])) /* or ${#C} "len of $C" ) */ | ||
5578 | ) /* NB: skipping ^^^specvar check mishandles ${#::2} */ | ||
5564 | ) { | 5579 | ) { |
5565 | /* It must be length operator: ${#var} */ | 5580 | /* It must be length operator: ${#var} */ |
5566 | var++; | 5581 | var++; |
@@ -5797,7 +5812,11 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
5797 | /* mimic bash message */ | 5812 | /* mimic bash message */ |
5798 | die_if_script("%s: %s", | 5813 | die_if_script("%s: %s", |
5799 | var, | 5814 | var, |
5800 | exp_word[0] ? exp_word : "parameter null or not set" | 5815 | exp_word[0] |
5816 | ? exp_word | ||
5817 | : "parameter null or not set" | ||
5818 | /* ash has more specific messages, a-la: */ | ||
5819 | /*: (exp_save == ':' ? "parameter null or not set" : "parameter not set")*/ | ||
5801 | ); | 5820 | ); |
5802 | //TODO: how interactive bash aborts expansion mid-command? | 5821 | //TODO: how interactive bash aborts expansion mid-command? |
5803 | } else { | 5822 | } else { |
@@ -6643,8 +6662,18 @@ struct squirrel { | |||
6643 | /* moved_to = -1: fd was opened by redirect; close orig_fd after redir */ | 6662 | /* moved_to = -1: fd was opened by redirect; close orig_fd after redir */ |
6644 | }; | 6663 | }; |
6645 | 6664 | ||
6665 | static struct squirrel *append_squirrel(struct squirrel *sq, int i, int orig, int moved) | ||
6666 | { | ||
6667 | sq = xrealloc(sq, (i + 2) * sizeof(sq[0])); | ||
6668 | sq[i].orig_fd = orig; | ||
6669 | sq[i].moved_to = moved; | ||
6670 | sq[i+1].orig_fd = -1; /* end marker */ | ||
6671 | return sq; | ||
6672 | } | ||
6673 | |||
6646 | static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) | 6674 | static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) |
6647 | { | 6675 | { |
6676 | int moved_to; | ||
6648 | int i = 0; | 6677 | int i = 0; |
6649 | 6678 | ||
6650 | if (sq) while (sq[i].orig_fd >= 0) { | 6679 | if (sq) while (sq[i].orig_fd >= 0) { |
@@ -6664,15 +6693,12 @@ static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) | |||
6664 | i++; | 6693 | i++; |
6665 | } | 6694 | } |
6666 | 6695 | ||
6667 | sq = xrealloc(sq, (i + 2) * sizeof(sq[0])); | ||
6668 | sq[i].orig_fd = fd; | ||
6669 | /* If this fd is open, we move and remember it; if it's closed, moved_to = -1 */ | 6696 | /* If this fd is open, we move and remember it; if it's closed, moved_to = -1 */ |
6670 | sq[i].moved_to = fcntl_F_DUPFD(fd, avoid_fd); | 6697 | moved_to = fcntl_F_DUPFD(fd, avoid_fd); |
6671 | debug_printf_redir("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, sq[i].moved_to); | 6698 | debug_printf_redir("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, moved_to); |
6672 | if (sq[i].moved_to < 0 && errno != EBADF) | 6699 | if (moved_to < 0 && errno != EBADF) |
6673 | xfunc_die(); | 6700 | xfunc_die(); |
6674 | sq[i+1].orig_fd = -1; /* end marker */ | 6701 | return append_squirrel(sq, i, fd, moved_to); |
6675 | return sq; | ||
6676 | } | 6702 | } |
6677 | 6703 | ||
6678 | /* fd: redirect wants this fd to be used (e.g. 3>file). | 6704 | /* fd: redirect wants this fd to be used (e.g. 3>file). |
@@ -6778,6 +6804,19 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) | |||
6778 | */ | 6804 | */ |
6779 | return 1; | 6805 | return 1; |
6780 | } | 6806 | } |
6807 | if (openfd == redir->rd_fd && sqp) { | ||
6808 | /* open() gave us precisely the fd we wanted. | ||
6809 | * This means that this fd was not busy | ||
6810 | * (not opened to anywhere). | ||
6811 | * Remember to close it on restore: | ||
6812 | */ | ||
6813 | struct squirrel *sq = *sqp; | ||
6814 | int i = 0; | ||
6815 | if (sq) while (sq[i].orig_fd >= 0) | ||
6816 | i++; | ||
6817 | *sqp = append_squirrel(sq, i, openfd, -1); /* -1 = "it was closed" */ | ||
6818 | debug_printf_redir("redir to previously closed fd %d\n", openfd); | ||
6819 | } | ||
6781 | } else { | 6820 | } else { |
6782 | /* "rd_fd<*>rd_dup" or "rd_fd<*>-" cases */ | 6821 | /* "rd_fd<*>rd_dup" or "rd_fd<*>-" cases */ |
6783 | openfd = redir->rd_dup; | 6822 | openfd = redir->rd_dup; |
@@ -9719,7 +9758,7 @@ static int FAST_FUNC builtin_trap(char **argv) | |||
9719 | sighandler_t handler; | 9758 | sighandler_t handler; |
9720 | 9759 | ||
9721 | sig = get_signum(*argv++); | 9760 | sig = get_signum(*argv++); |
9722 | if (sig < 0 || sig >= NSIG) { | 9761 | if (sig < 0) { |
9723 | ret = EXIT_FAILURE; | 9762 | ret = EXIT_FAILURE; |
9724 | /* Mimic bash message exactly */ | 9763 | /* Mimic bash message exactly */ |
9725 | bb_error_msg("trap: %s: invalid signal specification", argv[-1]); | 9764 | bb_error_msg("trap: %s: invalid signal specification", argv[-1]); |
diff --git a/shell/hush_test/hush-misc/env_and_func.tests b/shell/hush_test/hush-misc/env_and_func.tests index 1d4eaf3a7..3efef1a41 100755 --- a/shell/hush_test/hush-misc/env_and_func.tests +++ b/shell/hush_test/hush-misc/env_and_func.tests | |||
@@ -1,4 +1,8 @@ | |||
1 | var=old | 1 | var=old |
2 | f() { echo "var=$var"; } | 2 | f() { echo "var=$var"; } |
3 | # bash: POSIXLY_CORRECT behavior is to "leak" new variable values | ||
4 | # out of function invocations (similar to "special builtins" behavior); | ||
5 | # but in "bash mode", they don't leak. | ||
6 | # hush does not "leak" values. ash does. | ||
3 | var=val f | 7 | var=val f |
4 | echo "var=$var" | 8 | echo "var=$var" |
diff --git a/shell/hush_test/hush-redir/redir.right b/shell/hush_test/hush-redir/redir.right new file mode 100644 index 000000000..4de5ec701 --- /dev/null +++ b/shell/hush_test/hush-redir/redir.right | |||
@@ -0,0 +1,2 @@ | |||
1 | hush: write error: Bad file descriptor | ||
2 | TEST | ||
diff --git a/shell/hush_test/hush-redir/redir.tests b/shell/hush_test/hush-redir/redir.tests new file mode 100755 index 000000000..7a1a66806 --- /dev/null +++ b/shell/hush_test/hush-redir/redir.tests | |||
@@ -0,0 +1,6 @@ | |||
1 | # test: closed fds should stay closed | ||
2 | exec 1>&- | ||
3 | echo TEST >TEST | ||
4 | echo JUNK # lost: stdout is closed | ||
5 | cat TEST >&2 | ||
6 | rm TEST | ||
diff --git a/shell/hush_test/hush-vars/param_expand_alt.right b/shell/hush_test/hush-vars/param_expand_alt.right index 67f18d69c..c46786e1f 100644 --- a/shell/hush_test/hush-vars/param_expand_alt.right +++ b/shell/hush_test/hush-vars/param_expand_alt.right | |||
@@ -1,6 +1,7 @@ | |||
1 | hush: syntax error: unterminated ${name} | 1 | hush: syntax error: unterminated ${name} |
2 | hush: syntax error: unterminated ${name} | 2 | hush: syntax error: unterminated ${name} |
3 | __ __ | 3 | __ |
4 | _z_ _z_ | ||
4 | _ _ _ _ _ | 5 | _ _ _ _ _ |
5 | _aaaa _ _ _word _word | 6 | _aaaa _ _ _word _word |
6 | _ _ _ _ _ | 7 | _ _ _ _ _ |
diff --git a/shell/hush_test/hush-vars/param_expand_alt.tests b/shell/hush_test/hush-vars/param_expand_alt.tests index 3b646b142..23e9a26be 100755 --- a/shell/hush_test/hush-vars/param_expand_alt.tests +++ b/shell/hush_test/hush-vars/param_expand_alt.tests | |||
@@ -1,9 +1,20 @@ | |||
1 | # first try some invalid patterns (do in subshell due to parsing error) | 1 | # First try some invalid patterns. Do in subshell due to parsing error. |
2 | "$THIS_SH" -c 'echo ${+} ; echo moo' | 2 | # (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages) |
3 | "$THIS_SH" -c 'echo ${:+} ; echo moo' | 3 | "$THIS_SH" -c 'echo ${+} ; echo moo' SHELL |
4 | "$THIS_SH" -c 'echo ${:+} ; echo moo' SHELL | ||
4 | 5 | ||
5 | # now some funky ones. (bash doesn't accept ${#+}) | 6 | # now some funky ones. |
6 | echo _${#+}_ _${#:+}_ | 7 | # ${V+word} "if V unset, then substitute nothing, else substitute word" |
8 | # ${V:+word} "if V unset or '', then substitute nothing, else substitute word" | ||
9 | # | ||
10 | # ${#:+} is a :+ op on $#, but ${#+} (and any other ${#c}) is "length of $c", | ||
11 | # not + op on $#. | ||
12 | # bash and dash do not accept ${#+}. it's possible for some shell to skip | ||
13 | # the check that c is valid and interpret ${#+} as "len of $+". Not testing it. | ||
14 | # echo _${#+}_ | ||
15 | echo _${#:+}_ | ||
16 | # Forms with non-empty word work as expected in both ash and bash. | ||
17 | echo _${#+z}_ _${#:+z}_ | ||
7 | 18 | ||
8 | # now some valid ones | 19 | # now some valid ones |
9 | set -- | 20 | set -- |
diff --git a/shell/hush_test/hush-vars/param_expand_assign.tests b/shell/hush_test/hush-vars/param_expand_assign.tests index 149cb20df..79de95613 100755 --- a/shell/hush_test/hush-vars/param_expand_assign.tests +++ b/shell/hush_test/hush-vars/param_expand_assign.tests | |||
@@ -1,22 +1,23 @@ | |||
1 | # first try some invalid patterns (do in subshell due to parsing error) | 1 | # First try some invalid patterns. Do in subshell due to parsing error. |
2 | "$THIS_SH" -c 'echo ${=}' | 2 | # (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages) |
3 | "$THIS_SH" -c 'echo ${:=}' | 3 | "$THIS_SH" -c 'echo ${=}' SHELL |
4 | "$THIS_SH" -c 'echo ${:=}' SHELL | ||
4 | 5 | ||
5 | # now some funky ones | 6 | # now some funky ones |
6 | "$THIS_SH" -c 'echo ${#=}' | 7 | "$THIS_SH" -c 'echo ${#=}' SHELL |
7 | "$THIS_SH" -c 'echo ${#:=}' | 8 | "$THIS_SH" -c 'echo ${#:=}' SHELL |
8 | 9 | ||
9 | # should error out | 10 | # should error out |
10 | "$THIS_SH" -c 'set --; echo _${1=}' | 11 | "$THIS_SH" -c 'set --; echo _${1=}' SHELL |
11 | "$THIS_SH" -c 'set --; echo _${1:=}' | 12 | "$THIS_SH" -c 'set --; echo _${1:=}' SHELL |
12 | "$THIS_SH" -c 'set --; echo _${1=word}' | 13 | "$THIS_SH" -c 'set --; echo _${1=word}' SHELL |
13 | "$THIS_SH" -c 'set --; echo _${1:=word}' | 14 | "$THIS_SH" -c 'set --; echo _${1:=word}' SHELL |
14 | 15 | ||
15 | # should not error | 16 | # should not error |
16 | "$THIS_SH" -c 'set aa; echo _${1=}' | 17 | "$THIS_SH" -c 'set aa; echo _${1=}' SHELL |
17 | "$THIS_SH" -c 'set aa; echo _${1:=}' | 18 | "$THIS_SH" -c 'set aa; echo _${1:=}' SHELL |
18 | "$THIS_SH" -c 'set aa; echo _${1=word}' | 19 | "$THIS_SH" -c 'set aa; echo _${1=word}' SHELL |
19 | "$THIS_SH" -c 'set aa; echo _${1:=word}' | 20 | "$THIS_SH" -c 'set aa; echo _${1:=word}' SHELL |
20 | 21 | ||
21 | # should work fine | 22 | # should work fine |
22 | unset f; echo _$f | 23 | unset f; echo _$f |
diff --git a/shell/hush_test/hush-vars/param_expand_bash_substring.tests b/shell/hush_test/hush-vars/param_expand_bash_substring.tests index 5c9552dba..cce9f123e 100755 --- a/shell/hush_test/hush-vars/param_expand_bash_substring.tests +++ b/shell/hush_test/hush-vars/param_expand_bash_substring.tests | |||
@@ -1,13 +1,14 @@ | |||
1 | # first try some invalid patterns | 1 | # first try some invalid patterns |
2 | # do all of these in subshells since it's supposed to error out | 2 | # do all of these in subshells since it's supposed to error out |
3 | # (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages) | ||
3 | export var=0123456789 | 4 | export var=0123456789 |
4 | "$THIS_SH" -c 'echo ${:}' | 5 | "$THIS_SH" -c 'echo ${:}' SHELL |
5 | "$THIS_SH" -c 'echo ${::}' | 6 | "$THIS_SH" -c 'echo ${::}' SHELL |
6 | "$THIS_SH" -c 'echo ${:1}' | 7 | "$THIS_SH" -c 'echo ${:1}' SHELL |
7 | "$THIS_SH" -c 'echo ${::1}' | 8 | "$THIS_SH" -c 'echo ${::1}' SHELL |
8 | 9 | ||
9 | #this also is not valid in bash, but we accept it: | 10 | #this also is not valid in bash, hush accepts it: |
10 | "$THIS_SH" -c 'echo ${var:}' | 11 | "$THIS_SH" -c 'echo ${var:}' SHELL |
11 | 12 | ||
12 | # then some funky ones | 13 | # then some funky ones |
13 | # UNFIXED BUG: this should work: "$THIS_SH" -c 'echo ${?:0}' | 14 | # UNFIXED BUG: this should work: "$THIS_SH" -c 'echo ${?:0}' |
diff --git a/shell/hush_test/hush-vars/param_expand_default.tests b/shell/hush_test/hush-vars/param_expand_default.tests index 1ea051748..16e5f8efe 100755 --- a/shell/hush_test/hush-vars/param_expand_default.tests +++ b/shell/hush_test/hush-vars/param_expand_default.tests | |||
@@ -1,6 +1,8 @@ | |||
1 | # first try some invalid patterns (do in subshell due to parsing error) | 1 | # first try some invalid patterns (do in subshell due to parsing error) |
2 | "$THIS_SH" -c 'echo ${-}' | 2 | # (set argv0 to "SHELL" to avoid "/path/to/shell: blah" in error messages) |
3 | "$THIS_SH" -c 'echo ${:-}' | 3 | # valid in bash and ash (same as $-), not supported in hush (yet?): |
4 | "$THIS_SH" -c 'echo ${-}' SHELL | ||
5 | "$THIS_SH" -c 'echo ${:-}' SHELL | ||
4 | 6 | ||
5 | # now some funky ones | 7 | # now some funky ones |
6 | echo _${#-} _${#:-} | 8 | echo _${#-} _${#:-} |
diff --git a/shell/hush_test/hush-vars/param_expand_indicate_error.right b/shell/hush_test/hush-vars/param_expand_indicate_error.right index 06fcc5104..acf293893 100644 --- a/shell/hush_test/hush-vars/param_expand_indicate_error.right +++ b/shell/hush_test/hush-vars/param_expand_indicate_error.right | |||
@@ -1,5 +1,5 @@ | |||
1 | hush: syntax error: unterminated ${name} | 1 | hush: syntax error: unterminated ${name} |
2 | 0 | 2 | 1 |
3 | 0 | 3 | 0 |
4 | ==== | 4 | ==== |
5 | _ | 5 | _ |
diff --git a/shell/hush_test/hush-vars/param_expand_indicate_error.tests b/shell/hush_test/hush-vars/param_expand_indicate_error.tests index be14b1e37..5f946e39a 100755 --- a/shell/hush_test/hush-vars/param_expand_indicate_error.tests +++ b/shell/hush_test/hush-vars/param_expand_indicate_error.tests | |||
@@ -5,7 +5,7 @@ | |||
5 | "$THIS_SH" -c 'echo ${:?}' | 5 | "$THIS_SH" -c 'echo ${:?}' |
6 | 6 | ||
7 | # then some funky ones | 7 | # then some funky ones |
8 | # note: bash prints 1 - treats it as "length of $#"? We print 0 | 8 | # note: bash prints 1 - treats it as "length of $#" |
9 | "$THIS_SH" -c 'echo ${#?}' | 9 | "$THIS_SH" -c 'echo ${#?}' |
10 | # bash prints 0 | 10 | # bash prints 0 |
11 | "$THIS_SH" -c 'echo ${#:?}' | 11 | "$THIS_SH" -c 'echo ${#:?}' |
diff --git a/shell/hush_test/hush-vars/param_expand_len1.right b/shell/hush_test/hush-vars/param_expand_len1.right new file mode 100644 index 000000000..dff3c7bb1 --- /dev/null +++ b/shell/hush_test/hush-vars/param_expand_len1.right | |||
@@ -0,0 +1,11 @@ | |||
1 | One:1 | ||
2 | Two:2 | ||
3 | Three:3 | ||
4 | |||
5 | One:1 | ||
6 | Two:2 | ||
7 | Three:3 | ||
8 | |||
9 | Ok ${#$}: 0 | ||
10 | |||
11 | Ok ${#!}: 0 | ||
diff --git a/shell/hush_test/hush-vars/param_expand_len1.tests b/shell/hush_test/hush-vars/param_expand_len1.tests new file mode 100755 index 000000000..e1beab320 --- /dev/null +++ b/shell/hush_test/hush-vars/param_expand_len1.tests | |||
@@ -0,0 +1,31 @@ | |||
1 | # ${#c} for any single char c means "length of $c", including all special vars | ||
2 | |||
3 | false | ||
4 | echo One:${#?} | ||
5 | (exit 10) | ||
6 | echo Two:${#?} | ||
7 | (exit 100) | ||
8 | echo Three:${#?} | ||
9 | |||
10 | echo | ||
11 | echo One:${##} | ||
12 | set -- 1 2 3 4 5 6 7 8 9 0 | ||
13 | echo Two:${##} | ||
14 | set -- 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 \ | ||
15 | 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 \ | ||
16 | 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 \ | ||
17 | 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 | ||
18 | echo Three:${##} | ||
19 | |||
20 | echo | ||
21 | v=$$ | ||
22 | test "${#v}" = "${#$}" | ||
23 | echo 'Ok ${#$}:' $? | ||
24 | |||
25 | echo | ||
26 | sleep 0 & | ||
27 | v=$! | ||
28 | test "${#v}" = "${#!}" | ||
29 | echo 'Ok ${#!}:' $? | ||
30 | |||
31 | # TODO: ${#-} ${#_} | ||
diff --git a/shell/hush_test/hush-vars/unset.right b/shell/hush_test/hush-vars/unset.right index 1fbe76a73..097274201 100644 --- a/shell/hush_test/hush-vars/unset.right +++ b/shell/hush_test/hush-vars/unset.right | |||
@@ -12,7 +12,7 @@ ___ | |||
12 | 0 f g | 12 | 0 f g |
13 | 0 | 13 | 0 |
14 | ___ | 14 | ___ |
15 | hush: HUSH_VERSION: readonly variable | 15 | hush: VAR_RO: readonly variable |
16 | 1 f g | 16 | 1 f g |
17 | hush: HUSH_VERSION: readonly variable | 17 | hush: VAR_RO: readonly variable |
18 | 1 | 18 | 1 |
diff --git a/shell/hush_test/hush-vars/unset.tests b/shell/hush_test/hush-vars/unset.tests index f59ce5923..81243fbf9 100755 --- a/shell/hush_test/hush-vars/unset.tests +++ b/shell/hush_test/hush-vars/unset.tests | |||
@@ -1,4 +1,5 @@ | |||
1 | # check invalid options are rejected | 1 | # check invalid options are rejected |
2 | # bash: in posix mode, aborts if non-interactive | ||
2 | unset - | 3 | unset - |
3 | echo $? | 4 | echo $? |
4 | unset -m a b c | 5 | unset -m a b c |
@@ -30,7 +31,9 @@ echo $? $f $g | |||
30 | # check read only vars | 31 | # check read only vars |
31 | echo ___ | 32 | echo ___ |
32 | f=f g=g | 33 | f=f g=g |
33 | unset HUSH_VERSION | 34 | VAR_RO=1 |
35 | readonly VAR_RO | ||
36 | unset VAR_RO | ||
34 | echo $? $f $g | 37 | echo $? $f $g |
35 | unset f HUSH_VERSION g | 38 | unset f VAR_RO g |
36 | echo $? $f $g | 39 | echo $? $f $g |