*
* Credits:
* The parser routines proper are all original material, first
- * written Dec 2000 and Jan 2001 by Larry Doolittle.
- * The execution engine, the builtins, and much of the underlying
- * support has been adapted from busybox-0.49pre's lash,
- * which is Copyright (C) 2000 by Lineo, Inc., and
- * written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org>.
- * That, in turn, is based in part on ladsh.c, by Michael K. Johnson and
- * Erik W. Troan, which they placed in the public domain. I don't know
- * how much of the Johnson/Troan code has survived the repeated rewrites.
+ * written Dec 2000 and Jan 2001 by Larry Doolittle. The
+ * execution engine, the builtins, and much of the underlying
+ * support has been adapted from busybox-0.49pre's lash, which is
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * written by Erik Andersen <andersen@codepoet.org>. That, in turn,
+ * is based in part on ladsh.c, by Michael K. Johnson and Erik W.
+ * Troan, which they placed in the public domain. I don't know
+ * how much of the Johnson/Troan code has survived the repeated
+ * rewrites.
+ *
* Other credits:
* simple_itoa() was lifted from boa-0.93.15
* b_addchr() derived from similar w_addchar function in glibc-2.2
#include "busybox.h"
#include "cmdedit.h"
#else
-#define applet_name "hush"
+#define bb_applet_name "hush"
#include "standalone.h"
#define hush_main main
#undef CONFIG_FEATURE_SH_FANCY_PROMPT
redir_type type; /* type of redirection */
int fd; /* file descriptor being redirected */
int dup; /* -1, or file descriptor being duplicated */
- struct redir_struct *next; /* pointer to the next redirect in the list */
+ struct redir_struct *next; /* pointer to the next redirect in the list */
glob_t word; /* *word.gl_pathv is the filename */
};
unsigned int global_argc;
unsigned int last_return_code;
extern char **environ; /* This is in <unistd.h>, but protected with __USE_GNU */
-
+
/* "globals" within this file */
static char *ifs;
static char map[256];
#define final_printf debug_printf
static void __syntax(char *file, int line) {
- error_msg("syntax error %s:%d", file, line);
+ bb_error_msg("syntax error %s:%d", file, line);
}
#define syntax() __syntax(__FILE__, __LINE__)
{"continue", "Continue for, while or until loop", builtin_not_written},
{"env", "Print all environment variables", builtin_env},
{"eval", "Construct and run shell command", builtin_eval},
- {"exec", "Exec command, replacing this shell with the exec'd process",
+ {"exec", "Exec command, replacing this shell with the exec'd process",
builtin_exec},
{"exit", "Exit from shell()", builtin_exit},
{"export", "Set environment variable", builtin_export},
static const char *set_cwd(void)
{
- if(cwd==unknown)
+ if(cwd==bb_msg_unknown)
cwd = NULL; /* xgetcwd(arg) called free(arg) */
cwd = xgetcwd((char *)cwd);
if (!cwd)
- cwd = unknown;
+ cwd = bb_msg_unknown;
return cwd;
}
{
char *str = NULL;
int rcode = EXIT_SUCCESS;
-
+
if (child->argv[1]) {
str = make_string(child->argv + 1);
- parse_string_outer(str, FLAG_EXIT_FROM_LOOP |
+ parse_string_outer(str, FLAG_EXIT_FROM_LOOP |
FLAG_PARSE_SEMICOLON);
free(str);
rcode = last_return_code;
}
}
if (res<0)
- perror_msg("export");
+ bb_perror_msg("export");
else if(res==0)
res = set_local_var(name, 1);
else
}
}
if (!pi) {
- error_msg("%s: no current job", child->argv[0]);
+ bb_error_msg("%s: no current job", child->argv[0]);
return EXIT_FAILURE;
}
} else {
if (sscanf(child->argv[1], "%%%d", &jobnum) != 1) {
- error_msg("%s: bad argument '%s'", child->argv[0], child->argv[1]);
+ bb_error_msg("%s: bad argument '%s'", child->argv[0], child->argv[1]);
return EXIT_FAILURE;
}
for (pi = job_list; pi; pi = pi->next) {
}
}
if (!pi) {
- error_msg("%s: %d: no such job", child->argv[0], jobnum);
+ bb_error_msg("%s: %d: no such job", child->argv[0], jobnum);
return EXIT_FAILURE;
}
}
if (i == ESRCH) {
remove_bg_job(pi);
} else {
- perror_msg("kill (SIGCONT)");
+ bb_perror_msg("kill (SIGCONT)");
}
}
/* XXX search through $PATH is missing */
input = fopen(child->argv[1], "r");
if (!input) {
- error_msg("Couldn't open file '%s'", child->argv[1]);
+ bb_error_msg("Couldn't open file '%s'", child->argv[1]);
return EXIT_FAILURE;
}
PS1 = getenv("PS1");
if(PS1==0)
PS1 = "\\w \\$ ";
-#endif
+#endif
}
static inline void setup_prompt_string(int promptmode, char **prompt_str)
i->p = the_command;
}
-/* This is the magic location that prints prompts
+/* This is the magic location that prints prompts
* and gets data back from the user */
static int file_get(struct in_str *i)
{
{
struct close_me *tmp;
if (close_me_head == NULL || close_me_head->fd != fd)
- error_msg_and_die("corrupt close_me");
+ bb_error_msg_and_die("corrupt close_me");
tmp = close_me_head;
close_me_head = close_me_head->next;
free(tmp);
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!) */
- perror_msg("error opening %s", redir->word.gl_pathv[0]);
+ bb_perror_msg("error opening %s", redir->word.gl_pathv[0]);
return 1;
}
} else {
}
/* Check if the command matches any busybox internal commands
- * ("applets") here.
+ * ("applets") here.
* FIXME: This feature is not 100% safe, since
* BusyBox is not fully reentrant, so we have no guarantee the things
* from the .bss are still zeroed, or that things from .data are still
* at their defaults. We could exec ourself from /proc/self/exe, but I
* really dislike relying on /proc for things. We could exec ourself
* from global_argv[0], but if we are in a chroot, we may not be able
- * to find ourself... */
+ * to find ourself... */
#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
{
int argc_l;
char** argv_l=child->argv;
char *name = child->argv[0];
-#ifdef CONFIG_FEATURE_SH_APPLETS_ALWAYS_WIN
- /* Following discussions from November 2000 on the busybox mailing
- * list, the default configuration, (without
- * get_last_path_component()) lets the user force use of an
- * external command by specifying the full (with slashes) filename.
- * If you enable CONFIG_FEATURE_SH_APPLETS_ALWAYS_WIN, then applets
- * _aways_ override external commands, so if you want to run
- * /bin/cat, it will use BusyBox cat even if /bin/cat exists on the
- * filesystem and is _not_ busybox. Some systems may want this,
- * most do not. */
- name = get_last_path_component(name);
-#endif
/* Count argc for use in a second... */
for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++);
optind = 1;
#endif
debug_printf("exec of %s\n",child->argv[0]);
execvp(child->argv[0],child->argv);
- perror_msg("couldn't exec: %s",child->argv[0]);
+ bb_perror_msg("couldn't exec: %s",child->argv[0]);
_exit(1);
} else if (child->group) {
debug_printf("runtime nesting to group\n");
}
}
- /* we don't wait for background thejobs to return -- append it
+ /* we don't wait for background thejobs to return -- append it
to the list of backgrounded thejobs and leave it alone */
printf("[%d] %d\n", thejob->jobid, thejob->progs[0].pid);
last_bg_pid = thejob->progs[0].pid;
free(pi);
}
-/* Checks to see if any processes have exited -- if they
+/* Checks to see if any processes have exited -- if they
have, figure out why and see if a job has completed */
static int checkjobs(struct pipe* fg_pipe)
{
int i, rcode = 0;
for (i=0; i < fg_pipe->num_progs; i++) {
if (fg_pipe->progs[i].pid == childpid) {
- if (i==fg_pipe->num_progs-1)
+ if (i==fg_pipe->num_progs-1)
rcode=WEXITSTATUS(status);
(fg_pipe->num_progs)--;
return(rcode);
if (pi->stopped_progs == pi->num_progs) {
printf("\n"JOB_STATUS_FORMAT, pi->jobid, "Stopped", pi->text);
}
-#endif
+#endif
}
}
if (childpid == -1 && errno != ECHILD)
- perror_msg("waitpid");
+ bb_perror_msg("waitpid");
/* move the shell to the foreground */
//if (interactive && tcsetpgrp(shell_terminal, getpgid(0)))
- // perror_msg("tcsetpgrp-2");
+ // bb_perror_msg("tcsetpgrp-2");
return -1;
}
/* Ok, this case is tricky. We have to decide if this is a
* local variable, or an already exported variable. If it is
* already exported, we have to export the new value. If it is
- * not exported, we need only set this as a local variable.
+ * not exported, we need only set this as a local variable.
* This junk is all to decide whether or not to export this
* variable. */
int export_me=0;
char *name, *value;
- name = xstrdup(child->argv[i]);
+ name = bb_xstrdup(child->argv[i]);
debug_printf("Local environment set: %s\n", name);
value = strchr(name, '=');
if (value)
}
if (child->sp) {
char * str = NULL;
-
+
str = make_string((child->argv + i));
parse_string_outer(str, FLAG_EXIT_FROM_LOOP | FLAG_REPARSING);
free(str);
/* pipes are inserted between pairs of commands */
if ((i + 1) < pi->num_progs) {
- if (pipe(pipefds)<0) perror_msg_and_die("pipe");
+ if (pipe(pipefds)<0) bb_perror_msg_and_die("pipe");
nextout = pipefds[1];
} else {
nextout=1;
}
/* XXX test for failed fork()? */
-#if !defined(__UCLIBC__) || defined(__UCLIBC_HAS_MMU__)
+#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__)
if (!(child->pid = fork()))
#else
- if (!(child->pid = vfork()))
+ if (!(child->pid = vfork()))
#endif
{
/* Set the handling for job control signals back to the default. */
signal(SIGTTIN, SIG_DFL);
signal(SIGTTOU, SIG_DFL);
signal(SIGCHLD, SIG_DFL);
-
+
close_all();
if (nextin != 0) {
pseudo_exec(child);
}
-
+
/* put our child in the process group whose leader is the
first process in this pipe */
if (nextout != 1)
close(nextout);
- /* If there isn't another process, nextin is garbage
+ /* If there isn't another process, nextin is garbage
but it doesn't matter */
nextin = pipefds[0];
}
(rpipe->next == NULL)) {
syntax();
return 1;
- }
- if ((rpipe->r_mode == RES_IN &&
- (rpipe->next->r_mode == RES_IN &&
+ }
+ if ((rpipe->r_mode == RES_IN &&
+ (rpipe->next->r_mode == RES_IN &&
rpipe->next->progs->argv != NULL))||
(rpipe->r_mode == RES_FOR &&
- rpipe->next->r_mode != RES_IN)) {
+ rpipe->next->r_mode != RES_IN)) {
syntax();
return 1;
}
if (rmode == RES_THEN || rmode == RES_ELSE) if_code = next_if_code;
if (rmode == RES_THEN && if_code) continue;
if (rmode == RES_ELSE && !if_code) continue;
- if (rmode == RES_ELIF && !if_code) continue;
+ if (rmode == RES_ELIF && !if_code) break;
if (rmode == RES_FOR && pi->num_progs) {
if (!list) {
- /* if no variable values after "in" we skip "for" */
+ /* if no variable values after "in" we skip "for" */
if (!pi->next->progs->argv) continue;
/* create list of variable values */
list = make_list_in(pi->next->progs->argv,
save_name = pi->progs->argv[0];
pi->progs->argv[0] = NULL;
flag_rep = 1;
- }
+ }
if (!(*list)) {
free(pi->progs->argv[0]);
free(save_list);
pi->progs->glob_result.gl_pathv[0] =
pi->progs->argv[0];
continue;
- } else {
+ } else {
/* insert new value from list for variable */
- if (pi->progs->argv[0])
+ if (pi->progs->argv[0])
free(pi->progs->argv[0]);
pi->progs->argv[0] = *list++;
pi->progs->glob_result.gl_pathv[0] =
pi->progs->argv[0];
}
- }
+ }
if (rmode == RES_IN) continue;
if (rmode == RES_DO) {
if (!flag_rep) continue;
- }
+ }
if ((rmode == RES_DONE)) {
if (flag_rep) {
flag_restore = 1;
} else {
rpipe = NULL;
}
- }
+ }
if (pi->num_progs == 0) continue;
save_num_progs = pi->num_progs; /* save number of programs */
rcode = run_pipe_real(pi);
if (interactive) {
/* move the new process group into the foreground */
if (tcsetpgrp(shell_terminal, pi->pgrp) && errno != ENOTTY)
- perror_msg("tcsetpgrp-3");
+ bb_perror_msg("tcsetpgrp-3");
rcode = checkjobs(pi);
/* move the shell to the foreground */
if (tcsetpgrp(shell_terminal, getpgid(0)) && errno != ENOTTY)
- perror_msg("tcsetpgrp-4");
+ bb_perror_msg("tcsetpgrp-4");
} else {
rcode = checkjobs(pi);
}
pi->num_progs = save_num_progs; /* restore number of programs */
if ( rmode == RES_IF || rmode == RES_ELIF )
next_if_code=rcode; /* can be overwritten a number of times */
- if (rmode == RES_WHILE)
+ if (rmode == RES_WHILE)
flag_rep = !last_return_code;
- if (rmode == RES_UNTIL)
+ if (rmode == RES_UNTIL)
flag_rep = last_return_code;
if ( (rcode==EXIT_SUCCESS && pi->followup==PIPE_OR) ||
(rcode!=EXIT_SUCCESS && pi->followup==PIPE_AND) )
pi->next=NULL;
free(pi);
}
- return rcode;
+ return rcode;
}
/* Select which version we will use */
int rcode=0;
if (fake_mode==0) {
rcode = run_list_real(pi);
- }
+ }
/* free_pipe_list has the side effect of clearing memory
* In the long run that function can be merged with run_list_real,
* but doing that now would hobble the debugging effort. */
debug_printf("globhack returned %d\n",gr);
}
if (gr == GLOB_NOSPACE)
- error_msg_and_die("out of memory during glob");
+ bb_error_msg_and_die("out of memory during glob");
if (gr != 0) { /* GLOB_ABORTED ? */
- error_msg("glob(3) error %d",gr);
+ bb_error_msg("glob(3) error %d",gr);
}
/* globprint(glob_target); */
return gr;
/* Assume when we enter this function that we are already in
* NAME=VALUE format. So the first order of business is to
- * split 's' on the '=' into 'name' and 'value' */
+ * split 's' on the '=' into 'name' and 'value' */
value = strchr(name, '=');
if (value==0 && ++value==0) {
free(name);
result++;
} else {
if(cur->flg_read_only) {
- error_msg("%s: readonly variable", name);
+ bb_error_msg("%s: readonly variable", name);
result = -1;
} else {
if(flg_export>0 || cur->flg_export>1)
if(cur!=0) {
struct variables *next = top_vars;
if(cur->flg_read_only) {
- error_msg("%s: readonly variable", name);
+ bb_error_msg("%s: readonly variable", name);
return;
} else {
if(cur->flg_export)
debug_printf("Redirect type %d%s\n", redir->fd, redir_table[style].descrip);
- /* Check for a '2>&1' type redirect */
+ /* Check for a '2>&1' type redirect */
redir->dup = redirect_dup_num(input);
if (redir->dup == -2) return 1; /* syntax error */
if (redir->dup != -1) {
if (ctx->pending_redirect) {
ctx->pending_redirect=NULL;
if (glob_target->gl_pathc != 1) {
- error_msg("ambiguous redirect");
+ bb_error_msg("ambiguous redirect");
return 1;
}
} else {
}
if (ok) return d;
- error_msg("ambiguous redirect");
+ bb_error_msg("ambiguous redirect");
return -2;
}
FILE *pf;
#if 1
int pid, channel[2];
- if (pipe(channel)<0) perror_msg_and_die("pipe");
-#if !defined(__UCLIBC__) || defined(__UCLIBC_HAS_MMU__)
+ if (pipe(channel)<0) bb_perror_msg_and_die("pipe");
+#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__)
pid=fork();
#else
pid=vfork();
#endif
if (pid<0) {
- perror_msg_and_die("fork");
+ bb_perror_msg_and_die("fork");
} else if (pid==0) {
close(channel[0]);
if (channel[1] != 1) {
static char *lookup_param(char *src)
{
char *p=NULL;
- if (src) {
+ if (src) {
p = getenv(src);
- if (!p)
+ if (!p)
p = get_local_var(src);
}
return p;
case '-':
case '_':
/* still unhandled, but should be eventually */
- error_msg("unhandled syntax: $%c",ch);
+ bb_error_msg("unhandled syntax: $%c",ch);
return 1;
break;
default:
if (m==2) { /* unquoted IFS */
if (done_word(dest, ctx)) {
return 1;
- }
+ }
/* If we aren't performing a substitution, treat a newline as a
* command separator. */
if (end_trigger != '\0' && ch=='\n')
}
/* complain if quote? No, maybe we just finished a command substitution
* that was quoted. Example:
- * $ echo "`cat foo` plus more"
+ * $ echo "`cat foo` plus more"
* and we just got the EOF generated by the subshell that ran "cat foo"
* The only real complaint is if we got an EOF when end_trigger != '\0',
* that is, we were really supposed to get end_trigger, and never got
mapset(ifs, 2); /* also flow through if quoted */
}
-/* most recursion does not come through here, the exeception is
+/* most recursion does not come through here, the exception is
* from builtin_source() */
int parse_stream_outer(struct in_str *inp, int flag)
{
if (ctx.old_flag != 0) {
free(ctx.stack);
b_reset(&temp);
- }
+ }
temp.nonnull = 0;
temp.quote = 0;
inp->p = NULL;
/* XXX what should these be while sourcing /etc/profile? */
global_argc = argc;
global_argv = argv;
-
+
/* (re?) initialize globals. Sometimes hush_main() ends up calling
- * hush_main(), therefore we cannot rely on the BSS to zero out this
+ * hush_main(), therefore we cannot rely on the BSS to zero out this
* stuff. Reset these to 0 every time. */
ifs = NULL;
/* map[] is taken care of with call to update_ifs_map() */
#endif
PS2 = "> ";
- /* initialize our shell local variables with the values
+ /* initialize our shell local variables with the values
* currently living in the environment */
if (e) {
for (; *e; e++)
}
}
input=stdin;
-
+
while ((opt = getopt(argc, argv, "c:xif")) > 0) {
switch (opt) {
case 'c':
" or: sh -c command [args]...\n\n");
exit(EXIT_FAILURE);
#else
- show_usage();
+ bb_show_usage();
#endif
}
}
* standard output is a terminal
* Refer to Posix.2, the description of the `sh' utility. */
if (argv[optind]==NULL && input==stdin &&
- isatty(fileno(stdin)) && isatty(fileno(stdout))) {
+ isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
interactive++;
}
debug_printf("\ninteractive=%d\n", interactive);
if (interactive) {
/* Looks like they want an interactive shell */
-#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
+#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
printf( "\n\n" BB_BANNER " hush - the humble shell v0.01 (testing)\n");
printf( "Enter 'help' for a list of built-in commands.\n\n");
#endif
setup_job_control();
}
-
+
if (argv[optind]==NULL) {
opt=parse_file_outer(stdin);
goto final_return;
debug_printf("\nrunning script '%s'\n", argv[optind]);
global_argv = argv+optind;
global_argc = argc-optind;
- input = xfopen(argv[optind], "r");
+ input = bb_xfopen(argv[optind], "r");
opt = parse_file_outer(input);
#ifdef CONFIG_FEATURE_CLEAN_UP
fclose(input);
- if (cwd && cwd != unknown)
+ if (cwd && cwd != bb_msg_unknown)
free((char*)cwd);
{
struct variables *cur, *tmp;
int len;
int done = 0;
char *p, *p1, *res_str = NULL;
-
+
while ((p = strchr(inp, SPECIAL_VAR_SYMBOL))) {
if (p != inp) {
len = p - inp;
res_str = xrealloc(res_str, (1 + len));
strcpy((res_str + res_str_len), p1);
res_str_len = len;
- }
+ }
*p = SPECIAL_VAR_SYMBOL;
inp = ++p;
done = 1;
int n = 0;
char **list;
char *p1, *p2, *p3;
-
- /* create list of variable values */
+
+ /* create list of variable values */
list = xmalloc(sizeof(*list));
for (i = 0; inp[i]; i++) {
p3 = insert_var_value(inp[i]);
}
if ((p2 = strchr(p1, ' '))) {
len = p2 - p1;
- } else {
+ } else {
len = strlen(p1);
p2 = p1 + len;
}
- /* we use n + 2 in realloc for list,because we add
+ /* we use n + 2 in realloc for list,because we add
* new element and then we will add NULL element */
- list = xrealloc(list, sizeof(*list) * (n + 2));
+ list = xrealloc(list, sizeof(*list) * (n + 2));
list[n] = xmalloc(2 + name_len + len);
strcpy(list[n], name);
strcat(list[n], "=");
}
list[n] = NULL;
return list;
-}
+}
/* Make new string for parser */
static char * make_string(char ** inp)