changeset 3:266a462ed18c

Next drop of toysh, plus more infratructure.
author landley@driftwood
date Wed, 18 Oct 2006 18:38:16 -0400
parents 67b517913e56
children 732b055e17f7
files Makefile lib/functions.c lib/getmountlist.c lib/lib.h main.c toys.h toys/toysh.c
diffstat 7 files changed, 213 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Thu Oct 05 16:18:03 2006 -0400
+++ b/Makefile	Wed Oct 18 18:38:16 2006 -0400
@@ -1,5 +1,5 @@
 all:
-	gcc -Os -s $(CFLAGS) -I . main.c toys/*.c lib/*.c -o toybox
+	$(CC) -Os -s $(CFLAGS) -I . main.c toys/*.c lib/*.c -o toybox
 
 clean:
 	rm toybox
--- a/lib/functions.c	Thu Oct 05 16:18:03 2006 -0400
+++ b/lib/functions.c	Wed Oct 18 18:38:16 2006 -0400
@@ -1,8 +1,10 @@
-/* vi: set ts=4 :*/
+/* vi: set sw=4 ts=4 :*/
 /* functions.c - reusable stuff.
  *
- * Functions with the x prefix never return failure, they either succeed or
- * kill the program with an error message.
+ * Functions with the x prefix are wrappers for library functions.  They either
+ * succeed or kill the program with an error message, but never return failure.
+ * They usually have the same arguments and return value as the function they
+ * wrap.
  */
 
 #include "toys.h"
@@ -19,6 +21,7 @@
 	exit(toys.exitval);
 }
 
+// Like strncpy but always null terminated.
 void strlcpy(char *dest, char *src, size_t size)
 {
 	strncpy(dest,src,size);
@@ -30,9 +33,27 @@
 {
 	void *ret = malloc(size);
 	if (!ret) error_exit("xmalloc");
+
+	return ret;
 }
 
-// Die unless we can copy this string.
+// Die unless we can allocate prezeroed memory.
+void *xzalloc(size_t size)
+{
+	void *ret = xmalloc(size);
+	bzero(ret,size);
+	return ret;
+}
+
+// Die unless we can change the size of an existing allocation, possibly
+// moving it.  (Notice different arguments from libc function.)
+void xrealloc(void **ptr, size_t size)
+{
+	*ptr = realloc(*ptr, size);
+	if (!*ptr) error_exit("xrealloc");
+}
+
+// Die unless we can allocate a copy of this string.
 void *xstrndup(char *s, size_t n)
 {
 	void *ret = xmalloc(++n);
@@ -41,9 +62,11 @@
 	return ret;
 }
 
-// Die unless we can exec argv[]
+// Die unless we can exec argv[] (or run builtin command).  Note that anything
+// with a path isn't a builtin, so /bin/sh won't match the builtin sh.
 void *xexec(char **argv)
 {
+	toy_exec(argv);
 	execvp(argv[0], argv);
 	error_exit("No %s", argv[0]);
 }
--- a/lib/getmountlist.c	Thu Oct 05 16:18:03 2006 -0400
+++ b/lib/getmountlist.c	Wed Oct 18 18:38:16 2006 -0400
@@ -1,4 +1,4 @@
-/* vi: set ts=4 : */
+/* vi: set sw=4 ts=4 : */
 
 #include "toys.h"
 
--- a/lib/lib.h	Thu Oct 05 16:18:03 2006 -0400
+++ b/lib/lib.h	Wed Oct 18 18:38:16 2006 -0400
@@ -1,13 +1,20 @@
 /* vi: set ts=4 :*/
 
+// functions.c
 void error_exit(char *msg, ...);
 void strlcpy(char *dest, char *src, size_t size);
 void *xmalloc(size_t size);
+void *xzalloc(size_t size);
+void xrealloc(void **ptr, size_t size);
 void *xstrndup(char *s, size_t n);
 void *xexec(char **argv);
 int xopen(char *path, int flags, int mode);
 FILE *xfopen(char *path, char *mode);
 
+// llist.c
+void llist_free(void *list, void (*freeit)(void *data));
+
+// getmountlist.c
 struct mtab_list {
 	struct mtab_list *next;
 	char *dir;
--- a/main.c	Thu Oct 05 16:18:03 2006 -0400
+++ b/main.c	Wed Oct 18 18:38:16 2006 -0400
@@ -17,6 +17,7 @@
 	{"cd", cd_main, TOYFLAG_NOFORK},
 	{"df", df_main, TOYFLAG_USR|TOYFLAG_SBIN},
 	{"exit", exit_main, TOYFLAG_NOFORK},
+	{"sh", toysh_main, TOYFLAG_BIN},
 	{"toysh", toysh_main, TOYFLAG_BIN}
 };
 
@@ -51,6 +52,16 @@
 	}
 }
 
+void toy_init(struct toy_list *which, char *argv[])
+{
+	// Free old toys contents here?
+
+	toys.which = which;
+	toys.argv = argv;
+	for (toys.argc = 0; argv[toys.argc]; toys.argc++);
+	toys.exitval = 1;
+}
+
 // Run a toy.
 void toy_exec(char *argv[])
 {
@@ -59,12 +70,7 @@
 	which = toy_find(argv[0]);
 	if (!which) return;
 
-	// Free old toys contents here?
-
-	toys.which = which;
-	toys.argv = argv;
-	for (toys.argc = 0; argv[toys.argc]; toys.argc++);
-	toys.exitval = 1;
+	toy_init(which, argv);
 	
 	exit(toys.which->toy_main());
 }
--- a/toys.h	Thu Oct 05 16:18:03 2006 -0400
+++ b/toys.h	Wed Oct 18 18:38:16 2006 -0400
@@ -17,10 +17,10 @@
 #include "lib/lib.h"
 
 int cd_main(void);
+int df_main(void);
 int exit_main(void);
 int toybox_main(void);
 int toysh_main(void);
-int df_main(void);
 
 #define TOYFLAG_USR      (1<<0)
 #define TOYFLAG_BIN      (1<<1)
@@ -35,6 +35,8 @@
 	int flags;
 } toy_list[];
 struct toy_list *toy_find(char *name);
+void toy_init(struct toy_list *which, char *argv[]);
+void toy_exec(char *argv[]);
 
 // Global context for this applet.
 
@@ -60,5 +62,13 @@
 	struct df_data df;
 } toy;
 
-// I need a real config system.
-#define CFG_TOYS_FREE 0
+// Pending the addition of menuconfig...
+
+#define CFG_TOYS_FREE     0
+
+#define CFG_TOYSH_TTY     0  // Terminal control
+#define CFG_TOYSH_JOBCTL  0  // &, fg, bg, jobs.  ctrl-z with tty.
+#define CFG_TOYSH_FLOWCTL 0  // if, while, for, functions { }
+#define CFG_TOYSH_ENVVARS 0  // Environment variables
+#define CFG_TOYSH_LOCVARS 0  // Local, synthetic, fancy prompts, set, $?
+#define CFG_TOYSH_PIPES   0  // Pipes and redirects: | > < >> << && || & () ;
--- a/toys/toysh.c	Thu Oct 05 16:18:03 2006 -0400
+++ b/toys/toysh.c	Wed Oct 18 18:38:16 2006 -0400
@@ -14,47 +14,169 @@
 
 #include "toys.h"
 
-static int handle(char *command)
+// A single executable, its arguments, and other information we know about it.
+#define TOYSH_FLAG_EXIT    1
+#define TOYSH_FLAG_SUSPEND 2
+#define TOYSH_FLAG_PIPE    4
+#define TOYSH_FLAG_AND     8
+#define TOYSH_FLAG_OR      16
+#define TOYSH_FLAG_AMP     32
+#define TOYSH_FLAG_SEMI    64
+#define TOYSH_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;         // Unparsed line for display purposes
+	int cmdlinelen;        // How long is cmdline?
+};
+
+// 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)
 {
-	int argc = 0, status;
-	char *argv[10], *start = command;
-	pid_t pid;
-	struct toy_list *tl;
+	char *end;
+
+	// Detect end of line (and truncate line at comment)
+	if (CFG_TOYSH_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))
+		xrealloc((void **)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 *line)
+{
+	struct command **cmd = &(line->cmd);
+	char *start = line->cmdline = cmdline;
+
+	if (!cmdline) return 0;
+
+	if (CFG_TOYSH_JOBCTL) line->cmdline = cmdline;
+		
 	// Parse command into argv[]
 	for (;;) {
 		char *end;
+		
+		// Skip leading whitespace and detect end of line.
+		while (isspace(*start)) start++;
+		if (!*start || *start=='#') {
+			if (CFG_TOYSH_JOBCTL) line->cmdlinelen = start-cmdline;
+			return 0;
+		}
 
-		// Skip leading whitespace and detect EOL.
-		while(isspace(*start)) start++;
-		if (!*start || *start=='#') break;
+		// 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);
 
-		// Grab next word.  (Add dequote and envvar logic here)
-		end = start;
-		while (*end && !isspace(*end)) end++;
-		argv[argc++] = xstrndup(start, end-start);
-		start=end;
+		// If we hit the end of this command, how did it end?
+		if (!end) {
+			if (CFG_TOYSH_PIPES && *start) {
+				if (*start==';') {
+					start++;
+					break;
+				}
+				// handle | & < > >> << || &&
+			}
+			break;
+		}
+		start = end;
 	}
-	argv[argc]=0;
+
+	if (CFG_TOYSH_JOBCTL) line->cmdlinelen = start-cmdline;
+
+	return start;
+}
 
-	if (!argc) return 0;
+// Execute the commands in a pipeline
+static void run_pipeline(struct pipeline *line)
+{
+	struct toy_list *tl;
+	struct command *cmd = line->cmd;
+	if (!cmd || !cmd->argc) return;
+
+	tl = toy_find(cmd->argv[0]);
+	// Is this command a builtin that should run in this process?
+	if (tl && (tl->flags & TOYFLAG_NOFORK)) {
+		struct toy_list *which = toys.which;
+		char **argv = toys.argv;
 
-	tl = toy_find(argv[0]);
-	// This is a bit awkward, next design cycle should clean it up.
-	// Should vfork(), move to tryspawn()?
-	pid = 0;
-	if (tl && (tl->flags & TOYFLAG_NOFORK))
-		status = tl->toy_main();
-	else {
-		pid=fork();
-		if(!pid) {
-			toy_exec(argv);
-			xexec(argv);
-		} else waitpid(pid, &status, 0);
+		toy_init(tl, cmd->argv);
+		cmd->pid = tl->toy_main();
+		toy_init(which, argv);
+	} else {
+		int status;
+
+		cmd->pid = vfork();
+		if (!cmd->pid) xexec(cmd->argv);
+		else waitpid(cmd->pid, &status, 0);
+
+		if (CFG_TOYSH_FLOWCTL || CFG_TOYSH_PIPES) {
+			if (WIFEXITED(status)) cmd->pid = WEXITSTATUS(status);
+			if (WIFSIGNALED(status)) cmd->pid = WTERMSIG(status);
+		}
 	}
-	while(argc) free(argv[--argc]);
+
+	return;
+}
+
+// Free the contents of a command structure
+static void free_cmd(void *data)
+{
+	struct command *cmd=(struct command *)data;
+
+	while(cmd->argc) free(cmd->argv[--cmd->argc]);
+}
+
 
-	return 0;
+// Parse a command line and do what it says to do.
+static void handle(char *command)
+{
+	struct pipeline line;
+	char *start = command;
+
+	// Loop through commands in this line
+
+	for (;;) {
+
+		// Parse a group of connected commands
+
+		memset(&line,0,sizeof(struct pipeline));
+		start = parse_pipeline(start, &line);
+		if (!line.cmd) break;
+
+		// Run those commands
+
+		run_pipeline(&line);
+		llist_free(line.cmd, free_cmd);
+	}
 }
 
 int cd_main(void)