summaryrefslogtreecommitdiff
path: root/Src
diff options
context:
space:
mode:
Diffstat (limited to 'Src')
-rw-r--r--Src/Builtins/rlimits.c8
-rw-r--r--Src/Builtins/sched.c18
-rw-r--r--Src/Modules/curses.c36
-rw-r--r--Src/Modules/datetime.c2
-rw-r--r--Src/Modules/db_gdbm.c2
-rw-r--r--Src/Modules/files.c2
-rw-r--r--Src/Modules/langinfo.c2
-rw-r--r--Src/Modules/mapfile.c2
-rw-r--r--Src/Modules/mathfunc.c6
-rw-r--r--Src/Modules/newuser.c4
-rw-r--r--Src/Modules/param_private.c20
-rw-r--r--Src/Modules/parameter.c7
-rw-r--r--Src/Modules/pcre.c4
-rw-r--r--Src/Modules/regex.c2
-rw-r--r--Src/Modules/socket.c2
-rw-r--r--Src/Modules/stat.c2
-rw-r--r--Src/Modules/system.c10
-rw-r--r--Src/Modules/system.mdd7
-rw-r--r--Src/Modules/tcp.c4
-rw-r--r--Src/Modules/termcap.c4
-rw-r--r--Src/Modules/terminfo.c4
-rw-r--r--Src/Modules/zftp.c8
-rw-r--r--Src/Modules/zpty.c2
-rw-r--r--Src/Modules/zselect.c2
-rw-r--r--Src/Modules/zutil.c29
-rw-r--r--Src/Zle/comp.h29
-rw-r--r--Src/Zle/compcore.c64
-rw-r--r--Src/Zle/compctl.c31
-rw-r--r--Src/Zle/complete.c68
-rw-r--r--Src/Zle/complist.c110
-rw-r--r--Src/Zle/compmatch.c158
-rw-r--r--Src/Zle/compresult.c5
-rw-r--r--Src/Zle/computil.c77
-rw-r--r--Src/Zle/iwidgets.list2
-rw-r--r--Src/Zle/textobjects.c18
-rw-r--r--Src/Zle/zle.h14
-rw-r--r--Src/Zle/zle_keymap.c47
-rw-r--r--Src/Zle/zle_main.c152
-rw-r--r--Src/Zle/zle_misc.c14
-rw-r--r--Src/Zle/zle_params.c177
-rw-r--r--Src/Zle/zle_refresh.c10
-rw-r--r--Src/Zle/zle_thingy.c17
-rw-r--r--Src/Zle/zle_tricky.c80
-rw-r--r--Src/Zle/zle_utils.c20
-rw-r--r--Src/Zle/zle_vi.c195
-rw-r--r--Src/Zle/zle_word.c92
-rw-r--r--Src/builtin.c217
-rw-r--r--Src/compat.c54
-rw-r--r--Src/cond.c16
-rw-r--r--Src/exec.c502
-rw-r--r--Src/glob.c45
-rw-r--r--Src/hashtable.c6
-rw-r--r--Src/hist.c22
-rw-r--r--Src/init.c42
-rw-r--r--Src/input.c2
-rw-r--r--Src/jobs.c86
-rw-r--r--Src/lex.c48
-rw-r--r--Src/loop.c27
-rw-r--r--Src/mem.c34
-rw-r--r--Src/module.c3
-rw-r--r--Src/options.c7
-rw-r--r--Src/params.c270
-rw-r--r--Src/parse.c43
-rw-r--r--Src/pattern.c82
-rw-r--r--Src/prompt.c24
-rw-r--r--Src/signals.c77
-rw-r--r--Src/signals.h31
-rw-r--r--Src/string.c30
-rw-r--r--Src/subst.c122
-rw-r--r--Src/text.c9
-rw-r--r--Src/utils.c298
-rw-r--r--Src/wcwidth9.h1321
-rw-r--r--Src/zsh.h80
-rw-r--r--Src/zsh.mdd2
-rw-r--r--Src/zsh_system.h8
75 files changed, 4069 insertions, 1008 deletions
diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c
index 85ec1811c..29f97b41d 100644
--- a/Src/Builtins/rlimits.c
+++ b/Src/Builtins/rlimits.c
@@ -610,14 +610,16 @@ bin_limit(char *nam, char **argv, Options ops, UNUSED(int func))
return 1;
}
} else {
- /* memory-type resource -- `k' and `M' modifiers are permitted,
- meaning (respectively) 2^10 and 2^20. */
+ /* memory-type resource -- `k', `M' and `G' modifiers are *
+ * permitted, meaning (respectively) 2^10, 2^20 and 2^30. */
val = zstrtorlimt(s, &s, 10);
if (!*s || ((*s == 'k' || *s == 'K') && !s[1])) {
if (val != RLIM_INFINITY)
val *= 1024L;
} else if ((*s == 'M' || *s == 'm') && !s[1])
val *= 1024L * 1024;
+ else if ((*s == 'G' || *s == 'g') && !s[1])
+ val *= 1024L * 1024 * 1024;
else {
zwarnnam(nam, "unknown scaling factor: %s", s);
return 1;
@@ -1037,7 +1039,7 @@ enables_(Module m, int **enables)
/**/
int
-boot_(Module m)
+boot_(UNUSED(Module m))
{
return 0;
}
diff --git a/Src/Builtins/sched.c b/Src/Builtins/sched.c
index 5d5dac6b7..835e72cb7 100644
--- a/Src/Builtins/sched.c
+++ b/Src/Builtins/sched.c
@@ -58,7 +58,7 @@ static int schedcmdtimed;
/**/
static void
-schedaddtimed(time_t t)
+schedaddtimed(void)
{
/*
* The following code shouldn't be necessary and indicates
@@ -140,7 +140,7 @@ checksched(void)
*/
DPUTS(timedfns && firstnode(timedfns),
"BUG: already timed fn (1)");
- schedaddtimed(schedcmds->time);
+ schedaddtimed();
}
}
}
@@ -180,7 +180,7 @@ bin_sched(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
schedcmds = sch->next;
if (schedcmds) {
DPUTS(timedfns && firstnode(timedfns), "BUG: already timed fn (2)");
- schedaddtimed(schedcmds->time);
+ schedaddtimed();
}
}
zsfree(sch->cmd);
@@ -317,7 +317,7 @@ bin_sched(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
sch->next = schedcmds;
schedcmds = sch;
DPUTS(timedfns && firstnode(timedfns), "BUG: already timed fn (3)");
- schedaddtimed(t);
+ schedaddtimed();
} else {
for (sch2 = schedcmds;
sch2->next && sch2->next->time < sch->time;
@@ -330,7 +330,7 @@ bin_sched(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
sch->next = NULL;
schedcmds = sch;
DPUTS(timedfns && firstnode(timedfns), "BUG: already timed fn (4)");
- schedaddtimed(t);
+ schedaddtimed();
}
return 0;
}
@@ -353,7 +353,11 @@ schedgetfn(UNUSED(Param pm))
time_t t;
t = sch->time;
- sprintf(tbuf, "%ld", t);
+#if defined(PRINTF_HAS_LLD)
+ sprintf(tbuf, "%lld", (long long)t);
+#else
+ sprintf(tbuf, "%ld", (long)t);
+#endif
if (sch->flags & SCHEDFLAG_TRASH_ZLE)
flagstr = "-o";
else
@@ -411,7 +415,7 @@ enables_(Module m, int **enables)
/**/
int
-boot_(Module m)
+boot_(UNUSED(Module m))
{
addprepromptfn(&checksched);
return 0;
diff --git a/Src/Modules/curses.c b/Src/Modules/curses.c
index 64329f643..63c6748f5 100644
--- a/Src/Modules/curses.c
+++ b/Src/Modules/curses.c
@@ -202,7 +202,7 @@ static const struct zcurses_mouse_event zcurses_mouse_map[] = {
{ 0, 0, 0 }
};
-mmask_t zcurses_mouse_mask = ALL_MOUSE_EVENTS;
+static mmask_t zcurses_mouse_mask = ALL_MOUSE_EVENTS;
#endif
@@ -299,7 +299,7 @@ zcurses_free_window(ZCWin w)
}
static struct zcurses_namenumberpair *
-zcurses_attrget(WINDOW *w, char *attr)
+zcurses_attrget(UNUSED(WINDOW *w), char *attr)
{
struct zcurses_namenumberpair *zca;
@@ -324,7 +324,7 @@ zcurses_color(const char *color)
return (short)zc->number;
}
- return (short)-1;
+ return (short)-2;
}
static Colorpairnode
@@ -350,13 +350,25 @@ zcurses_colorget(const char *nam, char *colorpair)
}
*bg = '\0';
- f = zcurses_color(cp);
- b = zcurses_color(bg+1);
- if (f==-1 || b==-1) {
- if (f == -1)
+ // cp/bg can be {number}/{number} or {name}/{name}
+
+ if( cp[0] >= '0' && cp[0] <= '9' ) {
+ f = atoi(cp);
+ } else {
+ f = zcurses_color(cp);
+ }
+
+ if( (bg+1)[0] >= '0' && (bg+1)[0] <= '9' ) {
+ b = atoi(bg+1);
+ } else {
+ b = zcurses_color(bg+1);
+ }
+
+ if (f==-2 || b==-2) {
+ if (f == -2)
zwarnnam(nam, "foreground color `%s' not known", cp);
- if (b == -1)
+ if (b == -2)
zwarnnam(nam, "background color `%s' not known", bg+1);
*bg = '/';
zsfree(cp);
@@ -419,7 +431,7 @@ freecolorpairnode(HashNode hn)
*************/
static int
-zccmd_init(const char *nam, char **args)
+zccmd_init(UNUSED(const char *nam), UNUSED(char **args))
{
LinkNode stdscr_win = zcurses_getwindowbyname("stdscr");
@@ -808,7 +820,7 @@ zccmd_border(const char *nam, char **args)
static int
-zccmd_endwin(const char *nam, char **args)
+zccmd_endwin(UNUSED(const char *nam), UNUSED(char **args))
{
LinkNode stdscr_win = zcurses_getwindowbyname("stdscr");
@@ -1496,7 +1508,7 @@ zccmd_touch(const char *nam, char **args)
/**/
static int
-bin_zcurses(char *nam, char **args, Options ops, UNUSED(int func))
+bin_zcurses(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
{
char **saargs;
struct zcurses_subcommand *zcsc;
@@ -1693,7 +1705,7 @@ enables_(Module m, int **enables)
/**/
int
-boot_(Module m)
+boot_(UNUSED(Module m))
{
zcurses_windows = znewlinklist();
diff --git a/Src/Modules/datetime.c b/Src/Modules/datetime.c
index 86c61cf1c..bb82c542f 100644
--- a/Src/Modules/datetime.c
+++ b/Src/Modules/datetime.c
@@ -292,7 +292,7 @@ enables_(Module m, int **enables)
/**/
int
-boot_(Module m)
+boot_(UNUSED(Module m))
{
return 0;
}
diff --git a/Src/Modules/db_gdbm.c b/Src/Modules/db_gdbm.c
index 032963262..8dd60fc0d 100644
--- a/Src/Modules/db_gdbm.c
+++ b/Src/Modules/db_gdbm.c
@@ -390,7 +390,7 @@ boot_(UNUSED(Module m))
/**/
int
-cleanup_(UNUSED(Module m))
+cleanup_(Module m)
{
return setfeatureenables(m, &module_features, NULL);
}
diff --git a/Src/Modules/files.c b/Src/Modules/files.c
index dbcff6307..6f816bac0 100644
--- a/Src/Modules/files.c
+++ b/Src/Modules/files.c
@@ -805,7 +805,7 @@ enables_(Module m, int **enables)
/**/
int
-boot_(Module m)
+boot_(UNUSED(Module m))
{
return 0;
}
diff --git a/Src/Modules/langinfo.c b/Src/Modules/langinfo.c
index eb45d161b..520b0370c 100644
--- a/Src/Modules/langinfo.c
+++ b/Src/Modules/langinfo.c
@@ -498,7 +498,7 @@ boot_(UNUSED(Module m))
/**/
int
-cleanup_(UNUSED(Module m))
+cleanup_(Module m)
{
return setfeatureenables(m, &module_features, NULL);
}
diff --git a/Src/Modules/mapfile.c b/Src/Modules/mapfile.c
index 422c7077c..2503b361e 100644
--- a/Src/Modules/mapfile.c
+++ b/Src/Modules/mapfile.c
@@ -306,7 +306,7 @@ boot_(UNUSED(Module m))
/**/
int
-cleanup_(UNUSED(Module m))
+cleanup_(Module m)
{
return setfeatureenables(m, &module_features, NULL);
}
diff --git a/Src/Modules/mathfunc.c b/Src/Modules/mathfunc.c
index efadd86ff..a7e8b294c 100644
--- a/Src/Modules/mathfunc.c
+++ b/Src/Modules/mathfunc.c
@@ -411,7 +411,11 @@ math_func(char *name, int argc, mnumber *argv, int id)
break;
case MF_SCALB:
+#ifdef HAVE_SCALBN
+ retd = scalbn(argd, argi);
+#else
retd = scalb(argd, argi);
+#endif
break;
#ifdef HAVE_SIGNGAM
@@ -599,7 +603,7 @@ enables_(Module m, int **enables)
/**/
int
-boot_(Module m)
+boot_(UNUSED(Module m))
{
return 0;
}
diff --git a/Src/Modules/newuser.c b/Src/Modules/newuser.c
index efdb2abba..6f0d2e0aa 100644
--- a/Src/Modules/newuser.c
+++ b/Src/Modules/newuser.c
@@ -41,14 +41,14 @@ setup_(UNUSED(Module m))
/**/
int
-features_(Module m, char ***features)
+features_(UNUSED(Module m), UNUSED(char ***features))
{
return 1;
}
/**/
int
-enables_(Module m, int **enables)
+enables_(UNUSED(Module m), UNUSED(int **enables))
{
return 0;
}
diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c
index e13813c3d..86416c5c5 100644
--- a/Src/Modules/param_private.c
+++ b/Src/Modules/param_private.c
@@ -42,19 +42,19 @@ struct gsu_closure {
void *g;
};
-const struct gsu_scalar scalar_private_gsu =
+static const struct gsu_scalar scalar_private_gsu =
{ pps_getfn, pps_setfn, pps_unsetfn };
-const struct gsu_integer integer_private_gsu =
+static const struct gsu_integer integer_private_gsu =
{ ppi_getfn, ppi_setfn, ppi_unsetfn };
-const struct gsu_float float_private_gsu =
+static const struct gsu_float float_private_gsu =
{ ppf_getfn, ppf_setfn, ppf_unsetfn };
-const struct gsu_array array_private_gsu =
+static const struct gsu_array array_private_gsu =
{ ppa_getfn, ppa_setfn, ppa_unsetfn };
-const struct gsu_hash hash_private_gsu =
+static const struct gsu_hash hash_private_gsu =
{ pph_getfn, pph_setfn, pph_unsetfn };
/*
@@ -490,7 +490,7 @@ wrap_private(Eprog prog, FuncWrap w, char *name)
return 1;
}
-static HashNode (*getparamnode) _((HashTable, const char *));
+static GetNodeFunc getparamnode;
/**/
static HashNode
@@ -567,6 +567,8 @@ static struct features module_features = {
};
static struct builtin save_local;
+static GetNodeFunc save_getnode2;
+static ScanFunc save_printnode;
static struct reswd reswd_private = {{NULL, "private", 0}, TYPESET};
/**/
@@ -577,6 +579,8 @@ setup_(UNUSED(Module m))
/* Horrible, horrible hack */
getparamnode = realparamtab->getnode;
+ save_getnode2 = realparamtab->getnode2;
+ save_printnode = realparamtab->printnode;
realparamtab->getnode = getprivatenode;
realparamtab->getnode2 = getprivatenode2;
realparamtab->printnode = printprivatenode;
@@ -624,8 +628,8 @@ cleanup_(Module m)
removehashnode(reswdtab, "private");
realparamtab->getnode = getparamnode;
- realparamtab->getnode2 = gethashnode2;
- realparamtab->printnode = printparamnode;
+ realparamtab->getnode2 = save_getnode2;
+ realparamtab->printnode = save_printnode;
deletewrapper(m, wrapper);
return setfeatureenables(m, &module_features, NULL);
diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c
index 04d448529..98bcaba6e 100644
--- a/Src/Modules/parameter.c
+++ b/Src/Modules/parameter.c
@@ -487,11 +487,6 @@ scanfunctions(UNUSED(HashTable ht), ScanFunc func, int flags, int dis)
strcat(pm.u.str, " \"$@\"");
} else
pm.u.str = dyncat(start, t);
- /*
- * TBD: Is this unmetafy correct? Surely as this
- * is a parameter value it stays metafied?
- */
- unmetafy(pm.u.str, NULL);
zsfree(t);
if (shf->redir) {
@@ -2190,7 +2185,7 @@ enables_(Module m, int **enables)
/**/
int
-boot_(Module m)
+boot_(UNUSED(Module m))
{
return 0;
}
diff --git a/Src/Modules/pcre.c b/Src/Modules/pcre.c
index aa5c8ed5b..5fd67963d 100644
--- a/Src/Modules/pcre.c
+++ b/Src/Modules/pcre.c
@@ -228,7 +228,7 @@ zpcre_get_substrings(char *arg, int *ovec, int ret, char *matchvar,
/* Find the start offset */
MB_CHARINIT();
leftlen = ipair[0];
- while (leftlen) {
+ while (leftlen > 0) {
offs++;
clen = MB_CHARLEN(ptr, leftlen);
ptr += clen;
@@ -479,7 +479,7 @@ enables_(Module m, int **enables)
/**/
int
-boot_(Module m)
+boot_(UNUSED(Module m))
{
return 0;
}
diff --git a/Src/Modules/regex.c b/Src/Modules/regex.c
index 16cc77f30..edb7234d4 100644
--- a/Src/Modules/regex.c
+++ b/Src/Modules/regex.c
@@ -248,7 +248,7 @@ enables_(Module m, int **enables)
/**/
int
-boot_(Module m)
+boot_(UNUSED(Module m))
{
return 0;
}
diff --git a/Src/Modules/socket.c b/Src/Modules/socket.c
index 7c3fb5ebe..c65b7dfce 100644
--- a/Src/Modules/socket.c
+++ b/Src/Modules/socket.c
@@ -310,7 +310,7 @@ enables_(Module m, int **enables)
/**/
int
-boot_(Module m)
+boot_(UNUSED(Module m))
{
return 0;
}
diff --git a/Src/Modules/stat.c b/Src/Modules/stat.c
index 396177149..66baa1292 100644
--- a/Src/Modules/stat.c
+++ b/Src/Modules/stat.c
@@ -659,7 +659,7 @@ enables_(Module m, int **enables)
/**/
int
-boot_(Module m)
+boot_(UNUSED(Module m))
{
return 0;
}
diff --git a/Src/Modules/system.c b/Src/Modules/system.c
index 1ab1fb17e..afaec262a 100644
--- a/Src/Modules/system.c
+++ b/Src/Modules/system.c
@@ -439,7 +439,7 @@ bin_sysseek(char *nam, char **args, Options ops, UNUSED(int func))
/**/
static mnumber
-math_systell(UNUSED(char *name), int argc, mnumber *argv, UNUSED(int id))
+math_systell(UNUSED(char *name), UNUSED(int argc), mnumber *argv, UNUSED(int id))
{
int fd = (argv->type == MN_INTEGER) ? argv->u.l : (int)argv->u.d;
mnumber ret;
@@ -521,7 +521,7 @@ static int
bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
{
int cloexec = 1, unlock = 0, readlock = 0;
- time_t timeout = 0;
+ zlong timeout = -1;
char *fdvar = NULL;
#ifdef HAVE_FCNTL_H
struct flock lck;
@@ -573,7 +573,7 @@ bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
} else {
optarg = *args++;
}
- timeout = (time_t)mathevali(optarg);
+ timeout = mathevali(optarg);
break;
case 'u':
@@ -650,7 +650,7 @@ bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
sleep(1);
}
} else {
- while (fcntl(flock_fd, F_SETLKW, &lck) < 0) {
+ while (fcntl(flock_fd, timeout == 0 ? F_SETLK : F_SETLKW, &lck) < 0) {
if (errflag)
return 1;
if (errno == EINTR)
@@ -834,7 +834,7 @@ enables_(Module m, int **enables)
/**/
int
-boot_(Module m)
+boot_(UNUSED(Module m))
{
return 0;
}
diff --git a/Src/Modules/system.mdd b/Src/Modules/system.mdd
index eed0c1b9d..00a3e7896 100644
--- a/Src/Modules/system.mdd
+++ b/Src/Modules/system.mdd
@@ -15,7 +15,12 @@ errnames.c: errnames1.awk errnames2.awk $(dir_top)/config.h @ERRNO_H@
touch errtmp.out; \
else \
$(AWK) -f $(sdir)/errnames1.awk @ERRNO_H@ >errtmp.c; \
- $(CPP) errtmp.c >errtmp.out; \
+ case "`$(CPP) --version </dev/null 2>&1`" in \
+ *"Free Software Foundation"*) \
+ $(CPP) -P errtmp.c >errtmp.out;; \
+ *) \
+ $(CPP) errtmp.c >errtmp.out;; \
+ esac; \
fi
$(AWK) -f $(sdir)/errnames2.awk errtmp.out > $@
rm -f errtmp.c errtmp.out
diff --git a/Src/Modules/tcp.c b/Src/Modules/tcp.c
index 9fc1b29a2..dec12142b 100644
--- a/Src/Modules/tcp.c
+++ b/Src/Modules/tcp.c
@@ -208,7 +208,7 @@ freehostent(UNUSED(struct hostent *ptr))
/**/
#endif /* !HAVE_GETIPNODEBYNAME */
-LinkList ztcp_sessions;
+static LinkList ztcp_sessions;
/* "allocate" a tcp_session */
static Tcp_session
@@ -732,7 +732,7 @@ enables_(Module m, int **enables)
/**/
int
-boot_(Module m)
+boot_(UNUSED(Module m))
{
ztcp_sessions = znewlinklist();
return 0;
diff --git a/Src/Modules/termcap.c b/Src/Modules/termcap.c
index cd0e85885..60a6e138a 100644
--- a/Src/Modules/termcap.c
+++ b/Src/Modules/termcap.c
@@ -295,7 +295,7 @@ scantermcap(UNUSED(HashTable ht), ScanFunc func, int flags)
}
}
-struct paramdef partab[] = {
+static struct paramdef partab[] = {
SPECIALPMDEF("termcap", PM_READONLY, NULL, gettermcap, scantermcap)
};
@@ -342,7 +342,7 @@ enables_(Module m, int **enables)
/**/
int
-boot_(Module m)
+boot_(UNUSED(Module m))
{
#ifdef HAVE_TGETENT
# ifdef HAVE_SETUPTERM
diff --git a/Src/Modules/terminfo.c b/Src/Modules/terminfo.c
index 88e326f63..bbd325899 100644
--- a/Src/Modules/terminfo.c
+++ b/Src/Modules/terminfo.c
@@ -99,7 +99,7 @@ bin_echoti(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
return 1;
}
/* check that the number of arguments provided is not too high */
- if (arrlen(argv) > 9) {
+ if (arrlen_gt(argv, 9)) {
zwarnnam(name, "too many arguments");
return 1;
}
@@ -335,7 +335,7 @@ enables_(Module m, int **enables)
/**/
int
-boot_(Module m)
+boot_(UNUSED(Module m))
{
#ifdef USE_TERMINFO_MODULE
# ifdef HAVE_SETUPTERM
diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c
index b4081df5f..deed35e2f 100644
--- a/Src/Modules/zftp.c
+++ b/Src/Modules/zftp.c
@@ -344,10 +344,10 @@ static int zfsesscnt;
*/
/* flags for alarm set, alarm gone off */
-int zfalarmed, zfdrrrring;
+static int zfalarmed, zfdrrrring;
/* remember old alarm status */
-time_t oaltime;
-unsigned int oalremain;
+static time_t oaltime;
+static unsigned int oalremain;
/*
* Where to jump to when the alarm goes off. This is much
@@ -357,7 +357,7 @@ unsigned int oalremain;
*
* gcc -O gives apparently spurious `may be clobbered by longjmp' warnings.
*/
-jmp_buf zfalrmbuf;
+static jmp_buf zfalrmbuf;
/* The signal handler itself */
diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c
index 63ff7578c..0ef753915 100644
--- a/Src/Modules/zpty.c
+++ b/Src/Modules/zpty.c
@@ -891,7 +891,7 @@ enables_(Module m, int **enables)
/**/
int
-boot_(Module m)
+boot_(UNUSED(Module m))
{
ptycmds = NULL;
diff --git a/Src/Modules/zselect.c b/Src/Modules/zselect.c
index 30a3f51a5..8c1267240 100644
--- a/Src/Modules/zselect.c
+++ b/Src/Modules/zselect.c
@@ -307,7 +307,7 @@ enables_(Module m, int **enables)
/**/
int
-boot_(Module m)
+boot_(UNUSED(Module m))
{
return 0;
}
diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c
index 376cd8402..d95c0c254 100644
--- a/Src/Modules/zutil.c
+++ b/Src/Modules/zutil.c
@@ -472,7 +472,7 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
Patprog prog;
char *pat;
- if (arrlen(args) < 2) {
+ if (arrlen_lt(args, 2)) {
zwarnnam(nam, "not enough arguments");
return 1;
}
@@ -491,7 +491,7 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
Style s;
char *context, *stylename;
- switch (arrlen(args)) {
+ switch (arrlen_ge(args, 3) ? 3 : arrlen(args)) {
case 2:
context = args[0];
stylename = args[1];
@@ -1745,13 +1745,15 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
for (p = o; *p; p++) {
if (*p == '\\' && p[1])
p++;
- else if (*p == '+') {
- f |= ZOF_MULT;
- *p = '\0';
- p++;
- break;
- } else if (*p == ':' || *p == '=')
- break;
+ else if (p > o) { /* At least one character of option name */
+ if (*p == '+') {
+ f |= ZOF_MULT;
+ *p = '\0';
+ p++;
+ break;
+ } else if (*p == ':' || *p == '=')
+ break;
+ }
}
if (*p == ':') {
f |= ZOF_ARG;
@@ -1789,6 +1791,7 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
p++;
*n++ = *p;
}
+ *n = '\0';
if (get_opt_desc(o)) {
zwarnnam(nam, "option defined more than once: %s", o);
return 1;
@@ -1833,7 +1836,8 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
if (o[1]) {
add_opt_val(d, o + 1);
break;
- } else if (!(d->flags & ZOF_OPT)) {
+ } else if (!(d->flags & ZOF_OPT) ||
+ (pp[1] && pp[1][0] != '-')) {
if (!pp[1]) {
zwarnnam(nam, "missing argument for option: %s",
d->name);
@@ -1859,7 +1863,8 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
if (*e)
add_opt_val(d, e);
- else if (!(d->flags & ZOF_OPT)) {
+ else if (!(d->flags & ZOF_OPT) ||
+ (pp[1] && pp[1][0] != '-')) {
if (!pp[1]) {
zwarnnam(nam, "missing argument for option: %s",
d->name);
@@ -1990,7 +1995,7 @@ enables_(Module m, int **enables)
/**/
int
-boot_(Module m)
+boot_(UNUSED(Module m))
{
return 0;
}
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index 023c41814..3711fde29 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -125,7 +125,7 @@ struct cmatch {
#define CMF_REMOVE (1<< 1) /* remove the suffix */
#define CMF_ISPAR (1<< 2) /* is paramter expansion */
#define CMF_PARBR (1<< 3) /* paramter expansion with a brace */
-#define CMF_PARNEST (1<< 4) /* nested paramter expansion */
+#define CMF_PARNEST (1<< 4) /* nested parameter expansion */
#define CMF_NOLIST (1<< 5) /* should not be listed */
#define CMF_DISPLINE (1<< 6) /* display strings one per line */
#define CMF_HIDE (1<< 7) /* temporarily hide this one */
@@ -160,9 +160,15 @@ struct cmatcher {
int ralen; /* length of right anchor */
};
+/* Flags for cmatcher::flags */
+/* Upon match, insert the string from the line rather than the string
+ * from the trial completion ("word"). */
#define CMF_LINE 1
+/* Match with an anchor on the left. */
#define CMF_LEFT 2
+/* Match with an anchor on the right. */
#define CMF_RIGHT 4
+/* ... */
#define CMF_INTER 8
/*
@@ -229,7 +235,6 @@ struct cpattern {
* the anchor. */
typedef struct cline *Cline;
-typedef struct clsub Clsub;
struct cline {
Cline next;
@@ -285,14 +290,14 @@ struct menuinfo {
/* Flags for compadd and addmatches(). */
-#define CAF_QUOTE 1
-#define CAF_NOSORT 2
-#define CAF_MATCH 4
-#define CAF_UNIQCON 8
-#define CAF_UNIQALL 16
-#define CAF_ARRAYS 32
-#define CAF_KEYS 64
-#define CAF_ALL 128
+#define CAF_QUOTE 1 /* compadd -Q: positional arguments already quoted */
+#define CAF_NOSORT 2 /* compadd -V: don't sort */
+#define CAF_MATCH 4 /* compadd without -U: do matching */
+#define CAF_UNIQCON 8 /* compadd -2: don't deduplicate */
+#define CAF_UNIQALL 16 /* compadd -1: deduplicate */
+#define CAF_ARRAYS 32 /* compadd -a or -k: array/assoc parameter names */
+#define CAF_KEYS 64 /* compadd -k: assoc parameter names */
+#define CAF_ALL 128 /* compadd -C: _all_matches */
/* Data for compadd and addmatches() */
@@ -367,7 +372,7 @@ typedef void (*CLPrintFunc)(Cmgroup, Cmatch *, int, int, int, int);
#define CP_QISUFFIX (1 << CPN_QISUFFIX)
#define CPN_COMPSTATE 9
#define CP_COMPSTATE (1 << CPN_COMPSTATE)
-
+/* See comprpms */
#define CP_REALPARAMS 10
#define CP_ALLREALS ((unsigned int) 0x3ff)
@@ -424,7 +429,7 @@ typedef void (*CLPrintFunc)(Cmgroup, Cmatch *, int, int, int, int);
#define CP_QUOTES (1 << CPN_QUOTES)
#define CPN_IGNORED 25
#define CP_IGNORED (1 << CPN_IGNORED)
-
+/* See compkpms */
#define CP_KEYPARAMS 26
#define CP_ALLKEYS ((unsigned int) 0x3ffffff)
diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c
index ae3a64074..d1cf7a08a 100644
--- a/Src/Zle/compcore.c
+++ b/Src/Zle/compcore.c
@@ -30,10 +30,6 @@
#include "complete.mdh"
#include "compcore.pro"
-/* The last completion widget called. */
-
-static Widget lastcompwidget;
-
/* Flags saying what we have to do with the result. */
/**/
@@ -429,6 +425,7 @@ do_completion(UNUSED(Hookdef dummy), Compldat dat)
}
} else {
invalidatelist();
+ lastambig = isset(BASHAUTOLIST);
if (forcelist)
clearlist = 1;
zlemetacs = 0;
@@ -471,8 +468,7 @@ before_complete(UNUSED(Hookdef dummy), int *lst)
/* If we are doing a menu-completion... */
- if (minfo.cur && menucmp && *lst != COMP_LIST_EXPAND &&
- (menucmp != 1 || !compwidget || compwidget == lastcompwidget)) {
+ if (minfo.cur && menucmp && *lst != COMP_LIST_EXPAND) {
do_menucmp(*lst);
return 1;
}
@@ -481,7 +477,6 @@ before_complete(UNUSED(Hookdef dummy), int *lst)
onlyexpl = listdat.valid = 0;
return 1;
}
- lastcompwidget = compwidget;
/* We may have to reset the cursor to its position after the *
* string inserted by the last completion. */
@@ -841,6 +836,7 @@ callcompfunc(char *s, char *fn)
endparamscope();
lastcmd = 0;
incompfunc = icf;
+ startauto = 0;
if (!complist)
uselist = 0;
@@ -888,8 +884,13 @@ callcompfunc(char *s, char *fn)
useline = 1, usemenu = 1;
else if (strpfx("auto", compinsert))
useline = 1, usemenu = 2;
- else
+ else {
useline = usemenu = 0;
+ /* if compstate[insert] was emptied, no unambiguous prefix
+ * ever gets inserted so allow the next tab to already start
+ * menu completion */
+ startauto = lastambig = isset(AUTOMENU);
+ }
if (useline && (p = strchr(compinsert, ':'))) {
insmnum = atoi(++p);
@@ -902,7 +903,7 @@ callcompfunc(char *s, char *fn)
#endif
}
}
- startauto = ((compinsert &&
+ startauto = startauto || ((compinsert &&
!strcmp(compinsert, "automenu-unambiguous")) ||
(bashlistfirst && isset(AUTOMENU) &&
(!compinsert || !*compinsert)));
@@ -1049,6 +1050,13 @@ makecomplist(char *s, int incmd, int lst)
}
}
+/*
+ * Quote 's' according to compqstack, aka $compstate[all_quotes].
+ *
+ * If 'ign' is 1, skip the innermost quoting level. Otherwise 'ign'
+ * must be 0.
+ */
+
/**/
mod_export char *
multiquote(char *s, int ign)
@@ -1056,12 +1064,11 @@ multiquote(char *s, int ign)
if (s) {
char *os = s, *p = compqstack;
- if (p && *p && (ign < 1 || p[ign])) {
- if (ign > 0)
- p += ign;
+ if (p && *p && (ign == 0 || p[1])) {
+ if (ign)
+ p++;
while (*p) {
- if (ign >= 0 || p[1])
- s = quotestring(s, NULL, *p);
+ s = quotestring(s, *p);
p++;
}
}
@@ -1071,6 +1078,12 @@ multiquote(char *s, int ign)
return NULL;
}
+/*
+ * tildequote(s, ign): Equivalent to multiquote(s, ign), except that if
+ * compqstack[0] == QT_BACKSLASH and s[0] == '~', then that tilde is not
+ * quoted.
+ */
+
/**/
mod_export char *
tildequote(char *s, int ign)
@@ -1970,6 +1983,11 @@ get_user_var(char *nam)
}
}
+/*
+ * If KEYS, then NAME is an associative array; return its keys.
+ * Else, NAME is a plain array; return its elements.
+ */
+
static char **
get_data_arr(char *name, int keys)
{
@@ -2031,16 +2049,17 @@ addmatch(char *str, int flags, char ***dispp, int line)
int
addmatches(Cadata dat, char **argv)
{
+ /* ms: "match string" - string to use as completion.
+ * Overloaded at one place as a temporary. */
char *s, *ms, *lipre = NULL, *lisuf = NULL, *lpre = NULL, *lsuf = NULL;
char **aign = NULL, **dparr = NULL, *oaq = autoq, *oppre = dat->ppre;
char *oqp = qipre, *oqs = qisuf, qc, **disp = NULL, *ibuf = NULL;
char **arrays = NULL;
- int lpl, lsl, pl, sl, bcp = 0, bcs = 0, bpadd = 0, bsadd = 0;
+ int lpl, lsl, bcp = 0, bcs = 0, bpadd = 0, bsadd = 0;
int ppl = 0, psl = 0, ilen = 0;
int llpl = 0, llsl = 0, nm = mnum, gflags = 0, ohp = haspattern;
int isexact, doadd, ois = instring, oib = inbackt;
Cline lc = NULL, pline = NULL, sline = NULL;
- Cmatch cm;
struct cmlist mst;
Cmlist oms = mstack;
Patprog cp = NULL, *pign = NULL;
@@ -2199,9 +2218,14 @@ addmatches(Cadata dat, char **argv)
/* Test if there is an existing -P prefix. */
if (dat->pre && *dat->pre) {
- pl = pfxlen(dat->pre, lpre);
- llpl -= pl;
- lpre += pl;
+ int prefix_length = pfxlen(dat->pre, lpre);
+ if (dat->pre[prefix_length] == '\0' ||
+ lpre[prefix_length] == '\0') {
+ /* $compadd_args[-P] is a prefix of ${PREFIX}, or
+ * vice-versa. */
+ llpl -= prefix_length;
+ lpre += prefix_length;
+ }
}
}
/* Now duplicate the strings we have from the command line. */
@@ -2413,6 +2437,7 @@ addmatches(Cadata dat, char **argv)
if (dat->psuf)
psl = strlen(dat->psuf);
for (; (s = *argv); argv++) {
+ int sl;
bpl = obpl;
bsl = obsl;
if (disp) {
@@ -2473,6 +2498,7 @@ addmatches(Cadata dat, char **argv)
goto next_array;
}
if (doadd) {
+ Cmatch cm;
Brinfo bp;
for (bp = obpl; bp; bp = bp->next)
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index 8381867d0..52c6f1233 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -1400,7 +1400,7 @@ printcompctl(char *s, Compctl cc, int printflags, int ispat)
untokenize(p);
quotedzputs(p, stdout);
} else
- quotedzputs(quotestring(s, NULL, QT_BACKSLASH), stdout);
+ quotedzputs(quotestring(s, QT_BACKSLASH), stdout);
}
/* loop through flags w/o args that are set, printing them if so */
@@ -1536,7 +1536,7 @@ printcompctl(char *s, Compctl cc, int printflags, int ispat)
char *p = dupstring(s);
untokenize(p);
- quotedzputs(quotestring(p, NULL, QT_BACKSLASH), stdout);
+ quotedzputs(quotestring(p, QT_BACKSLASH), stdout);
}
}
putchar('\n');
@@ -1740,8 +1740,8 @@ static int addwhat;
* This uses the instring variable exported from zle_tricky.c.
*/
-#define quotename(s, e) \
-quotestring(s, e, instring == QT_NONE ? QT_BACKSLASH : instring)
+#define quotename(s) \
+quotestring(s, instring == QT_NONE ? QT_BACKSLASH : instring)
/* Hook functions */
@@ -1965,7 +1965,7 @@ addmatch(char *s, char *t)
if (!ms)
return;
- if (addwhat == -7 && !findcmd(s, 0))
+ if (addwhat == -7 && !findcmd(s, 0, 0))
return;
isfile = CMF_FILE;
} else if (addwhat == CC_QUOTEFLAG || addwhat == -2 ||
@@ -2135,7 +2135,7 @@ gen_matches_files(int dirs, int execs, int all)
{
DIR *d;
struct stat buf;
- char *n, p[PATH_MAX], *q = NULL, *e, *pathpref;
+ char *n, p[PATH_MAX+1], *q = NULL, *e, *pathpref;
LinkList l = NULL;
int ns = 0, ng = opts[NULLGLOB], test, aw = addwhat, pathpreflen;
@@ -2469,7 +2469,7 @@ makecomplistcmd(char *os, int incmd, int flags)
/* If the command string starts with `=', try the path name of the *
* command. */
if (cmdstr && cmdstr[0] == Equals) {
- char *c = findcmd(cmdstr + 1, 1);
+ char *c = findcmd(cmdstr + 1, 1, 0);
if (c) {
zsfree(cmdstr);
@@ -2509,7 +2509,8 @@ makecomplistpc(char *os, int incmd)
int ret = 0;
s = ((shfunctab->getnode(shfunctab, cmdstr) ||
- builtintab->getnode(builtintab, cmdstr)) ? NULL : findcmd(cmdstr, 1));
+ builtintab->getnode(builtintab, cmdstr)) ? NULL :
+ findcmd(cmdstr, 1, 0));
for (pc = patcomps; pc; pc = pc->next) {
if ((pat = patcompile(pc->pat, PAT_STATIC, NULL)) &&
@@ -3153,10 +3154,10 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
lpre = zhalloc(lpl + 1);
memcpy(lpre, s, lpl);
lpre[lpl] = '\0';
- qlpre = quotename(lpre, NULL);
+ qlpre = quotename(lpre);
lsuf = dupstring(s + offs);
lsl = strlen(lsuf);
- qlsuf = quotename(lsuf, NULL);
+ qlsuf = quotename(lsuf);
/* First check for ~.../... */
if (ic == Tilde) {
@@ -3175,11 +3176,11 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
rpre = (*p || *lpre == Tilde || *lpre == Equals) ?
(noreal = 0, getreal(tt)) :
dupstring(tt);
- qrpre = quotename(rpre, NULL);
+ qrpre = quotename(rpre);
for (p = lsuf; *p && *p != String && *p != Tick; p++);
rsuf = *p ? (noreal = 0, getreal(lsuf)) : dupstring(lsuf);
- qrsuf = quotename(rsuf, NULL);
+ qrsuf = quotename(rsuf);
/* Check if word is a pattern. */
@@ -3315,10 +3316,10 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
/* And get the file prefix. */
fpre = dupstring(((s1 == s || s1 == rpre || ic) &&
(*s != '/' || zlemetacs == wb)) ? s1 : s1 + 1);
- qfpre = quotename(fpre, NULL);
+ qfpre = quotename(fpre);
/* And the suffix. */
fsuf = dupstrpfx(rsuf, s2 - rsuf);
- qfsuf = quotename(fsuf, NULL);
+ qfsuf = quotename(fsuf);
if (comppatmatch && *comppatmatch && (ispattern & 2)) {
int t2;
@@ -3992,7 +3993,7 @@ enables_(Module m, int **enables)
/**/
int
-boot_(Module m)
+boot_(UNUSED(Module m))
{
addhookfunc("compctl_make", (Hookfn) ccmakehookfn);
addhookfunc("compctl_cleanup", (Hookfn) cccleanuphookfn);
diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c
index ee4e5b0a5..7980518b7 100644
--- a/Src/Zle/complete.c
+++ b/Src/Zle/complete.c
@@ -52,7 +52,7 @@ char **compwords,
*compqiprefix,
*compqisuffix,
*compquote,
- *compqstack,
+ *compqstack, /* compstate[all_quotes] */
*comppatmatch,
*complastprompt;
/**/
@@ -72,8 +72,26 @@ char *compiprefix,
*compoldins,
*compvared;
+/*
+ * An array of Param structures for compsys special parameters;
+ * see 'comprparams' below. An entry for $compstate is added
+ * by makecompparams().
+ *
+ * See CP_REALPARAMS.
+ */
+
/**/
-Param *comprpms, *compkpms;
+Param *comprpms;
+
+/*
+ * An array of Param structures for elemens of $compstate; see
+ * 'compkparams' below.
+ *
+ * See CP_KEYPARAMS.
+ */
+
+/**/
+Param *compkpms;
/**/
mod_export void
@@ -231,16 +249,17 @@ parse_cmatcher(char *name, char *s)
if (!*s) break;
switch (*s) {
- case 'b': fl2 = CMF_INTER;
+ case 'b': fl2 = CMF_INTER; /* FALLTHROUGH */
case 'l': fl = CMF_LEFT; break;
- case 'e': fl2 = CMF_INTER;
+ case 'e': fl2 = CMF_INTER; /* FALLTHROUGH */
case 'r': fl = CMF_RIGHT; break;
case 'm': fl = 0; break;
- case 'B': fl2 = CMF_INTER;
+ case 'B': fl2 = CMF_INTER; /* FALLTHROUGH */
case 'L': fl = CMF_LEFT | CMF_LINE; break;
- case 'E': fl2 = CMF_INTER;
+ case 'E': fl2 = CMF_INTER; /* FALLTHROUGH */
case 'R': fl = CMF_RIGHT | CMF_LINE; break;
case 'M': fl = CMF_LINE; break;
+ case 'x': break;
default:
if (name)
zwarnnam(name, "unknown match specification character `%c'",
@@ -252,6 +271,15 @@ parse_cmatcher(char *name, char *s)
zwarnnam(name, "missing `:'");
return pcm_err;
}
+ if (*s == 'x') {
+ if (s[2] && !inblank(s[2])) {
+ if (name)
+ zwarnnam(name,
+ "unexpected pattern following x: specification");
+ return pcm_err;
+ }
+ return ret;
+ }
s += 2;
if (!*s) {
if (name)
@@ -527,8 +555,8 @@ static int
bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
{
struct cadata dat;
- char *p, **sp, *e, *m = NULL, *mstr = NULL;
- int dm;
+ char *mstr = NULL; /* argument of -M options, accumulated */
+ int added; /* return value */
Cmatcher match = NULL;
if (incompfunc != 1) {
@@ -544,14 +572,16 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
dat.dummies = -1;
for (; *argv && **argv == '-'; argv++) {
+ char *p; /* loop variable, points into argv */
if (!(*argv)[1]) {
argv++;
break;
}
for (p = *argv + 1; *p; p++) {
- sp = NULL;
- e = NULL;
- dm = 0;
+ char *m = NULL; /* argument of -M option (this one only) */
+ char **sp = NULL; /* the argument to an option should be copied
+ to *sp. */
+ const char *e; /* error message */
switch (*p) {
case 'q':
dat.flags |= CMF_REMOVE;
@@ -633,7 +663,6 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
case 'M':
sp = &m;
e = "matching specification expected after -%c";
- dm = 1;
break;
case 'X':
sp = &(dat.exp);
@@ -704,27 +733,29 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
}
if (sp) {
if (p[1]) {
+ /* Pasted argument: -Xfoo. */
if (!*sp)
*sp = p + 1;
p = "" - 1;
} else if (argv[1]) {
+ /* Argument in a separate word: -X foo. */
argv++;
if (!*sp)
*sp = *argv;
p = "" - 1;
} else {
+ /* Missing argument: argv[N] == "-X", argv[N+1] == NULL. */
zwarnnam(name, e, *p);
zsfree(mstr);
return 1;
}
- if (dm) {
+ if (m) {
if (mstr) {
char *tmp = tricat(mstr, " ", m);
zsfree(mstr);
mstr = tmp;
} else
mstr = ztrdup(m);
- m = NULL;
}
}
}
@@ -743,10 +774,10 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
return 1;
dat.match = match = cpcmatcher(match);
- dm = addmatches(&dat, argv);
+ added = addmatches(&dat, argv);
freecmatcher(match);
- return dm;
+ return added;
}
#define CVT_RANGENUM 0
@@ -1207,8 +1238,9 @@ makecompparams(void)
addcompparams(comprparams, comprpms);
- if (!(cpm = createparam(COMPSTATENAME,
- PM_SPECIAL|PM_REMOVABLE|PM_LOCAL|PM_HASHED)))
+ if (!(cpm = createparam(
+ COMPSTATENAME,
+ PM_SPECIAL|PM_REMOVABLE|PM_SINGLE|PM_LOCAL|PM_HASHED)))
cpm = (Param) paramtab->getnode(paramtab, COMPSTATENAME);
DPUTS(!cpm, "param not set in makecompparams");
diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c
index 0ccb88505..2edaf6eca 100644
--- a/Src/Zle/complist.c
+++ b/Src/Zle/complist.c
@@ -34,8 +34,9 @@
/* Information about the list shown. */
/*
- * noselect: 1 if complistmatches indicated we shouldn't do selection.
- * Tested in domenuselect.
+ * noselect: 1 if complistmatches indicated we shouldn't do selection;
+ * -1 if interactive mode needs to reset the selection list.
+ * Tested in domenuselect, and in complistmatches to skip redraw.
* mselect: Local copy of the index of the currently selected match.
* Initialised to the gnum entry of the current match for
* each completion.
@@ -113,7 +114,7 @@ static Cmgroup *mgtab, *mgtabp;
* Allow us to keep track of pointer arithmetic for mgtab; could
* just as well have been for mtab but wasn't.
*/
-int mgtabsize;
+static int mgtabsize;
#endif
/*
@@ -346,9 +347,10 @@ getcoldef(char *s)
char sav = p[1];
p[1] = '\0';
+ s = metafy(s, -1, META_USEHEAP);
tokenize(s);
gprog = patcompile(s, 0, NULL);
- p[1] =sav;
+ p[1] = sav;
s = p + 1;
}
@@ -414,6 +416,7 @@ getcoldef(char *s)
break;
*s++ = '\0';
}
+ p = metafy(p, -1, META_USEHEAP);
tokenize(p);
if ((prog = patcompile(p, 0, NULL))) {
Patcol pc, po;
@@ -661,7 +664,9 @@ clprintfmt(char *p, int ml)
initiscol();
- for (; *p; p++) {
+ while (*p) {
+ convchar_t chr;
+ int chrlen = MB_METACHARLENCONV(p, &chr);
doiscol(i++);
cc++;
if (*p == '\n') {
@@ -672,11 +677,16 @@ clprintfmt(char *p, int ml)
if (ml == mlend - 1 && (cc % zterm_columns) == zterm_columns - 1)
return 0;
- if (*p == Meta) {
+ while (chrlen) {
+ if (*p == Meta) {
+ p++;
+ chrlen--;
+ putc(*p ^ 32, shout);
+ } else
+ putc(*p, shout);
+ chrlen--;
p++;
- putc(*p ^ 32, shout);
- } else
- putc(*p, shout);
+ }
if ((beg = !(cc % zterm_columns)))
ml++;
if (mscroll && !(cc % zterm_columns) &&
@@ -989,6 +999,7 @@ asklistscroll(int ml)
fflush(shout);
zsetterm();
+ menuselect_bindings(); /* sanity in case deleted by user */
selectlocalmap(lskeymap);
if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak))
ret = 1;
@@ -1979,7 +1990,8 @@ complistmatches(UNUSED(Hookdef dummy), Chdata dat)
}
#endif
- noselect = 0;
+ if (noselect > 0)
+ noselect = 0;
if ((minfo.asked == 2 && mselect < 0) || nlnct >= zterm_lines) {
showinglist = 0;
@@ -2077,9 +2089,11 @@ complistmatches(UNUSED(Hookdef dummy), Chdata dat)
last_cap = (char *) zhalloc(max_caplen + 1);
*last_cap = '\0';
- if (!mnew && inselect && onlnct == nlnct && mlbeg >= 0 && mlbeg == molbeg)
- singledraw();
- else if (!compprintlist(mselect >= 0) || !clearflag)
+ if (!mnew && inselect &&
+ onlnct == nlnct && mlbeg >= 0 && mlbeg == molbeg) {
+ if (!noselect)
+ singledraw();
+ } else if (!compprintlist(mselect >= 0) || !clearflag)
noselect = 1;
onlnct = nlnct;
@@ -2092,7 +2106,7 @@ complistmatches(UNUSED(Hookdef dummy), Chdata dat)
popheap();
opts[EXTENDEDGLOB] = extendedglob;
- return noselect;
+ return (noselect < 0 ? 0 : noselect);
}
static int
@@ -2433,6 +2447,7 @@ domenuselect(Hookdef dummy, Chdata dat)
unqueue_signals();
mhasstat = (mstatus && *mstatus);
fdat = dat;
+ menuselect_bindings(); /* sanity in case deleted by user */
selectlocalmap(mskeymap);
noselect = 1;
while ((menuacc &&
@@ -2545,14 +2560,23 @@ domenuselect(Hookdef dummy, Chdata dat)
} else {
statusline = NULL;
}
+ if (noselect < 0) {
+ showinglist = clearlist = 0;
+ clearflag = 1;
+ }
zrefresh();
statusline = NULL;
inselect = 1;
+ selected = 1;
if (noselect) {
+ if (noselect < 0) {
+ /* no selection until after processing keystroke */
+ noselect = 0;
+ goto getk;
+ }
broken = 1;
break;
}
- selected = 1;
if (!i) {
i = mcols * mlines;
while (i--)
@@ -2750,6 +2774,7 @@ domenuselect(Hookdef dummy, Chdata dat)
if (nmessages) {
showinglist = -2;
zrefresh();
+ noselect = -1;
} else {
trashzle();
zsetterm();
@@ -3383,7 +3408,7 @@ domenuselect(Hookdef dummy, Chdata dat)
do_single(*(minfo.cur));
}
if (wasnext || broken) {
- menucmp = 2;
+ menucmp = 1;
showinglist = ((validlist && !nolist) ? -2 : 0);
minfo.asked = 0;
if (!noselect) {
@@ -3486,6 +3511,37 @@ enables_(Module m, int **enables)
}
/**/
+static void
+menuselect_bindings(void)
+{
+ if (!(mskeymap = openkeymap("menuselect"))) {
+ mskeymap = newkeymap(NULL, "menuselect");
+ linkkeymap(mskeymap, "menuselect", 1);
+ bindkey(mskeymap, "\t", refthingy(t_completeword), NULL);
+ bindkey(mskeymap, "\n", refthingy(t_acceptline), NULL);
+ bindkey(mskeymap, "\r", refthingy(t_acceptline), NULL);
+ bindkey(mskeymap, "\33[A", refthingy(t_uplineorhistory), NULL);
+ bindkey(mskeymap, "\33[B", refthingy(t_downlineorhistory), NULL);
+ bindkey(mskeymap, "\33[C", refthingy(t_forwardchar), NULL);
+ bindkey(mskeymap, "\33[D", refthingy(t_backwardchar), NULL);
+ bindkey(mskeymap, "\33OA", refthingy(t_uplineorhistory), NULL);
+ bindkey(mskeymap, "\33OB", refthingy(t_downlineorhistory), NULL);
+ bindkey(mskeymap, "\33OC", refthingy(t_forwardchar), NULL);
+ bindkey(mskeymap, "\33OD", refthingy(t_backwardchar), NULL);
+ }
+ if (!(lskeymap = openkeymap("listscroll"))) {
+ lskeymap = newkeymap(NULL, "listscroll");
+ linkkeymap(lskeymap, "listscroll", 1);
+ bindkey(lskeymap, "\t", refthingy(t_completeword), NULL);
+ bindkey(lskeymap, " ", refthingy(t_completeword), NULL);
+ bindkey(lskeymap, "\n", refthingy(t_acceptline), NULL);
+ bindkey(lskeymap, "\r", refthingy(t_acceptline), NULL);
+ bindkey(lskeymap, "\33[B", refthingy(t_downlineorhistory), NULL);
+ bindkey(lskeymap, "\33OB", refthingy(t_downlineorhistory), NULL);
+ }
+}
+
+/**/
int
boot_(Module m)
{
@@ -3503,27 +3559,7 @@ boot_(Module m)
}
addhookfunc("comp_list_matches", (Hookfn) complistmatches);
addhookfunc("menu_start", (Hookfn) domenuselect);
- mskeymap = newkeymap(NULL, "menuselect");
- linkkeymap(mskeymap, "menuselect", 1);
- bindkey(mskeymap, "\t", refthingy(t_completeword), NULL);
- bindkey(mskeymap, "\n", refthingy(t_acceptline), NULL);
- bindkey(mskeymap, "\r", refthingy(t_acceptline), NULL);
- bindkey(mskeymap, "\33[A", refthingy(t_uplineorhistory), NULL);
- bindkey(mskeymap, "\33[B", refthingy(t_downlineorhistory), NULL);
- bindkey(mskeymap, "\33[C", refthingy(t_forwardchar), NULL);
- bindkey(mskeymap, "\33[D", refthingy(t_backwardchar), NULL);
- bindkey(mskeymap, "\33OA", refthingy(t_uplineorhistory), NULL);
- bindkey(mskeymap, "\33OB", refthingy(t_downlineorhistory), NULL);
- bindkey(mskeymap, "\33OC", refthingy(t_forwardchar), NULL);
- bindkey(mskeymap, "\33OD", refthingy(t_backwardchar), NULL);
- lskeymap = newkeymap(NULL, "listscroll");
- linkkeymap(lskeymap, "listscroll", 1);
- bindkey(lskeymap, "\t", refthingy(t_completeword), NULL);
- bindkey(lskeymap, " ", refthingy(t_completeword), NULL);
- bindkey(lskeymap, "\n", refthingy(t_acceptline), NULL);
- bindkey(lskeymap, "\r", refthingy(t_acceptline), NULL);
- bindkey(lskeymap, "\33[B", refthingy(t_downlineorhistory), NULL);
- bindkey(lskeymap, "\33OB", refthingy(t_downlineorhistory), NULL);
+ menuselect_bindings();
return 0;
}
diff --git a/Src/Zle/compmatch.c b/Src/Zle/compmatch.c
index 0e41ac3a5..f82f00e1d 100644
--- a/Src/Zle/compmatch.c
+++ b/Src/Zle/compmatch.c
@@ -498,14 +498,27 @@ add_match_sub(Cmatcher m, char *l, int ll, char *w, int wl)
/**/
int
match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
- int sfx, int test, int part)
+ const int sfx, int test, int part)
{
- int ll = strlen(l), lw = strlen(w), oll = ll, olw = lw, exact = 0, wexact = 0;
- int il = 0, iw = 0, t, ind, add, he = 0, bpc, obc = bc, bslash;
+ /* How many characters from the line string and from the word string are
+ * yet to be matched. */
+ int ll = strlen(l), lw = strlen(w);
+ /* Number of characters from the line string and word string matched. */
+ int il = 0, iw = 0;
+ /* How many characters were matched exactly in the line and in the word. */
+ int exact = 0, wexact = 0;
+ int he = 0;
+ int bslash;
char *ow;
- Cmlist ms;
+ Cmlist ms; /* loop variable */
Cmatcher mp, lm = NULL;
Brinfo bp = NULL;
+ const int obc = bc;
+ const int ind = (sfx ? -1 : 0);
+ const int add = (sfx ? -1 : 1);
+ const int original_ll = ll, original_lw = lw;
+
+ /* INVARIANT: il+ll == original_ll; iw+lw == original_lw */
if (!test) {
start_match();
@@ -516,9 +529,6 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
if (sfx) {
l += ll; w += lw;
- ind = -1; add = -1;
- } else {
- ind = 0; add = 1;
}
/* ow will always point to the beginning (or end) of that sub-string
* in w that wasn't put in the match-variables yet. */
@@ -559,8 +569,7 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
bslash = 0;
if (!sfx && lw && (!part || test) &&
(l[ind] == w[ind] ||
- (bslash = (lw > 1 && w[ind] == '\\' &&
- (ind ? (w[0] == l[0]) : (w[1] == l[0])))))) {
+ (bslash = (lw > 1 && w[ind] == '\\' && w[ind+1] == l[0])))) {
/* No matcher could be used, but the strings have the same
* character here, skip over it. */
l += add; w += (bslash ? (add + add) : add);
@@ -583,9 +592,8 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
/* First try the matchers. Err... see above. */
for (mp = NULL, ms = mstack; !mp && ms; ms = ms->next) {
for (mp = ms->matcher; mp; mp = mp->next) {
- t = 1;
if ((lm && lm == mp) ||
- ((oll == ll || olw == lw) &&
+ ((original_ll == ll || original_lw == lw) &&
(test == 1 || (test && !mp->left && !mp->right)) &&
mp->wlen < 0))
/* If we were called recursively, don't use `*' patterns
@@ -593,9 +601,88 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
continue;
if (mp->wlen < 0) {
- int both, loff, aoff, llen, alen, zoff, moff, ct, ict, aol;
- char *tp, savl = '\0', savw;
- Cpattern ap, aop;
+ /* `*'-pattern. */
+ /*
+ * Similar to the identically-named variable in the 'else'
+ * block.
+ */
+ int t;
+ /*
+ * 1 iff the anchor and the word are on the same side of
+ * the line pattern; that is: if either
+ * - the anchor is on the left and we are matching
+ * a prefix; or
+ * - the anchor is on the right and we are matching
+ * a suffix.
+ */
+ int both;
+ /*
+ * Offset from the line pattern pointer ('l') to the start
+ * of the line pattern.
+ */
+ int loff;
+ /*
+ * Offset from the line pattern pointer ('l') to the start
+ * of the anchor.
+ */
+ int aoff;
+ /*
+ * The length of the line pattern.
+ */
+ int llen;
+ /*
+ * The length of the anchor.
+ *
+ * SEE: ap; aol, aop
+ */
+ int alen;
+ /*
+ * ### Related to 'zoff', which was removed in 2016.
+ */
+ int moff;
+ /*
+ * ### These two are related.
+ *
+ * ### They may have a relation similar to that of lw/iw
+ * ### (q.v.), at least during the 'for' loop. They may be
+ * ### overloaded/repurposed after it.
+ */
+ int ct, ict;
+ /*
+ * The length of the OTHER anchor: the left anchor when
+ * we're anchored on the right, and of the right anchor
+ * when we're anchored on the left.
+ */
+ int aol;
+ /*
+ * LOST: Documentation comment. Last seen 10 years ago in
+ * the temporal lobe. Reward promised for its safe return.
+ * Contact zsh-workers@zsh.org.
+ */
+ char *tp;
+ /*
+ * Temporary variable. Used as temporary storage for a
+ *
+ * {
+ * () {
+ * local foo="$foo"
+ * foo[1]=bar
+ * ...
+ * }
+ * (use original $foo here)
+ * }
+ *
+ * operation. Similar to savw.
+ */
+ char savl;
+ /*
+ * The anchor on this end.
+ */
+ Cpattern ap;
+ /*
+ * The anchor on the other end.
+ */
+ Cpattern aop;
/* This is for `*' patterns, first initialise some
* local variables. */
@@ -611,14 +698,14 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
continue;
if (mp->flags & CMF_LEFT) {
- ap = mp->left; zoff = 0; moff = alen; aop = mp->right;
+ ap = mp->left; moff = alen; aop = mp->right;
if (sfx) {
both = 0; loff = -llen; aoff = -(llen + alen);
} else {
both = 1; loff = alen; aoff = 0;
}
} else {
- ap = mp->right; zoff = alen; moff = 0; aop = mp->left;
+ ap = mp->right; moff = 0; aop = mp->left;
if (sfx) {
both = 1; loff = -(llen + alen); aoff = -alen;
} else {
@@ -644,8 +731,8 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
/* Fine, now we call ourselves recursively to find the
* string matched by the `*'. */
- if (sfx && (savl = l[-(llen + zoff)]))
- l[-(llen + zoff)] = '\0';
+ if (sfx && (savl = l[-(llen + alen)]))
+ l[-(llen + alen)] = '\0';
for (t = 0, tp = w, ct = 0, ict = lw - alen + 1;
ict;
tp += add, ct++, ict--) {
@@ -667,22 +754,25 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
!match_parts(l + aoff , tp - moff, alen, part))
break;
if (sfx) {
- if ((savw = tp[-zoff]))
- tp[-zoff] = '\0';
+ /* Call ourselves recursively with the
+ * anchor removed. */
+ char savw;
+ if ((savw = tp[-alen]))
+ tp[-alen] = '\0';
t = match_str(l - ll, w - lw,
- NULL, 0, NULL, 1, 2, part);
+ NULL, 0, NULL, sfx, 2, part);
if (savw)
- tp[-zoff] = savw;
+ tp[-alen] = savw;
} else
t = match_str(l + llen + moff, tp + moff,
- NULL, 0, NULL, 0, 1, part);
+ NULL, 0, NULL, sfx, 1, part);
if (t || (mp->wlen == -1 && !both))
break;
}
}
ict = ct;
if (sfx && savl)
- l[-(llen + zoff)] = savl;
+ l[-(llen + alen)] = savl;
/* Have we found a position in w where the rest of l
* matches? */
@@ -752,18 +842,22 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
bc += llen;
exact = 0;
- if (!test)
+ if (!test) {
+ int bpc;
while (bp &&
bc >= (bpc = (useqbr ? bp->qpos : bp->pos))) {
bp->curpos = matchbufadded + bpc - bc + obc;
bp = bp->next;
}
+ }
ow = w;
if (!llen && !alen) {
lm = mp;
- if (he)
+ if (he) {
+ /* Signal the outer for loop to continue. */
mp = NULL;
+ }
else
he = 1;
} else {
@@ -772,6 +866,11 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
break;
} else if (ll >= mp->llen && lw >= mp->wlen) {
/* Non-`*'-pattern. */
+ /*
+ * Similar to the identically-named variable in the 'if'
+ * block.
+ */
+ int t = 1;
char *tl, *tw;
int tll, tlw, til, tiw;
@@ -875,12 +974,14 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
bc += mp->llen;
exact = 0;
- if (!test)
+ if (!test) {
+ int bpc;
while (bp &&
bc >= (bpc = (useqbr ? bp->qpos : bp->pos))) {
bp->curpos = matchbufadded + bpc - bc + obc;
bp = bp->next;
}
+ }
ow = w;
lm = NULL;
he = 0;
@@ -896,8 +997,7 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
bslash = 0;
if ((!test || sfx) && lw &&
(l[ind] == w[ind] ||
- (bslash = (lw > 1 && w[ind] == '\\' &&
- (ind ? (w[0] == l[0]) : (w[1] == l[0])))))) {
+ (bslash = (lw > 1 && w[ind] == '\\' && w[ind+1] == l[0])))) {
/* No matcher could be used, but the strings have the same
* character here, skip over it. */
l += add; w += (bslash ? (add + add ) : add);
diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c
index 7fec7c804..05799399d 100644
--- a/Src/Zle/compresult.c
+++ b/Src/Zle/compresult.c
@@ -1174,6 +1174,10 @@ do_single(Cmatch m)
zlemetacs = minfo.end;
if (zlemetacs + m->qisl == lastend)
zlemetacs += minfo.insc;
+
+ /* Advance CURSOR past compadd -s/-S suffixes. */
+ zlemetacs += strlen(psuf);
+ zlemetacs += m->suf ? strlen(m->suf) : 0;
}
{
Cmatch *om = minfo.cur;
@@ -1191,6 +1195,7 @@ do_single(Cmatch m)
if (menucmp)
minfo.cur = &m;
runhookdef(INSERTMATCHHOOK, (void *) &dat);
+ redrawhook();
minfo.cur = om;
}
}
diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c
index 60fb096be..c78167329 100644
--- a/Src/Zle/computil.c
+++ b/Src/Zle/computil.c
@@ -199,11 +199,11 @@ cd_calc(void)
set->count++;
if ((l = strlen(str->str)) > cd_state.pre)
cd_state.pre = l;
- if ((l = MB_METASTRWIDTH(str->str)) > cd_state.premaxw)
+ if ((l = ZMB_nicewidth(str->str)) > cd_state.premaxw)
cd_state.premaxw = l;
if (str->desc) {
set->desc++;
- if ((l = strlen(str->desc)) > cd_state.suf)
+ if ((l = strlen(str->desc)) > cd_state.suf) /* ### strlen() assumes no \n */
cd_state.suf = l;
}
}
@@ -490,7 +490,7 @@ cd_init(char *nam, char *hide, char *mlen, char *sep,
setp = &(cd_state.sets);
cd_state.sep = ztrdup(sep);
cd_state.slen = strlen(sep);
- cd_state.swidth = MB_METASTRWIDTH(sep);
+ cd_state.swidth = ZMB_nicewidth(sep);
cd_state.sets = NULL;
cd_state.showd = disp;
cd_state.maxg = cd_state.groups = cd_state.descs = 0;
@@ -526,7 +526,8 @@ cd_init(char *nam, char *hide, char *mlen, char *sep,
str->other = NULL;
str->set = set;
- for (tmp = *ap; *tmp && *tmp != ':'; tmp++)
+ /* Advance tmp to the first unescaped colon. */
+ for (tmp = *ap; *tmp && *tmp != ':'; tmp++)
if (*tmp == '\\' && tmp[1])
tmp++;
@@ -537,7 +538,7 @@ cd_init(char *nam, char *hide, char *mlen, char *sep,
*tmp = '\0';
str->str = str->match = ztrdup(rembslash(*ap));
str->len = strlen(str->str);
- str->width = MB_METASTRWIDTH(str->str);
+ str->width = ZMB_nicewidth(str->str);
str->sortstr = NULL;
}
if (str)
@@ -692,7 +693,7 @@ cd_get(char **params)
* end of screen as safety margin
*/
d = str->desc;
- w = MB_METASTRWIDTH(d);
+ w = ZMB_nicewidth(d);
if (w <= remw)
strcpy(p, d);
else {
@@ -701,7 +702,7 @@ cd_get(char **params)
l = MB_METACHARLEN(d);
memcpy(pp, d, l);
pp[l] = '\0';
- w = MB_METASTRWIDTH(pp);
+ w = ZMB_nicewidth(pp);
if (w > remw) {
*pp = '\0';
break;
@@ -792,7 +793,7 @@ cd_get(char **params)
cd_state.swidth - CM_SPACE;
p = pp = dbuf + cd_state.slen;
d = str->desc;
- w = MB_METASTRWIDTH(d);
+ w = ZMB_nicewidth(d);
if (w <= remw) {
strcpy(p, d);
remw -= w;
@@ -802,7 +803,7 @@ cd_get(char **params)
l = MB_METACHARLEN(d);
memcpy(pp, d, l);
pp[l] = '\0';
- w = MB_METASTRWIDTH(pp);
+ w = ZMB_nicewidth(pp);
if (w > remw) {
*pp = '\0';
break;
@@ -1693,10 +1694,10 @@ ca_get_opt(Cadef d, char *line, int full, char **end)
for (p = d->opts; p; p = p->next)
if (p->active && ((!p->args || p->type == CAO_NEXT) ?
!strcmp(p->name, line) : strpfx(p->name, line))) {
- int l = strlen(p->name);
- if ((p->type == CAO_OEQUAL || p->type == CAO_EQUAL) &&
- line[l] && line[l] != '=')
- continue;
+ int l = strlen(p->name);
+ if ((p->type == CAO_OEQUAL || p->type == CAO_EQUAL) &&
+ line[l] && line[l] != '=')
+ continue;
if (end) {
/* Return a pointer to the end of the option. */
@@ -2158,7 +2159,8 @@ ca_parse_line(Cadef d, int multi, int first)
state.opt = 0;
else
state.curopt = NULL;
- } else if (multi && (*line == '-' || *line == '+') && cur != compcurrent
+ } else if (multi && (*line == '-' || *line == '+') && cur != compcurrent &&
+ ca_get_opt(d, line, 0, NULL)
#if 0
/**** Ouch. Using this will disable the mutual exclusion
of different sets. Not using it will make the -A
@@ -2167,9 +2169,11 @@ ca_parse_line(Cadef d, int multi, int first)
#endif
)
return 1;
- else if (state.arg && (!napat || !pattry(napat, line))) {
+ else if (state.arg &&
+ (!napat || cur <= compcurrent || !pattry(napat, line))) {
/* Otherwise it's a normal argument. */
- if (napat && ca_inactive(d, NULL, cur + 1, 1, NULL))
+ if (napat && cur <= compcurrent &&
+ ca_inactive(d, NULL, cur + 1, 1, NULL))
return 1;
arglast = 1;
@@ -2308,7 +2312,10 @@ ca_parse_line(Cadef d, int multi, int first)
return 0;
}
-/* Build a colon-list from a list. */
+/* Build a colon-list from a list.
+ *
+ * This is only used to populate values of $opt_args.
+ */
static char *
ca_colonlist(LinkList l)
@@ -2318,16 +2325,19 @@ ca_colonlist(LinkList l)
int len = 0;
char *p, *ret, *q;
+ /* Compute the length to be allocated. */
for (n = firstnode(l); n; incnode(n)) {
len++;
for (p = (char *) getdata(n); *p; p++)
- len += (*p == ':' ? 2 : 1);
+ len += (*p == ':' || *p == '\\') ? 2 : 1;
}
ret = q = (char *) zalloc(len);
+ /* Join L into RET, joining with colons and escaping colons and
+ * backslashes. */
for (n = firstnode(l); n;) {
for (p = (char *) getdata(n); *p; p++) {
- if (*p == ':')
+ if (*p == ':' || *p == '\\')
*q++ = '\\';
*q++ = *p;
}
@@ -3546,7 +3556,7 @@ comp_quote(char *str, int prefix)
if ((x = (prefix && *str == '=')))
*str = 'x';
- ret = quotestring(str, NULL, *compqstack);
+ ret = quotestring(str, *compqstack);
if (x)
*str = *ret = '=';
@@ -4480,6 +4490,10 @@ cfp_matcher_pats(char *matcher, char *add)
return add;
}
+/*
+ * ### This function call is skipped by _approximate, so "opt" probably means "optimize".
+ */
+
static void
cfp_opt_pats(char **pats, char *matcher)
{
@@ -4732,7 +4746,7 @@ cf_ignore(char **names, LinkList ign, char *style, char *path)
for (; (n = *names); names++) {
if (!ztat(n, &nst, 1) && S_ISDIR(nst.st_mode)) {
if (tpwd && nst.st_dev == est.st_dev && nst.st_ino == est.st_ino) {
- addlinknode(ign, quotestring(n, NULL, QT_BACKSLASH));
+ addlinknode(ign, quotestring(n, QT_BACKSLASH));
continue;
}
if (tpar && !strncmp((c = dupstring(n)), path, pl)) {
@@ -4748,7 +4762,7 @@ cf_ignore(char **names, LinkList ign, char *style, char *path)
if (found || ((e = strrchr(c, '/')) && e > c + pl &&
!ztat(c, &st, 1) && st.st_dev == nst.st_dev &&
st.st_ino == nst.st_ino))
- addlinknode(ign, quotestring(n, NULL, QT_BACKSLASH));
+ addlinknode(ign, quotestring(n, QT_BACKSLASH));
}
}
}
@@ -4811,6 +4825,20 @@ cf_remove_other(char **names, char *pre, int *amb)
return NULL;
}
+/*
+ * SYNOPSIS:
+ * 1. compfiles -p parnam1 parnam2 skipped matcher sdirs parnam3 varargs [..varargs]
+ * 2. compfiles -p- parnam1 parnam2 skipped matcher sdirs parnam3 varargs [..varargs]
+ * 3. compfiles -P parnam1 parnam2 skipped matcher sdirs parnam3
+ *
+ * 1. Set parnam1 to an array of patterns....
+ * ${(P)parnam1} is an in/out parameter.
+ * 2. Like #1 but without calling cfp_opt_pats(). (This is only used by _approximate.)
+ * 3. Like #1 but varargs is implicitly set to char *varargs[2] = { "*(-/)", NULL };.
+ *
+ * parnam2 has to do with the accept-exact style (see cfp_test_exact()).
+ */
+
static int
bin_compfiles(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
{
@@ -4839,11 +4867,12 @@ bin_compfiles(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
}
queue_signals();
if (!(tmp = getaparam(args[1]))) {
+ unqueue_signals();
zwarnnam(nam, "unknown parameter: %s", args[1]);
return 0;
}
for (l = newlinklist(); *tmp; tmp++)
- addlinknode(l, *tmp);
+ addlinknode(l, quotestring(*tmp, QT_BACKSLASH_PATTERN));
set_list_array(args[1], cf_pats((args[0][1] == 'P'), !!args[0][2],
l, getaparam(args[2]), args[3],
args[4], args[5],
@@ -4994,7 +5023,7 @@ enables_(Module m, int **enables)
/**/
int
-boot_(Module m)
+boot_(UNUSED(Module m))
{
return 0;
}
diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list
index 2b2654c5d..58310cd74 100644
--- a/Src/Zle/iwidgets.list
+++ b/Src/Zle/iwidgets.list
@@ -143,6 +143,7 @@
"vi-delete", videlete, ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_VIOPER
"vi-delete-char", videletechar, ZLE_KEEPSUFFIX
"vi-digit-or-beginning-of-line", vidigitorbeginningofline, 0
+"vi-down-case", vidowncase, ZLE_LASTCOL | ZLE_VIOPER
"vi-down-line-or-history", vidownlineorhistory, ZLE_LINEMOVE
"vi-end-of-line", viendofline, ZLE_LASTCOL
"vi-fetch-history", vifetchhistory, ZLE_LINEMOVE
@@ -188,6 +189,7 @@
"vi-swap-case", viswapcase, ZLE_LASTCOL
"vi-undo-change", viundochange, ZLE_KEEPSUFFIX
"vi-unindent", viunindent, ZLE_LASTCOL | ZLE_VIOPER
+"vi-up-case", viupcase, ZLE_LASTCOL | ZLE_VIOPER
"vi-up-line-or-history", viuplineorhistory, ZLE_LINEMOVE
"vi-yank", viyank, ZLE_LASTCOL | ZLE_VIOPER
"vi-yank-eol", viyankeol, 0
diff --git a/Src/Zle/textobjects.c b/Src/Zle/textobjects.c
index 9b3277a97..3db0781ff 100644
--- a/Src/Zle/textobjects.c
+++ b/Src/Zle/textobjects.c
@@ -1,5 +1,5 @@
/*
- * textobjects.c - ZLE module implementing Vim style text objects
+ * textobjects.c - ZLE widgets implementing Vim style text objects
*
* This file is part of zsh, the Z shell.
*
@@ -54,11 +54,7 @@ selectword(UNUSED(char **args))
int sclass = viclass(zleline[zlecs]);
int doblanks = all && sclass;
- if (!invicmdmode()) {
- region_active = 1;
- mark = zlecs;
- }
- if (!region_active || zlecs == mark) {
+ if (!region_active || zlecs == mark || mark == -1) {
/* search back to first character of same class as the start position
* also stop at the beginning of the line */
mark = zlecs;
@@ -207,8 +203,12 @@ selectword(UNUSED(char **args))
/* Adjustment: vi operators don't include the cursor position, in insert
* or emacs mode the region also doesn't but for vi visual mode it is
* included. */
- if (zlecs && zlecs > mark && !virangeflag)
- DECCS();
+ if (!virangeflag) {
+ if (!invicmdmode())
+ region_active = 1;
+ else if (zlecs && zlecs > mark)
+ DECCS();
+ }
return 0;
}
@@ -315,7 +315,7 @@ selectargument(UNUSED(char **args))
}
/* Adjustment: vi operators don't include the cursor position */
- if (!virangeflag)
+ if (!virangeflag && invicmdmode())
DECCS();
return 0;
diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h
index e9b14281d..8f92e5611 100644
--- a/Src/Zle/zle.h
+++ b/Src/Zle/zle.h
@@ -284,6 +284,20 @@ struct change {
#define CH_NEXT (1<<0) /* next structure is also part of this change */
#define CH_PREV (1<<1) /* previous structure is also part of this change */
+/* vi change handling for vi-repeat-change */
+
+/*
+ * Examination of the code suggests vichgbuf is consistently tied
+ * to raw byte input, so it is left as a character array rather
+ * than turned into wide characters. In particular, when we replay
+ * it we use ungetbytes().
+ */
+struct vichange {
+ struct modifier mod; /* value of zmod associated with vi change */
+ char *buf; /* bytes for keys that make up the vi command */
+ int bufsz, bufptr; /* allocated and in use sizes of buf */
+};
+
/* known thingies */
#define Th(X) (&thingies[X])
diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
index 382eb8d41..04eb70675 100644
--- a/Src/Zle/zle_keymap.c
+++ b/Src/Zle/zle_keymap.c
@@ -135,7 +135,10 @@ mod_export HashTable keymapnamtab;
/**/
char *keybuf;
-static int keybuflen, keybufsz = 20;
+/**/
+int keybuflen;
+
+static int keybufsz = 20;
/* last command executed with execute-named-command */
@@ -1322,15 +1325,15 @@ default_bindings(void)
amap->first[i] = refthingy(t_undefinedkey);
/* safe fallback keymap:
- * 0-255 self-insert, except: *
- * '\n' accept-line *
- * '\r' accept-line */
+ * 0-255 .self-insert, except: *
+ * '\n' .accept-line *
+ * '\r' .accept-line */
for (i = 0; i < 256; i++)
- smap->first[i] = refthingy(t_selfinsert);
- unrefthingy(t_selfinsert);
- unrefthingy(t_selfinsert);
- smap->first['\n'] = refthingy(t_acceptline);
- smap->first['\r'] = refthingy(t_acceptline);
+ smap->first[i] = refthingy(t_Dselfinsert);
+ unrefthingy(t_Dselfinsert);
+ unrefthingy(t_Dselfinsert);
+ smap->first['\n'] = refthingy(t_Dacceptline);
+ smap->first['\r'] = refthingy(t_Dacceptline);
/* vt100 arrow keys are bound by default, for historical reasons. *
* Both standard and keypad modes are supported. */
@@ -1366,6 +1369,8 @@ default_bindings(void)
bindkey(vismap, "\33", refthingy(t_deactivateregion), NULL);
bindkey(vismap, "o", refthingy(t_exchangepointandmark), NULL);
bindkey(vismap, "p", refthingy(t_putreplaceselection), NULL);
+ bindkey(vismap, "u", refthingy(t_vidowncase), NULL);
+ bindkey(vismap, "U", refthingy(t_viupcase), NULL);
bindkey(vismap, "x", refthingy(t_videlete), NULL);
bindkey(vismap, "~", refthingy(t_vioperswapcase), NULL);
@@ -1374,8 +1379,12 @@ default_bindings(void)
bindkey(amap, "ge", refthingy(t_vibackwardwordend), NULL);
bindkey(amap, "gE", refthingy(t_vibackwardblankwordend), NULL);
bindkey(amap, "gg", refthingy(t_beginningofbufferorhistory), NULL);
+ bindkey(amap, "gu", refthingy(t_vidowncase), NULL);
+ bindkey(amap, "gU", refthingy(t_viupcase), NULL);
bindkey(amap, "g~", refthingy(t_vioperswapcase), NULL);
bindkey(amap, "g~~", NULL, "g~g~");
+ bindkey(amap, "guu", NULL, "gugu");
+ bindkey(amap, "gUU", NULL, "gUgU");
/* emacs mode: arrow keys */
add_cursor_key(emap, TCUPCURSOR, t_uplineorhistory, 'A');
@@ -1615,11 +1624,18 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp)
else
lastchar = lastc;
if(lastlen != keybuflen) {
+ /*
+ * We want to keep only the first lastlen bytes of the key
+ * buffer in the key buffer that were marked as used by the key
+ * binding above, and make the rest available for input again.
+ * That rest (but not what we are keeping) needs to be
+ * unmetafied.
+ */
unmetafy(keybuf + lastlen, &keybuflen);
ungetbytes(keybuf+lastlen, keybuflen);
if(vichgflag)
- vichgbufptr -= keybuflen;
- keybuf[lastlen] = 0;
+ curvichg.bufptr -= keybuflen;
+ keybuf[keybuflen = lastlen] = 0;
}
*funcp = func;
*strp = str;
@@ -1672,7 +1688,7 @@ getkeybuf(int w)
mod_export void
ungetkeycmd(void)
{
- ungetbytes(keybuf, keybuflen);
+ ungetbytes_unmeta(keybuf, keybuflen);
}
/* read a command from the current keymap, with widgets */
@@ -1690,17 +1706,12 @@ getkeycmd(void)
if(!*seq)
return NULL;
if(!func) {
- int len;
- char *pb;
-
if (++hops == 20) {
zerr("string inserting another one too many times");
hops = 0;
return NULL;
}
- pb = unmetafy(ztrdup(str), &len);
- ungetbytes(pb, len);
- zfree(pb, strlen(str) + 1);
+ ungetbytes_unmeta(str, strlen(str));
goto sentstring;
}
if (func == Th(z_executenamedcmd) && !statusline) {
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 6e2bfded8..15ea79643 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -357,6 +357,21 @@ ungetbytes(char *s, int len)
ungetbyte(*--s);
}
+/**/
+void
+ungetbytes_unmeta(char *s, int len)
+{
+ s += len;
+ while (len--) {
+ if (len && s[-2] == Meta) {
+ ungetbyte(*--s ^ 32);
+ len--;
+ s--;
+ } else
+ ungetbyte(*--s);
+ }
+}
+
#if defined(pyr) && defined(HAVE_SELECT)
static int
breakread(int fd, char *buf, int n)
@@ -456,7 +471,7 @@ calc_timeout(struct ztmout *tmoutp, long do_keytmout)
tfdat = (Timedfn)getdata(tfnode);
diff = tfdat->when - time(NULL);
- if (diff < 0) {
+ if (diff <= 0) {
/* Already due; call it and rescan. */
tfdat->func();
continue;
@@ -909,13 +924,13 @@ getbyte(long do_keytmout, int *timeout)
ret = STOUC(cc);
}
/*
- * vichgbuf is raw bytes, not wide characters, so is dealt
+ * curvichg.buf is raw bytes, not wide characters, so is dealt
* with here.
*/
if (vichgflag) {
- if (vichgbufptr == vichgbufsz)
- vichgbuf = realloc(vichgbuf, vichgbufsz *= 2);
- vichgbuf[vichgbufptr++] = ret;
+ if (curvichg.bufptr == curvichg.bufsz)
+ curvichg.buf = realloc(curvichg.buf, curvichg.bufsz *= 2);
+ curvichg.buf[curvichg.bufptr++] = ret;
}
errno = old_errno;
return lastchar = ret;
@@ -1026,28 +1041,43 @@ getrestchar(int inchar, char *outstr, int *outcount)
#endif
/**/
-void redrawhook(void)
+void
+redrawhook(void)
{
Thingy initthingy;
if ((initthingy = rthingy_nocreate("zle-line-pre-redraw"))) {
+ /* Duplicating most of zlecallhook() to save additional state */
+ int saverrflag = errflag, savretflag = retflag;
int lastcmd_prev = lastcmd;
int old_incompfunc = incompfunc;
char *args[2];
Thingy lbindk_save = lbindk, bindk_save = bindk;
+
refthingy(lbindk_save);
refthingy(bindk_save);
args[0] = initthingy->nam;
args[1] = NULL;
+
+ /* The generic redraw hook cannot be a completion function, so
+ * temporarily reset state for special variable handling etc.
+ */
incompfunc = 0;
- execzlefunc(initthingy, args, 0);
+ execzlefunc(initthingy, args, 1);
incompfunc = old_incompfunc;
+
+ /* Restore errflag and retflag as zlecallhook() does */
+ errflag = saverrflag | (errflag & ERRFLAG_INT);
+ retflag = savretflag;
+
unrefthingy(initthingy);
unrefthingy(lbindk);
unrefthingy(bindk);
lbindk = lbindk_save;
bindk = bindk_save;
+
/* we can't set ZLE_NOTCOMMAND since it's not a legit widget, so
- * restore lastcmd manually so that we don't mess up the global state */
+ * restore lastcmd manually so that we don't mess up the global state
+ */
lastcmd = lastcmd_prev;
}
}
@@ -1144,11 +1174,19 @@ zlecore(void)
}
- region_active = 0;
popheap();
}
-/* Read a line. It is returned metafied. */
+/* Read a line. It is returned metafied.
+ *
+ * Parameters:
+ * - lp: left prompt, e.g., $PS1
+ * - rp: right prompt, e.g., $RPS1
+ * - flags: ZLRF_* flags (I think), see zlereadflags
+ * - context: ZLCON_* flags (I think), see zlecontext
+ * - init: "zle-line-init"
+ * - finish: "zle-line-finish"
+ */
/**/
char *
@@ -1222,6 +1260,7 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
*zleline = ZWC('\0');
virangeflag = lastcmd = done = zlecs = zlell = mark = yankb = yanke = 0;
vichgflag = 0;
+ viinrepeat = 0;
viinsbegin = 0;
statusline = NULL;
selectkeymap("main", 1);
@@ -1277,6 +1316,7 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
lastcol = -1;
initmodifier(&zmod);
prefixflag = 0;
+ region_active = 0;
zrefresh();
@@ -1284,6 +1324,9 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
zlecallhook(init, NULL);
+ if (zleline && *zleline)
+ redrawhook();
+
if ((bracket = getaparam("zle_bracketed_paste")) && arrlen(bracket) == 2)
fputs(*bracket, shout);
@@ -1326,6 +1369,16 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
return s;
}
+/**/
+static int
+execimmortal(Thingy func, char **args)
+{
+ Thingy immortal = rthingy_nocreate(dyncat(".", func->nam));
+ if (immortal)
+ return execzlefunc(immortal, args, 0);
+ return 1;
+}
+
/*
* Execute a widget. The third argument indicates that the global
* variable bindk should be set temporarily so that WIDGET etc.
@@ -1337,6 +1390,8 @@ int
execzlefunc(Thingy func, char **args, int set_bindk)
{
int r = 0, ret = 0, remetafy = 0;
+ int nestedvichg = vichgflag;
+ int isrepeat = (viinrepeat == 3);
Widget w;
Thingy save_bindk = bindk;
@@ -1346,8 +1401,10 @@ execzlefunc(Thingy func, char **args, int set_bindk)
unmetafy_line();
remetafy = 1;
}
+ if (isrepeat)
+ viinrepeat = 2;
- if(func->flags & DISABLED) {
+ if (func->flags & DISABLED) {
/* this thingy is not the name of a widget */
char *nm = nicedup(func->nam, 0);
char *msg = tricat("No such widget `", nm, "'");
@@ -1355,7 +1412,7 @@ execzlefunc(Thingy func, char **args, int set_bindk)
zsfree(nm);
showmsg(msg);
zsfree(msg);
- ret = 1;
+ ret = execimmortal(func, args);
} else if((w = func->widget)->flags & (WIDGET_INT|WIDGET_NCOMP)) {
int wflags = w->flags;
@@ -1419,7 +1476,7 @@ execzlefunc(Thingy func, char **args, int set_bindk)
zsfree(nm);
showmsg(msg);
zsfree(msg);
- ret = 1;
+ ret = execimmortal(func, args);
} else {
int osc = sfcontext, osi = movefd(0);
int oxt = isset(XTRACE);
@@ -1441,6 +1498,12 @@ execzlefunc(Thingy func, char **args, int set_bindk)
opts[XTRACE] = oxt;
sfcontext = osc;
endparamscope();
+ if (errflag == ERRFLAG_ERROR) {
+ int saverr = errflag;
+ errflag &= ~ERRFLAG_ERROR;
+ if ((ret = execimmortal(func, args)) != 0)
+ errflag |= saverr;
+ }
lastcmd = w->flags & ~(WIDGET_INUSE|WIDGET_FREE);
if (inuse) {
w->flags &= WIDGET_INUSE|WIDGET_FREE;
@@ -1469,6 +1532,25 @@ execzlefunc(Thingy func, char **args, int set_bindk)
CCRIGHT();
if (remetafy)
metafy_line();
+
+ /* if this widget constituted the vi change, end it */
+ if (vichgflag == 2 && !nestedvichg) {
+ if (invicmdmode()) {
+ if (ret) {
+ free(curvichg.buf);
+ } else {
+ if (lastvichg.buf)
+ free(lastvichg.buf);
+ lastvichg = curvichg;
+ }
+ vichgflag = 0;
+ curvichg.buf = NULL;
+ } else
+ vichgflag = 1; /* vi change continues while in insert mode */
+ }
+ if (isrepeat)
+ viinrepeat = !invicmdmode();
+
return ret;
}
@@ -1604,6 +1686,7 @@ bin_vared(char *name, char **args, Options ops, UNUSED(int func))
return 1;
} else if (v) {
if (*s) {
+ unqueue_signals();
zwarnnam(name, "not an identifier: `%s'", args[0]);
return 1;
}
@@ -1840,11 +1923,17 @@ int
recursiveedit(UNUSED(char **args))
{
int locerror;
+ int q = queue_signal_level();
+
+ /* zlecore() expects to be entered with signal queue disabled */
+ dont_queue_signals();
redrawhook();
zrefresh();
zlecore();
+ restore_queue_signals(q);
+
locerror = errflag ? 1 : 0;
errflag = done = eofsent = 0;
@@ -1856,6 +1945,7 @@ void
reexpandprompt(void)
{
static int reexpanding;
+ static int looping;
if (!reexpanding++) {
/*
@@ -1866,15 +1956,33 @@ reexpandprompt(void)
int local_lastval = lastval;
lastval = pre_zle_status;
- free(lpromptbuf);
- lpromptbuf = promptexpand(raw_lp ? *raw_lp : NULL, 1, NULL, NULL,
- &pmpt_attr);
- rpmpt_attr = pmpt_attr;
- free(rpromptbuf);
- rpromptbuf = promptexpand(raw_rp ? *raw_rp : NULL, 1, NULL, NULL,
- &rpmpt_attr);
+ do {
+ /* A new SIGWINCH may arrive while in promptexpand(), causing
+ * looping to increment. This only happens when a command
+ * substitution is used in a PROMPT_SUBST prompt, but
+ * nevertheless keep trying until we see no more changes.
+ */
+ char *new_lprompt, *new_rprompt;
+ looping = reexpanding;
+
+ new_lprompt = promptexpand(raw_lp ? *raw_lp : NULL, 1, NULL, NULL,
+ &pmpt_attr);
+ free(lpromptbuf);
+ lpromptbuf = new_lprompt;
+
+ if (looping != reexpanding)
+ continue;
+
+ rpmpt_attr = pmpt_attr;
+ new_rprompt = promptexpand(raw_rp ? *raw_rp : NULL, 1, NULL, NULL,
+ &rpmpt_attr);
+ free(rpromptbuf);
+ rpromptbuf = new_rprompt;
+ } while (looping != reexpanding);
+
lastval = local_lastval;
- }
+ } else
+ looping = reexpanding;
reexpanding--;
}
@@ -2150,7 +2258,7 @@ finish_(UNUSED(Module m))
cleanup_keymaps();
deletehashtable(thingytab);
- zfree(vichgbuf, vichgbufsz);
+ zfree(lastvichg.buf, lastvichg.bufsz);
zfree(kungetbuf, kungetsz);
free_isrch_spots();
if (rdstrs)
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index 0483f758d..898b552de 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -597,7 +597,7 @@ static void pastebuf(Cutbuffer buf, int mult, int position)
zlecs += cc;
}
yanke = zlecs;
- if (zlecs)
+ if (zlecs && invicmdmode())
DECCS();
}
}
@@ -609,8 +609,10 @@ viputbefore(UNUSED(char **args))
int n = zmult;
startvichange(-1);
- if (n < 0 || zmod.flags & MOD_NULL)
+ if (n < 0)
return 1;
+ if (zmod.flags & MOD_NULL)
+ return 0;
if (zmod.flags & MOD_VIBUF)
kctbuf = &vibuf[zmod.vibuf];
else
@@ -630,8 +632,10 @@ viputafter(UNUSED(char **args))
int n = zmult;
startvichange(-1);
- if (n < 0 || zmod.flags & MOD_NULL)
+ if (n < 0)
return 1;
+ if (zmod.flags & MOD_NULL)
+ return 0;
if (zmod.flags & MOD_VIBUF)
kctbuf = &vibuf[zmod.vibuf];
else
@@ -780,7 +784,7 @@ bracketedpaste(char **args)
int n;
ZLE_STRING_T wpaste;
wpaste = stringaszleline((zmult == 1) ? pbuf :
- quotestring(pbuf, NULL, QT_SINGLE_OPTIONAL), 0, &n, NULL, NULL);
+ quotestring(pbuf, QT_SINGLE_OPTIONAL), 0, &n, NULL, NULL);
cuttext(wpaste, n, CUT_REPLACE);
if (!(zmod.flags & MOD_VIBUF)) {
kct = -1;
@@ -1497,7 +1501,7 @@ struct suffixset {
};
/* The list of suffix structures */
-struct suffixset *suffixlist;
+static struct suffixset *suffixlist;
/* Shell function to call to remove the suffix. */
diff --git a/Src/Zle/zle_params.c b/Src/Zle/zle_params.c
index b5bb288f1..1e4c5b832 100644
--- a/Src/Zle/zle_params.c
+++ b/Src/Zle/zle_params.c
@@ -103,9 +103,27 @@ static const struct gsu_integer yankend_gsu =
{ get_yankend, set_yankend, zleunsetfn };
static const struct gsu_integer yankactive_gsu =
{ get_yankactive, NULL, zleunsetfn };
+static const struct gsu_integer isearchmatchstart_gsu =
+{ get_isearchmatchstart, NULL, zleunsetfn };
+static const struct gsu_integer isearchmatchend_gsu =
+{ get_isearchmatchend, NULL, zleunsetfn };
+static const struct gsu_integer isearchmatchactive_gsu =
+{ get_isearchmatchactive, NULL, zleunsetfn };
+static const struct gsu_integer suffixstart_gsu =
+{ get_suffixstart, NULL, zleunsetfn };
+static const struct gsu_integer suffixend_gsu =
+{ get_suffixend, NULL, zleunsetfn };
+static const struct gsu_integer suffixactive_gsu =
+{ get_suffixactive, NULL, zleunsetfn };
static const struct gsu_array killring_gsu =
{ get_killring, set_killring, unset_killring };
+
+static const struct gsu_scalar register_gsu =
+{ strgetfn, set_register, unset_register };
+static const struct gsu_hash registers_gsu =
+{ hashgetfn, set_registers, zleunsetfn };
+
/* implementation is in zle_refresh.c */
static const struct gsu_array region_highlight_gsu =
{ get_region_highlight, set_region_highlight, unset_region_highlight };
@@ -152,6 +170,12 @@ static struct zleparam {
{ "YANK_START", PM_INTEGER, GSU(yankstart_gsu), NULL },
{ "YANK_END", PM_INTEGER, GSU(yankend_gsu), NULL },
{ "YANK_ACTIVE", PM_INTEGER | PM_READONLY, GSU(yankactive_gsu), NULL },
+ { "ISEARCHMATCH_START", PM_INTEGER, GSU(isearchmatchstart_gsu), NULL },
+ { "ISEARCHMATCH_END", PM_INTEGER, GSU(isearchmatchend_gsu), NULL },
+ { "ISEARCHMATCH_ACTIVE", PM_INTEGER | PM_READONLY, GSU(isearchmatchactive_gsu), NULL },
+ { "SUFFIX_START", PM_INTEGER, GSU(suffixstart_gsu), NULL },
+ { "SUFFIX_END", PM_INTEGER, GSU(suffixend_gsu), NULL },
+ { "SUFFIX_ACTIVE", PM_INTEGER | PM_READONLY, GSU(suffixactive_gsu), NULL },
{ "ZLE_STATE", PM_SCALAR | PM_READONLY, GSU(zle_state_gsu), NULL },
{ NULL, 0, NULL, NULL }
};
@@ -163,6 +187,7 @@ mod_export void
makezleparams(int ro)
{
struct zleparam *zp;
+ Param reg_param;
for(zp = zleparams; zp->name; zp++) {
Param pm = createparam(zp->name, (zp->type |PM_SPECIAL|PM_REMOVABLE|
@@ -188,6 +213,11 @@ makezleparams(int ro)
if ((zp->type & PM_UNSET) && (zmod.flags & (MOD_MULT|MOD_TMULT)))
pm->node.flags &= ~PM_UNSET;
}
+
+ reg_param = createspecialhash("registers", get_registers, &scan_registers,
+ PM_LOCAL|PM_REMOVABLE);
+ reg_param->gsu.h = &registers_gsu;
+ reg_param->level = locallevel + 1;
}
/* Special unset function for ZLE special parameters: act like the standard *
@@ -521,6 +551,48 @@ set_yankend(UNUSED(Param pm), zlong i)
}
/**/
+static zlong
+get_isearchmatchstart(UNUSED(Param pm))
+{
+ return isearch_startpos;
+}
+
+/**/
+static zlong
+get_isearchmatchend(UNUSED(Param pm))
+{
+ return isearch_endpos;
+}
+
+/**/
+static zlong
+get_isearchmatchactive(UNUSED(Param pm))
+{
+ return isearch_active;
+}
+
+/**/
+static zlong
+get_suffixstart(UNUSED(Param pm))
+{
+ return zlecs - suffixlen;
+}
+
+/**/
+static zlong
+get_suffixend(UNUSED(Param pm))
+{
+ return zlecs;
+}
+
+/**/
+static zlong
+get_suffixactive(UNUSED(Param pm))
+{
+ return suffixlen;
+}
+
+/**/
static char *
get_cutbuffer(UNUSED(Param pm))
{
@@ -652,6 +724,111 @@ unset_killring(Param pm, int exp)
}
}
+/**/
+static void
+set_register(Param pm, char *value)
+{
+ int n = 0;
+ int offset = -1;
+ Cutbuffer vbuf;
+
+ if (!pm->node.nam || pm->node.nam[1])
+ ;
+ else if (*pm->node.nam >= '0' && *pm->node.nam <= '9')
+ offset = '0' - 26;
+ else if (*pm->node.nam >= 'a' && *pm->node.nam <= 'z')
+ offset = 'a';
+
+ if (offset == -1) {
+ zerr("invalid zle register: %s", pm->node.nam);
+ return;
+ }
+
+ vbuf = &vibuf[*pm->node.nam - offset];
+ if (*value)
+ vbuf->buf = stringaszleline(value, 0, &n, NULL, NULL);
+ vbuf->len = n;
+}
+
+/**/
+static void
+unset_register(Param pm, UNUSED(int exp))
+{
+ set_register(pm, "");
+}
+
+/**/
+static void
+scan_registers(UNUSED(HashTable ht), ScanFunc func, int flags)
+{
+ int i;
+ char ch;
+ struct param pm;
+
+ memset((void *)&pm, 0, sizeof(struct param));
+ pm.node.flags = PM_SCALAR | PM_READONLY;
+ pm.gsu.s = &nullsetscalar_gsu;
+
+ for (i = 0, ch = 'a'; i < 36; i++) {
+ pm.node.nam = zhalloc(2);
+ *pm.node.nam = ch;
+ pm.node.nam[1] = '\0';
+ pm.u.str = zlelineasstring(vibuf[i].buf, vibuf[i].len, 0, NULL, NULL, 1);
+ func(&pm.node, flags);
+ if (ch++ == 'z')
+ ch = '0';
+ }
+}
+
+/**/
+static HashNode
+get_registers(UNUSED(HashTable ht), const char *name)
+{
+ Param pm = (Param) hcalloc(sizeof(struct param));
+ int vbuf = -1;
+ pm->node.nam = dupstring(name);
+ pm->node.flags = PM_SCALAR;
+ pm->gsu.s = &register_gsu;
+
+ if (name[1])
+ ;
+ else if (*name >= '0' && *name <= '9')
+ vbuf = *name - '0' + 26;
+ else if (*name >= 'a' && *name <= 'z')
+ vbuf = *name - 'a';
+
+ if (vbuf == -1) {
+ pm->u.str = dupstring("");
+ pm->node.flags |= (PM_UNSET|PM_SPECIAL);
+ } else
+ pm->u.str = zlelineasstring(vibuf[vbuf].buf, vibuf[vbuf].len, 0, NULL, NULL, 1);
+
+ return &pm->node;
+}
+
+/**/
+static void
+set_registers(UNUSED(Param pm), HashTable ht)
+{
+ int i;
+ HashNode hn;
+
+ if (!ht)
+ return;
+
+ for (i = 0; i < ht->hsize; i++)
+ for (hn = ht->nodes[i]; hn; hn = hn->next) {
+ struct value v;
+ v.isarr = v.flags = v.start = 0;
+ v.end = -1;
+ v.arr = NULL;
+ v.pm = (Param) hn;
+
+ set_register(v.pm, getstrvalue(&v));
+ }
+ deleteparamtable(ht);
+}
+
static void
set_prepost(ZLE_STRING_T *textvar, int *lenvar, char *x)
{
diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c
index 3d2471e27..8d173cda1 100644
--- a/Src/Zle/zle_refresh.c
+++ b/Src/Zle/zle_refresh.c
@@ -946,7 +946,7 @@ addmultiword(REFRESH_ELEMENT *base, ZLE_STRING_T tptr, int ichars)
/*
* Swap the old and new video buffers, plus any associated multiword
- * buffers. The new buffer becomes the old one; the new new buffer
+ * buffers. The new buffer becomes the old one; the new buffer
* will be filled with the command line next time.
*/
static void
@@ -1143,8 +1143,7 @@ zrefresh(void)
tsetcap(TCALLATTRSOFF, 0);
tsetcap(TCSTANDOUTEND, 0);
tsetcap(TCUNDERLINEEND, 0);
- /* cheat on attribute unset */
- txtunset(TXTBOLDFACE|TXTSTANDOUT|TXTUNDERLINE);
+ txtattrmask = 0;
if (trashedzle && !clearflag)
reexpandprompt();
@@ -2424,6 +2423,7 @@ clearscreen(UNUSED(char **args))
tcoutclear(TCCLEARSCREEN);
resetneeded = 1;
clearflag = 0;
+ reexpandprompt();
return 0;
}
@@ -2434,8 +2434,8 @@ redisplay(UNUSED(char **args))
moveto(0, 0);
zputc(&zr_cr); /* extra care */
tc_upcurs(lprompth - 1);
- resetneeded = 1;
- clearflag = 0;
+ resetneeded = !showinglist;
+ clearflag = showinglist;
return 0;
}
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index 21495b6f2..c7092854a 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -269,7 +269,7 @@ freewidget(Widget w)
zfree(w, sizeof(*w));
}
-/* Add am internal widget provided by a module. The name given is the *
+/* Add an internal widget provided by a module. The name given is the *
* canonical one, which must not begin with a dot. The widget is first *
* bound to the dotted canonical name; if that name is already taken by *
* an internal widget, failure is indicated. The same widget is then *
@@ -678,7 +678,16 @@ bin_zle_flags(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
else if (!strcmp(*flag, "keepsuffix"))
w->flags |= ZLE_KEEPSUFFIX;
*/
- else {
+ else if (!strcmp(*flag, "vichange")) {
+ if (invicmdmode()) {
+ startvichange(-1);
+ if (zmod.flags & (MOD_MULT|MOD_TMULT)) {
+ Param pm = (Param) paramtab->getnode(paramtab, "NUMERIC");
+ if (pm && pm->node.flags & PM_SPECIAL)
+ pm->node.flags &= ~PM_UNSET;
+ }
+ }
+ } else {
zwarnnam(name, "invalid flag `%s' given to zle -f", *flag);
ret = 1;
}
@@ -756,6 +765,10 @@ bin_zle_call(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
}
t = rthingy(wname);
+ /* for internal widgets we set bindk except for when getting
+ * a vi range to detect a repeated key */
+ setbindk = setbindk ||
+ (t->widget && (t->widget->flags & (WIDGET_INT | ZLE_VIOPER)) == WIDGET_INT);
ret = execzlefunc(t, args, setbindk);
unrefthingy(t);
if (saveflag)
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index cc4b7d673..3d8679119 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -100,8 +100,7 @@ mod_export int usemenu, useglob;
/**/
mod_export int wouldinstab;
-/* != 0 if we are in the middle of a menu completion. May be == 2 to force *
- * menu completion even if using different widgets. */
+/* != 0 if we are in the middle of a menu completion. */
/**/
mod_export int menucmp;
@@ -425,8 +424,8 @@ mod_export int instring, inbackt;
* This uses the instring variable above.
*/
-#define quotename(s, e) \
-quotestring(s, e, instring == QT_NONE ? QT_BACKSLASH : instring)
+#define quotename(s) \
+quotestring(s, instring == QT_NONE ? QT_BACKSLASH : instring)
/* Check if the given string is the name of a parameter and if this *
* parameter is one worth expanding. */
@@ -710,7 +709,8 @@ docomplete(int lst)
for (t0 = cmdnamtab->hsize - 1; t0 >= 0; t0--)
for (hn = cmdnamtab->nodes[t0]; hn;
hn = hn->next) {
- if (strpfx(q, hn->nam) && findcmd(hn->nam, 0))
+ if (strpfx(q, hn->nam) &&
+ findcmd(hn->nam, 0, 0))
n++;
if (n == 2)
break;
@@ -1162,6 +1162,7 @@ get_comp_string(void)
inpush(dupstrspace(linptr), 0, NULL);
strinbeg(0);
wordpos = cp = rd = ins = oins = linarr = parct = ia = redirpos = 0;
+ we = wb = zlemetacs;
tt0 = NULLTOK;
/* This loop is possibly the wrong way to do this. It goes through *
@@ -1239,6 +1240,20 @@ get_comp_string(void)
/* Record if we haven't had the command word yet */
if (wordpos == redirpos)
redirpos++;
+ if (zlemetacs < (zlemetall - inbufct) &&
+ zlemetacs >= wordbeg && wb == we) {
+ /* Cursor is in the middle of a redirection, treat as a word */
+ we = zlemetall - (inbufct + addedx);
+ if (addedx && we > wb) {
+ /* Assume we are in {param}> form, wb points at "{" */
+ wb++;
+ /* Should complete parameter names here */
+ } else {
+ /* In "2>" form, zlemetacs points at "2" */
+ wb = zlemetacs;
+ /* Should insert a space under cursor here */
+ }
+ }
}
if (tok == DINPAR)
tokstr = NULL;
@@ -1290,8 +1305,12 @@ get_comp_string(void)
zsfree(cmdstr);
cmdstr = ztrdup(tokstr);
cmdtok = tok;
- /* If everything before is a redirection, don't reset the index */
- if (wordpos != redirpos)
+ /*
+ * If everything before is a redirection, or anything
+ * complicated enough that we've seen the word the
+ * cursor is on, don't reset the index.
+ */
+ if (wordpos != redirpos && clwpos == -1)
wordpos = redirpos = 0;
} else if (tok == SEPER) {
/*
@@ -1399,9 +1418,17 @@ get_comp_string(void)
/* If this is the word the cursor is in and we added a `x', *
* remove it. */
if (clwpos == wordpos++ && addedx) {
+ int chuck_at, word_diff;
zlemetacs_qsub = zlemetacs - qsub;
- chuck(&clwords[wordpos - 1][((zlemetacs_qsub - wb) >= sl) ?
- (sl - 1) : (zlemetacs_qsub - wb)]);
+ word_diff = zlemetacs_qsub - wb;
+ /* Ensure we chuck within the word... */
+ if (word_diff >= sl)
+ chuck_at = sl -1;
+ else if (word_diff < 0)
+ chuck_at = 0;
+ else
+ chuck_at = word_diff;
+ chuck(&clwords[wordpos - 1][chuck_at]);
}
} while (tok != LEXERR && tok != ENDINPUT &&
(tok != SEPER || (lexflags && tt0 == NULLTOK)));
@@ -1449,7 +1476,9 @@ get_comp_string(void)
t0 = STRING;
} else if (t0 == STRING || t0 == TYPESET) {
/* We found a simple string. */
- s = ztrdup(clwords[clwpos]);
+ s = clwords[clwpos];
+ DPUTS(!s, "Completion word has disappeared!");
+ s = ztrdup(s ? s : "");
} else if (t0 == ENVSTRING) {
char sav;
/* The cursor was inside a parameter assignment. */
@@ -1850,8 +1879,12 @@ get_comp_string(void)
ocs = zlemetacs;
zlemetacs = i;
foredel(skipchars, CUT_RAW);
- if ((zlemetacs = ocs) > --i)
+ if ((zlemetacs = ocs) > --i) {
zlemetacs -= skipchars;
+ /* do not skip past the beginning of the word */
+ if (wb > zlemetacs)
+ zlemetacs = wb;
+ }
we -= skipchars;
}
} else {
@@ -1862,6 +1895,9 @@ get_comp_string(void)
zlemetacs = we - skipchars;
else
zlemetacs = ocs;
+ /* do not skip past the beginning of the word */
+ if (wb > zlemetacs)
+ zlemetacs = wb;
we -= skipchars;
}
/* we need to get rid of all the quotation bits... */
@@ -1983,11 +2019,11 @@ get_comp_string(void)
new->next = NULL;
new->str = dupstrpfx(bbeg, len);
- new->str = ztrdup(quotename(new->str, NULL));
+ new->str = ztrdup(quotename(new->str));
untokenize(new->str);
new->pos = begi;
*dbeg = '\0';
- new->qpos = strlen(quotename(predup, NULL));
+ new->qpos = strlen(quotename(predup));
*dbeg = '{';
i -= len;
boffs -= len;
@@ -2046,11 +2082,11 @@ get_comp_string(void)
lastbrbeg = new;
new->str = dupstrpfx(bbeg, len);
- new->str = ztrdup(quotename(new->str, NULL));
+ new->str = ztrdup(quotename(new->str));
untokenize(new->str);
new->pos = begi;
*dbeg = '\0';
- new->qpos = strlen(quotename(predup, NULL));
+ new->qpos = strlen(quotename(predup));
*dbeg = '{';
i -= len;
boffs -= len;
@@ -2096,7 +2132,7 @@ get_comp_string(void)
brend = new;
new->str = dupstrpfx(bbeg, len);
- new->str = ztrdup(quotename(new->str, NULL));
+ new->str = ztrdup(quotename(new->str));
untokenize(new->str);
new->pos = dp - predup - len + 1;
new->qpos = len;
@@ -2125,11 +2161,11 @@ get_comp_string(void)
lastbrbeg = new;
new->str = dupstrpfx(bbeg, len);
- new->str = ztrdup(quotename(new->str, NULL));
+ new->str = ztrdup(quotename(new->str));
untokenize(new->str);
new->pos = begi;
*dbeg = '\0';
- new->qpos = strlen(quotename(predup, NULL));
+ new->qpos = strlen(quotename(predup));
*dbeg = '{';
boffs -= len;
memmove(dbeg, dbeg + len, 1+strlen(dbeg+len));
@@ -2144,7 +2180,7 @@ get_comp_string(void)
p = bp->pos;
l = bp->qpos;
bp->pos = strlen(predup + p + l);
- bp->qpos = strlen(quotename(predup + p + l, NULL));
+ bp->qpos = strlen(quotename(predup + p + l));
memmove(predup + p, predup + p + l, 1+bp->pos);
}
}
@@ -2267,7 +2303,7 @@ doexpansion(char *s, int lst, int olst, int explincmd)
foredel(we - wb, CUT_RAW);
while ((ss = (char *)ugetnode(vl))) {
ret = 0;
- ss = quotename(ss, NULL);
+ ss = quotename(ss);
untokenize(ss);
inststr(ss);
if (nonempty(vl) || !first) {
@@ -2976,7 +3012,7 @@ processcmd(UNUSED(char **args))
inststr(" ");
untokenize(s);
- inststr(quotename(s, NULL));
+ inststr(quotename(s));
zsfree(s);
done = 1;
@@ -3006,7 +3042,7 @@ expandcmdpath(UNUSED(char **args))
return 1;
}
- str = findcmd(s, 1);
+ str = findcmd(s, 1, 0);
zsfree(s);
if (!str)
return 1;
diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c
index 6e9a98bde..c6df3d89c 100644
--- a/Src/Zle/zle_utils.c
+++ b/Src/Zle/zle_utils.c
@@ -587,7 +587,7 @@ struct zle_position {
};
/* LIFO stack of positions */
-struct zle_position *zle_positions;
+static struct zle_position *zle_positions;
/*
* Save positions including cursor, end-of-line and
@@ -1412,7 +1412,7 @@ zlong undo_changeno;
/* If positive, don't undo beyond this point */
-zlong undo_limitno;
+static zlong undo_limitno;
/**/
void
@@ -1589,9 +1589,14 @@ undo(char **args)
break;
if (prev->changeno <= undo_limitno && !*args)
return 1;
- if (!unapplychange(prev) && last_change >= 0)
- unapplychange(prev);
- curchange = prev;
+ if (!unapplychange(prev)) {
+ if (last_change >= 0) {
+ unapplychange(prev);
+ curchange = prev;
+ }
+ } else {
+ curchange = prev;
+ }
} while (last_change >= (zlong)0 || (curchange->flags & CH_PREV));
setlastline();
return 0;
@@ -1678,7 +1683,7 @@ viundochange(char **args)
/**/
int
-splitundo(char **args)
+splitundo(UNUSED(char **args))
{
if (vistartchange >= 0) {
mergeundo();
@@ -1699,6 +1704,7 @@ mergeundo(void)
current->flags |= CH_PREV;
current->prev->flags |= CH_NEXT;
}
+ vistartchange = -1;
}
/*
@@ -1720,6 +1726,8 @@ zlecallhook(char *name, char *arg)
if (!thingy)
return;
+ /* If anything here needs changing, see also redrawhook() */
+
saverrflag = errflag;
savretflag = retflag;
diff --git a/Src/Zle/zle_vi.c b/Src/Zle/zle_vi.c
index 86840bdd6..e0923db3e 100644
--- a/Src/Zle/zle_vi.c
+++ b/Src/Zle/zle_vi.c
@@ -45,61 +45,71 @@ int wordflag;
/**/
int vilinerange;
-/* last vi change buffer, for vi change repetition */
+/*
+ * lastvichg: last vi change buffer, for vi change repetition
+ * curvichg: current incomplete vi change
+ */
+
+/**/
+struct vichange lastvichg, curvichg;
+
+/*
+ * true whilst a vi change is active causing keys to be
+ * accumulated in curvichg.buf
+ * first set to 2 and when the initial widget finishes, reduced to 1 if
+ * in insert mode implying that the change continues until returning to
+ * normal mode
+ */
/**/
-int vichgbufsz, vichgbufptr, vichgflag;
+int vichgflag;
/*
- * Examination of the code suggests vichgbuf is consistently tied
- * to raw byte input, so it is left as a character array rather
- * than turned into wide characters. In particular, when we replay
- * it we use ungetbytes().
+ * analogous to vichgflag for a repeated change with the value following
+ * a similar pattern (is 3 until first repeated widget starts)
*/
+
/**/
-char *vichgbuf;
+int viinrepeat;
/* point where vi insert mode was last entered */
/**/
int viinsbegin;
-static struct modifier lastmod;
-static int inrepeat, vichgrepeat;
-
/**
* im: >= 0: is an insertmode
- * -1: skip setting insert mode
+ * -1: skip setting insert/overwrite mode
* -2: entering viins at start of editing from clean --- don't use
- * inrepeat or lastchar, synthesise an i to enter insert mode.
+ * inrepeat or keybuf, synthesise an entry to insert mode.
+ * Note that zmult is updated so this should be called before zmult is used.
*/
/**/
void
startvichange(int im)
{
- if (im != -1) {
- vichgflag = 1;
- if (im > -1)
- insmode = im;
- }
- if (inrepeat && im != -2) {
- zmod = lastmod;
- inrepeat = vichgflag = 0;
- vichgrepeat = 1;
- } else {
- lastmod = zmod;
- if (vichgbuf)
- free(vichgbuf);
- vichgbuf = (char *)zalloc(vichgbufsz = 16);
+ if (im > -1)
+ insmode = im;
+ if (viinrepeat && im != -2) {
+ zmod = lastvichg.mod;
+ vichgflag = 0;
+ } else if (!vichgflag) {
+ curvichg.mod = zmod;
+ if (curvichg.buf)
+ free(curvichg.buf);
+ curvichg.buf = (char *)zalloc(curvichg.bufsz = 16 + keybuflen);
if (im == -2) {
- vichgbuf[0] =
+ vichgflag = 1;
+ curvichg.buf[0] =
zlell ? (insmode ? (zlecs < zlell ? 'i' : 'a') : 'R') : 'o';
+ curvichg.buf[1] = '\0';
+ curvichg.bufptr = 1;
} else {
- vichgbuf[0] = lastchar;
+ vichgflag = 2;
+ strcpy(curvichg.buf, keybuf);
+ unmetafy(curvichg.buf, &curvichg.bufptr);
}
- vichgbufptr = 1;
- vichgrepeat = 0;
}
}
@@ -208,10 +218,13 @@ getvirange(int wf)
*/
if ((k2 == bindk) ? dovilinerange() : execzlefunc(k2, zlenoargs, 1))
ret = -1;
- if(vichgrepeat)
+ if (viinrepeat)
zmult = mult1;
- else
+ else {
zmult = mult1 * zmod.tmult;
+ if (vichgflag == 2)
+ curvichg.mod.mult = zmult;
+ }
} while(prefixflag && !ret);
wordflag = 0;
selectlocalmap(NULL);
@@ -255,7 +268,7 @@ getvirange(int wf)
}
/* visual selection mode needs to include additional position */
- if (visual == 1 && invicmdmode())
+ if (visual == 1 && pos < zlell && invicmdmode())
INCPOS(pos);
/* Was it a line-oriented move? If so, the command will have set *
@@ -383,7 +396,6 @@ videlete(UNUSED(char **args))
vifirstnonblank(zlenoargs);
}
}
- vichgflag = 0;
return ret;
}
@@ -391,9 +403,10 @@ videlete(UNUSED(char **args))
int
videletechar(char **args)
{
- int n = zmult;
+ int n;
startvichange(-1);
+ n = zmult;
/* handle negative argument */
if (n < 0) {
@@ -440,9 +453,10 @@ vichange(UNUSED(char **args))
int
visubstitute(UNUSED(char **args))
{
- int n = zmult;
+ int n;
startvichange(1);
+ n = zmult;
if (n < 0)
return 1;
/* it is an error to be on the end of line */
@@ -498,7 +512,6 @@ viyank(UNUSED(char **args))
cut(zlecs, c2 - zlecs, CUT_YANK);
ret = 0;
}
- vichgflag = 0;
/* cursor now at the start of the range yanked. For line mode
* restore the column position */
if (vilinerange && lastcol != -1) {
@@ -536,9 +549,10 @@ int
viyankwholeline(UNUSED(char **args))
{
int bol = findbol(), oldcs = zlecs;
- int n = zmult;
+ int n;
startvichange(-1);
+ n = zmult;
if (n < 1)
return 1;
while(n--) {
@@ -572,16 +586,17 @@ vireplace(UNUSED(char **args))
* a change, we always read the argument normally, even if the count *
* was bad. When recording a change for repeating, and a bad count is *
* given, we squash the repeat buffer to avoid repeating the partial *
- * command; we've lost the previous change, but that can't be avoided *
- * without a rewrite of the repeat code. */
+ * command. */
/**/
int
vireplacechars(UNUSED(char **args))
{
ZLE_INT_T ch;
- int n = zmult, fail = 0, newchars = 0;
+ int n, fail = 0, newchars = 0;
+ startvichange(1);
+ n = zmult;
if (n > 0) {
if (region_active) {
int a, b;
@@ -618,21 +633,15 @@ vireplacechars(UNUSED(char **args))
n = pos - zlecs;
}
}
- startvichange(1);
+
/* check argument range */
if (n < 1 || fail) {
- if(vichgrepeat)
+ if (viinrepeat)
vigetkey();
- if(vichgflag) {
- free(vichgbuf);
- vichgbuf = NULL;
- vichgflag = 0;
- }
return 1;
}
/* get key */
if((ch = vigetkey()) == ZLEEOF) {
- vichgflag = 0;
return 1;
}
/* do change */
@@ -659,7 +668,6 @@ vireplacechars(UNUSED(char **args))
zleline[zlecs++] = ch;
zlecs--;
}
- vichgflag = 0;
return 0;
}
@@ -670,7 +678,16 @@ vicmdmode(UNUSED(char **args))
if (invicmdmode() || selectkeymap("vicmd", 0))
return 1;
mergeundo();
- vichgflag = 0;
+ insmode = unset(OVERSTRIKE);
+ if (vichgflag == 1) {
+ vichgflag = 0;
+ if (lastvichg.buf)
+ free(lastvichg.buf);
+ lastvichg = curvichg;
+ curvichg.buf = NULL;
+ }
+ if (viinrepeat == 1)
+ viinrepeat = 0;
if (zlecs != findbol())
DECCS();
return 0;
@@ -725,7 +742,50 @@ vioperswapcase(UNUSED(char **args))
vifirstnonblank();
#endif
}
- vichgflag = 0;
+ return ret;
+}
+
+/**/
+int
+viupcase(UNUSED(char **args))
+{
+ int oldcs, c2, ret = 1;
+
+ /* get the range */
+ startvichange(1);
+ if ((c2 = getvirange(0)) != -1) {
+ oldcs = zlecs;
+ /* covert the case of all letters within range */
+ while (zlecs < c2) {
+ zleline[zlecs] = ZC_toupper(zleline[zlecs]);
+ INCCS();
+ }
+ /* go back to the first line of the range */
+ zlecs = oldcs;
+ ret = 0;
+ }
+ return ret;
+}
+
+/**/
+int
+vidowncase(UNUSED(char **args))
+{
+ int oldcs, c2, ret = 1;
+
+ /* get the range */
+ startvichange(1);
+ if ((c2 = getvirange(0)) != -1) {
+ oldcs = zlecs;
+ /* convert the case of all letters within range */
+ while (zlecs < c2) {
+ zleline[zlecs] = ZC_tolower(zleline[zlecs]);
+ INCCS();
+ }
+ /* go back to the first line of the range */
+ zlecs = oldcs;
+ ret = 0;
+ }
return ret;
}
@@ -734,21 +794,23 @@ int
virepeatchange(UNUSED(char **args))
{
/* make sure we have a change to repeat */
- if (!vichgbuf || vichgflag)
+ if (!lastvichg.buf || vichgflag || virangeflag)
return 1;
/* restore or update the saved count and buffer */
if (zmod.flags & MOD_MULT) {
- lastmod.mult = zmod.mult;
- lastmod.flags |= MOD_MULT;
+ lastvichg.mod.mult = zmod.mult;
+ lastvichg.mod.flags |= MOD_MULT;
}
if (zmod.flags & MOD_VIBUF) {
- lastmod.vibuf = zmod.vibuf;
- lastmod.flags = (lastmod.flags & ~MOD_VIAPP) |
+ lastvichg.mod.vibuf = zmod.vibuf;
+ lastvichg.mod.flags = (lastvichg.mod.flags & ~MOD_VIAPP) |
MOD_VIBUF | (zmod.flags & MOD_VIAPP);
- }
+ } else if (lastvichg.mod.flags & MOD_VIBUF &&
+ lastvichg.mod.vibuf >= 27 && lastvichg.mod.vibuf <= 34)
+ lastvichg.mod.vibuf++; /* for "1 to "8 advance to next buffer */
/* repeat the command */
- inrepeat = 1;
- ungetbytes(vichgbuf, vichgbufptr);
+ viinrepeat = 3;
+ ungetbytes(lastvichg.buf, lastvichg.bufptr);
return 0;
}
@@ -764,10 +826,8 @@ viindent(UNUSED(char **args))
region_active = 2;
/* get the range */
if ((c2 = getvirange(0)) == -1) {
- vichgflag = 0;
return 1;
}
- vichgflag = 0;
/* must be a line range */
if (!vilinerange) {
zlecs = oldcs;
@@ -802,10 +862,8 @@ viunindent(UNUSED(char **args))
region_active = 2;
/* get the range */
if ((c2 = getvirange(0)) == -1) {
- vichgflag = 0;
return 1;
}
- vichgflag = 0;
/* must be a line range */
if (!vilinerange) {
zlecs = oldcs;
@@ -828,12 +886,13 @@ viunindent(UNUSED(char **args))
int
vibackwarddeletechar(char **args)
{
- int n = zmult;
+ int n;
if (invicmdmode())
startvichange(-1);
/* handle negative argument */
+ n = zmult;
if (n < 0) {
int ret;
zmult = -n;
@@ -873,10 +932,11 @@ int
vijoin(UNUSED(char **args))
{
int x, pos;
- int n = zmult;
+ int n;
int visual = region_active;
startvichange(-1);
+ n = zmult;
if (n < 1)
return 1;
if (visual && zlecs > mark) {
@@ -915,9 +975,10 @@ vijoin(UNUSED(char **args))
int
viswapcase(UNUSED(char **args))
{
- int eol, n = zmult;
+ int eol, n;
startvichange(-1);
+ n = zmult;
if (n < 1)
return 1;
eol = findeol();
diff --git a/Src/Zle/zle_word.c b/Src/Zle/zle_word.c
index 2e6d75e86..e4a878eab 100644
--- a/Src/Zle/zle_word.c
+++ b/Src/Zle/zle_word.c
@@ -153,7 +153,7 @@ emacsforwardword(char **args)
/**/
int
-viforwardblankwordend(UNUSED(char **args))
+viforwardblankwordend(char **args)
{
int n = zmult;
@@ -678,46 +678,54 @@ killword(char **args)
int
transposewords(UNUSED(char **args))
{
- int p1, p2, p3, p4, len, x = zlecs, pos;
+ int p1, p2, p3, p4, pt, len, x = zlecs, pos;
ZLE_STRING_T temp, pp;
int n = zmult;
int neg = n < 0, ocs = zlecs;
if (neg)
n = -n;
- while (n--) {
- while (x != zlell && zleline[x] != ZWC('\n') && !ZC_iword(zleline[x]))
- INCPOS(x);
- if (x == zlell || zleline[x] == ZWC('\n')) {
- x = zlecs;
- while (x) {
- if (ZC_iword(zleline[x]))
- break;
- pos = x;
- DECPOS(pos);
- if (zleline[pos] == ZWC('\n'))
- break;
- x = pos;
- }
- if (!x)
- return 1;
+
+ while (x != zlell && zleline[x] != ZWC('\n') && !ZC_iword(zleline[x]))
+ INCPOS(x);
+
+ if (x == zlell || zleline[x] == ZWC('\n')) {
+ x = zlecs;
+ while (x) {
+ if (ZC_iword(zleline[x]))
+ break;
pos = x;
DECPOS(pos);
if (zleline[pos] == ZWC('\n'))
- return 1;
- }
- for (p4 = x; p4 != zlell && ZC_iword(zleline[p4]); INCPOS(p4))
- ;
- for (p3 = p4; p3; ) {
- pos = p3;
- DECPOS(pos);
- if (!ZC_iword(zleline[pos]))
break;
- p3 = pos;
+ x = pos;
}
- if (!p3)
+ if (!x)
return 1;
- for (p2 = p3; p2; ) {
+ pos = x;
+ DECPOS(pos);
+ if (zleline[pos] == ZWC('\n'))
+ return 1;
+ }
+
+ for (p4 = x; p4 != zlell && ZC_iword(zleline[p4]); INCPOS(p4))
+ ;
+
+ for (p3 = p4; p3; ) {
+ pos = p3;
+ DECPOS(pos);
+ if (!ZC_iword(zleline[pos]))
+ break;
+ p3 = pos;
+ }
+
+ if (!p3)
+ return 1;
+
+ p1 = p2 = pt = p3;
+
+ while (n--) {
+ for (p2 = pt; p2; ) {
pos = p2;
DECPOS(pos);
if (ZC_iword(zleline[pos]))
@@ -733,20 +741,24 @@ transposewords(UNUSED(char **args))
break;
p1 = pos;
}
- pp = temp = (ZLE_STRING_T)zhalloc((p4 - p1)*ZLE_CHAR_SIZE);
- len = p4 - p3;
- ZS_memcpy(pp, zleline + p3, len);
- pp += len;
- len = p3 - p2;
- ZS_memcpy(pp, zleline + p2, len);
- pp += len;
- ZS_memcpy(pp, zleline + p1, p2 - p1);
+ pt = p1;
+ }
- ZS_memcpy(zleline + p1, temp, p4 - p1);
+ pp = temp = (ZLE_STRING_T)zhalloc((p4 - p1)*ZLE_CHAR_SIZE);
+ len = p4 - p3;
+ ZS_memcpy(pp, zleline + p3, len);
+ pp += len;
+ len = p3 - p2;
+ ZS_memcpy(pp, zleline + p2, len);
+ pp += len;
+ ZS_memcpy(pp, zleline + p1, p2 - p1);
+
+ ZS_memcpy(zleline + p1, temp, p4 - p1);
- zlecs = p4;
- }
if (neg)
zlecs = ocs;
+ else
+ zlecs = p4;
+
return 0;
}
diff --git a/Src/builtin.c b/Src/builtin.c
index dd20f9eab..e641a97a1 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -249,7 +249,7 @@ int
execbuiltin(LinkList args, LinkList assigns, Builtin bn)
{
char *pp, *name, *optstr;
- int flags, sense, argc, execop, xtr = isset(XTRACE);
+ int flags, argc, execop, xtr = isset(XTRACE);
struct options ops;
/* initialise options structure */
@@ -294,6 +294,7 @@ execbuiltin(LinkList args, LinkList assigns, Builtin bn)
/* Sort out the options. */
if (optstr) {
char *arg = *argv;
+ int sense; /* 1 for -x, 0 for +x */
/* while arguments look like options ... */
while (arg &&
/* Must begin with - or maybe + */
@@ -387,7 +388,7 @@ execbuiltin(LinkList args, LinkList assigns, Builtin bn)
if (*arg) {
if(*arg == Meta)
*++arg ^= 32;
- zwarnnam(name, "bad option: -%c", *arg);
+ zwarnnam(name, "bad option: %c%c", "+-"[sense], *arg);
return 1;
}
arg = *++argv;
@@ -973,7 +974,7 @@ cd_do_chdir(char *cnam, char *dest, int hard)
* Normalize path under Cygwin to avoid messing with
* DOS style names with drives in them
*/
- static char buf[PATH_MAX];
+ static char buf[PATH_MAX+1];
#ifdef HAVE_CYGWIN_CONV_PATH
cygwin_conv_path(CCP_WIN_A_TO_POSIX | CCP_RELATIVE, dest, buf,
PATH_MAX);
@@ -1273,7 +1274,23 @@ fixdir(char *src)
#ifdef __CYGWIN__
char *s0 = src;
#endif
- int ret = 0;
+ /* This function is always called with n path containing at
+ * least one slash, either because one was input by the user or
+ * because the caller has prepended either pwd or a cdpath dir.
+ * If asked to make a relative change and pwd is set to ".",
+ * the current directory has been removed out from under us,
+ * so force links to be chased.
+ *
+ * Ordinarily we can't get here with "../" as the first component
+ * but handle the silly special case of ".." in cdpath.
+ *
+ * Order of comparisons here looks funny, but it short-circuits
+ * most rapidly in the event of a false condition. Set to 2
+ * here so we still obey the (lack of) CHASEDOTS option after
+ * the first "../" is preserved (test chasedots > 1 below).
+ */
+ int chasedots = (src[0] == '.' && pwd[0] == '.' && pwd[1] == '\0' &&
+ (src[1] == '/' || (src[1] == '.' && src[2] == '/'))) * 2;
/*** if have RFS superroot directory ***/
#ifdef HAVE_SUPERROOT
@@ -1305,12 +1322,12 @@ fixdir(char *src)
while (dest > d0 + 1 && dest[-1] == '/')
dest--;
*dest = '\0';
- return ret;
+ return chasedots;
}
if (src[0] == '.' && src[1] == '.' &&
(src[2] == '\0' || src[2] == '/')) {
- if (isset(CHASEDOTS)) {
- ret = 1;
+ if (isset(CHASEDOTS) || chasedots > 1) {
+ chasedots = 1;
/* and treat as normal path segment */
} else {
if (dest > d0 + 1) {
@@ -1348,6 +1365,7 @@ fixdir(char *src)
dest[-1] = *src++ ^ 32;
}
}
+ /* unreached */
}
/**/
@@ -1472,6 +1490,7 @@ bin_fc(char *nam, char **argv, Options ops, int func)
}
if (zleactive) {
+ unqueue_signals();
zwarnnam(nam, "no interactive history within ZLE");
return 1;
}
@@ -1610,7 +1629,7 @@ bin_fc(char *nam, char **argv, Options ops, int func)
unqueue_signals();
if (fcedit(editor, fil)) {
if (stuff(fil))
- zwarnnam("fc", "%e: %s", errno, s);
+ zwarnnam("fc", "%e: %s", errno, fil);
else {
loop(0,1);
retval = lastval;
@@ -1990,11 +2009,12 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
* handled in createparam(). Here we just avoid using it for the
* present tests if it's unset.
*
- * POSIXBUILTINS horror: we need to retain the 'readonly' flag
- * of an unset parameter.
+ * POSIXBUILTINS horror: we need to retain the 'readonly' or 'export'
+ * flags of an unset parameter.
*/
usepm = pm && (!(pm->node.flags & PM_UNSET) ||
- (isset(POSIXBUILTINS) && (pm->node.flags & PM_READONLY)));
+ (isset(POSIXBUILTINS) &&
+ (pm->node.flags & (PM_READONLY|PM_EXPORTED))));
/*
* We need to compare types with an existing pm if special,
@@ -2117,7 +2137,8 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
/*
* Stricter rules about retaining readonly attribute in this case.
*/
- if ((on & PM_READONLY) && (!usepm || (pm->node.flags & PM_UNSET)) &&
+ if ((on & (PM_READONLY|PM_EXPORTED)) &&
+ (!usepm || (pm->node.flags & PM_UNSET)) &&
!ASG_VALUEP(asg))
on |= PM_UNSET;
else if (usepm && (pm->node.flags & PM_READONLY) &&
@@ -2125,6 +2146,10 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
zerr("read-only variable: %s", pm->node.nam);
return NULL;
}
+ /* This is handled by createparam():
+ if (usepm && (pm->node.flags & PM_EXPORTED) && !(off & PM_EXPORTED))
+ on |= PM_EXPORTED;
+ */
}
/*
@@ -2266,6 +2291,10 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
zerrnam(cname, "%s: restricted", pname);
return pm;
}
+ if (pm->node.flags & PM_SINGLE) {
+ zerrnam(cname, "%s: can only have a single instance", pname);
+ return pm;
+ }
/*
* For specials, we keep the same struct but zero everything.
* Maybe it would be easier to create a new struct but copy
@@ -2787,6 +2816,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
return 0;
}
if (off & PM_TIED) {
+ unqueue_signals();
zerrnam(name, "use unset to remove tied variables");
return 1;
}
@@ -3117,6 +3147,7 @@ bin_functions(char *name, char **argv, Options ops, int func)
queue_signals();
for (q = mathfuncs; q; q = q->next) {
if (!strcmp(q->name, funcname)) {
+ unqueue_signals();
zwarnnam(name, "-M %s: function already exists",
funcname);
zsfree(p->name);
@@ -3142,15 +3173,33 @@ bin_functions(char *name, char **argv, Options ops, int func)
queue_signals();
if (OPT_MINUS(ops,'X')) {
- if ((shf = (Shfunc) shfunctab->getnode(shfunctab, scriptname))) {
- DPUTS(!shf->funcdef,
- "BUG: Calling autoload from empty function");
+ Funcstack fs;
+ char *funcname = NULL;
+ for (fs = funcstack; fs; fs = fs->prev) {
+ if (fs->tp == FS_FUNC) {
+ /*
+ * dupstring here is paranoia but unlikely to be
+ * problematic
+ */
+ funcname = dupstring(fs->name);
+ break;
+ }
+ }
+ if (!funcname)
+ {
+ zerrnam(name, "bad autoload");
+ ret = 1;
} else {
- shf = (Shfunc) zshcalloc(sizeof *shf);
- shfunctab->addnode(shfunctab, ztrdup(scriptname), shf);
+ if ((shf = (Shfunc) shfunctab->getnode(shfunctab, funcname))) {
+ DPUTS(!shf->funcdef,
+ "BUG: Calling autoload from empty function");
+ } else {
+ shf = (Shfunc) zshcalloc(sizeof *shf);
+ shfunctab->addnode(shfunctab, ztrdup(funcname), shf);
+ }
+ shf->node.flags = on;
+ ret = eval_autoload(shf, funcname, ops, func);
}
- shf->node.flags = on;
- ret = eval_autoload(shf, scriptname, ops, func);
} else {
if (OPT_ISSET(ops,'U') && !OPT_ISSET(ops,'u'))
on &= ~PM_UNDEFINED;
@@ -3332,15 +3381,24 @@ bin_unset(char *name, char **argv, Options ops, int func)
/* do not glob -- unset the given parameter */
queue_signals();
while ((s = *argv++)) {
- char *ss = strchr(s, '[');
- char *sse = ss;
+ char *ss = strchr(s, '['), *subscript = 0;
if (ss) {
- if (skipparens('[', ']', &sse) || *sse) {
- zerrnam(name, "%s: invalid parameter name", s);
- returnval = 1;
- continue;
- }
+ char *sse;
*ss = 0;
+ if ((sse = parse_subscript(ss+1, 1, ']'))) {
+ *sse = 0;
+ subscript = dupstring(ss+1);
+ *sse = ']';
+ remnulargs(subscript);
+ untokenize(subscript);
+ }
+ }
+ if ((ss && !subscript) || !isident(s)) {
+ if (ss)
+ *ss = '[';
+ zerrnam(name, "%s: invalid parameter name", s);
+ returnval = 1;
+ continue;
}
pm = (Param) (paramtab == realparamtab ?
/* getnode2() to avoid autoloading */
@@ -3358,11 +3416,8 @@ bin_unset(char *name, char **argv, Options ops, int func)
} else if (ss) {
if (PM_TYPE(pm->node.flags) == PM_HASHED) {
HashTable tht = paramtab;
- if ((paramtab = pm->gsu.h->getfn(pm))) {
- *--sse = 0;
- unsetparam(ss+1);
- *sse = ']';
- }
+ if ((paramtab = pm->gsu.h->getfn(pm)))
+ unsetparam(subscript);
paramtab = tht;
} else if (PM_TYPE(pm->node.flags) == PM_SCALAR ||
PM_TYPE(pm->node.flags) == PM_ARRAY) {
@@ -3382,7 +3437,7 @@ bin_unset(char *name, char **argv, Options ops, int func)
} else {
/* start is after the element for reverse index */
int start = vbuf.start - !!(vbuf.flags & VALFLAG_INV);
- if (start < arrlen(vbuf.pm->u.arr)) {
+ if (arrlen_gt(vbuf.pm->u.arr, start)) {
char *arr[2];
arr[0] = "";
arr[1] = 0;
@@ -3609,12 +3664,23 @@ bin_whence(char *nam, char **argv, Options ops, int func)
}
}
if (!informed && (wd || v || csh)) {
+ /* this is information and not an error so, as in csh, use stdout */
zputs(*argv, stdout);
puts(wd ? ": none" : " not found");
returnval = 1;
}
popheap();
- } else if ((cnam = findcmd(*argv, 1))) {
+ } else if (func == BIN_COMMAND && OPT_ISSET(ops,'p') &&
+ (hn = builtintab->getnode(builtintab, *argv))) {
+ /*
+ * Special case for "command -p[vV]" which needs to
+ * show a builtin in preference to an external command.
+ */
+ builtintab->printnode(hn, printflags);
+ informed = 1;
+ } else if ((cnam = findcmd(*argv, 1,
+ func == BIN_COMMAND &&
+ OPT_ISSET(ops,'p')))) {
/* Found external command. */
if (wd) {
printf("%s: command\n", *argv);
@@ -3628,7 +3694,7 @@ bin_whence(char *nam, char **argv, Options ops, int func)
}
informed = 1;
} else {
- /* Not found at all. */
+ /* Not found at all. That's not an error as such so this goes to stdout */
if (v || csh || wd)
zputs(*argv, stdout), puts(wd ? ": none" : " not found");
returnval = 1;
@@ -3724,6 +3790,7 @@ bin_hash(char *name, char **argv, Options ops, UNUSED(int func))
if (!(asg = getasg(&argv, NULL))) {
zwarnnam(name, "bad assignment");
returnval = 1;
+ break;
} else if (ASG_VALUEP(asg)) {
if(isset(RESTRICTED)) {
zwarnnam(name, "restricted: %s", asg->value.scalar);
@@ -4021,10 +4088,11 @@ bin_print(char *name, char **args, Options ops, int func)
{
int flen, width, prec, type, argc, n, narg, curlen = 0;
int nnl = 0, fmttrunc = 0, ret = 0, maxarg = 0, nc = 0;
- int flags[6], *len;
+ int flags[6], *len, visarr = 0;
char *start, *endptr, *c, *d, *flag, *buf = NULL, spec[14], *fmt = NULL;
char **first, **argp, *curarg, *flagch = "'0+- #", save = '\0', nullstr = '\0';
- size_t rcount, count = 0;
+ size_t rcount = 0, count = 0;
+ size_t *cursplit, *splits = 0;
FILE *fout = stdout;
#ifdef HAVE_OPEN_MEMSTREAM
size_t mcount;
@@ -4056,7 +4124,7 @@ bin_print(char *name, char **args, Options ops, int func)
return 1; \
} \
unlink(tmpf); \
- if ((fout = fdopen(tempfd, "w+")) == NULL) { \
+ if ((FOUT = fdopen(tempfd, "w+")) == NULL) { \
close(tempfd); \
zwarnnam(name, "can't open temp file: %e", errno); \
return 1; \
@@ -4485,6 +4553,7 @@ bin_print(char *name, char **args, Options ops, int func)
short *words;
if (nwords > 1) {
zwarnnam(name, "option -S takes a single argument");
+ unqueue_signals();
return 1;
}
words = NULL;
@@ -4554,7 +4623,8 @@ bin_print(char *name, char **args, Options ops, int func)
OPT_ISSET(ops,'N') ? '\0' : ' ', fout);
}
}
- if (!(OPT_ISSET(ops,'n') || OPT_ISSET(ops, 'v') || nnl))
+ if (!(OPT_ISSET(ops,'n') || nnl ||
+ (OPT_ISSET(ops, 'v') && !OPT_ISSET(ops, 'l'))))
fputc(OPT_ISSET(ops,'N') ? '\0' : '\n', fout);
if (IS_MSTREAM(fout) && (rcount = READ_MSTREAM(buf,fout)) == -1)
ret = 1;
@@ -4581,11 +4651,23 @@ bin_print(char *name, char **args, Options ops, int func)
* special cases of printing to a ZLE buffer or the history, however.
*/
+ if (OPT_ISSET(ops,'v')) {
+ struct value vbuf;
+ char* s = OPT_ARG(ops,'v');
+ Value v = getvalue(&vbuf, &s, 0);
+ visarr = v && PM_TYPE(v->pm->node.flags) == PM_ARRAY;
+ }
/* printf style output */
*spec = '%';
argp = args;
do {
rcount = count;
+ if (argp > args && visarr) { /* reusing format string */
+ if (!splits)
+ cursplit = splits = (size_t *)zhalloc(sizeof(size_t) *
+ (arrlen(args) / (argp - args) + 1));
+ *cursplit++ = count;
+ }
if (maxarg) {
first += maxarg;
argc -= maxarg;
@@ -4712,7 +4794,8 @@ bin_print(char *name, char **args, Options ops, int func)
} else if (idigit(*c)) {
prec = strtoul(c, &endptr, 0);
c = endptr;
- }
+ } else
+ prec = 0;
if (prec >= 0) *d++ = '.', *d++ = '*';
}
@@ -4813,9 +4896,10 @@ bin_print(char *name, char **args, Options ops, int func)
break;
case 'q':
stringval = curarg ?
- quotestring(curarg, NULL, QT_BACKSLASH_SHOWNULL) : &nullstr;
+ quotestring(metafy(curarg, curlen, META_USEHEAP),
+ QT_BACKSLASH_SHOWNULL) : &nullstr;
*d = 's';
- print_val(stringval);
+ print_val(unmetafy(stringval, &curlen));
break;
case 'd':
case 'i':
@@ -4951,18 +5035,30 @@ bin_print(char *name, char **args, Options ops, int func)
if (buf)
free(buf);
} else {
- stringval = metafy(buf, rcount, META_REALLOC);
- if (OPT_ISSET(ops,'z')) {
- zpushnode(bufstack, stringval);
- } else if (OPT_ISSET(ops,'v')) {
- setsparam(OPT_ARG(ops, 'v'), stringval);
+ if (visarr && splits) {
+ char **arrayval = zshcalloc((cursplit - splits + 2) * sizeof(char *));
+ for (;cursplit >= splits; cursplit--) {
+ int start = cursplit == splits ? 0 : cursplit[-1];
+ arrayval[cursplit - splits] =
+ metafy(buf + start, count - start, META_DUP);
+ count = start;
+ }
+ setaparam(OPT_ARG(ops, 'v'), arrayval);
+ free(buf);
} else {
- ent = prepnexthistent();
- ent->node.nam = stringval;
- ent->stim = ent->ftim = time(NULL);
- ent->node.flags = 0;
- ent->words = (short *)NULL;
- addhistnode(histtab, ent->node.nam, ent);
+ stringval = metafy(buf, rcount, META_REALLOC);
+ if (OPT_ISSET(ops,'z')) {
+ zpushnode(bufstack, stringval);
+ } else if (OPT_ISSET(ops,'v')) {
+ setsparam(OPT_ARG(ops, 'v'), stringval);
+ } else {
+ ent = prepnexthistent();
+ ent->node.nam = stringval;
+ ent->stim = ent->ftim = time(NULL);
+ ent->node.flags = 0;
+ ent->words = (short *)NULL;
+ addhistnode(histtab, ent->node.nam, ent);
+ }
}
}
unqueue_signals();
@@ -4999,7 +5095,7 @@ bin_shift(char *name, char **argv, Options ops, UNUSED(int func))
if (*argv) {
for (; *argv; argv++)
if ((s = getaparam(*argv))) {
- if (num > arrlen(s)) {
+ if (arrlen_lt(s, num)) {
zwarnnam(name, "shift count must be <= $#");
ret++;
continue;
@@ -5068,7 +5164,7 @@ bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int fun
zoptind = 1;
optcind = 0;
}
- if(zoptind > arrlen(args))
+ if (arrlen_lt(args, zoptind))
/* no more options */
return 1;
@@ -5340,6 +5436,11 @@ zexit(int val, int from_where)
}
}
lastval = val;
+ /*
+ * Now we are committed to exiting any previous state
+ * is irrelevant. Ensure trap can run.
+ */
+ errflag = intrap = 0;
if (sigtrapped[SIGEXIT])
dotrap(SIGEXIT);
callhookfunc("zshexit", NULL, 1, NULL);
@@ -6531,7 +6632,7 @@ bin_test(char *name, char **argv, UNUSED(Options ops), int func)
for (s = argv; *s; s++);
if (s == argv || strcmp(s[-1], "]")) {
zwarnnam(name, "']' expected");
- return 1;
+ return 2;
}
s[-1] = NULL;
}
@@ -6574,19 +6675,19 @@ bin_test(char *name, char **argv, UNUSED(Options ops), int func)
if (errflag) {
errflag &= ~ERRFLAG_ERROR;
zcontext_restore();
- return 1;
+ return 2;
}
if (!prog || tok == LEXERR) {
zwarnnam(name, tokstr ? "parse error" : "argument expected");
zcontext_restore();
- return 1;
+ return 2;
}
zcontext_restore();
if (*curtestarg) {
zwarnnam(name, "too many arguments");
- return 1;
+ return 2;
}
/* syntax is OK, so evaluate */
diff --git a/Src/compat.c b/Src/compat.c
index 9041c0bed..a2956946f 100644
--- a/Src/compat.c
+++ b/Src/compat.c
@@ -33,7 +33,10 @@
/* Return pointer to first occurence of string t *
* in string s. Return NULL if not present. */
+/**/
#ifndef HAVE_STRSTR
+
+/**/
char *
strstr(const char *s, const char *t)
{
@@ -48,10 +51,15 @@ strstr(const char *s, const char *t)
}
return NULL;
}
+
+/**/
#endif
+/**/
#ifndef HAVE_GETHOSTNAME
+
+/**/
int
gethostname(char *name, size_t namelen)
{
@@ -65,10 +73,15 @@ gethostname(char *name, size_t namelen)
strcpy(name, uts.nodename);
return 0;
}
+
+/**/
#endif
+/**/
#ifndef HAVE_GETTIMEOFDAY
+
+/**/
int
gettimeofday(struct timeval *tv, struct timezone *tz)
{
@@ -76,20 +89,28 @@ gettimeofday(struct timeval *tv, struct timezone *tz)
tv->tv_sec = (long)time((time_t) 0);
return 0;
}
+
+/**/
#endif
/* compute the difference between two calendar times */
+/**/
#ifndef HAVE_DIFFTIME
+
+/**/
double
difftime(time_t t2, time_t t1)
{
return ((double)t2 - (double)t1);
}
+
+/**/
#endif
+/**/
#ifndef HAVE_STRERROR
extern char *sys_errlist[];
@@ -97,11 +118,14 @@ extern char *sys_errlist[];
* error number, and returns a pointer to that string. *
* This is not a particularly robust version of strerror. */
+/**/
char *
strerror(int errnum)
{
return (sys_errlist[errnum]);
}
+
+/**/
#endif
@@ -186,6 +210,7 @@ zpathmax(char *dir)
}
#endif /* 0 */
+/**/
#ifdef HAVE_SYSCONF
/*
* This is replaced by a macro from system.h if not HAVE_SYSCONF.
@@ -230,6 +255,8 @@ zopenmax(void)
return (max_zsh_fd > openmax) ? max_zsh_fd : openmax;
}
+
+/**/
#endif
/*
@@ -270,7 +297,7 @@ zgetdir(struct dirsav *d)
int len;
#endif
- buf = zhalloc(bufsiz = PATH_MAX);
+ buf = zhalloc(bufsiz = PATH_MAX+1);
pos = bufsiz - 1;
buf[pos] = '\0';
strcpy(nbuf, "../");
@@ -439,11 +466,11 @@ zgetcwd(void)
free(cwd);
}
#else
- char *cwdbuf = zalloc(PATH_MAX);
+ char *cwdbuf = zalloc(PATH_MAX+1);
ret = getcwd(cwdbuf, PATH_MAX);
if (ret)
ret = dupstring(ret);
- zfree(cwdbuf, PATH_MAX);
+ zfree(cwdbuf, PATH_MAX+1);
#endif /* GETCWD_CALLS_MALLOC */
}
#endif /* HAVE_GETCWD */
@@ -532,6 +559,7 @@ output64(zlong val)
/**/
#endif /* ZSH_64_BIT_TYPE */
+/**/
#ifndef HAVE_STRTOUL
/*
@@ -569,6 +597,8 @@ output64(zlong val)
* Ignores `locale' stuff. Assumes that the upper and lower case
* alphabets and digits are each contiguous.
*/
+
+/**/
unsigned long
strtoul(nptr, endptr, base)
const char *nptr;
@@ -632,10 +662,26 @@ strtoul(nptr, endptr, base)
*endptr = any ? s - 1 : nptr;
return (acc);
}
+
+/**/
#endif /* HAVE_STRTOUL */
/**/
-#if defined(BROKEN_WCWIDTH) && (defined(__STDC_ISO_10646__) || defined(__APPLE__))
+#ifdef ENABLE_UNICODE9
+#include "./wcwidth9.h"
+
+/**/
+int
+mk_wcwidth(wchar_t ucs)
+{
+ int w = wcwidth9(ucs);
+ if (w < -1)
+ return 1;
+ return w;
+}
+
+/**/
+#elif defined(BROKEN_WCWIDTH) && (defined(__STDC_ISO_10646__) || defined(__APPLE__))
/*
* This is an implementation of wcwidth() and wcswidth() (defined in
diff --git a/Src/cond.c b/Src/cond.c
index c5ab65eea..42e9de30f 100644
--- a/Src/cond.c
+++ b/Src/cond.c
@@ -30,10 +30,11 @@
#include "zsh.mdh"
#include "cond.pro"
-int tracingcond;
+/**/
+int tracingcond; /* updated by execcond() in exec.c */
static char *condstr[COND_MOD] = {
- "!", "&&", "||", "==", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq",
+ "!", "&&", "||", "=", "==", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq",
"-ne", "-lt", "-gt", "-le", "-ge", "=~"
};
@@ -195,7 +196,8 @@ evalcond(Estate state, char *fromtest)
cond_subst(&left, !fromtest);
untokenize(left);
}
- if (ctype <= COND_GE && ctype != COND_STREQ && ctype != COND_STRNEQ) {
+ if (ctype <= COND_GE && ctype != COND_STREQ && ctype != COND_STRDEQ &&
+ ctype != COND_STRNEQ) {
right = ecgetstr(state, EC_DUPTOK, &htok);
if (htok) {
cond_subst(&right, !fromtest);
@@ -207,7 +209,8 @@ evalcond(Estate state, char *fromtest)
fputc(' ',xtrerr);
quotedzputs(left, xtrerr);
fprintf(xtrerr, " %s ", condstr[ctype]);
- if (ctype == COND_STREQ || ctype == COND_STRNEQ) {
+ if (ctype == COND_STREQ || ctype == COND_STRDEQ ||
+ ctype == COND_STRNEQ) {
char *rt = dupstring(ecrawstr(state->prog, state->pc, NULL));
cond_subst(&rt, !fromtest);
quote_tokenized_output(rt, xtrerr);
@@ -286,6 +289,7 @@ evalcond(Estate state, char *fromtest)
switch (ctype) {
case COND_STREQ:
+ case COND_STRDEQ:
case COND_STRNEQ:
{
int test, npat = state->pc[1];
@@ -312,7 +316,7 @@ evalcond(Estate state, char *fromtest)
state->pc += 2;
test = (pprog && pattry(pprog, left));
- return !(ctype == COND_STREQ ? test : !test);
+ return !(ctype == COND_STRNEQ ? !test : test);
}
case COND_STRLT:
return !(strcmp(left, right) < 0);
@@ -347,6 +351,8 @@ evalcond(Estate state, char *fromtest)
return (!S_ISSOCK(dostat(left)));
case 'u':
return (!(dostat(left) & S_ISUID));
+ case 'v':
+ return (!issetvar(left));
case 'w':
return (!doaccess(left, W_OK));
case 'x':
diff --git a/Src/exec.c b/Src/exec.c
index 352615c83..f544a33e7 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -207,7 +207,7 @@ static int (*execfuncs[WC_COUNT-WC_CURSH]) _((Estate, int)) = {
/* structure for command builtin for when it is used with -v or -V */
static struct builtin commandbn =
- BUILTIN(0, 0, bin_whence, 0, -1, BIN_COMMAND, "vV", NULL);
+ BUILTIN("command", 0, bin_whence, 0, -1, BIN_COMMAND, "pvV", NULL);
/* parse string into a list */
@@ -437,7 +437,7 @@ static int
zexecve(char *pth, char **argv, char **newenvp)
{
int eno;
- static char buf[PATH_MAX * 2];
+ static char buf[PATH_MAX * 2+1];
char **eep;
unmetafy(pth, NULL);
@@ -568,11 +568,49 @@ commandnotfound(char *arg0, LinkList args)
Shfunc shf = (Shfunc)
shfunctab->getnode(shfunctab, "command_not_found_handler");
- if (!shf)
- return 127;
+ if (!shf) {
+ lastval = 127;
+ return 1;
+ }
pushnode(args, arg0);
- return doshfunc(shf, args, 1);
+ lastval = doshfunc(shf, args, 1);
+ return 0;
+}
+
+/*
+ * Search the default path for cmd.
+ * pbuf of length plen is the buffer to use.
+ * Return NULL if not found.
+ */
+
+static char *
+search_defpath(char *cmd, char *pbuf, int plen)
+{
+ char *ps = DEFAULT_PATH, *pe = NULL, *s;
+
+ for (ps = DEFAULT_PATH; ps; ps = pe ? pe+1 : NULL) {
+ pe = strchr(ps, ':');
+ if (*ps == '/') {
+ s = pbuf;
+ if (pe) {
+ if (pe - ps >= plen)
+ continue;
+ struncpy(&s, ps, pe-ps);
+ } else {
+ if (strlen(ps) >= plen)
+ continue;
+ strucpy(&s, ps);
+ }
+ *s++ = '/';
+ if ((s - pbuf) + strlen(cmd) >= plen)
+ continue;
+ strucpy(&s, cmd);
+ if (iscom(pbuf))
+ return pbuf;
+ }
+ }
+ return NULL;
}
/* execute an external command */
@@ -582,7 +620,7 @@ static void
execute(LinkList args, int flags, int defpath)
{
Cmdnam cn;
- char buf[MAXCMDLEN], buf2[MAXCMDLEN];
+ char buf[MAXCMDLEN+1], buf2[MAXCMDLEN+1];
char *s, *z, *arg0;
char **argv, **pp, **newenvp = NULL;
int eno = 0, ee;
@@ -663,29 +701,12 @@ execute(LinkList args, int flags, int defpath)
/* for command -p, search the default path */
if (defpath) {
- char *s, pbuf[PATH_MAX];
- char *dptr, *pe, *ps = DEFAULT_PATH;
-
- for(;ps;ps = pe ? pe+1 : NULL) {
- pe = strchr(ps, ':');
- if (*ps == '/') {
- s = pbuf;
- if (pe)
- struncpy(&s, ps, pe-ps);
- else
- strucpy(&s, ps);
- *s++ = '/';
- if ((s - pbuf) + strlen(arg0) >= PATH_MAX)
- continue;
- strucpy(&s, arg0);
- if (iscom(pbuf))
- break;
- }
- }
+ char pbuf[PATH_MAX+1];
+ char *dptr;
- if (!ps) {
+ if (!search_defpath(arg0, pbuf, PATH_MAX)) {
if (commandnotfound(arg0, args) == 0)
- _exit(0);
+ _exit(lastval);
zerr("command not found: %s", arg0);
_exit(127);
}
@@ -700,7 +721,7 @@ execute(LinkList args, int flags, int defpath)
} else {
if ((cn = (Cmdnam) cmdnamtab->getnode(cmdnamtab, arg0))) {
- char nn[PATH_MAX], *dptr;
+ char nn[PATH_MAX+1], *dptr;
if (cn->node.flags & HASHED)
strcpy(nn, cn->u.cmd);
@@ -749,7 +770,7 @@ execute(LinkList args, int flags, int defpath)
if (eno)
zerr("%e: %s", eno, arg0);
else if (commandnotfound(arg0, args) == 0)
- _exit(0);
+ _exit(lastval);
else
zerr("command not found: %s", arg0);
_exit((eno == EACCES || eno == ENOEXEC) ? 126 : 127);
@@ -760,32 +781,40 @@ execute(LinkList args, int flags, int defpath)
/*
* Get the full pathname of an external command.
* If the second argument is zero, return the first argument if found;
- * if non-zero, return the path using heap memory. (RET_IF_COM(X), above).
+ * if non-zero, return the path using heap memory. (RET_IF_COM(X),
+ * above).
+ * If the third argument is non-zero, use the system default path
+ * instead of the current path.
*/
/**/
mod_export char *
-findcmd(char *arg0, int docopy)
+findcmd(char *arg0, int docopy, int default_path)
{
char **pp;
char *z, *s, buf[MAXCMDLEN];
Cmdnam cn;
+ if (default_path)
+ {
+ if (search_defpath(arg0, buf, MAXCMDLEN))
+ return docopy ? dupstring(buf) : arg0;
+ return NULL;
+ }
cn = (Cmdnam) cmdnamtab->getnode(cmdnamtab, arg0);
- if (!cn && isset(HASHCMDS))
+ if (!cn && isset(HASHCMDS) && !isrelative(arg0))
cn = hashcmd(arg0, path);
if ((int) strlen(arg0) > PATH_MAX)
return NULL;
- for (s = arg0; *s; s++)
- if (*s == '/') {
- RET_IF_COM(arg0);
- if (arg0 == s || unset(PATHDIRS)) {
- return NULL;
- }
- break;
+ if ((s = strchr(arg0, '/'))) {
+ RET_IF_COM(arg0);
+ if (arg0 == s || unset(PATHDIRS) || !strncmp(arg0, "./", 2) ||
+ !strncmp(arg0, "../", 3)) {
+ return NULL;
}
+ }
if (cn) {
- char nn[PATH_MAX];
+ char nn[PATH_MAX+1];
if (cn->node.flags & HASHED)
strcpy(nn, cn->u.cmd);
@@ -818,6 +847,11 @@ findcmd(char *arg0, int docopy)
return NULL;
}
+/*
+ * Return TRUE if the given path denotes an executable regular file, or a
+ * symlink to one.
+ */
+
/**/
int
iscom(char *s)
@@ -847,6 +881,11 @@ isreallycom(Cmdnam cn)
return iscom(fullnam);
}
+/*
+ * Return TRUE if the given path contains a dot or dot-dot component
+ * and does not start with a slash.
+ */
+
/**/
int
isrelative(char *s)
@@ -866,7 +905,7 @@ mod_export Cmdnam
hashcmd(char *arg0, char **pp)
{
Cmdnam cn;
- char *s, buf[PATH_MAX];
+ char *s, buf[PATH_MAX+1];
char **pq;
for (; *pp; pp++)
@@ -994,9 +1033,18 @@ entersubsh(int flags)
if ((flags & ESUB_REVERTPGRP) && getpid() == mypgrp)
release_pgrp();
shout = NULL;
- if (!job_control_ok) {
+ if (flags & ESUB_NOMONITOR) {
+ /*
+ * Allowing any form of interactive signalling here is
+ * actively harmful as we are in a context where there is no
+ * control over the process.
+ */
+ signal_ignore(SIGTTOU);
+ signal_ignore(SIGTTIN);
+ signal_ignore(SIGTSTP);
+ } else if (!job_control_ok) {
/*
- * If this process is not goign to be doing job control,
+ * If this process is not going to be doing job control,
* we don't want to do special things with the corresponding
* signals. If it is, we need to keep the special behaviour:
* see note about attachtty() above.
@@ -1190,6 +1238,7 @@ execlist(Estate state, int dont_change_job, int exiting)
}
while (wc_code(code) == WC_LIST && !breaks && !retflag && !errflag) {
int donedebug;
+ int this_noerrexit = 0, this_donetrap = 0;
ltype = WC_LIST_TYPE(code);
csp = cmdsp;
@@ -1273,9 +1322,17 @@ execlist(Estate state, int dont_change_job, int exiting)
goto sublist_done;
}
while (wc_code(code) == WC_SUBLIST) {
+ int isend = (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END);
next = state->pc + WC_SUBLIST_SKIP(code);
if (!oldnoerrexit)
- noerrexit = (WC_SUBLIST_TYPE(code) != WC_SUBLIST_END);
+ noerrexit = !isend;
+ if (WC_SUBLIST_FLAGS(code) & WC_SUBLIST_NOT) {
+ /* suppress errexit for "! this_command" */
+ if (isend)
+ this_noerrexit = 1;
+ /* suppress errexit for ! <list-of-shell-commands> */
+ noerrexit = 1;
+ }
switch (WC_SUBLIST_TYPE(code)) {
case WC_SUBLIST_END:
/* End of sublist; just execute, ignoring status. */
@@ -1305,10 +1362,10 @@ execlist(Estate state, int dont_change_job, int exiting)
/* We've skipped to the end of the list, not executing *
* the final pipeline, so don't perform error handling *
* for this sublist. */
- donetrap = 1;
+ this_donetrap = 1;
goto sublist_done;
} else if (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END) {
- donetrap = 1;
+ this_donetrap = 1;
/*
* Treat this in the same way as if we reached
* the end of the sublist normally.
@@ -1338,10 +1395,10 @@ execlist(Estate state, int dont_change_job, int exiting)
/* We've skipped to the end of the list, not executing *
* the final pipeline, so don't perform error handling *
* for this sublist. */
- donetrap = 1;
+ this_donetrap = 1;
goto sublist_done;
} else if (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END) {
- donetrap = 1;
+ this_donetrap = 1;
/*
* Treat this in the same way as if we reached
* the end of the sublist normally.
@@ -1391,7 +1448,7 @@ sublist_done:
/* Check whether we are suppressing traps/errexit *
* (typically in init scripts) and if we haven't *
* already performed them for this sublist. */
- if (!noerrexit && !donetrap) {
+ if (!noerrexit && !this_noerrexit && !donetrap && !this_donetrap) {
if (sigtrapped[SIGZERR] && lastval) {
dotrap(SIGZERR);
donetrap = 1;
@@ -1561,6 +1618,7 @@ execpline(Estate state, wordcode slcode, int how, int last1)
if (nowait) {
if(!pline_level) {
+ int jobsub;
struct process *pn, *qn;
curjob = newjob;
@@ -1573,6 +1631,20 @@ execpline(Estate state, wordcode slcode, int how, int last1)
if (!jn->procs->next || lpforked == 2) {
jn->gleader = list_pipe_pid;
jn->stat |= STAT_SUBLEADER;
+ /*
+ * Pick up any subjob that's still lying around
+ * as it's now our responsibility.
+ * If we find it we're a SUPERJOB.
+ */
+ for (jobsub = 1; jobsub <= maxjob; jobsub++) {
+ Job jnsub = jobtab + jobsub;
+ if (jnsub->stat & STAT_SUBJOB_ORPHANED) {
+ jn->other = jobsub;
+ jn->stat |= STAT_SUPERJOB;
+ jnsub->stat &= ~STAT_SUBJOB_ORPHANED;
+ jnsub->other = list_pipe_pid;
+ }
+ }
}
for (pn = jobtab[jn->other].procs; pn; pn = pn->next)
if (WIFSTOPPED(pn->status))
@@ -1584,7 +1656,8 @@ execpline(Estate state, wordcode slcode, int how, int last1)
}
jn->stat &= ~(STAT_DONE | STAT_NOPRINT);
- jn->stat |= STAT_STOPPED | STAT_CHANGED | STAT_LOCKED;
+ jn->stat |= STAT_STOPPED | STAT_CHANGED | STAT_LOCKED |
+ STAT_INUSE;
printjob(jn, !!isset(LONGLISTJOBS), 1);
}
else if (newjob != list_pipe_job)
@@ -1627,7 +1700,13 @@ execpline(Estate state, wordcode slcode, int how, int last1)
int synch[2];
struct timeval bgtime;
+ /*
+ * A pipeline with the shell handling the right
+ * hand side was stopped. We'll fork to allow
+ * it to continue.
+ */
if (pipe(synch) < 0 || (pid = zfork(&bgtime)) == -1) {
+ /* Failure */
if (pid < 0) {
close(synch[0]);
close(synch[1]);
@@ -1641,6 +1720,18 @@ execpline(Estate state, wordcode slcode, int how, int last1)
thisjob = newjob;
}
else if (pid) {
+ /*
+ * Parent: job control is here. If the job
+ * started for the RHS of the pipeline is still
+ * around, then its a SUBJOB and the job for
+ * earlier parts of the pipeeline is its SUPERJOB.
+ * The newly forked shell isn't recorded as a
+ * separate job here, just as list_pipe_pid.
+ * If the superjob exits (it may already have
+ * done so, see child branch below), we'll use
+ * list_pipe_pid to form the basis of a
+ * replacement job --- see SUBLEADER code above.
+ */
char dummy;
lpforked =
@@ -1659,7 +1750,9 @@ execpline(Estate state, wordcode slcode, int how, int last1)
jobtab[list_pipe_job].other = newjob;
jobtab[list_pipe_job].stat |= STAT_SUPERJOB;
jn->stat |= STAT_SUBJOB | STAT_NOPRINT;
- jn->other = pid;
+ jn->other = list_pipe_pid; /* see zsh.h */
+ if (hasprocs(list_pipe_job))
+ jn->gleader = jobtab[list_pipe_job].gleader;
}
if ((list_pipe || last1) && hasprocs(list_pipe_job))
killpg(jobtab[list_pipe_job].gleader, SIGSTOP);
@@ -1668,13 +1761,21 @@ execpline(Estate state, wordcode slcode, int how, int last1)
else {
close(synch[0]);
entersubsh(ESUB_ASYNC);
- if (jobtab[list_pipe_job].procs) {
- if (setpgrp(0L, mypgrp = jobtab[list_pipe_job].gleader)
- == -1) {
- setpgrp(0L, mypgrp = getpid());
- }
- } else
- setpgrp(0L, mypgrp = getpid());
+ /*
+ * At this point, we used to attach this process
+ * to the process group of list_pipe_job (the
+ * new superjob) any time that was still available.
+ * That caused problems in at least two
+ * cases because this forked shell was then
+ * suspended with the right hand side of the
+ * pipeline, and the SIGSTOP below suspended
+ * it a second time when it was continued.
+ *
+ * It's therefore not clear entirely why you'd ever
+ * do anything other than the following, but no
+ * doubt we'll find out...
+ */
+ setpgrp(0L, mypgrp = getpid());
close(synch[1]);
kill(getpid(), SIGSTOP);
list_pipe = 0;
@@ -1711,6 +1812,8 @@ execpline(Estate state, wordcode slcode, int how, int last1)
deletejob(jn, 0);
thisjob = pj;
}
+ else
+ unqueue_signals();
if ((slflags & WC_SUBLIST_NOT) && !errflag)
lastval = !lastval;
}
@@ -1728,6 +1831,7 @@ execpline2(Estate state, wordcode pcode,
{
pid_t pid;
int pipes[2];
+ struct execcmd_params eparams;
if (breaks || retflag)
return;
@@ -1745,17 +1849,16 @@ execpline2(Estate state, wordcode pcode,
else
list_pipe_text[0] = '\0';
}
- if (WC_PIPE_TYPE(pcode) == WC_PIPE_END)
- execcmd(state, input, output, how, last1 ? 1 : 2);
- else {
+ if (WC_PIPE_TYPE(pcode) == WC_PIPE_END) {
+ execcmd_analyse(state, &eparams);
+ execcmd_exec(state, &eparams, input, output, how, last1 ? 1 : 2);
+ } else {
int old_list_pipe = list_pipe;
int subsh_close = -1;
- Wordcode next = state->pc + (*state->pc), pc;
- wordcode code;
+ Wordcode next = state->pc + (*state->pc), start_pc;
- state->pc++;
- for (pc = state->pc; wc_code(code = *pc) == WC_REDIR;
- pc += WC_REDIR_WORDS(code));
+ start_pc = ++state->pc;
+ execcmd_analyse(state, &eparams);
if (mpipe(pipes) < 0) {
/* FIXME */
@@ -1764,7 +1867,8 @@ execpline2(Estate state, wordcode pcode,
/* if we are doing "foo | bar" where foo is a current *
* shell command, do foo in a subshell and do the *
* rest of the pipeline in the current shell. */
- if (wc_code(code) >= WC_CURSH && (how & Z_SYNC)) {
+ if ((eparams.type >= WC_CURSH || !eparams.args)
+ && (how & Z_SYNC)) {
int synch[2];
struct timeval bgtime;
@@ -1782,7 +1886,7 @@ execpline2(Estate state, wordcode pcode,
} else if (pid) {
char dummy, *text;
- text = getjobtext(state->prog, state->pc);
+ text = getjobtext(state->prog, start_pc);
addproc(pid, text, 0, &bgtime);
close(synch[1]);
read_loop(synch[0], &dummy, 1);
@@ -1793,14 +1897,18 @@ execpline2(Estate state, wordcode pcode,
entersubsh(((how & Z_ASYNC) ? ESUB_ASYNC : 0)
| ESUB_PGRP | ESUB_KEEPTRAP);
close(synch[1]);
- execcmd(state, input, pipes[1], how, 1);
+ if (sigtrapped[SIGEXIT])
+ {
+ unsettrap(SIGEXIT);
+ }
+ execcmd_exec(state, &eparams, input, pipes[1], how, 1);
_exit(lastval);
}
} else {
/* otherwise just do the pipeline normally. */
addfilelist(NULL, pipes[0]);
subsh_close = pipes[0];
- execcmd(state, input, pipes[1], how, 0);
+ execcmd_exec(state, &eparams, input, pipes[1], how, 0);
}
zclose(pipes[1]);
state->pc = next;
@@ -2456,55 +2564,51 @@ resolvebuiltin(const char *cmdarg, HashNode hn)
return hn;
}
+/*
+ * We are about to execute a command at the lowest level of the
+ * hierarchy. Analyse the parameters from the wordcode.
+ */
+
/**/
static void
-execcmd(Estate state, int input, int output, int how, int last1)
+execcmd_analyse(Estate state, Execcmd_params eparams)
{
- HashNode hn = NULL;
- LinkList args, filelist = NULL;
- LinkNode node;
- Redir fn;
- struct multio *mfds[10];
- char *text;
- int save[10];
- int fil, dfil, is_cursh, type, do_exec = 0, redir_err = 0, i, htok = 0;
- int nullexec = 0, assign = 0, forked = 0, postassigns = 0;
- int is_shfunc = 0, is_builtin = 0, is_exec = 0, use_defpath = 0;
- /* Various flags to the command. */
- int cflags = 0, orig_cflags = 0, checked = 0, oautocont = -1;
- LinkList redir;
wordcode code;
- Wordcode beg = state->pc, varspc, assignspc = (Wordcode)0;
- FILE *oxtrerr = xtrerr, *newxtrerr = NULL;
+ int i;
- doneps4 = 0;
- redir = (wc_code(*state->pc) == WC_REDIR ? ecgetredirs(state) : NULL);
+ eparams->beg = state->pc;
+ eparams->redir =
+ (wc_code(*state->pc) == WC_REDIR ? ecgetredirs(state) : NULL);
if (wc_code(*state->pc) == WC_ASSIGN) {
cmdoutval = 0;
- varspc = state->pc;
+ eparams->varspc = state->pc;
while (wc_code((code = *state->pc)) == WC_ASSIGN)
state->pc += (WC_ASSIGN_TYPE(code) == WC_ASSIGN_SCALAR ?
3 : WC_ASSIGN_NUM(code) + 2);
} else
- varspc = NULL;
+ eparams->varspc = NULL;
code = *state->pc++;
- type = wc_code(code);
+ eparams->type = wc_code(code);
+ eparams->postassigns = 0;
/* It would be nice if we could use EC_DUPTOK instead of EC_DUP here.
* But for that we would need to check/change all builtins so that
* they don't modify their argument strings. */
- switch (type) {
+ switch (eparams->type) {
case WC_SIMPLE:
- args = ecgetlist(state, WC_SIMPLE_ARGC(code), EC_DUP, &htok);
+ eparams->args = ecgetlist(state, WC_SIMPLE_ARGC(code), EC_DUP,
+ &eparams->htok);
+ eparams->assignspc = NULL;
break;
case WC_TYPESET:
- args = ecgetlist(state, WC_TYPESET_ARGC(code), EC_DUP, &htok);
- postassigns = *state->pc++;
- assignspc = state->pc;
- for (i = 0; i < postassigns; i++) {
+ eparams->args = ecgetlist(state, WC_TYPESET_ARGC(code), EC_DUP,
+ &eparams->htok);
+ eparams->postassigns = *state->pc++;
+ eparams->assignspc = state->pc;
+ for (i = 0; i < eparams->postassigns; i++) {
code = *state->pc;
DPUTS(wc_code(code) != WC_ASSIGN,
"BUG: miscounted typeset assignments");
@@ -2514,8 +2618,45 @@ execcmd(Estate state, int input, int output, int how, int last1)
break;
default:
- args = NULL;
+ eparams->args = NULL;
+ eparams->assignspc = NULL;
+ eparams->htok = 0;
+ break;
}
+}
+
+/*
+ * Execute a command at the lowest level of the hierarchy.
+ */
+
+/**/
+static void
+execcmd_exec(Estate state, Execcmd_params eparams,
+ int input, int output, int how, int last1)
+{
+ HashNode hn = NULL;
+ LinkList filelist = NULL;
+ LinkNode node;
+ Redir fn;
+ struct multio *mfds[10];
+ char *text;
+ int save[10];
+ int fil, dfil, is_cursh, do_exec = 0, redir_err = 0, i;
+ int nullexec = 0, assign = 0, forked = 0;
+ int is_shfunc = 0, is_builtin = 0, is_exec = 0, use_defpath = 0;
+ /* Various flags to the command. */
+ int cflags = 0, orig_cflags = 0, checked = 0, oautocont = -1;
+ FILE *oxtrerr = xtrerr, *newxtrerr = NULL;
+ /*
+ * Retrieve parameters for quick reference (they are unique
+ * to us so we can modify the structure if we want).
+ */
+ LinkList args = eparams->args;
+ LinkList redir = eparams->redir;
+ Wordcode varspc = eparams->varspc;
+ int type = eparams->type;
+
+ doneps4 = 0;
/*
* If assignment but no command get the status from variable
@@ -2609,24 +2750,76 @@ execcmd(Estate state, int input, int output, int how, int last1)
}
checked = 0;
if ((cflags & BINF_COMMAND) && nextnode(firstnode(args))) {
- /* check for options to command builtin */
- char *next = (char *) getdata(nextnode(firstnode(args)));
+ /*
+ * Check for options to "command".
+ * If just -p, this is handled here: use the default
+ * path to execute.
+ * If -v or -V, possibly with -p, dispatch to bin_whence
+ * but with flag to indicate special handling of -p.
+ * Otherwise, just leave marked as BINF_COMMAND
+ * modifier with no additional action.
+ */
+ LinkNode argnode = nextnode(firstnode(args));
+ char *argdata = (char *) getdata(argnode);
char *cmdopt;
- if (next && *next == '-' && strlen(next) == 2 &&
- (cmdopt = strchr("pvV", next[1])))
- {
- if (*cmdopt == 'p') {
- uremnode(args, firstnode(args));
- use_defpath = 1;
- if (nextnode(firstnode(args)))
- next = (char *) getdata(nextnode(firstnode(args)));
- } else {
- hn = &commandbn.node;
- is_builtin = 1;
+ int has_p = 0, has_vV = 0, has_other = 0;
+ while (*argdata == '-') {
+ /* Just to be definite, stop on single "-", too, */
+ if (!argdata[1] || (argdata[1] == '-' && !argdata[2]))
+ break;
+ for (cmdopt = argdata+1; *cmdopt; cmdopt++) {
+ switch (*cmdopt) {
+ case 'p':
+ /*
+ * If we've got this multiple times (command
+ * -p -p) we'll treat the second -p as a
+ * command because we only remove one below.
+ * Don't think that's a big issue, and it's
+ * also traditional behaviour.
+ */
+ has_p = 1;
+ break;
+ case 'v':
+ case 'V':
+ has_vV = 1;
+ break;
+ default:
+ has_other = 1;
+ break;
+ }
+ }
+ if (has_other) {
+ /* Don't know how to handle this, so don't */
+ has_p = has_vV = 0;
break;
}
+
+ argnode = nextnode(argnode);
+ if (!argnode)
+ break;
+ argdata = (char *) getdata(argnode);
}
- if (!strcmp(next, "--"))
+ if (has_vV) {
+ /* Leave everything alone, dispatch to whence */
+ hn = &commandbn.node;
+ is_builtin = 1;
+ break;
+ } else if (has_p) {
+ /* Use default path; absorb command and option. */
+ uremnode(args, firstnode(args));
+ use_defpath = 1;
+ if ((argnode = nextnode(firstnode(args))))
+ argdata = (char *) getdata(argnode);
+ }
+ /*
+ * Else just absorb command and any trailing
+ * end-of-options marker. This can only occur
+ * if we just had -p or something including more
+ * than just -p, -v and -V, in which case we behave
+ * as if this is command [non-option-stuff]. This
+ * isn't a good place for standard option handling.
+ */
+ if (!strcmp(argdata, "--"))
uremnode(args, firstnode(args));
}
if ((cflags & BINF_EXEC) && nextnode(firstnode(args))) {
@@ -2721,7 +2914,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
/* Do prefork substitutions */
esprefork = (assign || isset(MAGICEQUALSUBST)) ? PREFORK_TYPESET : 0;
- if (args && htok)
+ if (args && eparams->htok)
prefork(args, esprefork, NULL);
if (type == WC_SIMPLE || type == WC_TYPESET) {
@@ -2866,7 +3059,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
/* Get the text associated with this command. */
if ((how & Z_ASYNC) ||
(!sfcontext && !sourcelevel && (jobbing || (how & Z_TIMED))))
- text = getjobtext(state->prog, beg);
+ text = getjobtext(state->prog, eparams->beg);
else
text = NULL;
@@ -2893,11 +3086,11 @@ execcmd(Estate state, int input, int output, int how, int last1)
if (s[0] == Star && !s[1]) {
if (!checkrmall(pwd))
uremnode(args, node);
- } else if (l > 2 && s[l - 2] == '/' && s[l - 1] == Star) {
+ } else if (l >= 2 && s[l - 2] == '/' && s[l - 1] == Star) {
char t = s[l - 2];
s[l - 2] = 0;
- if (!checkrmall(s))
+ if (!checkrmall(*s ? s : "/"))
uremnode(args, node);
s[l - 2] = t;
}
@@ -3125,7 +3318,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
forked = 1;
}
- if ((esglob = !(cflags & BINF_NOGLOB)) && args && htok) {
+ if ((esglob = !(cflags & BINF_NOGLOB)) && args && eparams->htok) {
LinkList oargs = args;
globlist(args, 0);
args = oargs;
@@ -3439,7 +3632,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
}
if (type == WC_FUNCDEF) {
Eprog redir_prog;
- if (!redir && wc_code(*beg) == WC_REDIR) {
+ if (!redir && wc_code(*eparams->beg) == WC_REDIR) {
/*
* We're not using a redirection from the currently
* parsed environment, which is what we'd do for an
@@ -3449,7 +3642,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
struct estate s;
s.prog = state->prog;
- s.pc = beg;
+ s.pc = eparams->beg;
s.strs = state->prog->strs;
/*
@@ -3491,7 +3684,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
* if it's got "command" in front.
* If it's a normal command --- save.
*/
- if (is_shfunc || (hn->flags & BINF_PSPECIAL))
+ if (is_shfunc || (hn->flags & (BINF_PSPECIAL|BINF_ASSIGN)))
do_save = (orig_cflags & BINF_COMMAND);
else
do_save = 1;
@@ -3500,7 +3693,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
* Save if it's got "command" in front or it's
* not a magic-equals assignment.
*/
- if ((cflags & BINF_COMMAND) || !assign)
+ if ((cflags & (BINF_COMMAND|BINF_ASSIGN)) || !assign)
do_save = 1;
}
if (do_save && varspc)
@@ -3533,13 +3726,15 @@ execcmd(Estate state, int input, int output, int how, int last1)
} else {
/* It's a builtin */
LinkList assigns = (LinkList)0;
+ int postassigns = eparams->postassigns;
if (forked)
closem(FDT_INTERNAL);
if (postassigns) {
Wordcode opc = state->pc;
- state->pc = assignspc;
+ state->pc = eparams->assignspc;
assigns = newlinklist();
while (postassigns--) {
+ int htok;
wordcode ac = *state->pc++;
char *name = ecgetstr(state, EC_DUPTOK, &htok);
Asgment asg;
@@ -3648,7 +3843,8 @@ execcmd(Estate state, int input, int output, int how, int last1)
state->pc = opc;
}
dont_queue_signals();
- lastval = execbuiltin(args, assigns, (Builtin) hn);
+ if (!errflag)
+ lastval = execbuiltin(args, assigns, (Builtin) hn);
if (do_save & BINF_COMMAND)
errflag &= ~ERRFLAG_ERROR;
restore_queue_signals(q);
@@ -3685,12 +3881,15 @@ execcmd(Estate state, int input, int output, int how, int last1)
restore_params(restorelist, removelist);
} else {
- if (!forked)
- setiparam("SHLVL", --shlvl);
- if (do_exec) {
+ if (!subsh) {
+ /* for either implicit or explicit "exec", decrease $SHLVL
+ * as we're now done as a shell */
+ if (!forked)
+ setiparam("SHLVL", --shlvl);
+
/* If we are exec'ing a command, and we are not *
* in a subshell, then save the history file. */
- if (!subsh && isset(RCS) && interact && !nohistsave)
+ if (do_exec && isset(RCS) && interact && !nohistsave)
savehistfile(NULL, 1, HFILE_USE_OPTIONS);
}
if (type == WC_SIMPLE || type == WC_TYPESET) {
@@ -4022,7 +4221,7 @@ gethere(char **strp, int typ)
parsestr(&buf);
- if (!errflag) {
+ if (!(errflag & ERRFLAG_ERROR)) {
/* Retain any user interrupt error */
errflag = ef | (errflag & ERRFLAG_INT);
}
@@ -4275,11 +4474,26 @@ getoutputfile(char *cmd, char **eptr)
untokenize(s);
}
- addfilelist(nam, 0);
+ if (!s) /* Unclear why we need to do this before open() */
+ child_block(); /* but it has been so for a long time: leave it */
- if (!s)
- child_block();
- fd = open(nam, O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, 0600);
+ if ((fd = open(nam, O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, 0600)) < 0) {
+ zerr("process substitution failed: %e", errno);
+ free(nam);
+ if (!s)
+ child_unblock();
+ return NULL;
+ } else {
+ char *suffix = getsparam("TMPSUFFIX");
+ if (suffix && *suffix && !strstr(suffix, "/")) {
+ suffix = dyncat(nam, unmeta(suffix));
+ if (link(nam, suffix) == 0) {
+ addfilelist(nam, 0);
+ nam = ztrdup(suffix);
+ }
+ }
+ }
+ addfilelist(nam, 0);
if (s) {
/* optimised here-string */
@@ -4290,7 +4504,7 @@ getoutputfile(char *cmd, char **eptr)
return nam;
}
- if (fd < 0 || (cmdoutpid = pid = zfork(NULL)) == -1) {
+ if ((cmdoutpid = pid = zfork(NULL)) == -1) {
/* fork or open error */
child_unblock();
return nam;
@@ -4518,8 +4732,6 @@ spawnpipes(LinkList l, int nullexec)
}
}
-extern int tracingcond;
-
/* evaluate a [[ ... ]] */
/**/
@@ -4608,6 +4820,8 @@ exectime(Estate state, UNUSED(int do_exec))
/* Define a shell function */
+static const char *const ANONYMOUS_FUNCTION_NAME = "(anon)";
+
/**/
static int
execfuncdef(Estate state, Eprog redir_prog)
@@ -4725,7 +4939,7 @@ execfuncdef(Estate state, Eprog redir_prog)
if (!args)
args = newlinklist();
- shf->node.nam = "(anon)";
+ shf->node.nam = (char *) ANONYMOUS_FUNCTION_NAME;
pushnode(args, shf->node.nam);
execshfunc(shf, args);
@@ -4906,7 +5120,8 @@ execautofn_basic(Estate state, UNUSED(int do_exec))
oldscriptname = scriptname;
oldscriptfilename = scriptfilename;
- scriptname = scriptfilename = dupstring(shf->node.nam);
+ scriptname = dupstring(shf->node.nam);
+ scriptfilename = dupstring(shf->filename);
execode(shf->funcdef, 1, 0, "loadautofunc");
scriptname = oldscriptname;
scriptfilename = oldscriptfilename;
@@ -5158,8 +5373,12 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
if (flags & (PM_TAGGED|PM_TAGGED_LOCAL))
opts[XTRACE] = 1;
- else if (oflags & PM_TAGGED_LOCAL)
- opts[XTRACE] = 0;
+ else if (oflags & PM_TAGGED_LOCAL) {
+ if (shfunc->node.nam == ANONYMOUS_FUNCTION_NAME /* pointer comparison */)
+ flags |= PM_TAGGED_LOCAL;
+ else
+ opts[XTRACE] = 0;
+ }
ooflags = oflags;
/*
* oflags is static, because we compare it on the next recursive
@@ -5360,6 +5579,7 @@ runshfunc(Eprog prog, FuncWrap wrap, char *name)
if (!cont) {
if (ou)
zfree(ou, ouu);
+ unqueue_signals();
return;
}
wrap = wrap->next;
@@ -5382,7 +5602,7 @@ runshfunc(Eprog prog, FuncWrap wrap, char *name)
Eprog
getfpfunc(char *s, int *ksh, char **fname)
{
- char **pp, buf[PATH_MAX];
+ char **pp, buf[PATH_MAX+1];
off_t len;
off_t rlen;
char *d;
@@ -5512,7 +5732,7 @@ cancd(char *s)
char *t;
if (*s != '/') {
- char sbuf[PATH_MAX], **cp;
+ char sbuf[PATH_MAX+1], **cp;
if (cancd2(s))
return s;
diff --git a/Src/glob.c b/Src/glob.c
index 69de15544..623e6f1d6 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -41,7 +41,10 @@
typedef struct gmatch *Gmatch;
struct gmatch {
+ /* Metafied file name */
char *name;
+ /* Unmetafied file name; embedded nulls can't occur in file names */
+ char *uname;
/*
* Array of sort strings: one for each GS_EXEC sort type in
* the glob qualifiers.
@@ -280,7 +283,7 @@ addpath(char *s, int l)
static int
statfullpath(const char *s, struct stat *st, int l)
{
- char buf[PATH_MAX];
+ char buf[PATH_MAX+1];
DPUTS(strlen(s) + !*s + pathpos - pathbufcwd >= PATH_MAX,
"BUG: statfullpath(): pathname too long");
@@ -306,7 +309,7 @@ statfullpath(const char *s, struct stat *st, int l)
/* This may be set by qualifier functions to an array of strings to insert
* into the list instead of the original string. */
-char **inserts;
+static char **inserts;
/* add a match to the list */
@@ -776,7 +779,7 @@ parsepat(char *str)
/* Now there is no (#X) in front, we can check the path. */
if (!pathbuf)
- pathbuf = zalloc(pathbufsz = PATH_MAX);
+ pathbuf = zalloc(pathbufsz = PATH_MAX+1);
DPUTS(pathbufcwd, "BUG: glob changed directory");
if (*str == '/') { /* pattern has absolute path */
str++;
@@ -911,7 +914,8 @@ gmatchcmp(Gmatch a, Gmatch b)
for (i = gf_nsorts, s = gf_sortlist; i; i--, s++) {
switch (s->tp & ~GS_DESC) {
case GS_NAME:
- r = zstrcmp(b->name, a->name, gf_numsort ? SORTIT_NUMERICALLY : 0);
+ r = zstrcmp(b->uname, a->uname,
+ gf_numsort ? SORTIT_NUMERICALLY : 0);
break;
case GS_DEPTH:
{
@@ -1170,7 +1174,7 @@ checkglobqual(char *str, int sl, int nobareglob, char **sp)
}
/* Main entry point to the globbing code for filename globbing. *
- * np points to a node in the list list which will be expanded *
+ * np points to a node in the list which will be expanded *
* into a series of nodes. */
/**/
@@ -1278,14 +1282,7 @@ zglob(LinkList list, LinkNode np, int nountok)
*ptr = '-';
while (*s && !newcolonmod) {
func = (int (*) _((char *, Statptr, off_t, char *)))0;
- if (idigit(*s)) {
- /* Store numeric argument for qualifier */
- func = qualflags;
- data = 0;
- sdata = NULL;
- while (idigit(*s))
- data = data * 010 + (*s++ - '0');
- } else if (*s == ',') {
+ if (*s == ',') {
/* A comma separates alternative sets of qualifiers */
s++;
sense = 0;
@@ -1859,6 +1856,7 @@ zglob(LinkList list, LinkNode np, int nountok)
int nexecs = 0;
struct globsort *sortp;
struct globsort *lastsortp = gf_sortlist + gf_nsorts;
+ Gmatch gmptr;
/* First find out if there are any GS_EXECs, counting them. */
for (sortp = gf_sortlist; sortp < lastsortp; sortp++)
@@ -1910,6 +1908,23 @@ zglob(LinkList list, LinkNode np, int nountok)
}
}
+ /*
+ * Where necessary, create unmetafied version of names
+ * for comparison. If no Meta characters just point
+ * to original string. All on heap.
+ */
+ for (gmptr = matchbuf; gmptr < matchptr; gmptr++)
+ {
+ if (strchr(gmptr->name, Meta))
+ {
+ int dummy;
+ gmptr->uname = dupstring(gmptr->name);
+ unmetafy(gmptr->uname, &dummy);
+ } else {
+ gmptr->uname = gmptr->name;
+ }
+ }
+
/* Sort arguments in to lexical (and possibly numeric) order. *
* This is reversed to facilitate insertion into the list. */
qsort((void *) & matchbuf[0], matchct, sizeof(struct gmatch),
@@ -3484,6 +3499,10 @@ zshtokenize(char *s, int flags)
for (; *s; s++) {
cont:
switch (*s) {
+ case Meta:
+ /* skip both Meta and following character */
+ s++;
+ break;
case Bnull:
case Bnullkeep:
case '\\':
diff --git a/Src/hashtable.c b/Src/hashtable.c
index 0664c3694..7c3367568 100644
--- a/Src/hashtable.c
+++ b/Src/hashtable.c
@@ -558,7 +558,7 @@ printhashtabinfo(HashTable ht)
/**/
int
-bin_hashinfo(char *nam, char **args, Options ops, int func)
+bin_hashinfo(UNUSED(char *nam), UNUSED(char **args), UNUSED(Options ops), UNUSED(int func))
{
HashTable ht;
@@ -1291,9 +1291,9 @@ printaliasnode(HashNode hn, int printflags)
else if (a->node.flags & ALIAS_GLOBAL)
printf("-g ");
- /* If an alias begins with `-', then we must output `-- ' *
+ /* If an alias begins with `-' or `+', then we must output `-- '
* first, so that it is not interpreted as an option. */
- if(a->node.nam[0] == '-')
+ if(a->node.nam[0] == '-' || a->node.nam[0] == '+')
printf("-- ");
}
diff --git a/Src/hist.c b/Src/hist.c
index 007366a49..97fd34039 100644
--- a/Src/hist.c
+++ b/Src/hist.c
@@ -468,7 +468,7 @@ herrflush(void)
/**/
static int
-getsubsargs(char *subline, int *gbalp, int *cflagp)
+getsubsargs(UNUSED(char *subline), int *gbalp, int *cflagp)
{
int del, follow;
char *ptr1, *ptr2;
@@ -653,6 +653,7 @@ histsubchar(int c)
(c == '}' || c == ';' || c == '\'' || c == '"' || c == '`')) {
/* Neither event nor word designator, no expansion */
safeinungetc(c);
+ unqueue_signals();
return bangchar;
}
*ptr = 0;
@@ -1037,7 +1038,7 @@ hbegin(int dohist)
/*
* pws: We used to test for "|| (inbufflags & INP_ALIAS)"
* in this test, but at this point we don't have input
- * set up up so this can trigger unnecessarily.
+ * set up so this can trigger unnecessarily.
* I don't see how the test at this point could ever be
* useful, since we only get here when we're initialising
* the history mechanism, before we've done any input.
@@ -1378,7 +1379,6 @@ should_ignore_line(Eprog prog)
mod_export int
hend(Eprog prog)
{
- LinkList hookargs = newlinklist();
int flag, hookret, stack_pos = histsave_stack_pos;
/*
* save:
@@ -1418,9 +1418,17 @@ hend(Eprog prog)
DPUTS(hptr < chline, "History end pointer off start of line");
*hptr = '\0';
}
- addlinknode(hookargs, "zshaddhistory");
- addlinknode(hookargs, chline);
- callhookfunc("zshaddhistory", hookargs, 1, &hookret);
+ {
+ LinkList hookargs = newlinklist();
+ int save_errflag = errflag;
+ errflag = 0;
+
+ addlinknode(hookargs, "zshaddhistory");
+ addlinknode(hookargs, chline);
+ callhookfunc("zshaddhistory", hookargs, 1, &hookret);
+
+ errflag |= save_errflag;
+ }
/* For history sharing, lock history file once for both read and write */
hf = getsparam("HISTFILE");
if (isset(SHAREHISTORY) && !lockhistfile(hf, 0)) {
@@ -1835,7 +1843,7 @@ chrealpath(char **junkptr)
# ifdef REALPATH_ACCEPTS_NULL
char *lastpos, *nonreal, *real;
# else
- char *lastpos, *nonreal, pathbuf[PATH_MAX];
+ char *lastpos, *nonreal, pathbuf[PATH_MAX+1];
char *real = pathbuf;
# endif
#endif
diff --git a/Src/init.c b/Src/init.c
index 4097327ee..c12043b88 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -712,7 +712,7 @@ init_term(void)
if (tgetent(termbuf, term) != TGETENT_SUCCESS)
#endif
{
- if (isset(INTERACTIVE))
+ if (interact)
zerr("can't find terminal definition for %s", term);
errflag &= ~ERRFLAG_ERROR;
termflags |= TERM_BAD;
@@ -802,7 +802,7 @@ init_term(void)
/**/
void
-setupvals(char *cmd)
+setupvals(char *cmd, char *runscript, char *zsh_name)
{
#ifdef USE_GETPWUID
struct passwd *pswd;
@@ -1089,6 +1089,9 @@ setupvals(char *cmd)
if (cmd)
setsparam("ZSH_EXECUTION_STRING", ztrdup(cmd));
+ if (runscript)
+ setsparam("ZSH_SCRIPT", ztrdup(runscript));
+ setsparam("ZSH_NAME", ztrdup(zsh_name)); /* NOTE: already metafied early in zsh_main() */
}
/*
@@ -1202,19 +1205,22 @@ run_init_scripts(void)
if (islogin)
source("/etc/profile");
if (unset(PRIVILEGED)) {
- char *s = getsparam("ENV");
if (islogin)
sourcehome(".profile");
- noerrs = 2;
- if (s) {
- s = dupstring(s);
- if (!parsestr(&s)) {
- singsub(&s);
- noerrs = 0;
- source(s);
+
+ if (interact) {
+ noerrs = 2;
+ char *s = getsparam("ENV");
+ if (s) {
+ s = dupstring(s);
+ if (!parsestr(&s)) {
+ singsub(&s);
+ noerrs = 0;
+ source(s);
+ }
}
+ noerrs = 0;
}
- noerrs = 0;
} else
source("/etc/suid_profile");
} else {
@@ -1224,7 +1230,7 @@ run_init_scripts(void)
if (isset(RCS) && unset(PRIVILEGED))
{
- if (isset(INTERACTIVE)) {
+ if (interact) {
/*
* Always attempt to load the newuser module to perform
* checks for new zsh users. Don't care if we can't load it.
@@ -1270,7 +1276,7 @@ run_init_scripts(void)
/**/
void
-init_misc(char *cmd)
+init_misc(char *cmd, char *zsh_name)
{
#ifndef RESTRICTED_R
if ( restricted )
@@ -1436,8 +1442,10 @@ sourcehome(char *s)
queue_signals();
if (EMULATION(EMULATE_SH|EMULATE_KSH) || !(h = getsparam_u("ZDOTDIR"))) {
h = home;
- if (!h)
+ if (!h) {
+ unqueue_signals();
return;
+ }
}
{
@@ -1606,7 +1614,7 @@ mod_export int use_exit_printed;
mod_export int
zsh_main(UNUSED(int argc), char **argv)
{
- char **t, *runscript = NULL;
+ char **t, *runscript = NULL, *zsh_name;
char *cmd; /* argument to -c */
int t0;
#ifdef USE_LOCALE
@@ -1660,14 +1668,14 @@ zsh_main(UNUSED(int argc), char **argv)
SHTTY = -1;
init_io(cmd);
- setupvals(cmd);
+ setupvals(cmd, runscript, zsh_name);
init_signals();
init_bltinmods();
init_builtins();
run_init_scripts();
setupshin(runscript);
- init_misc(cmd);
+ init_misc(cmd, zsh_name);
for (;;) {
/*
diff --git a/Src/input.c b/Src/input.c
index eb968ea72..fe94b8ef7 100644
--- a/Src/input.c
+++ b/Src/input.c
@@ -51,7 +51,7 @@
* Note that the input string is itself used as the input buffer: it is not
* copied, nor is it every written back to, so using a constant string
* should work. Consequently, when passing areas of memory from the heap
- * it is necessary that that heap last as long as the operation of reading
+ * it is necessary that the heap last as long as the operation of reading
* the string. After the string is read, the stack should be popped with
* inpop(), which effectively flushes any unread input as well as restoring
* the previous input state.
diff --git a/Src/jobs.c b/Src/jobs.c
index b47ba8c60..d1b98ac4d 100644
--- a/Src/jobs.c
+++ b/Src/jobs.c
@@ -128,7 +128,7 @@ makerunning(Job jn)
Process pn;
jn->stat &= ~STAT_STOPPED;
- for (pn = jn->procs; pn; pn = pn->next)
+ for (pn = jn->procs; pn; pn = pn->next) {
#if 0
if (WIFSTOPPED(pn->status) &&
(!(jn->stat & STAT_SUPERJOB) || pn->next))
@@ -136,6 +136,7 @@ makerunning(Job jn)
#endif
if (WIFSTOPPED(pn->status))
pn->status = SP_RUNNING;
+ }
if (jn->stat & STAT_SUPERJOB)
makerunning(jobtab + jn->other);
@@ -231,12 +232,13 @@ super_job(int sub)
static int
handle_sub(int job, int fg)
{
+ /* job: superjob; sj: subjob. */
Job jn = jobtab + job, sj = jobtab + jn->other;
if ((sj->stat & STAT_DONE) || (!sj->procs && !sj->auxprocs)) {
struct process *p;
-
- for (p = sj->procs; p; p = p->next)
+
+ for (p = sj->procs; p; p = p->next) {
if (WIFSIGNALED(p->status)) {
if (jn->gleader != mypgrp && jn->procs->next)
killpg(jn->gleader, WTERMSIG(p->status));
@@ -246,6 +248,7 @@ handle_sub(int job, int fg)
kill(sj->other, WTERMSIG(p->status));
break;
}
+ }
if (!p) {
int cp;
@@ -884,37 +887,62 @@ should_report_time(Job j)
struct value vbuf;
Value v;
char *s = "REPORTTIME";
- zlong reporttime;
+ zlong reporttime = -1;
+#ifdef HAVE_GETRUSAGE
+ char *sm = "REPORTMEMORY";
+ zlong reportmemory = -1;
+#endif
/* if the time keyword was used */
if (j->stat & STAT_TIMED)
return 1;
queue_signals();
- if (!(v = getvalue(&vbuf, &s, 0)) ||
- (reporttime = getintvalue(v)) < 0) {
- unqueue_signals();
- return 0;
- }
+ if ((v = getvalue(&vbuf, &s, 0)))
+ reporttime = getintvalue(v);
+#ifdef HAVE_GETRUSAGE
+ if ((v = getvalue(&vbuf, &sm, 0)))
+ reportmemory = getintvalue(v);
+#endif
unqueue_signals();
+ if (reporttime < 0
+#ifdef HAVE_GETRUSAGE
+ && reportmemory < 0
+#endif
+ )
+ return 0;
/* can this ever happen? */
if (!j->procs)
return 0;
if (zleactive)
return 0;
+ if (reporttime >= 0)
+ {
#ifdef HAVE_GETRUSAGE
- reporttime -= j->procs->ti.ru_utime.tv_sec + j->procs->ti.ru_stime.tv_sec;
- if (j->procs->ti.ru_utime.tv_usec +
- j->procs->ti.ru_stime.tv_usec >= 1000000)
- reporttime--;
- return reporttime <= 0;
+ reporttime -= j->procs->ti.ru_utime.tv_sec +
+ j->procs->ti.ru_stime.tv_sec;
+ if (j->procs->ti.ru_utime.tv_usec +
+ j->procs->ti.ru_stime.tv_usec >= 1000000)
+ reporttime--;
+ if (reporttime <= 0)
+ return 1;
#else
- {
- clktck = get_clktck();
- return ((j->procs->ti.ut + j->procs->ti.st) / clktck >= reporttime);
+ {
+ clktck = get_clktck();
+ if ((j->procs->ti.ut + j->procs->ti.st) / clktck >= reporttime)
+ return 1;
+ }
+#endif
}
+
+#ifdef HAVE_GETRUSAGE
+ if (reportmemory >= 0 &&
+ j->procs->ti.ru_maxrss / 1024 > reportmemory)
+ return 1;
#endif
+
+ return 0;
}
/* !(lng & 3) means jobs *
@@ -1291,6 +1319,11 @@ deletejob(Job jn, int disowning)
attachtty(mypgrp);
adjustwinsize(0);
}
+ if (jn->stat & STAT_SUPERJOB) {
+ Job jno = jobtab + jn->other;
+ if (jno->stat & STAT_SUBJOB)
+ jno->stat |= STAT_SUBJOB_ORPHANED;
+ }
freejob(jn, 1);
}
@@ -1447,7 +1480,7 @@ zwaitjob(int job, int wait_cmd)
*/
pipecleanfilelist(jn->filelist, 0);
}
- while (!errflag && jn->stat &&
+ while (!(errflag & ERRFLAG_ERROR) && jn->stat &&
!(jn->stat & STAT_DONE) &&
!(interact && (jn->stat & STAT_STOPPED))) {
signal_suspend(SIGCHLD, wait_cmd);
@@ -1469,6 +1502,13 @@ zwaitjob(int job, int wait_cmd)
that's the one related to ^C. But that doesn't work.
There's something more here we don't understand. --pws
+ The change above to ignore ERRFLAG_INT in the loop test
+ solves a problem wherein child processes that ignore the
+ INT signal were never waited-for. Clearing the flag here
+ still seems the wrong thing, but perhaps ERRFLAG_INT
+ should be saved and restored around signal_suspend() to
+ prevent it being lost within a signal trap? --Bart
+
errflag = 0; */
if (subsh) {
@@ -1970,9 +2010,9 @@ struct bgstatus {
};
typedef struct bgstatus *Bgstatus;
/* The list of those entries */
-LinkList bgstatus_list;
+static LinkList bgstatus_list;
/* Count of entries. Reaches value of _SC_CHILD_MAX and stops. */
-long bgstatus_count;
+static long bgstatus_count;
/*
* Remove and free a bgstatus entry.
@@ -2372,7 +2412,7 @@ bin_fg(char *name, char **argv, Options ops, int func)
return retval;
}
-const struct {
+static const struct {
const char *name;
int num;
} alt_sigs[] = {
@@ -2527,6 +2567,10 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
argv++;
}
+ /* Discard the standard "-" and "--" option breaks */
+ if (*argv && (*argv)[0] == '-' && (!(*argv)[1] || (*argv)[1] == '-'))
+ argv++;
+
if (!*argv) {
zwarnnam(nam, "not enough arguments");
return 1;
diff --git a/Src/lex.c b/Src/lex.c
index 3ea878c7b..889612825 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -267,9 +267,13 @@ zshlex(void)
{
if (tok == LEXERR)
return;
- do
+ do {
+ if (inrepeat_)
+ ++inrepeat_;
+ if (inrepeat_ == 3 && isset(SHORTLOOPS))
+ incmdpos = 1;
tok = gettok();
- while (tok != ENDINPUT && exalias());
+ } while (tok != ENDINPUT && exalias());
nocorrect &= 1;
if (tok == NEWLIN || tok == ENDINPUT) {
while (hdocs) {
@@ -609,7 +613,7 @@ gettok(void)
if (lexstop)
return (errflag) ? LEXERR : ENDINPUT;
isfirstln = 0;
- if ((lexflags & LEXFLAGS_ZLE))
+ if ((lexflags & LEXFLAGS_ZLE) && !(inbufflags & INP_ALIAS))
wordbeg = inbufct - (qbang && c == bangchar);
hwbegin(-1-(qbang && c == bangchar));
/* word includes the last character read and possibly \ before ! */
@@ -1026,8 +1030,10 @@ gettokstr(int c, int sub)
c = Inbrace;
++bct;
cmdpush(CS_BRACEPAR);
- if (!in_brace_param)
- in_brace_param = bct;
+ if (!in_brace_param) {
+ if ((in_brace_param = bct))
+ seen_brct = 0;
+ }
} else {
hungetc(e);
lexstop = 0;
@@ -1783,9 +1789,17 @@ parse_subst_string(char *s)
static void
gotword(void)
{
- we = zlemetall + 1 - inbufct + (addedx == 2 ? 1 : 0);
- if (zlemetacs <= we) {
- wb = zlemetall - wordbeg + addedx;
+ int nwe = zlemetall + 1 - inbufct + (addedx == 2 ? 1 : 0);
+ if (zlemetacs <= nwe) {
+ int nwb = zlemetall - wordbeg + addedx;
+ if (zlemetacs >= nwb) {
+ wb = nwb;
+ we = nwe;
+ } else {
+ wb = zlemetacs + addedx;
+ if (we < wb)
+ we = wb;
+ }
lexflags = 0;
}
}
@@ -1829,9 +1843,9 @@ checkalias(void)
suf > zshlextext && suf[-1] != Meta &&
(an = (Alias)sufaliastab->getnode(sufaliastab, suf+1)) &&
!an->inuse && incmdpos) {
- inpush(dupstring(zshlextext), INP_ALIAS, NULL);
+ inpush(dupstring(zshlextext), INP_ALIAS, an);
inpush(" ", INP_ALIAS, NULL);
- inpush(an->text, INP_ALIAS, an);
+ inpush(an->text, INP_ALIAS, NULL);
lexstop = 0;
return 1;
}
@@ -1897,6 +1911,7 @@ exalias(void)
zshlextext[0] == '}' && !zshlextext[1])) &&
(rw = (Reswd) reswdtab->getnode(reswdtab, zshlextext))) {
tok = rw->token;
+ inrepeat_ = (tok == REPEAT);
if (tok == DINBRACK)
incond = 1;
} else if (incond && !strcmp(zshlextext, "]]")) {
@@ -2123,8 +2138,17 @@ skipcomm(void)
lexflags &= ~LEXFLAGS_ZLE;
dbparens = 0; /* restored by zcontext_restore_partial() */
- if (!parse_event(OUTPAR) || tok != OUTPAR)
- lexstop = 1;
+ if (!parse_event(OUTPAR) || tok != OUTPAR) {
+ if (strin) {
+ /*
+ * Get the rest of the string raw since we don't
+ * know where this token ends.
+ */
+ while (!lexstop)
+ (void)ingetc();
+ } else
+ lexstop = 1;
+ }
/* Outpar lexical token gets added in caller if present */
/*
diff --git a/Src/loop.c b/Src/loop.c
index 4def9b652..367c0df5c 100644
--- a/Src/loop.c
+++ b/Src/loop.c
@@ -289,6 +289,8 @@ execselect(Estate state, UNUSED(int do_exec))
}
} else
str = (char *)getlinknode(bufstack);
+ if (!str && !errflag)
+ setsparam("REPLY", ztrdup("")); /* EOF (user pressed Ctrl+D) */
if (!str || errflag) {
if (breaks)
breaks--;
@@ -437,13 +439,12 @@ execwhile(Estate state, UNUSED(int do_exec))
if (!((lastval == 0) ^ isuntil)) {
if (breaks)
breaks--;
- lastval = oldval;
+ if (!retflag)
+ lastval = oldval;
break;
}
- if (retflag) {
- lastval = oldval;
+ if (retflag)
break;
- }
/* In case the loop body is also a functional no-op,
* make sure signal handlers recognize ^C as above. */
@@ -493,7 +494,9 @@ execrepeat(Estate state, UNUSED(int do_exec))
tmp = ecgetstr(state, EC_DUPTOK, &htok);
if (htok)
singsub(&tmp);
- count = atoi(tmp);
+ count = mathevali(tmp);
+ if (errflag)
+ return 1;
pushheap();
cmdpush(CS_REPEAT);
loops++;
@@ -566,7 +569,8 @@ execif(Estate state, int do_exec)
cmdpop();
} else {
noerrexit = olderrexit;
- lastval = 0;
+ if (!retflag)
+ lastval = 0;
}
state->pc = end;
@@ -580,7 +584,7 @@ execcase(Estate state, int do_exec)
Wordcode end, next;
wordcode code = state->pc[-1];
char *word, *pat;
- int npat, save, nalts, ialt, patok;
+ int npat, save, nalts, ialt, patok, anypatok;
Patprog *spprog, pprog;
end = state->pc + WC_CASE_SKIP(code);
@@ -588,7 +592,7 @@ execcase(Estate state, int do_exec)
word = ecgetstr(state, EC_DUP, NULL);
singsub(&word);
untokenize(word);
- lastval = 0;
+ anypatok = 0;
cmdpush(CS_CASE);
while (state->pc < end) {
@@ -645,7 +649,7 @@ execcase(Estate state, int do_exec)
*spprog = pprog;
}
if (pprog && pattry(pprog, word))
- patok = 1;
+ patok = anypatok = 1;
state->pc += 2;
nalts--;
}
@@ -658,7 +662,7 @@ execcase(Estate state, int do_exec)
execlist(state, 1, ((WC_CASE_TYPE(code) == WC_CASE_OR) &&
do_exec));
while (!retflag && wc_code(code) == WC_CASE &&
- WC_CASE_TYPE(code) == WC_CASE_AND) {
+ WC_CASE_TYPE(code) == WC_CASE_AND && state->pc < end) {
state->pc = next;
code = *state->pc++;
next = state->pc + WC_CASE_SKIP(code);
@@ -676,6 +680,9 @@ execcase(Estate state, int do_exec)
state->pc = end;
+ if (!anypatok)
+ lastval = 0;
+
return lastval;
}
diff --git a/Src/mem.c b/Src/mem.c
index e31145e1e..840bbb6e4 100644
--- a/Src/mem.c
+++ b/Src/mem.c
@@ -157,13 +157,13 @@ mod_export Heapid last_heap_id;
* Assumes old_heaps() will come along and restore it later
* (outputs an error if old_heaps() is called out of sequence).
*/
-LinkList heaps_saved;
+static LinkList heaps_saved;
/*
* Debugging verbosity. This must be set from a debugger.
* An 'or' of bits from the enum heap_debug_verbosity.
*/
-volatile int heap_debug_verbosity;
+static volatile int heap_debug_verbosity;
/*
* Generate a heap identifier that's unique up to unsigned integer wrap.
@@ -497,7 +497,8 @@ popheap(void)
continue;
}
h->next = NULL;
- }
+ } else if (hl == h) /* This is the last arena of all */
+ hl = NULL;
#ifdef USE_MMAP
munmap((void *) h, h->size);
#else
@@ -903,27 +904,36 @@ memory_validate(Heapid heap_id)
queue_signals();
for (h = heaps; h; h = h->next) {
- if (h->heap_id == heap_id)
+ if (h->heap_id == heap_id) {
+ unqueue_signals();
return 0;
+ }
for (hs = heaps->sp; hs; hs = hs->next) {
- if (hs->heap_id == heap_id)
+ if (hs->heap_id == heap_id) {
+ unqueue_signals();
return 0;
+ }
}
}
if (heaps_saved) {
for (node = firstnode(heaps_saved); node; incnode(node)) {
for (h = (Heap)getdata(node); h; h = h->next) {
- if (h->heap_id == heap_id)
+ if (h->heap_id == heap_id) {
+ unqueue_signals();
return 0;
+ }
for (hs = heaps->sp; hs; hs = hs->next) {
- if (hs->heap_id == heap_id)
+ if (hs->heap_id == heap_id) {
+ unqueue_signals();
return 0;
+ }
}
}
}
}
+ unqueue_signals();
return 1;
}
/**/
@@ -966,18 +976,10 @@ zalloc(size_t size)
mod_export void *
zshcalloc(size_t size)
{
- void *ptr;
-
+ void *ptr = zalloc(size);
if (!size)
size = 1;
- queue_signals();
- if (!(ptr = (void *) malloc(size))) {
- zerr("fatal error: out of memory");
- exit(1);
- }
- unqueue_signals();
memset(ptr, 0, size);
-
return ptr;
}
diff --git a/Src/module.c b/Src/module.c
index 368254c29..41f142adb 100644
--- a/Src/module.c
+++ b/Src/module.c
@@ -2242,6 +2242,7 @@ load_module(char const *name, Feature_enables enablesarr, int silent)
return 0;
}
if (m->node.flags & MOD_BUSY) {
+ unqueue_signals();
zerr("circular dependencies for module ;%s", name);
return 1;
}
@@ -3350,6 +3351,8 @@ setfeatureenables(Module m, Features f, int *e)
if (f->mf_size) {
if (setmathfuncs(m->node.nam, f->mf_list, f->mf_size, e))
ret = 1;
+ if (e)
+ e += f->mf_size;
}
if (f->pd_size) {
if (setparamdefs(m->node.nam, f->pd_list, f->pd_size, e))
diff --git a/Src/options.c b/Src/options.c
index 17c46c311..18619c851 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -847,9 +847,10 @@ printoptionnodestate(HashNode hn, int hadplus)
int optno = on->optno;
if (hadplus) {
- if (defset(on, emulation) != isset(optno))
- printf("set -o %s%s\n", defset(on, emulation) ?
- "no" : "", on->node.nam);
+ printf("set %co %s%s\n",
+ defset(on, emulation) != isset(optno) ? '-' : '+',
+ defset(on, emulation) ? "no" : "",
+ on->node.nam);
} else {
if (defset(on, emulation))
printf("no%-19s %s\n", on->node.nam, isset(optno) ? "off" : "on");
diff --git a/Src/params.c b/Src/params.c
index b2e889738..aa8b196bd 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -80,14 +80,14 @@ char *argzero, /* $0 */
*rprompt, /* $RPROMPT */
*rprompt2, /* $RPROMPT2 */
*sprompt, /* $SPROMPT */
- *wordchars, /* $WORDCHARS */
- *zsh_name; /* $ZSH_NAME */
+ *wordchars; /* $WORDCHARS */
/**/
mod_export
char *ifs, /* $IFS */
*postedit, /* $POSTEDIT */
*term, /* $TERM */
*zsh_terminfo, /* $TERMINFO */
+ *zsh_terminfodirs, /* $TERMINFO_DIRS */
*ttystrname, /* $TTY */
*pwd; /* $PWD */
@@ -209,6 +209,8 @@ static const struct gsu_scalar term_gsu =
{ termgetfn, termsetfn, stdunsetfn };
static const struct gsu_scalar terminfo_gsu =
{ terminfogetfn, terminfosetfn, stdunsetfn };
+static const struct gsu_scalar terminfodirs_gsu =
+{ terminfodirsgetfn, terminfodirssetfn, stdunsetfn };
static const struct gsu_scalar wordchars_gsu =
{ wordcharsgetfn, wordcharssetfn, stdunsetfn };
static const struct gsu_scalar ifs_gsu =
@@ -284,8 +286,9 @@ IPDEF2("histchars", histchars_gsu, PM_DONTIMPORT),
IPDEF2("HOME", home_gsu, PM_UNSET),
IPDEF2("TERM", term_gsu, PM_UNSET),
IPDEF2("TERMINFO", terminfo_gsu, PM_UNSET),
+IPDEF2("TERMINFO_DIRS", terminfodirs_gsu, PM_UNSET),
IPDEF2("WORDCHARS", wordchars_gsu, 0),
-IPDEF2("IFS", ifs_gsu, PM_DONTIMPORT),
+IPDEF2("IFS", ifs_gsu, PM_DONTIMPORT | PM_RESTRICTED),
IPDEF2("_", underscore_gsu, PM_DONTIMPORT),
IPDEF2("KEYBOARD_HACK", keyboard_hack_gsu, PM_DONTIMPORT),
IPDEF2("0", argzero_gsu, 0),
@@ -334,6 +337,7 @@ IPDEF6("TRY_BLOCK_ERROR", &try_errflag, varinteger_gsu),
IPDEF6("TRY_BLOCK_INTERRUPT", &try_interrupt, varinteger_gsu),
#define IPDEF7(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0}
+#define IPDEF7R(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL|PM_DONTIMPORT_SUID},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),
@@ -346,7 +350,7 @@ IPDEF7("PS2", &prompt2),
IPDEF7U("RPS2", &rprompt2),
IPDEF7U("RPROMPT2", &rprompt2),
IPDEF7("PS3", &prompt3),
-IPDEF7("PS4", &prompt4),
+IPDEF7R("PS4", &prompt4),
IPDEF7("SPROMPT", &sprompt),
#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}
@@ -416,7 +420,7 @@ IPDEF10("pipestatus", pipestatus_gsu),
* and $@, this is not readonly. This parameter is not directly
* visible in user space.
*/
-initparam argvparam_pm = IPDEF9F("", &pparams, NULL, \
+static initparam argvparam_pm = IPDEF9F("", &pparams, NULL, \
PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT);
#undef BR
@@ -627,6 +631,36 @@ getvaluearr(Value v)
return NULL;
}
+/* Return whether the variable is set *
+ * checks that array slices are within range *
+ * used for [[ -v ... ]] condition test */
+
+/**/
+int
+issetvar(char *name)
+{
+ struct value vbuf;
+ Value v;
+ int slice;
+ char **arr;
+
+ if (!(v = getvalue(&vbuf, &name, 1)) || *name)
+ return 0; /* no value or more chars after the variable name */
+ if (v->isarr & ~SCANPM_ARRONLY)
+ return v->end > 1; /* for extracted elements, end gives us a count */
+
+ slice = v->start != 0 || v->end != -1;
+ if (PM_TYPE(v->pm->node.flags) != PM_ARRAY || !slice)
+ return !slice && !(v->pm->node.flags & PM_UNSET);
+
+ if (!v->end) /* empty array slice */
+ return 0;
+ /* get the array and check end is within range */
+ if (!(arr = getvaluearr(v)))
+ return 0;
+ return arrlen_ge(arr, v->end < 0 ? - v->end : v->end);
+}
+
/*
* Split environment string into (name, value) pair.
* this is used to avoid in-place editing of environment table
@@ -660,7 +694,28 @@ split_env_string(char *env, char **name, char **value)
} else
return 0;
}
-
+
+/**
+ * Check parameter flags to see if parameter shouldn't be imported
+ * from environment at start.
+ *
+ * return 1: don't import: 0: ok to import.
+ */
+static int dontimport(int flags)
+{
+ /* If explicitly marked as don't export */
+ if (flags & PM_DONTIMPORT)
+ return 1;
+ /* If value already exported */
+ if (flags & PM_EXPORTED)
+ return 1;
+ /* If security issue when importing and running with some privilege */
+ if ((flags & PM_DONTIMPORT_SUID) && isset(PRIVILEGED))
+ return 1;
+ /* OK to import */
+ return 0;
+}
+
/* Set up parameter hash table. This will add predefined *
* parameter entries as well as setting up parameter table *
* entries for environment variables we inherit. */
@@ -752,8 +807,13 @@ createparamtable(void)
envp2 = environ; *envp2; envp2++) {
if (split_env_string(*envp2, &iname, &ivalue)) {
if (!idigit(*iname) && isident(iname) && !strchr(iname, '[')) {
+ /*
+ * Parameters that aren't already in the parameter table
+ * aren't special to the shell, so it's always OK to
+ * import. Otherwise, check parameter flags.
+ */
if ((!(pm = (Param) paramtab->getnode(paramtab, iname)) ||
- !(pm->node.flags & PM_DONTIMPORT || pm->node.flags & PM_EXPORTED)) &&
+ !dontimport(pm->node.flags)) &&
(pm = assignsparam(iname, metafy(ivalue, -1, META_DUP),
ASSPM_ENV_IMPORT))) {
pm->node.flags |= PM_EXPORTED;
@@ -771,7 +831,7 @@ createparamtable(void)
}
popheap();
#ifndef USE_SET_UNSET_ENV
- *envp = '\0';
+ *envp = NULL;
#endif
opts[ALLEXPORT] = oae;
@@ -812,7 +872,7 @@ createparamtable(void)
setsparam("OSTYPE", ztrdup_metafy(OSTYPE));
setsparam("TTY", ztrdup_metafy(ttystrname));
setsparam("VENDOR", ztrdup_metafy(VENDOR));
- setsparam("ZSH_NAME", ztrdup_metafy(zsh_name));
+ setsparam("ZSH_ARGZERO", ztrdup(posixzero));
setsparam("ZSH_VERSION", ztrdup_metafy(ZSH_VERSION));
setsparam("ZSH_PATCHLEVEL", ztrdup_metafy(ZSH_PATCHLEVEL));
setaparam("signals", sigptr = zalloc((SIGCOUNT+4) * sizeof(char *)));
@@ -884,7 +944,10 @@ createparam(char *name, int flags)
zerr("%s: restricted", name);
return NULL;
}
- if (!(oldpm->node.flags & PM_UNSET) || (oldpm->node.flags & PM_SPECIAL)) {
+ if (!(oldpm->node.flags & PM_UNSET) ||
+ (oldpm->node.flags & PM_SPECIAL) ||
+ /* POSIXBUILTINS horror: we need to retain 'export' flags */
+ (isset(POSIXBUILTINS) && (oldpm->node.flags & PM_EXPORTED))) {
oldpm->node.flags &= ~PM_UNSET;
if ((oldpm->node.flags & PM_SPECIAL) && oldpm->ename) {
Param altpm =
@@ -2001,6 +2064,7 @@ getstrvalue(Value v)
{
char *s, **ss;
char buf[BDIGBUFSIZE];
+ int len;
if (!v)
return hcalloc(1);
@@ -2027,7 +2091,7 @@ getstrvalue(Value v)
else {
if (v->start < 0)
v->start += arrlen(ss);
- s = (v->start >= arrlen(ss) || v->start < 0) ?
+ s = (arrlen_le(ss, v->start) || v->start < 0) ?
(char *) hcalloc(1) : ss[v->start];
}
return s;
@@ -2177,23 +2241,27 @@ getstrvalue(Value v)
if (v->start == 0 && v->end == -1)
return s;
+ len = strlen(s);
if (v->start < 0) {
- v->start += strlen(s);
+ v->start += len;
if (v->start < 0)
v->start = 0;
}
if (v->end < 0) {
- v->end += strlen(s);
+ v->end += len;
if (v->end >= 0) {
char *eptr = s + v->end;
if (*eptr)
v->end += MB_METACHARLEN(eptr);
}
}
- s = (v->start > (int)strlen(s)) ? dupstring("") : dupstring(s + v->start);
+
+ s = (v->start > len) ? dupstring("") :
+ dupstring_wlen(s + v->start, len - v->start);
+
if (v->end <= v->start)
s[0] = '\0';
- else if (v->end - v->start <= (int)strlen(s))
+ else if (v->end - v->start <= len - v->start)
s[v->end - v->start] = '\0';
return s;
@@ -2226,14 +2294,31 @@ getarrvalue(Value v)
v->start += arrlen(s);
if (v->end < 0)
v->end += arrlen(s) + 1;
- if (v->start > arrlen(s) || v->start < 0)
- s = arrdup(nular);
- else
- s = arrdup(s + v->start);
- if (v->end <= v->start)
- s[0] = NULL;
- else if (v->end - v->start <= arrlen(s))
- s[v->end - v->start] = NULL;
+
+ /* Null if 1) array too short, 2) index still negative */
+ if (v->end <= v->start) {
+ s = arrdup_max(nular, 0);
+ }
+ else if (v->start < 0) {
+ s = arrdup_max(nular, 1);
+ }
+ else if (arrlen_le(s, v->start)) {
+ /* Handle $ary[i,i] consistently for any $i > $#ary
+ * and $ary[i,j] consistently for any $j > $i > $#ary
+ */
+ s = arrdup_max(nular, v->end - (v->start + 1));
+ }
+ else {
+ /* Copy to a point before the end of the source array:
+ * arrdup_max will copy at most v->end - v->start elements,
+ * starting from v->start element. Original code said:
+ * s[v->end - v->start] = NULL
+ * which means that there are exactly the same number of
+ * elements as the value of the above *0-based* index.
+ */
+ s = arrdup_max(s + v->start, v->end - v->start);
+ }
+
return s;
}
@@ -2362,10 +2447,11 @@ assignstrvalue(Value v, char *val, int flags)
v->pm->width = strlen(val);
} else {
char *z, *x;
- int zlen;
+ int zlen, vlen, newsize;
+
+ z = v->pm->gsu.s->getfn(v->pm);
+ zlen = strlen(z);
- z = dupstring(v->pm->gsu.s->getfn(v->pm));
- zlen = strlen(z);
if ((v->flags & VALFLAG_INV) && unset(KSHARRAYS))
v->start--, v->end--;
if (v->start < 0) {
@@ -2395,12 +2481,34 @@ assignstrvalue(Value v, char *val, int flags)
}
else if (v->end > zlen)
v->end = zlen;
- x = (char *) zalloc(v->start + strlen(val) + zlen - v->end + 1);
- strncpy(x, z, v->start);
- strcpy(x + v->start, val);
- strcat(x + v->start, z + v->end);
- v->pm->gsu.s->setfn(v->pm, x);
- zsfree(val);
+
+ vlen = strlen(val);
+ /* Characters preceding start index +
+ characters of what is assigned +
+ characters following end index */
+ newsize = v->start + vlen + (zlen - v->end);
+
+ /* Does new size differ? */
+ if (newsize != zlen || v->pm->gsu.s->setfn != strsetfn) {
+ x = (char *) zalloc(newsize + 1);
+ strncpy(x, z, v->start);
+ strcpy(x + v->start, val);
+ strcat(x + v->start, z + v->end);
+ v->pm->gsu.s->setfn(v->pm, x);
+ } else {
+ Param pm = v->pm;
+ /* Size doesn't change, can limit actions to only
+ * overwriting bytes in already allocated string */
+ strncpy(z + v->start, val, vlen);
+ /* Implement remainder of strsetfn */
+ if (!(pm->node.flags & PM_HASHELEM) &&
+ ((pm->node.flags & PM_NAMEDDIR) ||
+ isset(AUTONAMEDIRS))) {
+ pm->node.flags |= PM_NAMEDDIR;
+ adduserdir(pm->node.nam, z, 0, 0);
+ }
+ }
+ zsfree(val);
}
break;
case PM_INTEGER:
@@ -2580,23 +2688,36 @@ setarrvalue(Value v, char **val)
v->end = v->start;
post_assignment_length = v->start + arrlen(val);
- if (v->end <= pre_assignment_length)
- post_assignment_length += pre_assignment_length - v->end + 1;
+ if (v->end < pre_assignment_length) {
+ /*
+ * Allocate room for array elements between the end of the slice `v'
+ * and the original array's end.
+ */
+ post_assignment_length += pre_assignment_length - v->end;
+ }
- p = new = (char **) zshcalloc(sizeof(char *)
- * (post_assignment_length + 1));
+ p = new = (char **) zalloc(sizeof(char *)
+ * (post_assignment_length + 1));
for (i = 0; i < v->start; i++)
*p++ = i < pre_assignment_length ? ztrdup(*q++) : ztrdup("");
- for (r = val; *r;)
- *p++ = ztrdup(*r++);
+ for (r = val; *r;) {
+ /* Give away ownership of the string */
+ *p++ = *r++;
+ }
if (v->end < pre_assignment_length)
for (q = old + v->end; *q;)
*p++ = ztrdup(*q++);
*p = NULL;
+ DPUTS2(p - new != post_assignment_length, "setarrvalue: wrong allocation: %d 1= %lu",
+ post_assignment_length, (unsigned long)(p - new));
+
v->pm->gsu.a->setfn(v->pm, new);
- freearray(val);
+
+ /* Ownership of all strings has been
+ * given away, can plainly free */
+ free(val);
}
}
@@ -2747,6 +2868,7 @@ assignsparam(char *s, char *val, int flags)
zerr("read-only variable: %s", v->pm->node.nam);
*ss = '[';
zsfree(val);
+ unqueue_signals();
return NULL;
}
flags &= ~ASSPM_WARN_CREATE;
@@ -2983,7 +3105,6 @@ sethparam(char *s, char **val)
return NULL;
queue_signals();
if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING))) {
- DPUTS(!v, "BUG: assigning to undeclared associative array");
createparam(t, PM_HASHED);
checkcreate = isset(WARNCREATEGLOBAL) && locallevel > forklevel;
} else if (!(PM_TYPE(v->pm->node.flags) & PM_HASHED) &&
@@ -3062,6 +3183,7 @@ setnparam(char *s, mnumber val)
if (!(v = getvalue(&vbuf, &t, 1))) {
DPUTS(!v, "BUG: value not found for new parameter");
/* errflag |= ERRFLAG_ERROR; */
+ unqueue_signals();
return NULL;
}
if (!was_unset && isset(WARNCREATEGLOBAL) && locallevel > forklevel)
@@ -3803,8 +3925,8 @@ intsecondsgetfn(UNUSED(Param pm))
gettimeofday(&now, &dummy_tz);
- return (zlong)(now.tv_sec - shtimer.tv_sec) +
- (zlong)(now.tv_usec - shtimer.tv_usec) / (zlong)1000000;
+ return (zlong)(now.tv_sec - shtimer.tv_sec -
+ (now.tv_usec < shtimer.tv_usec ? 1 : 0));
}
/* Function to set value of special parameter `SECONDS' */
@@ -3822,7 +3944,7 @@ intsecondssetfn(UNUSED(Param pm), zlong x)
shtimer.tv_sec = diff;
if ((zlong)shtimer.tv_sec != diff)
zwarn("SECONDS truncated on assignment");
- shtimer.tv_usec = 0;
+ shtimer.tv_usec = now.tv_usec;
}
/**/
@@ -4157,7 +4279,9 @@ static void
argzerosetfn(UNUSED(Param pm), char *x)
{
if (x) {
- if (!isset(POSIXARGZERO)) {
+ if (isset(POSIXARGZERO))
+ zerr("read-only variable: 0");
+ else {
zsfree(argzero);
argzero = ztrdup(x);
}
@@ -4336,7 +4460,7 @@ void
homesetfn(UNUSED(Param pm), char *x)
{
zsfree(home);
- if (x && isset(CHASELINKS) && (home = xsymlink(x)))
+ if (x && isset(CHASELINKS) && (home = xsymlink(x, 0)))
zsfree(x);
else
home = x ? x : ztrdup("");
@@ -4435,6 +4559,33 @@ terminfosetfn(Param pm, char *x)
term_reinit_from_pm();
}
+/* Function to get value of special parameter `TERMINFO_DIRS' */
+
+/**/
+char *
+terminfodirsgetfn(UNUSED(Param pm))
+{
+ return zsh_terminfodirs ? zsh_terminfodirs : dupstring("");
+}
+
+/* Function to set value of special parameter `TERMINFO_DIRS' */
+
+/**/
+void
+terminfodirssetfn(Param pm, char *x)
+{
+ zsfree(zsh_terminfodirs);
+ zsh_terminfodirs = x;
+
+ /*
+ * terminfo relies on the value being exported before
+ * we reinitialise the terminal. This is a bit inefficient.
+ */
+ if ((pm->node.flags & PM_EXPORTED) && x)
+ addenv(pm, x);
+
+ term_reinit_from_pm();
+}
/* Function to get value for special parameter `pipestatus' */
/**/
@@ -4670,6 +4821,7 @@ addenv(Param pm, char *value)
if (pm->env)
zsfree(pm->env);
pm->env = newenv;
+ pm->node.flags |= PM_EXPORTED;
#else
/*
* Under Cygwin we must use putenv() to maintain consistency.
@@ -5165,10 +5317,6 @@ printparamvalue(Param p, int printflags)
{
char *t, **u;
- if (p->node.flags & PM_AUTOLOAD) {
- putchar('\n');
- return;
- }
if (printflags & PRINT_KV_PAIR)
putchar(' ');
else
@@ -5252,20 +5400,33 @@ printparamnode(HashNode hn, int printflags)
*/
printflags |= PRINT_NAMEONLY;
}
+ else if (p->node.flags & PM_EXPORTED)
+ printflags |= PRINT_NAMEONLY;
else
return;
}
+ if (p->node.flags & PM_AUTOLOAD)
+ printflags |= PRINT_NAMEONLY;
if (printflags & PRINT_TYPESET) {
if ((p->node.flags & (PM_READONLY|PM_SPECIAL)) ==
- (PM_READONLY|PM_SPECIAL)) {
+ (PM_READONLY|PM_SPECIAL) ||
+ (p->node.flags & PM_AUTOLOAD)) {
/*
* It's not possible to restore the state of
* these, so don't output.
*/
return;
}
- printf("typeset ");
+ if (locallevel && p->level >= locallevel) {
+ printf("typeset "); /* printf("local "); */
+ } else if ((p->node.flags & PM_EXPORTED) &&
+ !(p->node.flags & (PM_ARRAY|PM_HASHED))) {
+ printf("export ");
+ } else if (locallevel) {
+ printf("typeset -g ");
+ } else
+ printf("typeset ");
}
/* Print the attributes of the parameter */
@@ -5278,7 +5439,9 @@ printparamnode(HashNode hn, int printflags)
if (pmptr->flags & PMTF_TEST_LEVEL) {
if (p->level)
doprint = 1;
- } else if (p->node.flags & pmptr->binflag)
+ } else if ((pmptr->binflag != PM_EXPORTED || p->level ||
+ (p->node.flags & (PM_LOCAL|PM_ARRAY|PM_HASHED))) &&
+ (p->node.flags & pmptr->binflag))
doprint = 1;
if (doprint) {
@@ -5290,9 +5453,8 @@ printparamnode(HashNode hn, int printflags)
}
putchar(pmptr->typeflag);
}
- } else {
+ } else
printf("%s ", pmptr->string);
- }
if ((pmptr->flags & PMTF_USE_BASE) && p->base) {
printf("%d ", p->base);
doneminus = 0;
diff --git a/Src/parse.c b/Src/parse.c
index 4829e3a6d..50a0d5f9f 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -63,6 +63,12 @@ int isnewlin;
/**/
int infor;
+/* != 0 if we are after a repeat keyword; if it's nonzero it's a 1-based index
+ * of the current token from the last-seen command position */
+
+/**/
+int inrepeat_; /* trailing underscore because of name clash with Zle/zle_vi.c */
+
/* != 0 if parsing arguments of typeset etc. */
/**/
@@ -271,6 +277,7 @@ parse_context_save(struct parse_stack *ps, int toplevel)
ps->incasepat = incasepat;
ps->isnewlin = isnewlin;
ps->infor = infor;
+ ps->inrepeat_ = inrepeat_;
ps->intypeset = intypeset;
ps->hdocs = hdocs;
@@ -302,9 +309,9 @@ parse_context_restore(const struct parse_stack *ps, int toplevel)
incond = ps->incond;
inredir = ps->inredir;
incasepat = ps->incasepat;
- incasepat = ps->incasepat;
isnewlin = ps->isnewlin;
infor = ps->infor;
+ inrepeat_ = ps->inrepeat_;
intypeset = ps->intypeset;
hdocs = ps->hdocs;
@@ -447,6 +454,7 @@ init_parse_status(void)
* using the lexical analyser for strings as well as here.
*/
incasepat = incond = inredir = infor = intypeset = 0;
+ inrepeat_ = 0;
incmdpos = 1;
}
@@ -586,7 +594,7 @@ par_event(int endtok)
if (tok == ENDINPUT)
return 0;
if (tok == endtok)
- return 0;
+ return 1;
p = ecadd(0);
@@ -709,7 +717,7 @@ set_sublist_code(int p, int type, int flags, int skip, int cmplx)
*/
/**/
-static int
+static void
par_list(int *cmplx)
{
int p, lp = -1, c;
@@ -738,19 +746,15 @@ par_list(int *cmplx)
goto rec;
} else
set_list_code(p, (Z_SYNC | Z_END), c);
- return 1;
} else {
ecused--;
- if (lp >= 0) {
+ if (lp >= 0)
ecbuf[lp] |= wc_bdata(Z_END);
- return 1;
- }
- return 0;
}
}
/**/
-static int
+static void
par_list1(int *cmplx)
{
int p = ecadd(0), c = 0;
@@ -758,11 +762,8 @@ par_list1(int *cmplx)
if (par_sublist(&c)) {
set_list_code(p, (Z_SYNC | Z_END), c);
*cmplx |= c;
- return 1;
- } else {
+ } else
ecused--;
- return 0;
- }
}
/*
@@ -1404,7 +1405,7 @@ par_if(int *cmplx)
}
}
cmdpop();
- if (xtok == ELSE) {
+ if (xtok == ELSE || tok == ELSE) {
pp = ecadd(0);
cmdpush(CS_ELSE);
while (tok == SEPER)
@@ -1482,6 +1483,7 @@ par_while(int *cmplx)
static void
par_repeat(int *cmplx)
{
+ /* ### what to do about inrepeat_ here? */
int oecused = ecused, p;
p = ecadd(0);
@@ -2383,7 +2385,7 @@ par_cond_2(void)
s1 = tokstr;
dble = (s1 && *s1 == '-'
&& (!n_testargs
- || strspn(s1+1, "abcdefghknoprstuwxzLONGS") == 1)
+ || strspn(s1+1, "abcdefghknoprstuvwxzLONGS") == 1)
&& !s1[2]);
if (tok != STRING) {
/* Check first argument for [[ STRING ]] re-interpretation */
@@ -2462,7 +2464,7 @@ par_cond_double(char *a, char *b)
{
if (a[0] != '-' || !a[1])
COND_ERROR("parse error: condition expected: %s", a);
- else if (!a[2] && strspn(a+1, "abcdefgknoprstuwxzhLONGS") == 1) {
+ else if (!a[2] && strspn(a+1, "abcdefgknoprstuvwxzhLONGS") == 1) {
ecadd(WCB_COND(a[1], 0));
ecstr(b);
} else {
@@ -2495,12 +2497,17 @@ par_cond_triple(char *a, char *b, char *c)
{
int t0;
- if ((b[0] == Equals || b[0] == '=') &&
- (!b[1] || ((b[1] == Equals || b[1] == '=') && !b[2]))) {
+ if ((b[0] == Equals || b[0] == '=') && !b[1]) {
ecadd(WCB_COND(COND_STREQ, 0));
ecstr(a);
ecstr(c);
ecadd(ecnpats++);
+ } else if ((b[0] == Equals || b[0] == '=') &&
+ (b[1] == Equals || b[1] == '=') && !b[2]) {
+ ecadd(WCB_COND(COND_STRDEQ, 0));
+ ecstr(a);
+ ecstr(c);
+ ecadd(ecnpats++);
} else if (b[0] == '!' && (b[1] == Equals || b[1] == '=') && !b[2]) {
ecadd(WCB_COND(COND_STRNEQ, 0));
ecstr(a);
diff --git a/Src/pattern.c b/Src/pattern.c
index 72c7d97d5..1f2e94bd9 100644
--- a/Src/pattern.c
+++ b/Src/pattern.c
@@ -669,12 +669,14 @@ patcompile(char *exp, int inflags, char **endexp)
nmeta++;
if (nmeta) {
char *oldpatout = patout;
+ ptrdiff_t pd;
patadd(NULL, 0, nmeta, 0);
/*
* Yuk.
*/
p = (Patprog)patout;
- opnd = patout + (opnd - oldpatout);
+ pd = patout - oldpatout;
+ opnd += pd;
dst = patout + startoff;
}
@@ -686,6 +688,8 @@ patcompile(char *exp, int inflags, char **endexp)
*dst++ = *opnd++;
}
}
+ /* Only one string in a PAT_PURES, so now done. */
+ break;
}
}
p->size = dst - patout;
@@ -1838,7 +1842,8 @@ pattail(long p, long val)
/* do pattail, but on operand of first argument; nop if operandless */
/**/
-static void patoptail(long p, long val)
+static void
+patoptail(long p, long val)
{
Upat ptr = (Upat)patout + p;
int op = P_OP(ptr);
@@ -1854,19 +1859,34 @@ static void patoptail(long p, long val)
/*
* Run a pattern.
*/
-static char *patinstart; /* Start of input string */
-static char *patinend; /* End of input string */
-static char *patinput; /* String input pointer */
-static char *patinpath; /* Full path for use with ~ exclusions */
-static int patinlen; /* Length of last successful match.
+struct rpat {
+ char *patinstart; /* Start of input string */
+ char *patinend; /* End of input string */
+ char *patinput; /* String input pointer */
+ char *patinpath; /* Full path for use with ~ exclusions */
+ int patinlen; /* Length of last successful match.
* Includes count of Meta characters.
*/
-static char *patbeginp[NSUBEXP]; /* Pointer to backref beginnings */
-static char *patendp[NSUBEXP]; /* Pointer to backref ends */
-static int parsfound; /* parentheses (with backrefs) found */
+ char *patbeginp[NSUBEXP]; /* Pointer to backref beginnings */
+ char *patendp[NSUBEXP]; /* Pointer to backref ends */
+ int parsfound; /* parentheses (with backrefs) found */
+
+ int globdots; /* Glob initial dots? */
+};
+
+static struct rpat pattrystate;
+
+#define patinstart (pattrystate.patinstart)
+#define patinend (pattrystate.patinend)
+#define patinput (pattrystate.patinput)
+#define patinpath (pattrystate.patinpath)
+#define patinlen (pattrystate.patinlen)
+#define patbeginp (pattrystate.patbeginp)
+#define patendp (pattrystate.patendp)
+#define parsfound (pattrystate.parsfound)
+#define globdots (pattrystate.globdots)
-static int globdots; /* Glob initial dots? */
/*
* Character functions operating on unmetafied strings.
@@ -2302,7 +2322,7 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalenin,
* Either we are testing against a pure string,
* or we can match anything at all.
*/
- int ret, pstrlen;
+ int pstrlen;
char *pstr;
if (patstralloc->alloced)
{
@@ -2404,11 +2424,7 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalenin,
}
}
}
-
- 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.
@@ -2440,9 +2456,8 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalenin,
ret = 0;
}
}
- if (!ret) {
+ if (!ret)
return 0;
- }
patglobflags = prog->globflags;
if (!(patflags & PAT_FILE)) {
@@ -2454,8 +2469,6 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalenin,
patinput = patinstart;
- dont_queue_signals();
-
if (patmatch((Upat)progstr)) {
/*
* we were lazy and didn't save the globflags if an exclusion
@@ -2594,11 +2607,9 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalenin,
ret = 1;
} else
ret = 0;
-
- restore_queue_signals(q);
-
- return ret;
}
+
+ return ret;
}
/*
@@ -2674,6 +2685,26 @@ patmatch(Upat prog)
int savglobflags, op, no, min, fail = 0, saverrsfound;
zrange_t from, to, comp;
patint_t nextch;
+ int q = queue_signal_level();
+
+ /*
+ * To avoid overhead of saving state if there are no queued signals
+ * waiting, we pierce the signals.h veil and examine queue state.
+ */
+#define check_for_signals() do if (queue_front != queue_rear) { \
+ int savpatflags = patflags, savpatglobflags = patglobflags; \
+ char *savexactpos = exactpos, *savexactend = exactend; \
+ struct rpat savpattrystate = pattrystate; \
+ dont_queue_signals(); \
+ restore_queue_signals(q); \
+ exactpos = savexactpos; \
+ exactend = savexactend; \
+ patflags = savpatflags; \
+ patglobflags = savpatglobflags; \
+ pattrystate = savpattrystate; \
+ } while (0)
+
+ check_for_signals();
while (scan && !errflag) {
next = PATNEXT(scan);
@@ -3508,6 +3539,9 @@ patmatch(Upat prog)
}
scan = next;
+
+ /* Allow handlers to run once per loop */
+ check_for_signals();
}
return 0;
diff --git a/Src/prompt.c b/Src/prompt.c
index be067ee7e..ee77c8bc8 100644
--- a/Src/prompt.c
+++ b/Src/prompt.c
@@ -395,11 +395,11 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
test = 1;
break;
case 'v':
- if (arrlen(psvar) >= arg)
+ if (arrlen_ge(psvar, arg))
test = 1;
break;
case 'V':
- if (arrlen(psvar) >= arg) {
+ if (arrlen_ge(psvar, arg)) {
if (*psvar[(arg ? arg : 1) - 1])
test = 1;
}
@@ -491,8 +491,10 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
if (!arg)
arg++;
queue_signals();
- if (!(hostnam = getsparam("HOST")))
+ if (!(hostnam = getsparam("HOST"))) {
+ unqueue_signals();
break;
+ }
if (arg < 0) {
for (ss = hostnam + strlen(hostnam); ss > hostnam; ss--)
if (ss[-1] == '.' && !++arg)
@@ -523,8 +525,6 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
break;
case 'b':
txtchangeset(txtchangep, TXTNOBOLDFACE, TXTBOLDFACE);
- txtchangeset(txtchangep, TXTNOSTANDOUT, TXTSTANDOUT);
- txtchangeset(txtchangep, TXTNOUNDERLINE, TXTUNDERLINE);
txtunset(TXTBOLDFACE);
tsetcap(TCALLATTRSOFF, TSC_PROMPT|TSC_DIRTY);
break;
@@ -542,7 +542,8 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
arg = parsecolorchar(arg, 1);
if (arg >= 0 && !(arg & TXTNOFGCOLOUR)) {
txtchangeset(txtchangep, arg & TXT_ATTR_FG_ON_MASK,
- TXTNOFGCOLOUR);
+ TXTNOFGCOLOUR | TXT_ATTR_FG_COL_MASK);
+ txtunset(TXT_ATTR_FG_COL_MASK);
txtset(arg & TXT_ATTR_FG_ON_MASK);
set_colour_attribute(arg, COL_SEQ_FG, TSC_PROMPT);
break;
@@ -557,7 +558,8 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
arg = parsecolorchar(arg, 0);
if (arg >= 0 && !(arg & TXTNOBGCOLOUR)) {
txtchangeset(txtchangep, arg & TXT_ATTR_BG_ON_MASK,
- TXTNOBGCOLOUR);
+ TXTNOBGCOLOUR | TXT_ATTR_BG_COL_MASK);
+ txtunset(TXT_ATTR_BG_COL_MASK);
txtset(arg & TXT_ATTR_BG_ON_MASK);
set_colour_attribute(arg, COL_SEQ_BG, TSC_PROMPT);
break;
@@ -736,7 +738,7 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
arg = 1;
else if (arg < 0)
arg += arrlen(psvar) + 1;
- if (arg > 0 && arrlen(psvar) >= arg)
+ if (arg > 0 && arrlen_ge(psvar, arg))
stradd(psvar[arg - 1]);
break;
case 'E':
@@ -1041,6 +1043,10 @@ tsetcap(int cap, int flags)
tsetcap(TCSTANDOUTBEG, flags);
if (txtisset(TXTUNDERLINE))
tsetcap(TCUNDERLINEBEG, flags);
+ if (txtisset(TXTFGCOLOUR))
+ set_colour_attribute(txtattrmask, COL_SEQ_FG, TSC_PROMPT);
+ if (txtisset(TXTBGCOLOUR))
+ set_colour_attribute(txtattrmask, COL_SEQ_BG, TSC_PROMPT);
}
}
}
@@ -1831,7 +1837,7 @@ struct colour_sequences {
char *end; /* Escape sequence terminator */
char *def; /* Code to reset default colour */
};
-struct colour_sequences fg_bg_sequences[2];
+static struct colour_sequences fg_bg_sequences[2];
/*
* We need a buffer for colour sequence composition. It may
diff --git a/Src/signals.c b/Src/signals.c
index aa0b5aaa7..9e05add09 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -55,6 +55,15 @@ mod_export Eprog siglists[VSIGCOUNT];
/**/
mod_export int nsigtrapped;
+/*
+ * Flag that exit trap has been set in POSIX mode.
+ * The setter's expectation is therefore that it is run
+ * on programme exit, not function exit.
+ */
+
+/**/
+static int exit_trap_posix;
+
/* Variables used by signal queueing */
/**/
@@ -63,6 +72,10 @@ mod_export int queueing_enabled, queue_front, queue_rear;
mod_export int signal_queue[MAX_QUEUE_SIZE];
/**/
mod_export sigset_t signal_mask_queue[MAX_QUEUE_SIZE];
+#ifdef DEBUG
+/**/
+mod_export int queue_in;
+#endif
/* Variables used by trap queueing */
@@ -637,6 +650,7 @@ zhandler(int sig)
inerrflush();
check_cursh_sig(SIGINT);
}
+ lastval = 128 + SIGINT;
}
break;
@@ -714,7 +728,7 @@ killjb(Job jn, int sig)
{
Process pn;
int err = 0;
-
+
if (jobbing) {
if (jn->stat & STAT_SUPERJOB) {
if (sig == SIGCONT) {
@@ -722,11 +736,21 @@ killjb(Job jn, int sig)
if (killpg(pn->pid, sig) == -1)
if (kill(pn->pid, sig) == -1 && errno != ESRCH)
err = -1;
-
+
+ /*
+ * Note this does not kill the last process,
+ * which is assumed to be the one controlling the
+ * subjob, i.e. the forked zsh that was originally
+ * list_pipe_pid...
+ */
for (pn = jn->procs; pn->next; pn = pn->next)
if (kill(pn->pid, sig) == -1 && errno != ESRCH)
err = -1;
+ /*
+ * ...we only continue that once the external processes
+ * currently associated with the subjob are finished.
+ */
if (!jobtab[jn->other].procs && pn)
if (kill(pn->pid, sig) == -1 && errno != ESRCH)
err = -1;
@@ -735,7 +759,7 @@ killjb(Job jn, int sig)
}
if (killpg(jobtab[jn->other].gleader, sig) == -1 && errno != ESRCH)
err = -1;
-
+
if (killpg(jn->gleader, sig) == -1 && errno != ESRCH)
err = -1;
@@ -755,7 +779,7 @@ killjb(Job jn, int sig)
* at once, so just use a linked list.
*/
struct savetrap {
- int sig, flags, local;
+ int sig, flags, local, posix;
void *list;
};
@@ -774,6 +798,7 @@ dosavetrap(int sig, int level)
st = (struct savetrap *)zalloc(sizeof(*st));
st->sig = sig;
st->local = level;
+ st->posix = (sig == SIGEXIT) ? exit_trap_posix : 0;
if ((st->flags = sigtrapped[sig]) & ZSIG_FUNC) {
/*
* Get the old function: this assumes we haven't added
@@ -867,12 +892,21 @@ settrap(int sig, Eprog l, int flags)
sig != SIGCHLD)
install_handler(sig);
}
+ sigtrapped[sig] |= flags;
/*
* Note that introducing the locallevel does not affect whether
* sigtrapped[sig] is zero or not, i.e. a test without a mask
* works just the same.
*/
- sigtrapped[sig] |= (locallevel << ZSIG_SHIFT) | flags;
+ if (sig == SIGEXIT) {
+ /* Make POSIX behaviour of EXIT trap sticky */
+ exit_trap_posix = isset(POSIXTRAPS);
+ /* POSIX exit traps are not local. */
+ if (!exit_trap_posix)
+ sigtrapped[sig] |= (locallevel << ZSIG_SHIFT);
+ }
+ else
+ sigtrapped[sig] |= (locallevel << ZSIG_SHIFT);
unqueue_signals();
return 0;
}
@@ -906,6 +940,11 @@ removetrap(int sig)
* Note that we save the trap here even if there isn't an existing
* one, to aid in removing this one. However, if there's
* already one at the current locallevel we just overwrite it.
+ *
+ * Note we save EXIT traps based on the *current* setting of
+ * POSIXTRAPS --- so if there is POSIX EXIT trap set but
+ * we are in native mode it can be saved, replaced by a function
+ * trap, and then restored.
*/
if (!dontsavetrap &&
(sig == SIGEXIT ? !isset(POSIXTRAPS) : isset(LOCALTRAPS)) &&
@@ -935,6 +974,8 @@ removetrap(int sig)
#endif
sig != SIGCHLD)
signal_default(sig);
+ if (sig == SIGEXIT)
+ exit_trap_posix = 0;
/*
* At this point we free the appropriate structs. If we don't
@@ -978,7 +1019,7 @@ starttrapscope(void)
* so give it the next higher one. dosavetrap() is called
* automatically where necessary.
*/
- if (sigtrapped[SIGEXIT] && !isset(POSIXTRAPS)) {
+ if (sigtrapped[SIGEXIT] && !exit_trap_posix) {
locallevel++;
unsettrap(SIGEXIT);
locallevel--;
@@ -1005,7 +1046,7 @@ endtrapscope(void)
* Don't do this inside another trap.
*/
if (!intrap &&
- !isset(POSIXTRAPS) && (exittr = sigtrapped[SIGEXIT])) {
+ !exit_trap_posix && (exittr = sigtrapped[SIGEXIT])) {
if (exittr & ZSIG_FUNC) {
exitfn = removehashnode(shfunctab, "TRAPEXIT");
} else {
@@ -1031,7 +1072,9 @@ endtrapscope(void)
if (st->flags & ZSIG_FUNC)
settrap(sig, NULL, ZSIG_FUNC);
else
- settrap(sig, (Eprog) st->list, 0);
+ settrap(sig, (Eprog) st->list, 0);
+ if (sig == SIGEXIT)
+ exit_trap_posix = st->posix;
dontsavetrap--;
/*
* counting of nsigtrapped should presumably be handled
@@ -1042,16 +1085,26 @@ endtrapscope(void)
if ((sigtrapped[sig] = st->flags) & ZSIG_FUNC)
shfunctab->addnode(shfunctab, ((Shfunc)st->list)->node.nam,
(Shfunc) st->list);
- } else if (sigtrapped[sig])
- unsettrap(sig);
+ } else if (sigtrapped[sig]) {
+ /*
+ * Don't restore the old state if someone has set a
+ * POSIX-style exit trap --- allow this to propagate.
+ */
+ if (sig != SIGEXIT || !exit_trap_posix)
+ unsettrap(sig);
+ }
zfree(st, sizeof(*st));
}
}
if (exittr) {
- if (!isset(POSIXTRAPS))
- dotrapargs(SIGEXIT, &exittr, exitfn);
+ /*
+ * We already made sure this wasn't set as a POSIX exit trap.
+ * We respect the user's intention when the trap in question
+ * was set.
+ */
+ dotrapargs(SIGEXIT, &exittr, exitfn);
if (exittr & ZSIG_FUNC)
shfunctab->freenode((HashNode)exitfn);
else
diff --git a/Src/signals.h b/Src/signals.h
index d68096891..1904f4326 100644
--- a/Src/signals.h
+++ b/Src/signals.h
@@ -82,8 +82,6 @@
#define MAX_QUEUE_SIZE 128
-#define queue_signals() (queueing_enabled++)
-
#define run_queued_signals() do { \
while (queue_front != queue_rear) { /* while signals in queue */ \
sigset_t oset; \
@@ -94,12 +92,35 @@
} \
} while (0)
+#ifdef DEBUG
+
+#define queue_signals() (queue_in++, queueing_enabled++)
+
#define unqueue_signals() do { \
DPUTS(!queueing_enabled, "BUG: unqueue_signals called but not queueing"); \
+ --queue_in; \
if (!--queueing_enabled) run_queued_signals(); \
} while (0)
-#define queue_signal_level() queueing_enabled
+#define dont_queue_signals() do { \
+ queue_in = queueing_enabled; \
+ queueing_enabled = 0; \
+ run_queued_signals(); \
+} while (0)
+
+#define restore_queue_signals(q) do { \
+ DPUTS2(queueing_enabled && queue_in != q, \
+ "BUG: q = %d != queue_in = %d", q, queue_in); \
+ queue_in = (queueing_enabled = (q)); \
+} while (0)
+
+#else /* !DEBUG */
+
+#define queue_signals() (queueing_enabled++)
+
+#define unqueue_signals() do { \
+ if (!--queueing_enabled) run_queued_signals(); \
+} while (0)
#define dont_queue_signals() do { \
queueing_enabled = 0; \
@@ -108,6 +129,10 @@
#define restore_queue_signals(q) (queueing_enabled = (q))
+#endif /* DEBUG */
+
+#define queue_signal_level() queueing_enabled
+
#ifdef BSD_SIGNALS
#define signal_block(S) sigblock(S)
#else
diff --git a/Src/string.c b/Src/string.c
index 04e7446c9..a8da14fe0 100644
--- a/Src/string.c
+++ b/Src/string.c
@@ -41,6 +41,36 @@ dupstring(const char *s)
return t;
}
+/* Duplicate string on heap when length is known */
+
+/**/
+mod_export char *
+dupstring_wlen(const char *s, unsigned len)
+{
+ char *t;
+
+ if (!s)
+ return NULL;
+ t = (char *) zhalloc(len + 1);
+ strcpy(t, s);
+ return t;
+}
+
+/* Duplicate string on heap, returning length of string */
+
+/**/
+mod_export char *
+dupstring_glen(const char *s, unsigned *len_ret)
+{
+ char *t;
+
+ if (!s)
+ return NULL;
+ t = (char *) zhalloc((*len_ret = strlen((char *)s)) + 1);
+ strcpy(t, s);
+ return t;
+}
+
/**/
mod_export char *
ztrdup(const char *s)
diff --git a/Src/subst.c b/Src/subst.c
index bb1dd8939..06d2c9ea9 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -145,8 +145,12 @@ stringsubstquote(char *strstart, char **pstrdpos)
strret = dyncat(strstart, strsub);
} else if (strdpos[len])
strret = dyncat(strsub, strdpos + len);
- else
+ else if (*strsub)
strret = strsub;
+ else {
+ /* This ensures a $'' doesn't get elided. */
+ strret = dupstring(nulstring);
+ }
*pstrdpos = strret + (strdpos - strstart) + strlen(strsub);
@@ -407,7 +411,9 @@ globlist(LinkList list, int nountok)
next = nextnode(node);
zglob(list, node, nountok);
}
- if (badcshglob == 1)
+ if (noerrs)
+ badcshglob = 0;
+ else if (badcshglob == 1)
zerr("no match");
}
@@ -623,7 +629,7 @@ equalsubstr(char *str, int assign, int nomatch)
cmdstr = dupstrpfx(str, pp-str);
untokenize(cmdstr);
remnulargs(cmdstr);
- if (!(cnam = findcmd(cmdstr, 1))) {
+ if (!(cnam = findcmd(cmdstr, 1, 0))) {
if (nomatch)
zerr("%s not found", cmdstr);
return NULL;
@@ -680,19 +686,19 @@ filesubstr(char **namptr, int assign)
*namptr = dyncat(ds, ptr);
return 1;
} else if ((ptr = itype_end(str+1, IUSER, 0)) != str+1) { /* ~foo */
- char *hom, save;
+ char *untok, *hom;
- save = *ptr;
- if (!isend(save))
+ if (!isend(*ptr))
return 0;
- *ptr = 0;
- if (!(hom = getnameddir(++str))) {
- if (isset(NOMATCH))
- zerr("no such user or named directory: %s", str);
- *ptr = save;
+ untok = dupstring(++str);
+ untok[ptr-str] = 0;
+ untokenize(untok);
+
+ if (!(hom = getnameddir(untok))) {
+ if (isset(NOMATCH) && isset(EXECOPT))
+ zerr("no such user or named directory: %s", untok);
return 0;
}
- *ptr = save;
*namptr = dyncat(hom, ptr);
return 1;
}
@@ -2364,7 +2370,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
* This is the inner handling for the case referred to above
* where we have something like ${${(P)name}...}.
*
- * Treat this as as a normal value here; all transformations on
+ * Treat this as a normal value here; all transformations on
* result are in outer instance.
*/
aspar = 0;
@@ -2544,12 +2550,19 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
* necessary joining of arrays until this point
* to avoid the multsub() horror.
*/
- int tmplen = arrlen(v->pm->gsu.a->getfn(v->pm));
- if (v->start < 0)
+ /* arrlen() is expensive, so only compute it if needed. */
+ int tmplen = -1;
+
+ if (v->start < 0) {
+ tmplen = arrlen(v->pm->gsu.a->getfn(v->pm));
v->start += tmplen + ((v->flags & VALFLAG_INV) ? 1 : 0);
- if (!(v->flags & VALFLAG_INV) &&
- (v->start >= tmplen || v->start < 0))
+ }
+ if (!(v->flags & VALFLAG_INV))
+ if (v->start < 0 ||
+ (tmplen != -1
+ ? v->start >= tmplen
+ : arrlen_le(v->pm->gsu.a->getfn(v->pm), v->start)))
vunset = 1;
}
if (!vunset) {
@@ -2886,6 +2899,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
aval = paramvalarr(pm->gsu.h->getfn(pm), hkeys|hvals);
} else
setaparam(idbeg, a);
+ isarr = 1;
} else {
untokenize(val);
setsparam(idbeg, ztrdup(val));
@@ -2910,6 +2924,13 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
if (isset(EXECOPT)) {
*idend = '\0';
zerr("%s: %s", idbeg, *s ? s : "parameter not set");
+ /*
+ * In interactive shell we need to return to
+ * top-level prompt --- don't clear this error
+ * after handling a command as we do with
+ * most errors.
+ */
+ errflag |= ERRFLAG_HARD;
if (!interact) {
if (mypid == getpid()) {
/*
@@ -3443,13 +3464,24 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
* exception is that ${name:-word} and ${name:+word} will have already
* done any requested splitting of the word value with quoting preserved.
*/
- if (ssub || (spbreak && isarr >= 0) || spsep || sep) {
+ if (ssub || spbreak || spsep || sep) {
+ int force_split = !ssub && (spbreak || spsep);
if (isarr) {
- val = sepjoin(aval, sep, 1);
- isarr = 0;
- ms_flags = 0;
+ /* sep non-null here means F or j flag, force join */
+ if (nojoin == 0 || sep) {
+ val = sepjoin(aval, sep, 1);
+ isarr = 0;
+ ms_flags = 0;
+ } else if (force_split && (spsep || nojoin == 2)) {
+ /* Hack to simulate splitting individual elements:
+ * forced joining as previously determined, or
+ * join on what we later use to forcibly split
+ */
+ val = sepjoin(aval, (nojoin == 1 ? NULL : spsep), 1);
+ isarr = 0;
+ }
}
- if (!ssub && (spbreak || spsep)) {
+ if (force_split && !isarr) {
aval = sepsplit(val, spsep, 0, 1);
if (!aval || !aval[0])
val = dupstring("");
@@ -3516,7 +3548,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
}
/*
* TODO: It would be really quite nice to abstract the
- * isarr and !issarr code into a function which gets
+ * isarr and !isarr code into a function which gets
* passed a pointer to a function with the effect of
* the promptexpand bit. Then we could use this for
* a lot of stuff and bury val/aval/isarr inside a structure
@@ -3592,20 +3624,20 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
char *tmp;
for (; *ap; ap++) {
- tmp = quotestring(*ap, NULL, quotetype);
+ tmp = quotestring(*ap, quotetype);
sl = strlen(tmp);
*ap = (char *) zhalloc(pre + sl + post + 1);
strcpy((*ap) + pre, tmp);
if (pre)
ap[0][pre - 1] = ap[0][pre + sl] =
(quotetype != QT_DOUBLE ? '\'' : '"');
- ap[0][pre + sl + 1] = '\0';
+ ap[0][pre + sl + post] = '\0';
if (quotetype == QT_DOLLARS)
ap[0][0] = '$';
}
} else
for (; *ap; ap++)
- *ap = quotestring(*ap, NULL, QT_BACKSLASH_SHOWNULL);
+ *ap = quotestring(*ap, QT_BACKSLASH_SHOWNULL);
} else {
int one = noerrs, oef = errflag, haserr = 0;
@@ -3635,18 +3667,18 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
} else if (quotetype > QT_BACKSLASH) {
int sl;
char *tmp;
- tmp = quotestring(val, NULL, quotetype);
+ tmp = quotestring(val, quotetype);
sl = strlen(tmp);
- val = (char *) zhalloc(pre + sl + 2);
+ val = (char *) zhalloc(pre + sl + post + 1);
strcpy(val + pre, tmp);
if (pre)
val[pre - 1] = val[pre + sl] =
(quotetype != QT_DOUBLE ? '\'' : '"');
- val[pre + sl + 1] = '\0';
+ val[pre + sl + post] = '\0';
if (quotetype == QT_DOLLARS)
val[0] = '$';
} else
- val = quotestring(val, NULL, QT_BACKSLASH_SHOWNULL);
+ val = quotestring(val, QT_BACKSLASH_SHOWNULL);
} else {
int one = noerrs, oef = errflag, haserr;
@@ -4011,6 +4043,19 @@ arithsubst(char *a, char **bptr, char *rest)
return t;
}
+/* This function implements colon modifiers.
+ *
+ * STR is an in/out parameter. On entry it is the string (e.g., path)
+ * to modified. On return it is the modified path.
+ *
+ * PTR is an in/out parameter. On entry it contains the string of colon
+ * modifiers. On return it points past the last recognised modifier.
+ *
+ * Example:
+ * ENTRY: *str is "." *ptr is ":AN"
+ * RETURN: *str is "/home/foobar" (equal to $PWD) *ptr points to the "N"
+ */
+
/**/
void
modify(char **str, char **ptr)
@@ -4046,6 +4091,7 @@ modify(char **str, char **ptr)
case 'u':
case 'q':
case 'Q':
+ case 'P':
c = **ptr;
break;
@@ -4237,7 +4283,7 @@ modify(char **str, char **ptr)
subst(&copy, hsubl, hsubr, gbal);
break;
case 'q':
- copy = quotestring(copy, NULL, QT_BACKSLASH_SHOWNULL);
+ copy = quotestring(copy, QT_BACKSLASH_SHOWNULL);
break;
case 'Q':
{
@@ -4252,6 +4298,12 @@ modify(char **str, char **ptr)
untokenize(copy);
}
break;
+ case 'P':
+ if (*copy != '/') {
+ copy = zhtricat(metafy(zgetcwd(), -1, META_HEAPDUP), "/", copy);
+ }
+ copy = xsymlink(copy, 1);
+ break;
}
tc = *tt;
*tt = '\0';
@@ -4313,7 +4365,7 @@ modify(char **str, char **ptr)
subst(str, hsubl, hsubr, gbal);
break;
case 'q':
- *str = quotestring(*str, NULL, QT_BACKSLASH);
+ *str = quotestring(*str, QT_BACKSLASH);
break;
case 'Q':
{
@@ -4328,6 +4380,12 @@ modify(char **str, char **ptr)
untokenize(*str);
}
break;
+ case 'P':
+ if (**str != '/') {
+ *str = zhtricat(metafy(zgetcwd(), -1, META_HEAPDUP), "/", *str);
+ }
+ *str = xsymlink(*str, 1);
+ break;
}
}
if (rec < 0) {
diff --git a/Src/text.c b/Src/text.c
index 04acd2aac..3658b1bc6 100644
--- a/Src/text.c
+++ b/Src/text.c
@@ -46,8 +46,8 @@ int text_expand_tabs;
* et seq. in zsh.h.
*/
static const char *cond_binary_ops[] = {
- "=", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq",
- "-ne", "-lt", "-gt", "-le", "-ge", "=~"
+ "=", "==", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq",
+ "-ne", "-lt", "-gt", "-le", "-ge", "=~", NULL
};
static char *tptr, *tbuf, *tlim, *tpending;
@@ -934,6 +934,7 @@ gettext2(Estate state)
taddstr(" ");
taddstr(ecgetstr(state, EC_NODUP, NULL));
if (ctype == COND_STREQ ||
+ ctype == COND_STRDEQ ||
ctype == COND_STRNEQ)
state->pc++;
} else {
@@ -1068,11 +1069,11 @@ getredirs(LinkList redirs)
*/
if (!has_token(f->name)) {
taddchr('\'');
- taddstr(quotestring(f->name, NULL, QT_SINGLE));
+ taddstr(quotestring(f->name, QT_SINGLE));
taddchr('\'');
} else {
taddchr('"');
- taddstr(quotestring(f->name, NULL, QT_DOUBLE));
+ taddstr(quotestring(f->name, QT_DOUBLE));
taddchr('"');
}
if (sav)
diff --git a/Src/utils.c b/Src/utils.c
index fd0bab320..7f3ddad40 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -56,12 +56,12 @@ typedef struct widechar_array *Widechar_array;
* The wordchars variable turned into a wide character array.
* This is much more convenient for testing.
*/
-struct widechar_array wordchars_wide;
+static struct widechar_array wordchars_wide;
/*
* The same for the separators (IFS) array.
*/
-struct widechar_array ifs_wide;
+static struct widechar_array ifs_wide;
/* Function to set one of the above from the multibyte array */
@@ -84,7 +84,15 @@ set_widearray(char *mb_array, Widechar_array wca)
mb_charinit();
while (*mb_array) {
- int mblen = mb_metacharlenconv(mb_array, &wci);
+ int mblen;
+
+ if (STOUC(*mb_array) <= 0x7f) {
+ mb_array++;
+ *wcptr++ = (wchar_t)*mb_array;
+ continue;
+ }
+
+ mblen = mb_metacharlenconv(mb_array, &wci);
if (!mblen)
break;
@@ -133,9 +141,11 @@ zwarning(const char *cmd, const char *fmt, va_list ap)
if (isatty(2))
zleentry(ZLE_CMD_TRASH);
+ char *prefix = scriptname ? scriptname : (argzero ? argzero : "");
+
if (cmd) {
if (unset(SHINSTDIN) || locallevel) {
- nicezputs(scriptname ? scriptname : argzero, stderr);
+ nicezputs(prefix, stderr);
fputc((unsigned char)':', stderr);
}
nicezputs(cmd, stderr);
@@ -147,8 +157,7 @@ zwarning(const char *cmd, const char *fmt, va_list ap)
* program/script is running. It's also set in shell functions,
* so test locallevel, too.
*/
- nicezputs((isset(SHINSTDIN) && !locallevel) ? "zsh" :
- scriptname ? scriptname : argzero, stderr);
+ nicezputs((isset(SHINSTDIN) && !locallevel) ? "zsh" : prefix, stderr);
fputc((unsigned char)':', stderr);
}
@@ -169,12 +178,12 @@ VA_DCL
errflag |= ERRFLAG_ERROR;
return;
}
+ errflag |= ERRFLAG_ERROR;
VA_START(ap, fmt);
VA_GET_ARG(ap, fmt, const char *);
zwarning(NULL, fmt, ap);
va_end(ap);
- errflag |= ERRFLAG_ERROR;
}
/**/
@@ -188,13 +197,13 @@ VA_DCL
if (errflag || noerrs)
return;
+ errflag |= ERRFLAG_ERROR;
VA_START(ap, fmt);
VA_GET_ARG(ap, cmd, const char *);
VA_GET_ARG(ap, fmt, const char *);
zwarning(cmd, fmt, ap);
va_end(ap);
- errflag |= ERRFLAG_ERROR;
}
/**/
@@ -800,9 +809,9 @@ findpwd(char *s)
char *t;
if (*s == '/')
- return xsymlink(s);
+ return xsymlink(s, 0);
s = tricat((pwd[1]) ? pwd : "", "/", s);
- t = xsymlink(s);
+ t = xsymlink(s, 0);
zsfree(s);
return t;
}
@@ -836,7 +845,7 @@ ispwd(char *s)
return 0;
}
-static char xbuf[PATH_MAX*2];
+static char xbuf[PATH_MAX*2+1];
/**/
static char **
@@ -875,7 +884,7 @@ static int
xsymlinks(char *s, int full)
{
char **pp, **opp;
- char xbuf2[PATH_MAX*3], xbuf3[PATH_MAX*2];
+ char xbuf2[PATH_MAX*3+1], xbuf3[PATH_MAX*2+1];
int t0, ret = 0;
zulong xbuflen = strlen(xbuf);
@@ -968,11 +977,13 @@ xsymlinks(char *s, int full)
/*
* expand symlinks in s, and remove other weird things:
* note that this always expands symlinks.
+ *
+ * 'heap' indicates whether to malloc() or allocate on the heap.
*/
/**/
char *
-xsymlink(char *s)
+xsymlink(char *s, int heap)
{
if (*s != '/')
return NULL;
@@ -980,8 +991,8 @@ xsymlink(char *s)
if (xsymlinks(s + 1, 1) < 0)
zwarn("path expansion failed, using root directory");
if (!*xbuf)
- return ztrdup("/");
- return ztrdup(xbuf);
+ return heap ? dupstring("/") : ztrdup("/");
+ return heap ? dupstring(xbuf) : ztrdup(xbuf);
}
/**/
@@ -992,7 +1003,7 @@ print_if_link(char *s, int all)
*xbuf = '\0';
if (all) {
char *start = s + 1;
- char xbuflink[PATH_MAX];
+ char xbuflink[PATH_MAX+1];
for (;;) {
if (xsymlinks(start, 0) > 0) {
printf(" -> ");
@@ -1044,9 +1055,9 @@ substnamedir(char *s)
Nameddir d = finddir(s);
if (!d)
- return quotestring(s, NULL, QT_BACKSLASH);
+ return quotestring(s, QT_BACKSLASH);
return zhtricat("~", d->node.nam, quotestring(s + strlen(d->dir),
- NULL, QT_BACKSLASH));
+ QT_BACKSLASH));
}
@@ -1129,7 +1140,7 @@ finddir(char *s)
if(homenode.diff==1)
homenode.diff = 0;
if(!finddir_full)
- finddir_full = zalloc(ffsz = PATH_MAX);
+ finddir_full = zalloc(ffsz = PATH_MAX+1);
finddir_full[0] = 0;
return finddir_last = NULL;
}
@@ -1156,7 +1167,7 @@ finddir(char *s)
scanhashtable(nameddirtab, 0, 0, 0, finddir_scan, 0);
ares = subst_string_by_hook("zsh_directory_name", "d", finddir_full);
- if (ares && arrlen(ares) >= 2 &&
+ if (ares && arrlen_ge(ares, 2) &&
(len = (int)zstrtol(ares[1], NULL, 10)) > finddir_best) {
/* better duplicate this string since it's come from REPLY */
finddir_last = (Nameddir)hcalloc(sizeof(struct nameddir));
@@ -1259,7 +1270,7 @@ getnameddir(char *name)
/* Retrieve an entry from the password table/database for this user. */
struct passwd *pw;
if ((pw = getpwnam(name))) {
- char *dir = isset(CHASELINKS) ? xsymlink(pw->pw_dir)
+ char *dir = isset(CHASELINKS) ? xsymlink(pw->pw_dir, 0)
: ztrdup(pw->pw_dir);
if (dir) {
adduserdir(name, dir, ND_USERNAME, 1);
@@ -1633,7 +1644,7 @@ checkmailpath(char **s)
} else if (S_ISDIR(st.st_mode)) {
LinkList l;
DIR *lock = opendir(unmeta(*s));
- char buf[PATH_MAX * 2], **arr, **ap;
+ char buf[PATH_MAX * 2 + 1], **arr, **ap;
int ct = 1;
if (lock) {
@@ -2161,6 +2172,7 @@ gettempfile(const char *prefix, int use_heap, char **tempname)
#if HAVE_MKSTEMP
char *suffix = prefix ? ".XXXXXX" : "XXXXXX";
+ queue_signals();
if (!prefix && !(prefix = getsparam("TMPPREFIX")))
prefix = DEFAULT_TMPPREFIX;
if (use_heap)
@@ -2177,6 +2189,7 @@ gettempfile(const char *prefix, int use_heap, char **tempname)
#else
int failures = 0;
+ queue_signals();
do {
if (!(fn = gettempname(prefix, use_heap))) {
fd = -1;
@@ -2190,6 +2203,8 @@ gettempfile(const char *prefix, int use_heap, char **tempname)
} while (errno == EEXIST && ++failures < 16);
#endif
*tempname = fn;
+
+ unqueue_signals();
return fd;
}
@@ -2279,6 +2294,46 @@ arrlen(char **s)
return count;
}
+/* Return TRUE iff arrlen(s) >= lower_bound, but more efficiently. */
+
+/**/
+mod_export char
+arrlen_ge(char **s, unsigned lower_bound)
+{
+ while (lower_bound--)
+ if (!*s++)
+ return 0 /* FALSE */;
+
+ return 1 /* TRUE */;
+}
+
+/* Return TRUE iff arrlen(s) > lower_bound, but more efficiently. */
+
+/**/
+mod_export char
+arrlen_gt(char **s, unsigned lower_bound)
+{
+ return arrlen_ge(s, 1+lower_bound);
+}
+
+/* Return TRUE iff arrlen(s) <= upper_bound, but more efficiently. */
+
+/**/
+mod_export char
+arrlen_le(char **s, unsigned upper_bound)
+{
+ return arrlen_lt(s, 1+upper_bound);
+}
+
+/* Return TRUE iff arrlen(s) < upper_bound, but more efficiently. */
+
+/**/
+mod_export char
+arrlen_lt(char **s, unsigned upper_bound)
+{
+ return !arrlen_ge(s, upper_bound);
+}
+
/* Skip over a balanced pair of parenthesis. */
/**/
@@ -2533,7 +2588,7 @@ read_poll(int fd, int *readchar, int polltty, zlong microseconds)
#endif
#endif
- if (fd >= 0 && ret < 0) {
+ if (fd >= 0 && ret < 0 && !errflag) {
/*
* Final attempt: set non-blocking read and try to read a character.
* Praise Bill, this works under Cygwin (nothing else seems to).
@@ -2633,13 +2688,36 @@ zsleep_random(long max_us, time_t end_time)
int
checkrmall(char *s)
{
+ DIR *rmd;
+ int count = 0;
if (!shout)
return 1;
- fprintf(shout, "zsh: sure you want to delete all the files in ");
if (*s != '/') {
- nicezputs(pwd[1] ? pwd : "", shout);
- fputc('/', shout);
- }
+ if (pwd[1])
+ s = zhtricat(pwd, "/", s);
+ else
+ s = dyncat("/", s);
+ }
+ const int max_count = 100;
+ if ((rmd = opendir(unmeta(s)))) {
+ int ignoredots = !isset(GLOBDOTS);
+ while (zreaddir(rmd, ignoredots)) {
+ count++;
+ if (count > max_count)
+ break;
+ }
+ closedir(rmd);
+ }
+ if (count > max_count)
+ fprintf(shout, "zsh: sure you want to delete more than %d files in ",
+ max_count);
+ else if (count == 1)
+ fprintf(shout, "zsh: sure you want to delete the only file in ");
+ else if (count > 0)
+ fprintf(shout, "zsh: sure you want to delete all %d files in ",
+ count);
+ else
+ fprintf(shout, "zsh: sure you want to delete all the files in ");
nicezputs(s, shout);
if(isset(RMSTARWAIT)) {
fputs("? (waiting ten seconds)", shout);
@@ -2866,9 +2944,7 @@ mod_export void
spckword(char **s, int hist, int cmd, int ask)
{
char *t, *correct_ignore;
- int x;
char ic = '\0';
- int ne;
int preflen = 0;
int autocd = cmd && isset(AUTOCD) && strcmp(*s, ".") && strcmp(*s, "..");
@@ -2937,6 +3013,7 @@ spckword(char **s, int hist, int cmd, int ask)
} else {
guess = *s;
if (*guess == Tilde || *guess == String) {
+ int ne;
ic = *guess;
if (!*++t)
return;
@@ -2981,6 +3058,7 @@ spckword(char **s, int hist, int cmd, int ask)
if (errflag)
return;
if (best && (int)strlen(best) > 1 && strcmp(best, guess)) {
+ int x;
if (ic) {
char *u;
if (preflen) {
@@ -3010,14 +3088,14 @@ spckword(char **s, int hist, int cmd, int ask)
free(pptbuf);
fflush(shout);
zbeep();
- x = getquery("nyae \t", 0);
+ x = getquery("nyae", 0);
if (cmd && x == 'n')
pathchecked = path;
} else
x = 'n';
} else
x = 'y';
- if (x == 'y' || x == ' ' || x == '\t') {
+ if (x == 'y') {
*s = dupstring(best);
if (hist)
hwrep(best);
@@ -3881,7 +3959,7 @@ inittyptab(void)
#endif
/* typtab['.'] |= IIDENT; */ /* Allow '.' in variable names - broken */
typtab['_'] = IIDENT | IUSER;
- typtab['-'] = typtab['.'] = IUSER;
+ typtab['-'] = typtab['.'] = typtab[STOUC(Dash)] = IUSER;
typtab[' '] |= IBLANK | INBLANK;
typtab['\t'] |= IBLANK | INBLANK;
typtab['\n'] |= INBLANK;
@@ -4079,42 +4157,50 @@ itype_end(const char *ptr, int itype, int once)
(itype != IIDENT || !isset(POSIXIDENTIFIERS))) {
mb_charinit();
while (*ptr) {
- wint_t wc;
- int len = mb_metacharlenconv(ptr, &wc);
-
- if (!len)
- break;
-
- if (wc == WEOF) {
- /* invalid, treat as single character */
- int chr = STOUC(*ptr == Meta ? ptr[1] ^ 32 : *ptr);
- /* in this case non-ASCII characters can't match */
- if (chr > 127 || !zistype(chr,itype))
- break;
- } else if (len == 1 && isascii(*ptr)) {
- /* ASCII: can't be metafied, use standard test */
+ int len;
+ if (itok(*ptr)) {
+ /* Not untokenised yet --- can happen in raw command line */
+ len = 1;
if (!zistype(*ptr,itype))
break;
} else {
- /*
- * Valid non-ASCII character.
- */
- switch (itype) {
- case IWORD:
- if (!iswalnum(wc) &&
- !wmemchr(wordchars_wide.chars, wc,
- wordchars_wide.len))
- return (char *)ptr;
- break;
+ wint_t wc;
+ len = mb_metacharlenconv(ptr, &wc);
- case ISEP:
- if (!wmemchr(ifs_wide.chars, wc, ifs_wide.len))
- return (char *)ptr;
+ if (!len)
break;
- default:
- if (!iswalnum(wc))
- return (char *)ptr;
+ if (wc == WEOF) {
+ /* invalid, treat as single character */
+ int chr = STOUC(*ptr == Meta ? ptr[1] ^ 32 : *ptr);
+ /* in this case non-ASCII characters can't match */
+ if (chr > 127 || !zistype(chr,itype))
+ break;
+ } else if (len == 1 && isascii(*ptr)) {
+ /* ASCII: can't be metafied, use standard test */
+ if (!zistype(*ptr,itype))
+ break;
+ } else {
+ /*
+ * Valid non-ASCII character.
+ */
+ switch (itype) {
+ case IWORD:
+ if (!iswalnum(wc) &&
+ !wmemchr(wordchars_wide.chars, wc,
+ wordchars_wide.len))
+ return (char *)ptr;
+ break;
+
+ case ISEP:
+ if (!wmemchr(ifs_wide.chars, wc, ifs_wide.len))
+ return (char *)ptr;
+ break;
+
+ default:
+ if (!iswalnum(wc))
+ return (char *)ptr;
+ }
}
}
ptr += len;
@@ -4159,6 +4245,32 @@ arrdup(char **s)
return y;
}
+/* Duplicate at most max elements of the array s with heap memory */
+
+/**/
+mod_export char **
+arrdup_max(char **s, unsigned max)
+{
+ char **x, **y, **send;
+ int len = 0;
+
+ if (max)
+ len = arrlen(s);
+
+ /* Limit has sense only if not equal to len */
+ if (max > len)
+ max = len;
+
+ y = x = (char **) zhalloc(sizeof(char *) * (max + 1));
+
+ send = s + max;
+ while (s < send)
+ *x++ = dupstring(*s++);
+ *x = NULL;
+
+ return y;
+}
+
/**/
mod_export char **
zarrdup(char **s)
@@ -5016,8 +5128,10 @@ mb_niceformat(const char *s, FILE *stream, char **outstrp, int flags)
cnt = 1;
/* FALL THROUGH */
default:
- if (c == L'\'' && (flags & NICEFLAG_QUOTE))
+ if (c == L'\'' && (flags & NICEFLAG_QUOTE)) {
fmt = "\\'";
+ newl = 2;
+ }
else
fmt = wcs_nicechar_sel(c, &newl, NULL, flags & NICEFLAG_QUOTE);
break;
@@ -5152,6 +5266,12 @@ mb_metacharlenconv_r(const char *s, wint_t *wcp, mbstate_t *mbsp)
const char *ptr;
wchar_t wc;
+ if (STOUC(*s) <= 0x7f) {
+ if (wcp)
+ *wcp = (wint_t)*s;
+ return 1;
+ }
+
for (ptr = s; *ptr; ) {
if (*ptr == Meta) {
inchar = *++ptr ^ 32;
@@ -5204,7 +5324,7 @@ mb_metacharlenconv_r(const char *s, wint_t *wcp, mbstate_t *mbsp)
mod_export int
mb_metacharlenconv(const char *s, wint_t *wcp)
{
- if (!isset(MULTIBYTE)) {
+ if (!isset(MULTIBYTE) || STOUC(*s) <= 0x7f) {
/* treat as single byte, possibly metafied */
if (wcp)
*wcp = (wint_t)(*s == Meta ? s[1] ^ 32 : *s);
@@ -5251,7 +5371,7 @@ mb_metastrlenend(char *ptr, int width, char *eptr)
char inchar, *laststart;
size_t ret;
wchar_t wc;
- int num, num_in_char;
+ int num, num_in_char, complete;
if (!isset(MULTIBYTE))
return ztrlen(ptr);
@@ -5259,6 +5379,7 @@ mb_metastrlenend(char *ptr, int width, char *eptr)
laststart = ptr;
ret = MB_INVALID;
num = num_in_char = 0;
+ complete = 1;
memset(&mb_shiftstate, 0, sizeof(mb_shiftstate));
while (*ptr && !(eptr && ptr >= eptr)) {
@@ -5267,6 +5388,18 @@ mb_metastrlenend(char *ptr, int width, char *eptr)
else
inchar = *ptr;
ptr++;
+
+ if (complete && STOUC(inchar) <= STOUC(0x7f)) {
+ /*
+ * We rely on 7-bit US-ASCII as a subset, so skip
+ * multibyte handling if we have such a character.
+ */
+ num++;
+ laststart = ptr;
+ num_in_char = 0;
+ continue;
+ }
+
ret = mbrtowc(&wc, &inchar, 1, &mb_shiftstate);
if (ret == MB_INCOMPLETE) {
@@ -5286,6 +5419,7 @@ mb_metastrlenend(char *ptr, int width, char *eptr)
* so we don't count characters twice.
*/
num_in_char++;
+ complete = 0;
} else {
if (ret == MB_INVALID) {
/* Reset, treat as single character */
@@ -5308,6 +5442,7 @@ mb_metastrlenend(char *ptr, int width, char *eptr)
num++;
laststart = ptr;
num_in_char = 0;
+ complete = 1;
}
}
@@ -5330,6 +5465,12 @@ mb_charlenconv_r(const char *s, int slen, wint_t *wcp, mbstate_t *mbsp)
const char *ptr;
wchar_t wc;
+ if (slen && STOUC(*s) <= 0x7f) {
+ if (wcp)
+ *wcp = (wint_t)*s;
+ return 1;
+ }
+
for (ptr = s; slen; ) {
inchar = *ptr;
ptr++;
@@ -5365,7 +5506,7 @@ mb_charlenconv_r(const char *s, int slen, wint_t *wcp, mbstate_t *mbsp)
mod_export int
mb_charlenconv(const char *s, int slen, wint_t *wcp)
{
- if (!isset(MULTIBYTE)) {
+ if (!isset(MULTIBYTE) || STOUC(*s) <= 0x7f) {
if (wcp)
*wcp = (wint_t)*s;
return 1;
@@ -5581,10 +5722,6 @@ addunprintable(char *v, const char *u, const char *uend)
/*
* Quote the string s and return the result as a string from the heap.
*
- * If e is non-zero, the
- * pointer it points to may point to a position in s and in e the position
- * of the corresponding character in the quoted string is returned.
- *
* The last argument is a QT_ value defined in zsh.h other than QT_NONE.
*
* Most quote styles other than backslash assume the quotes are to
@@ -5597,13 +5734,13 @@ addunprintable(char *v, const char *u, const char *uend)
/**/
mod_export char *
-quotestring(const char *s, char **e, int instring)
+quotestring(const char *s, int instring)
{
const char *u;
char *v;
int alloclen;
char *buf;
- int sf = 0, shownull = 0;
+ int shownull = 0;
/*
* quotesub is used with QT_SINGLE_OPTIONAL.
* quotesub = 0: mechanism not active
@@ -5674,10 +5811,6 @@ quotestring(const char *s, char **e, int instring)
while (*u) {
uend = u + MB_METACHARLENCONV(u, &cc);
- if (e && !sf && *e <= u) {
- *e = v;
- sf = 1;
- }
if (
#ifdef MULTIBYTE_SUPPORT
cc != WEOF &&
@@ -5704,11 +5837,6 @@ quotestring(const char *s, char **e, int instring)
}
} else if (instring == QT_BACKSLASH_PATTERN) {
while (*u) {
- if (e && !sf && *e == u) {
- *e = v;
- sf = 1;
- }
-
if (ipattern(*u))
*v++ = '\\';
*v++ = *u++;
@@ -5727,8 +5855,6 @@ quotestring(const char *s, char **e, int instring)
*/
while (*u) {
int dobackslash = 0;
- if (e && *e == u)
- *e = v, sf = 1;
if (*u == Tick || *u == Qtick) {
char c = *u++;
@@ -5916,10 +6042,6 @@ quotestring(const char *s, char **e, int instring)
*v++ = '\'';
*v = '\0';
- if (e && *e == u)
- *e = v, sf = 1;
- DPUTS(e && !sf, "BUG: Wild pointer *e in quotestring()");
-
v = dupstring(buf);
zfree(buf, alloclen);
return v;
@@ -6794,7 +6916,7 @@ strsfx(char *s, char *t)
static int
upchdir(int n)
{
- char buf[PATH_MAX];
+ char buf[PATH_MAX+1];
char *s;
int err = -1;
diff --git a/Src/wcwidth9.h b/Src/wcwidth9.h
new file mode 100644
index 000000000..07e6bae1c
--- /dev/null
+++ b/Src/wcwidth9.h
@@ -0,0 +1,1321 @@
+#ifndef WCWIDTH9_H
+#define WCWIDTH9_H
+
+#include <stdlib.h>
+#include <stdbool.h>
+
+struct wcwidth9_interval {
+ long first;
+ long last;
+};
+
+static const struct wcwidth9_interval wcwidth9_private[] = {
+ {0x00e000, 0x00f8ff},
+ {0x0f0000, 0x0ffffd},
+ {0x100000, 0x10fffd},
+};
+
+static const struct wcwidth9_interval wcwidth9_nonprint[] = {
+ {0x0000, 0x001f},
+ {0x007f, 0x009f},
+ {0x00ad, 0x00ad},
+ {0x070f, 0x070f},
+ {0x180b, 0x180e},
+ {0x200b, 0x200f},
+ {0x202a, 0x202e},
+ {0x206a, 0x206f},
+ {0xd800, 0xdfff},
+ {0xfeff, 0xfeff},
+ {0xfff9, 0xfffb},
+ {0xfffe, 0xffff},
+};
+
+static const struct wcwidth9_interval wcwidth9_combining[] = {
+ {0x0300, 0x036f},
+ {0x0483, 0x0489},
+ {0x0591, 0x05bd},
+ {0x05bf, 0x05bf},
+ {0x05c1, 0x05c2},
+ {0x05c4, 0x05c5},
+ {0x05c7, 0x05c7},
+ {0x0610, 0x061a},
+ {0x064b, 0x065f},
+ {0x0670, 0x0670},
+ {0x06d6, 0x06dc},
+ {0x06df, 0x06e4},
+ {0x06e7, 0x06e8},
+ {0x06ea, 0x06ed},
+ {0x0711, 0x0711},
+ {0x0730, 0x074a},
+ {0x07a6, 0x07b0},
+ {0x07eb, 0x07f3},
+ {0x0816, 0x0819},
+ {0x081b, 0x0823},
+ {0x0825, 0x0827},
+ {0x0829, 0x082d},
+ {0x0859, 0x085b},
+ {0x08d4, 0x08e1},
+ {0x08e3, 0x0903},
+ {0x093a, 0x093c},
+ {0x093e, 0x094f},
+ {0x0951, 0x0957},
+ {0x0962, 0x0963},
+ {0x0981, 0x0983},
+ {0x09bc, 0x09bc},
+ {0x09be, 0x09c4},
+ {0x09c7, 0x09c8},
+ {0x09cb, 0x09cd},
+ {0x09d7, 0x09d7},
+ {0x09e2, 0x09e3},
+ {0x0a01, 0x0a03},
+ {0x0a3c, 0x0a3c},
+ {0x0a3e, 0x0a42},
+ {0x0a47, 0x0a48},
+ {0x0a4b, 0x0a4d},
+ {0x0a51, 0x0a51},
+ {0x0a70, 0x0a71},
+ {0x0a75, 0x0a75},
+ {0x0a81, 0x0a83},
+ {0x0abc, 0x0abc},
+ {0x0abe, 0x0ac5},
+ {0x0ac7, 0x0ac9},
+ {0x0acb, 0x0acd},
+ {0x0ae2, 0x0ae3},
+ {0x0b01, 0x0b03},
+ {0x0b3c, 0x0b3c},
+ {0x0b3e, 0x0b44},
+ {0x0b47, 0x0b48},
+ {0x0b4b, 0x0b4d},
+ {0x0b56, 0x0b57},
+ {0x0b62, 0x0b63},
+ {0x0b82, 0x0b82},
+ {0x0bbe, 0x0bc2},
+ {0x0bc6, 0x0bc8},
+ {0x0bca, 0x0bcd},
+ {0x0bd7, 0x0bd7},
+ {0x0c00, 0x0c03},
+ {0x0c3e, 0x0c44},
+ {0x0c46, 0x0c48},
+ {0x0c4a, 0x0c4d},
+ {0x0c55, 0x0c56},
+ {0x0c62, 0x0c63},
+ {0x0c81, 0x0c83},
+ {0x0cbc, 0x0cbc},
+ {0x0cbe, 0x0cc4},
+ {0x0cc6, 0x0cc8},
+ {0x0cca, 0x0ccd},
+ {0x0cd5, 0x0cd6},
+ {0x0ce2, 0x0ce3},
+ {0x0d01, 0x0d03},
+ {0x0d3e, 0x0d44},
+ {0x0d46, 0x0d48},
+ {0x0d4a, 0x0d4d},
+ {0x0d57, 0x0d57},
+ {0x0d62, 0x0d63},
+ {0x0d82, 0x0d83},
+ {0x0dca, 0x0dca},
+ {0x0dcf, 0x0dd4},
+ {0x0dd6, 0x0dd6},
+ {0x0dd8, 0x0ddf},
+ {0x0df2, 0x0df3},
+ {0x0e31, 0x0e31},
+ {0x0e34, 0x0e3a},
+ {0x0e47, 0x0e4e},
+ {0x0eb1, 0x0eb1},
+ {0x0eb4, 0x0eb9},
+ {0x0ebb, 0x0ebc},
+ {0x0ec8, 0x0ecd},
+ {0x0f18, 0x0f19},
+ {0x0f35, 0x0f35},
+ {0x0f37, 0x0f37},
+ {0x0f39, 0x0f39},
+ {0x0f3e, 0x0f3f},
+ {0x0f71, 0x0f84},
+ {0x0f86, 0x0f87},
+ {0x0f8d, 0x0f97},
+ {0x0f99, 0x0fbc},
+ {0x0fc6, 0x0fc6},
+ {0x102b, 0x103e},
+ {0x1056, 0x1059},
+ {0x105e, 0x1060},
+ {0x1062, 0x1064},
+ {0x1067, 0x106d},
+ {0x1071, 0x1074},
+ {0x1082, 0x108d},
+ {0x108f, 0x108f},
+ {0x109a, 0x109d},
+ {0x135d, 0x135f},
+ {0x1712, 0x1714},
+ {0x1732, 0x1734},
+ {0x1752, 0x1753},
+ {0x1772, 0x1773},
+ {0x17b4, 0x17d3},
+ {0x17dd, 0x17dd},
+ {0x180b, 0x180d},
+ {0x1885, 0x1886},
+ {0x18a9, 0x18a9},
+ {0x1920, 0x192b},
+ {0x1930, 0x193b},
+ {0x1a17, 0x1a1b},
+ {0x1a55, 0x1a5e},
+ {0x1a60, 0x1a7c},
+ {0x1a7f, 0x1a7f},
+ {0x1ab0, 0x1abe},
+ {0x1b00, 0x1b04},
+ {0x1b34, 0x1b44},
+ {0x1b6b, 0x1b73},
+ {0x1b80, 0x1b82},
+ {0x1ba1, 0x1bad},
+ {0x1be6, 0x1bf3},
+ {0x1c24, 0x1c37},
+ {0x1cd0, 0x1cd2},
+ {0x1cd4, 0x1ce8},
+ {0x1ced, 0x1ced},
+ {0x1cf2, 0x1cf4},
+ {0x1cf8, 0x1cf9},
+ {0x1dc0, 0x1df5},
+ {0x1dfb, 0x1dff},
+ {0x20d0, 0x20f0},
+ {0x2cef, 0x2cf1},
+ {0x2d7f, 0x2d7f},
+ {0x2de0, 0x2dff},
+ {0x302a, 0x302f},
+ {0x3099, 0x309a},
+ {0xa66f, 0xa672},
+ {0xa674, 0xa67d},
+ {0xa69e, 0xa69f},
+ {0xa6f0, 0xa6f1},
+ {0xa802, 0xa802},
+ {0xa806, 0xa806},
+ {0xa80b, 0xa80b},
+ {0xa823, 0xa827},
+ {0xa880, 0xa881},
+ {0xa8b4, 0xa8c5},
+ {0xa8e0, 0xa8f1},
+ {0xa926, 0xa92d},
+ {0xa947, 0xa953},
+ {0xa980, 0xa983},
+ {0xa9b3, 0xa9c0},
+ {0xa9e5, 0xa9e5},
+ {0xaa29, 0xaa36},
+ {0xaa43, 0xaa43},
+ {0xaa4c, 0xaa4d},
+ {0xaa7b, 0xaa7d},
+ {0xaab0, 0xaab0},
+ {0xaab2, 0xaab4},
+ {0xaab7, 0xaab8},
+ {0xaabe, 0xaabf},
+ {0xaac1, 0xaac1},
+ {0xaaeb, 0xaaef},
+ {0xaaf5, 0xaaf6},
+ {0xabe3, 0xabea},
+ {0xabec, 0xabed},
+ {0xfb1e, 0xfb1e},
+ {0xfe00, 0xfe0f},
+ {0xfe20, 0xfe2f},
+ {0x101fd, 0x101fd},
+ {0x102e0, 0x102e0},
+ {0x10376, 0x1037a},
+ {0x10a01, 0x10a03},
+ {0x10a05, 0x10a06},
+ {0x10a0c, 0x10a0f},
+ {0x10a38, 0x10a3a},
+ {0x10a3f, 0x10a3f},
+ {0x10ae5, 0x10ae6},
+ {0x11000, 0x11002},
+ {0x11038, 0x11046},
+ {0x1107f, 0x11082},
+ {0x110b0, 0x110ba},
+ {0x11100, 0x11102},
+ {0x11127, 0x11134},
+ {0x11173, 0x11173},
+ {0x11180, 0x11182},
+ {0x111b3, 0x111c0},
+ {0x111ca, 0x111cc},
+ {0x1122c, 0x11237},
+ {0x1123e, 0x1123e},
+ {0x112df, 0x112ea},
+ {0x11300, 0x11303},
+ {0x1133c, 0x1133c},
+ {0x1133e, 0x11344},
+ {0x11347, 0x11348},
+ {0x1134b, 0x1134d},
+ {0x11357, 0x11357},
+ {0x11362, 0x11363},
+ {0x11366, 0x1136c},
+ {0x11370, 0x11374},
+ {0x11435, 0x11446},
+ {0x114b0, 0x114c3},
+ {0x115af, 0x115b5},
+ {0x115b8, 0x115c0},
+ {0x115dc, 0x115dd},
+ {0x11630, 0x11640},
+ {0x116ab, 0x116b7},
+ {0x1171d, 0x1172b},
+ {0x11c2f, 0x11c36},
+ {0x11c38, 0x11c3f},
+ {0x11c92, 0x11ca7},
+ {0x11ca9, 0x11cb6},
+ {0x16af0, 0x16af4},
+ {0x16b30, 0x16b36},
+ {0x16f51, 0x16f7e},
+ {0x16f8f, 0x16f92},
+ {0x1bc9d, 0x1bc9e},
+ {0x1d165, 0x1d169},
+ {0x1d16d, 0x1d172},
+ {0x1d17b, 0x1d182},
+ {0x1d185, 0x1d18b},
+ {0x1d1aa, 0x1d1ad},
+ {0x1d242, 0x1d244},
+ {0x1da00, 0x1da36},
+ {0x1da3b, 0x1da6c},
+ {0x1da75, 0x1da75},
+ {0x1da84, 0x1da84},
+ {0x1da9b, 0x1da9f},
+ {0x1daa1, 0x1daaf},
+ {0x1e000, 0x1e006},
+ {0x1e008, 0x1e018},
+ {0x1e01b, 0x1e021},
+ {0x1e023, 0x1e024},
+ {0x1e026, 0x1e02a},
+ {0x1e8d0, 0x1e8d6},
+ {0x1e944, 0x1e94a},
+ {0xe0100, 0xe01ef},
+};
+
+static const struct wcwidth9_interval wcwidth9_doublewidth[] = {
+ {0x1100, 0x115f},
+ {0x231a, 0x231b},
+ {0x2329, 0x232a},
+ {0x23e9, 0x23ec},
+ {0x23f0, 0x23f0},
+ {0x23f3, 0x23f3},
+ {0x25fd, 0x25fe},
+ {0x2614, 0x2615},
+ {0x2648, 0x2653},
+ {0x267f, 0x267f},
+ {0x2693, 0x2693},
+ {0x26a1, 0x26a1},
+ {0x26aa, 0x26ab},
+ {0x26bd, 0x26be},
+ {0x26c4, 0x26c5},
+ {0x26ce, 0x26ce},
+ {0x26d4, 0x26d4},
+ {0x26ea, 0x26ea},
+ {0x26f2, 0x26f3},
+ {0x26f5, 0x26f5},
+ {0x26fa, 0x26fa},
+ {0x26fd, 0x26fd},
+ {0x2705, 0x2705},
+ {0x270a, 0x270b},
+ {0x2728, 0x2728},
+ {0x274c, 0x274c},
+ {0x274e, 0x274e},
+ {0x2753, 0x2755},
+ {0x2757, 0x2757},
+ {0x2795, 0x2797},
+ {0x27b0, 0x27b0},
+ {0x27bf, 0x27bf},
+ {0x2b1b, 0x2b1c},
+ {0x2b50, 0x2b50},
+ {0x2b55, 0x2b55},
+ {0x2e80, 0x2e99},
+ {0x2e9b, 0x2ef3},
+ {0x2f00, 0x2fd5},
+ {0x2ff0, 0x2ffb},
+ {0x3000, 0x303e},
+ {0x3041, 0x3096},
+ {0x3099, 0x30ff},
+ {0x3105, 0x312d},
+ {0x3131, 0x318e},
+ {0x3190, 0x31ba},
+ {0x31c0, 0x31e3},
+ {0x31f0, 0x321e},
+ {0x3220, 0x3247},
+ {0x3250, 0x32fe},
+ {0x3300, 0x4dbf},
+ {0x4e00, 0xa48c},
+ {0xa490, 0xa4c6},
+ {0xa960, 0xa97c},
+ {0xac00, 0xd7a3},
+ {0xf900, 0xfaff},
+ {0xfe10, 0xfe19},
+ {0xfe30, 0xfe52},
+ {0xfe54, 0xfe66},
+ {0xfe68, 0xfe6b},
+ {0xff01, 0xff60},
+ {0xffe0, 0xffe6},
+ {0x16fe0, 0x16fe0},
+ {0x17000, 0x187ec},
+ {0x18800, 0x18af2},
+ {0x1b000, 0x1b001},
+ {0x1f004, 0x1f004},
+ {0x1f0cf, 0x1f0cf},
+ {0x1f18e, 0x1f18e},
+ {0x1f191, 0x1f19a},
+ {0x1f200, 0x1f202},
+ {0x1f210, 0x1f23b},
+ {0x1f240, 0x1f248},
+ {0x1f250, 0x1f251},
+ {0x1f300, 0x1f320},
+ {0x1f32d, 0x1f335},
+ {0x1f337, 0x1f37c},
+ {0x1f37e, 0x1f393},
+ {0x1f3a0, 0x1f3ca},
+ {0x1f3cf, 0x1f3d3},
+ {0x1f3e0, 0x1f3f0},
+ {0x1f3f4, 0x1f3f4},
+ {0x1f3f8, 0x1f43e},
+ {0x1f440, 0x1f440},
+ {0x1f442, 0x1f4fc},
+ {0x1f4ff, 0x1f53d},
+ {0x1f54b, 0x1f54e},
+ {0x1f550, 0x1f567},
+ {0x1f57a, 0x1f57a},
+ {0x1f595, 0x1f596},
+ {0x1f5a4, 0x1f5a4},
+ {0x1f5fb, 0x1f64f},
+ {0x1f680, 0x1f6c5},
+ {0x1f6cc, 0x1f6cc},
+ {0x1f6d0, 0x1f6d2},
+ {0x1f6eb, 0x1f6ec},
+ {0x1f6f4, 0x1f6f6},
+ {0x1f910, 0x1f91e},
+ {0x1f920, 0x1f927},
+ {0x1f930, 0x1f930},
+ {0x1f933, 0x1f93e},
+ {0x1f940, 0x1f94b},
+ {0x1f950, 0x1f95e},
+ {0x1f980, 0x1f991},
+ {0x1f9c0, 0x1f9c0},
+ {0x20000, 0x2fffd},
+ {0x30000, 0x3fffd},
+};
+
+static const struct wcwidth9_interval wcwidth9_ambiguous[] = {
+ {0x00a1, 0x00a1},
+ {0x00a4, 0x00a4},
+ {0x00a7, 0x00a8},
+ {0x00aa, 0x00aa},
+ {0x00ad, 0x00ae},
+ {0x00b0, 0x00b4},
+ {0x00b6, 0x00ba},
+ {0x00bc, 0x00bf},
+ {0x00c6, 0x00c6},
+ {0x00d0, 0x00d0},
+ {0x00d7, 0x00d8},
+ {0x00de, 0x00e1},
+ {0x00e6, 0x00e6},
+ {0x00e8, 0x00ea},
+ {0x00ec, 0x00ed},
+ {0x00f0, 0x00f0},
+ {0x00f2, 0x00f3},
+ {0x00f7, 0x00fa},
+ {0x00fc, 0x00fc},
+ {0x00fe, 0x00fe},
+ {0x0101, 0x0101},
+ {0x0111, 0x0111},
+ {0x0113, 0x0113},
+ {0x011b, 0x011b},
+ {0x0126, 0x0127},
+ {0x012b, 0x012b},
+ {0x0131, 0x0133},
+ {0x0138, 0x0138},
+ {0x013f, 0x0142},
+ {0x0144, 0x0144},
+ {0x0148, 0x014b},
+ {0x014d, 0x014d},
+ {0x0152, 0x0153},
+ {0x0166, 0x0167},
+ {0x016b, 0x016b},
+ {0x01ce, 0x01ce},
+ {0x01d0, 0x01d0},
+ {0x01d2, 0x01d2},
+ {0x01d4, 0x01d4},
+ {0x01d6, 0x01d6},
+ {0x01d8, 0x01d8},
+ {0x01da, 0x01da},
+ {0x01dc, 0x01dc},
+ {0x0251, 0x0251},
+ {0x0261, 0x0261},
+ {0x02c4, 0x02c4},
+ {0x02c7, 0x02c7},
+ {0x02c9, 0x02cb},
+ {0x02cd, 0x02cd},
+ {0x02d0, 0x02d0},
+ {0x02d8, 0x02db},
+ {0x02dd, 0x02dd},
+ {0x02df, 0x02df},
+ {0x0300, 0x036f},
+ {0x0391, 0x03a1},
+ {0x03a3, 0x03a9},
+ {0x03b1, 0x03c1},
+ {0x03c3, 0x03c9},
+ {0x0401, 0x0401},
+ {0x0410, 0x044f},
+ {0x0451, 0x0451},
+ {0x2010, 0x2010},
+ {0x2013, 0x2016},
+ {0x2018, 0x2019},
+ {0x201c, 0x201d},
+ {0x2020, 0x2022},
+ {0x2024, 0x2027},
+ {0x2030, 0x2030},
+ {0x2032, 0x2033},
+ {0x2035, 0x2035},
+ {0x203b, 0x203b},
+ {0x203e, 0x203e},
+ {0x2074, 0x2074},
+ {0x207f, 0x207f},
+ {0x2081, 0x2084},
+ {0x20ac, 0x20ac},
+ {0x2103, 0x2103},
+ {0x2105, 0x2105},
+ {0x2109, 0x2109},
+ {0x2113, 0x2113},
+ {0x2116, 0x2116},
+ {0x2121, 0x2122},
+ {0x2126, 0x2126},
+ {0x212b, 0x212b},
+ {0x2153, 0x2154},
+ {0x215b, 0x215e},
+ {0x2160, 0x216b},
+ {0x2170, 0x2179},
+ {0x2189, 0x2189},
+ {0x2190, 0x2199},
+ {0x21b8, 0x21b9},
+ {0x21d2, 0x21d2},
+ {0x21d4, 0x21d4},
+ {0x21e7, 0x21e7},
+ {0x2200, 0x2200},
+ {0x2202, 0x2203},
+ {0x2207, 0x2208},
+ {0x220b, 0x220b},
+ {0x220f, 0x220f},
+ {0x2211, 0x2211},
+ {0x2215, 0x2215},
+ {0x221a, 0x221a},
+ {0x221d, 0x2220},
+ {0x2223, 0x2223},
+ {0x2225, 0x2225},
+ {0x2227, 0x222c},
+ {0x222e, 0x222e},
+ {0x2234, 0x2237},
+ {0x223c, 0x223d},
+ {0x2248, 0x2248},
+ {0x224c, 0x224c},
+ {0x2252, 0x2252},
+ {0x2260, 0x2261},
+ {0x2264, 0x2267},
+ {0x226a, 0x226b},
+ {0x226e, 0x226f},
+ {0x2282, 0x2283},
+ {0x2286, 0x2287},
+ {0x2295, 0x2295},
+ {0x2299, 0x2299},
+ {0x22a5, 0x22a5},
+ {0x22bf, 0x22bf},
+ {0x2312, 0x2312},
+ {0x2460, 0x24e9},
+ {0x24eb, 0x254b},
+ {0x2550, 0x2573},
+ {0x2580, 0x258f},
+ {0x2592, 0x2595},
+ {0x25a0, 0x25a1},
+ {0x25a3, 0x25a9},
+ {0x25b2, 0x25b3},
+ {0x25b6, 0x25b7},
+ {0x25bc, 0x25bd},
+ {0x25c0, 0x25c1},
+ {0x25c6, 0x25c8},
+ {0x25cb, 0x25cb},
+ {0x25ce, 0x25d1},
+ {0x25e2, 0x25e5},
+ {0x25ef, 0x25ef},
+ {0x2605, 0x2606},
+ {0x2609, 0x2609},
+ {0x260e, 0x260f},
+ {0x261c, 0x261c},
+ {0x261e, 0x261e},
+ {0x2640, 0x2640},
+ {0x2642, 0x2642},
+ {0x2660, 0x2661},
+ {0x2663, 0x2665},
+ {0x2667, 0x266a},
+ {0x266c, 0x266d},
+ {0x266f, 0x266f},
+ {0x269e, 0x269f},
+ {0x26bf, 0x26bf},
+ {0x26c6, 0x26cd},
+ {0x26cf, 0x26d3},
+ {0x26d5, 0x26e1},
+ {0x26e3, 0x26e3},
+ {0x26e8, 0x26e9},
+ {0x26eb, 0x26f1},
+ {0x26f4, 0x26f4},
+ {0x26f6, 0x26f9},
+ {0x26fb, 0x26fc},
+ {0x26fe, 0x26ff},
+ {0x273d, 0x273d},
+ {0x2776, 0x277f},
+ {0x2b56, 0x2b59},
+ {0x3248, 0x324f},
+ {0xe000, 0xf8ff},
+ {0xfe00, 0xfe0f},
+ {0xfffd, 0xfffd},
+ {0x1f100, 0x1f10a},
+ {0x1f110, 0x1f12d},
+ {0x1f130, 0x1f169},
+ {0x1f170, 0x1f18d},
+ {0x1f18f, 0x1f190},
+ {0x1f19b, 0x1f1ac},
+ {0xe0100, 0xe01ef},
+ {0xf0000, 0xffffd},
+ {0x100000, 0x10fffd},
+};
+
+static const struct wcwidth9_interval wcwidth9_emoji_width[] = {
+ {0x1f1e6, 0x1f1ff},
+ {0x1f321, 0x1f321},
+ {0x1f324, 0x1f32c},
+ {0x1f336, 0x1f336},
+ {0x1f37d, 0x1f37d},
+ {0x1f396, 0x1f397},
+ {0x1f399, 0x1f39b},
+ {0x1f39e, 0x1f39f},
+ {0x1f3cb, 0x1f3ce},
+ {0x1f3d4, 0x1f3df},
+ {0x1f3f3, 0x1f3f5},
+ {0x1f3f7, 0x1f3f7},
+ {0x1f43f, 0x1f43f},
+ {0x1f441, 0x1f441},
+ {0x1f4fd, 0x1f4fd},
+ {0x1f549, 0x1f54a},
+ {0x1f56f, 0x1f570},
+ {0x1f573, 0x1f579},
+ {0x1f587, 0x1f587},
+ {0x1f58a, 0x1f58d},
+ {0x1f590, 0x1f590},
+ {0x1f5a5, 0x1f5a5},
+ {0x1f5a8, 0x1f5a8},
+ {0x1f5b1, 0x1f5b2},
+ {0x1f5bc, 0x1f5bc},
+ {0x1f5c2, 0x1f5c4},
+ {0x1f5d1, 0x1f5d3},
+ {0x1f5dc, 0x1f5de},
+ {0x1f5e1, 0x1f5e1},
+ {0x1f5e3, 0x1f5e3},
+ {0x1f5e8, 0x1f5e8},
+ {0x1f5ef, 0x1f5ef},
+ {0x1f5f3, 0x1f5f3},
+ {0x1f5fa, 0x1f5fa},
+ {0x1f6cb, 0x1f6cf},
+ {0x1f6e0, 0x1f6e5},
+ {0x1f6e9, 0x1f6e9},
+ {0x1f6f0, 0x1f6f0},
+ {0x1f6f3, 0x1f6f3},
+};
+
+static const struct wcwidth9_interval wcwidth9_not_assigned[] = {
+ {0x0378, 0x0379},
+ {0x0380, 0x0383},
+ {0x038b, 0x038b},
+ {0x038d, 0x038d},
+ {0x03a2, 0x03a2},
+ {0x0530, 0x0530},
+ {0x0557, 0x0558},
+ {0x0560, 0x0560},
+ {0x0588, 0x0588},
+ {0x058b, 0x058c},
+ {0x0590, 0x0590},
+ {0x05c8, 0x05cf},
+ {0x05eb, 0x05ef},
+ {0x05f5, 0x05ff},
+ {0x061d, 0x061d},
+ {0x070e, 0x070e},
+ {0x074b, 0x074c},
+ {0x07b2, 0x07bf},
+ {0x07fb, 0x07ff},
+ {0x082e, 0x082f},
+ {0x083f, 0x083f},
+ {0x085c, 0x085d},
+ {0x085f, 0x089f},
+ {0x08b5, 0x08b5},
+ {0x08be, 0x08d3},
+ {0x0984, 0x0984},
+ {0x098d, 0x098e},
+ {0x0991, 0x0992},
+ {0x09a9, 0x09a9},
+ {0x09b1, 0x09b1},
+ {0x09b3, 0x09b5},
+ {0x09ba, 0x09bb},
+ {0x09c5, 0x09c6},
+ {0x09c9, 0x09ca},
+ {0x09cf, 0x09d6},
+ {0x09d8, 0x09db},
+ {0x09de, 0x09de},
+ {0x09e4, 0x09e5},
+ {0x09fc, 0x0a00},
+ {0x0a04, 0x0a04},
+ {0x0a0b, 0x0a0e},
+ {0x0a11, 0x0a12},
+ {0x0a29, 0x0a29},
+ {0x0a31, 0x0a31},
+ {0x0a34, 0x0a34},
+ {0x0a37, 0x0a37},
+ {0x0a3a, 0x0a3b},
+ {0x0a3d, 0x0a3d},
+ {0x0a43, 0x0a46},
+ {0x0a49, 0x0a4a},
+ {0x0a4e, 0x0a50},
+ {0x0a52, 0x0a58},
+ {0x0a5d, 0x0a5d},
+ {0x0a5f, 0x0a65},
+ {0x0a76, 0x0a80},
+ {0x0a84, 0x0a84},
+ {0x0a8e, 0x0a8e},
+ {0x0a92, 0x0a92},
+ {0x0aa9, 0x0aa9},
+ {0x0ab1, 0x0ab1},
+ {0x0ab4, 0x0ab4},
+ {0x0aba, 0x0abb},
+ {0x0ac6, 0x0ac6},
+ {0x0aca, 0x0aca},
+ {0x0ace, 0x0acf},
+ {0x0ad1, 0x0adf},
+ {0x0ae4, 0x0ae5},
+ {0x0af2, 0x0af8},
+ {0x0afa, 0x0b00},
+ {0x0b04, 0x0b04},
+ {0x0b0d, 0x0b0e},
+ {0x0b11, 0x0b12},
+ {0x0b29, 0x0b29},
+ {0x0b31, 0x0b31},
+ {0x0b34, 0x0b34},
+ {0x0b3a, 0x0b3b},
+ {0x0b45, 0x0b46},
+ {0x0b49, 0x0b4a},
+ {0x0b4e, 0x0b55},
+ {0x0b58, 0x0b5b},
+ {0x0b5e, 0x0b5e},
+ {0x0b64, 0x0b65},
+ {0x0b78, 0x0b81},
+ {0x0b84, 0x0b84},
+ {0x0b8b, 0x0b8d},
+ {0x0b91, 0x0b91},
+ {0x0b96, 0x0b98},
+ {0x0b9b, 0x0b9b},
+ {0x0b9d, 0x0b9d},
+ {0x0ba0, 0x0ba2},
+ {0x0ba5, 0x0ba7},
+ {0x0bab, 0x0bad},
+ {0x0bba, 0x0bbd},
+ {0x0bc3, 0x0bc5},
+ {0x0bc9, 0x0bc9},
+ {0x0bce, 0x0bcf},
+ {0x0bd1, 0x0bd6},
+ {0x0bd8, 0x0be5},
+ {0x0bfb, 0x0bff},
+ {0x0c04, 0x0c04},
+ {0x0c0d, 0x0c0d},
+ {0x0c11, 0x0c11},
+ {0x0c29, 0x0c29},
+ {0x0c3a, 0x0c3c},
+ {0x0c45, 0x0c45},
+ {0x0c49, 0x0c49},
+ {0x0c4e, 0x0c54},
+ {0x0c57, 0x0c57},
+ {0x0c5b, 0x0c5f},
+ {0x0c64, 0x0c65},
+ {0x0c70, 0x0c77},
+ {0x0c84, 0x0c84},
+ {0x0c8d, 0x0c8d},
+ {0x0c91, 0x0c91},
+ {0x0ca9, 0x0ca9},
+ {0x0cb4, 0x0cb4},
+ {0x0cba, 0x0cbb},
+ {0x0cc5, 0x0cc5},
+ {0x0cc9, 0x0cc9},
+ {0x0cce, 0x0cd4},
+ {0x0cd7, 0x0cdd},
+ {0x0cdf, 0x0cdf},
+ {0x0ce4, 0x0ce5},
+ {0x0cf0, 0x0cf0},
+ {0x0cf3, 0x0d00},
+ {0x0d04, 0x0d04},
+ {0x0d0d, 0x0d0d},
+ {0x0d11, 0x0d11},
+ {0x0d3b, 0x0d3c},
+ {0x0d45, 0x0d45},
+ {0x0d49, 0x0d49},
+ {0x0d50, 0x0d53},
+ {0x0d64, 0x0d65},
+ {0x0d80, 0x0d81},
+ {0x0d84, 0x0d84},
+ {0x0d97, 0x0d99},
+ {0x0db2, 0x0db2},
+ {0x0dbc, 0x0dbc},
+ {0x0dbe, 0x0dbf},
+ {0x0dc7, 0x0dc9},
+ {0x0dcb, 0x0dce},
+ {0x0dd5, 0x0dd5},
+ {0x0dd7, 0x0dd7},
+ {0x0de0, 0x0de5},
+ {0x0df0, 0x0df1},
+ {0x0df5, 0x0e00},
+ {0x0e3b, 0x0e3e},
+ {0x0e5c, 0x0e80},
+ {0x0e83, 0x0e83},
+ {0x0e85, 0x0e86},
+ {0x0e89, 0x0e89},
+ {0x0e8b, 0x0e8c},
+ {0x0e8e, 0x0e93},
+ {0x0e98, 0x0e98},
+ {0x0ea0, 0x0ea0},
+ {0x0ea4, 0x0ea4},
+ {0x0ea6, 0x0ea6},
+ {0x0ea8, 0x0ea9},
+ {0x0eac, 0x0eac},
+ {0x0eba, 0x0eba},
+ {0x0ebe, 0x0ebf},
+ {0x0ec5, 0x0ec5},
+ {0x0ec7, 0x0ec7},
+ {0x0ece, 0x0ecf},
+ {0x0eda, 0x0edb},
+ {0x0ee0, 0x0eff},
+ {0x0f48, 0x0f48},
+ {0x0f6d, 0x0f70},
+ {0x0f98, 0x0f98},
+ {0x0fbd, 0x0fbd},
+ {0x0fcd, 0x0fcd},
+ {0x0fdb, 0x0fff},
+ {0x10c6, 0x10c6},
+ {0x10c8, 0x10cc},
+ {0x10ce, 0x10cf},
+ {0x1249, 0x1249},
+ {0x124e, 0x124f},
+ {0x1257, 0x1257},
+ {0x1259, 0x1259},
+ {0x125e, 0x125f},
+ {0x1289, 0x1289},
+ {0x128e, 0x128f},
+ {0x12b1, 0x12b1},
+ {0x12b6, 0x12b7},
+ {0x12bf, 0x12bf},
+ {0x12c1, 0x12c1},
+ {0x12c6, 0x12c7},
+ {0x12d7, 0x12d7},
+ {0x1311, 0x1311},
+ {0x1316, 0x1317},
+ {0x135b, 0x135c},
+ {0x137d, 0x137f},
+ {0x139a, 0x139f},
+ {0x13f6, 0x13f7},
+ {0x13fe, 0x13ff},
+ {0x169d, 0x169f},
+ {0x16f9, 0x16ff},
+ {0x170d, 0x170d},
+ {0x1715, 0x171f},
+ {0x1737, 0x173f},
+ {0x1754, 0x175f},
+ {0x176d, 0x176d},
+ {0x1771, 0x1771},
+ {0x1774, 0x177f},
+ {0x17de, 0x17df},
+ {0x17ea, 0x17ef},
+ {0x17fa, 0x17ff},
+ {0x180f, 0x180f},
+ {0x181a, 0x181f},
+ {0x1878, 0x187f},
+ {0x18ab, 0x18af},
+ {0x18f6, 0x18ff},
+ {0x191f, 0x191f},
+ {0x192c, 0x192f},
+ {0x193c, 0x193f},
+ {0x1941, 0x1943},
+ {0x196e, 0x196f},
+ {0x1975, 0x197f},
+ {0x19ac, 0x19af},
+ {0x19ca, 0x19cf},
+ {0x19db, 0x19dd},
+ {0x1a1c, 0x1a1d},
+ {0x1a5f, 0x1a5f},
+ {0x1a7d, 0x1a7e},
+ {0x1a8a, 0x1a8f},
+ {0x1a9a, 0x1a9f},
+ {0x1aae, 0x1aaf},
+ {0x1abf, 0x1aff},
+ {0x1b4c, 0x1b4f},
+ {0x1b7d, 0x1b7f},
+ {0x1bf4, 0x1bfb},
+ {0x1c38, 0x1c3a},
+ {0x1c4a, 0x1c4c},
+ {0x1c89, 0x1cbf},
+ {0x1cc8, 0x1ccf},
+ {0x1cf7, 0x1cf7},
+ {0x1cfa, 0x1cff},
+ {0x1df6, 0x1dfa},
+ {0x1f16, 0x1f17},
+ {0x1f1e, 0x1f1f},
+ {0x1f46, 0x1f47},
+ {0x1f4e, 0x1f4f},
+ {0x1f58, 0x1f58},
+ {0x1f5a, 0x1f5a},
+ {0x1f5c, 0x1f5c},
+ {0x1f5e, 0x1f5e},
+ {0x1f7e, 0x1f7f},
+ {0x1fb5, 0x1fb5},
+ {0x1fc5, 0x1fc5},
+ {0x1fd4, 0x1fd5},
+ {0x1fdc, 0x1fdc},
+ {0x1ff0, 0x1ff1},
+ {0x1ff5, 0x1ff5},
+ {0x1fff, 0x1fff},
+ {0x2065, 0x2065},
+ {0x2072, 0x2073},
+ {0x208f, 0x208f},
+ {0x209d, 0x209f},
+ {0x20bf, 0x20cf},
+ {0x20f1, 0x20ff},
+ {0x218c, 0x218f},
+ {0x23ff, 0x23ff},
+ {0x2427, 0x243f},
+ {0x244b, 0x245f},
+ {0x2b74, 0x2b75},
+ {0x2b96, 0x2b97},
+ {0x2bba, 0x2bbc},
+ {0x2bc9, 0x2bc9},
+ {0x2bd2, 0x2beb},
+ {0x2bf0, 0x2bff},
+ {0x2c2f, 0x2c2f},
+ {0x2c5f, 0x2c5f},
+ {0x2cf4, 0x2cf8},
+ {0x2d26, 0x2d26},
+ {0x2d28, 0x2d2c},
+ {0x2d2e, 0x2d2f},
+ {0x2d68, 0x2d6e},
+ {0x2d71, 0x2d7e},
+ {0x2d97, 0x2d9f},
+ {0x2da7, 0x2da7},
+ {0x2daf, 0x2daf},
+ {0x2db7, 0x2db7},
+ {0x2dbf, 0x2dbf},
+ {0x2dc7, 0x2dc7},
+ {0x2dcf, 0x2dcf},
+ {0x2dd7, 0x2dd7},
+ {0x2ddf, 0x2ddf},
+ {0x2e45, 0x2e7f},
+ {0x2e9a, 0x2e9a},
+ {0x2ef4, 0x2eff},
+ {0x2fd6, 0x2fef},
+ {0x2ffc, 0x2fff},
+ {0x3040, 0x3040},
+ {0x3097, 0x3098},
+ {0x3100, 0x3104},
+ {0x312e, 0x3130},
+ {0x318f, 0x318f},
+ {0x31bb, 0x31bf},
+ {0x31e4, 0x31ef},
+ {0x321f, 0x321f},
+ {0x32ff, 0x32ff},
+ {0x4db6, 0x4dbf},
+ {0x9fd6, 0x9fff},
+ {0xa48d, 0xa48f},
+ {0xa4c7, 0xa4cf},
+ {0xa62c, 0xa63f},
+ {0xa6f8, 0xa6ff},
+ {0xa7af, 0xa7af},
+ {0xa7b8, 0xa7f6},
+ {0xa82c, 0xa82f},
+ {0xa83a, 0xa83f},
+ {0xa878, 0xa87f},
+ {0xa8c6, 0xa8cd},
+ {0xa8da, 0xa8df},
+ {0xa8fe, 0xa8ff},
+ {0xa954, 0xa95e},
+ {0xa97d, 0xa97f},
+ {0xa9ce, 0xa9ce},
+ {0xa9da, 0xa9dd},
+ {0xa9ff, 0xa9ff},
+ {0xaa37, 0xaa3f},
+ {0xaa4e, 0xaa4f},
+ {0xaa5a, 0xaa5b},
+ {0xaac3, 0xaada},
+ {0xaaf7, 0xab00},
+ {0xab07, 0xab08},
+ {0xab0f, 0xab10},
+ {0xab17, 0xab1f},
+ {0xab27, 0xab27},
+ {0xab2f, 0xab2f},
+ {0xab66, 0xab6f},
+ {0xabee, 0xabef},
+ {0xabfa, 0xabff},
+ {0xd7a4, 0xd7af},
+ {0xd7c7, 0xd7ca},
+ {0xd7fc, 0xd7ff},
+ {0xfa6e, 0xfa6f},
+ {0xfada, 0xfaff},
+ {0xfb07, 0xfb12},
+ {0xfb18, 0xfb1c},
+ {0xfb37, 0xfb37},
+ {0xfb3d, 0xfb3d},
+ {0xfb3f, 0xfb3f},
+ {0xfb42, 0xfb42},
+ {0xfb45, 0xfb45},
+ {0xfbc2, 0xfbd2},
+ {0xfd40, 0xfd4f},
+ {0xfd90, 0xfd91},
+ {0xfdc8, 0xfdef},
+ {0xfdfe, 0xfdff},
+ {0xfe1a, 0xfe1f},
+ {0xfe53, 0xfe53},
+ {0xfe67, 0xfe67},
+ {0xfe6c, 0xfe6f},
+ {0xfe75, 0xfe75},
+ {0xfefd, 0xfefe},
+ {0xff00, 0xff00},
+ {0xffbf, 0xffc1},
+ {0xffc8, 0xffc9},
+ {0xffd0, 0xffd1},
+ {0xffd8, 0xffd9},
+ {0xffdd, 0xffdf},
+ {0xffe7, 0xffe7},
+ {0xffef, 0xfff8},
+ {0xfffe, 0xffff},
+ {0x1000c, 0x1000c},
+ {0x10027, 0x10027},
+ {0x1003b, 0x1003b},
+ {0x1003e, 0x1003e},
+ {0x1004e, 0x1004f},
+ {0x1005e, 0x1007f},
+ {0x100fb, 0x100ff},
+ {0x10103, 0x10106},
+ {0x10134, 0x10136},
+ {0x1018f, 0x1018f},
+ {0x1019c, 0x1019f},
+ {0x101a1, 0x101cf},
+ {0x101fe, 0x1027f},
+ {0x1029d, 0x1029f},
+ {0x102d1, 0x102df},
+ {0x102fc, 0x102ff},
+ {0x10324, 0x1032f},
+ {0x1034b, 0x1034f},
+ {0x1037b, 0x1037f},
+ {0x1039e, 0x1039e},
+ {0x103c4, 0x103c7},
+ {0x103d6, 0x103ff},
+ {0x1049e, 0x1049f},
+ {0x104aa, 0x104af},
+ {0x104d4, 0x104d7},
+ {0x104fc, 0x104ff},
+ {0x10528, 0x1052f},
+ {0x10564, 0x1056e},
+ {0x10570, 0x105ff},
+ {0x10737, 0x1073f},
+ {0x10756, 0x1075f},
+ {0x10768, 0x107ff},
+ {0x10806, 0x10807},
+ {0x10809, 0x10809},
+ {0x10836, 0x10836},
+ {0x10839, 0x1083b},
+ {0x1083d, 0x1083e},
+ {0x10856, 0x10856},
+ {0x1089f, 0x108a6},
+ {0x108b0, 0x108df},
+ {0x108f3, 0x108f3},
+ {0x108f6, 0x108fa},
+ {0x1091c, 0x1091e},
+ {0x1093a, 0x1093e},
+ {0x10940, 0x1097f},
+ {0x109b8, 0x109bb},
+ {0x109d0, 0x109d1},
+ {0x10a04, 0x10a04},
+ {0x10a07, 0x10a0b},
+ {0x10a14, 0x10a14},
+ {0x10a18, 0x10a18},
+ {0x10a34, 0x10a37},
+ {0x10a3b, 0x10a3e},
+ {0x10a48, 0x10a4f},
+ {0x10a59, 0x10a5f},
+ {0x10aa0, 0x10abf},
+ {0x10ae7, 0x10aea},
+ {0x10af7, 0x10aff},
+ {0x10b36, 0x10b38},
+ {0x10b56, 0x10b57},
+ {0x10b73, 0x10b77},
+ {0x10b92, 0x10b98},
+ {0x10b9d, 0x10ba8},
+ {0x10bb0, 0x10bff},
+ {0x10c49, 0x10c7f},
+ {0x10cb3, 0x10cbf},
+ {0x10cf3, 0x10cf9},
+ {0x10d00, 0x10e5f},
+ {0x10e7f, 0x10fff},
+ {0x1104e, 0x11051},
+ {0x11070, 0x1107e},
+ {0x110c2, 0x110cf},
+ {0x110e9, 0x110ef},
+ {0x110fa, 0x110ff},
+ {0x11135, 0x11135},
+ {0x11144, 0x1114f},
+ {0x11177, 0x1117f},
+ {0x111ce, 0x111cf},
+ {0x111e0, 0x111e0},
+ {0x111f5, 0x111ff},
+ {0x11212, 0x11212},
+ {0x1123f, 0x1127f},
+ {0x11287, 0x11287},
+ {0x11289, 0x11289},
+ {0x1128e, 0x1128e},
+ {0x1129e, 0x1129e},
+ {0x112aa, 0x112af},
+ {0x112eb, 0x112ef},
+ {0x112fa, 0x112ff},
+ {0x11304, 0x11304},
+ {0x1130d, 0x1130e},
+ {0x11311, 0x11312},
+ {0x11329, 0x11329},
+ {0x11331, 0x11331},
+ {0x11334, 0x11334},
+ {0x1133a, 0x1133b},
+ {0x11345, 0x11346},
+ {0x11349, 0x1134a},
+ {0x1134e, 0x1134f},
+ {0x11351, 0x11356},
+ {0x11358, 0x1135c},
+ {0x11364, 0x11365},
+ {0x1136d, 0x1136f},
+ {0x11375, 0x113ff},
+ {0x1145a, 0x1145a},
+ {0x1145c, 0x1145c},
+ {0x1145e, 0x1147f},
+ {0x114c8, 0x114cf},
+ {0x114da, 0x1157f},
+ {0x115b6, 0x115b7},
+ {0x115de, 0x115ff},
+ {0x11645, 0x1164f},
+ {0x1165a, 0x1165f},
+ {0x1166d, 0x1167f},
+ {0x116b8, 0x116bf},
+ {0x116ca, 0x116ff},
+ {0x1171a, 0x1171c},
+ {0x1172c, 0x1172f},
+ {0x11740, 0x1189f},
+ {0x118f3, 0x118fe},
+ {0x11900, 0x11abf},
+ {0x11af9, 0x11bff},
+ {0x11c09, 0x11c09},
+ {0x11c37, 0x11c37},
+ {0x11c46, 0x11c4f},
+ {0x11c6d, 0x11c6f},
+ {0x11c90, 0x11c91},
+ {0x11ca8, 0x11ca8},
+ {0x11cb7, 0x11fff},
+ {0x1239a, 0x123ff},
+ {0x1246f, 0x1246f},
+ {0x12475, 0x1247f},
+ {0x12544, 0x12fff},
+ {0x1342f, 0x143ff},
+ {0x14647, 0x167ff},
+ {0x16a39, 0x16a3f},
+ {0x16a5f, 0x16a5f},
+ {0x16a6a, 0x16a6d},
+ {0x16a70, 0x16acf},
+ {0x16aee, 0x16aef},
+ {0x16af6, 0x16aff},
+ {0x16b46, 0x16b4f},
+ {0x16b5a, 0x16b5a},
+ {0x16b62, 0x16b62},
+ {0x16b78, 0x16b7c},
+ {0x16b90, 0x16eff},
+ {0x16f45, 0x16f4f},
+ {0x16f7f, 0x16f8e},
+ {0x16fa0, 0x16fdf},
+ {0x16fe1, 0x16fff},
+ {0x187ed, 0x187ff},
+ {0x18af3, 0x1afff},
+ {0x1b002, 0x1bbff},
+ {0x1bc6b, 0x1bc6f},
+ {0x1bc7d, 0x1bc7f},
+ {0x1bc89, 0x1bc8f},
+ {0x1bc9a, 0x1bc9b},
+ {0x1bca4, 0x1cfff},
+ {0x1d0f6, 0x1d0ff},
+ {0x1d127, 0x1d128},
+ {0x1d1e9, 0x1d1ff},
+ {0x1d246, 0x1d2ff},
+ {0x1d357, 0x1d35f},
+ {0x1d372, 0x1d3ff},
+ {0x1d455, 0x1d455},
+ {0x1d49d, 0x1d49d},
+ {0x1d4a0, 0x1d4a1},
+ {0x1d4a3, 0x1d4a4},
+ {0x1d4a7, 0x1d4a8},
+ {0x1d4ad, 0x1d4ad},
+ {0x1d4ba, 0x1d4ba},
+ {0x1d4bc, 0x1d4bc},
+ {0x1d4c4, 0x1d4c4},
+ {0x1d506, 0x1d506},
+ {0x1d50b, 0x1d50c},
+ {0x1d515, 0x1d515},
+ {0x1d51d, 0x1d51d},
+ {0x1d53a, 0x1d53a},
+ {0x1d53f, 0x1d53f},
+ {0x1d545, 0x1d545},
+ {0x1d547, 0x1d549},
+ {0x1d551, 0x1d551},
+ {0x1d6a6, 0x1d6a7},
+ {0x1d7cc, 0x1d7cd},
+ {0x1da8c, 0x1da9a},
+ {0x1daa0, 0x1daa0},
+ {0x1dab0, 0x1dfff},
+ {0x1e007, 0x1e007},
+ {0x1e019, 0x1e01a},
+ {0x1e022, 0x1e022},
+ {0x1e025, 0x1e025},
+ {0x1e02b, 0x1e7ff},
+ {0x1e8c5, 0x1e8c6},
+ {0x1e8d7, 0x1e8ff},
+ {0x1e94b, 0x1e94f},
+ {0x1e95a, 0x1e95d},
+ {0x1e960, 0x1edff},
+ {0x1ee04, 0x1ee04},
+ {0x1ee20, 0x1ee20},
+ {0x1ee23, 0x1ee23},
+ {0x1ee25, 0x1ee26},
+ {0x1ee28, 0x1ee28},
+ {0x1ee33, 0x1ee33},
+ {0x1ee38, 0x1ee38},
+ {0x1ee3a, 0x1ee3a},
+ {0x1ee3c, 0x1ee41},
+ {0x1ee43, 0x1ee46},
+ {0x1ee48, 0x1ee48},
+ {0x1ee4a, 0x1ee4a},
+ {0x1ee4c, 0x1ee4c},
+ {0x1ee50, 0x1ee50},
+ {0x1ee53, 0x1ee53},
+ {0x1ee55, 0x1ee56},
+ {0x1ee58, 0x1ee58},
+ {0x1ee5a, 0x1ee5a},
+ {0x1ee5c, 0x1ee5c},
+ {0x1ee5e, 0x1ee5e},
+ {0x1ee60, 0x1ee60},
+ {0x1ee63, 0x1ee63},
+ {0x1ee65, 0x1ee66},
+ {0x1ee6b, 0x1ee6b},
+ {0x1ee73, 0x1ee73},
+ {0x1ee78, 0x1ee78},
+ {0x1ee7d, 0x1ee7d},
+ {0x1ee7f, 0x1ee7f},
+ {0x1ee8a, 0x1ee8a},
+ {0x1ee9c, 0x1eea0},
+ {0x1eea4, 0x1eea4},
+ {0x1eeaa, 0x1eeaa},
+ {0x1eebc, 0x1eeef},
+ {0x1eef2, 0x1efff},
+ {0x1f02c, 0x1f02f},
+ {0x1f094, 0x1f09f},
+ {0x1f0af, 0x1f0b0},
+ {0x1f0c0, 0x1f0c0},
+ {0x1f0d0, 0x1f0d0},
+ {0x1f0f6, 0x1f0ff},
+ {0x1f10d, 0x1f10f},
+ {0x1f12f, 0x1f12f},
+ {0x1f16c, 0x1f16f},
+ {0x1f1ad, 0x1f1e5},
+ {0x1f203, 0x1f20f},
+ {0x1f23c, 0x1f23f},
+ {0x1f249, 0x1f24f},
+ {0x1f252, 0x1f2ff},
+ {0x1f6d3, 0x1f6df},
+ {0x1f6ed, 0x1f6ef},
+ {0x1f6f7, 0x1f6ff},
+ {0x1f774, 0x1f77f},
+ {0x1f7d5, 0x1f7ff},
+ {0x1f80c, 0x1f80f},
+ {0x1f848, 0x1f84f},
+ {0x1f85a, 0x1f85f},
+ {0x1f888, 0x1f88f},
+ {0x1f8ae, 0x1f90f},
+ {0x1f91f, 0x1f91f},
+ {0x1f928, 0x1f92f},
+ {0x1f931, 0x1f932},
+ {0x1f93f, 0x1f93f},
+ {0x1f94c, 0x1f94f},
+ {0x1f95f, 0x1f97f},
+ {0x1f992, 0x1f9bf},
+ {0x1f9c1, 0x1ffff},
+ {0x2a6d7, 0x2a6ff},
+ {0x2b735, 0x2b73f},
+ {0x2b81e, 0x2b81f},
+ {0x2cea2, 0x2f7ff},
+ {0x2fa1e, 0xe0000},
+ {0xe0002, 0xe001f},
+ {0xe0080, 0xe00ff},
+ {0xe01f0, 0xeffff},
+ {0xffffe, 0xfffff},
+};
+
+#define WCWIDTH9_ARRAY_SIZE(arr) ((sizeof(arr)/sizeof((arr)[0])) / ((size_t)(!(sizeof(arr) % sizeof((arr)[0])))))
+
+static inline bool wcwidth9_intable(const struct wcwidth9_interval *table, size_t n_items, int c) {
+ int mid, bot, top;
+
+ if (c < table[0].first) {
+ return false;
+ }
+
+ bot = 0;
+ top = (int)(n_items - 1);
+ while (top >= bot) {
+ mid = (bot + top) / 2;
+
+ if (table[mid].last < c) {
+ bot = mid + 1;
+ } else if (table[mid].first > c) {
+ top = mid - 1;
+ } else {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static inline int wcwidth9(int c) {
+ if (c < 0|| c > 0x10ffff) {
+ return -1;
+ }
+
+ if (wcwidth9_intable(wcwidth9_nonprint, WCWIDTH9_ARRAY_SIZE(wcwidth9_nonprint), c)) {
+ return -1;
+ }
+
+ if (wcwidth9_intable(wcwidth9_combining, WCWIDTH9_ARRAY_SIZE(wcwidth9_combining), c)) {
+ return -1;
+ }
+
+ if (wcwidth9_intable(wcwidth9_not_assigned, WCWIDTH9_ARRAY_SIZE(wcwidth9_not_assigned), c)) {
+ return -1;
+ }
+
+ if (wcwidth9_intable(wcwidth9_private, WCWIDTH9_ARRAY_SIZE(wcwidth9_private), c)) {
+ return -3;
+ }
+
+ if (wcwidth9_intable(wcwidth9_ambiguous, WCWIDTH9_ARRAY_SIZE(wcwidth9_ambiguous), c)) {
+ return -2;
+ }
+
+ if (wcwidth9_intable(wcwidth9_doublewidth, WCWIDTH9_ARRAY_SIZE(wcwidth9_doublewidth), c)) {
+ return 2;
+ }
+
+ if (wcwidth9_intable(wcwidth9_emoji_width, WCWIDTH9_ARRAY_SIZE(wcwidth9_emoji_width), c)) {
+ return 2;
+ }
+
+ return 1;
+}
+
+#endif /* WCWIDTH9_H */
diff --git a/Src/zsh.h b/Src/zsh.h
index b83b8bdbb..dae2b2459 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -489,6 +489,7 @@ typedef struct complist *Complist;
typedef struct conddef *Conddef;
typedef struct dirsav *Dirsav;
typedef struct emulation_options *Emulation_options;
+typedef struct execcmd_params *Execcmd_params;
typedef struct features *Features;
typedef struct feature_enables *Feature_enables;
typedef struct funcstack *Funcstack;
@@ -622,27 +623,34 @@ struct timedfn {
/* (1<<4) is used for Z_END, see the wordcode definitions */
/* (1<<5) is used for Z_SIMPLE, see the wordcode definitions */
-/* Condition types. */
+/*
+ * Condition types.
+ *
+ * Careful when changing these: both cond_binary_ops in text.c and
+ * condstr in cond.c depend on these. (The zsh motto is "two instances
+ * are better than one". Or something.)
+ */
#define COND_NOT 0
#define COND_AND 1
#define COND_OR 2
#define COND_STREQ 3
-#define COND_STRNEQ 4
-#define COND_STRLT 5
-#define COND_STRGTR 6
-#define COND_NT 7
-#define COND_OT 8
-#define COND_EF 9
-#define COND_EQ 10
-#define COND_NE 11
-#define COND_LT 12
-#define COND_GT 13
-#define COND_LE 14
-#define COND_GE 15
-#define COND_REGEX 16
-#define COND_MOD 17
-#define COND_MODI 18
+#define COND_STRDEQ 4
+#define COND_STRNEQ 5
+#define COND_STRLT 6
+#define COND_STRGTR 7
+#define COND_NT 8
+#define COND_OT 9
+#define COND_EF 10
+#define COND_EQ 11
+#define COND_NE 12
+#define COND_LT 13
+#define COND_GT 14
+#define COND_LE 15
+#define COND_GE 16
+#define COND_REGEX 17
+#define COND_MOD 18
+#define COND_MODI 19
typedef int (*CondHandler) _((char **, int));
@@ -976,7 +984,8 @@ struct jobfile {
struct job {
pid_t gleader; /* process group leader of this job */
- pid_t other; /* subjob id or subshell pid */
+ pid_t other; /* subjob id (SUPERJOB)
+ * or subshell pid (SUBJOB) */
int stat; /* see STATs below */
char *pwd; /* current working dir of shell when *
* this job was spawned */
@@ -1008,6 +1017,8 @@ struct job {
#define STAT_SUBLEADER (0x2000) /* is super-job, but leader is sub-shell */
#define STAT_BUILTIN (0x4000) /* job at tail of pipeline is a builtin */
+#define STAT_SUBJOB_ORPHANED (0x8000)
+ /* STAT_SUBJOB with STAT_SUPERJOB exited */
#define SP_RUNNING -1 /* fake status for jobs currently running */
@@ -1381,6 +1392,21 @@ struct builtin {
*/
#define BINF_ASSIGN (1<<19)
+/**
+ * Parameters passed to execcmd().
+ * These are not opaque --- they are also used by the pipeline manager.
+ */
+struct execcmd_params {
+ LinkList args; /* All command prefixes, arguments & options */
+ LinkList redir; /* Redirections */
+ Wordcode beg; /* The code at the start of the command */
+ Wordcode varspc; /* The code for assignment parsed as such */
+ Wordcode assignspc; /* The code for assignment parsed as typeset */
+ int type; /* The WC_* type of the command */
+ int postassigns; /* The number of assignspc assiguments */
+ int htok; /* tokens in parameter list */
+};
+
struct module {
struct hashnode node;
union {
@@ -1563,7 +1589,7 @@ struct zpc_disables_save {
/*
* Bit vector of ZPC_COUNT disabled characters.
* We'll live dangerously and assumed ZPC_COUNT is no greater
- * than the number of bits an an unsigned int.
+ * than the number of bits an unsigned int.
*/
unsigned int disables;
};
@@ -1792,6 +1818,8 @@ struct tieddata {
#define PM_ZSHSTORED (1<<18) /* function stored in zsh form */
/* Remaining flags do not correspond directly to command line arguments */
+#define PM_DONTIMPORT_SUID (1<<19) /* do not import if running setuid */
+#define PM_SINGLE (1<<20) /* special can only have a single instance */
#define PM_LOCAL (1<<21) /* this parameter will be made local */
#define PM_SPECIAL (1<<22) /* special builtin parameter */
#define PM_DONTIMPORT (1<<23) /* do not import this variable */
@@ -2567,7 +2595,7 @@ struct ttyinfo {
#define txtchangeisset(T,X) ((T) & (X))
#define txtchangeget(T,A) (((T) & A ## _MASK) >> A ## _SHIFT)
-#define txtchangeset(T, X, Y) ((void)(T && (*T |= (X), *T &= ~(Y))))
+#define txtchangeset(T, X, Y) ((void)(T && (*T &= ~(Y), *T |= (X))))
/*
* For outputting sequences to change colour: specify foreground
@@ -2799,7 +2827,14 @@ enum errflag_bits {
/*
* User interrupt.
*/
- ERRFLAG_INT = 2
+ ERRFLAG_INT = 2,
+ /*
+ * Hard error --- return to top-level prompt in interactive
+ * shell. In non-interactive shell we'll typically already
+ * have exited. This is reset by "errflag = 0" in
+ * loop(toplevel = 1, ...).
+ */
+ ERRFLAG_HARD = 4
};
/***********/
@@ -2921,6 +2956,7 @@ struct parse_stack {
int incasepat;
int isnewlin;
int infor;
+ int inrepeat_;
int intypeset;
int eclen, ecused, ecnpats;
@@ -3095,7 +3131,9 @@ typedef wint_t convchar_t;
* much what the definition tells us. However, we happen to know this
* works on MacOS which doesn't define that.
*/
-#if defined(BROKEN_WCWIDTH) && (defined(__STDC_ISO_10646__) || defined(__APPLE__))
+#ifdef ENABLE_UNICODE9
+#define WCWIDTH(wc) mk_wcwidth(wc)
+#elif defined(BROKEN_WCWIDTH) && (defined(__STDC_ISO_10646__) || defined(__APPLE__))
#define WCWIDTH(wc) mk_wcwidth(wc)
#else
#define WCWIDTH(wc) wcwidth(wc)
diff --git a/Src/zsh.mdd b/Src/zsh.mdd
index 86dd58866..324435d62 100644
--- a/Src/zsh.mdd
+++ b/Src/zsh.mdd
@@ -2,7 +2,7 @@ name=zsh/main
link=static
load=yes
# load=static should replace use of alwayslink
-functions='Functions/Chpwd/* Functions/Exceptions/* Functions/Misc/* Functions/MIME/* Functions/Prompts/* Functions/VCS_Info/* Functions/VCS_Info/Backends/*'
+functions='Functions/Chpwd/* Functions/Exceptions/* Functions/Math/* Functions/Misc/* Functions/MIME/* Functions/Prompts/* Functions/VCS_Info/* Functions/VCS_Info/Backends/*'
nozshdep=1
alwayslink=1
diff --git a/Src/zsh_system.h b/Src/zsh_system.h
index 811340d42..5339b496f 100644
--- a/Src/zsh_system.h
+++ b/Src/zsh_system.h
@@ -37,7 +37,7 @@
#endif
#endif
-#if defined(__linux) || defined(__GNU__) || defined(__GLIBC__) || defined(LIBC_MUSL)
+#if defined(__linux) || defined(__GNU__) || defined(__GLIBC__) || defined(LIBC_MUSL) || defined(__CYGWIN__)
/*
* Turn on numerous extensions.
* This is in order to get the functions for manipulating /dev/ptmx.
@@ -728,7 +728,7 @@ extern char **environ;
* We always need setenv and unsetenv in pairs, because
* we don't know how to do memory management on the values set.
*/
-#if defined(HAVE_SETENV) && defined(HAVE_UNSETENV)
+#if defined(HAVE_SETENV) && defined(HAVE_UNSETENV) && !defined(__APPLE__)
# define USE_SET_UNSET_ENV
#endif
@@ -882,6 +882,10 @@ extern short ospeed;
# endif
#endif
+#ifdef HAVE_SRAND_DETERMINISTIC
+# define srand srand_deterministic
+#endif
+
#ifdef ZSH_VALGRIND
# include "valgrind/valgrind.h"
# include "valgrind/memcheck.h"