diff options
Diffstat (limited to 'Src')
-rw-r--r-- | Src/Modules/db_gdbm.c | 2 | ||||
-rw-r--r-- | Src/Modules/mapfile.c | 2 | ||||
-rw-r--r-- | Src/Modules/pcre.c | 35 | ||||
-rw-r--r-- | Src/Modules/system.c | 14 | ||||
-rw-r--r-- | Src/Modules/zpty.c | 7 | ||||
-rw-r--r-- | Src/Zle/compctl.c | 70 | ||||
-rw-r--r-- | Src/Zle/complete.c | 54 | ||||
-rw-r--r-- | Src/Zle/complist.c | 6 | ||||
-rw-r--r-- | Src/Zle/computil.c | 44 | ||||
-rw-r--r-- | Src/Zle/textobjects.c | 7 | ||||
-rw-r--r-- | Src/Zle/zle.h | 2 | ||||
-rw-r--r-- | Src/Zle/zle_thingy.c | 5 | ||||
-rw-r--r-- | Src/Zle/zle_tricky.c | 11 | ||||
-rw-r--r-- | Src/Zle/zle_word.c | 119 | ||||
-rw-r--r-- | Src/builtin.c | 118 | ||||
-rw-r--r-- | Src/cond.c | 20 | ||||
-rw-r--r-- | Src/exec.c | 341 | ||||
-rw-r--r-- | Src/glob.c | 2 | ||||
-rw-r--r-- | Src/hashtable.c | 2 | ||||
-rw-r--r-- | Src/hashtable.h | 1 | ||||
-rw-r--r-- | Src/hist.c | 62 | ||||
-rw-r--r-- | Src/init.c | 132 | ||||
-rw-r--r-- | Src/jobs.c | 47 | ||||
-rw-r--r-- | Src/lex.c | 24 | ||||
-rw-r--r-- | Src/loop.c | 9 | ||||
-rw-r--r-- | Src/math.c | 10 | ||||
-rw-r--r-- | Src/mem.c | 14 | ||||
-rw-r--r-- | Src/options.c | 1 | ||||
-rw-r--r-- | Src/params.c | 264 | ||||
-rw-r--r-- | Src/parse.c | 22 | ||||
-rw-r--r-- | Src/prompt.c | 4 | ||||
-rw-r--r-- | Src/subst.c | 140 | ||||
-rw-r--r-- | Src/utils.c | 108 | ||||
-rw-r--r-- | Src/zsh.h | 63 |
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, ¨en); + 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? */ @@ -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; } @@ -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; @@ -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; }; /* |