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);