sh: Save/restore changed variables in optimized command substitution.

In optimized command substitution, save and restore any variables changed by
expansions (${var=value} and $((var=assigned))), instead of trying to
determine if an expansion may cause such changes.

If $! is referenced in optimized command substitution, do not cause jobs to
be remembered longer.

This fixes $(jobs $!) again, simplifies the man page and shortens the code.
This commit is contained in:
Jilles Tjoelker 2011-06-12 23:06:04 +00:00
parent 9eb8728aa5
commit c543e1ae9e
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=223024
7 changed files with 25 additions and 89 deletions

View File

@ -571,14 +571,8 @@ evalpipe(union node *n)
static int
is_valid_fast_cmdsubst(union node *n)
{
union node *argp;
if (n->type != NCMD)
return 0;
for (argp = n->ncmd.args ; argp ; argp = argp->narg.next)
if (expandhassideeffects(argp->narg.text))
return 0;
return 1;
return (n->type == NCMD);
}
/*
@ -596,6 +590,7 @@ evalbackcmd(union node *n, struct backcmd *result)
struct stackmark smark; /* unnecessary */
struct jmploc jmploc;
struct jmploc *savehandler;
struct localvar *savelocalvars;
setstackmark(&smark);
result->fd = -1;
@ -608,12 +603,18 @@ evalbackcmd(union node *n, struct backcmd *result)
}
if (is_valid_fast_cmdsubst(n)) {
exitstatus = oexitstatus;
savelocalvars = localvars;
localvars = NULL;
forcelocal++;
savehandler = handler;
if (setjmp(jmploc.loc)) {
if (exception == EXERROR || exception == EXEXEC)
exitstatus = 2;
else if (exception != 0) {
handler = savehandler;
forcelocal--;
poplocalvars();
localvars = savelocalvars;
longjmp(handler->loc, 1);
}
} else {
@ -621,6 +622,9 @@ evalbackcmd(union node *n, struct backcmd *result)
evalcommand(n, EV_BACKCMD, result);
}
handler = savehandler;
forcelocal--;
poplocalvars();
localvars = savelocalvars;
} else {
exitstatus = 0;
if (pipe(pip) < 0)

View File

@ -1620,78 +1620,6 @@ cvtnum(int num, char *buf)
return buf;
}
/*
* Check statically if expanding a string may have side effects.
*/
int
expandhassideeffects(const char *p)
{
int c;
int arinest;
arinest = 0;
while ((c = *p++) != '\0') {
switch (c) {
case CTLESC:
p++;
break;
case CTLVAR:
c = *p++;
/* Expanding $! sets the job to remembered. */
if (*p == '!')
return 1;
if ((c & VSTYPE) == VSASSIGN)
return 1;
/*
* If we are in arithmetic, the parameter may contain
* '=' which may cause side effects. Exceptions are
* the length of a parameter and $$, $# and $? which
* are always numeric.
*/
if ((c & VSTYPE) == VSLENGTH) {
while (*p != '=')
p++;
p++;
break;
}
if ((*p == '$' || *p == '#' || *p == '?') &&
p[1] == '=') {
p += 2;
break;
}
if (arinest > 0)
return 1;
break;
case CTLBACKQ:
case CTLBACKQ | CTLQUOTE:
if (arinest > 0)
return 1;
break;
case CTLARI:
arinest++;
break;
case CTLENDARI:
arinest--;
break;
case '=':
if (*p == '=') {
/* Allow '==' operator. */
p++;
continue;
}
if (arinest > 0)
return 1;
break;
case '!': case '<': case '>':
/* Allow '!=', '<=', '>=' operators. */
if (*p == '=')
p++;
break;
}
}
return 0;
}
/*
* Do most of the work for wordexp(3).
*/

View File

@ -63,5 +63,4 @@ void expari(int);
int patmatch(const char *, const char *, int);
void rmescapes(char *);
int casematch(union node *, const char *);
int expandhassideeffects(const char *);
int wordexpcmd(int, char **);

View File

@ -70,6 +70,7 @@ __FBSDID("$FreeBSD$");
#include "memalloc.h"
#include "error.h"
#include "mystring.h"
#include "var.h"
static struct job *jobtab; /* array of jobs */
@ -798,6 +799,7 @@ forkshell(struct job *jp, union node *n, int mode)
handler = &main_handler;
closescript();
INTON;
forcelocal = 0;
clear_traps();
#if JOBS
jobctl = 0; /* do job control only in root shell */
@ -1121,7 +1123,7 @@ backgndpidset(void)
pid_t
backgndpidval(void)
{
if (bgjob != NULL)
if (bgjob != NULL && !forcelocal)
bgjob->remembered = 1;
return backgndpid;
}

View File

@ -32,7 +32,7 @@
.\" from: @(#)sh.1 8.6 (Berkeley) 5/4/95
.\" $FreeBSD$
.\"
.Dd June 10, 2011
.Dd June 12, 2011
.Dt SH 1
.Os
.Sh NAME
@ -1536,10 +1536,7 @@ except that the built-in commands
and
.Ic trap
return information about the main shell environment
if they are the only command in a command substitution
and the substitutions in the command cannot cause side effects
(such as from assigning values to variables or referencing
.Li $! ).
if they are the only command in a command substitution.
.Ss Arithmetic Expansion
Arithmetic expansion provides a mechanism for evaluating an arithmetic
expression and substituting its value.

View File

@ -94,6 +94,8 @@ struct var vps4;
struct var vvers;
static struct var voptind;
int forcelocal;
static const struct varinit varinit[] = {
#ifndef NO_HISTORY
{ &vhistsize, VUNSET, "HISTSIZE=",
@ -325,6 +327,8 @@ setvareq(char *s, int flags)
if (aflag)
flags |= VEXPORT;
if (forcelocal && !(flags & (VNOSET | VNOLOCAL)))
mklocal(s);
vp = find_var(s, &vpp, &nlen);
if (vp != NULL) {
if (vp->flags & VREADONLY)
@ -740,9 +744,9 @@ mklocal(char *name)
vp = find_var(name, &vpp, NULL);
if (vp == NULL) {
if (strchr(name, '='))
setvareq(savestr(name), VSTRFIXED);
setvareq(savestr(name), VSTRFIXED | VNOLOCAL);
else
setvar(name, NULL, VSTRFIXED);
setvar(name, NULL, VSTRFIXED | VNOLOCAL);
vp = *vpp; /* the new variable */
lvp->text = NULL;
lvp->flags = VUNSET;
@ -751,7 +755,7 @@ mklocal(char *name)
lvp->flags = vp->flags;
vp->flags |= VSTRFIXED|VTEXTFIXED;
if (name[vp->name_len] == '=')
setvareq(savestr(name), 0);
setvareq(savestr(name), VNOLOCAL);
}
}
lvp->vp = vp;

View File

@ -46,6 +46,7 @@
#define VUNSET 0x20 /* the variable is not set */
#define VNOFUNC 0x40 /* don't call the callback function */
#define VNOSET 0x80 /* do not set variable - just readonly test */
#define VNOLOCAL 0x100 /* ignore forcelocal */
struct var {
@ -68,6 +69,7 @@ struct localvar {
struct localvar *localvars;
extern int forcelocal;
extern struct var vifs;
extern struct var vmail;