OSDN Git Service

Update to mksh R48
[android-x86/external-mksh.git] / src / eval.c
index c22e346..b6ff1dc 100644 (file)
@@ -1,7 +1,8 @@
-/*     $OpenBSD: eval.c,v 1.35 2010/03/24 08:27:26 fgsch Exp $ */
+/*     $OpenBSD: eval.c,v 1.39 2013/07/01 17:25:27 jca Exp $   */
 
 /*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+ * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ *              2011, 2012, 2013
  *     Thorsten Glaser <tg@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
@@ -22,7 +23,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.90 2010/07/17 22:09:33 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.142 2013/07/24 18:03:57 tg Exp $");
 
 /*
  * string expansion
@@ -32,15 +33,21 @@ __RCSID("$MirOS: src/bin/mksh/eval.c,v 1.90 2010/07/17 22:09:33 tg Exp $");
  */
 
 /* expansion generator state */
-typedef struct Expand {
-       /* int type; */                 /* see expand() */
-       const char *str;                /* string */
+typedef struct {
+       /* not including an "int type;" member, see expand() */
+       /* string */
+       const char *str;
+       /* source */
        union {
-               const char **strv;      /* string[] */
-               struct shf *shf;        /* file */
-       } u;                            /* source */
-       struct tbl *var;                /* variable in ${var..} */
-       short split;                    /* split "$@" / call waitlast $() */
+               /* string[] */
+               const char **strv;
+               /* file */
+               struct shf *shf;
+       } u;
+       /* variable in ${var...} */
+       struct tbl *var;
+       /* split "$@" / call waitlast in $() */
+       bool split;
 } Expand;
 
 #define        XBASE           0       /* scanning original */
@@ -57,21 +64,21 @@ typedef struct Expand {
 #define IFS_NWS                2       /* have seen IFS non-white-space */
 
 static int varsub(Expand *, const char *, const char *, int *, int *);
-static int comsub(Expand *, const char *);
+static int comsub(Expand *, const char *, int);
+static char *valsub(struct op *, Area *);
 static char *trimsub(char *, char *, int);
-static void glob(char *, XPtrV *, int);
+static void glob(char *, XPtrV *, bool);
 static void globit(XString *, char **, char *, XPtrV *, int);
 static const char *maybe_expand_tilde(const char *, XString *, char **, int);
-static char *tilde(char *);
 #ifndef MKSH_NOPWNAM
 static char *homedir(char *);
 #endif
 static void alt_expand(XPtrV *, char *, char *, char *, int);
-static size_t utflen(const char *);
+static int utflen(const char *);
 static void utfincptr(const char *, mksh_ari_t *);
 
 /* UTFMODE functions */
-static size_t
+static int
 utflen(const char *s)
 {
        size_t n;
@@ -84,7 +91,10 @@ utflen(const char *s)
                }
        } else
                n = strlen(s);
-       return (n);
+
+       if (n > 2147483647)
+               n = 2147483647;
+       return ((int)n);
 }
 
 static void
@@ -108,7 +118,7 @@ substitute(const char *cp, int f)
        s->start = s->str = cp;
        source = s;
        if (yylex(ONEWORD) != LWORD)
-               internal_errorf("substitute");
+               internal_errorf("bad substitution");
        source = sold;
        afree(s, ATEMP);
        return (evalstr(yylval.cp, f));
@@ -129,7 +139,8 @@ eval(const char **ap, int f)
                return (vap.rw);
        }
        XPinit(w, 32);
-       XPput(w, NULL);         /* space for shell name */
+       /* space for shell name */
+       XPput(w, NULL);
        while (*ap != NULL)
                expand(*ap++, &w, f);
        XPput(w, NULL);
@@ -185,39 +196,54 @@ typedef struct SubType {
        struct tbl *var;        /* variable for ${var..} */
        struct SubType *prev;   /* old type */
        struct SubType *next;   /* poped type (to avoid re-allocating) */
+       size_t  base;           /* begin position of expanded word */
        short   stype;          /* [=+-?%#] action after expanded word */
-       short   base;           /* begin position of expanded word */
        short   f;              /* saved value of f (DOPAT, etc) */
        uint8_t quotep;         /* saved value of quote (for ${..[%#]..}) */
        uint8_t quotew;         /* saved value of quote (for ${..[+-=]..}) */
 } SubType;
 
 void
-expand(const char *cp, /* input word */
-    XPtrV *wp,         /* output words */
-    int f)             /* DO* flags */
+expand(
+    /* input word */
+    const char *ccp,
+    /* output words */
+    XPtrV *wp,
+    /* DO* flags */
+    int f)
 {
        int c = 0;
-       int type;               /* expansion type */
-       int quote = 0;          /* quoted */
-       XString ds;             /* destination string */
-       char *dp;               /* destination */
-       const char *sp;         /* source */
-       int fdo, word;          /* second pass flags; have word */
-       int doblank;            /* field splitting of parameter/command subst */
-       Expand x = {            /* expansion variables */
+       /* expansion type */
+       int type;
+       /* quoted */
+       int quote = 0;
+       /* destination string and live pointer */
+       XString ds;
+       char *dp;
+       /* source */
+       const char *sp;
+       /* second pass flags */
+       int fdo;
+       /* have word */
+       int word;
+       /* field splitting of parameter/command substitution */
+       int doblank;
+       /* expansion variables */
+       Expand x = {
                NULL, { NULL }, NULL, 0
        };
        SubType st_head, *st;
-       int newlines = 0; /* For trailing newlines in COMSUB */
-       int saw_eq, tilde_ok;
-       int make_magic;
+       /* record number of trailing newlines in COMSUB */
+       int newlines = 0;
+       bool saw_eq, make_magic;
+       int tilde_ok;
        size_t len;
+       char *cp;
 
-       if (cp == NULL)
+       if (ccp == NULL)
                internal_errorf("expand(NULL)");
        /* for alias, readonly, set, typeset commands */
-       if ((f & DOVACHECK) && is_wdvarassign(cp)) {
+       if ((f & DOVACHECK) && is_wdvarassign(ccp)) {
                f &= ~(DOVACHECK|DOBLANK|DOGLOB|DOTILDE);
                f |= DOASNTILDE;
        }
@@ -226,26 +252,29 @@ expand(const char *cp,    /* input word */
        if (Flag(FMARKDIRS))
                f |= DOMARKDIRS;
        if (Flag(FBRACEEXPAND) && (f & DOGLOB))
-               f |= DOBRACE_;
+               f |= DOBRACE;
 
-       Xinit(ds, dp, 128, ATEMP);      /* init dest. string */
+       /* init destination string */
+       Xinit(ds, dp, 128, ATEMP);
        type = XBASE;
-       sp = cp;
+       sp = ccp;
        fdo = 0;
-       saw_eq = 0;
-       tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0; /* must be 1/0 */
+       saw_eq = false;
+       /* must be 1/0 */
+       tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0;
        doblank = 0;
-       make_magic = 0;
+       make_magic = false;
        word = (f&DOBLANK) ? IFS_WS : IFS_WORD;
        /* clang doesn't know OSUBST comes before CSUBST */
        memset(&st_head, 0, sizeof(st_head));
        st = &st_head;
 
-       while (1) {
+       while (/* CONSTCOND */ 1) {
                Xcheck(ds, dp);
 
                switch (type) {
-               case XBASE:     /* original prefixed string */
+               case XBASE:
+                       /* original prefixed string */
                        c = *sp++;
                        switch (c) {
                        case EOS:
@@ -255,7 +284,8 @@ expand(const char *cp,      /* input word */
                                c = *sp++;
                                break;
                        case QCHAR:
-                               quote |= 2; /* temporary quote */
+                               /* temporary quote */
+                               quote |= 2;
                                c = *sp++;
                                break;
                        case OQUOTE:
@@ -267,18 +297,27 @@ expand(const char *cp,    /* input word */
                                quote = st->quotew;
                                continue;
                        case COMSUB:
+                       case FUNSUB:
+                       case VALSUB:
                                tilde_ok = 0;
                                if (f & DONTRUNCOMMAND) {
                                        word = IFS_WORD;
-                                       *dp++ = '$'; *dp++ = '(';
+                                       *dp++ = '$';
+                                       *dp++ = c == COMSUB ? '(' : '{';
+                                       if (c != COMSUB)
+                                               *dp++ = c == FUNSUB ? ' ' : '|';
                                        while (*sp != '\0') {
                                                Xcheck(ds, dp);
                                                *dp++ = *sp++;
                                        }
-                                       *dp++ = ')';
+                                       if (c != COMSUB) {
+                                               *dp++ = ';';
+                                               *dp++ = '}';
+                                       } else
+                                               *dp++ = ')';
                                } else {
-                                       type = comsub(&x, sp);
-                                       if (type == XCOM && (f&DOBLANK))
+                                       type = comsub(&x, sp, c);
+                                       if (type != XBASE && (f & DOBLANK))
                                                doblank++;
                                        sp = strnul(sp) + 1;
                                        newlines = 0;
@@ -296,37 +335,42 @@ expand(const char *cp,    /* input word */
                                        *dp++ = ')'; *dp++ = ')';
                                } else {
                                        struct tbl v;
-                                       char *p;
 
                                        v.flag = DEFINED|ISSET|INTEGER;
-                                       v.type = 10; /* not default */
+                                       /* not default */
+                                       v.type = 10;
                                        v.name[0] = '\0';
                                        v_evaluate(&v, substitute(sp, 0),
                                            KSH_UNWIND_ERROR, true);
                                        sp = strnul(sp) + 1;
-                                       for (p = str_val(&v); *p; ) {
+                                       cp = str_val(&v);
+                                       while (*cp) {
                                                Xcheck(ds, dp);
-                                               *dp++ = *p++;
+                                               *dp++ = *cp++;
                                        }
                                }
                                continue;
-                       case OSUBST: {  /* ${{#}var{:}[=+-?#%]word} */
-                       /* format is:
+                       case OSUBST: {
+                               /* ${{#}var{:}[=+-?#%]word} */
+                       /*-
+                        * format is:
                         *      OSUBST [{x] plain-variable-part \0
                         *          compiled-word-part CSUBST [}x]
                         * This is where all syntax checking gets done...
                         */
-                               const char *varname = ++sp; /* skip the { or x (}) */
+                               /* skip the { or x (}) */
+                               const char *varname = ++sp;
                                int stype;
                                int slen = 0;
 
-                               sp = cstrchr(sp, '\0') + 1; /* skip variable */
+                               /* skip variable */
+                               sp = cstrchr(sp, '\0') + 1;
                                type = varsub(&x, varname, sp, &stype, &slen);
                                if (type < 0) {
                                        char *beg, *end, *str;
-
  unwind_substsyn:
-                                       sp = varname - 2; /* restore sp */
+                                       /* restore sp */
+                                       sp = varname - 2;
                                        end = (beg = wdcopy(sp, ATEMP)) +
                                            (wdscan(sp, CSUBST) - sp);
                                        /* ({) the } or x is already skipped */
@@ -334,12 +378,13 @@ expand(const char *cp,    /* input word */
                                                *end = EOS;
                                        str = snptreef(NULL, 64, "%S", beg);
                                        afree(beg, ATEMP);
-                                       errorf("%s: bad substitution", str);
+                                       errorf("%s: %s", str, "bad substitution");
                                }
                                if (f & DOBLANK)
                                        doblank++;
                                tilde_ok = 0;
-                               if (type == XBASE) {    /* expand? */
+                               if (type == XBASE) {
+                                       /* expand? */
                                        if (!st->next) {
                                                SubType *newst;
 
@@ -352,12 +397,50 @@ expand(const char *cp,    /* input word */
                                        st->stype = stype;
                                        st->base = Xsavepos(ds, dp);
                                        st->f = f;
-                                       st->var = x.var;
+                                       if (x.var == &vtemp) {
+                                               st->var = tempvar();
+                                               st->var->flag &= ~INTEGER;
+                                               /* can't fail here */
+                                               setstr(st->var,
+                                                   str_val(x.var),
+                                                   KSH_RETURN_ERROR | 0x4);
+                                       } else
+                                               st->var = x.var;
+
                                        st->quotew = st->quotep = quote;
                                        /* skip qualifier(s) */
                                        if (stype)
                                                sp += slen;
-                                       switch (stype & 0x7f) {
+                                       switch (stype & 0x17F) {
+                                       case 0x100 | '#': {
+                                               char *beg, *end;
+                                               mksh_ari_t seed;
+                                               register uint32_t h;
+
+                                               beg = wdcopy(sp, ATEMP);
+                                               end = beg + (wdscan(sp, CSUBST) - sp);
+                                               end[-2] = EOS;
+                                               end = wdstrip(beg, 0);
+                                               afree(beg, ATEMP);
+                                               evaluate(substitute(end, 0),
+                                                   &seed, KSH_UNWIND_ERROR, true);
+                                               /* hash with seed, for now */
+                                               h = seed;
+                                               NZATUpdateString(h,
+                                                   str_val(st->var));
+                                               NZAATFinish(h);
+                                               x.str = shf_smprintf("%08X",
+                                                   (unsigned int)h);
+                                               break;
+                                       }
+                                       case 0x100 | 'Q': {
+                                               struct shf shf;
+
+                                               shf_sopen(NULL, 0, SHF_WR|SHF_DYNAMIC, &shf);
+                                               print_value_quoted(&shf, str_val(st->var));
+                                               x.str = shf_sclose(&shf);
+                                               break;
+                                       }
                                        case '0': {
                                                char *beg, *mid, *end, *stg;
                                                mksh_ari_t from = 0, num = -1, flen, finc = 0;
@@ -374,16 +457,18 @@ expand(const char *cp,    /* input word */
                                                } else {
                                                        end = mid +
                                                            (wdscan(mid, ADELIM) - mid);
-                                                       if (end >= stg)
+                                                       if (end >= stg ||
+                                                           /* more than max delimiters */
+                                                           end[-1] != /*{*/ '}')
                                                                goto unwind_substsyn;
                                                        end[-2] = EOS;
                                                        sp += end - beg - 1;
                                                }
-                                               evaluate(substitute(stg = wdstrip(beg, false, false), 0),
+                                               evaluate(substitute(stg = wdstrip(beg, 0), 0),
                                                    &from, KSH_UNWIND_ERROR, true);
                                                afree(stg, ATEMP);
                                                if (end) {
-                                                       evaluate(substitute(stg = wdstrip(mid, false, false), 0),
+                                                       evaluate(substitute(stg = wdstrip(mid, 0), 0),
                                                            &num, KSH_UNWIND_ERROR, true);
                                                        afree(stg, ATEMP);
                                                }
@@ -422,10 +507,11 @@ expand(const char *cp,    /* input word */
                                                else
                                                        d[-2] = EOS;
                                                sp += (d ? d : p) - s - 1;
-                                               tpat0 = wdstrip(s, true, true);
+                                               tpat0 = wdstrip(s,
+                                                   WDS_KEEPQ | WDS_MAGIC);
                                                pat = substitute(tpat0, 0);
                                                if (d) {
-                                                       d = wdstrip(p, true, false);
+                                                       d = wdstrip(p, WDS_KEEPQ);
                                                        rrep = substitute(d, 0);
                                                        afree(d, ATEMP);
                                                } else
@@ -445,12 +531,45 @@ expand(const char *cp,    /* input word */
                                                *d = '\0';
                                                afree(tpat0, ATEMP);
 
-                                               /* reject empty pattern */
-                                               if (!*pat || gmatchx("", pat, false))
+                                               /* check for special cases */
+                                               d = str_val(st->var);
+                                               mkssert(d != NULL);
+                                               switch (*pat) {
+                                               case '#':
+                                                       /* anchor at begin */
+                                                       tpat0 = pat + 1;
+                                                       tpat1 = rrep;
+                                                       tpat2 = d;
+                                                       break;
+                                               case '%':
+                                                       /* anchor at end */
+                                                       tpat0 = pat + 1;
+                                                       tpat1 = d;
+                                                       tpat2 = rrep;
+                                                       break;
+                                               case '\0':
+                                                       /* empty pattern */
                                                        goto no_repl;
+                                               default:
+                                                       tpat0 = pat;
+                                                       /* silence gcc */
+                                                       tpat1 = tpat2 = NULL;
+                                               }
+                                               if (gmatchx(null, tpat0, false)) {
+                                                       /*
+                                                        * pattern matches
+                                                        * the empty string
+                                                        */
+                                                       if (tpat0 == pat)
+                                                               goto no_repl;
+                                                       /* but is anchored */
+                                                       s = shf_smprintf("%s%s",
+                                                           tpat1, tpat2);
+                                                       goto do_repl;
+                                               }
 
                                                /* prepare string on which to work */
-                                               strdupx(s, str_val(st->var), ATEMP);
+                                               strdupx(s, d, ATEMP);
                                                sbeg = s;
 
                                                /* first see if we have any match at all */
@@ -469,7 +588,8 @@ expand(const char *cp,      /* input word */
                                                        tpat2 = tpat1 + 2;
                                                }
  again_repl:
-                                               /* this would not be necessary if gmatchx would return
+                                               /*
+                                                * this would not be necessary if gmatchx would return
                                                 * the start and end values of a match found, like re*
                                                 */
                                                if (!gmatchx(sbeg, tpat1, false))
@@ -489,8 +609,9 @@ expand(const char *cp,      /* input word */
                                                        while (p >= sbeg) {
                                                                bool gotmatch;
 
-                                                               c = *p; *p = '\0';
-                                                               gotmatch = gmatchx(sbeg, tpat0, false);
+                                                               c = *p;
+                                                               *p = '\0';
+                                                               gotmatch = tobool(gmatchx(sbeg, tpat0, false));
                                                                *p = c;
                                                                if (gotmatch)
                                                                        break;
@@ -506,6 +627,7 @@ expand(const char *cp,      /* input word */
                                                        goto again_repl;
  end_repl:
                                                afree(tpat1, ATEMP);
+ do_repl:
                                                x.str = s;
  no_repl:
                                                afree(pat, ATEMP);
@@ -515,19 +637,23 @@ expand(const char *cp,    /* input word */
                                        }
                                        case '#':
                                        case '%':
-                                               /* ! DOBLANK,DOBRACE_,DOTILDE */
-                                               f = DOPAT | (f&DONTRUNCOMMAND) |
-                                                   DOTEMP_;
+                                               /* ! DOBLANK,DOBRACE,DOTILDE */
+                                               f = (f & DONTRUNCOMMAND) |
+                                                   DOPAT | DOTEMP;
                                                st->quotew = quote = 0;
-                                               /* Prepend open pattern (so |
+                                               /*
+                                                * Prepend open pattern (so |
                                                 * in a trim will work as
                                                 * expected)
                                                 */
-                                               *dp++ = MAGIC;
-                                               *dp++ = (char)('@' | 0x80);
+                                               if (!Flag(FSH)) {
+                                                       *dp++ = MAGIC;
+                                                       *dp++ = '@' | 0x80;
+                                               }
                                                break;
                                        case '=':
-                                               /* Enabling tilde expansion
+                                               /*
+                                                * Enabling tilde expansion
                                                 * after :s here is
                                                 * non-standard ksh, but is
                                                 * consistent with rules for
@@ -542,16 +668,17 @@ expand(const char *cp,    /* input word */
                                                 */
                                                if (!(x.var->flag & INTEGER))
                                                        f |= DOASNTILDE|DOTILDE;
-                                               f |= DOTEMP_;
-                                               /* These will be done after the
+                                               f |= DOTEMP;
+                                               /*
+                                                * These will be done after the
                                                 * value has been assigned.
                                                 */
-                                               f &= ~(DOBLANK|DOGLOB|DOBRACE_);
+                                               f &= ~(DOBLANK|DOGLOB|DOBRACE);
                                                tilde_ok = 1;
                                                break;
                                        case '?':
                                                f &= ~DOBLANK;
-                                               f |= DOTEMP_;
+                                               f |= DOTEMP;
                                                /* FALLTHROUGH */
                                        default:
                                                /* Enable tilde expansion */
@@ -563,22 +690,30 @@ expand(const char *cp,    /* input word */
                                        sp += wdscan(sp, CSUBST) - sp;
                                continue;
                        }
-                       case CSUBST: /* only get here if expanding word */
+                       case CSUBST:
+                               /* only get here if expanding word */
  do_CSUBST:
-                               sp++; /* ({) skip the } or x */
-                               tilde_ok = 0;   /* in case of ${unset:-} */
+                               /* ({) skip the } or x */
+                               sp++;
+                               /* in case of ${unset:-} */
+                               tilde_ok = 0;
                                *dp = '\0';
                                quote = st->quotep;
                                f = st->f;
                                if (f&DOBLANK)
                                        doblank--;
-                               switch (st->stype&0x7f) {
+                               switch (st->stype & 0x17F) {
                                case '#':
                                case '%':
-                                       /* Append end-pattern */
-                                       *dp++ = MAGIC; *dp++ = ')'; *dp = '\0';
+                                       if (!Flag(FSH)) {
+                                               /* Append end-pattern */
+                                               *dp++ = MAGIC;
+                                               *dp++ = ')';
+                                       }
+                                       *dp = '\0';
                                        dp = Xrestpos(ds, dp, st->base);
-                                       /* Must use st->var since calling
+                                       /*
+                                        * Must use st->var since calling
                                         * global would break things
                                         * like x[i+=1].
                                         */
@@ -593,20 +728,24 @@ expand(const char *cp,    /* input word */
                                        st = st->prev;
                                        continue;
                                case '=':
-                                       /* Restore our position and substitute
+                                       /*
+                                        * Restore our position and substitute
                                         * the value of st->var (may not be
                                         * the assigned value in the presence
                                         * of integer/right-adj/etc attributes).
                                         */
                                        dp = Xrestpos(ds, dp, st->base);
-                                       /* Must use st->var since calling
+                                       /*
+                                        * Must use st->var since calling
                                         * global would cause with things
                                         * like x[i+=1] to be evaluated twice.
                                         */
-                                       /* Note: not exported by FEXPORT
+                                       /*
+                                        * Note: not exported by FEXPORT
                                         * in AT&T ksh.
                                         */
-                                       /* XXX POSIX says readonly is only
+                                       /*
+                                        * XXX POSIX says readonly is only
                                         * fatal for special builtins (setstr
                                         * does readonly check).
                                         */
@@ -630,6 +769,8 @@ expand(const char *cp,      /* input word */
                                }
                                case '0':
                                case '/':
+                               case 0x100 | '#':
+                               case 0x100 | 'Q':
                                        dp = Xrestpos(ds, dp, st->base);
                                        type = XSUB;
                                        if (f&DOBLANK)
@@ -641,33 +782,38 @@ expand(const char *cp,    /* input word */
                                type = XBASE;
                                continue;
 
-                       case OPAT: /* open pattern: *(foo|bar) */
+                       case OPAT:
+                               /* open pattern: *(foo|bar) */
                                /* Next char is the type of pattern */
-                               make_magic = 1;
-                               c = *sp++ + 0x80;
+                               make_magic = true;
+                               c = *sp++ | 0x80;
                                break;
 
-                       case SPAT: /* pattern separator (|) */
-                               make_magic = 1;
+                       case SPAT:
+                               /* pattern separator (|) */
+                               make_magic = true;
                                c = '|';
                                break;
 
-                       case CPAT: /* close pattern */
-                               make_magic = 1;
+                       case CPAT:
+                               /* close pattern */
+                               make_magic = true;
                                c = /*(*/ ')';
                                break;
                        }
                        break;
 
                case XNULLSUB:
-                       /* Special case for "$@" (and "${foo[@]}") - no
+                       /*
+                        * Special case for "$@" (and "${foo[@]}") - no
                         * word is generated if $# is 0 (unless there is
                         * other stuff inside the quotes).
                         */
                        type = XBASE;
                        if (f&DOBLANK) {
                                doblank--;
-                               /* not really correct: x=; "$x$@" should
+                               /*
+                                * not really correct: x=; "$x$@" should
                                 * generate a null argument and
                                 * set A; "${@:+}" shouldn't.
                                 */
@@ -689,9 +835,11 @@ expand(const char *cp,     /* input word */
                case XARGSEP:
                        type = XARG;
                        quote = 1;
+                       /* FALLTHROUGH */
                case XARG:
                        if ((c = *x.str++) == '\0') {
-                               /* force null words to be created so
+                               /*
+                                * force null words to be created so
                                 * set -- '' 2 ''; foo "$@" will do
                                 * the right thing
                                 */
@@ -707,7 +855,10 @@ expand(const char *cp,     /* input word */
                                if (c == 0) {
                                        if (quote && !x.split)
                                                continue;
+                                       /* this is so we don't terminate */
                                        c = ' ';
+                                       /* now force-emit a word */
+                                       goto emit_word;
                                }
                                if (quote && x.split) {
                                        /* terminate word for "$@" */
@@ -718,13 +869,19 @@ expand(const char *cp,    /* input word */
                        break;
 
                case XCOM:
-                       if (newlines) {         /* Spit out saved NLs */
+                       if (x.u.shf == NULL) {
+                               /* $(<...) failed */
+                               subst_exstat = 1;
+                               /* fake EOF */
+                               c = EOF;
+                       } else if (newlines) {
+                               /* spit out saved NLs */
                                c = '\n';
                                --newlines;
                        } else {
                                while ((c = shf_getc(x.u.shf)) == 0 || c == '\n')
                                        if (c == '\n')
-                                               /* Save newlines */
+                                               /* save newlines */
                                                newlines++;
                                if (newlines && c != EOF) {
                                        shf_ungetc(c, x.u.shf);
@@ -734,7 +891,8 @@ expand(const char *cp,      /* input word */
                        }
                        if (c == EOF) {
                                newlines = 0;
-                               shf_close(x.u.shf);
+                               if (x.u.shf)
+                                       shf_close(x.u.shf);
                                if (x.split)
                                        subst_exstat = waitlast();
                                type = XBASE;
@@ -748,7 +906,8 @@ expand(const char *cp,      /* input word */
                /* check for end of word or IFS separation */
                if (c == 0 || (!quote && (f & DOBLANK) && doblank &&
                    !make_magic && ctype(c, C_IFS))) {
-                       /* How words are broken up:
+                       /*-
+                        * How words are broken up:
                         *                      |       value of c
                         *      word            |       ws      nws     0
                         *      -----------------------------------
@@ -761,39 +920,43 @@ expand(const char *cp,    /* input word */
                         */
                        if (word == IFS_WORD ||
                            (!ctype(c, C_IFSWS) && c && word == IFS_NWS)) {
-                               char *p;
-
+ emit_word:
                                *dp++ = '\0';
-                               p = Xclose(ds, dp);
-                               if (fdo & DOBRACE_)
+                               cp = Xclose(ds, dp);
+                               if (fdo & DOBRACE)
                                        /* also does globbing */
-                                       alt_expand(wp, p, p,
-                                           p + Xlength(ds, (dp - 1)),
+                                       alt_expand(wp, cp, cp,
+                                           cp + Xlength(ds, (dp - 1)),
                                            fdo | (f & DOMARKDIRS));
                                else if (fdo & DOGLOB)
-                                       glob(p, wp, f & DOMARKDIRS);
-                               else if ((f & DOPAT) || !(fdo & DOMAGIC_))
-                                       XPput(*wp, p);
+                                       glob(cp, wp, tobool(f & DOMARKDIRS));
+                               else if ((f & DOPAT) || !(fdo & DOMAGIC))
+                                       XPput(*wp, cp);
                                else
-                                       XPput(*wp, debunk(p, p, strlen(p) + 1));
+                                       XPput(*wp, debunk(cp, cp,
+                                           strlen(cp) + 1));
                                fdo = 0;
-                               saw_eq = 0;
+                               saw_eq = false;
                                tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0;
-                               if (c != 0)
-                                       Xinit(ds, dp, 128, ATEMP);
-                       }
-                       if (c == 0)
+                               if (c == 0)
+                                       return;
+                               Xinit(ds, dp, 128, ATEMP);
+                       } else if (c == 0) {
                                return;
+                       } else if (type == XSUB && ctype(c, C_IFS) &&
+                           !ctype(c, C_IFSWS) && Xlength(ds, dp) == 0) {
+                               *(cp = alloc(1, ATEMP)) = '\0';
+                               XPput(*wp, cp);
+                               type = XSUBMID;
+                       }
                        if (word != IFS_NWS)
                                word = ctype(c, C_IFSWS) ? IFS_WS : IFS_NWS;
                } else {
                        if (type == XSUB) {
                                if (word == IFS_NWS &&
                                    Xlength(ds, dp) == 0) {
-                                       char *p;
-
-                                       *(p = alloc(1, ATEMP)) = '\0';
-                                       XPput(*wp, p);
+                                       *(cp = alloc(1, ATEMP)) = '\0';
+                                       XPput(*wp, cp);
                                }
                                type = XSUBMID;
                        }
@@ -804,15 +967,16 @@ expand(const char *cp,    /* input word */
                        if (!quote)
                                switch (c) {
                                case '[':
-                               case NOT:
+                               case '!':
                                case '-':
                                case ']':
-                                       /* For character classes - doesn't hurt
+                                       /*
+                                        * For character classes - doesn't hurt
                                         * to have magic !,-,]s outside of
                                         * [...] expressions.
                                         */
                                        if (f & (DOPAT | DOGLOB)) {
-                                               fdo |= DOMAGIC_;
+                                               fdo |= DOMAGIC;
                                                if (c == '[')
                                                        fdo |= f & DOGLOB;
                                                *dp++ = MAGIC;
@@ -821,35 +985,37 @@ expand(const char *cp,    /* input word */
                                case '*':
                                case '?':
                                        if (f & (DOPAT | DOGLOB)) {
-                                               fdo |= DOMAGIC_ | (f & DOGLOB);
+                                               fdo |= DOMAGIC | (f & DOGLOB);
                                                *dp++ = MAGIC;
                                        }
                                        break;
-                               case OBRACE:
+                               case '{':
+                               case '}':
                                case ',':
-                               case CBRACE:
-                                       if ((f & DOBRACE_) && (c == OBRACE ||
-                                           (fdo & DOBRACE_))) {
-                                               fdo |= DOBRACE_|DOMAGIC_;
+                                       if ((f & DOBRACE) && (c == '{' /*}*/ ||
+                                           (fdo & DOBRACE))) {
+                                               fdo |= DOBRACE|DOMAGIC;
                                                *dp++ = MAGIC;
                                        }
                                        break;
                                case '=':
                                        /* Note first unquoted = for ~ */
-                                       if (!(f & DOTEMP_) && !saw_eq &&
+                                       if (!(f & DOTEMP) && !saw_eq &&
                                            (Flag(FBRACEEXPAND) ||
                                            (f & DOASNTILDE))) {
-                                               saw_eq = 1;
+                                               saw_eq = true;
                                                tilde_ok = 1;
                                        }
                                        break;
-                               case ':': /* : */
+                               case ':':
+                                       /* : */
                                        /* Note unquoted : for ~ */
-                                       if (!(f & DOTEMP_) && (f & DOASNTILDE))
+                                       if (!(f & DOTEMP) && (f & DOASNTILDE))
                                                tilde_ok = 1;
                                        break;
                                case '~':
-                                       /* tilde_ok is reset whenever
+                                       /*
+                                        * tilde_ok is reset whenever
                                         * any of ' " $( $(( ${ } are seen.
                                         * Note that tilde_ok must be preserved
                                         * through the sequence ${A=a=}~
@@ -857,35 +1023,36 @@ expand(const char *cp,   /* input word */
                                        if (type == XBASE &&
                                            (f & (DOTILDE|DOASNTILDE)) &&
                                            (tilde_ok & 2)) {
-                                               const char *p;
-                                               char *dp_x;
+                                               const char *tcp;
+                                               char *tdp = dp;
 
-                                               dp_x = dp;
-                                               p = maybe_expand_tilde(sp,
-                                                   &ds, &dp_x,
+                                               tcp = maybe_expand_tilde(sp,
+                                                   &ds, &tdp,
                                                    f & DOASNTILDE);
-                                               if (p) {
-                                                       if (dp != dp_x)
+                                               if (tcp) {
+                                                       if (dp != tdp)
                                                                word = IFS_WORD;
-                                                       dp = dp_x;
-                                                       sp = p;
+                                                       dp = tdp;
+                                                       sp = tcp;
                                                        continue;
                                                }
                                        }
                                        break;
                                }
                        else
-                               quote &= ~2; /* undo temporary */
+                               /* undo temporary */
+                               quote &= ~2;
 
                        if (make_magic) {
-                               make_magic = 0;
-                               fdo |= DOMAGIC_ | (f & DOGLOB);
+                               make_magic = false;
+                               fdo |= DOMAGIC | (f & DOGLOB);
                                *dp++ = MAGIC;
                        } else if (ISMAGIC(c)) {
-                               fdo |= DOMAGIC_;
+                               fdo |= DOMAGIC;
                                *dp++ = MAGIC;
                        }
-                       *dp++ = c; /* save output char */
+                       /* save output char */
+                       *dp++ = c;
                        word = IFS_WORD;
                }
        }
@@ -907,7 +1074,8 @@ varsub(Expand *xp, const char *sp, const char *word,
        struct tbl *vp;
        bool zero_ok = false;
 
-       if ((stype = sp[0]) == '\0')    /* Bad variable name */
+       if ((stype = sp[0]) == '\0')
+               /* Bad variable name */
                return (-1);
 
        xp->var = NULL;
@@ -974,8 +1142,9 @@ varsub(Expand *xp, const char *sp, const char *word,
                        }
                }
                if (Flag(FNOUNSET) && c == 0 && !zero_ok)
-                       errorf("%s: parameter not set", sp);
-               *stypep = 0; /* unqualified variable/string substitution */
+                       errorf("%s: %s", sp, "parameter not set");
+               /* unqualified variable/string substitution */
+               *stypep = 0;
                xp->str = shf_smprintf("%d", c);
                return (XSUB);
        }
@@ -1000,14 +1169,24 @@ varsub(Expand *xp, const char *sp, const char *word,
        } else if (ctype(c, C_SUBOP1)) {
                slen += 2;
                stype |= c;
-       } else if (ctype(c, C_SUBOP2)) { /* Note: ksh88 allows :%, :%%, etc */
+       } else if (ctype(c, C_SUBOP2)) {
+               /* Note: ksh88 allows :%, :%%, etc */
                slen += 2;
                stype = c;
                if (word[slen + 0] == CHAR && c == word[slen + 1]) {
                        stype |= 0x80;
                        slen += 2;
                }
-       } else if (stype)       /* : is not ok */
+       } else if (c == '@') {
+               /* @x where x is command char */
+               slen += 2;
+               stype |= 0x100;
+               if (word[slen] == CHAR) {
+                       stype |= word[slen + 1];
+                       slen += 2;
+               }
+       } else if (stype)
+               /* : is not ok */
                return (-1);
        if (!stype && *word != CSUBST)
                return (-1);
@@ -1016,12 +1195,16 @@ varsub(Expand *xp, const char *sp, const char *word,
 
        c = sp[0];
        if (c == '*' || c == '@') {
-               switch (stype & 0x7f) {
-               case '=':       /* can't assign to a vector */
-               case '%':       /* can't trim a vector (yet) */
+               switch (stype & 0x17F) {
+               /* can't assign to a vector */
+               case '=':
+               /* can't trim a vector (yet) */
+               case '%':
                case '#':
                case '0':
                case '/':
+               case 0x100 | '#':
+               case 0x100 | 'Q':
                        return (-1);
                }
                if (e->loc->argc == 0) {
@@ -1031,22 +1214,28 @@ varsub(Expand *xp, const char *sp, const char *word,
                } else {
                        xp->u.strv = (const char **)e->loc->argv + 1;
                        xp->str = *xp->u.strv++;
-                       xp->split = c == '@'; /* $@ */
+                       /* $@ */
+                       xp->split = tobool(c == '@');
                        state = XARG;
                }
-               zero_ok = true; /* POSIX 2009? */
+               /* POSIX 2009? */
+               zero_ok = true;
        } else {
                if ((p = cstrchr(sp, '[')) && (p[1] == '*' || p[1] == '@') &&
                    p[2] == ']') {
                        XPtrV wv;
 
-                       switch (stype & 0x7f) {
-                       case '=':       /* can't assign to a vector */
-                       case '%':       /* can't trim a vector (yet) */
+                       switch (stype & 0x17F) {
+                       /* can't assign to a vector */
+                       case '=':
+                       /* can't trim a vector (yet) */
+                       case '%':
                        case '#':
                        case '?':
                        case '0':
                        case '/':
+                       case 0x100 | '#':
+                       case 0x100 | 'Q':
                                return (-1);
                        }
                        XPinit(wv, 32);
@@ -1068,18 +1257,19 @@ varsub(Expand *xp, const char *sp, const char *word,
                                XPput(wv, 0);
                                xp->u.strv = (const char **)XPptrv(wv);
                                xp->str = *xp->u.strv++;
-                               xp->split = p[1] == '@'; /* ${foo[@]} */
+                               /* ${foo[@]} */
+                               xp->split = tobool(p[1] == '@');
                                state = XARG;
                        }
                } else {
                        /* Can't assign things like $! or $1 */
-                       if ((stype & 0x7f) == '=' &&
+                       if ((stype & 0x17F) == '=' &&
                            ctype(*sp, C_VAR1 | C_DIGIT))
                                return (-1);
                        if (*sp == '!' && sp[1]) {
                                ++sp;
                                xp->var = global(sp);
-                               if (cstrchr(sp, '[')) {
+                               if (vstrchr(sp, '[')) {
                                        if (xp->var->flag & ISSET)
                                                xp->str = shf_smprintf("%lu",
                                                    arrayindex(xp->var));
@@ -1088,7 +1278,8 @@ varsub(Expand *xp, const char *sp, const char *word,
                                } else if (xp->var->flag & ISSET)
                                        xp->str = xp->var->name;
                                else
-                                       xp->str = "0";  /* ksh93 compat */
+                                       /* ksh93 compat */
+                                       xp->str = "0";
                        } else {
                                xp->var = global(sp);
                                xp->str = str_val(xp->var);
@@ -1097,15 +1288,18 @@ varsub(Expand *xp, const char *sp, const char *word,
                }
        }
 
-       c = stype&0x7f;
+       c = stype & 0x7F;
        /* test the compiler's code generator */
-       if (ctype(c, C_SUBOP2) || stype == (0x80 | '0') || c == '/' ||
+       if (((stype < 0x100) && (ctype(c, C_SUBOP2) || c == '/' ||
            (((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */
-           c == '=' || c == '-' || c == '?' : c == '+'))
-               state = XBASE;  /* expand word instead of variable value */
+           c == '=' || c == '-' || c == '?' : c == '+'))) ||
+           stype == (0x80 | '0') || stype == (0x100 | '#') ||
+           stype == (0x100 | 'Q'))
+               /* expand word instead of variable value */
+               state = XBASE;
        if (Flag(FNOUNSET) && xp->str == null && !zero_ok &&
            (ctype(c, C_SUBOP2) || (state != XBASE && c != '+')))
-               errorf("%s: parameter not set", sp);
+               errorf("%s: %s", sp, "parameter not set");
        return (state);
 }
 
@@ -1113,37 +1307,78 @@ varsub(Expand *xp, const char *sp, const char *word,
  * Run the command in $(...) and read its output.
  */
 static int
-comsub(Expand *xp, const char *cp)
+comsub(Expand *xp, const char *cp, int fn MKSH_A_UNUSED)
 {
        Source *s, *sold;
        struct op *t;
        struct shf *shf;
+       uint8_t old_utfmode = UTFMODE;
 
        s = pushs(SSTRING, ATEMP);
        s->start = s->str = cp;
        sold = source;
-       t = compile(s);
+       t = compile(s, true);
        afree(s, ATEMP);
        source = sold;
 
+       UTFMODE = old_utfmode;
+
        if (t == NULL)
                return (XBASE);
 
-       if (t != NULL && t->type == TCOM && /* $(<file) */
+       /* no waitlast() unless specifically enabled later */
+       xp->split = false;
+
+       if (t->type == TCOM &&
            *t->args == NULL && *t->vars == NULL && t->ioact != NULL) {
+               /* $(<file) */
                struct ioword *io = *t->ioact;
                char *name;
 
-               if ((io->flag&IOTYPE) != IOREAD)
-                       errorf("funny $() command: %s",
+               if ((io->flag & IOTYPE) != IOREAD)
+                       errorf("%s: %s", "funny $() command",
                            snptreef(NULL, 32, "%R", io));
                shf = shf_open(name = evalstr(io->name, DOTILDE), O_RDONLY, 0,
                        SHF_MAPHI|SHF_CLEXEC);
                if (shf == NULL)
-                       errorf("%s: cannot open $() input", name);
-               xp->split = 0;  /* no waitlast() */
+                       warningf(!Flag(FTALKING), "%s: %s %s: %s", name,
+                           "can't open", "$(<...) input", cstrerror(errno));
+       } else if (fn == FUNSUB) {
+               int ofd1;
+               struct temp *tf = NULL;
+
+               /*
+                * create a temporary file, open for reading and writing,
+                * with an shf open for reading (buffered) but yet unused
+                */
+               maketemp(ATEMP, TT_FUNSUB, &tf);
+               if (!tf->shf) {
+                       errorf("can't %s temporary file %s: %s",
+                           "create", tf->tffn, cstrerror(errno));
+               }
+               /* extract shf from temporary file, unlink and free it */
+               shf = tf->shf;
+               unlink(tf->tffn);
+               afree(tf, ATEMP);
+               /* save stdout and let it point to the tempfile */
+               ofd1 = savefd(1);
+               ksh_dup2(shf_fileno(shf), 1, false);
+               /*
+                * run tree, with output thrown into the tempfile,
+                * in a new function block
+                */
+               valsub(t, NULL);
+               subst_exstat = exstat & 0xFF;
+               /* rewind the tempfile and restore regular stdout */
+               lseek(shf_fileno(shf), (off_t)0, SEEK_SET);
+               restfd(1, ofd1);
+       } else if (fn == VALSUB) {
+               xp->str = valsub(t, ATEMP);
+               subst_exstat = exstat & 0xFF;
+               return (XSUB);
        } else {
                int ofd1, pv[2];
+
                openpipe(pv);
                shf = shf_fdopen(pv[0], SHF_RD, NULL);
                ofd1 = savefd(1);
@@ -1151,10 +1386,11 @@ comsub(Expand *xp, const char *cp)
                        ksh_dup2(pv[1], 1, false);
                        close(pv[1]);
                }
-               execute(t, XFORK|XXCOM|XPIPEO, NULL);
+               execute(t, XXCOM | XPIPEO | XFORK, NULL);
                restfd(1, ofd1);
                startlast();
-               xp->split = 1;  /* waitlast() */
+               /* waitlast() */
+               xp->split = true;
        }
 
        xp->u.shf = shf;
@@ -1164,7 +1400,6 @@ comsub(Expand *xp, const char *cp)
 /*
  * perform #pattern and %pattern substitution in ${}
  */
-
 static char *
 trimsub(char *str, char *pat, int how)
 {
@@ -1172,7 +1407,8 @@ trimsub(char *str, char *pat, int how)
        char *p, c;
 
        switch (how & 0xFF) {
-       case '#':               /* shortest at beginning */
+       case '#':
+               /* shortest match at beginning */
                for (p = str; p <= end; p += utf_ptradj(p)) {
                        c = *p; *p = '\0';
                        if (gmatchx(str, pat, false)) {
@@ -1182,7 +1418,8 @@ trimsub(char *str, char *pat, int how)
                        *p = c;
                }
                break;
-       case '#'|0x80:          /* longest match at beginning */
+       case '#'|0x80:
+               /* longest match at beginning */
                for (p = end; p >= str; p--) {
                        c = *p; *p = '\0';
                        if (gmatchx(str, pat, false)) {
@@ -1192,7 +1429,8 @@ trimsub(char *str, char *pat, int how)
                        *p = c;
                }
                break;
-       case '%':               /* shortest match at end */
+       case '%':
+               /* shortest match at end */
                p = end;
                while (p >= str) {
                        if (gmatchx(p, pat, false))
@@ -1207,7 +1445,8 @@ trimsub(char *str, char *pat, int how)
                                --p;
                }
                break;
-       case '%'|0x80:          /* longest match at end */
+       case '%'|0x80:
+               /* longest match at end */
                for (p = str; p <= end; p++)
                        if (gmatchx(p, pat, false)) {
  trimsub_match:
@@ -1217,7 +1456,8 @@ trimsub(char *str, char *pat, int how)
                break;
        }
 
-       return (str);           /* no match, return string */
+       /* no match, return string */
+       return (str);
 }
 
 /*
@@ -1227,7 +1467,7 @@ trimsub(char *str, char *pat, int how)
 
 /* XXX cp not const 'cause slashes are temporarily replaced with NULs... */
 static void
-glob(char *cp, XPtrV *wp, int markdirs)
+glob(char *cp, XPtrV *wp, bool markdirs)
 {
        int oldsize = XPsize(*wp);
 
@@ -1243,11 +1483,12 @@ glob(char *cp, XPtrV *wp, int markdirs)
 #define GF_GLOBBED     BIT(1)          /* some globbing has been done */
 #define GF_MARKDIR     BIT(2)          /* add trailing / to directories */
 
-/* Apply file globbing to cp and store the matching files in wp. Returns
+/*
+ * Apply file globbing to cp and store the matching files in wp. Returns
  * the number of matches found.
  */
 int
-glob_str(char *cp, XPtrV *wp, int markdirs)
+glob_str(char *cp, XPtrV *wp, bool markdirs)
 {
        int oldsize = XPsize(*wp);
        XString xs;
@@ -1275,8 +1516,10 @@ globit(XString *xs,      /* dest string */
        /* This to allow long expansions to be interrupted */
        intrcheck();
 
-       if (sp == NULL) {       /* end of source path */
-               /* We only need to check if the file exists if a pattern
+       if (sp == NULL) {
+               /* end of source path */
+               /*
+                * We only need to check if the file exists if a pattern
                 * is followed by a non-pattern (eg, foo*x/bar; no check
                 * is needed for foo* since the match must exist) or if
                 * any patterns were expanded and the markdirs option is set.
@@ -1284,15 +1527,16 @@ globit(XString *xs,     /* dest string */
                 */
                if ((check & GF_EXCHECK) ||
                    ((check & GF_MARKDIR) && (check & GF_GLOBBED))) {
-#define stat_check()   (stat_done ? stat_done : \
-                           (stat_done = stat(Xstring(*xs, xp), &statb) < 0 \
-                               ? -1 : 1))
+#define stat_check()   (stat_done ? stat_done : (stat_done = \
+                           stat(Xstring(*xs, xp), &statb) < 0 ? -1 : 1))
                        struct stat lstatb, statb;
-                       int stat_done = 0;       /* -1: failed, 1 ok */
+                       /* -1: failed, 1 ok, 0 not yet done */
+                       int stat_done = 0;
 
-                       if (lstat(Xstring(*xs, xp), &lstatb) < 0)
+                       if (mksh_lstat(Xstring(*xs, xp), &lstatb) < 0)
                                return;
-                       /* special case for systems which strip trailing
+                       /*
+                        * special case for systems which strip trailing
                         * slashes from regular files (eg, /etc/passwd/).
                         * SunOS 4.1.3 does this...
                         */
@@ -1301,7 +1545,8 @@ globit(XString *xs,       /* dest string */
                            (!S_ISLNK(lstatb.st_mode) ||
                            stat_check() < 0 || !S_ISDIR(statb.st_mode)))
                                return;
-                       /* Possibly tack on a trailing / if there isn't already
+                       /*
+                        * Possibly tack on a trailing / if there isn't already
                         * one and if the file is a directory or a symlink to a
                         * directory
                         */
@@ -1328,7 +1573,8 @@ globit(XString *xs,       /* dest string */
        np = strchr(sp, '/');
        if (np != NULL) {
                se = np;
-               odirsep = *np;  /* don't assume '/', can be multiple kinds */
+               /* don't assume '/', can be multiple kinds */
+               odirsep = *np;
                *np++ = '\0';
        } else {
                odirsep = '\0'; /* keep gcc quiet */
@@ -1336,7 +1582,8 @@ globit(XString *xs,       /* dest string */
        }
 
 
-       /* Check if sp needs globbing - done to avoid pattern checks for strings
+       /*
+        * Check if sp needs globbing - done to avoid pattern checks for strings
         * containing MAGIC characters, open [s without the matching close ],
         * etc. (otherwise opendir() will be called which may fail because the
         * directory isn't readable - if no globbing is needed, only execute
@@ -1352,8 +1599,7 @@ globit(XString *xs,       /* dest string */
                DIR *dirp;
                struct dirent *d;
                char *name;
-               int len;
-               int prefix_len;
+               size_t len, prefix_len;
 
                /* xp = *xpp;   copy_non_glob() may have re-alloc'd xs */
                *xp = '\0';
@@ -1365,7 +1611,8 @@ globit(XString *xs,       /* dest string */
                        name = d->d_name;
                        if (name[0] == '.' &&
                            (name[1] == 0 || (name[1] == '.' && name[2] == 0)))
-                               continue; /* always ignore . and .. */
+                               /* always ignore . and .. */
+                               continue;
                        if ((*name == '.' && *sp != '.') ||
                            !gmatchx(name, sp, true))
                                continue;
@@ -1416,7 +1663,8 @@ debunk(char *dp, const char *sp, size_t dlen)
        return (dp);
 }
 
-/* Check if p is an unquoted name, possibly followed by a / or :. If so
+/*
+ * Check if p is an unquoted name, possibly followed by a / or :. If so
  * puts the expanded version in *dcp,dp and returns a pointer in p just
  * past the name, otherwise returns 0.
  */
@@ -1459,7 +1707,7 @@ maybe_expand_tilde(const char *p, XString *dsp, char **dpp, int isassign)
  * based on a version by Arnold Robbins
  */
 
-static char *
+char *
 tilde(char *cp)
 {
        char *dp = null;
@@ -1513,7 +1761,7 @@ alt_expand(XPtrV *wp, char *start, char *exp_start, char *end, int fdo)
        char *p;
 
        /* search for open brace */
-       for (p = exp_start; (p = strchr(p, MAGIC)) && p[1] != OBRACE; p += 2)
+       for (p = exp_start; (p = strchr(p, MAGIC)) && p[1] != '{' /*}*/; p += 2)
                ;
        brace_start = p;
 
@@ -1523,9 +1771,9 @@ alt_expand(XPtrV *wp, char *start, char *exp_start, char *end, int fdo)
                count = 1;
                for (p += 2; *p && count; p++) {
                        if (ISMAGIC(*p)) {
-                               if (*++p == OBRACE)
+                               if (*++p == '{' /*}*/)
                                        count++;
-                               else if (*p == CBRACE)
+                               else if (*p == /*{*/ '}')
                                        --count;
                                else if (*p == ',' && count == 1)
                                        comma = p;
@@ -1534,12 +1782,13 @@ alt_expand(XPtrV *wp, char *start, char *exp_start, char *end, int fdo)
        }
        /* no valid expansions... */
        if (!p || count != 0) {
-               /* Note that given a{{b,c} we do not expand anything (this is
+               /*
+                * Note that given a{{b,c} we do not expand anything (this is
                 * what AT&T ksh does. This may be changed to do the {b,c}
                 * expansion. }
                 */
                if (fdo & DOGLOB)
-                       glob(start, wp, fdo & DOMARKDIRS);
+                       glob(start, wp, tobool(fdo & DOMARKDIRS));
                else
                        XPput(*wp, debunk(start, start, end - start));
                return;
@@ -1555,13 +1804,17 @@ alt_expand(XPtrV *wp, char *start, char *exp_start, char *end, int fdo)
        count = 1;
        for (p = brace_start + 2; p != brace_end; p++) {
                if (ISMAGIC(*p)) {
-                       if (*++p == OBRACE)
+                       if (*++p == '{' /*}*/)
                                count++;
-                       else if ((*p == CBRACE && --count == 0) ||
+                       else if ((*p == /*{*/ '}' && --count == 0) ||
                            (*p == ',' && count == 1)) {
                                char *news;
                                int l1, l2, l3;
 
+                               /*
+                                * addition safe since these operate on
+                                * one string (separate substrings)
+                                */
                                l1 = brace_start - start;
                                l2 = (p - 1) - field_start;
                                l3 = end - brace_end;
@@ -1578,3 +1831,23 @@ alt_expand(XPtrV *wp, char *start, char *exp_start, char *end, int fdo)
        }
        return;
 }
+
+/* helper function due to setjmp/longjmp woes */
+static char *
+valsub(struct op *t, Area *ap)
+{
+       char * volatile cp = NULL;
+       struct tbl * volatile vp = NULL;
+
+       newenv(E_FUNC);
+       newblock();
+       if (ap)
+               vp = local("REPLY", false);
+       if (!kshsetjmp(e->jbuf))
+               execute(t, XXCOM | XERROK, NULL);
+       if (vp)
+               strdupx(cp, str_val(vp), ap);
+       quitenv(NULL);
+
+       return (cp);
+}