summaryrefslogtreecommitdiff
path: root/Src
diff options
context:
space:
mode:
Diffstat (limited to 'Src')
-rw-r--r--Src/Modules/clone.c2
-rw-r--r--Src/Modules/pcre.c33
-rw-r--r--Src/Modules/zpty.c26
-rw-r--r--Src/Zle/complist.c6
-rw-r--r--Src/Zle/zle.h2
-rw-r--r--Src/Zle/zle_hist.c2
-rw-r--r--Src/Zle/zle_keymap.c111
-rw-r--r--Src/Zle/zle_main.c53
-rw-r--r--Src/Zle/zle_refresh.c6
-rw-r--r--Src/Zle/zle_thingy.c7
-rw-r--r--Src/builtin.c256
-rw-r--r--Src/exec.c20
-rw-r--r--Src/glob.c17
-rw-r--r--Src/hashtable.c9
-rw-r--r--Src/hashtable.h2
-rw-r--r--Src/init.c30
-rw-r--r--Src/lex.c49
-rw-r--r--Src/options.c2
-rw-r--r--Src/params.c64
-rw-r--r--Src/parse.c34
-rw-r--r--Src/pattern.c17
-rw-r--r--Src/subst.c14
-rw-r--r--Src/text.c32
-rw-r--r--Src/utils.c429
-rw-r--r--Src/zsh.h34
25 files changed, 956 insertions, 301 deletions
diff --git a/Src/Modules/clone.c b/Src/Modules/clone.c
index 5db1c9222..930429248 100644
--- a/Src/Modules/clone.c
+++ b/Src/Modules/clone.c
@@ -93,7 +93,7 @@ bin_clone(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
/* Clear mygrp so that acquire_pgrp() gets the new process group.
* (acquire_pgrp() is called from init_io()) */
mypgrp = 0;
- init_io();
+ init_io(NULL);
setsparam("TTY", ztrdup(ttystrname));
}
close(ttyfd);
diff --git a/Src/Modules/pcre.c b/Src/Modules/pcre.c
index 2393cd1e7..aa5c8ed5b 100644
--- a/Src/Modules/pcre.c
+++ b/Src/Modules/pcre.c
@@ -190,18 +190,25 @@ zpcre_get_substrings(char *arg, int *ovec, int ret, char *matchvar,
if (want_begin_end) {
char *ptr = arg;
zlong offs = 0;
+ int clen, leftlen;
/* Count the characters before the match */
- MB_METACHARINIT();
- while (ptr < arg + ovec[0]) {
+ MB_CHARINIT();
+ leftlen = ovec[0];
+ while (leftlen) {
offs++;
- ptr += MB_METACHARLEN(ptr);
+ clen = MB_CHARLEN(ptr, leftlen);
+ ptr += clen;
+ leftlen -= clen;
}
setiparam("MBEGIN", offs + !isset(KSHARRAYS));
/* Add on the characters in the match */
- while (ptr < arg + ovec[1]) {
+ leftlen = ovec[1] - ovec[0];
+ while (leftlen) {
offs++;
- ptr += MB_METACHARLEN(ptr);
+ clen = MB_CHARLEN(ptr, leftlen);
+ ptr += clen;
+ leftlen -= clen;
}
setiparam("MEND", offs + !isset(KSHARRAYS) - 1);
if (nelem) {
@@ -219,17 +226,23 @@ zpcre_get_substrings(char *arg, int *ovec, int ret, char *matchvar,
ptr = arg;
offs = 0;
/* Find the start offset */
- MB_METACHARINIT();
- while (ptr < arg + ipair[0]) {
+ MB_CHARINIT();
+ leftlen = ipair[0];
+ while (leftlen) {
offs++;
- ptr += MB_METACHARLEN(ptr);
+ clen = MB_CHARLEN(ptr, leftlen);
+ ptr += clen;
+ leftlen -= clen;
}
convbase(buf, offs + !isset(KSHARRAYS), 10);
*bptr = ztrdup(buf);
/* Continue to the end offset */
- while (ptr < arg + ipair[1]) {
+ leftlen = ipair[1] - ipair[0];
+ while (leftlen) {
offs++;
- ptr += MB_METACHARLEN(ptr);
+ clen = MB_CHARLEN(ptr, leftlen);
+ ptr += clen;
+ leftlen -= clen;
}
convbase(buf, offs + !isset(KSHARRAYS) - 1, 10);
*eptr = ztrdup(buf);
diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c
index 3b8366076..63ff7578c 100644
--- a/Src/Modules/zpty.c
+++ b/Src/Modules/zpty.c
@@ -399,7 +399,7 @@ newptycmd(char *nam, char *pname, char **args, int echo, int nblock)
close(master);
close(coprocin);
close(coprocout);
- init_io();
+ init_io(NULL);
setsparam("TTY", ztrdup(ttystrname));
opts[INTERACTIVE] = 0;
@@ -614,14 +614,23 @@ ptyread(char *nam, Ptycmd cmd, char **args, int noblock, int mustmatch)
break;
}
if (cmd->read != -1 || (ret = read(cmd->fd, buf + used, 1)) == 1) {
+ int readchar;
if (cmd->read != -1) {
ret = 1;
- buf[used] = (char) cmd->read;
+ readchar = cmd->read;
cmd->read = -1;
- }
+ } else
+ readchar = STOUC(buf[used]);
+ if (imeta(readchar)) {
+ buf[used++] = Meta;
+ buf[used++] = (char) (readchar ^ 32);
+ } else
+ buf[used++] = (char) readchar;
seen = 1;
- if (++used == blen) {
+ if (used >= blen-1) {
if (!*args) {
+ buf[used] = '\0';
+ unmetafy(buf, &used);
write_loop(1, buf, used);
used = 0;
} else {
@@ -633,7 +642,8 @@ ptyread(char *nam, Ptycmd cmd, char **args, int noblock, int mustmatch)
buf[used] = '\0';
if (!prog) {
- if (ret <= 0 || (*args && buf[used - 1] == '\n'))
+ if (ret <= 0 || (*args && buf[used - 1] == '\n' &&
+ (used < 2 || buf[used-2] != Meta)))
break;
} else {
if (ret < 0
@@ -666,9 +676,11 @@ ptyread(char *nam, Ptycmd cmd, char **args, int noblock, int mustmatch)
return 1;
}
if (*args)
- setsparam(*args, ztrdup(metafy(buf, used, META_HREALLOC)));
- else if (used)
+ setsparam(*args, ztrdup(buf));
+ else if (used) {
+ unmetafy(buf, &used);
write_loop(1, buf, used);
+ }
if (seen && (!prog || matchok || !mustmatch))
return 0;
diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c
index 29aaee82a..0ccb88505 100644
--- a/Src/Zle/complist.c
+++ b/Src/Zle/complist.c
@@ -2584,6 +2584,12 @@ domenuselect(Hookdef dummy, Chdata dat)
if (!do_last_key) {
zmult = 1;
cmd = getkeycmd();
+ /*
+ * On interrupt, we'll exit due to cmd being empty.
+ * Don't propagate the interrupt any further, which
+ * can screw up redrawing.
+ */
+ errflag &= ~ERRFLAG_INT;
if (mtab_been_reallocated) {
do_last_key = 1;
continue;
diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h
index 2d672de3b..e9b14281d 100644
--- a/Src/Zle/zle.h
+++ b/Src/Zle/zle.h
@@ -213,6 +213,8 @@ struct widget {
#define ZLE_KEEPSUFFIX (1<<9) /* DON'T remove added suffix */
#define ZLE_NOTCOMMAND (1<<10) /* widget should not alter lastcmd */
#define ZLE_ISCOMP (1<<11) /* usable for new style completion */
+#define WIDGET_INUSE (1<<12) /* widget is in use */
+#define WIDGET_FREE (1<<13) /* request to free when no longer in use */
/* thingies */
diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c
index 95d96c95c..abd6e1749 100644
--- a/Src/Zle/zle_hist.c
+++ b/Src/Zle/zle_hist.c
@@ -1480,6 +1480,7 @@ doisearch(char **args, int dir, int pattern)
isearch_active = 0;
ref:
zlecallhook("zle-isearch-update", NULL);
+ redrawhook();
zrefresh();
if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) {
int i;
@@ -1694,6 +1695,7 @@ doisearch(char **args, int dir, int pattern)
statusline = NULL;
unmetafy_line();
zlecallhook("zle-isearch-exit", NULL);
+ redrawhook();
if (exitfn)
exitfn(zlenoargs);
selectkeymap(okeymap, 1);
diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
index 069580f8a..382eb8d41 100644
--- a/Src/Zle/zle_keymap.c
+++ b/Src/Zle/zle_keymap.c
@@ -1449,6 +1449,104 @@ default_bindings(void)
/*************************/
/* reading key sequences */
/*************************/
+/**/
+#ifdef MULTIBYTE_SUPPORT
+/*
+ * Get the remainder of a character if we support multibyte
+ * input strings. It may not require any more input, but
+ * we haven't yet checked. What's read in so far is available
+ * in keybuf; if we read more we will top keybuf up.
+ *
+ * This version is used when we are still resolving the input key stream
+ * into bindings. Once that has been done this function shouldn't be
+ * used: instead, see getrestchar() in zle_main.c.
+ *
+ * This supports a self-insert binding at any stage of a key sequence.
+ * Typically we handle 8-bit characters by having only the first byte
+ * bound to self insert; then we immediately get here and read in as
+ * many further bytes as necessary. However, it's possible that any set
+ * of bytes up to full character is bound to self-insert; then we get
+ * here later and read as much as possible, which could be a complete
+ * character, from keybuf before attempting further input.
+ *
+ * At the end of the process, the full multibyte character is available
+ * in keybuf, so the return value may be superfluous.
+ */
+
+/**/
+mod_export ZLE_INT_T
+getrestchar_keybuf(void)
+{
+ char c;
+ wchar_t outchar;
+ int inchar, timeout, bufind = 0, buflen = keybuflen;
+ static mbstate_t mbs;
+ size_t cnt;
+
+ /*
+ * We are guaranteed to set a valid wide last character,
+ * although it may be WEOF (which is technically not
+ * a wide character at all...)
+ */
+ lastchar_wide_valid = 1;
+ memset(&mbs, 0, sizeof mbs);
+
+ /*
+ * Return may be zero if we have a NULL; handle this like
+ * any other character.
+ */
+ while (1) {
+ if (bufind < buflen) {
+ c = STOUC(keybuf[bufind++]);
+ if (c == Meta) {
+ DPUTS(bufind == buflen, "Meta at end of keybuf");
+ c = STOUC(keybuf[bufind++]) ^ 32;
+ }
+ } else {
+ /*
+ * Always apply KEYTIMEOUT to the remains of the input
+ * character. The parts of a multibyte character should
+ * arrive together. If we don't do this the input can
+ * get stuck if an invalid byte sequence arrives.
+ */
+ inchar = getbyte(1L, &timeout);
+ /* getbyte deliberately resets lastchar_wide_valid */
+ lastchar_wide_valid = 1;
+ if (inchar == EOF) {
+ memset(&mbs, 0, sizeof mbs);
+ if (timeout)
+ {
+ /*
+ * This case means that we got a valid initial byte
+ * (since we tested for EOF above), but the followup
+ * timed out. This probably indicates a duff character.
+ * Return a '?'.
+ */
+ lastchar = '?';
+ return lastchar_wide = L'?';
+ }
+ else
+ return lastchar_wide = WEOF;
+ }
+ c = inchar;
+ addkeybuf(inchar);
+ }
+
+ cnt = mbrtowc(&outchar, &c, 1, &mbs);
+ if (cnt == MB_INVALID) {
+ /*
+ * Invalid input. Hmm, what's the right thing to do here?
+ */
+ memset(&mbs, 0, sizeof mbs);
+ return lastchar_wide = WEOF;
+ }
+ if (cnt != MB_INCOMPLETE)
+ break;
+ }
+ return lastchar_wide = (ZLE_INT_T)outchar;
+}
+/**/
+#endif
/* read a sequence of keys that is bound to some command in a keymap */
@@ -1503,16 +1601,9 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp)
f->widget->flags & ZLE_VIOPER);
#ifdef MULTIBYTE_SUPPORT
if ((f == Th(z_selfinsert) || f == Th(z_selfinsertunmeta)) &&
- !lastchar_wide_valid) {
- int len;
- VARARR(char, mbc, MB_CUR_MAX);
- ZLE_INT_T inchar = getrestchar(lastchar, mbc, &len);
- if (inchar != WEOF && len) {
- char *ptr = mbc;
- while (len--)
- addkeybuf(STOUC(*ptr++));
- lastlen = keybuflen;
- }
+ !lastchar_wide_valid && !ispfx) {
+ (void)getrestchar_keybuf();
+ lastlen = keybuflen;
}
#endif
}
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 38427e8e3..6e2bfded8 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -1025,6 +1025,32 @@ getrestchar(int inchar, char *outstr, int *outcount)
/**/
#endif
+/**/
+void redrawhook(void)
+{
+ Thingy initthingy;
+ if ((initthingy = rthingy_nocreate("zle-line-pre-redraw"))) {
+ 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;
+ incompfunc = 0;
+ execzlefunc(initthingy, args, 0);
+ incompfunc = old_incompfunc;
+ 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 */
+ lastcmd = lastcmd_prev;
+ }
+}
/**/
void
@@ -1084,6 +1110,8 @@ zlecore(void)
errflag |= ERRFLAG_ERROR;
break;
}
+
+ redrawhook();
#ifdef HAVE_POLL
if (baud && !(lastcmd & ZLE_MENUCMP)) {
struct pollfd pfd;
@@ -1113,6 +1141,7 @@ zlecore(void)
zrefresh();
freeheap();
+
}
region_active = 0;
@@ -1191,7 +1220,7 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
vistartchange = -1;
zleline = (ZLE_STRING_T)zalloc(((linesz = 256) + 2) * ZLE_CHAR_SIZE);
*zleline = ZWC('\0');
- virangeflag = lastcmd = done = zlecs = zlell = mark = 0;
+ virangeflag = lastcmd = done = zlecs = zlell = mark = yankb = yanke = 0;
vichgflag = 0;
viinsbegin = 0;
statusline = NULL;
@@ -1344,6 +1373,8 @@ execzlefunc(Thingy func, char **args, int set_bindk)
eofsent = 1;
ret = 1;
} else {
+ int inuse = wflags & WIDGET_INUSE;
+ w->flags |= WIDGET_INUSE;
if(!(wflags & ZLE_KEEPSUFFIX))
removesuffix();
if(!(wflags & ZLE_MENUCMP)) {
@@ -1367,6 +1398,12 @@ execzlefunc(Thingy func, char **args, int set_bindk)
ret = w->u.fn(args);
unqueue_signals();
}
+ if (!inuse) {
+ if (w->flags & WIDGET_FREE)
+ freewidget(w);
+ else
+ w->flags &= ~WIDGET_INUSE;
+ }
if (!(wflags & ZLE_NOTCOMMAND))
lastcmd = wflags;
}
@@ -1387,6 +1424,8 @@ execzlefunc(Thingy func, char **args, int set_bindk)
int osc = sfcontext, osi = movefd(0);
int oxt = isset(XTRACE);
LinkList largs = NULL;
+ int inuse = w->flags & WIDGET_INUSE;
+ w->flags |= WIDGET_INUSE;
if (*args) {
largs = newlinklist();
@@ -1402,8 +1441,15 @@ execzlefunc(Thingy func, char **args, int set_bindk)
opts[XTRACE] = oxt;
sfcontext = osc;
endparamscope();
- lastcmd = w->flags;
- w->flags = 0;
+ lastcmd = w->flags & ~(WIDGET_INUSE|WIDGET_FREE);
+ if (inuse) {
+ w->flags &= WIDGET_INUSE|WIDGET_FREE;
+ } else {
+ if (w->flags & WIDGET_FREE)
+ freewidget(w);
+ else
+ w->flags = 0;
+ }
r = 1;
redup(osi, 0);
}
@@ -1795,6 +1841,7 @@ recursiveedit(UNUSED(char **args))
{
int locerror;
+ redrawhook();
zrefresh();
zlecore();
diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c
index 6facff429..3d2471e27 100644
--- a/Src/Zle/zle_refresh.c
+++ b/Src/Zle/zle_refresh.c
@@ -338,9 +338,9 @@ zle_set_highlight(void)
for (; *atrs; atrs++) {
if (!strcmp(*atrs, "none")) {
/* reset attributes for consistency... usually unnecessary */
- special_atr_on = default_atr_on =
- paste_atr_on_set = 0;
- special_atr_on_set = region_atr_on_set =
+ special_atr_on = default_atr_on = 0;
+ special_atr_on_set = 1;
+ paste_atr_on_set = region_atr_on_set =
isearch_atr_on_set = suffix_atr_on_set = 1;
} else if (strpfx("default:", *atrs)) {
match_highlight(*atrs + 8, &default_atr_on);
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index 271fd8efc..21495b6f2 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -253,9 +253,14 @@ unbindwidget(Thingy t, int override)
/* Free a widget. */
/**/
-static void
+void
freewidget(Widget w)
{
+ if (w->flags & WIDGET_INUSE) {
+ w->flags |= WIDGET_FREE;
+ return;
+ }
+
if (w->flags & WIDGET_NCOMP) {
zsfree(w->u.comp.wid);
zsfree(w->u.comp.func);
diff --git a/Src/builtin.c b/Src/builtin.c
index cac4f42f9..dd20f9eab 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -62,7 +62,7 @@ static struct builtin builtins[] =
BUILTIN("enable", 0, bin_enable, 0, -1, BIN_ENABLE, "afmprs", NULL),
BUILTIN("eval", BINF_PSPECIAL, bin_eval, 0, -1, BIN_EVAL, NULL, NULL),
BUILTIN("exit", BINF_PSPECIAL, bin_break, 0, 1, BIN_EXIT, NULL, NULL),
- BUILTIN("export", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, BIN_EXPORT, "E:%F:%HL:%R:%TUZ:%afhi:%lprtu", "xg"),
+ BUILTIN("export", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%TUZ:%afhi:%lprtu", "xg"),
BUILTIN("false", 0, bin_false, 0, -1, 0, NULL, NULL),
/*
* We used to behave as if the argument to -e was optional.
@@ -99,14 +99,14 @@ static struct builtin builtins[] =
#endif
BUILTIN("popd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 1, BIN_POPD, "q", NULL),
- BUILTIN("print", BINF_PRINTOPTS, bin_print, 0, -1, BIN_PRINT, "abcC:Df:ilmnNoOpPrRsSu:x:X:z-", NULL),
- BUILTIN("printf", 0, bin_print, 1, -1, BIN_PRINTF, NULL, NULL),
+ BUILTIN("print", BINF_PRINTOPTS, bin_print, 0, -1, BIN_PRINT, "abcC:Df:ilmnNoOpPrRsSu:v:x:X:z-", NULL),
+ BUILTIN("printf", 0, bin_print, 1, -1, BIN_PRINTF, "v:", NULL),
BUILTIN("pushd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_PUSHD, "qsPL", NULL),
BUILTIN("pushln", 0, bin_print, 0, -1, BIN_PRINT, NULL, "-nz"),
BUILTIN("pwd", 0, bin_pwd, 0, 0, 0, "rLP", NULL),
BUILTIN("r", 0, bin_fc, 0, -1, BIN_R, "IlLnr", NULL),
BUILTIN("read", 0, bin_read, 0, -1, 0, "cd:ek:%lnpqrst:%zu:AE", NULL),
- BUILTIN("readonly", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%lptux", "r"),
+ BUILTIN("readonly", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, BIN_READONLY, "AE:%F:%HL:%R:%TUZ:%afghi:%lptux", "r"),
BUILTIN("rehash", 0, bin_hash, 0, 0, 0, "df", "r"),
BUILTIN("return", BINF_PSPECIAL, bin_break, 0, 1, BIN_RETURN, NULL, NULL),
BUILTIN("set", BINF_PSPECIAL | BINF_HANDLES_OPTS, bin_set, 0, -1, 0, NULL, NULL),
@@ -387,7 +387,7 @@ execbuiltin(LinkList args, LinkList assigns, Builtin bn)
if (*arg) {
if(*arg == Meta)
*++arg ^= 32;
- zwarn("bad option: -%c", *arg);
+ zwarnnam(name, "bad option: -%c", *arg);
return 1;
}
arg = *++argv;
@@ -2213,6 +2213,8 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
mkarray(NULL), 0)))
return NULL;
}
+ if (errflag)
+ return NULL;
pm->node.flags |= (on & PM_READONLY);
if (OPT_ISSET(ops,'p'))
paramtab->printnode(&pm->node, PRINT_TYPESET);
@@ -2533,6 +2535,10 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
if (OPT_ISSET(ops,'f'))
return bin_functions(name, argv, ops, func);
+ /* POSIX handles "readonly" specially */
+ if (func == BIN_READONLY && isset(POSIXBUILTINS) && !OPT_PLUS(ops, 'g'))
+ ops->ind['g'] = 1;
+
/* Translate the options into PM_* flags. *
* Unfortunately, this depends on the order *
* these flags are defined in zsh.h */
@@ -4019,12 +4025,62 @@ bin_print(char *name, char **args, Options ops, int func)
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;
+ FILE *fout = stdout;
#ifdef HAVE_OPEN_MEMSTREAM
size_t mcount;
+#define ASSIGN_MSTREAM(BUF,FOUT) \
+ do { \
+ if ((FOUT = open_memstream(&BUF, &mcount)) == NULL) { \
+ zwarnnam(name, "open_memstream failed"); \
+ return 1; \
+ } \
+ } while (0)
+ /*
+ * Some implementations of open_memstream() have a bug such that,
+ * if fflush() is followed by fclose(), another NUL byte is written
+ * to the buffer at the wrong position. Therefore we must fclose()
+ * before reading.
+ */
+#define READ_MSTREAM(BUF,FOUT) \
+ ((fclose(FOUT) == 0) ? mcount : (size_t)-1)
+#define CLOSE_MSTREAM(FOUT) 0
+
+#else /* simulate HAVE_OPEN_MEMSTREAM */
+
+#define ASSIGN_MSTREAM(BUF,FOUT) \
+ do { \
+ int tempfd; \
+ char *tmpf; \
+ if ((tempfd = gettempfile(NULL, 1, &tmpf)) < 0) { \
+ zwarnnam(name, "can't open temp file: %e", errno); \
+ return 1; \
+ } \
+ unlink(tmpf); \
+ if ((fout = fdopen(tempfd, "w+")) == NULL) { \
+ close(tempfd); \
+ zwarnnam(name, "can't open temp file: %e", errno); \
+ return 1; \
+ } \
+ } while (0)
+#define READ_MSTREAM(BUF,FOUT) \
+ ((((count = ftell(FOUT)), (BUF = (char *)zalloc(count + 1))) && \
+ ((fseek(FOUT, 0L, SEEK_SET) == 0) && !(BUF[count] = '\0')) && \
+ (fread(BUF, 1, count, FOUT) == count)) ? count : (size_t)-1)
+#define CLOSE_MSTREAM(FOUT) fclose(FOUT)
+
#endif
- FILE *fout = stdout;
- Histent ent;
+#define IS_MSTREAM(FOUT) \
+ (FOUT != stdout && \
+ (OPT_ISSET(ops,'z') || OPT_ISSET(ops,'s') || OPT_ISSET(ops,'v')))
+
+ /* Testing EBADF special-cases >&- redirections */
+#define CLOSE_CLEANLY(FOUT) \
+ (IS_MSTREAM(FOUT) ? CLOSE_MSTREAM(FOUT) == 0 : \
+ ((FOUT == stdout) ? (fflush(FOUT) == 0 || errno == EBADF) : \
+ (fclose(FOUT) == 0))) /* implies error for -u on a closed fd */
+
+ Histent ent;
mnumber mnumval;
double doubleval;
int intval;
@@ -4032,6 +4088,47 @@ bin_print(char *name, char **args, Options ops, int func)
zulong zulongval;
char *stringval;
+ /* Error check option combinations and option arguments */
+
+ if (OPT_ISSET(ops, 'z') +
+ OPT_ISSET(ops, 's') + OPT_ISSET(ops, 'S') +
+ OPT_ISSET(ops, 'v') > 1) {
+ zwarnnam(name, "only one of -s, -S, -v, or -z allowed");
+ return 1;
+ }
+ if ((OPT_ISSET(ops, 'z') | OPT_ISSET(ops, 's') | OPT_ISSET(ops, 'S')) +
+ (OPT_ISSET(ops, 'c') | OPT_ISSET(ops, 'C')) > 1) {
+ zwarnnam(name, "-c or -C not allowed with -s, -S, or -z");
+ return 1;
+ }
+ if ((OPT_ISSET(ops, 'z') | OPT_ISSET(ops, 'v') |
+ OPT_ISSET(ops, 's') | OPT_ISSET(ops, 'S')) +
+ (OPT_ISSET(ops, 'p') | OPT_ISSET(ops, 'u')) > 1) {
+ zwarnnam(name, "-p or -u not allowed with -s, -S, -v, or -z");
+ return 1;
+ }
+ /*
+ if (OPT_ISSET(ops, 'f') &&
+ (OPT_ISSET(ops, 'S') || OPT_ISSET(ops, 'c') || OPT_ISSET(ops, 'C'))) {
+ zwarnnam(name, "-f not allowed with -c, -C, or -S");
+ return 1;
+ }
+ */
+
+ /* -C -- number of columns */
+ if (!fmt && OPT_ISSET(ops,'C')) {
+ char *eptr, *argptr = OPT_ARG(ops,'C');
+ nc = (int)zstrtol(argptr, &eptr, 10);
+ if (*eptr) {
+ zwarnnam(name, "number expected after -%c: %s", 'C', argptr);
+ return 1;
+ }
+ if (nc <= 0) {
+ zwarnnam(name, "invalid number of columns: %s", argptr);
+ return 1;
+ }
+ }
+
if (func == BIN_PRINTF) {
if (!strcmp(*args, "--") && !*++args) {
zwarnnam(name, "not enough arguments");
@@ -4096,7 +4193,7 @@ bin_print(char *name, char **args, Options ops, int func)
}
}
/* -P option -- interpret as a prompt sequence */
- if(OPT_ISSET(ops,'P')) {
+ if (OPT_ISSET(ops,'P')) {
/*
* promptexpand uses permanent storage: to avoid
* messy memory management, stick it on the heap
@@ -4110,13 +4207,13 @@ bin_print(char *name, char **args, Options ops, int func)
free(str);
}
/* -D option -- interpret as a directory, and use ~ */
- if(OPT_ISSET(ops,'D')) {
+ if (OPT_ISSET(ops,'D')) {
Nameddir d;
queue_signals();
/* TODO: finddir takes a metafied file */
d = finddir(args[n]);
- if(d) {
+ if (d) {
int dirlen = strlen(d->dir);
char *arg = zhalloc(len[n] - dirlen + strlen(d->node.nam) + 2);
sprintf(arg, "~%s%s", d->node.nam, args[n] + dirlen);
@@ -4139,26 +4236,12 @@ bin_print(char *name, char **args, Options ops, int func)
strmetasort(args, flags, len);
}
- /* -C -- number of columns */
- if (!fmt && OPT_ISSET(ops,'C')) {
- char *eptr, *argptr = OPT_ARG(ops,'C');
- nc = (int)zstrtol(argptr, &eptr, 10);
- if (*eptr) {
- zwarnnam(name, "number expected after -%c: %s", 'C', argptr);
- return 1;
- }
- if (nc <= 0) {
- zwarnnam(name, "invalid number of columns: %s", argptr);
- return 1;
- }
- }
-
/* -u and -p -- output to other than standard output */
if ((OPT_HASARG(ops,'u') || OPT_ISSET(ops,'p')) &&
/* rule out conflicting options -- historical precedence */
((!fmt && (OPT_ISSET(ops,'c') || OPT_ISSET(ops,'C'))) ||
- !(OPT_ISSET(ops, 'z') ||
- OPT_ISSET(ops, 's') || OPT_ISSET(ops, 'S')))) {
+ !(OPT_ISSET(ops, 'z') || OPT_ISSET(ops, 'v') ||
+ OPT_ISSET(ops, 's') || OPT_ISSET(ops, 'S')))) {
int fdarg, fd;
if (OPT_ISSET(ops, 'p')) {
@@ -4179,8 +4262,7 @@ bin_print(char *name, char **args, Options ops, int func)
} else {
fdarg = (int)zstrtol(argptr, &eptr, 10);
if (*eptr) {
- zwarnnam(name, "number expected after -%c: %s", 'u',
- argptr);
+ zwarnnam(name, "number expected after -u: %s", argptr);
return 1;
}
}
@@ -4197,6 +4279,10 @@ bin_print(char *name, char **args, Options ops, int func)
}
}
+ if (OPT_ISSET(ops, 'v') ||
+ (fmt && (OPT_ISSET(ops,'z') || OPT_ISSET(ops,'s'))))
+ ASSIGN_MSTREAM(buf,fout);
+
/* -c -- output in columns */
if (!fmt && (OPT_ISSET(ops,'c') || OPT_ISSET(ops,'C'))) {
int l, nr, sc, n, t, i;
@@ -4348,18 +4434,29 @@ bin_print(char *name, char **args, Options ops, int func)
}
fputc(OPT_ISSET(ops,'N') ? '\0' : '\n', fout);
}
- /* Testing EBADF special-cases >&- redirections */
- if ((fout != stdout) ? (fclose(fout) != 0) :
- (fflush(fout) != 0 && errno != EBADF)) {
+ if (IS_MSTREAM(fout) && (rcount = READ_MSTREAM(buf,fout)) == -1)
+ ret = 1;
+ if (!CLOSE_CLEANLY(fout) || ret) {
zwarnnam(name, "write error: %e", errno);
ret = 1;
}
+ if (buf) {
+ /* assert: we must be doing -v at this point */
+ queue_signals();
+ if (ret)
+ free(buf);
+ else
+ setsparam(OPT_ARG(ops, 'v'),
+ metafy(buf, rcount, META_REALLOC));
+ unqueue_signals();
+ }
return ret;
}
/* normal output */
if (!fmt) {
- if (OPT_ISSET(ops, 'z') || OPT_ISSET(ops, 's')) {
+ if (OPT_ISSET(ops, 'z') || OPT_ISSET(ops, 'v') ||
+ OPT_ISSET(ops, 's') || OPT_ISSET(ops, 'S')) {
/*
* We don't want the arguments unmetafied after all.
*/
@@ -4457,14 +4554,24 @@ bin_print(char *name, char **args, Options ops, int func)
OPT_ISSET(ops,'N') ? '\0' : ' ', fout);
}
}
- if (!(OPT_ISSET(ops,'n') || nnl))
+ if (!(OPT_ISSET(ops,'n') || OPT_ISSET(ops, 'v') || nnl))
fputc(OPT_ISSET(ops,'N') ? '\0' : '\n', fout);
- /* Testing EBADF special-cases >&- redirections */
- if ((fout != stdout) ? (fclose(fout) != 0) :
- (fflush(fout) != 0 && errno != EBADF)) {
+ if (IS_MSTREAM(fout) && (rcount = READ_MSTREAM(buf,fout)) == -1)
+ ret = 1;
+ if (!CLOSE_CLEANLY(fout) || ret) {
zwarnnam(name, "write error: %e", errno);
ret = 1;
}
+ if (buf) {
+ /* assert: we must be doing -v at this point */
+ queue_signals();
+ if (ret)
+ free(buf);
+ else
+ setsparam(OPT_ARG(ops, 'v'),
+ metafy(buf, rcount, META_REALLOC));
+ unqueue_signals();
+ }
return ret;
}
@@ -4474,20 +4581,6 @@ 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,'z') || OPT_ISSET(ops,'s')) {
-#ifdef HAVE_OPEN_MEMSTREAM
- if ((fout = open_memstream(&buf, &mcount)) == NULL)
- zwarnnam(name, "open_memstream failed");
-#else
- int tempfd;
- char *tmpf;
- if ((tempfd = gettempfile(NULL, 1, &tmpf)) < 0
- || (fout = fdopen(tempfd, "w+")) == NULL)
- zwarnnam(name, "can't open temp file: %e", errno);
- unlink(tmpf);
-#endif
- }
-
/* printf style output */
*spec = '%';
argp = args;
@@ -4751,11 +4844,9 @@ bin_print(char *name, char **args, Options ops, int func)
}
zwarnnam(name, "%s: invalid directive", start);
if (*c) c[1] = save;
- /* Testing EBADF special-cases >&- redirections */
- if ((fout != stdout) ? (fclose(fout) != 0) :
- (fflush(fout) != 0 && errno != EBADF)) {
+ /* Why do we care about a clean close here? */
+ if (!CLOSE_CLEANLY(fout))
zwarnnam(name, "write error: %e", errno);
- }
#ifdef HAVE_OPEN_MEMSTREAM
if (buf)
free(buf);
@@ -4853,41 +4944,34 @@ bin_print(char *name, char **args, Options ops, int func)
/* if there are remaining args, reuse format string */
} while (*argp && argp != first && !fmttrunc && !OPT_ISSET(ops,'r'));
- if (OPT_ISSET(ops,'z') || OPT_ISSET(ops,'s')) {
-#ifdef HAVE_OPEN_MEMSTREAM
- putc(0, fout);
- fclose(fout);
- fout = NULL;
-#else
- rewind(fout);
- buf = (char *)zalloc(count + 1);
- fread(buf, count, 1, fout);
- buf[count] = '\0';
-#endif
+ if (IS_MSTREAM(fout)) {
queue_signals();
- if (OPT_ISSET(ops,'z')) {
- zpushnode(bufstack, buf);
+ if ((rcount = READ_MSTREAM(buf,fout)) == -1) {
+ zwarnnam(name, "i/o error: %e", errno);
+ if (buf)
+ free(buf);
} else {
- ent = prepnexthistent();
- ent->node.nam = buf;
- 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();
}
-#ifdef HAVE_OPEN_MEMSTREAM
- if (fout)
-#endif
+ if (!CLOSE_CLEANLY(fout))
{
- /* Testing EBADF special-cases >&- redirections */
- if ((fout != stdout) ? (fclose(fout) != 0) :
- (fflush(fout) != 0 && errno != EBADF)) {
- zwarnnam(name, "write error: %e", errno);
- ret = 1;
- }
+ zwarnnam(name, "write error: %e", errno);
+ ret = 1;
}
return ret;
}
@@ -6463,7 +6547,13 @@ bin_test(char *name, char **argv, UNUSED(Options ops), int func)
nargs = arrlen(argv);
if (nargs == 3 || nargs == 4)
{
- if (*argv[0] == '(' && *argv[nargs-1] == ')') {
+ /*
+ * As parentheses are an extension, we need to be careful ---
+ * if this is a three-argument expression that could
+ * be a binary operator, prefer that.
+ */
+ if (!strcmp(argv[0], "(") && !strcmp(argv[nargs-1],")") &&
+ (nargs != 3 || !is_cond_binary_op(argv[1]))) {
argv[nargs-1] = NULL;
argv++;
}
diff --git a/Src/exec.c b/Src/exec.c
index c0ee527b7..352615c83 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -471,9 +471,10 @@ zexecve(char *pth, char **argv, char **newenvp)
if ((fd = open(pth, O_RDONLY|O_NOCTTY)) >= 0) {
argv0 = *argv;
*argv = pth;
+ execvebuf[0] = '\0';
ct = read(fd, execvebuf, POUNDBANGLIMIT);
close(fd);
- if (ct > 0) {
+ if (ct >= 0) {
if (execvebuf[0] == '#') {
if (execvebuf[1] == '!') {
for (t0 = 0; t0 != ct; t0++)
@@ -2264,7 +2265,7 @@ addvars(Estate state, Wordcode pc, int addflags)
* is implicitly scoped.
*/
flags = (!(addflags & ADDVAR_RESTORE) &&
- locallevel > 0 && isset(WARNCREATEGLOBAL)) ?
+ locallevel > forklevel && isset(WARNCREATEGLOBAL)) ?
ASSPM_WARN_CREATE : 0;
xtr = isset(XTRACE);
if (xtr) {
@@ -2784,6 +2785,11 @@ execcmd(Estate state, int input, int output, int how, int last1)
* arguments before and no command substitution
* has provided a status.
*/
+ if (badcshglob == 1) {
+ zerr("no match");
+ lastval = 1;
+ return;
+ }
cmdoutval = use_cmdoutval ? lastval : 0;
if (varspc)
addvars(state, varspc, 0);
@@ -3225,7 +3231,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
* not terminal, unless `file' is a terminal. */
if (nullexec == 1 && fn->fd1 == 0 &&
isset(SHINSTDIN) && interact && !zleactive)
- init_io();
+ init_io(NULL);
break;
case REDIR_CLOSE:
if (fn->varid) {
@@ -3475,10 +3481,10 @@ execcmd(Estate state, int input, int output, int how, int last1)
restore_queue_signals(q);
} else if (is_builtin || is_shfunc) {
LinkList restorelist = 0, removelist = 0;
+ int do_save = 0;
/* builtin or shell function */
- if (!forked && varspc) {
- int do_save = 0;
+ if (!forked) {
if (isset(POSIXBUILTINS)) {
/*
* If it's a function or special builtin --- save
@@ -3497,7 +3503,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
if ((cflags & BINF_COMMAND) || !assign)
do_save = 1;
}
- if (do_save)
+ if (do_save && varspc)
save_params(state, varspc, &restorelist, &removelist);
}
if (varspc) {
@@ -3643,6 +3649,8 @@ execcmd(Estate state, int input, int output, int how, int last1)
}
dont_queue_signals();
lastval = execbuiltin(args, assigns, (Builtin) hn);
+ if (do_save & BINF_COMMAND)
+ errflag &= ~ERRFLAG_ERROR;
restore_queue_signals(q);
fflush(stdout);
if (save[1] == -2) {
diff --git a/Src/glob.c b/Src/glob.c
index 94b3f620d..69de15544 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -1230,7 +1230,7 @@ zglob(LinkList list, LinkNode np, int nountok)
char *s;
int sense, qualsfound;
off_t data;
- char *sdata, *newcolonmod;
+ char *sdata, *newcolonmod, *ptr;
int (*func) _((char *, Statptr, off_t, char *));
/*
@@ -1273,6 +1273,9 @@ zglob(LinkList list, LinkNode np, int nountok)
*s++ = 0;
if (qualsfound == 2)
s += 2;
+ for (ptr = s; *ptr; ptr++)
+ if (*ptr == Dash)
+ *ptr = '-';
while (*s && !newcolonmod) {
func = (int (*) _((char *, Statptr, off_t, char *)))0;
if (idigit(*s)) {
@@ -3512,6 +3515,7 @@ zshtokenize(char *s, int flags)
case ')':
if (flags & ZSHTOK_SHGLOB)
break;
+ /*FALLTHROUGH*/
case '>':
case '^':
case '#':
@@ -3521,7 +3525,9 @@ zshtokenize(char *s, int flags)
case '*':
case '?':
case '=':
- for (t = ztokens; *t; t++)
+ case '-':
+ case '!':
+ for (t = ztokens; *t; t++) {
if (*t == *s) {
if (bslash)
s[-1] = (flags & ZSHTOK_SUBST) ? Bnullkeep : Bnull;
@@ -3529,6 +3535,8 @@ zshtokenize(char *s, int flags)
*s = (t - ztokens) + Pound;
break;
}
+ }
+ break;
}
bslash = 0;
}
@@ -3802,13 +3810,16 @@ qualsheval(char *name, UNUSED(struct stat *buf), UNUSED(off_t days), char *str)
if ((prog = parse_string(str, 0))) {
int ef = errflag, lv = lastval, ret;
+ int cshglob = badcshglob;
unsetparam("reply");
setsparam("REPLY", ztrdup(name));
+ badcshglob = 0;
execode(prog, 1, 0, "globqual");
- ret = lastval;
+ if ((ret = lastval))
+ badcshglob |= cshglob;
/* Retain any user interrupt error status */
errflag = ef | (errflag & ERRFLAG_INT);
lastval = lv;
diff --git a/Src/hashtable.c b/Src/hashtable.c
index 2d1ff87cb..0664c3694 100644
--- a/Src/hashtable.c
+++ b/Src/hashtable.c
@@ -1276,6 +1276,15 @@ printaliasnode(HashNode hn, int printflags)
}
if (printflags & PRINT_LIST) {
+ /* Fast fail on unrepresentable values. */
+ if (strchr(a->node.nam, '=')) {
+ zwarn("invalid alias '%s' encountered while printing aliases",
+ a->node.nam);
+ /* ### TODO: Return an error status to the C caller */
+ return;
+ }
+
+ /* Normal path. */
printf("alias ");
if (a->node.flags & ALIAS_SUFFIX)
printf("-s ");
diff --git a/Src/hashtable.h b/Src/hashtable.h
index b6346bb9a..3606e9785 100644
--- a/Src/hashtable.h
+++ b/Src/hashtable.h
@@ -53,7 +53,7 @@
#define BIN_LOGOUT 19
#define BIN_TEST 20
#define BIN_BRACKET 21
-#define BIN_EXPORT 22
+#define BIN_READONLY 22
#define BIN_ECHO 23
#define BIN_DISABLE 24
#define BIN_ENABLE 25
diff --git a/Src/init.c b/Src/init.c
index dcce1d7ce..4097327ee 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -240,13 +240,11 @@ loop(int toplevel, int justonce)
return LOOP_OK;
}
-/* Shared among parseargs(), parseopts(), init_io(), and init_misc() */
-static char *cmd;
static int restricted;
/**/
static void
-parseargs(char **argv, char **runscript)
+parseargs(char **argv, char **runscript, char **cmdptr)
{
char **x;
LinkList paramlist;
@@ -272,7 +270,7 @@ parseargs(char **argv, char **runscript)
opts[SHINSTDIN] = 0;
opts[SINGLECOMMAND] = 0;
- if (parseopts(NULL, &argv, opts, &cmd, NULL))
+ if (parseopts(NULL, &argv, opts, cmdptr, NULL))
exit(1);
/*
@@ -290,7 +288,7 @@ parseargs(char **argv, char **runscript)
if (*argv) {
if (unset(SHINSTDIN)) {
posixzero = *argv;
- if (cmd)
+ if (*cmdptr)
argzero = *argv;
else
*runscript = *argv;
@@ -299,7 +297,7 @@ parseargs(char **argv, char **runscript)
}
while (*argv)
zaddlinknode(paramlist, ztrdup(*argv++));
- } else if (!cmd)
+ } else if (!*cmdptr)
opts[SHINSTDIN] = 1;
if(isset(SINGLECOMMAND))
opts[INTERACTIVE] &= 1;
@@ -497,7 +495,7 @@ printhelp(void)
/**/
mod_export void
-init_io(void)
+init_io(char *cmd)
{
static char outbuf[BUFSIZ], errbuf[BUFSIZ];
@@ -521,6 +519,8 @@ init_io(void)
for (i = 3; i < 10; i++)
close(i);
}
+#else
+ (void)cmd;
#endif
if (shout) {
@@ -802,7 +802,7 @@ init_term(void)
/**/
void
-setupvals(void)
+setupvals(char *cmd)
{
#ifdef USE_GETPWUID
struct passwd *pswd;
@@ -1086,6 +1086,9 @@ setupvals(void)
/* Colour sequences for outputting colours in prompts and zle */
set_default_colour_sequences();
+
+ if (cmd)
+ setsparam("ZSH_EXECUTION_STRING", ztrdup(cmd));
}
/*
@@ -1267,7 +1270,7 @@ run_init_scripts(void)
/**/
void
-init_misc(void)
+init_misc(char *cmd)
{
#ifndef RESTRICTED_R
if ( restricted )
@@ -1604,6 +1607,7 @@ mod_export int
zsh_main(UNUSED(int argc), char **argv)
{
char **t, *runscript = NULL;
+ char *cmd; /* argument to -c */
int t0;
#ifdef USE_LOCALE
setlocale(LC_ALL, "");
@@ -1652,18 +1656,18 @@ zsh_main(UNUSED(int argc), char **argv)
opts[LOGINSHELL] = (**argv == '-');
opts[PRIVILEGED] = (getuid() != geteuid() || getgid() != getegid());
/* sets ZLE, INTERACTIVE, SHINSTDIN and SINGLECOMMAND */
- parseargs(argv, &runscript);
+ parseargs(argv, &runscript, &cmd);
SHTTY = -1;
- init_io();
- setupvals();
+ init_io(cmd);
+ setupvals(cmd);
init_signals();
init_bltinmods();
init_builtins();
run_init_scripts();
setupshin(runscript);
- init_misc();
+ init_misc(cmd);
for (;;) {
/*
diff --git a/Src/lex.c b/Src/lex.c
index 0f260d08f..3ea878c7b 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -35,7 +35,7 @@
/* tokens */
/**/
-mod_export char ztokens[] = "#$^*(())$=|{}[]`<>>?~`,'\"\\\\";
+mod_export char ztokens[] = "#$^*(())$=|{}[]`<>>?~`,-!'\"\\\\";
/* parts of the current token */
@@ -394,8 +394,10 @@ ctxtlex(void)
#define LX2_DQUOTE 15
#define LX2_BQUOTE 16
#define LX2_COMMA 17
-#define LX2_OTHER 18
-#define LX2_META 19
+#define LX2_DASH 18
+#define LX2_BANG 19
+#define LX2_OTHER 20
+#define LX2_META 21
static unsigned char lexact1[256], lexact2[256], lextok2[256];
@@ -405,10 +407,10 @@ initlextabs(void)
{
int t0;
static char *lx1 = "\\q\n;!&|(){}[]<>";
- static char *lx2 = ";)|$[]~({}><=\\\'\"`,";
+ static char *lx2 = ";)|$[]~({}><=\\\'\"`,-!";
for (t0 = 0; t0 != 256; t0++) {
- lexact1[t0] = LX1_OTHER;
+ lexact1[t0] = LX1_OTHER;
lexact2[t0] = LX2_OTHER;
lextok2[t0] = t0;
}
@@ -801,7 +803,7 @@ gettok(void)
return INOUTPAR;
hungetc(d);
lexstop = 0;
- if (!(incond == 1 || incmdpos))
+ if (!(isset(SHGLOB) || incond == 1 || incmdpos))
break;
return INPAR;
case LX1_OUTPAR:
@@ -919,7 +921,7 @@ gettok(void)
static enum lextok
gettokstr(int c, int sub)
{
- int bct = 0, pct = 0, brct = 0, fdpar = 0;
+ int bct = 0, pct = 0, brct = 0, seen_brct = 0, fdpar = 0;
int intpos = 1, in_brace_param = 0;
int inquote, unmatched = 0;
enum lextok peek;
@@ -1033,8 +1035,10 @@ gettokstr(int c, int sub)
}
break;
case LX2_INBRACK:
- if (!in_brace_param)
+ if (!in_brace_param) {
brct++;
+ seen_brct = 1;
+ }
c = Inbrack;
break;
case LX2_OUTBRACK:
@@ -1346,9 +1350,32 @@ gettokstr(int c, int sub)
c = Tick;
SETPAREND
break;
- }
- add(c);
- c = hgetc();
+ case LX2_DASH:
+ /*
+ * - shouldn't be treated as a special character unless
+ * we're in a pattern. Howeve,simply counting "[" doesn't
+ * work as []a-z] is a valid expression and we don't know
+ * down here what this "[" is for as $foo[stuff] is valid
+ * in zsh. So just detect an opening [, which is enough
+ * to turn this into a pattern; the Dash will be harmlessly
+ * untokenised if not wanted.
+ */
+ if (seen_brct)
+ c = Dash;
+ else
+ c = '-';
+ break;
+ case LX2_BANG:
+ /*
+ * Same logic as Dash, for ! to perform negation in range.
+ */
+ if (seen_brct)
+ c = Bang;
+ else
+ c = '!';
+ }
+ add(c);
+ c = hgetc();
if (intpos)
intpos--;
if (lexstop)
diff --git a/Src/options.c b/Src/options.c
index 2678626c7..17c46c311 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -256,7 +256,7 @@ static struct optname optns[] = {
{{NULL, "unset", OPT_EMULATE|OPT_BSHELL}, UNSET},
{{NULL, "verbose", 0}, VERBOSE},
{{NULL, "vi", 0}, VIMODE},
-{{NULL, "warncreateglobal", 0}, WARNCREATEGLOBAL},
+{{NULL, "warncreateglobal", OPT_EMULATE}, WARNCREATEGLOBAL},
{{NULL, "xtrace", 0}, XTRACE},
{{NULL, "zle", OPT_SPECIAL}, USEZLE},
{{NULL, "braceexpand", OPT_ALIAS}, /* ksh/bash */ -IGNOREBRACES},
diff --git a/Src/params.c b/Src/params.c
index d8bf83d0e..b2e889738 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -2552,18 +2552,20 @@ setarrvalue(Value v, char **val)
v->pm->node.nam);
return;
} else {
- char **old, **new, **p, **q, **r;
- int pre_assignment_length;
+ char **const old = v->pm->gsu.a->getfn(v->pm);
+ char **new;
+ char **p, **q, **r; /* index variables */
+ const int pre_assignment_length = arrlen(old);
int post_assignment_length;
int i;
+ q = old;
+
if ((v->flags & VALFLAG_INV) && unset(KSHARRAYS)) {
if (v->start > 0)
v->start--;
v->end--;
}
- q = old = v->pm->gsu.a->getfn(v->pm);
- pre_assignment_length = arrlen(old);
if (v->start < 0) {
v->start += pre_assignment_length;
if (v->start < 0)
@@ -2703,30 +2705,18 @@ static void
check_warn_create(Param pm, const char *pmtype)
{
Funcstack i;
- const char *name;
if (pm->level != 0 || (pm->node.flags & PM_SPECIAL))
return;
- name = NULL;
for (i = funcstack; i; i = i->prev) {
if (i->tp == FS_FUNC) {
DPUTS(!i->name, "funcstack entry with no name");
- name = i->name;
+ zwarn("%s parameter %s created globally in function %s",
+ pmtype, pm->node.nam, i->name);
break;
}
}
-
- if (name)
- {
- zwarn("%s parameter %s created globally in function %s",
- pmtype, pm->node.nam, name);
- }
- else
- {
- zwarn("%s parameter %s created globally in function",
- pmtype, pm->node.nam);
- }
}
/**/
@@ -2866,7 +2856,7 @@ mod_export Param
setsparam(char *s, char *val)
{
return assignsparam(
- s, val, isset(WARNCREATEGLOBAL) && locallevel > 0 ?
+ s, val, isset(WARNCREATEGLOBAL) && locallevel > forklevel ?
ASSPM_WARN_CREATE : 0);
}
@@ -2964,7 +2954,7 @@ mod_export Param
setaparam(char *s, char **aval)
{
return assignaparam(
- s, aval, isset(WARNCREATEGLOBAL) && locallevel >0 ?
+ s, aval, isset(WARNCREATEGLOBAL) && locallevel > forklevel ?
ASSPM_WARN_CREATE : 0);
}
@@ -2995,7 +2985,7 @@ sethparam(char *s, char **val)
if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING))) {
DPUTS(!v, "BUG: assigning to undeclared associative array");
createparam(t, PM_HASHED);
- checkcreate = isset(WARNCREATEGLOBAL) && locallevel > 0;
+ checkcreate = isset(WARNCREATEGLOBAL) && locallevel > forklevel;
} else if (!(PM_TYPE(v->pm->node.flags) & PM_HASHED) &&
!(v->pm->node.flags & PM_SPECIAL)) {
unsetparam(t);
@@ -3059,6 +3049,7 @@ setnparam(char *s, mnumber val)
if (ss)
*ss = '\0';
pm = createparam(t, ss ? PM_ARRAY :
+ isset(POSIXIDENTIFIERS) ? PM_SCALAR :
(val.type & MN_INTEGER) ? PM_INTEGER : PM_FFLOAT);
if (!pm)
pm = (Param) paramtab->getnode(paramtab, t);
@@ -3073,7 +3064,7 @@ setnparam(char *s, mnumber val)
/* errflag |= ERRFLAG_ERROR; */
return NULL;
}
- if (!was_unset && isset(WARNCREATEGLOBAL) && locallevel > 0)
+ if (!was_unset && isset(WARNCREATEGLOBAL) && locallevel > forklevel)
check_warn_create(v->pm, "numeric");
}
setnumvalue(v, val);
@@ -3111,7 +3102,8 @@ setiparam_no_convert(char *s, zlong val)
convbase(buf, val, 10);
return assignsparam(
s, ztrdup(buf),
- isset(WARNCREATEGLOBAL) && locallevel > 0 ? ASSPM_WARN_CREATE : 0);
+ isset(WARNCREATEGLOBAL) && locallevel > forklevel ?
+ ASSPM_WARN_CREATE : 0);
}
/* Unset a parameter */
@@ -5179,9 +5171,6 @@ printparamvalue(Param p, int printflags)
}
if (printflags & PRINT_KV_PAIR)
putchar(' ');
- else if ((printflags & PRINT_TYPESET) &&
- (PM_TYPE(p->node.flags) == PM_ARRAY || PM_TYPE(p->node.flags) == PM_HASHED))
- printf("%s=", p->node.nam);
else
putchar('=');
@@ -5252,7 +5241,6 @@ mod_export void
printparamnode(HashNode hn, int printflags)
{
Param p = (Param) hn;
- int array_typeset;
if (p->node.flags & PM_UNSET) {
if (isset(POSIXBUILTINS) && (p->node.flags & PM_READONLY) &&
@@ -5277,28 +5265,8 @@ printparamnode(HashNode hn, int printflags)
*/
return;
}
- /*
- * Printing the value of array: this needs to be on
- * a separate line so more care is required.
- */
- array_typeset = (PM_TYPE(p->node.flags) == PM_ARRAY ||
- PM_TYPE(p->node.flags) == PM_HASHED) &&
- !(printflags & PRINT_NAMEONLY);
- if (array_typeset && (p->node.flags & PM_READONLY)) {
- /*
- * We need to create the array before making it
- * readonly.
- */
- printf("typeset -a ");
- zputs(p->node.nam, stdout);
- putchar('\n');
- printparamvalue(p, printflags);
- printflags |= PRINT_NAMEONLY;
- }
printf("typeset ");
}
- else
- array_typeset = 0;
/* Print the attributes of the parameter */
if (printflags & (PRINT_TYPE|PRINT_TYPESET)) {
@@ -5346,8 +5314,6 @@ printparamnode(HashNode hn, int printflags)
} else {
quotedzputs(p->node.nam, stdout);
- if (array_typeset)
- putchar('\n');
printparamvalue(p, printflags);
}
}
diff --git a/Src/parse.c b/Src/parse.c
index 83ba396b0..4829e3a6d 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -2252,6 +2252,8 @@ void (*condlex) _((void)) = zshlex;
* cond : cond_1 { SEPER } [ DBAR { SEPER } cond ]
*/
+#define COND_SEP() (tok == SEPER && condlex != testlex && *zshlextext != ';')
+
/**/
static int
par_cond(void)
@@ -2259,11 +2261,11 @@ par_cond(void)
int p = ecused, r;
r = par_cond_1();
- while (tok == SEPER)
+ while (COND_SEP())
condlex();
if (tok == DBAR) {
condlex();
- while (tok == SEPER)
+ while (COND_SEP())
condlex();
ecispace(p, 1);
par_cond();
@@ -2284,11 +2286,11 @@ par_cond_1(void)
int r, p = ecused;
r = par_cond_2();
- while (tok == SEPER)
+ while (COND_SEP())
condlex();
if (tok == DAMPER) {
condlex();
- while (tok == SEPER)
+ while (COND_SEP())
condlex();
ecispace(p, 1);
par_cond_1();
@@ -2348,7 +2350,9 @@ par_cond_2(void)
* We fall through here on any non-numeric infix operator
* or any other time there are at least two arguments.
*/
- }
+ } else
+ while (COND_SEP())
+ condlex();
if (tok == BANG) {
/*
* In "test" compatibility mode, "! -a ..." and "! -o ..."
@@ -2366,10 +2370,10 @@ par_cond_2(void)
int r;
condlex();
- while (tok == SEPER)
+ while (COND_SEP())
condlex();
r = par_cond();
- while (tok == SEPER)
+ while (COND_SEP())
condlex();
if (tok != OUTPAR)
YYERROR(ecused);
@@ -2385,7 +2389,7 @@ par_cond_2(void)
/* Check first argument for [[ STRING ]] re-interpretation */
if (s1 /* tok != DOUTBRACK && tok != DAMPER && tok != DBAR */
&& tok != LEXERR && (!dble || n_testargs)) {
- condlex();
+ do condlex(); while (COND_SEP());
return par_cond_double(dupstring("-n"), s1);
} else
YYERROR(ecused);
@@ -2398,14 +2402,16 @@ par_cond_2(void)
* checked it does have a string representation).
*/
tok = STRING;
- }
+ } else
+ while (COND_SEP())
+ condlex();
if (tok == INANG || tok == OUTANG) {
enum lextok xtok = tok;
- condlex();
+ do condlex(); while (COND_SEP());
if (tok != STRING)
YYERROR(ecused);
s3 = tokstr;
- condlex();
+ do condlex(); while (COND_SEP());
ecadd(WCB_COND((xtok == INANG ? COND_STRLT : COND_STRGTR), 0));
ecstr(s1);
ecstr(s3);
@@ -2428,11 +2434,11 @@ par_cond_2(void)
if (!n_testargs)
dble = (s2 && *s2 == '-' && !s2[2]);
incond++; /* parentheses do globbing */
- condlex();
+ do condlex(); while (COND_SEP());
incond--; /* parentheses do grouping */
if (tok == STRING && !dble) {
s3 = tokstr;
- condlex();
+ do condlex(); while (COND_SEP());
if (tok == STRING) {
LinkList l = newlinklist();
@@ -2441,7 +2447,7 @@ par_cond_2(void)
while (tok == STRING) {
addlinknode(l, tokstr);
- condlex();
+ do condlex(); while (COND_SEP());
}
return par_cond_multi(s1, l);
} else
diff --git a/Src/pattern.c b/Src/pattern.c
index 9e8a80ae1..72c7d97d5 100644
--- a/Src/pattern.c
+++ b/Src/pattern.c
@@ -247,7 +247,7 @@ typedef unsigned long zrange_t;
*/
static const char zpc_chars[ZPC_COUNT] = {
'/', '\0', Bar, Outpar, Tilde, Inpar, Quest, Star, Inbrack, Inang,
- Hat, Pound, Bnullkeep, Quest, Star, '+', '!', '@'
+ Hat, Pound, Bnullkeep, Quest, Star, '+', Bang, '!', '@'
};
/*
@@ -257,7 +257,7 @@ static const char zpc_chars[ZPC_COUNT] = {
/**/
mod_export const char *zpc_strings[ZPC_COUNT] = {
NULL, NULL, "|", NULL, "~", "(", "?", "*", "[", "<",
- "^", "#", NULL, "?(", "*(", "+(", "!(", "@("
+ "^", "#", NULL, "?(", "*(", "+(", "!(", "\\!(", "@("
};
/*
@@ -481,7 +481,7 @@ patcompcharsset(void)
*/
zpc_special[ZPC_KSH_QUEST] = zpc_special[ZPC_KSH_STAR] =
zpc_special[ZPC_KSH_PLUS] = zpc_special[ZPC_KSH_BANG] =
- zpc_special[ZPC_KSH_AT] = Marker;
+ zpc_special[ZPC_KSH_BANG2] = zpc_special[ZPC_KSH_AT] = Marker;
}
/*
* Note that if we are using KSHGLOB, then we test for a following
@@ -1268,6 +1268,8 @@ patcomppiece(int *flagp, int paren)
kshchar = STOUC('+');
else if (*patparse == zpc_special[ZPC_KSH_BANG])
kshchar = STOUC('!');
+ else if (*patparse == zpc_special[ZPC_KSH_BANG2])
+ kshchar = STOUC('!');
else if (*patparse == zpc_special[ZPC_KSH_AT])
kshchar = STOUC('@');
else if (*patparse == zpc_special[ZPC_KSH_STAR])
@@ -1424,7 +1426,7 @@ patcomppiece(int *flagp, int paren)
DPUTS(zpc_special[ZPC_INBRACK] == Marker,
"Treating '[' as pattern character although disabled");
flags |= P_SIMPLE;
- if (*patparse == Hat || *patparse == '^' || *patparse == '!') {
+ if (*patparse == Hat || *patparse == Bang) {
patparse++;
starter = patnode(P_ANYBUT);
} else
@@ -1459,7 +1461,7 @@ patcomppiece(int *flagp, int paren)
charstart = patparse;
METACHARINC(patparse);
- if (*patparse == '-' && patparse[1] &&
+ if (*patparse == Dash && patparse[1] &&
patparse[1] != Outbrack) {
patadd(NULL, STOUC(Meta)+PP_RANGE, 1, PA_NOALIGN);
if (itok(*charstart)) {
@@ -1468,7 +1470,7 @@ patcomppiece(int *flagp, int paren)
} else {
patadd(charstart, 0, patparse-charstart, PA_NOALIGN);
}
- charstart = ++patparse; /* skip ASCII '-' */
+ charstart = ++patparse; /* skip Dash token */
METACHARINC(patparse);
}
if (itok(*charstart)) {
@@ -4245,7 +4247,8 @@ haswilds(char *str)
((str[-1] == Quest && !zpc_disables[ZPC_KSH_QUEST]) ||
(str[-1] == Star && !zpc_disables[ZPC_KSH_STAR]) ||
(str[-1] == '+' && !zpc_disables[ZPC_KSH_PLUS]) ||
- (str[-1] == '!' && !zpc_disables[ZPC_KSH_BANG]) ||
+ (str[-1] == Bang && !zpc_disables[ZPC_KSH_BANG]) ||
+ (str[-1] == '!' && !zpc_disables[ZPC_KSH_BANG2]) ||
(str[-1] == '@' && !zpc_disables[ZPC_KSH_AT]))))
return 1;
break;
diff --git a/Src/subst.c b/Src/subst.c
index d9c9d24aa..bb1dd8939 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -1887,12 +1887,13 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
if (quotetype == QT_DOLLARS ||
quotetype == QT_BACKSLASH_PATTERN)
goto flagerr;
- if (s[1] == '-') {
+ if (s[1] == '-' || s[1] == '+') {
if (quotemod)
goto flagerr;
s++;
quotemod = 1;
- quotetype = QT_SINGLE_OPTIONAL;
+ quotetype = (*s == '-') ? QT_SINGLE_OPTIONAL :
+ QT_QUOTEDZPUTS;
} else {
if (quotetype == QT_SINGLE_OPTIONAL) {
/* extra q's after '-' not allowed */
@@ -3583,7 +3584,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
ap = aval;
if (quotemod > 0) {
- if (quotetype > QT_BACKSLASH) {
+ if (quotetype == QT_QUOTEDZPUTS) {
+ for (; *ap; ap++)
+ *ap = quotedzputs(*ap, NULL);
+ } else if (quotetype > QT_BACKSLASH) {
int sl;
char *tmp;
@@ -3626,7 +3630,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
if (!copied)
val = dupstring(val), copied = 1;
if (quotemod > 0) {
- if (quotetype > QT_BACKSLASH) {
+ if (quotetype == QT_QUOTEDZPUTS) {
+ val = quotedzputs(val, NULL);
+ } else if (quotetype > QT_BACKSLASH) {
int sl;
char *tmp;
tmp = quotestring(val, NULL, quotetype);
diff --git a/Src/text.c b/Src/text.c
index 9421d70ce..04acd2aac 100644
--- a/Src/text.c
+++ b/Src/text.c
@@ -40,9 +40,32 @@
/**/
int text_expand_tabs;
+/*
+ * Binary operators in conditions.
+ * There order is tied to the order of the definitions COND_STREQ
+ * et seq. in zsh.h.
+ */
+static const char *cond_binary_ops[] = {
+ "=", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq",
+ "-ne", "-lt", "-gt", "-le", "-ge", "=~"
+};
+
static char *tptr, *tbuf, *tlim, *tpending;
static int tsiz, tindent, tnewlins, tjob;
+/**/
+int
+is_cond_binary_op(const char *str)
+{
+ const char **op;
+ for (op = cond_binary_ops; *op; op++)
+ {
+ if (!strcmp(str, *op))
+ return 1;
+ }
+ return 0;
+}
+
static void
dec_tindent(void)
{
@@ -120,7 +143,7 @@ taddchr(int c)
/**/
static void
-taddstr(char *s)
+taddstr(const char *s)
{
int sl = strlen(s);
char c;
@@ -822,11 +845,6 @@ gettext2(Estate state)
break;
case WC_COND:
{
- static char *c1[] = {
- "=", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq",
- "-ne", "-lt", "-gt", "-le", "-ge", "=~"
- };
-
int ctype;
if (!s) {
@@ -912,7 +930,7 @@ gettext2(Estate state)
/* Binary test: `a = b' etc. */
taddstr(ecgetstr(state, EC_NODUP, NULL));
taddstr(" ");
- taddstr(c1[ctype - COND_STREQ]);
+ taddstr(cond_binary_ops[ctype - COND_STREQ]);
taddstr(" ");
taddstr(ecgetstr(state, EC_NODUP, NULL));
if (ctype == COND_STREQ ||
diff --git a/Src/utils.c b/Src/utils.c
index 464097034..fd0bab320 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -387,6 +387,7 @@ putshout(int c)
return 0;
}
+#ifdef MULTIBYTE_SUPPORT
/*
* Turn a character into a visible representation thereof. The visible
* string is put together in a static buffer, and this function returns
@@ -409,9 +410,76 @@ putshout(int c)
/**/
mod_export char *
+nicechar_sel(int c, int quotable)
+{
+ static char buf[10];
+ char *s = buf;
+ c &= 0xff;
+ if (ZISPRINT(c))
+ goto done;
+ if (c & 0x80) {
+ if (isset(PRINTEIGHTBIT))
+ goto done;
+ *s++ = '\\';
+ *s++ = 'M';
+ *s++ = '-';
+ c &= 0x7f;
+ if(ZISPRINT(c))
+ goto done;
+ }
+ if (c == 0x7f) {
+ if (quotable) {
+ *s++ = '\\';
+ *s++ = 'C';
+ *s++ = '-';
+ } else
+ *s++ = '^';
+ c = '?';
+ } else if (c == '\n') {
+ *s++ = '\\';
+ c = 'n';
+ } else if (c == '\t') {
+ *s++ = '\\';
+ c = 't';
+ } else if (c < 0x20) {
+ if (quotable) {
+ *s++ = '\\';
+ *s++ = 'C';
+ *s++ = '-';
+ } else
+ *s++ = '^';
+ c += 0x40;
+ }
+ done:
+ /*
+ * The resulting string is still metafied, so check if
+ * we are returning a character in the range that needs metafication.
+ * This can't happen if the character is printed "nicely", so
+ * this results in a maximum of two bytes total (plus the null).
+ */
+ if (imeta(c)) {
+ *s++ = Meta;
+ *s++ = c ^ 32;
+ } else
+ *s++ = c;
+ *s = 0;
+ return buf;
+}
+
+/**/
+mod_export char *
nicechar(int c)
{
- static char buf[6];
+ return nicechar_sel(c, 0);
+}
+
+#else /* MULTIBYTE_SUPPORT */
+
+/**/
+mod_export char *
+nicechar(int c)
+{
+ static char buf[10];
char *s = buf;
c &= 0xff;
if (ZISPRINT(c))
@@ -427,7 +495,9 @@ nicechar(int c)
goto done;
}
if (c == 0x7f) {
- *s++ = '^';
+ *s++ = '\\';
+ *s++ = 'C';
+ *s++ = '-';
c = '?';
} else if (c == '\n') {
*s++ = '\\';
@@ -436,7 +506,9 @@ nicechar(int c)
*s++ = '\\';
c = 't';
} else if (c < 0x20) {
- *s++ = '^';
+ *s++ = '\\';
+ *s++ = 'C';
+ *s++ = '-';
c += 0x40;
}
done:
@@ -455,6 +527,24 @@ nicechar(int c)
return buf;
}
+#endif /* MULTIBYTE_SUPPORT */
+
+/*
+ * Return 1 if nicechar() would reformat this character.
+ */
+
+/**/
+mod_export int
+is_nicechar(int c)
+{
+ c &= 0xff;
+ if (ZISPRINT(c))
+ return 0;
+ if (c & 0x80)
+ return !isset(PRINTEIGHTBIT);
+ return (c == 0x7f || c == '\n' || c == '\t' || c < 0x20);
+}
+
/**/
#ifdef MULTIBYTE_SUPPORT
static mbstate_t mb_shiftstate;
@@ -507,7 +597,7 @@ mb_charinit(void)
/**/
mod_export char *
-wcs_nicechar(wchar_t c, size_t *widthp, char **swidep)
+wcs_nicechar_sel(wchar_t c, size_t *widthp, char **swidep, int quotable)
{
static char *buf;
static int bufalloc = 0, newalloc;
@@ -532,7 +622,12 @@ wcs_nicechar(wchar_t c, size_t *widthp, char **swidep)
s = buf;
if (!iswprint(c) && (c < 0x80 || !isset(PRINTEIGHTBIT))) {
if (c == 0x7f) {
- *s++ = '^';
+ if (quotable) {
+ *s++ = '\\';
+ *s++ = 'C';
+ *s++ = '-';
+ } else
+ *s++ = '^';
c = '?';
} else if (c == L'\n') {
*s++ = '\\';
@@ -541,7 +636,12 @@ wcs_nicechar(wchar_t c, size_t *widthp, char **swidep)
*s++ = '\\';
c = 't';
} else if (c < 0x20) {
- *s++ = '^';
+ if (quotable) {
+ *s++ = '\\';
+ *s++ = 'C';
+ *s++ = '-';
+ } else
+ *s++ = '^';
c += 0x40;
} else if (c >= 0x80) {
ret = -1;
@@ -612,6 +712,30 @@ wcs_nicechar(wchar_t c, size_t *widthp, char **swidep)
}
/**/
+mod_export char *
+wcs_nicechar(wchar_t c, size_t *widthp, char **swidep)
+{
+ return wcs_nicechar_sel(c, widthp, swidep, 0);
+}
+
+/*
+ * Return 1 if wcs_nicechar() would reformat this character for display.
+ */
+
+/**/
+mod_export int is_wcs_nicechar(wchar_t c)
+{
+ if (!iswprint(c) && (c < 0x80 || !isset(PRINTEIGHTBIT))) {
+ if (c == 0x7f || c == L'\n' || c == L'\t' || c < 0x20)
+ return 1;
+ if (c >= 0x80) {
+ return (c >= 0x100);
+ }
+ }
+ return 0;
+}
+
+/**/
mod_export int
zwcwidth(wint_t wc)
{
@@ -1326,6 +1450,9 @@ time_t lastwatch;
* If "retval" is not NULL, the return value of the first hook function to
* return non-zero is stored in *"retval". The return value is not otherwise
* available as the calling context is restored.
+ *
+ * Returns 0 if at least one function was called (regardless of that function's
+ * exit status), and 1 otherwise.
*/
/**/
@@ -2861,11 +2988,12 @@ spckword(char **s, int hist, int cmd, int ask)
if (strncmp(guess, best, preflen))
return;
/* replace the temporarily expanded prefix with the original */
- u = (char *) hcalloc(t - *s + strlen(best + preflen) + 1);
+ u = (char *) zhalloc(t - *s + strlen(best + preflen) + 1);
strncpy(u, *s, t - *s);
strcpy(u + (t - *s), best + preflen);
} else {
- u = (char *) hcalloc(strlen(best) + 2);
+ u = (char *) zhalloc(strlen(best) + 2);
+ *u = '\0';
strcpy(u + 1, best);
}
best = u;
@@ -3204,7 +3332,7 @@ zjoin(char **arr, int delim, int heap)
len += strlen(*s) + 1 + (imeta(delim) ? 1 : 0);
if (!len)
return heap? "" : ztrdup("");
- ptr = ret = (heap ? (char *) hcalloc(len) : (char *) zshcalloc(len));
+ ptr = ret = (char *) (heap ? zhalloc(len) : zalloc(len));
for (s = arr; *s; s++) {
strucpy(&ptr, *s);
if (imeta(delim)) {
@@ -3290,7 +3418,8 @@ spacesplit(char *s, int allownull, int heap, int quote)
int l = sizeof(*ret) * (wordcount(s, NULL, -!allownull) + 1);
char *(*dup)(const char *) = (heap ? dupstring : ztrdup);
- ptr = ret = (heap ? (char **) hcalloc(l) : (char **) zshcalloc(l));
+ /* ### TODO: s/calloc/alloc/ */
+ ptr = ret = (char **) (heap ? hcalloc(l) : zshcalloc(l));
if (quote) {
/*
@@ -3320,8 +3449,8 @@ spacesplit(char *s, int allownull, int heap, int quote)
t = s;
(void)findsep(&s, NULL, quote);
if (s > t || allownull) {
- *ptr = (heap ? (char *) hcalloc((s - t) + 1) :
- (char *) zshcalloc((s - t) + 1));
+ *ptr = (char *) (heap ? zhalloc((s - t) + 1) :
+ zalloc((s - t) + 1));
ztrncpy(*ptr++, t, s - t);
} else
*ptr++ = dup(nulstring);
@@ -3511,7 +3640,7 @@ sepjoin(char **s, char *sep, int heap)
}
sl = strlen(sep);
for (t = s, l = 1 - sl; *t; l += strlen(*t) + sl, t++);
- r = p = (heap ? (char *) hcalloc(l) : (char *) zshcalloc(l));
+ r = p = (char *) (heap ? zhalloc(l) : zalloc(l));
t = s;
while (*t) {
strucpy(&p, *t);
@@ -3538,14 +3667,14 @@ sepsplit(char *s, char *sep, int allownull, int heap)
sl = strlen(sep);
n = wordcount(s, sep, 1);
- r = p = (heap ? (char **) hcalloc((n + 1) * sizeof(char *)) :
- (char **) zshcalloc((n + 1) * sizeof(char *)));
+ r = p = (char **) (heap ? zhalloc((n + 1) * sizeof(char *)) :
+ zalloc((n + 1) * sizeof(char *)));
for (t = s; n--;) {
tt = t;
(void)findsep(&t, sep, 0);
- *p = (heap ? (char *) hcalloc(t - tt + 1) :
- (char *) zshcalloc(t - tt + 1));
+ *p = (char *) (heap ? zhalloc(t - tt + 1) :
+ zalloc(t - tt + 1));
strncpy(*p, tt, t - tt);
(*p)[t - tt] = '\0';
p++;
@@ -3759,7 +3888,7 @@ inittyptab(void)
typtab['\0'] |= IMETA;
typtab[STOUC(Meta) ] |= IMETA;
typtab[STOUC(Marker)] |= IMETA;
- for (t0 = (int)STOUC(Pound); t0 <= (int)STOUC(Comma); t0++)
+ for (t0 = (int)STOUC(Pound); t0 <= (int)STOUC(LAST_NORMAL_TOK); t0++)
typtab[t0] |= ITOK | IMETA;
for (t0 = (int)STOUC(Snull); t0 <= (int)STOUC(Nularg); t0++)
typtab[t0] |= ITOK | IMETA | INULL;
@@ -4832,12 +4961,15 @@ niceztrlen(char const *s)
* If outstrp is not NULL, set *outstrp to a zalloc'd version of
* the output (still metafied).
*
- * If "heap" is non-zero, use the heap for *outstrp, else zalloc.
+ * If flags contains NICEFLAG_HEAP, use the heap for *outstrp, else
+ * zalloc.
+ * If flags contsins NICEFLAG_QUOTE, the output is going to be within
+ * $'...', so quote "'" with a backslash.
*/
/**/
mod_export size_t
-mb_niceformat(const char *s, FILE *stream, char **outstrp, int heap)
+mb_niceformat(const char *s, FILE *stream, char **outstrp, int flags)
{
size_t l = 0, newl;
int umlen, outalloc, outleft, eol = 0;
@@ -4872,7 +5004,7 @@ mb_niceformat(const char *s, FILE *stream, char **outstrp, int heap)
/* FALL THROUGH */
case MB_INVALID:
/* The byte didn't convert, so output it as a \M-... sequence. */
- fmt = nicechar(*ptr);
+ fmt = nicechar_sel(*ptr, flags & NICEFLAG_QUOTE);
newl = strlen(fmt);
cnt = 1;
/* Get mbs out of its undefined state. */
@@ -4884,7 +5016,10 @@ mb_niceformat(const char *s, FILE *stream, char **outstrp, int heap)
cnt = 1;
/* FALL THROUGH */
default:
- fmt = wcs_nicechar(c, &newl, NULL);
+ if (c == L'\'' && (flags & NICEFLAG_QUOTE))
+ fmt = "\\'";
+ else
+ fmt = wcs_nicechar_sel(c, &newl, NULL, flags & NICEFLAG_QUOTE);
break;
}
@@ -4918,13 +5053,76 @@ mb_niceformat(const char *s, FILE *stream, char **outstrp, int heap)
if (outstrp) {
*outptr = '\0';
/* Use more efficient storage for returned string */
- *outstrp = heap ? dupstring(outstr) : ztrdup(outstr);
- free(outstr);
+ if (flags & NICEFLAG_NODUP)
+ *outstrp = outstr;
+ else {
+ *outstrp = (flags & NICEFLAG_HEAP) ? dupstring(outstr) :
+ ztrdup(outstr);
+ free(outstr);
+ }
}
return l;
}
+/*
+ * Return 1 if mb_niceformat() would reformat this string, else 0.
+ */
+
+/**/
+mod_export int
+is_mb_niceformat(const char *s)
+{
+ int umlen, eol = 0, ret = 0;
+ wchar_t c;
+ char *ums, *ptr;
+ mbstate_t mbs;
+
+ ums = ztrdup(s);
+ untokenize(ums);
+ ptr = unmetafy(ums, &umlen);
+
+ memset(&mbs, 0, sizeof mbs);
+ while (umlen > 0) {
+ size_t cnt = eol ? MB_INVALID : mbrtowc(&c, ptr, umlen, &mbs);
+
+ switch (cnt) {
+ case MB_INCOMPLETE:
+ eol = 1;
+ /* FALL THROUGH */
+ case MB_INVALID:
+ /* The byte didn't convert, so output it as a \M-... sequence. */
+ if (is_nicechar(*ptr)) {
+ ret = 1;
+ break;
+ }
+ cnt = 1;
+ /* Get mbs out of its undefined state. */
+ memset(&mbs, 0, sizeof mbs);
+ break;
+ case 0:
+ /* Careful: converting '\0' returns 0, but a '\0' is a
+ * real character for us, so we should consume 1 byte. */
+ cnt = 1;
+ /* FALL THROUGH */
+ default:
+ if (is_wcs_nicechar(c))
+ ret = 1;
+ break;
+ }
+
+ if (ret)
+ break;
+
+ umlen -= cnt;
+ ptr += cnt;
+ }
+
+ free(ums);
+
+ return ret;
+}
+
/* ztrdup multibyte string with nice formatting */
/**/
@@ -4933,7 +5131,7 @@ nicedup(const char *s, int heap)
{
char *retstr;
- (void)mb_niceformat(s, NULL, &retstr, heap);
+ (void)mb_niceformat(s, NULL, &retstr, heap ? NICEFLAG_HEAP : 0);
return retstr;
}
@@ -5072,6 +5270,21 @@ mb_metastrlenend(char *ptr, int width, char *eptr)
ret = mbrtowc(&wc, &inchar, 1, &mb_shiftstate);
if (ret == MB_INCOMPLETE) {
+ /*
+ * "num_in_char" is only used for incomplete characters.
+ * The assumption is that we will output all trailing octets
+ * that form part of an incomplete character as a single
+ * character (of single width) if we don't get a complete
+ * character. This is purely pragmatic --- I'm not aware
+ * of a standard way of dealing with incomplete characters.
+ *
+ * If we do get a complete character, num_in_char
+ * becomes irrelevant and is set to zero
+ *
+ * This is in contrast to "num" which counts the characters
+ * or widths in complete characters. The two are summed,
+ * so we don't count characters twice.
+ */
num_in_char++;
} else {
if (ret == MB_INVALID) {
@@ -5098,8 +5311,8 @@ mb_metastrlenend(char *ptr, int width, char *eptr)
}
}
- /* If incomplete, treat remainder as trailing single bytes */
- return num + num_in_char;
+ /* If incomplete, treat remainder as trailing single character */
+ return num + (num_in_char ? 1 : 0);
}
/*
@@ -5712,25 +5925,76 @@ quotestring(const char *s, char **e, int instring)
return v;
}
-/* Unmetafy and output a string, quoted if it contains special characters. */
+/*
+ * Unmetafy and output a string, quoted if it contains special
+ * characters.
+ *
+ * If stream is NULL, return the same output with any allocation on the
+ * heap.
+ */
/**/
-mod_export int
+mod_export char *
quotedzputs(char const *s, FILE *stream)
{
int inquote = 0, c;
+ char *outstr, *ptr;
/* check for empty string */
- if(!*s)
- return fputs("''", stream);
+ if(!*s) {
+ if (!stream)
+ return dupstring("''");
+ fputs("''", stream);
+ return NULL;
+ }
- if (!hasspecial(s))
- return zputs(s, stream);
+#ifdef MULTIBYTE_SUPPORT
+ if (is_mb_niceformat(s)) {
+ if (stream) {
+ fputs("$'", stream);
+ mb_niceformat(s, stream, NULL, NICEFLAG_QUOTE);
+ fputc('\'', stream);
+ return NULL;
+ } else {
+ char *substr;
+ mb_niceformat(s, NULL, &substr, NICEFLAG_QUOTE|NICEFLAG_NODUP);
+ outstr = (char *)zhalloc(4 + strlen(substr));
+ sprintf(outstr, "$'%s'", substr);
+ free(substr);
+ return outstr;
+ }
+ }
+#endif /* MULTIBYTE_SUPPORT */
+ if (!hasspecial(s)) {
+ if (stream) {
+ zputs(s, stream);
+ return NULL;
+ } else {
+ return dupstring(s);
+ }
+ }
+
+ if (!stream) {
+ const char *cptr;
+ int l = strlen(s) + 2;
+ for (cptr = s; *cptr; cptr++) {
+ if (*cptr == Meta)
+ cptr++;
+ else if (*cptr == '\'')
+ l += isset(RCQUOTES) ? 1 : 3;
+ }
+ ptr = outstr = zhalloc(l + 1);
+ } else {
+ ptr = outstr = NULL;
+ }
if (isset(RCQUOTES)) {
/* use rc-style quotes-within-quotes for the whole string */
- if(fputc('\'', stream) < 0)
- return EOF;
+ if (stream) {
+ if (fputc('\'', stream) < 0)
+ return NULL;
+ } else
+ *ptr++ = '\'';
while(*s) {
if (*s == Meta)
c = *++s ^ 32;
@@ -5738,53 +6002,98 @@ quotedzputs(char const *s, FILE *stream)
c = *s;
s++;
if (c == '\'') {
- if(fputc('\'', stream) < 0)
- return EOF;
- } else if(c == '\n' && isset(CSHJUNKIEQUOTES)) {
- if(fputc('\\', stream) < 0)
- return EOF;
+ if (stream) {
+ if (fputc('\'', stream) < 0)
+ return NULL;
+ } else
+ *ptr++ = '\'';
+ } else if (c == '\n' && isset(CSHJUNKIEQUOTES)) {
+ if (stream) {
+ if (fputc('\\', stream) < 0)
+ return NULL;
+ } else
+ *ptr++ = '\\';
+ }
+ if (stream) {
+ if (fputc(c, stream) < 0)
+ return NULL;
+ } else {
+ if (imeta(c)) {
+ *ptr++ = Meta;
+ *ptr++ = c ^ 32;
+ } else
+ *ptr++ = c;
}
- if(fputc(c, stream) < 0)
- return EOF;
}
- if(fputc('\'', stream) < 0)
- return EOF;
+ if (stream) {
+ if (fputc('\'', stream) < 0)
+ return NULL;
+ } else
+ *ptr++ = '\'';
} else {
/* use Bourne-style quoting, avoiding empty quoted strings */
- while(*s) {
+ while (*s) {
if (*s == Meta)
c = *++s ^ 32;
else
c = *s;
s++;
if (c == '\'') {
- if(inquote) {
- if(fputc('\'', stream) < 0)
- return EOF;
+ if (inquote) {
+ if (stream) {
+ if (putc('\'', stream) < 0)
+ return NULL;
+ } else
+ *ptr++ = '\'';
inquote=0;
}
- if(fputs("\\'", stream) < 0)
- return EOF;
+ if (stream) {
+ if (fputs("\\'", stream) < 0)
+ return NULL;
+ } else {
+ *ptr++ = '\\';
+ *ptr++ = '\'';
+ }
} else {
if (!inquote) {
- if(fputc('\'', stream) < 0)
- return EOF;
+ if (stream) {
+ if (fputc('\'', stream) < 0)
+ return NULL;
+ } else
+ *ptr++ = '\'';
inquote=1;
}
- if(c == '\n' && isset(CSHJUNKIEQUOTES)) {
- if(fputc('\\', stream) < 0)
- return EOF;
+ if (c == '\n' && isset(CSHJUNKIEQUOTES)) {
+ if (stream) {
+ if (fputc('\\', stream) < 0)
+ return NULL;
+ } else
+ *ptr++ = '\\';
+ }
+ if (stream) {
+ if (fputc(c, stream) < 0)
+ return NULL;
+ } else {
+ if (imeta(c)) {
+ *ptr++ = Meta;
+ *ptr++ = c ^ 32;
+ } else
+ *ptr++ = c;
}
- if(fputc(c, stream) < 0)
- return EOF;
}
}
if (inquote) {
- if(fputc('\'', stream) < 0)
- return EOF;
+ if (stream) {
+ if (fputc('\'', stream) < 0)
+ return NULL;
+ } else
+ *ptr++ = '\'';
}
}
- return 0;
+ if (!stream)
+ *ptr++ = '\0';
+
+ return outstr;
}
/* Double-quote a metafied string. */
diff --git a/Src/zsh.h b/Src/zsh.h
index d3bfcefcc..b83b8bdbb 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -192,24 +192,31 @@ struct mathfunc {
#define Tilde ((char) 0x98)
#define Qtick ((char) 0x99)
#define Comma ((char) 0x9a)
+#define Dash ((char) 0x9b) /* Only in patterns */
+#define Bang ((char) 0x9c) /* Only in patterns */
+/*
+ * Marks the last of the group above.
+ * Remaining tokens are even more special.
+ */
+#define LAST_NORMAL_TOK Bang
/*
* Null arguments: placeholders for single and double quotes
* and backslashes.
*/
-#define Snull ((char) 0x9b)
-#define Dnull ((char) 0x9c)
-#define Bnull ((char) 0x9d)
+#define Snull ((char) 0x9d)
+#define Dnull ((char) 0x9e)
+#define Bnull ((char) 0x9f)
/*
* Backslash which will be returned to "\" instead of being stripped
* when we turn the string into a printable format.
*/
-#define Bnullkeep ((char) 0x9e)
+#define Bnullkeep ((char) 0xa0)
/*
* Null argument that does not correspond to any character.
* This should be last as it does not appear in ztokens and
* is used to initialise the IMETA type in inittyptab().
*/
-#define Nularg ((char) 0x9f)
+#define Nularg ((char) 0xa1)
/*
* Take care to update the use of IMETA appropriately when adding
@@ -220,7 +227,7 @@ struct mathfunc {
* Also used in pattern character arrays as guaranteed not to
* mark a character in a string.
*/
-#define Marker ((char) 0xa0)
+#define Marker ((char) 0xa2)
/* chars that need to be quoted if meant literally */
@@ -272,7 +279,12 @@ enum {
/*
* As QT_BACKSLASH, but a NULL string is shown as ''.
*/
- QT_BACKSLASH_SHOWNULL
+ QT_BACKSLASH_SHOWNULL,
+ /*
+ * Quoting as produced by quotedzputs(), used for human
+ * readability of parameter values.
+ */
+ QT_QUOTEDZPUTS
};
#define QT_IS_SINGLE(x) ((x) == QT_SINGLE || (x) == QT_SINGLE_OPTIONAL)
@@ -1538,6 +1550,7 @@ enum zpc_chars {
ZPC_KSH_STAR, /* * for *(...) in KSH_GLOB */
ZPC_KSH_PLUS, /* + for +(...) in KSH_GLOB */
ZPC_KSH_BANG, /* ! for !(...) in KSH_GLOB */
+ ZPC_KSH_BANG2, /* ! for !(...) in KSH_GLOB, untokenised */
ZPC_KSH_AT, /* @ for @(...) in KSH_GLOB */
ZPC_COUNT /* Number of special chararacters */
};
@@ -3051,6 +3064,13 @@ enum {
#define AFTERTRAPHOOK (zshhooks + 2)
#ifdef MULTIBYTE_SUPPORT
+/* Final argument to mb_niceformat() */
+enum {
+ NICEFLAG_HEAP = 1, /* Heap allocation where needed */
+ NICEFLAG_QUOTE = 2, /* Result will appear in $'...' */
+ NICEFLAG_NODUP = 4, /* Leave allocated */
+};
+
/* Metafied input */
#define nicezputs(str, outs) (void)mb_niceformat((str), (outs), NULL, 0)
#define MB_METACHARINIT() mb_charinit()