diff options
author | Ron Yorston <rmy@pobox.com> | 2016-10-27 09:51:46 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2016-10-27 09:51:46 +0100 |
commit | 6a816cfddc9d0cd82f380cfa4e4e27b5534dd68c (patch) | |
tree | e4d6481e86c17ae39806ef6b55834f0d9bc4112a /shell | |
parent | 8239357d4919047f4f8138857e26ce851f483f93 (diff) | |
parent | 70392331a98d266af539be4b910812fc7b0a72d4 (diff) | |
download | busybox-w32-6a816cfddc9d0cd82f380cfa4e4e27b5534dd68c.tar.gz busybox-w32-6a816cfddc9d0cd82f380cfa4e4e27b5534dd68c.tar.bz2 busybox-w32-6a816cfddc9d0cd82f380cfa4e4e27b5534dd68c.zip |
Merge branch 'busybox' into merge
Diffstat (limited to 'shell')
-rw-r--r-- | shell/ash.c | 278 | ||||
-rw-r--r-- | shell/ash_test/ash-heredoc/heredoc6.right | 2 | ||||
-rwxr-xr-x | shell/ash_test/ash-heredoc/heredoc6.tests | 4 | ||||
-rw-r--r-- | shell/ash_test/ash-heredoc/heredoc7.right | 1 | ||||
-rwxr-xr-x | shell/ash_test/ash-heredoc/heredoc7.tests | 3 | ||||
-rwxr-xr-x | shell/ash_test/ash-signals/return_in_trap1.tests | 4 | ||||
-rw-r--r-- | shell/ash_test/ash-vars/param_expand_len.right | 3 | ||||
-rwxr-xr-x | shell/ash_test/ash-vars/param_expand_len.tests | 7 | ||||
-rw-r--r-- | shell/hush_test/hush-heredoc/heredoc6.right | 2 | ||||
-rwxr-xr-x | shell/hush_test/hush-heredoc/heredoc6.tests | 4 | ||||
-rw-r--r-- | shell/hush_test/hush-heredoc/heredoc7.right | 1 | ||||
-rwxr-xr-x | shell/hush_test/hush-heredoc/heredoc7.tests | 3 | ||||
-rwxr-xr-x | shell/hush_test/hush-signals/return_in_trap1.tests | 4 | ||||
-rw-r--r-- | shell/hush_test/hush-vars/param_expand_len.right | 3 | ||||
-rwxr-xr-x | shell/hush_test/hush-vars/param_expand_len.tests | 7 |
15 files changed, 169 insertions, 157 deletions
diff --git a/shell/ash.c b/shell/ash.c index d2bbde2f7..9781bf65d 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -1561,12 +1561,10 @@ sstrdup(const char *p) | |||
1561 | return memcpy(stalloc(len), p, len); | 1561 | return memcpy(stalloc(len), p, len); |
1562 | } | 1562 | } |
1563 | 1563 | ||
1564 | static void | 1564 | static inline void |
1565 | grabstackblock(size_t len) | 1565 | grabstackblock(size_t len) |
1566 | { | 1566 | { |
1567 | len = SHELL_ALIGN(len); | 1567 | stalloc(len); |
1568 | g_stacknxt += len; | ||
1569 | g_stacknleft -= len; | ||
1570 | } | 1568 | } |
1571 | 1569 | ||
1572 | static void | 1570 | static void |
@@ -2328,32 +2326,6 @@ setvar0(const char *name, const char *val) | |||
2328 | setvar(name, val, 0); | 2326 | setvar(name, val, 0); |
2329 | } | 2327 | } |
2330 | 2328 | ||
2331 | #if ENABLE_ASH_GETOPTS | ||
2332 | /* | ||
2333 | * Safe version of setvar, returns 1 on success 0 on failure. | ||
2334 | */ | ||
2335 | static int | ||
2336 | setvarsafe(const char *name, const char *val, int flags) | ||
2337 | { | ||
2338 | int err; | ||
2339 | volatile int saveint; | ||
2340 | struct jmploc *volatile savehandler = exception_handler; | ||
2341 | struct jmploc jmploc; | ||
2342 | |||
2343 | SAVE_INT(saveint); | ||
2344 | if (setjmp(jmploc.loc)) | ||
2345 | err = 1; | ||
2346 | else { | ||
2347 | exception_handler = &jmploc; | ||
2348 | setvar(name, val, flags); | ||
2349 | err = 0; | ||
2350 | } | ||
2351 | exception_handler = savehandler; | ||
2352 | RESTORE_INT(saveint); | ||
2353 | return err; | ||
2354 | } | ||
2355 | #endif | ||
2356 | |||
2357 | /* | 2329 | /* |
2358 | * Unset the specified variable. | 2330 | * Unset the specified variable. |
2359 | */ | 2331 | */ |
@@ -2810,7 +2782,7 @@ setpwd(const char *val, int setold) | |||
2810 | static void hashcd(void); | 2782 | static void hashcd(void); |
2811 | 2783 | ||
2812 | /* | 2784 | /* |
2813 | * Actually do the chdir. We also call hashcd to let the routines in exec.c | 2785 | * Actually do the chdir. We also call hashcd to let other routines |
2814 | * know that the current directory has changed. | 2786 | * know that the current directory has changed. |
2815 | */ | 2787 | */ |
2816 | static int | 2788 | static int |
@@ -2858,7 +2830,7 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
2858 | if (!dest) | 2830 | if (!dest) |
2859 | dest = nullstr; | 2831 | dest = nullstr; |
2860 | if (is_absolute_path(dest)) | 2832 | if (is_absolute_path(dest)) |
2861 | goto step7; | 2833 | goto step6; |
2862 | if (*dest == '.') { | 2834 | if (*dest == '.') { |
2863 | c = dest[1]; | 2835 | c = dest[1]; |
2864 | dotdot: | 2836 | dotdot: |
@@ -2875,13 +2847,7 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
2875 | if (!*dest) | 2847 | if (!*dest) |
2876 | dest = "."; | 2848 | dest = "."; |
2877 | path = bltinlookup("CDPATH"); | 2849 | path = bltinlookup("CDPATH"); |
2878 | if (!path) { | 2850 | while (path) { |
2879 | step6: | ||
2880 | step7: | ||
2881 | p = dest; | ||
2882 | goto docd; | ||
2883 | } | ||
2884 | do { | ||
2885 | c = *path; | 2851 | c = *path; |
2886 | p = path_advance(&path, dest); | 2852 | p = path_advance(&path, dest); |
2887 | if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { | 2853 | if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { |
@@ -2890,9 +2856,15 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
2890 | docd: | 2856 | docd: |
2891 | if (!docd(p, flags)) | 2857 | if (!docd(p, flags)) |
2892 | goto out; | 2858 | goto out; |
2893 | break; | 2859 | goto err; |
2894 | } | 2860 | } |
2895 | } while (path); | 2861 | } |
2862 | |||
2863 | step6: | ||
2864 | p = dest; | ||
2865 | goto docd; | ||
2866 | |||
2867 | err: | ||
2896 | ash_msg_and_raise_error("can't cd to %s", dest); | 2868 | ash_msg_and_raise_error("can't cd to %s", dest); |
2897 | /* NOTREACHED */ | 2869 | /* NOTREACHED */ |
2898 | out: | 2870 | out: |
@@ -3959,12 +3931,12 @@ setjobctl(int on) | |||
3959 | if (--fd < 0) | 3931 | if (--fd < 0) |
3960 | goto out; | 3932 | goto out; |
3961 | } | 3933 | } |
3934 | /* fd is a tty at this point */ | ||
3962 | fd = fcntl(fd, F_DUPFD, 10); | 3935 | fd = fcntl(fd, F_DUPFD, 10); |
3963 | if (ofd >= 0) | 3936 | if (ofd >= 0) /* if it is "/dev/tty", close. If 0/1/2, dont */ |
3964 | close(ofd); | 3937 | close(ofd); |
3965 | if (fd < 0) | 3938 | if (fd < 0) |
3966 | goto out; | 3939 | goto out; /* F_DUPFD failed */ |
3967 | /* fd is a tty at this point */ | ||
3968 | close_on_exec_on(fd); | 3940 | close_on_exec_on(fd); |
3969 | while (1) { /* while we are in the background */ | 3941 | while (1) { /* while we are in the background */ |
3970 | pgrp = tcgetpgrp(fd); | 3942 | pgrp = tcgetpgrp(fd); |
@@ -5001,6 +4973,7 @@ clear_traps(void) | |||
5001 | static void closescript(void); | 4973 | static void closescript(void); |
5002 | 4974 | ||
5003 | /* Called after fork(), in child */ | 4975 | /* Called after fork(), in child */ |
4976 | /* jp and n are NULL when called by openhere() for heredoc support */ | ||
5004 | static NOINLINE void | 4977 | static NOINLINE void |
5005 | forkchild(struct job *jp, union node *n, int mode) | 4978 | forkchild(struct job *jp, union node *n, int mode) |
5006 | { | 4979 | { |
@@ -5140,6 +5113,7 @@ forkparent(struct job *jp, union node *n, int mode, HANDLE proc) | |||
5140 | #endif | 5113 | #endif |
5141 | TRACE(("In parent shell: child = %d\n", pid)); | 5114 | TRACE(("In parent shell: child = %d\n", pid)); |
5142 | if (!jp && !ENABLE_PLATFORM_MINGW32) { /* FIXME not quite understand this */ | 5115 | if (!jp && !ENABLE_PLATFORM_MINGW32) { /* FIXME not quite understand this */ |
5116 | /* jp is NULL when called by openhere() for heredoc support */ | ||
5143 | while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0) | 5117 | while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0) |
5144 | continue; | 5118 | continue; |
5145 | jobless++; | 5119 | jobless++; |
@@ -5177,6 +5151,7 @@ forkparent(struct job *jp, union node *n, int mode, HANDLE proc) | |||
5177 | } | 5151 | } |
5178 | 5152 | ||
5179 | #if !ENABLE_PLATFORM_MINGW32 | 5153 | #if !ENABLE_PLATFORM_MINGW32 |
5154 | /* jp and n are NULL when called by openhere() for heredoc support */ | ||
5180 | static int | 5155 | static int |
5181 | forkshell(struct job *jp, union node *n, int mode) | 5156 | forkshell(struct job *jp, union node *n, int mode) |
5182 | { | 5157 | { |
@@ -5307,8 +5282,7 @@ stoppedjobs(void) | |||
5307 | } | 5282 | } |
5308 | 5283 | ||
5309 | 5284 | ||
5310 | /* ============ redir.c | 5285 | /* |
5311 | * | ||
5312 | * Code for dealing with input/output redirection. | 5286 | * Code for dealing with input/output redirection. |
5313 | */ | 5287 | */ |
5314 | 5288 | ||
@@ -5527,26 +5501,31 @@ openredirect(union node *redir) | |||
5527 | } | 5501 | } |
5528 | 5502 | ||
5529 | /* | 5503 | /* |
5530 | * Copy a file descriptor to be >= to. Throws exception on error. | 5504 | * Copy a file descriptor to be >= 10. Throws exception on error. |
5531 | */ | 5505 | */ |
5532 | /* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD). | ||
5533 | * old code was doing close(to) prior to copyfd() to achieve the same */ | ||
5534 | enum { | ||
5535 | COPYFD_EXACT = (int)~(INT_MAX), | ||
5536 | COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1), | ||
5537 | }; | ||
5538 | static int | 5506 | static int |
5539 | copyfd(int from, int to) | 5507 | savefd(int from) |
5540 | { | 5508 | { |
5541 | int newfd; | 5509 | int newfd; |
5510 | int err; | ||
5542 | 5511 | ||
5543 | if (to & COPYFD_EXACT) { | 5512 | newfd = fcntl(from, F_DUPFD, 10); |
5544 | to &= ~COPYFD_EXACT; | 5513 | err = newfd < 0 ? errno : 0; |
5545 | /*if (from != to)*/ | 5514 | if (err != EBADF) { |
5546 | newfd = dup2(from, to); | 5515 | if (err) |
5547 | } else { | 5516 | ash_msg_and_raise_error("%d: %m", from); |
5548 | newfd = fcntl(from, F_DUPFD, to); | 5517 | close(from); |
5518 | fcntl(newfd, F_SETFD, FD_CLOEXEC); | ||
5549 | } | 5519 | } |
5520 | |||
5521 | return newfd; | ||
5522 | } | ||
5523 | static int | ||
5524 | dup2_or_raise(int from, int to) | ||
5525 | { | ||
5526 | int newfd; | ||
5527 | |||
5528 | newfd = (from != to) ? dup2(from, to) : to; | ||
5550 | if (newfd < 0) { | 5529 | if (newfd < 0) { |
5551 | /* Happens when source fd is not open: try "echo >&99" */ | 5530 | /* Happens when source fd is not open: try "echo >&99" */ |
5552 | ash_msg_and_raise_error("%d: %m", from); | 5531 | ash_msg_and_raise_error("%d: %m", from); |
@@ -5564,6 +5543,9 @@ struct redirtab { | |||
5564 | struct two_fd_t two_fd[]; | 5543 | struct two_fd_t two_fd[]; |
5565 | }; | 5544 | }; |
5566 | #define redirlist (G_var.redirlist) | 5545 | #define redirlist (G_var.redirlist) |
5546 | enum { | ||
5547 | COPYFD_RESTORE = (int)~(INT_MAX), | ||
5548 | }; | ||
5567 | 5549 | ||
5568 | static int | 5550 | static int |
5569 | need_to_remember(struct redirtab *rp, int fd) | 5551 | need_to_remember(struct redirtab *rp, int fd) |
@@ -5737,10 +5719,10 @@ redirect(union node *redir, int flags) | |||
5737 | if (fd != -1) | 5719 | if (fd != -1) |
5738 | close(fd); | 5720 | close(fd); |
5739 | } else { | 5721 | } else { |
5740 | copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT); | 5722 | dup2_or_raise(redir->ndup.dupfd, fd); |
5741 | } | 5723 | } |
5742 | } else if (fd != newfd) { /* move newfd to fd */ | 5724 | } else if (fd != newfd) { /* move newfd to fd */ |
5743 | copyfd(newfd, fd | COPYFD_EXACT); | 5725 | dup2_or_raise(newfd, fd); |
5744 | #if ENABLE_ASH_BASH_COMPAT | 5726 | #if ENABLE_ASH_BASH_COMPAT |
5745 | if (!(redir->nfile.type == NTO2 && fd == 2)) | 5727 | if (!(redir->nfile.type == NTO2 && fd == 2)) |
5746 | #endif | 5728 | #endif |
@@ -5786,7 +5768,7 @@ popredir(int drop, int restore) | |||
5786 | if (!drop || (restore && (copy & COPYFD_RESTORE))) { | 5768 | if (!drop || (restore && (copy & COPYFD_RESTORE))) { |
5787 | copy &= ~COPYFD_RESTORE; | 5769 | copy &= ~COPYFD_RESTORE; |
5788 | /*close(fd);*/ | 5770 | /*close(fd);*/ |
5789 | copyfd(copy, fd | COPYFD_EXACT); | 5771 | dup2_or_raise(copy, fd); |
5790 | } | 5772 | } |
5791 | close(copy & ~COPYFD_RESTORE); | 5773 | close(copy & ~COPYFD_RESTORE); |
5792 | } | 5774 | } |
@@ -5800,16 +5782,6 @@ popredir(int drop, int restore) | |||
5800 | * Undo all redirections. Called on error or interrupt. | 5782 | * Undo all redirections. Called on error or interrupt. |
5801 | */ | 5783 | */ |
5802 | 5784 | ||
5803 | /* | ||
5804 | * Discard all saved file descriptors. | ||
5805 | */ | ||
5806 | static void | ||
5807 | clearredir(int drop) | ||
5808 | { | ||
5809 | while (redirlist) | ||
5810 | popredir(drop, /*restore:*/ 0); | ||
5811 | } | ||
5812 | |||
5813 | static int | 5785 | static int |
5814 | redirectsafe(union node *redir, int flags) | 5786 | redirectsafe(union node *redir, int flags) |
5815 | { | 5787 | { |
@@ -6246,11 +6218,12 @@ evalbackcmd(union node *n, struct backcmd *result) | |||
6246 | ash_msg_and_raise_error("unable to spawn shell"); | 6218 | ash_msg_and_raise_error("unable to spawn shell"); |
6247 | #else | 6219 | #else |
6248 | if (forkshell(jp, n, FORK_NOJOB) == 0) { | 6220 | if (forkshell(jp, n, FORK_NOJOB) == 0) { |
6221 | /* child */ | ||
6249 | FORCE_INT_ON; | 6222 | FORCE_INT_ON; |
6250 | close(pip[0]); | 6223 | close(pip[0]); |
6251 | if (pip[1] != 1) { | 6224 | if (pip[1] != 1) { |
6252 | /*close(1);*/ | 6225 | /*close(1);*/ |
6253 | copyfd(pip[1], 1 | COPYFD_EXACT); | 6226 | dup2_or_raise(pip[1], 1); |
6254 | close(pip[1]); | 6227 | close(pip[1]); |
6255 | } | 6228 | } |
6256 | /* TODO: eflag clearing makes the following not abort: | 6229 | /* TODO: eflag clearing makes the following not abort: |
@@ -6266,6 +6239,7 @@ evalbackcmd(union node *n, struct backcmd *result) | |||
6266 | /* NOTREACHED */ | 6239 | /* NOTREACHED */ |
6267 | } | 6240 | } |
6268 | #endif | 6241 | #endif |
6242 | /* parent */ | ||
6269 | close(pip[1]); | 6243 | close(pip[1]); |
6270 | result->fd = pip[0]; | 6244 | result->fd = pip[0]; |
6271 | result->jp = jp; | 6245 | result->jp = jp; |
@@ -7855,13 +7829,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char ** | |||
7855 | #else | 7829 | #else |
7856 | execve(cmd, argv, envp); | 7830 | execve(cmd, argv, envp); |
7857 | #endif | 7831 | #endif |
7858 | if (cmd == (char*) bb_busybox_exec_path) { | 7832 | if (cmd != (char*) bb_busybox_exec_path && errno == ENOEXEC) { |
7859 | /* We already visited ENOEXEC branch below, don't do it again */ | ||
7860 | //TODO: try execve(initial_argv0_of_shell, argv, envp) before giving up? | ||
7861 | free(argv); | ||
7862 | return; | ||
7863 | } | ||
7864 | if (errno == ENOEXEC) { | ||
7865 | /* Run "cmd" as a shell script: | 7833 | /* Run "cmd" as a shell script: |
7866 | * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html | 7834 | * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html |
7867 | * "If the execve() function fails with ENOEXEC, the shell | 7835 | * "If the execve() function fails with ENOEXEC, the shell |
@@ -7878,19 +7846,13 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char ** | |||
7878 | * message and exit code 126. For one, this prevents attempts | 7846 | * message and exit code 126. For one, this prevents attempts |
7879 | * to interpret foreign ELF binaries as shell scripts. | 7847 | * to interpret foreign ELF binaries as shell scripts. |
7880 | */ | 7848 | */ |
7881 | char **ap; | 7849 | argv[0] = cmd; |
7882 | char **new; | ||
7883 | |||
7884 | for (ap = argv; *ap; ap++) | ||
7885 | continue; | ||
7886 | new = ckmalloc((ap - argv + 2) * sizeof(new[0])); | ||
7887 | new[0] = (char*) "ash"; | ||
7888 | new[1] = cmd; | ||
7889 | ap = new + 2; | ||
7890 | while ((*ap++ = *++argv) != NULL) | ||
7891 | continue; | ||
7892 | cmd = (char*) bb_busybox_exec_path; | 7850 | cmd = (char*) bb_busybox_exec_path; |
7893 | argv = new; | 7851 | /* NB: this is only possible because all callers of shellexec() |
7852 | * ensure that the argv[-1] slot exists! | ||
7853 | */ | ||
7854 | argv--; | ||
7855 | argv[0] = (char*) "ash"; | ||
7894 | goto repeat; | 7856 | goto repeat; |
7895 | } | 7857 | } |
7896 | } | 7858 | } |
@@ -7898,6 +7860,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char ** | |||
7898 | /* | 7860 | /* |
7899 | * Exec a program. Never returns. If you change this routine, you may | 7861 | * Exec a program. Never returns. If you change this routine, you may |
7900 | * have to change the find_command routine as well. | 7862 | * have to change the find_command routine as well. |
7863 | * argv[-1] must exist and be writable! See tryexec() for why. | ||
7901 | */ | 7864 | */ |
7902 | static void shellexec(char **, const char *, int) NORETURN; | 7865 | static void shellexec(char **, const char *, int) NORETURN; |
7903 | static void | 7866 | static void |
@@ -7909,7 +7872,6 @@ shellexec(char **argv, const char *path, int idx) | |||
7909 | int exerrno; | 7872 | int exerrno; |
7910 | int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */ | 7873 | int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */ |
7911 | 7874 | ||
7912 | clearredir(/*drop:*/ 1); | ||
7913 | envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL); | 7875 | envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL); |
7914 | if ((strchr(argv[0], '/') || (ENABLE_PLATFORM_MINGW32 && strchr(argv[0], '\\'))) | 7876 | if ((strchr(argv[0], '/') || (ENABLE_PLATFORM_MINGW32 && strchr(argv[0], '\\'))) |
7915 | #if ENABLE_FEATURE_SH_STANDALONE | 7877 | #if ENABLE_FEATURE_SH_STANDALONE |
@@ -9230,6 +9192,7 @@ evalsubshell(union node *n, int flags) | |||
9230 | evaltreenr(n->nredir.n, flags); | 9192 | evaltreenr(n->nredir.n, flags); |
9231 | /* never returns */ | 9193 | /* never returns */ |
9232 | } | 9194 | } |
9195 | /* parent */ | ||
9233 | status = 0; | 9196 | status = 0; |
9234 | if (!backgnd) | 9197 | if (!backgnd) |
9235 | status = waitforjob(jp); | 9198 | status = waitforjob(jp); |
@@ -9347,6 +9310,7 @@ evalpipe(union node *n, int flags) | |||
9347 | ash_msg_and_raise_error("unable to spawn shell"); | 9310 | ash_msg_and_raise_error("unable to spawn shell"); |
9348 | #else | 9311 | #else |
9349 | if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) { | 9312 | if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) { |
9313 | /* child */ | ||
9350 | INT_ON; | 9314 | INT_ON; |
9351 | if (pip[1] >= 0) { | 9315 | if (pip[1] >= 0) { |
9352 | close(pip[0]); | 9316 | close(pip[0]); |
@@ -9363,6 +9327,7 @@ evalpipe(union node *n, int flags) | |||
9363 | /* never returns */ | 9327 | /* never returns */ |
9364 | } | 9328 | } |
9365 | #endif | 9329 | #endif |
9330 | /* parent */ | ||
9366 | if (prevfd >= 0) | 9331 | if (prevfd >= 0) |
9367 | close(prevfd); | 9332 | close(prevfd); |
9368 | prevfd = pip[0]; | 9333 | prevfd = pip[0]; |
@@ -9884,7 +9849,9 @@ evalcommand(union node *cmd, int flags) | |||
9884 | argc++; | 9849 | argc++; |
9885 | } | 9850 | } |
9886 | 9851 | ||
9887 | argv = nargv = stalloc(sizeof(char *) * (argc + 1)); | 9852 | /* Reserve one extra spot at the front for shellexec. */ |
9853 | nargv = stalloc(sizeof(char *) * (argc + 2)); | ||
9854 | argv = ++nargv; | ||
9888 | for (sp = arglist.list; sp; sp = sp->next) { | 9855 | for (sp = arglist.list; sp; sp = sp->next) { |
9889 | TRACE(("evalcommand arg: %s\n", sp->text)); | 9856 | TRACE(("evalcommand arg: %s\n", sp->text)); |
9890 | *nargv++ = sp->text; | 9857 | *nargv++ = sp->text; |
@@ -10202,8 +10169,7 @@ breakcmd(int argc UNUSED_PARAM, char **argv) | |||
10202 | } | 10169 | } |
10203 | 10170 | ||
10204 | 10171 | ||
10205 | /* ============ input.c | 10172 | /* |
10206 | * | ||
10207 | * This implements the input routines used by the parser. | 10173 | * This implements the input routines used by the parser. |
10208 | */ | 10174 | */ |
10209 | 10175 | ||
@@ -10645,7 +10611,6 @@ closescript(void) | |||
10645 | static void | 10611 | static void |
10646 | setinputfd(int fd, int push) | 10612 | setinputfd(int fd, int push) |
10647 | { | 10613 | { |
10648 | close_on_exec_on(fd); | ||
10649 | if (push) { | 10614 | if (push) { |
10650 | pushfile(); | 10615 | pushfile(); |
10651 | g_parsefile->buf = NULL; | 10616 | g_parsefile->buf = NULL; |
@@ -10666,7 +10631,6 @@ static int | |||
10666 | setinputfile(const char *fname, int flags) | 10631 | setinputfile(const char *fname, int flags) |
10667 | { | 10632 | { |
10668 | int fd; | 10633 | int fd; |
10669 | int fd2; | ||
10670 | 10634 | ||
10671 | INT_OFF; | 10635 | INT_OFF; |
10672 | fd = open(fname, O_RDONLY); | 10636 | fd = open(fname, O_RDONLY); |
@@ -10676,11 +10640,10 @@ setinputfile(const char *fname, int flags) | |||
10676 | exitstatus = 127; | 10640 | exitstatus = 127; |
10677 | ash_msg_and_raise_error("can't open '%s'", fname); | 10641 | ash_msg_and_raise_error("can't open '%s'", fname); |
10678 | } | 10642 | } |
10679 | if (fd < 10) { | 10643 | if (fd < 10) |
10680 | fd2 = copyfd(fd, 10); | 10644 | fd = savefd(fd); |
10681 | close(fd); | 10645 | else |
10682 | fd = fd2; | 10646 | close_on_exec_on(fd); |
10683 | } | ||
10684 | setinputfd(fd, flags & INPUT_PUSH_FILE); | 10647 | setinputfd(fd, flags & INPUT_PUSH_FILE); |
10685 | out: | 10648 | out: |
10686 | INT_ON; | 10649 | INT_ON; |
@@ -10703,8 +10666,7 @@ setinputstring(char *string) | |||
10703 | } | 10666 | } |
10704 | 10667 | ||
10705 | 10668 | ||
10706 | /* ============ mail.c | 10669 | /* |
10707 | * | ||
10708 | * Routines to check for mail. | 10670 | * Routines to check for mail. |
10709 | */ | 10671 | */ |
10710 | 10672 | ||
@@ -11022,23 +10984,25 @@ change_random(const char *value) | |||
11022 | 10984 | ||
11023 | #if ENABLE_ASH_GETOPTS | 10985 | #if ENABLE_ASH_GETOPTS |
11024 | static int | 10986 | static int |
11025 | getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff) | 10987 | getopts(char *optstr, char *optvar, char **optfirst) |
11026 | { | 10988 | { |
11027 | char *p, *q; | 10989 | char *p, *q; |
11028 | char c = '?'; | 10990 | char c = '?'; |
11029 | int done = 0; | 10991 | int done = 0; |
11030 | int err = 0; | ||
11031 | char sbuf[2]; | 10992 | char sbuf[2]; |
11032 | char **optnext; | 10993 | char **optnext; |
10994 | int ind = shellparam.optind; | ||
10995 | int off = shellparam.optoff; | ||
11033 | 10996 | ||
11034 | sbuf[1] = '\0'; | 10997 | sbuf[1] = '\0'; |
11035 | 10998 | ||
11036 | optnext = optfirst + *param_optind - 1; | 10999 | shellparam.optind = -1; |
11000 | optnext = optfirst + ind - 1; | ||
11037 | 11001 | ||
11038 | if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff) | 11002 | if (ind <= 1 || off < 0 || (int)strlen(optnext[-1]) < off) |
11039 | p = NULL; | 11003 | p = NULL; |
11040 | else | 11004 | else |
11041 | p = optnext[-1] + *optoff; | 11005 | p = optnext[-1] + off; |
11042 | if (p == NULL || *p == '\0') { | 11006 | if (p == NULL || *p == '\0') { |
11043 | /* Current word is done, advance */ | 11007 | /* Current word is done, advance */ |
11044 | p = *optnext; | 11008 | p = *optnext; |
@@ -11059,7 +11023,7 @@ getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *opt | |||
11059 | if (optstr[0] == ':') { | 11023 | if (optstr[0] == ':') { |
11060 | sbuf[0] = c; | 11024 | sbuf[0] = c; |
11061 | /*sbuf[1] = '\0'; - already is */ | 11025 | /*sbuf[1] = '\0'; - already is */ |
11062 | err |= setvarsafe("OPTARG", sbuf, 0); | 11026 | setvar0("OPTARG", sbuf); |
11063 | } else { | 11027 | } else { |
11064 | fprintf(stderr, "Illegal option -%c\n", c); | 11028 | fprintf(stderr, "Illegal option -%c\n", c); |
11065 | unsetvar("OPTARG"); | 11029 | unsetvar("OPTARG"); |
@@ -11076,7 +11040,7 @@ getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *opt | |||
11076 | if (optstr[0] == ':') { | 11040 | if (optstr[0] == ':') { |
11077 | sbuf[0] = c; | 11041 | sbuf[0] = c; |
11078 | /*sbuf[1] = '\0'; - already is */ | 11042 | /*sbuf[1] = '\0'; - already is */ |
11079 | err |= setvarsafe("OPTARG", sbuf, 0); | 11043 | setvar0("OPTARG", sbuf); |
11080 | c = ':'; | 11044 | c = ':'; |
11081 | } else { | 11045 | } else { |
11082 | fprintf(stderr, "No arg for -%c option\n", c); | 11046 | fprintf(stderr, "No arg for -%c option\n", c); |
@@ -11088,23 +11052,20 @@ getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *opt | |||
11088 | 11052 | ||
11089 | if (p == *optnext) | 11053 | if (p == *optnext) |
11090 | optnext++; | 11054 | optnext++; |
11091 | err |= setvarsafe("OPTARG", p, 0); | 11055 | setvar0("OPTARG", p); |
11092 | p = NULL; | 11056 | p = NULL; |
11093 | } else | 11057 | } else |
11094 | err |= setvarsafe("OPTARG", nullstr, 0); | 11058 | setvar0("OPTARG", nullstr); |
11095 | out: | 11059 | out: |
11096 | *optoff = p ? p - *(optnext - 1) : -1; | 11060 | ind = optnext - optfirst + 1; |
11097 | *param_optind = optnext - optfirst + 1; | 11061 | setvar("OPTIND", itoa(ind), VNOFUNC); |
11098 | err |= setvarsafe("OPTIND", itoa(*param_optind), VNOFUNC); | ||
11099 | sbuf[0] = c; | 11062 | sbuf[0] = c; |
11100 | /*sbuf[1] = '\0'; - already is */ | 11063 | /*sbuf[1] = '\0'; - already is */ |
11101 | err |= setvarsafe(optvar, sbuf, 0); | 11064 | setvar0(optvar, sbuf); |
11102 | if (err) { | 11065 | |
11103 | *param_optind = 1; | 11066 | shellparam.optoff = p ? p - *(optnext - 1) : -1; |
11104 | *optoff = -1; | 11067 | shellparam.optind = ind; |
11105 | flush_stdout_stderr(); | 11068 | |
11106 | raise_exception(EXERROR); | ||
11107 | } | ||
11108 | return done; | 11069 | return done; |
11109 | } | 11070 | } |
11110 | 11071 | ||
@@ -11123,20 +11084,19 @@ getoptscmd(int argc, char **argv) | |||
11123 | ash_msg_and_raise_error("usage: getopts optstring var [arg]"); | 11084 | ash_msg_and_raise_error("usage: getopts optstring var [arg]"); |
11124 | if (argc == 3) { | 11085 | if (argc == 3) { |
11125 | optbase = shellparam.p; | 11086 | optbase = shellparam.p; |
11126 | if (shellparam.optind > shellparam.nparam + 1) { | 11087 | if ((unsigned)shellparam.optind > shellparam.nparam + 1) { |
11127 | shellparam.optind = 1; | 11088 | shellparam.optind = 1; |
11128 | shellparam.optoff = -1; | 11089 | shellparam.optoff = -1; |
11129 | } | 11090 | } |
11130 | } else { | 11091 | } else { |
11131 | optbase = &argv[3]; | 11092 | optbase = &argv[3]; |
11132 | if (shellparam.optind > argc - 2) { | 11093 | if ((unsigned)shellparam.optind > argc - 2) { |
11133 | shellparam.optind = 1; | 11094 | shellparam.optind = 1; |
11134 | shellparam.optoff = -1; | 11095 | shellparam.optoff = -1; |
11135 | } | 11096 | } |
11136 | } | 11097 | } |
11137 | 11098 | ||
11138 | return getopts(argv[1], argv[2], optbase, &shellparam.optind, | 11099 | return getopts(argv[1], argv[2], optbase); |
11139 | &shellparam.optoff); | ||
11140 | } | 11100 | } |
11141 | #endif /* ASH_GETOPTS */ | 11101 | #endif /* ASH_GETOPTS */ |
11142 | 11102 | ||
@@ -12060,11 +12020,17 @@ checkend: { | |||
12060 | if (c == *eofmark) { | 12020 | if (c == *eofmark) { |
12061 | if (pfgets(line, sizeof(line)) != NULL) { | 12021 | if (pfgets(line, sizeof(line)) != NULL) { |
12062 | char *p, *q; | 12022 | char *p, *q; |
12023 | int cc; | ||
12063 | 12024 | ||
12064 | p = line; | 12025 | p = line; |
12065 | for (q = eofmark + 1; *q && *p == *q; p++, q++) | 12026 | for (q = eofmark + 1;; p++, q++) { |
12066 | continue; | 12027 | cc = *p; |
12067 | if (*p == '\n' && *q == '\0') { | 12028 | if (cc == '\n') |
12029 | cc = 0; | ||
12030 | if (!*q || cc != *q) | ||
12031 | break; | ||
12032 | } | ||
12033 | if (cc == *q) { | ||
12068 | c = PEOF; | 12034 | c = PEOF; |
12069 | nlnoprompt(); | 12035 | nlnoprompt(); |
12070 | } else { | 12036 | } else { |
@@ -12163,7 +12129,6 @@ parseredir: { | |||
12163 | parsesub: { | 12129 | parsesub: { |
12164 | unsigned char subtype; | 12130 | unsigned char subtype; |
12165 | int typeloc; | 12131 | int typeloc; |
12166 | int flags = 0; | ||
12167 | 12132 | ||
12168 | c = pgetc_eatbnl(); | 12133 | c = pgetc_eatbnl(); |
12169 | if (c > 255 /* PEOA or PEOF */ | 12134 | if (c > 255 /* PEOA or PEOF */ |
@@ -12192,26 +12157,19 @@ parsesub: { | |||
12192 | /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */ | 12157 | /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */ |
12193 | USTPUTC(CTLVAR, out); | 12158 | USTPUTC(CTLVAR, out); |
12194 | typeloc = out - (char *)stackblock(); | 12159 | typeloc = out - (char *)stackblock(); |
12195 | USTPUTC(VSNORMAL, out); | 12160 | STADJUST(1, out); |
12196 | subtype = VSNORMAL; | 12161 | subtype = VSNORMAL; |
12197 | if (c == '{') { | 12162 | if (c == '{') { |
12198 | c = pgetc_eatbnl(); | 12163 | c = pgetc_eatbnl(); |
12199 | if (c == '#') { | 12164 | subtype = 0; |
12200 | c = pgetc_eatbnl(); | ||
12201 | if (c == '}') | ||
12202 | c = '#'; /* ${#} - same as $# */ | ||
12203 | else | ||
12204 | subtype = VSLENGTH; /* ${#VAR} */ | ||
12205 | } else { | ||
12206 | subtype = 0; | ||
12207 | } | ||
12208 | } | 12165 | } |
12209 | if (c <= 255 /* not PEOA or PEOF */ && is_name(c)) { | 12166 | varname: |
12167 | if (is_name(c)) { | ||
12210 | /* $[{[#]]NAME[}] */ | 12168 | /* $[{[#]]NAME[}] */ |
12211 | do { | 12169 | do { |
12212 | STPUTC(c, out); | 12170 | STPUTC(c, out); |
12213 | c = pgetc_eatbnl(); | 12171 | c = pgetc_eatbnl(); |
12214 | } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c)); | 12172 | } while (is_in_name(c)); |
12215 | } else if (isdigit(c)) { | 12173 | } else if (isdigit(c)) { |
12216 | /* $[{[#]]NUM[}] */ | 12174 | /* $[{[#]]NUM[}] */ |
12217 | do { | 12175 | do { |
@@ -12220,8 +12178,23 @@ parsesub: { | |||
12220 | } while (isdigit(c)); | 12178 | } while (isdigit(c)); |
12221 | } else if (is_special(c)) { | 12179 | } else if (is_special(c)) { |
12222 | /* $[{[#]]<specialchar>[}] */ | 12180 | /* $[{[#]]<specialchar>[}] */ |
12223 | USTPUTC(c, out); | 12181 | int cc = c; |
12182 | |||
12224 | c = pgetc_eatbnl(); | 12183 | c = pgetc_eatbnl(); |
12184 | if (!subtype && cc == '#') { | ||
12185 | subtype = VSLENGTH; | ||
12186 | if (c == '_' || isalnum(c)) | ||
12187 | goto varname; | ||
12188 | cc = c; | ||
12189 | c = pgetc_eatbnl(); | ||
12190 | if (cc == '}' || c != '}') { | ||
12191 | pungetc(); | ||
12192 | subtype = 0; | ||
12193 | c = cc; | ||
12194 | cc = '#'; | ||
12195 | } | ||
12196 | } | ||
12197 | USTPUTC(cc, out); | ||
12225 | } else { | 12198 | } else { |
12226 | goto badsub; | 12199 | goto badsub; |
12227 | } | 12200 | } |
@@ -12230,7 +12203,6 @@ parsesub: { | |||
12230 | goto badsub; | 12203 | goto badsub; |
12231 | } | 12204 | } |
12232 | 12205 | ||
12233 | flags = 0; | ||
12234 | if (subtype == 0) { | 12206 | if (subtype == 0) { |
12235 | static const char types[] ALIGN1 = "}-+?="; | 12207 | static const char types[] ALIGN1 = "}-+?="; |
12236 | /* ${VAR...} but not $VAR or ${#VAR} */ | 12208 | /* ${VAR...} but not $VAR or ${#VAR} */ |
@@ -12249,13 +12221,13 @@ parsesub: { | |||
12249 | break; /* "goto badsub" is bigger (!) */ | 12221 | break; /* "goto badsub" is bigger (!) */ |
12250 | } | 12222 | } |
12251 | #endif | 12223 | #endif |
12252 | flags = VSNUL; | 12224 | subtype = VSNUL; |
12253 | /*FALLTHROUGH*/ | 12225 | /*FALLTHROUGH*/ |
12254 | default: { | 12226 | default: { |
12255 | const char *p = strchr(types, c); | 12227 | const char *p = strchr(types, c); |
12256 | if (p == NULL) | 12228 | if (p == NULL) |
12257 | break; | 12229 | break; |
12258 | subtype = p - types + VSNORMAL; | 12230 | subtype |= p - types + VSNORMAL; |
12259 | break; | 12231 | break; |
12260 | } | 12232 | } |
12261 | case '%': | 12233 | case '%': |
@@ -12285,12 +12257,11 @@ parsesub: { | |||
12285 | badsub: | 12257 | badsub: |
12286 | pungetc(); | 12258 | pungetc(); |
12287 | } | 12259 | } |
12288 | ((unsigned char *)stackblock())[typeloc] = subtype | flags; | 12260 | ((unsigned char *)stackblock())[typeloc] = subtype; |
12289 | if (subtype != VSNORMAL) { | 12261 | if (subtype != VSNORMAL) { |
12290 | varnest++; | 12262 | varnest++; |
12291 | if (dblquote) { | 12263 | if (dblquote) |
12292 | dqvarnest++; | 12264 | dqvarnest++; |
12293 | } | ||
12294 | } | 12265 | } |
12295 | STPUTC('=', out); | 12266 | STPUTC('=', out); |
12296 | } | 12267 | } |
@@ -13921,7 +13892,8 @@ reset(void) | |||
13921 | tokpushback = 0; | 13892 | tokpushback = 0; |
13922 | checkkwd = 0; | 13893 | checkkwd = 0; |
13923 | /* from redir.c: */ | 13894 | /* from redir.c: */ |
13924 | clearredir(/*drop:*/ 0); | 13895 | while (redirlist) |
13896 | popredir(/*drop:*/ 0, /*restore:*/ 0); | ||
13925 | } | 13897 | } |
13926 | 13898 | ||
13927 | #if PROFILE | 13899 | #if PROFILE |
diff --git a/shell/ash_test/ash-heredoc/heredoc6.right b/shell/ash_test/ash-heredoc/heredoc6.right new file mode 100644 index 000000000..5d0f077cd --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc6.right | |||
@@ -0,0 +1,2 @@ | |||
1 | test | ||
2 | OK:0 | ||
diff --git a/shell/ash_test/ash-heredoc/heredoc6.tests b/shell/ash_test/ash-heredoc/heredoc6.tests new file mode 100755 index 000000000..346f5949a --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc6.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | eval 'cat <<- NOT | ||
2 | test | ||
3 | NOT' | ||
4 | echo OK:$? | ||
diff --git a/shell/ash_test/ash-heredoc/heredoc7.right b/shell/ash_test/ash-heredoc/heredoc7.right new file mode 100644 index 000000000..5d9c6c6c0 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc7.right | |||
@@ -0,0 +1 @@ | |||
_ASBOX | |||
diff --git a/shell/ash_test/ash-heredoc/heredoc7.tests b/shell/ash_test/ash-heredoc/heredoc7.tests new file mode 100755 index 000000000..abd5941d9 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc7.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | cat << _ACEOF | ||
2 | _ASBOX | ||
3 | _ACEOF | ||
diff --git a/shell/ash_test/ash-signals/return_in_trap1.tests b/shell/ash_test/ash-signals/return_in_trap1.tests index f2498024f..4c0d53bde 100755 --- a/shell/ash_test/ash-signals/return_in_trap1.tests +++ b/shell/ash_test/ash-signals/return_in_trap1.tests | |||
@@ -11,8 +11,8 @@ a | |||
11 | 11 | ||
12 | echo d:$? | 12 | echo d:$? |
13 | # It's debatable what is the correct value above. | 13 | # It's debatable what is the correct value above. |
14 | # Does 'return' in trap sees $? == 2 or $? == 3? | 14 | # Does 'return' in trap see $? == 2 or $? == 3? |
15 | # IOW: after (kill..), does shell first wait for its completion | 15 | # IOW: after (kill..), does shell first wait for its completion |
16 | # and sets $?, then checks pending signals and runs a trap handler, | 16 | # and sets $?, then checks pending signals and runs a trap handler, |
17 | # or does it first checks pending signals and runs handler? | 17 | # or does it first check pending signals and runs handler? |
18 | # hush does the former, and prints 3. | 18 | # hush does the former, and prints 3. |
diff --git a/shell/ash_test/ash-vars/param_expand_len.right b/shell/ash_test/ash-vars/param_expand_len.right index 96e8cb59b..48d01d2fe 100644 --- a/shell/ash_test/ash-vars/param_expand_len.right +++ b/shell/ash_test/ash-vars/param_expand_len.right | |||
@@ -7,3 +7,6 @@ Make sure len parsing doesnt break arg count | |||
7 | Testing len op | 7 | Testing len op |
8 | 4 3 2 1 0 0 | 8 | 4 3 2 1 0 0 |
9 | 0 3 0 | 9 | 0 3 0 |
10 | Nothing: | ||
11 | Nothing: | ||
12 | One:1 | ||
diff --git a/shell/ash_test/ash-vars/param_expand_len.tests b/shell/ash_test/ash-vars/param_expand_len.tests index fe20a45e9..369c8d456 100755 --- a/shell/ash_test/ash-vars/param_expand_len.tests +++ b/shell/ash_test/ash-vars/param_expand_len.tests | |||
@@ -15,3 +15,10 @@ unset e | |||
15 | f=abc | 15 | f=abc |
16 | g= | 16 | g= |
17 | echo ${#e} ${#f} ${#g} | 17 | echo ${#e} ${#f} ${#g} |
18 | |||
19 | set -- a | ||
20 | # This must be interpreted as: $# ("1"), then remove trailing "1". | ||
21 | # IOW: empty result. | ||
22 | echo Nothing:${##1} | ||
23 | echo Nothing:${#%1} | ||
24 | echo One:${##x} | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc6.right b/shell/hush_test/hush-heredoc/heredoc6.right new file mode 100644 index 000000000..5d0f077cd --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc6.right | |||
@@ -0,0 +1,2 @@ | |||
1 | test | ||
2 | OK:0 | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc6.tests b/shell/hush_test/hush-heredoc/heredoc6.tests new file mode 100755 index 000000000..346f5949a --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc6.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | eval 'cat <<- NOT | ||
2 | test | ||
3 | NOT' | ||
4 | echo OK:$? | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc7.right b/shell/hush_test/hush-heredoc/heredoc7.right new file mode 100644 index 000000000..5d9c6c6c0 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc7.right | |||
@@ -0,0 +1 @@ | |||
_ASBOX | |||
diff --git a/shell/hush_test/hush-heredoc/heredoc7.tests b/shell/hush_test/hush-heredoc/heredoc7.tests new file mode 100755 index 000000000..abd5941d9 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc7.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | cat << _ACEOF | ||
2 | _ASBOX | ||
3 | _ACEOF | ||
diff --git a/shell/hush_test/hush-signals/return_in_trap1.tests b/shell/hush_test/hush-signals/return_in_trap1.tests index f2498024f..4c0d53bde 100755 --- a/shell/hush_test/hush-signals/return_in_trap1.tests +++ b/shell/hush_test/hush-signals/return_in_trap1.tests | |||
@@ -11,8 +11,8 @@ a | |||
11 | 11 | ||
12 | echo d:$? | 12 | echo d:$? |
13 | # It's debatable what is the correct value above. | 13 | # It's debatable what is the correct value above. |
14 | # Does 'return' in trap sees $? == 2 or $? == 3? | 14 | # Does 'return' in trap see $? == 2 or $? == 3? |
15 | # IOW: after (kill..), does shell first wait for its completion | 15 | # IOW: after (kill..), does shell first wait for its completion |
16 | # and sets $?, then checks pending signals and runs a trap handler, | 16 | # and sets $?, then checks pending signals and runs a trap handler, |
17 | # or does it first checks pending signals and runs handler? | 17 | # or does it first check pending signals and runs handler? |
18 | # hush does the former, and prints 3. | 18 | # hush does the former, and prints 3. |
diff --git a/shell/hush_test/hush-vars/param_expand_len.right b/shell/hush_test/hush-vars/param_expand_len.right index 96e8cb59b..48d01d2fe 100644 --- a/shell/hush_test/hush-vars/param_expand_len.right +++ b/shell/hush_test/hush-vars/param_expand_len.right | |||
@@ -7,3 +7,6 @@ Make sure len parsing doesnt break arg count | |||
7 | Testing len op | 7 | Testing len op |
8 | 4 3 2 1 0 0 | 8 | 4 3 2 1 0 0 |
9 | 0 3 0 | 9 | 0 3 0 |
10 | Nothing: | ||
11 | Nothing: | ||
12 | One:1 | ||
diff --git a/shell/hush_test/hush-vars/param_expand_len.tests b/shell/hush_test/hush-vars/param_expand_len.tests index fe20a45e9..369c8d456 100755 --- a/shell/hush_test/hush-vars/param_expand_len.tests +++ b/shell/hush_test/hush-vars/param_expand_len.tests | |||
@@ -15,3 +15,10 @@ unset e | |||
15 | f=abc | 15 | f=abc |
16 | g= | 16 | g= |
17 | echo ${#e} ${#f} ${#g} | 17 | echo ${#e} ${#f} ${#g} |
18 | |||
19 | set -- a | ||
20 | # This must be interpreted as: $# ("1"), then remove trailing "1". | ||
21 | # IOW: empty result. | ||
22 | echo Nothing:${##1} | ||
23 | echo Nothing:${#%1} | ||
24 | echo One:${##x} | ||