echo and test builtins for msh/hush

Paul Fox pgf at brightstareng.com
Thu Jun 15 19:52:23 PDT 2006


when i started looking at msh the other day, i realized that it
would probably make some people happy if the other scriptable
shells in busybox had the builtin echo and test improvements.
(particularly msh, since it seems to be the only uclinux
compatible shell, correct?)

so i added them to msh, and to hush.  i didn't add them to lash
since it's not really scriptable, and this only really makes a
difference in that case.

patch below.  i can apply this, but rob asked for a moratorium, i
think.  the only reason to apply it before 1.2.0 is that it moves
the CONFIG vars to ..._SH_... names, instead of ..._ASH_... 
names, and since the ...BUILTIN_TEST... name is new anyway, it
might make sense to move it now, rather than later.

the changes in echo.c are to keep the elements of the argv array
from changing.  hush wants to free them on return.

(but regarding hush -- if i run "busybox hush", then type an
external command, like "ls", i get no output at all.  is this
just me?  i thought i'd broken something this evening, until i
re-created it with some very old (near 1.00) sources.  what's
weird is that if i "strace -f" the process, output is being
generated -- i just never see it.)

paul
=---------------------
 paul fox, pgf at brightstareng.com

Index: shell/hush.c
===================================================================
--- shell/hush.c	(revision 15401)
+++ shell/hush.c	(working copy)
@@ -318,6 +318,9 @@
 /* Index of subroutines: */
 /*   function prototypes for builtins */
 static int builtin_cd(struct child_prog *child);
+#ifdef CONFIG_SH_BUILTIN_ECHO
+static int builtin_echo(struct child_prog *child);
+#endif
 static int builtin_env(struct child_prog *child);
 static int builtin_eval(struct child_prog *child);
 static int builtin_exec(struct child_prog *child);
@@ -331,6 +334,9 @@
 static int builtin_set(struct child_prog *child);
 static int builtin_shift(struct child_prog *child);
 static int builtin_source(struct child_prog *child);
+#ifdef CONFIG_SH_BUILTIN_TEST
+static int builtin_test(struct child_prog *child);
+#endif
 static int builtin_umask(struct child_prog *child);
 static int builtin_unset(struct child_prog *child);
 static int builtin_not_written(struct child_prog *child);
@@ -404,10 +410,17 @@
  * For example, 'unset foo | whatever' will parse and run, but foo will
  * still be set at the end. */
 static const struct built_in_command bltins[] = {
+#ifdef CONFIG_SH_BUILTIN_TEST
+	{"[", "Check various conditions", builtin_test },
+	{"[[", "Check various conditions", builtin_test },
+#endif
 	{"bg", "Resume a job in the background", builtin_fg_bg},
 	{"break", "Exit for, while or until loop", builtin_not_written},
 	{"cd", "Change working directory", builtin_cd},
 	{"continue", "Continue for, while or until loop", builtin_not_written},
+#ifdef CONFIG_SH_BUILTIN_ECHO
+	{"echo", "Print arguments", builtin_echo },
+#endif
 	{"env", "Print all environment variables", builtin_env},
 	{"eval", "Construct and run shell command", builtin_eval},
 	{"exec", "Exec command, replacing this shell with the exec'd process",
@@ -421,6 +434,9 @@
 	{"return", "Return from a function", builtin_not_written},
 	{"set", "Set/unset shell local variables", builtin_set},
 	{"shift", "Shift positional parameters", builtin_shift},
+#ifdef CONFIG_SH_BUILTIN_TEST
+	{"test", "Check various conditions", builtin_test },
+#endif
 	{"trap", "Trap signals", builtin_not_written},
 	{"ulimit","Controls resource limits", builtin_not_written},
 	{"umask","Sets file creation mask", builtin_umask},
@@ -472,6 +488,15 @@
 	return EXIT_SUCCESS;
 }
 
+#ifdef CONFIG_SH_BUILTIN_ECHO
+static int builtin_echo(struct child_prog *child)
+{
+	int n;
+	for (n = 1; child->argv[n]; n++);
+	return bb_echo(n, child->argv);
+}
+#endif
+
 /* built-in 'env' handler */
 static int builtin_env(struct child_prog *dummy ATTRIBUTE_UNUSED)
 {
@@ -734,6 +759,15 @@
 	return (status);
 }
 
+#ifdef CONFIG_SH_BUILTIN_TEST
+static int builtin_test(struct child_prog *child)
+{
+	int n;
+	for (n = 1; child->argv[n]; n++);
+	return bb_test(n, child->argv);
+}
+#endif
+
 static int builtin_umask(struct child_prog *child)
 {
 	mode_t new_umask;
Index: shell/msh.c
===================================================================
--- shell/msh.c	(revision 15401)
+++ shell/msh.c	(working copy)
@@ -599,6 +599,12 @@
 static int dobreak(struct op *t);
 static int docontinue(struct op *t);
 static int brkcontin(char *cp, int val);
+#ifdef CONFIG_SH_BUILTIN_ECHO
+static int doecho(struct op *t);
+#endif
+#ifdef CONFIG_SH_BUILTIN_TEST
+static int dotest(struct op *t);
+#endif
 static int doexit(struct op *t);
 static int doexport(struct op *t);
 static int doreadonly(struct op *t);
@@ -687,9 +693,16 @@
 static const struct builtincmd builtincmds[] = {
 	{".", dodot},
 	{":", dolabel},
+#ifdef CONFIG_SH_BUILTIN_TEST
+	{"[", dotest },
+	{"[[", dotest },
+#endif
 	{"break", dobreak},
 	{"cd", dochdir},
 	{"continue", docontinue},
+#ifdef CONFIG_SH_BUILTIN_ECHO
+	{"echo", doecho },
+#endif
 	{"eval", doeval},
 	{"exec", doexec},
 	{"exit", doexit},
@@ -701,6 +714,9 @@
 	{"readonly", doreadonly},
 	{"set", doset},
 	{"shift", doshift},
+#ifdef CONFIG_SH_BUILTIN_TEST
+	{"test", dotest },
+#endif
 	{"times", dotimes},
 	{"trap", dotrap},
 	{"umask", doumask},
@@ -3782,6 +3798,27 @@
 	return (0);
 }
 
+#ifdef CONFIG_SH_BUILTIN_ECHO
+static int doecho(t)
+struct op *t;
+{
+	int n;
+	for (n = 1; t->words[n]; n++);
+	return bb_echo(n, t->words);
+}
+#endif
+
+#ifdef CONFIG_SH_BUILTIN_TEST
+static int dotest(t)
+struct op *t;
+{
+	int n;
+	for (n = 1; t->words[n]; n++);
+	return bb_test(n, t->words);
+}
+#endif
+
+
 static int doexport(t)
 struct op *t;
 {
Index: shell/Config.in
===================================================================
--- shell/Config.in	(revision 15401)
+++ shell/Config.in	(working copy)
@@ -101,22 +101,6 @@
 	help
 	  Enable getopts builtin in the ash shell.
 
-config CONFIG_ASH_BUILTIN_ECHO
-	bool "Builtin version of 'echo'"
-	default y
-	select CONFIG_ECHO
-	depends on CONFIG_ASH
-	help
-	  Enable support for echo, built in to ash.
-
-config CONFIG_ASH_BUILTIN_TEST
-	bool "Builtin version of 'test'"
-	default y
-	select CONFIG_TEST
-	depends on CONFIG_ASH
-	help
-	  Enable support for test, built in to ash.
-
 config CONFIG_ASH_CMDCMD
 	bool "'command' command to override shell builtins"
 	default n
@@ -209,6 +193,22 @@
 comment "Bourne Shell Options"
 	depends on CONFIG_MSH || CONFIG_LASH || CONFIG_HUSH || CONFIG_ASH
 
+config CONFIG_SH_BUILTIN_ECHO
+	bool "Builtin version of 'echo'"
+	default y
+	select CONFIG_ECHO
+	depends on CONFIG_ASH || CONFIG_MSH || CONFIG_HUSH
+	help
+	  Enable support for echo, as a shell builtin in ash and msh.
+
+config CONFIG_SH_BUILTIN_TEST
+	bool "Builtin version of 'test'"
+	default y
+	select CONFIG_TEST
+	depends on CONFIG_ASH || CONFIG_MSH || CONFIG_HUSH
+	help
+	  Enable support for test, as a shell builtin in ash and msh.
+
 config CONFIG_FEATURE_SH_EXTRA_QUIET
 	bool "Hide message on interactive shell startup"
 	default n
Index: shell/ash.c
===================================================================
--- shell/ash.c	(revision 15401)
+++ shell/ash.c	(working copy)
@@ -1222,10 +1222,10 @@
 #endif
 static int dotcmd(int, char **);
 static int evalcmd(int, char **);
-#ifdef CONFIG_ASH_BUILTIN_ECHO
+#ifdef CONFIG_SH_BUILTIN_ECHO
 static int echocmd(int, char **);
 #endif
-#ifdef CONFIG_ASH_BUILTIN_TEST
+#ifdef CONFIG_SH_BUILTIN_TEST
 static int testcmd(int, char **);
 #endif
 static int execcmd(int, char **);
@@ -1289,15 +1289,15 @@
 
 
 #define COMMANDCMD (builtincmd + 5 + \
-	2 * ENABLE_ASH_BUILTIN_TEST + \
+	2 * ENABLE_SH_BUILTIN_TEST + \
 	ENABLE_ASH_ALIAS + \
 	ENABLE_ASH_JOB_CONTROL)
 #define EXECCMD (builtincmd + 7 + \
-	2 * ENABLE_ASH_BUILTIN_TEST + \
+	2 * ENABLE_SH_BUILTIN_TEST + \
 	ENABLE_ASH_ALIAS + \
 	ENABLE_ASH_JOB_CONTROL + \
 	ENABLE_ASH_CMDCMD + \
-	ENABLE_ASH_BUILTIN_ECHO)
+	ENABLE_SH_BUILTIN_ECHO)
 
 #define BUILTIN_NOSPEC  "0"
 #define BUILTIN_SPECIAL "1"
@@ -1315,7 +1315,7 @@
 static const struct builtincmd builtincmd[] = {
 	{ BUILTIN_SPEC_REG      ".", dotcmd },
 	{ BUILTIN_SPEC_REG      ":", truecmd },
-#ifdef CONFIG_ASH_BUILTIN_TEST
+#ifdef CONFIG_SH_BUILTIN_TEST
 	{ BUILTIN_REGULAR	"[", testcmd },
 	{ BUILTIN_REGULAR	"[[", testcmd },
 #endif
@@ -1332,7 +1332,7 @@
 	{ BUILTIN_REGULAR       "command", commandcmd },
 #endif
 	{ BUILTIN_SPEC_REG      "continue", breakcmd },
-#ifdef CONFIG_ASH_BUILTIN_ECHO
+#ifdef CONFIG_SH_BUILTIN_ECHO
 	{ BUILTIN_REGULAR       "echo", echocmd },
 #endif
 	{ BUILTIN_SPEC_REG      "eval", evalcmd },
@@ -1365,7 +1365,7 @@
 	{ BUILTIN_SPEC_REG      "set", setcmd },
 	{ BUILTIN_SPEC_REG      "source", dotcmd },
 	{ BUILTIN_SPEC_REG      "shift", shiftcmd },
-#ifdef CONFIG_ASH_BUILTIN_TEST
+#ifdef CONFIG_SH_BUILTIN_TEST
 	{ BUILTIN_REGULAR	"test", testcmd },
 #endif
 	{ BUILTIN_SPEC_REG      "times", timescmd },
@@ -8149,7 +8149,7 @@
 	/* NOTREACHED */
 }
 
-#ifdef CONFIG_ASH_BUILTIN_ECHO
+#ifdef CONFIG_SH_BUILTIN_ECHO
 static int
 echocmd(int argc, char **argv)
 {
@@ -8157,7 +8157,7 @@
 }
 #endif
 
-#ifdef CONFIG_ASH_BUILTIN_TEST
+#ifdef CONFIG_SH_BUILTIN_TEST
 static int
 testcmd(int argc, char **argv)
 {
Index: coreutils/echo.c
===================================================================
--- coreutils/echo.c	(revision 15401)
+++ coreutils/echo.c	(working copy)
@@ -72,26 +72,27 @@
 #endif
 	while (*argv) {
 		register int c;
+		const char *arg = *argv;
 
-		while ((c = *(*argv)++)) {
+		while ((c = *arg++)) {
 			if (c == eflag) {	/* Check for escape seq. */
-				if (**argv == 'c') {
+				if (*arg == 'c') {
 					/* '\c' means cancel newline and
 					 * ignore all subsequent chars. */
 					return 0;
 				}
 #ifndef CONFIG_FEATURE_FANCY_ECHO
 				/* SUSv3 specifies that octal escapes must begin with '0'. */
-				if (((unsigned int)(**argv - '1')) >= 7)
+				if (((unsigned int)(*arg - '1')) >= 7)
 #endif
 				{
 					/* Since SUSv3 mandates a first digit of 0, 4-digit octals
 					* of the form \0### are accepted. */
-					if ((**argv == '0') && (((unsigned int)(argv[0][1] - '0')) < 8)) {
-						(*argv)++;
+					if ((*arg == '0') && (((unsigned int)(arg[1] - '0')) < 8)) {
+						arg++;
 					}
 					/* bb_process_escape_sequence can handle nul correctly */
-					c = bb_process_escape_sequence((const char **) argv);
+					c = bb_process_escape_sequence(&arg);
 				}
 			}
 			putchar(c);


More information about the busybox mailing list