svn commit: trunk/busybox/shell

landley at busybox.net landley at busybox.net
Fri Sep 8 16:59:09 UTC 2006


Author: landley
Date: 2006-09-08 09:59:08 -0700 (Fri, 08 Sep 2006)
New Revision: 16069

Log:
Second drop.  More infrastructure in place, especially for parsing pipelines.
The minimal version got a couple hundred bytes bigger, partly because it's
broken into more functions but mostly because it now dynamically reallocates
and frees everything with no hard-coded limits.  (I thought about making
that optional, but there's a limit to what I can debug and maintain.  It
should still be reasonably NOMMU friendly, the allocations are small and
short lived, and shouldn't contribute noticeably to long-term memory
fragmentation.)


Modified:
   trunk/busybox/shell/bbsh.c


Changeset:
Modified: trunk/busybox/shell/bbsh.c
===================================================================
--- trunk/busybox/shell/bbsh.c	2006-09-08 00:41:48 UTC (rev 16068)
+++ trunk/busybox/shell/bbsh.c	2006-09-08 16:59:08 UTC (rev 16069)
@@ -7,49 +7,197 @@
  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  */
 
-// Handle embedded NUL bytes in the command line.
+// A section of code that gets repeatedly or conditionally executed is stored
+// as a string and parsed each time it's run.
 
+
+
+// Wheee, debugging.
+
+// Terminal control
+#define ENABLE_BBSH_TTY        0
+
+// &, fg, bg, jobs.  (ctrl-z with tty.)
+#define ENABLE_BBSH_JOBCTL     0
+
+// Flow control (if, while, for, functions { })
+#define ENABLE_BBSH_FLOWCTL    0
+
+#define ENABLE_BBSH_ENVVARS    0  // Environment variable support
+
+// Local and synthetic variables, fancy prompts, set, $?, etc.
+#define ENABLE_BBSH_LOCALVARS  0
+
+// Pipes and redirects: | > < >> << && || & () ;
+#define ENABLE_BBSH_PIPES      0
+
+/* Fun:
+
+  echo `echo hello#comment " woot` and more
+*/
+
 #include <busybox.h>
 
-static int handle(char *command)
+// A single executable, its arguments, and other information we know about it.
+#define BBSH_FLAG_EXIT    1
+#define BBSH_FLAG_SUSPEND 2
+#define BBSH_FLAG_PIPE    4
+#define BBSH_FLAG_AND     8
+#define BBSH_FLAG_OR      16
+#define BBSH_FLAG_AMP     32
+#define BBSH_FLAG_SEMI    64
+#define BBSH_FLAG_PAREN   128
+
+// What we know about a single process.
+struct command {
+	struct command *next;
+	int flags;		// exit, suspend, && || 
+	int pid;		// pid (or exit code)
+	int argc;
+	char *argv[0];
+};
+
+// A collection of processes piped into/waiting on each other.
+struct pipeline {
+	struct pipeline *next;
+	int job_id;
+	struct command *cmd;
+	char *cmdline;
+	int cmdlinelen;
+};
+
+static void free_list(void *list, void (*freeit)(void *data))
 {
-	int argc=0;
-	char *argv[10], *start = command;
+	while(list) {
+		void **next = (void **)list;
+		void *list_next = *next;
+		freeit(list);
+		free(list);
+		list = list_next;
+	}
+}
 
+// Parse one word from the command line, appending one or more argv[] entries
+// to struct command.  Handles environment variable substitution and
+// substrings.  Returns pointer to next used byte, or NULL if it
+// hit an ending token.
+static char *parse_word(char *start, struct command **cmd)
+{
+	char *end;
+
+	// Detect end of line (and truncate line at comment)
+	if (ENABLE_BBSH_PIPES && strchr("><&|(;", *start)) return 0;
+
+	// Grab next word.  (Add dequote and envvar logic here)
+	end = start;
+	while (*end && !isspace(*end)) end++;
+	(*cmd)->argv[(*cmd)->argc++] = xstrndup(start, end-start);
+
+	// Allocate more space if there's no room for NULL terminator.
+
+	if (!((*cmd)->argc & 7))
+			*cmd = xrealloc(*cmd,
+					sizeof(struct command) + ((*cmd)->argc+8)*sizeof(char *));
+	(*cmd)->argv[(*cmd)->argc] = 0;
+	return end;
+}
+
+// Parse a line of text into a pipeline.
+// Returns a pointer to the next line.
+
+static char *parse_pipeline(char *cmdline, struct pipeline *pipe)
+{
+	struct command **cmd = &(pipe->cmd);
+	char *start = pipe->cmdline = cmdline;
+
+	if (!cmdline) return 0;
+
+	if (ENABLE_BBSH_JOBCTL) pipe->cmdline = cmdline;
+
 	// Parse command into argv[]
 	for (;;) {
 		char *end;
 
-		// Skip leading whitespace and detect EOL.
-		while(isspace(*start)) start++;
-		if(!*start || *start=='#') break;
+		// Skip leading whitespace and detect end of line.
+		while (isspace(*start)) start++;
+		if (!*start || *start=='#') {
+			if (ENABLE_BBSH_JOBCTL) pipe->cmdlinelen = start-cmdline;
+			return 0;
+		}
 
-		// Grab next word.  (Add dequote and envvar logic here)
-		end=start;
-		while(*end && !isspace(*end)) end++;
-		argv[argc++]=xstrndup(start,end-start);
-		start=end;
+		// Allocate next command structure if necessary		
+		if (!*cmd) *cmd = xzalloc(sizeof(struct command)+8*sizeof(char *));
+		
+		// Parse next argument and add the results to argv[]
+		end = parse_word(start, cmd);
+
+		// If we hit the end of this command, how did it end?
+		if (!end) {
+			if (ENABLE_BBSH_PIPES && *start) {
+				if (*start==';') {
+					start++;
+					break;
+				}
+				// handle | & < > >> << || && 
+			}
+			break;
+		}
+		start = end;
 	}
-	argv[argc]=0;
 
-	if (!argc) return 0;
-	if (argc==2 && !strcmp(argv[0],"cd")) chdir(argv[1]);
-	else if(!strcmp(argv[0],"exit")) exit(argc>1 ? atoi(argv[1]) : 0); 
+	if (ENABLE_BBSH_JOBCTL) pipe->cmdlinelen = start-cmdline;
+
+	return start;
+}
+
+// Execute the commands in a pipeline
+static int run_pipeline(struct pipeline *pipe)
+{
+	struct command *cmd = pipe->cmd;
+	if (!cmd || !cmd->argc) return 0;
+
+	// Handle local commands.  This is totally fake and plastic.
+	if (cmd->argc==2 && !strcmp(cmd->argv[0],"cd"))
+		chdir(cmd->argv[1]);
+	else if(!strcmp(cmd->argv[0],"exit"))
+		exit(cmd->argc>1 ? atoi(cmd->argv[1]) : 0); 
 	else {
 		int status;
 		pid_t pid=fork();
 		if(!pid) {
-			run_applet_by_name(argv[0],argc,argv);
-			execvp(argv[0],argv);
-			printf("No %s",argv[0]);
+			run_applet_by_name(cmd->argv[0],cmd->argc,cmd->argv);
+			execvp(cmd->argv[0],cmd->argv);
+			printf("No %s",cmd->argv[0]);
 			exit(1);
 		} else waitpid(pid, &status, 0);
 	}
-	while(argc) free(argv[--argc]);
 
 	return 0;
 }
 
+static void free_cmd(void *data)
+{
+	struct command *cmd=(struct command *)data;
+
+	while(cmd->argc) free(cmd->argv[--cmd->argc]);
+}
+
+
+static void handle(char *command)
+{
+	struct pipeline pipe;
+	char *start = command;
+
+	for (;;) {
+		memset(&pipe,0,sizeof(struct pipeline));
+		start = parse_pipeline(start, &pipe);
+		if (!pipe.cmd) break;
+
+		run_pipeline(&pipe);
+		free_list(pipe.cmd, free_cmd);
+	}
+}
+
 int bbsh_main(int argc, char *argv[])
 {
 	char *command=NULL;
@@ -62,8 +210,11 @@
 	else {
 		unsigned cmdlen=0;
 		for (;;) {
+			struct pipeline pipe;
+
 			if(!f) putchar('$');
-			if(1 > getline(&command,&cmdlen,f ? : stdin)) break;
+			if(1 > getline(&command, &cmdlen,f ? : stdin)) break;
+
 			handle(command);
 		}
 		if (ENABLE_FEATURE_CLEAN_UP) free(command);




More information about the busybox-cvs mailing list