X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=sh.c;h=60d67e9e8a529572d0cc0de0bd3af685129df63a;hb=34e1941c32cd9366d4ada22c3ab3e42b9c986a2b;hp=f17097c643dc16e56b9c9fac68fc9cd89ebfaa28;hpb=d4bc1fcad8b0f9f4ce423c60c9563e8224651a26;p=oweals%2Fbusybox.git diff --git a/sh.c b/sh.c index f17097c64..60d67e9e8 100644 --- a/sh.c +++ b/sh.c @@ -37,8 +37,11 @@ #include #include #include +#ifdef BB_FEATURE_SH_COMMAND_EDITING +#include "cmdedit.h" +#endif - +#define MAX_READ 128 /* size of input buffer for `read' builtin */ #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" @@ -86,18 +89,21 @@ struct builtInCommand { int (*function) (struct job *, struct jobSet * jobList); /* function ptr */ }; -/* Some function prototypes */ -static int shell_cd(struct job *cmd, struct jobSet *junk); -static int shell_env(struct job *dummy, struct jobSet *junk); -static int shell_exit(struct job *cmd, struct jobSet *junk); -static int shell_fg_bg(struct job *cmd, struct jobSet *jobList); -static int shell_help(struct job *cmd, struct jobSet *junk); -static int shell_jobs(struct job *dummy, struct jobSet *jobList); -static int shell_pwd(struct job *dummy, struct jobSet *junk); -static int shell_export(struct job *cmd, struct jobSet *junk); -static int shell_source(struct job *cmd, struct jobSet *jobList); -static int shell_unset(struct job *cmd, struct jobSet *junk); - +/* function prototypes for builtins */ +static int builtin_cd(struct job *cmd, struct jobSet *junk); +static int builtin_env(struct job *dummy, struct jobSet *junk); +static int builtin_exit(struct job *cmd, struct jobSet *junk); +static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList); +static int builtin_help(struct job *cmd, struct jobSet *junk); +static int builtin_jobs(struct job *dummy, struct jobSet *jobList); +static int builtin_pwd(struct job *dummy, struct jobSet *junk); +static int builtin_export(struct job *cmd, struct jobSet *junk); +static int builtin_source(struct job *cmd, struct jobSet *jobList); +static int builtin_unset(struct job *cmd, struct jobSet *junk); +static int builtin_read(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, int *isBg); @@ -108,34 +114,52 @@ static int busy_loop(FILE * input); /* Table of built-in functions */ static struct builtInCommand bltins[] = { - {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg}, - {"cd", "Change working directory", "cd [dir]", shell_cd}, - //{"echo", "Echo arguments on stdout", "echo arg1 [...]", shell_echo}, - {"env", "Print all environment variables", "env", shell_env}, - {"exit", "Exit from shell()", "exit", shell_exit}, - {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg}, - {"jobs", "Lists the active jobs", "jobs", shell_jobs}, - {"pwd", "Print current directory", "pwd", shell_pwd}, - {"export", "Set environment variable", "export [VAR=value]", shell_export}, - {"unset", "Unset environment variable", "unset VAR", shell_unset}, - - {".", "Source-in and run commands in a file", ". filename", shell_source}, - {"help", "List shell built-in commands", "help", shell_help}, + {"bg", "Resume a job in the background", "bg [%%job]", builtin_fg_bg}, + {"cd", "Change working directory", "cd [dir]", builtin_cd}, + {"exit", "Exit from shell()", "exit", builtin_exit}, + {"fg", "Bring job into the foreground", "fg [%%job]", builtin_fg_bg}, + {"jobs", "Lists the active jobs", "jobs", builtin_jobs}, + {"export", "Set environment variable", "export [VAR=value]", builtin_export}, + {"unset", "Unset environment variable", "unset VAR", builtin_unset}, + {"read", "Input environment variable", "read [VAR]", builtin_read}, {NULL, NULL, NULL, NULL} }; -static const char shell_usage[] = - - "sh [FILE]...\n\n" "The BusyBox command interpreter (shell).\n\n"; +/* Table of built-in functions */ +static struct builtInCommand bltins_forking[] = { + {"env", "Print all environment variables", "env", builtin_env}, + {"pwd", "Print current directory", "pwd", builtin_pwd}, + {".", "Source-in and run commands in a file", ". filename", builtin_source}, + {"help", "List shell built-in commands", "help", builtin_help}, + {NULL, NULL, NULL, NULL} +}; +static const char shell_usage[] = + "sh [FILE]...\n" + " or: sh -c command [args]...\n" +#ifndef BB_FEATURE_TRIVIAL_HELP + "\nlash: The BusyBox command interpreter (shell).\n\n" +#endif + ; -static char cwd[1024]; static char *prompt = "# "; +static char *cwd = NULL; +static char *local_pending_command = NULL; +#ifdef BB_FEATURE_SH_COMMAND_EDITING +void win_changed(int sig) +{ + struct winsize win = { 0, 0 }; + ioctl(0, TIOCGWINSZ, &win); + if (win.ws_col > 0) { + cmdedit_setwidth( win.ws_col - 1); + } +} +#endif /* built-in 'cd ' handler */ -static int shell_cd(struct job *cmd, struct jobSet *junk) +static int builtin_cd(struct job *cmd, struct jobSet *junk) { char *newdir; @@ -153,7 +177,7 @@ static int shell_cd(struct job *cmd, struct jobSet *junk) } /* built-in 'env' handler */ -static int shell_env(struct job *dummy, struct jobSet *junk) +static int builtin_env(struct job *dummy, struct jobSet *junk) { char **e; @@ -164,17 +188,16 @@ static int shell_env(struct job *dummy, struct jobSet *junk) } /* built-in 'exit' handler */ -static int shell_exit(struct job *cmd, struct jobSet *junk) +static int builtin_exit(struct job *cmd, struct jobSet *junk) { if (!cmd->progs[0].argv[1] == 1) exit TRUE; - else - exit(atoi(cmd->progs[0].argv[1])); + return(atoi(cmd->progs[0].argv[1])); } /* built-in 'fg' and 'bg' handler */ -static int shell_fg_bg(struct job *cmd, struct jobSet *jobList) +static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList) { int i, jobNum; struct job *job=NULL; @@ -207,8 +230,9 @@ static int shell_fg_bg(struct job *cmd, struct jobSet *jobList) if (*cmd->progs[0].argv[0] == 'f') { /* Make this job the foreground job */ - if (tcsetpgrp(0, job->pgrp)) - perror("tcsetpgrp"); + /* suppress messages when run from /linuxrc mag@sysgo.de */ + if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY) + perror("tcsetpgrp"); jobList->fg = job; } @@ -224,7 +248,7 @@ static int shell_fg_bg(struct job *cmd, struct jobSet *jobList) } /* built-in 'help' handler */ -static int shell_help(struct job *cmd, struct jobSet *junk) +static int builtin_help(struct job *cmd, struct jobSet *junk) { struct builtInCommand *x; @@ -233,12 +257,15 @@ static int shell_help(struct job *cmd, struct jobSet *junk) for (x = bltins; x->cmd; x++) { fprintf(stdout, "%s\t%s\n", x->cmd, x->descr); } + for (x = bltins_forking; x->cmd; x++) { + fprintf(stdout, "%s\t%s\n", x->cmd, x->descr); + } fprintf(stdout, "\n\n"); return TRUE; } /* built-in 'jobs' handler */ -static int shell_jobs(struct job *dummy, struct jobSet *jobList) +static int builtin_jobs(struct job *dummy, struct jobSet *jobList) { struct job *job; char *statusString; @@ -256,7 +283,7 @@ static int shell_jobs(struct job *dummy, struct jobSet *jobList) /* built-in 'pwd' handler */ -static int shell_pwd(struct job *dummy, struct jobSet *junk) +static int builtin_pwd(struct job *dummy, struct jobSet *junk) { getcwd(cwd, sizeof(cwd)); fprintf(stdout, "%s\n", cwd); @@ -264,12 +291,12 @@ static int shell_pwd(struct job *dummy, struct jobSet *junk) } /* built-in 'export VAR=value' handler */ -static int shell_export(struct job *cmd, struct jobSet *junk) +static int builtin_export(struct job *cmd, struct jobSet *junk) { int res; if (!cmd->progs[0].argv[1] == 1) { - return (shell_env(cmd, junk)); + return (builtin_env(cmd, junk)); } res = putenv(cmd->progs[0].argv[1]); if (res) @@ -277,8 +304,42 @@ static int shell_export(struct job *cmd, struct jobSet *junk) return (res); } +/* built-in 'read VAR' handler */ +static int builtin_read(struct job *cmd, struct jobSet *junk) +{ + int res = 0, len, newlen; + char *s; + char string[MAX_READ]; + + if (cmd->progs[0].argv[1]) { + /* argument (VAR) given: put "VAR=" into buffer */ + strcpy(string, cmd->progs[0].argv[1]); + len = strlen(string); + string[len++] = '='; + string[len] = '\0'; + fgets(&string[len], sizeof(string) - len, stdin); /* read string */ + newlen = strlen(string); + if(newlen > len) + string[--newlen] = '\0'; /* chomp trailing newline */ + /* + ** string should now contain "VAR=" + ** copy it (putenv() won't do that, so we must make sure + ** the string resides in a static buffer!) + */ + res = -1; + if((s = strdup(string))) + res = putenv(s); + if (res) + fprintf(stdout, "read: %s\n", strerror(errno)); + } + else + fgets(string, sizeof(string), stdin); + + return (res); +} + /* Built-in '.' handler (read-in and execute commands from file) */ -static int shell_source(struct job *cmd, struct jobSet *junk) +static int builtin_source(struct job *cmd, struct jobSet *junk) { FILE *input; int status; @@ -299,7 +360,7 @@ static int shell_source(struct job *cmd, struct jobSet *junk) } /* built-in 'unset VAR' handler */ -static int shell_unset(struct job *cmd, struct jobSet *junk) +static int builtin_unset(struct job *cmd, struct jobSet *junk) { if (!cmd->progs[0].argv[1] == 1) { fprintf(stdout, "unset: parameter required.\n"); @@ -390,6 +451,17 @@ static void checkJobs(struct jobSet *jobList) static int getCommand(FILE * source, char *command) { + if (source == NULL) { + if (local_pending_command) { + /* a command specified (-c option): return it & mark it done */ + strcpy(command, local_pending_command); + free(local_pending_command); + local_pending_command = NULL; + return 0; + } + return 1; + } + if (source == stdin) { #ifdef BB_FEATURE_SH_COMMAND_EDITING int len; @@ -398,7 +470,7 @@ static int getCommand(FILE * source, char *command) fflush(stdout); promptStr=(char*)malloc(sizeof(char)*(len+1)); sprintf(promptStr, "%s %s", cwd, prompt); - cmdedit_read_input(promptStr, fileno(stdin), fileno(stdout), command); + cmdedit_read_input(promptStr, command); free( promptStr); return 0; #else @@ -427,7 +499,7 @@ static void globLastArgument(struct childProgram *prog, int *argcPtr, int rc; int flags; int i; - char *src, *dst; + char *src, *dst, *var; if (argc > 1) { /* cmd->globResult is already initialized */ flags = GLOB_APPEND; @@ -437,6 +509,9 @@ static void globLastArgument(struct childProgram *prog, int *argcPtr, flags = 0; i = 0; } + /* do shell variable substitution */ + if(*prog->argv[argc - 1] == '$' && (var = getenv(prog->argv[argc - 1] + 1))) + prog->argv[argc - 1] = var; rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult); if (rc == GLOB_NOSPACE) { @@ -501,12 +576,13 @@ static int parseCommand(char **commandPtr, struct job *job, int *isBg) job->progs = malloc(sizeof(*job->progs)); /* We set the argv elements to point inside of this string. The - memory is freed by freeJob(). + memory is freed by freeJob(). Allocate twice the original + length in case we need to quote every single character. Getting clean memory relieves us of the task of NULL terminating things and makes the rest of this look a bit cleaner (though it is, admittedly, a tad less efficient) */ - job->cmdBuf = command = calloc(1, strlen(*commandPtr) + 1); + job->cmdBuf = command = calloc(1, 2*strlen(*commandPtr) + 1); job->text = NULL; prog = job->progs; @@ -549,9 +625,8 @@ static int parseCommand(char **commandPtr, struct job *job, int *isBg) sizeof(*prog->argv) * argvAlloced); } - prog->argv[argc] = buf; - globLastArgument(prog, &argc, &argvAlloced); + prog->argv[argc] = buf; } } else switch (*src) { @@ -581,6 +656,7 @@ static int parseCommand(char **commandPtr, struct job *job, int *isBg) if (*chptr && *prog->argv[argc]) { buf++, argc++; globLastArgument(prog, &argc, &argvAlloced); + prog->argv[argc] = buf; } } @@ -696,7 +772,6 @@ static int parseCommand(char **commandPtr, struct job *job, int *isBg) strcpy(job->text, *commandPtr); } else { /* This leaves any trailing spaces, which is a bit sloppy */ - count = returnCommand - *commandPtr; job->text = malloc(count + 1); strncpy(job->text, *commandPtr, count); @@ -708,6 +783,7 @@ static int parseCommand(char **commandPtr, struct job *job, int *isBg) return 0; } + static int runCommand(struct job newJob, struct jobSet *jobList, int inBg) { struct job *job; @@ -715,14 +791,10 @@ static int runCommand(struct job newJob, struct jobSet *jobList, int inBg) int nextin, nextout; int pipefds[2]; /* pipefd[0] is for reading */ struct builtInCommand *x; +#ifdef BB_FEATURE_SH_STANDALONE_SHELL + const struct BB_applet *a = applets; +#endif - /* handle built-ins here -- we don't fork() so we can't background - these very easily */ - for (x = bltins; x->cmd; x++) { - if (!strcmp(newJob.progs[0].argv[0], x->cmd)) { - return (x->function(&newJob, jobList)); - } - } nextin = 0, nextout = 1; for (i = 0; i < newJob.numProgs; i++) { @@ -733,6 +805,13 @@ static int runCommand(struct job newJob, struct jobSet *jobList, int inBg) nextout = 1; } + /* Check if the command matches any non-forking builtins */ + for (x = bltins; x->cmd; x++) { + if (!strcmp(newJob.progs[i].argv[0], x->cmd)) { + return (x->function(&newJob, jobList)); + } + } + if (!(newJob.progs[i].pid = fork())) { signal(SIGTTOU, SIG_DFL); @@ -749,6 +828,27 @@ static int runCommand(struct job newJob, struct jobSet *jobList, int inBg) /* explicit redirections override pipes */ setupRedirections(newJob.progs + i); + /* Check if the command matches any of the other builtins */ + for (x = bltins_forking; x->cmd; x++) { + if (!strcmp(newJob.progs[i].argv[0], x->cmd)) { + exit (x->function(&newJob, jobList)); + } + } +#ifdef BB_FEATURE_SH_STANDALONE_SHELL + /* Check if the command matches any busybox internal commands here */ + /* TODO: Add matching when paths are appended (i.e. 'cat' currently + * works, but '/bin/cat' doesn't ) */ + while (a->name != 0) { + if (strcmp(newJob.progs[i].argv[0], a->name) == 0) { + int argc; + char** argv=newJob.progs[i].argv; + for(argc=0;*argv!=NULL; argv++, argc++); + exit((*(a->main)) (argc, 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], strerror(errno)); @@ -793,15 +893,14 @@ static int runCommand(struct job newJob, struct jobSet *jobList, int inBg) if (inBg) { /* we don't wait for background jobs to return -- append it to the list of backgrounded jobs and leave it alone */ - printf("[%d] %d\n", job->jobId, newJob.progs[newJob.numProgs - 1].pid); } else { jobList->fg = job; /* move the new process group into the foreground */ - - if (tcsetpgrp(0, newJob.pgrp)) + /* suppress messages when run from /linuxrc mag@sysgo.de */ + if (tcsetpgrp(0, newJob.pgrp) && errno != ENOTTY) perror("tcsetpgrp"); } @@ -853,10 +952,14 @@ static int busy_loop(FILE * input) char *nextCommand = NULL; struct jobSet jobList = { NULL, NULL }; struct job newJob; + pid_t parent_pgrp; int i; int status; int inBg; + /* save current owner of TTY so we can restore it on exit */ + parent_pgrp = tcgetpgrp(0); + command = (char *) calloc(BUFSIZ, sizeof(char)); /* don't pay any attention to this signal; it just confuses @@ -898,10 +1001,6 @@ static int busy_loop(FILE * input) removeJob(&jobList, jobList.fg); jobList.fg = NULL; - - /* move the shell to the foreground */ - if (tcsetpgrp(0, getpid())) - perror("tcsetpgrp"); } } else { /* the child was stopped */ @@ -917,13 +1016,22 @@ static int busy_loop(FILE * input) if (!jobList.fg) { /* move the shell to the foreground */ - if (tcsetpgrp(0, getpid())) - perror("tcsetpgrp"); + /* suppress messages when run from /linuxrc mag@sysgo.de */ + if (tcsetpgrp(0, getpid()) && errno != ENOTTY) + perror("tcsetpgrp"); } } } free(command); + /* return controlling TTY back to parent process group before exiting */ + if (tcsetpgrp(0, parent_pgrp)) + perror("tcsetpgrp"); + + /* return exit status if called with "-c" */ + if (input == NULL && WIFEXITED(status)) + return WEXITSTATUS(status); + return 0; } @@ -932,35 +1040,58 @@ int shell_main(int argc, char **argv) { FILE *input = stdin; - if (argc > 2) { - usage(shell_usage); - } /* initialize the cwd */ - getcwd(cwd, sizeof(cwd)); + cwd = (char *) calloc(BUFSIZ, sizeof(char)); + if (cwd == 0) { + fatalError("sh: out of memory\n"); + } + getcwd(cwd, sizeof(char)*BUFSIZ); +#ifdef BB_FEATURE_SH_COMMAND_EDITING + cmdedit_init(); + signal(SIGWINCH, win_changed); + win_changed(0); +#endif //if (argv[0] && argv[0][0] == '-') { - // shell_source("/etc/profile"); + // builtin_source("/etc/profile"); //} if (argc < 2) { - fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, - BB_BT); - fprintf(stdout, - "Enter 'help' for a list of built-in commands.\n\n"); + fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT); + fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n"); } else { - input = fopen(argv[1], "r"); - if (!input) - fatalError("A: Couldn't open file '%s': %s\n", argv[1], - strerror(errno)); -// else -// fatalError("Got it.\n"); - //exit(shell_source(argv[1])); - - /* Set terminal IO to canonical mode, and save old term settings. */ -#ifdef BB_FEATURE_SH_COMMAND_EDITING - cmdedit_init(); -#endif + if (argv[1][0]=='-' && argv[1][1]=='c') { + int i; + local_pending_command = (char *) calloc(BUFSIZ, sizeof(char)); + if (local_pending_command == 0) { + fatalError("sh: out of memory\n"); + } + for(i=2; i= BUFSIZ) { + local_pending_command = realloc(local_pending_command, + strlen(local_pending_command) + strlen(argv[i])); + if (local_pending_command==NULL) + fatalError("sh: commands for -c option too long\n"); + } + strcat(local_pending_command, argv[i]); + if ( (i + 1) < argc) + strcat(local_pending_command, " "); + } + input = NULL; + + } + else if (argv[1][0]=='-') { + usage(shell_usage); + } + else { + input = fopen(argv[1], "r"); + if (!input) { + fatalError("sh: Couldn't open file '%s': %s\n", argv[1], + strerror(errno)); + } + } } return (busy_loop(input));