aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
Diffstat (limited to 'shell')
-rw-r--r--shell/ash.c104
-rw-r--r--shell/hush.c147
-rw-r--r--shell/hush_test/hush-misc/return1.right1
-rwxr-xr-xshell/hush_test/hush-misc/return1.tests4
-rw-r--r--shell/hush_test/hush-misc/source1.right5
-rwxr-xr-xshell/hush_test/hush-misc/source1.tests10
6 files changed, 172 insertions, 99 deletions
diff --git a/shell/ash.c b/shell/ash.c
index fd9141661..d12a483a3 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -56,6 +56,9 @@
56#include <fnmatch.h> 56#include <fnmatch.h>
57#include <sys/times.h> 57#include <sys/times.h>
58 58
59#include "busybox.h" /* for applet_names */
60#include "unicode.h"
61
59#include "shell_common.h" 62#include "shell_common.h"
60#if ENABLE_SH_MATH_SUPPORT 63#if ENABLE_SH_MATH_SUPPORT
61# include "math.h" 64# include "math.h"
@@ -86,13 +89,6 @@
86# error "Do not even bother, ash will not run on NOMMU machine" 89# error "Do not even bother, ash will not run on NOMMU machine"
87#endif 90#endif
88 91
89//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
90//applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, sh))
91//applet:IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, bash))
92
93//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
94//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
95
96//config:config ASH 92//config:config ASH
97//config: bool "ash" 93//config: bool "ash"
98//config: default y 94//config: default y
@@ -204,12 +200,12 @@
204//config: variable each time it is displayed. 200//config: variable each time it is displayed.
205//config: 201//config:
206 202
207//usage:#define ash_trivial_usage NOUSAGE_STR 203//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
208//usage:#define ash_full_usage "" 204//applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, sh))
209//usage:#define sh_trivial_usage NOUSAGE_STR 205//applet:IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, bash))
210//usage:#define sh_full_usage "" 206
211//usage:#define bash_trivial_usage NOUSAGE_STR 207//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
212//usage:#define bash_full_usage "" 208//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
213 209
214#if ENABLE_PLATFORM_MINGW32 210#if ENABLE_PLATFORM_MINGW32
215struct forkshell; 211struct forkshell;
@@ -3705,12 +3701,12 @@ set_curjob(struct job *jp, unsigned mode)
3705 3701
3706 /* first remove from list */ 3702 /* first remove from list */
3707 jpp = curp = &curjob; 3703 jpp = curp = &curjob;
3708 do { 3704 while (1) {
3709 jp1 = *jpp; 3705 jp1 = *jpp;
3710 if (jp1 == jp) 3706 if (jp1 == jp)
3711 break; 3707 break;
3712 jpp = &jp1->prev_job; 3708 jpp = &jp1->prev_job;
3713 } while (1); 3709 }
3714 *jpp = jp1->prev_job; 3710 *jpp = jp1->prev_job;
3715 3711
3716 /* Then re-insert in correct position */ 3712 /* Then re-insert in correct position */
@@ -3726,14 +3722,14 @@ set_curjob(struct job *jp, unsigned mode)
3726 case CUR_RUNNING: 3722 case CUR_RUNNING:
3727 /* newly created job or backgrounded job, 3723 /* newly created job or backgrounded job,
3728 put after all stopped jobs. */ 3724 put after all stopped jobs. */
3729 do { 3725 while (1) {
3730 jp1 = *jpp; 3726 jp1 = *jpp;
3731#if JOBS 3727#if JOBS
3732 if (!jp1 || jp1->state != JOBSTOPPED) 3728 if (!jp1 || jp1->state != JOBSTOPPED)
3733#endif 3729#endif
3734 break; 3730 break;
3735 jpp = &jp1->prev_job; 3731 jpp = &jp1->prev_job;
3736 } while (1); 3732 }
3737 /* FALLTHROUGH */ 3733 /* FALLTHROUGH */
3738#if JOBS 3734#if JOBS
3739 case CUR_STOPPED: 3735 case CUR_STOPPED:
@@ -3906,7 +3902,7 @@ setjobctl(int on)
3906 goto out; 3902 goto out;
3907 /* fd is a tty at this point */ 3903 /* fd is a tty at this point */
3908 close_on_exec_on(fd); 3904 close_on_exec_on(fd);
3909 do { /* while we are in the background */ 3905 while (1) { /* while we are in the background */
3910 pgrp = tcgetpgrp(fd); 3906 pgrp = tcgetpgrp(fd);
3911 if (pgrp < 0) { 3907 if (pgrp < 0) {
3912 out: 3908 out:
@@ -3917,7 +3913,7 @@ setjobctl(int on)
3917 if (pgrp == getpgrp()) 3913 if (pgrp == getpgrp())
3918 break; 3914 break;
3919 killpg(0, SIGTTIN); 3915 killpg(0, SIGTTIN);
3920 } while (1); 3916 }
3921 initialpgrp = pgrp; 3917 initialpgrp = pgrp;
3922 3918
3923 setsignal(SIGTSTP); 3919 setsignal(SIGTSTP);
@@ -6328,7 +6324,7 @@ expari(int quotes)
6328 p = expdest - 1; 6324 p = expdest - 1;
6329 *p = '\0'; 6325 *p = '\0';
6330 p--; 6326 p--;
6331 do { 6327 while (1) {
6332 int esc; 6328 int esc;
6333 6329
6334 while ((unsigned char)*p != CTLARI) { 6330 while ((unsigned char)*p != CTLARI) {
@@ -6346,7 +6342,7 @@ expari(int quotes)
6346 } 6342 }
6347 6343
6348 p -= esc + 1; 6344 p -= esc + 1;
6349 } while (1); 6345 }
6350 6346
6351 begoff = p - start; 6347 begoff = p - start;
6352 6348
@@ -7754,8 +7750,6 @@ static int builtinloc = -1; /* index in path of %builtin, or -1 */
7754static void 7750static void
7755tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp) 7751tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
7756{ 7752{
7757 int repeated = 0;
7758
7759#if ENABLE_FEATURE_SH_STANDALONE 7753#if ENABLE_FEATURE_SH_STANDALONE
7760 if (applet_no >= 0) { 7754 if (applet_no >= 0) {
7761 if (APPLET_IS_NOEXEC(applet_no)) { 7755 if (APPLET_IS_NOEXEC(applet_no)) {
@@ -7779,25 +7773,36 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **
7779#else 7773#else
7780 execve(cmd, argv, envp); 7774 execve(cmd, argv, envp);
7781#endif 7775#endif
7782 if (repeated) { 7776 if (cmd == (char*) bb_busybox_exec_path) {
7777 /* We already visited ENOEXEC branch below, don't do it again */
7778//TODO: try execve(initial_argv0_of_shell, argv, envp) before giving up?
7783 free(argv); 7779 free(argv);
7784 return; 7780 return;
7785 } 7781 }
7786 if (errno == ENOEXEC) { 7782 if (errno == ENOEXEC) {
7783 /* Run "cmd" as a shell script:
7784 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
7785 * "If the execve() function fails with ENOEXEC, the shell
7786 * shall execute a command equivalent to having a shell invoked
7787 * with the command name as its first operand,
7788 * with any remaining arguments passed to the new shell"
7789 *
7790 * That is, do not use $SHELL, user's shell, or /bin/sh;
7791 * just call ourselves.
7792 */
7787 char **ap; 7793 char **ap;
7788 char **new; 7794 char **new;
7789 7795
7790 for (ap = argv; *ap; ap++) 7796 for (ap = argv; *ap; ap++)
7791 continue; 7797 continue;
7792 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0])); 7798 new = ckmalloc((ap - argv + 2) * sizeof(new[0]));
7793 ap[1] = cmd; 7799 new[0] = (char*) "ash";
7794 ap[0] = cmd = (char *)DEFAULT_SHELL; 7800 new[1] = cmd;
7795 ap += 2; 7801 ap = new + 2;
7796 argv++; 7802 while ((*ap++ = *++argv) != NULL)
7797 while ((*ap++ = *argv++) != NULL)
7798 continue; 7803 continue;
7804 cmd = (char*) bb_busybox_exec_path;
7799 argv = new; 7805 argv = new;
7800 repeated++;
7801 goto repeat; 7806 goto repeat;
7802 } 7807 }
7803} 7808}
@@ -10126,6 +10131,11 @@ preadfd(void)
10126# if ENABLE_FEATURE_TAB_COMPLETION 10131# if ENABLE_FEATURE_TAB_COMPLETION
10127 line_input_state->path_lookup = pathval(); 10132 line_input_state->path_lookup = pathval();
10128# endif 10133# endif
10134 /* Unicode support should be activated even if LANG is set
10135 * _during_ shell execution, not only if it was set when
10136 * shell was started. Therefore, re-check LANG every time:
10137 */
10138 reinit_unicode(lookupvar("LANG"));
10129 nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ, timeout); 10139 nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ, timeout);
10130 if (nr == 0) { 10140 if (nr == 0) {
10131 /* Ctrl+C pressed */ 10141 /* Ctrl+C pressed */
@@ -10673,7 +10683,7 @@ options(int cmdline)
10673 else if (*argptr == NULL) 10683 else if (*argptr == NULL)
10674 setparam(argptr); 10684 setparam(argptr);
10675 } 10685 }
10676 break; /* "-" or "--" terminates options */ 10686 break; /* "-" or "--" terminates options */
10677 } 10687 }
10678 } 10688 }
10679 /* first char was + or - */ 10689 /* first char was + or - */
@@ -10775,10 +10785,10 @@ setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
10775 10785
10776 if (!argv[1]) 10786 if (!argv[1])
10777 return showvars(nullstr, 0, VUNSET); 10787 return showvars(nullstr, 0, VUNSET);
10788
10778 INT_OFF; 10789 INT_OFF;
10779 retval = 1; 10790 retval = options(/*cmdline:*/ 0);
10780 if (!options(0)) { /* if no parse error... */ 10791 if (retval == 0) { /* if no parse error... */
10781 retval = 0;
10782 optschanged(); 10792 optschanged();
10783 if (*argptr != NULL) { 10793 if (*argptr != NULL) {
10784 setparam(argptr); 10794 setparam(argptr);
@@ -13525,14 +13535,32 @@ init(void)
13525 setvar("PPID", utoa(getppid()), 0); 13535 setvar("PPID", utoa(getppid()), 0);
13526 13536
13527 p = lookupvar("PWD"); 13537 p = lookupvar("PWD");
13528 if (p) 13538 if (p) {
13529 if (*p != '/' || stat(p, &st1) || stat(".", &st2) 13539 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13530 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) 13540 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino
13541 ) {
13531 p = '\0'; 13542 p = '\0';
13543 }
13544 }
13532 setpwd(p, 0); 13545 setpwd(p, 0);
13533 } 13546 }
13534} 13547}
13535 13548
13549
13550//usage:#define ash_trivial_usage
13551//usage: "[-/+OPTIONS] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]"
13552//usage:#define ash_full_usage "\n\n"
13553//usage: "Unix shell interpreter"
13554
13555//usage:#if ENABLE_FEATURE_SH_IS_ASH
13556//usage:# define sh_trivial_usage ash_trivial_usage
13557//usage:# define sh_full_usage ash_full_usage
13558//usage:#endif
13559//usage:#if ENABLE_FEATURE_BASH_IS_ASH
13560//usage:# define bash_trivial_usage ash_trivial_usage
13561//usage:# define bash_full_usage ash_full_usage
13562//usage:#endif
13563
13536/* 13564/*
13537 * Process the shell command line arguments. 13565 * Process the shell command line arguments.
13538 */ 13566 */
@@ -13550,7 +13578,7 @@ procargs(char **argv)
13550 for (i = 0; i < NOPTS; i++) 13578 for (i = 0; i < NOPTS; i++)
13551 optlist[i] = 2; 13579 optlist[i] = 2;
13552 argptr = xargv; 13580 argptr = xargv;
13553 if (options(1)) { 13581 if (options(/*cmdline:*/ 1)) {
13554 /* it already printed err message */ 13582 /* it already printed err message */
13555 raise_exception(EXERROR); 13583 raise_exception(EXERROR);
13556 } 13584 }
diff --git a/shell/hush.c b/shell/hush.c
index 4d9e5f8c7..e4c3a7d77 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -81,7 +81,6 @@
81 * $ "export" i=`echo 'aaa bbb'`; echo "$i" 81 * $ "export" i=`echo 'aaa bbb'`; echo "$i"
82 * aaa 82 * aaa
83 */ 83 */
84#include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */
85#if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \ 84#if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \
86 || defined(__APPLE__) \ 85 || defined(__APPLE__) \
87 ) 86 )
@@ -93,6 +92,8 @@
93# include <fnmatch.h> 92# include <fnmatch.h>
94#endif 93#endif
95 94
95#include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */
96#include "unicode.h"
96#include "shell_common.h" 97#include "shell_common.h"
97#include "math.h" 98#include "math.h"
98#include "match.h" 99#include "match.h"
@@ -105,14 +106,6 @@
105# define PIPE_BUF 4096 /* amount of buffering in a pipe */ 106# define PIPE_BUF 4096 /* amount of buffering in a pipe */
106#endif 107#endif
107 108
108//applet:IF_HUSH(APPLET(hush, BB_DIR_BIN, BB_SUID_DROP))
109//applet:IF_MSH(APPLET(msh, BB_DIR_BIN, BB_SUID_DROP))
110//applet:IF_FEATURE_SH_IS_HUSH(APPLET_ODDNAME(sh, hush, BB_DIR_BIN, BB_SUID_DROP, sh))
111//applet:IF_FEATURE_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, BB_DIR_BIN, BB_SUID_DROP, bash))
112
113//kbuild:lib-$(CONFIG_HUSH) += hush.o match.o shell_common.o
114//kbuild:lib-$(CONFIG_HUSH_RANDOM_SUPPORT) += random.o
115
116//config:config HUSH 109//config:config HUSH
117//config: bool "hush" 110//config: bool "hush"
118//config: default y 111//config: default y
@@ -249,20 +242,35 @@
249//config: msh is deprecated and will be removed, please migrate to hush. 242//config: msh is deprecated and will be removed, please migrate to hush.
250//config: 243//config:
251 244
245//applet:IF_HUSH(APPLET(hush, BB_DIR_BIN, BB_SUID_DROP))
246//applet:IF_MSH(APPLET(msh, BB_DIR_BIN, BB_SUID_DROP))
247//applet:IF_FEATURE_SH_IS_HUSH(APPLET_ODDNAME(sh, hush, BB_DIR_BIN, BB_SUID_DROP, sh))
248//applet:IF_FEATURE_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, BB_DIR_BIN, BB_SUID_DROP, bash))
249
250//kbuild:lib-$(CONFIG_HUSH) += hush.o match.o shell_common.o
251//kbuild:lib-$(CONFIG_HUSH_RANDOM_SUPPORT) += random.o
252
252/* -i (interactive) and -s (read stdin) are also accepted, 253/* -i (interactive) and -s (read stdin) are also accepted,
253 * but currently do nothing, therefore aren't shown in help. 254 * but currently do nothing, therefore aren't shown in help.
254 * NOMMU-specific options are not meant to be used by users, 255 * NOMMU-specific options are not meant to be used by users,
255 * therefore we don't show them either. 256 * therefore we don't show them either.
256 */ 257 */
257//usage:#define hush_trivial_usage 258//usage:#define hush_trivial_usage
258//usage: "[-nx] [-c SCRIPT]" 259//usage: "[-nx] [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]"
259//usage:#define hush_full_usage "" 260//usage:#define hush_full_usage "\n\n"
261//usage: "Unix shell interpreter"
262
260//usage:#define msh_trivial_usage hush_trivial_usage 263//usage:#define msh_trivial_usage hush_trivial_usage
261//usage:#define msh_full_usage "" 264//usage:#define msh_full_usage hush_full_usage
262//usage:#define sh_trivial_usage NOUSAGE_STR 265
263//usage:#define sh_full_usage "" 266//usage:#if ENABLE_FEATURE_SH_IS_HUSH
264//usage:#define bash_trivial_usage NOUSAGE_STR 267//usage:# define sh_trivial_usage hush_trivial_usage
265//usage:#define bash_full_usage "" 268//usage:# define sh_full_usage hush_full_usage
269//usage:#endif
270//usage:#if ENABLE_FEATURE_BASH_IS_HUSH
271//usage:# define bash_trivial_usage hush_trivial_usage
272//usage:# define bash_full_usage hush_full_usage
273//usage:#endif
266 274
267 275
268/* Build knobs */ 276/* Build knobs */
@@ -1087,17 +1095,10 @@ static void syntax_error_unterm_str(unsigned lineno, const char *s)
1087 die_if_script(lineno, "syntax error: unterminated %s", s); 1095 die_if_script(lineno, "syntax error: unterminated %s", s);
1088} 1096}
1089 1097
1090/* It so happens that all such cases are totally fatal
1091 * even if shell is interactive: EOF while looking for closing
1092 * delimiter. There is nowhere to read stuff from after that,
1093 * it's EOF! The only choice is to terminate.
1094 */
1095static void syntax_error_unterm_ch(unsigned lineno, char ch) NORETURN;
1096static void syntax_error_unterm_ch(unsigned lineno, char ch) 1098static void syntax_error_unterm_ch(unsigned lineno, char ch)
1097{ 1099{
1098 char msg[2] = { ch, '\0' }; 1100 char msg[2] = { ch, '\0' };
1099 syntax_error_unterm_str(lineno, msg); 1101 syntax_error_unterm_str(lineno, msg);
1100 xfunc_die();
1101} 1102}
1102 1103
1103static void syntax_error_unexpected_ch(unsigned lineno, int ch) 1104static void syntax_error_unexpected_ch(unsigned lineno, int ch)
@@ -1899,6 +1900,12 @@ static void get_user_input(struct in_str *i)
1899 /* Enable command line editing only while a command line 1900 /* Enable command line editing only while a command line
1900 * is actually being read */ 1901 * is actually being read */
1901 do { 1902 do {
1903 /* Unicode support should be activated even if LANG is set
1904 * _during_ shell execution, not only if it was set when
1905 * shell was started. Therefore, re-check LANG every time:
1906 */
1907 reinit_unicode(get_local_var_value("LANG"));
1908
1902 G.flag_SIGINT = 0; 1909 G.flag_SIGINT = 0;
1903 /* buglet: SIGINT will not make new prompt to appear _at once_, 1910 /* buglet: SIGINT will not make new prompt to appear _at once_,
1904 * only after <Enter>. (^C will work) */ 1911 * only after <Enter>. (^C will work) */
@@ -3525,39 +3532,40 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
3525 3532
3526#if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS 3533#if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS
3527/* Subroutines for copying $(...) and `...` things */ 3534/* Subroutines for copying $(...) and `...` things */
3528static void add_till_backquote(o_string *dest, struct in_str *input, int in_dquote); 3535static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote);
3529/* '...' */ 3536/* '...' */
3530static void add_till_single_quote(o_string *dest, struct in_str *input) 3537static int add_till_single_quote(o_string *dest, struct in_str *input)
3531{ 3538{
3532 while (1) { 3539 while (1) {
3533 int ch = i_getch(input); 3540 int ch = i_getch(input);
3534 if (ch == EOF) { 3541 if (ch == EOF) {
3535 syntax_error_unterm_ch('\''); 3542 syntax_error_unterm_ch('\'');
3536 /*xfunc_die(); - redundant */ 3543 return 0;
3537 } 3544 }
3538 if (ch == '\'') 3545 if (ch == '\'')
3539 return; 3546 return 1;
3540 o_addchr(dest, ch); 3547 o_addchr(dest, ch);
3541 } 3548 }
3542} 3549}
3543/* "...\"...`..`...." - do we need to handle "...$(..)..." too? */ 3550/* "...\"...`..`...." - do we need to handle "...$(..)..." too? */
3544static void add_till_double_quote(o_string *dest, struct in_str *input) 3551static int add_till_double_quote(o_string *dest, struct in_str *input)
3545{ 3552{
3546 while (1) { 3553 while (1) {
3547 int ch = i_getch(input); 3554 int ch = i_getch(input);
3548 if (ch == EOF) { 3555 if (ch == EOF) {
3549 syntax_error_unterm_ch('"'); 3556 syntax_error_unterm_ch('"');
3550 /*xfunc_die(); - redundant */ 3557 return 0;
3551 } 3558 }
3552 if (ch == '"') 3559 if (ch == '"')
3553 return; 3560 return 1;
3554 if (ch == '\\') { /* \x. Copy both chars. */ 3561 if (ch == '\\') { /* \x. Copy both chars. */
3555 o_addchr(dest, ch); 3562 o_addchr(dest, ch);
3556 ch = i_getch(input); 3563 ch = i_getch(input);
3557 } 3564 }
3558 o_addchr(dest, ch); 3565 o_addchr(dest, ch);
3559 if (ch == '`') { 3566 if (ch == '`') {
3560 add_till_backquote(dest, input, /*in_dquote:*/ 1); 3567 if (!add_till_backquote(dest, input, /*in_dquote:*/ 1))
3568 return 0;
3561 o_addchr(dest, ch); 3569 o_addchr(dest, ch);
3562 continue; 3570 continue;
3563 } 3571 }
@@ -3578,12 +3586,12 @@ static void add_till_double_quote(o_string *dest, struct in_str *input)
3578 * Example Output 3586 * Example Output
3579 * echo `echo '\'TEST\`echo ZZ\`BEST` \TESTZZBEST 3587 * echo `echo '\'TEST\`echo ZZ\`BEST` \TESTZZBEST
3580 */ 3588 */
3581static void add_till_backquote(o_string *dest, struct in_str *input, int in_dquote) 3589static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote)
3582{ 3590{
3583 while (1) { 3591 while (1) {
3584 int ch = i_getch(input); 3592 int ch = i_getch(input);
3585 if (ch == '`') 3593 if (ch == '`')
3586 return; 3594 return 1;
3587 if (ch == '\\') { 3595 if (ch == '\\') {
3588 /* \x. Copy both unless it is \`, \$, \\ and maybe \" */ 3596 /* \x. Copy both unless it is \`, \$, \\ and maybe \" */
3589 ch = i_getch(input); 3597 ch = i_getch(input);
@@ -3597,7 +3605,7 @@ static void add_till_backquote(o_string *dest, struct in_str *input, int in_dquo
3597 } 3605 }
3598 if (ch == EOF) { 3606 if (ch == EOF) {
3599 syntax_error_unterm_ch('`'); 3607 syntax_error_unterm_ch('`');
3600 /*xfunc_die(); - redundant */ 3608 return 0;
3601 } 3609 }
3602 o_addchr(dest, ch); 3610 o_addchr(dest, ch);
3603 } 3611 }
@@ -3633,7 +3641,7 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign
3633 ch = i_getch(input); 3641 ch = i_getch(input);
3634 if (ch == EOF) { 3642 if (ch == EOF) {
3635 syntax_error_unterm_ch(end_ch); 3643 syntax_error_unterm_ch(end_ch);
3636 /*xfunc_die(); - redundant */ 3644 return 0;
3637 } 3645 }
3638 if (ch == end_ch IF_HUSH_BASH_COMPAT( || ch == end_char2)) { 3646 if (ch == end_ch IF_HUSH_BASH_COMPAT( || ch == end_char2)) {
3639 if (!dbl) 3647 if (!dbl)
@@ -3647,22 +3655,26 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign
3647 o_addchr(dest, ch); 3655 o_addchr(dest, ch);
3648 if (ch == '(' || ch == '{') { 3656 if (ch == '(' || ch == '{') {
3649 ch = (ch == '(' ? ')' : '}'); 3657 ch = (ch == '(' ? ')' : '}');
3650 add_till_closing_bracket(dest, input, ch); 3658 if (!add_till_closing_bracket(dest, input, ch))
3659 return 0;
3651 o_addchr(dest, ch); 3660 o_addchr(dest, ch);
3652 continue; 3661 continue;
3653 } 3662 }
3654 if (ch == '\'') { 3663 if (ch == '\'') {
3655 add_till_single_quote(dest, input); 3664 if (!add_till_single_quote(dest, input))
3665 return 0;
3656 o_addchr(dest, ch); 3666 o_addchr(dest, ch);
3657 continue; 3667 continue;
3658 } 3668 }
3659 if (ch == '"') { 3669 if (ch == '"') {
3660 add_till_double_quote(dest, input); 3670 if (!add_till_double_quote(dest, input))
3671 return 0;
3661 o_addchr(dest, ch); 3672 o_addchr(dest, ch);
3662 continue; 3673 continue;
3663 } 3674 }
3664 if (ch == '`') { 3675 if (ch == '`') {
3665 add_till_backquote(dest, input, /*in_dquote:*/ 0); 3676 if (!add_till_backquote(dest, input, /*in_dquote:*/ 0))
3677 return 0;
3666 o_addchr(dest, ch); 3678 o_addchr(dest, ch);
3667 continue; 3679 continue;
3668 } 3680 }
@@ -3671,7 +3683,7 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign
3671 ch = i_getch(input); 3683 ch = i_getch(input);
3672 if (ch == EOF) { 3684 if (ch == EOF) {
3673 syntax_error_unterm_ch(')'); 3685 syntax_error_unterm_ch(')');
3674 /*xfunc_die(); - redundant */ 3686 return 0;
3675 } 3687 }
3676 o_addchr(dest, ch); 3688 o_addchr(dest, ch);
3677 continue; 3689 continue;
@@ -3742,8 +3754,8 @@ static int parse_dollar(o_string *as_string,
3742 ) { 3754 ) {
3743 bad_dollar_syntax: 3755 bad_dollar_syntax:
3744 syntax_error_unterm_str("${name}"); 3756 syntax_error_unterm_str("${name}");
3745 debug_printf_parse("parse_dollar return 1: unterminated ${name}\n"); 3757 debug_printf_parse("parse_dollar return 0: unterminated ${name}\n");
3746 return 1; 3758 return 0;
3747 } 3759 }
3748 nommu_addchr(as_string, ch); 3760 nommu_addchr(as_string, ch);
3749 ch |= quote_mask; 3761 ch |= quote_mask;
@@ -3799,6 +3811,8 @@ static int parse_dollar(o_string *as_string,
3799 pos = dest->length; 3811 pos = dest->length;
3800#if ENABLE_HUSH_DOLLAR_OPS 3812#if ENABLE_HUSH_DOLLAR_OPS
3801 last_ch = add_till_closing_bracket(dest, input, end_ch); 3813 last_ch = add_till_closing_bracket(dest, input, end_ch);
3814 if (last_ch == 0) /* error? */
3815 return 0;
3802#else 3816#else
3803#error Simple code to only allow ${var} is not implemented 3817#error Simple code to only allow ${var} is not implemented
3804#endif 3818#endif
@@ -3843,7 +3857,8 @@ static int parse_dollar(o_string *as_string,
3843 o_addchr(dest, /*quote_mask |*/ '+'); 3857 o_addchr(dest, /*quote_mask |*/ '+');
3844 if (!BB_MMU) 3858 if (!BB_MMU)
3845 pos = dest->length; 3859 pos = dest->length;
3846 add_till_closing_bracket(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG); 3860 if (!add_till_closing_bracket(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG))
3861 return 0; /* error */
3847 if (as_string) { 3862 if (as_string) {
3848 o_addstr(as_string, dest->data + pos); 3863 o_addstr(as_string, dest->data + pos);
3849 o_addchr(as_string, ')'); 3864 o_addchr(as_string, ')');
@@ -3858,7 +3873,8 @@ static int parse_dollar(o_string *as_string,
3858 o_addchr(dest, quote_mask | '`'); 3873 o_addchr(dest, quote_mask | '`');
3859 if (!BB_MMU) 3874 if (!BB_MMU)
3860 pos = dest->length; 3875 pos = dest->length;
3861 add_till_closing_bracket(dest, input, ')'); 3876 if (!add_till_closing_bracket(dest, input, ')'))
3877 return 0; /* error */
3862 if (as_string) { 3878 if (as_string) {
3863 o_addstr(as_string, dest->data + pos); 3879 o_addstr(as_string, dest->data + pos);
3864 o_addchr(as_string, ')'); 3880 o_addchr(as_string, ')');
@@ -3885,8 +3901,8 @@ static int parse_dollar(o_string *as_string,
3885 default: 3901 default:
3886 o_addQchr(dest, '$'); 3902 o_addQchr(dest, '$');
3887 } 3903 }
3888 debug_printf_parse("parse_dollar return 0\n"); 3904 debug_printf_parse("parse_dollar return 1 (ok)\n");
3889 return 0; 3905 return 1;
3890#undef as_string 3906#undef as_string
3891} 3907}
3892 3908
@@ -3927,13 +3943,13 @@ static int encode_string(o_string *as_string,
3927 if (ch != EOF) 3943 if (ch != EOF)
3928 nommu_addchr(as_string, ch); 3944 nommu_addchr(as_string, ch);
3929 if (ch == dquote_end) { /* may be only '"' or EOF */ 3945 if (ch == dquote_end) { /* may be only '"' or EOF */
3930 debug_printf_parse("encode_string return 0\n"); 3946 debug_printf_parse("encode_string return 1 (ok)\n");
3931 return 0; 3947 return 1;
3932 } 3948 }
3933 /* note: can't move it above ch == dquote_end check! */ 3949 /* note: can't move it above ch == dquote_end check! */
3934 if (ch == EOF) { 3950 if (ch == EOF) {
3935 syntax_error_unterm_ch('"'); 3951 syntax_error_unterm_ch('"');
3936 /*xfunc_die(); - redundant */ 3952 return 0; /* error */
3937 } 3953 }
3938 next = '\0'; 3954 next = '\0';
3939 if (ch != '\n') { 3955 if (ch != '\n') {
@@ -3964,10 +3980,10 @@ static int encode_string(o_string *as_string,
3964 goto again; 3980 goto again;
3965 } 3981 }
3966 if (ch == '$') { 3982 if (ch == '$') {
3967 if (parse_dollar(as_string, dest, input, /*quote_mask:*/ 0x80) != 0) { 3983 if (!parse_dollar(as_string, dest, input, /*quote_mask:*/ 0x80)) {
3968 debug_printf_parse("encode_string return 1: " 3984 debug_printf_parse("encode_string return 0: "
3969 "parse_dollar returned non-0\n"); 3985 "parse_dollar returned 0 (error)\n");
3970 return 1; 3986 return 0;
3971 } 3987 }
3972 goto again; 3988 goto again;
3973 } 3989 }
@@ -3976,7 +3992,8 @@ static int encode_string(o_string *as_string,
3976 //unsigned pos = dest->length; 3992 //unsigned pos = dest->length;
3977 o_addchr(dest, SPECIAL_VAR_SYMBOL); 3993 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3978 o_addchr(dest, 0x80 | '`'); 3994 o_addchr(dest, 0x80 | '`');
3979 add_till_backquote(dest, input, /*in_dquote:*/ dquote_end == '"'); 3995 if (!add_till_backquote(dest, input, /*in_dquote:*/ dquote_end == '"'))
3996 return 0; /* error */
3980 o_addchr(dest, SPECIAL_VAR_SYMBOL); 3997 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3981 //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos); 3998 //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
3982 goto again; 3999 goto again;
@@ -4047,8 +4064,8 @@ static struct pipe *parse_stream(char **pstring,
4047 /* end_trigger == '}' case errors out earlier, 4064 /* end_trigger == '}' case errors out earlier,
4048 * checking only ')' */ 4065 * checking only ')' */
4049 if (end_trigger == ')') { 4066 if (end_trigger == ')') {
4050 syntax_error_unterm_ch('('); /* exits */ 4067 syntax_error_unterm_ch('(');
4051 /* goto parse_error; */ 4068 goto parse_error;
4052 } 4069 }
4053 4070
4054 if (done_word(&dest, &ctx)) { 4071 if (done_word(&dest, &ctx)) {
@@ -4339,9 +4356,9 @@ static struct pipe *parse_stream(char **pstring,
4339 dest.has_quoted_part = 1; 4356 dest.has_quoted_part = 1;
4340 break; 4357 break;
4341 case '$': 4358 case '$':
4342 if (parse_dollar(&ctx.as_string, &dest, input, /*quote_mask:*/ 0) != 0) { 4359 if (!parse_dollar(&ctx.as_string, &dest, input, /*quote_mask:*/ 0)) {
4343 debug_printf_parse("parse_stream parse error: " 4360 debug_printf_parse("parse_stream parse error: "
4344 "parse_dollar returned non-0\n"); 4361 "parse_dollar returned 0 (error)\n");
4345 goto parse_error; 4362 goto parse_error;
4346 } 4363 }
4347 break; 4364 break;
@@ -4351,7 +4368,7 @@ static struct pipe *parse_stream(char **pstring,
4351 ch = i_getch(input); 4368 ch = i_getch(input);
4352 if (ch == EOF) { 4369 if (ch == EOF) {
4353 syntax_error_unterm_ch('\''); 4370 syntax_error_unterm_ch('\'');
4354 /*xfunc_die(); - redundant */ 4371 goto parse_error;
4355 } 4372 }
4356 nommu_addchr(&ctx.as_string, ch); 4373 nommu_addchr(&ctx.as_string, ch);
4357 if (ch == '\'') 4374 if (ch == '\'')
@@ -4363,7 +4380,7 @@ static struct pipe *parse_stream(char **pstring,
4363 dest.has_quoted_part = 1; 4380 dest.has_quoted_part = 1;
4364 if (dest.o_assignment == NOT_ASSIGNMENT) 4381 if (dest.o_assignment == NOT_ASSIGNMENT)
4365 dest.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS; 4382 dest.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS;
4366 if (encode_string(&ctx.as_string, &dest, input, '"', /*process_bkslash:*/ 1)) 4383 if (!encode_string(&ctx.as_string, &dest, input, '"', /*process_bkslash:*/ 1))
4367 goto parse_error; 4384 goto parse_error;
4368 dest.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS; 4385 dest.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS;
4369 break; 4386 break;
@@ -4374,7 +4391,8 @@ static struct pipe *parse_stream(char **pstring,
4374 o_addchr(&dest, SPECIAL_VAR_SYMBOL); 4391 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
4375 o_addchr(&dest, '`'); 4392 o_addchr(&dest, '`');
4376 pos = dest.length; 4393 pos = dest.length;
4377 add_till_backquote(&dest, input, /*in_dquote:*/ 0); 4394 if (!add_till_backquote(&dest, input, /*in_dquote:*/ 0))
4395 goto parse_error;
4378# if !BB_MMU 4396# if !BB_MMU
4379 o_addstr(&ctx.as_string, dest.data + pos); 4397 o_addstr(&ctx.as_string, dest.data + pos);
4380 o_addchr(&ctx.as_string, '`'); 4398 o_addchr(&ctx.as_string, '`');
@@ -4650,6 +4668,7 @@ static char *encode_then_expand_string(const char *str, int process_bkslash, int
4650 */ 4668 */
4651 setup_string_in_str(&input, str); 4669 setup_string_in_str(&input, str);
4652 encode_string(NULL, &dest, &input, EOF, process_bkslash); 4670 encode_string(NULL, &dest, &input, EOF, process_bkslash);
4671//TODO: error check (encode_string returns 0 on error)?
4653 //bb_error_msg("'%s' -> '%s'", str, dest.data); 4672 //bb_error_msg("'%s' -> '%s'", str, dest.data);
4654 exp_str = expand_string_to_string(dest.data, /*unbackslash:*/ do_unbackslash); 4673 exp_str = expand_string_to_string(dest.data, /*unbackslash:*/ do_unbackslash);
4655 //bb_error_msg("'%s' -> '%s'", dest.data, exp_str); 4674 //bb_error_msg("'%s' -> '%s'", dest.data, exp_str);
@@ -5540,6 +5559,10 @@ static void parse_and_run_stream(struct in_str *inp, int end_trigger)
5540 debug_printf_exec("parse_and_run_stream: run_and_free_list\n"); 5559 debug_printf_exec("parse_and_run_stream: run_and_free_list\n");
5541 run_and_free_list(pipe_list); 5560 run_and_free_list(pipe_list);
5542 empty = 0; 5561 empty = 0;
5562#if ENABLE_HUSH_FUNCTIONS
5563 if (G.flag_return_in_progress == 1)
5564 break;
5565#endif
5543 } 5566 }
5544} 5567}
5545 5568
@@ -8607,6 +8630,8 @@ static int FAST_FUNC builtin_source(char **argv)
8607#endif 8630#endif
8608 save_and_replace_G_args(&sv, argv); 8631 save_and_replace_G_args(&sv, argv);
8609 8632
8633//TODO: syntax errors in sourced file should never abort the "calling" script.
8634//Try: bash -c '. ./bad_file; echo YES'
8610 parse_and_run_file(input); 8635 parse_and_run_file(input);
8611 fclose(input); 8636 fclose(input);
8612 8637
diff --git a/shell/hush_test/hush-misc/return1.right b/shell/hush_test/hush-misc/return1.right
new file mode 100644
index 000000000..7b24a35ff
--- /dev/null
+++ b/shell/hush_test/hush-misc/return1.right
@@ -0,0 +1 @@
Ok:0
diff --git a/shell/hush_test/hush-misc/return1.tests b/shell/hush_test/hush-misc/return1.tests
new file mode 100755
index 000000000..eeb92ef3f
--- /dev/null
+++ b/shell/hush_test/hush-misc/return1.tests
@@ -0,0 +1,4 @@
1echo "true && return; echo Should not be printed" >return_sourced
2. ./return_sourced
3rm return_sourced
4echo Ok:$?
diff --git a/shell/hush_test/hush-misc/source1.right b/shell/hush_test/hush-misc/source1.right
new file mode 100644
index 000000000..d4256034b
--- /dev/null
+++ b/shell/hush_test/hush-misc/source1.right
@@ -0,0 +1,5 @@
1hush: syntax error: unterminated ${name}
2line2
3Ok1:0
4hush: syntax error: unterminated '
5Ok2:1
diff --git a/shell/hush_test/hush-misc/source1.tests b/shell/hush_test/hush-misc/source1.tests
new file mode 100755
index 000000000..c13888359
--- /dev/null
+++ b/shell/hush_test/hush-misc/source1.tests
@@ -0,0 +1,10 @@
1echo 'echo ${^}
2echo line2' >sourced1
3. ./sourced1
4echo Ok1:$?
5
6echo "echo '" >sourced1
7. ./sourced1
8echo Ok2:$?
9
10rm sourced1