[BusyBox] builtin echo for ash

Paul Fox pgf at brightstareng.com
Fri Aug 5 21:05:06 UTC 2005


i've implemented a builtin echo command in ash.  i didn't have to
do much -- i moved the guts of the echo applet into libbb, and
now call bb_echo() from both echo.c and ash.c.  so the code size
impact of having both the applet and the builtin is minimal.

it seems to work, and on our platform (400mhz MPC5200, with small
caches) is at least 60x faster.  (running a script of 4000 echo
statements takes under a second with the builtin, versus about
one minute otherwise.)

i'll attach the patch here for review -- can anyone think of a
reason that this might break badly in ash, in some odd
redirection case, perhaps?  our system seems to be working fine,
and i suspect we use scripting and echo at least as much as
anyone else.

i have a few further changes to make, mostly having to do with
moving the copyrights on echo to be with the echo code, rather
than with the now almost-null caller.  i didn't want to clutter
this patch any further with those changes.

one other implementation question:  moving the echo code into
libbb/bb_echo.c now requires that CONFIG_FANCY_ECHO be accessible
from somewhere other than just the coreutils config menu, since
you might want to configure the builtin echo without configuring
the echo applet.  i wasn't sure how to handle this, so i simply
duplicated the CONFIG_FANCY_ECHO option into the ash config menu. 
this isn't right -- what should i do instead?

(you'll also notice that the new ENABLE_XXX symbols were useful for
making a bunch of ifdef junk in ash.c go away, since one can now
do math with the config vars.  nice.)

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


Index: shell/Config.in
===================================================================
--- shell/Config.in	(revision 11050)
+++ shell/Config.in	(working copy)
@@ -103,6 +103,20 @@
 	  you to run the specified command with the specified arguments,
 	  even when there is an ash builtin command with the same name.
 
+config CONFIG_ASH_BUILTIN_ECHO
+	bool "  Enable builtin version of 'echo'"
+	default n
+	depends on CONFIG_ASH
+	help
+	  Enable support for echo, built in to ash.
+
+config CONFIG_FEATURE_FANCY_ECHO
+	bool "  Enable echo options (-n and -e)"
+	default y
+	depends on CONFIG_ASH_BUILTIN_ECHO
+	help
+	  This adds options (-n and -e) to echo.
+
 config CONFIG_ASH_MAIL
 	bool "  Check for new mail on interactive shells"
 	default y
Index: shell/ash.c
===================================================================
--- shell/ash.c	(revision 11050)
+++ shell/ash.c	(working copy)
@@ -1249,6 +1249,9 @@
 #endif
 static int dotcmd(int, char **);
 static int evalcmd(int, char **);
+#ifdef CONFIG_ASH_BUILTIN_ECHO
+static int echocmd(int, char **);
+#endif
 static int execcmd(int, char **);
 static int exitcmd(int, char **);
 static int exportcmd(int, char **);
@@ -1308,40 +1311,13 @@
 	/* unsigned flags; */
 };
 
-#ifdef CONFIG_ASH_CMDCMD
-# ifdef JOBS
-#  ifdef CONFIG_ASH_ALIAS
-#    define COMMANDCMD (builtincmd + 7)
-#    define EXECCMD (builtincmd + 10)
-#  else
-#    define COMMANDCMD (builtincmd + 6)
-#    define EXECCMD (builtincmd + 9)
-#  endif
-# else /* ! JOBS */
-#  ifdef CONFIG_ASH_ALIAS
-#    define COMMANDCMD (builtincmd + 6)
-#    define EXECCMD (builtincmd + 9)
-#  else
-#    define COMMANDCMD (builtincmd + 5)
-#    define EXECCMD (builtincmd + 8)
-#  endif
-# endif /* JOBS */
-#else   /* ! CONFIG_ASH_CMDCMD */
-# ifdef JOBS
-#  ifdef CONFIG_ASH_ALIAS
-#    define EXECCMD (builtincmd + 9)
-#  else
-#    define EXECCMD (builtincmd + 8)
-#  endif
-# else /* ! JOBS */
-#  ifdef CONFIG_ASH_ALIAS
-#    define EXECCMD (builtincmd + 8)
-#  else
-#    define EXECCMD (builtincmd + 7)
-#  endif
-# endif /* JOBS */
-#endif /* CONFIG_ASH_CMDCMD */
 
+#define COMMANDCMD (builtincmd + 5 + \
+	ENABLE_ASH_ALIAS + ENABLE_ASH_JOB_CONTROL)
+#define EXECCMD (builtincmd + 7 + \
+	ENABLE_ASH_CMDCMD + ENABLE_ASH_ALIAS + \
+	ENABLE_ASH_BUILTIN_ECHO + ENABLE_ASH_JOB_CONTROL)
+
 #define BUILTIN_NOSPEC  "0"
 #define BUILTIN_SPECIAL "1"
 #define BUILTIN_REGULAR "2"
@@ -1371,6 +1347,9 @@
 	{ BUILTIN_REGULAR       "command", commandcmd },
 #endif
 	{ BUILTIN_SPEC_REG      "continue", breakcmd },
+#ifdef CONFIG_ASH_BUILTIN_ECHO
+	{ BUILTIN_REGULAR       "echo", echocmd },
+#endif
 	{ BUILTIN_SPEC_REG      "eval", evalcmd },
 	{ BUILTIN_SPEC_REG      "exec", execcmd },
 	{ BUILTIN_SPEC_REG      "exit", exitcmd },
@@ -8200,6 +8179,14 @@
 	/* NOTREACHED */
 }
 
+#ifdef CONFIG_ASH_BUILTIN_ECHO
+static int
+echocmd(int argc, char **argv)
+{
+	bb_echo(argc, argv);
+	return 0;
+}
+#endif
 /*      $NetBSD: memalloc.c,v 1.27 2003/01/22 20:36:04 dsl Exp $        */
 
 /*
Index: coreutils/echo.c
===================================================================
--- coreutils/echo.c	(revision 11050)
+++ coreutils/echo.c	(working copy)
@@ -42,86 +42,7 @@
 
 extern int echo_main(int argc, char** argv)
 {
-#ifndef CONFIG_FEATURE_FANCY_ECHO
-#define eflag '\\'
-	++argv;
-#else
-	const char *p;
-	int nflag = 1;
-	int eflag = 0;
-
-	while (*++argv && (**argv == '-')) {
-		/* If it appears that we are handling options, then make sure
-		 * that all of the options specified are actually valid.
-		 * Otherwise, the string should just be echoed.
-		 */
-
-		if (!*(p = *argv + 1)) {	/* A single '-', so echo it. */
-			goto just_echo;
-		}
-
-		do {
-			if (strrchr("neE", *p) == 0) {
-				goto just_echo;
-			}
-		} while (*++p);
-
-		/* All of the options in this arg are valid, so handle them. */
-		p = *argv + 1;
-		do {
-			if (*p == 'n') {
-				nflag = 0;
-			} else if (*p == 'e') {
-				eflag = '\\';
-			} else {
-				eflag = 0;
-			}
-		} while (*++p);
-	}
-
-just_echo:
-#endif
-	while (*argv) {
-		register int c;
-
-		while ((c = *(*argv)++)) {
-			if (c == eflag) {	/* Check for escape seq. */
-				if (**argv == 'c') {
-					/* '\c' means cancel newline and
-					 * ignore all subsequent chars. */
-					goto DONE;
-				}
-#ifndef CONFIG_FEATURE_FANCY_ECHO
-				/* SUSv3 specifies that octal escapes must begin with '0'. */
-				if (((unsigned int)(**argv - '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)++;
-					}
-					/* bb_process_escape_sequence can handle nul correctly */
-					c = bb_process_escape_sequence((const char **) argv);
-				}
-			}
-			putchar(c);
-		}
-
-		if (*++argv) {
-			putchar(' ');
-		}
-	}
-
-#ifdef CONFIG_FEATURE_FANCY_ECHO
-	if (nflag) {
-		putchar('\n');
-	}
-#else
-	putchar('\n');
-#endif
-
-DONE:
+	bb_echo(argc, argv);
 	bb_fflush_stdout_and_exit(EXIT_SUCCESS);
 }
 
Index: libbb/Makefile.in
===================================================================
--- libbb/Makefile.in	(revision 11050)
+++ libbb/Makefile.in	(working copy)
@@ -46,7 +46,8 @@
 	get_terminal_width_height.c fclose_nonstdin.c fflush_stdout_and_exit.c \
 	getopt_ulflags.c default_error_retval.c wfopen_input.c speed_table.c \
 	perror_nomsg_and_die.c perror_nomsg.c skip_whitespace.c bb_askpass.c \
-	warn_ignoring_args.c concat_subpath_file.c vfork_daemon_rexec.c
+	warn_ignoring_args.c concat_subpath_file.c vfork_daemon_rexec.c \
+	bb_echo.c
 
 LIBBB_OBJS=$(patsubst %.c,$(LIBBB_DIR)%.o, $(LIBBB_SRC))
 
Index: libbb/bb_echo.c
===================================================================
--- libbb/bb_echo.c	(revision 0)
+++ libbb/bb_echo.c	(revision 0)
@@ -0,0 +1,87 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "busybox.h"
+
+extern int bb_echo(int argc, char** argv)
+{
+#ifndef CONFIG_FEATURE_FANCY_ECHO
+#define eflag '\\'
+	++argv;
+#else
+	const char *p;
+	int nflag = 1;
+	int eflag = 0;
+
+	while (*++argv && (**argv == '-')) {
+		/* If it appears that we are handling options, then make sure
+		 * that all of the options specified are actually valid.
+		 * Otherwise, the string should just be echoed.
+		 */
+
+		if (!*(p = *argv + 1)) {	/* A single '-', so echo it. */
+			goto just_echo;
+		}
+
+		do {
+			if (strrchr("neE", *p) == 0) {
+				goto just_echo;
+			}
+		} while (*++p);
+
+		/* All of the options in this arg are valid, so handle them. */
+		p = *argv + 1;
+		do {
+			if (*p == 'n') {
+				nflag = 0;
+			} else if (*p == 'e') {
+				eflag = '\\';
+			} else {
+				eflag = 0;
+			}
+		} while (*++p);
+	}
+
+just_echo:
+#endif
+	while (*argv) {
+		register int c;
+
+		while ((c = *(*argv)++)) {
+			if (c == eflag) {	/* Check for escape seq. */
+				if (**argv == '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)
+#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)++;
+					}
+					/* bb_process_escape_sequence can handle nul correctly */
+					c = bb_process_escape_sequence((const char **) argv);
+				}
+			}
+			putchar(c);
+		}
+
+		if (*++argv) {
+			putchar(' ');
+		}
+	}
+
+#ifdef CONFIG_FEATURE_FANCY_ECHO
+	if (nflag) {
+		putchar('\n');
+	}
+#else
+	putchar('\n');
+#endif
+	return 0;
+}
Index: include/libbb.h
===================================================================
--- include/libbb.h	(revision 11050)
+++ include/libbb.h	(working copy)
@@ -105,6 +105,8 @@
 extern void bb_verror_msg(const char *s, va_list p) __attribute__ ((format (printf, 1, 0)));
 extern void bb_vperror_msg(const char *s, va_list p)  __attribute__ ((format (printf, 1, 0)));
 
+extern int bb_echo(int argc, char** argv);
+
 extern const char *bb_mode_string(int mode);
 extern int is_directory(const char *name, int followLinks, struct stat *statBuf);
 



More information about the busybox mailing list