summaryrefslogtreecommitdiff
path: root/Src
diff options
context:
space:
mode:
Diffstat (limited to 'Src')
-rw-r--r--Src/Builtins/sched.c3
-rw-r--r--Src/Modules/curses.c2
-rw-r--r--Src/Modules/datetime.c15
-rw-r--r--Src/Modules/files.c2
-rw-r--r--Src/Modules/newuser.c2
-rw-r--r--Src/Modules/regex.c38
-rw-r--r--Src/Modules/socket.c9
-rw-r--r--Src/Modules/stat.c7
-rw-r--r--Src/Modules/system.c183
-rw-r--r--Src/Modules/system.mdd2
-rw-r--r--Src/Modules/tcp.c9
-rw-r--r--Src/Modules/zftp.c8
-rw-r--r--Src/Modules/zpty.c2
-rw-r--r--Src/Zle/compcore.c10
-rw-r--r--Src/Zle/compctl.c2
-rw-r--r--Src/Zle/complete.c16
-rw-r--r--Src/Zle/complist.c97
-rw-r--r--Src/Zle/compresult.c83
-rw-r--r--Src/Zle/computil.c2
-rw-r--r--Src/Zle/iwidgets.list4
-rw-r--r--Src/Zle/zle.h6
-rw-r--r--Src/Zle/zle_bindings.c2
-rw-r--r--Src/Zle/zle_hist.c23
-rw-r--r--Src/Zle/zle_keymap.c8
-rw-r--r--Src/Zle/zle_main.c20
-rw-r--r--Src/Zle/zle_misc.c91
-rw-r--r--Src/Zle/zle_move.c43
-rw-r--r--Src/Zle/zle_params.c3
-rw-r--r--Src/Zle/zle_refresh.c15
-rw-r--r--Src/Zle/zle_tricky.c52
-rw-r--r--Src/Zle/zle_utils.c51
-rw-r--r--Src/builtin.c473
-rw-r--r--Src/compat.c9
-rw-r--r--Src/context.c4
-rw-r--r--Src/exec.c253
-rw-r--r--Src/glob.c34
-rw-r--r--Src/hashtable.c81
-rw-r--r--Src/hist.c13
-rw-r--r--Src/init.c10
-rw-r--r--Src/input.c17
-rw-r--r--Src/jobs.c26
-rw-r--r--Src/lex.c23
-rw-r--r--Src/loop.c41
-rw-r--r--Src/math.c21
-rw-r--r--Src/options.c5
-rw-r--r--Src/params.c74
-rw-r--r--Src/parse.c146
-rw-r--r--Src/pattern.c25
-rw-r--r--Src/prompt.c10
-rw-r--r--Src/signals.c26
-rw-r--r--Src/subst.c10
-rw-r--r--Src/text.c135
-rw-r--r--Src/utils.c387
-rw-r--r--Src/watch.c7
-rw-r--r--Src/zsh.h115
55 files changed, 2161 insertions, 594 deletions
diff --git a/Src/Builtins/sched.c b/Src/Builtins/sched.c
index bcf7661f4..5d5dac6b7 100644
--- a/Src/Builtins/sched.c
+++ b/Src/Builtins/sched.c
@@ -220,7 +220,8 @@ bin_sched(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
endstr = "-- ";
else
endstr = "";
- printf("%3d %s %s%s%s\n", sn, tbuf, flagstr, endstr, sch->cmd);
+ printf("%3d %s %s%s%s\n", sn, tbuf, flagstr, endstr,
+ unmeta(sch->cmd));
}
return 0;
} else if (!argptr[1]) {
diff --git a/Src/Modules/curses.c b/Src/Modules/curses.c
index 41ad2c6e4..62dbd55ea 100644
--- a/Src/Modules/curses.c
+++ b/Src/Modules/curses.c
@@ -765,7 +765,7 @@ zccmd_string(const char *nam, char **args)
w = (ZCWin)getdata(node);
#ifdef HAVE_WADDWSTR
- mb_metacharinit();
+ mb_charinit();
wptr = wstr = zhalloc((strlen(str)+1) * sizeof(wchar_t));
while (*str && (clen = mb_metacharlenconv(str, &wc))) {
diff --git a/Src/Modules/datetime.c b/Src/Modules/datetime.c
index 63a04dc89..86c61cf1c 100644
--- a/Src/Modules/datetime.c
+++ b/Src/Modules/datetime.c
@@ -53,10 +53,12 @@ reverse_strftime(char *nam, char **argv, char *scalar, int quiet)
* to use the current timezone. This is probably the best guess;
* it's the one that will cause dates and times output by strftime
* without the -r option and without an explicit timezone to be
- * converted back correctly.
+ * converted back correctly. Additionally, tm_mday is set to 1
+ * as that and not 0 corresponds to the first of the month.
*/
(void)memset(&tm, 0, sizeof(tm));
tm.tm_isdst = -1;
+ tm.tm_mday = 1;
endp = strptime(argv[1], argv[0], &tm);
if (!endp) {
@@ -96,7 +98,7 @@ reverse_strftime(char *nam, char **argv, char *scalar, int quiet)
static int
output_strftime(char *nam, char **argv, Options ops, UNUSED(int func))
{
- int bufsize, x;
+ int bufsize, x, len;
char *endptr = NULL, *scalar = NULL, *buffer;
time_t secs;
struct tm *t;
@@ -129,16 +131,19 @@ output_strftime(char *nam, char **argv, Options ops, UNUSED(int func))
bufsize = strlen(argv[0]) * 8;
buffer = zalloc(bufsize);
+ len = 0;
for (x=0; x < 4; x++) {
- if (ztrftime(buffer, bufsize, argv[0], t, 0L) >= 0)
+ if ((len = ztrftime(buffer, bufsize, argv[0], t, 0L)) >= 0)
break;
buffer = zrealloc(buffer, bufsize *= 2);
}
+ DPUTS(len < 0, "bad output from ztrftime");
if (scalar) {
- setsparam(scalar, metafy(buffer, -1, META_DUP));
+ setsparam(scalar, metafy(buffer, len, META_DUP));
} else {
- printf("%s\n", buffer);
+ fwrite(buffer, 1, len, stdout);
+ putchar('\n');
}
zfree(buffer, bufsize);
diff --git a/Src/Modules/files.c b/Src/Modules/files.c
index f86b9c1e9..dbcff6307 100644
--- a/Src/Modules/files.c
+++ b/Src/Modules/files.c
@@ -131,7 +131,7 @@ domkdir(char *nam, char *path, mode_t mode, int p)
return 0;
}
oumask = umask(0);
- err = mkdir(path, mode) ? errno : 0;
+ err = mkdir(rpath, mode) ? errno : 0;
umask(oumask);
if(!err)
return 0;
diff --git a/Src/Modules/newuser.c b/Src/Modules/newuser.c
index 71902da7d..efdb2abba 100644
--- a/Src/Modules/newuser.c
+++ b/Src/Modules/newuser.c
@@ -67,7 +67,7 @@ check_dotfile(const char *dotdir, const char *fname)
int
boot_(UNUSED(Module m))
{
- const char *dotdir = getsparam("ZDOTDIR");
+ const char *dotdir = getsparam_u("ZDOTDIR");
const char *spaths[] = {
#ifdef SITESCRIPT_DIR
SITESCRIPT_DIR,
diff --git a/Src/Modules/regex.c b/Src/Modules/regex.c
index ce57de986..16cc77f30 100644
--- a/Src/Modules/regex.c
+++ b/Src/Modules/regex.c
@@ -115,6 +115,7 @@ zcond_regex_match(char **a, int id)
} else {
zlong offs;
char *ptr;
+ int clen, leftlen;
m = matches;
s = metafy(lhstr + m->rm_so, m->rm_eo - m->rm_so, META_DUP);
@@ -123,19 +124,25 @@ zcond_regex_match(char **a, int id)
* Count the characters before the match.
*/
ptr = lhstr;
+ leftlen = m->rm_so;
offs = 0;
- MB_METACHARINIT();
- while (ptr < lhstr + m->rm_so) {
+ MB_CHARINIT();
+ 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 < lhstr + m->rm_eo) {
+ leftlen = m->rm_eo - m->rm_so;
+ while (leftlen) {
offs++;
- ptr += MB_METACHARLEN(ptr);
+ clen = MB_CHARLEN(ptr, leftlen);
+ ptr += clen;
+ leftlen -= clen;
}
setiparam("MEND", offs + !isset(KSHARRAYS) - 1);
if (nelem) {
@@ -148,20 +155,31 @@ zcond_regex_match(char **a, int id)
++n, ++m, ++bptr, ++eptr)
{
char buf[DIGBUFSIZE];
+ if (m->rm_so < 0 || m->rm_eo < 0) {
+ *bptr = ztrdup("-1");
+ *eptr = ztrdup("-1");
+ continue;
+ }
ptr = lhstr;
+ leftlen = m->rm_so;
offs = 0;
/* Find the start offset */
- MB_METACHARINIT();
- while (ptr < lhstr + m->rm_so) {
+ MB_CHARINIT();
+ 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 < lhstr + m->rm_eo) {
+ leftlen = m->rm_eo - m->rm_so;
+ 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/socket.c b/Src/Modules/socket.c
index 6c70d3166..65b87d7dd 100644
--- a/Src/Modules/socket.c
+++ b/Src/Modules/socket.c
@@ -175,7 +175,7 @@ bin_zsocket(char *nam, char **args, Options ops, UNUSED(int func))
tv.tv_sec = 0;
tv.tv_usec = 0;
- if ((ret = select(lfd+1, &rfds, NULL, NULL, &tv))) return 1;
+ if ((ret = select(lfd+1, &rfds, NULL, NULL, &tv)) == 0) return 1;
else if (ret == -1)
{
zwarnnam(nam, "select error: %e", errno);
@@ -191,8 +191,11 @@ bin_zsocket(char *nam, char **args, Options ops, UNUSED(int func))
}
len = sizeof(soun);
- if ((rfd = accept(lfd, (struct sockaddr *)&soun, &len)) == -1)
- {
+ do {
+ rfd = accept(lfd, (struct sockaddr *)&soun, &len);
+ } while (rfd < 0 && errno == EINTR && !errflag);
+
+ if (rfd == -1) {
zwarnnam(nam, "could not accept connection: %e", errno);
return 1;
}
diff --git a/Src/Modules/stat.c b/Src/Modules/stat.c
index 6fc53894c..396177149 100644
--- a/Src/Modules/stat.c
+++ b/Src/Modules/stat.c
@@ -197,8 +197,11 @@ stattimeprint(time_t tim, char *outbuf, int flags)
}
if (flags & STF_STRING) {
char *oend = outbuf + strlen(outbuf);
- ztrftime(oend, 40, timefmt, (flags & STF_GMT) ? gmtime(&tim) :
- localtime(&tim), 0L);
+ /* Where the heck does "40" come from? */
+ int len = ztrftime(oend, 40, timefmt, (flags & STF_GMT) ? gmtime(&tim) :
+ localtime(&tim), 0L);
+ if (len > 0)
+ metafy(oend, len, META_NOALLOC);
if (flags & STF_RAW)
strcat(oend, ")");
}
diff --git a/Src/Modules/system.c b/Src/Modules/system.c
index f6a21d160..1ab1fb17e 100644
--- a/Src/Modules/system.c
+++ b/Src/Modules/system.c
@@ -279,6 +279,182 @@ bin_syswrite(char *nam, char **args, Options ops, UNUSED(int func))
}
+static struct { char *name; int oflag; } openopts[] = {
+#ifdef O_CLOEXEC
+ { "cloexec", O_CLOEXEC },
+#else
+# ifdef FD_CLOEXEC
+ { "cloexec", 0 }, /* this needs to be first in the table */
+# endif
+#endif
+#ifdef O_NOFOLLOW
+ { "nofollow", O_NOFOLLOW },
+#endif
+#ifdef O_SYNC
+ { "sync", O_SYNC },
+#endif
+#ifdef O_NOATIME
+ { "noatime", O_NOATIME },
+#endif
+ { "excl", O_EXCL | O_CREAT },
+ { "creat", O_CREAT },
+ { "create", O_CREAT },
+ { "truncate", O_TRUNC },
+ { "trunc", O_TRUNC }
+};
+
+/**/
+static int
+bin_sysopen(char *nam, char **args, Options ops, UNUSED(int func))
+{
+ int read = OPT_ISSET(ops, 'r');
+ int write = OPT_ISSET(ops, 'w');
+ int append = OPT_ISSET(ops, 'a') ? O_APPEND : 0;
+ int flags = O_NOCTTY | append | ((append || write) ?
+ (read ? O_RDWR : O_WRONLY) : O_RDONLY);
+ char *opt, *ptr, *nextopt, *fdvar;
+ int o, fd, explicit = -1;
+ mode_t perms = 0666;
+#if defined(FD_CLOEXEC) && !defined(O_CLOEXEC)
+ int fdflags;
+#endif
+
+ if (!OPT_ISSET(ops, 'u')) {
+ zwarnnam(nam, "file descriptor not specified");
+ return 1;
+ }
+
+ /* file descriptor, either 0-9 or a variable name */
+ fdvar = OPT_ARG(ops, 'u');
+ if (idigit(*fdvar) && !fdvar[1]) {
+ explicit = atoi(fdvar);
+ } else if (!isident(fdvar)) {
+ zwarnnam(nam, "not an identifier: %s", fdvar);
+ return 1;
+ }
+
+ /* open options */
+ if (OPT_ISSET(ops, 'o')) {
+ opt = OPT_ARG(ops, 'o');
+ while (opt) {
+ if (!strncasecmp(opt, "O_", 2)) /* ignore initial O_ */
+ opt += 2;
+ if ((nextopt = strchr(opt, ',')))
+ *nextopt++ = '\0';
+ for (o = sizeof(openopts)/sizeof(*openopts) - 1; o >= 0 &&
+ strcasecmp(openopts[o].name, opt); o--) {}
+ if (o < 0) {
+ zwarnnam(nam, "unsupported option: %s\n", opt);
+ return 1;
+ }
+#if defined(FD_CLOEXEC) && !defined(O_CLOEXEC)
+ if (!openopts[o].oflag)
+ fdflags = FD_CLOEXEC;
+#endif
+ flags |= openopts[o].oflag;
+ opt = nextopt;
+ }
+ }
+
+ /* -m: permissions or mode for created files */
+ if (OPT_ISSET(ops, 'm')) {
+ ptr = opt = OPT_ARG(ops, 'm');
+ while (*ptr >= '0' && *ptr <= '7') ptr++;
+ if (*ptr || ptr - opt < 3) {
+ zwarnnam(nam, "invalid mode %s", opt);
+ return 1;
+ }
+ perms = zstrtol(opt, 0, 8); /* octal number */
+ }
+
+ if (flags & O_CREAT)
+ fd = open(*args, flags, perms);
+ else
+ fd = open(*args, flags);
+
+ if (fd == -1) {
+ zwarnnam(nam, "can't open file %s: %e", *args, errno);
+ return 1;
+ }
+ fd = (explicit > -1) ? redup(fd, explicit) : movefd(fd);
+ if (fd == -1) {
+ zwarnnam(nam, "can't open file %s", *args);
+ return 1;
+ }
+
+#if defined(FD_CLOEXEC) && !defined(O_CLOEXEC)
+ if (fdflags)
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+#endif
+ if (explicit == -1) {
+ fdtable[fd] = FDT_EXTERNAL;
+ setiparam(fdvar, fd);
+ /* if setting the variable failed, close fd to avoid leak */
+ if (errflag)
+ zclose(fd);
+ }
+
+ return 0;
+}
+
+
+/*
+ * Return values of bin_sysseek:
+ * 0 Success
+ * 1 Error in parameters to command
+ * 2 Error on seek, ERRNO set by system
+ */
+
+/**/
+static int
+bin_sysseek(char *nam, char **args, Options ops, UNUSED(int func))
+{
+ int w = SEEK_SET, fd = 0;
+ char *whence;
+ off_t pos;
+
+ /* -u: file descriptor if not stdin */
+ if (OPT_ISSET(ops, 'u')) {
+ fd = getposint(OPT_ARG(ops, 'u'), nam);
+ if (fd < 0)
+ return 1;
+ }
+
+ /* -w: whence - starting point of seek */
+ if (OPT_ISSET(ops, 'w')) {
+ whence = OPT_ARG(ops, 'w');
+ if (!(strcasecmp(whence, "current") && strcmp(whence, "1")))
+ w = SEEK_CUR;
+ else if (!(strcasecmp(whence, "end") && strcmp(whence, "2")))
+ w = SEEK_END;
+ else if (strcasecmp(whence, "start") && strcmp(whence, "0")) {
+ zwarnnam(nam, "unknown argument to -w: %s", whence);
+ return 1;
+ }
+ }
+
+ pos = (off_t)mathevali(*args);
+ return (lseek(fd, pos, w) == -1) ? 2 : 0;
+}
+
+/**/
+static mnumber
+math_systell(UNUSED(char *name), int argc, mnumber *argv, UNUSED(int id))
+{
+ int fd = (argv->type == MN_INTEGER) ? argv->u.l : (int)argv->u.d;
+ mnumber ret;
+ ret.type = MN_INTEGER;
+ ret.u.l = 0;
+
+ if (fd < 0) {
+ zerr("file descriptor out of range");
+ return ret;
+ }
+ ret.u.l = lseek(fd, 0, SEEK_CUR);
+ return ret;
+}
+
+
/*
* Return values of bin_syserror:
* 0 Successfully processed error
@@ -542,6 +718,8 @@ static struct builtin bintab[] = {
BUILTIN("syserror", 0, bin_syserror, 0, 1, 0, "e:p:", NULL),
BUILTIN("sysread", 0, bin_sysread, 0, 1, 0, "c:i:o:s:t:", NULL),
BUILTIN("syswrite", 0, bin_syswrite, 1, 1, 0, "c:o:", NULL),
+ BUILTIN("sysopen", 0, bin_sysopen, 1, 1, 0, "rwau:o:m:", NULL),
+ BUILTIN("sysseek", 0, bin_sysseek, 1, 1, 0, "u:w:", NULL),
BUILTIN("zsystem", 0, bin_zsystem, 1, -1, 0, NULL, NULL)
};
@@ -611,6 +789,9 @@ scanpmsysparams(UNUSED(HashTable ht), ScanFunc func, int flags)
func(&spm.node, flags);
}
+static struct mathfunc mftab[] = {
+ NUMMATHFUNC("systell", math_systell, 1, 1, 0)
+};
static struct paramdef partab[] = {
SPECIALPMDEF("errnos", PM_ARRAY|PM_READONLY,
@@ -622,7 +803,7 @@ static struct paramdef partab[] = {
static struct features module_features = {
bintab, sizeof(bintab)/sizeof(*bintab),
NULL, 0,
- NULL, 0,
+ mftab, sizeof(mftab)/sizeof(*mftab),
partab, sizeof(partab)/sizeof(*partab),
0
};
diff --git a/Src/Modules/system.mdd b/Src/Modules/system.mdd
index 46f02d166..eed0c1b9d 100644
--- a/Src/Modules/system.mdd
+++ b/Src/Modules/system.mdd
@@ -2,7 +2,7 @@ name=zsh/system
link=dynamic
load=no
-autofeatures="b:sysread b:syswrite b:syserror p:errnos"
+autofeatures="b:sysread b:syswrite b:sysopen b:sysseek b:syserror p:errnos f:systell"
objects="system.o errnames.o"
diff --git a/Src/Modules/tcp.c b/Src/Modules/tcp.c
index 0d9522047..bc1765da1 100644
--- a/Src/Modules/tcp.c
+++ b/Src/Modules/tcp.c
@@ -519,7 +519,7 @@ bin_ztcp(char *nam, char **args, Options ops, UNUSED(int func))
tv.tv_sec = 0;
tv.tv_usec = 0;
- if ((ret = select(lfd+1, &rfds, NULL, NULL, &tv))) return 1;
+ if ((ret = select(lfd+1, &rfds, NULL, NULL, &tv)) == 0) return 1;
else if (ret == -1)
{
zwarnnam(nam, "select error: %e", errno);
@@ -536,8 +536,11 @@ bin_ztcp(char *nam, char **args, Options ops, UNUSED(int func))
sess = zts_alloc(ZTCP_INBOUND);
len = sizeof(sess->peer.in);
- if ((rfd = accept(lfd, (struct sockaddr *)&sess->peer.in, &len)) == -1)
- {
+ do {
+ rfd = accept(lfd, (struct sockaddr *)&sess->peer.in, &len);
+ } while (rfd < 0 && errno == EINTR && !errflag);
+
+ if (rfd == -1) {
zwarnnam(nam, "could not accept connection: %e", errno);
tcp_close(sess);
return 1;
diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c
index 09d4bd703..bd51512f9 100644
--- a/Src/Modules/zftp.c
+++ b/Src/Modules/zftp.c
@@ -731,7 +731,7 @@ zfgetmsg(void)
stopit = (*ptr++ != '-');
queue_signals();
- if (!(verbose = getsparam("ZFTP_VERBOSE")))
+ if (!(verbose = getsparam_u("ZFTP_VERBOSE")))
verbose = "";
if (strchr(verbose, lastcodestr[0])) {
/* print the whole thing verbatim */
@@ -1785,7 +1785,7 @@ zftp_open(char *name, char **args, int flags)
char *hname;
alarm(0);
queue_signals();
- if ((hname = getsparam("ZFTP_HOST")) && *hname)
+ if ((hname = getsparam_u("ZFTP_HOST")) && *hname)
zwarnnam(name, "timeout connecting to %s", hname);
else
zwarnnam(name, "timeout on host name lookup");
@@ -3077,7 +3077,7 @@ bin_zftp(char *name, char **args, UNUSED(Options ops), UNUSED(int func))
}
queue_signals();
- if ((prefs = getsparam("ZFTP_PREFS"))) {
+ if ((prefs = getsparam_u("ZFTP_PREFS"))) {
zfprefs = 0;
for (ptr = prefs; *ptr; ptr++) {
switch (toupper(STOUC(*ptr))) {
@@ -3149,9 +3149,11 @@ zftp_cleanup(void)
zfclose(zfsess != cursess);
}
zsfree(lastmsg);
+ lastmsg = NULL;
zfunsetparam("ZFTP_SESSION");
freelinklist(zfsessions, (FreeFunc) freesession);
zfree(zfstatusp, sizeof(int)*zfsesscnt);
+ zfstatusp = NULL;
}
static int
diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c
index 7b6130c6f..12e42b5bd 100644
--- a/Src/Modules/zpty.c
+++ b/Src/Modules/zpty.c
@@ -463,6 +463,8 @@ newptycmd(char *nam, char *pname, char **args, int echo, int nblock)
#endif
errno == EINTR));
+ setiparam("REPLY", master);
+
return 0;
}
diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c
index d4051bda0..ae3a64074 100644
--- a/Src/Zle/compcore.c
+++ b/Src/Zle/compcore.c
@@ -327,9 +327,7 @@ do_completion(UNUSED(Hookdef dummy), Compldat dat)
haspattern = 0;
complistmax = getiparam("LISTMAX");
zsfree(complastprompt);
- complastprompt = ztrdup(((isset(ALWAYSLASTPROMPT) && zmult == 1) ||
- (unset(ALWAYSLASTPROMPT) && zmult != 1)) ?
- "yes" : "");
+ complastprompt = ztrdup(isset(ALWAYSLASTPROMPT) ? "yes" : "");
dolastprompt = 1;
zsfree(complist);
complist = ztrdup(isset(LISTROWSFIRST) ?
@@ -975,7 +973,7 @@ makecomplist(char *s, int incmd, int lst)
mnum = 0;
unambig_mnum = -1;
isuf = NULL;
- insmnum = 1;
+ insmnum = zmult;
#if 0
/* group-numbers in compstate[insert] */
insgnum = 1;
@@ -2051,7 +2049,7 @@ addmatches(Cadata dat, char **argv)
Heap oldheap;
SWITCHHEAPS(oldheap, compheap) {
- if (dat->dummies)
+ if (dat->dummies >= 0)
dat->aflags = ((dat->aflags | CAF_NOSORT | CAF_UNIQCON) &
~CAF_UNIQALL);
@@ -2536,7 +2534,7 @@ addmatches(Cadata dat, char **argv)
addmatch("<all>", dat->flags | CMF_ALL, &disp, 1);
hasallmatch = 1;
}
- while (dat->dummies--)
+ while (dat->dummies-- > 0)
addmatch("", dat->flags | CMF_DUMMY, &disp, 0);
} SWITCHBACKHEAPS(oldheap);
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index 189582d22..bac533e7e 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -1803,7 +1803,7 @@ ccmakehookfn(UNUSED(Hookdef dummy), struct ccmakedat *dat)
mnum = 0;
unambig_mnum = -1;
isuf = NULL;
- insmnum = 1;
+ insmnum = zmult;
#if 0
/* group-numbers in compstate[insert] */
insgnum = 1;
diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c
index ea5e41f5f..ee4e5b0a5 100644
--- a/Src/Zle/complete.c
+++ b/Src/Zle/complete.c
@@ -267,8 +267,12 @@ parse_cmatcher(char *name, char *s)
s++;
if (!*s || !*++s) {
- if (name)
- zwarnnam(name, "missing line pattern");
+ if (name) {
+ if (both)
+ zwarnnam(name, "missing right anchor");
+ else
+ zwarnnam(name, "missing line pattern");
+ }
return pcm_err;
}
} else
@@ -288,6 +292,7 @@ parse_cmatcher(char *name, char *s)
if ((fl & CMF_RIGHT) && !fl2 && (!*s || !*++s)) {
if (name)
zwarnnam(name, "missing right anchor");
+ return pcm_err;
} else if (!(fl & CMF_RIGHT) || fl2) {
if (!*s) {
if (name)
@@ -313,8 +318,7 @@ parse_cmatcher(char *name, char *s)
return pcm_err;
}
s++;
- } else
- right = NULL;
+ }
if (*s == '*') {
if (!(fl & (CMF_LEFT | CMF_RIGHT))) {
@@ -537,7 +541,7 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
dat.match = NULL;
dat.flags = 0;
dat.aflags = CAF_MATCH;
- dat.dummies = 0;
+ dat.dummies = -1;
for (; *argv && **argv == '-'; argv++) {
if (!(*argv)[1]) {
@@ -1625,7 +1629,6 @@ boot_(Module m)
addhookfunc("before_complete", (Hookfn) before_complete);
addhookfunc("after_complete", (Hookfn) after_complete);
addhookfunc("accept_completion", (Hookfn) accept_last);
- addhookfunc("reverse_menu", (Hookfn) reverse_menu);
addhookfunc("list_matches", (Hookfn) list_matches);
addhookfunc("invalidate_list", (Hookfn) invalidate_list);
(void)addhookdefs(m, comphooks, sizeof(comphooks)/sizeof(*comphooks));
@@ -1640,7 +1643,6 @@ cleanup_(Module m)
deletehookfunc("before_complete", (Hookfn) before_complete);
deletehookfunc("after_complete", (Hookfn) after_complete);
deletehookfunc("accept_completion", (Hookfn) accept_last);
- deletehookfunc("reverse_menu", (Hookfn) reverse_menu);
deletehookfunc("list_matches", (Hookfn) list_matches);
deletehookfunc("invalidate_list", (Hookfn) invalidate_list);
(void)deletehookdefs(m, comphooks,
diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c
index f54206619..fd90ccb31 100644
--- a/Src/Zle/complist.c
+++ b/Src/Zle/complist.c
@@ -507,8 +507,8 @@ getcols()
max_caplen = lr_caplen = 0;
mcolors.flags = 0;
queue_signals();
- if (!(s = getsparam("ZLS_COLORS")) &&
- !(s = getsparam("ZLS_COLOURS"))) {
+ if (!(s = getsparam_u("ZLS_COLORS")) &&
+ !(s = getsparam_u("ZLS_COLOURS"))) {
for (i = 0; i < NUM_COLS; i++)
mcolors.files[i] = filecol("");
mcolors.pats = NULL;
@@ -728,7 +728,7 @@ clnicezputs(int do_colors, char *s, int ml)
if (do_colors)
initiscol();
- mb_metacharinit();
+ mb_charinit();
while (umleft > 0) {
size_t cnt = eol ? MB_INVALID : mbrtowc(&cc, uptr, umleft, &mbs);
@@ -2071,6 +2071,7 @@ complistmatches(UNUSED(Hookdef dummy), Chdata dat)
memset(mgtab, 0, i * sizeof(Cmgroup));
mlastcols = mcols = zterm_columns;
mlastlines = mlines = listdat.nlines;
+ mmtabp = 0;
}
last_cap = (char *) zhalloc(max_caplen + 1);
*last_cap = '\0';
@@ -2269,41 +2270,16 @@ msearchpop(int *backp)
}
static Cmatch **
-msearch(Cmatch **ptr, int ins, int back, int rep, int *wrapp)
+msearch(Cmatch **ptr, char *ins, int back, int rep, int *wrapp)
{
-#ifdef MULTIBYTE_SUPPORT
- /* MB_CUR_MAX may not be constant */
- VARARR(char, s, MB_CUR_MAX+1);
-#else
- char s[2];
-#endif
Cmatch **p, *l = NULL, m;
int x = mcol, y = mline;
int ex, ey, wrap = 0, owrap = (msearchstate & MS_WRAPPED);
msearchpush(ptr, back);
- if (ins) {
-#ifdef MULTIBYTE_SUPPORT
- if (lastchar_wide_valid)
- {
- mbstate_t mbs;
- int len;
-
- memset(&mbs, 0, sizeof(mbs));
- len = wcrtomb(s, lastchar_wide, &mbs);
- if (len < 0)
- len = 0;
- s[len] = '\0';
- } else
-#endif
- {
- s[0] = lastchar;
- s[1] = '\0';
- }
-
- msearchstr = dyncat(msearchstr, s);
- }
+ if (ins)
+ msearchstr = dyncat(msearchstr, ins);
if (back) {
ex = mcols - 1;
ey = -1;
@@ -2587,6 +2563,8 @@ domenuselect(Hookdef dummy, Chdata dat)
}
p = mmtabp;
pg = mgtabp;
+ if (!p) /* selected match not in display, find line */
+ continue;
minfo.cur = *p;
minfo.group = *pg;
if (setwish)
@@ -2603,6 +2581,7 @@ domenuselect(Hookdef dummy, Chdata dat)
getk:
if (!do_last_key) {
+ zmult = 1;
cmd = getkeycmd();
if (mtab_been_reallocated) {
do_last_key = 1;
@@ -2680,7 +2659,7 @@ domenuselect(Hookdef dummy, Chdata dat)
s->nbrbeg = nbrbeg;
s->nbrend = nbrend;
s->nmatches = nmatches;
- s->origline = origline;
+ s->origline = dupstring(origline);
s->origcs = origcs;
s->origll = origll;
s->status = dupstring(status);
@@ -2811,7 +2790,7 @@ domenuselect(Hookdef dummy, Chdata dat)
s->nbrbeg = nbrbeg;
s->nbrend = nbrend;
s->nmatches = nmatches;
- s->origline = origline;
+ s->origline = dupstring(origline);
s->origcs = origcs;
s->origll = origll;
s->status = dupstring(status);
@@ -3273,38 +3252,74 @@ domenuselect(Hookdef dummy, Chdata dat)
cmd == Th(z_historyincrementalsearchbackward) ||
((mode == MM_FSEARCH || mode == MM_BSEARCH) &&
(cmd == Th(z_selfinsert) ||
- cmd == Th(z_selfinsertunmeta)))) {
+ cmd == Th(z_selfinsertunmeta) ||
+ cmd == Th(z_bracketedpaste)))) {
Cmatch **np, **op = p;
int was = (mode == MM_FSEARCH || mode == MM_BSEARCH);
- int ins = (cmd == Th(z_selfinsert) || cmd == Th(z_selfinsertunmeta));
+ int ins = (cmd == Th(z_selfinsert) || cmd == Th(z_selfinsertunmeta) ||
+ cmd == Th(z_bracketedpaste));
int back = (cmd == Th(z_historyincrementalsearchbackward));
int wrap;
do {
+ char *toins = NULL;
+#ifdef MULTIBYTE_SUPPORT
+ /* MB_CUR_MAX may not be constant */
+ VARARR(char, insert, MB_CUR_MAX+1);
+#else
+ char insert[2];
+#endif
if (was) {
p += wishcol - mcol;
mcol = wishcol;
}
if (!ins) {
if (was) {
- if (!*msearchstr && lastsearch) {
+ if (!*msearchstr && lastsearch &&
+ back == (mode == MM_BSEARCH)) {
msearchstr = dupstring(lastsearch);
mode = 0;
}
} else {
msearchstr = "";
msearchstack = NULL;
+ msearchstate = MS_OK;
}
- }
- if (cmd == Th(z_selfinsertunmeta)) {
- fixunmeta();
- }
+ } else {
+ if (cmd == Th(z_selfinsertunmeta)) {
+ fixunmeta();
+ }
+ if (cmd == Th(z_bracketedpaste)) {
+ toins = bracketedstring();
+ } else {
+ toins = insert;
+#ifdef MULTIBYTE_SUPPORT
+ if (lastchar_wide_valid)
+ {
+ mbstate_t mbs;
+ int len;
+
+ memset(&mbs, 0, sizeof(mbs));
+ len = wcrtomb(s, lastchar_wide, &mbs);
+ if (len < 0)
+ len = 0;
+ insert[len] = '\0';
+ } else
+#endif
+ {
+ insert[0] = lastchar;
+ insert[1] = '\0';
+ }
+ }
+ }
wrap = 0;
- np = msearch(p, ins, (ins ? (mode == MM_BSEARCH) : back),
+ np = msearch(p, toins, (ins ? (mode == MM_BSEARCH) : back),
(was && !ins), &wrap);
if (!ins)
mode = (back ? MM_BSEARCH : MM_FSEARCH);
+ else if (cmd == Th(z_bracketedpaste))
+ free(toins);
if (*msearchstr) {
zsfree(lastsearch);
diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c
index 9f383f4b8..7fec7c804 100644
--- a/Src/Zle/compresult.c
+++ b/Src/Zle/compresult.c
@@ -1220,25 +1220,39 @@ do_menucmp(int lst)
was_meta = 1;
/* Otherwise go to the next match in the array... */
- do {
- if (!*++(minfo.cur)) {
- do {
- if (!(minfo.group = (minfo.group)->next)) {
- minfo.group = amatches;
+ while (zmult) {
+ do {
+ if (zmult > 0) {
+ if (!*++(minfo.cur)) {
+ do {
+ if (!(minfo.group = (minfo.group)->next)) {
+ minfo.group = amatches;
#ifdef ZSH_HEAP_DEBUG
- if (memory_validate(minfo.group->heap_id)) {
- HEAP_ERROR(minfo.group->heap_id);
- }
+ if (memory_validate(minfo.group->heap_id)) {
+ HEAP_ERROR(minfo.group->heap_id);
+ }
#endif
+ }
+ } while (!(minfo.group)->mcount);
+ minfo.cur = minfo.group->matches;
}
- } while (!(minfo.group)->mcount);
- minfo.cur = minfo.group->matches;
- }
- } while ((menuacc &&
- !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr)) ||
- ((*minfo.cur)->flags & CMF_DUMMY) ||
- (((*minfo.cur)->flags & (CMF_NOLIST | CMF_MULT)) &&
- (!(*minfo.cur)->str || !*(*minfo.cur)->str)));
+ } else {
+ if (minfo.cur == (minfo.group)->matches) {
+ do {
+ if (!(minfo.group = (minfo.group)->prev))
+ minfo.group = lmatches;
+ } while (!(minfo.group)->mcount);
+ minfo.cur = (minfo.group)->matches + (minfo.group)->mcount - 1;
+ } else
+ minfo.cur--;
+ }
+ } while ((menuacc &&
+ !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr)) ||
+ ((*minfo.cur)->flags & CMF_DUMMY) ||
+ (((*minfo.cur)->flags & (CMF_NOLIST | CMF_MULT)) &&
+ (!(*minfo.cur)->str || !*(*minfo.cur)->str)));
+ zmult -= (0 < zmult) - (zmult < 0);
+ }
/* ... and insert it into the command line. */
do_single(*minfo.cur);
@@ -1246,43 +1260,6 @@ do_menucmp(int lst)
unmetafy_line();
}
-/**/
-int
-reverse_menu(UNUSED(Hookdef dummy), UNUSED(void *dummy2))
-{
- int was_meta;
-
- if (minfo.cur == NULL)
- return 1;
-
- do {
- if (minfo.cur == (minfo.group)->matches) {
- do {
- if (!(minfo.group = (minfo.group)->prev))
- minfo.group = lmatches;
- } while (!(minfo.group)->mcount);
- minfo.cur = (minfo.group)->matches + (minfo.group)->mcount - 1;
- } else
- minfo.cur--;
- } while ((menuacc &&
- !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr)) ||
- ((*minfo.cur)->flags & CMF_DUMMY) ||
- (((*minfo.cur)->flags & (CMF_NOLIST | CMF_MULT)) &&
- (!(*minfo.cur)->str || !*(*minfo.cur)->str)));
- /* May already be metafied if called from within a selection */
- if (zlemetaline == NULL) {
- metafy_line();
- was_meta = 0;
- }
- else
- was_meta = 1;
- do_single(*(minfo.cur));
- if (!was_meta)
- unmetafy_line();
-
- return 0;
-}
-
/* Accepts the current completion and starts a new arg, *
* with the next completions. This gives you a way to *
* accept several selections from the list of matches. */
diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c
index 27938c17f..e5db0867b 100644
--- a/Src/Zle/computil.c
+++ b/Src/Zle/computil.c
@@ -4196,7 +4196,7 @@ cfp_matcher_range(Cmatcher *ms, char *add)
addlen = MB_METACHARLENCONV(add, &addc);
#ifdef MULTIBYTE_SUPPORT
if (addc == WEOF)
- addc = (wchar_t)(*p == Meta ? p[1] ^ 32 : *p);
+ addc = (wchar_t)(*add == Meta ? add[1] ^ 32 : *add);
#endif
if (!(m = *mp)) {
diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list
index b41661a7d..2b2654c5d 100644
--- a/Src/Zle/iwidgets.list
+++ b/Src/Zle/iwidgets.list
@@ -28,12 +28,14 @@
"beginning-of-history", beginningofhistory, 0
"beginning-of-line", beginningofline, 0
"beginning-of-line-hist", beginningoflinehist, 0
+"bracketed-paste", bracketedpaste, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_YANKBEFORE
"capitalize-word", capitalizeword, 0
"clear-screen", clearscreen, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND
"complete-word", completeword, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
"copy-prev-word", copyprevword, ZLE_KEEPSUFFIX
"copy-prev-shell-word", copyprevshellword, ZLE_KEEPSUFFIX
"copy-region-as-kill", copyregionaskill, ZLE_KEEPSUFFIX
+"deactivate-region", deactivateregion, 0
"delete-char", deletechar, ZLE_KEEPSUFFIX
"delete-char-or-list", deletecharorlist, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
"delete-word", deleteword, ZLE_KEEPSUFFIX
@@ -93,7 +95,7 @@
"quoted-insert", quotedinsert, ZLE_MENUCMP | ZLE_KEEPSUFFIX
"quote-line", quoteline, 0
"quote-region", quoteregion, 0
-"read-command", readcommand, 0
+"read-command", readcommand, ZLE_NOTCOMMAND
"recursive-edit", recursiveedit, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
"redisplay", redisplay, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
"redo", redo, ZLE_KEEPSUFFIX
diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h
index 3c652909e..59f459185 100644
--- a/Src/Zle/zle.h
+++ b/Src/Zle/zle.h
@@ -352,8 +352,7 @@ struct brinfo {
#define BEFORECOMPLETEHOOK (zlehooks + 2)
#define AFTERCOMPLETEHOOK (zlehooks + 3)
#define ACCEPTCOMPHOOK (zlehooks + 4)
-#define REVERSEMENUHOOK (zlehooks + 5)
-#define INVALIDATELISTHOOK (zlehooks + 6)
+#define INVALIDATELISTHOOK (zlehooks + 5)
/* complete hook data struct */
@@ -430,8 +429,9 @@ struct region_highlight {
* 0: region between point and mark
* 1: isearch region
* 2: suffix
+ * 3: pasted text
*/
-#define N_SPECIAL_HIGHLIGHTS (3)
+#define N_SPECIAL_HIGHLIGHTS (4)
#ifdef MULTIBYTE_SUPPORT
diff --git a/Src/Zle/zle_bindings.c b/Src/Zle/zle_bindings.c
index 2ae8c8764..55863db1b 100644
--- a/Src/Zle/zle_bindings.c
+++ b/Src/Zle/zle_bindings.c
@@ -317,7 +317,7 @@ int vicmdbind[128] = {
/* ^X */ z_undefinedkey,
/* ^Y */ z_undefinedkey,
/* ^Z */ z_undefinedkey,
- /* ^[ */ z_undefinedkey,
+ /* ^[ */ z_beep,
/* ^\ */ z_undefinedkey,
/* ^] */ z_undefinedkey,
/* ^^ */ z_undefinedkey,
diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c
index cc66f99ae..c61b4ef0e 100644
--- a/Src/Zle/zle_hist.c
+++ b/Src/Zle/zle_hist.c
@@ -894,10 +894,8 @@ zgetline(UNUSED(char **args))
free(s);
free(lineadd);
clearlist = 1;
- if (stackhist != -1) {
- histline = stackhist;
- stackhist = -1;
- }
+ /* not restoring stackhist as we're inserting into current line */
+ stackhist = -1;
}
return 0;
}
@@ -1598,7 +1596,7 @@ doisearch(char **args, int dir, int pattern)
dir = odir;
skip_pos = 1;
rpt:
- if (!sbptr && previous_search_len) {
+ if (!sbptr && previous_search_len && dir == odir) {
if (previous_search_len > sibuf - FIRST_SEARCH_CHAR - 2) {
ibuf = hrealloc((char *)ibuf, sibuf,
(sibuf + previous_search_len));
@@ -1620,6 +1618,21 @@ doisearch(char **args, int dir, int pattern)
feep = 1;
else
goto ins;
+ } else if (cmd == Th(z_bracketedpaste)) {
+ char *paste = bracketedstring();
+ set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos, end_pos,
+ zlemetacs, sbptr, dir, nomatch);
+ size_t pastelen = strlen(paste);
+ if (sbptr + pastelen >= sibuf - FIRST_SEARCH_CHAR - 2) {
+ int oldsize = sibuf;
+ sibuf += (pastelen >= sibuf) ? pastelen + 1 : sibuf;
+ ibuf = hrealloc(ibuf, oldsize, sibuf);
+ sbuf = ibuf + FIRST_SEARCH_CHAR;
+ }
+ strcpy(sbuf + sbptr, paste);
+ sbptr += pastelen;
+ patprog = NULL;
+ free(paste);
} else if (cmd == Th(z_acceptsearch)) {
break;
} else {
diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
index c6fae251d..5b4189faa 100644
--- a/Src/Zle/zle_keymap.c
+++ b/Src/Zle/zle_keymap.c
@@ -540,7 +540,7 @@ reselectkeymap(void)
/**/
mod_export int
-bindkey(Keymap km, char *seq, Thingy bind, char *str)
+bindkey(Keymap km, const char *seq, Thingy bind, char *str)
{
Key k;
int f = seq[0] == Meta ? STOUC(seq[1])^32 : STOUC(seq[0]);
@@ -1363,6 +1363,7 @@ default_bindings(void)
}
/* escape in operator pending cancels the operation */
bindkey(oppmap, "\33", refthingy(t_vicmdmode), NULL);
+ bindkey(vismap, "\33", refthingy(t_deactivateregion), NULL);
bindkey(vismap, "o", refthingy(t_exchangepointandmark), NULL);
bindkey(vismap, "p", refthingy(t_putreplaceselection), NULL);
bindkey(vismap, "x", refthingy(t_videlete), NULL);
@@ -1400,6 +1401,11 @@ default_bindings(void)
bindkey(emap, "\30\30", refthingy(t_exchangepointandmark), NULL);
bindkey(emap, "\30=", refthingy(t_whatcursorposition), NULL);
+ /* bracketed paste applicable to all keymaps */
+ bindkey(emap, "\33[200~", refthingy(t_bracketedpaste), NULL);
+ bindkey(vmap, "\33[200~", refthingy(t_bracketedpaste), NULL);
+ bindkey(amap, "\33[200~", refthingy(t_bracketedpaste), NULL);
+
/* emacs mode: ESC sequences, all taken from the meta binding table */
buf[0] = '\33';
buf[2] = 0;
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index cec44c0ed..e610ae1f3 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -1119,7 +1119,7 @@ zlecore(void)
char *
zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
{
- char *s;
+ char *s, **bracket;
int old_errno = errno;
int tmout = getiparam("TMOUT");
@@ -1206,6 +1206,7 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
histline = stackhist;
stackhist = -1;
}
+ handleundo();
}
/*
* If main is linked to the viins keymap, we need to register
@@ -1248,6 +1249,9 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
zlecallhook(init, NULL);
+ if ((bracket = getaparam("zle_bracketed_paste")) && arrlen(bracket) == 2)
+ fputs(*bracket, shout);
+
zrefresh();
zlecore();
@@ -1257,6 +1261,9 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
"ZLE_VARED_ABORTED" :
"ZLE_LINE_ABORTED", zlegetline(NULL, NULL));
+ if ((bracket = getaparam("zle_bracketed_paste")) && arrlen(bracket) == 2)
+ fputs(bracket[1], shout);
+
if (done && !exit_pending && !errflag)
zlecallhook(finish, NULL);
@@ -1858,7 +1865,7 @@ trashzle(void)
clearflag = listshown = 0;
}
if (postedit)
- fprintf(shout, "%s", postedit);
+ fprintf(shout, "%s", unmeta(postedit));
fflush(shout);
resetneeded = 1;
if (!(zlereadflags & ZLRF_NOSETTY))
@@ -1986,8 +1993,6 @@ mod_export struct hookdef zlehooks[] = {
HOOKDEF("after_complete", NULL, 0),
/* ACCEPTCOMPHOOK */
HOOKDEF("accept_completion", NULL, 0),
- /* REVERSEMENUHOOK */
- HOOKDEF("reverse_menu", NULL, 0),
/* INVALIDATELISTHOOK */
HOOKDEF("invalidate_list", NULL, 0),
};
@@ -2004,6 +2009,8 @@ static struct features module_features = {
int
setup_(UNUSED(Module m))
{
+ char **bpaste;
+
/* Set up editor entry points */
zle_entry_ptr = zle_main_entry;
zle_load_state = 1;
@@ -2028,6 +2035,11 @@ setup_(UNUSED(Module m))
clwords = (char **) zshcalloc((clwsize = 16) * sizeof(char *));
+ bpaste = zshcalloc(3*sizeof(char *));
+ bpaste[0] = ztrdup("\033[?2004h");
+ bpaste[1] = ztrdup("\033[?2004l");
+ setaparam("zle_bracketed_paste", bpaste);
+
return 0;
}
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index 4669ef2ad..2d1862813 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -517,10 +517,12 @@ copyregionaskill(char **args)
/*
* kct: index into kill ring, or -1 for original cutbuffer of yank.
- * yankb, yanke: mark the start and end of last yank in editing buffer.
* yankcs marks the cursor position preceding the last yank
*/
-static int kct, yankb, yanke, yankcs;
+static int kct, yankcs;
+
+/**/
+int yankb, yanke; /* mark the start and end of last yank in editing buffer. */
/* The original cutbuffer, either cutbuf or one of the vi buffers. */
static Cutbuffer kctbuf;
@@ -736,6 +738,71 @@ yankpop(UNUSED(char **args))
}
/**/
+mod_export char *
+bracketedstring(void)
+{
+ static const char endesc[] = "\033[201~";
+ int endpos = 0;
+ size_t psize = 64;
+ char *pbuf = zalloc(psize);
+ size_t current = 0;
+ int next, timeout;
+
+ while (endesc[endpos]) {
+ if (current + 1 >= psize)
+ pbuf = zrealloc(pbuf, psize *= 2);
+ if ((next = getbyte(1L, &timeout)) == EOF)
+ break;
+ if (!endpos || next != endesc[endpos++])
+ endpos = (next == *endesc);
+ if (imeta(next)) {
+ pbuf[current++] = Meta;
+ pbuf[current++] = next ^ 32;
+ } else if (next == '\r')
+ pbuf[current++] = '\n';
+ else
+ pbuf[current++] = next;
+ }
+ pbuf[current-endpos] = '\0';
+ return pbuf;
+}
+
+/**/
+int
+bracketedpaste(char **args)
+{
+ char *pbuf = bracketedstring();
+
+ if (*args) {
+ setsparam(*args, pbuf);
+ } else {
+ int n;
+ ZLE_STRING_T wpaste;
+ wpaste = stringaszleline((zmult == 1) ? pbuf :
+ quotestring(pbuf, NULL, QT_SINGLE_OPTIONAL), 0, &n, NULL, NULL);
+ cuttext(wpaste, n, CUT_REPLACE);
+ if (!(zmod.flags & MOD_VIBUF)) {
+ kct = -1;
+ kctbuf = &cutbuf;
+ zmult = 1;
+ if (region_active)
+ killregion(zlenoargs);
+ /* Chop a final newline if its insertion would be hard to
+ * distinguish by the user from the line being accepted. */
+ else if (n > 1 && zlecontext != ZLCON_VARED &&
+ (zlecs + (insmode ? 0 : n - 1)) >= zlell &&
+ wpaste[n-1] == ZWC('\n'))
+ n--;
+ yankcs = yankb = zlecs;
+ doinsert(wpaste, n);
+ yanke = zlecs;
+ }
+ free(pbuf); free(wpaste);
+ }
+ return 0;
+}
+
+/**/
int
overwritemode(UNUSED(char **args))
{
@@ -1264,6 +1331,22 @@ executenamedcommand(char *prmt)
if (listed)
clearlist = listshown = 1;
curlist = 0;
+ } else if (cmd == Th(z_bracketedpaste)) {
+ char *insert = bracketedstring();
+ size_t inslen = strlen(insert);
+ if (len + inslen > NAMLEN)
+ feep = 1;
+ else {
+ strcpy(ptr, insert);
+ len += inslen;
+ ptr += inslen;
+ if (listed) {
+ clearlist = listshown = 1;
+ listed = 0;
+ } else
+ curlist = 0;
+ }
+ free(insert);
} else {
if(cmd == Th(z_acceptline) || cmd == Th(z_vicmdmode)) {
Thingy r;
@@ -1484,13 +1567,13 @@ makesuffix(int n)
{
char *suffixchars;
- if (!(suffixchars = getsparam("ZLE_REMOVE_SUFFIX_CHARS")))
+ if (!(suffixchars = getsparam_u("ZLE_REMOVE_SUFFIX_CHARS")))
suffixchars = " \t\n;&|";
addsuffixstring(SUFTYP_POSSTR, 0, suffixchars, n);
/* Do this second so it takes precedence */
- if ((suffixchars = getsparam("ZLE_SPACE_SUFFIX_CHARS")) && *suffixchars)
+ if ((suffixchars = getsparam_u("ZLE_SPACE_SUFFIX_CHARS")) && *suffixchars)
addsuffixstring(SUFTYP_POSSTR, SUFFLAGS_SPACE, suffixchars, n);
suffixlen = n;
diff --git a/Src/Zle/zle_move.c b/Src/Zle/zle_move.c
index d751c4333..f49df8647 100644
--- a/Src/Zle/zle_move.c
+++ b/Src/Zle/zle_move.c
@@ -555,6 +555,13 @@ visuallinemode(UNUSED(char **args))
return 0;
}
+/**/
+int
+deactivateregion(UNUSED(char **args))
+{
+ region_active = 0;
+ return 0;
+}
/**/
int
@@ -876,24 +883,36 @@ int
vigotomark(UNUSED(char **args))
{
ZLE_INT_T ch;
+ int *markcs, *markhist = 0;
int oldcs = zlecs;
int oldline = histline;
+ int tmpcs, tmphist;
ch = getfullchar(0);
- if (ch == ZWC('\'') || ch == ZWC('`'))
- ch = 26;
- else {
- if (ch < ZWC('a') || ch > ZWC('z'))
- return 1;
- ch -= ZWC('a');
- }
- if (!vimarkline[ch])
- return 1;
- if (curhist != vimarkline[ch] && !zle_goto_hist(vimarkline[ch], 0, 0)) {
- vimarkline[ch] = 0;
+ if (ch == ZWC('\'') || ch == ZWC('`')) {
+ markhist = vimarkline + 26;
+ markcs = vimarkcs + 26;
+ } else if (ch == ZWC('.') && curchange->prev) {
+ /* position cursor where it was after the last change. not exactly
+ * what vim does but close enough */
+ tmpcs = curchange->prev->new_cs;
+ tmphist = curchange->prev->hist;
+ markcs = &tmpcs;
+ markhist = &tmphist;
+ } else if (ch >= ZWC('a') && ch <= ZWC('z')) {
+ markhist = vimarkline + (ch - ZWC('a'));
+ markcs = vimarkcs + (ch - ZWC('a'));
+ } else
return 1;
+ if (markhist) {
+ if (!*markhist)
+ return 1;
+ if (histline != *markhist && !zle_goto_hist(*markhist, 0, 0)) {
+ *markhist = 0;
+ return 1;
+ }
}
- zlecs = vimarkcs[ch];
+ zlecs = *markcs;
vimarkcs[26] = oldcs;
vimarkline[26] = oldline;
if (zlecs > zlell)
diff --git a/Src/Zle/zle_params.c b/Src/Zle/zle_params.c
index ce4b0724d..b84e72088 100644
--- a/Src/Zle/zle_params.c
+++ b/Src/Zle/zle_params.c
@@ -95,6 +95,8 @@ static const struct gsu_integer region_active_gsu =
{ get_region_active, set_region_active, zleunsetfn };
static const struct gsu_integer undo_change_no_gsu =
{ get_undo_current_change, NULL, zleunsetfn };
+static const struct gsu_integer undo_limit_no_gsu =
+{ get_undo_limit_change, set_undo_limit_change, zleunsetfn };
static const struct gsu_array killring_gsu =
{ get_killring, set_killring, unset_killring };
@@ -137,6 +139,7 @@ static struct zleparam {
{ "region_highlight", PM_ARRAY, GSU(region_highlight_gsu), NULL },
{ "UNDO_CHANGE_NO", PM_INTEGER | PM_READONLY, GSU(undo_change_no_gsu),
NULL },
+ { "UNDO_LIMIT_NO", PM_INTEGER, GSU(undo_limit_no_gsu), NULL },
{ "WIDGET", PM_SCALAR | PM_READONLY, GSU(widget_gsu), NULL },
{ "WIDGETFUNC", PM_SCALAR | PM_READONLY, GSU(widgetfunc_gsu), NULL },
{ "WIDGETSTYLE", PM_SCALAR | PM_READONLY, GSU(widgetstyle_gsu), NULL },
diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c
index fe337993f..78046fb7b 100644
--- a/Src/Zle/zle_refresh.c
+++ b/Src/Zle/zle_refresh.c
@@ -318,6 +318,7 @@ zle_set_highlight(void)
int region_atr_on_set = 0;
int isearch_atr_on_set = 0;
int suffix_atr_on_set = 0;
+ int paste_atr_on_set = 0;
struct region_highlight *rhp;
special_atr_on = default_atr_on = 0;
@@ -337,7 +338,8 @@ zle_set_highlight(void)
for (; *atrs; atrs++) {
if (!strcmp(*atrs, "none")) {
/* reset attributes for consistency... usually unnecessary */
- special_atr_on = default_atr_on = 0;
+ special_atr_on = default_atr_on =
+ paste_atr_on_set = 0;
special_atr_on_set = region_atr_on_set =
isearch_atr_on_set = suffix_atr_on_set = 1;
} else if (strpfx("default:", *atrs)) {
@@ -354,6 +356,9 @@ zle_set_highlight(void)
} else if (strpfx("suffix:", *atrs)) {
match_highlight(*atrs + 7, &(region_highlights[2].atr));
suffix_atr_on_set = 1;
+ } else if (strpfx("paste:", *atrs)) {
+ match_highlight(*atrs + 6, &(region_highlights[3].atr));
+ paste_atr_on_set = 1;
}
}
}
@@ -367,6 +372,7 @@ zle_set_highlight(void)
region_highlights[1].atr = TXTUNDERLINE;
if (!suffix_atr_on_set)
region_highlights[2].atr = TXTBOLDFACE;
+ /* paste defaults to 0 */
allocate_colour_buffer();
}
@@ -1073,6 +1079,13 @@ zrefresh(void)
region_highlights[2].start = region_highlights[2].end = -1;
}
+ if (lastcmd & ZLE_YANK) {
+ region_highlights[3].start = yankb;
+ region_highlights[3].end = yanke;
+ } else {
+ region_highlights[3].start = region_highlights[3].end = -1;
+ }
+
if (clearlist && listshown > 0) {
if (tccan(TCCLEAREOD)) {
int ovln = vln, ovcs = vcs;
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index f18ad170e..b87b99b00 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -345,17 +345,8 @@ mod_export int
reversemenucomplete(char **args)
{
wouldinstab = 0;
- if (!menucmp) {
- menucomplete(args);
- /*
- * Drop through, since we are now on the first item instead of
- * the last. We've already updated the display, so this is a
- * bit inefficient, but it's simple and it works.
- */
- }
-
- runhookdef(REVERSEMENUHOOK, NULL);
- return 0;
+ zmult = -zmult;
+ return menucomplete(args);
}
/**/
@@ -730,11 +721,12 @@ docomplete(int lst)
}
}
}
- if (lst == COMP_EXPAND_COMPLETE)
+ if (lst == COMP_EXPAND_COMPLETE) {
do {
/* Check if there is a parameter expression. */
for (; *q && *q != String; q++);
- if (*q == String && q[1] != Inpar && q[1] != Inbrack) {
+ if (*q == String && q[1] != Inpar && q[1] != Inparmath &&
+ q[1] != Inbrack) {
if (*++q == Inbrace) {
if (! skipparens(Inbrace, Outbrace, &q) &&
q == s + zlemetacs - wb)
@@ -778,6 +770,7 @@ docomplete(int lst)
} else
break;
} while (q < s + zlemetacs - wb);
+ }
if (lst == COMP_EXPAND_COMPLETE) {
/* If it is still not clear if we should use expansion or *
* completion and there is a `$' or a backtick in the word, *
@@ -1182,14 +1175,30 @@ get_comp_string(void)
do {
qsub = noword = 0;
- lincmd = ((incmdpos && !ins && !incond) ||
- (oins == 2 && wordpos == 2) ||
- (ins == 3 && wordpos == 1));
+ /*
+ * pws: added cmdtok == NULLTOK test as fallback for detecting
+ * we haven't had a command yet. This is a cop out: it's needed
+ * after SEPER because of bizarre and incomprehensible dance
+ * that we otherwise do involving the "ins" flag when you might
+ * have thought we'd just reset everything because we're now
+ * considering a new command. Consequently, although this looks
+ * relatively harmless by itself, it's probably incomplete.
+ */
+ lincmd = (incmdpos && !ins && !incond) ||
+ (oins == 2 && wordpos == 2) ||
+ (ins == 3 && wordpos == 1) ||
+ (cmdtok == NULLTOK && !incond);
linredir = (inredir && !ins);
oins = ins;
/* Get the next token. */
if (linarr)
incmdpos = 0;
+ /*
+ * Arrange to parse assignments after typeset etc...
+ * but not if we're already in an array.
+ */
+ if (cmdtok == TYPESET)
+ intypeset = !linarr;
ctxtlex();
if (tok == LEXERR) {
@@ -1272,10 +1281,11 @@ get_comp_string(void)
tt0 = NULLTOK;
}
if (lincmd && (tok == STRING || tok == FOR || tok == FOREACH ||
- tok == SELECT || tok == REPEAT || tok == CASE)) {
+ tok == SELECT || tok == REPEAT || tok == CASE ||
+ tok == TYPESET)) {
/* The lexer says, this token is in command position, so *
* store the token string (to find the right compctl). */
- ins = (tok == REPEAT ? 2 : (tok != STRING));
+ ins = (tok == REPEAT ? 2 : (tok != STRING && tok != TYPESET));
zsfree(cmdstr);
cmdstr = ztrdup(tokstr);
cmdtok = tok;
@@ -1290,7 +1300,7 @@ get_comp_string(void)
* handle completing multiple SEPER-ated command positions on
* the same command line, e.g., pipelines.
*/
- ins = (cmdtok != STRING);
+ ins = (cmdtok != STRING && cmdtok != TYPESET);
}
if (!lexflags && tt0 == NULLTOK) {
/* This is done when the lexer reached the word the cursor is on. */
@@ -1436,7 +1446,7 @@ get_comp_string(void)
we = wb = zlemetacs;
clwpos = clwnum;
t0 = STRING;
- } else if (t0 == STRING) {
+ } else if (t0 == STRING || t0 == TYPESET) {
/* We found a simple string. */
s = ztrdup(clwords[clwpos]);
} else if (t0 == ENVSTRING) {
@@ -1492,7 +1502,7 @@ get_comp_string(void)
zlemetaline = tmp;
zlemetall = strlen(zlemetaline);
}
- if (t0 != STRING && inwhat != IN_MATH) {
+ if (t0 != STRING && t0 != TYPESET && inwhat != IN_MATH) {
if (tmp) {
tmp = NULL;
linptr = zlemetaline;
diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c
index e4ab97a54..d1d320613 100644
--- a/Src/Zle/zle_utils.c
+++ b/Src/Zle/zle_utils.c
@@ -1288,7 +1288,7 @@ showmsg(char const *msg)
p = unmetafy(umsg, &ulen);
memset(&mbs, 0, sizeof mbs);
- mb_metacharinit();
+ mb_charinit();
while (ulen > 0) {
char const *n;
if (*p == '\n') {
@@ -1405,9 +1405,9 @@ static struct change *nextchanges, *endnextchanges;
/**/
zlong undo_changeno;
-/* If non-zero, the last increment to undo_changeno was for the variable */
+/* If positive, don't undo beyond this point */
-static int undo_set_by_variable;
+zlong undo_limitno;
/**/
void
@@ -1418,8 +1418,7 @@ initundo(void)
curchange->prev = curchange->next = NULL;
curchange->del = curchange->ins = NULL;
curchange->dell = curchange->insl = 0;
- curchange->changeno = undo_changeno = 0;
- undo_set_by_variable = 0;
+ curchange->changeno = undo_changeno = undo_limitno = 0;
lastline = zalloc((lastlinesz = linesz) * ZLE_CHAR_SIZE);
ZS_memcpy(lastline, zleline, (lastll = zlell));
lastcs = zlecs;
@@ -1545,7 +1544,6 @@ mkundoent(void)
ch->prev = NULL;
}
ch->changeno = ++undo_changeno;
- undo_set_by_variable = 0;
endnextchanges = ch;
}
@@ -1580,12 +1578,13 @@ undo(char **args)
struct change *prev = curchange->prev;
if(!prev)
return 1;
- if (prev->changeno < last_change)
- break;
- if (unapplychange(prev))
- curchange = prev;
- else
+ if (prev->changeno <= last_change)
break;
+ if (prev->changeno <= undo_limitno && !*args)
+ return 1;
+ if (!unapplychange(prev) && last_change >= 0)
+ unapplychange(prev);
+ curchange = prev;
} while (last_change >= (zlong)0 || (curchange->flags & CH_PREV));
setlastline();
return 0;
@@ -1735,16 +1734,22 @@ zlecallhook(char *name, char *arg)
zlong
get_undo_current_change(UNUSED(Param pm))
{
- if (undo_set_by_variable) {
- /* We were the last to increment this, doesn't need another one. */
- return undo_changeno;
- }
- undo_set_by_variable = 1;
- /*
- * Increment the number in case a change is in progress;
- * we don't want to back off what's already been done when
- * we return to this change number. This eliminates any
- * problem about the point where a change is numbered.
- */
- return ++undo_changeno;
+ /* add entry for any pending changes */
+ mkundoent();
+ setlastline();
+ return undo_changeno;
+}
+
+/**/
+zlong
+get_undo_limit_change(UNUSED(Param pm))
+{
+ return undo_limitno;
+}
+
+/**/
+void
+set_undo_limit_change(UNUSED(Param pm), zlong value)
+{
+ undo_limitno = value;
}
diff --git a/Src/builtin.c b/Src/builtin.c
index 9358e8b1f..3d34aa74c 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -53,7 +53,7 @@ static struct builtin builtins[] =
BUILTIN("cd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL),
BUILTIN("chdir", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL),
BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL),
- BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmprtuxz", NULL),
+ BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmprtuxz", NULL),
BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "clpv", NULL),
BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmprs", NULL),
BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL),
@@ -62,7 +62,7 @@ static struct builtin builtins[] =
BUILTIN("enable", 0, bin_enable, 0, -1, BIN_ENABLE, "afmprs", NULL),
BUILTIN("eval", BINF_PSPECIAL, bin_eval, 0, -1, BIN_EVAL, NULL, NULL),
BUILTIN("exit", BINF_PSPECIAL, bin_break, 0, 1, BIN_EXIT, NULL, NULL),
- BUILTIN("export", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, 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, BIN_EXPORT, "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.
@@ -71,8 +71,8 @@ static struct builtin builtins[] =
*/
BUILTIN("fc", 0, bin_fc, 0, -1, BIN_FC, "aAdDe:EfiIlLmnpPrRt:W", NULL),
BUILTIN("fg", 0, bin_fg, 0, -1, BIN_FG, NULL, NULL),
- BUILTIN("float", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%Z:%ghlprtux", "E"),
- BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "kmMtTuUz", NULL),
+ BUILTIN("float", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%Z:%ghlprtux", "E"),
+ BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "kmMtTuUx:z", NULL),
BUILTIN("getln", 0, bin_read, 0, -1, 0, "ecnAlE", "zr"),
BUILTIN("getopts", 0, bin_getopts, 2, -1, 0, NULL, NULL),
BUILTIN("hash", BINF_MAGICEQUALS, bin_hash, 0, -1, 0, "Ldfmrv", NULL),
@@ -82,11 +82,11 @@ static struct builtin builtins[] =
#endif
BUILTIN("history", 0, bin_fc, 0, -1, BIN_FC, "adDEfiLmnpPrt:", "l"),
- BUILTIN("integer", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "HL:%R:%Z:%ghi:%lprtux", "i"),
+ BUILTIN("integer", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "HL:%R:%Z:%ghi:%lprtux", "i"),
BUILTIN("jobs", 0, bin_fg, 0, -1, BIN_JOBS, "dlpZrs", NULL),
BUILTIN("kill", BINF_HANDLES_OPTS, bin_kill, 0, -1, 0, NULL, NULL),
BUILTIN("let", 0, bin_let, 1, -1, 0, NULL, NULL),
- BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lprtux", NULL),
+ BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lprtux", NULL),
BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL),
BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL),
@@ -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:z-", 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("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, "nrlL", 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, 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, 0, "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),
@@ -120,7 +120,7 @@ static struct builtin builtins[] =
BUILTIN("trap", BINF_PSPECIAL | BINF_HANDLES_OPTS, bin_trap, 0, -1, 0, NULL, NULL),
BUILTIN("true", 0, bin_true, 0, -1, 0, NULL, NULL),
BUILTIN("type", 0, bin_whence, 0, -1, 0, "ampfsSw", "v"),
- BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klprtuxmz", NULL),
+ BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klprtuxmz", NULL),
BUILTIN("umask", 0, bin_umask, 0, 1, 0, "S", NULL),
BUILTIN("unalias", 0, bin_unhash, 0, -1, BIN_UNALIAS, "ams", NULL),
BUILTIN("unfunction", 0, bin_unhash, 1, -1, BIN_UNFUNCTION, "m", "f"),
@@ -128,9 +128,9 @@ static struct builtin builtins[] =
BUILTIN("unset", BINF_PSPECIAL, bin_unset, 1, -1, 0, "fmv", NULL),
BUILTIN("unsetopt", 0, bin_setopt, 0, -1, BIN_UNSETOPT, NULL, NULL),
BUILTIN("wait", 0, bin_fg, 0, -1, BIN_WAIT, NULL, NULL),
- BUILTIN("whence", 0, bin_whence, 0, -1, 0, "acmpvfsSw", NULL),
- BUILTIN("where", 0, bin_whence, 0, -1, 0, "pmsSw", "ca"),
- BUILTIN("which", 0, bin_whence, 0, -1, 0, "ampsSw", "c"),
+ BUILTIN("whence", 0, bin_whence, 0, -1, 0, "acmpvfsSwx:", NULL),
+ BUILTIN("where", 0, bin_whence, 0, -1, 0, "pmsSwx:", "ca"),
+ BUILTIN("which", 0, bin_whence, 0, -1, 0, "ampsSwx:", "c"),
BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "AFRILP:abcfdilmpue", NULL),
BUILTIN("zcompile", 0, bin_zcompile, 0, -1, 0, "tUMRcmzka", NULL),
};
@@ -246,7 +246,7 @@ new_optarg(Options ops)
/**/
int
-execbuiltin(LinkList args, Builtin bn)
+execbuiltin(LinkList args, LinkList assigns, Builtin bn)
{
char *pp, *name, *optstr;
int flags, sense, argc, execop, xtr = isset(XTRACE);
@@ -443,11 +443,46 @@ execbuiltin(LinkList args, Builtin bn)
fputc(' ', xtrerr);
quotedzputs(*fullargv++, xtrerr);
}
+ if (assigns) {
+ LinkNode node;
+ for (node = firstnode(assigns); node; incnode(node)) {
+ Asgment asg = (Asgment)node;
+ fputc(' ', xtrerr);
+ quotedzputs(asg->name, xtrerr);
+ if (asg->is_array) {
+ LinkNode arrnode;
+ fprintf(xtrerr, "=(");
+ if (asg->value.array) {
+ for (arrnode = firstnode(asg->value.array);
+ arrnode;
+ incnode(arrnode)) {
+ fputc(' ', xtrerr);
+ quotedzputs((char *)getdata(arrnode), xtrerr);
+ }
+ }
+ fprintf(xtrerr, " )");
+ } else if (asg->value.scalar) {
+ fputc('=', xtrerr);
+ quotedzputs(asg->value.scalar, xtrerr);
+ }
+ }
+ }
fputc('\n', xtrerr);
fflush(xtrerr);
}
/* call the handler function, and return its return value */
- return (*(bn->handlerfunc)) (name, argv, &ops, bn->funcid);
+ if (flags & BINF_ASSIGN)
+ {
+ /*
+ * Takes two sets of arguments.
+ */
+ HandlerFuncAssign assignfunc = (HandlerFuncAssign)bn->handlerfunc;
+ return (*(assignfunc)) (name, argv, assigns, &ops, bn->funcid);
+ }
+ else
+ {
+ return (*(bn->handlerfunc)) (name, argv, &ops, bn->funcid);
+ }
}
}
@@ -1128,7 +1163,7 @@ cd_try_chdir(char *pfix, char *dest, int hard)
* or a parent directory is renamed in the interim.
*/
if (lchdir(buf, NULL, hard) &&
- (pfix || *dest == '/' || lchdir(dest, NULL, hard))) {
+ (pfix || *dest == '/' || lchdir(unmeta(dest), NULL, hard))) {
free(buf);
return NULL;
}
@@ -1435,10 +1470,6 @@ bin_fc(char *nam, char **argv, Options ops, int func)
unqueue_signals();
return 0;
}
- if (OPT_ISSET(ops,'I')) {
- zwarnnam(nam, "-I requires one of -R/-W/-A");
- return 1;
- }
if (zleactive) {
zwarnnam(nam, "no interactive history within ZLE");
@@ -1456,12 +1487,13 @@ bin_fc(char *nam, char **argv, Options ops, int func)
if (!asgf)
asgf = asgl = a;
else {
- asgl->next = a;
+ asgl->node.next = &a->node;
asgl = a;
}
a->name = *argv;
- a->value = s;
- a->next = NULL;
+ a->is_array = 0;
+ a->value.scalar = s;
+ a->node.next = a->node.prev = NULL;
argv++;
}
/* interpret and check first history line specifier */
@@ -1635,8 +1667,8 @@ fcsubs(char **sp, struct asgment *sub)
/* loop through the linked list */
while (sub) {
oldstr = sub->name;
- newstr = sub->value;
- sub = sub->next;
+ newstr = sub->value.scalar;
+ sub = (Asgment)sub->node.next;
oldpos = s;
/* loop over occurences of oldstr in s, replacing them with newstr */
while ((newpos = (char *)strstr(oldpos, oldstr))) {
@@ -1672,7 +1704,7 @@ static int
fclist(FILE *f, Options ops, zlong first, zlong last,
struct asgment *subs, Patprog pprog, int is_command)
{
- int fclistdone = 0;
+ int fclistdone = 0, xflags = 0;
zlong tmp;
char *s, *tdfmt, *timebuf;
Histent ent;
@@ -1722,11 +1754,19 @@ fclist(FILE *f, Options ops, zlong first, zlong last,
tdfmt = timebuf = NULL;
}
+ /* xflags exclude events */
+ if (OPT_ISSET(ops,'L')) {
+ xflags |= HIST_FOREIGN;
+ }
+ if (OPT_ISSET(ops,'I')) {
+ xflags |= HIST_READ;
+ }
+
for (;;) {
- if (!OPT_ISSET(ops,'L') || !(ent->node.flags & HIST_FOREIGN))
- s = dupstring(ent->node.nam);
- else
+ if (ent->node.flags & xflags)
s = NULL;
+ else
+ s = dupstring(ent->node.nam);
/* this if does the pattern matching, if required */
if (s && (!pprog || pattry(pprog, s))) {
/* perform substitution */
@@ -1743,9 +1783,12 @@ fclist(FILE *f, Options ops, zlong first, zlong last,
command, if required */
if (tdfmt != NULL) {
struct tm *ltm;
+ int len;
ltm = localtime(&ent->stim);
- if (ztrftime(timebuf, 256, tdfmt, ltm, 0L))
- fprintf(f, "%s ", timebuf);
+ if ((len = ztrftime(timebuf, 256, tdfmt, ltm, 0L)) >= 0) {
+ fwrite(timebuf, 1, len, f);
+ fprintf(f, " ");
+ }
}
/* display the time taken by the command, if required */
if (OPT_ISSET(ops,'D')) {
@@ -1782,7 +1825,7 @@ fclist(FILE *f, Options ops, zlong first, zlong last,
if (!fclistdone) {
if (subs)
zwarnnam("fc", "no substitutions performed");
- else if (OPT_ISSET(ops,'L') || pprog)
+ else if (xflags || pprog)
zwarnnam("fc", "no matching events found");
return 1;
}
@@ -1816,13 +1859,22 @@ fcedit(char *ename, char *fn)
/**/
static Asgment
-getasg(char *s)
+getasg(char ***argvp, LinkList assigns)
{
+ char *s = **argvp;
static struct asgment asg;
/* sanity check for valid argument */
- if (!s)
+ if (!s) {
+ if (assigns) {
+ Asgment asgp = (Asgment)firstnode(assigns);
+ if (!asgp)
+ return NULL;
+ (void)uremnode(assigns, &asgp->node);
+ return asgp;
+ }
return NULL;
+ }
/* check if name is empty */
if (*s == '=') {
@@ -1830,6 +1882,7 @@ getasg(char *s)
return NULL;
}
asg.name = s;
+ asg.is_array = 0;
/* search for `=' */
for (; *s && *s != '='; s++);
@@ -1837,11 +1890,12 @@ getasg(char *s)
/* found `=', so return with a value */
if (*s) {
*s = '\0';
- asg.value = s + 1;
+ asg.value.scalar = s + 1;
} else {
/* didn't find `=', so we only have a name */
- asg.value = NULL;
+ asg.value.scalar = NULL;
}
+ (*argvp)++;
return &asg;
}
@@ -1923,10 +1977,10 @@ typeset_setwidth(const char * name, Param pm, Options ops, int on, int always)
/**/
static Param
typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
- int on, int off, int roff, char *value, Param altpm,
+ int on, int off, int roff, Asgment asg, Param altpm,
Options ops, int joinchar)
{
- int usepm, tc, keeplocal = 0, newspecial = NS_NONE, readonly;
+ int usepm, tc, keeplocal = 0, newspecial = NS_NONE, readonly, dont_set = 0;
char *subscript;
/*
@@ -1971,7 +2025,24 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
/* attempting a type conversion, or making a tied colonarray? */
tc = 0;
- if (usepm || newspecial != NS_NONE) {
+ if (ASG_ARRAYP(asg) && PM_TYPE(on) == PM_SCALAR &&
+ !(usepm && (PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED))))
+ on |= PM_ARRAY;
+ if (usepm && ASG_ARRAYP(asg) && newspecial == NS_NONE &&
+ PM_TYPE(pm->node.flags) != PM_ARRAY &&
+ PM_TYPE(pm->node.flags) != PM_HASHED) {
+ if (on & (PM_EFLOAT|PM_FFLOAT|PM_INTEGER)) {
+ zerrnam(cname, "%s: can't assign array value to non-array", pname);
+ return NULL;
+ }
+ if (pm->node.flags & PM_SPECIAL) {
+ zerrnam(cname, "%s: can't assign array value to non-array special", pname);
+ return NULL;
+ }
+ tc = 1;
+ usepm = 0;
+ }
+ else if (usepm || newspecial != NS_NONE) {
int chflags = ((off & pm->node.flags) | (on & ~pm->node.flags)) &
(PM_INTEGER|PM_EFLOAT|PM_FFLOAT|PM_HASHED|
PM_ARRAY|PM_TIED|PM_AUTOLOAD);
@@ -2019,7 +2090,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
tc = 0; /* but don't do a normal conversion */
}
} else if (!setsecondstype(pm, on, off)) {
- if (value && !(pm = setsparam(pname, ztrdup(value))))
+ if (asg->value.scalar && !(pm = setsparam(pname, ztrdup(asg->value.scalar))))
return NULL;
usepm = 1;
err = 0;
@@ -2045,7 +2116,7 @@ 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)) &&
- !value)
+ !ASG_VALUEP(asg))
on |= PM_UNSET;
else if (usepm && (pm->node.flags & PM_READONLY) &&
!(on & PM_READONLY)) {
@@ -2064,8 +2135,15 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
* ii. we are creating a new local parameter
*/
if (usepm) {
+ if (asg->is_array ?
+ !(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) :
+ (asg->value.scalar && (PM_TYPE(pm->node.flags &
+ (PM_ARRAY|PM_HASHED))))) {
+ zerrnam(cname, "%s: inconsistent type for assignment", pname);
+ return NULL;
+ }
on &= ~PM_LOCAL;
- if (!on && !roff && !value) {
+ if (!on && !roff && !ASG_VALUEP(asg)) {
if (OPT_ISSET(ops,'p'))
paramtab->printnode(&pm->node, PRINT_TYPESET);
else if (!OPT_ISSET(ops,'g') &&
@@ -2119,15 +2197,18 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
}
if (!(pm->node.flags & (PM_ARRAY|PM_HASHED))) {
if (pm->node.flags & PM_EXPORTED) {
- if (!(pm->node.flags & PM_UNSET) && !pm->env && !value)
+ if (!(pm->node.flags & PM_UNSET) && !pm->env && !ASG_VALUEP(asg))
addenv(pm, getsparam(pname));
} else if (pm->env && !(pm->node.flags & PM_HASHELEM))
delenv(pm);
- if (value && !(pm = setsparam(pname, ztrdup(value))))
+ DPUTS(ASG_ARRAYP(asg), "BUG: typeset got array value where scalar expected");
+ if (asg->value.scalar && !(pm = setsparam(pname, ztrdup(asg->value.scalar))))
+ return NULL;
+ } else if (asg->is_array) {
+ if (!(pm = setaparam(pname, asg->value.array ?
+ zlinklist2array(asg->value.array) :
+ mkarray(NULL))))
return NULL;
- } else if (value) {
- zwarnnam(cname, "can't assign new value for array %s", pname);
- return NULL;
}
pm->node.flags |= (on & PM_READONLY);
if (OPT_ISSET(ops,'p'))
@@ -2135,6 +2216,13 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
return pm;
}
+ if (asg->is_array ?
+ !(on & (PM_ARRAY|PM_HASHED)) :
+ (asg->value.scalar && (on & (PM_ARRAY|PM_HASHED)))) {
+ zerrnam(cname, "%s: inconsistent type for assignment", pname);
+ return NULL;
+ }
+
/*
* We're here either because we're creating a new parameter,
* or we're adding a parameter at a different local level,
@@ -2154,9 +2242,14 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
/*
* Try to carry over a value, but not when changing from,
* to, or between non-scalar types.
+ *
+ * (We can do better now, but it does have user-visible
+ * implications.)
*/
- if (!value && !((pm->node.flags|on) & (PM_ARRAY|PM_HASHED)))
- value = dupstring(getsparam(pname));
+ if (!ASG_VALUEP(asg) && !((pm->node.flags|on) & (PM_ARRAY|PM_HASHED))) {
+ asg->value.scalar = dupstring(getsparam(pname));
+ asg->is_array = 0;
+ }
/* pname may point to pm->nam which is about to disappear */
pname = dupstring(pname);
unsetparam_pm(pm, 0, 1);
@@ -2235,7 +2328,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
zerrnam(cname,
"%s: can't create readonly array elements", pname);
return NULL;
- } else if (on & PM_LOCAL) {
+ } else if ((on & PM_LOCAL) && locallevel) {
*subscript = 0;
pm = (Param) (paramtab == realparamtab ?
gethashnode2(paramtab, pname) :
@@ -2247,21 +2340,30 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
return NULL;
}
}
- if (PM_TYPE(on) == PM_SCALAR) {
+ if (PM_TYPE(on) == PM_SCALAR && !ASG_ARRAYP(asg)) {
/*
* This will either complain about bad identifiers, or will set
* a hash element or array slice. This once worked by accident,
* creating a stray parameter along the way via createparam(),
* now called below in the isident() branch.
*/
- if (!(pm = setsparam(pname, ztrdup(value ? value : ""))))
+ if (!(pm = setsparam(pname, ztrdup(asg->value.scalar ? asg->value.scalar : ""))))
return NULL;
- value = NULL;
+ dont_set = 1;
+ asg->is_array = 0;
+ keeplocal = 0;
+ on = pm->node.flags;
+ } else if (PM_TYPE(on) == PM_ARRAY && ASG_ARRAYP(asg)) {
+ if (!(pm = setaparam(pname, asg->value.array ?
+ zlinklist2array(asg->value.array) :
+ mkarray(NULL))))
+ return NULL;
+ dont_set = 1;
keeplocal = 0;
on = pm->node.flags;
} else {
zerrnam(cname,
- "%s: array elements must be scalar", pname);
+ "%s: inconsistent array element or slice assignment", pname);
return NULL;
}
}
@@ -2327,10 +2429,19 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
pm->level = keeplocal;
else if (on & PM_LOCAL)
pm->level = locallevel;
- if (value && !(pm->node.flags & (PM_ARRAY|PM_HASHED))) {
+ if (ASG_VALUEP(asg) && !dont_set) {
Param ipm = pm;
- if (!(pm = setsparam(pname, ztrdup(value))))
- return NULL;
+ if (pm->node.flags & (PM_ARRAY|PM_HASHED)) {
+ DPUTS(!ASG_ARRAYP(asg), "BUG: inconsistent scalar value for array");
+ if (!(pm=setaparam(pname, asg->value.array ?
+ zlinklist2array(asg->value.array) :
+ mkarray(NULL))))
+ return NULL;
+ } else {
+ DPUTS(ASG_ARRAYP(asg), "BUG: inconsistent array value for scalar");
+ if (!(pm = setsparam(pname, ztrdup(asg->value.scalar))))
+ return NULL;
+ }
if (pm != ipm) {
DPUTS(ipm->node.flags != pm->node.flags,
"BUG: parameter recreated with wrong flags");
@@ -2367,12 +2478,6 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
}
}
pm->node.flags |= (on & PM_READONLY);
- if (value && (pm->node.flags & (PM_ARRAY|PM_HASHED))) {
- zerrnam(cname, "%s: can't assign initial value for array", pname);
- /* the only safe thing to do here seems to be unset the param */
- unsetparam_pm(pm, 0, 1);
- return NULL;
- }
if (OPT_ISSET(ops,'p'))
paramtab->printnode(&pm->node, PRINT_TYPESET);
@@ -2380,11 +2485,18 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
return pm;
}
-/* declare, export, integer, local, readonly, typeset */
+/*
+ * declare, export, float, integer, local, readonly, typeset
+ *
+ * Note the difference in interface from most builtins, covered by the
+ * BINF_ASSIGN builtin flag. This is only made use of by builtins
+ * called by reserved word, which only covers declare, local, readonly
+ * and typeset. Otherwise assigns is NULL.
+ */
/**/
int
-bin_typeset(char *name, char **argv, Options ops, int func)
+bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
{
Param pm;
Asgment asg;
@@ -2393,6 +2505,7 @@ bin_typeset(char *name, char **argv, Options ops, int func)
int on = 0, off = 0, roff, bit = PM_ARRAY;
int i;
int returnval = 0, printflags = 0;
+ int hasargs;
/* hash -f is really the builtin `functions' */
if (OPT_ISSET(ops,'f'))
@@ -2445,7 +2558,8 @@ bin_typeset(char *name, char **argv, Options ops, int func)
/* Given no arguments, list whatever the options specify. */
if (OPT_ISSET(ops,'p'))
printflags |= PRINT_TYPESET;
- if (!*argv) {
+ hasargs = *argv != NULL || (assigns && firstnode(assigns));
+ if (!hasargs) {
if (!OPT_ISSET(ops,'p')) {
if (!(on|roff))
printflags |= PRINT_TYPE;
@@ -2464,9 +2578,9 @@ bin_typeset(char *name, char **argv, Options ops, int func)
if (on & PM_TIED) {
Param apm;
- struct asgment asg0;
- char *oldval = NULL;
- int joinchar;
+ struct asgment asg0, asg2;
+ char *oldval = NULL, *joinstr;
+ int joinchar, nargs;
if (OPT_ISSET(ops,'m')) {
zwarnnam(name, "incompatible options for -T");
@@ -2474,34 +2588,41 @@ bin_typeset(char *name, char **argv, Options ops, int func)
return 1;
}
on &= ~off;
- if (!argv[1] || (argv[2] && argv[3])) {
+ nargs = arrlen(argv) + (assigns ? countlinknodes(assigns) : 0);
+ if (nargs < 2) {
zwarnnam(name, "-T requires names of scalar and array");
unqueue_signals();
return 1;
}
+ if (nargs > 3) {
+ zwarnnam(name, "too many arguments for -T");
+ unqueue_signals();
+ return 1;
+ }
- /*
- * Third argument, if given, is character used to join
- * the elements of the array in the scalar.
- */
- if (!argv[2])
- joinchar = ':';
- else if (!*argv[2])
- joinchar = 0;
- else if (*argv[2] == Meta)
- joinchar = argv[2][1] ^ 32;
- else
- joinchar = *argv[2];
-
- if (!(asg = getasg(argv[0]))) {
+ if (!(asg = getasg(&argv, assigns))) {
unqueue_signals();
return 1;
}
asg0 = *asg;
- if (!(asg = getasg(argv[1]))) {
+ if (ASG_ARRAYP(&asg0)) {
+ unqueue_signals();
+ zwarnnam(name, "first argument of tie must be scalar: %s",
+ asg0.name);
+ return 1;
+ }
+
+ if (!(asg = getasg(&argv, assigns))) {
+ unqueue_signals();
+ return 1;
+ }
+ if (!ASG_ARRAYP(asg) && asg->value.scalar) {
unqueue_signals();
+ zwarnnam(name, "second argument of tie must be array: %s",
+ asg->name);
return 1;
}
+
if (!strcmp(asg0.name, asg->name)) {
unqueue_signals();
zerrnam(name, "can't tie a variable to itself: %s", asg0.name);
@@ -2512,6 +2633,36 @@ bin_typeset(char *name, char **argv, Options ops, int func)
zerrnam(name, "can't tie array elements: %s", asg0.name);
return 1;
}
+ if (ASG_VALUEP(asg) && ASG_VALUEP(&asg0)) {
+ unqueue_signals();
+ zerrnam(name, "only one tied parameter can have value: %s", asg0.name);
+ return 1;
+ }
+
+ /*
+ * Third argument, if given, is character used to join
+ * the elements of the array in the scalar.
+ */
+ if (*argv)
+ joinstr = *argv;
+ else if (assigns && firstnode(assigns)) {
+ Asgment nextasg = (Asgment)firstnode(assigns);
+ if (ASG_ARRAYP(nextasg) || ASG_VALUEP(nextasg)) {
+ zwarnnam(name, "third argument of tie must be join character");
+ unqueue_signals();
+ return 1;
+ }
+ joinstr = nextasg->name;
+ } else
+ joinstr = NULL;
+ if (!joinstr)
+ joinchar = ':';
+ else if (!*joinstr)
+ joinchar = 0;
+ else if (*joinstr == Meta)
+ joinchar = joinstr[1] ^ 32;
+ else
+ joinchar = *joinstr;
/*
* Keep the old value of the scalar. We need to do this
* here as if it is already tied to the same array it
@@ -2526,15 +2677,19 @@ bin_typeset(char *name, char **argv, Options ops, int func)
&& (locallevel == pm->level || !(on & PM_LOCAL))) {
if (pm->node.flags & PM_TIED) {
unqueue_signals();
- if (!strcmp(asg->name, pm->ename)) {
+ if (PM_TYPE(pm->node.flags) != PM_SCALAR) {
+ zwarnnam(name, "already tied as non-scalar: %s", asg0.name);
+ } else if (!strcmp(asg->name, pm->ename)) {
/*
* Already tied in the fashion requested.
*/
struct tieddata *tdp = (struct tieddata*)pm->u.data;
/* Update join character */
tdp->joinchar = joinchar;
- if (asg0.value)
- setsparam(asg0.name, ztrdup(asg0.value));
+ if (asg0.value.scalar)
+ setsparam(asg0.name, ztrdup(asg0.value.scalar));
+ else if (asg->value.array)
+ setaparam(asg->name, zlinklist2array(asg->value.array));
return 0;
} else {
zwarnnam(name, "can't tie already tied scalar: %s",
@@ -2542,7 +2697,8 @@ bin_typeset(char *name, char **argv, Options ops, int func)
}
return 1;
}
- if (!asg0.value && !(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)))
+ if (!asg0.value.scalar && !asg->value.array &&
+ !(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)))
oldval = ztrdup(getsparam(asg0.name));
on |= (pm->node.flags & PM_EXPORTED);
}
@@ -2550,12 +2706,18 @@ bin_typeset(char *name, char **argv, Options ops, int func)
* Create the tied array; this is normal except that
* it has the PM_TIED flag set. Do it first because
* we need the address.
+ *
+ * Don't attempt to set it yet, it's too early
+ * to be exported properly.
*/
+ asg2.name = asg->name;
+ asg2.is_array = 0;
+ asg2.value.array = (LinkList)0;
if (!(apm=typeset_single(name, asg->name,
(Param)paramtab->getnode(paramtab,
asg->name),
func, (on | PM_ARRAY) & ~PM_EXPORTED,
- off, roff, asg->value, NULL, ops, 0))) {
+ off, roff, &asg2, NULL, ops, 0))) {
if (oldval)
zsfree(oldval);
unqueue_signals();
@@ -2568,7 +2730,7 @@ bin_typeset(char *name, char **argv, Options ops, int func)
if (!(pm=typeset_single(name, asg0.name,
(Param)paramtab->getnode(paramtab,
asg0.name),
- func, on, off, roff, asg0.value, apm,
+ func, on, off, roff, &asg0, apm,
ops, joinchar))) {
if (oldval)
zsfree(oldval);
@@ -2587,7 +2749,9 @@ bin_typeset(char *name, char **argv, Options ops, int func)
if (apm->ename)
zsfree(apm->ename);
apm->ename = ztrdup(asg0.name);
- if (oldval)
+ if (asg->value.array)
+ setaparam(asg->name, zlinklist2array(asg->value.array));
+ else if (oldval)
setsparam(asg0.name, oldval);
unqueue_signals();
@@ -2607,18 +2771,18 @@ bin_typeset(char *name, char **argv, Options ops, int func)
printflags |= PRINT_NAMEONLY;
}
- while ((asg = getasg(*argv++))) {
+ while ((asg = getasg(&argv, assigns))) {
LinkList pmlist = newlinklist();
LinkNode pmnode;
tokenize(asg->name); /* expand argument */
if (!(pprog = patcompile(asg->name, 0, NULL))) {
untokenize(asg->name);
- zwarnnam(name, "bad pattern : %s", argv[-1]);
+ zwarnnam(name, "bad pattern : %s", asg->name);
returnval = 1;
continue;
}
- if (OPT_PLUS(ops,'m') && !asg->value) {
+ if (OPT_PLUS(ops,'m') && !ASG_VALUEP(asg)) {
scanmatchtable(paramtab, pprog, 1, on|roff, 0,
paramtab->printnode, printflags);
continue;
@@ -2644,7 +2808,7 @@ bin_typeset(char *name, char **argv, Options ops, int func)
for (pmnode = firstnode(pmlist); pmnode; incnode(pmnode)) {
pm = (Param) getdata(pmnode);
if (!typeset_single(name, pm->node.nam, pm, func, on, off, roff,
- asg->value, NULL, ops, 0))
+ asg, NULL, ops, 0))
returnval = 1;
}
}
@@ -2653,7 +2817,7 @@ bin_typeset(char *name, char **argv, Options ops, int func)
}
/* Take arguments literally. Don't glob */
- while ((asg = getasg(*argv++))) {
+ while ((asg = getasg(&argv, assigns))) {
HashNode hn = (paramtab == realparamtab ?
gethashnode2(paramtab, asg->name) :
paramtab->getnode(paramtab, asg->name));
@@ -2667,7 +2831,7 @@ bin_typeset(char *name, char **argv, Options ops, int func)
continue;
}
if (!typeset_single(name, asg->name, (Param)hn,
- func, on, off, roff, asg->value, NULL,
+ func, on, off, roff, asg, NULL,
ops, 0))
returnval = 1;
}
@@ -2749,7 +2913,7 @@ bin_functions(char *name, char **argv, Options ops, int func)
Patprog pprog;
Shfunc shf;
int i, returnval = 0;
- int on = 0, off = 0, pflags = 0, roff;
+ int on = 0, off = 0, pflags = 0, roff, expand = 0;
/* Do we have any flags defined? */
if (OPT_PLUS(ops,'u'))
@@ -2785,11 +2949,23 @@ bin_functions(char *name, char **argv, Options ops, int func)
}
if ((off & PM_UNDEFINED) || (OPT_ISSET(ops,'k') && OPT_ISSET(ops,'z')) ||
+ (OPT_ISSET(ops,'x') && !OPT_HASARG(ops,'x')) ||
(OPT_MINUS(ops,'X') && (OPT_ISSET(ops,'m') || *argv || !scriptname))) {
zwarnnam(name, "invalid option(s)");
return 1;
}
+ if (OPT_ISSET(ops,'x')) {
+ char *eptr;
+ expand = (int)zstrtol(OPT_ARG(ops,'x'), &eptr, 10);
+ if (*eptr) {
+ zwarnnam(name, "number expected after -x");
+ return 1;
+ }
+ if (expand == 0) /* no indentation at all */
+ expand = -1;
+ }
+
if (OPT_PLUS(ops,'f') || roff || OPT_ISSET(ops,'+'))
pflags |= PRINT_NAMEONLY;
@@ -2948,8 +3124,8 @@ bin_functions(char *name, char **argv, Options ops, int func)
} else {
if (OPT_ISSET(ops,'U') && !OPT_ISSET(ops,'u'))
on &= ~PM_UNDEFINED;
- scanhashtable(shfunctab, 1, on|off, DISABLED, shfunctab->printnode,
- pflags);
+ scanshfunc(1, on|off, DISABLED, shfunctab->printnode,
+ pflags, expand);
}
unqueue_signals();
return ret;
@@ -2965,8 +3141,8 @@ bin_functions(char *name, char **argv, Options ops, int func)
/* with no options, just print all functions matching the glob pattern */
queue_signals();
if (!(on|off) && !OPT_ISSET(ops,'X')) {
- scanmatchtable(shfunctab, pprog, 1, 0, DISABLED,
- shfunctab->printnode, pflags);
+ scanmatchshfunc(pprog, 1, 0, DISABLED,
+ shfunctab->printnode, pflags, expand);
} else {
/* apply the options to all functions matching the glob pattern */
for (i = 0; i < shfunctab->hsize; i++) {
@@ -3008,7 +3184,7 @@ bin_functions(char *name, char **argv, Options ops, int func)
returnval = 1;
} else
/* no flags, so just print */
- shfunctab->printnode(&shf->node, pflags);
+ printshfuncexpand(&shf->node, pflags, expand);
} else if (on & PM_UNDEFINED) {
int signum = -1, ok = 1;
@@ -3222,6 +3398,7 @@ bin_whence(char *nam, char **argv, Options ops, int func)
int aliasflags;
int csh, all, v, wd;
int informed = 0;
+ int expand = 0;
char *cnam, **allmatched = 0;
/* Check some option information */
@@ -3230,6 +3407,17 @@ bin_whence(char *nam, char **argv, Options ops, int func)
all = OPT_ISSET(ops,'a');
wd = OPT_ISSET(ops,'w');
+ if (OPT_ISSET(ops,'x')) {
+ char *eptr;
+ expand = (int)zstrtol(OPT_ARG(ops,'x'), &eptr, 10);
+ if (*eptr) {
+ zwarnnam(nam, "number expected after -x");
+ return 1;
+ }
+ if (expand == 0) /* no indentation at all */
+ expand = -1;
+ }
+
if (OPT_ISSET(ops,'w'))
printflags |= PRINT_WHENCE_WORD;
else if (OPT_ISSET(ops,'c'))
@@ -3286,8 +3474,8 @@ bin_whence(char *nam, char **argv, Options ops, int func)
/* and shell functions... */
informed +=
- scanmatchtable(shfunctab, pprog, 1, 0, DISABLED,
- shfunctab->printnode, printflags);
+ scanmatchshfunc(pprog, 1, 0, DISABLED,
+ shfunctab->printnode, printflags, expand);
/* and builtins. */
informed +=
@@ -3342,7 +3530,7 @@ bin_whence(char *nam, char **argv, Options ops, int func)
}
/* Look for shell function */
if ((hn = shfunctab->getnode(shfunctab, *argv))) {
- shfunctab->printnode(hn, printflags);
+ printshfuncexpand(hn, printflags, expand);
informed = 1;
if (!all)
continue;
@@ -3486,7 +3674,7 @@ bin_hash(char *name, char **argv, Options ops, UNUSED(int func))
}
queue_signals();
- for (;*argv;++argv) {
+ while (*argv) {
void *hn;
if (OPT_ISSET(ops,'m')) {
/* with the -m option, treat the argument as a glob pattern */
@@ -3501,12 +3689,12 @@ bin_hash(char *name, char **argv, Options ops, UNUSED(int func))
}
continue;
}
- if (!(asg = getasg(*argv))) {
+ if (!(asg = getasg(&argv, NULL))) {
zwarnnam(name, "bad assignment");
returnval = 1;
- } else if (asg->value) {
+ } else if (ASG_VALUEP(asg)) {
if(isset(RESTRICTED)) {
- zwarnnam(name, "restricted: %s", asg->value);
+ zwarnnam(name, "restricted: %s", asg->value.scalar);
returnval = 1;
} else {
/* The argument is of the form foo=bar, *
@@ -3522,12 +3710,12 @@ bin_hash(char *name, char **argv, Options ops, UNUSED(int func))
} else {
Nameddir nd = hn = zshcalloc(sizeof *nd);
nd->node.flags = 0;
- nd->dir = ztrdup(asg->value);
+ nd->dir = ztrdup(asg->value.scalar);
}
} else {
Cmdnam cn = hn = zshcalloc(sizeof *cn);
cn->node.flags = HASHED;
- cn->u.cmd = ztrdup(asg->value);
+ cn->u.cmd = ztrdup(asg->value.scalar);
}
ht->addnode(ht, ztrdup(asg->name), hn);
if(OPT_ISSET(ops,'v'))
@@ -3733,12 +3921,12 @@ bin_alias(char *name, char **argv, Options ops, UNUSED(int func))
/* Take arguments literally. Don't glob */
queue_signals();
- while ((asg = getasg(*argv++))) {
- if (asg->value && !OPT_ISSET(ops,'L')) {
+ while ((asg = getasg(&argv, NULL))) {
+ if (asg->value.scalar && !OPT_ISSET(ops,'L')) {
/* The argument is of the form foo=bar and we are not *
* forcing a listing with -L, so define an alias */
ht->addnode(ht, ztrdup(asg->name),
- createaliasnode(ztrdup(asg->value), flags1));
+ createaliasnode(ztrdup(asg->value.scalar), flags1));
} else if ((a = (Alias) ht->getnode(ht, asg->name))) {
/* display alias if appropriate */
if (!type_opts || ht == sufaliastab ||
@@ -4208,11 +4396,40 @@ bin_print(char *name, char **args, Options ops, int func)
return 0;
}
- for (; *args; args++, len++) {
- fwrite(*args, *len, 1, fout);
- if (args[1])
- fputc(OPT_ISSET(ops,'l') ? '\n' :
- OPT_ISSET(ops,'N') ? '\0' : ' ', fout);
+ if (OPT_HASARG(ops, 'x') || OPT_HASARG(ops, 'X')) {
+ char *eptr;
+ int expand, startpos = 0;
+ int all = OPT_HASARG(ops, 'X');
+ char *xarg = all ? OPT_ARG(ops, 'X') : OPT_ARG(ops, 'x');
+
+ expand = (int)zstrtol(xarg, &eptr, 10);
+ if (*eptr || expand <= 0) {
+ zwarnnam(name, "positive integer expected after -%c: %s", 'x',
+ xarg);
+ return 1;
+ }
+ for (; *args; args++, len++) {
+ startpos = zexpandtabs(*args, *len, expand, startpos, fout,
+ all);
+ if (args[1]) {
+ if (OPT_ISSET(ops, 'l')) {
+ fputc('\n', fout);
+ startpos = 0;
+ } else if (OPT_ISSET(ops,'N')) {
+ fputc('\0', fout);
+ } else {
+ fputc(' ', fout);
+ startpos++;
+ }
+ }
+ }
+ } else {
+ for (; *args; args++, len++) {
+ fwrite(*args, *len, 1, fout);
+ if (args[1])
+ fputc(OPT_ISSET(ops,'l') ? '\n' :
+ OPT_ISSET(ops,'N') ? '\0' : ' ', fout);
+ }
}
if (!(OPT_ISSET(ops,'n') || nnl))
fputc(OPT_ISSET(ops,'N') ? '\0' : '\n', fout);
@@ -4461,7 +4678,7 @@ bin_print(char *name, char **args, Options ops, int func)
lleft -= chars;
ptr += chars;
}
- if (width > 0 && flags[2]) width = -width;
+ if (width > 0 && flags[3]) width = -width;
if (width > 0 && lchars < width)
count += fprintf(fout, "%*c", width - lchars, ' ');
count += fwrite(b, 1, lbytes, fout);
@@ -4525,7 +4742,7 @@ bin_print(char *name, char **args, Options ops, int func)
convchar_t cc;
#ifdef MULTIBYTE_SUPPORT
if (isset(MULTIBYTE)) {
- mb_metacharinit();
+ mb_charinit();
(void)mb_metacharlenconv(metafy(curarg+1, curlen-1,
META_USEHEAP), &cc);
}
@@ -5500,7 +5717,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
wint_t wi;
if (isset(MULTIBYTE)) {
- mb_metacharinit();
+ mb_charinit();
(void)mb_metacharlenconv(delimstr, &wi);
}
else
@@ -6355,8 +6572,14 @@ bin_trap(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
zwarnnam(name, "undefined signal: %s", *argv);
break;
}
- if (!strcmp(sigs[sig], *argv))
+ if (idigit(**argv) ||
+ !strcmp(sigs[sig], *argv) ||
+ (!strncmp("SIG", *argv, 3) && !strcmp(sigs[sig], *argv+3))) {
+ /* The signal was specified by number or by canonical name (with
+ * or without SIG prefix).
+ */
flags = 0;
+ }
else {
/*
* Record that the signal is used under an assumed name.
diff --git a/Src/compat.c b/Src/compat.c
index b3a8b063c..db468529a 100644
--- a/Src/compat.c
+++ b/Src/compat.c
@@ -454,8 +454,13 @@ zgetcwd(void)
return ret;
}
-/* chdir with arbitrary long pathname. Returns 0 on success, -1 on normal *
- * failure and -2 when chdir failed and the current directory is lost. */
+/*
+ * chdir with arbitrary long pathname. Returns 0 on success, -1 on normal *
+ * failure and -2 when chdir failed and the current directory is lost.
+ *
+ * This is to be treated as if at system level, so dir is unmetafied but
+ * terminated by a NULL.
+ */
/**/
mod_export int
diff --git a/Src/context.c b/Src/context.c
index 1b8741f46..2dc8d3b89 100644
--- a/Src/context.c
+++ b/Src/context.c
@@ -53,6 +53,8 @@ zcontext_save_partial(int parts)
{
struct context_stack *cs;
+ queue_signals();
+
cs = (struct context_stack *)malloc(sizeof(struct context_stack));
if (parts & ZCONTEXT_HIST) {
@@ -67,6 +69,8 @@ zcontext_save_partial(int parts)
cs->next = cstack;
cstack = cs;
+
+ unqueue_signals();
}
/* save context in full */
diff --git a/Src/exec.c b/Src/exec.c
index 9f163a627..45f1c66f0 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -1115,15 +1115,20 @@ execsimple(Estate state)
if (code == WC_ASSIGN) {
cmdoutval = 0;
addvars(state, state->pc - 1, 0);
+ setunderscore("");
if (isset(XTRACE)) {
fputc('\n', xtrerr);
fflush(xtrerr);
}
lv = (errflag ? errflag : cmdoutval);
- } else if (code == WC_FUNCDEF) {
- lv = execfuncdef(state, NULL);
} else {
- lv = (execfuncs[code - WC_CURSH])(state, 0);
+ int q = queue_signal_level();
+ dont_queue_signals();
+ if (code == WC_FUNCDEF)
+ lv = execfuncdef(state, NULL);
+ else
+ lv = (execfuncs[code - WC_CURSH])(state, 0);
+ restore_queue_signals(q);
}
thisjob = otj;
@@ -1157,6 +1162,8 @@ execlist(Estate state, int dont_change_job, int exiting)
*/
int oldnoerrexit = noerrexit;
+ queue_signals();
+
cj = thisjob;
old_pline_level = pline_level;
old_list_pipe = list_pipe;
@@ -1350,7 +1357,13 @@ execlist(Estate state, int dont_change_job, int exiting)
state->pc--;
sublist_done:
- noerrexit = oldnoerrexit;
+ /*
+ * See hairy code near the end of execif() for the
+ * following. "noerrexit == 2" only applies until
+ * we hit execcmd on the way down. We're now
+ * on the way back up, so don't restore it.
+ */
+ noerrexit = (oldnoerrexit == 2) ? 0 : oldnoerrexit;
if (sigtrapped[SIGDEBUG] && !isset(DEBUGBEFORECMD) && !donedebug) {
/*
@@ -1421,6 +1434,8 @@ sublist_done:
/* Make sure this doesn't get executed again. */
sigtrapped[SIGEXIT] = 0;
}
+
+ unqueue_signals();
}
/* Execute a pipeline. *
@@ -1449,6 +1464,14 @@ execpline(Estate state, wordcode slcode, int how, int last1)
else if (slflags & WC_SUBLIST_NOT)
last1 = 0;
+ /* If trap handlers are allowed to run here, they may start another
+ * external job in the middle of us starting this one, which can
+ * result in jobs being reaped before their job table entries have
+ * been initialized, which in turn leads to waiting forever for
+ * jobs that no longer exist. So don't do that.
+ */
+ queue_signals();
+
pj = thisjob;
ipipe[0] = ipipe[1] = opipe[0] = opipe[1] = 0;
child_block();
@@ -1461,6 +1484,7 @@ execpline(Estate state, wordcode slcode, int how, int last1)
*/
if ((thisjob = newjob = initjob()) == -1) {
child_unblock();
+ unqueue_signals();
return 1;
}
if (how & Z_TIMED)
@@ -1516,6 +1540,7 @@ execpline(Estate state, wordcode slcode, int how, int last1)
else
spawnjob();
child_unblock();
+ unqueue_signals();
/* Executing background code resets shell status */
return lastval = 0;
} else {
@@ -1573,15 +1598,18 @@ execpline(Estate state, wordcode slcode, int how, int last1)
}
if (!(jn->stat & STAT_LOCKED)) {
updated = hasprocs(thisjob);
- waitjobs();
+ waitjobs(); /* deals with signal queue */
child_block();
} else
updated = 0;
if (!updated &&
list_pipe_job && hasprocs(list_pipe_job) &&
!(jobtab[list_pipe_job].stat & STAT_STOPPED)) {
+ int q = queue_signal_level();
child_unblock();
child_block();
+ dont_queue_signals();
+ restore_queue_signals(q);
}
if (list_pipe_child &&
jn->stat & STAT_DONE &&
@@ -1665,6 +1693,7 @@ execpline(Estate state, wordcode slcode, int how, int last1)
break;
}
child_unblock();
+ unqueue_signals();
if (list_pipe && (lastval & 0200) && pj >= 0 &&
(!(jn->stat & STAT_INUSE) || (jn->stat & STAT_DONE))) {
@@ -2264,14 +2293,14 @@ addvars(Estate state, Wordcode pc, int addflags)
state->pc = opc;
return;
}
- if (!isstr || (isset(GLOBASSIGN) &&
+ if (!isstr || (isset(GLOBASSIGN) && isstr &&
haswilds((char *)getdata(firstnode(vl))))) {
globlist(vl, 0);
/* Unset the parameter to force it to be recreated
* as either scalar or array depending on how many
* matches were found for the glob.
*/
- if (isset(GLOBASSIGN))
+ if (isset(GLOBASSIGN) && isstr)
unsetparam(name);
}
if (errflag) {
@@ -2436,13 +2465,13 @@ execcmd(Estate state, int input, int output, int how, int last1)
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;
+ 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;
+ Wordcode beg = state->pc, varspc, assignspc = (Wordcode)0;
FILE *oxtrerr = xtrerr, *newxtrerr = NULL;
doneps4 = 0;
@@ -2463,8 +2492,28 @@ execcmd(Estate state, int input, int output, int how, int last1)
/* 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. */
- args = (type == WC_SIMPLE ?
- ecgetlist(state, WC_SIMPLE_ARGC(code), EC_DUP, &htok) : NULL);
+ switch (type) {
+ case WC_SIMPLE:
+ args = ecgetlist(state, WC_SIMPLE_ARGC(code), EC_DUP, &htok);
+ 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++) {
+ code = *state->pc;
+ DPUTS(wc_code(code) != WC_ASSIGN,
+ "BUG: miscounted typeset assignments");
+ state->pc += (WC_ASSIGN_TYPE(code) == WC_ASSIGN_SCALAR ?
+ 3 : WC_ASSIGN_NUM(code) + 2);
+ }
+ break;
+
+ default:
+ args = NULL;
+ }
+
/*
* If assignment but no command get the status from variable
* assignment.
@@ -2487,7 +2536,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
/* If the command begins with `%', then assume it is a *
* reference to a job in the job table. */
- if (type == WC_SIMPLE && args && nonempty(args) &&
+ if ((type == WC_SIMPLE || type == WC_TYPESET) && args && nonempty(args) &&
*(char *)peekfirst(args) == '%') {
if (how & Z_DISOWN) {
oautocont = opts[AUTOCONTINUE];
@@ -2516,20 +2565,32 @@ execcmd(Estate state, int input, int output, int how, int last1)
* command if it contains some tokens (e.g. x=ex; ${x}port), so this *
* only works in simple cases. has_token() is called to make sure *
* this really is a simple case. */
- if (type == WC_SIMPLE) {
+ if (type == WC_SIMPLE || type == WC_TYPESET) {
while (args && nonempty(args)) {
char *cmdarg = (char *) peekfirst(args);
checked = !has_token(cmdarg);
if (!checked)
break;
- if (!(cflags & (BINF_BUILTIN | BINF_COMMAND)) &&
- (hn = shfunctab->getnode(shfunctab, cmdarg))) {
- is_shfunc = 1;
- break;
- }
- if (!(hn = builtintab->getnode(builtintab, cmdarg))) {
- checked = !(cflags & BINF_BUILTIN);
- break;
+ if (type == WC_TYPESET &&
+ (hn = builtintab->getnode2(builtintab, cmdarg))) {
+ /*
+ * If reserved word for typeset command found (and so
+ * enabled), use regardless of whether builtin is
+ * enabled as we share the implementation.
+ *
+ * Reserved words take precedence over shell functions.
+ */
+ checked = 1;
+ } else {
+ if (!(cflags & (BINF_BUILTIN | BINF_COMMAND)) &&
+ (hn = shfunctab->getnode(shfunctab, cmdarg))) {
+ is_shfunc = 1;
+ break;
+ }
+ if (!(hn = builtintab->getnode(builtintab, cmdarg))) {
+ checked = !(cflags & BINF_BUILTIN);
+ break;
+ }
}
orig_cflags |= cflags;
cflags &= ~BINF_BUILTIN & ~BINF_COMMAND;
@@ -2660,7 +2721,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
if (args && htok)
prefork(args, esprefork);
- if (type == WC_SIMPLE) {
+ if (type == WC_SIMPLE || type == WC_TYPESET) {
int unglobbed = 0;
for (;;) {
@@ -2896,7 +2957,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
return;
}
- if (type == WC_SIMPLE && !nullexec) {
+ if ((type == WC_SIMPLE || type == WC_TYPESET) && !nullexec) {
char *s;
char trycd = (isset(AUTOCD) && isset(SHINSTDIN) &&
(!redir || empty(redir)) && args && !empty(args) &&
@@ -3008,6 +3069,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
addproc(pid, text, 0, &bgtime);
if (oautocont >= 0)
opts[AUTOCONTINUE] = oautocont;
+ pipecleanfilelist(jobtab[thisjob].filelist, 1);
return;
}
/* pid == 0 */
@@ -3282,7 +3344,8 @@ execcmd(Estate state, int input, int output, int how, int last1)
fil = -1;
else if (IS_APPEND_REDIR(fn->type))
fil = open(unmeta(fn->name),
- (unset(CLOBBER) && !IS_CLOBBER_REDIR(fn->type)) ?
+ ((unset(CLOBBER) && unset(APPENDCREATE)) &&
+ !IS_CLOBBER_REDIR(fn->type)) ?
O_WRONLY | O_APPEND | O_NOCTTY :
O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, 0666);
else
@@ -3350,6 +3413,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
fflush(xtrerr);
}
} else if (isset(EXECOPT) && !errflag) {
+ int q = queue_signal_level();
/*
* We delay the entersubsh() to here when we are exec'ing
* the current shell (including a fake exec to run a builtin then
@@ -3390,11 +3454,14 @@ execcmd(Estate state, int input, int output, int how, int last1)
} else
redir_prog = NULL;
+ dont_queue_signals();
lastval = execfuncdef(state, redir_prog);
+ restore_queue_signals(q);
}
else if (type >= WC_CURSH) {
if (last1 == 1)
do_exec = 1;
+ dont_queue_signals();
if (type == WC_AUTOFN) {
/*
* We pre-loaded this to get any redirs.
@@ -3403,6 +3470,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
lastval = execautofn_basic(state, do_exec);
} else
lastval = (execfuncs[type - WC_CURSH])(state, do_exec);
+ restore_queue_signals(q);
} else if (is_builtin || is_shfunc) {
LinkList restorelist = 0, removelist = 0;
/* builtin or shell function */
@@ -3452,13 +3520,126 @@ execcmd(Estate state, int input, int output, int how, int last1)
if (is_shfunc) {
/* It's a shell function */
- pipecleanfilelist(filelist);
+ pipecleanfilelist(filelist, 0);
execshfunc((Shfunc) hn, args);
} else {
/* It's a builtin */
+ LinkList assigns = (LinkList)0;
if (forked)
closem(FDT_INTERNAL);
- lastval = execbuiltin(args, (Builtin) hn);
+ if (postassigns) {
+ Wordcode opc = state->pc;
+ state->pc = assignspc;
+ assigns = newlinklist();
+ while (postassigns--) {
+ wordcode ac = *state->pc++;
+ char *name = ecgetstr(state, EC_DUPTOK, &htok);
+ Asgment asg;
+ local_list1(svl);
+
+ DPUTS(wc_code(ac) != WC_ASSIGN,
+ "BUG: bad assignment list for typeset");
+ if (htok) {
+ init_list1(svl, name);
+ if (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR &&
+ WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC) {
+ char *data;
+ /*
+ * Special case: this is a name only, so
+ * it's not required to be a single
+ * expansion. Furthermore, for
+ * consistency with the builtin
+ * interface, it may expand into
+ * scalar assignments:
+ * ass=(one=two three=four)
+ * typeset a=b $ass
+ */
+ /* Unused dummy value for name */
+ (void)ecgetstr(state, EC_DUPTOK, &htok);
+ prefork(&svl, PREFORK_TYPESET);
+ if (errflag) {
+ state->pc = opc;
+ break;
+ }
+ globlist(&svl, 0);
+ if (errflag) {
+ state->pc = opc;
+ break;
+ }
+ while ((data = ugetnode(&svl))) {
+ char *ptr;
+ asg = (Asgment)zhalloc(sizeof(struct asgment));
+ asg->is_array = 0;
+ if ((ptr = strchr(data, '='))) {
+ *ptr++ = '\0';
+ asg->name = data;
+ asg->value.scalar = ptr;
+ } else {
+ asg->name = data;
+ asg->value.scalar = NULL;
+ }
+ uaddlinknode(assigns, &asg->node);
+ }
+ continue;
+ }
+ prefork(&svl, PREFORK_SINGLE);
+ name = empty(&svl) ? "" :
+ (char *)getdata(firstnode(&svl));
+ }
+ untokenize(name);
+ asg = (Asgment)zhalloc(sizeof(struct asgment));
+ asg->name = name;
+ if (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR) {
+ char *val = ecgetstr(state, EC_DUPTOK, &htok);
+ asg->is_array = 0;
+ if (WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC) {
+ /* Fake assignment, no value */
+ asg->value.scalar = NULL;
+ } else {
+ if (htok) {
+ init_list1(svl, val);
+ prefork(&svl, PREFORK_SINGLE|PREFORK_ASSIGN);
+ if (errflag) {
+ state->pc = opc;
+ break;
+ }
+ /*
+ * No globassign for typeset
+ * arguments, thank you
+ */
+ val = empty(&svl) ? "" :
+ (char *)getdata(firstnode(&svl));
+ }
+ untokenize(val);
+ asg->value.scalar = val;
+ }
+ } else {
+ asg->is_array = 1;
+ asg->value.array =
+ ecgetlist(state, WC_ASSIGN_NUM(ac),
+ EC_DUPTOK, &htok);
+ if (asg->value.array)
+ {
+ prefork(asg->value.array, PREFORK_ASSIGN);
+ if (errflag) {
+ state->pc = opc;
+ break;
+ }
+ globlist(asg->value.array, 0);
+ if (errflag) {
+ state->pc = opc;
+ break;
+ }
+ }
+ }
+
+ uaddlinknode(assigns, &asg->node);
+ }
+ state->pc = opc;
+ }
+ dont_queue_signals();
+ lastval = execbuiltin(args, assigns, (Builtin) hn);
+ restore_queue_signals(q);
fflush(stdout);
if (save[1] == -2) {
if (ferror(stdout)) {
@@ -3500,7 +3681,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
if (!subsh && isset(RCS) && interact && !nohistsave)
savehistfile(NULL, 1, HFILE_USE_OPTIONS);
}
- if (type == WC_SIMPLE) {
+ if (type == WC_SIMPLE || type == WC_TYPESET) {
if (varspc) {
int addflags = ADDVAR_EXPORT|ADDVAR_RESTRICT;
if (forked)
@@ -3531,7 +3712,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
DPUTS(varspc,
"BUG: assignment before complex command");
list_pipe = 0;
- pipecleanfilelist(filelist);
+ pipecleanfilelist(filelist, 0);
/* If we're forked (and we should be), no need to return */
DPUTS(last1 != 1 && !forked, "BUG: not exiting?");
DPUTS(type != WC_SUBSH, "Not sure what we're doing.");
@@ -4133,6 +4314,10 @@ namedpipe(void)
{
char *tnam = gettempname(NULL, 1);
+ if (!tnam) {
+ zerr("failed to create named pipe: %e", errno);
+ return NULL;
+ }
# ifdef HAVE_MKFIFO
if (mkfifo(tnam, 0600) < 0){
# else
@@ -4664,11 +4849,9 @@ execshfunc(Shfunc shf, LinkList args)
if ((osfc = sfcontext) == SFC_NONE)
sfcontext = SFC_DIRECT;
xtrerr = stderr;
- unqueue_signals();
doshfunc(shf, args, 0);
- queue_signals();
sfcontext = osfc;
free(cmdstack);
cmdstack = ocs;
@@ -4883,6 +5066,8 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
static int funcdepth;
#endif
+ queue_signals(); /* Lots of memory and global state changes coming */
+
pushheap();
oargv0 = NULL;
@@ -5105,6 +5290,8 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
}
popheap();
+ unqueue_signals();
+
/*
* Exit with a tidy up.
* Only leave if we're at the end of the appropriate function ---
@@ -5143,6 +5330,8 @@ runshfunc(Eprog prog, FuncWrap wrap, char *name)
int cont, ouu;
char *ou;
+ queue_signals();
+
ou = zalloc(ouu = underscoreused);
if (ou)
memcpy(ou, zunderscore, underscoreused);
@@ -5164,12 +5353,14 @@ runshfunc(Eprog prog, FuncWrap wrap, char *name)
wrap = wrap->next;
}
startparamscope();
- execode(prog, 1, 0, "shfunc");
+ execode(prog, 1, 0, "shfunc"); /* handles signal unqueueing */
if (ou) {
setunderscore(ou);
zfree(ou, ouu);
}
endparamscope();
+
+ unqueue_signals();
}
/* Search fpath for an undefined function. Finds the file, and returns the *
diff --git a/Src/glob.c b/Src/glob.c
index 057d44a17..dea1bf50e 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -102,8 +102,14 @@ int badcshglob;
/**/
int pathpos; /* position in pathbuf (needed by pattern code) */
+/*
+ * pathname buffer (needed by pattern code).
+ * It is currently believed the string in here is stored metafied and is
+ * unmetafied temporarily as needed by system calls.
+ */
+
/**/
-char *pathbuf; /* pathname buffer (needed by pattern code) */
+char *pathbuf;
typedef struct stat *Statptr; /* This makes the Ultrix compiler happy. Go figure. */
@@ -216,22 +222,26 @@ static struct globdata curglobdata;
#define save_globstate(N) \
do { \
+ queue_signals(); \
memcpy(&(N), &curglobdata, sizeof(struct globdata)); \
(N).gd_pathpos = pathpos; \
(N).gd_pathbuf = pathbuf; \
(N).gd_glob_pre = glob_pre; \
(N).gd_glob_suf = glob_suf; \
pathbuf = NULL; \
+ unqueue_signals(); \
} while (0)
#define restore_globstate(N) \
do { \
+ queue_signals(); \
zfree(pathbuf, pathbufsz); \
memcpy(&curglobdata, &(N), sizeof(struct globdata)); \
pathpos = (N).gd_pathpos; \
pathbuf = (N).gd_pathbuf; \
glob_pre = (N).gd_glob_pre; \
glob_suf = (N).gd_glob_suf; \
+ unqueue_signals(); \
} while (0)
/* pathname component in filename patterns */
@@ -253,7 +263,7 @@ addpath(char *s, int l)
{
DPUTS(!pathbuf, "BUG: pathbuf not initialised");
while (pathpos + l + 1 >= pathbufsz)
- pathbuf = realloc(pathbuf, pathbufsz *= 2);
+ pathbuf = zrealloc(pathbuf, pathbufsz *= 2);
while (l--)
pathbuf[pathpos++] = *s++;
pathbuf[pathpos++] = '/';
@@ -491,7 +501,7 @@ scanner(Complist q, int shortcircuit)
if (l >= PATH_MAX)
return;
- err = lchdir(pathbuf + pathbufcwd, &ds, 0);
+ err = lchdir(unmeta(pathbuf + pathbufcwd), &ds, 0);
if (err == -1)
return;
if (err) {
@@ -513,7 +523,7 @@ scanner(Complist q, int shortcircuit)
else if (!strcmp(str, "..")) {
struct stat sc, sr;
- add = (stat("/", &sr) || stat(pathbuf, &sc) ||
+ add = (stat("/", &sr) || stat(unmeta(pathbuf), &sc) ||
sr.st_ino != sc.st_ino ||
sr.st_dev != sc.st_dev);
}
@@ -560,7 +570,7 @@ scanner(Complist q, int shortcircuit)
DPUTS(pathpos == pathbufcwd,
"BUG: filename longer than PATH_MAX");
- err = lchdir(pathbuf + pathbufcwd, &ds, 0);
+ err = lchdir(unmeta(pathbuf + pathbufcwd), &ds, 0);
if (err == -1)
break;
if (err) {
@@ -2237,7 +2247,7 @@ xpandbraces(LinkList list, LinkNode *np)
#ifdef MULTIBYTE_SUPPORT
char *ncptr;
int nclen;
- mb_metacharinit();
+ mb_charinit();
ncptr = wcs_nicechar(cend, NULL, NULL);
nclen = strlen(ncptr);
p = zhalloc(lenalloc + nclen);
@@ -2805,7 +2815,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr,
* ... now we know whether it's worth looking for the
* shortest, which we do by brute force.
*/
- mb_metacharinit();
+ mb_charinit();
for (t = s, umlen = 0; t < s + mlen; ) {
set_pat_end(p, *t);
if (pattrylen(p, s, t - s, umlen, 0)) {
@@ -2831,7 +2841,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr,
* so that match, mbegin, mend and MATCH, MBEGIN, MEND are
* correct.
*/
- mb_metacharinit();
+ mb_charinit();
tmatch = NULL;
for (ioff = 0, t = s, umlen = umltot; t < s + l; ioff++) {
set_pat_start(p, t-s);
@@ -2855,7 +2865,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr,
/* Largest possible match at tail of string: *
* move forward along string until we get a match. *
* Again there's no optimisation. */
- mb_metacharinit();
+ mb_charinit();
for (ioff = 0, t = s, umlen = umltot; t < s + l; ioff++) {
set_pat_start(p, t-s);
if (pattrylen(p, t, s + l - t, umlen, ioff)) {
@@ -2889,7 +2899,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr,
}
ioff = 0; /* offset into string */
umlen = umltot;
- mb_metacharinit();
+ mb_charinit();
do {
/* loop over all matches for global substitution */
matched = 0;
@@ -2986,7 +2996,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr,
*/
nmatches = 0;
tmatch = NULL;
- mb_metacharinit();
+ mb_charinit();
for (ioff = 0, t = s, umlen = umltot; t < s + l; ioff++) {
set_pat_start(p, t-s);
if (pattrylen(p, t, s + l - t, umlen, ioff)) {
@@ -3002,7 +3012,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr,
* We need to find the n'th last match.
*/
n = nmatches - n;
- mb_metacharinit();
+ mb_charinit();
for (ioff = 0, t = s, umlen = umltot; t < s + l; ioff++) {
set_pat_start(p, t-s);
if (pattrylen(p, t, s + l - t, umlen, ioff) &&
diff --git a/Src/hashtable.c b/Src/hashtable.c
index ab381cc6a..90739a882 100644
--- a/Src/hashtable.c
+++ b/Src/hashtable.c
@@ -937,13 +937,17 @@ printshfuncnode(HashNode hn, int printflags)
quotedzputs(f->node.nam, stdout);
if (f->funcdef || f->node.flags & PM_UNDEFINED) {
- printf(" () {\n\t");
- if (f->node.flags & PM_UNDEFINED)
- printf("%c undefined\n\t", hashchar);
- else
+ printf(" () {\n");
+ zoutputtab(stdout);
+ if (f->node.flags & PM_UNDEFINED) {
+ printf("%c undefined\n", hashchar);
+ zoutputtab(stdout);
+ } else
t = getpermtext(f->funcdef, NULL, 1);
- if (f->node.flags & (PM_TAGGED|PM_TAGGED_LOCAL))
- printf("%c traced\n\t", hashchar);
+ if (f->node.flags & (PM_TAGGED|PM_TAGGED_LOCAL)) {
+ printf("%c traced\n", hashchar);
+ zoutputtab(stdout);
+ }
if (!t) {
char *fopt = "UtTkz";
int flgs[] = {
@@ -959,11 +963,12 @@ printshfuncnode(HashNode hn, int printflags)
zputs(t, stdout);
zsfree(t);
if (f->funcdef->flags & EF_RUN) {
- printf("\n\t");
+ printf("\n");
+ zoutputtab(stdout);
quotedzputs(f->node.nam, stdout);
printf(" \"$@\"");
}
- }
+ }
printf("\n}");
} else {
printf(" () { }");
@@ -979,6 +984,59 @@ printshfuncnode(HashNode hn, int printflags)
putchar('\n');
}
+/*
+ * Wrap scanmatchtable for shell functions with optional
+ * expansion of leading tabs.
+ * expand = 0 is standard: use hard tabs.
+ * expand > 0 uses that many spaces.
+ * expand < 0 uses no identation.
+ *
+ * Note this function and the following two are called with
+ * interrupts queued, so saving and restoring text_expand_tabs
+ * is safe.
+ */
+
+/**/
+mod_export int
+scanmatchshfunc(Patprog pprog, int sorted, int flags1, int flags2,
+ ScanFunc scanfunc, int scanflags, int expand)
+{
+ int ret, save_expand;
+
+ save_expand = text_expand_tabs;
+ text_expand_tabs = expand;
+ ret = scanmatchtable(shfunctab, pprog, sorted, flags1, flags2,
+ scanfunc, scanflags);
+ text_expand_tabs = save_expand;
+
+ return ret;
+}
+
+/* Wrap scanhashtable to expand tabs for shell functions */
+
+/**/
+mod_export int
+scanshfunc(int sorted, int flags1, int flags2,
+ ScanFunc scanfunc, int scanflags, int expand)
+{
+ return scanmatchshfunc(NULL, sorted, flags1, flags2,
+ scanfunc, scanflags, expand);
+}
+
+/* Wrap shfunctab->printnode to expand tabs */
+
+/**/
+mod_export void
+printshfuncexpand(HashNode hn, int printflags, int expand)
+{
+ int save_expand;
+
+ save_expand = text_expand_tabs;
+ text_expand_tabs = expand;
+ shfunctab->printnode(hn, printflags);
+ text_expand_tabs = save_expand;
+}
+
/**************************************/
/* Reserved Word Hash Table Functions */
/**************************************/
@@ -992,22 +1050,29 @@ static struct reswd reswds[] = {
{{NULL, "}", 0}, OUTBRACE},
{{NULL, "case", 0}, CASE},
{{NULL, "coproc", 0}, COPROC},
+ {{NULL, "declare", 0}, TYPESET},
{{NULL, "do", 0}, DOLOOP},
{{NULL, "done", 0}, DONE},
{{NULL, "elif", 0}, ELIF},
{{NULL, "else", 0}, ELSE},
{{NULL, "end", 0}, ZEND},
{{NULL, "esac", 0}, ESAC},
+ {{NULL, "export", 0}, TYPESET},
{{NULL, "fi", 0}, FI},
+ {{NULL, "float", 0}, TYPESET},
{{NULL, "for", 0}, FOR},
{{NULL, "foreach", 0}, FOREACH},
{{NULL, "function", 0}, FUNC},
{{NULL, "if", 0}, IF},
+ {{NULL, "integer", 0}, TYPESET},
+ {{NULL, "local", 0}, TYPESET},
{{NULL, "nocorrect", 0}, NOCORRECT},
+ {{NULL, "readonly", 0}, TYPESET},
{{NULL, "repeat", 0}, REPEAT},
{{NULL, "select", 0}, SELECT},
{{NULL, "then", 0}, THEN},
{{NULL, "time", 0}, TIME},
+ {{NULL, "typeset", 0}, TYPESET},
{{NULL, "until", 0}, UNTIL},
{{NULL, "while", 0}, WHILE},
{{NULL, NULL, 0}, 0}
diff --git a/Src/hist.c b/Src/hist.c
index bd03c4f11..75e809c48 100644
--- a/Src/hist.c
+++ b/Src/hist.c
@@ -136,6 +136,7 @@ mod_export int hist_skip_flags;
#define HA_NOINC (1<<1) /* Don't store, curhist not incremented */
#define HA_INWORD (1<<2) /* We're inside a word, don't add
start and end markers */
+#define HA_UNGET (1<<3) /* Recursively ungetting */
/* Array of word beginnings and endings in current history line. */
@@ -904,8 +905,14 @@ ihungetc(int c)
while (!lexstop && !errflag) {
if (hptr[-1] != (char) c && stophist < 4 &&
- hptr > chline + 1 && hptr[-1] == '\n' && hptr[-2] == '\\')
- hungetc('\n'), hungetc('\\');
+ hptr > chline + 1 && hptr[-1] == '\n' && hptr[-2] == '\\' &&
+ !(histactive & HA_UNGET) &&
+ (inbufflags & (INP_ALIAS|INP_HIST)) != INP_ALIAS) {
+ histactive |= HA_UNGET;
+ hungetc('\n');
+ hungetc('\\');
+ histactive &= ~HA_UNGET;
+ }
if (expanding) {
zlemetacs--;
@@ -2000,7 +2007,7 @@ casemodify(char *str, int how)
VARARR(char, mbstr, MB_CUR_MAX);
mbstate_t ps;
- mb_metacharinit();
+ mb_charinit();
memset(&ps, 0, sizeof(ps));
while (*str) {
wint_t wc;
diff --git a/Src/init.c b/Src/init.c
index 102276a64..22db4b3b2 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -105,6 +105,7 @@ loop(int toplevel, int justonce)
Eprog prog;
int err, non_empty = 0;
+ queue_signals();
pushheap();
if (!toplevel)
zcontext_save();
@@ -218,6 +219,7 @@ loop(int toplevel, int justonce)
if (((!interact || sourcelevel) && errflag) || retflag)
break;
if (isset(SINGLECOMMAND) && toplevel) {
+ dont_queue_signals();
if (sigtrapped[SIGEXIT])
dotrap(SIGEXIT);
exit(lastval);
@@ -229,6 +231,7 @@ loop(int toplevel, int justonce)
if (!toplevel)
zcontext_restore();
popheap();
+ unqueue_signals();
if (err)
return LOOP_ERROR;
@@ -1117,8 +1120,9 @@ setupshin(char *runscript)
exit(127);
}
scriptfilename = sfname;
- zsfree(argzero); /* ztrdup'd in parseargs */
- argzero = runscript;
+ sfname = argzero; /* copy to avoid race condition */
+ argzero = ztrdup(runscript);
+ zsfree(sfname); /* argzero ztrdup'd in parseargs */
}
/*
* We only initialise line numbering once there is a script to
@@ -1425,7 +1429,7 @@ sourcehome(char *s)
char *h;
queue_signals();
- if (EMULATION(EMULATE_SH|EMULATE_KSH) || !(h = getsparam("ZDOTDIR"))) {
+ if (EMULATION(EMULATE_SH|EMULATE_KSH) || !(h = getsparam_u("ZDOTDIR"))) {
h = home;
if (!h)
return;
diff --git a/Src/input.c b/Src/input.c
index 4a5bf89c6..eb968ea72 100644
--- a/Src/input.c
+++ b/Src/input.c
@@ -141,16 +141,19 @@ shingetline(void)
int c;
char buf[BUFSIZ];
char *p;
+ int q = queue_signal_level();
p = buf;
- winch_unblock();
for (;;) {
+ winch_unblock();
+ dont_queue_signals();
do {
errno = 0;
c = fgetc(bshin);
} while (c < 0 && errno == EINTR);
if (c < 0 || c == '\n') {
winch_block();
+ restore_queue_signals(q);
if (c == '\n')
*p++ = '\n';
if (p > buf) {
@@ -167,12 +170,13 @@ shingetline(void)
*p++ = c;
if (p >= buf + BUFSIZ - 1) {
winch_block();
+ queue_signals();
line = zrealloc(line, ll + (p - buf) + 1);
memcpy(line + ll, buf, p - buf);
ll += p - buf;
line[ll] = '\0';
p = buf;
- winch_unblock();
+ unqueue_signals();
}
}
}
@@ -222,7 +226,8 @@ ingetc(void)
if (inputline())
break;
}
- zshlex_raw_add(lastc);
+ if (!lexstop)
+ zshlex_raw_add(lastc);
return lastc;
}
@@ -376,6 +381,8 @@ inputline(void)
static void
inputsetline(char *str, int flags)
{
+ queue_signals();
+
if ((inbufflags & INP_FREE) && inbuf) {
free(inbuf);
}
@@ -393,6 +400,8 @@ inputsetline(char *str, int flags)
else
inbufct = inbufleft;
inbufflags = flags;
+
+ unqueue_signals();
}
/*
@@ -591,7 +600,7 @@ inpoptop(void)
* history is before, but they're both pushed onto
* the input stack.
*/
- if ((inbufflags & (INP_ALIAS|INP_HIST)) == INP_ALIAS)
+ if ((inbufflags & (INP_ALIAS|INP_HIST|INP_RAW_KEEP)) == INP_ALIAS)
zshlex_raw_back();
}
}
diff --git a/Src/jobs.c b/Src/jobs.c
index 948f61b01..b47ba8c60 100644
--- a/Src/jobs.c
+++ b/Src/jobs.c
@@ -1179,7 +1179,7 @@ addfilelist(const char *name, int fd)
/**/
void
-pipecleanfilelist(LinkList filelist)
+pipecleanfilelist(LinkList filelist, int proc_subst_only)
{
LinkNode node;
@@ -1188,7 +1188,12 @@ pipecleanfilelist(LinkList filelist)
node = firstnode(filelist);
while (node) {
Jobfile jf = (Jobfile)getdata(node);
- if (jf->is_fd) {
+ if (jf->is_fd &&
+ (!proc_subst_only
+#ifdef FDT_PROC_SUBST
+ || fdtable[jf->u.fd] == FDT_PROC_SUBST
+#endif
+ )) {
LinkNode next = nextnode(node);
zclose(jf->u.fd);
(void)remnode(filelist, node);
@@ -1379,10 +1384,17 @@ waitforpid(pid_t pid, int wait_cmd)
dont_queue_signals();
child_block(); /* unblocked in signal_suspend() */
queue_traps(wait_cmd);
+
+ /* This function should never be called with a pid that is not a
+ * child of the current shell. Consequently, if kill(0, pid)
+ * fails here with ESRCH, the child has already been reaped. In
+ * the loop body, we expect this to happen in signal_suspend()
+ * via zhandler(), after which this test terminates the loop.
+ */
while (!errflag && (kill(pid, 0) >= 0 || errno != ESRCH)) {
if (first)
first = 0;
- else
+ else if (!wait_cmd)
kill(pid, SIGCONT);
last_signal = -1;
@@ -1415,9 +1427,9 @@ zwaitjob(int job, int wait_cmd)
int q = queue_signal_level();
Job jn = jobtab + job;
- dont_queue_signals();
child_block(); /* unblocked during signal_suspend() */
queue_traps(wait_cmd);
+ dont_queue_signals();
if (jn->procs || jn->auxprocs) { /* if any forks were done */
jn->stat |= STAT_LOCKED;
if (jn->stat & STAT_CHANGED)
@@ -1433,7 +1445,7 @@ zwaitjob(int job, int wait_cmd)
* we can't deadlock on the fact that those still exist, so
* that's not a problem.
*/
- pipecleanfilelist(jn->filelist);
+ pipecleanfilelist(jn->filelist, 0);
}
while (!errflag && jn->stat &&
!(jn->stat & STAT_DONE) &&
@@ -1473,9 +1485,9 @@ zwaitjob(int job, int wait_cmd)
pipestats[0] = lastval;
numpipestats = 1;
}
+ restore_queue_signals(q);
unqueue_traps();
child_unblock();
- restore_queue_signals(q);
return 0;
}
@@ -1623,7 +1635,7 @@ spawnjob(void)
deletejob(jobtab + thisjob, 0);
else {
jobtab[thisjob].stat |= STAT_LOCKED;
- pipecleanfilelist(jobtab[thisjob].filelist);
+ pipecleanfilelist(jobtab[thisjob].filelist, 0);
}
thisjob = -1;
}
diff --git a/Src/lex.c b/Src/lex.c
index 841fb0b86..70f3d142a 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -339,6 +339,7 @@ ctxtlex(void)
incmdpos = 1;
break;
case STRING:
+ case TYPESET:
/* case ENVSTRING: */
case ENVARRAY:
case OUTPAR:
@@ -1182,7 +1183,7 @@ gettokstr(int c, int sub)
c = Outpar;
}
} else if (peek != ENVSTRING &&
- incmdpos && !bct && !brct) {
+ (incmdpos || intypeset) && !bct && !brct) {
char *t = tokstr;
if (idigit(*t))
while (++t < lexbuf.ptr && idigit(*t));
@@ -1200,7 +1201,7 @@ gettokstr(int c, int sub)
t++;
if (t == lexbuf.ptr) {
e = hgetc();
- if (e == '(' && incmdpos) {
+ if (e == '(') {
*lexbuf.ptr = '\0';
return ENVARRAY;
}
@@ -1387,7 +1388,7 @@ dquote_parse(char endchar, int sub)
{
int pct = 0, brct = 0, bct = 0, intick = 0, err = 0;
int c;
- int math = endchar == ')' || endchar == ']';
+ int math = endchar == ')' || endchar == ']' || infor;
int zlemath = math && zlemetacs > zlemetall + addedx - inbufct;
while (((c = hgetc()) != endchar || bct ||
@@ -1994,8 +1995,10 @@ skipcomm(void)
#else
char *new_tokstr;
int new_lexstop, new_lex_add_raw;
+ int save_infor = infor;
struct lexbufstate new_lexbuf;
+ infor = 0;
cmdpush(CS_CMDSUBST);
SETPARBEGIN
add(Inpar);
@@ -2020,6 +2023,18 @@ skipcomm(void)
new_tokstr = tokstr;
new_lexbuf = lexbuf;
+ /*
+ * If we're expanding an alias at this point, we need the whole
+ * remaining text as part of the string for the command in
+ * parentheses, so don't backtrack. This is different from the
+ * usual case where the alias is fully within the command, where
+ * we want the unexpanded text so that it will be expanded
+ * again when the command in the parentheses is executed.
+ *
+ * I never wanted to be a software engineer, you know.
+ */
+ if (inbufflags & INP_ALIAS)
+ inbufflags |= INP_RAW_KEEP;
zcontext_save_partial(ZCONTEXT_LEX|ZCONTEXT_PARSE);
hist_in_word(1);
} else {
@@ -2052,6 +2067,7 @@ skipcomm(void)
* the recursive parsing.
*/
lexflags &= ~LEXFLAGS_ZLE;
+ dbparens = 0; /* restored by zcontext_restore_partial() */
if (!parse_event(OUTPAR) || tok != OUTPAR)
lexstop = 1;
@@ -2098,6 +2114,7 @@ skipcomm(void)
if (!lexstop)
SETPAREND
cmdpop();
+ infor = save_infor;
return lexstop;
#endif
diff --git a/Src/loop.c b/Src/loop.c
index e4e8e2df8..4def9b652 100644
--- a/Src/loop.c
+++ b/Src/loop.c
@@ -56,6 +56,10 @@ execfor(Estate state, int do_exec)
char *name, *str, *cond = NULL, *advance = NULL;
zlong val = 0;
LinkList vars = NULL, args = NULL;
+ int old_simple_pline = simple_pline;
+
+ /* See comments in execwhile() */
+ simple_pline = 1;
end = state->pc + WC_FOR_SKIP(code);
@@ -69,10 +73,12 @@ execfor(Estate state, int do_exec)
fprintf(xtrerr, "%s\n", str2);
fflush(xtrerr);
}
- if (!errflag)
+ if (!errflag) {
matheval(str);
+ }
if (errflag) {
state->pc = end;
+ simple_pline = old_simple_pline;
return 1;
}
cond = ecgetstr(state, EC_NODUP, &ctok);
@@ -85,12 +91,14 @@ execfor(Estate state, int do_exec)
if (!(args = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok))) {
state->pc = end;
+ simple_pline = old_simple_pline;
return 0;
}
if (htok) {
execsubst(args);
if (errflag) {
state->pc = end;
+ simple_pline = old_simple_pline;
return 1;
}
}
@@ -198,6 +206,7 @@ execfor(Estate state, int do_exec)
popheap();
cmdpop();
loops--;
+ simple_pline = old_simple_pline;
state->pc = end;
return lastval;
}
@@ -214,6 +223,10 @@ execselect(Estate state, UNUSED(int do_exec))
FILE *inp;
size_t more;
LinkList args;
+ int old_simple_pline = simple_pline;
+
+ /* See comments in execwhile() */
+ simple_pline = 1;
end = state->pc + WC_FOR_SKIP(code);
name = ecgetstr(state, EC_NODUP, NULL);
@@ -229,18 +242,21 @@ execselect(Estate state, UNUSED(int do_exec))
if (!(args = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok))) {
state->pc = end;
+ simple_pline = old_simple_pline;
return 0;
}
if (htok) {
execsubst(args);
if (errflag) {
state->pc = end;
+ simple_pline = old_simple_pline;
return 1;
}
}
}
if (!args || empty(args)) {
state->pc = end;
+ simple_pline = old_simple_pline;
return 0;
}
loops++;
@@ -315,6 +331,7 @@ execselect(Estate state, UNUSED(int do_exec))
popheap();
fclose(inp);
loops--;
+ simple_pline = old_simple_pline;
state->pc = end;
return lastval;
}
@@ -382,6 +399,7 @@ execwhile(Estate state, UNUSED(int do_exec))
Wordcode end, loop;
wordcode code = state->pc[-1];
int olderrexit, oldval, isuntil = (WC_WHILE_TYPE(code) == WC_WHILE_UNTIL);
+ int old_simple_pline = simple_pline;
end = state->pc + WC_WHILE_SKIP(code);
olderrexit = noerrexit;
@@ -396,8 +414,6 @@ execwhile(Estate state, UNUSED(int do_exec))
/* This is an empty loop. Make sure the signal handler sets the
* flags and then just wait for someone hitting ^C. */
- int old_simple_pline = simple_pline;
-
simple_pline = 1;
while (!breaks)
@@ -409,7 +425,14 @@ execwhile(Estate state, UNUSED(int do_exec))
for (;;) {
state->pc = loop;
noerrexit = 1;
+
+ /* In case the test condition is a functional no-op,
+ * make sure signal handlers recognize ^C to end the loop. */
+ simple_pline = 1;
+
execlist(state, 1, 0);
+
+ simple_pline = old_simple_pline;
noerrexit = olderrexit;
if (!((lastval == 0) ^ isuntil)) {
if (breaks)
@@ -421,7 +444,14 @@ execwhile(Estate state, UNUSED(int do_exec))
lastval = oldval;
break;
}
+
+ /* In case the loop body is also a functional no-op,
+ * make sure signal handlers recognize ^C as above. */
+ simple_pline = 1;
+
execlist(state, 1, 0);
+
+ simple_pline = old_simple_pline;
if (breaks) {
breaks--;
if (breaks || !contflag)
@@ -452,6 +482,10 @@ execrepeat(Estate state, UNUSED(int do_exec))
wordcode code = state->pc[-1];
int count, htok = 0;
char *tmp;
+ int old_simple_pline = simple_pline;
+
+ /* See comments in execwhile() */
+ simple_pline = 1;
end = state->pc + WC_REPEAT_SKIP(code);
@@ -484,6 +518,7 @@ execrepeat(Estate state, UNUSED(int do_exec))
cmdpop();
popheap();
loops--;
+ simple_pline = old_simple_pline;
state->pc = end;
return lastval;
}
diff --git a/Src/math.c b/Src/math.c
index 97a97b32b..977e92345 100644
--- a/Src/math.c
+++ b/Src/math.c
@@ -407,6 +407,13 @@ mathevall(char *s, enum prec_type prec_tp, char **ep)
stack[0].val.type = MN_INTEGER;
stack[0].val.u.l = 0;
mathparse(prec_tp == MPREC_TOP ? TOPPREC : ARGPREC);
+ /*
+ * Internally, we parse the contents of parentheses at top
+ * precedence... so we can return a parenthesis here if
+ * there are too many at the end.
+ */
+ if (mtok == M_OUTPAR && !errflag)
+ zerr("bad math expression: unexpected ')'");
*ep = ptr;
DPUTS(!errflag && sp > 0,
"BUG: math: wallabies roaming too freely in outback");
@@ -791,7 +798,7 @@ zzlex(void)
ptr++;
if (!*ptr) {
- zerr("character missing after ##");
+ zerr("bad math expression: character missing after ##");
return EOI;
}
ptr = getkeystring(ptr, NULL, GETKEYS_MATH, &v);
@@ -914,7 +921,7 @@ setmathvar(struct mathvalue *mvp, mnumber v)
mvp->pval = NULL;
}
if (!mvp->lval) {
- zerr("lvalue required");
+ zerr("bad math expression: lvalue required");
v.type = MN_INTEGER;
v.u.l = 0;
return v;
@@ -1256,7 +1263,7 @@ op(int what)
/* Error if (-num ** b) and b is not an integer */
double tst = (double)(zlong)b.u.d;
if (tst != b.u.d) {
- zerr("imaginary power");
+ zerr("bad math expression: imaginary power");
return;
}
}
@@ -1338,7 +1345,7 @@ op(int what)
push(((a.type & MN_FLOAT) ? a.u.d : a.u.l) ? b : c, NULL, 0);
break;
case COLON:
- zerr("':' without '?'");
+ zerr("bad math expression: ':' without '?'");
break;
case PREPLUS:
if (spval->type & MN_FLOAT)
@@ -1355,7 +1362,7 @@ op(int what)
setmathvar(stack + sp, *spval);
break;
default:
- zerr("out of integers");
+ zerr("bad math expression: out of integers");
return;
}
}
@@ -1525,7 +1532,7 @@ mathparse(int pc)
mathparse(TOPPREC);
if (mtok != M_OUTPAR) {
if (!errflag)
- zerr("')' expected");
+ zerr("bad math expression: ')' expected");
return;
}
break;
@@ -1543,7 +1550,7 @@ mathparse(int pc)
noeval--;
if (mtok != COLON) {
if (!errflag)
- zerr("':' expected");
+ zerr("bad math expression: ':' expected");
return;
}
if (q)
diff --git a/Src/options.c b/Src/options.c
index 3e3e07474..1fb102f1d 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -81,6 +81,7 @@ static struct optname optns[] = {
{{NULL, "allexport", OPT_EMULATE}, ALLEXPORT},
{{NULL, "alwayslastprompt", OPT_ALL}, ALWAYSLASTPROMPT},
{{NULL, "alwaystoend", 0}, ALWAYSTOEND},
+{{NULL, "appendcreate", OPT_EMULATE|OPT_BOURNE}, APPENDCREATE},
{{NULL, "appendhistory", OPT_ALL}, APPENDHISTORY},
{{NULL, "autocd", OPT_EMULATE}, AUTOCD},
{{NULL, "autocontinue", 0}, AUTOCONTINUE},
@@ -172,7 +173,7 @@ static struct optname optns[] = {
{{NULL, "kshautoload", OPT_EMULATE|OPT_BOURNE}, KSHAUTOLOAD},
{{NULL, "kshglob", OPT_EMULATE|OPT_KSH}, KSHGLOB},
{{NULL, "kshoptionprint", OPT_EMULATE|OPT_KSH}, KSHOPTIONPRINT},
-{{NULL, "kshtypeset", OPT_EMULATE|OPT_KSH}, KSHTYPESET},
+{{NULL, "kshtypeset", 0}, KSHTYPESET},
{{NULL, "kshzerosubscript", 0}, KSHZEROSUBSCRIPT},
{{NULL, "listambiguous", OPT_ALL}, LISTAMBIGUOUS},
{{NULL, "listbeep", OPT_ALL}, LISTBEEP},
@@ -192,7 +193,7 @@ static struct optname optns[] = {
{{NULL, "monitor", OPT_SPECIAL}, MONITOR},
{{NULL, "multibyte",
#ifdef MULTIBYTE_SUPPORT
- OPT_EMULATE|OPT_ZSH|OPT_CSH|OPT_KSH
+ OPT_ALL
#else
0
#endif
diff --git a/Src/params.c b/Src/params.c
index 98541a6da..00f43e47d 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -196,7 +196,7 @@ static const struct gsu_integer ttyidle_gsu =
{ ttyidlegetfn, nullintsetfn, stdunsetfn };
static const struct gsu_scalar argzero_gsu =
-{ argzerogetfn, nullstrsetfn, nullunsetfn };
+{ argzerogetfn, argzerosetfn, nullunsetfn };
static const struct gsu_scalar username_gsu =
{ usernamegetfn, usernamesetfn, stdunsetfn };
static const struct gsu_scalar dash_gsu =
@@ -1116,14 +1116,12 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w,
Patprog pprog = NULL;
/*
- * If in NO_EXEC mode, the parameters won't be set up
- * properly, so there's no point even doing any sanity checking.
- * Just return 0 now.
+ * If in NO_EXEC mode, the parameters won't be set up properly,
+ * so just pretend everything is a hash for subscript parsing
*/
- if (unset(EXECOPT))
- return 0;
- ishash = (v->pm && PM_TYPE(v->pm->node.flags) == PM_HASHED);
+ ishash = (unset(EXECOPT) ||
+ (v->pm && PM_TYPE(v->pm->node.flags) == PM_HASHED));
if (prevcharlen)
*prevcharlen = 1;
if (nextcharlen)
@@ -1278,8 +1276,18 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w,
}
if (!c)
return 0;
- s = dupstrpfx(s, t - s);
*str = tt = t;
+
+ /*
+ * If in NO_EXEC mode, the parameters won't be set up properly,
+ * so there's no additional sanity checking we can do.
+ * Just return 0 now.
+ */
+ if (unset(EXECOPT))
+ return 0;
+
+ s = dupstrpfx(s, t - s);
+
/* If we're NOT reverse subscripting, strip the inull()s so brackets *
* are not backslashed after parsestr(). Otherwise leave them alone *
* so that the brackets will be escaped when we patcompile() or when *
@@ -2631,6 +2639,15 @@ getsparam(char *s)
return getstrvalue(v);
}
+/**/
+mod_export char *
+getsparam_u(char *s)
+{
+ if ((s = getsparam(s)))
+ return unmetafy(s, NULL);
+ return s;
+}
+
/* Retrieve an array parameter */
/**/
@@ -3963,7 +3980,7 @@ setlang(char *x)
struct localename *ln;
char *x2;
- if ((x2 = getsparam("LC_ALL")) && *x2)
+ if ((x2 = getsparam_u("LC_ALL")) && *x2)
return;
/*
@@ -3977,10 +3994,10 @@ setlang(char *x)
* from this is meaningless. So just all $LANG to show through in
* that case.
*/
- setlocale(LC_ALL, x ? x : "");
+ setlocale(LC_ALL, x ? unmeta(x) : "");
queue_signals();
for (ln = lc_names; ln->name; ln++)
- if ((x = getsparam(ln->name)) && *x)
+ if ((x = getsparam_u(ln->name)) && *x)
setlocale(ln->category, x);
unqueue_signals();
}
@@ -3996,7 +4013,7 @@ lc_allsetfn(Param pm, char *x)
* that with any LC_* that are set.
*/
if (!x || !*x) {
- x = getsparam("LANG");
+ x = getsparam_u("LANG");
if (x && *x) {
queue_signals();
setlang(x);
@@ -4004,7 +4021,7 @@ lc_allsetfn(Param pm, char *x)
}
}
else
- setlocale(LC_ALL, x);
+ setlocale(LC_ALL, unmeta(x));
}
/**/
@@ -4012,7 +4029,7 @@ void
langsetfn(Param pm, char *x)
{
strsetfn(pm, x);
- setlang(x);
+ setlang(unmeta(x));
}
/**/
@@ -4038,12 +4055,27 @@ lcsetfn(Param pm, char *x)
if (x && *x) {
for (ln = lc_names; ln->name; ln++)
if (!strcmp(ln->name, pm->node.nam))
- setlocale(ln->category, x);
+ setlocale(ln->category, unmeta(x));
}
unqueue_signals();
}
#endif /* USE_LOCALE */
+/* Function to set value for special parameter `0' */
+
+/**/
+static void
+argzerosetfn(UNUSED(Param pm), char *x)
+{
+ if (x) {
+ if (!isset(POSIXARGZERO)) {
+ zsfree(argzero);
+ argzero = ztrdup(x);
+ }
+ zsfree(x);
+ }
+}
+
/* Function to get value for special parameter `0' */
/**/
@@ -5076,8 +5108,10 @@ printparamvalue(Param p, int printflags)
break;
case PM_ARRAY:
/* array */
- if (!(printflags & PRINT_KV_PAIR))
+ if (!(printflags & PRINT_KV_PAIR)) {
putchar('(');
+ putchar(' ');
+ }
u = p->gsu.a->getfn(p);
if(*u) {
quotedzputs(*u++, stdout);
@@ -5086,13 +5120,17 @@ printparamvalue(Param p, int printflags)
quotedzputs(*u++, stdout);
}
}
- if (!(printflags & PRINT_KV_PAIR))
+ if (!(printflags & PRINT_KV_PAIR)) {
+ putchar(' ');
putchar(')');
+ }
break;
case PM_HASHED:
/* association */
- if (!(printflags & PRINT_KV_PAIR))
+ if (!(printflags & PRINT_KV_PAIR)) {
putchar('(');
+ putchar(' ');
+ }
{
HashTable ht = p->gsu.h->getfn(p);
if (ht)
diff --git a/Src/parse.c b/Src/parse.c
index c932851d9..7c2d20250 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -63,6 +63,11 @@ int isnewlin;
/**/
int infor;
+/* != 0 if parsing arguments of typeset etc. */
+
+/**/
+mod_export int intypeset;
+
/* list of here-documents */
/**/
@@ -118,11 +123,20 @@ struct heredocs *hdocs;
* WC_ASSIGN
* - data contains type (scalar, array) and number of array-elements
* - followed by name and value
+ * Note variant for WC_TYPESET assignments: WC_ASSIGN_INC indicates
+ * a name with no equals, not an =+ which isn't valid here.
*
* WC_SIMPLE
* - data contains the number of arguments (plus command)
* - followed by strings
*
+ * WC_TYPESET
+ * Variant of WC_SIMPLE used when TYPESET reserved word found.
+ * - data contains the number of string arguments (plus command)
+ * - followed by strings
+ * - followed by number of assignments
+ * - followed by assignments if non-zero number.
+ *
* WC_SUBSH
* - data unused
* - followed by list
@@ -257,6 +271,7 @@ parse_context_save(struct parse_stack *ps, int toplevel)
ps->incasepat = incasepat;
ps->isnewlin = isnewlin;
ps->infor = infor;
+ ps->intypeset = intypeset;
ps->hdocs = hdocs;
ps->eclen = eclen;
@@ -290,6 +305,7 @@ parse_context_restore(const struct parse_stack *ps, int toplevel)
incasepat = ps->incasepat;
isnewlin = ps->isnewlin;
infor = ps->infor;
+ intypeset = ps->intypeset;
hdocs = ps->hdocs;
eclen = ps->eclen;
@@ -430,7 +446,7 @@ init_parse_status(void)
* between the two it's a bit ambiguous. We clear them when
* using the lexical analyser for strings as well as here.
*/
- incasepat = incond = inredir = infor = 0;
+ incasepat = incond = inredir = infor = intypeset = 0;
incmdpos = 1;
}
@@ -440,6 +456,8 @@ init_parse_status(void)
void
init_parse(void)
{
+ queue_signals();
+
if (ecbuf) zfree(ecbuf, eclen);
ecbuf = (Wordcode) zalloc((eclen = EC_INIT_SIZE) * sizeof(wordcode));
@@ -450,6 +468,8 @@ init_parse(void)
ecnfunc = 0;
init_parse_status();
+
+ unqueue_signals();
}
/* Build eprog. */
@@ -472,6 +492,8 @@ bld_eprog(int heap)
Eprog ret;
int l;
+ queue_signals();
+
ecadd(WCB_END());
ret = heap ? (Eprog) zhalloc(sizeof(*ret)) : (Eprog) zalloc(sizeof(*ret));
@@ -495,6 +517,8 @@ bld_eprog(int heap)
zfree(ecbuf, eclen);
ecbuf = NULL;
+ unqueue_signals();
+
return ret;
}
@@ -992,6 +1016,7 @@ par_cmd(int *cmplx, int zsh_construct)
incmdpos = 1;
incasepat = 0;
incond = 0;
+ intypeset = 0;
return 1;
}
@@ -1575,9 +1600,9 @@ par_funcdef(int *cmplx)
p = ecadd(0);
ecadd(0);
- incmdpos = 1;
while (tok == STRING) {
- if (*tokstr == Inbrace && !tokstr[1]) {
+ if ((*tokstr == Inbrace || *tokstr == '{') &&
+ !tokstr[1]) {
tok = INBRACE;
break;
}
@@ -1590,6 +1615,7 @@ par_funcdef(int *cmplx)
ecadd(0);
nocorrect = 0;
+ incmdpos = 1;
if (tok == INOUTPAR)
zshlex();
while (tok == SEPER)
@@ -1709,7 +1735,8 @@ static int
par_simple(int *cmplx, int nr)
{
int oecused = ecused, isnull = 1, r, argc = 0, p, isfunc = 0, sr = 0;
- int c = *cmplx, nrediradd, assignments = 0;
+ int c = *cmplx, nrediradd, assignments = 0, ppost = 0, is_typeset = 0;
+ wordcode postassigns = 0;
r = ecused;
for (;;) {
@@ -1717,31 +1744,32 @@ par_simple(int *cmplx, int nr)
*cmplx = c = 1;
nocorrect = 1;
} else if (tok == ENVSTRING) {
- char *p, *name, *str;
+ char *ptr, *name, *str;
name = tokstr;
- for (p = tokstr; *p && *p != Inbrack && *p != '=' && *p != '+';
- p++);
- if (*p == Inbrack) skipparens(Inbrack, Outbrack, &p);
- if (*p == '+') {
- *p++ = '\0';
+ for (ptr = tokstr;
+ *ptr && *ptr != Inbrack && *ptr != '=' && *ptr != '+';
+ ptr++);
+ if (*ptr == Inbrack) skipparens(Inbrack, Outbrack, &ptr);
+ if (*ptr == '+') {
+ *ptr++ = '\0';
ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_INC, 0));
} else
ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_NEW, 0));
-
- if (*p == '=') {
- *p = '\0';
- str = p + 1;
+
+ if (*ptr == '=') {
+ *ptr = '\0';
+ str = ptr + 1;
} else
equalsplit(tokstr, &str);
- for (p = str; *p; p++) {
+ for (ptr = str; *ptr; ptr++) {
/*
* We can't treat this as "simple" if it contains
* expansions that require process subsitution, since then
* we need process handling.
*/
- if (p[1] == Inpar &&
- (*p == Equals || *p == Inang || *p == OutangProc)) {
+ if (ptr[1] == Inpar &&
+ (*ptr == Equals || *ptr == Inang || *ptr == OutangProc)) {
*cmplx = 1;
break;
}
@@ -1786,14 +1814,18 @@ par_simple(int *cmplx, int nr)
p = ecadd(WCB_SIMPLE(0));
for (;;) {
- if (tok == STRING) {
+ if (tok == STRING || tok == TYPESET) {
int redir_var = 0;
*cmplx = 1;
incmdpos = 0;
+ if (tok == TYPESET)
+ intypeset = is_typeset = 1;
+
if (!isset(IGNOREBRACES) && *tokstr == Inbrace)
{
+ /* Look for redirs of the form {var}>file etc. */
char *eptr = tokstr + strlen(tokstr) - 1;
char *ptr = eptr;
@@ -1824,15 +1856,74 @@ par_simple(int *cmplx, int nr)
if (!redir_var)
{
- ecstr(tokstr);
- argc++;
+ if (postassigns) {
+ /*
+ * We're in the variable part of a typeset,
+ * but this doesn't have an assignment.
+ * We'll parse it as if it does, but mark
+ * it specially with WC_ASSIGN_INC.
+ */
+ postassigns++;
+ ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_INC, 0));
+ ecstr(tokstr);
+ ecstr(""); /* TBD can possibly optimise out */
+ } else {
+ ecstr(tokstr);
+ argc++;
+ }
zshlex();
}
} else if (IS_REDIROP(tok)) {
*cmplx = c = 1;
nrediradd = par_redir(&r, NULL);
p += nrediradd;
+ if (ppost)
+ ppost += nrediradd;
sr += nrediradd;
+ } else if (tok == ENVSTRING) {
+ char *ptr, *name, *str;
+
+ if (!postassigns++)
+ ppost = ecadd(0);
+
+ name = tokstr;
+ for (ptr = tokstr; *ptr && *ptr != Inbrack && *ptr != '=' && *ptr != '+';
+ ptr++);
+ if (*ptr == Inbrack) skipparens(Inbrack, Outbrack, &ptr);
+ ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_NEW, 0));
+
+ if (*ptr == '=') {
+ *ptr = '\0';
+ str = ptr + 1;
+ } else
+ equalsplit(tokstr, &str);
+ ecstr(name);
+ ecstr(str);
+ zshlex();
+ } else if (tok == ENVARRAY) {
+ int n, parr;
+
+ if (!postassigns++)
+ ppost = ecadd(0);
+
+ parr = ecadd(0);
+ ecstr(tokstr);
+ cmdpush(CS_ARRAY);
+ /*
+ * Careful here: this must be the typeset case,
+ * but we need to tell the lexer not to look
+ * for assignments until we've finished the
+ * present one.
+ */
+ intypeset = 0;
+ zshlex();
+ n = par_nl_wordlist();
+ ecbuf[parr] = WCB_ASSIGN(WC_ASSIGN_ARRAY, WC_ASSIGN_NEW, n);
+ cmdpop();
+ intypeset = 1;
+ if (tok != OUTPAR)
+ YYERROR(oecused);
+ zshlex();
} else if (tok == INOUTPAR) {
zlong oldlineno = lineno;
int onp, so, oecssub = ecssub;
@@ -1841,7 +1932,7 @@ par_simple(int *cmplx, int nr)
if (!isset(MULTIFUNCDEF) && argc > 1)
YYERROR(oecused);
/* Error if preceding assignments */
- if (assignments)
+ if (assignments || postassigns)
YYERROR(oecused);
*cmplx = c;
@@ -1947,9 +2038,18 @@ par_simple(int *cmplx, int nr)
return 0;
}
incmdpos = 1;
+ intypeset = 0;
- if (!isfunc)
- ecbuf[p] = WCB_SIMPLE(argc);
+ if (!isfunc) {
+ if (is_typeset) {
+ ecbuf[p] = WCB_TYPESET(argc);
+ if (postassigns)
+ ecbuf[ppost] = postassigns;
+ else
+ ecadd(0);
+ } else
+ ecbuf[p] = WCB_SIMPLE(argc);
+ }
return sr + 1;
}
diff --git a/Src/pattern.c b/Src/pattern.c
index 4e5e8a110..7d38988a0 100644
--- a/Src/pattern.c
+++ b/Src/pattern.c
@@ -520,6 +520,8 @@ patcompile(char *exp, int inflags, char **endexp)
char *lng, *strp = NULL;
Patprog p;
+ queue_signals();
+
startoff = sizeof(struct patprog);
/* Ensure alignment of start of program string */
startoff = (startoff + sizeof(union upat) - 1) & ~(sizeof(union upat) - 1);
@@ -582,8 +584,10 @@ patcompile(char *exp, int inflags, char **endexp)
if (!strp || (*strp && *strp != '/')) {
/* No, do normal compilation. */
strp = NULL;
- if (patcompswitch(0, &flags) == 0)
+ if (patcompswitch(0, &flags) == 0) {
+ unqueue_signals();
return NULL;
+ }
} else {
/*
* Yes, copy the string, and skip compilation altogether.
@@ -715,6 +719,8 @@ patcompile(char *exp, int inflags, char **endexp)
if (endexp)
*endexp = patparse;
+
+ unqueue_signals();
return p;
}
@@ -1113,8 +1119,8 @@ range_type(char *start, int len)
const char **csp;
for (csp = colon_stuffs; *csp; csp++) {
- if (!strncmp(start, *csp, len))
- return (csp - colon_stuffs) + PP_FIRST;
+ if (strlen(*csp) == len && !strncmp(start, *csp, len))
+ return (csp - colon_stuffs) + PP_FIRST;
}
return PP_UNKWN;
@@ -2202,20 +2208,15 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalen,
if ((patglobflags & GF_MATCHREF) &&
!(patflags & PAT_FILE)) {
char *str = ztrduppfx(patinstart, patinlen);
- char *ptr = patinstart;
- int mlen = 0;
+ int mlen;
/*
* Count the characters. We're not using CHARSUB()
- * because the string is still metafied. We're
- * not using mb_metastrlen() because that expects
- * the string to be null terminated.
+ * because the string is still metafied.
*/
MB_METACHARINIT();
- while (ptr < patinstart + patinlen) {
- mlen++;
- ptr += MB_METACHARLEN(ptr);
- }
+ mlen = MB_METASTRLEN2END(patinstart, 0,
+ patinstart + patinlen);
setsparam("MATCH", str);
setiparam("MBEGIN",
diff --git a/Src/prompt.c b/Src/prompt.c
index ffc1d0df2..be067ee7e 100644
--- a/Src/prompt.c
+++ b/Src/prompt.c
@@ -271,7 +271,7 @@ static int
putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
{
char *ss, *hostnam;
- int t0, arg, test, sep, j, numjobs;
+ int t0, arg, test, sep, j, numjobs, len;
struct tm *tm;
struct timezone dummy_tz;
struct timeval tv;
@@ -673,12 +673,14 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
*/
for(j = 0, t0 = strlen(tmfmt)*8; j < 3; j++, t0*=2) {
addbufspc(t0);
- if (ztrftime(bv->bp, t0, tmfmt, tm, tv.tv_usec) >= 0)
+ if ((len = ztrftime(bv->bp, t0, tmfmt, tm, tv.tv_usec))
+ >= 0)
break;
}
/* There is enough room for this because addbufspc(t0)
* allocates room for t0 * 2 bytes. */
- metafy(bv->bp, -1, META_NOALLOC);
+ if (len >= 0)
+ metafy(bv->bp, len, META_NOALLOC);
bv->bp += strlen(bv->bp);
zsfree(tmbuf);
break;
@@ -964,7 +966,7 @@ stradd(char *d)
/* FALL THROUGH */
default:
/* Take full wide character in one go */
- mb_metacharinit();
+ mb_charinit();
pc = wcs_nicechar(cc, NULL, NULL);
break;
}
diff --git a/Src/signals.c b/Src/signals.c
index 3950ad1a2..f45c1860c 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -487,6 +487,12 @@ wait_for_processes(void)
break;
}
+ /* This is necessary to be sure queueing_enabled > 0 when
+ * we enter printjob() from update_job(), so that we don't
+ * decrement to zero in should_report_time() and improperly
+ * run other handlers in the middle of processing this one */
+ queue_signals();
+
/*
* Find the process and job containing this pid and
* update it.
@@ -528,8 +534,16 @@ wait_for_processes(void)
* and is not equal to the current foreground job.
*/
if (jn && !(jn->stat & (STAT_CURSH|STAT_BUILTIN)) &&
- jn - jobtab != thisjob)
- addbgstatus(pid, (int)lastval2);
+ jn - jobtab != thisjob) {
+ int val = (WIFSIGNALED(status) ?
+ 0200 | WTERMSIG(status) :
+ (WIFSTOPPED(status) ?
+ 0200 | WEXITSTATUS(status) :
+ WEXITSTATUS(status)));
+ addbgstatus(pid, val);
+ }
+
+ unqueue_signals();
}
}
@@ -1207,6 +1221,8 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
}
}
+ queue_signals(); /* Any time we manage memory or global state */
+
intrap++;
*sigtr |= ZSIG_IGNORED;
@@ -1244,7 +1260,7 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
sfcontext = SFC_SIGNAL;
incompfunc = 0;
- doshfunc((Shfunc)sigfn, args, 1);
+ doshfunc((Shfunc)sigfn, args, 1); /* manages signal queueing */
sfcontext = osc;
incompfunc= old_incompfunc;
freelinklist(args, (FreeFunc) NULL);
@@ -1254,7 +1270,7 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
trap_state = TRAP_STATE_PRIMED;
trapisfunc = isfunc = 0;
- execode((Eprog)sigfn, 1, 0, "trap");
+ execode((Eprog)sigfn, 1, 0, "trap"); /* manages signal queueing */
}
runhookdef(AFTERTRAPHOOK, NULL);
@@ -1321,6 +1337,8 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
if (*sigtr != ZSIG_IGNORED)
*sigtr &= ~ZSIG_IGNORED;
intrap--;
+
+ unqueue_signals();
}
/* Standard call to execute a trap for a given signal. */
diff --git a/Src/subst.c b/Src/subst.c
index 81d34d28a..021d23444 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -3834,8 +3834,14 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags)
y = dupstring(nulstring);
insertlinknode(l, n, (void *) y), incnode(n);
}
- if (eval)
- n = on;
+ /* This used to omit restoring of *str and instead test
+ * if (eval)
+ * n = on;
+ * but that causes strange behavior of history modifiers when
+ * applied across all values of an array. What is magic about
+ * eval here that *str seemed not to need restoring?
+ */
+ *str = getdata(n = on);
} else {
/*
* Scalar value. Handle last minute transformations
diff --git a/Src/text.c b/Src/text.c
index 958303c68..3978a26a9 100644
--- a/Src/text.c
+++ b/Src/text.c
@@ -30,6 +30,16 @@
#include "zsh.mdh"
#include "text.pro"
+/*
+ * If non-zero, expand syntactically significant leading tabs in text
+ * to this number of spaces.
+ *
+ * If negative, don't output leading whitespace at all.
+ */
+
+/**/
+int text_expand_tabs;
+
static char *tptr, *tbuf, *tlim, *tpending;
static int tsiz, tindent, tnewlins, tjob;
@@ -67,7 +77,7 @@ taddpending(char *str1, char *str2)
*/
if (tpending) {
int oldlen = strlen(tpending);
- tpending = realloc(tpending, len + oldlen);
+ tpending = zrealloc(tpending, len + oldlen);
sprintf(tpending + oldlen, "%s%s", str1, str2);
} else {
tpending = (char *)zalloc(len);
@@ -100,7 +110,7 @@ taddchr(int c)
tptr--;
return;
}
- tbuf = realloc(tbuf, tsiz *= 2);
+ tbuf = zrealloc(tbuf, tsiz *= 2);
tlim = tbuf + tsiz;
tptr = tbuf + tsiz / 2;
}
@@ -120,7 +130,7 @@ taddstr(char *s)
if (!tbuf)
return;
- tbuf = realloc(tbuf, tsiz *= 2);
+ tbuf = zrealloc(tbuf, tsiz *= 2);
tlim = tbuf + tsiz;
tptr = tbuf + x;
}
@@ -145,6 +155,48 @@ taddlist(Estate state, int num)
}
}
+/* add an assignment */
+
+static void
+taddassign(wordcode code, Estate state, int typeset)
+{
+ /* name */
+ taddstr(ecgetstr(state, EC_NODUP, NULL));
+ /* value... maybe */
+ if (WC_ASSIGN_TYPE2(code) == WC_ASSIGN_INC) {
+ if (typeset) {
+ /* dummy assignment --- just var name */
+ (void)ecgetstr(state, EC_NODUP, NULL);
+ taddchr(' ');
+ return;
+ }
+ taddchr('+');
+ }
+ taddchr('=');
+ if (WC_ASSIGN_TYPE(code) == WC_ASSIGN_ARRAY) {
+ taddchr('(');
+ taddlist(state, WC_ASSIGN_NUM(code));
+ taddstr(") ");
+ } else {
+ taddstr(ecgetstr(state, EC_NODUP, NULL));
+ taddchr(' ');
+ }
+}
+
+/* add a number of assignments from typeset */
+
+/**/
+static void
+taddassignlist(Estate state, wordcode count)
+{
+ if (count)
+ taddchr(' ');
+ while (count--) {
+ wordcode code = *state->pc++;
+ taddassign(code, state, 1);
+ }
+}
+
/* add a newline, or something equivalent, to the text buffer */
/**/
@@ -156,8 +208,16 @@ taddnl(int no_semicolon)
if (tnewlins) {
tdopending();
taddchr('\n');
- for (t0 = 0; t0 != tindent; t0++)
- taddchr('\t');
+ for (t0 = 0; t0 != tindent; t0++) {
+ if (text_expand_tabs >= 0) {
+ if (text_expand_tabs) {
+ int t1;
+ for (t1 = 0; t1 < text_expand_tabs; t1++)
+ taddchr(' ');
+ } else
+ taddchr('\t');
+ }
+ }
} else if (no_semicolon) {
taddstr(" ");
} else {
@@ -165,6 +225,30 @@ taddnl(int no_semicolon)
}
}
+/*
+ * Output a tab that may be expanded as part of a leading set.
+ * Note this is not part of the text framework; it's for
+ * code that needs to output its own tabs that are to be
+ * consistent with those from getpermtext().
+ *
+ * Note these tabs are only expected to be useful at the
+ * start of the line, so we make no attempt to count columns.
+ */
+
+/**/
+void
+zoutputtab(FILE *outf)
+{
+ if (text_expand_tabs < 0)
+ return;
+ if (text_expand_tabs) {
+ int i;
+ for (i = 0; i < text_expand_tabs; i++)
+ fputc(' ', outf);
+ } else
+ fputc('\t', outf);
+}
+
/* get a permanent textual representation of n */
/**/
@@ -397,22 +481,17 @@ gettext2(Estate state)
}
break;
case WC_ASSIGN:
- taddstr(ecgetstr(state, EC_NODUP, NULL));
- if (WC_ASSIGN_TYPE2(code) == WC_ASSIGN_INC) taddchr('+');
- taddchr('=');
- if (WC_ASSIGN_TYPE(code) == WC_ASSIGN_ARRAY) {
- taddchr('(');
- taddlist(state, WC_ASSIGN_NUM(code));
- taddstr(") ");
- } else {
- taddstr(ecgetstr(state, EC_NODUP, NULL));
- taddchr(' ');
- }
+ taddassign(code, state, 0);
break;
case WC_SIMPLE:
taddlist(state, WC_SIMPLE_ARGC(code));
stack = 1;
break;
+ case WC_TYPESET:
+ taddlist(state, WC_TYPESET_ARGC(code));
+ taddassignlist(state, *state->pc++);
+ stack = 1;
+ break;
case WC_SUBSH:
if (!s) {
taddstr("(");
@@ -602,7 +681,7 @@ gettext2(Estate state)
case WC_CASE:
if (!s) {
Wordcode end = state->pc + WC_CASE_SKIP(code);
- wordcode nalts;
+ wordcode ialts;
taddstr("case ");
taddstr(ecgetstr(state, EC_NODUP, NULL));
@@ -616,6 +695,7 @@ gettext2(Estate state)
taddstr("esac");
stack = 1;
} else {
+ Wordcode prev_pc;
tindent++;
if (tnewlins)
taddnl(0);
@@ -623,21 +703,23 @@ gettext2(Estate state)
taddchr(' ');
taddstr("(");
code = *state->pc++;
- nalts = *state->pc++;
- while (nalts--) {
+ prev_pc = state->pc++;
+ ialts = *prev_pc;
+ while (ialts--) {
taddstr(ecgetstr(state, EC_NODUP, NULL));
state->pc++;
- if (nalts)
+ if (ialts)
taddstr(" | ");
}
taddstr(") ");
tindent++;
n = tpush(code, 0);
n->u._case.end = end;
- n->pop = (state->pc - 2 + WC_CASE_SKIP(code) >= end);
+ n->pop = (prev_pc + WC_CASE_SKIP(code) >= end);
}
} else if (state->pc < s->u._case.end) {
- wordcode nalts;
+ Wordcode prev_pc;
+ wordcode ialts;
dec_tindent();
switch (WC_CASE_TYPE(code)) {
case WC_CASE_OR:
@@ -658,17 +740,18 @@ gettext2(Estate state)
taddchr(' ');
taddstr("(");
code = *state->pc++;
- nalts = *state->pc++;
- while (nalts--) {
+ prev_pc = state->pc++;
+ ialts = *prev_pc;
+ while (ialts--) {
taddstr(ecgetstr(state, EC_NODUP, NULL));
state->pc++;
- if (nalts)
+ if (ialts)
taddstr(" | ");
}
taddstr(") ");
tindent++;
s->code = code;
- s->pop = ((state->pc - 2 + WC_CASE_SKIP(code)) >=
+ s->pop = (prev_pc + WC_CASE_SKIP(code) >=
s->u._case.end);
} else {
dec_tindent();
diff --git a/Src/utils.c b/Src/utils.c
index 271c800fd..4c4dc55cd 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -82,7 +82,7 @@ set_widearray(char *mb_array, Widechar_array wca)
wchar_t *wcptr = tmpwcs;
wint_t wci;
- mb_metacharinit();
+ mb_charinit();
while (*mb_array) {
int mblen = mb_metacharlenconv(mb_array, &wci);
@@ -248,7 +248,7 @@ VA_DCL
VA_START(ap, message);
VA_GET_ARG(ap, message, const char *);
- if ((filename = getsparam("ZSH_DEBUG_LOG")) != NULL &&
+ if ((filename = getsparam_u("ZSH_DEBUG_LOG")) != NULL &&
(file = fopen(filename, "a")) != NULL) {
zerrmsg(file, message, ap);
fclose(file);
@@ -332,7 +332,7 @@ zerrmsg(FILE *file, const char *fmt, va_list ap)
case 'c':
num = va_arg(ap, int);
#ifdef MULTIBYTE_SUPPORT
- mb_metacharinit();
+ mb_charinit();
zputs(wcs_nicechar(num, NULL, NULL), file);
#else
zputs(nicechar(num), file);
@@ -461,12 +461,13 @@ static mbstate_t mb_shiftstate;
/*
* Initialise multibyte state: called before a sequence of
- * wcs_nicechar() or mb_metacharlenconv().
+ * wcs_nicechar(), mb_metacharlenconv(), or
+ * mb_charlenconv().
*/
/**/
mod_export void
-mb_metacharinit(void)
+mb_charinit(void)
{
memset(&mb_shiftstate, 0, sizeof(mb_shiftstate));
}
@@ -500,7 +501,7 @@ mb_metacharinit(void)
* (but not both). (Note the complication that the wide character
* part may contain metafied characters.)
*
- * The caller needs to call mb_metacharinit() before the first call, to
+ * The caller needs to call mb_charinit() before the first call, to
* set up the multibyte shift state for a range of characters.
*/
@@ -1948,7 +1949,8 @@ extern char *_mktemp(char *);
/* Get a unique filename for use as a temporary file. If "prefix" is
* NULL, the name is relative to $TMPPREFIX; If it is non-NULL, the
* unique suffix includes a prefixed '.' for improved readability. If
- * "use_heap" is true, we allocate the returned name on the heap. */
+ * "use_heap" is true, we allocate the returned name on the heap.
+ * The string passed as "prefix" is expected to be metafied. */
/**/
mod_export char *
@@ -1975,6 +1977,9 @@ gettempname(const char *prefix, int use_heap)
return ret;
}
+/* The gettempfile() "prefix" is expected to be metafied, see hist.c
+ * and gettempname(). */
+
/**/
mod_export int
gettempfile(const char *prefix, int use_heap, char **tempname)
@@ -2873,6 +2878,10 @@ ztrftimebuf(int *bufsizeptr, int decr)
* not enough memory --- and return -1. Not guaranteed to be portable,
* since the strftime() interface doesn't make any guarantees about
* the state of the buffer if it returns zero.
+ *
+ * fmt is metafied, but we need to unmetafy it on the fly to
+ * pass into strftime / combine with the output from strftime.
+ * The return value in buf is not metafied.
*/
/**/
@@ -2882,7 +2891,7 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec)
int hr12;
#ifdef HAVE_STRFTIME
int decr;
- char tmp[4];
+ char *fmtstart;
#else
static char *astr[] =
{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
@@ -2893,12 +2902,22 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec)
char *origbuf = buf;
- while (*fmt)
- if (*fmt == '%') {
+ while (*fmt) {
+ if (*fmt == Meta) {
+ int chr = fmt[1] ^ 32;
+ if (ztrftimebuf(&bufsize, 1))
+ return -1;
+ *buf++ = chr;
+ fmt += 2;
+ } else if (*fmt == '%') {
int strip;
int digs = 3;
+#ifdef HAVE_STRFTIME
+ fmtstart =
+#endif
fmt++;
+
if (*fmt == '-') {
strip = 1;
fmt++;
@@ -2923,6 +2942,21 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec)
*/
if (ztrftimebuf(&bufsize, 2))
return -1;
+#ifdef HAVE_STRFTIME
+ /* Our internal handling doesn't handle padding and other gnu extensions,
+ * so here we detect them and pass over to strftime(). We don't want
+ * to do this unconditionally though, as we have some extensions that
+ * strftime() doesn't have (%., %f, %L and %K) */
+morefmt:
+ if (!((fmt - fmtstart == 1) || (fmt - fmtstart == 2 && strip) || *fmt == '.')) {
+ while (*fmt && strchr("OE^#_-0123456789", *fmt))
+ fmt++;
+ if (*fmt) {
+ fmt++;
+ goto strftimehandling;
+ }
+ }
+#endif
switch (*fmt++) {
case '.':
if (ztrftimebuf(&bufsize, digs))
@@ -2938,10 +2972,10 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec)
sprintf(buf, "%0*ld", digs, usec);
buf += digs;
break;
- case 'd':
- if (tm->tm_mday > 9 || !strip)
- *buf++ = '0' + tm->tm_mday / 10;
- *buf++ = '0' + tm->tm_mday % 10;
+ case '\0':
+ /* Guard against premature end of string */
+ *buf++ = '%';
+ fmt--;
break;
case 'f':
strip = 1;
@@ -2982,6 +3016,11 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec)
*buf++ = '0' + (hr12 % 10);
break;
+ case 'd':
+ if (tm->tm_mday > 9 || !strip)
+ *buf++ = '0' + tm->tm_mday / 10;
+ *buf++ = '0' + tm->tm_mday % 10;
+ break;
case 'm':
if (tm->tm_mon > 8 || !strip)
*buf++ = '0' + (tm->tm_mon + 1) / 10;
@@ -3002,18 +3041,9 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec)
*buf++ = '0' + (tm->tm_year / 10) % 10;
*buf++ = '0' + tm->tm_year % 10;
break;
- case '\0':
- /* Guard against premature end of string */
- *buf++ = '%';
- fmt--;
- break;
#ifndef HAVE_STRFTIME
case 'Y':
{
- /*
- * Not worth handling this natively if
- * strftime has it.
- */
int year, digits, testyear;
year = tm->tm_year + 1900;
digits = 1;
@@ -3047,24 +3077,51 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec)
if (fmt[-1] != '%')
*buf++ = fmt[-1];
#else
+ case 'E':
+ case 'O':
+ case '^':
+ case '#':
+ case '_':
+ case '-':
+ case '0' ... '9':
+ goto morefmt;
+strftimehandling:
default:
/*
* Remember we've already allowed for two characters
* in the accounting in bufsize (but nowhere else).
*/
- *buf = '\1';
- sprintf(tmp, strip ? "%%-%c" : "%%%c", fmt[-1]);
- if (!strftime(buf, bufsize + 2, tmp, tm))
{
- if (*buf) {
- buf[0] = '\0';
- return -1;
+ int size = fmt - fmtstart;
+ char *tmp, *last;
+ tmp = zhalloc(size + 1);
+ strncpy(tmp, fmtstart, size);
+ last = fmt-1;
+ if (*last == Meta) {
+ /*
+ * This is for consistency in counting:
+ * a metafiable character isn't actually
+ * a valid strftime descriptor.
+ *
+ * Previous characters were explicitly checked,
+ * so can't be metafied.
+ */
+ *last = *++fmt ^ 32;
+ }
+ tmp[size] = '\0';
+ *buf = '\1';
+ if (!strftime(buf, bufsize + 2, tmp, tm))
+ {
+ if (*buf) {
+ buf[0] = '\0';
+ return -1;
+ }
+ return 0;
}
- return 0;
+ decr = strlen(buf);
+ buf += decr;
+ bufsize -= decr - 2;
}
- decr = strlen(buf);
- buf += decr;
- bufsize -= decr - 2;
#endif
break;
}
@@ -3073,6 +3130,7 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec)
return -1;
*buf++ = *fmt++;
}
+ }
*buf = '\0';
return buf - origbuf;
}
@@ -3555,7 +3613,7 @@ zbeep(void)
{
char *vb;
queue_signals();
- if ((vb = getsparam("ZBEEP"))) {
+ if ((vb = getsparam_u("ZBEEP"))) {
int len;
vb = getkeystring(vb, &len, GETKEYS_BINDKEY, NULL);
write_loop(SHTTY, vb, len);
@@ -3832,7 +3890,7 @@ itype_end(const char *ptr, int itype, int once)
#ifdef MULTIBYTE_SUPPORT
if (isset(MULTIBYTE) &&
(itype != IIDENT || !isset(POSIXIDENTIFIERS))) {
- mb_metacharinit();
+ mb_charinit();
while (*ptr) {
wint_t wc;
int len = mb_metacharlenconv(ptr, &wc);
@@ -4367,7 +4425,10 @@ unmeta(const char *file_name)
char *p;
const char *t;
int newsz, meta;
-
+
+ if (!file_name)
+ return NULL;
+
meta = 0;
for (t = file_name; *t; t++) {
if (*t == Meta)
@@ -4471,9 +4532,37 @@ ztrlen(char const *s)
for (l = 0; *s; l++) {
if (*s++ == Meta) {
#ifdef DEBUG
- if (! *s)
+ if (! *s) {
fprintf(stderr, "BUG: unexpected end of string in ztrlen()\n");
- else
+ break;
+ } else
+#endif
+ s++;
+ }
+ }
+ return l;
+}
+
+#ifndef MULTIBYTE_SUPPORT
+/*
+ * ztrlen() but with explicit end point for non-null-terminated
+ * segments. eptr may not be NULL.
+ */
+
+/**/
+mod_export int
+ztrlenend(char const *s, char const *eptr)
+{
+ int l;
+
+ for (l = 0; s < eptr; l++) {
+ if (*s++ == Meta) {
+#ifdef DEBUG
+ if (! *s) {
+ fprintf(stderr,
+ "BUG: unexpected end of string in ztrlenend()\n");
+ break;
+ } else
#endif
s++;
}
@@ -4481,6 +4570,8 @@ ztrlen(char const *s)
return l;
}
+#endif /* MULTIBYTE_SUPPORT */
+
/* Subtract two pointers in a metafied string. */
/**/
@@ -4879,11 +4970,16 @@ mb_metacharlenconv(const char *s, wint_t *wcp)
* If width is 1, return total character width rather than number.
* If width is greater than 1, return 1 if character has non-zero width,
* else 0.
+ *
+ * Ends if either *ptr is '\0', the normal case (eptr may be NULL for
+ * this), or ptr is eptr (i.e. *eptr is where the null would be if null
+ * terminated) for strings not delimited by nulls --- note these are
+ * still metafied.
*/
/**/
mod_export int
-mb_metastrlen(char *ptr, int width)
+mb_metastrlenend(char *ptr, int width, char *eptr)
{
char inchar, *laststart;
size_t ret;
@@ -4898,7 +4994,7 @@ mb_metastrlen(char *ptr, int width)
num = num_in_char = 0;
memset(&mb_shiftstate, 0, sizeof(mb_shiftstate));
- while (*ptr) {
+ while (*ptr && !(eptr && ptr >= eptr)) {
if (*ptr == Meta)
inchar = *++ptr ^ 32;
else
@@ -4937,6 +5033,65 @@ mb_metastrlen(char *ptr, int width)
return num + num_in_char;
}
+/*
+ * The equivalent of mb_metacharlenconv_r() for
+ * strings that aren't metafied and hence have
+ * explicit lengths.
+ */
+
+/**/
+mod_export int
+mb_charlenconv_r(const char *s, int slen, wint_t *wcp, mbstate_t *mbsp)
+{
+ size_t ret = MB_INVALID;
+ char inchar;
+ const char *ptr;
+ wchar_t wc;
+
+ for (ptr = s; slen; ) {
+ inchar = *ptr;
+ ptr++;
+ slen--;
+ ret = mbrtowc(&wc, &inchar, 1, mbsp);
+
+ if (ret == MB_INVALID)
+ break;
+ if (ret == MB_INCOMPLETE)
+ continue;
+ if (wcp)
+ *wcp = wc;
+ return ptr - s;
+ }
+
+ if (wcp)
+ *wcp = WEOF;
+ /* No valid multibyte sequence */
+ memset(mbsp, 0, sizeof(*mbsp));
+ if (ptr > s) {
+ return 1; /* Treat as single byte character */
+ } else
+ return 0; /* Probably shouldn't happen */
+}
+
+/*
+ * The equivalent of mb_metacharlenconv() for
+ * strings that aren't metafied and hence have
+ * explicit lengths;
+ */
+
+/**/
+mod_export int
+mb_charlenconv(const char *s, int slen, wint_t *wcp)
+{
+ if (!isset(MULTIBYTE)) {
+ if (wcp)
+ *wcp = (wint_t)*s;
+ return 1;
+ }
+
+ return mb_charlenconv_r(s, slen, wcp, &mb_shiftstate);
+}
+
/**/
#else
@@ -4961,8 +5116,127 @@ metacharlenconv(const char *x, int *c)
return 1;
}
+/* Simple replacement for mb_charlenconv */
+
/**/
+mod_export int
+charlenconv(const char *x, int len, int *c)
+{
+ if (!len) {
+ if (c)
+ *c = '\0';
+ return 0;
+ }
+
+ if (c)
+ *c = (char)*x;
+ return 1;
+}
+
+/**/
+#endif /* MULTIBYTE_SUPPORT */
+
+/*
+ * Expand tabs to given width, with given starting position on line.
+ * len is length of unmetafied string in bytes.
+ * Output to fout.
+ * Return the end position on the line, i.e. if this is 0 modulo width
+ * the next character is aligned with a tab stop.
+ *
+ * If all is set, all tabs are expanded, else only leading tabs.
+ */
+
+/**/
+mod_export int
+zexpandtabs(const char *s, int len, int width, int startpos, FILE *fout,
+ int all)
+{
+ int at_start = 1;
+
+#ifdef MULTIBYTE_SUPPORT
+ mbstate_t mbs;
+ size_t ret;
+ wchar_t wc;
+
+ memset(&mbs, 0, sizeof(mbs));
+#endif
+
+ while (len) {
+ if (*s == '\t') {
+ if (all || at_start) {
+ s++;
+ len--;
+ if (width <= 0 || !(startpos % width)) {
+ /* always output at least one space */
+ fputc(' ', fout);
+ startpos++;
+ }
+ if (width <= 0)
+ continue; /* paranoia */
+ while (startpos % width) {
+ fputc(' ', fout);
+ startpos++;
+ }
+ } else {
+ /*
+ * Leave tab alone.
+ * Guess width to apply... we might get this wrong.
+ * This is only needed if there's a following string
+ * that needs tabs expanding, which is unusual.
+ */
+ startpos += width - startpos % width;
+ s++;
+ len--;
+ fputc('\t', fout);
+ }
+ continue;
+ } else if (*s == '\n' || *s == '\r') {
+ fputc(*s, fout);
+ s++;
+ len--;
+ startpos = 0;
+ at_start = 1;
+ continue;
+ }
+
+ at_start = 0;
+#ifdef MULTIBYTE_SUPPORT
+ if (isset(MULTIBYTE)) {
+ const char *sstart = s;
+ ret = mbrtowc(&wc, s, len, &mbs);
+ if (ret == MB_INVALID) {
+ /* Assume single character per character */
+ memset(&mbs, 0, sizeof(mbs));
+ s++;
+ len--;
+ } else if (ret == MB_INCOMPLETE) {
+ /* incomplete at end --- assume likewise, best we've got */
+ s++;
+ len--;
+ } else {
+ s += ret;
+ len -= (int)ret;
+ }
+ if (ret == MB_INVALID || ret == MB_INCOMPLETE) {
+ startpos++;
+ } else {
+ int wcw = WCWIDTH(wc);
+ if (wcw > 0) /* paranoia */
+ startpos += wcw;
+ }
+ fwrite(sstart, s - sstart, 1, fout);
+
+ continue;
+ }
#endif /* MULTIBYTE_SUPPORT */
+ fputc(*s, fout);
+ s++;
+ len--;
+ startpos++;
+ }
+
+ return startpos;
+}
/* check for special characters in the string */
@@ -5301,7 +5575,25 @@ quotestring(const char *s, char **e, int instring)
/* Needs to be passed straight through. */
if (dobackslash)
*v++ = '\\';
- *v++ = *u++;
+ if (*u == Inparmath) {
+ /*
+ * Already syntactically quoted: don't
+ * add more.
+ */
+ int inmath = 1;
+ *v++ = *u++;
+ for (;;) {
+ char uc = *u;
+ *v++ = *u++;
+ if (uc == '\0')
+ break;
+ else if (uc == Outparmath && !--inmath)
+ break;
+ else if (uc == Inparmath)
+ ++inmath;
+ }
+ } else
+ *v++ = *u++;
continue;
}
@@ -6148,10 +6440,15 @@ init_dirsav(Dirsav d)
d->dirfd = d->level = -1;
}
-/* Change directory, without following symlinks. Returns 0 on success, -1 *
- * on failure. Sets errno to ENOTDIR if any symlinks are encountered. If *
- * fchdir() fails, or the current directory is unreadable, we might end up *
- * in an unwanted directory in case of failure. */
+/*
+ * Change directory, without following symlinks. Returns 0 on success, -1
+ * on failure. Sets errno to ENOTDIR if any symlinks are encountered. If
+ * fchdir() fails, or the current directory is unreadable, we might end up
+ * in an unwanted directory in case of failure.
+ *
+ * path is an unmetafied but null-terminated string, as needed by system
+ * calls.
+ */
/**/
mod_export int
diff --git a/Src/watch.c b/Src/watch.c
index fe409f91a..c804913ad 100644
--- a/Src/watch.c
+++ b/Src/watch.c
@@ -237,6 +237,7 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
time_t timet;
struct tm *tm;
char *fm2;
+ int len;
# ifdef WATCH_UTMP_UT_HOST
char *p;
int i;
@@ -330,7 +331,9 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
}
timet = getlogtime(u, inout);
tm = localtime(&timet);
- ztrftime(buf, 40, fm2, tm, 0L);
+ len = ztrftime(buf, 40, fm2, tm, 0L);
+ if (len > 0)
+ metafy(buf, len, META_NOALLOC);
printf("%s", (*buf == ' ') ? buf + 1 : buf);
break;
case '%':
@@ -566,7 +569,7 @@ dowatch(void)
return;
}
queue_signals();
- if (!(fmt = getsparam("WATCHFMT")))
+ if (!(fmt = getsparam_u("WATCHFMT")))
fmt = DEFAULT_WATCHFMT;
while ((uct || wct) && !errflag)
if (!uct || (wct && ucmp(uptr, wptr) > 0))
diff --git a/Src/zsh.h b/Src/zsh.h
index f6e08e28d..a99c90065 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -336,7 +336,8 @@ enum lextok {
THEN, /* then */
TIME, /* time */ /* 60 */
UNTIL, /* until */
- WHILE /* while */
+ WHILE, /* while */
+ TYPESET /* typeset or similar */
};
/* Redirection types. If you modify this, you may also have to modify *
@@ -424,6 +425,7 @@ enum {
#define INP_HISTCONT (1<<5) /* stack is continued from history expn. */
#define INP_LINENO (1<<6) /* update line number */
#define INP_APPEND (1<<7) /* Append new lines to allow backup */
+#define INP_RAW_KEEP (1<<8) /* Input needed in raw mode even if alias */
/* Flags for metafy */
#define META_REALLOC 0
@@ -671,14 +673,6 @@ struct multio {
int fds[MULTIOUNIT]; /* list of src/dests redirected to/from this fd */
};
-/* structure for foo=bar assignments */
-
-struct asgment {
- struct asgment *next;
- char *name;
- char *value;
-};
-
/* lvalue for variable assignment/expansion */
struct value {
@@ -789,23 +783,24 @@ struct eccstr {
#define WC_REDIR 4
#define WC_ASSIGN 5
#define WC_SIMPLE 6
-#define WC_SUBSH 7
-#define WC_CURSH 8
-#define WC_TIMED 9
-#define WC_FUNCDEF 10
-#define WC_FOR 11
-#define WC_SELECT 12
-#define WC_WHILE 13
-#define WC_REPEAT 14
-#define WC_CASE 15
-#define WC_IF 16
-#define WC_COND 17
-#define WC_ARITH 18
-#define WC_AUTOFN 19
-#define WC_TRY 20
+#define WC_TYPESET 7
+#define WC_SUBSH 8
+#define WC_CURSH 9
+#define WC_TIMED 10
+#define WC_FUNCDEF 11
+#define WC_FOR 12
+#define WC_SELECT 13
+#define WC_WHILE 14
+#define WC_REPEAT 15
+#define WC_CASE 16
+#define WC_IF 17
+#define WC_COND 18
+#define WC_ARITH 19
+#define WC_AUTOFN 20
+#define WC_TRY 21
/* increment as necessary */
-#define WC_COUNT 21
+#define WC_COUNT 22
#define WCB_END() wc_bld(WC_END, 0)
@@ -849,6 +844,12 @@ struct eccstr {
#define WC_ASSIGN_SCALAR 0
#define WC_ASSIGN_ARRAY 1
#define WC_ASSIGN_NEW 0
+/*
+ * In normal assignment, this indicate += to append.
+ * In assignment following a typeset, where that's not allowed,
+ * we overload this to indicate a variable without an
+ * assignment.
+ */
#define WC_ASSIGN_INC 1
#define WC_ASSIGN_NUM(C) (wc_data(C) >> 2)
#define WCB_ASSIGN(T,A,N) wc_bld(WC_ASSIGN, ((T) | ((A) << 1) | ((N) << 2)))
@@ -856,6 +857,9 @@ struct eccstr {
#define WC_SIMPLE_ARGC(C) wc_data(C)
#define WCB_SIMPLE(N) wc_bld(WC_SIMPLE, (N))
+#define WC_TYPESET_ARGC(C) wc_data(C)
+#define WCB_TYPESET(N) wc_bld(WC_TYPESET, (N))
+
#define WC_SUBSH_SKIP(C) wc_data(C)
#define WCB_SUBSH(O) wc_bld(WC_SUBSH, (O))
@@ -1140,6 +1144,32 @@ struct alias {
/* is this an alias for suffix handling? */
#define ALIAS_SUFFIX (1<<2)
+/* structure for foo=bar assignments */
+
+struct asgment {
+ struct linknode node;
+ char *name;
+ int is_array;
+ union {
+ char *scalar;
+ LinkList array;
+ } value;
+};
+
+/*
+ * Assignment is array?
+ */
+#define ASG_ARRAYP(asg) ((asg)->is_array)
+
+/*
+ * Assignment has value?
+ * If the assignment is an arrray, then it certainly has a value --- we
+ * can only tell if there's an expicit assignment.
+ */
+
+#define ASG_VALUEP(asg) (ASG_ARRAYP(asg) || \
+ ((asg)->value.scalar != (char *)0))
+
/* node in command path hash table (cmdnamtab) */
struct cmdnam {
@@ -1268,6 +1298,7 @@ struct options {
*/
typedef int (*HandlerFunc) _((char *, char **, Options, int));
+typedef int (*HandlerFuncAssign) _((char *, char **, LinkList, Options, int));
#define NULLBINCMD ((HandlerFunc) 0)
struct builtin {
@@ -1311,6 +1342,12 @@ struct builtin {
* does not terminate options.
*/
#define BINF_HANDLES_OPTS (1<<18)
+/*
+ * Handles the assignement interface. The argv list actually contains
+ * two nested litsts, the first of normal arguments, and the second of
+ * assignment structures.
+ */
+#define BINF_ASSIGN (1<<19)
struct module {
struct hashnode node;
@@ -1715,9 +1752,10 @@ struct tieddata {
* necessarily want to match multiple
* elements
*/
-#define SCANPM_ISVAR_AT ((-1)<<15) /* "$foo[@]"-style substitution
- * Only sign bit is significant
- */
+/* "$foo[@]"-style substitution
+ * Only sign bit is significant
+ */
+#define SCANPM_ISVAR_AT ((int)(((unsigned int)-1)<<15))
/*
* Flags for doing matches inside parameter substitutions, i.e.
@@ -2084,6 +2122,7 @@ enum {
CHASELINKS,
CHECKJOBS,
CLOBBER,
+ APPENDCREATE,
COMBININGCHARS,
COMPLETEALIASES,
COMPLETEINWORD,
@@ -2779,6 +2818,7 @@ struct parse_stack {
int incasepat;
int isnewlin;
int infor;
+ int intypeset;
int eclen, ecused, ecnpats;
Wordcode ecbuf;
@@ -2921,14 +2961,22 @@ enum {
#define AFTERTRAPHOOK (zshhooks + 2)
#ifdef MULTIBYTE_SUPPORT
+/* Metafied input */
#define nicezputs(str, outs) (void)mb_niceformat((str), (outs), NULL, 0)
-#define MB_METACHARINIT() mb_metacharinit()
+#define MB_METACHARINIT() mb_charinit()
typedef wint_t convchar_t;
#define MB_METACHARLENCONV(str, cp) mb_metacharlenconv((str), (cp))
#define MB_METACHARLEN(str) mb_metacharlenconv(str, NULL)
-#define MB_METASTRLEN(str) mb_metastrlen(str, 0)
-#define MB_METASTRWIDTH(str) mb_metastrlen(str, 1)
-#define MB_METASTRLEN2(str, widthp) mb_metastrlen(str, widthp)
+#define MB_METASTRLEN(str) mb_metastrlenend(str, 0, NULL)
+#define MB_METASTRWIDTH(str) mb_metastrlenend(str, 1, NULL)
+#define MB_METASTRLEN2(str, widthp) mb_metastrlenend(str, widthp, NULL)
+#define MB_METASTRLEN2END(str, widthp, eptr) \
+ mb_metastrlenend(str, widthp, eptr)
+
+/* Unmetafined input */
+#define MB_CHARINIT() mb_charinit()
+#define MB_CHARLENCONV(str, len, cp) mb_charlenconv((str), (len), (cp))
+#define MB_CHARLEN(str, len) mb_charlenconv((str), (len), NULL)
/*
* We replace broken implementations with one that uses Unicode
@@ -3011,6 +3059,11 @@ typedef int convchar_t;
#define MB_METASTRLEN(str) ztrlen(str)
#define MB_METASTRWIDTH(str) ztrlen(str)
#define MB_METASTRLEN2(str, widthp) ztrlen(str)
+#define MB_METASTRLEN2END(str, widthp, eptr) ztrlenend(str, eptr)
+
+#define MB_CHARINIT()
+#define MB_CHARLENCONV(str, len, cp) charlenconv((str), (len), (cp))
+#define MB_CHARLEN(str, len) ((len) ? 1 : 0)
#define WCWIDTH_WINT(c) (1)