From 940c7206c2a4acb386ab47199a6c313c04387f3b Mon Sep 17 00:00:00 2001
From: Denys Vlasenko <vda.linux@googlemail.com>
Date: Wed, 2 Mar 2011 04:07:14 +0100
Subject: convert "do {...} while (1);" -> "while (1) {...}"

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
---
 shell/ash.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

(limited to 'shell')

diff --git a/shell/ash.c b/shell/ash.c
index 98d2c7c29..c3c953656 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -3539,12 +3539,12 @@ set_curjob(struct job *jp, unsigned mode)
 
 	/* first remove from list */
 	jpp = curp = &curjob;
-	do {
+	while (1) {
 		jp1 = *jpp;
 		if (jp1 == jp)
 			break;
 		jpp = &jp1->prev_job;
-	} while (1);
+	}
 	*jpp = jp1->prev_job;
 
 	/* Then re-insert in correct position */
@@ -3560,14 +3560,14 @@ set_curjob(struct job *jp, unsigned mode)
 	case CUR_RUNNING:
 		/* newly created job or backgrounded job,
 		   put after all stopped jobs. */
-		do {
+		while (1) {
 			jp1 = *jpp;
 #if JOBS
 			if (!jp1 || jp1->state != JOBSTOPPED)
 #endif
 				break;
 			jpp = &jp1->prev_job;
-		} while (1);
+		}
 		/* FALLTHROUGH */
 #if JOBS
 	case CUR_STOPPED:
@@ -3740,7 +3740,7 @@ setjobctl(int on)
 			goto out;
 		/* fd is a tty at this point */
 		close_on_exec_on(fd);
-		do { /* while we are in the background */
+		while (1) { /* while we are in the background */
 			pgrp = tcgetpgrp(fd);
 			if (pgrp < 0) {
  out:
@@ -3751,7 +3751,7 @@ setjobctl(int on)
 			if (pgrp == getpgrp())
 				break;
 			killpg(0, SIGTTIN);
-		} while (1);
+		}
 		initialpgrp = pgrp;
 
 		setsignal(SIGTSTP);
@@ -5970,7 +5970,7 @@ expari(int quotes)
 	p = expdest - 1;
 	*p = '\0';
 	p--;
-	do {
+	while (1) {
 		int esc;
 
 		while ((unsigned char)*p != CTLARI) {
@@ -5988,7 +5988,7 @@ expari(int quotes)
 		}
 
 		p -= esc + 1;
-	} while (1);
+	}
 
 	begoff = p - start;
 
-- 
cgit v1.2.3-55-g6feb


From aefe1c260ec5aa691e8b6f773c7fa393bfad0f7b Mon Sep 17 00:00:00 2001
From: Denys Vlasenko <vda.linux@googlemail.com>
Date: Mon, 7 Mar 2011 12:02:40 +0100
Subject: ash: fix execution of shell scripts without shebang

We were assuming #!/bin/sh, whereas we had to simply re-enter ash.

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
---
 shell/ash.c | 29 +++++++++++++++++++----------
 1 file changed, 19 insertions(+), 10 deletions(-)

(limited to 'shell')

diff --git a/shell/ash.c b/shell/ash.c
index c3c953656..09db046fe 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -7396,8 +7396,6 @@ static int builtinloc = -1;     /* index in path of %builtin, or -1 */
 static void
 tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
 {
-	int repeated = 0;
-
 #if ENABLE_FEATURE_SH_STANDALONE
 	if (applet_no >= 0) {
 		if (APPLET_IS_NOEXEC(applet_no)) {
@@ -7421,25 +7419,36 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **
 #else
 	execve(cmd, argv, envp);
 #endif
-	if (repeated) {
+	if (cmd == (char*) bb_busybox_exec_path) {
+		/* We already visited ENOEXEC branch below, don't do it again */
+//TODO: try execve(initial_argv0_of_shell, argv, envp) before giving up?
 		free(argv);
 		return;
 	}
 	if (errno == ENOEXEC) {
+		/* Run "cmd" as a shell script:
+		 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
+		 * "If the execve() function fails with ENOEXEC, the shell
+		 * shall execute a command equivalent to having a shell invoked
+		 * with the command name as its first operand,
+		 * with any remaining arguments passed to the new shell"
+		 *
+		 * That is, do not use $SHELL, user's shell, or /bin/sh;
+		 * just call ourselves.
+		 */
 		char **ap;
 		char **new;
 
 		for (ap = argv; *ap; ap++)
 			continue;
-		ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
-		ap[1] = cmd;
-		ap[0] = cmd = (char *)DEFAULT_SHELL;
-		ap += 2;
-		argv++;
-		while ((*ap++ = *argv++) != NULL)
+		new = ckmalloc((ap - argv + 2) * sizeof(new[0]));
+		new[0] = (char*) "ash";
+		new[1] = cmd;
+		ap = new + 2;
+		while ((*ap++ = *++argv) != NULL)
 			continue;
+		cmd = (char*) bb_busybox_exec_path;
 		argv = new;
-		repeated++;
 		goto repeat;
 	}
 }
-- 
cgit v1.2.3-55-g6feb


From b0b834342ffece7f3debd8c7199a07ce8a6d942c Mon Sep 17 00:00:00 2001
From: Denys Vlasenko <vda.linux@googlemail.com>
Date: Mon, 7 Mar 2011 12:34:59 +0100
Subject: ash/hush: provide help text

$ ./busybox ash --help
BusyBox v1.19.0.git (2011-03-07 11:25:29 CET) multi-call binary.

Usage: ash [-/+OPTCHARS] [-/+o OPTNAME]... [-c 'SCRIPT' [ARG0 [ARGS]] / SCRIPT_FILE [ARGS]]

Unix shell interpreter

$ ./busybox hush --help
BusyBox v1.19.0.git (2011-03-07 11:25:29 CET) multi-call binary.

Usage: hush [-nx] [-c 'SCRIPT' [ARG0 [ARGS]] / SCRIPT_FILE [ARGS]]

Unix shell interpreter

function                                             old     new   delta
packed_usage                                       28163   28212     +49
setcmd                                                85      78      -7
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 1/1 up/down: 49/-7)              Total: 42 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
---
 shell/ash.c  | 40 ++++++++++++++++++++++++++--------------
 shell/hush.c | 21 ++++++++++++++-------
 2 files changed, 40 insertions(+), 21 deletions(-)

(limited to 'shell')

diff --git a/shell/ash.c b/shell/ash.c
index 09db046fe..bfbd60d78 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -190,13 +190,6 @@
 //config:	  variable each time it is displayed.
 //config:
 
-//usage:#define ash_trivial_usage NOUSAGE_STR
-//usage:#define ash_full_usage ""
-//usage:#define sh_trivial_usage NOUSAGE_STR
-//usage:#define sh_full_usage ""
-//usage:#define bash_trivial_usage NOUSAGE_STR
-//usage:#define bash_full_usage ""
-
 
 /* ============ Hash table sizes. Configurable. */
 
@@ -10155,6 +10148,7 @@ setoption(int flag, int val)
 	/* NOTREACHED */
 }
 static int
+
 options(int cmdline)
 {
 	char *p;
@@ -10180,7 +10174,7 @@ options(int cmdline)
 					else if (*argptr == NULL)
 						setparam(argptr);
 				}
-				break;    /* "-" or  "--" terminates options */
+				break;    /* "-" or "--" terminates options */
 			}
 		}
 		/* first char was + or - */
@@ -10282,10 +10276,10 @@ setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
 
 	if (!argv[1])
 		return showvars(nullstr, 0, VUNSET);
+
 	INT_OFF;
-	retval = 1;
-	if (!options(0)) { /* if no parse error... */
-		retval = 0;
+	retval = options(/*cmdline:*/ 0);
+	if (retval == 0) { /* if no parse error... */
 		optschanged();
 		if (*argptr != NULL) {
 			setparam(argptr);
@@ -12938,14 +12932,32 @@ init(void)
 		setvar("PPID", utoa(getppid()), 0);
 
 		p = lookupvar("PWD");
-		if (p)
+		if (p) {
 			if (*p != '/' || stat(p, &st1) || stat(".", &st2)
-			 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
+			 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino
+			) {
 				p = '\0';
+			}
+		}
 		setpwd(p, 0);
 	}
 }
 
+
+//usage:#define ash_trivial_usage
+//usage:	"[-/+OPTCHARS] [-/+o OPTNAME]... [-c 'SCRIPT' [ARG0 [ARGS]] / SCRIPT_FILE [ARGS]]"
+//usage:#define ash_full_usage "\n\n"
+//usage:	"Unix shell interpreter"
+
+//usage:#if ENABLE_FEATURE_SH_IS_ASH
+//usage:# define sh_trivial_usage ash_trivial_usage
+//usage:# define sh_full_usage    ash_full_usage
+//usage:#endif
+//usage:#if ENABLE_FEATURE_BASH_IS_ASH
+//usage:# define bash_trivial_usage ash_trivial_usage
+//usage:# define bash_full_usage    ash_full_usage
+//usage:#endif
+
 /*
  * Process the shell command line arguments.
  */
@@ -12963,7 +12975,7 @@ procargs(char **argv)
 	for (i = 0; i < NOPTS; i++)
 		optlist[i] = 2;
 	argptr = xargv;
-	if (options(1)) {
+	if (options(/*cmdline:*/ 1)) {
 		/* it already printed err message */
 		raise_exception(EXERROR);
 	}
diff --git a/shell/hush.c b/shell/hush.c
index 4d9e5f8c7..6b3dc46d4 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -255,14 +255,21 @@
  * therefore we don't show them either.
  */
 //usage:#define hush_trivial_usage
-//usage:       "[-nx] [-c SCRIPT]"
-//usage:#define hush_full_usage ""
+//usage:	"[-nx] [-c 'SCRIPT' [ARG0 [ARGS]] / SCRIPT_FILE [ARGS]]"
+//usage:#define hush_full_usage "\n\n"
+//usage:	"Unix shell interpreter"
+
 //usage:#define msh_trivial_usage hush_trivial_usage
-//usage:#define msh_full_usage ""
-//usage:#define sh_trivial_usage NOUSAGE_STR
-//usage:#define sh_full_usage ""
-//usage:#define bash_trivial_usage NOUSAGE_STR
-//usage:#define bash_full_usage ""
+//usage:#define msh_full_usage hush_full_usage
+
+//usage:#if ENABLE_FEATURE_SH_IS_HUSH
+//usage:# define sh_trivial_usage hush_trivial_usage
+//usage:# define sh_full_usage    hush_full_usage
+//usage:#endif
+//usage:#if ENABLE_FEATURE_BASH_IS_HUSH
+//usage:# define bash_trivial_usage hush_trivial_usage
+//usage:# define bash_full_usage    hush_full_usage
+//usage:#endif
 
 
 /* Build knobs */
-- 
cgit v1.2.3-55-g6feb


From 976ec23da5a335572be9a6130fb4efcc08d62d0e Mon Sep 17 00:00:00 2001
From: Denys Vlasenko <vda.linux@googlemail.com>
Date: Mon, 7 Mar 2011 13:12:01 +0100
Subject: remove stray empty line

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
---
 shell/ash.c | 1 -
 1 file changed, 1 deletion(-)

(limited to 'shell')

diff --git a/shell/ash.c b/shell/ash.c
index bfbd60d78..dc50bc102 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -10148,7 +10148,6 @@ setoption(int flag, int val)
 	/* NOTREACHED */
 }
 static int
-
 options(int cmdline)
 {
 	char *p;
-- 
cgit v1.2.3-55-g6feb


From 6b6af53426d98ab513972bd710f250e246e7fc98 Mon Sep 17 00:00:00 2001
From: Denys Vlasenko <vda.linux@googlemail.com>
Date: Tue, 8 Mar 2011 10:24:17 +0100
Subject: ash/hush: shrink help text

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
---
 shell/ash.c  | 2 +-
 shell/hush.c | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

(limited to 'shell')

diff --git a/shell/ash.c b/shell/ash.c
index dc50bc102..0baf7c8e5 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -12944,7 +12944,7 @@ init(void)
 
 
 //usage:#define ash_trivial_usage
-//usage:	"[-/+OPTCHARS] [-/+o OPTNAME]... [-c 'SCRIPT' [ARG0 [ARGS]] / SCRIPT_FILE [ARGS]]"
+//usage:	"[-/+OPTIONS] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]"
 //usage:#define ash_full_usage "\n\n"
 //usage:	"Unix shell interpreter"
 
diff --git a/shell/hush.c b/shell/hush.c
index 6b3dc46d4..64d5e8587 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -255,7 +255,7 @@
  * therefore we don't show them either.
  */
 //usage:#define hush_trivial_usage
-//usage:	"[-nx] [-c 'SCRIPT' [ARG0 [ARGS]] / SCRIPT_FILE [ARGS]]"
+//usage:	"[-nx] [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]"
 //usage:#define hush_full_usage "\n\n"
 //usage:	"Unix shell interpreter"
 
-- 
cgit v1.2.3-55-g6feb


From 20704f066250744c0c2b84920c27d0fd0aa9e935 Mon Sep 17 00:00:00 2001
From: Denys Vlasenko <vda.linux@googlemail.com>
Date: Wed, 23 Mar 2011 17:59:27 +0100
Subject: ash,hush: recheck LANG before every line input

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
---
 include/unicode.h |  4 ++++
 libbb/unicode.c   | 28 +++++++++++++++++-----------
 shell/ash.c       | 23 +++++++++++++++--------
 shell/hush.c      | 25 ++++++++++++++++---------
 4 files changed, 52 insertions(+), 28 deletions(-)

(limited to 'shell')

diff --git a/include/unicode.h b/include/unicode.h
index dee02e777..0317a2151 100644
--- a/include/unicode.h
+++ b/include/unicode.h
@@ -27,6 +27,7 @@ enum {
 # define unicode_strwidth(string) strlen(string)
 # define unicode_status UNICODE_OFF
 # define init_unicode() ((void)0)
+# define reinit_unicode(LANG) ((void)0)
 
 #else
 
@@ -67,6 +68,7 @@ char* FAST_FUNC unicode_conv_to_printable_fixedwidth(/*uni_stat_t *stats,*/ cons
 
 extern uint8_t unicode_status;
 void init_unicode(void) FAST_FUNC;
+void reinit_unicode(const char *LANG) FAST_FUNC;
 
 # else
 
@@ -75,9 +77,11 @@ void init_unicode(void) FAST_FUNC;
 #  if !ENABLE_FEATURE_CHECK_UNICODE_IN_ENV
 #   define unicode_status UNICODE_ON
 #   define init_unicode() ((void)0)
+#   define reinit_unicode(LANG) ((void)0)
 #  else
 extern uint8_t unicode_status;
 void init_unicode(void) FAST_FUNC;
+void reinit_unicode(const char *LANG) FAST_FUNC;
 #  endif
 
 #  undef MB_CUR_MAX
diff --git a/libbb/unicode.c b/libbb/unicode.c
index 08a4c7427..d01efd9a2 100644
--- a/libbb/unicode.c
+++ b/libbb/unicode.c
@@ -23,37 +23,43 @@ uint8_t unicode_status;
 
 /* Unicode support using libc locale support. */
 
-void FAST_FUNC init_unicode(void)
+void FAST_FUNC reinit_unicode(const char *LANG UNUSED_PARAM)
 {
 	static const char unicode_0x394[] = { 0xce, 0x94, 0 };
 	size_t width;
 
-	if (unicode_status != UNICODE_UNKNOWN)
-		return;
+//TODO: call setlocale(LC_ALL, LANG) here?
+
 	/* In unicode, this is a one character string */
 // can use unicode_strlen(string) too, but otherwise unicode_strlen() is unused
 	width = mbstowcs(NULL, unicode_0x394, INT_MAX);
 	unicode_status = (width == 1 ? UNICODE_ON : UNICODE_OFF);
 }
 
+void FAST_FUNC init_unicode(void)
+{
+	if (unicode_status == UNICODE_UNKNOWN)
+		reinit_unicode(NULL /*getenv("LANG")*/);
+}
+
 #else
 
 /* Homegrown Unicode support. It knows only C and Unicode locales. */
 
 # if ENABLE_FEATURE_CHECK_UNICODE_IN_ENV
-void FAST_FUNC init_unicode(void)
+void FAST_FUNC reinit_unicode(const char *LANG)
 {
-	char *lang;
-
-	if (unicode_status != UNICODE_UNKNOWN)
-		return;
-
 	unicode_status = UNICODE_OFF;
-	lang = getenv("LANG");
-	if (!lang || !(strstr(lang, ".utf") || strstr(lang, ".UTF")))
+	if (!LANG || !(strstr(LANG, ".utf") || strstr(LANG, ".UTF")))
 		return;
 	unicode_status = UNICODE_ON;
 }
+
+void FAST_FUNC init_unicode(void)
+{
+	if (unicode_status == UNICODE_UNKNOWN)
+		reinit_unicode(getenv("LANG"));
+}
 # endif
 
 static size_t wcrtomb_internal(char *s, wchar_t wc)
diff --git a/shell/ash.c b/shell/ash.c
index 0baf7c8e5..1520c5ae5 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -36,12 +36,14 @@
 
 #define JOBS ENABLE_ASH_JOB_CONTROL
 
-#include "busybox.h" /* for applet_names */
 #include <paths.h>
 #include <setjmp.h>
 #include <fnmatch.h>
 #include <sys/times.h>
 
+#include "busybox.h" /* for applet_names */
+#include "unicode.h"
+
 #include "shell_common.h"
 #if ENABLE_SH_MATH_SUPPORT
 # include "math.h"
@@ -72,13 +74,6 @@
 # error "Do not even bother, ash will not run on NOMMU machine"
 #endif
 
-//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
-//applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, sh))
-//applet:IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, bash))
-
-//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
-//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
-
 //config:config ASH
 //config:	bool "ash"
 //config:	default y
@@ -190,6 +185,13 @@
 //config:	  variable each time it is displayed.
 //config:
 
+//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
+//applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, sh))
+//applet:IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, bash))
+
+//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
+//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
+
 
 /* ============ Hash table sizes. Configurable. */
 
@@ -9626,6 +9628,11 @@ preadfd(void)
 # if ENABLE_FEATURE_TAB_COMPLETION
 		line_input_state->path_lookup = pathval();
 # endif
+		/* Unicode support should be activated even if LANG is set
+		 * _during_ shell execution, not only if it was set when
+		 * shell was started. Therefore, re-check LANG every time:
+		 */
+		reinit_unicode(lookupvar("LANG"));
 		nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ, timeout);
 		if (nr == 0) {
 			/* Ctrl+C pressed */
diff --git a/shell/hush.c b/shell/hush.c
index 64d5e8587..339f3349a 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -81,7 +81,6 @@
  *              $ "export" i=`echo 'aaa  bbb'`; echo "$i"
  *              aaa
  */
-#include "busybox.h"  /* for APPLET_IS_NOFORK/NOEXEC */
 #if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \
 	|| defined(__APPLE__) \
     )
@@ -93,6 +92,8 @@
 # include <fnmatch.h>
 #endif
 
+#include "busybox.h"  /* for APPLET_IS_NOFORK/NOEXEC */
+#include "unicode.h"
 #include "shell_common.h"
 #include "math.h"
 #include "match.h"
@@ -105,14 +106,6 @@
 # define PIPE_BUF 4096  /* amount of buffering in a pipe */
 #endif
 
-//applet:IF_HUSH(APPLET(hush, BB_DIR_BIN, BB_SUID_DROP))
-//applet:IF_MSH(APPLET(msh, BB_DIR_BIN, BB_SUID_DROP))
-//applet:IF_FEATURE_SH_IS_HUSH(APPLET_ODDNAME(sh, hush, BB_DIR_BIN, BB_SUID_DROP, sh))
-//applet:IF_FEATURE_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, BB_DIR_BIN, BB_SUID_DROP, bash))
-
-//kbuild:lib-$(CONFIG_HUSH) += hush.o match.o shell_common.o
-//kbuild:lib-$(CONFIG_HUSH_RANDOM_SUPPORT) += random.o
-
 //config:config HUSH
 //config:	bool "hush"
 //config:	default y
@@ -249,6 +242,14 @@
 //config:	  msh is deprecated and will be removed, please migrate to hush.
 //config:
 
+//applet:IF_HUSH(APPLET(hush, BB_DIR_BIN, BB_SUID_DROP))
+//applet:IF_MSH(APPLET(msh, BB_DIR_BIN, BB_SUID_DROP))
+//applet:IF_FEATURE_SH_IS_HUSH(APPLET_ODDNAME(sh, hush, BB_DIR_BIN, BB_SUID_DROP, sh))
+//applet:IF_FEATURE_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, BB_DIR_BIN, BB_SUID_DROP, bash))
+
+//kbuild:lib-$(CONFIG_HUSH) += hush.o match.o shell_common.o
+//kbuild:lib-$(CONFIG_HUSH_RANDOM_SUPPORT) += random.o
+
 /* -i (interactive) and -s (read stdin) are also accepted,
  * but currently do nothing, therefore aren't shown in help.
  * NOMMU-specific options are not meant to be used by users,
@@ -1906,6 +1907,12 @@ static void get_user_input(struct in_str *i)
 	/* Enable command line editing only while a command line
 	 * is actually being read */
 	do {
+		/* Unicode support should be activated even if LANG is set
+		 * _during_ shell execution, not only if it was set when
+		 * shell was started. Therefore, re-check LANG every time:
+		 */
+		reinit_unicode(get_local_var_value("LANG"));
+
 		G.flag_SIGINT = 0;
 		/* buglet: SIGINT will not make new prompt to appear _at once_,
 		 * only after <Enter>. (^C will work) */
-- 
cgit v1.2.3-55-g6feb


From 68d5cb5dacbc80347119ac9cff365e5f04105ef1 Mon Sep 17 00:00:00 2001
From: Denys Vlasenko <vda.linux@googlemail.com>
Date: Thu, 24 Mar 2011 02:50:03 +0100
Subject: hush: fix a case where return in sourced file has no effect

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
---
 shell/hush.c                            | 4 ++++
 shell/hush_test/hush-misc/return1.right | 1 +
 shell/hush_test/hush-misc/return1.tests | 4 ++++
 3 files changed, 9 insertions(+)
 create mode 100644 shell/hush_test/hush-misc/return1.right
 create mode 100755 shell/hush_test/hush-misc/return1.tests

(limited to 'shell')

diff --git a/shell/hush.c b/shell/hush.c
index 339f3349a..8154ac47b 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -5554,6 +5554,10 @@ static void parse_and_run_stream(struct in_str *inp, int end_trigger)
 		debug_printf_exec("parse_and_run_stream: run_and_free_list\n");
 		run_and_free_list(pipe_list);
 		empty = 0;
+#if ENABLE_HUSH_FUNCTIONS
+		if (G.flag_return_in_progress == 1)
+			break;
+#endif
 	}
 }
 
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 @@
+echo "true && return; echo Should not be printed" >return_sourced
+. ./return_sourced
+rm return_sourced
+echo Ok:$?
-- 
cgit v1.2.3-55-g6feb


From 3eab24e64a65746ed9fcc5bed3e9a19a489b0573 Mon Sep 17 00:00:00 2001
From: Denys Vlasenko <vda.linux@googlemail.com>
Date: Thu, 24 Mar 2011 05:25:59 +0100
Subject: hush: make parse errors in sourced file non-fatal in interactive
 script

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
---
 shell/hush.c | 97 ++++++++++++++++++++++++++++++++----------------------------
 1 file changed, 52 insertions(+), 45 deletions(-)

(limited to 'shell')

diff --git a/shell/hush.c b/shell/hush.c
index 8154ac47b..e4c3a7d77 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -1095,17 +1095,10 @@ static void syntax_error_unterm_str(unsigned lineno, const char *s)
 	die_if_script(lineno, "syntax error: unterminated %s", s);
 }
 
-/* It so happens that all such cases are totally fatal
- * even if shell is interactive: EOF while looking for closing
- * delimiter. There is nowhere to read stuff from after that,
- * it's EOF! The only choice is to terminate.
- */
-static void syntax_error_unterm_ch(unsigned lineno, char ch) NORETURN;
 static void syntax_error_unterm_ch(unsigned lineno, char ch)
 {
 	char msg[2] = { ch, '\0' };
 	syntax_error_unterm_str(lineno, msg);
-	xfunc_die();
 }
 
 static void syntax_error_unexpected_ch(unsigned lineno, int ch)
@@ -3539,39 +3532,40 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
 
 #if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS
 /* Subroutines for copying $(...) and `...` things */
-static void add_till_backquote(o_string *dest, struct in_str *input, int in_dquote);
+static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote);
 /* '...' */
-static void add_till_single_quote(o_string *dest, struct in_str *input)
+static int add_till_single_quote(o_string *dest, struct in_str *input)
 {
 	while (1) {
 		int ch = i_getch(input);
 		if (ch == EOF) {
 			syntax_error_unterm_ch('\'');
-			/*xfunc_die(); - redundant */
+			return 0;
 		}
 		if (ch == '\'')
-			return;
+			return 1;
 		o_addchr(dest, ch);
 	}
 }
 /* "...\"...`..`...." - do we need to handle "...$(..)..." too? */
-static void add_till_double_quote(o_string *dest, struct in_str *input)
+static int add_till_double_quote(o_string *dest, struct in_str *input)
 {
 	while (1) {
 		int ch = i_getch(input);
 		if (ch == EOF) {
 			syntax_error_unterm_ch('"');
-			/*xfunc_die(); - redundant */
+			return 0;
 		}
 		if (ch == '"')
-			return;
+			return 1;
 		if (ch == '\\') {  /* \x. Copy both chars. */
 			o_addchr(dest, ch);
 			ch = i_getch(input);
 		}
 		o_addchr(dest, ch);
 		if (ch == '`') {
-			add_till_backquote(dest, input, /*in_dquote:*/ 1);
+			if (!add_till_backquote(dest, input, /*in_dquote:*/ 1))
+				return 0;
 			o_addchr(dest, ch);
 			continue;
 		}
@@ -3592,12 +3586,12 @@ static void add_till_double_quote(o_string *dest, struct in_str *input)
  * Example                               Output
  * echo `echo '\'TEST\`echo ZZ\`BEST`    \TESTZZBEST
  */
-static void add_till_backquote(o_string *dest, struct in_str *input, int in_dquote)
+static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote)
 {
 	while (1) {
 		int ch = i_getch(input);
 		if (ch == '`')
-			return;
+			return 1;
 		if (ch == '\\') {
 			/* \x. Copy both unless it is \`, \$, \\ and maybe \" */
 			ch = i_getch(input);
@@ -3611,7 +3605,7 @@ static void add_till_backquote(o_string *dest, struct in_str *input, int in_dquo
 		}
 		if (ch == EOF) {
 			syntax_error_unterm_ch('`');
-			/*xfunc_die(); - redundant */
+			return 0;
 		}
 		o_addchr(dest, ch);
 	}
@@ -3647,7 +3641,7 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign
 		ch = i_getch(input);
 		if (ch == EOF) {
 			syntax_error_unterm_ch(end_ch);
-			/*xfunc_die(); - redundant */
+			return 0;
 		}
 		if (ch == end_ch  IF_HUSH_BASH_COMPAT( || ch == end_char2)) {
 			if (!dbl)
@@ -3661,22 +3655,26 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign
 		o_addchr(dest, ch);
 		if (ch == '(' || ch == '{') {
 			ch = (ch == '(' ? ')' : '}');
-			add_till_closing_bracket(dest, input, ch);
+			if (!add_till_closing_bracket(dest, input, ch))
+				return 0;
 			o_addchr(dest, ch);
 			continue;
 		}
 		if (ch == '\'') {
-			add_till_single_quote(dest, input);
+			if (!add_till_single_quote(dest, input))
+				return 0;
 			o_addchr(dest, ch);
 			continue;
 		}
 		if (ch == '"') {
-			add_till_double_quote(dest, input);
+			if (!add_till_double_quote(dest, input))
+				return 0;
 			o_addchr(dest, ch);
 			continue;
 		}
 		if (ch == '`') {
-			add_till_backquote(dest, input, /*in_dquote:*/ 0);
+			if (!add_till_backquote(dest, input, /*in_dquote:*/ 0))
+				return 0;
 			o_addchr(dest, ch);
 			continue;
 		}
@@ -3685,7 +3683,7 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign
 			ch = i_getch(input);
 			if (ch == EOF) {
 				syntax_error_unterm_ch(')');
-				/*xfunc_die(); - redundant */
+				return 0;
 			}
 			o_addchr(dest, ch);
 			continue;
@@ -3756,8 +3754,8 @@ static int parse_dollar(o_string *as_string,
 		) {
  bad_dollar_syntax:
 			syntax_error_unterm_str("${name}");
-			debug_printf_parse("parse_dollar return 1: unterminated ${name}\n");
-			return 1;
+			debug_printf_parse("parse_dollar return 0: unterminated ${name}\n");
+			return 0;
 		}
 		nommu_addchr(as_string, ch);
 		ch |= quote_mask;
@@ -3813,6 +3811,8 @@ static int parse_dollar(o_string *as_string,
 					pos = dest->length;
 #if ENABLE_HUSH_DOLLAR_OPS
 				last_ch = add_till_closing_bracket(dest, input, end_ch);
+				if (last_ch == 0) /* error? */
+					return 0;
 #else
 #error Simple code to only allow ${var} is not implemented
 #endif
@@ -3857,7 +3857,8 @@ static int parse_dollar(o_string *as_string,
 			o_addchr(dest, /*quote_mask |*/ '+');
 			if (!BB_MMU)
 				pos = dest->length;
-			add_till_closing_bracket(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG);
+			if (!add_till_closing_bracket(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG))
+				return 0; /* error */
 			if (as_string) {
 				o_addstr(as_string, dest->data + pos);
 				o_addchr(as_string, ')');
@@ -3872,7 +3873,8 @@ static int parse_dollar(o_string *as_string,
 		o_addchr(dest, quote_mask | '`');
 		if (!BB_MMU)
 			pos = dest->length;
-		add_till_closing_bracket(dest, input, ')');
+		if (!add_till_closing_bracket(dest, input, ')'))
+			return 0; /* error */
 		if (as_string) {
 			o_addstr(as_string, dest->data + pos);
 			o_addchr(as_string, ')');
@@ -3899,8 +3901,8 @@ static int parse_dollar(o_string *as_string,
 	default:
 		o_addQchr(dest, '$');
 	}
-	debug_printf_parse("parse_dollar return 0\n");
-	return 0;
+	debug_printf_parse("parse_dollar return 1 (ok)\n");
+	return 1;
 #undef as_string
 }
 
@@ -3941,13 +3943,13 @@ static int encode_string(o_string *as_string,
 	if (ch != EOF)
 		nommu_addchr(as_string, ch);
 	if (ch == dquote_end) { /* may be only '"' or EOF */
-		debug_printf_parse("encode_string return 0\n");
-		return 0;
+		debug_printf_parse("encode_string return 1 (ok)\n");
+		return 1;
 	}
 	/* note: can't move it above ch == dquote_end check! */
 	if (ch == EOF) {
 		syntax_error_unterm_ch('"');
-		/*xfunc_die(); - redundant */
+		return 0; /* error */
 	}
 	next = '\0';
 	if (ch != '\n') {
@@ -3978,10 +3980,10 @@ static int encode_string(o_string *as_string,
 		goto again;
 	}
 	if (ch == '$') {
-		if (parse_dollar(as_string, dest, input, /*quote_mask:*/ 0x80) != 0) {
-			debug_printf_parse("encode_string return 1: "
-					"parse_dollar returned non-0\n");
-			return 1;
+		if (!parse_dollar(as_string, dest, input, /*quote_mask:*/ 0x80)) {
+			debug_printf_parse("encode_string return 0: "
+					"parse_dollar returned 0 (error)\n");
+			return 0;
 		}
 		goto again;
 	}
@@ -3990,7 +3992,8 @@ static int encode_string(o_string *as_string,
 		//unsigned pos = dest->length;
 		o_addchr(dest, SPECIAL_VAR_SYMBOL);
 		o_addchr(dest, 0x80 | '`');
-		add_till_backquote(dest, input, /*in_dquote:*/ dquote_end == '"');
+		if (!add_till_backquote(dest, input, /*in_dquote:*/ dquote_end == '"'))
+			return 0; /* error */
 		o_addchr(dest, SPECIAL_VAR_SYMBOL);
 		//debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
 		goto again;
@@ -4061,8 +4064,8 @@ static struct pipe *parse_stream(char **pstring,
 			/* end_trigger == '}' case errors out earlier,
 			 * checking only ')' */
 			if (end_trigger == ')') {
-				syntax_error_unterm_ch('('); /* exits */
-				/* goto parse_error; */
+				syntax_error_unterm_ch('(');
+				goto parse_error;
 			}
 
 			if (done_word(&dest, &ctx)) {
@@ -4353,9 +4356,9 @@ static struct pipe *parse_stream(char **pstring,
 			dest.has_quoted_part = 1;
 			break;
 		case '$':
-			if (parse_dollar(&ctx.as_string, &dest, input, /*quote_mask:*/ 0) != 0) {
+			if (!parse_dollar(&ctx.as_string, &dest, input, /*quote_mask:*/ 0)) {
 				debug_printf_parse("parse_stream parse error: "
-					"parse_dollar returned non-0\n");
+					"parse_dollar returned 0 (error)\n");
 				goto parse_error;
 			}
 			break;
@@ -4365,7 +4368,7 @@ static struct pipe *parse_stream(char **pstring,
 				ch = i_getch(input);
 				if (ch == EOF) {
 					syntax_error_unterm_ch('\'');
-					/*xfunc_die(); - redundant */
+					goto parse_error;
 				}
 				nommu_addchr(&ctx.as_string, ch);
 				if (ch == '\'')
@@ -4377,7 +4380,7 @@ static struct pipe *parse_stream(char **pstring,
 			dest.has_quoted_part = 1;
 			if (dest.o_assignment == NOT_ASSIGNMENT)
 				dest.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS;
-			if (encode_string(&ctx.as_string, &dest, input, '"', /*process_bkslash:*/ 1))
+			if (!encode_string(&ctx.as_string, &dest, input, '"', /*process_bkslash:*/ 1))
 				goto parse_error;
 			dest.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS;
 			break;
@@ -4388,7 +4391,8 @@ static struct pipe *parse_stream(char **pstring,
 			o_addchr(&dest, SPECIAL_VAR_SYMBOL);
 			o_addchr(&dest, '`');
 			pos = dest.length;
-			add_till_backquote(&dest, input, /*in_dquote:*/ 0);
+			if (!add_till_backquote(&dest, input, /*in_dquote:*/ 0))
+				goto parse_error;
 # if !BB_MMU
 			o_addstr(&ctx.as_string, dest.data + pos);
 			o_addchr(&ctx.as_string, '`');
@@ -4664,6 +4668,7 @@ static char *encode_then_expand_string(const char *str, int process_bkslash, int
 	 */
 	setup_string_in_str(&input, str);
 	encode_string(NULL, &dest, &input, EOF, process_bkslash);
+//TODO: error check (encode_string returns 0 on error)?
 	//bb_error_msg("'%s' -> '%s'", str, dest.data);
 	exp_str = expand_string_to_string(dest.data, /*unbackslash:*/ do_unbackslash);
 	//bb_error_msg("'%s' -> '%s'", dest.data, exp_str);
@@ -8625,6 +8630,8 @@ static int FAST_FUNC builtin_source(char **argv)
 #endif
 	save_and_replace_G_args(&sv, argv);
 
+//TODO: syntax errors in sourced file should never abort the "calling" script.
+//Try: bash -c '. ./bad_file; echo YES'
 	parse_and_run_file(input);
 	fclose(input);
 
-- 
cgit v1.2.3-55-g6feb


From c162bcdcd1c5cb000a8ceaf4413d4abdfa6e3dfc Mon Sep 17 00:00:00 2001
From: Denys Vlasenko <vda.linux@googlemail.com>
Date: Thu, 24 Mar 2011 05:38:51 +0100
Subject: hush: document a bug about aborting on sourced file error when
 non-interactive

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
---
 shell/hush_test/hush-misc/source1.right |  5 +++++
 shell/hush_test/hush-misc/source1.tests | 10 ++++++++++
 2 files changed, 15 insertions(+)
 create mode 100644 shell/hush_test/hush-misc/source1.right
 create mode 100755 shell/hush_test/hush-misc/source1.tests

(limited to 'shell')

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 @@
+hush: syntax error: unterminated ${name}
+line2
+Ok1:0
+hush: syntax error: unterminated '
+Ok2: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 @@
+echo 'echo ${^}
+echo line2' >sourced1
+. ./sourced1
+echo Ok1:$?
+
+echo "echo '" >sourced1
+. ./sourced1
+echo Ok2:$?
+
+rm sourced1
-- 
cgit v1.2.3-55-g6feb