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

350
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_ENVIRONMENT
#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 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 @@ static int builtin_fi(struct job *cmd, struct jobSet *junk);
/* 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 @@ static struct builtInCommand bltins[] = {
{"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[] = {
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 *cwd;
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 @@ static int builtin_read(struct job *cmd, struct jobSet *junk)
#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;
}
cmd->jobContext |= THEN_EXP_CONTEXT;
printf("Hit an then -- jobContext=%d\n", cmd->jobContext);
/* 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);
/* 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");
cmd->jobContext |= ELSE_EXP_CONTEXT;
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;
//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 @@ static void checkJobs(struct jobSet *jobList)
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 @@ static int getCommand(FILE * source, char *command)
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 @@ static void globLastArgument(struct childProgram *prog, int *argcPtr,
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 @@ static void globLastArgument(struct childProgram *prog, int *argcPtr,
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 @@ static void globLastArgument(struct childProgram *prog, int *argcPtr,
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 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
return 0;
}
*isBg = 0;
*inBg = 0;
job->numProgs = 1;
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) */
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 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
*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,6 +860,9 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
break;
case '#': /* comment */
if (*(src-1)== '$')
*buf++ = *src;
else
done = 1;
break;
@ -718,16 +874,16 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
(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 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
*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 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
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 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
break;
case '&': /* background */
*isBg = 1;
*inBg = 1;
case ';': /* multiple commands */
done = 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);
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 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
* 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 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
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 @@ static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int
* 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 @@ static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int
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 @@ static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int
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 @@ static int busy_loop(FILE * input)
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 @@ static int busy_loop(FILE * input)
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 @@ void free_memory(void)
#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);

350
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_ENVIRONMENT
#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 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 @@ static int builtin_fi(struct job *cmd, struct jobSet *junk);
/* 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 @@ static struct builtInCommand bltins[] = {
{"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[] = {
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 *cwd;
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 @@ static int builtin_read(struct job *cmd, struct jobSet *junk)
#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;
}
cmd->jobContext |= THEN_EXP_CONTEXT;
printf("Hit an then -- jobContext=%d\n", cmd->jobContext);
/* 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);
/* 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");
cmd->jobContext |= ELSE_EXP_CONTEXT;
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;
//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 @@ static void checkJobs(struct jobSet *jobList)
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 @@ static int getCommand(FILE * source, char *command)
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 @@ static void globLastArgument(struct childProgram *prog, int *argcPtr,
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 @@ static void globLastArgument(struct childProgram *prog, int *argcPtr,
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 @@ static void globLastArgument(struct childProgram *prog, int *argcPtr,
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 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
return 0;
}
*isBg = 0;
*inBg = 0;
job->numProgs = 1;
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) */
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 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
*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,6 +860,9 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
break;
case '#': /* comment */
if (*(src-1)== '$')
*buf++ = *src;
else
done = 1;
break;
@ -718,16 +874,16 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
(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 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
*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 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
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 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
break;
case '&': /* background */
*isBg = 1;
*inBg = 1;
case ';': /* multiple commands */
done = 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);
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 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
* 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 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
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 @@ static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int
* 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 @@ static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int
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 @@ static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int
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 @@ static int busy_loop(FILE * input)
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 @@ static int busy_loop(FILE * input)
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 @@ void free_memory(void)
#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);

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_ENVIRONMENT
#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 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 @@ static int builtin_fi(struct job *cmd, struct jobSet *junk);
/* 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 @@ static struct builtInCommand bltins[] = {
{"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[] = {
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 *cwd;
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 @@ static int builtin_read(struct job *cmd, struct jobSet *junk)
#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;
}
cmd->jobContext |= THEN_EXP_CONTEXT;
printf("Hit an then -- jobContext=%d\n", cmd->jobContext);
/* 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);
/* 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");
cmd->jobContext |= ELSE_EXP_CONTEXT;
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;
//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 @@ static void checkJobs(struct jobSet *jobList)
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 @@ static int getCommand(FILE * source, char *command)
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 @@ static void globLastArgument(struct childProgram *prog, int *argcPtr,
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 @@ static void globLastArgument(struct childProgram *prog, int *argcPtr,
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 @@ static void globLastArgument(struct childProgram *prog, int *argcPtr,
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 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
return 0;
}
*isBg = 0;
*inBg = 0;
job->numProgs = 1;
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) */
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 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
*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,6 +860,9 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
break;
case '#': /* comment */
if (*(src-1)== '$')
*buf++ = *src;
else
done = 1;
break;
@ -718,16 +874,16 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
(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 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
*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 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
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 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
break;
case '&': /* background */
*isBg = 1;
*inBg = 1;
case ';': /* multiple commands */
done = 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);
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 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
* 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 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi
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 @@ static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int
* 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 @@ static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int
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 @@ static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int
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 @@ static int busy_loop(FILE * input)
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 @@ static int busy_loop(FILE * input)
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 @@ void free_memory(void)
#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);