From 3b5d77d819e1b7a94c4b14d69bddb2dddf8605ff Mon Sep 17 00:00:00 2001
From: Peter Stephenson
Date: Sun, 12 Oct 2014 17:52:11 +0100
Subject: 33423: expand ${(p)...} to allow ${(ps.$param.)...}
---
Src/subst.c | 21 ++++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)
(limited to 'Src/subst.c')
diff --git a/Src/subst.c b/Src/subst.c
index 1aa9b982e..61aa1c136 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -1385,12 +1385,23 @@ static char *
untok_and_escape(char *s, int escapes, int tok_arg)
{
int klen;
- char *dst;
+ char *dst = NULL;
- untokenize(dst = dupstring(s));
- if (escapes) {
- dst = getkeystring(dst, &klen, GETKEYS_SEP, NULL);
- dst = metafy(dst, klen, META_HREALLOC);
+ if (escapes && (*s == String || *s == Qstring) && s[1]) {
+ char *pstart = s+1, *pend;
+ for (pend = pstart; *pend; pend++)
+ if (!iident(*pend))
+ break;
+ if (!*pend) {
+ dst = dupstring(getsparam(pstart));
+ }
+ }
+ if (dst == NULL) {
+ untokenize(dst = dupstring(s));
+ if (escapes) {
+ dst = getkeystring(dst, &klen, GETKEYS_SEP, NULL);
+ dst = metafy(dst, klen, META_HREALLOC);
+ }
}
if (tok_arg)
shtokenize(dst);
--
cgit v1.2.3
From d067ebcacd55472f720b2765ec686a69b25c9a90 Mon Sep 17 00:00:00 2001
From: Peter Stephenson
Date: Sun, 7 Dec 2014 16:24:19 +0000
Subject: 33876: etc.: Separate errors and keyboards interrupts
Combination of 12 commits from interrupt_abort branch.
Basic strategy is to introduce bits to errflag and to set and
reset them separately.
Remove interrupt status on return to main keymap.
Turn off ERRFLAG_INT for always block.
Restore bit thereafter: we probably need a new variable in order
to allow user interrupts to be reset in the always block.
Add TRY_BLOCK_INTERRUPT
This works the same as TRY_BLOCK_ERROR, but for a SIGINT, too.
Ensure propagation of SIGINT from exited job.
If received by foreground job, shell uses ERRFLAG_INT, not
ERRFLAG_ERROR, to set the new state.
Reset errflag before precmd()
Add always block in _main_completion to fix ZLS_COLORS
Ensures we get the right state of $ZLS_COLORS at the end of _main_complete
even if there's an interrupt. However, the "right state" is a bit messy
as it depends on styles.
---
ChangeLog | 20 +++++++++++--
Completion/Base/Core/_main_complete | 21 ++++++++------
Doc/Zsh/params.yo | 11 ++++++++
Src/Modules/zpty.c | 2 +-
Src/Modules/zutil.c | 8 ++++--
Src/Zle/compcore.c | 2 +-
Src/Zle/compctl.c | 10 +++----
Src/Zle/compresult.c | 2 +-
Src/Zle/textobjects.c | 2 +-
Src/Zle/zle_hist.c | 6 ++--
Src/Zle/zle_keymap.c | 10 +++++++
Src/Zle/zle_main.c | 14 ++++++----
Src/Zle/zle_misc.c | 2 +-
Src/Zle/zle_tricky.c | 20 +++++++++++--
Src/Zle/zle_utils.c | 3 +-
Src/builtin.c | 22 +++++++--------
Src/exec.c | 56 +++++++++++++++++++++++--------------
Src/glob.c | 12 ++++----
Src/hist.c | 10 ++++---
Src/init.c | 29 ++++++++++++++++---
Src/input.c | 3 +-
Src/jobs.c | 23 +++++++++------
Src/lex.c | 5 ++--
Src/loop.c | 28 ++++++++++++++++---
Src/params.c | 13 +++++----
Src/parse.c | 31 ++++++++++----------
Src/prompt.c | 7 +++--
Src/signals.c | 28 ++++++++++++++++---
Src/subst.c | 27 ++++++++++++------
Src/utils.c | 8 +++---
Src/zsh.h | 14 ++++++++++
31 files changed, 315 insertions(+), 134 deletions(-)
(limited to 'Src/subst.c')
diff --git a/ChangeLog b/ChangeLog
index 94e28ccb2..e30324894 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2014-12-11 Peter Stephenson
+
+ * 33876: etc.: Completion/Base/Core/_main_complete,
+ Doc/Zsh/params.yo, Src/Modules/zpty.c, Src/Modules/zutil.c,
+ Src/Zle/compcore.c, Src/Zle/compctl.c, Src/Zle/compresult.c,
+ Src/Zle/textobjects.c, Src/Zle/zle_hist.c, Src/Zle/zle_keymap.c,
+ Src/Zle/zle_main.c, Src/Zle/zle_misc.c, Src/Zle/zle_tricky.c,
+ Src/Zle/zle_utils.c, Src/builtin.c, Src/exec.c, Src/glob.c,
+ Src/hist.c, Src/init.c, Src/input.c, Src/jobs.c, Src/lex.c,
+ Src/loop.c, Src/params.c, Src/parse.c, Src/prompt.c,
+ Src/signals.c, Src/subst.c, Src/utils.c, Src/zsh.h: Separate
+ shell errors and user interrupt flags into different bits of
+ errflag: ERRFLAG_ERROR and ERRFLAG_INT. Various
+ rationalisations to make keyboard interrupts work smoothly.
+ Work done on interrupt_abort branch.
+
2014-12-10 Mikael Magnusson
* 33948: Completion/Unix/Command/_getent,
@@ -612,7 +628,7 @@
* 33354: Src/jobs.c, Test/A05execution.ztst: when backgrounding
a pipeline, close all pipe descriptors in the parent; add test
for both this and 33345+33346
-
+
2014-10-03 Bart Schaefer
* 33346: Src/parse.c: another bit of the 33345 repair
@@ -2083,7 +2099,7 @@
2013-12-20 Barton E. Schaefer
* 32172; Test/A05execution.ztst: regression test for 32171
-
+
* 32171: Src/exec.c: fix leaked pipe descriptor that could
deadlock a pipeline from a complex shell construct or function
into an external command
diff --git a/Completion/Base/Core/_main_complete b/Completion/Base/Core/_main_complete
index bc63e83fb..d6a100777 100644
--- a/Completion/Base/Core/_main_complete
+++ b/Completion/Base/Core/_main_complete
@@ -43,6 +43,8 @@ local -a precommands
typeset -U _lastdescr _comp_ignore _comp_colors
+{
+
[[ -z "$curcontext" ]] && curcontext=:::
zstyle -s ":completion:${curcontext}:" insert-tab tmp || tmp=yes
@@ -349,17 +351,20 @@ fi
( "$_comp_force_list" = ?* && nm -ge _comp_force_list ) ]] &&
compstate[list]="${compstate[list]//messages} force"
-if [[ "$compstate[old_list]" = keep ]]; then
- if [[ $_saved_colors_set = 1 ]]; then
- ZLS_COLORS="$_saved_colors"
+} always {
+ # Stuff we always do to clean up.
+ if [[ "$compstate[old_list]" = keep ]]; then
+ if [[ $_saved_colors_set = 1 ]]; then
+ ZLS_COLORS="$_saved_colors"
+ else
+ unset ZLS_COLORS
+ fi
+ elif (( $#_comp_colors )); then
+ ZLS_COLORS="${(j.:.)_comp_colors}"
else
unset ZLS_COLORS
fi
-elif (( $#_comp_colors )); then
- ZLS_COLORS="${(j.:.)_comp_colors}"
-else
- unset ZLS_COLORS
-fi
+}
# Now call the post-functions.
diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index 5833d6be9..391a5fb0a 100644
--- a/Doc/Zsh/params.yo
+++ b/Doc/Zsh/params.yo
@@ -754,6 +754,17 @@ It may be reset, clearing the error condition. See
ifzman(em(Complex Commands) in zmanref(zshmisc))\
ifnzman(noderef(Complex Commands))
)
+vindex(TRY_BLOCK_INTERRUPT)
+item(tt(TRY_BLOCK_INTERRUPT) )(
+This variable works in a similar way to tt(TRY_BLOCK_ERROR), but
+represents the status of an interrupt from the signal SIGINT, which
+typically comes from the keyboard when the user types tt(^C). If set to
+0, any such interrupt will be reset; otherwise, the interrupt is
+propagated after the tt(always) block.
+
+Note that it is possible that an interrupt arrives during the execution
+of the tt(always) block; this interrupt is also propagated.
+)
vindex(TTY)
item(tt(TTY))(
The name of the tty associated with the shell, if any.
diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c
index 63c79a731..7b6130c6f 100644
--- a/Src/Modules/zpty.c
+++ b/Src/Modules/zpty.c
@@ -308,7 +308,7 @@ newptycmd(char *nam, char *pname, char **args, int echo, int nblock)
prog = parse_string(zjoin(args, ' ', 1), 0);
if (!prog) {
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
scriptname = oscriptname;
ineval = oineval;
return 1;
diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c
index 1cca0c4b8..c89495070 100644
--- a/Src/Modules/zutil.c
+++ b/Src/Modules/zutil.c
@@ -301,7 +301,8 @@ setstypat(Style s, char *pat, Patprog prog, char **vals, int eval)
int ef = errflag;
eprog = parse_string(zjoin(vals, ' ', 1), 0);
- errflag = ef;
+ /* Keep any user interrupt error status */
+ errflag = ef | (errflag & ERRFLAG_INT);
if (!eprog)
{
@@ -394,10 +395,11 @@ evalstyle(Stypat p)
unsetparam("reply");
execode(p->eval, 1, 0, "style");
if (errflag) {
- errflag = ef;
+ /* Keep any user interrupt error status */
+ errflag = ef | (errflag & ERRFLAG_INT);
return NULL;
}
- errflag = ef;
+ errflag = ef | (errflag & ERRFLAG_INT);
queue_signals();
if ((ret = getaparam("reply")))
diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c
index 35d410cc6..b0c6e06f8 100644
--- a/Src/Zle/compcore.c
+++ b/Src/Zle/compcore.c
@@ -1671,7 +1671,7 @@ set_comp_sep(void)
noaliases = ona;
strinend();
inpop();
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
noerrs = ne;
lexrestore();
wb = owb;
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index 0b7a32445..d15c2d1b0 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -1879,7 +1879,7 @@ ccmakehookfn(UNUSED(Hookdef dummy), struct ccmakedat *dat)
if (!m || !(m = m->next))
break;
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
}
redup(osi, 0);
dat->lst = 1;
@@ -2121,7 +2121,7 @@ getreal(char *str)
if (!errflag && nonempty(l) &&
((char *) peekfirst(l)) && ((char *) peekfirst(l))[0])
return dupstring(peekfirst(l));
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
return dupstring(str);
}
@@ -2599,7 +2599,7 @@ makecomplistlist(Compctl cc, char *s, int incmd, int compadd)
makecomplistflags(cc, s, incmd, compadd);
/* Reset some information variables for the next try. */
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
offs = oloffs;
wb = owb;
we = owe;
@@ -2847,7 +2847,7 @@ sep_comp_string(char *ss, char *s, int noffs)
noaliases = ona;
strinend();
inpop();
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
noerrs = ne;
lexrestore();
wb = owb;
@@ -3725,7 +3725,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
noaliases = ona;
strinend();
inpop();
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
lexrestore();
/* Fine, now do full expansion. */
prefork(foo, 0);
diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c
index fcceb670c..93438a053 100644
--- a/Src/Zle/compresult.c
+++ b/Src/Zle/compresult.c
@@ -1092,7 +1092,7 @@ do_single(Cmatch m)
noerrs = 1;
parsestr(p);
singsub(&p);
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
noerrs = ne;
}
} else {
diff --git a/Src/Zle/textobjects.c b/Src/Zle/textobjects.c
index 85d014b27..37d2c0ad9 100644
--- a/Src/Zle/textobjects.c
+++ b/Src/Zle/textobjects.c
@@ -275,7 +275,7 @@ selectargument(UNUSED(char **args))
noaliases = ona;
strinend();
inpop();
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
noerrs = ne;
lexrestore();
zlemetacs = ocs;
diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c
index 9f65994dc..88623bb3c 100644
--- a/Src/Zle/zle_hist.c
+++ b/Src/Zle/zle_hist.c
@@ -853,8 +853,10 @@ pushlineoredit(char **args)
free(zhline);
}
ret = pushline(args);
- if (!isfirstln)
- errflag = done = 1;
+ if (!isfirstln) {
+ errflag |= ERRFLAG_ERROR;
+ done = 1;
+ }
clearlist = 1;
return ret;
}
diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
index 30d25ebaa..48f210c7e 100644
--- a/Src/Zle/zle_keymap.c
+++ b/Src/Zle/zle_keymap.c
@@ -504,6 +504,16 @@ mod_export void
selectlocalmap(Keymap m)
{
localkeymap = m;
+ if (!m)
+ {
+ /*
+ * No local keymap; so we are returning to the global map. If
+ * the user ^Ced in the local map, they probably just want to go
+ * back to normal editing. So remove the interrupt error
+ * status.
+ */
+ errflag &= ~ERRFLAG_INT;
+ }
}
/* Reopen the currently selected keymap, in case it got deleted. This *
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index caa052b13..a2f48e13a 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -744,7 +744,7 @@ raw_getbyte(long do_keytmout, char *cptr)
}
if (errflag) {
/* No sensible way of handling errors here */
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
/*
* Paranoia: don't run the hooks again this
* time.
@@ -882,7 +882,7 @@ getbyte(long do_keytmout, int *timeout)
die = 0;
if (!errflag && !retflag && !breaks && !exit_pending)
continue;
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
breaks = obreaks;
errno = old_errno;
return lastchar = EOF;
@@ -1075,7 +1075,7 @@ zlecore(void)
DECCS();
handleundo();
} else {
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
break;
}
#ifdef HAVE_POLL
@@ -1233,6 +1233,10 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
zleactive = 1;
resetneeded = 1;
+ /*
+ * Start of the main zle read.
+ * Fully reset error conditions, including user interrupt.
+ */
errflag = retflag = 0;
lastcol = -1;
initmodifier(&zmod);
@@ -1658,7 +1662,7 @@ bin_vared(char *name, char **args, Options ops, UNUSED(int func))
}
if (!t || errflag) {
/* error in editing */
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
breaks = obreaks;
if (t)
zsfree(t);
@@ -1778,7 +1782,7 @@ recursiveedit(UNUSED(char **args))
zrefresh();
zlecore();
- locerror = errflag;
+ locerror = errflag ? 1 : 0;
errflag = done = eofsent = 0;
return locerror;
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index d432acf7b..23286fc20 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -1041,7 +1041,7 @@ copyprevshellword(UNUSED(char **args))
int
sendbreak(UNUSED(char **args))
{
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
return 1;
}
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index b15d91c8e..864f804b7 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -829,7 +829,7 @@ docomplete(int lst)
if (olst == COMP_EXPAND_COMPLETE &&
!strcmp(ol, zlemetaline)) {
zlemetacs = ocs;
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
if (!compfunc) {
char *p;
@@ -877,6 +877,19 @@ docomplete(int lst)
active = 0;
makecommaspecial(0);
+
+ /*
+ * As a special case, we reset user interrupts here.
+ * That's because completion is an intensive piece of
+ * computation that the user might want to interrupt separately
+ * from anything else going on. If they do, they probably
+ * want to keep the line edit buffer intact.
+ *
+ * There's a race here that the user might hit ^C just
+ * after completion exited anyway, but that's inevitable.
+ */
+ errflag &= ~ERRFLAG_INT;
+
return dat[1];
}
@@ -1394,7 +1407,8 @@ get_comp_string(void)
}
strinend();
inpop();
- errflag = lexflags = 0;
+ lexflags = 0;
+ errflag &= ~ERRFLAG_ERROR;
if (parbegin != -1) {
/* We are in command or process substitution if we are not in
* a $((...)). */
@@ -2917,7 +2931,7 @@ getcurcmd(void)
popheap();
strinend();
inpop();
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
unmetafy_line();
lexrestore();
diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c
index 08a32c30e..de91182b5 100644
--- a/Src/Zle/zle_utils.c
+++ b/Src/Zle/zle_utils.c
@@ -1715,7 +1715,8 @@ zlecallhook(char *name, char *arg)
execzlefunc(thingy, args, 1);
unrefthingy(thingy);
- errflag = saverrflag;
+ /* Retain any user interrupt error status */
+ errflag = saverrflag | (errflag & ERRFLAG_INT);
retflag = savretflag;
}
diff --git a/Src/builtin.c b/Src/builtin.c
index c2af51f2e..ad3a1925f 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -422,7 +422,7 @@ execbuiltin(LinkList args, Builtin bn)
argc -= argv - argarr;
if (errflag) {
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
return 1;
}
@@ -3136,7 +3136,7 @@ bin_unset(char *name, char **argv, Options ops, int func)
}
}
returnval = errflag;
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
} else {
zerrnam(name, "%s: invalid element for unset", s);
returnval = 1;
@@ -4242,7 +4242,7 @@ bin_print(char *name, char **args, Options ops, int func)
if (*argp) {
width = (int)mathevali(*argp++);
if (errflag) {
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
ret = 1;
}
}
@@ -4272,7 +4272,7 @@ bin_print(char *name, char **args, Options ops, int func)
if (*argp) {
prec = (int)mathevali(*argp++);
if (errflag) {
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
ret = 1;
}
}
@@ -4452,7 +4452,7 @@ bin_print(char *name, char **args, Options ops, int func)
zlongval = (curarg) ? mathevali(curarg) : 0;
if (errflag) {
zlongval = 0;
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
ret = 1;
}
print_val(zlongval)
@@ -4481,7 +4481,7 @@ bin_print(char *name, char **args, Options ops, int func)
} else doubleval = 0;
if (errflag) {
doubleval = 0;
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
ret = 1;
}
print_val(doubleval)
@@ -4494,7 +4494,7 @@ bin_print(char *name, char **args, Options ops, int func)
zulongval = (curarg) ? mathevali(curarg) : 0;
if (errflag) {
zulongval = 0;
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
ret = 1;
}
print_val(zulongval)
@@ -4871,7 +4871,7 @@ zexit(int val, int from_where)
in_exit = -1;
/*
* We want to do all remaining processing regardless of preceding
- * errors.
+ * errors, even user interrupts.
*/
errflag = 0;
@@ -5074,7 +5074,7 @@ eval(char **argv)
if (fpushed)
funcstack = funcstack->prev;
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
scriptname = oscriptname;
ineval = oineval;
@@ -6101,7 +6101,7 @@ bin_test(char *name, char **argv, UNUSED(Options ops), int func)
condlex = zshlex;
if (errflag) {
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
lexrestore();
return 1;
}
@@ -6278,7 +6278,7 @@ bin_let(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int func))
while (*argv)
val = matheval(*argv++);
/* Errors in math evaluation in let are non-fatal. */
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
/* should test for fabs(val.u.d) < epsilon? */
return (val.type == MN_INTEGER) ? val.u.l == 0 : val.u.d == 0.0;
}
diff --git a/Src/exec.c b/Src/exec.c
index a5f877191..6a7dbb1e1 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -59,7 +59,7 @@ mod_export int noerrs;
/**/
int nohistsave;
-/* error/break flag */
+/* error flag: bits from enum errflag_bits */
/**/
mod_export int errflag;
@@ -1601,7 +1601,8 @@ execpline(Estate state, wordcode slcode, int how, int last1)
(killpg(jobtab[list_pipe_job].gleader, 0) == -1 ? 2 : 1);
list_pipe_pid = pid;
list_pipe_start = bgtime;
- nowait = errflag = 1;
+ nowait = 1;
+ errflag |= ERRFLAG_ERROR;
breaks = loops;
close(synch[1]);
read_loop(synch[0], &dummy, 1);
@@ -1634,7 +1635,10 @@ execpline(Estate state, wordcode slcode, int how, int last1)
list_pipe_child = 1;
opts[INTERACTIVE] = 0;
if (errbrk_saved) {
- errflag = prev_errflag;
+ /*
+ * Keep any user interrupt bit in errflag.
+ */
+ errflag = prev_errflag | (errflag & ERRFLAG_INT);
breaks = prev_breaks;
}
break;
@@ -1719,12 +1723,14 @@ execpline2(Estate state, wordcode pcode,
if (pipe(synch) < 0) {
zerr("pipe failed: %e", errno);
- lastval = errflag = 1;
+ lastval = 1;
+ errflag |= ERRFLAG_ERROR;
return;
} else if ((pid = zfork(&bgtime)) == -1) {
close(synch[0]);
close(synch[1]);
- lastval = errflag = 1;
+ lastval = 1;
+ errflag |= ERRFLAG_ERROR;
return;
} else if (pid) {
char dummy, *text;
@@ -2560,7 +2566,8 @@ execcmd(Estate state, int input, int output, int how, int last1)
while (next && *next == '-' && strlen(next) >= 2) {
if (!firstnode(args)) {
zerr("exec requires a command to execute");
- errflag = lastval = 1;
+ lastval = 1;
+ errflag |= ERRFLAG_ERROR;
goto done;
}
uremnode(args, firstnode(args));
@@ -2577,12 +2584,14 @@ execcmd(Estate state, int input, int output, int how, int last1)
} else {
if (!firstnode(args)) {
zerr("exec requires a command to execute");
- errflag = lastval = 1;
+ lastval = 1;
+ errflag |= ERRFLAG_ERROR;
goto done;
}
if (!nextnode(firstnode(args))) {
zerr("exec flag -a requires a parameter");
- errflag = lastval = 1;
+ lastval = 1;
+ errflag |= ERRFLAG_ERROR;
goto done;
}
exec_argv0 = (char *)
@@ -2598,7 +2607,8 @@ execcmd(Estate state, int input, int output, int how, int last1)
break;
default:
zerr("unknown exec flag -%c", *cmdopt);
- errflag = lastval = 1;
+ lastval = 1;
+ errflag |= ERRFLAG_ERROR;
return;
}
}
@@ -2661,7 +2671,8 @@ execcmd(Estate state, int input, int output, int how, int last1)
} else if (!nullcmd || !*nullcmd || opts[CSHNULLCMD] ||
(cflags & BINF_PREFIX)) {
zerr("redirection with no command");
- errflag = lastval = 1;
+ lastval = 1;
+ errflag |= ERRFLAG_ERROR;
return;
} else if (!nullcmd || !*nullcmd || opts[SHNULLCMD]) {
if (!args)
@@ -2691,7 +2702,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
if (varspc)
addvars(state, varspc, 0);
if (errflag)
- lastval = errflag;
+ lastval = 1;
else
lastval = cmdoutval;
if (isset(XTRACE)) {
@@ -2795,7 +2806,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
}
}
if (!nextnode(firstnode(args)))
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
}
if (type == WC_FUNCDEF) {
@@ -2940,7 +2951,8 @@ execcmd(Estate state, int input, int output, int how, int last1)
} else if ((pid = zfork(&bgtime)) == -1) {
close(synch[0]);
close(synch[1]);
- lastval = errflag = 1;
+ lastval = 1;
+ errflag |= ERRFLAG_ERROR;
goto fatal;
}
if (pid) {
@@ -3529,7 +3541,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
else
exit(1);
}
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
}
}
if (newxtrerr) {
@@ -3759,8 +3771,10 @@ gethere(char **strp, int typ)
parsestr(buf);
- if (!errflag)
- errflag = ef;
+ if (!errflag) {
+ /* Retain any user interrupt error */
+ errflag = ef | (errflag & ERRFLAG_INT);
+ }
}
s = dupstring(buf);
zfree(buf, bsiz);
@@ -3854,7 +3868,7 @@ getoutput(char *cmd, int qt)
return readoutput(stream, qt);
}
if (mpipe(pipes) < 0) {
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
cmdoutpid = 0;
return NULL;
}
@@ -3864,7 +3878,7 @@ getoutput(char *cmd, int qt)
/* fork error */
zclose(pipes[0]);
zclose(pipes[1]);
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
cmdoutpid = 0;
child_unblock();
return NULL;
@@ -4274,7 +4288,7 @@ execcond(Estate state, UNUSED(int do_exec))
* into a shell error.
*/
if (stat == 2)
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
cmdpop();
if (isset(XTRACE)) {
fprintf(xtrerr, " ]]\n");
@@ -4314,7 +4328,7 @@ execarith(Estate state, UNUSED(int do_exec))
fflush(xtrerr);
}
if (errflag) {
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
return 2;
}
/* should test for fabs(val.u.d) < epsilon? */
@@ -4932,7 +4946,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
(name = fname)))) {
zwarn("%s: function not defined by file", name);
if (noreturnval)
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
else
lastval = 1;
goto doneshfunc;
diff --git a/Src/glob.c b/Src/glob.c
index b3903f2ff..82f8d626c 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -682,7 +682,7 @@ parsecomplist(char *instr)
/* Now get the next path component if there is one. */
l1 = (Complist) zhalloc(sizeof *l1);
if ((l1->next = parsecomplist(instr)) == NULL) {
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
return NULL;
}
l1->pat = patcompile(NULL, compflags | PAT_ANY, NULL);
@@ -728,7 +728,7 @@ parsecomplist(char *instr)
return (ef && !l1->next) ? NULL : l1;
}
}
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
return NULL;
}
@@ -1790,7 +1790,7 @@ zglob(LinkList list, LinkNode np, int nountok)
insertlinknode(list, node, ostr);
return;
}
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
zerr("bad pattern: %s", ostr);
return;
}
@@ -1873,7 +1873,8 @@ zglob(LinkList list, LinkNode np, int nountok)
tmpptr->sortstrs[iexec] = tmpptr->name;
}
- errflag = ef;
+ /* Retain any user interrupt error status */
+ errflag = ef | (errflag & ERRFLAG_INT);
lastval = lv;
} else {
/* Failed, let's be safe */
@@ -3733,7 +3734,8 @@ qualsheval(char *name, UNUSED(struct stat *buf), UNUSED(off_t days), char *str)
execode(prog, 1, 0, "globqual");
ret = lastval;
- errflag = ef;
+ /* Retain any user interrupt error status */
+ errflag = ef | (errflag & ERRFLAG_INT);
lastval = lv;
if (!(inserts = getaparam("reply")) &&
diff --git a/Src/hist.c b/Src/hist.c
index 7fe843a4a..a0061707c 100644
--- a/Src/hist.c
+++ b/Src/hist.c
@@ -287,7 +287,8 @@ ihgetc(void)
c = histsubchar(c);
if (c < 0) {
/* bad expansion */
- errflag = lexstop = 1;
+ lexstop = 1;
+ errflag |= ERRFLAG_ERROR;
return ' ';
}
}
@@ -721,7 +722,7 @@ histsubchar(int c)
noerrs = 1;
parse_subst_string(sline);
noerrs = one;
- errflag = oef;
+ errflag = oef | (errflag & ERRFLAG_INT);
remnulargs(sline);
untokenize(sline);
}
@@ -880,7 +881,8 @@ hbegin(int dohist)
char *hf;
isfirstln = isfirstch = 1;
- errflag = histdone = 0;
+ errflag &= ~ERRFLAG_ERROR;
+ histdone = 0;
if (!dohist)
stophist = 2;
else if (dohist != 2)
@@ -3182,7 +3184,7 @@ bufferwords(LinkList list, char *buf, int *index, int flags)
noaliases = ona;
strinend();
inpop();
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
nocomments = onc;
noerrs = ne;
lexrestore();
diff --git a/Src/init.c b/Src/init.c
index 655166135..305908724 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -118,11 +118,24 @@ loop(int toplevel, int justonce)
if (interact && toplevel) {
int hstop = stophist;
stophist = 3;
+ /*
+ * Reset all errors including the interrupt error status
+ * immediately, so preprompt runs regardless of what
+ * just happened. We'll reset again below as a
+ * precaution to ensure we get back to the command line
+ * no matter what.
+ */
+ errflag = 0;
preprompt();
if (stophist != 3)
hbegin(1);
else
stophist = hstop;
+ /*
+ * Reset all errors, including user interupts.
+ * This is what allows ^C in an interactive shell
+ * to return us to the command line.
+ */
errflag = 0;
}
}
@@ -178,7 +191,15 @@ loop(int toplevel, int justonce)
/* The only permanent storage is from getpermtext() */
zsfree(cmdstr);
- errflag = 0;
+ /*
+ * Note this does *not* remove a user interrupt error
+ * condition, even though we're at the top level loop:
+ * that would be inconsistent with the case where
+ * we didn't execute a preexec function. This is
+ * an implementation detail that an interrupting user
+ * does't care about.
+ */
+ errflag &= ~ERRFLAG_ERROR;
}
if (stopmsg) /* unset 'you have stopped jobs' flag */
stopmsg--;
@@ -689,7 +710,7 @@ init_term(void)
{
if (isset(INTERACTIVE))
zerr("can't find terminal definition for %s", term);
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
termflags |= TERM_BAD;
return 0;
} else {
@@ -1336,7 +1357,7 @@ source(char *s)
if (prog) {
pushheap();
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
execode(prog, 1, 0, "filecode");
popheap();
if (errflag)
@@ -1379,7 +1400,7 @@ source(char *s)
lineno = oldlineno; /* our current lineno */
loops = oloops; /* the # of nested loops we are in */
dosetopt(SHINSTDIN, oldshst, 1, opts); /* SHINSTDIN option */
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
if (!exit_pending)
retflag = 0;
scriptname = old_scriptname;
diff --git a/Src/input.c b/Src/input.c
index 4ac7e6ec8..9552331a9 100644
--- a/Src/input.c
+++ b/Src/input.c
@@ -291,7 +291,8 @@ inputline(void)
}
if (errflag) {
free(ingetcline);
- return lexstop = errflag = 1;
+ errflag |= ERRFLAG_ERROR;
+ return lexstop = 1;
}
if (isset(VERBOSE)) {
/* Output the whole line read so far. */
diff --git a/Src/jobs.c b/Src/jobs.c
index 6663a40a6..a668b07e6 100644
--- a/Src/jobs.c
+++ b/Src/jobs.c
@@ -509,7 +509,7 @@ update_job(Job jn)
prev_errflag = errflag;
}
breaks = loops;
- errflag = 1;
+ errflag |= ERRFLAG_INT;
inerrflush();
}
} else {
@@ -526,7 +526,7 @@ update_job(Job jn)
prev_errflag = errflag;
}
breaks = loops;
- errflag = 1;
+ errflag |= ERRFLAG_INT;
inerrflush();
}
if (somestopped && jn->stat & STAT_SUPERJOB)
@@ -581,7 +581,7 @@ update_job(Job jn)
breaks = loops;
} else {
breaks = loops;
- errflag = 1;
+ errflag |= ERRFLAG_INT;
}
check_cursh_sig(sig);
}
@@ -1444,12 +1444,19 @@ zwaitjob(int job, int wait_cmd)
restore_queue_signals(q);
return 128 + last_signal;
}
- /* Commenting this out makes ^C-ing a job started by a function
- stop the whole function again. But I guess it will stop
- something else from working properly, we have to find out
- what this might be. --oberon
+ /* Commenting this out makes ^C-ing a job started by a function
+ stop the whole function again. But I guess it will stop
+ something else from working properly, we have to find out
+ what this might be. --oberon
+
+ When attempting to separate errors and interrupts, we
+ assumed because of the previous comment it would be OK
+ to remove ERRFLAG_ERROR and leave ERRFLAG_INT set, since
+ that's the one related to ^C. But that doesn't work.
+ There's something more here we don't understand. --pws
+
+ errflag = 0; */
- errflag = 0; */
if (subsh) {
killjb(jn, SIGCONT);
jn->stat &= ~STAT_STOPPED;
diff --git a/Src/lex.c b/Src/lex.c
index b2a05448c..4addf8033 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -385,7 +385,7 @@ lexrestore(void)
ecnfunc = ln->ecnfunc;
hlinesz = ln->hlinesz;
toklineno = ln->toklineno;
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
free(ln);
unqueue_signals();
@@ -1737,7 +1737,8 @@ parse_subst_string(char *s)
inpop();
DPUTS(cmdsp, "BUG: parse_subst_string: cmdstack not empty.");
lexrestore();
- errflag = err;
+ /* Keep any interrupt error status */
+ errflag = err | (errflag & ERRFLAG_INT);
if (ctok == LEXERR) {
untokenize(s);
return 1;
diff --git a/Src/loop.c b/Src/loop.c
index 82d2fe31a..8bb1ec9dd 100644
--- a/Src/loop.c
+++ b/Src/loop.c
@@ -259,7 +259,8 @@ execselect(Estate state, UNUSED(int do_exec))
0, ZLCON_SELECT);
if (errflag)
str = NULL;
- errflag = oef;
+ /* Keep any user interrupt error status */
+ errflag = oef | (errflag & ERRFLAG_INT);
} else {
str = promptexpand(prompt3, 0, NULL, NULL, NULL);
zputs(str, stderr);
@@ -632,6 +633,14 @@ execcase(Estate state, int do_exec)
zlong
try_errflag = -1;
+/**
+ * Corresponding interrupt error status form `try' block.
+ */
+
+/**/
+zlong
+try_interrupt = -1;
+
/**/
zlong
try_tryflag = 0;
@@ -643,7 +652,7 @@ exectry(Estate state, int do_exec)
Wordcode end, always;
int endval;
int save_retflag, save_breaks, save_contflag;
- zlong save_try_errflag, save_try_tryflag;
+ zlong save_try_errflag, save_try_tryflag, save_try_interrupt;
end = state->pc + WC_TRY_SKIP(state->pc[-1]);
always = state->pc + 1 + WC_TRY_SKIP(*state->pc);
@@ -670,7 +679,10 @@ exectry(Estate state, int do_exec)
/* The always clause. */
save_try_errflag = try_errflag;
- try_errflag = (zlong)errflag;
+ save_try_interrupt = try_interrupt;
+ try_errflag = (zlong)(errflag & ERRFLAG_ERROR);
+ try_interrupt = (zlong)((errflag & ERRFLAG_INT) ? 1 : 0);
+ /* We need to reset all errors to allow the block to execute */
errflag = 0;
save_retflag = retflag;
retflag = 0;
@@ -682,8 +694,16 @@ exectry(Estate state, int do_exec)
state->pc = always;
execlist(state, 1, do_exec);
- errflag = try_errflag ? 1 : 0;
+ if (try_errflag)
+ errflag |= ERRFLAG_ERROR;
+ else
+ errflag &= ~ERRFLAG_ERROR;
+ if (try_interrupt)
+ errflag |= ERRFLAG_INT;
+ else
+ errflag &= ~ERRFLAG_INT;
try_errflag = save_try_errflag;
+ try_interrupt = save_try_interrupt;
if (!retflag)
retflag = save_retflag;
if (!breaks)
diff --git a/Src/params.c b/Src/params.c
index 61edc5d08..79088d162 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -331,6 +331,7 @@ IPDEF5("SHLVL", &shlvl, varinteger_gsu),
#define IPDEF6(A,B,F) {{NULL,A,PM_INTEGER|PM_SPECIAL|PM_DONTIMPORT},BR((void *)B),GSU(F),10,0,NULL,NULL,NULL,0}
IPDEF6("OPTIND", &zoptind, varinteger_gsu),
IPDEF6("TRY_BLOCK_ERROR", &try_errflag, varinteger_gsu),
+IPDEF6("TRY_BLOCK_INTERRUPT", &try_interrupt, varinteger_gsu),
#define IPDEF7(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0}
#define IPDEF7U(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL|PM_UNSET},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0}
@@ -2654,7 +2655,7 @@ assignsparam(char *s, char *val, int flags)
if (!isident(s)) {
zerr("not an identifier: %s", s);
zsfree(val);
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
return NULL;
}
queue_signals();
@@ -2783,7 +2784,7 @@ assignaparam(char *s, char **val, int flags)
if (!isident(s)) {
zerr("not an identifier: %s", s);
freearray(val);
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
return NULL;
}
queue_signals();
@@ -2799,7 +2800,7 @@ assignaparam(char *s, char **val, int flags)
zerr("%s: attempt to set slice of associative array",
v->pm->node.nam);
freearray(val);
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
return NULL;
}
v = NULL;
@@ -2870,13 +2871,13 @@ sethparam(char *s, char **val)
if (!isident(s)) {
zerr("not an identifier: %s", s);
freearray(val);
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
return NULL;
}
if (strchr(s, '[')) {
freearray(val);
zerr("nested associative arrays not yet supported");
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
return NULL;
}
if (unset(EXECOPT))
@@ -2916,7 +2917,7 @@ setnparam(char *s, mnumber val)
if (!isident(s)) {
zerr("not an identifier: %s", s);
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
return NULL;
}
if (unset(EXECOPT))
diff --git a/Src/parse.c b/Src/parse.c
index 4ceeb4eaf..c1709e03a 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -71,13 +71,14 @@ struct heredocs *hdocs;
#define YYERROR(O) { tok = LEXERR; ecused = (O); return 0; }
#define YYERRORV(O) { tok = LEXERR; ecused = (O); return; }
-#define COND_ERROR(X,Y) do { \
- zwarn(X,Y); \
- herrflush(); \
- if (noerrs != 2) \
- errflag = 1; \
- YYERROR(ecused) \
-} while(0)
+#define COND_ERROR(X,Y) \
+ do { \
+ zwarn(X,Y); \
+ herrflush(); \
+ if (noerrs != 2) \
+ errflag |= ERRFLAG_ERROR; \
+ YYERROR(ecused) \
+ } while(0)
/*
@@ -506,7 +507,7 @@ par_event(void)
yyerror(1);
herrflush();
if (noerrs != 2)
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
ecused--;
return 0;
} else {
@@ -2339,7 +2340,7 @@ yyerror(int noerr)
zwarn("parse error");
}
if (!noerr && noerrs != 2)
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
}
/*
@@ -3031,7 +3032,7 @@ build_dump(char *nam, char *dump, char **files, int ali, int map, int flags)
file = metafy(file, flen, META_REALLOC);
if (!(prog = parse_string(file, 1)) || errflag) {
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
close(dfd);
zfree(file, flen);
zwarnnam(nam, "can't read file: %s", *files);
@@ -3141,7 +3142,7 @@ build_cur_dump(char *nam, char *dump, char **names, int match, int map,
for (hn = shfunctab->nodes[i]; hn; hn = hn->next)
if (cur_add_func(nam, (Shfunc) hn, lnames, progs,
&hlen, &tlen, what)) {
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
close(dfd);
unlink(dump);
return 1;
@@ -3166,7 +3167,7 @@ build_cur_dump(char *nam, char *dump, char **names, int match, int map,
pattry(pprog, hn->nam) &&
cur_add_func(nam, (Shfunc) hn, lnames, progs,
&hlen, &tlen, what)) {
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
close(dfd);
unlink(dump);
return 1;
@@ -3177,13 +3178,13 @@ build_cur_dump(char *nam, char *dump, char **names, int match, int map,
if (errflag ||
!(shf = (Shfunc) shfunctab->getnode(shfunctab, *names))) {
zwarnnam(nam, "unknown function: %s", *names);
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
close(dfd);
unlink(dump);
return 1;
}
if (cur_add_func(nam, shf, lnames, progs, &hlen, &tlen, what)) {
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
close(dfd);
unlink(dump);
return 1;
@@ -3192,7 +3193,7 @@ build_cur_dump(char *nam, char *dump, char **names, int match, int map,
}
if (empty(progs)) {
zwarnnam(nam, "no functions");
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
close(dfd);
unlink(dump);
return 1;
diff --git a/Src/prompt.c b/Src/prompt.c
index 0cc9ef917..3552575f3 100644
--- a/Src/prompt.c
+++ b/Src/prompt.c
@@ -192,8 +192,11 @@ promptexpand(char *s, int ns, char *rs, char *Rs, unsigned int *txtchangep)
if (*s == Nularg && s[1] == '\0')
*s = '\0';
- /* Ignore errors and status change in prompt substitution */
- errflag = olderr;
+ /*
+ * Ignore errors and status change in prompt substitution.
+ * However, keep any user interrupt error that occurred.
+ */
+ errflag = olderr | (errflag & ERRFLAG_INT);
lastval = oldval;
}
diff --git a/Src/signals.c b/Src/signals.c
index e72850516..899f1217b 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -619,7 +619,7 @@ zhandler(int sig)
zexit(SIGINT, 1);
if (list_pipe || chline || simple_pline) {
breaks = loops;
- errflag = 1;
+ errflag |= ERRFLAG_INT;
inerrflush();
check_cursh_sig(SIGINT);
}
@@ -640,6 +640,11 @@ zhandler(int sig)
if (idle >= 0 && idle < tmout)
alarm(tmout - idle);
else {
+ /*
+ * We want to exit now.
+ * Cancel all errors, including a user interrupt
+ * which is now redundant.
+ */
errflag = noerrs = 0;
zwarn("timeout");
stopmsg = 1;
@@ -1267,7 +1272,18 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
!(isfunc && new_trap_return == 0)) {
if (isfunc) {
breaks = loops;
- errflag = 1;
+ /*
+ * For SIGINT we behave the same as the default behaviour
+ * i.e. we set the error bit indicating an interrupt.
+ * We do this with SIGQUIT, too, even though we don't
+ * handle SIGQUIT by default. That's to try to make
+ * it behave a bit more like its normal behaviour when
+ * the trap handler has told us that's what it wants.
+ */
+ if (sig == SIGINT || sig == SIGQUIT)
+ errflag |= ERRFLAG_INT;
+ else
+ errflag |= ERRFLAG_ERROR;
}
lastval = new_trap_return;
/* return triggered */
@@ -1282,8 +1298,12 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
*/
lastval = olastval;
}
- if (try_tryflag)
- errflag = traperr;
+ if (try_tryflag) {
+ if (traperr)
+ errflag |= ERRFLAG_ERROR;
+ else
+ errflag &= ~ERRFLAG_ERROR;
+ }
breaks += obreaks;
/* return not triggered: restore old flag */
retflag = oretflag;
diff --git a/Src/subst.c b/Src/subst.c
index 61aa1c136..43932c256 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -2822,7 +2822,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags)
haserr = parse_subst_string(s);
noerrs = one;
if (!quoteerr) {
- errflag = oef;
+ /* Retain user interrupt error status */
+ errflag = oef | (errflag & ERRFLAG_INT);
if (haserr)
shtokenize(s);
} else if (haserr || errflag) {
@@ -3249,8 +3250,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags)
haserr = 1;
}
noerrs = one;
- if (!quoteerr)
- errflag = oef;
+ if (!quoteerr) {
+ /* Retain user interrupt error status */
+ errflag = oef | (errflag & ERRFLAG_INT);
+ }
if (haserr || errflag)
return NULL;
}
@@ -3483,8 +3486,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags)
untokenize(*ap);
}
noerrs = one;
- if (!quoteerr)
- errflag = oef;
+ if (!quoteerr) {
+ /* Retain any user interrupt error status */
+ errflag = oef | (errflag & ERRFLAG_INT);
+ }
else if (haserr || errflag) {
zerr("parse error in parameter value");
return NULL;
@@ -3516,8 +3521,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags)
noerrs = 1;
haserr = parse_subst_string(val);
noerrs = one;
- if (!quoteerr)
- errflag = oef;
+ if (!quoteerr) {
+ /* Retain any user interrupt error status */
+ errflag = oef | (errflag & ERRFLAG_INT);
+ }
else if (haserr || errflag) {
zerr("parse error in parameter value");
return NULL;
@@ -4086,7 +4093,8 @@ modify(char **str, char **ptr)
noerrs = 1;
parse_subst_string(copy);
noerrs = one;
- errflag = oef;
+ /* Retain any user interrupt error status */
+ errflag = oef | (errflag & ERRFLAG_INT);
remnulargs(copy);
untokenize(copy);
}
@@ -4161,7 +4169,8 @@ modify(char **str, char **ptr)
noerrs = 1;
parse_subst_string(*str);
noerrs = one;
- errflag = oef;
+ /* Retain any user interrupt error status */
+ errflag = oef | (errflag & ERRFLAG_INT);
remnulargs(*str);
untokenize(*str);
}
diff --git a/Src/utils.c b/Src/utils.c
index ab3d3da93..893a8ca02 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -153,7 +153,7 @@ VA_DCL
if (errflag || noerrs) {
if (noerrs < 2)
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
return;
}
@@ -161,7 +161,7 @@ VA_DCL
VA_GET_ARG(ap, fmt, const char *);
zwarning(NULL, fmt, ap);
va_end(ap);
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
}
/**/
@@ -181,7 +181,7 @@ VA_DCL
VA_GET_ARG(ap, fmt, const char *);
zwarning(cmd, fmt, ap);
va_end(ap);
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
}
/**/
@@ -330,7 +330,7 @@ zerrmsg(FILE *file, const char *fmt, va_list ap)
num = va_arg(ap, int);
if (num == EINTR) {
fputs("interrupt\n", file);
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
return;
}
errmsg = strerror(num);
diff --git a/Src/zsh.h b/Src/zsh.h
index 031deaf3f..b366e0ff4 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -2623,6 +2623,20 @@ enum trap_state {
#define IN_EVAL_TRAP() \
(intrap && !trapisfunc && traplocallevel == locallevel)
+/*
+ * Bits in the errflag variable.
+ */
+enum errflag_bits {
+ /*
+ * Standard internal error bit.
+ */
+ ERRFLAG_ERROR = 1,
+ /*
+ * User interrupt.
+ */
+ ERRFLAG_INT = 2
+};
+
/***********/
/* Sorting */
/***********/
--
cgit v1.2.3
From 36ec763dbd4e30b614f491482b77fd4e0507fe85 Mon Sep 17 00:00:00 2001
From: "Barton E. Schaefer"
Date: Mon, 15 Dec 2014 16:41:08 -0800
Subject: 33976: fix overlapping strcpy()
---
ChangeLog | 4 ++++
Src/subst.c | 2 +-
2 files changed, 5 insertions(+), 1 deletion(-)
(limited to 'Src/subst.c')
diff --git a/ChangeLog b/ChangeLog
index 17d56a742..a53321e51 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2014-12-15 Barton E. Schaefer
+
+ * 33976: Src/subst.c: fix overlapping strcpy()
+
2014-12-14 Mikael Magnusson
* users/19530: Completion/Unix/Type/_file_systems: Handle
diff --git a/Src/subst.c b/Src/subst.c
index 43932c256..4100803e7 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -298,7 +298,7 @@ stringsubst(LinkList list, LinkNode node, int pf_flags, int asssub)
if (endchar == Outpar)
str2--;
if (!(s = (char *) ugetnode(pl))) {
- str = strcpy(str2, str);
+ str = (char *)memmove(str2, str, strlen(str)+1);
continue;
}
if (!qt && (pf_flags & PREFORK_SINGLE) && isset(GLOBSUBST))
--
cgit v1.2.3
From 7e7449592a6e2f10857e6a5e57a17d05e773b554 Mon Sep 17 00:00:00 2001
From: "Barton E. Schaefer"
Date: Tue, 6 Jan 2015 09:20:43 -0800
Subject: 34103: fix ancient double-quote handling thinko in subst_parse_str()
This doesn't seem to have mattered, but must in some obscure cases
---
Src/subst.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
(limited to 'Src/subst.c')
diff --git a/Src/subst.c b/Src/subst.c
index 4100803e7..610d71ee2 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -1333,14 +1333,16 @@ subst_parse_str(char **sp, int single, int err)
if (!single) {
int qt = 0;
- for (; *s; s++)
+ for (; *s; s++) {
if (!qt) {
if (*s == Qstring)
*s = String;
else if (*s == Qtick)
*s = Tick;
- } else if (*s == Dnull)
+ }
+ if (*s == Dnull)
qt = !qt;
+ }
}
return 0;
}
--
cgit v1.2.3
From 6c72895bc2e8fc3a6bc98ba089390b4d2fb12dde Mon Sep 17 00:00:00 2001
From: Mikael Magnusson
Date: Tue, 6 Jan 2015 01:59:53 +0100
Subject: 34105: subst: remove dead code
Found by Coverity (Issue 1255810).
---
ChangeLog | 2 ++
Src/subst.c | 2 +-
2 files changed, 3 insertions(+), 1 deletion(-)
(limited to 'Src/subst.c')
diff --git a/ChangeLog b/ChangeLog
index 46f1826c6..72013066d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,7 @@
2015-01-06 Mikael Magnusson
+ * 34105: Src/subst.c: remove dead code
+
* 34121: Src/Zle/compresult.c: Remove unneeded NULL check
* 34104: Src/Zle/compctl.c: Remove pointless check
diff --git a/Src/subst.c b/Src/subst.c
index 610d71ee2..5f993d6fd 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -1317,7 +1317,7 @@ get_intarg(char **s, int *delmatchp)
if (ret < 0)
ret = -ret;
*delmatchp = arglen;
- return ret < 0 ? -ret : ret;
+ return ret;
}
/* Parsing for the (e) flag. */
--
cgit v1.2.3
From c6c9f5daf2e196e6ab7346dfbf5f5166a1d87f0c Mon Sep 17 00:00:00 2001
From: Peter Stephenson
Date: Sun, 18 Jan 2015 22:38:57 +0000
Subject: 34322: bug with interface to parsestr() etc.
Was showing up in places like ${(e)...} where command substitution
could reallocate the token string, but actually there was never any
guarantee that the lexer wouldn't do that, so this was always
a bit iffy.
---
ChangeLog | 6 ++++++
Src/Zle/compctl.c | 4 ++--
Src/Zle/compresult.c | 3 ++-
Src/exec.c | 9 +++++----
Src/init.c | 11 +++++++----
Src/lex.c | 30 +++++++++++++++++++++---------
Src/params.c | 3 ++-
Src/prompt.c | 2 +-
Src/subst.c | 8 +++++---
Src/utils.c | 2 +-
Test/D04parameter.ztst | 7 +++++++
11 files changed, 59 insertions(+), 26 deletions(-)
(limited to 'Src/subst.c')
diff --git a/ChangeLog b/ChangeLog
index 30068eb8b..5d52f6693 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
2015-01-18 Peter Stephenson
+ * 34322: Src/Zle/compctl.c, Src/Zle/compresult.c, Src/exec.c,
+ Src/init.c, Src/lex.c, Src/params.c, Src/prompt.c, Src/subst.c,
+ Src/utils.c, Test/D04parameter.ztst: update interface to
+ parsestr()/parsestrnoerr() to ensure correct token string
+ is passed back.
+
* 34320: Src/hist.c, Src/lex.c: alias expansion in history of
command substitution.
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index 43dd4e2a9..189582d22 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -3853,7 +3853,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
yaptr = get_user_var(uv);
if ((tt = cc->explain)) {
tt = dupstring(tt);
- if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) {
+ if ((cc->mask & CC_EXPANDEXPL) && !parsestr(&tt)) {
singsub(&tt);
untokenize(tt);
}
@@ -3873,7 +3873,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
}
} else if ((tt = cc->explain)) {
tt = dupstring(tt);
- if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) {
+ if ((cc->mask & CC_EXPANDEXPL) && !parsestr(&tt)) {
singsub(&tt);
untokenize(tt);
}
diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c
index dbef7f841..9f383f4b8 100644
--- a/Src/Zle/compresult.c
+++ b/Src/Zle/compresult.c
@@ -1090,7 +1090,8 @@ do_single(Cmatch m)
}
if (tryit) {
noerrs = 1;
- parsestr(p);
+ p = dupstring(p);
+ parsestr(&p);
singsub(&p);
errflag &= ~ERRFLAG_ERROR;
noerrs = ne;
diff --git a/Src/exec.c b/Src/exec.c
index 7b6495113..f42fb2b9b 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -3772,19 +3772,20 @@ gethere(char **strp, int typ)
*bptr++ = '\n';
}
*t = '\0';
+ s = buf;
+ buf = dupstring(buf);
+ zfree(s, bsiz);
if (!qt) {
int ef = errflag;
- parsestr(buf);
+ parsestr(&buf);
if (!errflag) {
/* Retain any user interrupt error */
errflag = ef | (errflag & ERRFLAG_INT);
}
}
- s = dupstring(buf);
- zfree(buf, bsiz);
- return s;
+ return buf;
}
/* open here string fd */
diff --git a/Src/init.c b/Src/init.c
index e7d86feac..3e41fb9dd 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -1197,10 +1197,13 @@ run_init_scripts(void)
if (islogin)
sourcehome(".profile");
noerrs = 2;
- if (s && !parsestr(s)) {
- singsub(&s);
- noerrs = 0;
- source(s);
+ if (s) {
+ s = dupstring(s);
+ if (!parsestr(&s)) {
+ singsub(&s);
+ noerrs = 0;
+ source(s);
+ }
}
noerrs = 0;
} else
diff --git a/Src/lex.c b/Src/lex.c
index e4dfdfaca..433c27fbb 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -1503,17 +1503,27 @@ dquote_parse(char endchar, int sub)
return err;
}
-/* Tokenize a string given in s. Parsing is done as in double *
- * quotes. This is usually called before singsub(). */
+/*
+ * Tokenize a string given in s. Parsing is done as in double
+ * quotes. This is usually called before singsub().
+ *
+ * parsestr() is noisier, reporting an error if the parse failed.
+ *
+ * On entry, *s must point to a string allocated from the stack of
+ * exactly the right length, i.e. strlen(*s) + 1, as the string
+ * is used as the lexical token string whose memory management
+ * demands this. Usually the input string will therefore be
+ * the result of an immediately preceding dupstring().
+ */
/**/
mod_export int
-parsestr(char *s)
+parsestr(char **s)
{
int err;
if ((err = parsestrnoerr(s))) {
- untokenize(s);
+ untokenize(*s);
if (err > 32 && err < 127)
zerr("parse error near `%c'", err);
else
@@ -1524,18 +1534,20 @@ parsestr(char *s)
/**/
mod_export int
-parsestrnoerr(char *s)
+parsestrnoerr(char **s)
{
- int l = strlen(s), err;
+ int l = strlen(*s), err;
zcontext_save();
- untokenize(s);
- inpush(dupstring(s), 0, NULL);
+ untokenize(*s);
+ inpush(dupstring(*s), 0, NULL);
strinbeg(0);
lexbuf.len = 0;
- lexbuf.ptr = tokstr = s;
+ lexbuf.ptr = tokstr = *s;
lexbuf.siz = l + 1;
err = dquote_parse('\0', 1);
+ if (tokstr)
+ *s = tokstr;
*lexbuf.ptr = '\0';
strinend();
inpop();
diff --git a/Src/params.c b/Src/params.c
index b8e0c429b..64c78bd63 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -1260,7 +1260,8 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w,
if (ishash && (keymatch || !rev))
remnulargs(s);
if (needtok) {
- if (parsestr(s))
+ s = dupstring(s);
+ if (parsestr(&s))
return 0;
singsub(&s);
} else if (rev)
diff --git a/Src/prompt.c b/Src/prompt.c
index 3552575f3..ffc1d0df2 100644
--- a/Src/prompt.c
+++ b/Src/prompt.c
@@ -183,7 +183,7 @@ promptexpand(char *s, int ns, char *rs, char *Rs, unsigned int *txtchangep)
int oldval = lastval;
s = dupstring(s);
- if (!parsestr(s))
+ if (!parsestr(&s))
singsub(&s);
/*
* We don't need the special Nularg hack here and we're
diff --git a/Src/subst.c b/Src/subst.c
index 5f993d6fd..a2bb6483a 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -1306,7 +1306,7 @@ get_intarg(char **s, int *delmatchp)
p = dupstring(*s + arglen);
*s = t + arglen;
*t = sav;
- if (parsestr(p))
+ if (parsestr(&p))
return -1;
singsub(&p);
if (errflag)
@@ -1329,7 +1329,8 @@ subst_parse_str(char **sp, int single, int err)
*sp = s = dupstring(*sp);
- if (!(err ? parsestr(s) : parsestrnoerr(s))) {
+ if (!(err ? parsestr(&s) : parsestrnoerr(&s))) {
+ *sp = s;
if (!single) {
int qt = 0;
@@ -1439,7 +1440,8 @@ check_colon_subscript(char *str, char **endp)
}
sav = **endp;
**endp = '\0';
- if (parsestr(str = dupstring(str)))
+ str = dupstring(str);
+ if (parsestr(&str))
return NULL;
singsub(&str);
remnulargs(str);
diff --git a/Src/utils.c b/Src/utils.c
index f8d239458..4561b5e9a 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -1515,7 +1515,7 @@ checkmailpath(char **s)
setunderscore(*s);
u = dupstring(u);
- if (! parsestr(u)) {
+ if (!parsestr(&u)) {
singsub(&u);
zputs(u, shout);
fputc('\n', shout);
diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst
index 94d15f205..cf639fa8c 100644
--- a/Test/D04parameter.ztst
+++ b/Test/D04parameter.ztst
@@ -1663,3 +1663,10 @@
0:SHLVL appears sensible when about to exit shell
>2
>2
+
+# The following tests the return behaviour of parsestr/parsestrnoerr
+ alias param-test-alias='print $'\''\x45xpanded in substitution'\'
+ param='$(param-test-alias)'
+ print ${(e)param}
+0:Alias expansion in command substitution in parameter evaluation
+>Expanded in substitution
--
cgit v1.2.3
From 52e938bac954d6d503e180287bcf657187ef8803 Mon Sep 17 00:00:00 2001
From: Peter Stephenson
Date: Thu, 19 Feb 2015 10:22:40 +0000
Subject: 34570: Another nasty command / math substituion thing.
Mark arithmetic substitutions with tokens to make sure the substitution
go knows what to do. Before it was guessing by counting the
parentheses at the end.
---
ChangeLog | 6 +++++
Config/version.mk | 4 ++--
Src/lex.c | 64 ++++++++++++++++++++++++++++++++++++++----------------
Src/subst.c | 28 ++++++++++++++----------
Src/zsh.h | 44 +++++++++++++++++++------------------
Test/C01arith.ztst | 23 ++++++++++++++++++++
6 files changed, 116 insertions(+), 53 deletions(-)
(limited to 'Src/subst.c')
diff --git a/ChangeLog b/ChangeLog
index df483c30d..7610357dd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2015-02-19 Peter Stephenson
+
+ * 34570: Config/version.mk, Src/lex.c, Src/subst.c, Src/zsh.h,
+ Test/C01arith.ztst: mark arithmetic substitutions with tokens
+ so the substitution code knows what to do.
+
2015-02-17 Peter Stephenson
* 34563: Src/input.c: Fix up memory allocation in 34560.
diff --git a/Config/version.mk b/Config/version.mk
index eb51638fe..a8eafa5fe 100644
--- a/Config/version.mk
+++ b/Config/version.mk
@@ -27,5 +27,5 @@
# This must also serve as a shell script, so do not add spaces around the
# `=' signs.
-VERSION=5.0.7-dev-0
-VERSION_DATE='October 8, 2014'
+VERSION=5.0.7-dev-1
+VERSION_DATE='February 19, 2014'
diff --git a/Src/lex.c b/Src/lex.c
index 006848543..307b6e985 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -35,7 +35,7 @@
/* tokens */
/**/
-mod_export char ztokens[] = "#$^*()$=|{}[]`<>>?~`,'\"\\\\";
+mod_export char ztokens[] = "#$^*(())$=|{}[]`<>>?~`,'\"\\\\";
/* parts of the current token */
@@ -473,8 +473,14 @@ add(int c)
} \
}
+enum {
+ CMD_OR_MATH_CMD,
+ CMD_OR_MATH_MATH,
+ CMD_OR_MATH_ERR
+};
+
/*
- * Return 1 for math, 0 for a command, 2 for an error. If it couldn't be
+ * Return one of the above. If it couldn't be
* parsed as math, but there was no gross error, it's a command.
*/
@@ -496,13 +502,13 @@ cmd_or_math(int cs_type)
/* Successfully parsed, see if it was math */
c = hgetc();
if (c == ')')
- return 1; /* yes */
+ return CMD_OR_MATH_MATH; /* yes */
hungetc(c);
lexstop = 0;
c = ')';
} else if (lexstop) {
/* we haven't got anything to unget */
- return 2;
+ return CMD_OR_MATH_ERR;
}
/* else unsuccessful: unget the whole thing */
hungetc(c);
@@ -513,15 +519,15 @@ cmd_or_math(int cs_type)
ztokens[*lexbuf.ptr - Pound] : *lexbuf.ptr);
}
if (errflag)
- return 2;
+ return CMD_OR_MATH_ERR;
hungetc('(');
- return errflag ? 2 : 0;
+ return errflag ? CMD_OR_MATH_ERR : CMD_OR_MATH_CMD;
}
/*
* Parse either a $(( ... )) or a $(...)
- * Return 0 on success, 1 on failure.
+ * Return the same as cmd_or_math().
*/
static int
cmd_or_math_sub(void)
@@ -529,21 +535,23 @@ cmd_or_math_sub(void)
int c = hgetc(), ret;
if (c == '(') {
+ int lexpos = (int)(lexbuf.ptr - tokstr);
add(Inpar);
add('(');
- if ((ret = cmd_or_math(CS_MATHSUBST)) == 1) {
+ if ((ret = cmd_or_math(CS_MATHSUBST)) == CMD_OR_MATH_MATH) {
+ tokstr[lexpos] = Inparmath;
add(')');
- return 0;
+ return CMD_OR_MATH_MATH;
}
- if (ret == 2)
- return 1;
+ if (ret == CMD_OR_MATH_ERR)
+ return CMD_OR_MATH_ERR;
lexbuf.ptr -= 2;
lexbuf.len -= 2;
} else {
hungetc(c);
lexstop = 0;
}
- return skipcomm();
+ return skipcomm() ? CMD_OR_MATH_ERR : CMD_OR_MATH_CMD;
}
/* Check whether we're looking at valid numeric globbing syntax *
@@ -764,10 +772,10 @@ gettok(void)
lexbuf.ptr = tokstr = (char *)
hcalloc(lexbuf.siz = LEX_HEAP_SIZE);
switch (cmd_or_math(CS_MATH)) {
- case 1:
+ case CMD_OR_MATH_MATH:
return DINPAR;
- case 0:
+ case CMD_OR_MATH_CMD:
/*
* Not math, so we don't return the contents
* as a string in this case.
@@ -987,12 +995,19 @@ gettokstr(int c, int sub)
c = Outbrack;
} else if (e == '(') {
add(String);
- c = cmd_or_math_sub();
- if (c) {
+ switch (cmd_or_math_sub()) {
+ case CMD_OR_MATH_CMD:
+ c = Outpar;
+ break;
+
+ case CMD_OR_MATH_MATH:
+ c = Outparmath;
+ break;
+
+ default:
peek = LEXERR;
goto brk;
}
- c = Outpar;
} else {
if (e == '{') {
add(c);
@@ -1400,8 +1415,19 @@ dquote_parse(char endchar, int sub)
c = hgetc();
if (c == '(') {
add(Qstring);
- err = cmd_or_math_sub();
- c = Outpar;
+ switch (cmd_or_math_sub()) {
+ case CMD_OR_MATH_CMD:
+ c = Outpar;
+ break;
+
+ case CMD_OR_MATH_MATH:
+ c = Outparmath;
+ break;
+
+ default:
+ err = 1;
+ break;
+ }
} else if (c == '[') {
add(String);
add(Inbrack);
diff --git a/Src/subst.c b/Src/subst.c
index a2bb6483a..056b12b27 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -195,7 +195,7 @@ stringsubst(LinkList list, LinkNode node, int pf_flags, int asssub)
while (!errflag && (c = *str)) {
if ((qt = c == Qstring) || c == String) {
- if ((c = str[1]) == Inpar) {
+ if ((c = str[1]) == Inpar || c == Inparmath) {
if (!qt)
list->list.flags |= LF_ARRAY;
str++;
@@ -258,6 +258,22 @@ stringsubst(LinkList list, LinkNode node, int pf_flags, int asssub)
skipparens(Inpar, Outpar, &str);
#endif
str--;
+ } else if (c == Inparmath) {
+ /* Math substitution of the form $((...)) */
+ str[-1] = '\0';
+ while (*str != Outparmath && *str)
+ str++;
+ if (*str != Outparmath) {
+ zerr("Failed to find end of math substitution");
+ return NULL;
+ }
+ str[-1] = '\0';
+ if (isset(EXECOPT))
+ str = arithsubst(str2 + 2, &str3, str+1);
+ else
+ strncpy(str3, str2, 1);
+ setdata(node, (void *) str3);
+ continue;
} else {
endchar = c;
*str = '\0';
@@ -266,16 +282,6 @@ stringsubst(LinkList list, LinkNode node, int pf_flags, int asssub)
DPUTS(!*str, "BUG: parse error in command substitution");
}
*str++ = '\0';
- if (endchar == Outpar && str2[1] == '(' && str[-2] == ')') {
- /* Math substitution of the form $((...)) */
- str[-2] = '\0';
- if (isset(EXECOPT))
- str = arithsubst(str2 + 2, &str3, str);
- else
- strncpy(str3, str2, 1);
- setdata(node, (void *) str3);
- continue;
- }
/* It is a command substitution, which will be parsed again *
* by the lexer, so we untokenize it first, but we cannot use *
diff --git a/Src/zsh.h b/Src/zsh.h
index dd946d214..9a9726339 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -163,40 +163,42 @@ struct mathfunc {
#define Hat ((char) 0x86)
#define Star ((char) 0x87)
#define Inpar ((char) 0x88)
-#define Outpar ((char) 0x89)
-#define Qstring ((char) 0x8a)
-#define Equals ((char) 0x8b)
-#define Bar ((char) 0x8c)
-#define Inbrace ((char) 0x8d)
-#define Outbrace ((char) 0x8e)
-#define Inbrack ((char) 0x8f)
-#define Outbrack ((char) 0x90)
-#define Tick ((char) 0x91)
-#define Inang ((char) 0x92)
-#define Outang ((char) 0x93)
-#define OutangProc ((char) 0x94)
-#define Quest ((char) 0x95)
-#define Tilde ((char) 0x96)
-#define Qtick ((char) 0x97)
-#define Comma ((char) 0x98)
+#define Inparmath ((char) 0x89)
+#define Outpar ((char) 0x8a)
+#define Outparmath ((char) 0x8b)
+#define Qstring ((char) 0x8c)
+#define Equals ((char) 0x8d)
+#define Bar ((char) 0x8e)
+#define Inbrace ((char) 0x8f)
+#define Outbrace ((char) 0x90)
+#define Inbrack ((char) 0x91)
+#define Outbrack ((char) 0x92)
+#define Tick ((char) 0x93)
+#define Inang ((char) 0x94)
+#define Outang ((char) 0x95)
+#define OutangProc ((char) 0x96)
+#define Quest ((char) 0x97)
+#define Tilde ((char) 0x98)
+#define Qtick ((char) 0x99)
+#define Comma ((char) 0x9a)
/*
* Null arguments: placeholders for single and double quotes
* and backslashes.
*/
-#define Snull ((char) 0x99)
-#define Dnull ((char) 0x9a)
-#define Bnull ((char) 0x9b)
+#define Snull ((char) 0x9b)
+#define Dnull ((char) 0x9c)
+#define Bnull ((char) 0x9d)
/*
* Backslash which will be returned to "\" instead of being stripped
* when we turn the string into a printable format.
*/
-#define Bnullkeep ((char) 0x9c)
+#define Bnullkeep ((char) 0x9e)
/*
* Null argument that does not correspond to any character.
* This should be last as it does not appear in ztokens and
* is used to initialise the IMETA type in inittyptab().
*/
-#define Nularg ((char) 0x9d)
+#define Nularg ((char) 0x9f)
/*
* Take care to update the use of IMETA appropriately when adding
diff --git a/Test/C01arith.ztst b/Test/C01arith.ztst
index 09c08224e..67d78ee13 100644
--- a/Test/C01arith.ztst
+++ b/Test/C01arith.ztst
@@ -353,3 +353,26 @@
'
0:Non-arithmetic subst with command subsitution parse from hell
>yes, this one after case in subshell
+
+ print "a$((echo one subst)
+ (echo two subst))b"
+0:Another tricky case that is actually a command substitution
+>aone subst
+>two substb
+
+ print "x$((echo one frob); (echo two frob))y"
+0:Same on a single line
+>xone frob
+>two froby
+
+ # This case actually only works by accident: if it wasn't for the
+ # unbalanced parenthesis this would be a valid math substitution.
+ # Hence it's definitely not recommended code. However, it does give
+ # the algorithm an extra check.
+ print $((case foo in
+ foo)
+ print Worked OK
+ ;;
+ esac))
+0:Would-be math expansion with extra parenthesis making it a cmd subst
+>Worked OK
--
cgit v1.2.3
From 2cbf9d7e65b2f5aeb51c165cdf9033938cba50e6 Mon Sep 17 00:00:00 2001
From: Peter Stephenson
Date: Thu, 19 Feb 2015 12:01:16 +0000
Subject: 34573: Safer failure to handle command substitution
---
ChangeLog | 3 +++
Src/subst.c | 10 +++++++---
2 files changed, 10 insertions(+), 3 deletions(-)
(limited to 'Src/subst.c')
diff --git a/ChangeLog b/ChangeLog
index 7610357dd..a621044f5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,8 @@
2015-02-19 Peter Stephenson
+ * 34573: Src/subst.c: safer handling of failure to perform
+ command substitution.
+
* 34570: Config/version.mk, Src/lex.c, Src/subst.c, Src/zsh.h,
Test/C01arith.ztst: mark arithmetic substitutions with tokens
so the substitution code knows what to do.
diff --git a/Src/subst.c b/Src/subst.c
index 056b12b27..176a004ac 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -264,7 +264,7 @@ stringsubst(LinkList list, LinkNode node, int pf_flags, int asssub)
while (*str != Outparmath && *str)
str++;
if (*str != Outparmath) {
- zerr("Failed to find end of math substitution");
+ zerr("failed to find end of math substitution");
return NULL;
}
str[-1] = '\0';
@@ -278,8 +278,12 @@ stringsubst(LinkList list, LinkNode node, int pf_flags, int asssub)
endchar = c;
*str = '\0';
- while (*++str != endchar)
- DPUTS(!*str, "BUG: parse error in command substitution");
+ while (*++str != endchar) {
+ if (!*str) {
+ zerr("failed to find end of command substitution");
+ return NULL;
+ }
+ }
}
*str++ = '\0';
--
cgit v1.2.3
From 6fa63a0ce2b2aa31a343ca0841b57895d11b5754 Mon Sep 17 00:00:00 2001
From: Peter Stephenson
Date: Sun, 22 Feb 2015 21:32:08 +0000
Subject: 34606: fix up nested arithmetic substitution
Arithmetic within a parameter substitution is a special
case that needs fixing with the introduction of the new
Inparmath token.
Add test.
---
ChangeLog | 3 +++
Src/subst.c | 20 ++++++++++++++++++--
Test/C01arith.ztst | 8 ++++++++
3 files changed, 29 insertions(+), 2 deletions(-)
(limited to 'Src/subst.c')
diff --git a/ChangeLog b/ChangeLog
index db222762a..31988c5b6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,8 @@
2015-02-22 Peter Stephenson
+ * 34606: Src/subst.c, Test/C01arith.ztst: fix up arithmetic
+ nested in parameter substitution.
+
* 34604: Src/hist.c: Work around problem with changes in Meta
affecting history file when read in.
diff --git a/Src/subst.c b/Src/subst.c
index 176a004ac..f52bcdfc8 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -2217,12 +2217,28 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags)
*/
idbeg = s;
if ((subexp = (inbrace && s[-1] && isstring(*s) &&
- (s[1] == Inbrace || s[1] == Inpar)))) {
+ (s[1] == Inbrace || s[1] == Inpar || s[1] == Inparmath)))) {
int sav;
int quoted = *s == Qstring;
+ int outtok;
val = s++;
- skipparens(*s, *s == Inpar ? Outpar : Outbrace, &s);
+ switch (*s) {
+ case Inbrace:
+ outtok = Outbrace;
+ break;
+ case Inpar:
+ outtok = Outpar;
+ break;
+ case Inparmath:
+ outtok = Outparmath;
+ break;
+ default:
+ /* "Can't Happen" (TM) */
+ DPUTS(1, "Nested substitution: This Can't Happen (TM)");
+ return NULL;
+ }
+ skipparens(*s, outtok, &s);
sav = *s;
*s = 0;
/*
diff --git a/Test/C01arith.ztst b/Test/C01arith.ztst
index 67d78ee13..d3176dd5e 100644
--- a/Test/C01arith.ztst
+++ b/Test/C01arith.ztst
@@ -300,6 +300,7 @@
print $(( 0b2 ))
1:Binary numbers don't tend to have 2's in
?(eval):1: bad math expression: operator expected at `2 '
+# ` for emacs shell mode
integer varassi
print $(( varassi = 5.5 / 2.0 ))
@@ -376,3 +377,10 @@
esac))
0:Would-be math expansion with extra parenthesis making it a cmd subst
>Worked OK
+
+ (setopt extendedglob
+ set -- 32.463
+ print ${$(( $1 * 100 ))%%.[0-9]#})
+0:Arithmetic substitution nested in parameter substitution
+>3246
+
--
cgit v1.2.3