summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjilles <jilles@FreeBSD.org>2011-06-12 23:06:04 +0000
committerjilles <jilles@FreeBSD.org>2011-06-12 23:06:04 +0000
commit91789615b49a87147298eee12c9b111d8c1b64c9 (patch)
treeb1cbd950b159dbb3fa6211ba5a7f7fb7709a612e
parent5f2600c6a978923a3904716ce73814baca63fd0f (diff)
downloadFreeBSD-src-91789615b49a87147298eee12c9b111d8c1b64c9.zip
FreeBSD-src-91789615b49a87147298eee12c9b111d8c1b64c9.tar.gz
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.
-rw-r--r--bin/sh/eval.c18
-rw-r--r--bin/sh/expand.c72
-rw-r--r--bin/sh/expand.h1
-rw-r--r--bin/sh/jobs.c4
-rw-r--r--bin/sh/sh.17
-rw-r--r--bin/sh/var.c10
-rw-r--r--bin/sh/var.h2
7 files changed, 25 insertions, 89 deletions
diff --git a/bin/sh/eval.c b/bin/sh/eval.c
index 92a18e4..9d67b9e 100644
--- a/bin/sh/eval.c
+++ b/bin/sh/eval.c
@@ -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)
diff --git a/bin/sh/expand.c b/bin/sh/expand.c
index beb655d..c6c54ab 100644
--- a/bin/sh/expand.c
+++ b/bin/sh/expand.c
@@ -1621,78 +1621,6 @@ cvtnum(int num, char *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).
*/
diff --git a/bin/sh/expand.h b/bin/sh/expand.h
index be08dec..a4bb198 100644
--- a/bin/sh/expand.h
+++ b/bin/sh/expand.h
@@ -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 **);
diff --git a/bin/sh/jobs.c b/bin/sh/jobs.c
index 434af22..cbc29f1 100644
--- a/bin/sh/jobs.c
+++ b/bin/sh/jobs.c
@@ -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;
}
diff --git a/bin/sh/sh.1 b/bin/sh/sh.1
index f6682e6..3b7f3d3 100644
--- a/bin/sh/sh.1
+++ b/bin/sh/sh.1
@@ -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.
diff --git a/bin/sh/var.c b/bin/sh/var.c
index f6d7fb1..e405b66 100644
--- a/bin/sh/var.c
+++ b/bin/sh/var.c
@@ -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;
diff --git a/bin/sh/var.h b/bin/sh/var.h
index ff21c7d..f10bfcc 100644
--- a/bin/sh/var.h
+++ b/bin/sh/var.h
@@ -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;
OpenPOWER on IntegriCloud