summaryrefslogtreecommitdiff
path: root/Src
diff options
context:
space:
mode:
Diffstat (limited to 'Src')
-rw-r--r--Src/Builtins/rlimits.c25
-rw-r--r--Src/Builtins/sched.c2
-rw-r--r--Src/Modules/datetime.c2
-rw-r--r--Src/Modules/parameter.c30
-rw-r--r--Src/Modules/pcre.c11
-rw-r--r--Src/Modules/stat.c2
-rw-r--r--Src/Modules/zutil.c159
-rw-r--r--Src/Zle/compcore.c14
-rw-r--r--Src/Zle/compctl.c4
-rw-r--r--Src/Zle/complist.c19
-rw-r--r--Src/Zle/compresult.c2
-rw-r--r--Src/Zle/computil.c10
-rw-r--r--Src/Zle/iwidgets.list1
-rw-r--r--Src/Zle/zle_hist.c4
-rw-r--r--Src/Zle/zle_main.c66
-rw-r--r--Src/Zle/zle_misc.c28
-rw-r--r--Src/Zle/zle_params.c2
-rw-r--r--Src/Zle/zle_refresh.c9
-rw-r--r--Src/Zle/zle_tricky.c5
-rw-r--r--Src/Zle/zle_utils.c52
-rw-r--r--Src/Zle/zle_vi.c37
-rw-r--r--Src/builtin.c54
-rw-r--r--Src/compat.c2
-rw-r--r--Src/cond.c24
-rw-r--r--Src/exec.c52
-rw-r--r--Src/glob.c305
-rw-r--r--Src/hist.c78
-rw-r--r--Src/init.c24
-rw-r--r--Src/jobs.c32
-rw-r--r--Src/lex.c10
-rw-r--r--Src/loop.c20
-rw-r--r--Src/math.c35
-rw-r--r--Src/mem.c125
-rw-r--r--Src/mkmakemod.sh4
-rw-r--r--Src/options.c3
-rw-r--r--Src/params.c186
-rw-r--r--Src/parse.c45
-rw-r--r--Src/pattern.c8
-rw-r--r--Src/prompt.c24
-rw-r--r--Src/prototypes.h2
-rw-r--r--Src/signals.c10
-rw-r--r--Src/subst.c73
-rw-r--r--Src/utils.c119
-rw-r--r--Src/watch.c2
-rw-r--r--Src/zsh.h32
-rw-r--r--Src/zsh.mdd2
-rw-r--r--Src/zsh_system.h14
47 files changed, 1391 insertions, 378 deletions
diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c
index fd4c94aaa..9da31831d 100644
--- a/Src/Builtins/rlimits.c
+++ b/Src/Builtins/rlimits.c
@@ -32,12 +32,17 @@
#if defined(HAVE_GETRLIMIT) && defined(RLIM_INFINITY)
-#ifdef RLIMIT_POSIXLOCKS
+#if defined(HAVE_RLIMIT_POSIXLOCKS) && !defined(HAVE_RLIMIT_LOCKS)
# define RLIMIT_LOCKS RLIMIT_POSIXLOCKS
+# define HAVE_RLIMIT_LOCKS 1
#endif
-#ifdef RLIMIT_NTHR
+#if defined(HAVE_RLIMIT_NTHR) && !defined(HAVE_RLIMIT_PTHREAD)
# define RLIMIT_PTHREAD RLIMIT_NTHR
+# define HAVE_RLIMIT_PTHREAD 1
+# define THREAD_FMT "-T: threads "
+#else
+# define THREAD_FMT "-T: threads per process "
#endif
enum {
@@ -367,13 +372,13 @@ printulimit(char *nam, int lim, int hard, int head)
# ifdef HAVE_RLIMIT_SBSIZE
case RLIMIT_SBSIZE:
if (head)
- printf("-b: socket buffer size (bytes) ", RLIMIT_SBSIZE);
+ printf("-b: socket buffer size (bytes) ");
break;
# endif /* HAVE_RLIMIT_SBSIZE */
# ifdef HAVE_RLIMIT_PTHREAD
case RLIMIT_PTHREAD:
if (head)
- printf("-T: threads per process ");
+ printf("%s", THREAD_FMT);
break;
# endif /* HAVE_RLIMIT_PTHREAD */
# ifdef HAVE_RLIMIT_NICE
@@ -860,6 +865,13 @@ bin_ulimit(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
case 'r':
res = RLIMIT_RTPRIO;
break;
+# else
+# ifdef HAVE_RLIMIT_NTHR
+ /* For compatibility with sh on NetBSD */
+ case 'r':
+ res = RLIMIT_NTHR;
+ break;
+# endif /* HAVE_RLIMIT_NTHR */
# endif
# ifdef HAVE_RLIMIT_NPTS
case 'p':
@@ -876,6 +888,11 @@ bin_ulimit(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
res = RLIMIT_KQUEUES;
break;
# endif
+# ifdef HAVE_RLIMIT_PTHREAD
+ case 'T':
+ res = RLIMIT_PTHREAD;
+ break;
+# endif
default:
/* unrecognised limit */
zwarnnam(name, "bad option: -%c", *options);
diff --git a/Src/Builtins/sched.c b/Src/Builtins/sched.c
index 1ec3269bd..c1cc98354 100644
--- a/Src/Builtins/sched.c
+++ b/Src/Builtins/sched.c
@@ -211,7 +211,7 @@ bin_sched(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
t = sch->time;
tmp = localtime(&t);
- ztrftime(tbuf, 40, "%a %b %e %k:%M:%S", tmp);
+ ztrftime(tbuf, 40, "%a %b %e %k:%M:%S", tmp, 0L);
if (sch->flags & SCHEDFLAG_TRASH_ZLE)
flagstr = "-o ";
else
diff --git a/Src/Modules/datetime.c b/Src/Modules/datetime.c
index a4e7eca86..00ebd2b49 100644
--- a/Src/Modules/datetime.c
+++ b/Src/Modules/datetime.c
@@ -130,7 +130,7 @@ bin_strftime(char *nam, char **argv, Options ops, UNUSED(int func))
buffer = zalloc(bufsize);
for (x=0; x < 4; x++) {
- if (ztrftime(buffer, bufsize, argv[0], t) >= 0)
+ if (ztrftime(buffer, bufsize, argv[0], t, 0L) >= 0)
break;
buffer = zrealloc(buffer, bufsize *= 2);
}
diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c
index 22148f991..0385a709e 100644
--- a/Src/Modules/parameter.c
+++ b/Src/Modules/parameter.c
@@ -106,7 +106,7 @@ getpmparameter(UNUSED(HashTable ht), const char *name)
pm->u.str = paramtypestr(rpm);
else {
pm->u.str = dupstring("");
- pm->node.flags |= PM_UNSET;
+ pm->node.flags |= (PM_UNSET|PM_SPECIAL);
}
return &pm->node;
}
@@ -224,7 +224,7 @@ getpmcommand(UNUSED(HashTable ht), const char *name)
}
} else {
pm->u.str = dupstring("");
- pm->node.flags |= PM_UNSET;
+ pm->node.flags |= (PM_UNSET|PM_SPECIAL);
}
return &pm->node;
}
@@ -410,7 +410,7 @@ getfunction(UNUSED(HashTable ht), const char *name, int dis)
}
} else {
pm->u.str = dupstring("");
- pm->node.flags |= PM_UNSET;
+ pm->node.flags |= (PM_UNSET|PM_SPECIAL);
}
return &pm->node;
}
@@ -661,7 +661,7 @@ getbuiltin(UNUSED(HashTable ht), const char *name, int dis)
pm->u.str = dupstring(t);
} else {
pm->u.str = dupstring("");
- pm->node.flags |= PM_UNSET;
+ pm->node.flags |= (PM_UNSET|PM_SPECIAL);
}
return &pm->node;
}
@@ -876,7 +876,7 @@ getpmoption(UNUSED(HashTable ht), const char *name)
}
else {
pm->u.str = dupstring("");
- pm->node.flags |= PM_UNSET;
+ pm->node.flags |= (PM_UNSET|PM_SPECIAL);
}
return &pm->node;
}
@@ -934,7 +934,7 @@ getpmmodule(UNUSED(HashTable ht), const char *name)
pm->u.str = dupstring(type);
else {
pm->u.str = dupstring("");
- pm->node.flags |= PM_UNSET;
+ pm->node.flags |= (PM_UNSET|PM_SPECIAL);
}
return &pm->node;
}
@@ -1048,7 +1048,7 @@ getpmhistory(UNUSED(HashTable ht), const char *name)
pm->u.str = dupstring(he->node.nam);
else {
pm->u.str = dupstring("");
- pm->node.flags |= PM_UNSET;
+ pm->node.flags |= (PM_UNSET|PM_SPECIAL);
}
return &pm->node;
}
@@ -1158,7 +1158,7 @@ getpmjobtext(UNUSED(HashTable ht), const char *name)
pm->u.str = pmjobtext(job);
else {
pm->u.str = dupstring("");
- pm->node.flags |= PM_UNSET;
+ pm->node.flags |= (PM_UNSET|PM_SPECIAL);
}
return &pm->node;
}
@@ -1259,7 +1259,7 @@ getpmjobstate(UNUSED(HashTable ht), const char *name)
pm->u.str = pmjobstate(job);
else {
pm->u.str = dupstring("");
- pm->node.flags |= PM_UNSET;
+ pm->node.flags |= (PM_UNSET|PM_SPECIAL);
}
return &pm->node;
}
@@ -1325,7 +1325,7 @@ getpmjobdir(UNUSED(HashTable ht), const char *name)
pm->u.str = pmjobdir(job);
else {
pm->u.str = dupstring("");
- pm->node.flags |= PM_UNSET;
+ pm->node.flags |= (PM_UNSET|PM_SPECIAL);
}
return &pm->node;
}
@@ -1451,7 +1451,7 @@ getpmnameddir(UNUSED(HashTable ht), const char *name)
pm->u.str = dupstring(nd->dir);
else {
pm->u.str = dupstring("");
- pm->node.flags |= PM_UNSET;
+ pm->node.flags |= (PM_UNSET|PM_SPECIAL);
}
return &pm->node;
}
@@ -1502,7 +1502,7 @@ getpmuserdir(UNUSED(HashTable ht), const char *name)
pm->u.str = dupstring(nd->dir);
else {
pm->u.str = dupstring("");
- pm->node.flags |= PM_UNSET;
+ pm->node.flags |= (PM_UNSET|PM_SPECIAL);
}
return &pm->node;
}
@@ -1754,7 +1754,7 @@ getalias(HashTable alht, UNUSED(HashTable ht), const char *name, int flags)
pm->u.str = dupstring(al->text);
else {
pm->u.str = dupstring("");
- pm->node.flags |= PM_UNSET;
+ pm->node.flags |= (PM_UNSET|PM_SPECIAL);
}
return &pm->node;
}
@@ -1950,7 +1950,7 @@ getpmusergroups(UNUSED(HashTable ht), const char *name)
if (!gs) {
zerr("failed to retrieve groups for user: %e", errno);
pm->u.str = dupstring("");
- pm->node.flags |= PM_UNSET;
+ pm->node.flags |= (PM_UNSET|PM_SPECIAL);
return &pm->node;
}
@@ -1965,7 +1965,7 @@ getpmusergroups(UNUSED(HashTable ht), const char *name)
}
pm->u.str = dupstring("");
- pm->node.flags |= PM_UNSET;
+ pm->node.flags |= (PM_UNSET|PM_SPECIAL);
return &pm->node;
}
diff --git a/Src/Modules/pcre.c b/Src/Modules/pcre.c
index cb9f8ef57..040a33f8e 100644
--- a/Src/Modules/pcre.c
+++ b/Src/Modules/pcre.c
@@ -289,14 +289,11 @@ bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func))
matched_portion = OPT_ARG(ops,c);
}
if(OPT_HASARG(ops,c='n')) { /* The offset position to start the search, in bytes. */
- offset_start = getposint(OPT_ARG(ops,c), nam);
+ if ((offset_start = getposint(OPT_ARG(ops,c), nam) < 0))
+ return 1;
}
/* For the entire match, 'Return' the offset byte positions instead of the matched string */
if(OPT_ISSET(ops,'b')) want_offset_pair = 1;
-
- if(!*args) {
- zwarnnam(nam, "not enough arguments");
- }
if ((ret = pcre_fullinfo(pcre_pattern, pcre_hints, PCRE_INFO_CAPTURECOUNT, &capcount)))
{
@@ -311,7 +308,7 @@ bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func))
unmetafy(plaintext, NULL);
subject_len = (int)strlen(plaintext);
- if (offset_start < 0 || offset_start >= subject_len)
+ if (offset_start > 0 && offset_start >= subject_len)
ret = PCRE_ERROR_NOMATCH;
else
ret = pcre_exec(pcre_pattern, pcre_hints, plaintext, subject_len, offset_start, 0, ovec, ovecsize);
@@ -345,6 +342,8 @@ cond_pcre_match(char **a, int id)
if (zpcre_utf8_enabled())
pcre_opts |= PCRE_UTF8;
+ if (isset(REMATCHPCRE) && !isset(CASEMATCH))
+ pcre_opts |= PCRE_CASELESS;
lhstr = cond_str(a,0,0);
rhre = cond_str(a,1,0);
diff --git a/Src/Modules/stat.c b/Src/Modules/stat.c
index edae0841e..6fc53894c 100644
--- a/Src/Modules/stat.c
+++ b/Src/Modules/stat.c
@@ -198,7 +198,7 @@ stattimeprint(time_t tim, char *outbuf, int flags)
if (flags & STF_STRING) {
char *oend = outbuf + strlen(outbuf);
ztrftime(oend, 40, timefmt, (flags & STF_GMT) ? gmtime(&tim) :
- localtime(&tim));
+ localtime(&tim), 0L);
if (flags & STF_RAW)
strcat(oend, ")");
}
diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c
index 399c45f46..1cca0c4b8 100644
--- a/Src/Modules/zutil.c
+++ b/Src/Modules/zutil.c
@@ -30,6 +30,55 @@
#include "zutil.mdh"
#include "zutil.pro"
+typedef struct {
+ char **match;
+ char **mbegin;
+ char **mend;
+} MatchData;
+
+static void
+savematch(MatchData *m)
+{
+ char **a;
+
+ queue_signals();
+ a = getaparam("match");
+ m->match = a ? zarrdup(a) : NULL;
+ a = getaparam("mbegin");
+ m->mbegin = a ? zarrdup(a) : NULL;
+ a = getaparam("mend");
+ m->mend = a ? zarrdup(a) : NULL;
+ unqueue_signals();
+}
+
+static void
+restorematch(MatchData *m)
+{
+ if (m->match)
+ setaparam("match", m->match);
+ else
+ unsetparam("match");
+ if (m->mbegin)
+ setaparam("mbegin", m->mbegin);
+ else
+ unsetparam("mbegin");
+ if (m->mend)
+ setaparam("mend", m->mend);
+ else
+ unsetparam("mend");
+}
+
+static void
+freematch(MatchData *m)
+{
+ if (m->match)
+ freearray(m->match);
+ if (m->mbegin)
+ freearray(m->mbegin);
+ if (m->mend)
+ freearray(m->mend);
+}
+
/* Style stuff. */
typedef struct stypat *Stypat;
@@ -370,15 +419,21 @@ lookupstyle(char *ctxt, char *style)
{
Style s;
Stypat p;
+ char **found = NULL;
s = (Style)zstyletab->getnode2(zstyletab, style);
- if (!s)
- return NULL;
- for (p = s->pats; p; p = p->next)
- if (pattry(p->prog, ctxt))
- return (p->eval ? evalstyle(p) : p->vals);
+ if (s) {
+ MatchData match;
+ savematch(&match);
+ for (p = s->pats; p; p = p->next)
+ if (pattry(p->prog, ctxt)) {
+ found = (p->eval ? evalstyle(p) : p->vals);
+ break;
+ }
+ restorematch(&match);
+ }
- return NULL;
+ return found;
}
static int
@@ -915,55 +970,6 @@ bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
/* Zregexparse stuff. */
typedef struct {
- char **match;
- char **mbegin;
- char **mend;
-} MatchData;
-
-static void
-savematch(MatchData *m)
-{
- char **a;
-
- queue_signals();
- a = getaparam("match");
- m->match = a ? zarrdup(a) : NULL;
- a = getaparam("mbegin");
- m->mbegin = a ? zarrdup(a) : NULL;
- a = getaparam("mend");
- m->mend = a ? zarrdup(a) : NULL;
- unqueue_signals();
-}
-
-static void
-restorematch(MatchData *m)
-{
- if (m->match)
- setaparam("match", m->match);
- else
- unsetparam("match");
- if (m->mbegin)
- setaparam("mbegin", m->mbegin);
- else
- unsetparam("mbegin");
- if (m->mend)
- setaparam("mend", m->mend);
- else
- unsetparam("mend");
-}
-
-static void
-freematch(MatchData *m)
-{
- if (m->match)
- freearray(m->match);
- if (m->mbegin)
- freearray(m->mbegin);
- if (m->mend)
- freearray(m->mend);
-}
-
-typedef struct {
int cutoff;
char *pattern;
Patprog patprog;
@@ -1543,6 +1549,45 @@ add_opt_val(Zoptdesc d, char *arg)
}
}
+/*
+ * For "zparseopts -K -A assoc ..." this function copies the keys and
+ * values from the default and allocates the extra space for any parsed
+ * values having the same keys. If there are no new values, creates an
+ * empty array. Returns a pointer to the NULL element marking the end.
+ *
+ * aval = pointer to the newly allocated array
+ * assoc = name of the default hash param to copy
+ * keep = whether we need to make the copy at all
+ * num = count of new values to add space for
+ */
+static char **
+zalloc_default_array(char ***aval, char *assoc, int keep, int num)
+{
+ char **ap = 0;
+
+ *aval = 0;
+ if (keep && num) {
+ struct value vbuf;
+ Value v = fetchvalue(&vbuf, &assoc, 0,
+ SCANPM_WANTKEYS|SCANPM_WANTVALS|SCANPM_MATCHMANY);
+ if (v && v->isarr) {
+ char **dp, **dval = getarrvalue(v);
+ int dnum = (dval ? arrlen(dval) : 0) + 1;
+ *aval = (char **) zalloc(((num * 2) + dnum) * sizeof(char *));
+ for (ap = *aval, dp = dval; dp && *dp; dp++) {
+ *ap = (char *) zalloc(strlen(*dp) + 1);
+ strcpy(*ap++, *dp);
+ }
+ *ap = NULL;
+ }
+ }
+ if (!ap) {
+ ap = *aval = (char **) zalloc(((num * 2) + 1) * sizeof(char *));
+ *ap = NULL;
+ }
+ return ap;
+}
+
static int
bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
{
@@ -1825,8 +1870,8 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
num++;
if (!keep || num) {
- aval = (char **) zalloc(((num * 2) + 1) * sizeof(char *));
- for (ap = aval, d = opt_descs; d; d = d->next) {
+ ap = zalloc_default_array(&aval, assoc, keep, num);
+ for (d = opt_descs; d; d = d->next) {
if (d->vals) {
*ap++ = n = (char *) zalloc(strlen(d->name) + 2);
*n = '-';
diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c
index 5c5628a8d..ac7785ab7 100644
--- a/Src/Zle/compcore.c
+++ b/Src/Zle/compcore.c
@@ -1260,6 +1260,20 @@ check_param(char *s, int set, int test)
ispar = (br >= 2 ? 2 : 1);
b[we-wb] = '\0';
return b;
+ } else if (offs > e - s && *e == ':') {
+ /*
+ * Guess whether we are in modifiers.
+ * If the name is followed by a : and the stuff after
+ * that is either colons or alphanumerics we probably are.
+ * This is a very rough guess.
+ */
+ char *offsptr = s + offs;
+ for (; e < offsptr; e++) {
+ if (*e != ':' && !ialnum(*e))
+ break;
+ }
+ ispar = (br >= 2 ? 2 : 1);
+ return NULL;
}
}
return NULL;
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index ab1857c0a..52b9e9c82 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -193,7 +193,7 @@ compctlread(char *name, char **args, Options ops, char *reply)
/* only allowed to be called for completion */
if (!incompctlfunc) {
- zwarnnam(name, "option valid only in functions called for completion");
+ zwarnnam(name, "option valid only in functions called via compctl");
return 1;
}
@@ -3391,7 +3391,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
mflags |= CMF_FILE;
for (n = firstnode(l); n; incnode(n))
addmatch(getdata(n), NULL);
- mflags &= !CMF_FILE;
+ mflags &= ~CMF_FILE;
}
opts[NULLGLOB] = ng;
} else {
diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c
index bcf356179..5e5ba9f20 100644
--- a/Src/Zle/complist.c
+++ b/Src/Zle/complist.c
@@ -383,12 +383,25 @@ getcoldef(char *s)
} else if (*s == '=') {
char *p = ++s, *t, *cols[MAX_POS];
int ncols = 0;
+ int nesting = 0;
Patprog prog;
/* This is for a pattern. */
- while (*s && *s != '=')
- s++;
+ while (*s && (nesting || *s != '=')) {
+ switch (*s++) {
+ case '\\':
+ if (*s)
+ s++;
+ break;
+ case '(':
+ nesting++;
+ break;
+ case ')':
+ nesting--;
+ break;
+ }
+ }
if (!*s)
return s;
*s++ = '\0';
@@ -2500,7 +2513,7 @@ domenuselect(Hookdef dummy, Chdata dat)
mlbeg--;
}
}
- if ((space = zterm_lines - pl - mhasstat))
+ if ((space = zterm_lines - pl - mhasstat) > 0)
while (mline >= mlbeg + space)
if ((mlbeg += step) + space > mlines)
mlbeg = mlines - space;
diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c
index c0e5ff3d8..fcceb670c 100644
--- a/Src/Zle/compresult.c
+++ b/Src/Zle/compresult.c
@@ -1131,7 +1131,7 @@ do_single(Cmatch m)
/* If a suffix was added, and is removable, let *
* `,' and `}' remove it. */
if (isset(AUTOPARAMKEYS))
- addsuffix(SUFTYP_POSSTR, 0, ZWS(",}"), 2, suffixnoinslen);
+ addsuffix(SUFTYP_POSSTR, 0, ZWS(",}"), 2, suffixlen);
} else if (!menucmp) {
/*{{*/
/* Otherwise, add a `,' suffix, and let `}' remove it. */
diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c
index f5e6ba195..b11c39f25 100644
--- a/Src/Zle/computil.c
+++ b/Src/Zle/computil.c
@@ -1500,9 +1500,11 @@ parse_cadef(char *nam, char **args)
nodopts++;
/* If this is for single-letter option we also store a
- * pointer for the definition in the array for fast lookup. */
+ * pointer for the definition in the array for fast lookup.
+ * But don't treat '--' as a single option called '-' */
- if (single && name[1] && !name[2])
+
+ if (single && name[1] && !name[2] && name[1] != '-')
ret->single[STOUC(name[1])] = opt;
if (again == 1) {
@@ -2034,7 +2036,9 @@ ca_parse_line(Cadef d, int multi, int first)
state.optbeg = state.argbeg = state.inopt = cur;
state.argend = argend;
state.singles = (d->single && (!pe || !*pe) &&
- state.curopt->name[1] && !state.curopt->name[2]);
+ state.curopt->name[1] && !state.curopt->name[2] &&
+ /* Don't treat '--' as a single option called '-' */
+ state.curopt->name[1] != '-');
if (!state.oargs[state.curopt->num])
state.oargs[state.curopt->num] = znewlinklist();
diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list
index 4372fe36e..e3ffe3e8c 100644
--- a/Src/Zle/iwidgets.list
+++ b/Src/Zle/iwidgets.list
@@ -102,6 +102,7 @@
"self-insert-unmeta", selfinsertunmeta, ZLE_MENUCMP | ZLE_KEEPSUFFIX
"send-break", sendbreak, 0
"set-mark-command", setmarkcommand, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
+"split-undo", splitundo, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_NOTCOMMAND
"spell-word", spellword, 0
"set-local-history", setlocalhistory, 0
"transpose-chars", transposechars, 0
diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c
index bd5bc36d5..44b39d186 100644
--- a/Src/Zle/zle_hist.c
+++ b/Src/Zle/zle_hist.c
@@ -890,6 +890,10 @@ zgetline(UNUSED(char **args))
free(s);
free(lineadd);
clearlist = 1;
+ if (stackhist != -1) {
+ histline = stackhist;
+ stackhist = -1;
+ }
}
return 0;
}
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 040b7cb83..442c31995 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -157,10 +157,10 @@ mod_export char *statusline;
/**/
int stackhist, stackcs;
-/* != 0 if we are making undo records */
+/* position in undo stack from when the current vi change started */
/**/
-int undoing;
+zlong vistartchange;
/* current modifier status */
@@ -525,7 +525,8 @@ raw_getbyte(long do_keytmout, char *cptr)
#endif
#ifndef HAVE_POLL
# ifdef HAVE_SELECT
- fd_set foofd;
+ fd_set foofd, errfd;
+ FD_ZERO(&errfd);
# endif
#endif
@@ -613,11 +614,14 @@ raw_getbyte(long do_keytmout, char *cptr)
if (!errtry) {
for (i = 0; i < nwatch; i++) {
int fd = watch_fds[i].fd;
+ if (FD_ISSET(fd, &errfd))
+ continue;
FD_SET(fd, &foofd);
if (fd > fdmax)
fdmax = fd;
}
}
+ FD_ZERO(&errfd);
if (tmout.tp != ZTM_NONE) {
expire_tv.tv_sec = tmout.exp100ths / 100;
@@ -732,9 +736,10 @@ raw_getbyte(long do_keytmout, char *cptr)
Watch_fd lwatch_fd = lwatch_fds + i;
if (
# ifdef HAVE_POLL
- (fds[i+1].revents & POLLIN)
+ (fds[i+1].revents & (POLLIN|POLLERR|POLLHUP|POLLNVAL))
# else
- FD_ISSET(lwatch_fd->fd, &foofd)
+ FD_ISSET(lwatch_fd->fd, &foofd) ||
+ FD_ISSET(lwatch_fd->fd, &errfd)
# endif
) {
/* Handle the fd. */
@@ -765,6 +770,9 @@ raw_getbyte(long do_keytmout, char *cptr)
if (fds[i+1].revents & POLLNVAL)
zaddlinknode(funcargs, ztrdup("nval"));
# endif
+# else
+ if (FD_ISSET(lwatch_fd->fd, &errfd))
+ zaddlinknode(funcargs, ztrdup("err"));
# endif
callhookfunc(lwatch_fd->func, funcargs, 0, NULL);
freelinklist(funcargs, freestr);
@@ -786,6 +794,31 @@ raw_getbyte(long do_keytmout, char *cptr)
for (i = 0; i < lnwatch; i++)
zsfree(lwatch_fds[i].func);
zfree(lwatch_fds, lnwatch*sizeof(struct watch_fd));
+
+# ifdef HAVE_POLL
+ /* Function may have added or removed handlers */
+ nfds = 1 + nwatch;
+ if (nfds > 1) {
+ fds = zrealloc(fds, sizeof(struct pollfd) * nfds);
+ for (i = 0; i < nwatch; i++) {
+ /*
+ * This is imperfect because it assumes fds[] and
+ * watch_fds[] remain in sync, which may be false
+ * if handlers are shuffled. However, it should
+ * be harmless (e.g., produce one extra pass of
+ * the loop) in the event they fall out of sync.
+ */
+ if (fds[i+1].fd == watch_fds[i].fd &&
+ (fds[i+1].revents & (POLLERR|POLLHUP|POLLNVAL))) {
+ fds[i+1].events = 0; /* Don't poll this */
+ } else {
+ fds[i+1].fd = watch_fds[i].fd;
+ fds[i+1].events = POLLIN;
+ }
+ fds[i+1].revents = 0;
+ }
+ }
+# endif
}
}
# ifdef HAVE_POLL
@@ -1080,8 +1113,7 @@ zlecore(void)
if (invicmdmode() && zlecs > findbol() &&
(zlecs == zlell || zleline[zlecs] == ZWC('\n')))
DECCS();
- if (undoing)
- handleundo();
+ handleundo();
} else {
errflag = 1;
break;
@@ -1190,7 +1222,7 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
zlereadflags = flags;
zlecontext = context;
histline = curhist;
- undoing = 1;
+ vistartchange = -1;
zleline = (ZLE_STRING_T)zalloc(((linesz = 256) + 2) * ZLE_CHAR_SIZE);
*zleline = ZWC('\0');
virangeflag = lastcmd = done = zlecs = zlell = mark = 0;
@@ -1198,14 +1230,7 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
viinsbegin = 0;
statusline = NULL;
selectkeymap("main", 1);
- /*
- * If main is linked to the viins keymap, we need to register
- * explicitly that we're now in vi insert mode as there's
- * no user operation to indicate this.
- */
- if (openkeymap("main") == openkeymap("viins"))
- viinsert(NULL);
- selectlocalmap(NULL);
+ initundo();
fixsuffix();
if ((s = getlinknode(bufstack))) {
setline(s, ZSL_TOEND);
@@ -1222,7 +1247,14 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
stackhist = -1;
}
}
- initundo();
+ /*
+ * If main is linked to the viins keymap, we need to register
+ * explicitly that we're now in vi insert mode as there's
+ * no user operation to indicate this.
+ */
+ if (openkeymap("main") == openkeymap("viins"))
+ viinsert_init();
+ selectlocalmap(NULL);
if (isset(PROMPTCR))
putc('\r', shout);
if (tmout)
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index 7be0ebbd1..9bc1cf6f5 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -1249,10 +1249,14 @@ static char *suffixfunc;
/* Length associated with the suffix function */
static int suffixfunclen;
-/* Length associated with uninsertable characters */
+/* Whether to remove suffix on uninsertable characters */
+/**/
+int suffixnoinsrem;
+
+/* Length of the currently active, auto-removable suffix. */
/**/
mod_export int
-suffixnoinslen;
+suffixlen;
/**/
mod_export void
@@ -1309,7 +1313,8 @@ makesuffix(int n)
if ((suffixchars = getsparam("ZLE_SPACE_SUFFIX_CHARS")) && *suffixchars)
addsuffixstring(SUFTYP_POSSTR, SUFFLAGS_SPACE, suffixchars, n);
- suffixnoinslen = n;
+ suffixlen = n;
+ suffixnoinsrem = 1;
}
/* Set up suffix for parameter names: the last n characters are a suffix *
@@ -1358,15 +1363,10 @@ makesuffixstr(char *f, char *s, int n)
s = metafy(s, i, META_USEHEAP);
ws = stringaszleline(s, 0, &i, NULL, NULL);
- if (z)
- suffixnoinslen = inv ? 0 : n;
- else if (inv) {
- /*
- * negative match, \- wasn't present, so it *should*
- * have this suffix length
- */
- suffixnoinslen = n;
- }
+ /* Remove suffix on uninsertable characters if \- was given *
+ * and the character class wasn't negated -- or vice versa. */
+ suffixnoinsrem = z ^ inv;
+ suffixlen = n;
lasts = wptr = ws;
while (i) {
@@ -1444,7 +1444,7 @@ iremovesuffix(ZLE_INT_T c, int keep)
struct suffixset *ss;
if (c == NO_INSERT_CHAR) {
- sl = suffixnoinslen;
+ sl = suffixnoinsrem ? suffixlen : 0;
} else {
ZLE_CHAR_T ch = c;
/*
@@ -1538,5 +1538,5 @@ fixsuffix(void)
suffixlist = next;
}
- suffixfunclen = suffixnoinslen = 0;
+ suffixfunclen = suffixnoinsrem = suffixlen = 0;
}
diff --git a/Src/Zle/zle_params.c b/Src/Zle/zle_params.c
index a9bbf136a..5845207fa 100644
--- a/Src/Zle/zle_params.c
+++ b/Src/Zle/zle_params.c
@@ -363,7 +363,7 @@ get_prebuffer(UNUSED(Param pm))
static char *
get_widget(UNUSED(Param pm))
{
- return bindk->nam;
+ return bindk ? bindk->nam : "";
}
/**/
diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c
index fd5485770..80be27f03 100644
--- a/Src/Zle/zle_refresh.c
+++ b/Src/Zle/zle_refresh.c
@@ -444,6 +444,7 @@ void
set_region_highlight(UNUSED(Param pm), char **aval)
{
int len;
+ char **av = aval;
struct region_highlight *rhp;
len = aval ? arrlen(aval) : 0;
@@ -490,6 +491,8 @@ set_region_highlight(UNUSED(Param pm), char **aval)
match_highlight(strp, &rhp->atr);
}
+
+ freearray(av);
}
@@ -977,7 +980,7 @@ zrefresh(void)
int tmpalloced; /* flag to free tmpline when finished */
int remetafy; /* flag that zle line is metafied */
int txtchange; /* attributes set after prompts */
- int rprompt_off; /* Offset of rprompt from right of screen */
+ int rprompt_off = 1; /* Offset of rprompt from right of screen */
struct rparams rpms;
#ifdef MULTIBYTE_SUPPORT
int width; /* width of wide character */
@@ -1046,8 +1049,8 @@ zrefresh(void)
region_highlights[1].start = region_highlights[1].end = -1;
}
/* check for an active completion suffix */
- if (suffixnoinslen) {
- region_highlights[2].start = zlecs - suffixnoinslen;
+ if (suffixlen) {
+ region_highlights[2].start = zlecs - suffixlen;
region_highlights[2].end = zlecs;
} else {
region_highlights[2].start = region_highlights[2].end = -1;
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 9d163ad9e..499c4ae77 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -611,8 +611,6 @@ docomplete(int lst)
active = 1;
comprecursive = 0;
makecommaspecial(0);
- if (undoing)
- setlastline();
/* From the C-code's point of view, we can only use compctl as a default
* type of completion. Load it if it hasn't been loaded already and
@@ -1164,7 +1162,7 @@ get_comp_string(void)
* being separated by tokens | & &! |& || &&). The loop stops when *
* the end of the command containing the cursor is reached. What *
* makes this messy is checking for things like redirections, loops *
- * and whatnot. */
+ * and whatnot. */
do {
qsub = noword = 0;
@@ -2797,6 +2795,7 @@ doexpandhist(void)
if (!err) {
zlemetacs = excs;
if (strcmp(zlemetaline, ol)) {
+ zle_free_positions();
unmetafy_line();
/* For vi mode -- reset the beginning-of-insertion pointer *
* to the beginning of the line. This seems a little silly, *
diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c
index b82e54ce5..1089e274f 100644
--- a/Src/Zle/zle_utils.c
+++ b/Src/Zle/zle_utils.c
@@ -710,6 +710,27 @@ zle_restore_positions(void)
}
/*
+ * Discard positions previously saved, the line has been updated.
+ */
+
+/**/
+mod_export void
+zle_free_positions(void)
+{
+ struct zle_position *oldpos = zle_positions;
+ struct zle_region *oldrhp;
+
+ zle_positions = oldpos->next;
+ oldrhp = oldpos->regions;
+ while (oldrhp) {
+ struct zle_region *nextrhp = oldrhp->next;
+ zfree(oldrhp, sizeof(*oldrhp));
+ oldrhp = nextrhp;
+ }
+ zfree(oldpos, sizeof(*oldpos));
+}
+
+/*
* Basic utility functions for adding to line or removing from line.
* At this level the counts supplied are raw character counts, so
* the calling code must be aware of combining characters where
@@ -1354,7 +1375,10 @@ handlesuffix(UNUSED(char **args))
/* head of the undo list, and the current position */
-static struct change *changes, *curchange;
+/**/
+struct change *curchange;
+
+static struct change *changes;
/* list of pending changes, not yet in the undo system */
@@ -1629,6 +1653,32 @@ viundochange(char **args)
return undo(args);
}
+/**/
+int
+splitundo(char **args)
+{
+ if (vistartchange >= 0) {
+ mergeundo();
+ vistartchange = (curchange && curchange->prev) ?
+ curchange->prev->changeno : 0;
+ }
+ handleundo();
+ return 0;
+}
+
+/**/
+void
+mergeundo(void)
+{
+ struct change *current;
+ for (current = curchange->prev;
+ current && current->prev && current->changeno > vistartchange+1;
+ current = current->prev) {
+ current->flags |= CH_PREV;
+ current->prev->flags |= CH_NEXT;
+ }
+}
+
/*
* Call a ZLE hook: a user-defined widget called at a specific point
* within the line editor.
diff --git a/Src/Zle/zle_vi.c b/Src/Zle/zle_vi.c
index 173a49ef9..9e39143d0 100644
--- a/Src/Zle/zle_vi.c
+++ b/Src/Zle/zle_vi.c
@@ -67,15 +67,23 @@ int viinsbegin;
static struct modifier lastmod;
static int inrepeat, vichgrepeat;
+/**
+ * im: >= 0: is an insertmode
+ * -1: skip setting insert mode
+ * -2: entering viins at start of editing from clean --- don't use
+ * inrepeat or lastchar, synthesise an i to enter insert mode.
+ */
+
/**/
static void
startvichange(int im)
{
if (im != -1) {
- insmode = im;
vichgflag = 1;
+ if (im > -1)
+ insmode = im;
}
- if (inrepeat) {
+ if (inrepeat && im != -2) {
zmod = lastmod;
inrepeat = vichgflag = 0;
vichgrepeat = 1;
@@ -84,7 +92,12 @@ startvichange(int im)
if (vichgbuf)
free(vichgbuf);
vichgbuf = (char *)zalloc(vichgbufsz = 16);
- vichgbuf[0] = lastchar;
+ if (im == -2) {
+ vichgbuf[0] =
+ zlell ? (insmode ? (zlecs < zlell ? 'i' : 'a') : 'R') : 'o';
+ } else {
+ vichgbuf[0] = lastchar;
+ }
vichgbufptr = 1;
vichgrepeat = 0;
}
@@ -96,7 +109,7 @@ startvitext(int im)
{
startvichange(im);
selectkeymap("main", 1);
- undoing = 0;
+ vistartchange = (curchange && curchange->prev) ? curchange->prev->changeno : 0;
viinsbegin = zlecs;
}
@@ -303,6 +316,18 @@ viinsert(UNUSED(char **args))
return 0;
}
+/*
+ * Go to vi insert mode when we first start the line editor.
+ * Iniialises some other stuff.
+ */
+
+/**/
+void
+viinsert_init(void)
+{
+ startvitext(-2);
+}
+
/**/
int
viinsertbol(UNUSED(char **args))
@@ -376,7 +401,7 @@ vichange(UNUSED(char **args))
forekill(c2 - zlecs, CUT_RAW);
selectkeymap("main", 1);
viinsbegin = zlecs;
- undoing = 0;
+ vistartchange = (curchange && curchange->prev) ? curchange->prev->changeno : 0;
}
return ret;
}
@@ -561,7 +586,7 @@ vicmdmode(UNUSED(char **args))
{
if (invicmdmode() || selectkeymap("vicmd", 0))
return 1;
- undoing = 1;
+ mergeundo();
vichgflag = 0;
if (zlecs != findbol())
DECCS();
diff --git a/Src/builtin.c b/Src/builtin.c
index c3f0169c7..a2a3ad748 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -111,7 +111,7 @@ static struct builtin builtins[] =
BUILTIN("return", BINF_PSPECIAL, bin_break, 0, 1, BIN_RETURN, NULL, NULL),
BUILTIN("set", BINF_PSPECIAL | BINF_HANDLES_OPTS, bin_set, 0, -1, 0, NULL, NULL),
BUILTIN("setopt", 0, bin_setopt, 0, -1, BIN_SETOPT, NULL, NULL),
- BUILTIN("shift", BINF_PSPECIAL, bin_shift, 0, -1, 0, NULL, NULL),
+ BUILTIN("shift", BINF_PSPECIAL, bin_shift, 0, -1, 0, "p", NULL),
BUILTIN("source", BINF_PSPECIAL, bin_dot, 1, -1, 0, NULL, NULL),
BUILTIN("suspend", 0, bin_suspend, 0, 0, 0, "f", NULL),
BUILTIN("test", BINF_HANDLES_OPTS, bin_test, 0, -1, BIN_TEST, NULL, NULL),
@@ -1422,6 +1422,10 @@ bin_fc(char *nam, char **argv, Options ops, int func)
unqueue_signals();
return 0;
}
+ if (OPT_ISSET(ops,'I')) {
+ zwarnnam(nam, "-I requires one of -R/-W/-A");
+ return 1;
+ }
if (zleactive) {
zwarnnam(nam, "no interactive history within ZLE");
@@ -1727,7 +1731,7 @@ fclist(FILE *f, Options ops, zlong first, zlong last,
if (tdfmt != NULL) {
struct tm *ltm;
ltm = localtime(&ent->stim);
- if (ztrftime(timebuf, 256, tdfmt, ltm))
+ if (ztrftime(timebuf, 256, tdfmt, ltm, 0L))
fprintf(f, "%s ", timebuf);
}
/* display the time taken by the command, if required */
@@ -4509,7 +4513,7 @@ bin_print(char *name, char **args, Options ops, int func)
/**/
int
-bin_shift(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
+bin_shift(char *name, char **argv, Options ops, UNUSED(int func))
{
int num = 1, l, ret = 0;
char **s;
@@ -4533,7 +4537,19 @@ bin_shift(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
ret++;
continue;
}
- s = zarrdup(s + num);
+ if (OPT_ISSET(ops,'p')) {
+ char **s2, **src, **dst;
+ int count;
+ l = arrlen(s);
+ src = s;
+ dst = s2 = (char **)zalloc((l - num + 1) * sizeof(char *));
+ for (count = l - num; count; count--)
+ *dst++ = ztrdup(*src++);
+ *dst = NULL;
+ s = s2;
+ } else {
+ s = zarrdup(s + num);
+ }
setaparam(*argv, s);
}
} else {
@@ -4542,9 +4558,16 @@ bin_shift(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
ret = 1;
} else {
s = zalloc((l - num + 1) * sizeof(char *));
- memcpy(s, pparams + num, (l - num + 1) * sizeof(char *));
- while (num--)
- zsfree(pparams[num]);
+ if (OPT_ISSET(ops,'p')) {
+ memcpy(s, pparams, (l - num) * sizeof(char *));
+ s[l-num] = NULL;
+ while (num--)
+ zsfree(pparams[l-1-num]);
+ } else {
+ memcpy(s, pparams + num, (l - num + 1) * sizeof(char *));
+ while (num--)
+ zsfree(pparams[num]);
+ }
zfree(pparams, (l + 1) * sizeof(char *));
pparams = s;
}
@@ -4669,9 +4692,10 @@ exit_pending;
int
bin_break(char *name, char **argv, UNUSED(Options ops), int func)
{
- int num = lastval, nump = 0;
+ int num = lastval, nump = 0, implicit;
/* handle one optional numeric argument */
+ implicit = !*argv;
if (*argv) {
num = mathevali(*argv++);
nump = 1;
@@ -4702,7 +4726,13 @@ bin_break(char *name, char **argv, UNUSED(Options ops), int func)
retflag = 1;
breaks = loops;
lastval = num;
- if (trap_state == TRAP_STATE_PRIMED && trap_return == -2) {
+ if (trap_state == TRAP_STATE_PRIMED && trap_return == -2
+ /*
+ * With POSIX, "return" on its own in a trap doesn't
+ * update $? --- we keep the status from before the
+ * trap.
+ */
+ && !(isset(POSIXTRAPS) && implicit)) {
trap_state = TRAP_STATE_FORCE_RETURN;
trap_return = lastval;
}
@@ -6069,8 +6099,9 @@ bin_test(char *name, char **argv, UNUSED(Options ops), int func)
}
/* display a time, provided in units of 1/60s, as minutes and seconds */
-#define pttime(X) printf("%ldm%ld.%02lds",((long) (X))/3600,\
- ((long) (X))/60%60,((long) (X))*100/60%100)
+#define pttime(X) printf("%ldm%ld.%02lds",((long) (X))/(60 * clktck),\
+ ((long) (X))/clktck%clktck,\
+ ((long) (X))*100/clktck%100)
/* times: display, in a two-line format, the times provided by times(3) */
@@ -6079,6 +6110,7 @@ int
bin_times(UNUSED(char *name), UNUSED(char **argv), UNUSED(Options ops), UNUSED(int func))
{
struct tms buf;
+ long clktck = get_clktck();
/* get time accounting information */
if (times(&buf) == -1)
diff --git a/Src/compat.c b/Src/compat.c
index cc4e876da..b0bcb6265 100644
--- a/Src/compat.c
+++ b/Src/compat.c
@@ -37,7 +37,7 @@
char *
strstr(const char *s, const char *t)
{
- char *p1, *p2;
+ const char *p1, *p2;
for (; *s; s++) {
for (p1 = s, p2 = t; *p2; p1++, p2++)
diff --git a/Src/cond.c b/Src/cond.c
index c67354297..df9065660 100644
--- a/Src/cond.c
+++ b/Src/cond.c
@@ -37,6 +37,21 @@ static char *condstr[COND_MOD] = {
"-ne", "-lt", "-gt", "-le", "-ge", "=~"
};
+static void cond_subst(char **strp, int glob_ok)
+{
+ if (glob_ok &&
+ checkglobqual(*strp, strlen(*strp), 1, NULL)) {
+ LinkList args = newlinklist();
+ addlinknode(args, *strp);
+ prefork(args, 0);
+ while (!errflag && args && nonempty(args) &&
+ has_token((char *)peekfirst(args)))
+ zglob(args, firstnode(args), 0);
+ *strp = sepjoin(hlinklist2array(args, 0), NULL, 1);
+ } else
+ singsub(strp);
+}
+
/*
* Evaluate a conditional expression given the arguments.
* If fromtest is set, the caller is the test or [ builtin;
@@ -177,13 +192,13 @@ evalcond(Estate state, char *fromtest)
}
left = ecgetstr(state, EC_DUPTOK, &htok);
if (htok) {
- singsub(&left);
+ cond_subst(&left, !fromtest);
untokenize(left);
}
if (ctype <= COND_GE && ctype != COND_STREQ && ctype != COND_STRNEQ) {
right = ecgetstr(state, EC_DUPTOK, &htok);
if (htok) {
- singsub(&right);
+ cond_subst(&right, !fromtest);
untokenize(right);
}
}
@@ -194,7 +209,7 @@ evalcond(Estate state, char *fromtest)
fprintf(xtrerr, " %s ", condstr[ctype]);
if (ctype == COND_STREQ || ctype == COND_STRNEQ) {
char *rt = dupstring(ecrawstr(state->prog, state->pc, NULL));
- singsub(&rt);
+ cond_subst(&rt, !fromtest);
quote_tokenized_output(rt, xtrerr);
}
else
@@ -282,8 +297,7 @@ evalcond(Estate state, char *fromtest)
right = dupstring(opat = ecrawstr(state->prog, state->pc,
&htok));
- if (htok)
- singsub(&right);
+ singsub(&right);
save = (!(state->prog->flags & EF_HEAP) &&
!strcmp(opat, right) && pprog != dummy_patprog2);
diff --git a/Src/exec.c b/Src/exec.c
index f16cfd391..5ad957f98 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -2346,7 +2346,7 @@ execsubst(LinkList strs)
{
if (strs) {
prefork(strs, esprefork);
- if (esglob) {
+ if (esglob && !errflag) {
LinkList ostrs = strs;
globlist(strs, 0);
strs = ostrs;
@@ -3867,8 +3867,10 @@ getoutputfile(char *cmd, char **eptr)
int fd;
char *s;
- if (thisjob == -1)
+ if (thisjob == -1){
+ zerr("process substitution %s cannot be used here", cmd);
return NULL;
+ }
if (!(prog = parsecmd(cmd, eptr)))
return NULL;
if (!(nam = gettempname(NULL, 0)))
@@ -3939,11 +3941,13 @@ namedpipe(void)
char *tnam = gettempname(NULL, 1);
# ifdef HAVE_MKFIFO
- if (mkfifo(tnam, 0600) < 0)
+ if (mkfifo(tnam, 0600) < 0){
# else
- if (mknod(tnam, 0010600, 0) < 0)
+ if (mknod(tnam, 0010600, 0) < 0){
# endif
+ zerr("failed to create named pipe: %s, %e", tnam, errno);
return NULL;
+ }
return tnam;
}
#endif /* ! PATH_DEV_FD && HAVE_FIFOS */
@@ -3966,9 +3970,10 @@ getproc(char *cmd, char **eptr)
#ifndef PATH_DEV_FD
int fd;
-
- if (thisjob == -1)
+ if (thisjob == -1) {
+ zerr("process substitution %s cannot be used here", cmd);
return NULL;
+ }
if (!(pnam = namedpipe()))
return NULL;
if (!(prog = parsecmd(cmd, eptr)))
@@ -3993,8 +3998,10 @@ getproc(char *cmd, char **eptr)
#else /* PATH_DEV_FD */
int pipes[2], fd;
- if (thisjob == -1)
+ if (thisjob == -1) {
+ zerr("process substitution %s cannot be used here", cmd);
return NULL;
+ }
pnam = hcalloc(strlen(PATH_DEV_FD) + 6);
if (!(prog = parsecmd(cmd, eptr)))
return NULL;
@@ -4234,8 +4241,13 @@ execfuncdef(Estate state, UNUSED(int do_exec))
plen = nprg * sizeof(wordcode);
len = plen + (npats * sizeof(Patprog)) + nstrs;
- if (htok && names)
+ if (htok && names) {
execsubst(names);
+ if (errflag) {
+ state->pc = end;
+ return 1;
+ }
+ }
while (!names || (s = (char *) ugetnode(names))) {
if (!names) {
@@ -4291,8 +4303,13 @@ execfuncdef(Estate state, UNUSED(int do_exec))
end += *state->pc++;
args = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok);
- if (htok && args)
+ if (htok && args) {
execsubst(args);
+ if (errflag) {
+ state->pc = end;
+ return 1;
+ }
+ }
if (!args)
args = newlinklist();
@@ -4597,7 +4614,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
char *name = shfunc->node.nam;
int flags = shfunc->node.flags, ooflags;
char *fname = dupstring(name);
- int obreaks, saveemulation, restore_sticky;
+ int obreaks, ocontflag, oloops, saveemulation, restore_sticky;
Eprog prog;
struct funcstack fstack;
static int oflags;
@@ -4609,7 +4626,9 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
pushheap();
oargv0 = NULL;
- obreaks = breaks;;
+ obreaks = breaks;
+ ocontflag = contflag;
+ oloops = loops;
if (trap_state == TRAP_STATE_PRIMED)
trap_return--;
oldlastval = lastval;
@@ -4797,6 +4816,17 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
opts[XTRACE] = saveopts[XTRACE];
opts[PRINTEXITVALUE] = saveopts[PRINTEXITVALUE];
opts[LOCALOPTIONS] = saveopts[LOCALOPTIONS];
+ opts[LOCALLOOPS] = saveopts[LOCALLOOPS];
+ }
+
+ if (opts[LOCALLOOPS]) {
+ if (contflag)
+ zwarn("`continue' active at end of function scope");
+ if (breaks)
+ zwarn("`break' active at end of function scope");
+ breaks = obreaks;
+ contflag = ocontflag;
+ loops = oloops;
}
endtrapscope();
diff --git a/Src/glob.c b/Src/glob.c
index e0d0cf68e..627166c7a 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -120,6 +120,8 @@ typedef struct stat *Statptr; /* This makes the Ultrix compiler happy. Go figu
#define TT_POSIX_BLOCKS 1
#define TT_KILOBYTES 2
#define TT_MEGABYTES 3
+#define TT_GIGABYTES 4
+#define TT_TERABYTES 5
typedef int (*TestMatchFunc) _((char *, struct stat *, off_t, char *));
@@ -443,6 +445,7 @@ insert(char *s, int checked)
break;
}
unqueue_signals();
+ return;
}
/* Do the globbing: scanner is called recursively *
@@ -451,7 +454,7 @@ insert(char *s, int checked)
/**/
static void
-scanner(Complist q)
+scanner(Complist q, int shortcircuit)
{
Patprog p;
int closure;
@@ -459,16 +462,19 @@ scanner(Complist q)
int errssofar = errsfound;
struct dirsav ds;
- init_dirsav(&ds);
if (!q)
return;
+ init_dirsav(&ds);
if ((closure = q->closure)) {
/* (foo/)# - match zero or more dirs */
if (q->closure == 2) /* (foo/)## - match one or more dirs */
q->closure = 1;
- else
- scanner(q->next);
+ else {
+ scanner(q->next, shortcircuit);
+ if (shortcircuit && shortcircuit == matchct)
+ return;
+ }
}
p = q->pat;
/* Now the actual matching for the current path section. */
@@ -513,8 +519,11 @@ scanner(Complist q)
}
if (add) {
addpath(str, l);
- if (!closure || !statfullpath("", NULL, 1))
- scanner((q->closure) ? q : q->next);
+ if (!closure || !statfullpath("", NULL, 1)) {
+ scanner((q->closure) ? q : q->next, shortcircuit);
+ if (shortcircuit && shortcircuit == matchct)
+ return;
+ }
pathbuf[pathpos = oppos] = '\0';
}
}
@@ -522,6 +531,8 @@ scanner(Complist q)
if (str[l])
str = dupstrpfx(str, l);
insert(str, 0);
+ if (shortcircuit && shortcircuit == matchct)
+ return;
}
} else {
/* Do pattern matching on current path section. */
@@ -609,9 +620,12 @@ scanner(Complist q)
memcpy(subdirs + subdirlen, (char *)&errsfound,
sizeof(int));
subdirlen += sizeof(int);
- } else
+ } else {
/* if the last filename component, just add it */
insert(fn, 1);
+ if (shortcircuit && shortcircuit == matchct)
+ return;
+ }
}
}
closedir(lock);
@@ -624,7 +638,10 @@ scanner(Complist q)
fn += l + 1;
memcpy((char *)&errsfound, fn, sizeof(int));
fn += sizeof(int);
- scanner((q->closure) ? q : q->next); /* scan next level */
+ /* scan next level */
+ scanner((q->closure) ? q : q->next, shortcircuit);
+ if (shortcircuit && shortcircuit == matchct)
+ return;
pathbuf[pathpos = oppos] = '\0';
}
hrealloc(subdirs, subdirlen, 0);
@@ -638,6 +655,7 @@ scanner(Complist q)
close(ds.dirfd);
pathbufcwd = pbcwdsav;
}
+ return;
}
/* This function tokenizes a zsh glob pattern */
@@ -1059,6 +1077,67 @@ insert_glob_match(LinkList list, LinkNode next, char *data)
insertlinknode(list, next, data);
}
+/*
+ * Return
+ * 1 if str ends in bare glob qualifiers
+ * 2 if str ends in non-bare glob qualifiers (#q)
+ * 0 otherwise.
+ *
+ * str is the string to check.
+ * sl is its length (to avoid recalculation).
+ * nobareglob is 1 if bare glob qualifiers are not allowed.
+ * *sp, if sp is not null, will be a pointer to the opening parenthesis.
+ */
+
+/**/
+int
+checkglobqual(char *str, int sl, int nobareglob, char **sp)
+{
+ char *s;
+ int paren, ret = 1;
+
+ if (str[sl - 1] != Outpar)
+ return 0;
+
+ /* Check these are really qualifiers, not a set of *
+ * alternatives or exclusions. We can be more *
+ * lenient with an explicit (#q) than with a bare *
+ * set of qualifiers. */
+ paren = 0;
+ for (s = str + sl - 2; *s && (*s != Inpar || paren); s--) {
+ switch (*s) {
+ case Outpar:
+ paren++; /*FALLTHROUGH*/
+ case Bar:
+ if (!zpc_disables[ZPC_BAR])
+ nobareglob = 1;
+ break;
+ case Tilde:
+ if (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_TILDE])
+ nobareglob = 1;
+ break;
+ case Inpar:
+ paren--;
+ break;
+ }
+ if (s == str)
+ break;
+ }
+ if (*s != Inpar)
+ return 0;
+ if (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_HASH] && s[1] == Pound) {
+ if (s[2] != 'q')
+ return 0;
+ ret = 2;
+ } else if (nobareglob)
+ return 0;
+
+ if (sp)
+ *sp = s;
+
+ return ret;
+}
+
/* Main entry point to the globbing code for filename globbing. *
* np points to a node in the list list which will be expanded *
* into a series of nodes. */
@@ -1078,6 +1157,8 @@ zglob(LinkList list, LinkNode np, int nountok)
/* and index+1 of the last match */
struct globdata saved; /* saved glob state */
int nobareglob = !isset(BAREGLOBQUAL);
+ int shortcircuit = 0; /* How many files to match; */
+ /* 0 means no limit */
if (unset(GLOBOPT) || !haswilds(ostr) || unset(EXECOPT)) {
if (!nountok)
@@ -1116,7 +1197,7 @@ zglob(LinkList list, LinkNode np, int nountok)
(isset(EXTENDEDGLOB) && !zpc_disables[ZPC_HASH])) {
struct qual *newquals;
char *s;
- int sense, paren;
+ int sense, qualsfound;
off_t data;
char *sdata, *newcolonmod;
int (*func) _((char *, Statptr, off_t, char *));
@@ -1146,40 +1227,7 @@ zglob(LinkList list, LinkNode np, int nountok)
newquals = qo = qn = ql = NULL;
sl = strlen(str);
- if (str[sl - 1] != Outpar)
- break;
-
- /* Check these are really qualifiers, not a set of *
- * alternatives or exclusions. We can be more *
- * lenient with an explicit (#q) than with a bare *
- * set of qualifiers. */
- paren = 0;
- for (s = str + sl - 2; *s && (*s != Inpar || paren); s--) {
- switch (*s) {
- case Outpar:
- paren++; /*FALLTHROUGH*/
- case Bar:
- if (!zpc_disables[ZPC_BAR])
- nobareglob = 1;
- break;
- case Tilde:
- if (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_TILDE])
- nobareglob = 1;
- break;
- case Inpar:
- paren--;
- break;
- }
- }
- if (*s != Inpar)
- break;
- if (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_HASH] && s[1] == Pound) {
- if (s[2] == 'q') {
- *s = 0;
- s += 2;
- } else
- break;
- } else if (nobareglob)
+ if (!(qualsfound = checkglobqual(str, sl, nobareglob, &s)))
break;
/* Real qualifiers found. */
@@ -1192,6 +1240,8 @@ zglob(LinkList list, LinkNode np, int nountok)
str[sl-1] = 0;
*s++ = 0;
+ if (qualsfound == 2)
+ s += 2;
while (*s && !newcolonmod) {
func = (int (*) _((char *, Statptr, off_t, char *)))0;
if (idigit(*s)) {
@@ -1362,7 +1412,7 @@ zglob(LinkList list, LinkNode np, int nountok)
/* Find matching delimiters */
tt = get_strarg(s, &arglen);
if (!*tt) {
- zerr("missing end of name");
+ zerr("missing delimiter for 'u' glob qualifier");
data = 0;
} else {
#ifdef USE_GETPWNAM
@@ -1402,7 +1452,7 @@ zglob(LinkList list, LinkNode np, int nountok)
tt = get_strarg(s, &arglen);
if (!*tt) {
- zerr("missing end of name");
+ zerr("missing delimiter for 'g' glob qualifier");
data = 0;
} else {
#ifdef USE_GETGRNAM
@@ -1459,6 +1509,23 @@ zglob(LinkList list, LinkNode np, int nountok)
/* Numeric glob sort */
gf_numsort = !(sense & 1);
break;
+ case 'Y':
+ {
+ /* Short circuit: limit number of matches */
+ const char *s_saved = s;
+ shortcircuit = !(sense & 1);
+ if (shortcircuit) {
+ /* Parse the argument. */
+ data = qgetnum(&s);
+ if ((shortcircuit = data) != data) {
+ /* Integer overflow */
+ zerr("value too big: Y%s", s_saved);
+ restore_globstate(saved);
+ return;
+ }
+ }
+ break;
+ }
case 'a':
/* Access time in given range */
g_amc = 0;
@@ -1486,6 +1553,12 @@ zglob(LinkList list, LinkNode np, int nountok)
g_units = TT_KILOBYTES, ++s;
else if (*s == 'm' || *s == 'M')
g_units = TT_MEGABYTES, ++s;
+#if defined(ZSH_64_BIT_TYPE) || defined(LONG_IS_64_BIT)
+ else if (*s == 'g' || *s == 'G')
+ g_units = TT_GIGABYTES, ++s;
+ else if (*s == 't' || *s == 'T')
+ g_units = TT_TERABYTES, ++s;
+#endif
getrange:
/* Get time multiplier */
if (g_amc >= 0) {
@@ -1549,9 +1622,10 @@ zglob(LinkList list, LinkNode np, int nountok)
restore_globstate(saved);
return;
}
+ if ((sense & 2) &&
+ (t & (GS_SIZE|GS_ATIME|GS_MTIME|GS_CTIME|GS_LINKS)))
+ t <<= GS_SHIFT; /* HERE: GS_EXEC? */
if (t != GS_EXEC) {
- if ((sense & 2) && !(t & (GS_NAME|GS_DEPTH)))
- t <<= GS_SHIFT; /* HERE: GS_EXEC? */
if (gf_sorts & t) {
zerr("doubled sort specifier");
restore_globstate(saved);
@@ -1709,7 +1783,7 @@ zglob(LinkList list, LinkNode np, int nountok)
return;
}
if (!gf_nsorts) {
- gf_sortlist[0].tp = gf_sorts = GS_NAME;
+ gf_sortlist[0].tp = gf_sorts = (shortcircuit ? GS_NONE : GS_NAME);
gf_nsorts = 1;
}
/* Initialise receptacle for matched files, *
@@ -1721,7 +1795,7 @@ zglob(LinkList list, LinkNode np, int nountok)
/* The actual processing takes place here: matches go into *
* matchbuf. This is the only top-level call to scanner(). */
- scanner(q);
+ scanner(q, shortcircuit);
/* Deal with failures to match depending on options */
if (matchct)
@@ -1832,6 +1906,8 @@ zglob(LinkList list, LinkNode np, int nountok)
matchptr++;
}
}
+ } else if (!badcshglob && !isset(NOMATCH) && matchct == 1) {
+ insert_glob_match(list, node, (--matchptr)->name);
}
free(matchbuf);
@@ -1895,6 +1971,8 @@ hasbraces(char *str)
switch (*str++) {
case Inbrace:
if (!lbr) {
+ if (bracechardots(str-1, NULL, NULL))
+ return 1;
lbr = str - 1;
if (*str == '-')
str++;
@@ -2027,6 +2105,68 @@ xpandredir(struct redir *fn, LinkList redirtab)
return ret;
}
+/*
+ * Check for a brace expansion of the form {<char>..<char>}.
+ * On input str must be positioned at an Inbrace, but the sequence
+ * of characters beyond that has not necessarily been checked.
+ * Return 1 if found else 0.
+ *
+ * The other parameters are optionaland if the function returns 1 are
+ * used to return:
+ * - *c1p: the first character in the expansion.
+ * - *c2p: the final character in the expansion.
+ */
+
+/**/
+static int
+bracechardots(char *str, convchar_t *c1p, convchar_t *c2p)
+{
+ convchar_t cstart, cend;
+ char *pnext = str + 1, *pconv, convstr[2];
+ if (itok(*pnext)) {
+ if (*pnext == Inbrace)
+ return 0;
+ convstr[0] = ztokens[*pnext - Pound];
+ convstr[1] = '\0';
+ pconv = convstr;
+ } else
+ pconv = pnext;
+ MB_METACHARINIT();
+ pnext += MB_METACHARLENCONV(pconv, &cstart);
+ if (
+#ifdef MULTIBYTE_SUPPORT
+ cstart == WEOF ||
+#else
+ !cstart ||
+#endif
+ pnext[0] != '.' || pnext[1] != '.')
+ return 0;
+ pnext += 2;
+ if (itok(*pnext)) {
+ if (*pnext == Inbrace)
+ return 0;
+ convstr[0] = ztokens[*pnext - Pound];
+ convstr[1] = '\0';
+ pconv = convstr;
+ } else
+ pconv = pnext;
+ MB_METACHARINIT();
+ pnext += MB_METACHARLENCONV(pconv, &cend);
+ if (
+#ifdef MULTIBYTE_SUPPORT
+ cend == WEOF ||
+#else
+ !cend ||
+#endif
+ *pnext != Outbrace)
+ return 0;
+ if (c1p)
+ *c1p = cstart;
+ if (c2p)
+ *c2p = cend;
+ return 1;
+}
+
/* brace expansion */
/**/
@@ -2060,10 +2200,57 @@ xpandbraces(LinkList list, LinkNode *np)
char *dots, *p, *dots2 = NULL;
LinkNode olast = last;
/* Get the first number of the range */
- zlong rstart = zstrtol(str+1,&dots,10), rend = 0;
+ zlong rstart, rend;
int err = 0, rev = 0, rincr = 1;
- int wid1 = (dots - str) - 1, wid2 = (str2 - dots) - 2, wid3 = 0;
- int strp = str - str3;
+ int wid1, wid2, wid3, strp;
+ convchar_t cstart, cend;
+
+ if (bracechardots(str, &cstart, &cend)) {
+ int lenalloc;
+ /*
+ * This is a character range.
+ */
+ if (cend < cstart) {
+ convchar_t ctmp = cend;
+ cend = cstart;
+ cstart = ctmp;
+ rev = 1;
+ }
+ uremnode(list, node);
+ strp = str - str3;
+ lenalloc = strp + strlen(str2+1) + 1;
+ for (; cend >= cstart; cend--) {
+#ifdef MULTIBYTE_SUPPORT
+ char *ncptr;
+ int nclen;
+ mb_metacharinit();
+ ncptr = wcs_nicechar(cend, NULL, NULL);
+ nclen = strlen(ncptr);
+ p = zhalloc(lenalloc + nclen);
+ memcpy(p, str3, strp);
+ memcpy(p + strp, ncptr, nclen);
+ strcpy(p + strp + nclen, str2 + 1);
+#else
+ p = zhalloc(lenalloc + 1);
+ memcpy(p, str3, strp);
+ sprintf(p + strp, "%c", cend);
+ strcat(p + strp, str2 + 1);
+#endif
+ insertlinknode(list, last, p);
+ if (rev) /* decreasing: add in reverse order. */
+ last = nextnode(last);
+ }
+ *np = nextnode(olast);
+ return;
+ }
+
+ /* Get the first number of the range */
+ rstart = zstrtol(str+1,&dots,10);
+ rend = 0;
+ wid1 = (dots - str) - 1;
+ wid2 = (str2 - dots) - 2;
+ wid3 = 0;
+ strp = str - str3;
if (dots == str + 1 || *dots != '.' || dots[1] != '.')
err++;
@@ -3443,9 +3630,9 @@ qualiscom(UNUSED(char *name), struct stat *buf, UNUSED(off_t mod), UNUSED(char *
static int
qualsize(UNUSED(char *name), struct stat *buf, off_t size, UNUSED(char *dummy))
{
-#if defined(LONG_IS_64_BIT) || defined(OFF_T_IS_64_BIT)
+#if defined(ZSH_64_BIT_TYPE) || defined(LONG_IS_64_BIT)
# define QS_CAST_SIZE()
- off_t scaled = buf->st_size;
+ zlong scaled = buf->st_size;
#else
# define QS_CAST_SIZE() (unsigned long)
unsigned long scaled = (unsigned long)buf->st_size;
@@ -3464,6 +3651,16 @@ qualsize(UNUSED(char *name), struct stat *buf, off_t size, UNUSED(char *dummy))
scaled += 1048575l;
scaled /= 1048576l;
break;
+#if defined(ZSH_64_BIT_TYPE) || defined(LONG_IS_64_BIT)
+ case TT_GIGABYTES:
+ scaled += ZLONG_CONST(1073741823);
+ scaled /= ZLONG_CONST(1073741824);
+ break;
+ case TT_TERABYTES:
+ scaled += ZLONG_CONST(1099511627775);
+ scaled /= ZLONG_CONST(1099511627776);
+ break;
+#endif
}
return (g_range < 0 ? scaled < QS_CAST_SIZE() size :
diff --git a/Src/hist.c b/Src/hist.c
index 1845bd8ad..770d559b1 100644
--- a/Src/hist.c
+++ b/Src/hist.c
@@ -935,9 +935,11 @@ hbegin(int dohist)
hf = getsparam("HISTFILE");
/*
- * For INCAPPENDHISTORY, when interactive, save the history here
+ * For INCAPPENDHISTORYTIME, when interactive, save the history here
* as it gives a better estimate of the times of commands.
*
+ * If INCAPPENDHISTORY is also set we've already done it.
+ *
* If SHAREHISTORY is also set continue to do so in the
* standard place, because that's safer about reading and
* rewriting history atomically.
@@ -950,7 +952,8 @@ hbegin(int dohist)
* so that (correctly) nothing happens here. But it shows
* I thought about it.
*/
- if (isset(INCAPPENDHISTORY) && !isset(SHAREHISTORY) &&
+ if (isset(INCAPPENDHISTORYTIME) && !isset(SHAREHISTORY) &&
+ !isset(INCAPPENDHISTORY) &&
!(histactive & HA_NOINC) && !strin && histsave_stack_pos == 0)
savehistfile(hf, 0, HFILE_USE_OPTIONS | HFILE_FAST);
}
@@ -1378,7 +1381,8 @@ hend(Eprog prog)
* For normal INCAPPENDHISTORY case and reasoning, see hbegin().
*/
if (isset(SHAREHISTORY) ? histfileIsLocked() :
- (isset(INCAPPENDHISTORY) && histsave_stack_pos != 0))
+ (isset(INCAPPENDHISTORY) || (isset(INCAPPENDHISTORYTIME) &&
+ histsave_stack_pos != 0)))
savehistfile(hf, 0, HFILE_USE_OPTIONS | HFILE_FAST);
unlockhistfile(hf); /* It's OK to call this even if we aren't locked */
/*
@@ -1764,7 +1768,8 @@ chrealpath(char **junkptr)
str++;
}
- *junkptr = metafy(bicat(real, nonreal), -1, META_HEAPDUP);
+ *junkptr = metafy(str = bicat(real, nonreal), -1, META_HEAPDUP);
+ zsfree(str);
#ifdef HAVE_CANONICALIZE_FILE_NAME
free(real);
#endif
@@ -2303,8 +2308,7 @@ readhistline(int start, char **bufp, int *bufsiz, FILE *in)
}
else {
buf[len - 1] = '\0';
- if (len > 1 && buf[len - 2] == '\\' &&
- (len < 3 || buf[len - 3] != '\\')) {
+ if (len > 1 && buf[len - 2] == '\\') {
buf[--len - 1] = '\n';
if (!feof(in))
return readhistline(len, bufp, bufsiz, in);
@@ -2541,7 +2545,7 @@ savehistfile(char *fn, int err, int writeflags)
}
if (writeflags & HFILE_USE_OPTIONS) {
if (isset(APPENDHISTORY) || isset(INCAPPENDHISTORY)
- || isset(SHAREHISTORY))
+ || isset(INCAPPENDHISTORYTIME) || isset(SHAREHISTORY))
writeflags |= HFILE_APPEND | HFILE_SKIPOLD;
else
histfile_linect = 0;
@@ -2577,7 +2581,7 @@ savehistfile(char *fn, int err, int writeflags)
tmpfile = NULL;
if (err) {
if (isset(APPENDHISTORY) || isset(INCAPPENDHISTORY)
- || isset(SHAREHISTORY))
+ || isset(INCAPPENDHISTORYTIME) || isset(SHAREHISTORY))
zerr("rewriting %s would change its ownership -- skipped", fn);
else
zerr("rewriting %s would change its ownership -- history not saved", fn);
@@ -2613,6 +2617,8 @@ savehistfile(char *fn, int err, int writeflags)
ret = 0;
for (; he && he->histnum <= xcurhist; he = down_histent(he)) {
+ int count_backslashes = 0;
+
if ((writeflags & HFILE_SKIPDUPS && he->node.flags & HIST_DUP)
|| (writeflags & HFILE_SKIPFOREIGN && he->node.flags & HIST_FOREIGN)
|| he->node.flags & HIST_TMPSTORE)
@@ -2644,9 +2650,18 @@ savehistfile(char *fn, int err, int writeflags)
if (*t == '\n')
if ((ret = fputc('\\', out)) < 0)
break;
+ if (*t == '\\')
+ count_backslashes++;
+ else
+ count_backslashes = 0;
if ((ret = fputc(*t, out)) < 0)
break;
}
+ if (ret < 0)
+ break;
+ if (count_backslashes && (count_backslashes % 2 == 0))
+ if ((ret = fputc(' ', out)) < 0)
+ break;
if (ret < 0 || (ret = fputc('\n', out)) < 0)
break;
}
@@ -2719,6 +2734,25 @@ savehistfile(char *fn, int err, int writeflags)
static int lockhistct;
+static int
+checklocktime(char *lockfile, time_t then)
+{
+ time_t now = time(NULL);
+
+ if (now + 10 < then) {
+ /* File is more than 10 seconds in the future? */
+ errno = EEXIST;
+ return -1;
+ }
+
+ if (now - then < 10)
+ sleep(1);
+ else
+ unlink(lockfile);
+
+ return 0;
+}
+
/*
* Lock history file. Return 0 on success, 1 on failure to lock this
* time, 2 on permanent failure (e.g. permission).
@@ -2736,9 +2770,7 @@ lockhistfile(char *fn, int keep_trying)
#ifdef HAVE_FCNTL_H
if (isset(HISTFCNTLLOCK) && flock_fd < 0) {
- ret = flockhistfile(fn, keep_trying);
- if (ret)
- return ret;
+ return flockhistfile(fn, keep_trying);
}
#endif
@@ -2775,10 +2807,10 @@ lockhistfile(char *fn, int keep_trying)
continue;
break;
}
- if (time(NULL) - sb.st_mtime < 10)
- sleep(1);
- else
- unlink(lockfile);
+ if (checklocktime(lockfile, sb.st_mtime) < 0) {
+ ret = 1;
+ break;
+ }
}
if (fd < 0)
lockhistct--;
@@ -2803,10 +2835,10 @@ lockhistfile(char *fn, int keep_trying)
continue;
ret = 2;
} else {
- if (time(NULL) - sb.st_mtime < 10)
- sleep(1);
- else
- unlink(lockfile);
+ if (checklocktime(lockfile, sb.st_mtime) < 0) {
+ ret = 1;
+ break;
+ }
continue;
}
lockhistct--;
@@ -2831,10 +2863,10 @@ lockhistfile(char *fn, int keep_trying)
ret = 2;
break;
}
- if (time(NULL) - sb.st_mtime < 10)
- sleep(1);
- else
- unlink(lockfile);
+ if (checklocktime(lockfile, sb.st_mtime) < 0) {
+ ret = 1;
+ break;
+ }
}
if (fd < 0)
lockhistct--;
diff --git a/Src/init.c b/Src/init.c
index f5aae71f2..5e92f59df 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -77,7 +77,7 @@ mod_export int tclen[TC_COUNT];
/**/
int tclines, tccolumns;
/**/
-mod_export int hasam, hasxn, hasye;
+mod_export int hasam, hasbw, hasxn, hasye;
/* Value of the Co (max_colors) entry: may not be set */
@@ -226,7 +226,7 @@ parseargs(char **argv, char **runscript)
char **x;
LinkList paramlist;
- argzero = *argv++;
+ argzero = posixzero = *argv++;
SHIN = 0;
/* There's a bit of trickery with opts[INTERACTIVE] here. It starts *
@@ -253,7 +253,7 @@ parseargs(char **argv, char **runscript)
if (*argv) {
if (unset(SHINSTDIN)) {
if (cmd)
- argzero = *argv;
+ argzero = posixzero = *argv;
else
*runscript = *argv;
opts[INTERACTIVE] &= 1;
@@ -275,6 +275,7 @@ parseargs(char **argv, char **runscript)
while ((*x++ = (char *)getlinknode(paramlist)));
free(paramlist);
argzero = ztrdup(argzero);
+ posixzero = ztrdup(posixzero);
}
/* Insert into list in order of pointer value */
@@ -698,6 +699,7 @@ init_term(void)
/* check whether terminal has automargin (wraparound) capability */
hasam = tgetflag("am");
+ hasbw = tgetflag("bw");
hasxn = tgetflag("xn"); /* also check for newline wraparound glitch */
hasye = tgetflag("YE"); /* print in last column does carriage return */
@@ -749,9 +751,8 @@ init_term(void)
tcstr[TCCLEARSCREEN] = ztrdup("\14");
tclen[TCCLEARSCREEN] = 1;
}
-#if 0 /* This might work, but there may be more to it */
- rprompt_indent = (hasye || !tccan(TCLEFT)) ? 1 : 0;
-#endif
+ /* This might work, but there may be more to it */
+ rprompt_indent = ((hasam && !hasbw) || hasye || !tccan(TCLEFT));
}
return 1;
}
@@ -1003,15 +1004,6 @@ setupvals(void)
setiparam("COLUMNS", zterm_columns);
setiparam("LINES", zterm_lines);
#endif
- {
- /* Import from environment, overrides init_term() */
- struct value vbuf;
- char *name = "ZLE_RPROMPT_INDENT";
- if (getvalue(&vbuf, &name, 1) && !(vbuf.flags & PM_UNSET))
- rprompt_indent = getintvalue(&vbuf);
- else
- rprompt_indent = 1;
- }
#ifdef HAVE_GETRLIMIT
for (i = 0; i != RLIM_NLIMITS; i++) {
@@ -1535,7 +1527,7 @@ mod_export CompctlReadFn compctlreadptr = fallback_compctlread;
mod_export int
fallback_compctlread(char *name, UNUSED(char **args), UNUSED(Options ops), UNUSED(char *reply))
{
- zwarnnam(name, "option valid only in functions called from completion");
+ zwarnnam(name, "no loaded module provides read for completion context");
return 1;
}
diff --git a/Src/jobs.c b/Src/jobs.c
index 871946598..c4a0707d4 100644
--- a/Src/jobs.c
+++ b/Src/jobs.c
@@ -618,13 +618,11 @@ setprevjob(void)
}
/**/
-#ifndef HAVE_GETRUSAGE
-static long clktck = 0;
-
-/**/
-static void
-set_clktck(void)
+long
+get_clktck(void)
{
+ static long clktck;
+
#ifdef _SC_CLK_TCK
if (!clktck)
/* fetch clock ticks per second from *
@@ -646,9 +644,9 @@ set_clktck(void)
# endif
# endif
#endif
+
+ return clktck;
}
-/**/
-#endif
/**/
static void
@@ -698,11 +696,13 @@ printtime(struct timeval *real, child_times_t *ti, char *desc)
percent = 100.0 * total_time
/ (real->tv_sec + real->tv_usec / 1000000.0);
#else
- set_clktck();
- user_time = ti->ut / (double) clktck;
- system_time = ti->st / (double) clktck;
- percent = 100.0 * (ti->ut + ti->st)
- / (clktck * real->tv_sec + clktck * real->tv_usec / 1000000.0);
+ {
+ long clktck = get_clktck();
+ user_time = ti->ut / (double) clktck;
+ system_time = ti->st / (double) clktck;
+ percent = 100.0 * (ti->ut + ti->st)
+ / (clktck * real->tv_sec + clktck * real->tv_usec / 1000000.0);
+ }
#endif
queue_signals();
@@ -910,8 +910,10 @@ should_report_time(Job j)
reporttime--;
return reporttime <= 0;
#else
- set_clktck();
- return ((j->procs->ti.ut + j->procs->ti.st) / clktck >= reporttime);
+ {
+ clktck = get_clktck();
+ return ((j->procs->ti.ut + j->procs->ti.st) / clktck >= reporttime);
+ }
#endif
}
diff --git a/Src/lex.c b/Src/lex.c
index d0f845020..8e9a49f3b 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -30,6 +30,8 @@
#include "zsh.mdh"
#include "lex.pro"
+#define LEX_HEAP_SIZE (32)
+
/* tokens */
/**/
@@ -721,7 +723,7 @@ gettok(void)
/* word includes the last character read and possibly \ before ! */
if (dbparens) {
len = 0;
- bptr = tokstr = (char *) hcalloc(bsiz = 32);
+ bptr = tokstr = (char *) hcalloc(bsiz = LEX_HEAP_SIZE);
hungetc(c);
cmdpush(CS_MATH);
c = dquote_parse(infor ? ';' : ')', 0);
@@ -775,7 +777,7 @@ gettok(void)
if (lexflags & LEXFLAGS_COMMENTS_KEEP) {
len = 0;
- bptr = tokstr = (char *)hcalloc(bsiz = 32);
+ bptr = tokstr = (char *)hcalloc(bsiz = LEX_HEAP_SIZE);
add(c);
}
hwend();
@@ -877,7 +879,7 @@ gettok(void)
}
if (incmdpos || (isset(SHGLOB) && !isset(KSHGLOB))) {
len = 0;
- bptr = tokstr = (char *) hcalloc(bsiz = 32);
+ bptr = tokstr = (char *) hcalloc(bsiz = LEX_HEAP_SIZE);
switch (cmd_or_math(CS_MATH)) {
case 1:
return DINPAR;
@@ -1027,7 +1029,7 @@ gettokstr(int c, int sub)
peek = STRING;
if (!sub) {
len = 0;
- bptr = tokstr = (char *) hcalloc(bsiz = 32);
+ bptr = tokstr = (char *) hcalloc(bsiz = LEX_HEAP_SIZE);
}
for (;;) {
int act;
diff --git a/Src/loop.c b/Src/loop.c
index 90a0761b3..2f639fd5a 100644
--- a/Src/loop.c
+++ b/Src/loop.c
@@ -73,7 +73,7 @@ execfor(Estate state, int do_exec)
matheval(str);
if (errflag) {
state->pc = end;
- return lastval = errflag;
+ return 1;
}
cond = ecgetstr(state, EC_NODUP, &ctok);
advance = ecgetstr(state, EC_NODUP, &atok);
@@ -87,8 +87,13 @@ execfor(Estate state, int do_exec)
state->pc = end;
return 0;
}
- if (htok)
+ if (htok) {
execsubst(args);
+ if (errflag) {
+ state->pc = end;
+ return 1;
+ }
+ }
} else {
char **x;
@@ -97,7 +102,7 @@ execfor(Estate state, int do_exec)
addlinknode(args, dupstring(*x));
}
}
- lastval = 0;
+ /* lastval = 0; */
loops++;
pushheap();
cmdpush(CS_FOR);
@@ -223,15 +228,20 @@ execselect(Estate state, UNUSED(int do_exec))
state->pc = end;
return 0;
}
- if (htok)
+ if (htok) {
execsubst(args);
+ if (errflag) {
+ state->pc = end;
+ return 1;
+ }
+ }
}
if (!args || empty(args)) {
state->pc = end;
return 1;
}
loops++;
- lastval = 0;
+ /* lastval = 0; */
pushheap();
cmdpush(CS_SELECT);
usezle = interact && SHTTY != -1 && isset(USEZLE);
diff --git a/Src/math.c b/Src/math.c
index 42355f885..266569827 100644
--- a/Src/math.c
+++ b/Src/math.c
@@ -556,6 +556,9 @@ lexconstant(void)
int outputradix;
/**/
+int outputunderscore;
+
+/**/
static int
zzlex(void)
{
@@ -713,7 +716,7 @@ zzlex(void)
return EOI;
case '[':
{
- int n;
+ int n, checkradix = 0;
if (idigit(*ptr)) {
n = zstrtol(ptr, &ptr, 10);
@@ -730,9 +733,19 @@ zzlex(void)
n = -1;
ptr++;
}
- if (!idigit(*ptr))
+ if (!idigit(*ptr) && *ptr != '_')
goto bofs;
- outputradix = n * zstrtol(ptr, &ptr, 10);
+ if (idigit(*ptr)) {
+ outputradix = n * zstrtol(ptr, &ptr, 10);
+ checkradix = 1;
+ }
+ if (*ptr == '_') {
+ ptr++;
+ if (idigit(*ptr))
+ outputunderscore = zstrtol(ptr, &ptr, 10);
+ else
+ outputunderscore = 3;
+ }
} else {
bofs:
zerr("bad output format specification");
@@ -740,11 +753,13 @@ zzlex(void)
}
if(*ptr != ']')
goto bofs;
- n = (outputradix < 0) ? -outputradix : outputradix;
- if (n < 2 || n > 36) {
- zerr("invalid base (must be 2 to 36 inclusive): %d",
- outputradix);
- return EOI;
+ if (checkradix) {
+ n = (outputradix < 0) ? -outputradix : outputradix;
+ if (n < 2 || n > 36) {
+ zerr("invalid base (must be 2 to 36 inclusive): %d",
+ outputradix);
+ return EOI;
+ }
}
ptr++;
break;
@@ -1337,9 +1352,9 @@ matheval(char *s)
char *junk;
mnumber x;
int xmtok = mtok;
- /* maintain outputradix across levels of evaluation */
+ /* maintain outputradix and outputunderscore across levels of evaluation */
if (!mlevel)
- outputradix = 0;
+ outputradix = outputunderscore = 0;
if (!*s) {
x.type = MN_INTEGER;
diff --git a/Src/mem.c b/Src/mem.c
index 5275c6c98..7e0667a33 100644
--- a/Src/mem.c
+++ b/Src/mem.c
@@ -226,6 +226,9 @@ old_heaps(Heap old)
#else
zfree(h, HEAPSIZE);
#endif
+#ifdef ZSH_VALGRIND
+ VALGRIND_DESTROY_MEMPOOL((char *)h);
+#endif
}
heaps = old;
#ifdef ZSH_HEAP_DEBUG
@@ -319,23 +322,26 @@ freeheap(void)
h_free++;
#endif
- /* At this point we used to do:
- fheap = NULL;
- *
+ /*
* When pushheap() is called, it sweeps over the entire heaps list of
* arenas and marks every one of them with the amount of free space in
* that arena at that moment. zhalloc() is then allowed to grab bits
* out of any of those arenas that have free space.
*
- * With the above reset of fheap, the loop below sweeps back over the
+ * Whenever fheap is NULL here, the loop below sweeps back over the
* entire heap list again, resetting the free space in every arena to
* the amount stashed by pushheap() and finding the first arena with
* free space to optimize zhalloc()'s next search. When there's a lot
* of stuff already on the heap, this is an enormous amount of work,
* and performance goes to hell.
*
- * However, there doesn't seem to be any reason to reset fheap before
- * beginning this loop. Either it's already correct, or it has never
+ * However, if the arena to which fheap points is unused, we want to
+ * free it, so we have no choice but to do the sweep for a new fheap.
+ */
+ if (fheap && !fheap->sp)
+ fheap = NULL; /* We used to do this unconditionally */
+ /*
+ * In other cases, either fheap is already correct, or it has never
* been set and this loop will do it, or it'll be reset from scratch
* on the next popheap(). So all that's needed here is to pick up
* the scan wherever the last pass [or the last popheap()] left off.
@@ -344,6 +350,10 @@ freeheap(void)
hn = h->next;
if (h->sp) {
#ifdef ZSH_MEM_DEBUG
+#ifdef ZSH_VALGRIND
+ VALGRIND_MAKE_MEM_UNDEFINED((char *)arena(h) + h->sp->used,
+ h->used - h->sp->used);
+#endif
memset(arena(h) + h->sp->used, 0xff, h->used - h->sp->used);
#endif
h->used = h->sp->used;
@@ -366,12 +376,18 @@ freeheap(void)
h->heap_id = new_id;
}
#endif
+#ifdef ZSH_VALGRIND
+ VALGRIND_MEMPOOL_TRIM((char *)h, (char *)arena(h), h->used);
+#endif
} else {
#ifdef USE_MMAP
munmap((void *) h, h->size);
#else
zfree(h, HEAPSIZE);
#endif
+#ifdef ZSH_VALGRIND
+ VALGRIND_DESTROY_MEMPOOL((char *)h);
+#endif
}
}
if (hl)
@@ -403,6 +419,10 @@ popheap(void)
if ((hs = h->sp)) {
h->sp = hs->next;
#ifdef ZSH_MEM_DEBUG
+#ifdef ZSH_VALGRIND
+ VALGRIND_MAKE_MEM_UNDEFINED((char *)arena(h) + hs->used,
+ h->used - hs->used);
+#endif
memset(arena(h) + hs->used, 0xff, h->used - hs->used);
#endif
h->used = hs->used;
@@ -414,6 +434,9 @@ popheap(void)
}
h->heap_id = hs->heap_id;
#endif
+#ifdef ZSH_VALGRIND
+ VALGRIND_MEMPOOL_TRIM((char *)h, (char *)arena(h), h->used);
+#endif
if (!fheap && h->used < ARENA_SIZEOF(h))
fheap = h;
zfree(hs, sizeof(*hs));
@@ -425,6 +448,9 @@ popheap(void)
#else
zfree(h, HEAPSIZE);
#endif
+#ifdef ZSH_VALGRIND
+ VALGRIND_DESTROY_MEMPOOL((char *)h);
+#endif
}
}
if (hl)
@@ -496,6 +522,12 @@ zhalloc(size_t size)
{
Heap h;
size_t n;
+#ifdef ZSH_VALGRIND
+ size_t req_size = size;
+
+ if (size == 0)
+ return NULL;
+#endif
size = (size + H_ISIZE - 1) & ~(H_ISIZE - 1);
@@ -523,6 +555,9 @@ zhalloc(size_t size)
HEAPID_FMT ".\n", h->heap_id);
}
#endif
+#ifdef ZSH_VALGRIND
+ VALGRIND_MEMPOOL_ALLOC((char *)h, (char *)ret, req_size);
+#endif
return ret;
}
}
@@ -561,6 +596,12 @@ zhalloc(size_t size)
h->heap_id);
}
#endif
+#ifdef ZSH_VALGRIND
+ VALGRIND_CREATE_MEMPOOL((char *)h, 0, 0);
+ VALGRIND_MAKE_MEM_NOACCESS((char *)arena(h),
+ n - ((char *)arena(h)-(char *)h));
+ VALGRIND_MEMPOOL_ALLOC((char *)h, (char *)arena(h), req_size);
+#endif
if (hp)
hp->next = h;
@@ -586,13 +627,21 @@ hrealloc(char *p, size_t old, size_t new)
{
Heap h, ph;
+#ifdef ZSH_VALGRIND
+ size_t new_req = new;
+#endif
+
old = (old + H_ISIZE - 1) & ~(H_ISIZE - 1);
new = (new + H_ISIZE - 1) & ~(H_ISIZE - 1);
if (old == new)
return p;
if (!old && !p)
+#ifdef ZSH_VALGRIND
+ return zhalloc(new_req);
+#else
return zhalloc(new);
+#endif
/* find the heap with p */
@@ -615,14 +664,33 @@ hrealloc(char *p, size_t old, size_t new)
*/
if (p + old < arena(h) + h->used) {
if (new > old) {
+#ifdef ZSH_VALGRIND
+ char *ptr = (char *) zhalloc(new_req);
+#else
char *ptr = (char *) zhalloc(new);
+#endif
memcpy(ptr, p, old);
#ifdef ZSH_MEM_DEBUG
memset(p, 0xff, old);
#endif
+#ifdef ZSH_VALGRIND
+ VALGRIND_MEMPOOL_FREE((char *)h, (char *)p);
+ /*
+ * zhalloc() marked h,ptr,new as an allocation so we don't
+ * need to do that here.
+ */
+#endif
unqueue_signals();
return ptr;
} else {
+#ifdef ZSH_VALGRIND
+ VALGRIND_MEMPOOL_FREE((char *)h, (char *)p);
+ if (p) {
+ VALGRIND_MEMPOOL_ALLOC((char *)h, (char *)p,
+ new_req);
+ VALGRIND_MAKE_MEM_DEFINED((char *)h, (char *)p);
+ }
+#endif
unqueue_signals();
return new ? p : NULL;
}
@@ -657,10 +725,14 @@ hrealloc(char *p, size_t old, size_t new)
#else
zfree(h, HEAPSIZE);
#endif
+#ifdef ZSH_VALGRIND
+ VALGRIND_DESTROY_MEMPOOL((char *)h);
+#endif
unqueue_signals();
return NULL;
}
if (new > ARENA_SIZEOF(h)) {
+ Heap hnew;
/*
* Not enough memory in this heap. Allocate a new
* one of sufficient size.
@@ -682,17 +754,23 @@ hrealloc(char *p, size_t old, size_t new)
* a mmap'd segment be extended, so simply allocate
* a new one and copy.
*/
- Heap hnew;
-
hnew = mmap_heap_alloc(&n);
/* Copy the entire heap, header (with next pointer) included */
memcpy(hnew, h, h->size);
munmap((void *)h, h->size);
- h = hnew;
}
#else
- h = (Heap) realloc(h, n);
+ hnew = (Heap) realloc(h, n);
#endif
+#ifdef ZSH_VALGRIND
+ VALGRIND_MEMPOOL_FREE((char *)h, p);
+ VALGRIND_DESTROY_MEMPOOL((char *)h);
+ VALGRIND_CREATE_MEMPOOL((char *)hnew, 0, 0);
+ VALGRIND_MEMPOOL_ALLOC((char *)hnew, (char *)arena(hnew),
+ new_req);
+ VALGRIND_MAKE_MEM_DEFINED((char *)hnew, (char *)arena(hnew));
+#endif
+ h = hnew;
h->size = n;
if (ph)
@@ -700,6 +778,13 @@ hrealloc(char *p, size_t old, size_t new)
else
heaps = h;
}
+#ifdef ZSH_VALGRIND
+ else {
+ VALGRIND_MEMPOOL_FREE((char *)h, (char *)p);
+ VALGRIND_MEMPOOL_ALLOC((char *)h, (char *)p, new_req);
+ VALGRIND_MAKE_MEM_DEFINED((char *)h, (char *)p);
+ }
+#endif
h->used = new;
#ifdef ZSH_HEAP_DEBUG
h->heap_id = heap_id;
@@ -713,6 +798,11 @@ hrealloc(char *p, size_t old, size_t new)
if (h->used + (new - old) <= ARENA_SIZEOF(h)) {
h->used += new - old;
unqueue_signals();
+#ifdef ZSH_VALGRIND
+ VALGRIND_MEMPOOL_FREE((char *)h, (char *)p);
+ VALGRIND_MEMPOOL_ALLOC((char *)h, (char *)p, new_req);
+ VALGRIND_MAKE_MEM_DEFINED((char *)h, (char *)p);
+#endif
return p;
} else {
char *t = zhalloc(new);
@@ -721,6 +811,10 @@ hrealloc(char *p, size_t old, size_t new)
#ifdef ZSH_MEM_DEBUG
memset(p, 0xff, old);
#endif
+#ifdef ZSH_VALGRIND
+ VALGRIND_MEMPOOL_FREE((char *)h, (char *)p);
+ /* t already marked as allocated by zhalloc() */
+#endif
unqueue_signals();
return t;
}
@@ -856,7 +950,10 @@ zrealloc(void *ptr, size_t size)
ptr = NULL;
} else {
/* If ptr is NULL, then behave like malloc */
- ptr = malloc(size);
+ if (!(ptr = (void *) malloc(size))) {
+ zerr("fatal error: out of memory");
+ exit(1);
+ }
}
unqueue_signals();
@@ -1505,7 +1602,7 @@ zsfree(char *p)
MALLOC_RET_T
realloc(MALLOC_RET_T p, MALLOC_ARG_T size)
{
- struct m_hdr *m = (struct m_hdr *)(((char *)p) - M_ISIZE), *mp, *mt;
+ struct m_hdr *m = (struct m_hdr *)(((char *)p) - M_ISIZE), *mt;
char *r;
int i, l = 0;
@@ -1521,10 +1618,10 @@ realloc(MALLOC_RET_T p, MALLOC_ARG_T size)
/* check if we are reallocating a small block, if we do, we have
to compute the size of the block from the sort of block it is in */
for (i = 0; i < M_NSMALL; i++) {
- for (mp = NULL, mt = m_small[i];
+ for (mt = m_small[i];
mt && (((char *)mt) > ((char *)p) ||
(((char *)mt) + mt->len) < ((char *)p));
- mp = mt, mt = mt->next);
+ mt = mt->next);
if (mt) {
l = M_BSLEN(mt->len);
diff --git a/Src/mkmakemod.sh b/Src/mkmakemod.sh
index 2633c27d4..002160910 100644
--- a/Src/mkmakemod.sh
+++ b/Src/mkmakemod.sh
@@ -307,10 +307,10 @@ if $first_stage; then
echo "\$(SYMS_${mddname}): \$(PROTODEPS)"
echo
echo "${mddname}.export: \$(SYMS_${mddname})"
- echo " ( echo '#!'; cat \$(SYMS_${mddname}) | sed -n '/^X/{s/^X//;p;}' | sort -u ) > \$@"
+ echo " @( echo '#!'; cat \$(SYMS_${mddname}) | sed -n '/^X/{s/^X//;p;}' | sort -u ) > \$@"
echo
echo "modobjs.${mddname}: \$(MODOBJS_${mddname})"
- echo " echo '' \$(MODOBJS_${mddname}) $modobjs_sed>> \$(dir_src)/stamp-modobjs.tmp"
+ echo " @echo '' \$(MODOBJS_${mddname}) $modobjs_sed>> \$(dir_src)/stamp-modobjs.tmp"
echo
if test -z "$alwayslink"; then
case " $all_modules" in *" ${mddname}."*)
diff --git a/Src/options.c b/Src/options.c
index ce73d9901..6e4e7b911 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -165,6 +165,7 @@ static struct optname optns[] = {
{{NULL, "ignoreclosebraces", OPT_EMULATE}, IGNORECLOSEBRACES},
{{NULL, "ignoreeof", 0}, IGNOREEOF},
{{NULL, "incappendhistory", 0}, INCAPPENDHISTORY},
+{{NULL, "incappendhistorytime", 0}, INCAPPENDHISTORYTIME},
{{NULL, "interactive", OPT_SPECIAL}, INTERACTIVE},
{{NULL, "interactivecomments",OPT_BOURNE}, INTERACTIVECOMMENTS},
{{NULL, "ksharrays", OPT_EMULATE|OPT_BOURNE}, KSHARRAYS},
@@ -179,6 +180,7 @@ static struct optname optns[] = {
{{NULL, "listrowsfirst", 0}, LISTROWSFIRST},
{{NULL, "listtypes", OPT_ALL}, LISTTYPES},
{{NULL, "localoptions", OPT_EMULATE|OPT_KSH}, LOCALOPTIONS},
+{{NULL, "localloops", OPT_EMULATE}, LOCALLOOPS},
{{NULL, "localpatterns", OPT_EMULATE}, LOCALPATTERNS},
{{NULL, "localtraps", OPT_EMULATE|OPT_KSH}, LOCALTRAPS},
{{NULL, "login", OPT_SPECIAL}, LOGINSHELL},
@@ -207,6 +209,7 @@ static struct optname optns[] = {
{{NULL, "pathscript", OPT_EMULATE|OPT_BOURNE}, PATHSCRIPT},
{{NULL, "pipefail", OPT_EMULATE}, PIPEFAIL},
{{NULL, "posixaliases", OPT_EMULATE|OPT_BOURNE}, POSIXALIASES},
+{{NULL, "posixargzero", OPT_EMULATE}, POSIXARGZERO},
{{NULL, "posixbuiltins", OPT_EMULATE|OPT_BOURNE}, POSIXBUILTINS},
{{NULL, "posixcd", OPT_EMULATE|OPT_BOURNE}, POSIXCD},
{{NULL, "posixidentifiers", OPT_EMULATE|OPT_BOURNE}, POSIXIDENTIFIERS},
diff --git a/Src/params.c b/Src/params.c
index ad9e3470b..0699ead85 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -67,6 +67,7 @@ char **path, /* $path */
/**/
mod_export
char *argzero, /* $0 */
+ *posixzero, /* $0 */
*home, /* $HOME */
*nullcmd, /* $NULLCMD */
*oldpwd, /* $OLDPWD */
@@ -194,6 +195,8 @@ static const struct gsu_integer euid_gsu =
static const struct gsu_integer ttyidle_gsu =
{ ttyidlegetfn, nullintsetfn, stdunsetfn };
+static const struct gsu_scalar argzero_gsu =
+{ argzerogetfn, nullstrsetfn, nullunsetfn };
static const struct gsu_scalar username_gsu =
{ usernamegetfn, usernamesetfn, stdunsetfn };
static const struct gsu_scalar dash_gsu =
@@ -263,7 +266,7 @@ static initparam special_params[] ={
#define NULL_GSU BR((GsuScalar)(void *)NULL)
#define IPDEF1(A,B,C) {{NULL,A,PM_INTEGER|PM_SPECIAL|C},BR(NULL),GSU(B),10,0,NULL,NULL,NULL,0}
IPDEF1("#", pound_gsu, PM_READONLY),
-IPDEF1("ERRNO", errno_gsu, 0),
+IPDEF1("ERRNO", errno_gsu, PM_UNSET),
IPDEF1("GID", gid_gsu, PM_DONTIMPORT | PM_RESTRICTED),
IPDEF1("EGID", egid_gsu, PM_DONTIMPORT | PM_RESTRICTED),
IPDEF1("HISTSIZE", histsize_gsu, PM_RESTRICTED),
@@ -279,12 +282,13 @@ IPDEF2("USERNAME", username_gsu, PM_DONTIMPORT|PM_RESTRICTED),
IPDEF2("-", dash_gsu, PM_READONLY),
IPDEF2("histchars", histchars_gsu, PM_DONTIMPORT),
IPDEF2("HOME", home_gsu, PM_UNSET),
-IPDEF2("TERM", term_gsu, 0),
+IPDEF2("TERM", term_gsu, PM_UNSET),
IPDEF2("TERMINFO", terminfo_gsu, PM_UNSET),
IPDEF2("WORDCHARS", wordchars_gsu, 0),
IPDEF2("IFS", ifs_gsu, PM_DONTIMPORT),
-IPDEF2("_", underscore_gsu, PM_READONLY),
+IPDEF2("_", underscore_gsu, PM_DONTIMPORT),
IPDEF2("KEYBOARD_HACK", keyboard_hack_gsu, PM_DONTIMPORT),
+IPDEF2("0", argzero_gsu, 0),
#ifdef USE_LOCALE
# define LCIPDEF(name) IPDEF2(name, lc_blah_gsu, PM_UNSET)
@@ -326,20 +330,20 @@ IPDEF5("SHLVL", &shlvl, varinteger_gsu),
IPDEF5("TRY_BLOCK_ERROR", &try_errflag, varinteger_gsu),
#define IPDEF7(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0}
+#define IPDEF7U(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL|PM_UNSET},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0}
IPDEF7("OPTARG", &zoptarg),
IPDEF7("NULLCMD", &nullcmd),
-IPDEF7("POSTEDIT", &postedit),
+IPDEF7U("POSTEDIT", &postedit),
IPDEF7("READNULLCMD", &readnullcmd),
IPDEF7("PS1", &prompt),
-IPDEF7("RPS1", &rprompt),
-IPDEF7("RPROMPT", &rprompt),
+IPDEF7U("RPS1", &rprompt),
+IPDEF7U("RPROMPT", &rprompt),
IPDEF7("PS2", &prompt2),
-IPDEF7("RPS2", &rprompt2),
-IPDEF7("RPROMPT2", &rprompt2),
+IPDEF7U("RPS2", &rprompt2),
+IPDEF7U("RPROMPT2", &rprompt2),
IPDEF7("PS3", &prompt3),
IPDEF7("PS4", &prompt4),
IPDEF7("SPROMPT", &sprompt),
-IPDEF7("0", &argzero),
#define IPDEF8(A,B,C,D) {{NULL,A,D|PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(colonarr_gsu),0,0,NULL,C,NULL,0}
IPDEF8("CDPATH", &cdpath, "cdpath", 0),
@@ -2416,9 +2420,10 @@ setnumvalue(Value v, mnumber val)
if ((val.type & MN_INTEGER) || outputradix) {
if (!(val.type & MN_INTEGER))
val.u.l = (zlong) val.u.d;
- convbase(p = buf, val.u.l, outputradix);
+ p = convbase_underscore(buf, val.u.l, outputradix,
+ outputunderscore);
} else
- p = convfloat(val.u.d, 0, 0, NULL);
+ p = convfloat_underscore(val.u.d, outputunderscore);
setstrvalue(v, ztrdup(p));
break;
case PM_INTEGER:
@@ -3979,6 +3984,17 @@ lcsetfn(Param pm, char *x)
}
#endif /* USE_LOCALE */
+/* Function to get value for special parameter `0' */
+
+/**/
+static char *
+argzerogetfn(UNUSED(Param pm))
+{
+ if (isset(POSIXARGZERO))
+ return posixzero;
+ return argzero;
+}
+
/* Function to get value for special parameter `HISTSIZE' */
/**/
@@ -4245,7 +4261,7 @@ static char **
pipestatgetfn(UNUSED(Param pm))
{
char **x = (char **) zhalloc((numpipestats + 1) * sizeof(char *));
- char buf[20], **p;
+ char buf[DIGBUFSIZE], **p;
int *q, i;
for (p = x, q = pipestats, i = numpipestats; i--; p++, q++) {
@@ -4555,9 +4571,14 @@ delenv(Param pm)
*/
}
+/*
+ * Guts of convbase: this version can return the number of digits
+ * sans any base discriminator.
+ */
+
/**/
-mod_export void
-convbase(char *s, zlong v, int base)
+void
+convbase_ptr(char *s, zlong v, int base, int *ndigits)
{
int digs = 0;
zulong x;
@@ -4583,6 +4604,8 @@ convbase(char *s, zlong v, int base)
x /= base;
if (!digs)
digs = 1;
+ if (ndigits)
+ *ndigits = digs;
s[digs--] = '\0';
x = v;
while (digs >= 0) {
@@ -4594,6 +4617,64 @@ convbase(char *s, zlong v, int base)
}
/*
+ * Basic conversion of integer to a string given a base.
+ * If 0 base is 10.
+ * If negative no base discriminator is output.
+ */
+
+/**/
+mod_export void
+convbase(char *s, zlong v, int base)
+{
+ convbase_ptr(s, v, base, NULL);
+}
+
+/*
+ * Add underscores to converted integer for readability with given spacing.
+ * s is as for convbase: at least BDIGBUFSIZE.
+ * If underscores were added, returned value with underscores comes from
+ * heap, else the returned value is s.
+ */
+
+/**/
+char *
+convbase_underscore(char *s, zlong v, int base, int underscore)
+{
+ char *retptr, *sptr, *dptr;
+ int ndigits, nunderscore, mod, len;
+
+ convbase_ptr(s, v, base, &ndigits);
+
+ if (underscore <= 0)
+ return s;
+
+ nunderscore = (ndigits - 1) / underscore;
+ if (!nunderscore)
+ return s;
+ len = strlen(s);
+ retptr = zhalloc(len + nunderscore + 1);
+ mod = 0;
+ memcpy(retptr, s, len - ndigits);
+ sptr = s + len;
+ dptr = retptr + len + nunderscore;
+ /* copy the null */
+ *dptr-- = *sptr--;
+ for (;;) {
+ *dptr = *sptr;
+ if (!--ndigits)
+ break;
+ dptr--;
+ sptr--;
+ if (++mod == underscore) {
+ mod = 0;
+ *dptr-- = '_';
+ }
+ }
+
+ return retptr;
+}
+
+/*
* Convert a floating point value for output.
* Unlike convbase(), this has its own internal storage and returns
* a value from the heap.
@@ -4659,6 +4740,83 @@ convfloat(double dval, int digits, int flags, FILE *fout)
return ret;
}
+/*
+ * convert float to string with basic options but inserting underscores
+ * for readability.
+ */
+
+/**/
+char *convfloat_underscore(double dval, int underscore)
+{
+ int ndigits_int = 0, ndigits_frac = 0, nunderscore, len;
+ char *s, *retptr, *sptr, *dptr;
+
+ s = convfloat(dval, 0, 0, NULL);
+ if (underscore <= 0)
+ return s;
+
+ /*
+ * Count the number of digits before and after the decimal point, if any.
+ */
+ sptr = s;
+ if (*sptr == '-')
+ sptr++;
+ while (idigit(*sptr)) {
+ ndigits_int++;
+ sptr++;
+ }
+ if (*sptr == '.') {
+ sptr++;
+ while (idigit(*sptr)) {
+ ndigits_frac++;
+ sptr++;
+ }
+ }
+
+ /*
+ * Work out how many underscores to insert --- remember we
+ * put them in integer and fractional parts separately.
+ */
+ nunderscore = (ndigits_int-1) / underscore + (ndigits_frac-1) / underscore;
+ if (!nunderscore)
+ return s;
+ len = strlen(s);
+ dptr = retptr = zhalloc(len + nunderscore + 1);
+
+ /*
+ * Insert underscores in integer part.
+ * Grouping starts from the point in both directions.
+ */
+ sptr = s;
+ if (*sptr == '-')
+ *dptr++ = *sptr++;
+ while (ndigits_int) {
+ *dptr++ = *sptr++;
+ if (--ndigits_int && !(ndigits_int % underscore))
+ *dptr++ = '_';
+ }
+ if (ndigits_frac) {
+ /*
+ * Insert underscores in the fractional part.
+ */
+ int mod = 0;
+ /* decimal point, we already checked */
+ *dptr++ = *sptr++;
+ while (ndigits_frac) {
+ *dptr++ = *sptr++;
+ mod++;
+ if (--ndigits_frac && mod == underscore) {
+ *dptr++ = '_';
+ mod = 0;
+ }
+ }
+ }
+ /* Copy exponent and anything else up to null */
+ while ((*dptr++ = *sptr++))
+ ;
+ return retptr;
+}
+
/* Start a parameter scope */
/**/
diff --git a/Src/parse.c b/Src/parse.c
index f0d0855d3..5f1303f1c 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -1471,7 +1471,6 @@ par_funcdef(int *complex)
if (num == 0) {
/* Anonymous function, possibly with arguments */
incmdpos = 0;
- *complex = 1;
}
zshlex();
} else if (unset(SHORTLOOPS)) {
@@ -1503,6 +1502,7 @@ par_funcdef(int *complex)
num++;
zshlex();
}
+ *complex = (num > 0);
ecbuf[parg] = ecused - parg; /*?*/
ecbuf[parg+1] = num;
}
@@ -1736,7 +1736,6 @@ par_simple(int *complex, int nr)
if (argc == 0) {
/* Anonymous function, possibly with arguments */
incmdpos = 0;
- *complex = 1;
}
zshlex();
} else {
@@ -1776,6 +1775,7 @@ par_simple(int *complex, int nr)
argc++;
zshlex();
}
+ *complex = (argc > 0);
ecbuf[parg] = ecused - parg; /*?*/
ecbuf[parg+1] = argc;
}
@@ -2068,6 +2068,9 @@ par_cond_2(void)
/* one argument: [ foo ] is equivalent to [ -n foo ] */
s1 = tokstr;
condlex();
+ /* ksh behavior: [ -t ] means [ -t 1 ]; bash disagrees */
+ if (unset(POSIXBUILTINS) && !strcmp(s1, "-t"))
+ return par_cond_double(s1, dupstring("1"));
return par_cond_double(dupstring("-n"), s1);
}
if (testargs[1]) {
@@ -2086,6 +2089,10 @@ par_cond_2(void)
return par_cond_triple(s1, s2, s3);
}
}
+ /*
+ * We fall through here on any non-numeric infix operator
+ * or any other time there are at least two arguments.
+ */
}
if (tok == BANG) {
/*
@@ -2114,18 +2121,20 @@ par_cond_2(void)
condlex();
return r;
}
+ s1 = tokstr;
+ dble = (s1 && *s1 == '-'
+ && (condlex != testlex
+ || strspn(s1+1, "abcdefghknoprstuwxzLONGS") == 1)
+ && !s1[2]);
if (tok != STRING) {
- if (tok && tok != LEXERR && condlex == testlex) {
- s1 = tokstr;
+ /* Check first argument for [[ STRING ]] re-interpretation */
+ if (s1 /* tok != DOUTBRACK && tok != DAMPER && tok != DBAR */
+ && tok != LEXERR && (!dble || condlex == testlex)) {
condlex();
- return par_cond_double("-n", s1);
+ return par_cond_double(dupstring("-n"), s1);
} else
YYERROR(ecused);
}
- s1 = tokstr;
- if (condlex == testlex)
- dble = (*s1 == '-' && strspn(s1+1, "abcdefghknoprstuwxzLONGS") == 1
- && !s1[2]);
condlex();
if (tok == INANG || tok == OUTANG) {
enum lextok xtok = tok;
@@ -2140,15 +2149,21 @@ par_cond_2(void)
return 1;
}
if (tok != STRING) {
- if (tok != LEXERR && condlex == testlex) {
- if (!dble)
- return par_cond_double("-n", s1);
- else if (!strcmp(s1, "-t"))
- return par_cond_double(s1, "1");
+ /*
+ * Check second argument in case semantics e.g. [ = -a = ]
+ * mean we have to go back and fix up the first one
+ */
+ if (tok != LEXERR) {
+ if (!dble || condlex == testlex)
+ return par_cond_double(dupstring("-n"), s1);
+ else
+ return par_cond_multi(s1, newlinklist());
} else
YYERROR(ecused);
}
- s2 = tokstr;
+ s2 = tokstr;
+ if (condlex != testlex)
+ dble = (s2 && *s2 == '-' && !s2[2]);
incond++; /* parentheses do globbing */
condlex();
incond--; /* parentheses do grouping */
diff --git a/Src/pattern.c b/Src/pattern.c
index b79c3b444..94a299ebb 100644
--- a/Src/pattern.c
+++ b/Src/pattern.c
@@ -2223,6 +2223,8 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalen,
return ret;
} else {
+ int q = queue_signal_level();
+
/*
* Test for a `must match' string, unless we're scanning for a match
* in which case we don't need to do this each time.
@@ -2270,6 +2272,8 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalen,
patinput = patinstart;
+ dont_queue_signals();
+
if (patmatch((Upat)progstr)) {
/*
* we were lazy and didn't save the globflags if an exclusion
@@ -2406,6 +2410,8 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalen,
} else
ret = 0;
+ restore_queue_signals(q);
+
if (tryalloced)
zfree(tryalloced, unmetalen + unmetalenp);
@@ -2485,7 +2491,7 @@ patmatch(Upat prog)
zrange_t from, to, comp;
patint_t nextch;
- while (scan) {
+ while (scan && !errflag) {
next = PATNEXT(scan);
if (!globdots && P_NOTDOT(scan) && patinput == patinstart &&
diff --git a/Src/prompt.c b/Src/prompt.c
index 95a7d4969..328ae3c66 100644
--- a/Src/prompt.c
+++ b/Src/prompt.c
@@ -270,6 +270,8 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
char *ss, *hostnam;
int t0, arg, test, sep, j, numjobs;
struct tm *tm;
+ struct timezone dummy_tz;
+ struct timeval tv;
time_t timet;
Nameddir nd;
@@ -365,6 +367,8 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
case 'l':
*bv->bp = '\0';
countprompt(bv->bufline, &t0, 0, 0);
+ if (minus)
+ t0 = zterm_columns - t0;
if (t0 >= arg)
test = 1;
break;
@@ -558,6 +562,14 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
break;
case '<':
case '>':
+ /* Test (minus) here so -0 means "at the right margin" */
+ if (minus) {
+ *bv->bp = '\0';
+ countprompt(bv->bufline, &t0, 0, 0);
+ arg = zterm_columns - t0 + arg;
+ if (arg <= 0)
+ arg = 1;
+ }
if (!prompttrunc(arg, *bv->fm, doprint, endchar, txtchangep))
return *bv->fm;
break;
@@ -636,8 +648,8 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
tmfmt = "%l:%M%p";
break;
}
- timet = time(NULL);
- tm = localtime(&timet);
+ gettimeofday(&tv, &dummy_tz);
+ tm = localtime(&tv.tv_sec);
/*
* Hack because strftime won't say how
* much space it actually needs. Try to add it
@@ -647,7 +659,7 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
*/
for(j = 0, t0 = strlen(tmfmt)*8; j < 3; j++, t0*=2) {
addbufspc(t0);
- if (ztrftime(bv->bp, t0, tmfmt, tm) >= 0)
+ if (ztrftime(bv->bp, t0, tmfmt, tm, tv.tv_usec) >= 0)
break;
}
/* There is enough room for this because addbufspc(t0)
@@ -1172,7 +1184,7 @@ prompttrunc(int arg, int truncchar, int doprint, int endchar,
addbufspc(1);
*bv->bp++ = '<';
}
- ptr = bv->buf + w; /* addbv->bufspc() may have realloc()'d bv->buf */
+ ptr = bv->buf + w; /* addbufspc() may have realloc()'d bv->buf */
/*
* Now:
* bv->buf is the start of the output prompt buffer
@@ -1187,7 +1199,7 @@ prompttrunc(int arg, int truncchar, int doprint, int endchar,
bv->trunccount = bv->dontcount;
putpromptchar(doprint, endchar, txtchangep);
bv->trunccount = 0;
- ptr = bv->buf + w; /* putpromptchar() may have realloc()'d */
+ ptr = bv->buf + w; /* putpromptchar() may have realloc()'d */
*bv->bp = '\0';
/*
* Now:
@@ -1473,7 +1485,7 @@ prompttrunc(int arg, int truncchar, int doprint, int endchar,
/* Now we have to trick it into matching endchar again */
bv->fm--;
} else {
- if (*bv->fm != ']')
+ if (*bv->fm != endchar)
bv->fm++;
while(*bv->fm && *bv->fm != truncchar) {
if (*bv->fm == '\\' && bv->fm[1])
diff --git a/Src/prototypes.h b/Src/prototypes.h
index 00988ac4c..e3db4f5ee 100644
--- a/Src/prototypes.h
+++ b/Src/prototypes.h
@@ -130,5 +130,5 @@ extern char *strerror _((int errnum));
/***************************************************/
#ifndef HAVE_MEMMOVE
-extern void bcopy _((const void *, void *, int));
+extern void bcopy _((const void *, void *, size_t));
#endif
diff --git a/Src/signals.c b/Src/signals.c
index c8f5fbcca..cb2b58161 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -369,7 +369,7 @@ signal_suspend(UNUSED(int sig), int wait_cmd)
#ifdef POSIX_SIGNALS
# ifdef BROKEN_POSIX_SIGSUSPEND
sigprocmask(SIG_SETMASK, &set, &oset);
- pause();
+ ret = pause();
sigprocmask(SIG_SETMASK, &oset, NULL);
# else /* not BROKEN_POSIX_SIGSUSPEND */
ret = sigsuspend(&set);
@@ -1155,6 +1155,7 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
char *name, num[4];
int obreaks = breaks;
int oretflag = retflag;
+ int olastval = lastval;
int isfunc;
int traperr, new_trap_state, new_trap_return;
@@ -1261,6 +1262,13 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
} else {
if (traperr && !EMULATION(EMULATE_SH))
lastval = 1;
+ else {
+ /*
+ * With no explicit forced return, we keep the
+ * lastval from before the trap ran.
+ */
+ lastval = olastval;
+ }
if (try_tryflag)
errflag = traperr;
breaks += obreaks;
diff --git a/Src/subst.c b/Src/subst.c
index 1059508ef..4a5fe3a3c 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -169,7 +169,7 @@ stringsubst(LinkList list, LinkNode node, int pf_flags, int asssub)
if (errflag)
return NULL;
if (!subst)
- subst = "";
+ rest = subst = "";
sublen = strlen(subst);
restlen = strlen(rest);
@@ -2878,6 +2878,67 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags)
}
break;
}
+ } else if (inbrace && (*s == '^' || *s == Hat)) {
+ char **zip;
+ int shortest = 1;
+ ++s;
+ if (*s == '^' || *s == Hat) {
+ shortest = 0;
+ ++s;
+ }
+ if (*itype_end(s, IIDENT, 0)) {
+ untokenize(s);
+ zerr("not an identifier: %s", s);
+ return NULL;
+ }
+ if (vunset) {
+ if (unset(UNSET)) {
+ *idend = '\0';
+ zerr("%s: parameter not set", idbeg);
+ return NULL;
+ }
+ val = dupstring("");
+ } else {
+ char *sval;
+ zip = getaparam(s);
+ if (!zip) {
+ sval = getsparam(s);
+ if (sval)
+ zip = hmkarray(sval);
+ }
+ if (!isarr) aval = mkarray(val);
+ if (zip) {
+ char **out;
+ int alen, ziplen, outlen, i = 0;
+ alen = arrlen(aval);
+ ziplen = arrlen(zip);
+ outlen = shortest ^ (alen > ziplen) ? alen : ziplen;
+ if (!shortest && (alen == 0 || ziplen == 0)) {
+ if (ziplen)
+ aval = arrdup(zip);
+ } else {
+ out = zhalloc(sizeof(char *) * (2 * outlen + 1));
+ while (i < outlen) {
+ if (copied)
+ out[i*2] = aval[i % alen];
+ else
+ out[i*2] = dupstring(aval[i % alen]);
+ out[i*2+1] = dupstring(zip[i % ziplen]);
+ i++;
+ }
+ out[i*2] = NULL;
+ aval = out;
+ copied = 1;
+ isarr = 1;
+ }
+ } else {
+ if (unset(UNSET)) {
+ zerr("%s: parameter not set", s);
+ return NULL;
+ }
+ val = dupstring("");
+ }
+ }
} else if (inbrace && (*s == '|' || *s == Bar ||
*s == '*' || *s == Star)) {
int intersect = (*s == '*' || *s == Star);
@@ -2935,7 +2996,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags)
*/
if (!vunset) {
if (isarr) {
- aval = mkarray(NULL);
+ aval = hmkarray(NULL);
} else {
val = dupstring("");
}
@@ -3754,19 +3815,19 @@ static char *
arithsubst(char *a, char **bptr, char *rest)
{
char *s = *bptr, *t;
- char buf[BDIGBUFSIZE], *b = buf;
+ char buf[BDIGBUFSIZE], *b;
mnumber v;
singsub(&a);
v = matheval(a);
if ((v.type & MN_FLOAT) && !outputradix)
- b = convfloat(v.u.d, 0, 0, NULL);
+ b = convfloat_underscore(v.u.d, outputunderscore);
else {
if (v.type & MN_FLOAT)
v.u.l = (zlong) v.u.d;
- convbase(buf, v.u.l, outputradix);
+ b = convbase_underscore(buf, v.u.l, outputradix, outputunderscore);
}
- t = *bptr = (char *) hcalloc(strlen(*bptr) + strlen(b) +
+ t = *bptr = (char *) hcalloc(strlen(*bptr) + strlen(b) +
strlen(rest) + 1);
t--;
while ((*++t = *s++));
diff --git a/Src/utils.c b/Src/utils.c
index c6d178ce2..998e46a36 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -725,32 +725,36 @@ xsymlinks(char *s)
char **pp, **opp;
char xbuf2[PATH_MAX*2], xbuf3[PATH_MAX*2];
int t0, ret = 0;
+ zulong xbuflen = strlen(xbuf);
opp = pp = slashsplit(s);
- for (; *pp; pp++) {
- if (!strcmp(*pp, ".")) {
- zsfree(*pp);
+ for (; xbuflen < sizeof(xbuf) && *pp; pp++) {
+ if (!strcmp(*pp, "."))
continue;
- }
if (!strcmp(*pp, "..")) {
char *p;
- zsfree(*pp);
if (!strcmp(xbuf, "/"))
continue;
if (!*xbuf)
continue;
- p = xbuf + strlen(xbuf);
- while (*--p != '/');
+ p = xbuf + xbuflen;
+ while (*--p != '/')
+ xbuflen--;
*p = '\0';
continue;
}
sprintf(xbuf2, "%s/%s", xbuf, *pp);
t0 = readlink(unmeta(xbuf2), xbuf3, PATH_MAX);
if (t0 == -1) {
- strcat(xbuf, "/");
- strcat(xbuf, *pp);
- zsfree(*pp);
+ zulong pplen = strlen(*pp) + 1;
+ if ((xbuflen += pplen) < sizeof(xbuf)) {
+ strcat(xbuf, "/");
+ strcat(xbuf, *pp);
+ } else {
+ *xbuf = 0;
+ break;
+ }
} else {
ret = 1;
metafy(xbuf3, t0, META_NOALLOC);
@@ -759,10 +763,9 @@ xsymlinks(char *s)
xsymlinks(xbuf3 + 1);
} else
xsymlinks(xbuf3);
- zsfree(*pp);
}
}
- free(opp);
+ freearray(opp);
return ret;
}
@@ -779,8 +782,10 @@ xsymlink(char *s)
return NULL;
*xbuf = '\0';
xsymlinks(s + 1);
- if (!*xbuf)
+ if (!*xbuf) {
+ zwarn("path expansion failed, using root directory");
return ztrdup("/");
+ }
return ztrdup(xbuf);
}
@@ -2486,7 +2491,7 @@ getquery(char *valid_chars, int purge)
static int d;
static char *guess, *best;
-static Patprog spckpat;
+static Patprog spckpat, spnamepat;
/**/
static void
@@ -2557,6 +2562,13 @@ spckword(char **s, int hist, int cmd, int ask)
} else
spckpat = NULL;
+ if ((correct_ignore = getsparam("CORRECT_IGNORE_FILE")) != NULL) {
+ tokenize(correct_ignore = dupstring(correct_ignore));
+ remnulargs(correct_ignore);
+ spnamepat = patcompile(correct_ignore, 0, NULL);
+ } else
+ spnamepat = NULL;
+
if (**s == String && !*t) {
guess = *s + 1;
if (itype_end(guess, IIDENT, 1) == guess)
@@ -2698,10 +2710,13 @@ ztrftimebuf(int *bufsizeptr, int decr)
/**/
mod_export int
-ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm)
+ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec)
{
- int hr12, decr;
-#ifndef HAVE_STRFTIME
+ int hr12;
+#ifdef HAVE_STRFTIME
+ int decr;
+ char tmp[4];
+#else
static char *astr[] =
{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
static char *estr[] =
@@ -2709,12 +2724,12 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm)
"Aug", "Sep", "Oct", "Nov", "Dec"};
#endif
char *origbuf = buf;
- char tmp[4];
while (*fmt)
if (*fmt == '%') {
int strip;
+ int digs = 3;
fmt++;
if (*fmt == '-') {
@@ -2722,6 +2737,17 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm)
fmt++;
} else
strip = 0;
+ if (idigit(*fmt)) {
+ /* Digit --- only useful with . */
+ char *dstart = fmt;
+ char *dend = fmt+1;
+ while (idigit(*dend))
+ dend++;
+ if (*dend == '.') {
+ fmt = dend;
+ digs = atoi(dstart);
+ }
+ }
/*
* Assume this format will take up at least two
* characters. Not always true, but if that matters
@@ -2731,6 +2757,20 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm)
if (ztrftimebuf(&bufsize, 2))
return -1;
switch (*fmt++) {
+ case '.':
+ if (ztrftimebuf(&bufsize, digs))
+ return -1;
+ if (digs > 6)
+ digs = 6;
+ if (digs < 6) {
+ int trunc;
+ for (trunc = 5 - digs; trunc; trunc--)
+ usec /= 10;
+ usec = (usec + 5) / 10;
+ }
+ sprintf(buf, "%0*ld", digs, usec);
+ buf += digs;
+ break;
case 'd':
if (tm->tm_mday > 9 || !strip)
*buf++ = '0' + tm->tm_mday / 10;
@@ -3332,6 +3372,17 @@ mkarray(char *s)
}
/**/
+mod_export char **
+hmkarray(char *s)
+{
+ char **t = (char **) zhalloc((s) ? (2 * sizeof s) : (sizeof s));
+
+ if ((*t = s))
+ t[1] = NULL;
+ return t;
+}
+
+/**/
mod_export void
zbeep(void)
{
@@ -3778,6 +3829,8 @@ mindist(char *dir, char *mindistguess, char *mindistbest)
if (!(dd = opendir(unmeta(dir))))
return mindistd;
while ((fn = zreaddir(dd, 0))) {
+ if (spnamepat && pattry(spnamepat, fn))
+ continue;
nd = spdist(fn, mindistguess,
(int)strlen(mindistguess) / 4 + 1);
if (nd <= mindistd) {
@@ -4256,6 +4309,12 @@ mod_export char *
zreaddir(DIR *dir, int ignoredots)
{
struct dirent *de;
+#if defined(HAVE_ICONV) && defined(__APPLE__)
+ static iconv_t conv_ds = (iconv_t)0;
+ static char *conv_name = 0;
+ char *conv_name_ptr, *orig_name_ptr;
+ size_t conv_name_len, orig_name_len;
+#endif
do {
de = readdir(dir);
@@ -4264,6 +4323,30 @@ zreaddir(DIR *dir, int ignoredots)
} while(ignoredots && de->d_name[0] == '.' &&
(!de->d_name[1] || (de->d_name[1] == '.' && !de->d_name[2])));
+#if defined(HAVE_ICONV) && defined(__APPLE__)
+ if (!conv_ds)
+ conv_ds = iconv_open("UTF-8", "UTF-8-MAC");
+ if (conv_ds != (iconv_t)(-1)) {
+ /* Force initial state in case re-using conv_ds */
+ (void) iconv(conv_ds, 0, &orig_name_len, 0, &conv_name_len);
+
+ orig_name_ptr = de->d_name;
+ orig_name_len = strlen(de->d_name);
+ conv_name = zrealloc(conv_name, orig_name_len+1);
+ conv_name_ptr = conv_name;
+ conv_name_len = orig_name_len;
+ if (iconv(conv_ds,
+ &orig_name_ptr, &orig_name_len,
+ &conv_name_ptr, &conv_name_len) != (size_t)(-1) &&
+ orig_name_len == 0) {
+ /* Completely converted, metafy and return */
+ *conv_name_ptr = '\0';
+ return metafy(conv_name, -1, META_STATIC);
+ }
+ /* Error, or conversion incomplete, keep the original name */
+ }
+#endif
+
return metafy(de->d_name, -1, META_STATIC);
}
diff --git a/Src/watch.c b/Src/watch.c
index 5231579f8..8dea0b495 100644
--- a/Src/watch.c
+++ b/Src/watch.c
@@ -330,7 +330,7 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
}
timet = getlogtime(u, inout);
tm = localtime(&timet);
- ztrftime(buf, 40, fm2, tm);
+ ztrftime(buf, 40, fm2, tm, 0L);
printf("%s", (*buf == ' ') ? buf + 1 : buf);
break;
case '%':
diff --git a/Src/zsh.h b/Src/zsh.h
index c86d2a62c..fa7396112 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -33,9 +33,6 @@
/*
* Our longest integer type: will be a 64 bit either if long already is,
* or if we found some alternative such as long long.
- * Currently we only define this to be longer than a long if
- * --enable-largefile * was given. That enables internal use of 64-bit
- * types even if no actual large file support is present.
*/
#ifdef ZSH_64_BIT_TYPE
typedef ZSH_64_BIT_TYPE zlong;
@@ -50,6 +47,32 @@ typedef unsigned long zulong;
#endif
/*
+ * Work out how to define large integer constants that will fit
+ * in a zlong.
+ */
+#if defined(ZSH_64_BIT_TYPE) || defined(LONG_IS_64_BIT)
+/* We have some 64-bit type */
+#ifdef LONG_IS_64_BIT
+/* It's long */
+#define ZLONG_CONST(x) x ## l
+#else
+/* It's long long */
+#ifdef ZLONG_IS_LONG_LONG
+#define ZLONG_CONST(x) x ## ll
+#else
+/*
+ * There's some 64-bit type, but we don't know what it is.
+ * We'll just cast it and hope the compiler does the right thing.
+ */
+#define ZLONG_CONST(x) ((zlong)x)
+#endif
+#endif
+#else
+/* We're stuck with long */
+#define ZLONG_CONST(x) (x ## l)
+#endif
+
+/*
* Double float support requires 64-bit alignment, so if longs and
* pointers are less we need to pad out.
*/
@@ -2092,6 +2115,7 @@ enum {
IGNORECLOSEBRACES,
IGNOREEOF,
INCAPPENDHISTORY,
+ INCAPPENDHISTORYTIME,
INTERACTIVE,
INTERACTIVECOMMENTS,
KSHARRAYS,
@@ -2105,6 +2129,7 @@ enum {
LISTPACKED,
LISTROWSFIRST,
LISTTYPES,
+ LOCALLOOPS,
LOCALOPTIONS,
LOCALPATTERNS,
LOCALTRAPS,
@@ -2128,6 +2153,7 @@ enum {
PATHSCRIPT,
PIPEFAIL,
POSIXALIASES,
+ POSIXARGZERO,
POSIXBUILTINS,
POSIXCD,
POSIXIDENTIFIERS,
diff --git a/Src/zsh.mdd b/Src/zsh.mdd
index d77fc4336..cec3edab7 100644
--- a/Src/zsh.mdd
+++ b/Src/zsh.mdd
@@ -44,7 +44,7 @@ patchlevel.h: FORCE
cp -f $(sdir)/$@.release $@; \
else \
echo '#define ZSH_PATCHLEVEL "'`cd $(sdir) && git describe --tags --long`'"' > $@.tmp; \
- cmp $@ $@.tmp 2>/dev/null && rm -f $@.tmp || mv $@.tmp $@; \
+ cmp $@ $@.tmp >/dev/null 2>&1 && rm -f $@.tmp || mv $@.tmp $@; \
fi
FORCE:
diff --git a/Src/zsh_system.h b/Src/zsh_system.h
index e68fd62f9..811340d42 100644
--- a/Src/zsh_system.h
+++ b/Src/zsh_system.h
@@ -286,11 +286,15 @@ struct timezone {
# include <limits.h>
#endif
+#ifdef USE_STACK_ALLOCATION
#ifdef HAVE_VARIABLE_LENGTH_ARRAYS
# define VARARR(X,Y,Z) X (Y)[Z]
#else
# define VARARR(X,Y,Z) X *(Y) = (X *) alloca(sizeof(X) * (Z))
#endif
+#else
+# define VARARR(X,Y,Z) X *(Y) = (X *) zhalloc(sizeof(X) * (Z))
+#endif
/* we should handle unlimited sizes from pathconf(_PC_PATH_MAX) */
/* but this is too much trouble */
@@ -708,7 +712,10 @@ struct timezone {
#endif
#ifndef HAVE_MEMMOVE
-# define memmove(dest, src, len) bcopy((src), (dest), (len))
+# ifndef memmove
+static char *zmmv;
+# define memmove(dest, src, len) (bcopy((src), zmmv = (dest), (len)), zmmv)
+# endif
#endif
#ifndef offsetof
@@ -874,3 +881,8 @@ extern short ospeed;
# endif
# endif
#endif
+
+#ifdef ZSH_VALGRIND
+# include "valgrind/valgrind.h"
+# include "valgrind/memcheck.h"
+#endif