aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2016-10-27 09:51:46 +0100
committerRon Yorston <rmy@pobox.com>2016-10-27 09:51:46 +0100
commit6a816cfddc9d0cd82f380cfa4e4e27b5534dd68c (patch)
treee4d6481e86c17ae39806ef6b55834f0d9bc4112a /shell
parent8239357d4919047f4f8138857e26ce851f483f93 (diff)
parent70392331a98d266af539be4b910812fc7b0a72d4 (diff)
downloadbusybox-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.c278
-rw-r--r--shell/ash_test/ash-heredoc/heredoc6.right2
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc6.tests4
-rw-r--r--shell/ash_test/ash-heredoc/heredoc7.right1
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc7.tests3
-rwxr-xr-xshell/ash_test/ash-signals/return_in_trap1.tests4
-rw-r--r--shell/ash_test/ash-vars/param_expand_len.right3
-rwxr-xr-xshell/ash_test/ash-vars/param_expand_len.tests7
-rw-r--r--shell/hush_test/hush-heredoc/heredoc6.right2
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc6.tests4
-rw-r--r--shell/hush_test/hush-heredoc/heredoc7.right1
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc7.tests3
-rwxr-xr-xshell/hush_test/hush-signals/return_in_trap1.tests4
-rw-r--r--shell/hush_test/hush-vars/param_expand_len.right3
-rwxr-xr-xshell/hush_test/hush-vars/param_expand_len.tests7
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
1564static void 1564static inline void
1565grabstackblock(size_t len) 1565grabstackblock(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
1572static void 1570static 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 */
2335static int
2336setvarsafe(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)
2810static void hashcd(void); 2782static 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 */
2816static int 2788static 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)
5001static void closescript(void); 4973static 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 */
5004static NOINLINE void 4977static NOINLINE void
5005forkchild(struct job *jp, union node *n, int mode) 4978forkchild(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 */
5180static int 5155static int
5181forkshell(struct job *jp, union node *n, int mode) 5156forkshell(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 */
5534enum {
5535 COPYFD_EXACT = (int)~(INT_MAX),
5536 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
5537};
5538static int 5506static int
5539copyfd(int from, int to) 5507savefd(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}
5523static int
5524dup2_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)
5546enum {
5547 COPYFD_RESTORE = (int)~(INT_MAX),
5548};
5567 5549
5568static int 5550static int
5569need_to_remember(struct redirtab *rp, int fd) 5551need_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 */
5806static void
5807clearredir(int drop)
5808{
5809 while (redirlist)
5810 popredir(drop, /*restore:*/ 0);
5811}
5812
5813static int 5785static int
5814redirectsafe(union node *redir, int flags) 5786redirectsafe(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 */
7902static void shellexec(char **, const char *, int) NORETURN; 7865static void shellexec(char **, const char *, int) NORETURN;
7903static void 7866static 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)
10645static void 10611static void
10646setinputfd(int fd, int push) 10612setinputfd(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
10666setinputfile(const char *fname, int flags) 10631setinputfile(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
11024static int 10986static int
11025getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff) 10987getopts(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: {
12163parsesub: { 12129parsesub: {
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 @@
1test
2OK: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 @@
1eval 'cat <<- NOT
2test
3NOT'
4echo 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 @@
1cat << _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
12echo d:$? 12echo 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
7Testing len op 7Testing len op
84 3 2 1 0 0 84 3 2 1 0 0
90 3 0 90 3 0
10Nothing:
11Nothing:
12One: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
15f=abc 15f=abc
16g= 16g=
17echo ${#e} ${#f} ${#g} 17echo ${#e} ${#f} ${#g}
18
19set -- a
20# This must be interpreted as: $# ("1"), then remove trailing "1".
21# IOW: empty result.
22echo Nothing:${##1}
23echo Nothing:${#%1}
24echo 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 @@
1test
2OK: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 @@
1eval 'cat <<- NOT
2test
3NOT'
4echo 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 @@
1cat << _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
12echo d:$? 12echo 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
7Testing len op 7Testing len op
84 3 2 1 0 0 84 3 2 1 0 0
90 3 0 90 3 0
10Nothing:
11Nothing:
12One: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
15f=abc 15f=abc
16g= 16g=
17echo ${#e} ${#f} ${#g} 17echo ${#e} ${#f} ${#g}
18
19set -- a
20# This must be interpreted as: $# ("1"), then remove trailing "1".
21# IOW: empty result.
22echo Nothing:${##1}
23echo Nothing:${#%1}
24echo One:${##x}