#include "shell_common.h"
#if ENABLE_FEATURE_SH_MATH
# include "math.h"
+#else
+typedef long arith_t;
+# define ARITH_FMT "%ld"
#endif
#if ENABLE_ASH_RANDOM_SUPPORT
# include "random.h"
va_list ap;
int ret;
- va_start(ap, fmt);
INT_OFF;
+ va_start(ap, fmt);
ret = vsnprintf(outbuf, length, fmt, ap);
va_end(ap);
INT_ON;
static struct parsefile *g_parsefile = &basepf; /* current input file */
static int startlinno; /* line # where last token started */
static char *commandname; /* currently executing command */
-static struct strlist *cmdenviron; /* environment for builtin command */
/* ============ Message printing */
stack_nputstr(const char *s, size_t n, char *p)
{
p = makestrspace(n, p);
- p = (char *)memcpy(p, s, n) + n;
+ p = (char *)mempcpy(p, s, n);
return p;
}
q = p = makestrspace(len + 3, p);
*q++ = '\'';
- q = (char *)memcpy(q, s, len) + len;
+ q = (char *)mempcpy(q, s, len);
*q++ = '\'';
s += len;
q = p = makestrspace(len + 3, p);
*q++ = '"';
- q = (char *)memcpy(q, s - len, len) + len;
+ q = (char *)mempcpy(q, s - len, len);
*q++ = '"';
STADJUST(q - p, p);
/*
* Produce a possibly single quoted string suitable as input to the shell.
- * If 'conditional' is nonzero, quoting is only done if the string contains
- * non-shellsafe characters, or is identical to a shell keyword (reserved
- * word); if it is zero, quoting is always done.
* If quoting was done, the return string is allocated on the stack,
* otherwise a pointer to the original string is returned.
*/
/*
* Search the environment of a builtin command.
*/
-static const char *
+static ALWAYS_INLINE const char *
bltinlookup(const char *name)
{
- struct strlist *sp;
-
- for (sp = cmdenviron; sp; sp = sp->next) {
- if (varcmp(sp->text, name) == 0)
- return var_end(sp->text);
- }
return lookupvar(name);
}
* will go away.
* Called with interrupts off.
*/
-static void
+static struct var *
setvareq(char *s, int flags)
{
struct var *vp, **vpp;
vpp = hashvar(s);
flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
- vp = *findvar(vpp, s);
+ vpp = findvar(vpp, s);
+ vp = *vpp;
if (vp) {
if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
const char *n;
}
if (flags & VNOSET)
- return;
+ goto out;
if (vp->var_func && !(flags & VNOFUNC))
vp->var_func(var_end(s));
if (!(vp->flags & (VTEXTFIXED|VSTACK)))
free((char*)vp->var_text);
+ if (((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) | (vp->flags & VSTRFIXED)) == VUNSET) {
+ *vpp = vp->next;
+ free(vp);
+ out_free:
+ if ((flags & (VTEXTFIXED|VSTACK|VNOSAVE)) == VNOSAVE)
+ free(s);
+ goto out;
+ }
+
flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
} else {
/* variable s is not found */
if (flags & VNOSET)
- return;
+ goto out;
+ if ((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) == VUNSET)
+ goto out_free;
vp = ckzalloc(sizeof(*vp));
vp->next = *vpp;
/*vp->func = NULL; - ckzalloc did it */
s = ckstrdup(s);
vp->var_text = s;
vp->flags = flags;
+
+ out:
+ return vp;
}
/*
* Set the value of a variable. The flags argument is ored with the
* flags of the variable. If val is NULL, the variable is unset.
*/
-static void
+static struct var *
setvar(const char *name, const char *val, int flags)
{
const char *q;
char *nameeq;
size_t namelen;
size_t vallen;
+ struct var *vp;
q = endofname(name);
p = strchrnul(q, '=');
INT_OFF;
nameeq = ckmalloc(namelen + vallen + 2);
- p = memcpy(nameeq, name, namelen) + namelen;
+ p = mempcpy(nameeq, name, namelen);
if (val) {
*p++ = '=';
- p = memcpy(p, val, vallen) + vallen;
+ p = mempcpy(p, val, vallen);
}
*p = '\0';
- setvareq(nameeq, flags | VNOSAVE);
+ vp = setvareq(nameeq, flags | VNOSAVE);
INT_ON;
+
+ return vp;
}
static void FAST_FUNC
/*
* Unset the specified variable.
*/
-static int
+static void
unsetvar(const char *s)
{
- struct var **vpp;
- struct var *vp;
- int retval;
-
- vpp = findvar(hashvar(s), s);
- vp = *vpp;
- retval = 2;
- if (vp) {
- int flags = vp->flags;
-
- retval = 1;
- if (flags & VREADONLY)
- goto out;
-#if ENABLE_ASH_RANDOM_SUPPORT
- vp->flags &= ~VDYNAMIC;
-#endif
- if (flags & VUNSET)
- goto ok;
- if ((flags & VSTRFIXED) == 0) {
- INT_OFF;
- if ((flags & (VTEXTFIXED|VSTACK)) == 0)
- free((char*)vp->var_text);
- *vpp = vp->next;
- free(vp);
- INT_ON;
- } else {
- setvar0(s, NULL);
- vp->flags &= ~VEXPORT;
- }
- ok:
- retval = 0;
- }
- out:
- return retval;
+ setvar(s, NULL, 0);
}
/*
growstackblock();
q = stackblock();
if (p != start) {
- memcpy(q, start, p - start);
- q += p - start;
+ q = mempcpy(q, start, p - start);
*q++ = '/';
}
strcpy(q, name);
#define EMPTY -2 /* marks an unused slot in redirtab */
#define CLOSED -3 /* marks a slot of previously-closed fd */
-/*
- * Open a file in noclobber mode.
- * The code was copied from bash.
- */
-static int
-noclobberopen(const char *fname)
-{
- int r, fd;
- struct stat finfo, finfo2;
-
- /*
- * If the file exists and is a regular file, return an error
- * immediately.
- */
- r = stat(fname, &finfo);
- if (r == 0 && S_ISREG(finfo.st_mode)) {
- errno = EEXIST;
- return -1;
- }
-
- /*
- * If the file was not present (r != 0), make sure we open it
- * exclusively so that if it is created before we open it, our open
- * will fail. Make sure that we do not truncate an existing file.
- * Note that we don't turn on O_EXCL unless the stat failed -- if the
- * file was not a regular file, we leave O_EXCL off.
- */
- if (r != 0)
- return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
- fd = open(fname, O_WRONLY|O_CREAT, 0666);
-
- /* If the open failed, return the file descriptor right away. */
- if (fd < 0)
- return fd;
-
- /*
- * OK, the open succeeded, but the file may have been changed from a
- * non-regular file to a regular file between the stat and the open.
- * We are assuming that the O_EXCL open handles the case where FILENAME
- * did not exist and is symlinked to an existing file between the stat
- * and open.
- */
-
- /*
- * If we can open it and fstat the file descriptor, and neither check
- * revealed that it was a regular file, and the file has not been
- * replaced, return the file descriptor.
- */
- if (fstat(fd, &finfo2) == 0
- && !S_ISREG(finfo2.st_mode)
- && finfo.st_dev == finfo2.st_dev
- && finfo.st_ino == finfo2.st_ino
- ) {
- return fd;
- }
-
- /* The file has been replaced. badness. */
- close(fd);
- errno = EEXIST;
- return -1;
-}
-
/*
* Handle here documents. Normally we fork off a process to write the
* data to a pipe. If the document is short, we can stuff the data in
static int
openredirect(union node *redir)
{
+ struct stat sb;
char *fname;
int f;
#endif
/* Take care of noclobber mode. */
if (Cflag) {
- f = noclobberopen(fname);
- if (f < 0)
+ if (stat(fname, &sb) < 0) {
+ f = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
+ if (f < 0)
+ goto ecreate;
+ } else if (!S_ISREG(sb.st_mode)) {
+ f = open(fname, O_WRONLY, 0666);
+ if (f < 0)
+ goto ecreate;
+ if (fstat(f, &sb) < 0 && S_ISREG(sb.st_mode)) {
+ close(f);
+ errno = EEXIST;
+ goto ecreate;
+ }
+ } else {
+ errno = EEXIST;
goto ecreate;
+ }
break;
}
/* FALLTHROUGH */
#define RMESCAPE_SLASH 0x20 /* Stop globbing after slash */
/* Add CTLESC when necessary. */
-#define QUOTES_ESC (EXP_FULL | EXP_CASE | EXP_QPAT | EXP_REDIR)
+#define QUOTES_ESC (EXP_FULL | EXP_CASE | EXP_QPAT)
/* Do not skip NUL characters. */
#define QUOTES_KEEPNUL EXP_TILDE
/*
* Our own itoa().
+ * cvtnum() is used even if math support is off (to prepare $? values and such).
*/
-#if !ENABLE_FEATURE_SH_MATH
-/* cvtnum() is used even if math support is off (to prepare $? values and such) */
-typedef long arith_t;
-# define ARITH_FMT "%ld"
-#endif
static int
cvtnum(arith_t num)
{
int len;
- expdest = makestrspace(sizeof(arith_t)*3 + 2, expdest);
- len = fmtstr(expdest, sizeof(arith_t)*3 + 2, ARITH_FMT, num);
+ /* 32-bit and wider ints require buffer size of bytes*3 (or less) */
+ len = sizeof(arith_t) * 3;
+ /* If narrower: worst case, 1-byte ints: need 5 bytes: "-127<NUL>" */
+ if (sizeof(arith_t) < 4) len += 2;
+
+ expdest = makestrspace(len, expdest);
+ len = fmtstr(expdest, len, ARITH_FMT, num);
STADJUST(len, expdest);
return len;
}
}
q = r;
if (len > 0) {
- q = (char *)memcpy(q, str, len) + len;
+ q = (char *)mempcpy(q, str, len);
}
}
};
/* These forward decls are needed to use "eval" code for backticks handling: */
-#define EV_EXIT 01 /* exit after evaluating tree */
+/* flags in argument to evaltree */
+#define EV_EXIT 01 /* exit after evaluating tree */
+#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
static int evaltree(union node *, int);
static void FAST_FUNC
#endif
/* argstr needs it */
-static char *evalvar(char *p, int flags, struct strlist *var_str_list);
+static char *evalvar(char *p, int flags);
/*
* Perform variable and command substitution. If EXP_FULL is set, output CTLESC
* characters to allow for further processing. Otherwise treat
* $@ like $* since no splitting will be performed.
- *
- * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
- * over shell variables. Needed for "A=a B=$A; echo $B" case - we use it
- * for correct expansion of "B=$A" word.
*/
static void
-argstr(char *p, int flags, struct strlist *var_str_list)
+argstr(char *p, int flags)
{
static const char spclchars[] ALIGN1 = {
'=',
inquotes ^= EXP_QUOTED;
/* "$@" syntax adherence hack */
if (inquotes && !memcmp(p, dolatstr + 1, DOLATSTRLEN - 1)) {
- p = evalvar(p + 1, flags | inquotes, /* var_str_list: */ NULL) + 1;
+ p = evalvar(p + 1, flags | inquotes) + 1;
goto start;
}
addquote:
goto addquote;
case CTLVAR:
TRACE(("argstr: evalvar('%s')\n", p));
- p = evalvar(p, flags | inquotes, var_str_list);
+ p = evalvar(p, flags | inquotes);
TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock()));
goto start;
case CTLBACKQ:
static const char *
subevalvar(char *p, char *varname, int strloc, int subtype,
- int startloc, int varflags, int flag, struct strlist *var_str_list)
+ int startloc, int varflags, int flag)
{
struct nodelist *saveargbackq = argbackq;
int quotes = flag & QUOTES_ESC;
// p, varname, strloc, subtype, startloc, varflags, quotes);
argstr(p, EXP_TILDE | (subtype != VSASSIGN && subtype != VSQUESTION ?
- (flag & (EXP_QUOTED | EXP_QPAT) ? EXP_QPAT : EXP_CASE) : 0),
- var_str_list);
+ (flag & (EXP_QUOTED | EXP_QPAT) ? EXP_QPAT : EXP_CASE) : 0)
+ );
STPUTC('\0', expdest);
argbackq = saveargbackq;
startp = (char *)stackblock() + startloc;
* ash -c 'echo ${#1#}' name:'1=#'
*/
static NOINLINE ssize_t
-varvalue(char *name, int varflags, int flags, struct strlist *var_str_list, int *quotedp)
+varvalue(char *name, int varflags, int flags, int *quotedp)
{
const char *p;
int num;
goto value;
default:
/* NB: name has form "VAR=..." */
-
- /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
- * which should be considered before we check variables. */
- if (var_str_list) {
- unsigned name_len = (strchrnul(name, '=') - name) + 1;
- p = NULL;
- do {
- char *str, *eq;
- str = var_str_list->text;
- eq = strchr(str, '=');
- if (!eq) /* stop at first non-assignment */
- break;
- eq++;
- if (name_len == (unsigned)(eq - str)
- && strncmp(str, name, name_len) == 0
- ) {
- p = eq;
- /* goto value; - WRONG! */
- /* think "A=1 A=2 B=$A" */
- }
- var_str_list = var_str_list->next;
- } while (var_str_list);
- if (p)
- goto value;
- }
p = lookupvar(name);
value:
if (!p)
* input string.
*/
static char *
-evalvar(char *p, int flag, struct strlist *var_str_list)
+evalvar(char *p, int flag)
{
char varflags;
char subtype;
p = strchr(p, '=') + 1; //TODO: use var_end(p)?
again:
- varlen = varvalue(var, varflags, flag, var_str_list, "ed);
+ varlen = varvalue(var, varflags, flag, "ed);
if (varflags & VSNUL)
varlen--;
if (varlen < 0) {
argstr(
p,
- flag | EXP_TILDE | EXP_WORD,
- var_str_list
+ flag | EXP_TILDE | EXP_WORD
);
goto end;
}
goto record;
subevalvar(p, var, 0, subtype, startloc, varflags,
- flag & ~QUOTES_ESC, var_str_list);
+ flag & ~QUOTES_ESC);
varflags &= ~VSNUL;
/*
* Remove any recorded regions beyond
STPUTC('\0', expdest);
patloc = expdest - (char *)stackblock();
if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype,
- startloc, varflags, flag, var_str_list)) {
+ startloc, varflags, flag)) {
int amount = expdest - (
(char *)stackblock() + patloc - 1
);
argbackq = arg->narg.backquote;
STARTSTACKSTR(expdest);
TRACE(("expandarg: argstr('%s',flags:%x)\n", arg->narg.text, flag));
- argstr(arg->narg.text, flag,
- /* var_str_list: */ arglist ? arglist->list : NULL);
+ argstr(arg->narg.text, flag);
p = _STPUTC('\0', expdest);
expdest = p - 1;
if (arglist == NULL) {
exparg.lastp = &exparg.list;
expandmeta(exparg.list /*, flag*/);
} else {
- if (flag & EXP_REDIR) { /*XXX - for now, just remove escapes */
- rmescapes(p, 0);
- TRACE(("expandarg: rmescapes:'%s'\n", p));
- }
sp = stzalloc(sizeof(*sp));
sp->text = p;
*exparg.lastp = sp;
setstackmark(&smark);
argbackq = pattern->narg.backquote;
STARTSTACKSTR(expdest);
- argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
- /* var_str_list: */ NULL);
+ argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
STACKSTRNUL(expdest);
ifsfree();
result = patmatch(stackblock(), val);
clearenv();
while (*envp)
putenv(*envp++);
+ popredir(/*drop:*/ 1, /*restore:*/ 0);
run_applet_no_and_exit(applet_no, cmd, argv);
}
/* re-exec ourselves with the new arguments */
static void *funcblock; /* block to allocate function from */
static char *funcstring_end; /* end of block to allocate strings from */
-/* flags in argument to evaltree */
-#define EV_EXIT 01 /* exit after evaluating tree */
-#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
-
static const uint8_t nodesize[N_NUMBER] ALIGN1 = {
[NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
[NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
#endif
}
-static struct localvar *localvars;
+struct localvar_list {
+ struct localvar_list *next;
+ struct localvar *lv;
+};
+
+static struct localvar_list *localvar_stack;
/*
* Called after a function returns.
* Interrupts must be off.
*/
static void
-poplocalvars(void)
+poplocalvars(int keep)
{
- struct localvar *lvp;
+ struct localvar_list *ll;
+ struct localvar *lvp, *next;
struct var *vp;
- while ((lvp = localvars) != NULL) {
- localvars = lvp->next;
+ INT_OFF;
+ ll = localvar_stack;
+ localvar_stack = ll->next;
+
+ next = ll->lv;
+ free(ll);
+
+ while ((lvp = next) != NULL) {
+ next = lvp->next;
vp = lvp->vp;
TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-"));
- if (vp == NULL) { /* $- saved */
+ if (keep) {
+ int bits = VSTRFIXED;
+
+ if (lvp->flags != VUNSET) {
+ if (vp->var_text == lvp->text)
+ bits |= VTEXTFIXED;
+ else if (!(lvp->flags & (VTEXTFIXED|VSTACK)))
+ free((char*)lvp->text);
+ }
+
+ vp->flags &= ~bits;
+ vp->flags |= (lvp->flags & bits);
+
+ if ((vp->flags &
+ (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) == VUNSET)
+ unsetvar(vp->var_text);
+ } else if (vp == NULL) { /* $- saved */
memcpy(optlist, lvp->text, sizeof(optlist));
free((char*)lvp->text);
optschanged();
- } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
+ } else if (lvp->flags == VUNSET) {
+ vp->flags &= ~(VSTRFIXED|VREADONLY);
unsetvar(vp->var_text);
} else {
if (vp->var_func)
}
free(lvp);
}
+ INT_ON;
+}
+
+/*
+ * Create a new localvar environment.
+ */
+static struct localvar_list *
+pushlocalvars(void)
+{
+ struct localvar_list *ll;
+
+ INT_OFF;
+ ll = ckzalloc(sizeof(*ll));
+ /*ll->lv = NULL; - zalloc did it */
+ ll->next = localvar_stack;
+ localvar_stack = ll;
+ INT_ON;
+
+ return ll->next;
+}
+
+static void
+unwindlocalvars(struct localvar_list *stop)
+{
+ while (localvar_stack != stop)
+ poplocalvars(0);
}
static int
evalfun(struct funcnode *func, int argc, char **argv, int flags)
{
volatile struct shparam saveparam;
- struct localvar *volatile savelocalvars;
struct jmploc *volatile savehandler;
struct jmploc jmploc;
int e;
saveparam = shellparam;
- savelocalvars = localvars;
savehandler = exception_handler;
e = setjmp(jmploc.loc);
if (e) {
}
INT_OFF;
exception_handler = &jmploc;
- localvars = NULL;
shellparam.malloced = 0;
func->count++;
funcnest++;
shellparam.optind = 1;
shellparam.optoff = -1;
#endif
+ pushlocalvars();
evaltree(func->n.narg.next, flags & EV_TESTED);
+ poplocalvars(0);
funcdone:
INT_OFF;
funcnest--;
freefunc(func);
- poplocalvars();
- localvars = savelocalvars;
freeparam(&shellparam);
shellparam = saveparam;
exception_handler = savehandler;
* x=0; f() { local x=1; echo $x; local x; echo $x; }; f; echo $x
* x=0; f() { local x=1; echo $x; local x=2; echo $x; }; f; echo $x
*/
- lvp = localvars;
+ lvp = localvar_stack->lv;
while (lvp) {
if (lvp->vp && varcmp(lvp->vp->var_text, name) == 0) {
if (eq)
if (vp == NULL) {
/* variable did not exist yet */
if (eq)
- setvareq(name, VSTRFIXED);
+ vp = setvareq(name, VSTRFIXED);
else
- setvar(name, NULL, VSTRFIXED);
- vp = *vpp; /* the new variable */
+ vp = setvar(name, NULL, VSTRFIXED);
lvp->flags = VUNSET;
} else {
lvp->text = vp->var_text;
}
}
lvp->vp = vp;
- lvp->next = localvars;
- localvars = lvp;
+ lvp->next = localvar_stack->lv;
+ localvar_stack->lv = lvp;
ret:
INT_ON;
}
{
char *name;
- if (!funcnest)
+ if (!localvar_stack)
ash_msg_and_raise_error("not in a function");
argv = argptr;
#if ENABLE_FEATURE_SH_MATH
{ BUILTIN_NOSPEC "let" , letcmd },
#endif
- { BUILTIN_ASSIGN "local" , localcmd },
+ { BUILTIN_SPEC_REG_ASSG "local" , localcmd },
#if ENABLE_ASH_PRINTF
{ BUILTIN_REGULAR "printf" , printfcmd },
#endif
static const struct builtincmd null_bltin = {
"\0\0", bltincmd /* why three NULs? */
};
+ struct localvar_list *localvar_stop;
struct stackmark smark;
union node *argp;
struct arglist arglist;
/* First expand the arguments. */
TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
setstackmark(&smark);
+ localvar_stop = pushlocalvars();
back_exitstatus = 0;
cmdentry.cmdtype = CMDBUILTIN;
spp = varlist.lastp;
expandarg(argp, &varlist, EXP_VARTILDE);
+ mklocal((*spp)->text);
+
/*
* Modify the command lookup path, if a PATH= assignment
* is present
/* NOTREACHED */
} /* default */
case CMDBUILTIN:
- cmdenviron = varlist.list;
- if (cmdenviron) {
- struct strlist *list = cmdenviron;
- int i = VNOSET;
- if (spclbltin > 0 || argc == 0) {
- i = 0;
- if (cmd_is_exec && argc > 1)
- i = VEXPORT;
- }
- listsetvar(list, i);
+ if (spclbltin > 0 || argc == 0) {
+ poplocalvars(1);
+ if (cmd_is_exec && argc > 1)
+ listsetvar(varlist.list, VEXPORT);
}
+
/* Tight loop with builtins only:
* "while kill -0 $child; do true; done"
* will never exit even if $child died, unless we do this
goto readstatus;
case CMDFUNCTION:
- listsetvar(varlist.list, 0);
+ poplocalvars(1);
/* See above for the rationale */
dowait(DOWAIT_NONBLOCK, NULL);
if (evalfun(cmdentry.u.func, argc, argv, flags))
out:
if (cmd->ncmd.redirect)
popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
+ unwindlocalvars(localvar_stop);
if (lastarg) {
/* dsl: I think this is intended to be used to support
* '_' in 'vi' command mode during line editing...
return stackblock();
}
+static inline int
+parser_eof(void)
+{
+ return tokpushback && lasttoken == TEOF;
+}
+
/*
* Execute a command or commands contained in a string.
*/
while ((n = parsecmd(0)) != NODE_EOF) {
int i;
- i = evaltree(n, flags);
+ i = evaltree(n, flags & ~(parser_eof() ? 0 : EV_EXIT));
if (n)
status = i;
popstackmark(&smark);
char *fullname;
char **argv;
char *args_need_save;
- struct strlist *sp;
volatile struct shparam saveparam;
- for (sp = cmdenviron; sp; sp = sp->next)
- setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
+//???
+// struct strlist *sp;
+// for (sp = cmdenviron; sp; sp = sp->next)
+// setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
nextopt(nullstr); /* handle possible "--" */
argv = argptr;
return 0;
}
+ /* Why the second check?
+ * "trap NUM [sig2]..." is the same as "trap - NUM [sig2]..."
+ * In this case, NUM is signal no, not an action.
+ */
action = NULL;
- if (ap[1])
+ if (ap[1] && !is_number(ap[0]))
action = *ap++;
+
exitcode = 0;
while (*ap) {
signo = get_signum(*ap);
- if (signo < 0 || signo >= NSIG) {
+ if (signo < 0) {
/* Mimic bash message exactly */
ash_msg("%s: invalid signal specification", *ap);
exitcode = 1;
char **ap;
int i;
int flag = 0;
- int ret = 0;
while ((i = nextopt("vf")) != 0) {
flag = i;
for (ap = argptr; *ap; ap++) {
if (flag != 'f') {
- i = unsetvar(*ap);
- ret |= i;
- if (!(i & 2))
- continue;
+ unsetvar(*ap);
+ continue;
}
if (flag != 'v')
unsetfunc(*ap);
}
- return ret & 1;
+ return 0;
}
static const unsigned char timescmd_str[] ALIGN1 = {
/* from redir.c: */
while (redirlist)
popredir(/*drop:*/ 0, /*restore:*/ 0);
+
+ /* from var.c: */
+ unwindlocalvars(NULL);
}
#if PROFILE
// if (!sflag) g_parsefile->pf_fd = -1;
// ^^ not necessary since now we special-case fd 0
// in is_hidden_fd() to not be considered "hidden fd"
- evalstring(minusc, 0);
+ evalstring(minusc, sflag ? 0 : EV_EXIT);
}
if (sflag || minusc == NULL) {