summaryrefslogtreecommitdiff
path: root/Src
diff options
context:
space:
mode:
Diffstat (limited to 'Src')
-rw-r--r--Src/Modules/db_gdbm.c2
-rw-r--r--Src/Modules/mapfile.c2
-rw-r--r--Src/Modules/pcre.c35
-rw-r--r--Src/Modules/system.c14
-rw-r--r--Src/Modules/zpty.c7
-rw-r--r--Src/Zle/compctl.c70
-rw-r--r--Src/Zle/complete.c54
-rw-r--r--Src/Zle/complist.c6
-rw-r--r--Src/Zle/computil.c44
-rw-r--r--Src/Zle/textobjects.c7
-rw-r--r--Src/Zle/zle.h2
-rw-r--r--Src/Zle/zle_thingy.c5
-rw-r--r--Src/Zle/zle_tricky.c11
-rw-r--r--Src/Zle/zle_word.c119
-rw-r--r--Src/builtin.c118
-rw-r--r--Src/cond.c20
-rw-r--r--Src/exec.c341
-rw-r--r--Src/glob.c2
-rw-r--r--Src/hashtable.c2
-rw-r--r--Src/hashtable.h1
-rw-r--r--Src/hist.c62
-rw-r--r--Src/init.c132
-rw-r--r--Src/jobs.c47
-rw-r--r--Src/lex.c24
-rw-r--r--Src/loop.c9
-rw-r--r--Src/math.c10
-rw-r--r--Src/mem.c14
-rw-r--r--Src/options.c1
-rw-r--r--Src/params.c264
-rw-r--r--Src/parse.c22
-rw-r--r--Src/prompt.c4
-rw-r--r--Src/subst.c140
-rw-r--r--Src/utils.c108
-rw-r--r--Src/zsh.h63
34 files changed, 1282 insertions, 480 deletions
diff --git a/Src/Modules/db_gdbm.c b/Src/Modules/db_gdbm.c
index cf1322459..5f776f407 100644
--- a/Src/Modules/db_gdbm.c
+++ b/Src/Modules/db_gdbm.c
@@ -98,7 +98,7 @@ static struct builtin bintab[] = {
{ name, PM_ARRAY | PM_READONLY, (void *) var, NULL, NULL, NULL, NULL }
/* Holds names of all tied parameters */
-char **zgdbm_tied;
+static char **zgdbm_tied;
static struct paramdef patab[] = {
ROARRPARAMDEF( "zgdbm_tied", &zgdbm_tied ),
diff --git a/Src/Modules/mapfile.c b/Src/Modules/mapfile.c
index 2503b361e..7a903418f 100644
--- a/Src/Modules/mapfile.c
+++ b/Src/Modules/mapfile.c
@@ -198,7 +198,7 @@ get_contents(char *fname)
if ((fd = open(fname, O_RDONLY | O_NOCTTY)) >= 0) {
LinkList ll;
- if ((ll = readoutput(fd, 1)))
+ if ((ll = readoutput(fd, 1, 0)))
val = peekfirst(ll);
}
#endif /* USE_MMAP */
diff --git a/Src/Modules/pcre.c b/Src/Modules/pcre.c
index 659fd22d5..15ee34bc8 100644
--- a/Src/Modules/pcre.c
+++ b/Src/Modules/pcre.c
@@ -88,10 +88,19 @@ bin_pcre_compile(char *nam, char **args, Options ops, UNUSED(int func))
if (zpcre_utf8_enabled())
pcre_opts |= PCRE_UTF8;
- pcre_hints = NULL; /* Is this necessary? */
+#ifdef HAVE_PCRE_STUDY
+ if (pcre_hints)
+#ifdef PCRE_CONFIG_JIT
+ pcre_free_study(pcre_hints);
+#else
+ pcre_free(pcre_hints);
+#endif
+ pcre_hints = NULL;
+#endif
if (pcre_pattern)
pcre_free(pcre_pattern);
+ pcre_pattern = NULL;
target = ztrdup(*args);
unmetafy(target, &target_len);
@@ -128,6 +137,14 @@ bin_pcre_study(char *nam, UNUSED(char **args), UNUSED(Options ops), UNUSED(int f
return 1;
}
+ if (pcre_hints)
+#ifdef PCRE_CONFIG_JIT
+ pcre_free_study(pcre_hints);
+#else
+ pcre_free(pcre_hints);
+#endif
+ pcre_hints = NULL;
+
pcre_hints = pcre_study(pcre_pattern, 0, &pcre_error);
if (pcre_error != NULL)
{
@@ -528,5 +545,21 @@ cleanup_(Module m)
int
finish_(UNUSED(Module m))
{
+#if defined(HAVE_PCRE_COMPILE) && defined(HAVE_PCRE_EXEC)
+#ifdef HAVE_PCRE_STUDY
+ if (pcre_hints)
+#ifdef PCRE_CONFIG_JIT
+ pcre_free_study(pcre_hints);
+#else
+ pcre_free(pcre_hints);
+#endif
+ pcre_hints = NULL;
+#endif
+
+ if (pcre_pattern)
+ pcre_free(pcre_pattern);
+ pcre_pattern = NULL;
+#endif
+
return 0;
}
diff --git a/Src/Modules/system.c b/Src/Modules/system.c
index 3eecd7e95..9fd4d2583 100644
--- a/Src/Modules/system.c
+++ b/Src/Modules/system.c
@@ -649,22 +649,30 @@ bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
if (timeout > 0) {
time_t end = time(NULL) + (time_t)timeout;
while (fcntl(flock_fd, F_SETLK, &lck) < 0) {
- if (errflag)
+ if (errflag) {
+ zclose(flock_fd);
return 1;
+ }
if (errno != EINTR && errno != EACCES && errno != EAGAIN) {
+ zclose(flock_fd);
zwarnnam(nam, "failed to lock file %s: %e", args[0], errno);
return 1;
}
- if (time(NULL) >= end)
+ if (time(NULL) >= end) {
+ zclose(flock_fd);
return 2;
+ }
sleep(1);
}
} else {
while (fcntl(flock_fd, timeout == 0 ? F_SETLK : F_SETLKW, &lck) < 0) {
- if (errflag)
+ if (errflag) {
+ zclose(flock_fd);
return 1;
+ }
if (errno == EINTR)
continue;
+ zclose(flock_fd);
zwarnnam(nam, "failed to lock file %s: %e", args[0], errno);
return 1;
}
diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c
index 3c1bef58f..1c93a1d02 100644
--- a/Src/Modules/zpty.c
+++ b/Src/Modules/zpty.c
@@ -254,7 +254,12 @@ get_pty(int master, int *retfd)
#elif defined(__FreeBSD__) || defined(__DragonFly__)
static char char1[] = "pqrsPQRS";
static char char2[] = "0123456789abcdefghijklmnopqrstuv";
-#else /* __FreeBSD__ || __DragonFly__ */
+#elif defined(__OpenBSD__)
+ static char char1[] = "pqrstuvwxyzPQRST";
+ static char char2[] = "0123456789"
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+#else /* __FreeBSD__ || __DragonFly__ || __OpenBSD*/
static char char1[] = "pqrstuvwxyzPQRST";
static char char2[] = "0123456789abcdef";
#endif
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index 5414b8ff6..87d13afc1 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -383,7 +383,7 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef, int cl)
* cct is a temporary just to hold flags; it never needs freeing.
*/
struct compctl cct;
- char **argv = *av;
+ char **argv = *av, argv_end[2] = "x";
int ready = 0, hx = 0;
/* Handle `compctl + foo ...' specially: turn it into
@@ -516,14 +516,14 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef, int cl)
}
if ((*argv)[1]) {
p = (*argv) + 1;
- *argv = "" - 1;
+ *argv = argv_end;
} else if (!argv[1]) {
zwarnnam(name, "retry specification expected after -%c",
**argv);
return 1;
} else {
p = *++argv;
- *argv = "" - 1;
+ *argv = argv_end;
}
switch (*p) {
case '+':
@@ -553,25 +553,25 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef, int cl)
case 'k':
if ((*argv)[1]) {
cct.keyvar = (*argv) + 1;
- *argv = "" - 1;
+ *argv = argv_end;
} else if (!argv[1]) {
zwarnnam(name, "variable name expected after -%c", **argv);
return 1;
} else {
cct.keyvar = *++argv;
- *argv = "" - 1;
+ *argv = argv_end;
}
break;
case 'K':
if ((*argv)[1]) {
cct.func = (*argv) + 1;
- *argv = "" - 1;
+ *argv = argv_end;
} else if (!argv[1]) {
zwarnnam(name, "function name expected after -%c", **argv);
return 1;
} else {
cct.func = *++argv;
- *argv = "" - 1;
+ *argv = argv_end;
}
break;
case 'Y':
@@ -582,74 +582,74 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef, int cl)
expl:
if ((*argv)[1]) {
cct.explain = (*argv) + 1;
- *argv = "" - 1;
+ *argv = argv_end;
} else if (!argv[1]) {
zwarnnam(name, "string expected after -%c", **argv);
return 1;
} else {
cct.explain = *++argv;
- *argv = "" - 1;
+ *argv = argv_end;
}
break;
case 'y':
if ((*argv)[1]) {
cct.ylist = (*argv) + 1;
- *argv = "" - 1;
+ *argv = argv_end;
} else if (!argv[1]) {
zwarnnam(name, "function/variable expected after -%c",
**argv);
} else {
cct.ylist = *++argv;
- *argv = "" - 1;
+ *argv = argv_end;
}
break;
case 'P':
if ((*argv)[1]) {
cct.prefix = (*argv) + 1;
- *argv = "" - 1;
+ *argv = argv_end;
} else if (!argv[1]) {
zwarnnam(name, "string expected after -%c", **argv);
return 1;
} else {
cct.prefix = *++argv;
- *argv = "" - 1;
+ *argv = argv_end;
}
break;
case 'S':
if ((*argv)[1]) {
cct.suffix = (*argv) + 1;
- *argv = "" - 1;
+ *argv = argv_end;
} else if (!argv[1]) {
zwarnnam(name, "string expected after -%c", **argv);
return 1;
} else {
cct.suffix = *++argv;
- *argv = "" - 1;
+ *argv = argv_end;
}
break;
case 'g':
if ((*argv)[1]) {
cct.glob = (*argv) + 1;
- *argv = "" - 1;
+ *argv = argv_end;
} else if (!argv[1]) {
zwarnnam(name, "glob pattern expected after -%c", **argv);
return 1;
} else {
cct.glob = *++argv;
- *argv = "" - 1;
+ *argv = argv_end;
}
break;
case 's':
if ((*argv)[1]) {
cct.str = (*argv) + 1;
- *argv = "" - 1;
+ *argv = argv_end;
} else if (!argv[1]) {
zwarnnam(name, "command string expected after -%c",
**argv);
return 1;
} else {
cct.str = *++argv;
- *argv = "" - 1;
+ *argv = argv_end;
}
break;
case 'l':
@@ -658,13 +658,13 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef, int cl)
return 1;
} else if ((*argv)[1]) {
cct.subcmd = (*argv) + 1;
- *argv = "" - 1;
+ *argv = argv_end;
} else if (!argv[1]) {
zwarnnam(name, "command name expected after -%c", **argv);
return 1;
} else {
cct.subcmd = *++argv;
- *argv = "" - 1;
+ *argv = argv_end;
}
break;
case 'h':
@@ -673,49 +673,49 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef, int cl)
return 1;
} else if ((*argv)[1]) {
cct.substr = (*argv) + 1;
- *argv = "" - 1;
+ *argv = argv_end;
} else if (!argv[1]) {
zwarnnam(name, "command name expected after -%c", **argv);
return 1;
} else {
cct.substr = *++argv;
- *argv = "" - 1;
+ *argv = argv_end;
}
break;
case 'W':
if ((*argv)[1]) {
cct.withd = (*argv) + 1;
- *argv = "" - 1;
+ *argv = argv_end;
} else if (!argv[1]) {
zwarnnam(name, "path expected after -%c", **argv);
return 1;
} else {
cct.withd = *++argv;
- *argv = "" - 1;
+ *argv = argv_end;
}
break;
case 'J':
if ((*argv)[1]) {
cct.gname = (*argv) + 1;
- *argv = "" - 1;
+ *argv = argv_end;
} else if (!argv[1]) {
zwarnnam(name, "group name expected after -%c", **argv);
return 1;
} else {
cct.gname = *++argv;
- *argv = "" - 1;
+ *argv = argv_end;
}
break;
case 'V':
if ((*argv)[1]) {
cct.gname = (*argv) + 1;
- *argv = "" - 1;
+ *argv = argv_end;
} else if (!argv[1]) {
zwarnnam(name, "group name expected after -%c", **argv);
return 1;
} else {
cct.gname = *++argv;
- *argv = "" - 1;
+ *argv = argv_end;
}
cct.mask2 |= CC_NOSORT;
break;
@@ -738,7 +738,7 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef, int cl)
cct.mstr = NULL;
return 1;
}
- *argv = "" - 1;
+ *argv = argv_end;
} else if (!argv[1]) {
zwarnnam(name, "matching specification expected after -%c",
**argv);
@@ -751,7 +751,7 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef, int cl)
cct.mstr = NULL;
return 1;
}
- *argv = "" - 1;
+ *argv = argv_end;
}
break;
case 'H':
@@ -772,7 +772,7 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef, int cl)
cct.hnum = 0;
if (*cct.hpat == '*' && !cct.hpat[1])
cct.hpat = "";
- *argv = "" - 1;
+ *argv = argv_end;
break;
case 'C':
if (cl) {
@@ -2176,6 +2176,8 @@ gen_matches_files(int dirs, int execs, int all)
if (prpre && *prpre) {
pathpref = dupstring(prpre);
unmetafy(pathpref, &pathpreflen);
+ if (pathpreflen > PATH_MAX)
+ return;
/* system needs NULL termination, not provided by unmetafy */
pathpref[pathpreflen] = '\0';
} else {
@@ -2218,6 +2220,8 @@ gen_matches_files(int dirs, int execs, int all)
* the path buffer by appending the filename. */
ums = dupstring(n);
unmetafy(ums, &umlen);
+ if (umlen + pathpreflen + 1 > PATH_MAX)
+ continue;
memcpy(q, ums, umlen);
q[umlen] = '\0';
/* And do the stat. */
@@ -2232,6 +2236,8 @@ gen_matches_files(int dirs, int execs, int all)
/* We have to test for a path suffix. */
int o = strlen(p), tt;
+ if (o + strlen(psuf) > PATH_MAX)
+ continue;
/* Append it to the path buffer. */
strcpy(p + o, psuf);
diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c
index 68bdf2332..313dcb92f 100644
--- a/Src/Zle/complete.c
+++ b/Src/Zle/complete.c
@@ -715,11 +715,10 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
case 'E':
if (p[1]) {
dat.dummies = atoi(p + 1);
- p = "" - 1;
+ p += strlen(p+1);
} else if (argv[1]) {
argv++;
dat.dummies = atoi(*argv);
- p = "" - 1;
} else {
zwarnnam(name, "number expected after -%c", *p);
zsfree(mstr);
@@ -744,13 +743,12 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
/* Pasted argument: -Xfoo. */
if (!*sp)
*sp = p + 1;
- p = "" - 1;
+ p += strlen(p+1);
} else if (argv[1]) {
/* Argument in a separate word: -X foo. */
argv++;
if (!*sp)
*sp = *argv;
- p = "" - 1;
} else {
/* Missing argument: argv[N] == "-X", argv[N+1] == NULL. */
zwarnnam(name, e, *p);
@@ -934,19 +932,45 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod)
}
case CVT_PRENUM:
case CVT_SUFNUM:
- if (!na)
- return 1;
- if (na > 0 &&
- (int)strlen(test == CVT_PRENUM ? compprefix : compsuffix) >= na) {
- if (mod) {
- if (test == CVT_PRENUM)
- ignore_prefix(na);
- else
- ignore_suffix(na);
- return 1;
- }
+ if (na < 0)
return 0;
+ if (na > 0 && mod) {
+#ifdef MULTIBYTE_SUPPORT
+ if (isset(MULTIBYTE)) {
+ if (test == CVT_PRENUM) {
+ const char *ptr = compprefix;
+ int len = 1;
+ int sum = 0;
+ while (*ptr && na && len) {
+ wint_t wc;
+ len = mb_metacharlenconv(ptr, &wc);
+ ptr += len;
+ sum += len;
+ na--;
+ }
+ if (na)
+ return 0;
+ na = sum;
+ } else {
+ char *end = compsuffix + strlen(compsuffix);
+ char *ptr = end;
+ while (na-- && ptr > compsuffix)
+ ptr = backwardmetafiedchar(compsuffix, ptr, NULL);
+ if (na >= 0)
+ return 0;
+ na = end - ptr;
+ }
+ } else
+#endif
+ if ((int)strlen(test == CVT_PRENUM ? compprefix : compsuffix) >= na)
+ return 0;
+ if (test == CVT_PRENUM)
+ ignore_prefix(na);
+ else
+ ignore_suffix(na);
+ return 1;
}
+ return 1;
case CVT_PREPAT:
case CVT_SUFPAT:
{
diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c
index a83daeff9..e768aee5d 100644
--- a/Src/Zle/complist.c
+++ b/Src/Zle/complist.c
@@ -1097,6 +1097,9 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop)
if (*p) {
int arg = 0, is_fg;
+ if (idigit(*p))
+ arg = zstrtol(p, &p, 10);
+
len = MB_METACHARLENCONV(p, &cchar);
#ifdef MULTIBYTE_SUPPORT
if (cchar == WEOF)
@@ -1104,9 +1107,6 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop)
#endif
p += len;
- if (idigit(*p))
- arg = zstrtol(p, &p, 10);
-
m = 0;
switch (cchar) {
case ZWC('%'):
diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c
index e704f9ffa..4ce8eeee5 100644
--- a/Src/Zle/computil.c
+++ b/Src/Zle/computil.c
@@ -1222,7 +1222,7 @@ parse_cadef(char *nam, char **args)
else if (*p == 'A') {
if (p[1]) {
nonarg = p + 1;
- p = "" - 1;
+ p += strlen(p+1);
} else if (args[1])
nonarg = *++args;
else
@@ -1230,7 +1230,7 @@ parse_cadef(char *nam, char **args)
} else if (*p == 'M') {
if (p[1]) {
match = p + 1;
- p = "" - 1;
+ p += strlen(p+1);
} else if (args[1])
match = *++args;
else
@@ -1824,7 +1824,7 @@ ca_inactive(Cadef d, char **xor, int cur, int opts)
char *x;
/* current word could be a prefix of a longer one so only do
* exclusions for single-letter options (for option clumping) */
- int single = (cur == compcurrent);
+ int single = !opts && (cur == compcurrent);
for (; (x = (opts ? "-" : *xor)); xor++) {
int excludeall = 0;
@@ -1933,7 +1933,6 @@ struct castate {
int inopt; /* set to current word pos if word is a recognised option */
int inarg; /* in a normal argument */
int nth; /* number of current normal arg */
- int doff; /* length of current option */
int singles; /* argument consists of clumped options */
int oopt;
int actopts; /* count of active options */
@@ -1943,6 +1942,7 @@ struct castate {
static struct castate ca_laststate;
static int ca_parsed = 0, ca_alloced = 0;
+static int ca_doff; /* no. of chars of ignored prefix (for clumped options or arg to an option) */
static void
freecastate(Castate s)
@@ -2033,7 +2033,7 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first)
state.argbeg = state.optbeg = state.nargbeg = state.restbeg = state.actopts =
state.nth = state.inopt = state.inarg = state.opt = state.arg = 1;
state.argend = argend = arrlen(compwords) - 1;
- state.doff = state.singles = state.oopt = 0;
+ state.singles = state.oopt = 0;
state.curpos = compcurrent;
state.args = znewlinklist();
state.oargs = (LinkList *) zalloc(d->nopts * sizeof(LinkList));
@@ -2057,7 +2057,7 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first)
line; line = compwords[cur++]) {
ddef = adef = NULL;
dopt = NULL;
- doff = state.singles = arglast = 0;
+ state.singles = arglast = 0;
oline = line;
#if 0
@@ -2080,7 +2080,7 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first)
remnulargs(line);
untokenize(line);
- ca_inactive(d, argxor, cur, 0);
+ ca_inactive(d, argxor, cur - 1, 0);
if ((d->flags & CDF_SEP) && cur != compcurrent && !strcmp(line, "--")) {
ca_inactive(d, NULL, cur, 1);
continue;
@@ -2114,7 +2114,7 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first)
state.opt = 0;
state.argbeg = state.optbeg = state.inopt = cur;
state.argend = argend;
- doff = state.doff = 0;
+ doff = 0;
state.singles = 1;
if (!state.oargs[state.curopt->num])
state.oargs[state.curopt->num] = znewlinklist();
@@ -2287,7 +2287,6 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first)
memcpy(&ca_laststate, &state, sizeof(state));
ca_laststate.ddef = NULL;
ca_laststate.dopt = NULL;
- ca_laststate.doff = 0;
break;
}
zaddlinknode(state.args, ztrdup(line));
@@ -2324,7 +2323,6 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first)
ca_laststate.ddef = NULL;
ca_laststate.dopt = NULL;
- ca_laststate.doff = 0;
break;
}
} else if (state.def && state.def->end)
@@ -2338,7 +2336,6 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first)
memcpy(&ca_laststate, &state, sizeof(state));
ca_laststate.ddef = NULL;
ca_laststate.dopt = NULL;
- ca_laststate.doff = 0;
} else if (cur == compcurrent && !ca_laststate.def) {
if ((ca_laststate.def = ddef)) {
ca_laststate.singles = state.singles;
@@ -2349,7 +2346,7 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first)
ca_laststate.opt = 1;
state.curopt->active = 1;
} else {
- ca_laststate.doff = doff;
+ ca_doff = doff;
ca_laststate.opt = 0;
}
} else {
@@ -2452,11 +2449,16 @@ ca_set_data(LinkList descr, LinkList act, LinkList subc,
!strcmp((char *) getdata(anode), arg->action))
break;
+ /* with an ignored prefix, we're not completing any normal arguments */
+ if (single && !arg->opt)
+ return;
+
if (!dnode) {
addlinknode(descr, arg->descr);
addlinknode(act, arg->action);
if (!restr) {
+
if ((restr = (arg->type == CAA_RARGS)))
restrict_range(ca_laststate.optbeg, ca_laststate.argend);
else if ((restr = (arg->type == CAA_RREST)))
@@ -2491,8 +2493,11 @@ ca_set_data(LinkList descr, LinkList act, LinkList subc,
* the case above right.
*/
if (arg->type == CAA_NORMAL &&
- opt && optdef && optdef->type == CAO_NEXT)
+ opt && optdef &&
+ (optdef->type == CAO_NEXT || optdef->type == CAO_ODIRECT ||
+ optdef->type == CAO_OEQUAL))
return;
+
if (single)
break;
@@ -2521,7 +2526,6 @@ ca_set_data(LinkList descr, LinkList act, LinkList subc,
if (!single && opt && (lopt || ca_laststate.oopt)) {
opt = NULL;
arg = ca_get_arg(ca_laststate.d, ca_laststate.nth);
-
goto rec;
}
if (!opt && oopt > 0) {
@@ -2590,6 +2594,7 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
multi = !!def->snext; /* if we have sets */
ca_parsed = cap;
+ ca_doff = 0;
while (def) { /* for each set */
use = !ca_parse_line(def, all, multi, first);
@@ -2629,23 +2634,20 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
{
LinkList descr, act, subc;
Caarg arg;
- int ign = 0, ret = 1;
+ int ret = 1;
descr = newlinklist();
act = newlinklist();
subc = newlinklist();
+ ignore_prefix(ca_doff);
while (lstate) {
arg = lstate->def;
if (arg) {
ret = 0;
- if (!ign && lstate->doff > 0) {
- ign = 1;
- ignore_prefix(lstate->doff);
- }
ca_set_data(descr, act, subc, arg->opt, arg,
- lstate->curopt, (lstate->doff > 0));
+ lstate->curopt, (ca_doff > 0));
}
lstate = lstate->snext;
}
@@ -2673,7 +2675,7 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
for (; lstate; lstate = lstate->snext) {
if (lstate->actopts &&
- (lstate->opt || (lstate->doff && lstate->def) ||
+ (lstate->opt || lstate->def ||
(lstate->def && lstate->def->opt &&
(lstate->def->type == CAA_OPT ||
(lstate->def->type >= CAA_RARGS &&
diff --git a/Src/Zle/textobjects.c b/Src/Zle/textobjects.c
index bf83906f2..c93777b65 100644
--- a/Src/Zle/textobjects.c
+++ b/Src/Zle/textobjects.c
@@ -30,13 +30,6 @@
#include "zle.mdh"
#include "textobjects.pro"
-/* class of character: 0 is whitespace, 1 is word character, 2 is other */
-static int
-wordclass(ZLE_CHAR_T x)
-{
- return (ZC_iblank(x) ? 0 : ((ZC_ialnum(x) || (ZWC('_') == x)) ? 1 : 2));
-}
-
static int
blankwordclass(ZLE_CHAR_T x)
{
diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h
index 07b310180..8261da92b 100644
--- a/Src/Zle/zle.h
+++ b/Src/Zle/zle.h
@@ -67,6 +67,7 @@ typedef wint_t ZLE_INT_T;
#define ZC_inblank iswspace
#define ZC_iupper iswupper
#define ZC_iword(x) wcsitype((x), IWORD)
+#define ZC_ipunct iswpunct
#define ZC_tolower towlower
#define ZC_toupper towupper
@@ -153,6 +154,7 @@ static inline int ZS_strncmp(ZLE_STRING_T s1, ZLE_STRING_T s2, size_t l)
#define ZC_inblank inblank
#define ZC_iupper isupper
#define ZC_iword iword
+#define ZC_ipunct ispunct
#define ZC_tolower tulower
#define ZC_toupper tuupper
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index f7e9829c2..5601c1178 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -731,6 +731,7 @@ bin_zle_call(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
break;
}
while (*++(*args)) {
+ char skip_this_arg[2] = "x";
switch (**args) {
case 'n':
num = args[0][1] ? args[0]+1 : args[1];
@@ -741,7 +742,7 @@ bin_zle_call(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
return 1;
}
if (!args[0][1])
- *++args = "" - 1;
+ *++args = skip_this_arg;
saveflag = 1;
zmod.mult = atoi(num);
zmod.flags |= MOD_MULT;
@@ -760,7 +761,7 @@ bin_zle_call(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
return 1;
}
if (!args[0][1])
- *++args = "" - 1;
+ *++args = skip_this_arg;
keymap_restore = dupstring(curkeymapname);
if (selectkeymap(keymap_tmp, 0)) {
if (remetafy)
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 5a9cccb6f..22c381237 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -456,6 +456,7 @@ checkparams(char *p)
static int
cmphaswilds(char *str)
{
+ char *ptr;
if ((*str == Inbrack || *str == Outbrack) && !str[1])
return 0;
@@ -465,6 +466,14 @@ cmphaswilds(char *str)
if (str[0] == '%' && str[1] ==Quest)
str += 2;
+ /*
+ * In ~[foo], the square brackets are not wild cards.
+ * This test matches the master one in filesubstr().
+ */
+ if (*str == Tilde && str[1] == Inbrack &&
+ (ptr = strchr(str+2, Outbrack)))
+ str = ptr + 1;
+
for (; *str;) {
if (*str == String || *str == Qstring) {
/* A parameter expression. */
@@ -2268,7 +2277,7 @@ doexpansion(char *s, int lst, int olst, int explincmd)
int ng = opts[NULLGLOB];
opts[NULLGLOB] = 1;
- globlist(vl, 1);
+ globlist(vl, PREFORK_NO_UNTOK);
opts[NULLGLOB] = ng;
}
if (errflag)
diff --git a/Src/Zle/zle_word.c b/Src/Zle/zle_word.c
index e4a878eab..89959b20c 100644
--- a/Src/Zle/zle_word.c
+++ b/Src/Zle/zle_word.c
@@ -64,7 +64,18 @@ forwardword(char **args)
return 0;
}
-#define Z_vialnum(X) (ZC_ialnum(X) || (ZWC('_') == X))
+/*
+ * class of character (for vi-mode word motion)
+ * 0: blank, 1: alnum or _, 2: punctuation, 3: the others
+ */
+
+/**/
+int
+wordclass(ZLE_CHAR_T x)
+{
+ return (ZC_iblank(x) ? 0 : ((ZC_ialnum(x) || (ZWC('_') == x)) ? 1 :
+ ZC_ipunct(x) ? 2 : 3));
+}
/**/
int
@@ -75,19 +86,16 @@ viforwardword(char **args)
if (n < 0) {
int ret;
zmult = -n;
- ret = backwardword(args);
+ ret = vibackwardword(args);
zmult = n;
return ret;
}
while (n--) {
int nl;
- if (Z_vialnum(zleline[zlecs]))
- while (zlecs != zlell && Z_vialnum(zleline[zlecs]))
- INCCS();
- else
- while (zlecs != zlell && !Z_vialnum(zleline[zlecs]) &&
- !ZC_inblank(zleline[zlecs]))
- INCCS();
+ int cc = wordclass(zleline[zlecs]);
+ while (zlecs != zlell && wordclass(zleline[zlecs]) == cc) {
+ INCCS();
+ }
if (wordflag && !n)
return 0;
nl = (zleline[zlecs] == ZWC('\n'));
@@ -160,7 +168,7 @@ viforwardblankwordend(char **args)
if (n < 0) {
int ret;
zmult = -n;
- ret = viforwardblankwordend(args);
+ ret = vibackwardblankwordend(args);
zmult = n;
return ret;
}
@@ -208,26 +216,17 @@ viforwardwordend(char **args)
zlecs = pos;
}
if (zlecs != zlell) {
+ int cc;
pos = zlecs;
INCPOS(pos);
- if (Z_vialnum(zleline[pos])) {
- for (;;) {
- zlecs = pos;
- if (zlecs == zlell)
+ cc = wordclass(zleline[pos]);
+ for (;;) {
+ zlecs = pos;
+ if (zlecs == zlell)
+ break;
+ INCPOS(pos);
+ if (wordclass(zleline[pos]) != cc)
break;
- INCPOS(pos);
- if (!Z_vialnum(zleline[pos]))
- break;
- }
- } else {
- for (;;) {
- zlecs = pos;
- if (zlecs == zlell)
- break;
- INCPOS(pos);
- if (Z_vialnum(zleline[pos]) || ZC_inblank(zleline[pos]))
- break;
- }
}
}
}
@@ -277,7 +276,7 @@ vibackwardword(char **args)
if (n < 0) {
int ret;
zmult = -n;
- ret = backwardword(args);
+ ret = viforwardword(args);
zmult = n;
return ret;
}
@@ -295,24 +294,14 @@ vibackwardword(char **args)
}
if (zlecs) {
int pos = zlecs;
- if (Z_vialnum(zleline[pos])) {
- for (;;) {
- zlecs = pos;
- if (zlecs == 0)
- break;
- DECPOS(pos);
- if (!Z_vialnum(zleline[pos]))
- break;
- }
- } else {
- for (;;) {
- zlecs = pos;
- if (zlecs == 0)
- break;
- DECPOS(pos);
- if (Z_vialnum(zleline[pos]) || ZC_inblank(zleline[pos]))
- break;
- }
+ int cc = wordclass(zleline[pos]);
+ for (;;) {
+ zlecs = pos;
+ if (zlecs == 0)
+ break;
+ DECPOS(pos);
+ if (wordclass(zleline[pos]) != cc || ZC_inblank(zleline[pos]))
+ break;
}
}
}
@@ -368,17 +357,10 @@ vibackwardwordend(char **args)
return ret;
}
while (n-- && zlecs > 1) {
- int start = 0;
- if (Z_vialnum(zleline[zlecs]))
- start = 1;
- else if (!ZC_inblank(zleline[zlecs]))
- start = 2;
+ int cc = wordclass(zleline[zlecs]);
DECCS();
while (zlecs) {
- int same = (start != 1) && ZC_iblank(zleline[zlecs]);
- if (start)
- same |= Z_vialnum(zleline[zlecs]);
- if (same == (start == 2))
+ if (wordclass(zleline[zlecs]) != cc || ZC_iblank(zleline[zlecs]))
break;
DECCS();
}
@@ -494,26 +476,17 @@ vibackwardkillword(UNUSED(char **args))
x = pos;
}
if (x > lim) {
+ int cc;
int pos = x;
DECPOS(pos);
- if (Z_vialnum(zleline[pos])) {
- for (;;) {
- x = pos;
- if (x <= lim)
- break;
- DECPOS(pos);
- if (!Z_vialnum(zleline[pos]))
- break;
- }
- } else {
- for (;;) {
- x = pos;
- if (x <= lim)
- break;
- DECPOS(pos);
- if (Z_vialnum(zleline[pos]) || ZC_iblank(zleline[pos]))
- break;
- }
+ cc = wordclass(zleline[pos]);
+ for (;;) {
+ x = pos;
+ if (x <= lim)
+ break;
+ DECPOS(pos);
+ if (wordclass(zleline[pos]) != cc)
+ break;
}
}
}
diff --git a/Src/builtin.c b/Src/builtin.c
index 2e72ba20a..fb59738f3 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -53,7 +53,7 @@ static struct builtin builtins[] =
BUILTIN("cd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL),
BUILTIN("chdir", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL),
BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL),
- BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmprtuxz", NULL),
+ BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmp:%rtuxz", NULL),
BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "clpv", NULL),
BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmprs", NULL),
BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL),
@@ -62,7 +62,7 @@ static struct builtin builtins[] =
BUILTIN("enable", 0, bin_enable, 0, -1, BIN_ENABLE, "afmprs", NULL),
BUILTIN("eval", BINF_PSPECIAL, bin_eval, 0, -1, BIN_EVAL, NULL, NULL),
BUILTIN("exit", BINF_PSPECIAL, bin_break, 0, 1, BIN_EXIT, NULL, NULL),
- BUILTIN("export", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%TUZ:%afhi:%lprtu", "xg"),
+ BUILTIN("export", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%TUZ:%afhi:%lp:%rtu", "xg"),
BUILTIN("false", 0, bin_false, 0, -1, 0, NULL, NULL),
/*
* We used to behave as if the argument to -e was optional.
@@ -71,7 +71,7 @@ static struct builtin builtins[] =
*/
BUILTIN("fc", 0, bin_fc, 0, -1, BIN_FC, "aAdDe:EfiIlLmnpPrRt:W", NULL),
BUILTIN("fg", 0, bin_fg, 0, -1, BIN_FG, NULL, NULL),
- BUILTIN("float", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%Z:%ghlprtux", "E"),
+ BUILTIN("float", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%Z:%ghlp:%rtux", "E"),
BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "kmMstTuUWx:z", NULL),
BUILTIN("getln", 0, bin_read, 0, -1, 0, "ecnAlE", "zr"),
BUILTIN("getopts", 0, bin_getopts, 2, -1, 0, NULL, NULL),
@@ -82,11 +82,11 @@ static struct builtin builtins[] =
#endif
BUILTIN("history", 0, bin_fc, 0, -1, BIN_FC, "adDEfiLmnpPrt:", "l"),
- BUILTIN("integer", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "HL:%R:%Z:%ghi:%lprtux", "i"),
+ BUILTIN("integer", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "HL:%R:%Z:%ghi:%lp:%rtux", "i"),
BUILTIN("jobs", 0, bin_fg, 0, -1, BIN_JOBS, "dlpZrs", NULL),
BUILTIN("kill", BINF_HANDLES_OPTS, bin_kill, 0, -1, 0, NULL, NULL),
BUILTIN("let", 0, bin_let, 1, -1, 0, NULL, NULL),
- BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lprtux", NULL),
+ BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lp:%rtux", NULL),
BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL),
BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL),
@@ -120,12 +120,12 @@ static struct builtin builtins[] =
BUILTIN("trap", BINF_PSPECIAL | BINF_HANDLES_OPTS, bin_trap, 0, -1, 0, NULL, NULL),
BUILTIN("true", 0, bin_true, 0, -1, 0, NULL, NULL),
BUILTIN("type", 0, bin_whence, 0, -1, 0, "ampfsSw", "v"),
- BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klprtuxmz", NULL),
+ BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klp:%rtuxmz", NULL),
BUILTIN("umask", 0, bin_umask, 0, 1, 0, "S", NULL),
BUILTIN("unalias", 0, bin_unhash, 0, -1, BIN_UNALIAS, "ams", NULL),
BUILTIN("unfunction", 0, bin_unhash, 1, -1, BIN_UNFUNCTION, "m", "f"),
BUILTIN("unhash", 0, bin_unhash, 1, -1, BIN_UNHASH, "adfms", NULL),
- BUILTIN("unset", BINF_PSPECIAL, bin_unset, 1, -1, 0, "fmv", NULL),
+ BUILTIN("unset", BINF_PSPECIAL, bin_unset, 1, -1, BIN_UNSET, "fmv", NULL),
BUILTIN("unsetopt", 0, bin_setopt, 0, -1, BIN_UNSETOPT, NULL, NULL),
BUILTIN("wait", 0, bin_fg, 0, -1, BIN_WAIT, NULL, NULL),
BUILTIN("whence", 0, bin_whence, 0, -1, 0, "acmpvfsSwx:", NULL),
@@ -450,15 +450,35 @@ execbuiltin(LinkList args, LinkList assigns, Builtin bn)
Asgment asg = (Asgment)node;
fputc(' ', xtrerr);
quotedzputs(asg->name, xtrerr);
- if (asg->is_array) {
- LinkNode arrnode;
+ if (asg->flags & ASG_ARRAY) {
fprintf(xtrerr, "=(");
if (asg->value.array) {
- for (arrnode = firstnode(asg->value.array);
- arrnode;
- incnode(arrnode)) {
- fputc(' ', xtrerr);
- quotedzputs((char *)getdata(arrnode), xtrerr);
+ if (asg->flags & ASG_KEY_VALUE) {
+ LinkNode keynode, valnode;
+ keynode = firstnode(asg->value.array);
+ for (;;) {
+ if (!keynode)
+ break;
+ valnode = nextnode(keynode);
+ if (!valnode)
+ break;
+ fputc('[', xtrerr);
+ quotedzputs((char *)getdata(keynode),
+ xtrerr);
+ fprintf(stderr, "]=");
+ quotedzputs((char *)getdata(valnode),
+ xtrerr);
+ keynode = nextnode(valnode);
+ }
+ } else {
+ LinkNode arrnode;
+ for (arrnode = firstnode(asg->value.array);
+ arrnode;
+ incnode(arrnode)) {
+ fputc(' ', xtrerr);
+ quotedzputs((char *)getdata(arrnode),
+ xtrerr);
+ }
}
}
fprintf(xtrerr, " )");
@@ -1519,7 +1539,7 @@ bin_fc(char *nam, char **argv, Options ops, int func)
asgl = a;
}
a->name = *argv;
- a->is_array = 0;
+ a->flags = 0;
a->value.scalar = s;
a->node.next = a->node.prev = NULL;
argv++;
@@ -1910,7 +1930,7 @@ getasg(char ***argvp, LinkList assigns)
return NULL;
}
asg.name = s;
- asg.is_array = 0;
+ asg.flags = 0;
/* search for `=' */
for (; *s && *s != '='; s++);
@@ -2171,7 +2191,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
* ii. we are creating a new local parameter
*/
if (usepm) {
- if (asg->is_array ?
+ if ((asg->flags & ASG_ARRAY) ?
!(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) :
(asg->value.scalar && (PM_TYPE(pm->node.flags &
(PM_ARRAY|PM_HASHED))))) {
@@ -2241,10 +2261,11 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
if (asg->value.scalar &&
!(pm = assignsparam(pname, ztrdup(asg->value.scalar), 0)))
return NULL;
- } else if (asg->is_array) {
+ } else if (asg->flags & ASG_ARRAY) {
+ int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0;
if (!(pm = assignaparam(pname, asg->value.array ?
zlinklist2array(asg->value.array) :
- mkarray(NULL), 0)))
+ mkarray(NULL), flags)))
return NULL;
}
if (errflag)
@@ -2255,7 +2276,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
return pm;
}
- if (asg->is_array ?
+ if ((asg->flags & ASG_ARRAY) ?
!(on & (PM_ARRAY|PM_HASHED)) :
(asg->value.scalar && (on & (PM_ARRAY|PM_HASHED)))) {
zerrnam(cname, "%s: inconsistent type for assignment", pname);
@@ -2287,7 +2308,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
*/
if (!ASG_VALUEP(asg) && !((pm->node.flags|on) & (PM_ARRAY|PM_HASHED))) {
asg->value.scalar = dupstring(getsparam(pname));
- asg->is_array = 0;
+ asg->flags = 0;
}
/* pname may point to pm->nam which is about to disappear */
pname = dupstring(pname);
@@ -2396,13 +2417,14 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
ztrdup(asg->value.scalar ? asg->value.scalar : ""), 0)))
return NULL;
dont_set = 1;
- asg->is_array = 0;
+ asg->flags = 0;
keeplocal = 0;
on = pm->node.flags;
} else if (PM_TYPE(on) == PM_ARRAY && ASG_ARRAYP(asg)) {
+ int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0;
if (!(pm = assignaparam(pname, asg->value.array ?
zlinklist2array(asg->value.array) :
- mkarray(NULL), 0)))
+ mkarray(NULL), flags)))
return NULL;
dont_set = 1;
keeplocal = 0;
@@ -2479,6 +2501,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
Param ipm = pm;
if (pm->node.flags & (PM_ARRAY|PM_HASHED)) {
char **arrayval;
+ int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0;
if (!ASG_ARRAYP(asg)) {
/*
* Attempt to assign a scalar value to an array.
@@ -2497,7 +2520,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
arrayval = zlinklist2array(asg->value.array);
else
arrayval = mkarray(NULL);
- if (!(pm=assignaparam(pname, arrayval, 0)))
+ if (!(pm=assignaparam(pname, arrayval, flags)))
return NULL;
} else {
DPUTS(ASG_ARRAYP(asg), "BUG: inconsistent array value for scalar");
@@ -2622,8 +2645,21 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
queue_signals();
/* Given no arguments, list whatever the options specify. */
- if (OPT_ISSET(ops,'p'))
+ if (OPT_ISSET(ops,'p')) {
printflags |= PRINT_TYPESET;
+ if (OPT_HASARG(ops,'p')) {
+ char *eptr;
+ int pflag = (int)zstrtol(OPT_ARG(ops,'p'), &eptr, 10);
+ if (pflag == 1 && !*eptr)
+ printflags |= PRINT_LINE;
+ else if (pflag || *eptr) {
+ zwarnnam(name, "bad argument to -p: %s", OPT_ARG(ops,'p'));
+ unqueue_signals();
+ return 1;
+ }
+ /* -p0 treated as -p for consistency */
+ }
+ }
hasargs = *argv != NULL || (assigns && firstnode(assigns));
if (!hasargs) {
if (!OPT_ISSET(ops,'p')) {
@@ -2750,13 +2786,15 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
* Already tied in the fashion requested.
*/
struct tieddata *tdp = (struct tieddata*)pm->u.data;
+ int flags = (asg->flags & ASG_KEY_VALUE) ?
+ ASSPM_KEY_VALUE : 0;
/* Update join character */
tdp->joinchar = joinchar;
if (asg0.value.scalar)
assignsparam(asg0.name, ztrdup(asg0.value.scalar), 0);
else if (asg->value.array)
assignaparam(
- asg->name, zlinklist2array(asg->value.array), 0);
+ asg->name, zlinklist2array(asg->value.array),flags);
return 0;
} else {
zwarnnam(name, "can't tie already tied scalar: %s",
@@ -2778,7 +2816,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
* to be exported properly.
*/
asg2.name = asg->name;
- asg2.is_array = 0;
+ asg2.flags = 0;
asg2.value.array = (LinkList)0;
if (!(apm=typeset_single(name, asg->name,
(Param)paramtab->getnode(paramtab,
@@ -2816,9 +2854,10 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
if (apm->ename)
zsfree(apm->ename);
apm->ename = ztrdup(asg0.name);
- if (asg->value.array)
- assignaparam(asg->name, zlinklist2array(asg->value.array), 0);
- else if (oldval)
+ if (asg->value.array) {
+ int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0;
+ assignaparam(asg->name, zlinklist2array(asg->value.array), flags);
+ } else if (oldval)
assignsparam(asg0.name, oldval, 0);
unqueue_signals();
@@ -4119,6 +4158,10 @@ bin_unhash(char *name, char **argv, Options ops, int func)
for (; *argv; argv++) {
if ((hn = ht->removenode(ht, *argv))) {
ht->freenode(hn);
+ } else if (func == BIN_UNSET && isset(POSIXBUILTINS)) {
+ /* POSIX: unset: "Unsetting a variable or function that was *
+ * not previously set shall not be considered an error." */
+ returnval = 0;
} else {
zwarnnam(name, "no such hash table element: %s", *argv);
returnval = 1;
@@ -5200,7 +5243,10 @@ bin_print(char *name, char **args, Options ops, int func)
*d++ = 'l';
#endif
*d++ = 'l', *d++ = *c, *d = '\0';
- zulongval = (curarg) ? mathevali(curarg) : 0;
+ if (!curarg)
+ zulongval = (zulong)0;
+ else if (!zstrtoul_underscore(curarg, &zulongval))
+ zulongval = mathevali(curarg);
if (errflag) {
zulongval = 0;
errflag &= ~ERRFLAG_ERROR;
@@ -5399,8 +5445,9 @@ bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int fun
if(quiet) {
zoptarg = metafy(optbuf, lenoptbuf, META_DUP);
} else {
- zwarn(*p == '?' ? "bad option: -%c" :
- "argument expected after -%c option", opch);
+ zwarn(*p == '?' ? "bad option: %c%c" :
+ "argument expected after %c%c option",
+ "?-+"[lenoptbuf], opch);
zoptarg=ztrdup("");
}
return 0;
@@ -5550,7 +5597,8 @@ checkjobs(void)
for (i = 1; i <= maxjob; i++)
if (i != thisjob && (jobtab[i].stat & STAT_LOCKED) &&
- !(jobtab[i].stat & STAT_NOPRINT))
+ !(jobtab[i].stat & STAT_NOPRINT) &&
+ (isset(CHECKRUNNINGJOBS) || jobtab[i].stat & STAT_STOPPED))
break;
if (i <= maxjob) {
if (jobtab[i].stat & STAT_STOPPED) {
@@ -5905,7 +5953,7 @@ bin_emulate(char *nam, char **argv, Options ops, UNUSED(int func))
savehackchar = keyboardhackchar;
emulate(shname, opt_R, &new_emulation, new_opts);
optlist = newlinklist();
- if (parseopts(nam, &argv, new_opts, &cmd, optlist)) {
+ if (parseopts(nam, &argv, new_opts, &cmd, optlist, 0)) {
ret = 1;
goto restore;
}
diff --git a/Src/cond.c b/Src/cond.c
index b9a47cea5..9f13e07d7 100644
--- a/Src/cond.c
+++ b/Src/cond.c
@@ -61,7 +61,8 @@ static void cond_subst(char **strp, int glob_ok)
* of functionality.
*
* Return status is the final shell status, i.e. 0 for true,
- * 1 for false and 2 for error.
+ * 1 for false, 2 for syntax error, 3 for "option in tested in
+ * -o does not exist".
*/
/**/
@@ -86,10 +87,10 @@ evalcond(Estate state, char *fromtest)
if (tracingcond)
fprintf(xtrerr, " %s", condstr[ctype]);
ret = evalcond(state, fromtest);
- if (ret == 2)
- return ret;
- else
+ if (ret == 0 || ret == 1)
return !ret;
+ else
+ return ret;
case COND_AND:
if (!(ret = evalcond(state, fromtest))) {
if (tracingcond)
@@ -100,7 +101,8 @@ evalcond(Estate state, char *fromtest)
return ret;
}
case COND_OR:
- if ((ret = evalcond(state, fromtest)) == 1) {
+ ret = evalcond(state, fromtest);
+ if (ret == 1 || ret == 3) {
if (tracingcond)
fprintf(xtrerr, " %s", condstr[ctype]);
goto rec;
@@ -506,8 +508,12 @@ optison(char *name, char *s)
else
i = optlookup(s);
if (!i) {
- zwarnnam(name, "no such option: %s", s);
- return 2;
+ if (isset(POSIXBUILTINS))
+ return 1;
+ else {
+ zwarnnam(name, "no such option: %s", s);
+ return 3;
+ }
} else if(i < 0)
return !unset(-i);
else
diff --git a/Src/exec.c b/Src/exec.c
index cd99733f1..e154d1249 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -41,6 +41,20 @@ enum {
ADDVAR_RESTORE = 1 << 2
};
+/* Structure in which to save values around shell function call */
+
+struct funcsave {
+ char opts[OPT_SIZE];
+ char *argv0;
+ int zoptind, lastval, optcind, numpipestats;
+ int *pipestats;
+ char *scriptname;
+ int breaks, contflag, loops, emulation, noerrexit, oflags, restore_sticky;
+ Emulation_options sticky;
+ struct funcstack fstack;
+};
+typedef struct funcsave *Funcsave;
+
/*
* used to suppress ERREXIT and trapping of SIGZERR, SIGEXIT.
* Bits from noerrexit_bits.
@@ -920,7 +934,7 @@ hashcmd(char *arg0, char **pp)
for (; *pp; pp++)
if (**pp == '/') {
s = buf;
- strucpy(&s, *pp);
+ struncpy(&s, *pp, PATH_MAX);
*s++ = '/';
if ((s - buf) + strlen(arg0) >= PATH_MAX)
continue;
@@ -1305,7 +1319,9 @@ execlist(Estate state, int dont_change_job, int exiting)
noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN;
if (ltype & Z_SIMPLE) /* skip the line number */
pc2++;
- pm = setsparam("ZSH_DEBUG_CMD", getpermtext(state->prog, pc2, 0));
+ pm = assignsparam("ZSH_DEBUG_CMD",
+ getpermtext(state->prog, pc2, 0),
+ 0);
exiting = donetrap;
ret = lastval;
@@ -2325,16 +2341,19 @@ addfd(int forked, int *save, struct multio **mfds, int fd1, int fd2, int rflag,
* fd1 may already be closed here, so
* ignore bad file descriptor error
*/
- if (fdN < 0 && errno != EBADF) {
- zerr("cannot duplicate fd %d: %e", fd1, errno);
- mfds[fd1] = NULL;
- closemnodes(mfds);
- return;
+ if (fdN < 0) {
+ if (errno != EBADF) {
+ zerr("cannot duplicate fd %d: %e", fd1, errno);
+ mfds[fd1] = NULL;
+ closemnodes(mfds);
+ return;
+ }
+ } else {
+ DPUTS(fdtable[fdN] != FDT_INTERNAL,
+ "Saved file descriptor not marked as internal");
+ fdtable[fdN] |= FDT_SAVED_MASK;
}
save[fd1] = fdN;
- DPUTS(fdtable[fdN] != FDT_INTERNAL,
- "Saved file descriptor not marked as internal");
- fdtable[fdN] |= FDT_SAVED_MASK;
}
}
}
@@ -2428,29 +2447,37 @@ addvars(Estate state, Wordcode pc, int addflags)
if ((isstr = (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR))) {
init_list1(svl, ecgetstr(state, EC_DUPTOK, &htok));
vl = &svl;
- } else
+ } else {
vl = ecgetlist(state, WC_ASSIGN_NUM(ac), EC_DUPTOK, &htok);
+ if (errflag) {
+ state->pc = opc;
+ return;
+ }
+ }
if (vl && htok) {
+ int prefork_ret = 0;
prefork(vl, (isstr ? (PREFORK_SINGLE|PREFORK_ASSIGN) :
- PREFORK_ASSIGN), NULL);
+ PREFORK_ASSIGN), &prefork_ret);
if (errflag) {
state->pc = opc;
return;
}
+ if (prefork_ret & PREFORK_KEY_VALUE)
+ myflags |= ASSPM_KEY_VALUE;
if (!isstr || (isset(GLOBASSIGN) && isstr &&
haswilds((char *)getdata(firstnode(vl))))) {
- globlist(vl, 0);
+ globlist(vl, prefork_ret);
/* Unset the parameter to force it to be recreated
* as either scalar or array depending on how many
* matches were found for the glob.
*/
if (isset(GLOBASSIGN) && isstr)
- unsetparam(name);
- }
- if (errflag) {
- state->pc = opc;
- return;
+ unsetparam(name);
+ if (errflag) {
+ state->pc = opc;
+ return;
+ }
}
}
if (isstr && (empty(vl) || !nextnode(firstnode(vl)))) {
@@ -3007,6 +3034,9 @@ execcmd_exec(Estate state, Execcmd_params eparams,
}
if (exec_argv0) {
char *str, *s;
+ exec_argv0 = dupstring(exec_argv0);
+ remnulargs(exec_argv0);
+ untokenize(exec_argv0);
size_t sz = strlen(exec_argv0);
str = s = zalloc(5 + 1 + sz + 1);
strcpy(s, "ARGV0=");
@@ -3025,7 +3055,7 @@ execcmd_exec(Estate state, Execcmd_params eparams,
preargs = NULL;
/* if we get this far, it is OK to pay attention to lastval again */
- if ((noerrexit & NOERREXIT_UNTIL_EXEC) && !is_shfunc)
+ if (noerrexit & NOERREXIT_UNTIL_EXEC)
noerrexit = 0;
/* Do prefork substitutions.
@@ -3188,7 +3218,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
}
if (errflag) {
- lastval = 1;
+ if (!lastval)
+ lastval = 1;
if (oautocont >= 0)
opts[AUTOCONTINUE] = oautocont;
return;
@@ -3222,19 +3253,24 @@ execcmd_exec(Estate state, Execcmd_params eparams,
next = nextnode(node);
if (s[0] == Star && !s[1]) {
- if (!checkrmall(pwd))
- uremnode(args, node);
+ if (!checkrmall(pwd)) {
+ errflag |= ERRFLAG_ERROR;
+ break;
+ }
} else if (l >= 2 && s[l - 2] == '/' && s[l - 1] == Star) {
char t = s[l - 2];
+ int rmall;
s[l - 2] = 0;
- if (!checkrmall(*s ? s : "/"))
- uremnode(args, node);
+ rmall = checkrmall(s);
s[l - 2] = t;
+
+ if (!rmall) {
+ errflag |= ERRFLAG_ERROR;
+ break;
+ }
}
}
- if (!nextnode(firstnode(args)))
- errflag |= ERRFLAG_ERROR;
}
if (type == WC_FUNCDEF) {
@@ -3911,7 +3947,7 @@ execcmd_exec(Estate state, Execcmd_params eparams,
while ((data = ugetnode(&svl))) {
char *ptr;
asg = (Asgment)zhalloc(sizeof(struct asgment));
- asg->is_array = 0;
+ asg->flags = 0;
if ((ptr = strchr(data, '='))) {
*ptr++ = '\0';
asg->name = data;
@@ -3933,7 +3969,7 @@ execcmd_exec(Estate state, Execcmd_params eparams,
asg->name = name;
if (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR) {
char *val = ecgetstr(state, EC_DUPTOK, &htok);
- asg->is_array = 0;
+ asg->flags = 0;
if (WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC) {
/* Fake assignment, no value */
asg->value.scalar = NULL;
@@ -3958,18 +3994,24 @@ execcmd_exec(Estate state, Execcmd_params eparams,
asg->value.scalar = val;
}
} else {
- asg->is_array = 1;
+ asg->flags = ASG_ARRAY;
asg->value.array =
ecgetlist(state, WC_ASSIGN_NUM(ac),
EC_DUPTOK, &htok);
if (asg->value.array)
{
- prefork(asg->value.array, PREFORK_ASSIGN, NULL);
- if (errflag) {
- state->pc = opc;
- break;
+ if (!errflag) {
+ int prefork_ret = 0;
+ prefork(asg->value.array, PREFORK_ASSIGN,
+ &prefork_ret);
+ if (errflag) {
+ state->pc = opc;
+ break;
+ }
+ if (prefork_ret & PREFORK_KEY_VALUE)
+ asg->flags |= ASG_KEY_VALUE;
+ globlist(asg->value.array, prefork_ret);
}
- globlist(asg->value.array, 0);
if (errflag) {
state->pc = opc;
break;
@@ -3982,8 +4024,15 @@ execcmd_exec(Estate state, Execcmd_params eparams,
state->pc = opc;
}
dont_queue_signals();
- if (!errflag)
- lastval = execbuiltin(args, assigns, (Builtin) hn);
+ if (!errflag) {
+ int ret = execbuiltin(args, assigns, (Builtin) hn);
+ /*
+ * In case of interruption assume builtin status
+ * is less useful than what interrupt set.
+ */
+ if (!(errflag & ERRFLAG_INT))
+ lastval = ret;
+ }
if (do_save & BINF_COMMAND)
errflag &= ~ERRFLAG_ERROR;
restore_queue_signals(q);
@@ -4338,8 +4387,17 @@ gethere(char **strp, int typ)
bptr = buf + bsiz;
bsiz *= 2;
}
- if (lexstop || c == '\n')
+ if (lexstop)
break;
+ if (c == '\n') {
+ if (!qt && bptr > t && *(bptr - 1) == '\\') {
+ /* line continuation */
+ bptr--;
+ c = hgetc();
+ continue;
+ } else
+ break;
+ }
*bptr++ = c;
c = hgetc();
}
@@ -4438,12 +4496,19 @@ getoutput(char *cmd, int qt)
pid_t pid;
char *s;
- if (!(prog = parse_string(cmd, 0)))
+ int onc = nocomments;
+ nocomments = (interact && unset(INTERACTIVECOMMENTS));
+ prog = parse_string(cmd, 0);
+ nocomments = onc;
+
+ if (!prog)
return NULL;
if ((s = simple_redir_name(prog, REDIR_READ))) {
/* $(< word) */
int stream;
+ LinkList retval;
+ int readerror;
singsub(&s);
if (errflag)
@@ -4451,9 +4516,15 @@ getoutput(char *cmd, int qt)
untokenize(s);
if ((stream = open(unmeta(s), O_RDONLY | O_NOCTTY)) == -1) {
zwarn("%e: %s", errno, s);
+ lastval = cmdoutval = 1;
return newlinklist();
}
- return readoutput(stream, qt);
+ retval = readoutput(stream, qt, &readerror);
+ if (readerror) {
+ zwarn("error when reading %s: %e", s, readerror);
+ lastval = cmdoutval = 1;
+ }
+ return retval;
}
if (mpipe(pipes) < 0) {
errflag |= ERRFLAG_ERROR;
@@ -4474,7 +4545,7 @@ getoutput(char *cmd, int qt)
LinkList retval;
zclose(pipes[1]);
- retval = readoutput(pipes[0], qt);
+ retval = readoutput(pipes[0], qt, NULL);
fdtable[pipes[0]] = FDT_UNUSED;
waitforpid(pid, 0); /* unblocks */
lastval = cmdoutval;
@@ -4499,7 +4570,7 @@ getoutput(char *cmd, int qt)
/**/
mod_export LinkList
-readoutput(int in, int qt)
+readoutput(int in, int qt, int *readerror)
{
LinkList ret;
char *buf, *ptr;
@@ -4528,6 +4599,8 @@ readoutput(int in, int qt)
}
*ptr++ = c;
}
+ if (readerror)
+ *readerror = ferror(fin) ? errno : 0;
fclose(fin);
while (cnt && ptr[-1] == '\n')
ptr--, cnt--;
@@ -5037,7 +5110,11 @@ execfuncdef(Estate state, Eprog redir_prog)
shf->node.flags = 0;
/* No dircache here, not a directory */
shf->filename = ztrdup(scriptfilename);
- shf->lineno = lineno;
+ shf->lineno =
+ (funcstack && (funcstack->tp == FS_FUNC ||
+ funcstack->tp == FS_EVAL)) ?
+ funcstack->flineno + lineno :
+ lineno;
/*
* redir_prog is permanently allocated --- but if
* this function has multiple names we need an additional
@@ -5057,6 +5134,7 @@ execfuncdef(Estate state, Eprog redir_prog)
LinkList args;
anon_func = 1;
+ shf->node.flags |= PM_ANONYMOUS;
state->pc = end;
end += *state->pc++;
@@ -5462,36 +5540,36 @@ int sticky_emulation_differs(Emulation_options sticky2)
mod_export int
doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
{
- char **pptab, **x, *oargv0;
- int oldzoptind, oldlastval, oldoptcind, oldnumpipestats, ret;
- int *oldpipestats = NULL;
- char saveopts[OPT_SIZE], *oldscriptname = scriptname;
+ char **pptab, **x;
+ int ret;
char *name = shfunc->node.nam;
- int flags = shfunc->node.flags, ooflags;
- int savnoerrexit;
+ int flags = shfunc->node.flags;
char *fname = dupstring(name);
- int obreaks, ocontflag, oloops, saveemulation, restore_sticky;
Eprog prog;
- struct funcstack fstack;
static int oflags;
- Emulation_options save_sticky = NULL;
-#ifdef MAX_FUNCTION_DEPTH
static int funcdepth;
-#endif
Heap funcheap;
queue_signals(); /* Lots of memory and global state changes coming */
NEWHEAPS(funcheap) {
- oargv0 = NULL;
- obreaks = breaks;
- ocontflag = contflag;
- oloops = loops;
+ /*
+ * Save data in heap rather than on stack to keep recursive
+ * function cost down --- use of heap memory should be efficient
+ * at this point. Saving is not actually massive.
+ */
+ Funcsave funcsave = zhalloc(sizeof(struct funcsave));
+ funcsave->scriptname = scriptname;
+ funcsave->argv0 = NULL;
+ funcsave->breaks = breaks;
+ funcsave->contflag = contflag;
+ funcsave->loops = loops;
+ funcsave->lastval = lastval;
+ funcsave->pipestats = NULL;
+ funcsave->numpipestats = numpipestats;
+ funcsave->noerrexit = noerrexit;
if (trap_state == TRAP_STATE_PRIMED)
trap_return--;
- oldlastval = lastval;
- oldnumpipestats = numpipestats;
- savnoerrexit = noerrexit;
/*
* Suppression of ERR_RETURN is turned off in function scope.
*/
@@ -5502,8 +5580,8 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
* immediately by a pushheap/popheap pair.
*/
size_t bytes = sizeof(int)*numpipestats;
- oldpipestats = (int *)zhalloc(bytes);
- memcpy(oldpipestats, pipestats, bytes);
+ funcsave->pipestats = (int *)zhalloc(bytes);
+ memcpy(funcsave->pipestats, pipestats, bytes);
}
starttrapscope();
@@ -5512,8 +5590,8 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
pptab = pparams;
if (!(flags & PM_UNDEFINED))
scriptname = dupstring(name);
- oldzoptind = zoptind;
- oldoptcind = optcind;
+ funcsave->zoptind = zoptind;
+ funcsave->optcind = optcind;
if (!isset(POSIXBUILTINS)) {
zoptind = 1;
optcind = 0;
@@ -5522,9 +5600,9 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
/* We need to save the current options even if LOCALOPTIONS is *
* not currently set. That's because if it gets set in the *
* function we need to restore the original options on exit. */
- memcpy(saveopts, opts, sizeof(opts));
- saveemulation = emulation;
- save_sticky = sticky;
+ memcpy(funcsave->opts, opts, sizeof(opts));
+ funcsave->emulation = emulation;
+ funcsave->sticky = sticky;
if (sticky_emulation_differs(shfunc->sticky)) {
/*
@@ -5541,7 +5619,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
*/
sticky = sticky_emulation_dup(shfunc->sticky, 1);
emulation = sticky->emulation;
- restore_sticky = 1;
+ funcsave->restore_sticky = 1;
installemulation(emulation, opts);
if (sticky->n_on_opts) {
OptIndex *onptr;
@@ -5560,7 +5638,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
/* All emulations start with pattern disables clear */
clearpatterndisables();
} else
- restore_sticky = 0;
+ funcsave->restore_sticky = 0;
if (flags & (PM_TAGGED|PM_TAGGED_LOCAL))
opts[XTRACE] = 1;
@@ -5578,11 +5656,11 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
else
opts[WARNNESTEDVAR] = 0;
}
- ooflags = oflags;
+ funcsave->oflags = oflags;
/*
* oflags is static, because we compare it on the next recursive
- * call. Hence also we maintain ooflags for restoring the previous
- * value of oflags after the call.
+ * call. Hence also we maintain a saved version for restoring
+ * the previous value of oflags after the call.
*/
oflags = flags;
opts[PRINTEXITVALUE] = 0;
@@ -5593,7 +5671,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
pparams = x = (char **) zshcalloc(((sizeof *x) *
(1 + countlinknodes(doshargs))));
if (isset(FUNCTIONARGZERO)) {
- oargv0 = argzero;
+ funcsave->argv0 = argzero;
argzero = ztrdup(getdata(node));
}
/* first node contains name regardless of option */
@@ -5603,32 +5681,31 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
} else {
pparams = (char **) zshcalloc(sizeof *pparams);
if (isset(FUNCTIONARGZERO)) {
- oargv0 = argzero;
+ funcsave->argv0 = argzero;
argzero = ztrdup(argzero);
}
}
-#ifdef MAX_FUNCTION_DEPTH
- if(++funcdepth > MAX_FUNCTION_DEPTH)
- {
- zerr("maximum nested function level reached");
- goto undoshfunc;
- }
-#endif
- fstack.name = dupstring(name);
+ ++funcdepth;
+ if (zsh_funcnest >= 0 && funcdepth > zsh_funcnest) {
+ zerr("maximum nested function level reached; increase FUNCNEST?");
+ lastval = 1;
+ goto undoshfunc;
+ }
+ funcsave->fstack.name = dupstring(name);
/*
* The caller is whatever is immediately before on the stack,
* unless we're at the top, in which case it's the script
* or interactive shell name.
*/
- fstack.caller = funcstack ? funcstack->name :
- dupstring(oargv0 ? oargv0 : argzero);
- fstack.lineno = lineno;
- fstack.prev = funcstack;
- fstack.tp = FS_FUNC;
- funcstack = &fstack;
+ funcsave->fstack.caller = funcstack ? funcstack->name :
+ dupstring(funcsave->argv0 ? funcsave->argv0 : argzero);
+ funcsave->fstack.lineno = lineno;
+ funcsave->fstack.prev = funcstack;
+ funcsave->fstack.tp = FS_FUNC;
+ funcstack = &funcsave->fstack;
- fstack.flineno = shfunc->lineno;
- fstack.filename = getshfuncfile(shfunc);
+ funcsave->fstack.flineno = shfunc->lineno;
+ funcsave->fstack.filename = getshfuncfile(shfunc);
prog = shfunc->funcdef;
if (prog->flags & EF_RUN) {
@@ -5636,7 +5713,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
prog->flags &= ~EF_RUN;
- runshfunc(prog, NULL, fstack.name);
+ runshfunc(prog, NULL, funcsave->fstack.name);
if (!(shf = (Shfunc) shfunctab->getnode(shfunctab,
(name = fname)))) {
@@ -5649,54 +5726,52 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
}
prog = shf->funcdef;
}
- runshfunc(prog, wrappers, fstack.name);
+ runshfunc(prog, wrappers, funcsave->fstack.name);
doneshfunc:
- funcstack = fstack.prev;
-#ifdef MAX_FUNCTION_DEPTH
+ funcstack = funcsave->fstack.prev;
undoshfunc:
--funcdepth;
-#endif
if (retflag) {
retflag = 0;
- breaks = obreaks;
+ breaks = funcsave->breaks;
}
freearray(pparams);
- if (oargv0) {
+ if (funcsave->argv0) {
zsfree(argzero);
- argzero = oargv0;
+ argzero = funcsave->argv0;
}
pparams = pptab;
if (!isset(POSIXBUILTINS)) {
- zoptind = oldzoptind;
- optcind = oldoptcind;
+ zoptind = funcsave->zoptind;
+ optcind = funcsave->optcind;
}
- scriptname = oldscriptname;
- oflags = ooflags;
+ scriptname = funcsave->scriptname;
+ oflags = funcsave->oflags;
endpatternscope(); /* before restoring old LOCALPATTERNS */
- if (restore_sticky) {
+ if (funcsave->restore_sticky) {
/*
* If we switched to an emulation environment just for
* this function, we interpret the option and emulation
* switch as being a firewall between environments.
*/
- memcpy(opts, saveopts, sizeof(opts));
- emulation = saveemulation;
- sticky = save_sticky;
+ memcpy(opts, funcsave->opts, sizeof(opts));
+ emulation = funcsave->emulation;
+ sticky = funcsave->sticky;
} else if (isset(LOCALOPTIONS)) {
/* restore all shell options except PRIVILEGED and RESTRICTED */
- saveopts[PRIVILEGED] = opts[PRIVILEGED];
- saveopts[RESTRICTED] = opts[RESTRICTED];
- memcpy(opts, saveopts, sizeof(opts));
- emulation = saveemulation;
+ funcsave->opts[PRIVILEGED] = opts[PRIVILEGED];
+ funcsave->opts[RESTRICTED] = opts[RESTRICTED];
+ memcpy(opts, funcsave->opts, sizeof(opts));
+ emulation = funcsave->emulation;
} else {
/* just restore a couple. */
- opts[XTRACE] = saveopts[XTRACE];
- opts[PRINTEXITVALUE] = saveopts[PRINTEXITVALUE];
- opts[LOCALOPTIONS] = saveopts[LOCALOPTIONS];
- opts[LOCALLOOPS] = saveopts[LOCALLOOPS];
- opts[WARNNESTEDVAR] = saveopts[WARNNESTEDVAR];
+ opts[XTRACE] = funcsave->opts[XTRACE];
+ opts[PRINTEXITVALUE] = funcsave->opts[PRINTEXITVALUE];
+ opts[LOCALOPTIONS] = funcsave->opts[LOCALOPTIONS];
+ opts[LOCALLOOPS] = funcsave->opts[LOCALLOOPS];
+ opts[WARNNESTEDVAR] = funcsave->opts[WARNNESTEDVAR];
}
if (opts[LOCALLOOPS]) {
@@ -5704,9 +5779,9 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
zwarn("`continue' active at end of function scope");
if (breaks)
zwarn("`break' active at end of function scope");
- breaks = obreaks;
- contflag = ocontflag;
- loops = oloops;
+ breaks = funcsave->breaks;
+ contflag = funcsave->contflag;
+ loops = funcsave->loops;
}
endtrapscope();
@@ -5714,11 +5789,11 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
if (trap_state == TRAP_STATE_PRIMED)
trap_return++;
ret = lastval;
- noerrexit = savnoerrexit;
+ noerrexit = funcsave->noerrexit;
if (noreturnval) {
- lastval = oldlastval;
- numpipestats = oldnumpipestats;
- memcpy(pipestats, oldpipestats, sizeof(int)*numpipestats);
+ lastval = funcsave->lastval;
+ numpipestats = funcsave->numpipestats;
+ memcpy(pipestats, funcsave->pipestats, sizeof(int)*numpipestats);
}
} OLDHEAPS;
@@ -5888,6 +5963,7 @@ stripkshdef(Eprog prog, char *name)
{
Wordcode pc;
wordcode code;
+ char *ptr1, *ptr2;
if (!prog)
return NULL;
@@ -5898,8 +5974,25 @@ stripkshdef(Eprog prog, char *name)
return prog;
pc++;
code = *pc++;
- if (wc_code(code) != WC_FUNCDEF ||
- *pc != 1 || strcmp(name, ecrawstr(prog, pc + 1, NULL)))
+ if (wc_code(code) != WC_FUNCDEF || *pc != 1)
+ return prog;
+
+ /*
+ * See if name of function requested (name) is same as
+ * name of function in word code. name may still have "-"
+ * tokenised. The word code shouldn't, as function names should be
+ * untokenised, but reports say it sometimes does.
+ */
+ ptr1 = name;
+ ptr2 = ecrawstr(prog, pc + 1, NULL);
+ while (*ptr1 && *ptr2) {
+ if (*ptr1 != *ptr2 && *ptr1 != Dash && *ptr1 != '-' &&
+ *ptr2 != Dash && *ptr2 != '-')
+ break;
+ ptr1++;
+ ptr2++;
+ }
+ if (*ptr1 || *ptr2)
return prog;
{
diff --git a/Src/glob.c b/Src/glob.c
index c9ec97e0e..66a95329f 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -387,7 +387,7 @@ insert(char *s, int checked)
qn = qn->next;
}
} else if (!checked) {
- if (statfullpath(s, NULL, 1)) {
+ if (statfullpath(s, &buf, 1)) {
unqueue_signals();
return;
}
diff --git a/Src/hashtable.c b/Src/hashtable.c
index 6ec2ed220..b7baa3142 100644
--- a/Src/hashtable.c
+++ b/Src/hashtable.c
@@ -1261,6 +1261,8 @@ printaliasnode(HashNode hn, int printflags)
if (printflags & PRINT_WHENCE_WORD) {
if (a->node.flags & ALIAS_SUFFIX)
printf("%s: suffix alias\n", a->node.nam);
+ else if (a->node.flags & ALIAS_GLOBAL)
+ printf("%s: global alias\n", a->node.nam);
else
printf("%s: alias\n", a->node.nam);
return;
diff --git a/Src/hashtable.h b/Src/hashtable.h
index 3606e9785..21398e17c 100644
--- a/Src/hashtable.h
+++ b/Src/hashtable.h
@@ -62,6 +62,7 @@
#define BIN_UNHASH 28
#define BIN_UNALIAS 29
#define BIN_UNFUNCTION 30
+#define BIN_UNSET 31
/* These currently depend on being 0 and 1. */
#define BIN_SETOPT 0
diff --git a/Src/hist.c b/Src/hist.c
index da5a8b29f..dbdc1e4e5 100644
--- a/Src/hist.c
+++ b/Src/hist.c
@@ -46,6 +46,9 @@ void (*hwaddc) _((int));
void (*hwbegin) _((int));
/**/
+void (*hwabort) _((void));
+
+/**/
void (*hwend) _((void));
/**/
@@ -201,6 +204,13 @@ int hlinesz;
static zlong defev;
+/*
+ * Flag that we stopped reading line when we got to a comment,
+ * but we want to keep it in the histofy even if there were no words
+ * (i.e. the comment was the entire line).
+ */
+static int hist_keep_comment;
+
/* Remember the last line in the history file so we can find it again. */
static struct histfile_stats {
char *text;
@@ -250,10 +260,12 @@ hist_context_save(struct hist_stack *hs, int toplevel)
hs->hungetc = hungetc;
hs->hwaddc = hwaddc;
hs->hwbegin = hwbegin;
+ hs->hwabort = hwabort;
hs->hwend = hwend;
hs->addtoline = addtoline;
hs->hlinesz = hlinesz;
hs->defev = defev;
+ hs->hist_keep_comment = hist_keep_comment;
/*
* We save and restore the command stack with history
* as it's visible to the user interactively, so if
@@ -294,10 +306,12 @@ hist_context_restore(const struct hist_stack *hs, int toplevel)
hungetc = hs->hungetc;
hwaddc = hs->hwaddc;
hwbegin = hs->hwbegin;
+ hwabort = hs->hwabort;
hwend = hs->hwend;
addtoline = hs->addtoline;
hlinesz = hs->hlinesz;
defev = hs->defev;
+ hist_keep_comment = hs->hist_keep_comment;
if (cmdstack)
zfree(cmdstack, CMDSTACKSZ);
cmdstack = hs->cstack;
@@ -451,8 +465,26 @@ herrflush(void)
{
inpopalias();
- while (!lexstop && inbufct && !strin)
- hwaddc(ingetc());
+ if (lexstop)
+ return;
+ /*
+ * The lex_add_raw test is needed if we are parsing a command
+ * substitution when expanding history for ZLE: strin is set but we
+ * need to finish off the input because the string we are reading is
+ * going to be used directly in the line that goes to ZLE.
+ *
+ * Note that this is a side effect --- this is not the usual reason
+ * for testing lex_add_raw which is to add the text to a different
+ * buffer used when we are actually parsing the command substituion
+ * (nothing to do with ZLE). Sorry.
+ */
+ while (inbufct && (!strin || lex_add_raw)) {
+ int c = ingetc();
+ if (!lexstop) {
+ hwaddc(c);
+ addtoline(c);
+ }
+ }
}
/*
@@ -986,6 +1018,11 @@ nohw(UNUSED(int c))
}
static void
+nohwabort(void)
+{
+}
+
+static void
nohwe(void)
{
}
@@ -1057,6 +1094,7 @@ hbegin(int dohist)
hungetc = inungetc;
hwaddc = nohw;
hwbegin = nohw;
+ hwabort = nohwabort;
hwend = nohwe;
addtoline = nohw;
} else {
@@ -1066,6 +1104,7 @@ hbegin(int dohist)
hungetc = ihungetc;
hwaddc = ihwaddc;
hwbegin = ihwbegin;
+ hwabort = ihwabort;
hwend = ihwend;
addtoline = iaddtoline;
if (!isset(BANGHIST))
@@ -1083,7 +1122,6 @@ hbegin(int dohist)
} else
histactive = HA_ACTIVE | HA_NOINC;
- hf = getsparam("HISTFILE");
/*
* For INCAPPENDHISTORYTIME, when interactive, save the history here
* as it gives a better estimate of the times of commands.
@@ -1104,8 +1142,10 @@ hbegin(int dohist)
*/
if (isset(INCAPPENDHISTORYTIME) && !isset(SHAREHISTORY) &&
!isset(INCAPPENDHISTORY) &&
- !(histactive & HA_NOINC) && !strin && histsave_stack_pos == 0)
+ !(histactive & HA_NOINC) && !strin && histsave_stack_pos == 0) {
+ hf = getsparam("HISTFILE");
savehistfile(hf, 0, HFILE_USE_OPTIONS | HFILE_FAST);
+ }
}
/**/
@@ -1449,7 +1489,7 @@ hend(Eprog prog)
} else
save = 0;
}
- if (chwordpos <= 2)
+ if (chwordpos <= 2 && !hist_keep_comment)
save = 0;
else if (should_ignore_line(prog))
save = -1;
@@ -1552,6 +1592,7 @@ hend(Eprog prog)
*/
while (histsave_stack_pos > stack_pos)
pophiststack();
+ hist_keep_comment = 0;
unqueue_signals();
return !(flag & HISTFLAG_NOEXEC || errflag);
}
@@ -1570,6 +1611,17 @@ ihwbegin(int offset)
chwords[chwordpos++] = hptr - chline + offset;
}
+/* Abort current history word, not needed */
+
+/**/
+void
+ihwabort(void)
+{
+ if (chwordpos%2)
+ chwordpos--;
+ hist_keep_comment = 1;
+}
+
/* add a word to the history List */
/**/
diff --git a/Src/init.c b/Src/init.c
index 87dd2e26d..c5372665a 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -244,33 +244,24 @@ static int restricted;
/**/
static void
-parseargs(char **argv, char **runscript, char **cmdptr)
+parseargs(char *zsh_name, char **argv, char **runscript, char **cmdptr)
{
char **x;
LinkList paramlist;
+ int flags = PARSEARGS_TOPLEVEL;
+ if (**argv == '-')
+ flags |= PARSEARGS_LOGIN;
argzero = posixzero = *argv++;
SHIN = 0;
- /* There's a bit of trickery with opts[INTERACTIVE] here. It starts *
- * at a value of 2 (instead of 1) or 0. If it is explicitly set on *
- * the command line, it goes to 1 or 0. If input is coming from *
- * somewhere that normally makes the shell non-interactive, we do *
- * "opts[INTERACTIVE] &= 1", so that only a *default* on state will *
- * be changed. At the end of the function, a value of 2 gets *
- * changed to 1. */
- opts[INTERACTIVE] = isatty(0) ? 2 : 0;
/*
- * MONITOR is similar: we initialise it to 2, and if it's
- * still 2 at the end, we set it to the value of INTERACTIVE.
+ * parseopts sets up some options after we deal with emulation in
+ * order to be consistent --- the code in parseopts_setemulate() is
+ * matched by code at the end of the present function.
*/
- opts[MONITOR] = 2; /* may be unset in init_io() */
- opts[HASHDIRS] = 2; /* same relationship to INTERACTIVE */
- opts[USEZLE] = 1; /* see below, related to SHINSTDIN */
- opts[SHINSTDIN] = 0;
- opts[SINGLECOMMAND] = 0;
- if (parseopts(NULL, &argv, opts, cmdptr, NULL))
+ if (parseopts(zsh_name, &argv, opts, cmdptr, NULL, flags))
exit(1);
/*
@@ -334,9 +325,45 @@ parseopts_insert(LinkList optlist, char *base, int optno)
}
/*
+ * This sets the global emulation plus the options we traditionally
+ * set immediately after that. This is just for historical consistency
+ * --- I don't think those options actually need to be set here.
+ */
+static void parseopts_setemulate(char *nam, int flags)
+{
+ emulate(nam, 1, &emulation, opts); /* initialises most options */
+ opts[LOGINSHELL] = ((flags & PARSEARGS_LOGIN) != 0);
+ opts[PRIVILEGED] = (getuid() != geteuid() || getgid() != getegid());
+
+ /* There's a bit of trickery with opts[INTERACTIVE] here. It starts *
+ * at a value of 2 (instead of 1) or 0. If it is explicitly set on *
+ * the command line, it goes to 1 or 0. If input is coming from *
+ * somewhere that normally makes the shell non-interactive, we do *
+ * "opts[INTERACTIVE] &= 1", so that only a *default* on state will *
+ * be changed. At the end of the function, a value of 2 gets *
+ * changed to 1. */
+ opts[INTERACTIVE] = isatty(0) ? 2 : 0;
+ /*
+ * MONITOR is similar: we initialise it to 2, and if it's
+ * still 2 at the end, we set it to the value of INTERACTIVE.
+ */
+ opts[MONITOR] = 2; /* may be unset in init_io() */
+ opts[HASHDIRS] = 2; /* same relationship to INTERACTIVE */
+ opts[USEZLE] = 1; /* see below, related to SHINSTDIN */
+ opts[SHINSTDIN] = 0;
+ opts[SINGLECOMMAND] = 0;
+}
+
+/*
* Parse shell options.
- * If nam is not NULL, this is called from a command; don't
- * exit on failure.
+ *
+ * If (flags & PARSEARGS_TOPLEVEL):
+ * - we are doing shell initilisation
+ * - nam is the name under which the shell was started
+ * - set up emulation and standard options based on that.
+ * Otherwise:
+ * - nam is a command name
+ * - don't exit on failure.
*
* If optlist is not NULL, it used to form a list of pointers
* into new_opts indicating which options have been changed.
@@ -345,23 +372,26 @@ parseopts_insert(LinkList optlist, char *base, int optno)
/**/
mod_export int
parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
- LinkList optlist)
+ LinkList optlist, int flags)
{
int optionbreak = 0;
int action, optno;
char **argv = *argvp;
+ int toplevel = ((flags & PARSEARGS_TOPLEVEL) != 0u);
+ int emulate_required = toplevel;
+ char *top_emulation = nam;
*cmdp = 0;
#define WARN_OPTION(F, S) \
do { \
- if (nam) \
+ if (!toplevel) \
zwarnnam(nam, F, S); \
else \
zerr(F, S); \
} while (0)
#define LAST_OPTION(N) \
do { \
- if (nam) { \
+ if (!toplevel) { \
if (*argv) \
argv++; \
goto doneargv; \
@@ -381,7 +411,7 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
argv++;
goto doneoptions;
}
- if (nam || *argv != args+1 || **argv != '-')
+ if (!toplevel || *argv != args+1 || **argv != '-')
goto badoptionstring;
/* GNU-style long options */
++*argv;
@@ -394,6 +424,19 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
printhelp();
LAST_OPTION(0);
}
+ if (!strcmp(*argv, "emulate")) {
+ ++argv;
+ if (!*argv) {
+ zerr("--emulate: argument required");
+ exit(1);
+ }
+ if (!emulate_required) {
+ zerr("--emulate: must precede other options");
+ exit(1);
+ }
+ top_emulation = *argv;
+ break;
+ }
/* `-' characters are allowed in long options */
for(args = *argv; *args; args++)
if(*args == '-')
@@ -402,9 +445,17 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
}
if (unset(SHOPTIONLETTERS) && **argv == 'b') {
+ if (emulate_required) {
+ parseopts_setemulate(top_emulation, flags);
+ emulate_required = 0;
+ }
/* -b ends options at the end of this argument */
optionbreak = 1;
} else if (**argv == 'c') {
+ if (emulate_required) {
+ parseopts_setemulate(top_emulation, flags);
+ emulate_required = 0;
+ }
/* -c command */
*cmdp = *argv;
new_opts[INTERACTIVE] &= 1;
@@ -417,15 +468,20 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
return 1;
}
longoptions:
+ if (emulate_required) {
+ parseopts_setemulate(top_emulation, flags);
+ emulate_required = 0;
+ }
if (!(optno = optlookup(*argv))) {
WARN_OPTION("no such option: %s", *argv);
return 1;
- } else if (optno == RESTRICTED && !nam) {
+ } else if (optno == RESTRICTED && toplevel) {
restricted = action;
- } else if ((optno == EMACSMODE || optno == VIMODE) && nam) {
+ } else if ((optno == EMACSMODE || optno == VIMODE) && !toplevel) {
WARN_OPTION("can't change option: %s", *argv);
} else {
- if (dosetopt(optno, action, !nam, new_opts) && nam) {
+ if (dosetopt(optno, action, toplevel, new_opts) &&
+ !toplevel) {
WARN_OPTION("can't change option: %s", *argv);
} else if (optlist) {
parseopts_insert(optlist, new_opts, optno);
@@ -442,15 +498,21 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
}
break;
} else {
+ if (emulate_required) {
+ parseopts_setemulate(top_emulation, flags);
+ emulate_required = 0;
+ }
if (!(optno = optlookupc(**argv))) {
WARN_OPTION("bad option: -%c", **argv);
return 1;
- } else if (optno == RESTRICTED && !nam) {
+ } else if (optno == RESTRICTED && toplevel) {
restricted = action;
- } else if ((optno == EMACSMODE || optno == VIMODE) && nam) {
+ } else if ((optno == EMACSMODE || optno == VIMODE) &&
+ !toplevel) {
WARN_OPTION("can't change option: %s", *argv);
} else {
- if (dosetopt(optno, action, !nam, new_opts) && nam) {
+ if (dosetopt(optno, action, toplevel, new_opts) &&
+ !toplevel) {
WARN_OPTION("can't change option: -%c", **argv);
} else if (optlist) {
parseopts_insert(optlist, new_opts, optno);
@@ -470,6 +532,10 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
}
doneargv:
*argvp = argv;
+ if (emulate_required) {
+ parseopts_setemulate(top_emulation, flags);
+ emulate_required = 0;
+ }
return 0;
}
@@ -1660,11 +1726,9 @@ zsh_main(UNUSED(int argc), char **argv)
fdtable[0] = fdtable[1] = fdtable[2] = FDT_EXTERNAL;
createoptiontable();
- emulate(zsh_name, 1, &emulation, opts); /* initialises most options */
- opts[LOGINSHELL] = (**argv == '-');
- opts[PRIVILEGED] = (getuid() != geteuid() || getgid() != getegid());
- /* sets ZLE, INTERACTIVE, SHINSTDIN and SINGLECOMMAND */
- parseargs(argv, &runscript, &cmd);
+ /* sets emulation, LOGINSHELL, PRIVILEGED, ZLE, INTERACTIVE,
+ * SHINSTDIN and SINGLECOMMAND */
+ parseargs(zsh_name, argv, &runscript, &cmd);
SHTTY = -1;
init_io(cmd);
diff --git a/Src/jobs.c b/Src/jobs.c
index 66dfb5a7e..330ee6b37 100644
--- a/Src/jobs.c
+++ b/Src/jobs.c
@@ -728,6 +728,40 @@ printtime(struct timeval *real, child_times_t *ti, char *desc)
case 'S':
fprintf(stderr, "%4.2fs", system_time);
break;
+ case 'm':
+ switch (*++s) {
+ case 'E':
+ fprintf(stderr, "%0.fms", elapsed_time * 1000.0);
+ break;
+ case 'U':
+ fprintf(stderr, "%0.fms", user_time * 1000.0);
+ break;
+ case 'S':
+ fprintf(stderr, "%0.fms", system_time * 1000.0);
+ break;
+ default:
+ fprintf(stderr, "%%m");
+ s--;
+ break;
+ }
+ break;
+ case 'u':
+ switch (*++s) {
+ case 'E':
+ fprintf(stderr, "%0.fus", elapsed_time * 1000000.0);
+ break;
+ case 'U':
+ fprintf(stderr, "%0.fus", user_time * 1000000.0);
+ break;
+ case 'S':
+ fprintf(stderr, "%0.fus", system_time * 1000000.0);
+ break;
+ default:
+ fprintf(stderr, "%%u");
+ s--;
+ break;
+ }
+ break;
case '*':
switch (*++s) {
case 'E':
@@ -891,6 +925,7 @@ should_report_time(Job j)
struct value vbuf;
Value v;
char *s = "REPORTTIME";
+ int save_errflag = errflag;
zlong reporttime = -1;
#ifdef HAVE_GETRUSAGE
char *sm = "REPORTMEMORY";
@@ -902,12 +937,14 @@ should_report_time(Job j)
return 1;
queue_signals();
+ errflag = 0;
if ((v = getvalue(&vbuf, &s, 0)))
reporttime = getintvalue(v);
#ifdef HAVE_GETRUSAGE
if ((v = getvalue(&vbuf, &sm, 0)))
reportmemory = getintvalue(v);
#endif
+ errflag = save_errflag;
unqueue_signals();
if (reporttime < 0
#ifdef HAVE_GETRUSAGE
@@ -2217,7 +2254,8 @@ bin_fg(char *name, char **argv, Options ops, int func)
return 0;
} else { /* Must be BIN_WAIT, so wait for all jobs */
for (job = 0; job <= maxjob; job++)
- if (job != thisjob && jobtab[job].stat)
+ if (job != thisjob && jobtab[job].stat &&
+ !(jobtab[job].stat & STAT_NOPRINT))
retval = zwaitjob(job, 1);
unqueue_signals();
return retval;
@@ -2251,8 +2289,11 @@ bin_fg(char *name, char **argv, Options ops, int func)
*/
retval = waitforpid(pid, 1);
}
- if (retval == 0)
- retval = lastval2;
+ if (retval == 0) {
+ if ((retval = getbgstatus(pid)) < 0) {
+ retval = lastval2;
+ }
+ }
} else if ((retval = getbgstatus(pid)) < 0) {
zwarnnam(name, "pid %d is not a child of this shell", pid);
/* presumably lastval2 doesn't tell us a heck of a lot? */
diff --git a/Src/lex.c b/Src/lex.c
index 8493d4737..44ad88043 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -158,7 +158,7 @@ mod_export int nocomments;
/* add raw input characters while parsing command substitution */
/**/
-static int lex_add_raw;
+int lex_add_raw;
/* variables associated with the above */
@@ -677,7 +677,7 @@ gettok(void)
(char *)hcalloc(lexbuf.siz = LEX_HEAP_SIZE);
add(c);
}
- hwend();
+ hwabort();
while ((c = ingetc()) != '\n' && !lexstop) {
hwaddc(c);
addtoline(c);
@@ -1291,7 +1291,9 @@ gettokstr(int c, int sub)
ALLOWHIST
if (c != '\'') {
unmatched = '\'';
- peek = LEXERR;
+ /* Not an error when called from bufferwords() */
+ if (!(lexflags & LEXFLAGS_ACTIVE))
+ peek = LEXERR;
cmdpop();
goto brk;
}
@@ -1313,7 +1315,9 @@ gettokstr(int c, int sub)
cmdpop();
if (c) {
unmatched = '"';
- peek = LEXERR;
+ /* Not an error when called from bufferwords() */
+ if (!(lexflags & LEXFLAGS_ACTIVE))
+ peek = LEXERR;
goto brk;
}
c = Dnull;
@@ -1350,7 +1354,9 @@ gettokstr(int c, int sub)
cmdpop();
if (c != '`') {
unmatched = '`';
- peek = LEXERR;
+ /* Not an error when called from bufferwords() */
+ if (!(lexflags & LEXFLAGS_ACTIVE))
+ peek = LEXERR;
goto brk;
}
c = Tick;
@@ -1392,7 +1398,7 @@ gettokstr(int c, int sub)
return LEXERR;
}
hungetc(c);
- if (unmatched)
+ if (unmatched && !(lexflags & LEXFLAGS_ACTIVE))
zerr("unmatched %c", unmatched);
if (in_brace_param) {
while(bct-- >= in_brace_param)
@@ -2128,8 +2134,12 @@ skipcomm(void)
* function at the history layer --- this is consistent with the
* intention of maintaining the history and input layers across
* the recursive parsing.
+ *
+ * Also turn off LEXFLAGS_NEWLINE because this is already skipping
+ * across the entire construct, and parse_event() needs embedded
+ * newlines to be "real" when looking for the OUTPAR token.
*/
- lexflags &= ~LEXFLAGS_ZLE;
+ lexflags &= ~(LEXFLAGS_ZLE|LEXFLAGS_NEWLINE);
dbparens = 0; /* restored by zcontext_restore_partial() */
if (!parse_event(OUTPAR) || tok != OUTPAR) {
diff --git a/Src/loop.c b/Src/loop.c
index 4859c976b..1013aeb50 100644
--- a/Src/loop.c
+++ b/Src/loop.c
@@ -428,7 +428,7 @@ execwhile(Estate state, UNUSED(int do_exec))
} else
for (;;) {
state->pc = loop;
- noerrexit = 1;
+ noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN;
/* In case the test condition is a functional no-op,
* make sure signal handlers recognize ^C to end the loop. */
@@ -541,8 +541,7 @@ execif(Estate state, int do_exec)
olderrexit = noerrexit;
end = state->pc + WC_IF_SKIP(code);
- if (!noerrexit)
- noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN;
+ noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN;
while (state->pc < end) {
code = *state->pc++;
if (wc_code(code) != WC_IF ||
@@ -570,9 +569,9 @@ execif(Estate state, int do_exec)
if (olderrexit)
noerrexit = olderrexit;
else if (lastval)
- noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN | NOERREXIT_UNTIL_EXEC;
+ noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN | NOERREXIT_UNTIL_EXEC;
else
- noerrexit = 0;
+ noerrexit &= ~ (NOERREXIT_EXIT | NOERREXIT_RETURN);
cmdpush(run == 2 ? CS_ELSE : (s ? CS_ELIFTHEN : CS_IFTHEN));
execlist(state, 1, do_exec);
cmdpop();
diff --git a/Src/math.c b/Src/math.c
index f9613001a..c3831602b 100644
--- a/Src/math.c
+++ b/Src/math.c
@@ -1306,8 +1306,6 @@ op(int what)
spval->type = MN_INTEGER;
} else
spval->u.l = !spval->u.l;
- stack[sp].lval = NULL;
- stack[sp].pval = NULL;
break;
case COMP:
if (spval->type & MN_FLOAT) {
@@ -1315,8 +1313,6 @@ op(int what)
spval->type = MN_INTEGER;
} else
spval->u.l = ~spval->u.l;
- stack[sp].lval = NULL;
- stack[sp].pval = NULL;
break;
case POSTPLUS:
a = *spval;
@@ -1335,16 +1331,12 @@ op(int what)
(void)setmathvar(stack + sp, a);
break;
case UPLUS:
- stack[sp].lval = NULL;
- stack[sp].pval = NULL;
break;
case UMINUS:
if (spval->type & MN_FLOAT)
spval->u.d = -spval->u.d;
else
spval->u.l = -spval->u.l;
- stack[sp].lval = NULL;
- stack[sp].pval = NULL;
break;
case QUEST:
DPUTS(sp < 2, "BUG: math: three shall be the number of the counting.");
@@ -1377,6 +1369,8 @@ op(int what)
zerr("bad math expression: out of integers");
return;
}
+ stack[sp].lval = NULL;
+ stack[sp].pval = NULL;
}
diff --git a/Src/mem.c b/Src/mem.c
index 840bbb6e4..77e4375f0 100644
--- a/Src/mem.c
+++ b/Src/mem.c
@@ -1719,7 +1719,13 @@ calloc(MALLOC_ARG_T n, MALLOC_ARG_T size)
if (!(l = n * size))
return (MALLOC_RET_T) m_high;
- r = malloc(l);
+ /*
+ * use realloc() (with a NULL `p` argument it behaves exactly the same
+ * as malloc() does) to prevent an infinite loop caused by sibling-call
+ * optimizations (the malloc() call would otherwise be replaced by an
+ * unconditional branch back to line 1719 ad infinitum).
+ */
+ r = realloc(NULL, l);
memset(r, 0, l);
@@ -1879,16 +1885,14 @@ bin_mem(char *name, char **argv, Options ops, int func)
mod_export void
zfree(void *p, UNUSED(int sz))
{
- if (p)
- free(p);
+ free(p);
}
/**/
mod_export void
zsfree(char *p)
{
- if (p)
- free(p);
+ free(p);
}
/**/
diff --git a/Src/options.c b/Src/options.c
index 2b5795bab..590652ea9 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -111,6 +111,7 @@ static struct optname optns[] = {
{{NULL, "chasedots", OPT_EMULATE}, CHASEDOTS},
{{NULL, "chaselinks", OPT_EMULATE}, CHASELINKS},
{{NULL, "checkjobs", OPT_EMULATE|OPT_ZSH}, CHECKJOBS},
+{{NULL, "checkrunningjobs", OPT_EMULATE|OPT_ZSH}, CHECKRUNNINGJOBS},
{{NULL, "clobber", OPT_EMULATE|OPT_ALL}, CLOBBER},
{{NULL, "combiningchars", 0}, COMBININGCHARS},
{{NULL, "completealiases", 0}, COMPLETEALIASES},
diff --git a/Src/params.c b/Src/params.c
index 6fbee880c..de7730ae7 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -101,6 +101,19 @@ zlong lastval, /* $? */
rprompt_indent, /* $ZLE_RPROMPT_INDENT */
ppid, /* $PPID */
zsh_subshell; /* $ZSH_SUBSHELL */
+
+/* $FUNCNEST */
+/**/
+mod_export
+zlong zsh_funcnest =
+#ifdef MAX_FUNCTION_DEPTH
+ MAX_FUNCTION_DEPTH
+#else
+ /* Disabled by default but can be enabled at run time */
+ -1
+#endif
+ ;
+
/**/
zlong lineno, /* $LINENO */
zoptind, /* $OPTIND */
@@ -337,6 +350,7 @@ IPDEF5("COLUMNS", &zterm_columns, zlevar_gsu),
IPDEF5("LINES", &zterm_lines, zlevar_gsu),
IPDEF5U("ZLE_RPROMPT_INDENT", &rprompt_indent, rprompt_indent_gsu),
IPDEF5("SHLVL", &shlvl, varinteger_gsu),
+IPDEF5("FUNCNEST", &zsh_funcnest, varinteger_gsu),
/* Don't import internal integer status variables. */
#define IPDEF6(A,B,F) {{NULL,A,PM_INTEGER|PM_SPECIAL|PM_DONTIMPORT},BR((void *)B),GSU(F),10,0,NULL,NULL,NULL,0}
@@ -535,10 +549,13 @@ scancopyparams(HashNode hn, UNUSED(int flags))
HashTable
copyparamtable(HashTable ht, char *name)
{
- HashTable nht = newparamtable(ht->hsize, name);
- outtable = nht;
- scanhashtable(ht, 0, 0, 0, scancopyparams, 0);
- outtable = NULL;
+ HashTable nht = 0;
+ if (ht) {
+ nht = newparamtable(ht->hsize, name);
+ outtable = nht;
+ scanhashtable(ht, 0, 0, 0, scancopyparams, 0);
+ outtable = NULL;
+ }
return nht;
}
@@ -1204,7 +1221,7 @@ isident(char *s)
/**/
static zlong
getarg(char **str, int *inv, Value v, int a2, zlong *w,
- int *prevcharlen, int *nextcharlen)
+ int *prevcharlen, int *nextcharlen, int flags)
{
int hasbeg = 0, word = 0, rev = 0, ind = 0, down = 0, l, i, ishash;
int keymatch = 0, needtok = 0, arglen, len, inpar = 0;
@@ -1407,6 +1424,8 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w,
if (ishash) {
HashTable ht = v->pm->gsu.h->getfn(v->pm);
if (!ht) {
+ if (flags & SCANPM_CHECKING)
+ return isset(KSHARRAYS) ? 1 : 0;
ht = newparamtable(17, v->pm->node.nam);
v->pm->gsu.h->setfn(v->pm, ht);
}
@@ -1848,7 +1867,8 @@ getindex(char **pptr, Value v, int flags)
zlong we = 0, dummy;
int startprevlen, startnextlen;
- start = getarg(&s, &inv, v, 0, &we, &startprevlen, &startnextlen);
+ start = getarg(&s, &inv, v, 0, &we, &startprevlen, &startnextlen,
+ flags);
if (inv) {
if (!v->isarr && start != 0) {
@@ -1922,7 +1942,7 @@ getindex(char **pptr, Value v, int flags)
if ((com = (*s == ','))) {
s++;
- end = getarg(&s, &inv, v, 1, &dummy, NULL, NULL);
+ end = getarg(&s, &inv, v, 1, &dummy, NULL, NULL, flags);
} else {
end = we ? we : start;
}
@@ -2704,7 +2724,7 @@ setarrvalue(Value v, char **val)
v->pm->gsu.a->setfn(v->pm, val);
} else if (v->start == -1 && v->end == 0 &&
PM_TYPE(v->pm->node.flags) == PM_HASHED) {
- arrhashsetfn(v->pm, val, 1);
+ arrhashsetfn(v->pm, val, ASSPM_AUGMENT);
} else if ((PM_TYPE(v->pm->node.flags) == PM_HASHED)) {
freearray(val);
zerr("%s: attempt to set slice of associative array",
@@ -3185,6 +3205,140 @@ assignaparam(char *s, char **val, int flags)
if (flags & ASSPM_WARN)
check_warn_pm(v->pm, "array", created, may_warn_about_nested_vars);
+
+ /*
+ * At this point, we may have array entries consisting of
+ * - a Marker element --- normally allocated array entry but
+ * with just Marker char and null
+ * - an array index element --- as normal for associative array,
+ * but non-standard for normal array which we handle now.
+ * - a value for the indexed element.
+ * This only applies if the flag ASSPM_KEY_VALUE is passed in,
+ * indicating prefork() detected this syntax.
+ *
+ * For associative arrays we just junk the Makrer elements.
+ */
+ if (flags & ASSPM_KEY_VALUE) {
+ char **aptr;
+ if (PM_TYPE(v->pm->node.flags) & PM_ARRAY) {
+ /*
+ * This is an ordinary array with key / value pairs.
+ */
+ int maxlen, origlen, nextind;
+ char **fullval, **origptr;
+ zlong *subscripts = (zlong *)zhalloc(arrlen(val) * sizeof(zlong));
+ zlong *iptr = subscripts;
+ if (flags & ASSPM_AUGMENT) {
+ origptr = v->pm->gsu.a->getfn(v->pm);
+ maxlen = origlen = arrlen(origptr);
+ } else {
+ maxlen = origlen = 0;
+ origptr = NULL;
+ }
+ nextind = 0;
+ for (aptr = val; *aptr; ) {
+ if (**aptr == Marker) {
+ *iptr = mathevali(*++aptr);
+ if (*iptr < 0 ||
+ (!isset(KSHARRAYS) && *iptr == 0)) {
+ unqueue_signals();
+ zerr("bad subscript for direct array assignment: %s", *aptr);
+ return NULL;
+ }
+ if (!isset(KSHARRAYS))
+ --*iptr;
+ nextind = *iptr + 1;
+ ++iptr;
+ aptr += 2;
+ } else {
+ ++nextind;
+ ++aptr;
+ }
+ if (nextind > maxlen)
+ maxlen = nextind;
+ }
+ fullval = zshcalloc((maxlen+1) * sizeof(char *));
+ if (!fullval) {
+ zerr("array too large");
+ return NULL;
+ }
+ fullval[maxlen] = NULL;
+ if (flags & ASSPM_AUGMENT) {
+ char **srcptr = origptr;
+ for (aptr = fullval; aptr <= fullval + origlen; aptr++) {
+ *aptr = ztrdup(*srcptr);
+ srcptr++;
+ }
+ }
+ iptr = subscripts;
+ nextind = 0;
+ for (aptr = val; *aptr; ++aptr) {
+ char *old;
+ if (**aptr == Marker) {
+ int augment = ((*aptr)[1] == '+');
+ zsfree(*aptr);
+ zsfree(*++aptr); /* Index, no longer needed */
+ old = fullval[*iptr];
+ if (augment && old) {
+ fullval[*iptr] = bicat(old, *++aptr);
+ zsfree(*aptr);
+ } else {
+ fullval[*iptr] = *++aptr;
+ }
+ nextind = *iptr + 1;
+ ++iptr;
+ } else {
+ old = fullval[nextind];
+ fullval[nextind] = *aptr;
+ ++nextind;
+ }
+ if (old)
+ zsfree(old);
+ /* aptr now on value in both cases */
+ }
+ if (*aptr) { /* Shouldn't be possible */
+ DPUTS(1, "Extra element in key / value array");
+ zsfree(*aptr);
+ }
+ free(val);
+ for (aptr = fullval; aptr < fullval + maxlen; aptr++) {
+ /*
+ * Remember we don't have sparse arrays but and they're null
+ * terminated --- so any value we don't set has to be an
+ * empty string.
+ */
+ if (!*aptr)
+ *aptr = ztrdup("");
+ }
+ setarrvalue(v, fullval);
+ unqueue_signals();
+ return v->pm;
+ } else if (PM_TYPE(v->pm->node.flags & PM_HASHED)) {
+ /*
+ * We strictly enforce [key]=value syntax for associative
+ * arrays. Marker can only indicate a Marker / key / value
+ * triad; it cannot be there by accident.
+ *
+ * It's too inefficient to strip Markers here, and they
+ * can't be there in the other form --- so just ignore
+ * them willy nilly lower down.
+ */
+ for (aptr = val; *aptr; aptr += 3) {
+ if (**aptr != Marker) {
+ unqueue_signals();
+ freearray(val);
+ zerr("bad [key]=value syntax for associative array");
+ return NULL;
+ }
+ }
+ } else {
+ unqueue_signals();
+ freearray(val);
+ zerr("invalid use of [key]=value assignment syntax");
+ return NULL;
+ }
+ }
+
if (flags & ASSPM_AUGMENT) {
if (v->start == 0 && v->end == -1) {
if (PM_TYPE(v->pm->node.flags) & PM_ARRAY) {
@@ -3675,30 +3829,49 @@ nullsethashfn(UNUSED(Param pm), HashTable x)
/* Function to set value of an association parameter using key/value pairs */
/**/
-mod_export void
-arrhashsetfn(Param pm, char **val, int augment)
+static void
+arrhashsetfn(Param pm, char **val, int flags)
{
/* Best not to shortcut this by using the existing hash table, *
* since that could cause trouble for special hashes. This way, *
* it's up to pm->gsu.h->setfn() what to do. */
- int alen = arrlen(val);
+ int alen = 0;
HashTable opmtab = paramtab, ht = 0;
- char **aptr = val;
+ char **aptr;
Value v = (Value) hcalloc(sizeof *v);
v->end = -1;
+ for (aptr = val; *aptr; ++aptr) {
+ if (**aptr != Marker)
+ ++alen;
+ }
+
if (alen % 2) {
freearray(val);
zerr("bad set of key/value pairs for associative array");
return;
}
- if (augment) {
+ if (flags & ASSPM_AUGMENT) {
ht = paramtab = pm->gsu.h->getfn(pm);
}
- if (alen && (!augment || !paramtab)) {
+ if (alen && (!(flags & ASSPM_AUGMENT) || !paramtab)) {
ht = paramtab = newparamtable(17, pm->node.nam);
}
- while (*aptr) {
+ for (aptr = val; *aptr; ) {
+ int eltflags = 0;
+ if (**aptr == Marker) {
+ /* Either all elements have Marker or none. Checked in caller. */
+ if ((*aptr)[1] == '+') {
+ /* Actually, assignstrvalue currently doesn't handle this... */
+ eltflags = ASSPM_AUGMENT;
+ /* ...so we'll use the trick from setsparam(). */
+ v->start = INT_MAX;
+ } else {
+ v->start = 0;
+ }
+ v->end = -1;
+ zsfree(*aptr++);
+ }
/* The parameter name is ztrdup'd... */
v->pm = createparam(*aptr, PM_SCALAR|PM_UNSET);
/*
@@ -3709,7 +3882,7 @@ arrhashsetfn(Param pm, char **val, int augment)
v->pm = (Param) paramtab->getnode(paramtab, *aptr);
zsfree(*aptr++);
/* ...but we can use the value without copying. */
- setstrvalue(v, *aptr++);
+ assignstrvalue(v, *aptr++, eltflags);
}
paramtab = opmtab;
pm->gsu.h->setfn(pm, ht);
@@ -5498,9 +5671,7 @@ printparamvalue(Param p, int printflags)
{
char *t, **u;
- if (printflags & PRINT_KV_PAIR)
- putchar(' ');
- else
+ if (!(printflags & PRINT_KV_PAIR))
putchar('=');
/* How the value is displayed depends *
@@ -5528,40 +5699,60 @@ printparamvalue(Param p, int printflags)
/* array */
if (!(printflags & PRINT_KV_PAIR)) {
putchar('(');
- putchar(' ');
+ if (!(printflags & PRINT_LINE))
+ putchar(' ');
}
u = p->gsu.a->getfn(p);
if(*u) {
+ if (printflags & PRINT_LINE) {
+ if (printflags & PRINT_KV_PAIR)
+ printf(" ");
+ else
+ printf("\n ");
+ }
quotedzputs(*u++, stdout);
while (*u) {
- putchar(' ');
+ if (printflags & PRINT_LINE)
+ printf("\n ");
+ else
+ putchar(' ');
quotedzputs(*u++, stdout);
}
+ if ((printflags & (PRINT_LINE|PRINT_KV_PAIR)) == PRINT_LINE)
+ putchar('\n');
}
if (!(printflags & PRINT_KV_PAIR)) {
- putchar(' ');
+ if (!(printflags & PRINT_LINE))
+ putchar(' ');
putchar(')');
}
break;
case PM_HASHED:
/* association */
- if (!(printflags & PRINT_KV_PAIR)) {
- putchar('(');
- putchar(' ');
- }
{
- HashTable ht = p->gsu.h->getfn(p);
+ HashTable ht;
+ int found = 0;
+ if (!(printflags & PRINT_KV_PAIR)) {
+ putchar('(');
+ if (!(printflags & PRINT_LINE))
+ putchar(' ');
+ }
+ ht = p->gsu.h->getfn(p);
if (ht)
- scanhashtable(ht, 1, 0, PM_UNSET,
- ht->printnode, PRINT_KV_PAIR);
+ found = scanhashtable(ht, 1, 0, PM_UNSET,
+ ht->printnode, PRINT_KV_PAIR |
+ (printflags & PRINT_LINE));
+ if (!(printflags & PRINT_KV_PAIR)) {
+ if (found && (printflags & PRINT_LINE))
+ putchar('\n');
+ putchar(')');
+ }
}
- if (!(printflags & PRINT_KV_PAIR))
- putchar(')');
break;
}
- if (printflags & PRINT_KV_PAIR)
+ if ((printflags & (PRINT_KV_PAIR|PRINT_LINE)) == PRINT_KV_PAIR)
putchar(' ');
- else
+ else if (!(printflags & PRINT_KV_PAIR))
putchar('\n');
}
@@ -5655,7 +5846,14 @@ printparamnode(HashNode hn, int printflags)
zputs(p->node.nam, stdout);
putchar('\n');
} else {
+ if (printflags & PRINT_KV_PAIR) {
+ if (printflags & PRINT_LINE)
+ printf("\n ");
+ putchar('[');
+ }
quotedzputs(p->node.nam, stdout);
+ if (printflags & PRINT_KV_PAIR)
+ printf("]=");
printparamvalue(p, printflags);
}
diff --git a/Src/parse.c b/Src/parse.c
index 27052527d..47e5a246a 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -808,8 +808,13 @@ par_sublist(int *cmplx)
WC_SUBLIST_END),
f, (e - 1 - p), c);
cmdpop();
- } else
+ } else {
+ if (tok == AMPER || tok == AMPERBANG) {
+ c = 1;
+ *cmplx |= c;
+ }
set_sublist_code(p, WC_SUBLIST_END, f, (e - 1 - p), c);
+ }
return 1;
} else {
ecused--;
@@ -1843,6 +1848,10 @@ par_simple(int *cmplx, int nr)
incmdpos = oldcmdpos;
isnull = 0;
assignments = 1;
+ } else if (IS_REDIROP(tok)) {
+ *cmplx = c = 1;
+ nr += par_redir(&r, NULL);
+ continue;
} else
break;
zshlex();
@@ -2737,7 +2746,8 @@ freeeprog(Eprog p)
DPUTS(p->nref < 0 && !(p->flags & EF_HEAP), "Real EPROG has nref < 0");
DPUTS(p->nref < -1, "Uninitialised EPROG nref");
#ifdef MAX_FUNCTION_DEPTH
- DPUTS(p->nref > MAX_FUNCTION_DEPTH + 10, "Overlarge EPROG nref");
+ DPUTS(zsh_funcnest >=0 && p->nref > zsh_funcnest + 10,
+ "Overlarge EPROG nref");
#endif
if (p->nref > 0 && !--p->nref) {
for (i = p->npats, pp = p->pats; i--; pp++)
@@ -3667,15 +3677,15 @@ try_dump_file(char *path, char *name, char *file, int *ksh, int test_only)
* function. */
queue_signals();
if (!rd &&
- (rc || std.st_mtime > stc.st_mtime) &&
- (rn || std.st_mtime > stn.st_mtime) &&
+ (rc || std.st_mtime >= stc.st_mtime) &&
+ (rn || std.st_mtime >= stn.st_mtime) &&
(prog = check_dump_file(dig, &std, name, ksh, test_only))) {
unqueue_signals();
return prog;
}
/* No digest file. Now look for the per-function compiled file. */
if (!rc &&
- (rn || stc.st_mtime > stn.st_mtime) &&
+ (rn || stc.st_mtime >= stn.st_mtime) &&
(prog = check_dump_file(wc, &stc, name, ksh, test_only))) {
unqueue_signals();
return prog;
@@ -3714,7 +3724,7 @@ try_source_file(char *file)
rn = stat(file, &stn);
queue_signals();
- if (!rc && (rn || stc.st_mtime > stn.st_mtime) &&
+ if (!rc && (rn || stc.st_mtime >= stn.st_mtime) &&
(prog = check_dump_file(wc, &stc, tail, NULL, 0))) {
unqueue_signals();
return prog;
diff --git a/Src/prompt.c b/Src/prompt.c
index c478e69fb..95da52559 100644
--- a/Src/prompt.c
+++ b/Src/prompt.c
@@ -315,7 +315,7 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
case '/':
case 'C':
/* `/' gives 0, `/any' gives 1, etc. */
- if (*ss++ == '/' && *ss)
+ if (*ss && *ss++ == '/' && *ss)
arg--;
for (; *ss; ss++)
if (*ss == '/')
@@ -1087,7 +1087,7 @@ countprompt(char *str, int *wp, int *hp, int overf)
#endif
for (; *str; str++) {
- if (w >= zterm_columns && overf >= 0) {
+ if (w > zterm_columns && overf >= 0) {
w = 0;
h++;
}
diff --git a/Src/subst.c b/Src/subst.c
index 5b1bf8988..a265a187e 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -35,6 +35,50 @@
/**/
char nulstring[] = {Nularg, '\0'};
+/* Check for array assignent with entries like [key]=val.
+ *
+ * Insert Marker node, convert following nodes to list to alternate key
+ * / val form, perform appropriate substitution, and return last
+ * inserted (value) node if found.
+ *
+ * Caller to check errflag.
+ */
+
+/**/
+static LinkNode
+keyvalpairelement(LinkList list, LinkNode node)
+{
+ char *start, *end, *dat;
+
+ if ((start = (char *)getdata(node)) &&
+ start[0] == Inbrack &&
+ (end = strchr(start+1, Outbrack)) &&
+ /* ..]=value or ]+=Value */
+ (end[1] == Equals ||
+ (end[1] == '+' && end[2] == Equals))) {
+ static char marker[2] = { Marker, '\0' };
+ static char marker_plus[3] = { Marker, '+', '\0' };
+ *end = '\0';
+
+ dat = start + 1;
+ singsub(&dat);
+ untokenize(dat);
+ if (end[1] == '+') {
+ setdata(node, marker_plus);
+ node = insertlinknode(list, node, dat);
+ dat = end + 3;
+ } else {
+ setdata(node, marker);
+ node = insertlinknode(list, node, dat);
+ dat = end + 2;
+ }
+ singsub(&dat);
+ untokenize(dat);
+ return insertlinknode(list, node, dat);
+ }
+ return NULL;
+}
+
/* Do substitutions before fork. These are:
* - Process substitution: <(...), >(...), =(...)
* - Parameter substitution
@@ -46,24 +90,35 @@ char nulstring[] = {Nularg, '\0'};
*
* "flag"s contains PREFORK_* flags, defined in zsh.h.
*
- * "ret_flags" is used to return values from nested parameter
- * substitions. It may be NULL in which case PREFORK_SUBEXP
- * must not appear in flags; any return value from below
- * will be discarded.
+ * "ret_flags" is used to return PREFORK_* values from nested parameter
+ * substitions. It may be NULL in which case PREFORK_SUBEXP must not
+ * appear in flags; any return value from below will be discarded.
*/
/**/
mod_export void
prefork(LinkList list, int flags, int *ret_flags)
{
- LinkNode node, stop = 0;
+ LinkNode node, insnode, stop = 0;
int keep = 0, asssub = (flags & PREFORK_TYPESET) && isset(KSHTYPESET);
int ret_flags_local = 0;
if (!ret_flags)
ret_flags = &ret_flags_local; /* will be discarded */
queue_signals();
- for (node = firstnode(list); node; incnode(node)) {
+ node = firstnode(list);
+ while (node) {
+ if ((flags & (PREFORK_SINGLE|PREFORK_ASSIGN)) == PREFORK_ASSIGN &&
+ (insnode = keyvalpairelement(list, node))) {
+ node = insnode;
+ incnode(node);
+ *ret_flags |= PREFORK_KEY_VALUE;
+ continue;
+ }
+ if (errflag) {
+ unqueue_signals();
+ return;
+ }
if (isset(SHFILEEXPANSION)) {
/*
* Here and below we avoid taking the address
@@ -82,11 +137,29 @@ prefork(LinkList list, int flags, int *ret_flags)
*/
setdata(node, cptr);
}
- if (!(node = stringsubst(list, node,
- flags & ~(PREFORK_TYPESET|PREFORK_ASSIGN),
- ret_flags, asssub))) {
- unqueue_signals();
- return;
+ else
+ {
+ if (!(node = stringsubst(list, node,
+ flags & ~(PREFORK_TYPESET|PREFORK_ASSIGN),
+ ret_flags, asssub))) {
+ unqueue_signals();
+ return;
+ }
+ }
+ incnode(node);
+ }
+ if (isset(SHFILEEXPANSION)) {
+ /*
+ * stringsubst() may insert new nodes, so doesn't work
+ * well in the same loop as file expansion.
+ */
+ for (node = firstnode(list); node; incnode(node)) {
+ if (!(node = stringsubst(list, node,
+ flags & ~(PREFORK_TYPESET|PREFORK_ASSIGN),
+ ret_flags, asssub))) {
+ unqueue_signals();
+ return;
+ }
}
}
for (node = firstnode(list); node; incnode(node)) {
@@ -107,7 +180,9 @@ prefork(LinkList list, int flags, int *ret_flags)
filesub(&cptr, flags & (PREFORK_TYPESET|PREFORK_ASSIGN));
setdata(node, cptr);
}
- } else if (!(flags & PREFORK_SINGLE) && !keep)
+ } else if (!(flags & PREFORK_SINGLE) &&
+ !(*ret_flags & PREFORK_KEY_VALUE) &&
+ !keep)
uremnode(list, node);
if (errflag) {
unqueue_signals();
@@ -400,16 +475,31 @@ quotesubst(char *str)
return str;
}
+/* Glob entries of a linked list.
+ *
+ * flags are from PREFORK_*, but only two are handled:
+ * - PREFORK_NO_UNTOK: pass into zglob() a flag saying do not untokenise.
+ * - PREFORK_KEY_VALUE: look out for Marker / Key / Value list triads
+ * and don't glob them. The key and value should already have
+ * been untokenised as they are not subject to further expansion.
+ */
+
/**/
mod_export void
-globlist(LinkList list, int nountok)
+globlist(LinkList list, int flags)
{
LinkNode node, next;
badcshglob = 0;
for (node = firstnode(list); !errflag && node; node = next) {
next = nextnode(node);
- zglob(list, node, nountok);
+ if ((flags & PREFORK_KEY_VALUE) &&
+ *(char *)getdata(node) == Marker) {
+ /* Skip key / value pair */
+ next = nextnode(nextnode(next));
+ } else {
+ zglob(list, node, (flags & PREFORK_NO_UNTOK) != 0);
+ }
}
if (noerrs)
badcshglob = 0;
@@ -2340,7 +2430,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
val = aval[0];
isarr = 0;
}
- s = dyncat(val, s);
+ s = val ? dyncat(val, s) : dupstring(s);
/* Now behave po-faced as if it was always like that... */
subexp = 0;
/*
@@ -2389,7 +2479,13 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
*/
if (!subexp || aspar) {
char *ov = val;
-
+ int scanflags = hkeys | hvals;
+ if (arrasg)
+ scanflags |= SCANPM_ASSIGNING;
+ if (qt)
+ scanflags |= SCANPM_DQUOTED;
+ if (chkset)
+ scanflags |= SCANPM_CHECKING;
/*
* Second argument: decide whether to use the subexpression or
* the string next on the line as the parameter name.
@@ -2418,9 +2514,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
if (!(v = fetchvalue(&vbuf, (subexp ? &ov : &s),
(wantt ? -1 :
((unset(KSHARRAYS) || inbrace) ? 1 : -1)),
- hkeys|hvals|
- (arrasg ? SCANPM_ASSIGNING : 0)|
- (qt ? SCANPM_DQUOTED : 0))) ||
+ scanflags)) ||
(v->pm && (v->pm->node.flags & PM_UNSET)) ||
(v->flags & VALFLAG_EMPTY))
vunset = 1;
@@ -3747,11 +3841,15 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
if (isarr) {
char **ap;
- for (ap = aval; *ap; ap++)
+ for (ap = aval; *ap; ap++) {
+ untokenize(*ap);
list = bufferwords(list, *ap, NULL, shsplit);
+ }
isarr = 0;
- } else
+ } else {
+ untokenize(val);
list = bufferwords(NULL, val, NULL, shsplit);
+ }
if (!list || !firstnode(list))
val = dupstring("");
diff --git a/Src/utils.c b/Src/utils.c
index 5055d69fe..3587c3622 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -1832,7 +1832,7 @@ adjustlines(int signalled)
else
shttyinfo.winsize.ws_row = zterm_lines;
#endif /* TIOCGWINSZ */
- if (zterm_lines <= 0) {
+ if (zterm_lines < 0) {
DPUTS(signalled, "BUG: Impossible TIOCGWINSZ rows");
zterm_lines = tclines > 0 ? tclines : 24;
}
@@ -1856,7 +1856,7 @@ adjustcolumns(int signalled)
else
shttyinfo.winsize.ws_col = zterm_columns;
#endif /* TIOCGWINSZ */
- if (zterm_columns <= 0) {
+ if (zterm_columns < 0) {
DPUTS(signalled, "BUG: Impossible TIOCGWINSZ cols");
zterm_columns = tccolumns > 0 ? tccolumns : 80;
}
@@ -2177,10 +2177,12 @@ gettempfile(const char *prefix, int use_heap, char **tempname)
{
char *fn;
int fd;
+ mode_t old_umask;
#if HAVE_MKSTEMP
char *suffix = prefix ? ".XXXXXX" : "XXXXXX";
queue_signals();
+ old_umask = umask(0177);
if (!prefix && !(prefix = getsparam("TMPPREFIX")))
prefix = DEFAULT_TMPPREFIX;
if (use_heap)
@@ -2198,6 +2200,7 @@ gettempfile(const char *prefix, int use_heap, char **tempname)
int failures = 0;
queue_signals();
+ old_umask = umask(0177);
do {
if (!(fn = gettempname(prefix, use_heap))) {
fd = -1;
@@ -2212,6 +2215,7 @@ gettempfile(const char *prefix, int use_heap, char **tempname)
#endif
*tempname = fn;
+ umask(old_umask);
unqueue_signals();
return fd;
}
@@ -2283,10 +2287,11 @@ struncpy(char **s, char *t, int n)
{
char *u = *s;
- while (n--)
- *u++ = *t++;
+ while (n-- && (*u = *t++))
+ u++;
*s = u;
- *u = '\0';
+ if (n > 0) /* just one null-byte will do, unlike strncpy(3) */
+ *u = '\0';
}
/* Return the number of elements in an array of pointers. *
@@ -2455,6 +2460,67 @@ zstrtol_underscore(const char *s, char **t, int base, int underscore)
return neg ? -(zlong)calc : (zlong)calc;
}
+/*
+ * If s represents a complete unsigned integer (and nothing else)
+ * return 1 and set retval to the value. Otherwise return 0.
+ *
+ * Underscores are always allowed.
+ *
+ * Sensitive to OCTAL_ZEROES.
+ */
+
+/**/
+mod_export int
+zstrtoul_underscore(const char *s, zulong *retval)
+{
+ zulong calc = 0, newcalc = 0, base;
+
+ if (*s == '+')
+ s++;
+
+ if (*s != '0')
+ base = 10;
+ else if (*++s == 'x' || *s == 'X')
+ base = 16, s++;
+ else if (*s == 'b' || *s == 'B')
+ base = 2, s++;
+ else
+ base = isset(OCTALZEROES) ? 8 : 10;
+ if (base < 2 || base > 36) {
+ return 0;
+ } else if (base <= 10) {
+ for (; (*s >= '0' && *s < ('0' + base)) ||
+ *s == '_'; s++) {
+ if (*s == '_')
+ continue;
+ newcalc = calc * base + *s - '0';
+ if (newcalc < calc)
+ {
+ return 0;
+ }
+ calc = newcalc;
+ }
+ } else {
+ for (; idigit(*s) || (*s >= 'a' && *s < ('a' + base - 10))
+ || (*s >= 'A' && *s < ('A' + base - 10))
+ || *s == '_'; s++) {
+ if (*s == '_')
+ continue;
+ newcalc = calc*base + (idigit(*s) ? (*s - '0') : (*s & 0x1f) + 9);
+ if (newcalc < calc)
+ {
+ return 0;
+ }
+ calc = newcalc;
+ }
+ }
+
+ if (*s)
+ return 0;
+ *retval = calc;
+ return 1;
+}
+
/**/
mod_export int
setblock_fd(int turnonblocking, int fd, long *modep)
@@ -2709,6 +2775,9 @@ checkrmall(char *s)
const int max_count = 100;
if ((rmd = opendir(unmeta(s)))) {
int ignoredots = !isset(GLOBDOTS);
+ /* ### TODO: Passing ignoredots here is wrong. See workers/41672
+ aka <https://bugs.debian.org/875460>.
+ */
while (zreaddir(rmd, ignoredots)) {
count++;
if (count > max_count)
@@ -2724,8 +2793,10 @@ checkrmall(char *s)
else if (count > 0)
fprintf(shout, "zsh: sure you want to delete all %d files in ",
count);
- else
+ else {
+ /* We don't know how many files the glob will expand to; see 41707. */
fprintf(shout, "zsh: sure you want to delete all the files in ");
+ }
nicezputs(s, shout);
if(isset(RMSTARWAIT)) {
fputs("? (waiting ten seconds)", shout);
@@ -4325,7 +4396,7 @@ spname(char *oldname)
* Rationale for this, if there ever was any, has been forgotten. */
for (;;) {
while (*old == '/') {
- if ((new - newname) >= (sizeof(newname)-1))
+ if (new >= newname + sizeof(newname) - 1)
return NULL;
*new++ = *old++;
}
@@ -4354,17 +4425,20 @@ spname(char *oldname)
* odd to the human reader, and we may make use of the total *
* distance for all corrections at some point in the future. */
if (bestdist < maxthresh) {
- strcpy(new, spnameguess);
- strcat(new, old);
- return newname;
+ struncpy(&new, spnameguess, sizeof(newname) - (new - newname));
+ struncpy(&new, old, sizeof(newname) - (new - newname));
+ return (new >= newname + sizeof(newname) -1) ? NULL : newname;
} else
return NULL;
} else {
maxthresh = bestdist + thresh;
bestdist += thisdist;
}
- for (p = spnamebest; (*new = *p++);)
+ for (p = spnamebest; (*new = *p++);) {
+ if (new >= newname + sizeof(newname) - 1)
+ return NULL;
new++;
+ }
}
}
@@ -4947,6 +5021,16 @@ ztrsub(char const *t, char const *s)
return l;
}
+/*
+ * Wrapper for readdir().
+ *
+ * If ignoredots is true, skip the "." and ".." entries.
+ *
+ * When __APPLE__ is defined, recode dirent names from UTF-8-MAC to UTF-8.
+ *
+ * Return the dirent's name, metafied.
+ */
+
/**/
mod_export char *
zreaddir(DIR *dir, int ignoredots)
@@ -5420,7 +5504,7 @@ mb_metastrlenend(char *ptr, int width, char *eptr)
wchar_t wc;
int num, num_in_char, complete;
- if (!isset(MULTIBYTE))
+ if (!isset(MULTIBYTE) || MB_CUR_MAX == 1)
return eptr ? (int)(eptr - ptr) : ztrlen(ptr);
laststart = ptr;
diff --git a/Src/zsh.h b/Src/zsh.h
index abe9a9c82..8b4898477 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -223,9 +223,16 @@ struct mathfunc {
* tokens here.
*/
/*
- * Marker used in paramsubst for rc_expand_param.
- * Also used in pattern character arrays as guaranteed not to
- * mark a character in a string.
+ * Marker is used in the following special circumstances:
+ * - In paramsubst for rc_expand_param.
+ * - In pattern character arrays as guaranteed not to mark a character in
+ * a string.
+ * - In assignments with the ASSPM_KEY_VALUE flag set in order to
+ * mark that there is a key / value pair following. If this
+ * comes from [key]=value the Marker is followed by a null;
+ * if from [key]+=value the Marker is followed by a '+' then a null.
+ * All the above are local uses --- any case where the Marker has
+ * escaped beyond the context in question is an error.
*/
#define Marker ((char) 0xa2)
@@ -1217,17 +1224,25 @@ struct alias {
struct asgment {
struct linknode node;
char *name;
- int is_array;
+ int flags;
union {
char *scalar;
LinkList array;
} value;
};
+/* Flags for flags element of asgment */
+enum {
+ /* Array value */
+ ASG_ARRAY = 1,
+ /* Key / value array pair */
+ ASG_KEY_VALUE = 2
+};
+
/*
* Assignment is array?
*/
-#define ASG_ARRAYP(asg) ((asg)->is_array)
+#define ASG_ARRAYP(asg) ((asg)->flags & ASG_ARRAY)
/*
* Assignment has value?
@@ -1361,6 +1376,14 @@ struct options {
int argscount, argsalloc;
};
+/* Flags to parseargs() */
+
+enum {
+ PARSEARGS_TOPLEVEL = 0x1, /* Call to initialise shell */
+ PARSEARGS_LOGIN = 0x2 /* Shell is login shell */
+};
+
+
/*
* Handler arguments are: builtin name, null-terminated argument
* list excluding command name, option structure, the funcid element from the
@@ -1852,6 +1875,7 @@ struct tieddata {
#define PM_DONTIMPORT_SUID (1<<19) /* do not import if running setuid */
#define PM_LOADDIR (1<<19) /* (function) filename gives load directory */
#define PM_SINGLE (1<<20) /* special can only have a single instance */
+#define PM_ANONYMOUS (1<<20) /* (function) anonymous function */
#define PM_LOCAL (1<<21) /* this parameter will be made local */
#define PM_SPECIAL (1<<22) /* special builtin parameter */
#define PM_DONTIMPORT (1<<23) /* do not import this variable */
@@ -1890,6 +1914,7 @@ struct tieddata {
* necessarily want to match multiple
* elements
*/
+#define SCANPM_CHECKING (1<<10) /* Check if set, no need to create */
/* "$foo[@]"-style substitution
* Only sign bit is significant
*/
@@ -1953,7 +1978,14 @@ enum {
/* SHWORDSPLIT forced off in nested subst */
PREFORK_NOSHWORDSPLIT = 0x20,
/* Prefork is part of a parameter subexpression */
- PREFORK_SUBEXP = 0x40
+ PREFORK_SUBEXP = 0x40,
+ /* Prefork detected an assignment list with [key]=value syntax,
+ * Only used on return from prefork, not meaningful passed down.
+ * Also used as flag to globlist.
+ */
+ PREFORK_KEY_VALUE = 0x80,
+ /* No untokenise: used only as flag to globlist */
+ PREFORK_NO_UNTOK = 0x100
};
/*
@@ -2052,6 +2084,11 @@ enum {
ASSPM_WARN = (ASSPM_WARN_CREATE|ASSPM_WARN_NESTED),
/* Import from environment, so exercise care evaluating value */
ASSPM_ENV_IMPORT = 1 << 3,
+ /* Array is key / value pairs.
+ * This is normal for associative arrays but variant behaviour for
+ * normal arrays.
+ */
+ ASSPM_KEY_VALUE = 1 << 4
};
/* node for named directory hash table (nameddirtab) */
@@ -2092,13 +2129,14 @@ typedef groupset *Groupset;
#define PRINT_KV_PAIR (1<<3)
#define PRINT_INCLUDEVALUE (1<<4)
#define PRINT_TYPESET (1<<5)
+#define PRINT_LINE (1<<6)
/* flags for printing for the whence builtin */
-#define PRINT_WHENCE_CSH (1<<6)
-#define PRINT_WHENCE_VERBOSE (1<<7)
-#define PRINT_WHENCE_SIMPLE (1<<8)
-#define PRINT_WHENCE_FUNCDEF (1<<9)
-#define PRINT_WHENCE_WORD (1<<10)
+#define PRINT_WHENCE_CSH (1<<7)
+#define PRINT_WHENCE_VERBOSE (1<<8)
+#define PRINT_WHENCE_SIMPLE (1<<9)
+#define PRINT_WHENCE_FUNCDEF (1<<10)
+#define PRINT_WHENCE_WORD (1<<11)
/* Return values from loop() */
@@ -2301,6 +2339,7 @@ enum {
CHASEDOTS,
CHASELINKS,
CHECKJOBS,
+ CHECKRUNNINGJOBS,
CLOBBER,
APPENDCREATE,
COMBININGCHARS,
@@ -2953,10 +2992,12 @@ struct hist_stack {
void (*hungetc) _((int));
void (*hwaddc) _((int));
void (*hwbegin) _((int));
+ void (*hwabort) _((void));
void (*hwend) _((void));
void (*addtoline) _((int));
unsigned char *cstack;
int csp;
+ int hist_keep_comment;
};
/*