/* $OpenBSD: eval.c,v 1.37 2011/10/11 14:32:43 otto 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.136 2013/02/10 23:43:59 tg Exp $");
/*
* string expansion
struct shf *shf; /* file */
} u; /* source */
struct tbl *var; /* variable in ${var..} */
- short split; /* split "$@" / call waitlast $() */
+ bool split; /* split "$@" / call waitlast $() */
} 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 void funsub(struct op *);
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 st_head, *st;
/* For 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;
if (cp == NULL)
type = XBASE;
sp = cp;
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:
tilde_ok = 0;
if (f & DONTRUNCOMMAND) {
word = IFS_WORD;
- *dp++ = '$'; *dp++ = '(';
+ *dp++ = '$';
+ if (c == FUNSUB) {
+ *dp++ = '{';
+ *dp++ = ' ';
+ } else
+ *dp++ = '(';
while (*sp != '\0') {
Xcheck(ds, dp);
*dp++ = *sp++;
}
- *dp++ = ')';
+ if (c == FUNSUB) {
+ *dp++ = ';';
+ *dp++ = '}';
+ } else
+ *dp++ = ')';
} else {
- type = comsub(&x, sp);
+ type = comsub(&x, sp, c);
if (type == XCOM && (f&DOBLANK))
doblank++;
sp = strnul(sp) + 1;
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 | '#':
+ {
+ 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') {
/*
p + Xlength(ds, (dp - 1)),
fdo | (f & DOMARKDIRS));
else if (fdo & DOGLOB)
- glob(p, wp, f & DOMARKDIRS);
+ glob(p, wp, tobool(f & DOMARKDIRS));
else if ((f & DOPAT) || !(fdo & DOMAGIC))
XPput(*wp, p);
else
XPput(*wp, debunk(p, p, strlen(p) + 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) {
+ char *p;
+
+ *(p = alloc(1, ATEMP)) = '\0';
+ XPput(*wp, p);
+ type = XSUBMID;
+ }
if (word != IFS_NWS)
word = ctype(c, C_IFSWS) ? IFS_WS : IFS_NWS;
} else {
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;
quote &= ~2;
if (make_magic) {
- make_magic = 0;
+ make_magic = false;
fdo |= DOMAGIC | (f & DOGLOB);
*dp++ = MAGIC;
} else if (ISMAGIC(c)) {
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? */
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_MAPHI|SHF_CLEXEC);
if (shf == NULL)
errorf("%s: %s %s", name, "can't open", "$() input");
- /* no waitlast() */
- xp->split = 0;
+ } else if (fn == FUNSUB) {
+ int ofd1;
+ struct temp *tf = NULL;
+
+ /* create a temporary file, open for writing */
+ maketemp(ATEMP, TT_FUNSUB, &tf);
+ if (!tf->shf) {
+ errorf("can't %s temporary file %s: %s",
+ "create", tf->tffn, cstrerror(errno));
+ }
+ /* save stdout and make the temporary file it */
+ ofd1 = savefd(1);
+ ksh_dup2(shf_fileno(tf->shf), 1, false);
+ /*
+ * run tree, with output thrown into the tempfile,
+ * in a new function block
+ */
+ funsub(t);
+ subst_exstat = exstat & 0xFF;
+ /* close the tempfile and restore regular stdout */
+ shf_close(tf->shf);
+ restfd(1, ofd1);
+ /* now open, unlink and free the tempfile for reading */
+ shf = shf_open(tf->tffn, O_RDONLY, 0, SHF_MAPHI | SHF_CLEXEC);
+ unlink(tf->tffn);
+ afree(tf, ATEMP);
} 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;
struct stat lstatb, statb;
int stat_done = 0; /* -1: failed, 1 ok */
- 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 void
+funsub(struct op *t)
+{
+ newblock();
+ e->type = E_FUNC;
+ if (!kshsetjmp(e->jbuf))
+ execute(t, XXCOM | XERROK, NULL);
+ popblock();
+}