hush: make expand_vars_to_list() a bit more sane
[oweals/busybox.git] / shell / ash.c
index 24958c0fceb3208ab265129aa17d7d73a4000838..051cc671fcef4a21d68a36ccea38c8251ce476fc 100644 (file)
 #define IF_BASH_PATTERN_SUBST       IF_ASH_BASH_COMPAT
 #define    BASH_SUBSTR          ENABLE_ASH_BASH_COMPAT
 #define IF_BASH_SUBSTR              IF_ASH_BASH_COMPAT
-/* [[ EXPR ]] */
+/* BASH_TEST2: [[ EXPR ]]
+ * Status of [[ support:
+ * We replace && and || with -a and -o
+ * TODO:
+ * singleword+noglob expansion:
+ *   v='a b'; [[ $v = 'a b' ]]; echo 0:$?
+ *   [[ /bin/n* ]]; echo 0:$?
+ * -a/-o are not AND/OR ops! (they are just strings)
+ * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc)
+ * = is glob match operator, not equality operator: STR = GLOB
+ * (in GLOB, quoting is significant on char-by-char basis: a*cd"*")
+ * == same as =
+ * add =~ regex match operator: STR =~ REGEX
+ */
 #define    BASH_TEST2           (ENABLE_ASH_BASH_COMPAT * ENABLE_ASH_TEST)
 #define    BASH_SOURCE          ENABLE_ASH_BASH_COMPAT
 #define    BASH_PIPEFAIL        ENABLE_ASH_BASH_COMPAT
@@ -7562,9 +7575,16 @@ expandmeta(struct strlist *str /*, int flag*/)
 /*
  * Do metacharacter (i.e. *, ?, [...]) expansion.
  */
+typedef struct exp_t {
+       char *dir;
+       unsigned dir_max;
+} exp_t;
 static void
-expmeta(char *expdir, char *enddir, char *name)
+expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
 {
+#define expdir exp->dir
+#define expdir_max exp->dir_max
+       char *enddir = expdir + expdir_len;
        char *p;
        const char *cp;
        char *start;
@@ -7607,15 +7627,15 @@ expmeta(char *expdir, char *enddir, char *name)
                }
        }
        if (metaflag == 0) {    /* we've reached the end of the file name */
-               if (enddir != expdir)
-                       metaflag++;
+               if (!expdir_len)
+                       return;
                p = name;
                do {
                        if (*p == '\\')
                                p++;
                        *enddir++ = *p;
                } while (*p++);
-               if (metaflag == 0 || lstat(expdir, &statb) >= 0)
+               if (lstat(expdir, &statb) == 0)
                        addfname(expdir);
                return;
        }
@@ -7628,19 +7648,14 @@ expmeta(char *expdir, char *enddir, char *name)
                        *enddir++ = *p++;
                } while (p < start);
        }
-       if (enddir == expdir) {
+       *enddir = '\0';
+       cp = expdir;
+       expdir_len = enddir - cp;
+       if (!expdir_len)
                cp = ".";
-       } else if (enddir == expdir + 1 && *expdir == '/') {
-               cp = "/";
-       } else {
-               cp = expdir;
-               enddir[-1] = '\0';
-       }
        dirp = opendir(cp);
        if (dirp == NULL)
                return;
-       if (enddir != expdir)
-               enddir[-1] = '/';
        if (*endname == 0) {
                atend = 1;
        } else {
@@ -7648,6 +7663,7 @@ expmeta(char *expdir, char *enddir, char *name)
                *endname = '\0';
                endname += esc + 1;
        }
+       name_len -= endname - name;
        matchdot = 0;
        p = start;
        if (*p == '\\')
@@ -7662,16 +7678,30 @@ expmeta(char *expdir, char *enddir, char *name)
                                strcpy(enddir, dp->d_name);
                                addfname(expdir);
                        } else {
-                               for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
-                                       continue;
-                               p[-1] = '/';
-                               expmeta(expdir, p, endname);
+                               unsigned offset;
+                               unsigned len;
+
+                               p = stpcpy(enddir, dp->d_name);
+                               *p = '/';
+
+                               offset = p - expdir + 1;
+                               len = offset + name_len + NAME_MAX;
+                               if (len > expdir_max) {
+                                       len += PATH_MAX;
+                                       expdir = ckrealloc(expdir, len);
+                                       expdir_max = len;
+                               }
+
+                               expmeta(exp, endname, name_len, offset);
+                               enddir = expdir + expdir_len;
                        }
                }
        }
        closedir(dirp);
        if (!atend)
                endname[-esc - 1] = esc ? '\\' : '/';
+#undef expdir
+#undef expdir_max
 }
 
 static struct strlist *
@@ -7744,10 +7774,11 @@ expandmeta(struct strlist *str /*, int flag*/)
        /* TODO - EXP_REDIR */
 
        while (str) {
-               char *expdir;
+               exp_t exp;
                struct strlist **savelastp;
                struct strlist *sp;
                char *p;
+               unsigned len;
 
                if (fflag)
                        goto nometa;
@@ -7757,13 +7788,12 @@ expandmeta(struct strlist *str /*, int flag*/)
 
                INT_OFF;
                p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
-               {
-                       int i = strlen(str->text);
-//BUGGY estimation of how long expanded name can be
-                       expdir = ckmalloc(i < 2048 ? 2048 : i+1);
-               }
-               expmeta(expdir, expdir, p);
-               free(expdir);
+               len = strlen(p);
+               exp.dir_max = len + PATH_MAX;
+               exp.dir = ckmalloc(exp.dir_max);
+
+               expmeta(&exp, p, len, 0);
+               free(exp.dir);
                if (p != str->text)
                        free(p);
                INT_ON;
@@ -11611,10 +11641,12 @@ simplecmd(void)
                                case TLP:
                                        function_flag = 0;
                                        break;
+# if BASH_TEST2
                                case TWORD:
                                        if (strcmp("[[", wordtext) == 0)
                                                goto do_func;
                                        /* fall through */
+# endif
                                default:
                                        raise_error_unexpected_syntax(-1);
                                }
@@ -11662,7 +11694,8 @@ simplecmd(void)
        *vpp = NULL;
        *rpp = NULL;
        n = stzalloc(sizeof(struct ncmd));
-       n->type = NCMD;
+       if (NCMD != 0)
+               n->type = NCMD;
        n->ncmd.linno = savelinno;
        n->ncmd.args = args;
        n->ncmd.assign = vars;
@@ -11983,8 +12016,11 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
                CHECKSTRSPACE(4, out);  /* permit 4 calls to USTPUTC */
                switch (SIT(c, synstack->syntax)) {
                case CNL:       /* '\n' */
-                       if (synstack->syntax == BASESYNTAX)
+                       if (synstack->syntax == BASESYNTAX
+                        && !synstack->varnest
+                       ) {
                                goto endword;   /* exit outer loop */
+                       }
                        USTPUTC(c, out);
                        nlprompt();
                        c = pgetc();