Added some more '/* getopt not needed */' lines.
[oweals/busybox.git] / lash.c
diff --git a/lash.c b/lash.c
index 874c0aca90fccc251669307abac1864ca6acf567..8f19e526aae35d9b1079fcfee93c789f823869e1 100644 (file)
--- a/lash.c
+++ b/lash.c
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <wordexp.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"
@@ -901,9 +913,10 @@ static char* itoa(register int i)
 static int expand_arguments(char *command)
 {
 #ifdef BB_FEATURE_SH_ENVIRONMENT
-       wordexp_t wrdexp;
+       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 */
@@ -911,15 +924,15 @@ static int expand_arguments(char *command)
 
 #ifdef BB_FEATURE_SH_ENVIRONMENT
 
+
+#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, &wrdexp, 0);
-       
+       retval = wordexp (command, &expand_result, 0);
        if (retval == WRDE_NOSPACE) {
                /* Mem may have been allocated... */
-               wordfree (&wrdexp);
-               error_msg("out of space during expansion");
+               wordfree (&expand_result);
+               error_msg(out_of_space);
                return FALSE;
        }
        if (retval < 0) {
@@ -931,65 +944,159 @@ static int expand_arguments(char *command)
        /* 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 < wrdexp.we_wordc && total_length < BUFSIZ) {
-               length=strlen(wrdexp.we_wordv[i])+1;
+       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 during expansion");
+                       error_msg(out_of_space);
                        return FALSE;
                }
-               strcat(command+total_length, wrdexp.we_wordv[i++]);
+               strcat(command+total_length, expand_result.we_wordv[i++]);
                strcat(command+total_length, " ");
                total_length+=length;
        }
-       wordfree (&wrdexp);
-       
+       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);
+                       }
+               }
+               free(cmd);
+               trim(command);
+       }
        
+#endif 
+
        /* Now do the shell variable substitutions which 
         * wordexp can't do for us, namely $? and $! */
        src = command;
        while((dst = strchr(src,'$')) != NULL){
-               if (!(var = getenv(dst + 1))) {
-                       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;
-#if 0
-                               /* Everything else like $$, $#, $[0-9], etcshould all be 
-                                * expanded by wordexp(), so we can skip that stuff here */
-                               case '$':
-                               case '#':
-                               case '0':case '1':case '2':case '3':case '4':
-                               case '5':case '6':case '7':case '8':case '9':
-                                       break;
-#endif 
-                       }
+               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++;
+                       }
+               }
+               if (var == NULL) {
+                       /* Seems we got an un-expandable variable.  So delete it. */
+                       var = "";
+               }
+               {
                        int subst_len = strlen(var);
-                       char *next_dst;
-                       if ((next_dst=strpbrk(dst+1, " \t~`!$^&*()=|\\{}[];\"'<>?")) == NULL) {
-                               next_dst = dst;
+                       int trail_len = strlen(src);
+                       if (dst+subst_len+trail_len >= command+BUFSIZ) {
+                               error_msg(out_of_space);
+                               return FALSE;
                        }
-                       src = (char*)xrealloc(src, strlen(src) - strlen(next_dst)+strlen(var)+1);
-                       /* Move stuff to the end of the string to accommodate filling 
-                        * the created gap with the new stuff */
-                       memmove(dst+subst_len, next_dst+1, subst_len); 
+                       /* 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 */
-                       strncpy(dst, var, subst_len);
+                       memcpy(dst, var, subst_len);
+                       src = dst+subst_len;
                }
-               src = dst;
-               src++;
        }
 
-
-
-
 #endif 
        return TRUE;
 }
@@ -1626,9 +1733,9 @@ static int busy_loop(FILE * input)
 
 #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);