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
This commit is contained in:
Eric Andersen 2000-07-27 00:15:20 +00:00
parent 7ce41ad692
commit 6a99aaf020
3 changed files with 717 additions and 339 deletions

352
lash.c
View File

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

352
sh.c
View File

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

View File

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