More shell features.... if-then-else-fi is now basically usable (disable
by default pending further debugging).  Added in some basic shell environment
support (i.e. $?, $0-$9, $$, $!, $#).
 -Erik
diff --git a/sh.c b/sh.c
index f746757..e575676 100644
--- a/sh.c
+++ b/sh.c
@@ -26,9 +26,9 @@
  */
 
 
-#define BB_FEATURE_SH_BACKTICKS
+//#define BB_FEATURE_SH_BACKTICKS
 //#define BB_FEATURE_SH_IF_EXPRESSIONS
-
+//#define BB_FEATURE_SH_ENVIRONMENT
 
 
 #include "internal.h"
@@ -57,14 +57,12 @@
 };
 
 static const unsigned int REGULAR_JOB_CONTEXT=0x1;
-static const unsigned int IF_EXP_CONTEXT=0x2;
-static const unsigned int THEN_EXP_CONTEXT=0x4;
-static const unsigned int ELSE_EXP_CONTEXT=0x8;
+static const unsigned int IF_TRUE_CONTEXT=0x2;
+static const unsigned int IF_FALSE_CONTEXT=0x4;
+static const unsigned int THEN_EXP_CONTEXT=0x8;
+static const unsigned int ELSE_EXP_CONTEXT=0x10;
 
 
-enum jobContext { REGULAR_APP, IF_CONTEXT, THEN_CONTEXT
-};
-
 struct jobSet {
 	struct job *head;			/* head of list of running jobs */
 	struct job *fg;				/* current foreground job */
@@ -128,8 +126,7 @@
 /* function prototypes for shell stuff */
 static void checkJobs(struct jobSet *jobList);
 static int getCommand(FILE * source, char *command);
-static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg);
-static int setupRedirections(struct childProgram *prog);
+static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg);
 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2]);
 static int busy_loop(FILE * input);
 
@@ -146,6 +143,12 @@
 	{"export", "Set environment variable", builtin_export},
 	{"unset", "Unset environment variable", builtin_unset},
 	{"read", "Input environment variable", builtin_read},
+#ifdef BB_FEATURE_SH_IF_EXPRESSIONS
+	{"if", NULL, builtin_if},
+	{"then", NULL, builtin_then},
+	{"else", NULL, builtin_else},
+	{"fi", NULL, builtin_fi},
+#endif
 	{NULL, NULL, NULL}
 };
 
@@ -154,12 +157,6 @@
 static struct builtInCommand bltins_forking[] = {
 	{"env", "Print all environment variables", builtin_env},
 	{"pwd", "Print current directory", builtin_pwd},
-#ifdef BB_FEATURE_SH_IF_EXPRESSIONS
-	{"if", NULL, builtin_if},
-	{"then", NULL, builtin_then},
-	{"else", NULL, builtin_else},
-	{"fi", NULL, builtin_fi},
-#endif
 	{".", "Source-in and run commands in a file", builtin_source},
 	{"help", "List shell built-in commands", builtin_help},
 	{NULL, NULL, NULL}
@@ -170,6 +167,13 @@
 static char *local_pending_command = NULL;
 static char *promptStr = NULL;
 static struct jobSet jobList = { NULL, NULL };
+static int argc;
+static char **argv;
+#ifdef BB_FEATURE_SH_ENVIRONMENT
+static int lastBgPid=-1;
+static int lastReturnCode=-1;
+#endif
+
 
 #ifdef BB_FEATURE_SH_COMMAND_EDITING
 void win_changed(int junk)
@@ -369,38 +373,91 @@
 
 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
 /* Built-in handler for 'if' commands */
-static int builtin_if(struct job *cmd, struct jobSet *junk)
+static int builtin_if(struct job *cmd, struct jobSet *jobList)
 {
-	cmd->jobContext |= IF_EXP_CONTEXT;
-	printf("Hit an if -- jobContext=%d\n", cmd->jobContext);
-	return TRUE;
+	int status;
+	char* charptr1=cmd->text+3; /* skip over the leading 'if ' */
+
+	/* Now run the 'if' command */
+	status=strlen(charptr1);
+	local_pending_command = xmalloc(status+1);
+	strncpy(local_pending_command, charptr1, status); 
+	printf("'if' now running '%s'\n", charptr1);
+	status = busy_loop(NULL); /* Frees local_pending_command */
+	printf("if test returned ");
+	if (status == 0) {
+		printf("TRUE\n");
+		cmd->jobContext |= IF_TRUE_CONTEXT;
+	} else {
+		printf("FALSE\n");
+		cmd->jobContext |= IF_FALSE_CONTEXT;
+	}
+
+	return status;
 }
 
 /* Built-in handler for 'then' (part of the 'if' command) */
 static int builtin_then(struct job *cmd, struct jobSet *junk)
 {
-	if (cmd->jobContext & IF_EXP_CONTEXT) {
-		fprintf(stderr, "unexpected token `then'\n");
-		fflush(stderr);
+	int status;
+	char* charptr1=cmd->text+5; /* skip over the leading 'then ' */
+
+	if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
+		errorMsg("unexpected token `then'\n");
 		return FALSE;
 	}
+	/* If the if result was FALSE, skip the 'then' stuff */
+	if (cmd->jobContext & IF_TRUE_CONTEXT) {
+		return TRUE;
+	}
+
 	cmd->jobContext |= THEN_EXP_CONTEXT;
-	printf("Hit an then -- jobContext=%d\n", cmd->jobContext);
-	return TRUE;
+	//printf("Hit an then -- jobContext=%d\n", cmd->jobContext);
+
+	/* Now run the 'then' command */
+	status=strlen(charptr1);
+	local_pending_command = xmalloc(status+1);
+	strncpy(local_pending_command, charptr1, status); 
+	printf("'then' now running '%s'\n", charptr1);
+	return( busy_loop(NULL));
 }
 
 /* Built-in handler for 'else' (part of the 'if' command) */
 static int builtin_else(struct job *cmd, struct jobSet *junk)
 {
-	printf("Hit an else\n");
+	int status;
+	char* charptr1=cmd->text+5; /* skip over the leading 'else ' */
+
+	if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
+		errorMsg("unexpected token `else'\n");
+		return FALSE;
+	}
+	/* If the if result was TRUE, skip the 'else' stuff */
+	if (cmd->jobContext & IF_FALSE_CONTEXT) {
+		return TRUE;
+	}
+
 	cmd->jobContext |= ELSE_EXP_CONTEXT;
-	return TRUE;
+	//printf("Hit an else -- jobContext=%d\n", cmd->jobContext);
+
+	/* Now run the 'else' command */
+	status=strlen(charptr1);
+	local_pending_command = xmalloc(status+1);
+	strncpy(local_pending_command, charptr1, status); 
+	printf("'else' now running '%s'\n", charptr1);
+	return( busy_loop(NULL));
 }
 
 /* Built-in handler for 'fi' (part of the 'if' command) */
 static int builtin_fi(struct job *cmd, struct jobSet *junk)
 {
-	printf("Hit an fi\n");
+	if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
+		errorMsg("unexpected token `fi'\n");
+		return FALSE;
+	}
+	/* Clear out the if and then context bits */
+	cmd->jobContext &= ~(IF_TRUE_CONTEXT|IF_FALSE_CONTEXT|THEN_EXP_CONTEXT|ELSE_EXP_CONTEXT);
+	printf("Hit an fi   -- jobContext=%d\n", cmd->jobContext);
 	return TRUE;
 }
 #endif
@@ -521,6 +578,45 @@
 		perror("waitpid");
 }
 
+static int setupRedirections(struct childProgram *prog)
+{
+	int i;
+	int openfd;
+	int mode = O_RDONLY;
+	struct redirectionSpecifier *redir = prog->redirections;
+
+	for (i = 0; i < prog->numRedirections; i++, redir++) {
+		switch (redir->type) {
+		case REDIRECT_INPUT:
+			mode = O_RDONLY;
+			break;
+		case REDIRECT_OVERWRITE:
+			mode = O_RDWR | O_CREAT | O_TRUNC;
+			break;
+		case REDIRECT_APPEND:
+			mode = O_RDWR | O_CREAT | O_APPEND;
+			break;
+		}
+
+		openfd = open(redir->filename, mode, 0666);
+		if (openfd < 0) {
+			/* this could get lost if stderr has been redirected, but
+			   bash and ash both lose it as well (though zsh doesn't!) */
+			errorMsg("error opening %s: %s\n", redir->filename,
+					strerror(errno));
+			return 1;
+		}
+
+		if (openfd != redir->fd) {
+			dup2(openfd, redir->fd);
+			close(openfd);
+		}
+	}
+
+	return 0;
+}
+
+
 static int getCommand(FILE * source, char *command)
 {
 	if (source == NULL) {
@@ -562,17 +658,40 @@
 	return 0;
 }
 
+#ifdef BB_FEATURE_SH_ENVIRONMENT
+#define __MAX_INT_CHARS 7
+static char* itoa(register int i)
+{
+	static char a[__MAX_INT_CHARS];
+	register char *b = a + sizeof(a) - 1;
+	int   sign = (i < 0);
+
+	if (sign)
+		i = -i;
+	*b = 0;
+	do
+	{
+		*--b = '0' + (i % 10);
+		i /= 10;
+	}
+	while (i);
+	if (sign)
+		*--b = '-';
+	return b;
+}
+#endif
+
 static void globLastArgument(struct childProgram *prog, int *argcPtr,
 							 int *argcAllocedPtr)
 {
-	int argc = *argcPtr;
+	int argc_l = *argcPtr;
 	int argcAlloced = *argcAllocedPtr;
 	int rc;
 	int flags;
 	int i;
 	char *src, *dst, *var;
 
-	if (argc > 1) {				/* cmd->globResult is already initialized */
+	if (argc_l > 1) {				/* cmd->globResult is already initialized */
 		flags = GLOB_APPEND;
 		i = prog->globResult.gl_pathc;
 	} else {
@@ -581,19 +700,54 @@
 		i = 0;
 	}
 	/* do shell variable substitution */
-	if(*prog->argv[argc - 1] == '$' && (var = getenv(prog->argv[argc - 1] + 1)))
-		prog->argv[argc - 1] = var;
+	if(*prog->argv[argc_l - 1] == '$') {
+		if ((var = getenv(prog->argv[argc_l - 1] + 1))) {
+			prog->argv[argc_l - 1] = var;
+		} 
+#ifdef BB_FEATURE_SH_ENVIRONMENT
+		else {
+			switch(*(prog->argv[argc_l - 1] + 1)) {
+				case '?':
+					prog->argv[argc_l - 1] = itoa(lastReturnCode);
+					break;
+				case '$':
+					prog->argv[argc_l - 1] = itoa(getpid());
+					break;
+				case '#':
+					prog->argv[argc_l - 1] = itoa(argc-1);
+					break;
+				case '!':
+					if (lastBgPid==-1)
+						*(prog->argv[argc_l - 1])='\0';
+					else
+						prog->argv[argc_l - 1] = itoa(lastBgPid);
+					break;
+				case '0':case '1':case '2':case '3':case '4':
+				case '5':case '6':case '7':case '8':case '9':
+					{
+						int index=*(prog->argv[argc_l - 1] + 1)-48;
+						if (index >= argc) {
+							*(prog->argv[argc_l - 1])='\0';
+						} else {
+							prog->argv[argc_l - 1] = argv[index];
+						}
+					}
+					break;
+			}
+		}
+#endif
+	}
 
-	rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
+	rc = glob(prog->argv[argc_l - 1], flags, NULL, &prog->globResult);
 	if (rc == GLOB_NOSPACE) {
 		errorMsg("out of space during glob operation\n");
 		return;
 	} else if (rc == GLOB_NOMATCH ||
 			   (!rc && (prog->globResult.gl_pathc - i) == 1 &&
-				strcmp(prog->argv[argc - 1],
+				strcmp(prog->argv[argc_l - 1],
 						prog->globResult.gl_pathv[i]) == 0)) {
 		/* we need to remove whatever \ quoting is still present */
-		src = dst = prog->argv[argc - 1];
+		src = dst = prog->argv[argc_l - 1];
 		while (*src) {
 			if (*src != '\\')
 				*dst++ = *src;
@@ -604,13 +758,13 @@
 		argcAlloced += (prog->globResult.gl_pathc - i);
 		prog->argv =
 			realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
-		memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
+		memcpy(prog->argv + (argc_l - 1), prog->globResult.gl_pathv + i,
 			   sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
-		argc += (prog->globResult.gl_pathc - i - 1);
+		argc_l += (prog->globResult.gl_pathc - i - 1);
 	}
 
 	*argcAllocedPtr = argcAlloced;
-	*argcPtr = argc;
+	*argcPtr = argc_l;
 }
 
 /* Return cmd->numProgs as 0 if no command is present (e.g. an empty
@@ -618,12 +772,12 @@
    the beginning of the next command (if the original command had more 
    then one job associated with it) or NULL if no more commands are 
    present. */
-static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg)
+static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg)
 {
 	char *command;
 	char *returnCommand = NULL;
 	char *src, *buf, *chptr;
-	int argc = 0;
+	int argc_l = 0;
 	int done = 0;
 	int argvAlloced;
 	int i;
@@ -641,7 +795,7 @@
 		return 0;
 	}
 
-	*isBg = 0;
+	*inBg = 0;
 	job->numProgs = 1;
 	job->progs = xmalloc(sizeof(*job->progs));
 
@@ -654,7 +808,6 @@
 	   cleaner (though it is, admittedly, a tad less efficient) */
 	job->cmdBuf = command = calloc(2*strlen(*commandPtr) + 1, sizeof(char));
 	job->text = NULL;
-	job->jobContext = REGULAR_JOB_CONTEXT;
 
 	prog = job->progs;
 	prog->numRedirections = 0;
@@ -687,17 +840,17 @@
 					   *src == ']') *buf++ = '\\';
 			*buf++ = *src;
 		} else if (isspace(*src)) {
-			if (*prog->argv[argc]) {
-				buf++, argc++;
+			if (*prog->argv[argc_l]) {
+				buf++, argc_l++;
 				/* +1 here leaves room for the NULL which ends argv */
-				if ((argc + 1) == argvAlloced) {
+				if ((argc_l + 1) == argvAlloced) {
 					argvAlloced += 5;
 					prog->argv = realloc(prog->argv,
 										 sizeof(*prog->argv) *
 										 argvAlloced);
 				}
-				globLastArgument(prog, &argc, &argvAlloced);
-				prog->argv[argc] = buf;
+				globLastArgument(prog, &argc_l, &argvAlloced);
+				prog->argv[argc_l] = buf;
 			}
 		} else
 			switch (*src) {
@@ -707,7 +860,10 @@
 				break;
 
 			case '#':			/* comment */
-				done = 1;
+				if (*(src-1)== '$')
+					*buf++ = *src;
+				else
+					done = 1;
 				break;
 
 			case '>':			/* redirections */
@@ -718,16 +874,16 @@
 											 (i + 1));
 
 				prog->redirections[i].fd = -1;
-				if (buf != prog->argv[argc]) {
+				if (buf != prog->argv[argc_l]) {
 					/* the stuff before this character may be the file number 
 					   being redirected */
 					prog->redirections[i].fd =
-						strtol(prog->argv[argc], &chptr, 10);
+						strtol(prog->argv[argc_l], &chptr, 10);
 
-					if (*chptr && *prog->argv[argc]) {
-						buf++, argc++;
-						globLastArgument(prog, &argc, &argvAlloced);
-						prog->argv[argc] = buf;
+					if (*chptr && *prog->argv[argc_l]) {
+						buf++, argc_l++;
+						globLastArgument(prog, &argc_l, &argvAlloced);
+						prog->argv[argc_l] = buf;
 					}
 				}
 
@@ -765,20 +921,20 @@
 					*buf++ = *chptr++;
 
 				src = chptr - 1;	/* we src++ later */
-				prog->argv[argc] = ++buf;
+				prog->argv[argc_l] = ++buf;
 				break;
 
 			case '|':			/* pipe */
 				/* finish this command */
-				if (*prog->argv[argc])
-					argc++;
-				if (!argc) {
+				if (*prog->argv[argc_l])
+					argc_l++;
+				if (!argc_l) {
 					errorMsg("empty command in pipe\n");
 					freeJob(job);
 					job->numProgs=0;
 					return 1;
 				}
-				prog->argv[argc] = NULL;
+				prog->argv[argc_l] = NULL;
 
 				/* and start the next */
 				job->numProgs++;
@@ -788,7 +944,7 @@
 				prog->numRedirections = 0;
 				prog->redirections = NULL;
 				prog->freeGlob = 0;
-				argc = 0;
+				argc_l = 0;
 
 				argvAlloced = 5;
 				prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
@@ -809,7 +965,7 @@
 				break;
 
 			case '&':			/* background */
-				*isBg = 1;
+				*inBg = 1;
 			case ';':			/* multiple commands */
 				done = 1;
 				returnCommand = *commandPtr + (src - *commandPtr) + 1;
@@ -848,7 +1004,7 @@
 					snprintf(charptr1, 1+ptr-src, src);
 					newJob = xmalloc(sizeof(struct job));
 					/* Now parse and run the backticked command */
-					if (!parseCommand(&charptr1, newJob, &njobList, isBg) 
+					if (!parseCommand(&charptr1, newJob, &njobList, inBg) 
 							&& newJob->numProgs) {
 						pipe(pipefd);
 						runCommand(newJob, &njobList, 0, pipefd);
@@ -890,7 +1046,7 @@
 					 * and improved version of the command line with the backtick
 					 * results expanded in place... */
 					freeJob(job);
-					return(parseCommand(commandPtr, job, jobList, isBg));
+					return(parseCommand(commandPtr, job, jobList, inBg));
 				}
 				break;
 #endif // BB_FEATURE_SH_BACKTICKS
@@ -901,15 +1057,15 @@
 		src++;
 	}
 
-	if (*prog->argv[argc]) {
-		argc++;
-		globLastArgument(prog, &argc, &argvAlloced);
+	if (*prog->argv[argc_l]) {
+		argc_l++;
+		globLastArgument(prog, &argc_l, &argvAlloced);
 	}
-	if (!argc) {
+	if (!argc_l) {
 		freeJob(job);
 		return 0;
 	}
-	prog->argv[argc] = NULL;
+	prog->argv[argc_l] = NULL;
 
 	if (!returnCommand) {
 		job->text = xmalloc(strlen(*commandPtr) + 1);
@@ -991,17 +1147,17 @@
 			 * works, but '/bin/cat' doesn't ) */
 			while (a->name != 0) {
 				if (strcmp(newJob->progs[i].argv[0], a->name) == 0) {
-					int argc;
+					int argc_l;
 					char** argv=newJob->progs[i].argv;
-					for(argc=0;*argv!=NULL; argv++, argc++);
-					exit((*(a->main)) (argc, newJob->progs[i].argv));
+					for(argc_l=0;*argv!=NULL; argv++, argc_l++);
+					exit((*(a->main)) (argc_l, newJob->progs[i].argv));
 				}
 				a++;
 			}
 #endif
 
 			execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
-			fatalError("sh: %s: %s\n", newJob->progs[i].argv[0],
+			fatalError("%s: %s\n", newJob->progs[i].argv[0],
 					   strerror(errno));
 		}
 		if (outPipe[1]!=-1) {
@@ -1048,6 +1204,9 @@
 		   to the list of backgrounded theJobs and leave it alone */
 		printf("[%d] %d\n", theJob->jobId,
 			   newJob->progs[newJob->numProgs - 1].pid);
+#ifdef BB_FEATURE_SH_ENVIRONMENT
+		lastBgPid=newJob->progs[newJob->numProgs - 1].pid;
+#endif
 	} else {
 		jobList->fg = theJob;
 
@@ -1060,45 +1219,6 @@
 	return 0;
 }
 
-static int setupRedirections(struct childProgram *prog)
-{
-	int i;
-	int openfd;
-	int mode = O_RDONLY;
-	struct redirectionSpecifier *redir = prog->redirections;
-
-	for (i = 0; i < prog->numRedirections; i++, redir++) {
-		switch (redir->type) {
-		case REDIRECT_INPUT:
-			mode = O_RDONLY;
-			break;
-		case REDIRECT_OVERWRITE:
-			mode = O_RDWR | O_CREAT | O_TRUNC;
-			break;
-		case REDIRECT_APPEND:
-			mode = O_RDWR | O_CREAT | O_APPEND;
-			break;
-		}
-
-		openfd = open(redir->filename, mode, 0666);
-		if (openfd < 0) {
-			/* this could get lost if stderr has been redirected, but
-			   bash and ash both lose it as well (though zsh doesn't!) */
-			errorMsg("error opening %s: %s\n", redir->filename,
-					strerror(errno));
-			return 1;
-		}
-
-		if (openfd != redir->fd) {
-			dup2(openfd, redir->fd);
-			close(openfd);
-		}
-	}
-
-	return 0;
-}
-
-
 static int busy_loop(FILE * input)
 {
 	char *command;
@@ -1106,8 +1226,9 @@
 	struct job newJob;
 	pid_t  parent_pgrp;
 	int i;
-	int status;
 	int inBg;
+	int status;
+	newJob.jobContext = REGULAR_JOB_CONTEXT;
 
 	/* save current owner of TTY so we can restore it on exit */
 	parent_pgrp = tcgetpgrp(0);
@@ -1160,6 +1281,9 @@
 					removeJob(&jobList, jobList.fg);
 					jobList.fg = NULL;
 				}
+#ifdef BB_FEATURE_SH_ENVIRONMENT
+				lastReturnCode=WEXITSTATUS(status);
+#endif
 			} else {
 				/* the child was stopped */
 				jobList.fg->stoppedProgs++;
@@ -1211,9 +1335,11 @@
 #endif
 
 
-int shell_main(int argc, char **argv)
+int shell_main(int argc_l, char **argv_l)
 {
 	FILE *input = stdin;
+	argc = argc_l;
+	argv = argv_l;
 
 	/* initialize the cwd -- this is never freed...*/
 	cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1);