#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
-#include <glob.h>
#include <signal.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <unistd.h>
#include <getopt.h>
+
+//#undef __GLIBC__
+//#undef __UCLIBC__
+
+#if ( (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1) ) || defined (__UCLIBC__)
+#include <wordexp.h>
+#define expand_t wordexp_t
+#undef BB_FEATURE_SH_BACKTICKS
+#else
+#include <glob.h>
+#define expand_t glob_t
+#endif
#include "busybox.h"
#include "cmdedit.h"
+
static const int MAX_LINE = 256; /* size of input buffer for cwd data */
static const int MAX_READ = 128; /* size of input buffer for `read' builtin */
#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
char **argv; /* program name and arguments */
int num_redirects; /* elements in redirection array */
struct redir_struct *redirects; /* I/O redirects */
- glob_t glob_result; /* result of parameter globbing */
- int free_glob; /* should we globfree(&glob_result)? */
int is_stopped; /* is the program currently running? */
struct job *family; /* pointer back to the child's parent job */
};
/* function prototypes for shell stuff */
static void mark_open(int fd);
static void mark_closed(int fd);
+static void close_all(void);
static void checkjobs(struct jobset *job_list);
static int get_command(FILE * source, char *command);
static int parse_command(char **command_ptr, struct job *job, int *inbg);
static struct jobset job_list = { NULL, NULL };
static int argc;
static char **argv;
-static struct close_me *close_me_head = NULL;
+static struct close_me *close_me_head;
#ifdef BB_FEATURE_SH_ENVIRONMENT
-static int last_bg_pid=-1;
-static int last_return_code=-1;
-static int show_x_trace=FALSE;
+static int last_bg_pid;
+static int last_return_code;
+static int show_x_trace;
#endif
#ifdef BB_FEATURE_SH_IF_EXPRESSIONS
static char syntax_err[]="syntax error near unexpected token";
if (child->argv[1] == NULL)
return EXIT_SUCCESS; /* Really? */
child->argv++;
+ close_all();
pseudo_exec(child);
/* never returns */
}
static void close_all()
{
- struct close_me *c;
- for (c=close_me_head; c; c=c->next) {
+ struct close_me *c, *tmp;
+ for (c=close_me_head; c; c=tmp) {
close(c->fd);
+ tmp=c->next;
+ free(c);
}
close_me_head = NULL;
}
free(cmd->progs[i].argv);
if (cmd->progs[i].redirects)
free(cmd->progs[i].redirects);
- if (cmd->progs[i].free_glob)
- globfree(&cmd->progs[i].glob_result);
}
free(cmd->progs);
if (cmd->text)
}
/* remove trailing newline */
- command[strlen(command) - 1] = '\0';
+ chomp(command);
return 0;
}
*--b = '-';
return b;
}
-#endif
+#endif
-static void expand_argument(struct child_prog *prog, int *argcPtr,
- int *argv_alloced_ptr)
+static int expand_arguments(char *command)
{
- int argc_l = *argcPtr;
- int argv_alloced = *argv_alloced_ptr;
- int rc;
- int flags;
- int i;
+#ifdef BB_FEATURE_SH_ENVIRONMENT
+ expand_t expand_result;
char *src, *dst, *var;
+ int i=0, length, total_length=0, retval;
+ const char *out_of_space = "out of space during expansion";
+#endif
+
+ /* get rid of the terminating \n */
+ chomp(command);
- if (argc_l > 1) { /* cmd->glob_result is already initialized */
- flags = GLOB_APPEND;
- i = prog->glob_result.gl_pathc;
- } else {
- prog->free_glob = 1;
- flags = 0;
- i = 0;
- }
#ifdef BB_FEATURE_SH_ENVIRONMENT
- /* do shell variable substitution */
- src = prog->argv[argc_l - 1];
- while((dst = strchr(src,'$')) != NULL){
- if (!(var = getenv(dst + 1))) {
- switch(*(dst+1)) {
- case '?':
- var = itoa(last_return_code);
- break;
- case '$':
- var = itoa(getpid());
- break;
- case '#':
- var = itoa(argc-1);
- break;
- case '!':
- if (last_bg_pid==-1)
- *(var)='\0';
- else
- var = itoa(last_bg_pid);
- break;
- case '0':case '1':case '2':case '3':case '4':
- case '5':case '6':case '7':case '8':case '9':
- {
- int index=*(dst + 1)-48;
- if (index >= argc) {
- var='\0';
- } else {
- var = argv[index];
- }
- }
- break;
- }
+
+
+#if ( (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1) ) || defined (__UCLIBC__)
+ /* This first part uses wordexp() which is a wonderful C lib
+ * function which expands nearly everything. */
+ retval = wordexp (command, &expand_result, 0);
+ if (retval == WRDE_NOSPACE) {
+ /* Mem may have been allocated... */
+ wordfree (&expand_result);
+ error_msg(out_of_space);
+ return FALSE;
+ }
+ if (retval < 0) {
+ /* Some other error. */
+ error_msg("syntax error");
+ return FALSE;
+ }
+
+ /* Convert from char** (one word per string) to a simple char*,
+ * but don't overflow command which is BUFSIZ in length */
+ *command = '\0';
+ while (i < expand_result.we_wordc && total_length < BUFSIZ) {
+ length=strlen(expand_result.we_wordv[i])+1;
+ if (BUFSIZ-total_length-length <= 0) {
+ error_msg(out_of_space);
+ return FALSE;
}
- if (var) {
- int offset = dst-src;
-#warning I have a memory leak which needs to be plugged somehow
- src = (char*)xmalloc(strlen(src)-strlen(dst)+strlen(var)+1);
- strncpy(src, prog->argv[argc_l -1], offset);
- safe_strncpy(src+offset, var, strlen(var)+1);
- /* If there are any remaining $ variables in the src string, put them back */
- if ((dst = strchr(prog->argv[argc_l -1]+offset+1,'$')) != NULL) {
- offset=strlen(src);
- safe_strncpy(src+strlen(src), dst, strlen(dst)+1);
+ strcat(command+total_length, expand_result.we_wordv[i++]);
+ strcat(command+total_length, " ");
+ total_length+=length;
+ }
+ wordfree (&expand_result);
+#else
+
+ /* Ok. They don't have a recent glibc and they don't have uClibc. Chances
+ * are about 100% they don't have wordexp(). So instead the best we can do
+ * is use glob and then fixup environment variables and such ourselves.
+ * This is better then nothing, but certainly not perfect */
+
+ /* It turns out that glob is very stupid. We have to feed it one word at a
+ * time since it can't cope with a full string. Here we convert command
+ * (char*) into cmd (char**, one word per string) */
+ {
+
+ int flags = GLOB_NOCHECK|GLOB_BRACE|GLOB_TILDE;
+ char * tmpcmd;
+ /* We need a clean copy, so strsep can mess up the copy while
+ * we write stuff into the original (in a minute) */
+ char * cmd = strdup(command);
+ *command = '\0';
+ for (tmpcmd = cmd; (tmpcmd = strsep(&cmd, " \t")) != NULL;) {
+ if (*tmpcmd == '\0')
+ break;
+ retval = glob(tmpcmd, flags, NULL, &expand_result);
+ if (retval == GLOB_NOSPACE) {
+ /* Mem may have been allocated... */
+ globfree (&expand_result);
+ error_msg(out_of_space);
+ return FALSE;
+ } else if (retval != 0) {
+ /* Some other error. GLOB_NOMATCH shouldn't
+ * happen because of the GLOB_NOCHECK flag in
+ * the glob call. */
+ error_msg("syntax error");
+ return FALSE;
+ } else {
+ /* Convert from char** (one word per string) to a simple char*,
+ * but don't overflow command which is BUFSIZ in length */
+ for (i=0; i < expand_result.gl_pathc; i++) {
+ length=strlen(expand_result.gl_pathv[i])+1;
+ if (BUFSIZ-total_length-length <= 0) {
+ error_msg(out_of_space);
+ return FALSE;
+ }
+ strcat(command+total_length, expand_result.gl_pathv[i]);
+ strcat(command+total_length, " ");
+ total_length+=length;
+ }
+ globfree (&expand_result);
}
- prog->argv[argc_l -1] = src;
- } else {
- memset(dst, 0, strlen(src)-strlen(dst));
}
+ free(cmd);
+ trim(command);
}
-#endif
+
+#endif
- if (strpbrk(prog->argv[argc_l - 1],"*[]?")!= NULL){
- rc = glob(prog->argv[argc_l - 1], flags, NULL, &prog->glob_result);
- if (rc == GLOB_NOSPACE) {
- error_msg("out of space during glob operation");
- return;
- } else if (rc == GLOB_NOMATCH ||
- (!rc && (prog->glob_result.gl_pathc - i) == 1 &&
- strcmp(prog->argv[argc_l - 1],
- prog->glob_result.gl_pathv[i]) == 0)) {
- /* we need to remove whatever \ quoting is still present */
- src = dst = prog->argv[argc_l - 1];
- while (*src) {
- if (*src == '\\') {
- src++;
- *dst++ = process_escape_sequence(&src);
- } else {
- *dst++ = *src;
- src++;
+ /* Now do the shell variable substitutions which
+ * wordexp can't do for us, namely $? and $! */
+ src = command;
+ while((dst = strchr(src,'$')) != NULL){
+ var = NULL;
+ switch(*(dst+1)) {
+ case '?':
+ var = itoa(last_return_code);
+ break;
+ case '!':
+ if (last_bg_pid==-1)
+ *(var)='\0';
+ else
+ var = itoa(last_bg_pid);
+ break;
+ /* Everything else like $$, $#, $[0-9], etc should all be
+ * expanded by wordexp(), so we can in theory skip that stuff
+ * here, but just to be on the safe side (i.e. since uClibc
+ * wordexp doesn't do this stuff yet), lets leave it in for
+ * now. */
+ case '$':
+ var = itoa(getpid());
+ break;
+ case '#':
+ var = itoa(argc-1);
+ break;
+ case '0':case '1':case '2':case '3':case '4':
+ case '5':case '6':case '7':case '8':case '9':
+ {
+ int index=*(dst + 1)-48;
+ if (index >= argc) {
+ var='\0';
+ } else {
+ var = argv[index];
+ }
}
+ break;
+
+ }
+ if (var) {
+ /* a single character construction was found, and
+ * already handled in the case statement */
+ src=dst+2;
+ } else {
+ /* Looks like an environment variable */
+ char delim_hold;
+ int num_skip_chars=1;
+ int dstlen = strlen(dst);
+ /* Is this a ${foo} type variable? */
+ if (dstlen >=2 && *(dst+1) == '{') {
+ src=strchr(dst+1, '}');
+ num_skip_chars=2;
+ } else {
+ src=strpbrk(dst+1, " \t~`!$^&*()=|\\[];\"'<>?./");
+ }
+ if (src == NULL) {
+ src = dst+dstlen;
+ }
+ delim_hold=*src;
+ *src='\0'; /* temporary */
+ var = getenv(dst + num_skip_chars);
+ *src=delim_hold;
+ if (num_skip_chars==2) {
+ src++;
}
- *dst = '\0';
- } else if (!rc) {
- argv_alloced += (prog->glob_result.gl_pathc - i);
- prog->argv = xrealloc(prog->argv, argv_alloced * sizeof(*prog->argv));
- memcpy(prog->argv + (argc_l - 1), prog->glob_result.gl_pathv + i,
- sizeof(*(prog->argv)) * (prog->glob_result.gl_pathc - i));
- argc_l += (prog->glob_result.gl_pathc - i - 1);
}
- }else{
- src = dst = prog->argv[argc_l - 1];
- while (*src) {
- if (*src == '\\') {
- src++;
- *dst++ = process_escape_sequence(&src);
- } else {
- *dst++ = *src;
- src++;
- }
+ if (var == NULL) {
+ /* Seems we got an un-expandable variable. So delete it. */
+ var = "";
+ }
+ {
+ int subst_len = strlen(var);
+ int trail_len = strlen(src);
+ if (dst+subst_len+trail_len >= command+BUFSIZ) {
+ error_msg(out_of_space);
+ return FALSE;
}
- *dst = '\0';
-
- prog->glob_result.gl_pathc=0;
- if (flags==0)
- prog->glob_result.gl_pathv=NULL;
+ /* Move stuff to the end of the string to accommodate
+ * filling the created gap with the new stuff */
+ memmove(dst+subst_len, src, trail_len);
+ *(dst+subst_len+trail_len)='\0';
+ /* Now copy in the new stuff */
+ memcpy(dst, var, subst_len);
+ src = dst+subst_len;
+ }
}
- *argv_alloced_ptr = argv_alloced;
- *argcPtr = argc_l;
+
+#endif
+ return TRUE;
}
/* Return cmd->num_progs as 0 if no command is present (e.g. an empty
prog = job->progs;
prog->num_redirects = 0;
prog->redirects = NULL;
- prog->free_glob = 0;
prog->is_stopped = 0;
prog->family = job;
sizeof(*prog->argv) *
argv_alloced);
}
- expand_argument(prog, &argc_l, &argv_alloced);
prog->argv[argc_l] = buf;
}
} else
if (*chptr && *prog->argv[argc_l]) {
buf++, argc_l++;
- expand_argument(prog, &argc_l, &argv_alloced);
prog->argv[argc_l] = buf;
}
}
prog = job->progs + (job->num_progs - 1);
prog->num_redirects = 0;
prog->redirects = NULL;
- prog->free_glob = 0;
prog->is_stopped = 0;
prog->family = job;
argc_l = 0;
if (*prog->argv[argc_l]) {
argc_l++;
- expand_argument(prog, &argc_l, &argv_alloced);
}
if (!argc_l) {
free_job(job);
next_command = command;
}
+ if (expand_arguments(next_command) == FALSE) {
+ free(command);
+ command = (char *) xcalloc(BUFSIZ, sizeof(char));
+ next_command = NULL;
+ continue;
+ }
+
if (!parse_command(&next_command, &newjob, &inbg) &&
newjob.num_progs) {
int pipefds[2] = {-1,-1};
#ifdef BB_FEATURE_SH_ENVIRONMENT
last_return_code=WEXITSTATUS(status);
-#endif
debug_printf("'%s' exited -- return code %d\n",
job_list.fg->text, last_return_code);
+#endif
if (!job_list.fg->running_progs) {
/* child exited */
remove_job(&job_list, job_list.fg);
argc = argc_l;
argv = argv_l;
+ /* These variables need re-initializing when recursing */
shell_context = 0;
cwd=NULL;
-#ifdef BB_FEATURE_SH_STANDALONE_SHELL
- /* These variables need re-initializing when recursing */
local_pending_command = NULL;
+ close_me_head = NULL;
job_list.head = NULL;
job_list.fg = NULL;
#ifdef BB_FEATURE_SH_ENVIRONMENT
- last_bg_pid=-1;
- last_return_code=-1;
+ last_bg_pid=1;
+ last_return_code=1;
show_x_trace=FALSE;
-#endif
#endif
if (argv[0] && argv[0][0] == '-') {