-/* $OpenBSD: eval.c,v 1.37 2011/10/11 14:32:43 otto 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, 2011
+ * 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
#include "sh.h"
-__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.109 2011/10/11 19:06:07 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.142 2013/07/24 18:03:57 tg Exp $");
/*
* string expansion
*/
/* 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 */
#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
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 */
+ /* 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 = {
- /* expansion variables */
NULL, { NULL }, NULL, 0
};
SubType st_head, *st;
- /* For trailing newlines in COMSUB */
+ /* record number of trailing newlines in COMSUB */
int newlines = 0;
- int saw_eq, tilde_ok;
- int make_magic;
+ 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;
}
/* init destination string */
Xinit(ds, dp, 128, ATEMP);
type = XBASE;
- sp = cp;
+ sp = ccp;
fdo = 0;
- saw_eq = 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));
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;
*dp++ = ')'; *dp++ = ')';
} else {
struct tbl v;
- char *p;
v.flag = DEFINED|ISSET|INTEGER;
/* not default */
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;
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 & 0x17F) {
- case 0x100 | '#':
+ 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)hash(str_val(st->var)));
+ (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;
/* check for special cases */
d = str_val(st->var);
+ mkssert(d != NULL);
switch (*pat) {
case '#':
/* anchor at begin */
case '#':
case '%':
/* ! DOBLANK,DOBRACE,DOTILDE */
- f = DOPAT | (f&DONTRUNCOMMAND) |
- DOTEMP;
+ f = (f & DONTRUNCOMMAND) |
+ DOPAT | DOTEMP;
st->quotew = quote = 0;
/*
* Prepend open pattern (so |
case '0':
case '/':
case 0x100 | '#':
+ case 0x100 | 'Q':
dp = Xrestpos(ds, dp, st->base);
type = XSUB;
if (f&DOBLANK)
case OPAT:
/* open pattern: *(foo|bar) */
/* Next char is the type of pattern */
- make_magic = 1;
+ make_magic = true;
c = *sp++ | 0x80;
break;
case SPAT:
/* pattern separator (|) */
- make_magic = 1;
+ make_magic = true;
c = '|';
break;
case CPAT:
/* close pattern */
- make_magic = 1;
+ make_magic = true;
c = /*(*/ ')';
break;
}
case XARGSEP:
type = XARG;
quote = 1;
+ /* FALLTHROUGH */
case XARG:
if ((c = *x.str++) == '\0') {
/*
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 "$@" */
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);
}
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;
*/
if (word == IFS_WORD ||
(!ctype(c, C_IFSWS) && c && word == IFS_NWS)) {
- char *p;
-
+ emit_word:
*dp++ = '\0';
- p = Xclose(ds, dp);
+ 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);
+ glob(cp, wp, tobool(f & DOMARKDIRS));
else if ((f & DOPAT) || !(fdo & DOMAGIC))
- XPput(*wp, p);
+ 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;
}
if (!quote)
switch (c) {
case '[':
- case NOT:
+ case '!':
case '-':
case ']':
/*
*dp++ = MAGIC;
}
break;
- case OBRACE:
+ case '{':
+ case '}':
case ',':
- case CBRACE:
- if ((f & DOBRACE) && (c == OBRACE ||
+ if ((f & DOBRACE) && (c == '{' /*}*/ ||
(fdo & DOBRACE))) {
fdo |= DOBRACE|DOMAGIC;
*dp++ = MAGIC;
if (!(f & DOTEMP) && !saw_eq &&
(Flag(FBRACEEXPAND) ||
(f & DOASNTILDE))) {
- saw_eq = 1;
+ saw_eq = true;
tilde_ok = 1;
}
break;
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;
}
}
quote &= ~2;
if (make_magic) {
- make_magic = 0;
+ make_magic = false;
fdo |= DOMAGIC | (f & DOGLOB);
*dp++ = MAGIC;
} else if (ISMAGIC(c)) {
c = sp[0];
if (c == '*' || c == '@') {
switch (stype & 0x17F) {
- case '=': /* can't assign to a vector */
- case '%': /* can't trim a vector (yet) */
+ /* 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) {
} else {
xp->u.strv = (const char **)e->loc->argv + 1;
xp->str = *xp->u.strv++;
- xp->split = c == '@'; /* $@ */
+ /* $@ */
+ xp->split = tobool(c == '@');
state = XARG;
}
/* POSIX 2009? */
XPtrV wv;
switch (stype & 0x17F) {
- case '=': /* can't assign to a vector */
- case '%': /* can't trim a vector (yet) */
+ /* 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);
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 {
if (((stype < 0x100) && (ctype(c, C_SUBOP2) || c == '/' ||
(((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */
c == '=' || c == '-' || c == '?' : c == '+'))) ||
- stype == (0x80 | '0') || stype == (0x100 | '#'))
+ stype == (0x80 | '0') || stype == (0x100 | '#') ||
+ stype == (0x100 | 'Q'))
/* expand word instead of variable value */
state = XBASE;
if (Flag(FNOUNSET) && xp->str == null && !zero_ok &&
* 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;
afree(s, ATEMP);
source = sold;
+ UTFMODE = old_utfmode;
+
if (t == NULL)
return (XBASE);
- if (t != NULL && t->type == TCOM &&
+ /* 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;
shf = shf_open(name = evalstr(io->name, DOTILDE), O_RDONLY, 0,
SHF_MAPHI|SHF_CLEXEC);
if (shf == NULL)
- errorf("%s: %s %s", name, "can't open", "$() input");
- /* no waitlast() */
- xp->split = 0;
+ 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);
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();
/* waitlast() */
- xp->split = 1;
+ xp->split = true;
}
- UTFMODE = old_utfmode;
xp->u.shf = shf;
return (XCOM);
}
/*
* perform #pattern and %pattern substitution in ${}
*/
-
static char *
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);
* 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;
*/
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
* based on a version by Arnold Robbins
*/
-static char *
+char *
tilde(char *cp)
{
char *dp = null;
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;
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;
* expansion. }
*/
if (fdo & DOGLOB)
- glob(start, wp, fdo & DOMARKDIRS);
+ glob(start, wp, tobool(fdo & DOMARKDIRS));
else
XPput(*wp, debunk(start, start, end - start));
return;
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;
}
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);
+}