summaryrefslogtreecommitdiff
path: root/Src
diff options
context:
space:
mode:
Diffstat (limited to 'Src')
-rw-r--r--Src/Builtins/rlimits.c2
-rw-r--r--Src/Builtins/sched.c2
-rw-r--r--Src/Modules/attr.c67
-rw-r--r--Src/Modules/datetime.c21
-rw-r--r--Src/Modules/db_gdbm.c184
-rw-r--r--Src/Modules/langinfo.c3
-rw-r--r--Src/Modules/tcp.c4
-rw-r--r--Src/Modules/zftp.c1
-rw-r--r--Src/Modules/zpty.c6
-rw-r--r--Src/Modules/zselect.c2
-rw-r--r--Src/Modules/zutil.c8
-rw-r--r--Src/Zle/compcore.c8
-rw-r--r--Src/Zle/compctl.c26
-rw-r--r--Src/Zle/complist.c28
-rw-r--r--Src/Zle/compmatch.c3
-rw-r--r--Src/Zle/compresult.c7
-rw-r--r--Src/Zle/computil.c3
-rw-r--r--Src/Zle/iwidgets.list55
-rw-r--r--Src/Zle/textobjects.c322
-rw-r--r--Src/Zle/zle.h21
-rw-r--r--Src/Zle/zle.mdd3
-rw-r--r--Src/Zle/zle_bindings.c10
-rw-r--r--Src/Zle/zle_hist.c28
-rw-r--r--Src/Zle/zle_keymap.c59
-rw-r--r--Src/Zle/zle_main.c72
-rw-r--r--Src/Zle/zle_misc.c238
-rw-r--r--Src/Zle/zle_move.c61
-rw-r--r--Src/Zle/zle_params.c2
-rw-r--r--Src/Zle/zle_refresh.c26
-rw-r--r--Src/Zle/zle_tricky.c62
-rw-r--r--Src/Zle/zle_utils.c105
-rw-r--r--Src/Zle/zle_vi.c411
-rw-r--r--Src/Zle/zle_word.c113
-rw-r--r--Src/builtin.c297
-rw-r--r--Src/compat.c17
-rw-r--r--Src/context.c116
-rw-r--r--Src/exec.c239
-rw-r--r--Src/glob.c27
-rw-r--r--Src/hashtable.c14
-rw-r--r--Src/hist.c357
-rw-r--r--Src/init.c70
-rw-r--r--Src/input.c99
-rw-r--r--Src/jobs.c180
-rw-r--r--Src/lex.c770
-rw-r--r--Src/linklist.c4
-rw-r--r--Src/loop.c51
-rw-r--r--Src/math.c60
-rw-r--r--Src/mem.c4
-rw-r--r--Src/module.c9
-rw-r--r--Src/options.c12
-rw-r--r--Src/params.c74
-rw-r--r--Src/parse.c152
-rw-r--r--Src/pattern.c2
-rw-r--r--Src/prompt.c9
-rw-r--r--Src/signals.c46
-rw-r--r--Src/sort.c2
-rw-r--r--Src/subst.c120
-rw-r--r--Src/text.c19
-rw-r--r--Src/utils.c240
-rw-r--r--Src/watch.c27
-rw-r--r--Src/zsh.h158
-rw-r--r--Src/zsh.mdd14
-rw-r--r--Src/ztype.h6
63 files changed, 3808 insertions, 1350 deletions
diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c
index 9da31831d..85ec1811c 100644
--- a/Src/Builtins/rlimits.c
+++ b/Src/Builtins/rlimits.c
@@ -518,7 +518,7 @@ do_limit(char *nam, int lim, rlim_t val, int hard, int soft, int set)
}
} else
limits[lim].rlim_cur = val;
- if (set && zsetlimit(lim, "limit"))
+ if (set && zsetlimit(lim, nam))
return 1;
}
}
diff --git a/Src/Builtins/sched.c b/Src/Builtins/sched.c
index c1cc98354..bcf7661f4 100644
--- a/Src/Builtins/sched.c
+++ b/Src/Builtins/sched.c
@@ -346,7 +346,7 @@ schedgetfn(UNUSED(Param pm))
for (i = 0, sch = schedcmds; sch; sch = sch->next, i++)
;
- aptr = ret = zhalloc(sizeof(char **) * (i+1));
+ aptr = ret = zhalloc(sizeof(char *) * (i+1));
for (sch = schedcmds; sch; sch = sch->next, aptr++) {
char tbuf[40], *flagstr;
time_t t;
diff --git a/Src/Modules/attr.c b/Src/Modules/attr.c
index 6e08b10c5..78c1104a9 100644
--- a/Src/Modules/attr.c
+++ b/Src/Modules/attr.c
@@ -98,36 +98,33 @@ static int
bin_getattr(char *nam, char **argv, Options ops, UNUSED(int func))
{
int ret = 0;
- int list_len, val_len = 0, attr_len = 0, slen;
+ int val_len = 0, attr_len = 0, slen;
char *value, *file = argv[0], *attr = argv[1], *param = argv[2];
int symlink = OPT_ISSET(ops, 'h');
unmetafy(file, &slen);
unmetafy(attr, NULL);
- list_len = xlistxattr(file, NULL, 0, symlink);
- if (list_len > 0) {
- val_len = xgetxattr(file, attr, NULL, 0, symlink);
- if (val_len == 0) {
+ val_len = xgetxattr(file, attr, NULL, 0, symlink);
+ if (val_len == 0) {
+ if (param)
+ unsetparam(param);
+ return 0;
+ }
+ if (val_len > 0) {
+ value = (char *)zalloc(val_len+1);
+ attr_len = xgetxattr(file, attr, value, val_len, symlink);
+ if (attr_len > 0 && attr_len <= val_len) {
+ value[attr_len] = '\0';
if (param)
- unsetparam(param);
- return 0;
- }
- if (val_len > 0) {
- value = (char *)zalloc(val_len+1);
- attr_len = xgetxattr(file, attr, value, val_len, symlink);
- if (attr_len > 0 && attr_len <= val_len) {
- value[attr_len] = '\0';
- if (param)
- setsparam(param, metafy(value, attr_len, META_DUP));
- else
- printf("%s\n", value);
- }
- zfree(value, val_len+1);
+ setsparam(param, metafy(value, attr_len, META_DUP));
+ else
+ printf("%s\n", value);
}
+ zfree(value, val_len+1);
}
- if (list_len < 0 || val_len < 0 || attr_len < 0 || attr_len > val_len) {
+ if (val_len < 0 || attr_len < 0 || attr_len > val_len) {
zwarnnam(nam, "%s: %e", metafy(file, slen, META_NOALLOC), errno);
- ret = 1 + (attr_len > val_len || attr_len < 0);
+ ret = 1 + ((val_len > 0 && attr_len > val_len) || attr_len < 0);
}
return ret;
}
@@ -189,24 +186,20 @@ bin_listattr(char *nam, char **argv, Options ops, UNUSED(int func))
if (list_len > 0 && list_len <= val_len) {
char *p = value;
if (param) {
- if (strlen(value) + 1 == list_len)
- setsparam(param, metafy(value, list_len-1, META_DUP));
- else {
- int arrlen = 0;
- char **array = NULL, **arrptr = NULL;
+ int arrlen = 0;
+ char **array = NULL, **arrptr = NULL;
- while (p < &value[list_len]) {
- arrlen++;
- p += strlen(p) + 1;
- }
- arrptr = array = (char **)zshcalloc((arrlen+1) * sizeof(char *));
- p = value;
- while (p < &value[list_len]) {
- *arrptr++ = metafy(p, -1, META_DUP);
- p += strlen(p) + 1;
- }
- setaparam(param, array);
+ while (p < &value[list_len]) {
+ arrlen++;
+ p += strlen(p) + 1;
+ }
+ arrptr = array = (char **)zshcalloc((arrlen+1) * sizeof(char *));
+ p = value;
+ while (p < &value[list_len]) {
+ *arrptr++ = metafy(p, -1, META_DUP);
+ p += strlen(p) + 1;
}
+ setaparam(param, array);
} else while (p < &value[list_len]) {
printf("%s\n", p);
p += strlen(p) + 1;
diff --git a/Src/Modules/datetime.c b/Src/Modules/datetime.c
index 00ebd2b49..63a04dc89 100644
--- a/Src/Modules/datetime.c
+++ b/Src/Modules/datetime.c
@@ -94,7 +94,7 @@ reverse_strftime(char *nam, char **argv, char *scalar, int quiet)
}
static int
-bin_strftime(char *nam, char **argv, Options ops, UNUSED(int func))
+output_strftime(char *nam, char **argv, Options ops, UNUSED(int func))
{
int bufsize, x;
char *endptr = NULL, *scalar = NULL, *buffer;
@@ -145,6 +145,25 @@ bin_strftime(char *nam, char **argv, Options ops, UNUSED(int func))
return 0;
}
+static int
+bin_strftime(char *nam, char **argv, Options ops, int func)
+{
+ int result = 1;
+ char *tz = getsparam("TZ");
+
+ startparamscope();
+ if (tz && *tz) {
+ Param pm = createparam("TZ", PM_LOCAL|PM_SCALAR|PM_EXPORTED);
+ if (pm)
+ pm->level = locallevel; /* because createparam() doesn't */
+ setsparam("TZ", ztrdup(tz));
+ }
+ result = output_strftime(nam, argv, ops, func);
+ endparamscope();
+
+ return result;
+}
+
static zlong
getcurrentsecs(UNUSED(Param pm))
{
diff --git a/Src/Modules/db_gdbm.c b/Src/Modules/db_gdbm.c
index 9a2a7a5b9..76d4751bf 100644
--- a/Src/Modules/db_gdbm.c
+++ b/Src/Modules/db_gdbm.c
@@ -39,16 +39,17 @@
#include <gdbm.h>
-#if 0 /* what is this for? */
static char *backtype = "db/gdbm";
-#endif
static const struct gsu_scalar gdbm_gsu =
{ gdbmgetfn, gdbmsetfn, gdbmunsetfn };
+/**/
+static const struct gsu_hash gdbm_hash_gsu =
+{ hashgetfn, gdbmhashsetfn, gdbmhashunsetfn };
static struct builtin bintab[] = {
- BUILTIN("ztie", 0, bin_ztie, 1, -1, 0, "d:f:", NULL),
- BUILTIN("zuntie", 0, bin_zuntie, 1, -1, 0, NULL, NULL),
+ BUILTIN("ztie", 0, bin_ztie, 1, -1, 0, "d:f:r", NULL),
+ BUILTIN("zuntie", 0, bin_zuntie, 1, -1, 0, "u", NULL),
};
/**/
@@ -57,36 +58,67 @@ bin_ztie(char *nam, char **args, Options ops, UNUSED(int func))
{
char *resource_name, *pmname;
GDBM_FILE dbf = NULL;
+ int read_write = GDBM_SYNC, pmflags = PM_REMOVABLE;
Param tied_param;
if(!OPT_ISSET(ops,'d')) {
- zwarnnam(nam, "you must pass `-d db/gdbm' to ztie", NULL);
+ zwarnnam(nam, "you must pass `-d %s'", backtype);
return 1;
}
if(!OPT_ISSET(ops,'f')) {
- zwarnnam(nam, "you must pass `-f' with a filename to ztie", NULL);
+ zwarnnam(nam, "you must pass `-f' with a filename", NULL);
return 1;
}
+ if (OPT_ISSET(ops,'r')) {
+ read_write |= GDBM_READER;
+ pmflags |= PM_READONLY;
+ } else {
+ read_write |= GDBM_WRCREAT;
+ }
/* Here should be a lookup of the backend type against
* a registry.
*/
-
- pmname = ztrdup(*args);
+ if (strcmp(OPT_ARG(ops, 'd'), backtype) != 0) {
+ zwarnnam(nam, "unsupported backend type `%s'", OPT_ARG(ops, 'd'));
+ return 1;
+ }
resource_name = OPT_ARG(ops, 'f');
+ pmname = *args;
+
+ if ((tied_param = (Param)paramtab->getnode(paramtab, pmname)) &&
+ !(tied_param->node.flags & PM_UNSET)) {
+ /*
+ * Unset any existing parameter. Note there's no implicit
+ * "local" here, but if the existing parameter is local
+ * that will be reflected in the new one.
+ *
+ * We need to do this before attempting to open the DB
+ * in case this variable is already tied to a DB.
+ *
+ * This can fail if the variable is readonly or restricted.
+ * We could call unsetparam() and check errflag instead
+ * of the return status.
+ */
+ if (unsetparam_pm(tied_param, 0, 1))
+ return 1;
+ }
- if (!(tied_param = createspecialhash(pmname, &getgdbmnode, &scangdbmkeys, 0))) {
- zwarnnam(nam, "cannot create the requested parameter name", NULL);
+ dbf = gdbm_open(resource_name, 0, read_write, 0666, 0);
+ if(!dbf) {
+ zwarnnam(nam, "error opening database file %s", resource_name);
return 1;
}
- dbf = gdbm_open(resource_name, 0, GDBM_WRCREAT | GDBM_SYNC, 0666, 0);
- if(!dbf) {
- zwarnnam(nam, "error opening database file %s", resource_name);
+ if (!(tied_param = createspecialhash(pmname, &getgdbmnode, &scangdbmkeys,
+ pmflags))) {
+ zwarnnam(nam, "cannot create the requested parameter %s", pmname);
+ gdbm_close(dbf);
return 1;
}
+ tied_param->gsu.h = &gdbm_hash_gsu;
tied_param->u.hash->tmpdata = (void *)dbf;
return 0;
@@ -97,20 +129,33 @@ static int
bin_zuntie(char *nam, char **args, Options ops, UNUSED(int func))
{
Param pm;
- GDBM_FILE dbf;
-
- pm = (Param) paramtab->getnode(paramtab, args[0]);
- if(!pm) {
- zwarnnam(nam, "cannot untie %s", args[0]);
- return 1;
+ char *pmname;
+ int ret = 0;
+
+ for (pmname = *args; *args++; pmname = *args) {
+ pm = (Param) paramtab->getnode(paramtab, pmname);
+ if(!pm) {
+ zwarnnam(nam, "cannot untie %s", pmname);
+ ret = 1;
+ continue;
+ }
+ if (pm->gsu.h != &gdbm_hash_gsu) {
+ zwarnnam(nam, "not a tied gdbm hash: %s", pmname);
+ ret = 1;
+ continue;
+ }
+
+ queue_signals();
+ if (OPT_ISSET(ops,'u'))
+ gdbmuntie(pm); /* clear read-only-ness */
+ if (unsetparam_pm(pm, 0, 1)) {
+ /* assume already reported */
+ ret = 1;
+ }
+ unqueue_signals();
}
- dbf = (GDBM_FILE)(pm->u.hash->tmpdata);
- gdbm_close(dbf);
-/* free(pm->u.hash->tmpdata); */
- paramtab->removenode(paramtab, pm->node.nam);
-
- return 0;
+ return ret;
}
/**/
@@ -153,7 +198,7 @@ gdbmsetfn(Param pm, char *val)
/**/
static void
-gdbmunsetfn(Param pm, int um)
+gdbmunsetfn(Param pm, UNUSED(int um))
{
datum key;
GDBM_FILE dbf;
@@ -191,9 +236,7 @@ scangdbmkeys(HashTable ht, ScanFunc func, int flags)
{
Param pm = NULL;
datum key, content;
- GDBM_FILE dbf;
-
- dbf = (GDBM_FILE)(ht->tmpdata);
+ GDBM_FILE dbf = (GDBM_FILE)(ht->tmpdata);
pm = (Param) hcalloc(sizeof(struct param));
@@ -216,6 +259,89 @@ scangdbmkeys(HashTable ht, ScanFunc func, int flags)
}
+/**/
+static void
+gdbmhashsetfn(Param pm, HashTable ht)
+{
+ int i;
+ HashNode hn;
+ GDBM_FILE dbf;
+ datum key, content;
+
+ if (!pm->u.hash || pm->u.hash == ht)
+ return;
+
+ if (!(dbf = (GDBM_FILE)(pm->u.hash->tmpdata)))
+ return;
+
+ key = gdbm_firstkey(dbf);
+ while (key.dptr) {
+ queue_signals();
+ (void)gdbm_delete(dbf, key);
+ free(key.dptr);
+ unqueue_signals();
+ key = gdbm_firstkey(dbf);
+ }
+
+ /* just deleted everything, clean up */
+ (void)gdbm_reorganize(dbf);
+
+ if (!ht)
+ return;
+
+ for (i = 0; i < ht->hsize; i++)
+ for (hn = ht->nodes[i]; hn; hn = hn->next) {
+ struct value v;
+
+ v.isarr = v.flags = v.start = 0;
+ v.end = -1;
+ v.arr = NULL;
+ v.pm = (Param) hn;
+
+ key.dptr = v.pm->node.nam;
+ key.dsize = strlen(key.dptr) + 1;
+
+ queue_signals();
+
+ content.dptr = getstrvalue(&v);
+ content.dsize = strlen(content.dptr) + 1;
+
+ (void)gdbm_store(dbf, key, content, GDBM_REPLACE);
+
+ unqueue_signals();
+ }
+}
+
+/**/
+static void
+gdbmuntie(Param pm)
+{
+ GDBM_FILE dbf = (GDBM_FILE)(pm->u.hash->tmpdata);
+ HashTable ht = pm->u.hash;
+
+ if (dbf) /* paranoia */
+ gdbm_close(dbf);
+
+ ht->tmpdata = NULL;
+
+ /* for completeness ... createspecialhash() should have an inverse */
+ ht->getnode = ht->getnode2 = gethashnode2;
+ ht->scantab = NULL;
+
+ pm->node.flags &= ~(PM_SPECIAL|PM_READONLY);
+ pm->gsu.h = &stdhash_gsu;
+}
+
+/**/
+static void
+gdbmhashunsetfn(Param pm, UNUSED(int exp))
+{
+ gdbmuntie(pm);
+ /* hash table is now normal, so proceed normally... */
+ pm->gsu.h->setfn(pm, NULL);
+ pm->node.flags |= PM_UNSET;
+}
+
#else
# error no gdbm
#endif /* have gdbm */
diff --git a/Src/Modules/langinfo.c b/Src/Modules/langinfo.c
index f10fdfef5..eb45d161b 100644
--- a/Src/Modules/langinfo.c
+++ b/Src/Modules/langinfo.c
@@ -395,7 +395,8 @@ liitem(const char *name)
static HashNode
getlanginfo(UNUSED(HashTable ht), const char *name)
{
- int len, *elem;
+ int len;
+ nl_item *elem;
char *listr, *nameu;
Param pm = NULL;
diff --git a/Src/Modules/tcp.c b/Src/Modules/tcp.c
index 3f92050ae..0d9522047 100644
--- a/Src/Modules/tcp.c
+++ b/Src/Modules/tcp.c
@@ -623,6 +623,7 @@ bin_ztcp(char *nam, char **args, Options ops, UNUSED(int func))
zthost = zsh_getipnodebyname(desthost, AF_INET, 0, &herrno);
if (!zthost || errflag) {
zwarnnam(nam, "host resolution failure: %s", desthost);
+ zsfree(desthost);
return 1;
}
@@ -630,6 +631,7 @@ bin_ztcp(char *nam, char **args, Options ops, UNUSED(int func))
if (!sess) {
zwarnnam(nam, "unable to allocate a TCP session slot");
+ zsfree(desthost);
return 1;
}
@@ -665,6 +667,8 @@ bin_ztcp(char *nam, char **args, Options ops, UNUSED(int func))
sess->fd = redup(sess->fd, targetfd);
if (sess->fd < 0) {
zerrnam(nam, "could not duplicate socket fd to %d: %e", targetfd, errno);
+ zsfree(desthost);
+ tcp_close(sess);
return 1;
}
}
diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c
index d16e2f618..09d4bd703 100644
--- a/Src/Modules/zftp.c
+++ b/Src/Modules/zftp.c
@@ -50,7 +50,6 @@ struct zftp_session;
typedef struct zftp_session *Zftp_session;
#include "tcp.h"
-#include "tcp.mdh"
#include "zftp.mdh"
#include "zftp.pro"
diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c
index d119658c3..7b6130c6f 100644
--- a/Src/Modules/zpty.c
+++ b/Src/Modules/zpty.c
@@ -189,7 +189,11 @@ get_pty(int master, int *retfd)
#endif
if (master) {
+#ifdef HAVE_POSIX_OPENPT
+ if ((mfd = posix_openpt(O_RDWR|O_NOCTTY)) < 0)
+#else
if ((mfd = open("/dev/ptmx", O_RDWR|O_NOCTTY)) < 0)
+#endif
return 1;
if (grantpt(mfd) || unlockpt(mfd) || !(name = ptsname(mfd))) {
@@ -304,7 +308,7 @@ newptycmd(char *nam, char *pname, char **args, int echo, int nblock)
prog = parse_string(zjoin(args, ' ', 1), 0);
if (!prog) {
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
scriptname = oscriptname;
ineval = oineval;
return 1;
diff --git a/Src/Modules/zselect.c b/Src/Modules/zselect.c
index c02074646..30a3f51a5 100644
--- a/Src/Modules/zselect.c
+++ b/Src/Modules/zselect.c
@@ -251,7 +251,7 @@ bin_zselect(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
outptr = outdata = (char **)zalloc((fdcount+1)*sizeof(char *));
while (nonempty(fdlist))
*outptr++ = getlinknode(fdlist);
- *outptr = '\0';
+ *outptr = NULL;
/* and store in array parameter */
if (outhash)
sethparam(outhash, outdata);
diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c
index 1cca0c4b8..c89495070 100644
--- a/Src/Modules/zutil.c
+++ b/Src/Modules/zutil.c
@@ -301,7 +301,8 @@ setstypat(Style s, char *pat, Patprog prog, char **vals, int eval)
int ef = errflag;
eprog = parse_string(zjoin(vals, ' ', 1), 0);
- errflag = ef;
+ /* Keep any user interrupt error status */
+ errflag = ef | (errflag & ERRFLAG_INT);
if (!eprog)
{
@@ -394,10 +395,11 @@ evalstyle(Stypat p)
unsetparam("reply");
execode(p->eval, 1, 0, "style");
if (errflag) {
- errflag = ef;
+ /* Keep any user interrupt error status */
+ errflag = ef | (errflag & ERRFLAG_INT);
return NULL;
}
- errflag = ef;
+ errflag = ef | (errflag & ERRFLAG_INT);
queue_signals();
if ((ret = getaparam("reply")))
diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c
index 35d410cc6..000f9da2a 100644
--- a/Src/Zle/compcore.c
+++ b/Src/Zle/compcore.c
@@ -1524,7 +1524,7 @@ set_comp_sep(void)
ol = zlemetaline;
addedx = 1;
noerrs = 1;
- lexsave();
+ zcontext_save();
lexflags = LEXFLAGS_ZLE;
/*
* tl is the length of the temporary string including
@@ -1671,9 +1671,9 @@ set_comp_sep(void)
noaliases = ona;
strinend();
inpop();
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
noerrs = ne;
- lexrestore();
+ zcontext_restore();
wb = owb;
we = owe;
zlemetaline = ol;
@@ -3492,7 +3492,7 @@ freematch(Cmatch m, int nbeg, int nend)
if (m->brsl)
zfree(m->brsl, nend * sizeof(int));
- zfree(m, sizeof(m));
+ zfree(m, sizeof(*m));
}
/* This frees the groups of matches. */
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index 0b7a32445..189582d22 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -1515,7 +1515,7 @@ printcompctl(char *s, Compctl cc, int printflags, int ispat)
if (cclist & COMP_LIST)
printf(" --");
}
- if (cc && cc->xor) {
+ if (cc->xor) {
/* print xor'd (+) completions */
printf(" +");
if (cc->xor != &cc_default)
@@ -1879,7 +1879,7 @@ ccmakehookfn(UNUSED(Hookdef dummy), struct ccmakedat *dat)
if (!m || !(m = m->next))
break;
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
}
redup(osi, 0);
dat->lst = 1;
@@ -2121,7 +2121,7 @@ getreal(char *str)
if (!errflag && nonempty(l) &&
((char *) peekfirst(l)) && ((char *) peekfirst(l))[0])
return dupstring(peekfirst(l));
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
return dupstring(str);
}
@@ -2599,7 +2599,7 @@ makecomplistlist(Compctl cc, char *s, int incmd, int compadd)
makecomplistflags(cc, s, incmd, compadd);
/* Reset some information variables for the next try. */
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
offs = oloffs;
wb = owb;
we = owe;
@@ -2795,7 +2795,7 @@ sep_comp_string(char *ss, char *s, int noffs)
* get the words we have to expand. */
addedx = 1;
noerrs = 1;
- lexsave();
+ zcontext_save();
lexflags = LEXFLAGS_ZLE;
tmp = (char *) zhalloc(tl = sl + 3 + strlen(s));
strcpy(tmp, ss);
@@ -2847,9 +2847,9 @@ sep_comp_string(char *ss, char *s, int noffs)
noaliases = ona;
strinend();
inpop();
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
noerrs = ne;
- lexrestore();
+ zcontext_restore();
wb = owb;
we = owe;
zlemetacs = ocs;
@@ -3685,7 +3685,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
for (i = 0; i <= maxjob; i++)
if ((jobtab[i].stat & STAT_INUSE) &&
- jobtab[i].procs && jobtab[i].procs->text) {
+ jobtab[i].procs && jobtab[i].procs->text[0]) {
int stopped = jobtab[i].stat & STAT_STOPPED;
j = dupstring(jobtab[i].procs->text);
@@ -3707,7 +3707,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
/* Put the string in the lexer buffer and call the lexer to *
* get the words we have to expand. */
- lexsave();
+ zcontext_save();
lexflags = LEXFLAGS_ZLE;
tmpbuf = (char *)zhalloc(strlen(cc->str) + 5);
sprintf(tmpbuf, "foo %s", cc->str); /* KLUDGE! */
@@ -3725,8 +3725,8 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
noaliases = ona;
strinend();
inpop();
- errflag = 0;
- lexrestore();
+ errflag &= ~ERRFLAG_ERROR;
+ zcontext_restore();
/* Fine, now do full expansion. */
prefork(foo, 0);
if (!errflag) {
@@ -3853,7 +3853,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
yaptr = get_user_var(uv);
if ((tt = cc->explain)) {
tt = dupstring(tt);
- if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) {
+ if ((cc->mask & CC_EXPANDEXPL) && !parsestr(&tt)) {
singsub(&tt);
untokenize(tt);
}
@@ -3873,7 +3873,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
}
} else if ((tt = cc->explain)) {
tt = dupstring(tt);
- if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) {
+ if ((cc->mask & CC_EXPANDEXPL) && !parsestr(&tt)) {
singsub(&tt);
untokenize(tt);
}
diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c
index 5e5ba9f20..f54206619 100644
--- a/Src/Zle/complist.c
+++ b/Src/Zle/complist.c
@@ -780,6 +780,7 @@ clnicezputs(int do_colors, char *s, int ml)
/* Is the screen full? */
if (ml == mlend - 1 && col == zterm_columns - 1) {
mlprinted = ml - oml;
+ free(ums);
return 0;
}
if (t < wptr) {
@@ -804,6 +805,7 @@ clnicezputs(int do_colors, char *s, int ml)
ml++;
if (mscroll && !--mrestlines && (ask = asklistscroll(ml))) {
mlprinted = ml - oml;
+ free(ums);
return ask;
}
col -= zterm_columns;
@@ -1375,7 +1377,7 @@ compprintlist(int showall)
tcout(TCCLEAREOD);
g = ((lasttype && lastg) ? lastg : amatches);
- while (g) {
+ while (g && !errflag) {
char **pp = g->ylist;
#ifdef ZSH_HEAP_DEBUG
@@ -1389,7 +1391,7 @@ compprintlist(int showall)
ml = lastml;
lastused = 1;
}
- while (*e) {
+ while (*e && !errflag) {
if (((*e)->count || (*e)->always) &&
(!listdat.onlyexpl ||
(listdat.onlyexpl & ((*e)->always > 0 ? 2 : 1)))) {
@@ -1469,11 +1471,11 @@ compprintlist(int showall)
nl = nc = g->lins;
- while (n && nl--) {
+ while (n && nl-- && !errflag) {
i = g->cols;
mc = 0;
pq = pp;
- while (n && i--) {
+ while (n && i-- && !errflag) {
if (pq - g->ylist >= g->lcount)
break;
if (compzputs(*pq, mscroll))
@@ -1582,7 +1584,7 @@ compprintlist(int showall)
} else
p = skipnolist(g->matches, showall);
- while (n && nl--) {
+ while (n && nl-- && !errflag) {
if (!lasttype && ml >= mlbeg) {
lasttype = 3;
lastg = g;
@@ -1596,7 +1598,7 @@ compprintlist(int showall)
i = g->cols;
mc = 0;
q = p;
- while (n && i--) {
+ while (n && i-- && !errflag) {
wid = (g->widths ? g->widths[mc] : g->width);
if (!(m = *q)) {
if (clprintm(g, NULL, mc, ml, (!i), wid))
@@ -2059,8 +2061,8 @@ complistmatches(UNUSED(Hookdef dummy), Chdata dat)
i = zterm_columns * listdat.nlines;
free(mtab);
- mtab = (Cmatch **) zalloc(i * sizeof(Cmatch **));
- memset(mtab, 0, i * sizeof(Cmatch **));
+ mtab = (Cmatch **) zalloc(i * sizeof(Cmatch *));
+ memset(mtab, 0, i * sizeof(Cmatch *));
free(mgtab);
mgtab = (Cmgroup *) zalloc(i * sizeof(Cmgroup));
#ifdef DEBUG
@@ -2790,7 +2792,9 @@ domenuselect(Hookdef dummy, Chdata dat)
Menustack s = (Menustack) zhalloc(sizeof(*s));
int ol;
- mode = 0;
+ if (mode == MM_INTER)
+ do_single(*minfo.cur);
+ mode = 0;
s->prev = u;
u = s;
s->line = dupstring(zlemetaline);
@@ -2880,7 +2884,8 @@ domenuselect(Hookdef dummy, Chdata dat)
brend = dupbrinfo(u->brend, &lastbrend, 0);
nbrbeg = u->nbrbeg;
nbrend = u->nbrend;
- origline = u->origline;
+ zsfree(origline);
+ origline = ztrdup(u->origline);
origcs = u->origcs;
origll = u->origll;
strcpy(status, u->status);
@@ -3234,7 +3239,8 @@ domenuselect(Hookdef dummy, Chdata dat)
* don't want that, just what the user typed,
* so restore the information.
*/
- origline = modeline;
+ zsfree(origline);
+ origline = ztrdup(modeline);
origcs = modecs;
origll = modell;
zlemetacs = 0;
diff --git a/Src/Zle/compmatch.c b/Src/Zle/compmatch.c
index 4cd3b9ffe..05ae43ae6 100644
--- a/Src/Zle/compmatch.c
+++ b/Src/Zle/compmatch.c
@@ -1014,6 +1014,7 @@ comp_match(char *pfx, char *sfx, char *w, Patprog cp, Cline *clp, int qu,
Brinfo *bpl, int bcp, Brinfo *bsl, int bcs, int *exact)
{
char *r = NULL;
+ int onoerrs = noerrs;
if (cp) {
/* We have a globcomplete-like pattern, just use that. */
@@ -1033,12 +1034,14 @@ comp_match(char *pfx, char *sfx, char *w, Patprog cp, Cline *clp, int qu,
*/
teststr = dupstring(r);
tokenize(teststr);
+ noerrs = 1;
if (parse_subst_string(teststr))
teststr = r;
else {
remnulargs(teststr);
untokenize(teststr);
}
+ noerrs = onoerrs;
} else
teststr = r;
if (!pattry(cp, teststr))
diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c
index fcceb670c..9f383f4b8 100644
--- a/Src/Zle/compresult.c
+++ b/Src/Zle/compresult.c
@@ -1090,15 +1090,16 @@ do_single(Cmatch m)
}
if (tryit) {
noerrs = 1;
- parsestr(p);
+ p = dupstring(p);
+ parsestr(&p);
singsub(&p);
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
noerrs = ne;
}
} else {
p = (char *) zhalloc(strlen(prpre) + strlen(str) +
strlen(psuf) + 3);
- sprintf(p, "%s%s%s", ((prpre && *prpre) ?
+ sprintf(p, "%s%s%s", (*prpre ?
prpre : "./"), str, psuf);
}
/* And do the stat. */
diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c
index b11c39f25..a81d1ddad 100644
--- a/Src/Zle/computil.c
+++ b/Src/Zle/computil.c
@@ -4060,7 +4060,8 @@ cfp_test_exact(LinkList names, char **accept, char *skipped)
if (sl > PATH_MAX2)
return NULL;
- suf = dyncat(skipped, rembslash(dyncat(compprefix, compsuffix)));
+ suf = dyncat(skipped, rembslash(dyncat(compprefix ? compprefix : "",
+ compsuffix ? compsuffix : "")));
for (node = firstnode(names); node; incnode(node)) {
l = strlen(p = (char *) getdata(node));
diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list
index e3ffe3e8c..b41661a7d 100644
--- a/Src/Zle/iwidgets.list
+++ b/Src/Zle/iwidgets.list
@@ -24,7 +24,7 @@
"backward-kill-word", backwardkillword, ZLE_KILL | ZLE_KEEPSUFFIX
"backward-word", backwardword, 0
"beep", handlefeep, 0
-"beginning-of-buffer-or-history", beginningofbufferorhistory, 0
+"beginning-of-buffer-or-history", beginningofbufferorhistory, ZLE_LINEMOVE
"beginning-of-history", beginningofhistory, 0
"beginning-of-line", beginningofline, 0
"beginning-of-line-hist", beginningoflinehist, 0
@@ -41,11 +41,12 @@
"digit-argument", digitargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND
"down-case-word", downcaseword, 0
"down-history", downhistory, 0
+"down-line", downline, ZLE_LINEMOVE | ZLE_LASTCOL
"down-line-or-history", downlineorhistory, ZLE_LINEMOVE | ZLE_LASTCOL
"down-line-or-search", downlineorsearch, ZLE_LINEMOVE | ZLE_LASTCOL
"emacs-backward-word", emacsbackwardword, 0
"emacs-forward-word", emacsforwardword, 0
-"end-of-buffer-or-history", endofbufferorhistory, 0
+"end-of-buffer-or-history", endofbufferorhistory, ZLE_LINEMOVE
"end-of-history", endofhistory, 0
"end-of-line", endofline, 0
"end-of-line-hist", endoflinehist, 0
@@ -88,6 +89,7 @@
"push-input", pushinput, 0
"push-line", pushline, 0
"push-line-or-edit", pushlineoredit, 0
+"put-replace-selection", putreplaceselection, ZLE_KEEPSUFFIX
"quoted-insert", quotedinsert, ZLE_MENUCMP | ZLE_KEEPSUFFIX
"quote-line", quoteline, 0
"quote-region", quoteregion, 0
@@ -98,13 +100,19 @@
"reset-prompt", resetprompt, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
"reverse-menu-complete", reversemenucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
"run-help", processcmd, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
+"select-a-word", selectword, ZLE_KEEPSUFFIX
+"select-in-word", selectword, ZLE_KEEPSUFFIX
+"select-a-blank-word", selectword, ZLE_KEEPSUFFIX
+"select-in-blank-word", selectword, ZLE_KEEPSUFFIX
+"select-a-shell-word", selectargument, ZLE_KEEPSUFFIX
+"select-in-shell-word", selectargument, ZLE_KEEPSUFFIX
"self-insert", selfinsert, ZLE_MENUCMP | ZLE_KEEPSUFFIX
"self-insert-unmeta", selfinsertunmeta, ZLE_MENUCMP | ZLE_KEEPSUFFIX
"send-break", sendbreak, 0
"set-mark-command", setmarkcommand, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
-"split-undo", splitundo, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_NOTCOMMAND
+"split-undo", splitundo, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND
"spell-word", spellword, 0
-"set-local-history", setlocalhistory, 0
+"set-local-history", setlocalhistory, ZLE_LASTCOL
"transpose-chars", transposechars, 0
"transpose-words", transposewords, 0
"undefined-key", undefinedkey, 0
@@ -112,6 +120,7 @@
"universal-argument", universalargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND
"up-case-word", upcaseword, 0
"up-history", uphistory, 0
+"up-line", upline, ZLE_LINEMOVE | ZLE_LASTCOL
"up-line-or-history", uplineorhistory, ZLE_LINEMOVE | ZLE_LASTCOL
"up-line-or-search", uplineorsearch, ZLE_LINEMOVE | ZLE_LASTCOL
"vi-add-eol", viaddeol, 0
@@ -119,20 +128,22 @@
"vi-backward-blank-word", vibackwardblankword, 0
"vi-backward-char", vibackwardchar, 0
"vi-backward-delete-char", vibackwarddeletechar, ZLE_KEEPSUFFIX
-"vi-backward-kill-word", vibackwardkillword, ZLE_KILL | ZLE_KEEPSUFFIX
+"vi-backward-kill-word", vibackwardkillword, ZLE_KEEPSUFFIX
"vi-backward-word", vibackwardword, 0
+"vi-backward-word-end", vibackwardwordend, 0
+"vi-backward-blank-word-end", vibackwardblankwordend, 0
"vi-beginning-of-line", vibeginningofline, 0
-"vi-caps-lock-panic", vicapslockpanic, 0
-"vi-change", vichange, 0
+"vi-caps-lock-panic", vicapslockpanic, ZLE_LASTCOL
+"vi-change", vichange, ZLE_LASTCOL | ZLE_VIOPER
"vi-change-eol", vichangeeol, 0
"vi-change-whole-line", vichangewholeline, 0
"vi-cmd-mode", vicmdmode, 0
-"vi-delete", videlete, ZLE_KILL | ZLE_KEEPSUFFIX
+"vi-delete", videlete, ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_VIOPER
"vi-delete-char", videletechar, ZLE_KEEPSUFFIX
"vi-digit-or-beginning-of-line", vidigitorbeginningofline, 0
"vi-down-line-or-history", vidownlineorhistory, ZLE_LINEMOVE
"vi-end-of-line", viendofline, ZLE_LASTCOL
-"vi-fetch-history", vifetchhistory, 0
+"vi-fetch-history", vifetchhistory, ZLE_LINEMOVE
"vi-find-next-char", vifindnextchar, 0
"vi-find-next-char-skip", vifindnextcharskip, 0
"vi-find-prev-char", vifindprevchar, 0
@@ -145,22 +156,22 @@
"vi-forward-word-end", viforwardwordend, 0
"vi-goto-column", vigotocolumn, 0
"vi-goto-mark", vigotomark, 0
-"vi-goto-mark-line", vigotomarkline, 0
+"vi-goto-mark-line", vigotomarkline, ZLE_LINEMOVE
"vi-history-search-backward", vihistorysearchbackward, 0
"vi-history-search-forward", vihistorysearchforward, 0
-"vi-indent", viindent, 0
+"vi-indent", viindent, ZLE_LASTCOL | ZLE_VIOPER
"vi-insert", viinsert, 0
"vi-insert-bol", viinsertbol, 0
"vi-join", vijoin, 0
-"vi-kill-eol", vikilleol, ZLE_KILL | ZLE_KEEPSUFFIX
-"vi-kill-line", vikillline, ZLE_KILL | ZLE_KEEPSUFFIX
+"vi-kill-eol", vikilleol, ZLE_KEEPSUFFIX
+"vi-kill-line", vikillline, ZLE_KEEPSUFFIX
"vi-match-bracket", vimatchbracket, 0
"vi-open-line-above", viopenlineabove, 0
"vi-open-line-below", viopenlinebelow, 0
-"vi-oper-swap-case", vioperswapcase, 0
+"vi-oper-swap-case", vioperswapcase, ZLE_LASTCOL | ZLE_VIOPER
"vi-pound-insert", vipoundinsert, 0
-"vi-put-after", viputafter, ZLE_YANK | ZLE_KEEPSUFFIX
-"vi-put-before", viputbefore, ZLE_YANK | ZLE_KEEPSUFFIX
+"vi-put-after", viputafter, ZLE_YANKAFTER | ZLE_KEEPSUFFIX
+"vi-put-before", viputbefore, ZLE_YANKBEFORE | ZLE_KEEPSUFFIX
"vi-quoted-insert", viquotedinsert, ZLE_MENUCMP | ZLE_KEEPSUFFIX
"vi-repeat-change", virepeatchange, 0
"vi-repeat-find", virepeatfind, 0
@@ -172,15 +183,17 @@
"vi-set-buffer", visetbuffer, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
"vi-set-mark", visetmark, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
"vi-substitute", visubstitute, 0
-"vi-swap-case", viswapcase, 0
+"vi-swap-case", viswapcase, ZLE_LASTCOL
"vi-undo-change", viundochange, ZLE_KEEPSUFFIX
-"vi-unindent", viunindent, 0
+"vi-unindent", viunindent, ZLE_LASTCOL | ZLE_VIOPER
"vi-up-line-or-history", viuplineorhistory, ZLE_LINEMOVE
-"vi-yank", viyank, 0
+"vi-yank", viyank, ZLE_LASTCOL | ZLE_VIOPER
"vi-yank-eol", viyankeol, 0
"vi-yank-whole-line", viyankwholeline, 0
+"visual-line-mode", visuallinemode, ZLE_MENUCMP | ZLE_LASTCOL
+"visual-mode", visualmode, ZLE_MENUCMP | ZLE_LASTCOL
"what-cursor-position", whatcursorposition, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
"where-is", whereis, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
"which-command", processcmd, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
-"yank", yank, ZLE_YANK | ZLE_KEEPSUFFIX
-"yank-pop", yankpop, ZLE_YANK | ZLE_KEEPSUFFIX
+"yank", yank, ZLE_YANKBEFORE | ZLE_KEEPSUFFIX
+"yank-pop", yankpop, ZLE_KEEPSUFFIX | ZLE_NOTCOMMAND
diff --git a/Src/Zle/textobjects.c b/Src/Zle/textobjects.c
new file mode 100644
index 000000000..9b3277a97
--- /dev/null
+++ b/Src/Zle/textobjects.c
@@ -0,0 +1,322 @@
+/*
+ * textobjects.c - ZLE module implementing Vim style text objects
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 2014 Oliver Kiddle
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Oliver Kiddle or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Oliver Kiddle and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Oliver Kiddle and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose. The software
+ * provided hereunder is on an "as is" basis, and Oliver Kiddle and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "zle.mdh"
+#include "textobjects.pro"
+
+/* class of character: 0 is whitespace, 1 is word character, 2 is other */
+static int
+wordclass(ZLE_CHAR_T x)
+{
+ return (ZC_iblank(x) ? 0 : ((ZC_ialnum(x) || (ZWC('_') == x)) ? 1 : 2));
+}
+
+static int
+blankwordclass(ZLE_CHAR_T x)
+{
+ return (ZC_iblank(x) ? 0 : 1);
+}
+
+/**/
+int
+selectword(UNUSED(char **args))
+{
+ int n = zmult;
+ int all = (bindk == t_selectaword || bindk == t_selectablankword);
+ int (*viclass)(ZLE_CHAR_T) = (bindk == t_selectaword ||
+ bindk == t_selectinword) ? wordclass : blankwordclass;
+ int sclass = viclass(zleline[zlecs]);
+ int doblanks = all && sclass;
+
+ if (!invicmdmode()) {
+ region_active = 1;
+ mark = zlecs;
+ }
+ if (!region_active || zlecs == mark) {
+ /* search back to first character of same class as the start position
+ * also stop at the beginning of the line */
+ mark = zlecs;
+ while (mark) {
+ int pos = mark;
+ DECPOS(pos);
+ if (zleline[pos] == ZWC('\n') || viclass(zleline[pos]) != sclass)
+ break;
+ mark = pos;
+ }
+ /* similarly scan forward over characters of the same class */
+ while (zlecs < zlell) {
+ INCCS();
+ int pos = zlecs;
+ /* single newlines within blanks are included */
+ if (all && !sclass && pos < zlell && zleline[pos] == ZWC('\n'))
+ INCPOS(pos);
+
+ if (zleline[pos] == ZWC('\n') || viclass(zleline[pos]) != sclass)
+ break;
+ }
+
+ if (all) {
+ int nclass = viclass(zleline[zlecs]);
+ /* if either start or new position is blank advance over
+ * a new block of characters of a common type */
+ if (!nclass || !sclass) {
+ while (zlecs < zlell) {
+ INCCS();
+ if (zleline[zlecs] == ZWC('\n') ||
+ viclass(zleline[zlecs]) != nclass)
+ break;
+ }
+ if (n < 2)
+ doblanks = 0;
+ }
+ }
+ } else {
+ /* For visual mode, advance one char so repeated
+ * invocations select subsequent words */
+ if (zlecs > mark) {
+ if (zlecs < zlell)
+ INCCS();
+ } else if (zlecs)
+ DECCS();
+ if (zlecs < mark) {
+ /* visual mode with the cursor before the mark: move cursor back */
+ while (n-- > 0) {
+ int pos = zlecs;
+ /* first over blanks */
+ if (all && (!viclass(zleline[pos]) ||
+ zleline[pos] == ZWC('\n'))) {
+ all = 0;
+ while (pos) {
+ DECPOS(pos);
+ if (zleline[pos] == ZWC('\n'))
+ break;
+ zlecs = pos;
+ if (viclass(zleline[pos]))
+ break;
+ }
+ } else if (zlecs && zleline[zlecs] == ZWC('\n')) {
+ /* for in widgets pass over one newline */
+ DECPOS(pos);
+ if (zleline[pos] != ZWC('\n'))
+ zlecs = pos;
+ }
+ pos = zlecs;
+ sclass = viclass(zleline[zlecs]);
+ /* now retreat over non-blanks */
+ while (zleline[pos] != ZWC('\n') &&
+ viclass(zleline[pos]) == sclass) {
+ zlecs = pos;
+ if (!pos) {
+ zlecs = 0;
+ break;
+ }
+ DECPOS(pos);
+ }
+ /* blanks again but only if there were none first time */
+ if (all && zlecs) {
+ pos = zlecs;
+ DECPOS(pos);
+ if (!viclass(zleline[pos])) {
+ while (pos) {
+ DECPOS(pos);
+ if (zleline[pos] == ZWC('\n') ||
+ viclass(zleline[pos]))
+ break;
+ zlecs = pos;
+ }
+ }
+ }
+ }
+ return 0;
+ }
+ n++;
+ doblanks = 0;
+ }
+ region_active = !!region_active; /* force to character wise */
+
+ /* for each digit argument, advance over further block of one class */
+ while (--n > 0) {
+ if (zlecs < zlell && zleline[zlecs] == ZWC('\n'))
+ INCCS();
+ sclass = viclass(zleline[zlecs]);
+ while (zlecs < zlell) {
+ INCCS();
+ if (zleline[zlecs] == ZWC('\n') ||
+ viclass(zleline[zlecs]) != sclass)
+ break;
+ }
+ /* for 'a' widgets, advance extra block if either consists of blanks */
+ if (all) {
+ if (zlecs < zlell && zleline[zlecs] == ZWC('\n'))
+ INCCS();
+ if (!sclass || !viclass(zleline[zlecs]) ) {
+ sclass = viclass(zleline[zlecs]);
+ if (n == 1 && !sclass)
+ doblanks = 0;
+ while (zlecs < zlell) {
+ INCCS();
+ if (zleline[zlecs] == ZWC('\n') ||
+ viclass(zleline[zlecs]) != sclass)
+ break;
+ }
+ }
+ }
+ }
+
+ /* if we didn't remove blanks at either end we remove some at the start */
+ if (doblanks) {
+ int pos = mark;
+ while (pos) {
+ DECPOS(pos);
+ /* don't remove blanks at the start of the line, i.e indentation */
+ if (zleline[pos] == ZWC('\n'))
+ break;
+ if (!ZC_iblank(zleline[pos])) {
+ INCPOS(pos);
+ mark = pos;
+ break;
+ }
+ }
+ }
+ /* Adjustment: vi operators don't include the cursor position, in insert
+ * or emacs mode the region also doesn't but for vi visual mode it is
+ * included. */
+ if (zlecs && zlecs > mark && !virangeflag)
+ DECCS();
+
+ return 0;
+}
+
+/**/
+int
+selectargument(UNUSED(char **args))
+{
+ int ne = noerrs, ocs = zlemetacs;
+ int owb = wb, owe= we, oadx = addedx, ona = noaliases;
+ char *p;
+ int ll, cs;
+ char *linein;
+ int wend = 0, wcur = 0;
+ int n = zmult;
+ int *wstarts;
+ int tmpsz;
+
+ if (n < 1 || 2*n > zlell + 1)
+ return 1;
+
+ /* if used from emacs mode enable the region */
+ if (!invicmdmode()) {
+ region_active = 1;
+ mark = zlecs;
+ }
+
+ wstarts = (int *) zhalloc(n * sizeof(int));
+ memset(wstarts, 0, n * sizeof(int));
+
+ addedx = 0;
+ noerrs = 1;
+ zcontext_save();
+ lexflags = LEXFLAGS_ACTIVE;
+ linein = zlegetline(&ll, &cs);
+ zlemetall = ll;
+ zlemetacs = cs;
+
+ if (!isfirstln && chline) {
+ p = (char *) zhalloc(hptr - chline + zlemetall + 2);
+ memcpy(p, chline, hptr - chline);
+ memcpy(p + (hptr - chline), linein, ll);
+ p[(hptr - chline) + ll] = '\0';
+ inpush(p, 0, NULL);
+ zlemetacs += hptr - chline;
+ } else {
+ p = (char *) zhalloc(ll + 1);
+ memcpy(p, linein, ll);
+ p[ll] = '\0';
+ inpush(p, 0, NULL);
+ }
+ if (zlemetacs)
+ zlemetacs--;
+ strinbeg(0);
+ noaliases = 1;
+ do {
+ wstarts[wcur++] = wend;
+ wcur %= n;
+ ctxtlex();
+ if (tok == ENDINPUT || tok == LEXERR)
+ break;
+ wend = zlemetall - inbufct;
+ } while (tok != ENDINPUT && tok != LEXERR && wend <= zlemetacs);
+ noaliases = ona;
+ strinend();
+ inpop();
+ errflag &= ~ERRFLAG_ERROR;
+ noerrs = ne;
+ zcontext_restore();
+ zlemetacs = ocs;
+ wb = owb;
+ we = owe;
+ addedx = oadx;
+
+ /* convert offsets for mark and zlecs back to ZLE internal format */
+ linein[wend] = '\0'; /* a bit of a hack to get two offsets */
+ free(stringaszleline(linein, wstarts[wcur], &zlecs, &tmpsz, &mark));
+ free(linein);
+
+ if (bindk == t_selectinshellword) {
+ ZLE_CHAR_T *match = ZWS("`\'\"");
+ ZLE_CHAR_T *lmatch = ZWS("\'({"), *rmatch = ZWS("\')}");
+ ZLE_CHAR_T *ematch = match, *found;
+ int start, end = zlecs;
+ /* for 'in' widget, don't include initial blanks ... */
+ while (mark < zlecs && ZC_iblank(zleline[mark]))
+ INCPOS(mark);
+ /* ... or a matching pair of quotes */
+ start = mark;
+ if (zleline[start] == ZWC('$')) {
+ match = lmatch;
+ ematch = rmatch;
+ INCPOS(start);
+ }
+ found = ZS_strchr(match, zleline[start]);
+ if (found) {
+ DECPOS(end);
+ if (zleline[end] == ematch[found-match]) {
+ zlecs = end;
+ INCPOS(start);
+ mark = start;
+ }
+ }
+ }
+
+ /* Adjustment: vi operators don't include the cursor position */
+ if (!virangeflag)
+ DECCS();
+
+ return 0;
+}
diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h
index 870e2149d..3c652909e 100644
--- a/Src/Zle/zle.h
+++ b/Src/Zle/zle.h
@@ -203,13 +203,16 @@ struct widget {
#define WIDGET_INT (1<<0) /* widget is internally implemented */
#define WIDGET_NCOMP (1<<1) /* new style completion widget */
#define ZLE_MENUCMP (1<<2) /* DON'T invalidate completion list */
-#define ZLE_YANK (1<<3)
-#define ZLE_LINEMOVE (1<<4) /* command is a line-oriented movement */
-#define ZLE_LASTCOL (1<<5) /* command maintains lastcol correctly */
-#define ZLE_KILL (1<<6)
-#define ZLE_KEEPSUFFIX (1<<7) /* DON'T remove added suffix */
-#define ZLE_NOTCOMMAND (1<<8) /* widget should not alter lastcmd */
-#define ZLE_ISCOMP (1<<9) /* usable for new style completion */
+#define ZLE_YANKAFTER (1<<3)
+#define ZLE_YANKBEFORE (1<<4)
+#define ZLE_YANK (ZLE_YANKAFTER | ZLE_YANKBEFORE)
+#define ZLE_LINEMOVE (1<<5) /* command is a line-oriented movement */
+#define ZLE_VIOPER (1<<6) /* widget reads further keys so wait if prefix */
+#define ZLE_LASTCOL (1<<7) /* command maintains lastcol correctly */
+#define ZLE_KILL (1<<8)
+#define ZLE_KEEPSUFFIX (1<<9) /* DON'T remove added suffix */
+#define ZLE_NOTCOMMAND (1<<10) /* widget should not alter lastcmd */
+#define ZLE_ISCOMP (1<<11) /* usable for new style completion */
/* thingies */
@@ -240,6 +243,9 @@ struct modifier {
#define MOD_VIBUF (1<<2) /* a vi cut buffer has been selected */
#define MOD_VIAPP (1<<3) /* appending to the vi cut buffer */
#define MOD_NEG (1<<4) /* last command was negate argument */
+#define MOD_NULL (1<<5) /* throw away text for the vi cut buffer */
+#define MOD_CHAR (1<<6) /* force character-wise movement */
+#define MOD_LINE (1<<7) /* force line-wise movement */
/* current modifier status */
@@ -256,6 +262,7 @@ struct modifier {
* of visible characters directly input by
* the user.
*/
+#define CUT_YANK (1<<3) /* vi yank: use register 0 instead of 1-9 */
/* undo system */
diff --git a/Src/Zle/zle.mdd b/Src/Zle/zle.mdd
index c6e4d11c2..dd69eff2c 100644
--- a/Src/Zle/zle.mdd
+++ b/Src/Zle/zle.mdd
@@ -7,7 +7,8 @@ autofeatures="b:bindkey b:vared b:zle"
objects="zle_bindings.o zle_hist.o zle_keymap.o zle_main.o \
zle_misc.o zle_move.o zle_params.o zle_refresh.o \
-zle_thingy.o zle_tricky.o zle_utils.o zle_vi.o zle_word.o"
+zle_thingy.o zle_tricky.o zle_utils.o zle_vi.o zle_word.o \
+textobjects.o"
headers="zle.h zle_things.h"
diff --git a/Src/Zle/zle_bindings.c b/Src/Zle/zle_bindings.c
index 682691347..2ae8c8764 100644
--- a/Src/Zle/zle_bindings.c
+++ b/Src/Zle/zle_bindings.c
@@ -278,7 +278,7 @@ int viinsbind[32] = {
/* ^U */ z_vikillline,
/* ^V */ z_viquotedinsert,
/* ^W */ z_vibackwardkillword,
- /* ^X */ z_selfinsert,
+ /* ^X */ z_undefinedkey,
/* ^Y */ z_selfinsert,
/* ^Z */ z_selfinsert,
/* ^[ */ z_vicmdmode,
@@ -308,7 +308,7 @@ int vicmdbind[128] = {
/* ^O */ z_undefinedkey,
/* ^P */ z_uphistory,
/* ^Q */ z_undefinedkey,
- /* ^R */ z_redisplay,
+ /* ^R */ z_redo,
/* ^S */ z_undefinedkey,
/* ^T */ z_undefinedkey,
/* ^U */ z_undefinedkey,
@@ -376,7 +376,7 @@ int vicmdbind[128] = {
/* S */ z_vichangewholeline,
/* T */ z_vifindprevcharskip,
/* U */ z_undefinedkey,
- /* V */ z_undefinedkey,
+ /* V */ z_visuallinemode,
/* W */ z_viforwardblankword,
/* X */ z_vibackwarddeletechar,
/* Y */ z_viyankwholeline,
@@ -407,8 +407,8 @@ int vicmdbind[128] = {
/* r */ z_vireplacechars,
/* s */ z_visubstitute,
/* t */ z_vifindnextcharskip,
- /* u */ z_viundochange,
- /* v */ z_undefinedkey,
+ /* u */ z_undo,
+ /* v */ z_visualmode,
/* w */ z_viforwardword,
/* x */ z_videletechar,
/* y */ z_viyank,
diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c
index 44b39d186..cc66f99ae 100644
--- a/Src/Zle/zle_hist.c
+++ b/Src/Zle/zle_hist.c
@@ -227,14 +227,14 @@ uphistory(UNUSED(char **args))
}
/**/
-static int
-upline(void)
+int
+upline(char **args)
{
int n = zmult;
if (n < 0) {
zmult = -zmult;
- n = -downline();
+ n = -downline(args);
zmult = -zmult;
return n;
}
@@ -270,7 +270,7 @@ int
uplineorhistory(char **args)
{
int ocs = zlecs;
- int n = upline();
+ int n = upline(args);
if (n) {
int m = zmult, ret;
@@ -300,7 +300,7 @@ int
uplineorsearch(char **args)
{
int ocs = zlecs;
- int n = upline();
+ int n = upline(args);
if (n) {
int m = zmult, ret;
@@ -316,14 +316,14 @@ uplineorsearch(char **args)
}
/**/
-static int
-downline(void)
+int
+downline(char **args)
{
int n = zmult;
if (n < 0) {
zmult = -zmult;
- n = -upline();
+ n = -upline(args);
zmult = -zmult;
return n;
}
@@ -358,7 +358,7 @@ int
downlineorhistory(char **args)
{
int ocs = zlecs;
- int n = downline();
+ int n = downline(args);
if (n) {
int m = zmult, ret;
@@ -388,7 +388,7 @@ int
downlineorsearch(char **args)
{
int ocs = zlecs;
- int n = downline();
+ int n = downline(args);
if (n) {
int m = zmult, ret;
@@ -821,6 +821,8 @@ pushline(UNUSED(char **args))
zpushnode(bufstack, zlelineasstring(zleline, zlell, 0, NULL, NULL, 0));
while (--n)
zpushnode(bufstack, ztrdup(""));
+ if (invicmdmode())
+ INCCS();
stackcs = zlecs;
*zleline = ZWC('\0');
zlell = zlecs = 0;
@@ -851,8 +853,10 @@ pushlineoredit(char **args)
free(zhline);
}
ret = pushline(args);
- if (!isfirstln)
- errflag = done = 1;
+ if (!isfirstln) {
+ errflag |= ERRFLAG_ERROR|ERRFLAG_INT;
+ done = 1;
+ }
clearlist = 1;
return ret;
}
diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
index e21e769bd..c6fae251d 100644
--- a/Src/Zle/zle_keymap.c
+++ b/Src/Zle/zle_keymap.c
@@ -503,7 +503,18 @@ selectkeymap(char *name, int fb)
mod_export void
selectlocalmap(Keymap m)
{
+ Keymap oldm = localkeymap;
localkeymap = m;
+ if (oldm && !m)
+ {
+ /*
+ * No local keymap; so we are returning to the global map. If
+ * the user ^Ced in the local map, they probably just want to go
+ * back to normal editing. So remove the interrupt error
+ * status.
+ */
+ errflag &= ~ERRFLAG_INT;
+ }
}
/* Reopen the currently selected keymap, in case it got deleted. This *
@@ -1191,7 +1202,7 @@ init_keymaps(void)
{
createkeymapnamtab();
default_bindings();
- keybuf = (char *)zalloc(keybufsz);
+ keybuf = (char *)zshcalloc(keybufsz);
lastnamed = refthingy(t_undefinedkey);
}
@@ -1277,8 +1288,10 @@ default_bindings(void)
Keymap vmap = newkeymap(NULL, "viins");
Keymap emap = newkeymap(NULL, "emacs");
Keymap amap = newkeymap(NULL, "vicmd");
+ Keymap oppmap = newkeymap(NULL, "viopp");
+ Keymap vismap = newkeymap(NULL, "visual");
Keymap smap = newkeymap(NULL, ".safe");
- Keymap vimaps[2], kptr;
+ Keymap vimaps[2], vilmaps[2], kptr;
char buf[3], *ed;
int i;
@@ -1332,6 +1345,36 @@ default_bindings(void)
add_cursor_key(kptr, TCLEFTCURSOR, t_vibackwardchar, 'D');
add_cursor_key(kptr, TCRIGHTCURSOR, t_viforwardchar, 'C');
}
+ vilmaps[0] = oppmap;
+ vilmaps[1] = vismap;
+ for (i = 0; i < 2; i++) {
+ /* vi visual selection and operator pending local maps */
+ kptr = vilmaps[i];
+ add_cursor_key(kptr, TCUPCURSOR, t_upline, 'A');
+ add_cursor_key(kptr, TCDOWNCURSOR, t_downline, 'B');
+ bindkey(kptr, "k", refthingy(t_upline), NULL);
+ bindkey(kptr, "j", refthingy(t_downline), NULL);
+ bindkey(kptr, "aa", refthingy(t_selectashellword), NULL);
+ bindkey(kptr, "ia", refthingy(t_selectinshellword), NULL);
+ bindkey(kptr, "aw", refthingy(t_selectaword), NULL);
+ bindkey(kptr, "iw", refthingy(t_selectinword), NULL);
+ bindkey(kptr, "aW", refthingy(t_selectablankword), NULL);
+ bindkey(kptr, "iW", refthingy(t_selectinblankword), NULL);
+ }
+ /* escape in operator pending cancels the operation */
+ bindkey(oppmap, "\33", refthingy(t_vicmdmode), NULL);
+ bindkey(vismap, "o", refthingy(t_exchangepointandmark), NULL);
+ bindkey(vismap, "p", refthingy(t_putreplaceselection), NULL);
+ bindkey(vismap, "x", refthingy(t_videlete), NULL);
+ bindkey(vismap, "~", refthingy(t_vioperswapcase), NULL);
+
+ /* vi mode: some common vim bindings */
+ bindkey(amap, "ga", refthingy(t_whatcursorposition), NULL);
+ bindkey(amap, "ge", refthingy(t_vibackwardwordend), NULL);
+ bindkey(amap, "gE", refthingy(t_vibackwardblankwordend), NULL);
+ bindkey(amap, "gg", refthingy(t_beginningofbufferorhistory), NULL);
+ bindkey(amap, "g~", refthingy(t_vioperswapcase), NULL);
+ bindkey(amap, "g~~", NULL, "g~g~");
/* emacs mode: arrow keys */
add_cursor_key(emap, TCUPCURSOR, t_uplineorhistory, 'A');
@@ -1373,6 +1416,8 @@ default_bindings(void)
linkkeymap(vmap, "viins", 0);
linkkeymap(emap, "emacs", 0);
linkkeymap(amap, "vicmd", 0);
+ linkkeymap(oppmap, "viopp", 0);
+ linkkeymap(vismap, "visual", 0);
linkkeymap(smap, ".safe", 1);
if (((ed = zgetenv("VISUAL")) && strstr(ed, "vi")) ||
((ed = zgetenv("EDITOR")) && strstr(ed, "vi")))
@@ -1408,6 +1453,7 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp)
Thingy func = t_undefinedkey;
char *str = NULL;
int lastlen = 0, lastc = lastchar;
+ int timeout = 0;
keybuflen = 0;
keybuf[0] = 0;
@@ -1425,7 +1471,7 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp)
* argument to bindkey is in the correct form for the locale.
* That's beyond our control.
*/
- while(getkeybuf(!!lastlen) != EOF) {
+ while(getkeybuf(timeout) != EOF) {
char *s;
Thingy f;
int loc = !!localkeymap;
@@ -1435,7 +1481,7 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp)
loc = ((f = keybind(localkeymap, keybuf, &s)) != t_undefinedkey);
ispfx = keyisprefix(localkeymap, keybuf);
}
- if (!loc)
+ if (!loc && !ispfx)
f = keybind(km, keybuf, &s);
ispfx |= keyisprefix(km, keybuf);
@@ -1444,6 +1490,11 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp)
func = f;
str = s;
lastc = lastchar;
+
+ /* can be patient with vi commands that need a motion operator: *
+ * they wait till a key is pressed for the movement anyway */
+ timeout = !(!virangeflag && !region_active && f && f->widget &&
+ f->widget->flags & ZLE_VIOPER);
}
if (!ispfx)
break;
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 442c31995..cec44c0ed 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -187,10 +187,6 @@ mod_export char *zlenoargs[1] = { NULL };
static char **raw_lp, **raw_rp;
-#ifdef FIONREAD
-static int delayzsetterm;
-#endif
-
/*
* File descriptors we are watching as well as the terminal fd.
* These are all for reading; we don't watch for writes or exceptions.
@@ -210,9 +206,6 @@ mod_export void
zsetterm(void)
{
struct ttyinfo ti;
-#if defined(FIONREAD)
- int val;
-#endif
if (fetchttyinfo) {
/*
@@ -224,30 +217,6 @@ zsetterm(void)
fetchttyinfo = 0;
}
-#if defined(FIONREAD)
- ioctl(SHTTY, FIONREAD, (char *)&val);
- if (val) {
- /*
- * Problems can occur on some systems when switching from
- * canonical to non-canonical input. The former is usually
- * set while running programmes, but the latter is necessary
- * for zle. If there is input in canonical mode, then we
- * need to read it without setting up the terminal. Furthermore,
- * while that input gets processed there may be more input
- * being typed (i.e. further typeahead). This means that
- * we can't set up the terminal for zle *at all* until
- * we are sure there is no more typeahead to come. So
- * if there is typeahead, we set the flag delayzsetterm.
- * Then getbyte() performs another FIONREAD call; if that is
- * 0, we have finally used up all the typeahead, and it is
- * safe to alter the terminal, which we do at that point.
- */
- delayzsetterm = 1;
- return;
- } else
- delayzsetterm = 0;
-#endif
-
/* sanitize the tty */
#ifdef HAS_TIO
shttyinfo.tio.c_lflag |= ICANON | ECHO;
@@ -343,7 +312,7 @@ zsetterm(void)
ti.ltchars.t_dsuspc = ti.ltchars.t_lnextc = -1;
#endif
-#if defined(TTY_NEEDS_DRAINING) && defined(TIOCOUTQ) && defined(HAVE_SELECT)
+#if defined(TIOCOUTQ) && defined(HAVE_SELECT)
if (baud) { /**/
int n = 0;
@@ -541,11 +510,7 @@ raw_getbyte(long do_keytmout, char *cptr)
* timeouts may be external, so we may have both a permanent watched
* fd and a long-term timeout.
*/
- if ((nwatch || tmout.tp != ZTM_NONE)
-#ifdef FIONREAD
- && ! delayzsetterm
-#endif
- ) {
+ if ((nwatch || tmout.tp != ZTM_NONE)) {
#if defined(HAVE_SELECT) || defined(HAVE_POLL)
int i, errtry = 0, selret;
# ifdef HAVE_POLL
@@ -779,7 +744,7 @@ raw_getbyte(long do_keytmout, char *cptr)
}
if (errflag) {
/* No sensible way of handling errors here */
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
/*
* Paranoia: don't run the hooks again this
* time.
@@ -883,14 +848,6 @@ getbyte(long do_keytmout, int *timeout)
if (kungetct)
ret = STOUC(kungetbuf[--kungetct]);
else {
-#ifdef FIONREAD
- if (delayzsetterm) {
- int val;
- ioctl(SHTTY, FIONREAD, (char *)&val);
- if (!val)
- zsetterm();
- }
-#endif
for (;;) {
int q = queue_signal_level();
dont_queue_signals();
@@ -925,7 +882,7 @@ getbyte(long do_keytmout, int *timeout)
die = 0;
if (!errflag && !retflag && !breaks && !exit_pending)
continue;
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
breaks = obreaks;
errno = old_errno;
return lastchar = EOF;
@@ -1067,6 +1024,7 @@ getrestchar(int inchar)
void
zlecore(void)
{
+ Keymap km;
#if !defined(HAVE_POLL) && defined(HAVE_SELECT)
struct timeval tv;
fd_set foofd;
@@ -1088,8 +1046,10 @@ zlecore(void)
statusline = NULL;
vilinerange = 0;
reselectkeymap();
- selectlocalmap(NULL);
+ selectlocalmap(invicmdmode() && region_active && (km = openkeymap("visual"))
+ ? km : NULL);
bindk = getkeycmd();
+ selectlocalmap(NULL);
if (bindk) {
if (!zlell && isfirstln && !(zlereadflags & ZLRF_IGNOREEOF) &&
lastchar == eofchar) {
@@ -1115,7 +1075,7 @@ zlecore(void)
DECCS();
handleundo();
} else {
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
break;
}
#ifdef HAVE_POLL
@@ -1273,6 +1233,10 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
zleactive = 1;
resetneeded = 1;
+ /*
+ * Start of the main zle read.
+ * Fully reset error conditions, including user interrupt.
+ */
errflag = retflag = 0;
lastcol = -1;
initmodifier(&zmod);
@@ -1289,7 +1253,9 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
zlecore();
if (errflag)
- setsparam("ZLE_LINE_ABORTED", zlegetline(NULL, NULL));
+ setsparam((zlecontext == ZLCON_VARED) ?
+ "ZLE_VARED_ABORTED" :
+ "ZLE_LINE_ABORTED", zlegetline(NULL, NULL));
if (done && !exit_pending && !errflag)
zlecallhook(finish, NULL);
@@ -1698,7 +1664,7 @@ bin_vared(char *name, char **args, Options ops, UNUSED(int func))
}
if (!t || errflag) {
/* error in editing */
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
breaks = obreaks;
if (t)
zsfree(t);
@@ -1818,7 +1784,7 @@ recursiveedit(UNUSED(char **args))
zrefresh();
zlecore();
- locerror = errflag;
+ locerror = errflag ? 1 : 0;
errflag = done = eofsent = 0;
return locerror;
@@ -2128,7 +2094,7 @@ finish_(UNUSED(Module m))
free(kring[i].buf);
zfree(kring, kringsize * sizeof(struct cutbuffer));
}
- for(i = 35; i--; )
+ for(i = 36; i--; )
zfree(vibuf[i].buf, vibuf[i].len);
/* editor entry points */
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index 9bc1cf6f5..4669ef2ad 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -47,13 +47,13 @@ doinsert(ZLE_STRING_T zstr, int len)
iremovesuffix(c1, 0);
invalidatelist();
- if (insmode)
+ /* In overwrite mode, don't replace newlines. */
+ if (insmode || zleline[zlecs] == ZWC('\n'))
spaceinline(m * len);
else
-#ifdef MULTIBYTE_SUPPORT
{
int pos = zlecs, diff, i;
-
+#ifdef MULTIBYTE_SUPPORT
/*
* Calculate the number of character positions we are
* going to be using. The algorithm is that
@@ -68,15 +68,18 @@ doinsert(ZLE_STRING_T zstr, int len)
* useful there anyway and this doesn't cause any
* particular harm.
*/
- for (i = 0, count = 0; i < len; i++) {
+ for (i = 0, count = 0; i < len * m; i++) {
if (!IS_COMBINING(zstr[i]))
count++;
}
+#else
+ count = len * m;
+#endif
/*
- * Ensure we replace a complete combining character
- * for each character we overwrite.
+ * Ensure we replace a complete combining characterfor each
+ * character we overwrite. Switch to inserting at first newline.
*/
- for (i = count; pos < zlell && i--; ) {
+ for (i = count; pos < zlell && zleline[pos] != ZWC('\n') && i--; ) {
INCPOS(pos);
}
/*
@@ -96,10 +99,6 @@ doinsert(ZLE_STRING_T zstr, int len)
shiftchars(zlecs, diff);
}
}
-#else
- if (zlecs + m * len > zlell)
- spaceinline(zlecs + m * len - zlell);
-#endif
while (m--)
for (s = zstr, count = len; count; s++, count--)
zleline[zlecs++] = *s;
@@ -440,15 +439,52 @@ killline(char **args)
}
/**/
+void
+regionlines(int *start, int *end)
+{
+ int origcs = zlecs;
+
+ UNMETACHECK();
+ if (zlecs < mark) {
+ *start = findbol();
+ zlecs = (mark > zlell) ? zlell : mark;
+ *end = findeol();
+ } else {
+ *end = findeol();
+ zlecs = mark;
+ *start = findbol();
+ }
+ zlecs = origcs;
+}
+
+/**/
int
killregion(UNUSED(char **args))
{
if (mark > zlell)
mark = zlell;
- if (mark > zlecs)
+ if (region_active == 2) {
+ int a, b;
+ regionlines(&a, &b);
+ zlecs = a;
+ region_active = 0;
+ cut(zlecs, b - zlecs, CUT_RAW);
+ shiftchars(zlecs, b - zlecs);
+ if (zlell) {
+ if (zlecs == zlell)
+ DECCS();
+ foredel(1, 0);
+ vifirstnonblank(zlenoargs);
+ }
+ } else if (mark > zlecs) {
+ if (invicmdmode())
+ INCPOS(mark);
forekill(mark - zlecs, CUT_RAW);
- else
+ } else {
+ if (invicmdmode())
+ INCCS();
backkill(zlecs - mark, CUT_FRONT|CUT_RAW);
+ }
return 0;
}
@@ -456,6 +492,7 @@ killregion(UNUSED(char **args))
int
copyregionaskill(char **args)
{
+ int start, end;
if (*args) {
int len;
ZLE_STRING_T line = stringaszleline(*args, 0, &len, NULL, NULL);
@@ -464,10 +501,16 @@ copyregionaskill(char **args)
} else {
if (mark > zlell)
mark = zlell;
- if (mark > zlecs)
- cut(zlecs, mark - zlecs, 0);
- else
- cut(mark, zlecs - mark, CUT_FRONT);
+ if (mark > zlecs) {
+ start = zlecs;
+ end = mark;
+ } else {
+ start = mark;
+ end = zlecs;
+ }
+ if (invicmdmode())
+ INCPOS(end);
+ cut(start, end - start, mark > zlecs ? 0 : CUT_FRONT);
}
return 0;
}
@@ -475,8 +518,10 @@ 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;
+static int kct, yankb, yanke, yankcs;
+
/* The original cutbuffer, either cutbuf or one of the vi buffers. */
static Cutbuffer kctbuf;
@@ -494,8 +539,7 @@ yank(UNUSED(char **args))
kctbuf = &cutbuf;
if (!kctbuf->buf)
return 1;
- mark = zlecs;
- yankb = zlecs;
+ yankb = yankcs = mark = zlecs;
while (n--) {
kct = -1;
spaceinline(kctbuf->len);
@@ -506,11 +550,140 @@ yank(UNUSED(char **args))
return 0;
}
+/* position: 0 is before, 1 after, 2 split the line */
+static void pastebuf(Cutbuffer buf, int mult, int position)
+{
+ int cc;
+ if (buf->flags & CUTBUFFER_LINE) {
+ if (position == 2) {
+ if (!zlecs)
+ position = 0;
+ else if (zlecs == zlell)
+ position = 1;
+ }
+ if (position == 2) {
+ yankb = zlecs;
+ spaceinline(buf->len + 2);
+ zleline[zlecs++] = ZWC('\n');
+ ZS_memcpy(zleline + zlecs, buf->buf, buf->len);
+ zlecs += buf->len;
+ zleline[zlecs] = ZWC('\n');
+ yanke = zlecs + 1;
+ } else if (position != 0) {
+ yankb = zlecs = findeol();
+ spaceinline(buf->len + 1);
+ zleline[zlecs++] = ZWC('\n');
+ yanke = zlecs + buf->len;
+ ZS_memcpy(zleline + zlecs, buf->buf, buf->len);
+ } else {
+ yankb = zlecs = findbol();
+ spaceinline(buf->len + 1);
+ ZS_memcpy(zleline + zlecs, buf->buf, buf->len);
+ yanke = zlecs + buf->len + 1;
+ zleline[zlecs + buf->len] = ZWC('\n');
+ }
+ vifirstnonblank(zlenoargs);
+ } else {
+ if (position == 1 && zlecs != findeol())
+ INCCS();
+ yankb = zlecs;
+ cc = buf->len;
+ while (mult--) {
+ spaceinline(cc);
+ ZS_memcpy(zleline + zlecs, buf->buf, cc);
+ zlecs += cc;
+ }
+ yanke = zlecs;
+ if (zlecs)
+ DECCS();
+ }
+}
+
+/**/
+int
+viputbefore(UNUSED(char **args))
+{
+ int n = zmult;
+
+ startvichange(-1);
+ if (n < 0 || zmod.flags & MOD_NULL)
+ return 1;
+ if (zmod.flags & MOD_VIBUF)
+ kctbuf = &vibuf[zmod.vibuf];
+ else
+ kctbuf = &cutbuf;
+ if (!kctbuf->buf)
+ return 1;
+ kct = -1;
+ yankcs = zlecs;
+ pastebuf(kctbuf, n, 0);
+ return 0;
+}
+
+/**/
+int
+viputafter(UNUSED(char **args))
+{
+ int n = zmult;
+
+ startvichange(-1);
+ if (n < 0 || zmod.flags & MOD_NULL)
+ return 1;
+ if (zmod.flags & MOD_VIBUF)
+ kctbuf = &vibuf[zmod.vibuf];
+ else
+ kctbuf = &cutbuf;
+ if (!kctbuf->buf)
+ return 1;
+ kct = -1;
+ yankcs = zlecs;
+ pastebuf(kctbuf, n, 1);
+ return 0;
+}
+
+/**/
+int
+putreplaceselection(UNUSED(char **args))
+{
+ int n = zmult;
+ struct cutbuffer prevbuf;
+ Cutbuffer putbuf;
+ int clear = 0;
+ int pos = 2;
+
+ startvichange(-1);
+ if (n < 0 || zmod.flags & MOD_NULL)
+ return 1;
+ putbuf = (zmod.flags & MOD_VIBUF) ? &vibuf[zmod.vibuf] : &cutbuf;
+ if (!putbuf->buf)
+ return 1;
+ memcpy(&prevbuf, putbuf, sizeof(prevbuf));
+
+ /* if "9 was specified, prevent killregion from freeing it */
+ if (zmod.vibuf == 35) {
+ putbuf->buf = 0;
+ clear = 1;
+ }
+
+ zmod.flags = 0; /* flags apply to paste not kill */
+ if (region_active == 2 && prevbuf.flags & CUTBUFFER_LINE) {
+ int a, b;
+ regionlines(&a, &b);
+ pos = (b == zlell);
+ }
+ killregion(zlenoargs);
+
+ pastebuf(&prevbuf, n, pos);
+ if (clear)
+ free(prevbuf.buf);
+ return 0;
+}
+
/**/
int
yankpop(UNUSED(char **args))
{
- int cc, kctstart = kct;
+ int kctstart = kct;
Cutbuffer buf;
if (!(lastcmd & ZLE_YANK) || !kring || !kctbuf) {
@@ -557,11 +730,8 @@ yankpop(UNUSED(char **args))
zlecs = yankb;
foredel(yanke - yankb, CUT_RAW);
- cc = buf->len;
- spaceinline(cc);
- ZS_memcpy(zleline + zlecs, buf->buf, cc);
- zlecs += cc;
- yanke = zlecs;
+ zlecs = yankcs;
+ pastebuf(buf, 1, !!(lastcmd & ZLE_YANKAFTER));
return 0;
}
@@ -871,7 +1041,7 @@ copyprevshellword(UNUSED(char **args))
int
sendbreak(UNUSED(char **args))
{
- errflag = 1;
+ errflag |= ERRFLAG_ERROR|ERRFLAG_INT;
return 1;
}
@@ -881,15 +1051,25 @@ quoteregion(UNUSED(char **args))
{
ZLE_STRING_T str;
size_t len;
+ int extra = invicmdmode();
if (mark > zlell)
mark = zlell;
- if (mark < zlecs) {
+ if (region_active == 2) {
+ int a, b;
+ regionlines(&a, &b);
+ zlecs = a;
+ mark = b;
+ extra = 0;
+ } else if (mark < zlecs) {
int tmp = mark;
mark = zlecs;
zlecs = tmp;
}
- str = (ZLE_STRING_T)hcalloc((len = mark - zlecs) * ZLE_CHAR_SIZE);
+ if (extra)
+ INCPOS(mark);
+ str = (ZLE_STRING_T)hcalloc((len = mark - zlecs) *
+ ZLE_CHAR_SIZE);
ZS_memcpy(str, zleline + zlecs, len);
foredel(len, CUT_RAW);
str = makequote(str, &len);
diff --git a/Src/Zle/zle_move.c b/Src/Zle/zle_move.c
index 7312b3f20..d751c4333 100644
--- a/Src/Zle/zle_move.c
+++ b/Src/Zle/zle_move.c
@@ -509,6 +509,55 @@ exchangepointandmark(UNUSED(char **args))
/**/
int
+visualmode(UNUSED(char **args))
+{
+ if (virangeflag) {
+ prefixflag = 1;
+ zmod.flags &= ~MOD_LINE;
+ zmod.flags |= MOD_CHAR;
+ return 0;
+ }
+ switch (region_active) {
+ case 1:
+ region_active = 0;
+ break;
+ case 0:
+ mark = zlecs;
+ /* fall through */
+ case 2:
+ region_active = 1;
+ break;
+ }
+ return 0;
+}
+
+/**/
+int
+visuallinemode(UNUSED(char **args))
+{
+ if (virangeflag) {
+ prefixflag = 1;
+ zmod.flags &= ~MOD_CHAR;
+ zmod.flags |= MOD_LINE;
+ return 0;
+ }
+ switch (region_active) {
+ case 2:
+ region_active = 0;
+ break;
+ case 0:
+ mark = zlecs;
+ /* fall through */
+ case 1:
+ region_active = 2;
+ break;
+ }
+ return 0;
+}
+
+
+/**/
+int
vigotocolumn(UNUSED(char **args))
{
int x, y, n = zmult;
@@ -538,7 +587,8 @@ vimatchbracket(UNUSED(char **args))
if ((zlecs == zlell || zleline[zlecs] == '\n') && zlecs > 0)
DECCS();
-
+ if (virangeflag)
+ mark = zlecs;
otog:
if (zlecs == zlell || zleline[zlecs] == '\n') {
zlecs = ocs;
@@ -550,7 +600,6 @@ vimatchbracket(UNUSED(char **args))
oth = '}';
break;
case /*{*/ '}':
- virangeflag = -virangeflag;
dir = -1;
oth = '{'; /*}*/
break;
@@ -559,7 +608,6 @@ vimatchbracket(UNUSED(char **args))
oth = ')';
break;
case ')':
- virangeflag = -virangeflag;
dir = -1;
oth = '(';
break;
@@ -568,7 +616,6 @@ vimatchbracket(UNUSED(char **args))
oth = ']';
break;
case ']':
- virangeflag = -virangeflag;
dir = -1;
oth = '[';
break;
@@ -576,6 +623,8 @@ vimatchbracket(UNUSED(char **args))
INCCS();
goto otog;
}
+ if (virangeflag && dir < 0)
+ INCPOS(mark); /* include starting position when going backwards */
ct = 1;
while (zlecs >= 0 && zlecs < zlell && ct) {
if (dir < 0)
@@ -599,7 +648,7 @@ vimatchbracket(UNUSED(char **args))
int
viforwardchar(char **args)
{
- int lim = findeol() - invicmdmode();
+ int lim = findeol();
int n = zmult;
if (n < 0) {
@@ -609,6 +658,8 @@ viforwardchar(char **args)
zmult = n;
return ret;
}
+ if (invicmdmode() && !virangeflag)
+ DECPOS(lim);
if (zlecs >= lim)
return 1;
while (n-- && zlecs < lim)
diff --git a/Src/Zle/zle_params.c b/Src/Zle/zle_params.c
index 5845207fa..dc5fed4ce 100644
--- a/Src/Zle/zle_params.c
+++ b/Src/Zle/zle_params.c
@@ -198,7 +198,7 @@ set_buffer(UNUSED(Param pm), char *x)
setline(x, 0);
zsfree(x);
} else
- zlecs = zlell = 0;
+ viinsbegin = zlecs = zlell = 0;
fixsuffix();
menucmp = 0;
}
diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c
index 684ac13a2..fe337993f 100644
--- a/Src/Zle/zle_refresh.c
+++ b/Src/Zle/zle_refresh.c
@@ -396,8 +396,10 @@ get_region_highlight(UNUSED(Param pm))
struct region_highlight *rhp;
/* region_highlights may not have been set yet */
- if (arrsize)
- arrsize -= N_SPECIAL_HIGHLIGHTS;
+ if (!arrsize)
+ return hmkarray(NULL);
+ arrsize -= N_SPECIAL_HIGHLIGHTS;
+ DPUTS(arrsize < 0, "arrsize is negative from n_region_highlights");
arrp = retarr = (char **)zhalloc((arrsize+1)*sizeof(char *));
/* ignore special highlighting */
@@ -449,10 +451,15 @@ set_region_highlight(UNUSED(Param pm), char **aval)
len = aval ? arrlen(aval) : 0;
if (n_region_highlights != len + N_SPECIAL_HIGHLIGHTS) {
/* no null termination, but include special highlighting at start */
- n_region_highlights = len + N_SPECIAL_HIGHLIGHTS;
+ int newsize = len + N_SPECIAL_HIGHLIGHTS;
+ int diffsize = newsize - n_region_highlights;
region_highlights = (struct region_highlight *)
zrealloc(region_highlights,
- sizeof(struct region_highlight) * n_region_highlights);
+ sizeof(struct region_highlight) * newsize);
+ if (diffsize > 0)
+ memset(region_highlights + newsize - diffsize, 0,
+ sizeof(struct region_highlight) * diffsize);
+ n_region_highlights = newsize;
}
if (!aval)
@@ -1028,6 +1035,8 @@ zrefresh(void)
/* this will create region_highlights if it's still NULL */
zle_set_highlight();
+ DPUTS(!region_highlights, "region_highlights not created");
+
/* check for region between point ($CURSOR) and mark ($MARK) */
if (region_active) {
if (zlecs <= mark) {
@@ -1037,6 +1046,15 @@ zrefresh(void)
region_highlights[0].start = mark;
region_highlights[0].end = zlecs;
}
+ if (region_active == 2) {
+ int origcs = zlecs;
+ zlecs = region_highlights[0].end;
+ region_highlights[0].end = findeol();
+ zlecs = region_highlights[0].start;
+ region_highlights[0].start = findbol();
+ zlecs = origcs;
+ } else if (invicmdmode())
+ INCPOS(region_highlights[0].end);
} else {
region_highlights[0].start = region_highlights[0].end = -1;
}
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 499c4ae77..f18ad170e 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -634,7 +634,8 @@ docomplete(int lst)
metafy_line();
ocs = zlemetacs;
- origline = dupstring(zlemetaline);
+ zsfree(origline);
+ origline = ztrdup(zlemetaline);
origcs = zlemetacs;
origll = zlemetall;
if (!isfirstln && (chline != NULL || zle_chline != NULL)) {
@@ -662,8 +663,9 @@ docomplete(int lst)
* NOTE: get_comp_string() calls pushheap(), but not popheap(). */
noerrs = 1;
s = get_comp_string();
- DPUTS(wb < 0 || zlemetacs < wb || zlemetacs > we,
- "BUG: 0 <= wb <= zlemetacs <= we is not true!");
+ DPUTS3(wb < 0 || zlemetacs < wb || zlemetacs > we,
+ "BUG: 0 <= wb (%d) <= zlemetacs (%d) <= we (%d) is not true!",
+ wb, zlemetacs, we);
noerrs = ne;
/* For vi mode, reset the start-of-insertion pointer to the beginning *
* of the word being completed, if it is currently later. Vi itself *
@@ -696,7 +698,7 @@ docomplete(int lst)
freeheap();
/* Save the lexer state, in case the completion code uses the lexer *
* somewhere (e.g. when processing a compctl -s flag). */
- lexsave();
+ zcontext_save();
if (inwhat == IN_ENV)
lincmd = 0;
if (s) {
@@ -828,7 +830,7 @@ docomplete(int lst)
if (olst == COMP_EXPAND_COMPLETE &&
!strcmp(ol, zlemetaline)) {
zlemetacs = ocs;
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
if (!compfunc) {
char *p;
@@ -866,7 +868,7 @@ docomplete(int lst)
} else
ret = 1;
/* Reset the lexer state, pop the heap. */
- lexrestore();
+ zcontext_restore();
popheap();
dat[0] = lst;
@@ -876,6 +878,19 @@ docomplete(int lst)
active = 0;
makecommaspecial(0);
+
+ /*
+ * As a special case, we reset user interrupts here.
+ * That's because completion is an intensive piece of
+ * computation that the user might want to interrupt separately
+ * from anything else going on. If they do, they probably
+ * want to keep the line edit buffer intact.
+ *
+ * There's a race here that the user might hit ^C just
+ * after completion exited anyway, but that's inevitable.
+ */
+ errflag &= ~ERRFLAG_INT;
+
return dat[1];
}
@@ -1149,7 +1164,7 @@ get_comp_string(void)
varname = NULL;
insubscr = 0;
clwpos = -1;
- lexsave();
+ zcontext_save();
lexflags = LEXFLAGS_ZLE;
inpush(dupstrspace(linptr), 0, NULL);
strinbeg(0);
@@ -1393,7 +1408,8 @@ get_comp_string(void)
}
strinend();
inpop();
- errflag = lexflags = 0;
+ lexflags = 0;
+ errflag &= ~ERRFLAG_ERROR;
if (parbegin != -1) {
/* We are in command or process substitution if we are not in
* a $((...)). */
@@ -1406,7 +1422,7 @@ get_comp_string(void)
zlemetall -= parend;
zlemetaline[zlemetall + addedx] = '\0';
}
- lexrestore();
+ zcontext_restore();
tt = NULL;
goto start;
}
@@ -1480,12 +1496,12 @@ get_comp_string(void)
if (tmp) {
tmp = NULL;
linptr = zlemetaline;
- lexrestore();
+ zcontext_restore();
addedx = 0;
goto start;
}
noaliases = ona;
- lexrestore();
+ zcontext_restore();
return NULL;
}
@@ -1720,9 +1736,11 @@ get_comp_string(void)
for (pe = p + 2; *pe && *pe != Snull && i + (pe - p) < zlemetacs;
pe++)
;
- if (!*pe) {
+ if (*pe != Snull) {
/* no terminating Snull, can't substitute */
skipchars = 2;
+ if (*pe)
+ j = 1;
} else {
/*
* Try and substitute the $'...' expression.
@@ -1795,6 +1813,10 @@ get_comp_string(void)
* first clue how the completion system actually works.
*/
skipchars = 2;
+ /*
+ * Also pretend we're in single quotes.
+ */
+ j = 1;
}
}
}
@@ -1817,7 +1839,7 @@ get_comp_string(void)
ocs = zlemetacs;
zlemetacs = i;
foredel(skipchars, CUT_RAW);
- if ((zlemetacs = ocs) > (i -= skipchars))
+ if ((zlemetacs = ocs) > --i)
zlemetacs -= skipchars;
we -= skipchars;
}
@@ -2129,7 +2151,7 @@ get_comp_string(void)
offs = boffs;
}
}
- lexrestore();
+ zcontext_restore();
return (char *)s;
}
@@ -2769,7 +2791,7 @@ doexpandhist(void)
expanding = 1;
excs = zlemetacs;
zlemetall = zlemetacs = 0;
- lexsave();
+ zcontext_save();
/* We push ol as it will remain unchanged */
inpush(ol, 0, NULL);
strinbeg(1);
@@ -2781,7 +2803,7 @@ doexpandhist(void)
} while (tok != ENDINPUT && tok != LEXERR);
while (!lexstop)
hgetc();
- /* We have to save errflags because it's reset in lexrestore. Since *
+ /* We have to save errflags because it's reset in zcontext_restore. Since *
* noerrs was set to 1 errflag is true if there was a habort() which *
* means that the expanded string is unusable. */
err = errflag;
@@ -2789,7 +2811,7 @@ doexpandhist(void)
noaliases = ona;
strinend();
inpop();
- lexrestore();
+ zcontext_restore();
expanding = 0;
if (!err) {
@@ -2888,7 +2910,7 @@ getcurcmd(void)
int curlincmd;
char *s = NULL;
- lexsave();
+ zcontext_save();
lexflags = LEXFLAGS_ZLE;
metafy_line();
inpush(dupstrspace(zlemetaline), 0, NULL);
@@ -2910,9 +2932,9 @@ getcurcmd(void)
popheap();
strinend();
inpop();
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
unmetafy_line();
- lexrestore();
+ zcontext_restore();
return s;
}
diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c
index 1089e274f..e4ab97a54 100644
--- a/Src/Zle/zle_utils.c
+++ b/Src/Zle/zle_utils.c
@@ -43,10 +43,10 @@ struct cutbuffer *kring;
int kringsize, kringnum;
/* Vi named cut buffers. 0-25 are the named buffers "a to "z, and *
- * 26-34 are the numbered buffer stack "1 to "9. */
+ * 26-35 are the numbered buffer stack "0 to "9. */
/**/
-struct cutbuffer vibuf[35];
+struct cutbuffer vibuf[36];
/* the line before last mod (for undo purposes) */
@@ -117,7 +117,7 @@ int
zlecharasstring(ZLE_CHAR_T inchar, char *buf)
{
#ifdef MULTIBYTE_SUPPORT
- size_t ret;
+ int ret;
char *ptr;
#ifdef __STDC_ISO_10646__
@@ -675,35 +675,42 @@ zle_restore_positions(void)
zlell = oldpos->ll;
}
- /* Count number of regions and see if the array needs resizing */
- for (nreg = 0, oldrhp = oldpos->regions;
- oldrhp;
- nreg++, oldrhp = oldrhp->next)
- ;
- if (nreg + N_SPECIAL_HIGHLIGHTS != n_region_highlights) {
- n_region_highlights = nreg + N_SPECIAL_HIGHLIGHTS;
- region_highlights = (struct region_highlight *)
- zrealloc(region_highlights,
- sizeof(struct region_highlight) * n_region_highlights);
- }
- oldrhp = oldpos->regions;
- rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
- while (oldrhp) {
- struct zle_region *nextrhp = oldrhp->next;
-
- rhp->atr = oldrhp->atr;
- rhp->flags = oldrhp->flags;
- if (zlemetaline) {
- rhp->start_meta = oldrhp->start;
- rhp->end_meta = oldrhp->end;
- } else {
- rhp->start = oldrhp->start;
- rhp->end = oldrhp->end;
+ if (oldpos->regions) {
+ /* Count number of regions and see if the array needs resizing */
+ for (nreg = 0, oldrhp = oldpos->regions;
+ oldrhp;
+ nreg++, oldrhp = oldrhp->next)
+ ;
+ if (nreg + N_SPECIAL_HIGHLIGHTS != n_region_highlights) {
+ n_region_highlights = nreg + N_SPECIAL_HIGHLIGHTS;
+ region_highlights = (struct region_highlight *)
+ zrealloc(region_highlights,
+ sizeof(struct region_highlight) * n_region_highlights);
}
+ oldrhp = oldpos->regions;
+ rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
+ while (oldrhp) {
+ struct zle_region *nextrhp = oldrhp->next;
- zfree(oldrhp, sizeof(*oldrhp));
- oldrhp = nextrhp;
- rhp++;
+ rhp->atr = oldrhp->atr;
+ rhp->flags = oldrhp->flags;
+ if (zlemetaline) {
+ rhp->start_meta = oldrhp->start;
+ rhp->end_meta = oldrhp->end;
+ } else {
+ rhp->start = oldrhp->start;
+ rhp->end = oldrhp->end;
+ }
+
+ zfree(oldrhp, sizeof(*oldrhp));
+ oldrhp = nextrhp;
+ rhp++;
+ }
+ } else if (region_highlights) {
+ zfree(region_highlights, sizeof(struct region_highlight) *
+ n_region_highlights);
+ region_highlights = NULL;
+ n_region_highlights = 0;
}
zfree(oldpos, sizeof(*oldpos));
@@ -785,6 +792,8 @@ spaceinline(int ct)
if (mark > zlecs)
mark += ct;
+ if (viinsbegin > zlecs)
+ viinsbegin = 0;
if (region_highlights) {
for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
@@ -914,7 +923,7 @@ cut(int i, int ct, int flags)
void
cuttext(ZLE_STRING_T line, int ct, int flags)
{
- if (!ct)
+ if (!(ct || vilinerange) || zmod.flags & MOD_NULL)
return;
UNMETACHECK();
@@ -941,17 +950,23 @@ cuttext(ZLE_STRING_T line, int ct, int flags)
ZS_memcpy(b->buf + len, line, ct);
b->len = len + ct;
}
- return;
- } else {
- /* Save in "1, shifting "1-"8 along to "2-"9 */
- int n;
- free(vibuf[34].buf);
- for(n=34; n>26; n--)
- vibuf[n] = vibuf[n-1];
+ } else if (flags & CUT_YANK) {
+ /* Save in "0 */
+ free(vibuf[26].buf);
vibuf[26].buf = (ZLE_STRING_T)zalloc(ct * ZLE_CHAR_SIZE);
ZS_memcpy(vibuf[26].buf, line, ct);
vibuf[26].len = ct;
vibuf[26].flags = vilinerange ? CUTBUFFER_LINE : 0;
+ } else {
+ /* Save in "1, shifting "1-"8 along to "2-"9 */
+ int n;
+ free(vibuf[35].buf);
+ for(n=35; n>27; n--)
+ vibuf[n] = vibuf[n-1];
+ vibuf[27].buf = (ZLE_STRING_T)zalloc(ct * ZLE_CHAR_SIZE);
+ ZS_memcpy(vibuf[27].buf, line, ct);
+ vibuf[27].len = ct;
+ vibuf[27].flags = vilinerange ? CUTBUFFER_LINE : 0;
}
if (!cutbuf.buf) {
cutbuf.buf = (ZLE_STRING_T)zalloc(ZLE_CHAR_SIZE);
@@ -981,8 +996,9 @@ cuttext(ZLE_STRING_T line, int ct, int flags)
cutbuf.buf = s;
cutbuf.len += ct;
} else {
+ /* don't alloc 0 bytes; length 0 occurs for blank lines in vi mode */
cutbuf.buf = realloc((char *)cutbuf.buf,
- (cutbuf.len + ct) * ZLE_CHAR_SIZE);
+ (cutbuf.len + (ct ? ct : 1)) * ZLE_CHAR_SIZE);
ZS_memcpy(cutbuf.buf + cutbuf.len, line, ct);
cutbuf.len += ct;
}
@@ -1098,6 +1114,7 @@ setline(char *s, int flags)
*/
free(zleline);
+ viinsbegin = 0;
zleline = stringaszleline(scp, 0, &zlell, &linesz, NULL);
if ((flags & ZSL_TOEND) && (zlecs = zlell) && invicmdmode())
@@ -1356,7 +1373,6 @@ int
handlefeep(UNUSED(char **args))
{
zbeep();
- region_active = 0;
return 0;
}
@@ -1386,7 +1402,8 @@ static struct change *nextchanges, *endnextchanges;
/* incremented to provide a unique change number */
-static zlong undo_changeno;
+/**/
+zlong undo_changeno;
/* If non-zero, the last increment to undo_changeno was for the variable */
@@ -1659,8 +1676,7 @@ splitundo(char **args)
{
if (vistartchange >= 0) {
mergeundo();
- vistartchange = (curchange && curchange->prev) ?
- curchange->prev->changeno : 0;
+ vistartchange = undo_changeno;
}
handleundo();
return 0;
@@ -1706,7 +1722,8 @@ zlecallhook(char *name, char *arg)
execzlefunc(thingy, args, 1);
unrefthingy(thingy);
- errflag = saverrflag;
+ /* Retain any user interrupt error status */
+ errflag = saverrflag | (errflag & ERRFLAG_INT);
retflag = savretflag;
}
diff --git a/Src/Zle/zle_vi.c b/Src/Zle/zle_vi.c
index 9e39143d0..1a11ca7d5 100644
--- a/Src/Zle/zle_vi.c
+++ b/Src/Zle/zle_vi.c
@@ -75,7 +75,7 @@ static int inrepeat, vichgrepeat;
*/
/**/
-static void
+void
startvichange(int im)
{
if (im != -1) {
@@ -109,7 +109,7 @@ startvitext(int im)
{
startvichange(im);
selectkeymap("main", 1);
- vistartchange = (curchange && curchange->prev) ? curchange->prev->changeno : 0;
+ vistartchange = undo_changeno;
viinsbegin = zlecs;
}
@@ -161,75 +161,87 @@ vigetkey(void)
static int
getvirange(int wf)
{
- int pos = zlecs, ret = 0;
+ int pos = zlecs, mpos = mark, ret = 0;
+ int visual = region_active; /* movement command might set it */
int mult1 = zmult, hist1 = histline;
Thingy k2;
- virangeflag = 1;
- wordflag = wf;
- /* Now we need to execute the movement command, to see where it *
- * actually goes. virangeflag here indicates to the movement *
- * function that it should place the cursor at the end of the *
- * range, rather than where the cursor would actually go if it *
- * were executed normally. This makes a difference to some *
- * commands, but not all. For example, if searching forward *
- * for a character, under normal circumstances the cursor lands *
- * on the character. For a range, the range must include the *
- * character, so the cursor gets placed after the character if *
- * virangeflag is set. vi-match-bracket needs to change the *
- * value of virangeflag under some circumstances, meaning that *
- * we need to change the *starting* position. */
- zmod.flags &= ~MOD_TMULT;
- do {
- vilinerange = 0;
- prefixflag = 0;
- if (!(k2 = getkeycmd()) || (k2->flags & DISABLED) ||
- k2 == Th(z_sendbreak)) {
- wordflag = 0;
- virangeflag = 0;
+ if (visual) {
+ if (!zlell)
+ return -1;
+ pos = mark;
+ vilinerange = (visual == 2);
+ region_active = 0;
+ } else {
+ virangeflag = 1;
+ wordflag = wf;
+ mark = -1;
+ /* use operator-pending keymap if one exists */
+ Keymap km = openkeymap("viopp");
+ if (km)
+ selectlocalmap(km);
+ /* Now we need to execute the movement command, to see where it *
+ * actually goes. virangeflag here indicates to the movement *
+ * function that it should place the cursor at the end of the *
+ * range, rather than where the cursor would actually go if it *
+ * were executed normally. This makes a difference to some *
+ * commands, but not all. For example, if searching forward *
+ * for a character, under normal circumstances the cursor lands *
+ * on the character. For a range, the range must include the *
+ * character, so the cursor gets placed after the character if *
+ * virangeflag is set. */
+ zmod.flags &= ~MOD_TMULT;
+ do {
+ vilinerange = 0;
+ prefixflag = 0;
+ if (!(k2 = getkeycmd()) || (k2->flags & DISABLED) ||
+ k2 == Th(z_sendbreak)) {
+ wordflag = 0;
+ virangeflag = 0;
+ mark = mpos;
+ return -1;
+ }
+ /*
+ * With k2 == bindk, the command key is repeated:
+ * a number of lines is used. If the function used
+ * returns 1, we fail.
+ */
+ if ((k2 == bindk) ? dovilinerange() : execzlefunc(k2, zlenoargs, 1))
+ ret = -1;
+ if(vichgrepeat)
+ zmult = mult1;
+ else
+ zmult = mult1 * zmod.tmult;
+ } while(prefixflag && !ret);
+ wordflag = 0;
+ selectlocalmap(NULL);
+
+ /* It is an error to use a non-movement command to delimit the *
+ * range. We here reject the case where the command modified *
+ * the line, or selected a different history line. */
+ if (histline != hist1 || zlell != lastll || memcmp(zleline, lastline, zlell)) {
+ histline = hist1;
+ ZS_memcpy(zleline, lastline, zlell = lastll);
+ zlecs = pos;
+ mark = mpos;
return -1;
}
- /*
- * With k2 == bindk, the command key is repeated:
- * a number of lines is used. If the function used
- * returns 1, we fail.
- */
- if ((k2 == bindk) ? dovilinerange() : execzlefunc(k2, zlenoargs, 1))
- ret = -1;
- if(vichgrepeat)
- zmult = mult1;
- else
- zmult = mult1 * zmod.tmult;
- } while(prefixflag && !ret);
- wordflag = 0;
- virangeflag = 0;
-
- /* It is an error to use a non-movement command to delimit the *
- * range. We here reject the case where the command modified *
- * the line, or selected a different history line. */
- if (histline != hist1 || zlell != lastll || memcmp(zleline, lastline, zlell)) {
- histline = hist1;
- ZS_memcpy(zleline, lastline, zlell = lastll);
- zlecs = pos;
- return -1;
- }
- /* Can't handle an empty file. Also, if the movement command *
- * failed, or didn't move, it is an error. */
- if (!zlell || (zlecs == pos && virangeflag != 2) || ret == -1)
- return -1;
+ /* Can't handle an empty file. Also, if the movement command *
+ * failed, or didn't move, it is an error. */
+ if (!zlell || (zlecs == pos && (mark == -1 || mark == zlecs) &&
+ virangeflag != 2) || ret == -1) {
+ mark = mpos;
+ return -1;
+ }
+ virangeflag = 0;
- /* vi-match-bracket changes the value of virangeflag when *
- * moving to the opening bracket, meaning that we need to *
- * change the *starting* position. */
- if(virangeflag == -1)
- {
- int origcs = zlecs;
- zlecs = pos;
- INCCS();
- pos = zlecs;
- zlecs = origcs;
+ /* if the mark has moved, ignore the original cursor position *
+ * and use the mark. */
+ if (mark != -1)
+ pos = mark;
}
+ mark = mpos;
/* Get the range the right way round. zlecs is placed at the *
* start of the range, and pos (the return value of this *
@@ -240,17 +252,31 @@ getvirange(int wf)
pos = tmp;
}
+ /* visual selection mode needs to include additional position */
+ if (visual == 1 && invicmdmode())
+ INCPOS(pos);
+
/* Was it a line-oriented move? If so, the command will have set *
* the vilinerange flag. In this case, entire lines are taken, *
* rather than just the sequence of characters delimited by pos *
- * and zlecs. The terminating newline is left out of the range, *
+ * and zlecs. The terminating newline is left out of the range, *
* which the real command must deal with appropriately. At this *
* point we just need to make the range encompass entire lines. */
- if(vilinerange) {
+ vilinerange = (zmod.flags & MOD_LINE) ||
+ (vilinerange && !(zmod.flags & MOD_CHAR));
+ if (vilinerange) {
int newcs = findbol();
+ lastcol = zlecs - newcs;
zlecs = pos;
pos = findeol();
zlecs = newcs;
+ } else if (!visual) {
+ /* for a character-wise move don't include a newline at the *
+ * end of the range */
+ int prev = pos;
+ DECPOS(prev);
+ if (zleline[prev] == ZWC('\n'))
+ pos = prev;
}
return pos;
}
@@ -348,6 +374,7 @@ videlete(UNUSED(char **args))
forekill(c2 - zlecs, CUT_RAW);
ret = 0;
if (vilinerange && zlell) {
+ lastcol = -1;
if (zlecs == zlell)
DECCS();
foredel(1, 0);
@@ -365,6 +392,7 @@ videletechar(char **args)
int n = zmult;
startvichange(-1);
+
/* handle negative argument */
if (n < 0) {
int ret;
@@ -401,7 +429,7 @@ vichange(UNUSED(char **args))
forekill(c2 - zlecs, CUT_RAW);
selectkeymap("main", 1);
viinsbegin = zlecs;
- vistartchange = (curchange && curchange->prev) ? curchange->prev->changeno : 0;
+ vistartchange = undo_changeno;
}
return ret;
}
@@ -418,12 +446,16 @@ visubstitute(UNUSED(char **args))
/* it is an error to be on the end of line */
if (zlecs == zlell || zleline[zlecs] == '\n')
return 1;
- /* Put argument into the acceptable range -- it is not an error to *
- * specify a greater count than the number of available characters. */
- if (n > findeol() - zlecs)
- n = findeol() - zlecs;
- /* do the substitution */
- forekill(n, CUT_RAW);
+ if (region_active) {
+ killregion(zlenoargs);
+ } else {
+ /* Put argument into the acceptable range -- it is not an error to *
+ * specify a greater count than the number of available characters. */
+ if (n > findeol() - zlecs)
+ n = findeol() - zlecs;
+ /* do the substitution */
+ forekill(n, CUT_RAW);
+ }
startvitext(1);
return 0;
}
@@ -432,7 +464,15 @@ visubstitute(UNUSED(char **args))
int
vichangeeol(UNUSED(char **args))
{
- forekill(findeol() - zlecs, CUT_RAW);
+ int a, b;
+ if (region_active) {
+ regionlines(&a, &b);
+ zlecs = a;
+ region_active = 0;
+ cut(zlecs, b - zlecs, CUT_RAW);
+ shiftchars(zlecs, b - zlecs);
+ } else
+ forekill(findeol() - zlecs, CUT_RAW);
startvitext(1);
return 0;
}
@@ -449,15 +489,30 @@ vichangewholeline(char **args)
int
viyank(UNUSED(char **args))
{
- int oldcs = zlecs, c2, ret = 1;
+ int c2, ret = 1;
startvichange(1);
if ((c2 = getvirange(0)) != -1) {
- cut(zlecs, c2 - zlecs, 0);
+ cut(zlecs, c2 - zlecs, CUT_YANK);
ret = 0;
}
vichgflag = 0;
- zlecs = oldcs;
+ /* cursor now at the start of the range yanked. For line mode
+ * restore the column position */
+ if (vilinerange && lastcol != -1) {
+ int x = findeol();
+
+ if ((zlecs += lastcol) >= x) {
+ zlecs = x;
+ if (zlecs > findbol() && invicmdmode())
+ DECCS();
+ }
+#ifdef MULTIBYTE_SUPPORT
+ else
+ CCRIGHT();
+#endif
+ lastcol = -1;
+ }
return ret;
}
@@ -470,7 +525,7 @@ viyankeol(UNUSED(char **args))
startvichange(-1);
if (x == zlecs)
return 1;
- cut(zlecs, x - zlecs, 0);
+ cut(zlecs, x - zlecs, CUT_YANK);
return 0;
}
@@ -492,7 +547,7 @@ viyankwholeline(UNUSED(char **args))
zlecs = findeol() + 1;
}
vilinerange = 1;
- cut(bol, zlecs - bol - 1, 0);
+ cut(bol, zlecs - bol - 1, CUT_YANK);
zlecs = oldcs;
return 0;
}
@@ -526,16 +581,40 @@ vireplacechars(UNUSED(char **args))
int n = zmult, fail = 0, newchars = 0;
if (n > 0) {
- int pos = zlecs;
- while (n-- > 0) {
- if (pos == zlell || zleline[pos] == ZWC('\n')) {
- fail = 1;
- break;
+ if (region_active) {
+ int a, b;
+ if (region_active == 1) {
+ if (mark > zlecs) {
+ a = zlecs;
+ b = mark;
+ } else {
+ a = mark;
+ b = zlecs;
+ }
+ INCPOS(b);
+ } else
+ regionlines(&a, &b);
+ zlecs = a;
+ if (b > zlell)
+ b = zlell;
+ n = b - a;
+ while (a < b) {
+ newchars++;
+ INCPOS(a);
+ }
+ region_active = 0;
+ } else {
+ int pos = zlecs;
+ while (n-- > 0) {
+ if (pos == zlell || zleline[pos] == ZWC('\n')) {
+ fail = 1;
+ break;
+ }
+ newchars++;
+ INCPOS(pos);
}
- newchars++;
- INCPOS(pos);
+ n = pos - zlecs;
}
- n = pos - zlecs;
}
startvichange(1);
/* check argument range */
@@ -567,6 +646,8 @@ vireplacechars(UNUSED(char **args))
* buffer offset.
* Use shiftchars so as not to adjust the cursor position;
* we are overwriting anything that remains directly.
+ * With a selection this will replace newlines which vim
+ * doesn't do but this simplifies things a lot.
*/
if (n > newchars)
shiftchars(zlecs, n - newchars);
@@ -675,8 +756,11 @@ viindent(UNUSED(char **args))
{
int oldcs = zlecs, c2;
- /* get the range */
startvichange(1);
+ /* force line range */
+ if (region_active == 1)
+ region_active = 2;
+ /* get the range */
if ((c2 = getvirange(0)) == -1) {
vichgflag = 0;
return 1;
@@ -689,10 +773,14 @@ viindent(UNUSED(char **args))
}
oldcs = zlecs;
/* add a tab to the beginning of each line within range */
- while (zlecs < c2) {
- spaceinline(1);
- zleline[zlecs] = '\t';
- zlecs = findeol() + 1;
+ while (zlecs <= c2 + 1) {
+ if (zleline[zlecs] == '\n') { /* leave blank lines alone */
+ ++zlecs;
+ } else {
+ spaceinline(1);
+ zleline[zlecs] = '\t';
+ zlecs = findeol() + 1;
+ }
}
/* go back to the first line of the range */
zlecs = oldcs;
@@ -706,8 +794,11 @@ viunindent(UNUSED(char **args))
{
int oldcs = zlecs, c2;
- /* get the range */
startvichange(1);
+ /* force line range */
+ if (region_active == 1)
+ region_active = 2;
+ /* get the range */
if ((c2 = getvirange(0)) == -1) {
vichgflag = 0;
return 1;
@@ -739,6 +830,7 @@ vibackwarddeletechar(char **args)
if (invicmdmode())
startvichange(-1);
+
/* handle negative argument */
if (n < 0) {
int ret;
@@ -776,95 +868,44 @@ vikillline(UNUSED(char **args))
/**/
int
-viputbefore(UNUSED(char **args))
+vijoin(UNUSED(char **args))
{
- Cutbuffer buf = &cutbuf;
+ int x, pos;
int n = zmult;
+ int visual = region_active;
startvichange(-1);
- if (n < 0)
- return 1;
- if (zmod.flags & MOD_VIBUF)
- buf = &vibuf[zmod.vibuf];
- if (!buf->buf)
+ if (n < 1)
return 1;
- if(buf->flags & CUTBUFFER_LINE) {
- zlecs = findbol();
- spaceinline(buf->len + 1);
- ZS_memcpy(zleline + zlecs, buf->buf, buf->len);
- zleline[zlecs + buf->len] = ZWC('\n');
- vifirstnonblank(zlenoargs);
- } else {
- while (n--) {
- spaceinline(buf->len);
- ZS_memcpy(zleline + zlecs, buf->buf, buf->len);
- zlecs += buf->len;
+ if (visual && zlecs > mark) {
+ exchangepointandmark(zlenoargs);
+ x = findeol();
+ if (x >= mark) {
+ exchangepointandmark(zlenoargs);
+ return 1;
}
- if (zlecs)
- DECCS();
- }
- return 0;
-}
-
-/**/
-int
-viputafter(UNUSED(char **args))
-{
- Cutbuffer buf = &cutbuf;
- int n = zmult;
-
- startvichange(-1);
- if (n < 0)
- return 1;
- if (zmod.flags & MOD_VIBUF)
- buf = &vibuf[zmod.vibuf];
- if (!buf->buf)
+ } else if ((x = findeol()) == zlell || (visual && x >= mark))
return 1;
- if(buf->flags & CUTBUFFER_LINE) {
- zlecs = findeol();
- spaceinline(buf->len + 1);
- zleline[zlecs++] = ZWC('\n');
- ZS_memcpy(zleline + zlecs, buf->buf, buf->len);
- vifirstnonblank(zlenoargs);
- } else {
- if (zlecs != findeol())
- INCCS();
- while (n--) {
- spaceinline(buf->len);
- ZS_memcpy(zleline + zlecs, buf->buf, buf->len);
- zlecs += buf->len;
- }
- if (zlecs)
- DECCS();
- }
- return 0;
-}
-
-/**/
-int
-vijoin(UNUSED(char **args))
-{
- int x, pos;
- startvichange(-1);
- if ((x = findeol()) == zlell)
- return 1;
- zlecs = x + 1;
- pos = zlecs;
- for (; zlecs != zlell && ZC_iblank(zleline[zlecs]); INCPOS(zlecs))
- ;
- x = 1 + (zlecs - pos);
- backdel(x, CUT_RAW);
- if (zlecs) {
- int pos = zlecs;
- DECPOS(pos);
- if (ZC_iblank(zleline[pos])) {
- zlecs = pos;
- return 0;
+ do {
+ zlecs = x + 1;
+ pos = zlecs;
+ for (; zlecs != zlell && ZC_iblank(zleline[zlecs]); INCPOS(zlecs))
+ ;
+ x = 1 + (zlecs - pos);
+ backdel(x, CUT_RAW);
+ if (zlecs) {
+ int pos = zlecs;
+ DECPOS(pos);
+ if (ZC_iblank(zleline[pos])) {
+ zlecs = pos;
+ continue;
+ }
}
- }
- spaceinline(1);
- zleline[zlecs] = ZWC(' ');
+ spaceinline(1);
+ zleline[zlecs] = ZWC(' ');
+ } while (!((!visual && --n < 2) || (x = findeol()) == zlell || (visual && x >= mark)));
+
return 0;
}
@@ -878,6 +919,8 @@ viswapcase(UNUSED(char **args))
if (n < 1)
return 1;
eol = findeol();
+ if (zlecs == eol)
+ return 1;
while (zlecs < eol && n--) {
if (ZC_ilower(zleline[zlecs]))
zleline[zlecs] = ZC_toupper(zleline[zlecs]);
@@ -905,14 +948,26 @@ vicapslockpanic(UNUSED(char **args))
/**/
int
-visetbuffer(UNUSED(char **args))
+visetbuffer(char **args)
{
ZLE_INT_T ch;
- if ((zmod.flags & MOD_VIBUF) ||
- (((ch = getfullchar(0)) < ZWC('1') || ch > ZWC('9')) &&
+ if (*args) {
+ ch = **args;
+ if (args[1] || (ch && (*args)[1]))
+ return 1;
+ } else {
+ ch = getfullchar(0);
+ }
+ if (ch == ZWC('_')) {
+ zmod.flags |= MOD_NULL;
+ prefixflag = 1;
+ return 0;
+ } else
+ zmod.flags &= ~MOD_NULL;
+ if ((ch < ZWC('0') || ch > ZWC('9')) &&
(ch < ZWC('a') || ch > ZWC('z')) &&
- (ch < ZWC('A') || ch > ZWC('Z'))))
+ (ch < ZWC('A') || ch > ZWC('Z')))
return 1;
if (ch >= ZWC('A') && ch <= ZWC('Z')) /* needed in cut() */
zmod.flags |= MOD_VIAPP;
@@ -920,8 +975,8 @@ visetbuffer(UNUSED(char **args))
zmod.flags &= ~MOD_VIAPP;
/* FIXME how portable is it for multibyte encoding? */
zmod.vibuf = ZC_tolower(ch);
- if (ch >= ZWC('1') && ch <= ZWC('9'))
- zmod.vibuf += - (int)ZWC('1') + 26;
+ if (ch >= ZWC('0') && ch <= ZWC('9'))
+ zmod.vibuf += - (int)ZWC('0') + 26;
else
zmod.vibuf += - (int)ZWC('a');
zmod.flags |= MOD_VIBUF;
diff --git a/Src/Zle/zle_word.c b/Src/Zle/zle_word.c
index e59304ecd..2e6d75e86 100644
--- a/Src/Zle/zle_word.c
+++ b/Src/Zle/zle_word.c
@@ -80,16 +80,21 @@ viforwardword(char **args)
return ret;
}
while (n--) {
+ int nl;
if (Z_vialnum(zleline[zlecs]))
while (zlecs != zlell && Z_vialnum(zleline[zlecs]))
INCCS();
else
- while (zlecs != zlell && !Z_vialnum(zleline[zlecs]) && !ZC_iblank(zleline[zlecs]))
+ while (zlecs != zlell && !Z_vialnum(zleline[zlecs]) &&
+ !ZC_inblank(zleline[zlecs]))
INCCS();
if (wordflag && !n)
return 0;
- while (zlecs != zlell && ZC_inblank(zleline[zlecs]))
+ nl = (zleline[zlecs] == ZWC('\n'));
+ while (zlecs != zlell && nl < 2 && ZC_inblank(zleline[zlecs])) {
INCCS();
+ nl += (zleline[zlecs] == ZWC('\n'));
+ }
}
return 0;
}
@@ -108,12 +113,16 @@ viforwardblankword(char **args)
return ret;
}
while (n--) {
- while (zlecs != zlell && !ZC_iblank(zleline[zlecs]))
+ int nl;
+ while (zlecs != zlell && !ZC_inblank(zleline[zlecs]))
INCCS();
if (wordflag && !n)
return 0;
- while (zlecs != zlell && ZC_iblank(zleline[zlecs]))
+ nl = (zleline[zlecs] == ZWC('\n'));
+ while (zlecs != zlell && nl < 2 && ZC_inblank(zleline[zlecs])) {
INCCS();
+ nl += (zleline[zlecs] == ZWC('\n'));
+ }
}
return 0;
}
@@ -148,20 +157,25 @@ viforwardblankwordend(UNUSED(char **args))
{
int n = zmult;
- if (n < 0)
- return 1;
+ if (n < 0) {
+ int ret;
+ zmult = -n;
+ ret = viforwardblankwordend(args);
+ zmult = n;
+ return ret;
+ }
while (n--) {
while (zlecs != zlell) {
int pos = zlecs;
INCPOS(pos);
- if (!ZC_iblank(zleline[pos]))
+ if (!ZC_inblank(zleline[pos]))
break;
zlecs = pos;
}
while (zlecs != zlell) {
int pos = zlecs;
INCPOS(pos);
- if (ZC_iblank(zleline[pos]))
+ if (ZC_inblank(zleline[pos]))
break;
zlecs = pos;
}
@@ -180,7 +194,7 @@ viforwardwordend(char **args)
if (n < 0) {
int ret;
zmult = -n;
- ret = backwardword(args);
+ ret = vibackwardwordend(args);
zmult = n;
return ret;
}
@@ -211,7 +225,7 @@ viforwardwordend(char **args)
if (zlecs == zlell)
break;
INCPOS(pos);
- if (Z_vialnum(zleline[pos]) || ZC_iblank(zleline[pos]))
+ if (Z_vialnum(zleline[pos]) || ZC_inblank(zleline[pos]))
break;
}
}
@@ -268,16 +282,19 @@ vibackwardword(char **args)
return ret;
}
while (n--) {
+ int nl = 0;
while (zlecs) {
- int pos = zlecs;
- DECPOS(pos);
- if (!ZC_iblank(zleline[pos]))
+ DECCS();
+ if (!ZC_inblank(zleline[zlecs]))
break;
- zlecs = pos;
+ nl += (zleline[zlecs] == ZWC('\n'));
+ if (nl == 2) {
+ INCCS();
+ break;
+ }
}
if (zlecs) {
int pos = zlecs;
- DECPOS(pos);
if (Z_vialnum(zleline[pos])) {
for (;;) {
zlecs = pos;
@@ -293,7 +310,7 @@ vibackwardword(char **args)
if (zlecs == 0)
break;
DECPOS(pos);
- if (Z_vialnum(zleline[pos]) || ZC_iblank(zleline[pos]))
+ if (Z_vialnum(zleline[pos]) || ZC_inblank(zleline[pos]))
break;
}
}
@@ -316,17 +333,20 @@ vibackwardblankword(char **args)
return ret;
}
while (n--) {
+ int nl = 0;
while (zlecs) {
int pos = zlecs;
DECPOS(pos);
- if (!ZC_iblank(zleline[pos]))
+ if (!ZC_inblank(zleline[pos]))
break;
+ nl += (zleline[pos] == ZWC('\n'));
+ if (nl == 2) break;
zlecs = pos;
}
while (zlecs) {
int pos = zlecs;
DECPOS(pos);
- if (ZC_iblank(zleline[pos]))
+ if (ZC_inblank(zleline[pos]))
break;
zlecs = pos;
}
@@ -336,6 +356,62 @@ vibackwardblankword(char **args)
/**/
int
+vibackwardwordend(char **args)
+{
+ int n = zmult;
+
+ if (n < 0) {
+ int ret;
+ zmult = -n;
+ ret = viforwardwordend(args);
+ zmult = n;
+ return ret;
+ }
+ while (n-- && zlecs > 1) {
+ int start = 0;
+ if (Z_vialnum(zleline[zlecs]))
+ start = 1;
+ else if (!ZC_inblank(zleline[zlecs]))
+ start = 2;
+ DECCS();
+ while (zlecs) {
+ int same = (start != 1) && ZC_iblank(zleline[zlecs]);
+ if (start)
+ same |= Z_vialnum(zleline[zlecs]);
+ if (same == (start == 2))
+ break;
+ DECCS();
+ }
+ while (zlecs && ZC_iblank(zleline[zlecs]))
+ DECCS();
+ }
+ return 0;
+}
+
+/**/
+int
+vibackwardblankwordend(char **args)
+{
+ int n = zmult;
+
+ if (n < 0) {
+ int ret;
+ zmult = -n;
+ ret = viforwardblankwordend(args);
+ zmult = n;
+ return ret;
+ }
+ while (n--) {
+ while (zlecs && !ZC_inblank(zleline[zlecs]))
+ DECCS();
+ while (zlecs && ZC_inblank(zleline[zlecs]))
+ DECCS();
+ }
+ return 0;
+}
+
+/**/
+int
emacsbackwardword(char **args)
{
int n = zmult;
@@ -629,7 +705,6 @@ transposewords(UNUSED(char **args))
DECPOS(pos);
if (zleline[pos] == ZWC('\n'))
return 1;
- x = pos;
}
for (p4 = x; p4 != zlell && ZC_iword(zleline[p4]); INCPOS(p4))
;
diff --git a/Src/builtin.c b/Src/builtin.c
index 4a10c7dd1..ffde5c916 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -119,7 +119,7 @@ static struct builtin builtins[] =
BUILTIN("times", BINF_PSPECIAL, bin_times, 0, 0, 0, NULL, NULL),
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, "ampfsw", "v"),
+ 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("umask", 0, bin_umask, 0, 1, 0, "S", NULL),
BUILTIN("unalias", 0, bin_unhash, 1, -1, 0, "ms", "a"),
@@ -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, "acmpvfsw", NULL),
- BUILTIN("where", 0, bin_whence, 0, -1, 0, "pmsw", "ca"),
- BUILTIN("which", 0, bin_whence, 0, -1, 0, "ampsw", "c"),
+ 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("zmodload", 0, bin_zmodload, 0, -1, 0, "AFRILP:abcfdilmpue", NULL),
BUILTIN("zcompile", 0, bin_zcompile, 0, -1, 0, "tUMRcmzka", NULL),
};
@@ -422,7 +422,7 @@ execbuiltin(LinkList args, Builtin bn)
argc -= argv - argarr;
if (errflag) {
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
return 1;
}
@@ -719,7 +719,7 @@ bin_dirs(UNUSED(char *name), char **argv, Options ops, UNUSED(int func))
for (node = firstnode(dirstack); node; incnode(node)) {
printf(fmt, pos++);
if (OPT_ISSET(ops,'l'))
- fputs(getdata(node), stdout);
+ zputs(getdata(node), stdout);
else
fprintdir(getdata(node), stdout);
@@ -1093,9 +1093,11 @@ cd_try_chdir(char *pfix, char *dest, int hard)
} else {
int pfl = strlen(pfix);
dlen = strlen(pwd);
-
+ if (dlen == 1 && *pwd == '/')
+ dlen = 0;
buf = zalloc(dlen + pfl + strlen(dest) + 3);
- strcpy(buf, pwd);
+ if (dlen)
+ strcpy(buf, pwd);
buf[dlen] = '/';
strcpy(buf + dlen + 1, pfix);
buf[dlen + 1 + pfl] = '/';
@@ -1156,9 +1158,11 @@ cd_new_pwd(int func, LinkNode dir, int quiet)
zsfree(getlinknode(dirstack));
if (chasinglinks) {
- s = new_pwd;
- new_pwd = findpwd(s);
- zsfree(s);
+ s = findpwd(new_pwd);
+ if (s) {
+ zsfree(new_pwd);
+ new_pwd = s;
+ }
}
if (isset(PUSHDIGNOREDUPS)) {
LinkNode n;
@@ -1363,10 +1367,19 @@ bin_fc(char *nam, char **argv, Options ops, int func)
if (*argv) {
hf = *argv++;
if (*argv) {
- hs = zstrtol(*argv++, NULL, 10);
- if (*argv)
- shs = zstrtol(*argv++, NULL, 10);
- else
+ char *check;
+ hs = zstrtol(*argv++, &check, 10);
+ if (*check) {
+ zwarnnam("fc", "HISTSIZE must be an integer");
+ return 1;
+ }
+ if (*argv) {
+ shs = zstrtol(*argv++, &check, 10);
+ if (*check) {
+ zwarnnam("fc", "SAVEHIST must be an integer");
+ return 1;
+ }
+ } else
shs = hs;
if (*argv) {
zwarnnam("fc", "too many arguments");
@@ -1855,7 +1868,7 @@ typeset_setbase(const char *name, Param pm, Options ops, int on, int always)
if (arg) {
char *eptr;
- pm->base = (int)zstrtol(arg, &eptr, 10);
+ int base = (int)zstrtol(arg, &eptr, 10);
if (*eptr) {
if (on & PM_INTEGER)
zwarnnam(name, "bad base value: %s", arg);
@@ -1863,11 +1876,12 @@ typeset_setbase(const char *name, Param pm, Options ops, int on, int always)
zwarnnam(name, "bad precision value: %s", arg);
return 1;
}
- if (pm->base < 2 || pm->base > 36) {
+ if ((on & PM_INTEGER) && (base < 2 || base > 36)) {
zwarnnam(name, "invalid base (must be 2 to 36 inclusive): %d",
- pm->base);
+ base);
return 1;
}
+ pm->base = base;
} else if (always)
pm->base = 0;
@@ -1918,15 +1932,19 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
* locallevel as an unset one we use the pm struct anyway: that's
* handled in createparam(). Here we just avoid using it for the
* present tests if it's unset.
+ *
+ * POSIXBUILTINS horror: we need to retain the 'readonly' flag
+ * of an unset parameter.
*/
- usepm = pm && !(pm->node.flags & PM_UNSET);
+ usepm = pm && (!(pm->node.flags & PM_UNSET) ||
+ (isset(POSIXBUILTINS) && (pm->node.flags & PM_READONLY)));
/*
* We need to compare types with an existing pm if special,
* even if that's unset
*/
- if (pm && (pm->node.flags & PM_SPECIAL))
- usepm = 1;
+ if (!usepm && pm && (pm->node.flags & PM_SPECIAL))
+ usepm = 2; /* indicate that we preserve the PM_UNSET flag */
/*
* Don't use an existing param if
@@ -2019,6 +2037,20 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
else if (newspecial != NS_NONE && strcmp(pname, "SECONDS") == 0)
newspecial = NS_SECONDS;
+ if (isset(POSIXBUILTINS)) {
+ /*
+ * Stricter rules about retaining readonly attribute in this case.
+ */
+ if ((on & PM_READONLY) && (!usepm || (pm->node.flags & PM_UNSET)) &&
+ !value)
+ on |= PM_UNSET;
+ else if (usepm && (pm->node.flags & PM_READONLY) &&
+ !(on & PM_READONLY)) {
+ zerr("read-only variable: %s", pm->node.nam);
+ return NULL;
+ }
+ }
+
/*
* A parameter will be local if
* 1. we are re-using an existing local parameter
@@ -2063,7 +2095,17 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
arrfixenv(pm->node.nam, x);
}
}
- pm->node.flags = (pm->node.flags | (on & ~PM_READONLY)) & ~(off | PM_UNSET);
+ if (usepm == 2) /* do not change the PM_UNSET flag */
+ pm->node.flags = (pm->node.flags | (on & ~PM_READONLY)) & ~off;
+ else {
+ /*
+ * Keep unset if using readonly in POSIX mode.
+ */
+ if (!(on & PM_READONLY) || !isset(POSIXBUILTINS))
+ off |= PM_UNSET;
+ pm->node.flags = (pm->node.flags |
+ (on & ~PM_READONLY)) & ~off;
+ }
if (on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) {
if (typeset_setwidth(cname, pm, ops, on, 0))
return NULL;
@@ -2239,7 +2281,12 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
* readonly flag
*/
pm = createparam(pname, on & ~PM_READONLY);
- DPUTS(!pm, "BUG: parameter not created");
+ if (!pm) {
+ if (on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z |
+ PM_INTEGER | PM_EFLOAT | PM_FFLOAT))
+ zerrnam(cname, "can't change variable attribute: %s", pname);
+ return NULL;
+ }
if (on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) {
if (typeset_setwidth(cname, pm, ops, on, 0))
return NULL;
@@ -2501,6 +2548,8 @@ bin_typeset(char *name, char **argv, Options ops, int func)
asg->name),
func, (on | PM_ARRAY) & ~PM_EXPORTED,
off, roff, asg->value, NULL, ops, 0))) {
+ if (oldval)
+ zsfree(oldval);
unqueue_signals();
return 1;
}
@@ -3127,7 +3176,7 @@ bin_unset(char *name, char **argv, Options ops, int func)
}
}
returnval = errflag;
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
} else {
zerrnam(name, "%s: invalid element for unset", s);
returnval = 1;
@@ -3145,6 +3194,15 @@ bin_unset(char *name, char **argv, Options ops, int func)
/* type, whence, which, command */
+static LinkList matchednodes;
+
+static void
+fetchcmdnamnode(HashNode hn, UNUSED(int printflags))
+{
+ Cmdnam cn = (Cmdnam) hn;
+ addlinknode(matchednodes, cn->node.nam);
+}
+
/**/
int
bin_whence(char *nam, char **argv, Options ops, int func)
@@ -3155,8 +3213,8 @@ bin_whence(char *nam, char **argv, Options ops, int func)
int printflags = 0;
int aliasflags;
int csh, all, v, wd;
- int informed;
- char *cnam;
+ int informed = 0;
+ char *cnam, **allmatched = 0;
/* Check some option information */
csh = OPT_ISSET(ops,'c');
@@ -3189,6 +3247,11 @@ bin_whence(char *nam, char **argv, Options ops, int func)
/* With -m option -- treat arguments as a glob patterns */
if (OPT_ISSET(ops,'m')) {
+ cmdnamtab->filltable(cmdnamtab);
+ if (all) {
+ pushheap();
+ matchednodes = newlinklist();
+ }
for (; *argv; argv++) {
/* parse the pattern */
tokenize(*argv);
@@ -3204,97 +3267,105 @@ bin_whence(char *nam, char **argv, Options ops, int func)
* We're not using it, so search for ... */
/* aliases ... */
+ informed +=
scanmatchtable(aliastab, pprog, 1, 0, DISABLED,
aliastab->printnode, printflags);
/* and reserved words ... */
+ informed +=
scanmatchtable(reswdtab, pprog, 1, 0, DISABLED,
reswdtab->printnode, printflags);
/* and shell functions... */
+ informed +=
scanmatchtable(shfunctab, pprog, 1, 0, DISABLED,
shfunctab->printnode, printflags);
/* and builtins. */
+ informed +=
scanmatchtable(builtintab, pprog, 1, 0, DISABLED,
builtintab->printnode, printflags);
}
/* Done search for `internal' commands, if the -p option *
* was not used. Now search the path. */
- cmdnamtab->filltable(cmdnamtab);
+ informed +=
scanmatchtable(cmdnamtab, pprog, 1, 0, 0,
- cmdnamtab->printnode, printflags);
+ (all ? fetchcmdnamnode : cmdnamtab->printnode),
+ printflags);
unqueue_signals();
}
- return returnval;
+ if (all) {
+ allmatched = argv = zlinklist2array(matchednodes);
+ matchednodes = NULL;
+ popheap();
+ } else
+ return returnval || !informed;
}
/* Take arguments literally -- do not glob */
queue_signals();
for (; *argv; argv++) {
- informed = 0;
-
- if (!OPT_ISSET(ops,'p')) {
+ if (!OPT_ISSET(ops,'p') && !allmatched) {
char *suf;
/* Look for alias */
if ((hn = aliastab->getnode(aliastab, *argv))) {
aliastab->printnode(hn, aliasflags);
+ informed = 1;
if (!all)
continue;
- informed = 1;
}
/* Look for suffix alias */
if ((suf = strrchr(*argv, '.')) && suf[1] &&
suf > *argv && suf[-1] != Meta &&
(hn = sufaliastab->getnode(sufaliastab, suf+1))) {
sufaliastab->printnode(hn, printflags);
+ informed = 1;
if (!all)
continue;
- informed = 1;
}
/* Look for reserved word */
if ((hn = reswdtab->getnode(reswdtab, *argv))) {
reswdtab->printnode(hn, printflags);
+ informed = 1;
if (!all)
continue;
- informed = 1;
}
/* Look for shell function */
if ((hn = shfunctab->getnode(shfunctab, *argv))) {
shfunctab->printnode(hn, printflags);
+ informed = 1;
if (!all)
continue;
- informed = 1;
}
/* Look for builtin command */
if ((hn = builtintab->getnode(builtintab, *argv))) {
builtintab->printnode(hn, printflags);
+ informed = 1;
if (!all)
continue;
- informed = 1;
}
/* Look for commands that have been added to the *
* cmdnamtab with the builtin `hash foo=bar'. */
if ((hn = cmdnamtab->getnode(cmdnamtab, *argv)) && (hn->flags & HASHED)) {
cmdnamtab->printnode(hn, printflags);
+ informed = 1;
if (!all)
continue;
- informed = 1;
}
}
/* Option -a is to search the entire path, *
* rather than just looking for one match. */
- if (all) {
+ if (all && **argv != '/') {
char **pp, *buf;
pushheap();
for (pp = path; *pp; pp++) {
if (**pp) {
buf = zhtricat(*pp, "/", *argv);
- } else buf = ztrdup(*argv);
+ } else buf = dupstring(*argv);
if (iscom(buf)) {
if (wd) {
@@ -3303,8 +3374,8 @@ bin_whence(char *nam, char **argv, Options ops, int func)
if (v && !csh)
zputs(*argv, stdout), fputs(" is ", stdout);
zputs(buf, stdout);
- if (OPT_ISSET(ops,'s'))
- print_if_link(buf);
+ if (OPT_ISSET(ops,'s') || OPT_ISSET(ops, 'S'))
+ print_if_link(buf, OPT_ISSET(ops, 'S'));
fputc('\n', stdout);
}
informed = 1;
@@ -3324,10 +3395,11 @@ bin_whence(char *nam, char **argv, Options ops, int func)
if (v && !csh)
zputs(*argv, stdout), fputs(" is ", stdout);
zputs(cnam, stdout);
- if (OPT_ISSET(ops,'s'))
- print_if_link(cnam);
+ if (OPT_ISSET(ops,'s') || OPT_ISSET(ops,'S'))
+ print_if_link(cnam, OPT_ISSET(ops,'S'));
fputc('\n', stdout);
}
+ informed = 1;
} else {
/* Not found at all. */
if (v || csh || wd)
@@ -3335,8 +3407,11 @@ bin_whence(char *nam, char **argv, Options ops, int func)
returnval = 1;
}
}
+ if (allmatched)
+ freearray(allmatched);
+
unqueue_signals();
- return returnval;
+ return returnval || !informed;
}
/**** command & named directory hash table builtins ****/
@@ -3689,9 +3764,9 @@ int
bin_print(char *name, char **args, Options ops, int func)
{
int flen, width, prec, type, argc, n, narg, curlen = 0;
- int nnl = 0, fmttrunc = 0, ret = 0, maxarg = 0;
+ int nnl = 0, fmttrunc = 0, ret = 0, maxarg = 0, nc = 0;
int flags[5], *len;
- char *start, *endptr, *c, *d, *flag, *buf, spec[13], *fmt = NULL;
+ char *start, *endptr, *c, *d, *flag, *buf = NULL, spec[13], *fmt = NULL;
char **first, **argp, *curarg, *flagch = "0+- #", save = '\0', nullstr = '\0';
size_t rcount, count = 0;
#ifdef HAVE_OPEN_MEMSTREAM
@@ -3749,7 +3824,7 @@ bin_print(char *name, char **args, Options ops, int func)
/* compute lengths, and interpret according to -P, -D, -e, etc. */
argc = arrlen(args);
len = (int *) hcalloc(argc * sizeof(int));
- for(n = 0; n < argc; n++) {
+ for (n = 0; n < argc; n++) {
/* first \ sequences */
if (fmt ||
(!OPT_ISSET(ops,'e') &&
@@ -3802,8 +3877,38 @@ bin_print(char *name, char **args, Options ops, int func)
}
}
+ /* -o and -O -- sort the arguments */
+ if (OPT_ISSET(ops,'o') || OPT_ISSET(ops,'O')) {
+ int flags;
+
+ if (fmt && !*args)
+ return 0;
+ flags = OPT_ISSET(ops,'i') ? SORTIT_IGNORING_CASE : 0;
+ if (OPT_ISSET(ops,'O'))
+ flags |= SORTIT_BACKWARDS;
+ strmetasort(args, flags, len);
+ }
+
+ /* -C -- number of columns */
+ if (!fmt && OPT_ISSET(ops,'C')) {
+ char *eptr, *argptr = OPT_ARG(ops,'C');
+ nc = (int)zstrtol(argptr, &eptr, 10);
+ if (*eptr) {
+ zwarnnam(name, "number expected after -%c: %s", 'C', argptr);
+ return 1;
+ }
+ if (nc <= 0) {
+ zwarnnam(name, "invalid number of columns: %s", argptr);
+ return 1;
+ }
+ }
+
/* -u and -p -- output to other than standard output */
- if (OPT_HASARG(ops,'u') || OPT_ISSET(ops,'p')) {
+ if ((OPT_HASARG(ops,'u') || OPT_ISSET(ops,'p')) &&
+ /* rule out conflicting options -- historical precedence */
+ ((!fmt && (OPT_ISSET(ops,'c') || OPT_ISSET(ops,'C'))) ||
+ !(OPT_ISSET(ops, 'z') ||
+ OPT_ISSET(ops, 's') || OPT_ISSET(ops, 'S')))) {
int fdarg, fd;
if (OPT_ISSET(ops, 'p')) {
@@ -3816,7 +3921,7 @@ bin_print(char *name, char **args, Options ops, int func)
char *argptr = OPT_ARG(ops,'u'), *eptr;
/* Handle undocumented feature that -up worked */
if (!strcmp(argptr, "p")) {
- fdarg= coprocout;
+ fdarg = coprocout;
if (fdarg < 0) {
zwarnnam(name, "-p: no coprocess");
return 1;
@@ -3842,24 +3947,9 @@ bin_print(char *name, char **args, Options ops, int func)
}
}
- /* -o and -O -- sort the arguments */
- if (OPT_ISSET(ops,'o') || OPT_ISSET(ops,'O')) {
- int flags;
-
- if (fmt && !*args) {
- if (fout != stdout)
- fclose(fout);
- return 0;
- }
- flags = OPT_ISSET(ops,'i') ? SORTIT_IGNORING_CASE : 0;
- if (OPT_ISSET(ops,'O'))
- flags |= SORTIT_BACKWARDS;
- strmetasort(args, flags, len);
- }
-
/* -c -- output in columns */
if (!fmt && (OPT_ISSET(ops,'c') || OPT_ISSET(ops,'C'))) {
- int l, nc, nr, sc, n, t, i;
+ int l, nr, sc, n, t, i;
#ifdef MULTIBYTE_SUPPORT
int *widths;
@@ -3930,19 +4020,9 @@ bin_print(char *name, char **args, Options ops, int func)
#endif
if (OPT_ISSET(ops,'C')) {
- char *eptr, *argptr = OPT_ARG(ops,'C');
- nc = (int)zstrtol(argptr, &eptr, 10);
- if (*eptr) {
- zwarnnam(name, "number expcted after -%c: %s", 'C', argptr);
- return 1;
- }
- if (nc <= 0) {
- zwarnnam(name, "invalid number of columns: %s", argptr);
- return 1;
- }
/*
* n: number of elements
- * nc: number of columns
+ * nc: number of columns (above)
* nr: number of rows
*/
n = arrlen(args);
@@ -4168,6 +4248,10 @@ bin_print(char *name, char **args, Options ops, int func)
narg);
if (fout != stdout)
fclose(fout);
+#ifdef HAVE_OPEN_MEMSTREAM
+ if (buf)
+ free(buf);
+#endif
return 1;
} else {
if (narg > maxarg) maxarg = narg;
@@ -4201,6 +4285,10 @@ bin_print(char *name, char **args, Options ops, int func)
narg);
if (fout != stdout)
fclose(fout);
+#ifdef HAVE_OPEN_MEMSTREAM
+ if (buf)
+ free(buf);
+#endif
return 1;
} else {
if (narg > maxarg) maxarg = narg;
@@ -4211,7 +4299,7 @@ bin_print(char *name, char **args, Options ops, int func)
if (*argp) {
width = (int)mathevali(*argp++);
if (errflag) {
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
ret = 1;
}
}
@@ -4230,6 +4318,10 @@ bin_print(char *name, char **args, Options ops, int func)
narg);
if (fout != stdout)
fclose(fout);
+#ifdef HAVE_OPEN_MEMSTREAM
+ if (buf)
+ free(buf);
+#endif
return 1;
} else {
if (narg > maxarg) maxarg = narg;
@@ -4241,7 +4333,7 @@ bin_print(char *name, char **args, Options ops, int func)
if (*argp) {
prec = (int)mathevali(*argp++);
if (errflag) {
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
ret = 1;
}
}
@@ -4385,6 +4477,10 @@ bin_print(char *name, char **args, Options ops, int func)
(fflush(fout) != 0 && errno != EBADF)) {
zwarnnam(name, "write error: %e", errno);
}
+#ifdef HAVE_OPEN_MEMSTREAM
+ if (buf)
+ free(buf);
+#endif
return 1;
}
@@ -4421,7 +4517,7 @@ bin_print(char *name, char **args, Options ops, int func)
zlongval = (curarg) ? mathevali(curarg) : 0;
if (errflag) {
zlongval = 0;
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
ret = 1;
}
print_val(zlongval)
@@ -4450,7 +4546,7 @@ bin_print(char *name, char **args, Options ops, int func)
} else doubleval = 0;
if (errflag) {
doubleval = 0;
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
ret = 1;
}
print_val(doubleval)
@@ -4463,7 +4559,7 @@ bin_print(char *name, char **args, Options ops, int func)
zulongval = (curarg) ? mathevali(curarg) : 0;
if (errflag) {
zulongval = 0;
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
ret = 1;
}
print_val(zulongval)
@@ -4481,7 +4577,8 @@ bin_print(char *name, char **args, Options ops, int func)
if (OPT_ISSET(ops,'z') || OPT_ISSET(ops,'s')) {
#ifdef HAVE_OPEN_MEMSTREAM
putc(0, fout);
- fflush(fout);
+ fclose(fout);
+ fout = NULL;
#else
rewind(fout);
buf = (char *)zalloc(count + 1);
@@ -4502,11 +4599,16 @@ bin_print(char *name, char **args, Options ops, int func)
unqueue_signals();
}
- /* Testing EBADF special-cases >&- redirections */
- if ((fout != stdout) ? (fclose(fout) != 0) :
- (fflush(fout) != 0 && errno != EBADF)) {
- zwarnnam(name, "write error: %e", errno);
- ret = 1;
+#ifdef HAVE_OPEN_MEMSTREAM
+ if (fout)
+#endif
+ {
+ /* Testing EBADF special-cases >&- redirections */
+ if ((fout != stdout) ? (fclose(fout) != 0) :
+ (fflush(fout) != 0 && errno != EBADF)) {
+ zwarnnam(name, "write error: %e", errno);
+ ret = 1;
+ }
}
return ret;
}
@@ -4840,7 +4942,7 @@ zexit(int val, int from_where)
in_exit = -1;
/*
* We want to do all remaining processing regardless of preceding
- * errors.
+ * errors, even user interrupts.
*/
errflag = 0;
@@ -5043,7 +5145,7 @@ eval(char **argv)
if (fpushed)
funcstack = funcstack->prev;
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
scriptname = oscriptname;
ineval = oineval;
@@ -5140,7 +5242,7 @@ bin_emulate(UNUSED(char *nam), char **argv, Options ops, UNUSED(int func))
if (cmd) {
if (opt_L) {
zwarnnam("emulate", "option -L incompatible with -c");
- goto restore;
+ goto restore2;
}
*--argv = cmd; /* on stack, never free()d, see execbuiltin() */
} else
@@ -5178,6 +5280,7 @@ bin_emulate(UNUSED(char *nam), char **argv, Options ops, UNUSED(int func))
}
ret = eval(argv);
sticky = save_sticky;
+restore2:
emulation = saveemulation;
memcpy(opts, saveopts, sizeof(opts));
restorepatterndisables(savepatterns);
@@ -6061,7 +6164,7 @@ bin_test(char *name, char **argv, UNUSED(Options ops), int func)
}
}
- lexsave();
+ zcontext_save();
testargs = argv;
tok = NULLTOK;
condlex = testlex;
@@ -6070,17 +6173,17 @@ bin_test(char *name, char **argv, UNUSED(Options ops), int func)
condlex = zshlex;
if (errflag) {
- errflag = 0;
- lexrestore();
+ errflag &= ~ERRFLAG_ERROR;
+ zcontext_restore();
return 1;
}
if (!prog || tok == LEXERR) {
zwarnnam(name, tokstr ? "parse error" : "argument expected");
- lexrestore();
+ zcontext_restore();
return 1;
}
- lexrestore();
+ zcontext_restore();
if (*curtestarg) {
zwarnnam(name, "too many arguments");
@@ -6247,7 +6350,7 @@ bin_let(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int func))
while (*argv)
val = matheval(*argv++);
/* Errors in math evaluation in let are non-fatal. */
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
/* should test for fabs(val.u.d) < epsilon? */
return (val.type == MN_INTEGER) ? val.u.l == 0 : val.u.d == 0.0;
}
diff --git a/Src/compat.c b/Src/compat.c
index b0bcb6265..b3a8b063c 100644
--- a/Src/compat.c
+++ b/Src/compat.c
@@ -443,7 +443,7 @@ zgetcwd(void)
ret = getcwd(cwdbuf, PATH_MAX);
if (ret)
ret = dupstring(ret);
- free(cwdbuf);
+ zfree(cwdbuf, PATH_MAX);
#endif /* GETCWD_CALLS_MALLOC */
}
#endif /* HAVE_GETCWD */
@@ -951,3 +951,18 @@ int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n)
/**/
#endif /* BROKEN_WCWIDTH && (__STDC_ISO_10646__ || __APPLE__) */
+/**/
+#if defined(__APPLE__) && defined(BROKEN_ISPRINT)
+
+/**/
+int
+isprint_ascii(int c)
+{
+ if (!strcmp(nl_langinfo(CODESET), "UTF-8"))
+ return (c >= 0x20 && c <= 0x7e);
+ else
+ return isprint(c);
+}
+
+/**/
+#endif /* __APPLE__ && BROKEN_ISPRINT */
diff --git a/Src/context.c b/Src/context.c
new file mode 100644
index 000000000..1b8741f46
--- /dev/null
+++ b/Src/context.c
@@ -0,0 +1,116 @@
+/*
+ * context.c - context save and restore
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 2015 Peter Stephenson
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Peter Stephenson or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Peter Stephenson and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Peter Stephenson and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose. The software
+ * provided hereunder is on an "as is" basis, and Peter Stephenson and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+/*
+ * This short file provides a home for the stack of saved contexts.
+ * The actions for saving and restoring are encapsulated within
+ * individual modules.
+ */
+
+#include "zsh.mdh"
+#include "context.pro"
+
+struct context_stack {
+ struct context_stack *next;
+
+ struct hist_stack hist_stack;
+ struct lex_stack lex_stack;
+ struct parse_stack parse_stack;
+};
+
+static struct context_stack *cstack;
+
+/* save some or all of current context */
+
+/**/
+mod_export void
+zcontext_save_partial(int parts)
+{
+ struct context_stack *cs;
+
+ cs = (struct context_stack *)malloc(sizeof(struct context_stack));
+
+ if (parts & ZCONTEXT_HIST) {
+ hist_context_save(&cs->hist_stack, !cstack);
+ }
+ if (parts & ZCONTEXT_LEX) {
+ lex_context_save(&cs->lex_stack, !cstack);
+ }
+ if (parts & ZCONTEXT_PARSE) {
+ parse_context_save(&cs->parse_stack, !cstack);
+ }
+
+ cs->next = cstack;
+ cstack = cs;
+}
+
+/* save context in full */
+
+/**/
+mod_export void
+zcontext_save(void)
+{
+ zcontext_save_partial(ZCONTEXT_HIST|ZCONTEXT_LEX|ZCONTEXT_PARSE);
+}
+
+/* restore context or part thereof */
+
+/**/
+mod_export void
+zcontext_restore_partial(int parts)
+{
+ struct context_stack *cs = cstack;
+
+ DPUTS(!cstack, "BUG: zcontext_restore() without zcontext_save()");
+
+ queue_signals();
+ cstack = cstack->next;
+
+ if (parts & ZCONTEXT_HIST) {
+ hist_context_restore(&cs->hist_stack, !cstack);
+ }
+ if (parts & ZCONTEXT_LEX) {
+ lex_context_restore(&cs->lex_stack, !cstack);
+ }
+ if (parts & ZCONTEXT_PARSE) {
+ parse_context_restore(&cs->parse_stack, !cstack);
+ }
+
+ free(cs);
+
+ unqueue_signals();
+}
+
+/* restore full context */
+
+/**/
+mod_export void
+zcontext_restore(void)
+{
+ zcontext_restore_partial(ZCONTEXT_HIST|ZCONTEXT_LEX|ZCONTEXT_PARSE);
+}
diff --git a/Src/exec.c b/Src/exec.c
index d0fadd69a..6a8b35a36 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -59,7 +59,7 @@ mod_export int noerrs;
/**/
int nohistsave;
-/* error/break flag */
+/* error flag: bits from enum errflag_bits */
/**/
mod_export int errflag;
@@ -217,7 +217,7 @@ parse_string(char *s, int reset_lineno)
Eprog p;
zlong oldlineno;
- lexsave();
+ zcontext_save();
inpush(s, INP_LINENO, NULL);
strinbeg(0);
oldlineno = lineno;
@@ -229,7 +229,7 @@ parse_string(char *s, int reset_lineno)
lastval = 1;
strinend();
inpop();
- lexrestore();
+ zcontext_restore();
return p;
}
@@ -250,6 +250,7 @@ zsetlimit(int limnum, char *nam)
if (setrlimit(limnum, limits + limnum)) {
if (nam)
zwarnnam(nam, "setrlimit failed: %e", errno);
+ limits[limnum] = current_limits[limnum];
return -1;
}
current_limits[limnum] = limits[limnum];
@@ -1145,7 +1146,8 @@ execlist(Estate state, int dont_change_job, int exiting)
Wordcode next;
wordcode code;
int ret, cj, csp, ltype;
- int old_pline_level, old_list_pipe;
+ int old_pline_level, old_list_pipe, old_list_pipe_job;
+ char *old_list_pipe_text;
zlong oldlineno;
/*
* ERREXIT only forces the shell to exit if the last command in a &&
@@ -1158,10 +1160,17 @@ execlist(Estate state, int dont_change_job, int exiting)
cj = thisjob;
old_pline_level = pline_level;
old_list_pipe = list_pipe;
+ old_list_pipe_job = list_pipe_job;
+ if (*list_pipe_text)
+ old_list_pipe_text = ztrdup(list_pipe_text);
+ else
+ old_list_pipe_text = NULL;
oldlineno = lineno;
- if (sourcelevel && unset(SHINSTDIN))
- pline_level = list_pipe = 0;
+ if (sourcelevel && unset(SHINSTDIN)) {
+ pline_level = list_pipe = list_pipe_job = 0;
+ *list_pipe_text = '\0';
+ }
/* Loop over all sets of comands separated by newline, *
* semi-colon or ampersand (`sublists'). */
@@ -1396,6 +1405,13 @@ sublist_done:
}
pline_level = old_pline_level;
list_pipe = old_list_pipe;
+ list_pipe_job = old_list_pipe_job;
+ if (old_list_pipe_text) {
+ strcpy(list_pipe_text, old_list_pipe_text);
+ zsfree(old_list_pipe_text);
+ } else {
+ *list_pipe_text = '\0';
+ }
lineno = oldlineno;
if (dont_change_job)
thisjob = cj;
@@ -1600,7 +1616,8 @@ execpline(Estate state, wordcode slcode, int how, int last1)
(killpg(jobtab[list_pipe_job].gleader, 0) == -1 ? 2 : 1);
list_pipe_pid = pid;
list_pipe_start = bgtime;
- nowait = errflag = 1;
+ nowait = 1;
+ errflag |= ERRFLAG_ERROR;
breaks = loops;
close(synch[1]);
read_loop(synch[0], &dummy, 1);
@@ -1633,7 +1650,10 @@ execpline(Estate state, wordcode slcode, int how, int last1)
list_pipe_child = 1;
opts[INTERACTIVE] = 0;
if (errbrk_saved) {
- errflag = prev_errflag;
+ /*
+ * Keep any user interrupt bit in errflag.
+ */
+ errflag = prev_errflag | (errflag & ERRFLAG_INT);
breaks = prev_breaks;
}
break;
@@ -1718,12 +1738,14 @@ execpline2(Estate state, wordcode pcode,
if (pipe(synch) < 0) {
zerr("pipe failed: %e", errno);
- lastval = errflag = 1;
+ lastval = 1;
+ errflag |= ERRFLAG_ERROR;
return;
} else if ((pid = zfork(&bgtime)) == -1) {
close(synch[0]);
close(synch[1]);
- lastval = errflag = 1;
+ lastval = 1;
+ errflag |= ERRFLAG_ERROR;
return;
} else if (pid) {
char dummy, *text;
@@ -2242,8 +2264,16 @@ addvars(Estate state, Wordcode pc, int addflags)
state->pc = opc;
return;
}
- if (isset(GLOBASSIGN) || !isstr)
+ if (!isstr || (isset(GLOBASSIGN) &&
+ 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))
+ unsetparam(name);
+ }
if (errflag) {
state->pc = opc;
return;
@@ -2279,6 +2309,8 @@ addvars(Estate state, Wordcode pc, int addflags)
}
allexp = opts[ALLEXPORT];
opts[ALLEXPORT] = 1;
+ if (isset(KSHARRAYS))
+ unsetparam(name);
pm = assignsparam(name, val, myflags);
opts[ALLEXPORT] = allexp;
} else
@@ -2290,13 +2322,13 @@ addvars(Estate state, Wordcode pc, int addflags)
continue;
}
if (vl) {
- ptr = arr = (char **) zalloc(sizeof(char **) *
+ ptr = arr = (char **) zalloc(sizeof(char *) *
(countlinknodes(vl) + 1));
while (nonempty(vl))
*ptr++ = ztrdup((char *) ugetnode(vl));
} else
- ptr = arr = (char **) zalloc(sizeof(char **));
+ ptr = arr = (char **) zalloc(sizeof(char *));
*ptr = NULL;
if (xtr) {
@@ -2320,6 +2352,7 @@ addvars(Estate state, Wordcode pc, int addflags)
void
setunderscore(char *str)
{
+ queue_signals();
if (str && *str) {
int l = strlen(str) + 1, nl = (l + 31) & ~31;
@@ -2337,6 +2370,7 @@ setunderscore(char *str)
*zunderscore = '\0';
underscoreused = 1;
}
+ unqueue_signals();
}
/* These describe the type of expansions that need to be done on the words
@@ -2551,7 +2585,8 @@ execcmd(Estate state, int input, int output, int how, int last1)
while (next && *next == '-' && strlen(next) >= 2) {
if (!firstnode(args)) {
zerr("exec requires a command to execute");
- errflag = lastval = 1;
+ lastval = 1;
+ errflag |= ERRFLAG_ERROR;
goto done;
}
uremnode(args, firstnode(args));
@@ -2568,12 +2603,14 @@ execcmd(Estate state, int input, int output, int how, int last1)
} else {
if (!firstnode(args)) {
zerr("exec requires a command to execute");
- errflag = lastval = 1;
+ lastval = 1;
+ errflag |= ERRFLAG_ERROR;
goto done;
}
if (!nextnode(firstnode(args))) {
zerr("exec flag -a requires a parameter");
- errflag = lastval = 1;
+ lastval = 1;
+ errflag |= ERRFLAG_ERROR;
goto done;
}
exec_argv0 = (char *)
@@ -2589,7 +2626,8 @@ execcmd(Estate state, int input, int output, int how, int last1)
break;
default:
zerr("unknown exec flag -%c", *cmdopt);
- errflag = lastval = 1;
+ lastval = 1;
+ errflag |= ERRFLAG_ERROR;
return;
}
}
@@ -2613,6 +2651,10 @@ execcmd(Estate state, int input, int output, int how, int last1)
}
}
+ /* if we get this far, it is OK to pay attention to lastval again */
+ if (noerrexit == 2 && !is_shfunc)
+ noerrexit = 0;
+
/* Do prefork substitutions */
esprefork = (assign || isset(MAGICEQUALSUBST)) ? PREFORK_TYPESET : 0;
if (args && htok)
@@ -2652,7 +2694,8 @@ execcmd(Estate state, int input, int output, int how, int last1)
} else if (!nullcmd || !*nullcmd || opts[CSHNULLCMD] ||
(cflags & BINF_PREFIX)) {
zerr("redirection with no command");
- errflag = lastval = 1;
+ lastval = 1;
+ errflag |= ERRFLAG_ERROR;
return;
} else if (!nullcmd || !*nullcmd || opts[SHNULLCMD]) {
if (!args)
@@ -2682,7 +2725,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
if (varspc)
addvars(state, varspc, 0);
if (errflag)
- lastval = errflag;
+ lastval = 1;
else
lastval = cmdoutval;
if (isset(XTRACE)) {
@@ -2758,9 +2801,14 @@ execcmd(Estate state, int input, int output, int how, int last1)
else
text = NULL;
- /* Set up special parameter $_ */
-
- setunderscore((args && nonempty(args)) ? ((char *) getdata(lastnode(args))) : "");
+ /*
+ * Set up special parameter $_
+ * For execfuncdef we may need to take account of an
+ * anonymous function with arguments.
+ */
+ if (type != WC_FUNCDEF)
+ setunderscore((args && nonempty(args)) ?
+ ((char *) getdata(lastnode(args))) : "");
/* Warn about "rm *" */
if (type == WC_SIMPLE && interact && unset(RMSTARSILENT) &&
@@ -2786,7 +2834,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
}
}
if (!nextnode(firstnode(args)))
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
}
if (type == WC_FUNCDEF) {
@@ -2882,6 +2930,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
* is a directory we should AUTOCD to. */
if (!hn && trycd && (s = cancd(peekfirst(args)))) {
peekfirst(args) = (void *) s;
+ pushnode(args, dupstring("--"));
pushnode(args, dupstring("cd"));
if ((hn = builtintab->getnode(builtintab, "cd")))
is_builtin = 1;
@@ -2931,7 +2980,8 @@ execcmd(Estate state, int input, int output, int how, int last1)
} else if ((pid = zfork(&bgtime)) == -1) {
close(synch[0]);
close(synch[1]);
- lastval = errflag = 1;
+ lastval = 1;
+ errflag |= ERRFLAG_ERROR;
goto fatal;
}
if (pid) {
@@ -2941,8 +2991,6 @@ execcmd(Estate state, int input, int output, int how, int last1)
close(synch[0]);
if (how & Z_ASYNC) {
lastpid = (zlong) pid;
- /* indicate it's possible to set status for lastpid */
- lastpid_status = -2L;
} else if (!jobtab[thisjob].stty_in_env && varspc) {
/* search for STTY=... */
Wordcode p = varspc;
@@ -2996,6 +3044,15 @@ execcmd(Estate state, int input, int output, int how, int last1)
* Note that any form of exec means that the subshell is fake *
* (but we may be in a subshell already). */
is_exec = 1;
+ /*
+ * If we are in a subshell environment anyway, say we're forked,
+ * even if we're actually not forked because we know the
+ * subshell is exiting. This ensures SHLVL reflects the current
+ * shell, and also optimises out any save/restore we'd need to
+ * do if we were returning to the main shell.
+ */
+ if (type == WC_SUBSH)
+ forked = 1;
}
if ((esglob = !(cflags & BINF_NOGLOB)) && args && htok) {
@@ -3169,7 +3226,12 @@ execcmd(Estate state, int input, int output, int how, int last1)
}
if (fn->fd1 < 10)
closemn(mfds, fn->fd1, REDIR_CLOSE);
- if (!closed && zclose(fn->fd1) < 0) {
+ /*
+ * Only report failures to close file descriptors
+ * if they're under user control as we don't know
+ * what the previous status of others was.
+ */
+ if (!closed && zclose(fn->fd1) < 0 && fn->varid) {
zwarn("failed to close file descriptor %d: %e",
fn->fd1, errno);
}
@@ -3259,6 +3321,20 @@ execcmd(Estate state, int input, int output, int how, int last1)
closemn(mfds, i, REDIR_CLOSE);
if (nullexec) {
+ /*
+ * If nullexec is 2, we have variables to add with the redirections
+ * in place. If nullexec is 1, we may have variables but they
+ * need the standard restore logic.
+ */
+ if (varspc) {
+ LinkList restorelist = 0, removelist = 0;
+ if (!isset(POSIXBUILTINS) && nullexec != 2)
+ save_params(state, varspc, &restorelist, &removelist);
+ addvars(state, varspc, 0);
+ if (restorelist)
+ restore_params(restorelist, removelist);
+ }
+ lastval = errflag ? errflag : cmdoutval;
if (nullexec == 1) {
/*
* If nullexec is 1 we specifically *don't* restore the original
@@ -3269,13 +3345,6 @@ execcmd(Estate state, int input, int output, int how, int last1)
zclose(save[i]);
goto done;
}
- /*
- * If nullexec is 2, we have variables to add with the redirections
- * in place.
- */
- if (varspc)
- addvars(state, varspc, 0);
- lastval = errflag ? errflag : cmdoutval;
if (isset(XTRACE)) {
fputc('\n', xtrerr);
fflush(xtrerr);
@@ -3315,14 +3384,14 @@ execcmd(Estate state, int input, int output, int how, int last1)
* The copy uses the wordcode parsing area, so save and
* restore state.
*/
- lexsave();
+ zcontext_save();
redir_prog = eccopyredirs(&s);
- lexrestore();
+ zcontext_restore();
} else
redir_prog = NULL;
-
+
lastval = execfuncdef(state, redir_prog);
- }
+ }
else if (type >= WC_CURSH) {
if (last1 == 1)
do_exec = 1;
@@ -3338,14 +3407,28 @@ execcmd(Estate state, int input, int output, int how, int last1)
LinkList restorelist = 0, removelist = 0;
/* builtin or shell function */
- if (!forked && ((cflags & BINF_COMMAND) ||
- (unset(POSIXBUILTINS) && !assign) ||
- (isset(POSIXBUILTINS) && !is_shfunc &&
- !(hn->flags & BINF_PSPECIAL)))) {
- if (varspc)
+ if (!forked && varspc) {
+ int do_save = 0;
+ if (isset(POSIXBUILTINS)) {
+ /*
+ * If it's a function or special builtin --- save
+ * if it's got "command" in front.
+ * If it's a normal command --- save.
+ */
+ if (is_shfunc || (hn->flags & BINF_PSPECIAL))
+ do_save = (orig_cflags & BINF_COMMAND);
+ else
+ do_save = 1;
+ } else {
+ /*
+ * Save if it's got "command" in front or it's
+ * not a magic-equals assignment.
+ */
+ if ((cflags & BINF_COMMAND) || !assign)
+ do_save = 1;
+ }
+ if (do_save)
save_params(state, varspc, &restorelist, &removelist);
- else
- restorelist = removelist = NULL;
}
if (varspc) {
/* Export this if the command is a shell function,
@@ -3513,7 +3596,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
else
exit(1);
}
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
}
}
if (newxtrerr) {
@@ -3738,17 +3821,20 @@ gethere(char **strp, int typ)
*bptr++ = '\n';
}
*t = '\0';
+ s = buf;
+ buf = dupstring(buf);
+ zfree(s, bsiz);
if (!qt) {
int ef = errflag;
- parsestr(buf);
+ parsestr(&buf);
- if (!errflag)
- errflag = ef;
+ if (!errflag) {
+ /* Retain any user interrupt error */
+ errflag = ef | (errflag & ERRFLAG_INT);
+ }
}
- s = dupstring(buf);
- zfree(buf, bsiz);
- return s;
+ return buf;
}
/* open here string fd */
@@ -3832,13 +3918,13 @@ getoutput(char *cmd, int qt)
return NULL;
untokenize(s);
if ((stream = open(unmeta(s), O_RDONLY | O_NOCTTY)) == -1) {
- zerr("%e: %s", errno, s);
- return NULL;
+ zwarn("%e: %s", errno, s);
+ return newlinklist();
}
return readoutput(stream, qt);
}
if (mpipe(pipes) < 0) {
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
cmdoutpid = 0;
return NULL;
}
@@ -3848,7 +3934,7 @@ getoutput(char *cmd, int qt)
/* fork error */
zclose(pipes[0]);
zclose(pipes[1]);
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
cmdoutpid = 0;
child_unblock();
return NULL;
@@ -4258,7 +4344,7 @@ execcond(Estate state, UNUSED(int do_exec))
* into a shell error.
*/
if (stat == 2)
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
cmdpop();
if (isset(XTRACE)) {
fprintf(xtrerr, " ]]\n");
@@ -4298,7 +4384,7 @@ execarith(Estate state, UNUSED(int do_exec))
fflush(xtrerr);
}
if (errflag) {
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
return 2;
}
/* should test for fabs(val.u.d) < epsilon? */
@@ -4332,7 +4418,7 @@ execfuncdef(Estate state, Eprog redir_prog)
Shfunc shf;
char *s = NULL;
int signum, nprg, sbeg, nstrs, npats, len, plen, i, htok = 0, ret = 0;
- int nfunc = 0;
+ int nfunc = 0, anon_func = 0;
Wordcode beg = state->pc, end;
Eprog prog;
Patprog *pp;
@@ -4414,10 +4500,12 @@ execfuncdef(Estate state, Eprog redir_prog)
if (!names) {
/*
* Anonymous function, execute immediately.
- * Function name is "(anon)", parameter list is empty.
+ * Function name is "(anon)".
*/
LinkList args;
+ anon_func = 1;
+
state->pc = end;
end += *state->pc++;
args = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok);
@@ -4425,11 +4513,19 @@ execfuncdef(Estate state, Eprog redir_prog)
if (htok && args) {
execsubst(args);
if (errflag) {
+ freeeprog(shf->funcdef);
+ if (shf->redir) /* shouldn't be */
+ freeeprog(shf->redir);
+ zsfree(shf->filename);
+ zfree(shf, sizeof(*shf));
state->pc = end;
return 1;
}
}
+ setunderscore((args && nonempty(args)) ?
+ ((char *) getdata(lastnode(args))) : "");
+
if (!args)
args = newlinklist();
shf->node.nam = "(anon)";
@@ -4438,6 +4534,16 @@ execfuncdef(Estate state, Eprog redir_prog)
execshfunc(shf, args);
ret = lastval;
+ if (isset(PRINTEXITVALUE) && isset(SHINSTDIN) &&
+ lastval) {
+#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
+ fprintf(stderr, "zsh: exit %lld\n", lastval);
+#else
+ fprintf(stderr, "zsh: exit %ld\n", (long)lastval);
+#endif
+ fflush(stderr);
+ }
+
freeeprog(shf->funcdef);
if (shf->redir) /* shouldn't be */
freeeprog(shf->redir);
@@ -4450,6 +4556,7 @@ execfuncdef(Estate state, Eprog redir_prog)
(signum = getsignum(s + 4)) != -1) {
if (settrap(signum, NULL, ZSIG_FUNC)) {
freeeprog(shf->funcdef);
+ zsfree(shf->filename);
zfree(shf, sizeof(*shf));
state->pc = end;
return 1;
@@ -4464,6 +4571,8 @@ execfuncdef(Estate state, Eprog redir_prog)
shfunctab->addnode(shfunctab, ztrdup(s), shf);
}
}
+ if (!anon_func)
+ setunderscore("");
if (!nfunc && redir_prog) {
/* For completeness, shouldn't happen */
freeeprog(redir_prog);
@@ -4547,6 +4656,7 @@ execshfunc(Shfunc shf, LinkList args)
fputc('\n', xtrerr);
fflush(xtrerr);
}
+ queue_signals();
ocs = cmdstack;
ocsp = cmdsp;
cmdstack = (unsigned char *) zalloc(CMDSTACKSZ);
@@ -4554,7 +4664,11 @@ 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;
@@ -4562,6 +4676,7 @@ execshfunc(Shfunc shf, LinkList args)
if (!list_pipe)
deletefilelist(last_file_list, 0);
+ unqueue_signals();
}
/*
@@ -4910,7 +5025,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
(name = fname)))) {
zwarn("%s: function not defined by file", name);
if (noreturnval)
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
else
lastval = 1;
goto doneshfunc;
@@ -5243,7 +5358,7 @@ execsave(void)
{
struct execstack *es;
- es = (struct execstack *) malloc(sizeof(struct execstack));
+ es = (struct execstack *) zalloc(sizeof(struct execstack));
es->list_pipe_pid = list_pipe_pid;
es->nowait = nowait;
es->pline_level = pline_level;
diff --git a/Src/glob.c b/Src/glob.c
index ca7bc4410..057d44a17 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -386,8 +386,8 @@ insert(char *s, int checked)
while (!inserts || (news = dupstring(*inserts++))) {
if (colonmod) {
/* Handle the remainder of the qualifier: e.g. (:r:s/foo/bar/). */
- s = colonmod;
- modify(&news, &s);
+ char *mod = colonmod;
+ modify(&news, &mod);
}
if (!statted && (gf_sorts & GS_NORMAL)) {
statfullpath(s, &buf, 1);
@@ -437,7 +437,7 @@ insert(char *s, int checked)
matchptr++;
if (++matchct == matchsz) {
- matchbuf = (Gmatch )realloc((char *)matchbuf,
+ matchbuf = (Gmatch)zrealloc((char *)matchbuf,
sizeof(struct gmatch) * (matchsz *= 2));
matchptr = matchbuf + matchct;
@@ -463,7 +463,7 @@ scanner(Complist q, int shortcircuit)
int errssofar = errsfound;
struct dirsav ds;
- if (!q)
+ if (!q || errflag)
return;
init_dirsav(&ds);
@@ -682,7 +682,7 @@ parsecomplist(char *instr)
/* Now get the next path component if there is one. */
l1 = (Complist) zhalloc(sizeof *l1);
if ((l1->next = parsecomplist(instr)) == NULL) {
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
return NULL;
}
l1->pat = patcompile(NULL, compflags | PAT_ANY, NULL);
@@ -708,7 +708,8 @@ parsecomplist(char *instr)
}
l1 = (Complist) zhalloc(sizeof *l1);
l1->pat = p1;
- l1->closure = 1 + pdflag;
+ /* special case (/)# to avoid infinite recursion */
+ l1->closure = (*((char *)p1 + p1->startoff)) ? 1 + pdflag : 0;
l1->follow = 0;
l1->next = parsecomplist(instr);
return (l1->pat) ? l1 : NULL;
@@ -728,7 +729,7 @@ parsecomplist(char *instr)
return (ef && !l1->next) ? NULL : l1;
}
}
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
return NULL;
}
@@ -1790,7 +1791,7 @@ zglob(LinkList list, LinkNode np, int nountok)
insertlinknode(list, node, ostr);
return;
}
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
zerr("bad pattern: %s", ostr);
return;
}
@@ -1817,7 +1818,7 @@ zglob(LinkList list, LinkNode np, int nountok)
badcshglob |= 1; /* at least one cmd. line expansion failed */
} else if (isset(NOMATCH)) {
zerr("no matches found: %s", ostr);
- free(matchbuf);
+ zfree(matchbuf, 0);
restore_globstate(saved);
return;
} else {
@@ -1873,7 +1874,8 @@ zglob(LinkList list, LinkNode np, int nountok)
tmpptr->sortstrs[iexec] = tmpptr->name;
}
- errflag = ef;
+ /* Retain any user interrupt error status */
+ errflag = ef | (errflag & ERRFLAG_INT);
lastval = lv;
} else {
/* Failed, let's be safe */
@@ -1921,7 +1923,7 @@ zglob(LinkList list, LinkNode np, int nountok)
} else if (!badcshglob && !isset(NOMATCH) && matchct == 1) {
insert_glob_match(list, node, (--matchptr)->name);
}
- free(matchbuf);
+ zfree(matchbuf, 0);
restore_globstate(saved);
}
@@ -3733,7 +3735,8 @@ qualsheval(char *name, UNUSED(struct stat *buf), UNUSED(off_t days), char *str)
execode(prog, 1, 0, "globqual");
ret = lastval;
- errflag = ef;
+ /* Retain any user interrupt error status */
+ errflag = ef | (errflag & ERRFLAG_INT);
lastval = lv;
if (!(inserts = getaparam("reply")) &&
diff --git a/Src/hashtable.c b/Src/hashtable.c
index 7a430629d..ab381cc6a 100644
--- a/Src/hashtable.c
+++ b/Src/hashtable.c
@@ -910,7 +910,7 @@ printshfuncnode(HashNode hn, int printflags)
{
Shfunc f = (Shfunc) hn;
char *t = 0;
-
+
if ((printflags & PRINT_NAMEONLY) ||
((printflags & PRINT_WHENCE_SIMPLE) &&
!(printflags & PRINT_WHENCE_FUNCDEF))) {
@@ -922,8 +922,16 @@ printshfuncnode(HashNode hn, int printflags)
if ((printflags & (PRINT_WHENCE_VERBOSE|PRINT_WHENCE_WORD)) &&
!(printflags & PRINT_WHENCE_FUNCDEF)) {
nicezputs(f->node.nam, stdout);
- printf((printflags & PRINT_WHENCE_WORD) ? ": function\n" :
- " is a shell function\n");
+ printf((printflags & PRINT_WHENCE_WORD) ? ": function" :
+ (f->node.flags & PM_UNDEFINED) ?
+ " is an autoload shell function" :
+ " is a shell function");
+ if (f->filename && (printflags & PRINT_WHENCE_VERBOSE) &&
+ strcmp(f->filename, f->node.nam) != 0) {
+ printf(" from ");
+ quotedzputs(f->filename, stdout);
+ }
+ putchar('\n');
return;
}
diff --git a/Src/hist.c b/Src/hist.c
index 4660fd073..bd03c4f11 100644
--- a/Src/hist.c
+++ b/Src/hist.c
@@ -134,6 +134,8 @@ mod_export int hist_skip_flags;
/* Bits of histactive variable */
#define HA_ACTIVE (1<<0) /* History mechanism is active */
#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 */
/* Array of word beginnings and endings in current history line. */
@@ -222,13 +224,119 @@ static int histsave_stack_pos = 0;
static zlong histfile_linect;
+/* save history context */
+
+/**/
+void
+hist_context_save(struct hist_stack *hs, int toplevel)
+{
+ if (toplevel) {
+ /* top level, make this version visible to ZLE */
+ zle_chline = chline;
+ /* ensure line stored is NULL-terminated */
+ if (hptr)
+ *hptr = '\0';
+ }
+ hs->histactive = histactive;
+ hs->histdone = histdone;
+ hs->stophist = stophist;
+ hs->hline = chline;
+ hs->hptr = hptr;
+ hs->chwords = chwords;
+ hs->chwordlen = chwordlen;
+ hs->chwordpos = chwordpos;
+ hs->hgetc = hgetc;
+ hs->hungetc = hungetc;
+ hs->hwaddc = hwaddc;
+ hs->hwbegin = hwbegin;
+ hs->hwend = hwend;
+ hs->addtoline = addtoline;
+ hs->hlinesz = hlinesz;
+ /*
+ * We save and restore the command stack with history
+ * as it's visible to the user interactively, so if
+ * we're preserving history state we'll continue to
+ * show the current set of commands from input.
+ */
+ hs->cstack = cmdstack;
+ hs->csp = cmdsp;
+
+ stophist = 0;
+ chline = NULL;
+ hptr = NULL;
+ histactive = 0;
+ cmdstack = (unsigned char *)zalloc(CMDSTACKSZ);
+ cmdsp = 0;
+}
+
+/* restore history context */
+
+/**/
+void
+hist_context_restore(const struct hist_stack *hs, int toplevel)
+{
+ if (toplevel) {
+ /* Back to top level: don't need special ZLE value */
+ DPUTS(hs->hline != zle_chline, "BUG: Ouch, wrong chline for ZLE");
+ zle_chline = NULL;
+ }
+ histactive = hs->histactive;
+ histdone = hs->histdone;
+ stophist = hs->stophist;
+ chline = hs->hline;
+ hptr = hs->hptr;
+ chwords = hs->chwords;
+ chwordlen = hs->chwordlen;
+ chwordpos = hs->chwordpos;
+ hgetc = hs->hgetc;
+ hungetc = hs->hungetc;
+ hwaddc = hs->hwaddc;
+ hwbegin = hs->hwbegin;
+ hwend = hs->hwend;
+ addtoline = hs->addtoline;
+ hlinesz = hs->hlinesz;
+ if (cmdstack)
+ zfree(cmdstack, CMDSTACKSZ);
+ cmdstack = hs->cstack;
+ cmdsp = hs->csp;
+}
+
+/*
+ * Mark that the current level of history is within a word whatever
+ * characters turn up, or turn that mode off. This is used for nested
+ * parsing of substitutions.
+ *
+ * The caller takes care only to turn this on or off at the start
+ * or end of recursive use of the same mode, so a single flag is
+ * good enough here.
+ */
+
+/**/
+void
+hist_in_word(int yesno)
+{
+ if (yesno)
+ histactive |= HA_INWORD;
+ else
+ histactive &= ~HA_INWORD;
+}
+
/* add a character to the current history word */
static void
ihwaddc(int c)
{
/* Only if history line exists and lexing has not finished. */
- if (chline && !(errflag || lexstop)) {
+ if (chline && !(errflag || lexstop) &&
+ /*
+ * If we're reading inside a word for command substitution
+ * we allow the lexer to expand aliases but don't deal
+ * with them here. Note matching code in ihungetc().
+ * TBD: it might be neater to deal with all aliases in this
+ * fashion as we never need the expansion in the history
+ * line, only in the lexer and above.
+ */
+ (inbufflags & (INP_ALIAS|INP_HIST)) != INP_ALIAS) {
/* Quote un-expanded bangs in the history line. */
if (c == bangchar && stophist < 2 && qbang)
/* If qbang is not set, we do not escape this bangchar as it's *
@@ -287,7 +395,8 @@ ihgetc(void)
c = histsubchar(c);
if (c < 0) {
/* bad expansion */
- errflag = lexstop = 1;
+ lexstop = 1;
+ errflag |= ERRFLAG_ERROR;
return ' ';
}
}
@@ -366,6 +475,7 @@ getsubsargs(char *subline, int *gbalp, int *cflagp)
zsfree(hsubl);
hsubl = ptr1;
} else if (!hsubl) { /* fail silently on this */
+ zsfree(ptr1);
zsfree(ptr2);
return 0;
}
@@ -415,9 +525,20 @@ histsubchar(int c)
static zlong mev = -1;
char *buf, *ptr;
char *sline;
+ int lexraw_mark;
Histent ehist;
size_t buflen;
+ /*
+ * If accumulating raw input for use in command substitution,
+ * we don't want the history text, so mark it for later removal.
+ * It would be better to do this at a level above the history
+ * and below the lexer --- but there isn't one.
+ *
+ * Include the character we are attempting to substitute.
+ */
+ lexraw_mark = zshlex_raw_mark(-1);
+
/* look, no goto's */
if (isfirstch && c == hatchar) {
int gbal = 0;
@@ -721,7 +842,7 @@ histsubchar(int c)
noerrs = 1;
parse_subst_string(sline);
noerrs = one;
- errflag = oef;
+ errflag = oef | (errflag & ERRFLAG_INT);
remnulargs(sline);
untokenize(sline);
}
@@ -751,6 +872,8 @@ histsubchar(int c)
}
}
+ zshlex_raw_back_to_mark(lexraw_mark);
+
/*
* Push the expanded value onto the input stack,
* marking this as a history word for purposes of the alias stack.
@@ -789,11 +912,16 @@ ihungetc(int c)
zlemetall--;
exlast++;
}
- DPUTS(hptr <= chline, "BUG: hungetc attempted at buffer start");
- hptr--;
- DPUTS(*hptr != (char) c, "BUG: wrong character in hungetc() ");
- qbang = (c == bangchar && stophist < 2 &&
- hptr > chline && hptr[-1] == '\\');
+ if ((inbufflags & (INP_ALIAS|INP_HIST)) != INP_ALIAS) {
+ DPUTS(hptr <= chline, "BUG: hungetc attempted at buffer start");
+ hptr--;
+ DPUTS(*hptr != (char) c, "BUG: wrong character in hungetc() ");
+ qbang = (c == bangchar && stophist < 2 &&
+ hptr > chline && hptr[-1] == '\\');
+ } else {
+ /* No active bangs in aliases */
+ qbang = 0;
+ }
if (doit)
inungetc(c);
if (!qbang)
@@ -813,6 +941,11 @@ strinbeg(int dohist)
strin++;
hbegin(dohist);
lexinit();
+ /*
+ * Also initialise some variables owned by the parser but
+ * used for communication between the parser and lexer.
+ */
+ init_parse_status();
}
/* done reading a string */
@@ -880,7 +1013,8 @@ hbegin(int dohist)
char *hf;
isfirstln = isfirstch = 1;
- errflag = histdone = 0;
+ errflag &= ~ERRFLAG_ERROR;
+ histdone = 0;
if (!dohist)
stophist = 2;
else if (dohist != 2)
@@ -1110,8 +1244,11 @@ static void
putoldhistentryontop(short keep_going)
{
static Histent next = NULL;
- Histent he = keep_going? next : hist_ring->down;
- next = he->down;
+ Histent he = (keep_going || !hist_ring) ? next : hist_ring->down;
+ if (he)
+ next = he->down;
+ else
+ return;
if (isset(HISTEXPIREDUPSFIRST) && !(he->node.flags & HIST_DUP)) {
static zlong max_unique_ct = 0;
if (!keep_going)
@@ -1151,7 +1288,7 @@ prepnexthistent(void)
freehistnode(&hist_ring->node);
}
- if (histlinect < histsiz) {
+ if (histlinect < histsiz || !hist_ring) {
he = (Histent)zshcalloc(sizeof *he);
if (!hist_ring)
hist_ring = he->up = he->down = he;
@@ -1395,28 +1532,17 @@ hend(Eprog prog)
return !(flag & HISTFLAG_NOEXEC || errflag);
}
-/* Gives current expansion word if not last word before chwordpos. */
-
-/**/
-int hwgetword = -1;
-
/* begin a word */
/**/
void
ihwbegin(int offset)
{
- if (stophist == 2)
+ if (stophist == 2 || (histactive & HA_INWORD) ||
+ (inbufflags & (INP_ALIAS|INP_HIST)) == INP_ALIAS)
return;
if (chwordpos%2)
chwordpos--; /* make sure we're on a word start, not end */
- /* If we're expanding an alias, we should overwrite the expansion
- * in the history.
- */
- if ((inbufflags & INP_ALIAS) && !(inbufflags & INP_HIST))
- hwgetword = chwordpos;
- else
- hwgetword = -1;
chwords[chwordpos++] = hptr - chline + offset;
}
@@ -1426,7 +1552,8 @@ ihwbegin(int offset)
void
ihwend(void)
{
- if (stophist == 2)
+ if (stophist == 2 || (histactive & HA_INWORD) ||
+ (inbufflags & (INP_ALIAS|INP_HIST)) == INP_ALIAS)
return;
if (chwordpos%2 && chline) {
/* end of word reached and we've already begun a word */
@@ -1437,13 +1564,6 @@ ihwend(void)
(chwordlen += 32) *
sizeof(short));
}
- if (hwgetword > -1 &&
- (inbufflags & INP_ALIAS) && !(inbufflags & INP_HIST)) {
- /* We want to reuse the current word position */
- chwordpos = hwgetword;
- /* Start from where previous word ended, if possible */
- hptr = chline + chwords[chwordpos ? chwordpos - 1 : 0];
- }
} else {
/* scrub that last word, it doesn't exist */
chwordpos--;
@@ -1467,17 +1587,17 @@ histbackword(void)
static void
hwget(char **startptr)
{
- int pos = hwgetword > -1 ? hwgetword : chwordpos - 2;
+ int pos = chwordpos - 2;
#ifdef DEBUG
/* debugging only */
- if (hwgetword == -1 && !chwordpos) {
+ if (!chwordpos) {
/* no words available */
DPUTS(1, "BUG: hwget() called with no words");
*startptr = "";
return;
- }
- else if (hwgetword == -1 && chwordpos%2) {
+ }
+ else if (chwordpos%2) {
DPUTS(1, "BUG: hwget() called in middle of word");
*startptr = "";
return;
@@ -1499,9 +1619,9 @@ hwrep(char *rep)
if (!strcmp(rep, start))
return;
-
+
hptr = start;
- chwordpos = (hwgetword > -1) ? hwgetword : chwordpos - 2;
+ chwordpos = chwordpos - 2;
hwbegin(0);
qbang = 1;
while (*rep)
@@ -1529,7 +1649,6 @@ hgetline(void)
/* reset line */
hptr = chline;
chwordpos = 0;
- hwgetword = -1;
return ret;
}
@@ -1699,11 +1818,12 @@ int
chrealpath(char **junkptr)
{
char *str;
-#ifdef HAVE_CANONICALIZE_FILE_NAME
+#ifdef HAVE_REALPATH
+# ifdef REALPATH_ACCEPTS_NULL
char *lastpos, *nonreal, *real;
-#else
-# ifdef HAVE_REALPATH
- char *lastpos, *nonreal, real[PATH_MAX];
+# else
+ char *lastpos, *nonreal, pathbuf[PATH_MAX];
+ char *real = pathbuf;
# endif
#endif
@@ -1714,7 +1834,7 @@ chrealpath(char **junkptr)
if (!chabspath(junkptr))
return 0;
-#if !defined(HAVE_REALPATH) && !defined(HAVE_CANONICALIZE_FILE_NAME)
+#ifndef HAVE_REALPATH
return 1;
#else
/*
@@ -1730,29 +1850,21 @@ chrealpath(char **junkptr)
nonreal = lastpos + 1;
while (!
-#ifdef HAVE_CANONICALIZE_FILE_NAME
- /*
- * This is a GNU extension to realpath(); it's the
- * same as calling realpath() with a NULL second argument
- * which uses malloc() to get memory. The alternative
- * interface is easier to test for, however.
- */
- (real = canonicalize_file_name(*junkptr))
+#ifdef REALPATH_ACCEPTS_NULL
+ /* realpath() with a NULL second argument uses malloc() to get
+ * memory so we don't need to worry about overflowing PATH_MAX */
+ (real = realpath(*junkptr, NULL))
#else
realpath(*junkptr, real)
#endif
) {
- if (errno == EINVAL || errno == ELOOP ||
- errno == ENAMETOOLONG || errno == ENOMEM)
+ if (errno == EINVAL || errno == ENOMEM)
return 0;
-#ifdef HAVE_CANONICALIZE_FILE_NAME
- if (!real)
- return 0;
-#endif
-
if (nonreal == *junkptr) {
- *real = '\0';
+#ifndef REALPATH_ACCEPTS_NULL
+ real = NULL;
+#endif
break;
}
@@ -1768,11 +1880,15 @@ chrealpath(char **junkptr)
str++;
}
- *junkptr = metafy(str = bicat(real, nonreal), -1, META_HEAPDUP);
- zsfree(str);
-#ifdef HAVE_CANONICALIZE_FILE_NAME
- free(real);
+ if (real) {
+ *junkptr = metafy(str = bicat(real, nonreal), -1, META_HEAPDUP);
+ zsfree(str);
+#ifdef REALPATH_ACCEPTS_NULL
+ free(real);
#endif
+ } else {
+ *junkptr = metafy(nonreal, lastpos - nonreal + 1, META_HEAPDUP);
+ }
#endif
return 1;
@@ -2139,10 +2255,10 @@ getargs(Histent elist, int arg1, int arg2)
}
/**/
-int
+static int
quote(char **tr)
{
- char *ptr, *rptr, **str = (char **)tr;
+ char *ptr, *rptr, **str = tr;
int len = 3;
int inquotes = 0;
@@ -2176,7 +2292,6 @@ quote(char **tr)
*rptr++ = *ptr;
*rptr++ = '\'';
*rptr++ = 0;
- str[1] = NULL;
return 0;
}
@@ -2184,7 +2299,7 @@ quote(char **tr)
static int
quotebreak(char **tr)
{
- char *ptr, *rptr, **str = (char **)tr;
+ char *ptr, *rptr, **str = tr;
int len = 3;
for (ptr = *str; *ptr; ptr++, len++)
@@ -2378,12 +2493,40 @@ readhistfile(char *fn, int err, int readflags)
|| (hist_ignore_all_dups && newflags & hist_skip_flags))
newflags |= HIST_MAKEUNIQUE;
while (fpos = ftell(in), (l = readhistline(0, &buf, &bufsiz, in))) {
- char *pt = buf;
+ char *pt;
+ int remeta = 0;
if (l < 0) {
zerr("corrupt history file %s", fn);
break;
}
+
+ /*
+ * Handle the special case that we're reading from an
+ * old shell with fewer meta characters, so we need to
+ * metafy some more. (It's not clear why the history
+ * file is metafied at all; some would say this is plain
+ * stupid. But we're stuck with it now without some
+ * hairy workarounds for compatibility).
+ *
+ * This is rare so doesn't need to be that efficient; just
+ * allocate space off the heap.
+ */
+ for (pt = buf; *pt; pt++) {
+ if (*pt == Meta && pt[1])
+ pt++;
+ else if (imeta(*pt)) {
+ remeta = 1;
+ break;
+ }
+ }
+ if (remeta) {
+ unmetafy(buf, &remeta);
+ pt = metafy(buf, remeta, META_USEHEAP);
+ } else {
+ pt = buf;
+ }
+
if (*pt == ':') {
pt++;
stim = zstrtol(pt, NULL, 0);
@@ -2443,8 +2586,6 @@ readhistfile(char *fn, int err, int readflags)
start = pt;
uselex = isset(HISTLEXWORDS) && !(readflags & HFILE_FAST);
histsplitwords(pt, &words, &nwords, &nwordpos, uselex);
- if (uselex)
- freeheap();
he->nwords = nwordpos/2;
if (he->nwords) {
@@ -2457,6 +2598,14 @@ readhistfile(char *fn, int err, int readflags)
freehistnode(&he->node);
curhist--;
}
+ /*
+ * Do this last out of paranoia in case use of
+ * heap is disguised...
+ */
+ if (uselex || remeta)
+ freeheap();
+ if (errflag & ERRFLAG_INT)
+ break;
}
if (start && readflags & HFILE_USE_OPTIONS) {
zsfree(lasthist.text);
@@ -2488,7 +2637,8 @@ static int
flockhistfile(char *fn, int keep_trying)
{
struct flock lck;
- int ctr = keep_trying ? 9 : 0;
+ long sleep_us = 0x10000; /* about 67 ms */
+ time_t end_time;
if (flock_fd >= 0)
return 0; /* already locked */
@@ -2501,13 +2651,22 @@ flockhistfile(char *fn, int keep_trying)
lck.l_start = 0;
lck.l_len = 0; /* lock the whole file */
+ /*
+ * Timeout is ten seconds.
+ */
+ end_time = time(NULL) + (time_t)10;
while (fcntl(flock_fd, F_SETLKW, &lck) == -1) {
- if (--ctr < 0) {
+ if (!keep_trying || time(NULL) >= end_time ||
+ /*
+ * Randomise wait to minimise clashes with shells exiting at
+ * the same time.
+ */
+ !zsleep_random(sleep_us, end_time)) {
close(flock_fd);
flock_fd = -1;
return 1;
}
- sleep(1);
+ sleep_us <<= 1;
}
return 0;
@@ -2743,7 +2902,7 @@ savehistfile(char *fn, int err, int writeflags)
static int lockhistct;
static int
-checklocktime(char *lockfile, time_t then)
+checklocktime(char *lockfile, long *sleep_usp, time_t then)
{
time_t now = time(NULL);
@@ -2753,9 +2912,19 @@ checklocktime(char *lockfile, time_t then)
return -1;
}
- if (now - then < 10)
- sleep(1);
- else
+ if (now - then < 10) {
+ /*
+ * To give the effect of a gradually increasing backoff,
+ * we'll sleep a period based on the time we've spent so far.
+ */
+ DPUTS(now < then, "time flowing backwards through history");
+ /*
+ * Randomise to minimise clashes with shells exiting at the same
+ * time.
+ */
+ (void)zsleep_random(*sleep_usp, then + 10);
+ *sleep_usp <<= 1;
+ } else
unlink(lockfile);
return 0;
@@ -2772,6 +2941,7 @@ lockhistfile(char *fn, int keep_trying)
{
int ct = lockhistct;
int ret = 0;
+ long sleep_us = 0x10000; /* about 67 ms */
if (!fn && !(fn = getsparam("HISTFILE")))
return 1;
@@ -2799,7 +2969,8 @@ lockhistfile(char *fn, int keep_trying)
#ifdef HAVE_LINK
# ifdef HAVE_SYMLINK
sprintf(pidbuf, "/pid-%ld/host-", (long)mypid);
- lnk = bicat(pidbuf, getsparam("HOST"));
+ lnk = getsparam("HOST");
+ lnk = bicat(pidbuf, lnk ? lnk : "");
/* We'll abuse fd as our success flag. */
while ((fd = symlink(lnk, lockfile)) < 0) {
if (errno != EEXIST) {
@@ -2814,7 +2985,7 @@ lockhistfile(char *fn, int keep_trying)
continue;
break;
}
- if (checklocktime(lockfile, sb.st_mtime) < 0) {
+ if (checklocktime(lockfile, &sleep_us, sb.st_mtime) < 0) {
ret = 1;
break;
}
@@ -2842,7 +3013,7 @@ lockhistfile(char *fn, int keep_trying)
continue;
ret = 2;
} else {
- if (checklocktime(lockfile, sb.st_mtime) < 0) {
+ if (checklocktime(lockfile, &sleep_us, sb.st_mtime) < 0) {
ret = 1;
break;
}
@@ -2870,7 +3041,7 @@ lockhistfile(char *fn, int keep_trying)
ret = 2;
break;
}
- if (checklocktime(lockfile, sb.st_mtime) < 0) {
+ if (checklocktime(lockfile, &sleep_us, sb.st_mtime) < 0) {
ret = 1;
break;
}
@@ -2988,7 +3159,7 @@ bufferwords(LinkList list, char *buf, int *index, int flags)
opts[RCQUOTES] = 0;
addedx = 0;
noerrs = 1;
- lexsave();
+ zcontext_save();
lexflags = flags | LEXFLAGS_ACTIVE;
/*
* Are we handling comments?
@@ -3108,7 +3279,7 @@ bufferwords(LinkList list, char *buf, int *index, int flags)
* double quotes. Whitespace in the middle is
* similarly retained, so just add the parentheses back.
*/
- p = tricat("((", tokstr, "))");
+ p = zhtricat("((", tokstr, "))");
}
break;
@@ -3162,7 +3333,7 @@ bufferwords(LinkList list, char *buf, int *index, int flags)
got = 1;
cur = num - 1;
}
- } while (tok != ENDINPUT && tok != LEXERR);
+ } while (tok != ENDINPUT && tok != LEXERR && !(errflag & ERRFLAG_INT));
if (buf && tok == LEXERR && tokstr && *tokstr) {
int plen;
untokenize((p = dupstring(tokstr)));
@@ -3182,10 +3353,10 @@ bufferwords(LinkList list, char *buf, int *index, int flags)
noaliases = ona;
strinend();
inpop();
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
nocomments = onc;
noerrs = ne;
- lexrestore();
+ zcontext_restore();
zlemetacs = ocs;
zlemetall = oll;
wb = owb;
@@ -3233,11 +3404,14 @@ histsplitwords(char *lineptr, short **wordsp, int *nwordsp, int *nwordposp,
char *start = lineptr;
if (uselex) {
- LinkList wordlist = bufferwords(NULL, lineptr, NULL,
- LEXFLAGS_COMMENTS_KEEP);
+ LinkList wordlist;
LinkNode wordnode;
int nwords_max;
+ wordlist = bufferwords(NULL, lineptr, NULL,
+ LEXFLAGS_COMMENTS_KEEP);
+ if (errflag)
+ return;
nwords_max = 2 * countlinknodes(wordlist);
if (nwords_max > nwords) {
*nwordsp = nwords = nwords_max;
@@ -3329,7 +3503,8 @@ histsplitwords(char *lineptr, short **wordsp, int *nwordsp, int *nwordposp,
if (*lptr == *wptr ||
(*lptr == '!' && *wptr == '|')) {
lptr++;
- wptr++;
+ if (!*++wptr)
+ break;
} else if (lptr[0] == '\\' &&
lptr[1] == '\n') {
/*
diff --git a/Src/init.c b/Src/init.c
index 6d005dce7..102276a64 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -107,7 +107,7 @@ loop(int toplevel, int justonce)
pushheap();
if (!toplevel)
- lexsave();
+ zcontext_save();
for (;;) {
freeheap();
if (stophist == 3) /* re-entry via preprompt() */
@@ -118,18 +118,32 @@ loop(int toplevel, int justonce)
if (interact && toplevel) {
int hstop = stophist;
stophist = 3;
+ /*
+ * Reset all errors including the interrupt error status
+ * immediately, so preprompt runs regardless of what
+ * just happened. We'll reset again below as a
+ * precaution to ensure we get back to the command line
+ * no matter what.
+ */
+ errflag = 0;
preprompt();
if (stophist != 3)
hbegin(1);
else
stophist = hstop;
+ /*
+ * Reset all errors, including user interupts.
+ * This is what allows ^C in an interactive shell
+ * to return us to the command line.
+ */
errflag = 0;
}
}
use_exit_printed = 0;
intr(); /* interrupts on */
lexinit(); /* initialize lexical state */
- if (!(prog = parse_event())) { /* if we couldn't parse a list */
+ if (!(prog = parse_event(ENDINPUT))) {
+ /* if we couldn't parse a list */
hend(NULL);
if ((tok == ENDINPUT && !errflag) ||
(tok == LEXERR && (!isset(SHINSTDIN) || !toplevel)) ||
@@ -178,7 +192,15 @@ loop(int toplevel, int justonce)
/* The only permanent storage is from getpermtext() */
zsfree(cmdstr);
- errflag = 0;
+ /*
+ * Note this does *not* remove a user interrupt error
+ * condition, even though we're at the top level loop:
+ * that would be inconsistent with the case where
+ * we didn't execute a preexec function. This is
+ * an implementation detail that an interrupting user
+ * does't care about.
+ */
+ errflag &= ~ERRFLAG_ERROR;
}
if (stopmsg) /* unset 'you have stopped jobs' flag */
stopmsg--;
@@ -205,7 +227,7 @@ loop(int toplevel, int justonce)
}
err = errflag;
if (!toplevel)
- lexrestore();
+ zcontext_restore();
popheap();
if (err)
@@ -243,12 +265,24 @@ parseargs(char **argv, char **runscript)
*/
opts[MONITOR] = 2; /* may be unset in init_io() */
opts[HASHDIRS] = 2; /* same relationship to INTERACTIVE */
+ opts[USEZLE] = 1; /* see below, related to SHINSTDIN */
opts[SHINSTDIN] = 0;
opts[SINGLECOMMAND] = 0;
if (parseopts(NULL, &argv, opts, &cmd, NULL))
exit(1);
+ /*
+ * USEZLE remains set if the shell has access to a terminal and
+ * is not reading from some other source as indicated by SHINSTDIN.
+ * SHINSTDIN becomes set below if there is no command argument,
+ * but it is the explicit setting (or not) that matters to USEZLE.
+ * USEZLE may also become unset in init_io() if the shell is not
+ * interactive or the terminal cannot be re-opened read/write.
+ */
+ if (opts[SHINSTDIN])
+ opts[USEZLE] = (opts[USEZLE] && isatty(0));
+
paramlist = znewlinklist();
if (*argv) {
if (unset(SHINSTDIN)) {
@@ -603,7 +637,7 @@ init_shout(void)
if (SHTTY == -1)
{
- /* Since we're interative, it's nice to have somewhere to write. */
+ /* Since we're interactive, it's nice to have somewhere to write. */
shout = stderr;
return;
}
@@ -616,7 +650,8 @@ init_shout(void)
/* Associate terminal file descriptor with a FILE pointer */
shout = fdopen(SHTTY, "w");
#ifdef _IOFBF
- setvbuf(shout, shoutbuf, _IOFBF, BUFSIZ);
+ if (shout)
+ setvbuf(shout, shoutbuf, _IOFBF, BUFSIZ);
#endif
gettyinfo(&shttyinfo); /* get tty state */
@@ -676,7 +711,7 @@ init_term(void)
{
if (isset(INTERACTIVE))
zerr("can't find terminal definition for %s", term);
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
termflags |= TERM_BAD;
return 0;
} else {
@@ -1036,7 +1071,6 @@ setupvals(void)
bufstack = znewlinklist();
hsubl = hsubr = NULL;
lastpid = 0;
- lastpid_status = -1L;
get_usage();
@@ -1163,10 +1197,13 @@ run_init_scripts(void)
if (islogin)
sourcehome(".profile");
noerrs = 2;
- if (s && !parsestr(s)) {
- singsub(&s);
- noerrs = 0;
- source(s);
+ if (s) {
+ s = dupstring(s);
+ if (!parsestr(&s)) {
+ singsub(&s);
+ noerrs = 0;
+ source(s);
+ }
}
noerrs = 0;
} else
@@ -1324,7 +1361,7 @@ source(char *s)
if (prog) {
pushheap();
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
execode(prog, 1, 0, "filecode");
popheap();
if (errflag)
@@ -1367,12 +1404,12 @@ source(char *s)
lineno = oldlineno; /* our current lineno */
loops = oloops; /* the # of nested loops we are in */
dosetopt(SHINSTDIN, oldshst, 1, opts); /* SHINSTDIN option */
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
if (!exit_pending)
retflag = 0;
scriptname = old_scriptname;
scriptfilename = old_scriptfilename;
- free(cmdstack);
+ zfree(cmdstack, CMDSTACKSZ);
cmdstack = ocs;
cmdsp = ocsp;
@@ -1608,8 +1645,7 @@ zsh_main(UNUSED(int argc), char **argv)
emulate(zsh_name, 1, &emulation, opts); /* initialises most options */
opts[LOGINSHELL] = (**argv == '-');
opts[PRIVILEGED] = (getuid() != geteuid() || getgid() != getegid());
- opts[USEZLE] = 1; /* may be unset in init_io() */
- /* sets INTERACTIVE, SHINSTDIN and SINGLECOMMAND */
+ /* sets ZLE, INTERACTIVE, SHINSTDIN and SINGLECOMMAND */
parseargs(argv, &runscript);
SHTTY = -1;
diff --git a/Src/input.c b/Src/input.c
index 4ac7e6ec8..4a5bf89c6 100644
--- a/Src/input.c
+++ b/Src/input.c
@@ -56,8 +56,9 @@
* inpop(), which effectively flushes any unread input as well as restoring
* the previous input state.
*
- * The internal flag INP_ALCONT shows that the stack element was pushed
- * by an alias expansion; it should not be needed elsewhere.
+ * The internal flags INP_ALCONT and INP_HISTCONT show that the stack
+ * element was pushed by an alias or history expansion; they should not
+ * be needed elsewhere.
*
* The global variable inalmore is set to indicate aliases should
* continue to be expanded because the last alias expansion ended
@@ -179,12 +180,12 @@ shingetline(void)
/* Get the next character from the input.
* Will call inputline() to get a new line where necessary.
*/
-
+
/**/
int
ingetc(void)
{
- int lastc;
+ int lastc = ' ';
if (lexstop)
return ' ';
@@ -196,7 +197,7 @@ ingetc(void)
continue;
if (((inbufflags & INP_LINENO) || !strin) && lastc == '\n')
lineno++;
- return lastc;
+ break;
}
/*
@@ -208,7 +209,7 @@ ingetc(void)
*/
if (!inbufct && (strin || errflag)) {
lexstop = 1;
- return ' ';
+ break;
}
/* If the next element down the input stack is a continuation of
* this, use it.
@@ -219,8 +220,10 @@ ingetc(void)
}
/* As a last resort, get some more input */
if (inputline())
- return ' ';
+ break;
}
+ zshlex_raw_add(lastc);
+ return lastc;
}
/* Read a line from the current command stream and store it as input */
@@ -291,7 +294,8 @@ inputline(void)
}
if (errflag) {
free(ingetcline);
- return lexstop = errflag = 1;
+ errflag |= ERRFLAG_ERROR;
+ return lexstop = 1;
}
if (isset(VERBOSE)) {
/* Output the whole line read so far. */
@@ -327,8 +331,38 @@ inputline(void)
}
}
isfirstch = 1;
- /* Put this into the input channel. */
- inputsetline(ingetcline, INP_FREE);
+ if ((inbufflags & INP_APPEND) && inbuf) {
+ /*
+ * We need new input but need to be able to back up
+ * over the old input, so append this line.
+ * Pushing the line onto the stack doesn't have the right
+ * effect.
+ *
+ * This is quite a simple and inefficient fix, but currently
+ * we only need it when backing up over a multi-line $((...
+ * that turned out to be a command substitution rather than
+ * a math substitution, which is a very special case.
+ * So it's not worth rewriting.
+ */
+ char *oinbuf = inbuf;
+ int newlen = strlen(ingetcline);
+ int oldlen = (int)(inbufptr - inbuf) + inbufleft;
+ if (inbufflags & INP_FREE) {
+ inbuf = realloc(inbuf, oldlen + newlen + 1);
+ } else {
+ inbuf = zalloc(oldlen + newlen + 1);
+ memcpy(inbuf, oinbuf, oldlen);
+ }
+ inbufptr += inbuf - oinbuf;
+ strcpy(inbuf + oldlen, ingetcline);
+ free(ingetcline);
+ inbufleft += newlen;
+ inbufct += newlen;
+ inbufflags |= INP_FREE;
+ } else {
+ /* Put this into the input channel. */
+ inputsetline(ingetcline, INP_FREE);
+ }
return 0;
}
@@ -390,12 +424,14 @@ inungetc(int c)
if (((inbufflags & INP_LINENO) || !strin) && c == '\n')
lineno--;
}
-#ifdef DEBUG
else if (!(inbufflags & INP_CONT)) {
+#ifdef DEBUG
/* Just for debugging */
fprintf(stderr, "Attempt to inungetc() at start of input.\n");
- }
#endif
+ zerr("Garbled input at %c (binary file as commands?)", c);
+ return;
+ }
else {
/*
* The character is being backed up from a previous input stack
@@ -411,7 +447,8 @@ inungetc(int c)
* we may need to restore an alias popped from the stack.
* Note this may be a dummy (history expansion) entry.
*/
- if (inbufptr == inbufpush && inbufflags & INP_ALCONT) {
+ if (inbufptr == inbufpush &&
+ (inbufflags & (INP_ALCONT|INP_HISTCONT))) {
/*
* Go back up the stack over all entries which were alias
* expansions and were pushed with nothing remaining to read.
@@ -420,11 +457,16 @@ inungetc(int c)
if (instacktop->alias)
instacktop->alias->inuse = 1;
instacktop++;
- } while ((instacktop->flags & INP_ALCONT) && !instacktop->bufleft);
- inbufflags = INP_CONT|INP_ALIAS;
+ } while ((instacktop->flags & (INP_ALCONT|INP_HISTCONT))
+ && !instacktop->bufleft);
+ if (inbufflags & INP_HISTCONT)
+ inbufflags = INP_CONT|INP_ALIAS|INP_HIST;
+ else
+ inbufflags = INP_CONT|INP_ALIAS;
inbufleft = 0;
inbuf = inbufptr = "";
}
+ zshlex_raw_back();
}
}
@@ -486,7 +528,7 @@ inpush(char *str, int flags, Alias inalias)
instacktop->bufptr = inbufptr;
instacktop->bufleft = inbufleft;
instacktop->bufct = inbufct;
- inbufflags &= ~INP_ALCONT;
+ inbufflags &= ~(INP_ALCONT|INP_HISTCONT);
if (flags & (INP_ALIAS|INP_HIST)) {
/*
* Text is expansion for history or alias, so continue
@@ -495,7 +537,10 @@ inpush(char *str, int flags, Alias inalias)
* and mark alias as in use.
*/
flags |= INP_CONT|INP_ALIAS;
- instacktop->flags = inbufflags | INP_ALCONT;
+ if (flags & INP_HIST)
+ instacktop->flags = inbufflags | INP_HISTCONT;
+ else
+ instacktop->flags = inbufflags | INP_ALCONT;
if ((instacktop->alias = inalias))
inalias->inuse = 1;
} else {
@@ -533,6 +578,24 @@ inpush(char *str, int flags, Alias inalias)
static void
inpoptop(void)
{
+ if (!lexstop) {
+ inbufflags &= ~(INP_ALCONT|INP_HISTCONT);
+ while (inbufptr > inbuf) {
+ inbufptr--;
+ inbufct++;
+ inbufleft++;
+ /*
+ * As elsewhere in input and history mechanisms:
+ * unwinding aliases and unwinding history have different
+ * implications as aliases are after the lexer while
+ * history is before, but they're both pushed onto
+ * the input stack.
+ */
+ if ((inbufflags & (INP_ALIAS|INP_HIST)) == INP_ALIAS)
+ zshlex_raw_back();
+ }
+ }
+
if (inbuf && (inbufflags & INP_FREE))
free(inbuf);
@@ -544,7 +607,7 @@ inpoptop(void)
inbufct = instacktop->bufct;
inbufflags = instacktop->flags;
- if (!(inbufflags & INP_ALCONT))
+ if (!(inbufflags & (INP_ALCONT|INP_HISTCONT)))
return;
if (instacktop->alias) {
diff --git a/Src/jobs.c b/Src/jobs.c
index bd95afb7a..948f61b01 100644
--- a/Src/jobs.c
+++ b/Src/jobs.c
@@ -104,15 +104,6 @@ int prev_errflag, prev_breaks, errbrk_saved;
/**/
int numpipestats, pipestats[MAX_PIPESTATS];
-/*
- * The status associated with the process lastpid.
- * -1 if not set and no associated lastpid
- * -2 if lastpid is set and status isn't yet
- * else the value returned by wait().
- */
-/**/
-long lastpid_status;
-
/* Diff two timevals for elapsed-time computations */
/**/
@@ -430,8 +421,10 @@ update_job(Job jn)
for (pn = jn->procs; pn; pn = pn->next) {
#ifdef WIFCONTINUED
- if (WIFCONTINUED(pn->status))
+ if (WIFCONTINUED(pn->status)) {
+ jn->stat &= ~STAT_STOPPED;
pn->status = SP_RUNNING;
+ }
#endif
if (pn->status == SP_RUNNING) /* some processes in this job are running */
return; /* so no need to update job table entry */
@@ -518,7 +511,7 @@ update_job(Job jn)
prev_errflag = errflag;
}
breaks = loops;
- errflag = 1;
+ errflag |= ERRFLAG_INT;
inerrflush();
}
} else {
@@ -535,7 +528,7 @@ update_job(Job jn)
prev_errflag = errflag;
}
breaks = loops;
- errflag = 1;
+ errflag |= ERRFLAG_INT;
inerrflush();
}
if (somestopped && jn->stat & STAT_SUPERJOB)
@@ -590,7 +583,7 @@ update_job(Job jn)
breaks = loops;
} else {
breaks = loops;
- errflag = 1;
+ errflag |= ERRFLAG_INT;
}
check_cursh_sig(sig);
}
@@ -713,6 +706,8 @@ printtime(struct timeval *real, child_times_t *ti, char *desc)
queue_signals();
if (!(s = getsparam("TIMEFMT")))
s = DEFAULT_TIMEFMT;
+ else
+ s = unmetafy(s, NULL);
for (; *s; s++)
if (*s == '%')
@@ -1309,14 +1304,6 @@ addproc(pid_t pid, char *text, int aux, struct timeval *bgtime)
{
Process pn, *pnlist;
- if (pid == lastpid && lastpid_status != -2L) {
- /*
- * The status for the previous lastpid is invalid.
- * Presumably process numbers have wrapped.
- */
- lastpid_status = -1L;
- }
-
DPUTS(thisjob == -1, "No valid job in addproc.");
pn = (Process) zshcalloc(sizeof *pn);
pn->pid = pid;
@@ -1459,12 +1446,19 @@ zwaitjob(int job, int wait_cmd)
restore_queue_signals(q);
return 128 + last_signal;
}
- /* Commenting this out makes ^C-ing a job started by a function
- stop the whole function again. But I guess it will stop
- something else from working properly, we have to find out
- what this might be. --oberon
+ /* Commenting this out makes ^C-ing a job started by a function
+ stop the whole function again. But I guess it will stop
+ something else from working properly, we have to find out
+ what this might be. --oberon
+
+ When attempting to separate errors and interrupts, we
+ assumed because of the previous comment it would be OK
+ to remove ERRFLAG_ERROR and leave ERRFLAG_INT set, since
+ that's the one related to ^C. But that doesn't work.
+ There's something more here we don't understand. --pws
+
+ errflag = 0; */
- errflag = 0; */
if (subsh) {
killjb(jn, SIGCONT);
jn->stat &= ~STAT_STOPPED;
@@ -1828,7 +1822,9 @@ static int hackspace;
void
init_jobs(char **argv, char **envp)
{
+#ifndef HAVE_SETPROCTITLE
char *p, *q;
+#endif
size_t init_bytes = MAXJOBS_ALLOC*sizeof(struct job);
/*
@@ -1940,6 +1936,122 @@ maybeshrinkjobtab(void)
unqueue_signals();
}
+/*
+ * Definitions for the background process stuff recorded below.
+ * This would be more efficient as a hash, but
+ * - that's quite heavyweight for something not needed very often
+ * - we need some kind of ordering as POSIX allows us to limit
+ * the size of the list to the value of _SC_CHILD_MAX and clearly
+ * we want to clear the oldest first
+ * - cases with a long list of background jobs where the user doesn't
+ * wait for a large number, and then does wait for one (the only
+ * inefficient case) are rare
+ * - in the context of waiting for an external process, looping
+ * over a list isn't so very inefficient.
+ * Enough excuses already.
+ */
+
+/* Data in the link list, a key (process ID) / value (exit status) pair. */
+struct bgstatus {
+ pid_t pid;
+ int status;
+};
+typedef struct bgstatus *Bgstatus;
+/* The list of those entries */
+LinkList bgstatus_list;
+/* Count of entries. Reaches value of _SC_CHILD_MAX and stops. */
+long bgstatus_count;
+
+/*
+ * Remove and free a bgstatus entry.
+ */
+static void rembgstatus(LinkNode node)
+{
+ zfree(remnode(bgstatus_list, node), sizeof(struct bgstatus));
+ bgstatus_count--;
+}
+
+/*
+ * Record the status of a background process that exited so we
+ * can execute the builtin wait for it.
+ *
+ * We can't execute the wait builtin for something that exited in the
+ * foreground as it's not visible to the user, so don't bother recording.
+ */
+
+/**/
+void
+addbgstatus(pid_t pid, int status)
+{
+ static long child_max;
+ Bgstatus bgstatus_entry;
+
+ if (!child_max) {
+#ifdef _SC_CHILD_MAX
+ child_max = sysconf(_SC_CHILD_MAX);
+ if (!child_max) /* paranoia */
+#endif
+ {
+ /* Be inventive */
+ child_max = 1024L;
+ }
+ }
+
+ if (!bgstatus_list) {
+ bgstatus_list = znewlinklist();
+ /*
+ * We're not always robust about memory failures, but
+ * this is pretty deep in the shell basics to be failing owing
+ * to memory, and a failure to wait is reported loudly, so test
+ * and fail silently here.
+ */
+ if (!bgstatus_list)
+ return;
+ }
+ if (bgstatus_count == child_max) {
+ /* Overflow. List is in order, remove first */
+ rembgstatus(firstnode(bgstatus_list));
+ }
+ bgstatus_entry = (Bgstatus)zalloc(sizeof(*bgstatus_entry));
+ if (!bgstatus_entry) {
+ /* See note above */
+ return;
+ }
+ bgstatus_entry->pid = pid;
+ bgstatus_entry->status = status;
+ if (!zaddlinknode(bgstatus_list, bgstatus_entry)) {
+ zfree(bgstatus_entry, sizeof(*bgstatus_entry));
+ return;
+ }
+ bgstatus_count++;
+}
+
+/*
+ * See if pid has a recorded exit status.
+ * Note we make no guarantee that the PIDs haven't wrapped, so this
+ * may not be the right process.
+ *
+ * This is only used by wait, which must only work on each
+ * pid once, so we need to remove the entry if we find it.
+ */
+
+static int getbgstatus(pid_t pid)
+{
+ LinkNode node;
+ Bgstatus bgstatus_entry;
+
+ if (!bgstatus_list)
+ return -1;
+ for (node = firstnode(bgstatus_list); node; incnode(node)) {
+ bgstatus_entry = (Bgstatus)getdata(node);
+ if (bgstatus_entry->pid == pid) {
+ int status = bgstatus_entry->status;
+ rembgstatus(node);
+ return status;
+ }
+ }
+ return -1;
+}
/* bg, disown, fg, jobs, wait: most of the job control commands are *
* here. They all take the same type of argument. Exception: wait can *
@@ -2085,10 +2197,7 @@ bin_fg(char *name, char **argv, Options ops, int func)
}
if (retval == 0)
retval = lastval2;
- } else if (isset(POSIXJOBS) &&
- pid == lastpid && lastpid_status >= 0L) {
- retval = (int)lastpid_status;
- } else {
+ } else if ((retval = getbgstatus(pid)) < 0) {
zwarnnam(name, "pid %d is not a child of this shell", pid);
/* presumably lastval2 doesn't tell us a heck of a lot? */
retval = 1;
@@ -2611,7 +2720,7 @@ findjobnam(const char *s)
for (jobnum = maxjob; jobnum >= 0; jobnum--)
if (!(jobtab[jobnum].stat & (STAT_SUBJOB | STAT_NOPRINT)) &&
jobtab[jobnum].stat && jobtab[jobnum].procs && jobnum != thisjob &&
- jobtab[jobnum].procs->text && strpfx(s, jobtab[jobnum].procs->text))
+ jobtab[jobnum].procs->text[0] && strpfx(s, jobtab[jobnum].procs->text))
return jobnum;
return -1;
}
@@ -2627,7 +2736,7 @@ acquire_pgrp(void)
long ttpgrp;
sigset_t blockset, oldset;
- if ((mypgrp = GETPGRP()) > 0) {
+ if ((mypgrp = GETPGRP()) >= 0) {
long lastpgrp = mypgrp;
sigemptyset(&blockset);
sigaddset(&blockset, SIGTTIN);
@@ -2672,8 +2781,11 @@ void
release_pgrp(void)
{
if (origpgrp != mypgrp) {
- attachtty(origpgrp);
- setpgrp(0, origpgrp);
+ /* in linux pid namespaces, origpgrp may never have been set */
+ if (origpgrp) {
+ attachtty(origpgrp);
+ setpgrp(0, origpgrp);
+ }
mypgrp = origpgrp;
}
}
diff --git a/Src/lex.c b/Src/lex.c
index 1a854f52b..c929bb9ba 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -35,7 +35,7 @@
/* tokens */
/**/
-mod_export char ztokens[] = "#$^*()$=|{}[]`<>>?~`,'\"\\\\";
+mod_export char ztokens[] = "#$^*(())$=|{}[]`<>>?~`,'\"\\\\";
/* parts of the current token */
@@ -90,6 +90,12 @@ int inalmore;
int nocorrect;
/*
+ * TBD: the following exported variables are part of the non-interface
+ * with ZLE for completion. They are poorly named and the whole
+ * scheme is incredibly brittle. One piece of robustness is applied:
+ * the variables are only set if LEXFLAGS_ZLE is set. Improvements
+ * should therefore concentrate on areas with this flag set.
+ *
* Cursor position and line length in zle when the line is
* metafied for access from the main shell.
*/
@@ -113,6 +119,16 @@ mod_export int addedx;
/**/
mod_export int wb, we;
+/**/
+mod_export int wordbeg;
+
+/**/
+mod_export int parbegin;
+
+/**/
+mod_export int parend;
+
+
/* 1 if aliases should not be expanded */
/**/
@@ -134,19 +150,20 @@ mod_export int noaliases;
/**/
mod_export int lexflags;
-/**/
-mod_export int wordbeg;
+/* don't recognize comments */
/**/
-mod_export int parbegin;
+mod_export int nocomments;
+
+/* add raw input characters while parsing command substitution */
/**/
-mod_export int parend;
+static int lex_add_raw;
-/* don't recognize comments */
+/* variables associated with the above */
-/**/
-mod_export int nocomments;
+static char *tokstr_raw;
+static struct lexbufstate lexbuf_raw;
/* text of punctuation tokens */
@@ -190,205 +207,58 @@ mod_export char *tokstrings[WHILE + 1] = {
/* lexical state */
static int dbparens;
-static int len = 0, bsiz = 256;
-static char *bptr;
-
-struct lexstack {
- struct lexstack *next;
-
- int incmdpos;
- int incond;
- int incasepat;
- int dbparens;
- int isfirstln;
- int isfirstch;
- int histactive;
- int histdone;
- int lexflags;
- int stophist;
- int hlinesz;
- char *hline;
- char *hptr;
- enum lextok tok;
- int isnewlin;
- char *tokstr;
- char *zshlextext;
- char *bptr;
- int bsiz;
- int len;
- short *chwords;
- int chwordlen;
- int chwordpos;
- int hwgetword;
- int lexstop;
- struct heredocs *hdocs;
- int (*hgetc) _((void));
- void (*hungetc) _((int));
- void (*hwaddc) _((int));
- void (*hwbegin) _((int));
- void (*hwend) _((void));
- void (*addtoline) _((int));
-
- int eclen, ecused, ecnpats;
- Wordcode ecbuf;
- Eccstr ecstrs;
- int ecsoffs, ecssub, ecnfunc;
-
- unsigned char *cstack;
- int csp;
- zlong toklineno;
-};
-
-static struct lexstack *lstack = NULL;
+static struct lexbufstate lexbuf = { NULL, 256, 0 };
-/* save the lexical state */
-
-/* is this a hack or what? */
+/* save lexical context */
/**/
-mod_export void
-lexsave(void)
+void
+lex_context_save(struct lex_stack *ls, int toplevel)
{
- struct lexstack *ls;
+ (void)toplevel;
- ls = (struct lexstack *)malloc(sizeof(struct lexstack));
-
- ls->incmdpos = incmdpos;
- ls->incond = incond;
- ls->incasepat = incasepat;
ls->dbparens = dbparens;
ls->isfirstln = isfirstln;
ls->isfirstch = isfirstch;
- ls->histactive = histactive;
- ls->histdone = histdone;
ls->lexflags = lexflags;
- ls->stophist = stophist;
- stophist = 0;
- if (!lstack) {
- /* top level, make this version visible to ZLE */
- zle_chline = chline;
- /* ensure line stored is NULL-terminated */
- if (hptr)
- *hptr = '\0';
- }
- ls->hline = chline;
- chline = NULL;
- ls->hptr = hptr;
- hptr = NULL;
- ls->hlinesz = hlinesz;
- ls->cstack = cmdstack;
- ls->csp = cmdsp;
- cmdstack = (unsigned char *)zalloc(CMDSTACKSZ);
+
ls->tok = tok;
- ls->isnewlin = isnewlin;
ls->tokstr = tokstr;
ls->zshlextext = zshlextext;
- ls->bptr = bptr;
- tokstr = zshlextext = bptr = NULL;
- ls->bsiz = bsiz;
- bsiz = 256;
- ls->len = len;
- ls->chwords = chwords;
- ls->chwordlen = chwordlen;
- ls->chwordpos = chwordpos;
- ls->hwgetword = hwgetword;
+ ls->lexbuf = lexbuf;
+ ls->lex_add_raw = lex_add_raw;
+ ls->tokstr_raw = tokstr_raw;
+ ls->lexbuf_raw = lexbuf_raw;
ls->lexstop = lexstop;
- ls->hdocs = hdocs;
- ls->hgetc = hgetc;
- ls->hungetc = hungetc;
- ls->hwaddc = hwaddc;
- ls->hwbegin = hwbegin;
- ls->hwend = hwend;
- ls->addtoline = addtoline;
- ls->eclen = eclen;
- ls->ecused = ecused;
- ls->ecnpats = ecnpats;
- ls->ecbuf = ecbuf;
- ls->ecstrs = ecstrs;
- ls->ecsoffs = ecsoffs;
- ls->ecssub = ecssub;
- ls->ecnfunc = ecnfunc;
ls->toklineno = toklineno;
- cmdsp = 0;
- inredir = 0;
- hdocs = NULL;
- histactive = 0;
- ecbuf = NULL;
-
- ls->next = lstack;
- lstack = ls;
+
+ tokstr = zshlextext = lexbuf.ptr = NULL;
+ lexbuf.siz = 256;
+ tokstr_raw = lexbuf_raw.ptr = NULL;
+ lexbuf_raw.siz = lexbuf_raw.len = lex_add_raw = 0;
}
-/* restore lexical state */
+/* restore lexical context */
/**/
mod_export void
-lexrestore(void)
+lex_context_restore(const struct lex_stack *ls, int toplevel)
{
- struct lexstack *ln = lstack;
-
- DPUTS(!lstack, "BUG: lexrestore() without lexsave()");
-
- queue_signals();
- lstack = lstack->next;
-
- if (!lstack) {
- /* Back to top level: don't need special ZLE value */
- DPUTS(ln->hline != zle_chline, "BUG: Ouch, wrong chline for ZLE");
- zle_chline = NULL;
- }
-
- incmdpos = ln->incmdpos;
- incond = ln->incond;
- incasepat = ln->incasepat;
- dbparens = ln->dbparens;
- isfirstln = ln->isfirstln;
- isfirstch = ln->isfirstch;
- histactive = ln->histactive;
- histdone = ln->histdone;
- lexflags = ln->lexflags;
- stophist = ln->stophist;
- chline = ln->hline;
- hptr = ln->hptr;
- if (cmdstack)
- zfree(cmdstack, CMDSTACKSZ);
- cmdstack = ln->cstack;
- cmdsp = ln->csp;
- tok = ln->tok;
- isnewlin = ln->isnewlin;
- tokstr = ln->tokstr;
- zshlextext = ln->zshlextext;
- bptr = ln->bptr;
- bsiz = ln->bsiz;
- len = ln->len;
- chwords = ln->chwords;
- chwordlen = ln->chwordlen;
- chwordpos = ln->chwordpos;
- hwgetword = ln->hwgetword;
- lexstop = ln->lexstop;
- hdocs = ln->hdocs;
- hgetc = ln->hgetc;
- hungetc = ln->hungetc;
- hwaddc = ln->hwaddc;
- hwbegin = ln->hwbegin;
- hwend = ln->hwend;
- addtoline = ln->addtoline;
- if (ecbuf)
- zfree(ecbuf, eclen);
- eclen = ln->eclen;
- ecused = ln->ecused;
- ecnpats = ln->ecnpats;
- ecbuf = ln->ecbuf;
- ecstrs = ln->ecstrs;
- ecsoffs = ln->ecsoffs;
- ecssub = ln->ecssub;
- ecnfunc = ln->ecnfunc;
- hlinesz = ln->hlinesz;
- toklineno = ln->toklineno;
- errflag = 0;
- free(ln);
-
- unqueue_signals();
+ (void)toplevel;
+
+ dbparens = ls->dbparens;
+ isfirstln = ls->isfirstln;
+ isfirstch = ls->isfirstch;
+ lexflags = ls->lexflags;
+ tok = ls->tok;
+ tokstr = ls->tokstr;
+ zshlextext = ls->zshlextext;
+ lexbuf = ls->lexbuf;
+ lex_add_raw = ls->lex_add_raw;
+ tokstr_raw = ls->tokstr_raw;
+ lexbuf_raw = ls->lexbuf_raw;
+ lexstop = ls->lexstop;
+ toklineno = ls->toklineno;
}
/**/
@@ -563,9 +433,7 @@ initlextabs(void)
void
lexinit(void)
{
- incond = incasepat = nocorrect =
- infor = dbparens = lexstop = 0;
- incmdpos = 1;
+ nocorrect = dbparens = lexstop = 0;
tok = ENDINPUT;
}
@@ -575,17 +443,18 @@ lexinit(void)
void
add(int c)
{
- *bptr++ = c;
- if (bsiz == ++len) {
- int newbsiz = bsiz * 2;
+ *lexbuf.ptr++ = c;
+ if (lexbuf.siz == ++lexbuf.len) {
+ int newbsiz = lexbuf.siz * 2;
- if (newbsiz > inbufct && inbufct > bsiz)
+ if (newbsiz > inbufct && inbufct > lexbuf.siz)
newbsiz = inbufct;
- bptr = len + (tokstr = (char *)hrealloc(tokstr, bsiz, newbsiz));
+ tokstr = (char *)hrealloc(tokstr, lexbuf.siz, newbsiz);
+ lexbuf.ptr = tokstr + lexbuf.len;
/* len == bsiz, so bptr is at the start of newly allocated memory */
- memset(bptr, 0, newbsiz - bsiz);
- bsiz = newbsiz;
+ memset(lexbuf.ptr, 0, newbsiz - lexbuf.siz);
+ lexbuf.siz = newbsiz;
}
}
@@ -604,48 +473,61 @@ add(int c)
} \
}
+enum {
+ CMD_OR_MATH_CMD,
+ CMD_OR_MATH_MATH,
+ CMD_OR_MATH_ERR
+};
+
/*
- * Return 1 for math, 0 for a command, 2 for an error. If it couldn't be
+ * Return one of the above. If it couldn't be
* parsed as math, but there was no gross error, it's a command.
*/
static int
cmd_or_math(int cs_type)
{
- int oldlen = len;
+ int oldlen = lexbuf.len;
int c;
+ int oinflags = inbufflags;
cmdpush(cs_type);
+ inbufflags |= INP_APPEND;
c = dquote_parse(')', 0);
+ if (!(oinflags & INP_APPEND))
+ inbufflags &= ~INP_APPEND;
cmdpop();
- *bptr = '\0';
+ *lexbuf.ptr = '\0';
if (!c) {
/* Successfully parsed, see if it was math */
c = hgetc();
if (c == ')')
- return 1; /* yes */
+ return CMD_OR_MATH_MATH; /* yes */
hungetc(c);
lexstop = 0;
c = ')';
} else if (lexstop) {
/* we haven't got anything to unget */
- return 2;
+ return CMD_OR_MATH_ERR;
}
/* else unsuccessful: unget the whole thing */
hungetc(c);
lexstop = 0;
- while (len > oldlen) {
- len--;
- hungetc(itok(*--bptr) ? ztokens[*bptr - Pound] : *bptr);
+ while (lexbuf.len > oldlen && !(errflag & ERRFLAG_ERROR)) {
+ lexbuf.len--;
+ hungetc(itok(*--lexbuf.ptr) ?
+ ztokens[*lexbuf.ptr - Pound] : *lexbuf.ptr);
}
+ if (errflag)
+ return CMD_OR_MATH_ERR;
hungetc('(');
- return 0;
+ return errflag ? CMD_OR_MATH_ERR : CMD_OR_MATH_CMD;
}
/*
* Parse either a $(( ... )) or a $(...)
- * Return 0 on success, 1 on failure.
+ * Return the same as cmd_or_math().
*/
static int
cmd_or_math_sub(void)
@@ -653,21 +535,23 @@ cmd_or_math_sub(void)
int c = hgetc(), ret;
if (c == '(') {
+ int lexpos = (int)(lexbuf.ptr - tokstr);
add(Inpar);
add('(');
- if ((ret = cmd_or_math(CS_MATHSUBST)) == 1) {
+ if ((ret = cmd_or_math(CS_MATHSUBST)) == CMD_OR_MATH_MATH) {
+ tokstr[lexpos] = Inparmath;
add(')');
- return 0;
+ return CMD_OR_MATH_MATH;
}
- if (ret == 2)
- return 1;
- bptr -= 2;
- len -= 2;
+ if (ret == CMD_OR_MATH_ERR)
+ return CMD_OR_MATH_ERR;
+ lexbuf.ptr -= 2;
+ lexbuf.len -= 2;
} else {
hungetc(c);
lexstop = 0;
}
- return skipcomm();
+ return skipcomm() ? CMD_OR_MATH_ERR : CMD_OR_MATH_CMD;
}
/* Check whether we're looking at valid numeric globbing syntax *
@@ -722,17 +606,18 @@ gettok(void)
if (lexstop)
return (errflag) ? LEXERR : ENDINPUT;
isfirstln = 0;
- wordbeg = inbufct - (qbang && c == bangchar);
+ if ((lexflags & LEXFLAGS_ZLE))
+ wordbeg = inbufct - (qbang && c == bangchar);
hwbegin(-1-(qbang && c == bangchar));
/* word includes the last character read and possibly \ before ! */
if (dbparens) {
- len = 0;
- bptr = tokstr = (char *) hcalloc(bsiz = LEX_HEAP_SIZE);
+ lexbuf.len = 0;
+ lexbuf.ptr = tokstr = (char *) hcalloc(lexbuf.siz = LEX_HEAP_SIZE);
hungetc(c);
cmdpush(CS_MATH);
c = dquote_parse(infor ? ';' : ')', 0);
cmdpop();
- *bptr = '\0';
+ *lexbuf.ptr = '\0';
if (!c && infor) {
infor--;
return DINPAR;
@@ -780,8 +665,9 @@ gettok(void)
* newlines being inserted into the history. */
if (lexflags & LEXFLAGS_COMMENTS_KEEP) {
- len = 0;
- bptr = tokstr = (char *)hcalloc(bsiz = LEX_HEAP_SIZE);
+ lexbuf.len = 0;
+ lexbuf.ptr = tokstr =
+ (char *)hcalloc(lexbuf.siz = LEX_HEAP_SIZE);
add(c);
}
hwend();
@@ -796,7 +682,7 @@ gettok(void)
peek = LEXERR;
else {
if (lexflags & LEXFLAGS_COMMENTS_KEEP) {
- *bptr = '\0';
+ *lexbuf.ptr = '\0';
if (!lexstop)
hungetc(c);
peek = STRING;
@@ -882,13 +768,14 @@ gettok(void)
return DINPAR;
}
if (incmdpos || (isset(SHGLOB) && !isset(KSHGLOB))) {
- len = 0;
- bptr = tokstr = (char *) hcalloc(bsiz = LEX_HEAP_SIZE);
+ lexbuf.len = 0;
+ lexbuf.ptr = tokstr = (char *)
+ hcalloc(lexbuf.siz = LEX_HEAP_SIZE);
switch (cmd_or_math(CS_MATH)) {
- case 1:
+ case CMD_OR_MATH_MATH:
return DINPAR;
- case 0:
+ case CMD_OR_MATH_CMD:
/*
* Not math, so we don't return the contents
* as a string in this case.
@@ -1032,8 +919,8 @@ gettokstr(int c, int sub)
peek = STRING;
if (!sub) {
- len = 0;
- bptr = tokstr = (char *) hcalloc(bsiz = LEX_HEAP_SIZE);
+ lexbuf.len = 0;
+ lexbuf.ptr = tokstr = (char *) hcalloc(lexbuf.siz = LEX_HEAP_SIZE);
}
for (;;) {
int act;
@@ -1069,7 +956,7 @@ gettokstr(int c, int sub)
if (fdpar) {
/* this is a single word `( )', treat as INOUTPAR */
add(c);
- *bptr = '\0';
+ *lexbuf.ptr = '\0';
return INOUTPAR;
}
if ((sub || in_brace_param) && isset(SHGLOB))
@@ -1108,12 +995,19 @@ gettokstr(int c, int sub)
c = Outbrack;
} else if (e == '(') {
add(String);
- c = cmd_or_math_sub();
- if (c) {
+ switch (cmd_or_math_sub()) {
+ case CMD_OR_MATH_CMD:
+ c = Outpar;
+ break;
+
+ case CMD_OR_MATH_MATH:
+ c = Outparmath;
+ break;
+
+ default:
peek = LEXERR;
goto brk;
}
- c = Outpar;
} else {
if (e == '{') {
add(c);
@@ -1144,9 +1038,9 @@ gettokstr(int c, int sub)
if (isset(SHGLOB)) {
if (sub || in_brace_param)
break;
- if (incasepat && !len)
+ if (incasepat && !lexbuf.len)
return INPAR;
- if (!isset(KSHGLOB) && len)
+ if (!isset(KSHGLOB) && lexbuf.len)
goto brk;
}
if (!in_brace_param) {
@@ -1203,9 +1097,9 @@ gettokstr(int c, int sub)
if (isset(IGNOREBRACES) || sub)
c = '{';
else {
- if (!len && incmdpos) {
+ if (!lexbuf.len && incmdpos) {
add('{');
- *bptr = '\0';
+ *lexbuf.ptr = '\0';
return STRING;
}
if (in_brace_param) {
@@ -1291,23 +1185,23 @@ gettokstr(int c, int sub)
incmdpos && !bct && !brct) {
char *t = tokstr;
if (idigit(*t))
- while (++t < bptr && idigit(*t));
+ while (++t < lexbuf.ptr && idigit(*t));
else {
- int sav = *bptr;
- *bptr = '\0';
+ int sav = *lexbuf.ptr;
+ *lexbuf.ptr = '\0';
t = itype_end(t, IIDENT, 0);
- if (t < bptr) {
+ if (t < lexbuf.ptr) {
skipparens(Inbrack, Outbrack, &t);
} else {
- *bptr = sav;
+ *lexbuf.ptr = sav;
}
}
if (*t == '+')
t++;
- if (t == bptr) {
+ if (t == lexbuf.ptr) {
e = hgetc();
if (e == '(' && incmdpos) {
- *bptr = '\0';
+ *lexbuf.ptr = '\0';
return ENVARRAY;
}
hungetc(e);
@@ -1326,13 +1220,25 @@ gettokstr(int c, int sub)
c = hgetc();
if (!lexstop)
continue;
- } else
+ } else {
add(Bnull);
+ if (c == STOUC(Meta)) {
+ c = hgetc();
+#ifdef DEBUG
+ if (lexstop) {
+ fputs("BUG: input terminated by Meta\n", stderr);
+ fflush(stderr);
+ goto brk;
+ }
+#endif
+ add(Meta);
+ }
+ }
if (lexstop)
goto brk;
break;
case LX2_QUOTE: {
- int strquote = (len && bptr[-1] == String);
+ int strquote = (lexbuf.len && lexbuf.ptr[-1] == String);
add(Snull);
cmdpush(CS_QUOTE);
@@ -1355,8 +1261,8 @@ gettokstr(int c, int sub)
else
add('\\');
} else if (!sub && isset(CSHJUNKIEQUOTES) && c == '\n') {
- if (bptr[-1] == '\\')
- bptr--, len--;
+ if (lexbuf.ptr[-1] == '\\')
+ lexbuf.ptr--, lexbuf.len--;
else
break;
}
@@ -1439,6 +1345,8 @@ gettokstr(int c, int sub)
break;
}
brk:
+ if (errflag)
+ return LEXERR;
hungetc(c);
if (unmatched)
zerr("unmatched %c", unmatched);
@@ -1446,15 +1354,16 @@ gettokstr(int c, int sub)
while(bct-- >= in_brace_param)
cmdpop();
zerr("closing brace expected");
- } else if (unset(IGNOREBRACES) && !sub && len > 1 &&
- peek == STRING && bptr[-1] == '}' && bptr[-2] != Bnull) {
+ } else if (unset(IGNOREBRACES) && !sub && lexbuf.len > 1 &&
+ peek == STRING && lexbuf.ptr[-1] == '}' &&
+ lexbuf.ptr[-2] != Bnull) {
/* hack to get {foo} command syntax work */
- bptr--;
- len--;
+ lexbuf.ptr--;
+ lexbuf.len--;
lexstop = 0;
hungetc('}');
}
- *bptr = '\0';
+ *lexbuf.ptr = '\0';
DPUTS(cmdsp != ocmdsp, "BUG: gettok: cmdstack changed.");
return peek;
}
@@ -1508,8 +1417,19 @@ dquote_parse(char endchar, int sub)
c = hgetc();
if (c == '(') {
add(Qstring);
- err = cmd_or_math_sub();
- c = Outpar;
+ switch (cmd_or_math_sub()) {
+ case CMD_OR_MATH_CMD:
+ c = Outpar;
+ break;
+
+ case CMD_OR_MATH_MATH:
+ c = Outparmath;
+ break;
+
+ default:
+ err = 1;
+ break;
+ }
} else if (c == '[') {
add(String);
add(Inbrack);
@@ -1617,44 +1537,58 @@ dquote_parse(char endchar, int sub)
return err;
}
-/* Tokenize a string given in s. Parsing is done as in double *
- * quotes. This is usually called before singsub(). */
+/*
+ * Tokenize a string given in s. Parsing is done as in double
+ * quotes. This is usually called before singsub().
+ *
+ * parsestr() is noisier, reporting an error if the parse failed.
+ *
+ * On entry, *s must point to a string allocated from the stack of
+ * exactly the right length, i.e. strlen(*s) + 1, as the string
+ * is used as the lexical token string whose memory management
+ * demands this. Usually the input string will therefore be
+ * the result of an immediately preceding dupstring().
+ */
/**/
mod_export int
-parsestr(char *s)
+parsestr(char **s)
{
int err;
if ((err = parsestrnoerr(s))) {
- untokenize(s);
- if (err > 32 && err < 127)
- zerr("parse error near `%c'", err);
- else
- zerr("parse error");
+ untokenize(*s);
+ if (!(errflag & ERRFLAG_INT)) {
+ if (err > 32 && err < 127)
+ zerr("parse error near `%c'", err);
+ else
+ zerr("parse error");
+ }
}
return err;
}
/**/
mod_export int
-parsestrnoerr(char *s)
+parsestrnoerr(char **s)
{
- int l = strlen(s), err;
+ int l = strlen(*s), err;
- lexsave();
- untokenize(s);
- inpush(dupstring(s), 0, NULL);
+ zcontext_save();
+ untokenize(*s);
+ inpush(dupstring(*s), 0, NULL);
strinbeg(0);
- len = 0;
- bptr = tokstr = s;
- bsiz = l + 1;
+ lexbuf.len = 0;
+ lexbuf.ptr = tokstr = *s;
+ lexbuf.siz = l + 1;
err = dquote_parse('\0', 1);
- *bptr = '\0';
+ if (tokstr)
+ *s = tokstr;
+ *lexbuf.ptr = '\0';
strinend();
inpop();
DPUTS(cmdsp, "BUG: parsestr: cmdstack not empty.");
- lexrestore();
+ zcontext_restore();
return err;
}
@@ -1673,27 +1607,27 @@ parse_subscript(char *s, int sub, int endchar)
if (!*s || *s == endchar)
return 0;
- lexsave();
+ zcontext_save();
untokenize(t = dupstring(s));
inpush(t, 0, NULL);
strinbeg(0);
- len = 0;
- bptr = tokstr = s;
- bsiz = l + 1;
+ lexbuf.len = 0;
+ lexbuf.ptr = tokstr = s;
+ lexbuf.siz = l + 1;
err = dquote_parse(endchar, sub);
if (err) {
- err = *bptr;
- *bptr = '\0';
+ err = *lexbuf.ptr;
+ *lexbuf.ptr = '\0';
untokenize(s);
- *bptr = err;
+ *lexbuf.ptr = err;
s = NULL;
} else {
- s = bptr;
+ s = lexbuf.ptr;
}
strinend();
inpop();
DPUTS(cmdsp, "BUG: parse_subscript: cmdstack not empty.");
- lexrestore();
+ zcontext_restore();
return s;
}
@@ -1711,29 +1645,30 @@ parse_subst_string(char *s)
if (!*s || !strcmp(s, nulstring))
return 0;
- lexsave();
+ zcontext_save();
untokenize(s);
inpush(dupstring(s), 0, NULL);
strinbeg(0);
- len = 0;
- bptr = tokstr = s;
- bsiz = l + 1;
+ lexbuf.len = 0;
+ lexbuf.ptr = tokstr = s;
+ lexbuf.siz = l + 1;
c = hgetc();
ctok = gettokstr(c, 1);
err = errflag;
strinend();
inpop();
DPUTS(cmdsp, "BUG: parse_subst_string: cmdstack not empty.");
- lexrestore();
- errflag = err;
+ zcontext_restore();
+ /* Keep any interrupt error status */
+ errflag = err | (errflag & ERRFLAG_INT);
if (ctok == LEXERR) {
untokenize(s);
return 1;
}
#ifdef DEBUG
/*
- * Historical note: we used to check here for olen (the value of len
- * before lexrestore()) == l, but that's not necessarily the case if
+ * Historical note: we used to check here for olen (the value of lexbuf.len
+ * before zcontext_restore()) == l, but that's not necessarily the case if
* we stripped an RCQUOTE.
*/
if (ctok != STRING || (errflag && !noerrs)) {
@@ -1787,7 +1722,7 @@ parse_subst_string(char *s)
/* Called below to report word positions. */
/**/
-mod_export void
+static void
gotword(void)
{
we = zlemetall + 1 - inbufct + (addedx == 2 ? 1 : 0);
@@ -1797,13 +1732,62 @@ gotword(void)
}
}
+/* Check if current lex text matches an alias: 1 if so, else 0 */
+
+static int
+checkalias(void)
+{
+ Alias an;
+
+ if (!zshlextext)
+ return 0;
+
+ if (!noaliases && isset(ALIASESOPT) &&
+ (!isset(POSIXALIASES) ||
+ (tok == STRING && !reswdtab->getnode(reswdtab, zshlextext)))) {
+ char *suf;
+
+ an = (Alias) aliastab->getnode(aliastab, zshlextext);
+ if (an && !an->inuse &&
+ ((an->node.flags & ALIAS_GLOBAL) ||
+ (incmdpos && tok == STRING) || inalmore)) {
+ if (!lexstop) {
+ /*
+ * Tokens that don't require a space after, get one,
+ * because they are treated as if preceded by one.
+ */
+ int c = hgetc();
+ hungetc(c);
+ if (!iblank(c))
+ inpush(" ", INP_ALIAS, 0);
+ }
+ inpush(an->text, INP_ALIAS, an);
+ if (an->text[0] == ' ' && !(an->node.flags & ALIAS_GLOBAL))
+ aliasspaceflag = 1;
+ lexstop = 0;
+ return 1;
+ }
+ if ((suf = strrchr(zshlextext, '.')) && suf[1] &&
+ suf > zshlextext && suf[-1] != Meta &&
+ (an = (Alias)sufaliastab->getnode(sufaliastab, suf+1)) &&
+ !an->inuse && incmdpos) {
+ inpush(dupstring(zshlextext), INP_ALIAS, NULL);
+ inpush(" ", INP_ALIAS, NULL);
+ inpush(an->text, INP_ALIAS, an);
+ lexstop = 0;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
/* expand aliases and reserved words */
/**/
int
exalias(void)
{
- Alias an;
Reswd rw;
hwend();
@@ -1815,7 +1799,9 @@ exalias(void)
if (!tokstr) {
zshlextext = tokstrings[tok];
- return 0;
+ if (tok == NEWLIN)
+ return 0;
+ return checkalias();
} else {
VARARR(char, copy, (strlen(tokstr) + 1));
@@ -1841,34 +1827,10 @@ exalias(void)
if (tok == STRING) {
/* Check for an alias */
- if (!noaliases && isset(ALIASESOPT) &&
- (!isset(POSIXALIASES) ||
- !reswdtab->getnode(reswdtab, zshlextext))) {
- char *suf;
-
- an = (Alias) aliastab->getnode(aliastab, zshlextext);
- if (an && !an->inuse &&
- ((an->node.flags & ALIAS_GLOBAL) || incmdpos || inalmore)) {
- inpush(an->text, INP_ALIAS, an);
- if (an->text[0] == ' ' && !(an->node.flags & ALIAS_GLOBAL))
- aliasspaceflag = 1;
- lexstop = 0;
- if (zshlextext == copy)
- zshlextext = tokstr;
- return 1;
- }
- if ((suf = strrchr(zshlextext, '.')) && suf[1] &&
- suf > zshlextext && suf[-1] != Meta &&
- (an = (Alias)sufaliastab->getnode(sufaliastab, suf+1)) &&
- !an->inuse && incmdpos) {
- inpush(dupstring(zshlextext), INP_ALIAS, NULL);
- inpush(" ", INP_ALIAS, NULL);
- inpush(an->text, INP_ALIAS, an);
- lexstop = 0;
- if (zshlextext == copy)
- zshlextext = tokstr;
- return 1;
- }
+ if ((zshlextext != copy || !isset(POSIXALIASES)) && checkalias()) {
+ if (zshlextext == copy)
+ zshlextext = tokstr;
+ return 1;
}
/* Then check for a reserved word */
@@ -1892,12 +1854,68 @@ exalias(void)
return 0;
}
-/* skip (...) */
+/**/
+void
+zshlex_raw_add(int c)
+{
+ if (!lex_add_raw)
+ return;
+
+ *lexbuf_raw.ptr++ = c;
+ if (lexbuf_raw.siz == ++lexbuf_raw.len) {
+ int newbsiz = lexbuf_raw.siz * 2;
+
+ tokstr_raw = (char *)hrealloc(tokstr_raw, lexbuf_raw.siz, newbsiz);
+ lexbuf_raw.ptr = tokstr_raw + lexbuf_raw.len;
+ memset(lexbuf_raw.ptr, 0, newbsiz - lexbuf_raw.siz);
+ lexbuf_raw.siz = newbsiz;
+ }
+}
+
+/**/
+void
+zshlex_raw_back(void)
+{
+ if (!lex_add_raw)
+ return;
+ lexbuf_raw.ptr--;
+ lexbuf_raw.len--;
+}
+
+/**/
+int
+zshlex_raw_mark(int offset)
+{
+ if (!lex_add_raw)
+ return 0;
+ return lexbuf_raw.len + offset;
+}
+
+/**/
+void
+zshlex_raw_back_to_mark(int mark)
+{
+ if (!lex_add_raw)
+ return;
+ lexbuf_raw.ptr = tokstr_raw + mark;
+ lexbuf_raw.len = mark;
+}
+
+/*
+ * Skip (...) for command-style substitutions: $(...), <(...), >(...)
+ *
+ * In order to ensure we don't stop at closing parentheses with
+ * some other syntactic significance, we'll parse the input until
+ * we find an unmatched closing parenthesis. However, we'll throw
+ * away the result of the parsing and just keep the string we've built
+ * up on the way.
+ */
/**/
static int
skipcomm(void)
{
+#ifdef ZSH_OLD_SKIPCOMM
int pct = 1, c, start = 1;
cmdpush(CS_CMDSUBST);
@@ -1922,7 +1940,7 @@ skipcomm(void)
c = hgetc();
break;
case '\'': {
- int strquote = bptr[-1] == '$';
+ int strquote = lexbuf.ptr[-1] == '$';
add(c);
STOPHIST
while ((c = hgetc()) != '\'' && !lexstop) {
@@ -1968,4 +1986,114 @@ skipcomm(void)
SETPAREND
cmdpop();
return lexstop;
+#else
+ char *new_tokstr;
+ int new_lexstop, new_lex_add_raw;
+ struct lexbufstate new_lexbuf;
+
+ cmdpush(CS_CMDSUBST);
+ SETPARBEGIN
+ add(Inpar);
+
+ new_lex_add_raw = lex_add_raw + 1;
+ if (!lex_add_raw) {
+ /*
+ * We'll combine the string so far with the input
+ * read in for the command substitution. To do this
+ * we'll just propagate the current tokstr etc. as the
+ * variables used for adding raw input, and
+ * ensure we swap those for the real tokstr etc. at the end.
+ *
+ * However, we need to save and restore the rest of the
+ * lexical and parse state as we're effectively parsing
+ * an internal string. Because we're still parsing it from
+ * the original input source (we have to --- we don't know
+ * when to stop inputting it otherwise and can't rely on
+ * the input being recoverable until we've read it) we need
+ * to keep the same history context.
+ */
+ new_tokstr = tokstr;
+ new_lexbuf = lexbuf;
+
+ zcontext_save_partial(ZCONTEXT_LEX|ZCONTEXT_PARSE);
+ hist_in_word(1);
+ } else {
+ /*
+ * Set up for nested command subsitution, however
+ * we don't actually need the string until we get
+ * back to the top level and recover the lot.
+ * The $() body just appears empty.
+ *
+ * We do need to propagate the raw variables which would
+ * otherwise by cleared, though.
+ */
+ new_tokstr = tokstr_raw;
+ new_lexbuf = lexbuf_raw;
+
+ zcontext_save_partial(ZCONTEXT_LEX|ZCONTEXT_PARSE);
+ }
+ tokstr_raw = new_tokstr;
+ lexbuf_raw = new_lexbuf;
+ lex_add_raw = new_lex_add_raw;
+ /*
+ * Don't do any ZLE specials down here: they're only needed
+ * when we return the string from the recursive parse.
+ * (TBD: this probably means we should be initialising lexflags
+ * more consistently.)
+ *
+ * Note that in that case we're still using the ZLE line reading
+ * function at the history layer --- this is consistent with the
+ * intention of maintaining the history and input layers across
+ * the recursive parsing.
+ */
+ lexflags &= ~LEXFLAGS_ZLE;
+
+ if (!parse_event(OUTPAR) || tok != OUTPAR)
+ lexstop = 1;
+ /* Outpar lexical token gets added in caller if present */
+
+ /*
+ * We're going to keep the full raw input string
+ * as the current token string after popping the stack.
+ */
+ new_tokstr = tokstr_raw;
+ new_lexbuf = lexbuf_raw;
+ /*
+ * We're also going to propagate the lexical state:
+ * if we couldn't parse the command substitution we
+ * can't continue.
+ */
+ new_lexstop = lexstop;
+
+ zcontext_restore_partial(ZCONTEXT_LEX|ZCONTEXT_PARSE);
+
+ if (lex_add_raw) {
+ /*
+ * Keep going, so retain the raw variables.
+ */
+ tokstr_raw = new_tokstr;
+ lexbuf_raw = new_lexbuf;
+ } else {
+ if (!new_lexstop) {
+ /* Ignore the ')' added on input */
+ new_lexbuf.len--;
+ *--new_lexbuf.ptr = '\0';
+ }
+
+ /*
+ * Convince the rest of lex.c we were examining a string
+ * all along.
+ */
+ tokstr = new_tokstr;
+ lexbuf = new_lexbuf;
+ lexstop = new_lexstop;
+ hist_in_word(0);
+ }
+
+ if (!lexstop)
+ SETPAREND
+ cmdpop();
+
+ return lexstop;
+#endif
}
diff --git a/Src/linklist.c b/Src/linklist.c
index 1e364fb4e..3aa8125d9 100644
--- a/Src/linklist.c
+++ b/Src/linklist.c
@@ -118,6 +118,8 @@ znewlinklist(void)
LinkList list;
list = (LinkList) zalloc(sizeof *list);
+ if (!list)
+ return NULL;
list->list.first = NULL;
list->list.last = &list->node;
list->list.flags = 0;
@@ -152,6 +154,8 @@ zinsertlinknode(LinkList list, LinkNode node, void *dat)
tmp = node->next;
node->next = new = (LinkNode) zalloc(sizeof *tmp);
+ if (!new)
+ return NULL;
new->prev = node;
new->dat = dat;
new->next = tmp;
diff --git a/Src/loop.c b/Src/loop.c
index 2f639fd5a..d025fbb9f 100644
--- a/Src/loop.c
+++ b/Src/loop.c
@@ -102,7 +102,10 @@ execfor(Estate state, int do_exec)
addlinknode(args, dupstring(*x));
}
}
- /* lastval = 0; */
+
+ if (!args || empty(args))
+ lastval = 0;
+
loops++;
pushheap();
cmdpush(CS_FOR);
@@ -238,10 +241,10 @@ execselect(Estate state, UNUSED(int do_exec))
}
if (!args || empty(args)) {
state->pc = end;
- return 1;
+ return 0;
}
loops++;
- /* lastval = 0; */
+
pushheap();
cmdpush(CS_SELECT);
usezle = interact && SHTTY != -1 && isset(USEZLE);
@@ -259,13 +262,14 @@ execselect(Estate state, UNUSED(int do_exec))
0, ZLCON_SELECT);
if (errflag)
str = NULL;
- errflag = oef;
+ /* Keep any user interrupt error status */
+ errflag = oef | (errflag & ERRFLAG_INT);
} else {
str = promptexpand(prompt3, 0, NULL, NULL, NULL);
zputs(str, stderr);
free(str);
fflush(stderr);
- str = fgets(zalloc(256), 256, inp);
+ str = fgets(zhalloc(256), 256, inp);
}
} else
str = (char *)getlinknode(bufstack);
@@ -518,14 +522,17 @@ execif(Estate state, int do_exec)
s = 1;
state->pc = next;
}
- noerrexit = olderrexit;
if (run) {
+ /* we need to ignore lastval until we reach execcmd() */
+ noerrexit = olderrexit ? olderrexit : lastval ? 2 : 0;
cmdpush(run == 2 ? CS_ELSE : (s ? CS_ELIFTHEN : CS_IFTHEN));
execlist(state, 1, do_exec);
cmdpop();
- } else
+ } else {
+ noerrexit = olderrexit;
lastval = 0;
+ }
state->pc = end;
return lastval;
@@ -632,6 +639,14 @@ execcase(Estate state, int do_exec)
zlong
try_errflag = -1;
+/**
+ * Corresponding interrupt error status form `try' block.
+ */
+
+/**/
+zlong
+try_interrupt = -1;
+
/**/
zlong
try_tryflag = 0;
@@ -643,7 +658,7 @@ exectry(Estate state, int do_exec)
Wordcode end, always;
int endval;
int save_retflag, save_breaks, save_contflag;
- zlong save_try_errflag, save_try_tryflag;
+ zlong save_try_errflag, save_try_tryflag, save_try_interrupt;
end = state->pc + WC_TRY_SKIP(state->pc[-1]);
always = state->pc + 1 + WC_TRY_SKIP(*state->pc);
@@ -659,8 +674,9 @@ exectry(Estate state, int do_exec)
try_tryflag = save_try_tryflag;
- /* Don't record errflag here, may be reset. */
- endval = lastval;
+ /* Don't record errflag here, may be reset. However, */
+ /* endval should show failure when there is an error. */
+ endval = lastval ? lastval : errflag;
freeheap();
@@ -669,7 +685,10 @@ exectry(Estate state, int do_exec)
/* The always clause. */
save_try_errflag = try_errflag;
- try_errflag = (zlong)errflag;
+ save_try_interrupt = try_interrupt;
+ try_errflag = (zlong)(errflag & ERRFLAG_ERROR);
+ try_interrupt = (zlong)((errflag & ERRFLAG_INT) ? 1 : 0);
+ /* We need to reset all errors to allow the block to execute */
errflag = 0;
save_retflag = retflag;
retflag = 0;
@@ -681,8 +700,16 @@ exectry(Estate state, int do_exec)
state->pc = always;
execlist(state, 1, do_exec);
- errflag = try_errflag ? 1 : 0;
+ if (try_errflag)
+ errflag |= ERRFLAG_ERROR;
+ else
+ errflag &= ~ERRFLAG_ERROR;
+ if (try_interrupt)
+ errflag |= ERRFLAG_INT;
+ else
+ errflag &= ~ERRFLAG_INT;
try_errflag = save_try_errflag;
+ try_interrupt = save_try_interrupt;
if (!retflag)
retflag = save_retflag;
if (!breaks)
diff --git a/Src/math.c b/Src/math.c
index 266569827..97a97b32b 100644
--- a/Src/math.c
+++ b/Src/math.c
@@ -288,11 +288,11 @@ static int type[TOKCOUNT] =
{
/* 0 */ LR, LR|OP_OP|OP_OPF, RL, RL, RL|OP_OP|OP_OPF,
/* 5 */ RL|OP_OP|OP_OPF, RL, RL, LR|OP_A2IO, LR|OP_A2IO,
-/* 10 */ LR|OP_A2IO, LR|OP_A2, LR|OP_A2, LR|OP_A2IO, LR|OP_A2,
+/* 10 */ LR|OP_A2IO, LR|OP_A2, LR|OP_A2, LR|OP_A2, LR|OP_A2,
/* 15 */ LR|OP_A2, LR|OP_A2IO, LR|OP_A2IO, LR|OP_A2IR, LR|OP_A2IR,
/* 20 */ LR|OP_A2IR, LR|OP_A2IR, LR|OP_A2IR, LR|OP_A2IR, BOOL|OP_A2IO,
/* 25 */ BOOL|OP_A2IO, LR|OP_A2IO, RL|OP_OP, RL|OP_OP, RL|OP_E2,
-/* 30 */ RL|OP_E2, RL|OP_E2, RL|OP_E2, RL|OP_E2, RL|OP_E2IO,
+/* 30 */ RL|OP_E2, RL|OP_E2, RL|OP_E2, RL|OP_E2, RL|OP_E2,
/* 35 */ RL|OP_E2IO, RL|OP_E2IO, RL|OP_E2IO, RL|OP_E2IO, RL|OP_E2IO,
/* 40 */ BOOL|OP_E2IO, BOOL|OP_E2IO, RL|OP_A2IO, RL|OP_A2, RL|OP_OP,
/* 45 */ RL, RL, LR|OP_OPF, LR|OP_OPF, RL|OP_A2,
@@ -336,16 +336,27 @@ enum prec_type {
static mnumber
getmathparam(struct mathvalue *mptr)
{
+ mnumber result;
if (!mptr->pval) {
char *s = mptr->lval;
mptr->pval = (Value)zhalloc(sizeof(struct value));
if (!getvalue(mptr->pval, &s, 1))
{
mptr->pval = NULL;
+ if (isset(FORCEFLOAT)) {
+ result.type = MN_FLOAT;
+ result.u.d = 0.0;
+ return result;
+ }
return zero_mnumber;
}
}
- return getnumvalue(mptr->pval);
+ result = getnumvalue(mptr->pval);
+ if (isset(FORCEFLOAT) && result.type == MN_INTEGER) {
+ result.type = MN_FLOAT;
+ result.u.d = (double)result.u.l;
+ }
+ return result;
}
static mnumber
@@ -449,12 +460,14 @@ lexconstant(void)
nptr++;
if (*nptr == '0') {
+ int lowchar;
nptr++;
- if (*nptr == 'x' || *nptr == 'X') {
+ lowchar = tolower(*nptr);
+ if (lowchar == 'x' || lowchar == 'b') {
/* Let zstrtol parse number with base */
yyval.u.l = zstrtol_underscore(ptr, &ptr, 0, 1);
/* Should we set lastbase here? */
- lastbase = 16;
+ lastbase = (lowchar == 'b') ? 2 : 16;
if (isset(FORCEFLOAT))
{
yyval.type = MN_FLOAT;
@@ -878,6 +891,8 @@ getcvar(char *s)
static mnumber
setmathvar(struct mathvalue *mvp, mnumber v)
{
+ Param pm;
+
if (mvp->pval) {
/*
* This value may have been hanging around for a while.
@@ -907,7 +922,32 @@ setmathvar(struct mathvalue *mvp, mnumber v)
if (noeval)
return v;
untokenize(mvp->lval);
- setnparam(mvp->lval, v);
+ pm = setnparam(mvp->lval, v);
+ if (pm) {
+ /*
+ * If we are performing an assignment, we return the
+ * number with the same type as the parameter we are
+ * assigning to, in the spirit of the way assignments
+ * in C work. Note this was a change to long-standing
+ * zsh behaviour.
+ */
+ switch (PM_TYPE(pm->node.flags)) {
+ case PM_INTEGER:
+ if (v.type != MN_INTEGER) {
+ v.u.l = (zlong)v.u.d;
+ v.type = MN_INTEGER;
+ }
+ break;
+
+ case PM_EFLOAT:
+ case PM_FFLOAT:
+ if (v.type != MN_FLOAT) {
+ v.u.d = (double)v.u.l;
+ v.type = MN_FLOAT;
+ }
+ break;
+ }
+ }
return v;
}
@@ -1131,7 +1171,9 @@ op(int what)
* Any integer mod -1 is the same as any integer mod 1
* i.e. zero.
*/
- if (b.u.l == -1)
+ if (c.type == MN_FLOAT)
+ c.u.d = fmod(a.u.d, b.u.d);
+ else if (b.u.l == -1)
c.u.l = 0;
else
c.u.l = a.u.l % b.u.l;
@@ -1356,6 +1398,8 @@ matheval(char *s)
if (!mlevel)
outputradix = outputunderscore = 0;
+ if (*s == Nularg)
+ s++;
if (!*s) {
x.type = MN_INTEGER;
x.u.l = 0;
@@ -1393,6 +1437,8 @@ mathevalarg(char *s, char **ss)
*
* To avoid a more opaque error further in, bail out here.
*/
+ if (*s == Nularg)
+ s++;
if (!*s) {
zerr("bad math expression: empty string");
return (zlong)0;
diff --git a/Src/mem.c b/Src/mem.c
index 7e0667a33..b9569ea0c 100644
--- a/Src/mem.c
+++ b/Src/mem.c
@@ -79,6 +79,10 @@
#include <sys/mman.h>
+#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+
#if defined(MAP_ANONYMOUS) && defined(MAP_PRIVATE)
#define USE_MMAP 1
diff --git a/Src/module.c b/Src/module.c
index 9e8b3cc2a..368254c29 100644
--- a/Src/module.c
+++ b/Src/module.c
@@ -1577,7 +1577,9 @@ try_load_module(char const *name)
if (l + (**pp ? strlen(*pp) : 1) > PATH_MAX)
continue;
sprintf(buf, "%s/%s.%s", **pp ? *pp : ".", name, DL_EXT);
- ret = dlopen(unmeta(buf), RTLD_LAZY | RTLD_GLOBAL);
+ unmetafy(buf, NULL);
+ if (*buf) /* dlopen(NULL) returns a handle to the main binary */
+ ret = dlopen(buf, RTLD_LAZY | RTLD_GLOBAL);
}
return ret;
@@ -1597,8 +1599,9 @@ do_load_module(char const *name, int silent)
ret = try_load_module(name);
if (!ret && !silent) {
#ifdef HAVE_DLERROR
+ char *errstr = dlerror();
zwarn("failed to load module `%s': %s", name,
- metafy(dlerror(), -1, META_USEHEAP));
+ errstr ? metafy(errstr, -1, META_HEAPDUP) : "empty module path");
#else
zwarn("failed to load module: %s", name);
#endif
@@ -3367,7 +3370,7 @@ mod_export int
handlefeatures(Module m, Features f, int **enables)
{
if (!enables || *enables)
- return setfeatureenables(m, f, *enables);
+ return setfeatureenables(m, f, enables ? *enables : NULL);
*enables = getfeatureenables(m, f);
return 0;
}
diff --git a/Src/options.c b/Src/options.c
index 6e4e7b911..3e3e07474 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -766,7 +766,17 @@ dosetopt(int optno, int value, int force, char *new_opts)
#ifdef HAVE_SETUID
setuid(getuid());
setgid(getgid());
-#endif /* HAVE_SETUID */
+ if (setuid(getuid())) {
+ zwarn("failed to change user ID: %e", errno);
+ return -1;
+ } else if (setgid(getgid())) {
+ zwarn("failed to change group ID: %e", errno);
+ return -1;
+ }
+#else
+ zwarn("setuid not available");
+ return -1;
+#endif /* not HAVE_SETUID */
#ifdef JOB_CONTROL
} else if (!force && optno == MONITOR && value) {
if (new_opts[optno] == value)
diff --git a/Src/params.c b/Src/params.c
index 61edc5d08..d53b6ca7e 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -331,6 +331,7 @@ IPDEF5("SHLVL", &shlvl, varinteger_gsu),
#define IPDEF6(A,B,F) {{NULL,A,PM_INTEGER|PM_SPECIAL|PM_DONTIMPORT},BR((void *)B),GSU(F),10,0,NULL,NULL,NULL,0}
IPDEF6("OPTIND", &zoptind, varinteger_gsu),
IPDEF6("TRY_BLOCK_ERROR", &try_errflag, varinteger_gsu),
+IPDEF6("TRY_BLOCK_INTERRUPT", &try_interrupt, varinteger_gsu),
#define IPDEF7(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0}
#define IPDEF7U(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL|PM_UNSET},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0}
@@ -641,8 +642,16 @@ split_env_string(char *env, char **name, char **value)
return 0;
tenv = strcpy(zhalloc(strlen(env) + 1), env);
- for (str = tenv; *str && *str != '='; str++)
- ;
+ for (str = tenv; *str && *str != '='; str++) {
+ if (STOUC(*str) >= 128) {
+ /*
+ * We'll ignore environment variables with names not
+ * from the portable character set since we don't
+ * know of a good reason to accept them.
+ */
+ return 0;
+ }
+ }
if (str != tenv && *str == '=') {
*str = '\0';
*name = tenv;
@@ -865,10 +874,14 @@ createparam(char *name, int flags)
DPUTS(oldpm && oldpm->level > locallevel,
"BUG: old local parameter not deleted");
if (oldpm && (oldpm->level == locallevel || !(flags & PM_LOCAL))) {
+ if (isset(POSIXBUILTINS) && (oldpm->node.flags & PM_READONLY)) {
+ zerr("read-only variable: %s", name);
+ return NULL;
+ }
if (!(oldpm->node.flags & PM_UNSET) || (oldpm->node.flags & PM_SPECIAL)) {
oldpm->node.flags &= ~PM_UNSET;
if ((oldpm->node.flags & PM_SPECIAL) && oldpm->ename) {
- Param altpm =
+ Param altpm =
(Param) paramtab->getnode(paramtab, oldpm->ename);
if (altpm)
altpm->node.flags &= ~PM_UNSET;
@@ -918,7 +931,15 @@ shempty(void)
{
}
-/* Create a simple special hash parameter. */
+/*
+ * Create a simple special hash parameter.
+ *
+ * This is for hashes added internally --- it's not possible to add
+ * special hashes from shell commands. It's currently used
+ * - by addparamdef() for special parameters in the zsh/parameter
+ * module
+ * - by ztie for special parameters tied to databases.
+ */
/**/
mod_export Param
@@ -930,7 +951,22 @@ createspecialhash(char *name, GetNodeFunc get, ScanTabFunc scan, int flags)
if (!(pm = createparam(name, PM_SPECIAL|PM_HASHED|flags)))
return NULL;
- pm->level = pm->old ? locallevel : 0;
+ /*
+ * If there's an old parameter, we'll put the new one at
+ * the current locallevel, so that the old parameter is
+ * exposed again after leaving the function. Otherwise,
+ * we'll leave it alone. Usually this means the parameter
+ * will stay in place until explicitly unloaded, however
+ * if the parameter was previously unset within a function
+ * we'll inherit the level of that function and follow the
+ * standard convention that the parameter remains local
+ * even if unset.
+ *
+ * These semantics are similar to those of a normal parameter set
+ * within a function without a local definition.
+ */
+ if (pm->old)
+ pm->level = locallevel;
pm->gsu.h = (flags & PM_READONLY) ? &stdhash_gsu :
&nullsethash_gsu;
pm->u.hash = ht = newhashtable(0, name, NULL);
@@ -1251,7 +1287,8 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w,
if (ishash && (keymatch || !rev))
remnulargs(s);
if (needtok) {
- if (parsestr(s))
+ s = dupstring(s);
+ if (parsestr(&s))
return 0;
singsub(&s);
} else if (rev)
@@ -2654,7 +2691,7 @@ assignsparam(char *s, char *val, int flags)
if (!isident(s)) {
zerr("not an identifier: %s", s);
zsfree(val);
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
return NULL;
}
queue_signals();
@@ -2783,7 +2820,7 @@ assignaparam(char *s, char **val, int flags)
if (!isident(s)) {
zerr("not an identifier: %s", s);
freearray(val);
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
return NULL;
}
queue_signals();
@@ -2799,7 +2836,7 @@ assignaparam(char *s, char **val, int flags)
zerr("%s: attempt to set slice of associative array",
v->pm->node.nam);
freearray(val);
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
return NULL;
}
v = NULL;
@@ -2870,13 +2907,13 @@ sethparam(char *s, char **val)
if (!isident(s)) {
zerr("not an identifier: %s", s);
freearray(val);
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
return NULL;
}
if (strchr(s, '[')) {
freearray(val);
zerr("nested associative arrays not yet supported");
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
return NULL;
}
if (unset(EXECOPT))
@@ -2916,7 +2953,7 @@ setnparam(char *s, mnumber val)
if (!isident(s)) {
zerr("not an identifier: %s", s);
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
return NULL;
}
if (unset(EXECOPT))
@@ -4368,9 +4405,18 @@ zputenv(char *str)
char *ptr;
int ret;
- for (ptr = str; *ptr && *ptr != '='; ptr++)
+ for (ptr = str; *ptr && STOUC(*ptr) < 128 && *ptr != '='; ptr++)
;
- if (*ptr) {
+ if (STOUC(*ptr) >= 128) {
+ /*
+ * Environment variables not in the portable character
+ * set are non-standard and we don't really know of
+ * a use for them.
+ *
+ * We'll disable until someone complains.
+ */
+ return 1;
+ } else if (*ptr) {
*ptr = '\0';
ret = setenv(str, ptr+1, 1);
*ptr = '=';
diff --git a/Src/parse.c b/Src/parse.c
index 433efb94e..985eb8e71 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -31,7 +31,7 @@
#include "parse.pro"
/* != 0 if we are about to read a command word */
-
+
/**/
mod_export int incmdpos;
@@ -71,13 +71,14 @@ struct heredocs *hdocs;
#define YYERROR(O) { tok = LEXERR; ecused = (O); return 0; }
#define YYERRORV(O) { tok = LEXERR; ecused = (O); return; }
-#define COND_ERROR(X,Y) do { \
- zwarn(X,Y); \
- herrflush(); \
- if (noerrs != 2) \
- errflag = 1; \
- YYERROR(ecused) \
-} while(0)
+#define COND_ERROR(X,Y) \
+ do { \
+ zwarn(X,Y); \
+ herrflush(); \
+ if (noerrs != 2) \
+ errflag |= ERRFLAG_ERROR; \
+ YYERROR(ecused) \
+ } while(0)
/*
@@ -241,6 +242,67 @@ int ecsoffs, ecssub, ecnfunc;
#define EC_DOUBLE_THRESHOLD 32768
#define EC_INCREMENT 1024
+/* save parse context */
+
+/**/
+void
+parse_context_save(struct parse_stack *ps, int toplevel)
+{
+ (void)toplevel;
+
+ ps->incmdpos = incmdpos;
+ ps->aliasspaceflag = aliasspaceflag;
+ ps->incond = incond;
+ ps->inredir = inredir;
+ ps->incasepat = incasepat;
+ ps->isnewlin = isnewlin;
+ ps->infor = infor;
+
+ ps->hdocs = hdocs;
+ ps->eclen = eclen;
+ ps->ecused = ecused;
+ ps->ecnpats = ecnpats;
+ ps->ecbuf = ecbuf;
+ ps->ecstrs = ecstrs;
+ ps->ecsoffs = ecsoffs;
+ ps->ecssub = ecssub;
+ ps->ecnfunc = ecnfunc;
+ ecbuf = NULL;
+ hdocs = NULL;
+}
+
+/* restore parse context */
+
+/**/
+void
+parse_context_restore(const struct parse_stack *ps, int toplevel)
+{
+ (void)toplevel;
+
+ if (ecbuf)
+ zfree(ecbuf, eclen);
+
+ incmdpos = ps->incmdpos;
+ aliasspaceflag = ps->aliasspaceflag;
+ incond = ps->incond;
+ inredir = ps->inredir;
+ incasepat = ps->incasepat;
+ incasepat = ps->incasepat;
+ isnewlin = ps->isnewlin;
+ infor = ps->infor;
+
+ hdocs = ps->hdocs;
+ eclen = ps->eclen;
+ ecused = ps->ecused;
+ ecnpats = ps->ecnpats;
+ ecbuf = ps->ecbuf;
+ ecstrs = ps->ecstrs;
+ ecsoffs = ps->ecsoffs;
+ ecssub = ps->ecssub;
+ ecnfunc = ps->ecnfunc;
+
+ errflag &= ~ERRFLAG_ERROR;
+}
/* Adjust pointers in here-doc structs. */
@@ -358,9 +420,25 @@ ecstrcode(char *s)
} while (0)
+/**/
+mod_export void
+init_parse_status(void)
+{
+ /*
+ * These variables are currently declared by the parser, so we
+ * initialise them here. Possibly they are more naturally declared
+ * by the lexical anaylser; however, as they are used for signalling
+ * 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;
+ incmdpos = 1;
+}
+
/* Initialise wordcode buffer. */
-static void
+/**/
+void
init_parse(void)
{
if (ecbuf) zfree(ecbuf, eclen);
@@ -371,6 +449,8 @@ init_parse(void)
ecsoffs = ecnpats = 0;
ecssub = 0;
ecnfunc = 0;
+
+ init_parse_status();
}
/* Build eprog. */
@@ -442,11 +522,15 @@ clear_hdocs()
* event : ENDINPUT
* | SEPER
* | sublist [ SEPER | AMPER | AMPERBANG ]
+ *
+ * cmdsubst indicates our event is part of a command-style
+ * substitution terminated by the token indicationg, usual closing
+ * parenthesis. In other cases endtok is ENDINPUT.
*/
/**/
Eprog
-parse_event(void)
+parse_event(int endtok)
{
tok = ENDINPUT;
incmdpos = 1;
@@ -454,36 +538,42 @@ parse_event(void)
zshlex();
init_parse();
- if (!par_event()) {
+ if (!par_event(endtok)) {
clear_hdocs();
return NULL;
}
+ if (endtok != ENDINPUT) {
+ /* don't need to build an eprog for this */
+ return &dummy_eprog;
+ }
return bld_eprog(1);
}
/**/
-static int
-par_event(void)
+int
+par_event(int endtok)
{
int r = 0, p, c = 0;
while (tok == SEPER) {
- if (isnewlin > 0)
+ if (isnewlin > 0 && endtok == ENDINPUT)
return 0;
zshlex();
}
if (tok == ENDINPUT)
return 0;
+ if (tok == endtok)
+ return 0;
p = ecadd(0);
if (par_sublist(&c)) {
- if (tok == ENDINPUT) {
+ if (tok == ENDINPUT || tok == endtok) {
set_list_code(p, Z_SYNC, c);
r = 1;
} else if (tok == SEPER) {
set_list_code(p, Z_SYNC, c);
- if (isnewlin <= 0)
+ if (isnewlin <= 0 || endtok != ENDINPUT)
zshlex();
r = 1;
} else if (tok == AMPER) {
@@ -506,15 +596,16 @@ par_event(void)
yyerror(1);
herrflush();
if (noerrs != 2)
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
ecused--;
return 0;
} else {
int oec = ecused;
- if (!par_event()) {
+ if (!par_event(endtok)) {
ecused = oec;
ecbuf[p] |= wc_bdata(Z_END);
+ return errflag ? 0 : 1;
}
}
return 1;
@@ -527,9 +618,8 @@ parse_list(void)
int c = 0;
tok = ENDINPUT;
- incmdpos = 1;
- zshlex();
init_parse();
+ zshlex();
par_list(&c);
if (tok != ENDINPUT) {
clear_hdocs();
@@ -2330,7 +2420,7 @@ yyerror(int noerr)
for (t0 = 0; t0 != 20; t0++)
if (!t || !t[t0] || t[t0] == '\n')
break;
- if (!(histdone & HISTFLAG_NOEXEC)) {
+ if (!(histdone & HISTFLAG_NOEXEC) && !(errflag & ERRFLAG_INT)) {
if (t0 == 20)
zwarn("parse error near `%l...'", t, 20);
else if (t0)
@@ -2339,7 +2429,7 @@ yyerror(int noerr)
zwarn("parse error");
}
if (!noerr && noerrs != 2)
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
}
/*
@@ -2590,7 +2680,7 @@ eccopyredirs(Estate s)
{
Wordcode pc = s->pc;
wordcode code = *pc;
- int ncode, ncodes = 0, r, type;
+ int ncode, ncodes = 0, r;
if (wc_code(code) != WC_REDIR)
return NULL;
@@ -2598,7 +2688,9 @@ eccopyredirs(Estate s)
init_parse();
while (wc_code(code) == WC_REDIR) {
- type = WC_REDIR_TYPE(code);
+#ifdef DEBUG
+ int type = WC_REDIR_TYPE(code);
+#endif
DPUTS(type == REDIR_HEREDOC || type == REDIR_HEREDOCDASH,
"unexpanded here document");
@@ -3029,7 +3121,7 @@ build_dump(char *nam, char *dump, char **files, int ali, int map, int flags)
file = metafy(file, flen, META_REALLOC);
if (!(prog = parse_string(file, 1)) || errflag) {
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
close(dfd);
zfree(file, flen);
zwarnnam(nam, "can't read file: %s", *files);
@@ -3139,7 +3231,7 @@ build_cur_dump(char *nam, char *dump, char **names, int match, int map,
for (hn = shfunctab->nodes[i]; hn; hn = hn->next)
if (cur_add_func(nam, (Shfunc) hn, lnames, progs,
&hlen, &tlen, what)) {
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
close(dfd);
unlink(dump);
return 1;
@@ -3164,7 +3256,7 @@ build_cur_dump(char *nam, char *dump, char **names, int match, int map,
pattry(pprog, hn->nam) &&
cur_add_func(nam, (Shfunc) hn, lnames, progs,
&hlen, &tlen, what)) {
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
close(dfd);
unlink(dump);
return 1;
@@ -3175,13 +3267,13 @@ build_cur_dump(char *nam, char *dump, char **names, int match, int map,
if (errflag ||
!(shf = (Shfunc) shfunctab->getnode(shfunctab, *names))) {
zwarnnam(nam, "unknown function: %s", *names);
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
close(dfd);
unlink(dump);
return 1;
}
if (cur_add_func(nam, shf, lnames, progs, &hlen, &tlen, what)) {
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
close(dfd);
unlink(dump);
return 1;
@@ -3190,7 +3282,7 @@ build_cur_dump(char *nam, char *dump, char **names, int match, int map,
}
if (empty(progs)) {
zwarnnam(nam, "no functions");
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
close(dfd);
unlink(dump);
return 1;
diff --git a/Src/pattern.c b/Src/pattern.c
index df5e602ca..17cd40c23 100644
--- a/Src/pattern.c
+++ b/Src/pattern.c
@@ -3622,7 +3622,7 @@ patmatchrange(char *range, int ch, int *indptr, int *mtp)
return 1;
break;
case PP_PRINT:
- if (isprint(ch))
+ if (ISPRINT(ch))
return 1;
break;
case PP_PUNCT:
diff --git a/Src/prompt.c b/Src/prompt.c
index 0cc9ef917..ffc1d0df2 100644
--- a/Src/prompt.c
+++ b/Src/prompt.c
@@ -183,7 +183,7 @@ promptexpand(char *s, int ns, char *rs, char *Rs, unsigned int *txtchangep)
int oldval = lastval;
s = dupstring(s);
- if (!parsestr(s))
+ if (!parsestr(&s))
singsub(&s);
/*
* We don't need the special Nularg hack here and we're
@@ -192,8 +192,11 @@ promptexpand(char *s, int ns, char *rs, char *Rs, unsigned int *txtchangep)
if (*s == Nularg && s[1] == '\0')
*s = '\0';
- /* Ignore errors and status change in prompt substitution */
- errflag = olderr;
+ /*
+ * Ignore errors and status change in prompt substitution.
+ * However, keep any user interrupt error that occurred.
+ */
+ errflag = olderr | (errflag & ERRFLAG_INT);
lastval = oldval;
}
diff --git a/Src/signals.c b/Src/signals.c
index 2df69f96e..3950ad1a2 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -522,14 +522,14 @@ wait_for_processes(void)
get_usage();
}
/*
- * Remember the status associated with $!, so we can
- * wait for it even if it's exited. This value is
- * only used if we can't find the PID in the job table,
- * so it doesn't matter that the value we save here isn't
- * useful until the process has exited.
+ * Accumulate a list of older jobs. We only do this for
+ * background jobs, which is something in the job table
+ * that's not marked as in the current shell or as shell builtin
+ * and is not equal to the current foreground job.
*/
- if (pn != NULL && pid == lastpid && lastpid_status != -1L)
- lastpid_status = lastval2;
+ if (jn && !(jn->stat & (STAT_CURSH|STAT_BUILTIN)) &&
+ jn - jobtab != thisjob)
+ addbgstatus(pid, (int)lastval2);
}
}
@@ -619,7 +619,7 @@ zhandler(int sig)
zexit(SIGINT, 1);
if (list_pipe || chline || simple_pline) {
breaks = loops;
- errflag = 1;
+ errflag |= ERRFLAG_INT;
inerrflush();
check_cursh_sig(SIGINT);
}
@@ -640,6 +640,11 @@ zhandler(int sig)
if (idle >= 0 && idle < tmout)
alarm(tmout - idle);
else {
+ /*
+ * We want to exit now.
+ * Cancel all errors, including a user interrupt
+ * which is now redundant.
+ */
errflag = noerrs = 0;
zwarn("timeout");
stopmsg = 1;
@@ -1205,7 +1210,7 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
intrap++;
*sigtr |= ZSIG_IGNORED;
- lexsave();
+ zcontext_save();
/* execsave will save the old trap_return and trap_state */
execsave();
breaks = retflag = 0;
@@ -1260,14 +1265,25 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
new_trap_return = trap_return;
execrestore();
- lexrestore();
+ zcontext_restore();
if (new_trap_state == TRAP_STATE_FORCE_RETURN &&
/* zero return from function isn't special */
!(isfunc && new_trap_return == 0)) {
if (isfunc) {
breaks = loops;
- errflag = 1;
+ /*
+ * For SIGINT we behave the same as the default behaviour
+ * i.e. we set the error bit indicating an interrupt.
+ * We do this with SIGQUIT, too, even though we don't
+ * handle SIGQUIT by default. That's to try to make
+ * it behave a bit more like its normal behaviour when
+ * the trap handler has told us that's what it wants.
+ */
+ if (sig == SIGINT || sig == SIGQUIT)
+ errflag |= ERRFLAG_INT;
+ else
+ errflag |= ERRFLAG_ERROR;
}
lastval = new_trap_return;
/* return triggered */
@@ -1282,8 +1298,12 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
*/
lastval = olastval;
}
- if (try_tryflag)
- errflag = traperr;
+ if (try_tryflag) {
+ if (traperr)
+ errflag |= ERRFLAG_ERROR;
+ else
+ errflag &= ~ERRFLAG_ERROR;
+ }
breaks += obreaks;
/* return not triggered: restore old flag */
retflag = oretflag;
diff --git a/Src/sort.c b/Src/sort.c
index 3d00bb576..92ee1c0d4 100644
--- a/Src/sort.c
+++ b/Src/sort.c
@@ -368,7 +368,7 @@ strmetasort(char **array, int sortwhat, int *unmetalenp)
sortdir = (sortwhat & SORTIT_BACKWARDS) ? -1 : 1;
sortnumeric = (sortwhat & SORTIT_NUMERICALLY) ? 1 : 0;
- qsort(sortptrarr, nsort, sizeof(SortElt *), eltpcmp);
+ qsort(sortptrarr, nsort, sizeof(SortElt), eltpcmp);
sortnumeric = oldsortnumeric;
sortdir = oldsortdir;
diff --git a/Src/subst.c b/Src/subst.c
index 1aa9b982e..f52bcdfc8 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -195,7 +195,7 @@ stringsubst(LinkList list, LinkNode node, int pf_flags, int asssub)
while (!errflag && (c = *str)) {
if ((qt = c == Qstring) || c == String) {
- if ((c = str[1]) == Inpar) {
+ if ((c = str[1]) == Inpar || c == Inparmath) {
if (!qt)
list->list.flags |= LF_ARRAY;
str++;
@@ -258,24 +258,34 @@ stringsubst(LinkList list, LinkNode node, int pf_flags, int asssub)
skipparens(Inpar, Outpar, &str);
#endif
str--;
- } else {
- endchar = c;
- *str = '\0';
-
- while (*++str != endchar)
- DPUTS(!*str, "BUG: parse error in command substitution");
- }
- *str++ = '\0';
- if (endchar == Outpar && str2[1] == '(' && str[-2] == ')') {
+ } else if (c == Inparmath) {
/* Math substitution of the form $((...)) */
- str[-2] = '\0';
+ str[-1] = '\0';
+ while (*str != Outparmath && *str)
+ str++;
+ if (*str != Outparmath) {
+ zerr("failed to find end of math substitution");
+ return NULL;
+ }
+ str[-1] = '\0';
if (isset(EXECOPT))
- str = arithsubst(str2 + 2, &str3, str);
+ str = arithsubst(str2 + 2, &str3, str+1);
else
strncpy(str3, str2, 1);
setdata(node, (void *) str3);
continue;
+ } else {
+ endchar = c;
+ *str = '\0';
+
+ while (*++str != endchar) {
+ if (!*str) {
+ zerr("failed to find end of command substitution");
+ return NULL;
+ }
+ }
}
+ *str++ = '\0';
/* It is a command substitution, which will be parsed again *
* by the lexer, so we untokenize it first, but we cannot use *
@@ -298,7 +308,7 @@ stringsubst(LinkList list, LinkNode node, int pf_flags, int asssub)
if (endchar == Outpar)
str2--;
if (!(s = (char *) ugetnode(pl))) {
- str = strcpy(str2, str);
+ str = (char *)memmove(str2, str, strlen(str)+1);
continue;
}
if (!qt && (pf_flags & PREFORK_SINGLE) && isset(GLOBSUBST))
@@ -1306,7 +1316,7 @@ get_intarg(char **s, int *delmatchp)
p = dupstring(*s + arglen);
*s = t + arglen;
*t = sav;
- if (parsestr(p))
+ if (parsestr(&p))
return -1;
singsub(&p);
if (errflag)
@@ -1317,7 +1327,7 @@ get_intarg(char **s, int *delmatchp)
if (ret < 0)
ret = -ret;
*delmatchp = arglen;
- return ret < 0 ? -ret : ret;
+ return ret;
}
/* Parsing for the (e) flag. */
@@ -1329,18 +1339,21 @@ subst_parse_str(char **sp, int single, int err)
*sp = s = dupstring(*sp);
- if (!(err ? parsestr(s) : parsestrnoerr(s))) {
+ if (!(err ? parsestr(&s) : parsestrnoerr(&s))) {
+ *sp = s;
if (!single) {
int qt = 0;
- for (; *s; s++)
+ for (; *s; s++) {
if (!qt) {
if (*s == Qstring)
*s = String;
else if (*s == Qtick)
*s = Tick;
- } else if (*s == Dnull)
+ }
+ if (*s == Dnull)
qt = !qt;
+ }
}
return 0;
}
@@ -1385,12 +1398,23 @@ static char *
untok_and_escape(char *s, int escapes, int tok_arg)
{
int klen;
- char *dst;
+ char *dst = NULL;
- untokenize(dst = dupstring(s));
- if (escapes) {
- dst = getkeystring(dst, &klen, GETKEYS_SEP, NULL);
- dst = metafy(dst, klen, META_HREALLOC);
+ if (escapes && (*s == String || *s == Qstring) && s[1]) {
+ char *pstart = s+1, *pend;
+ for (pend = pstart; *pend; pend++)
+ if (!iident(*pend))
+ break;
+ if (!*pend) {
+ dst = dupstring(getsparam(pstart));
+ }
+ }
+ if (dst == NULL) {
+ untokenize(dst = dupstring(s));
+ if (escapes) {
+ dst = getkeystring(dst, &klen, GETKEYS_SEP, NULL);
+ dst = metafy(dst, klen, META_HREALLOC);
+ }
}
if (tok_arg)
shtokenize(dst);
@@ -1426,7 +1450,8 @@ check_colon_subscript(char *str, char **endp)
}
sav = **endp;
**endp = '\0';
- if (parsestr(str = dupstring(str)))
+ str = dupstring(str);
+ if (parsestr(&str))
return NULL;
singsub(&str);
remnulargs(str);
@@ -2192,12 +2217,28 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags)
*/
idbeg = s;
if ((subexp = (inbrace && s[-1] && isstring(*s) &&
- (s[1] == Inbrace || s[1] == Inpar)))) {
+ (s[1] == Inbrace || s[1] == Inpar || s[1] == Inparmath)))) {
int sav;
int quoted = *s == Qstring;
+ int outtok;
val = s++;
- skipparens(*s, *s == Inpar ? Outpar : Outbrace, &s);
+ switch (*s) {
+ case Inbrace:
+ outtok = Outbrace;
+ break;
+ case Inpar:
+ outtok = Outpar;
+ break;
+ case Inparmath:
+ outtok = Outparmath;
+ break;
+ default:
+ /* "Can't Happen" (TM) */
+ DPUTS(1, "Nested substitution: This Can't Happen (TM)");
+ return NULL;
+ }
+ skipparens(*s, outtok, &s);
sav = *s;
*s = 0;
/*
@@ -2811,7 +2852,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags)
haserr = parse_subst_string(s);
noerrs = one;
if (!quoteerr) {
- errflag = oef;
+ /* Retain user interrupt error status */
+ errflag = oef | (errflag & ERRFLAG_INT);
if (haserr)
shtokenize(s);
} else if (haserr || errflag) {
@@ -3238,8 +3280,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags)
haserr = 1;
}
noerrs = one;
- if (!quoteerr)
- errflag = oef;
+ if (!quoteerr) {
+ /* Retain user interrupt error status */
+ errflag = oef | (errflag & ERRFLAG_INT);
+ }
if (haserr || errflag)
return NULL;
}
@@ -3472,8 +3516,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags)
untokenize(*ap);
}
noerrs = one;
- if (!quoteerr)
- errflag = oef;
+ if (!quoteerr) {
+ /* Retain any user interrupt error status */
+ errflag = oef | (errflag & ERRFLAG_INT);
+ }
else if (haserr || errflag) {
zerr("parse error in parameter value");
return NULL;
@@ -3505,8 +3551,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags)
noerrs = 1;
haserr = parse_subst_string(val);
noerrs = one;
- if (!quoteerr)
- errflag = oef;
+ if (!quoteerr) {
+ /* Retain any user interrupt error status */
+ errflag = oef | (errflag & ERRFLAG_INT);
+ }
else if (haserr || errflag) {
zerr("parse error in parameter value");
return NULL;
@@ -4075,7 +4123,8 @@ modify(char **str, char **ptr)
noerrs = 1;
parse_subst_string(copy);
noerrs = one;
- errflag = oef;
+ /* Retain any user interrupt error status */
+ errflag = oef | (errflag & ERRFLAG_INT);
remnulargs(copy);
untokenize(copy);
}
@@ -4150,7 +4199,8 @@ modify(char **str, char **ptr)
noerrs = 1;
parse_subst_string(*str);
noerrs = one;
- errflag = oef;
+ /* Retain any user interrupt error status */
+ errflag = oef | (errflag & ERRFLAG_INT);
remnulargs(*str);
untokenize(*str);
}
diff --git a/Src/text.c b/Src/text.c
index f55553ed0..b58c2516d 100644
--- a/Src/text.c
+++ b/Src/text.c
@@ -173,6 +173,8 @@ getpermtext(Eprog prog, Wordcode c, int start_indent)
{
struct estate s;
+ queue_signals();
+
if (!c)
c = prog->prog;
@@ -193,6 +195,9 @@ getpermtext(Eprog prog, Wordcode c, int start_indent)
*tptr = '\0';
freeeprog(prog); /* mark as unused */
untokenize(tbuf);
+
+ unqueue_signals();
+
return tbuf;
}
@@ -206,6 +211,8 @@ getjobtext(Eprog prog, Wordcode c)
struct estate s;
+ queue_signals();
+
if (!c)
c = prog->prog;
@@ -224,6 +231,9 @@ getjobtext(Eprog prog, Wordcode c)
*tptr = '\0';
freeeprog(prog); /* mark as unused */
untokenize(jbuf);
+
+ unqueue_signals();
+
return jbuf;
}
@@ -834,6 +844,10 @@ gettext2(Estate state)
taddstr("))");
stack = 1;
break;
+ case WC_AUTOFN:
+ taddstr("builtin autoload -X");
+ stack = 1;
+ break;
case WC_TRY:
if (!s) {
taddstr("{");
@@ -879,6 +893,9 @@ getredirs(LinkList redirs)
">", ">|", ">>", ">>|", "&>", "&>|", "&>>", "&>>|", "<>", "<",
"<<", "<<-", "<<<", "<&", ">&", NULL /* >&- */, "<", ">"
};
+
+ queue_signals();
+
taddchr(' ');
for (n = firstnode(redirs); n; incnode(n)) {
Redir f = (Redir) getdata(n);
@@ -966,4 +983,6 @@ getredirs(LinkList redirs)
}
}
tptr--;
+
+ unqueue_signals();
}
diff --git a/Src/utils.c b/Src/utils.c
index e6eb8e6a7..3d12807e2 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -112,7 +112,20 @@ set_widearray(char *mb_array, Widechar_array wca)
#endif
-/* Print an error */
+/* Print an error
+
+ The following functions use the following printf-like format codes
+ (implemented by zerrmsg()):
+
+ Code Argument types Prints
+ %s const char * C string (null terminated)
+ %l const char *, int C string of given length (null not required)
+ %L long decimal value
+ %d int decimal value
+ %% (none) literal '%'
+ %c int character at that codepoint
+ %e int strerror() message (argument is typically 'errno')
+ */
static void
zwarning(const char *cmd, const char *fmt, va_list ap)
@@ -153,7 +166,7 @@ VA_DCL
if (errflag || noerrs) {
if (noerrs < 2)
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
return;
}
@@ -161,7 +174,7 @@ VA_DCL
VA_GET_ARG(ap, fmt, const char *);
zwarning(NULL, fmt, ap);
va_end(ap);
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
}
/**/
@@ -181,7 +194,7 @@ VA_DCL
VA_GET_ARG(ap, fmt, const char *);
zwarning(cmd, fmt, ap);
va_end(ap);
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
}
/**/
@@ -330,7 +343,7 @@ zerrmsg(FILE *file, const char *fmt, va_list ap)
num = va_arg(ap, int);
if (num == EINTR) {
fputs("interrupt\n", file);
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
return;
}
errmsg = strerror(num);
@@ -343,6 +356,7 @@ zerrmsg(FILE *file, const char *fmt, va_list ap)
fputs(errmsg + 1, file);
}
break;
+ /* When adding format codes, update the comment above zwarning(). */
}
} else {
putc(*fmt == Meta ? *++fmt ^ 32 : *fmt, file);
@@ -400,7 +414,7 @@ nicechar(int c)
static char buf[6];
char *s = buf;
c &= 0xff;
- if (isprint(c))
+ if (ISPRINT(c))
goto done;
if (c & 0x80) {
if (isset(PRINTEIGHTBIT))
@@ -409,7 +423,7 @@ nicechar(int c)
*s++ = 'M';
*s++ = '-';
c &= 0x7f;
- if(isprint(c))
+ if(ISPRINT(c))
goto done;
}
if (c == 0x7f) {
@@ -566,7 +580,7 @@ wcs_nicechar(wchar_t c, size_t *widthp, char **swidep)
return buf;
}
if (swidep)
- *swidep = buf + *widthp;
+ *swidep = widthp ? buf + *widthp : buf;
return buf;
}
@@ -693,12 +707,12 @@ slashsplit(char *s)
int t0;
if (!*s)
- return (char **) zshcalloc(sizeof(char **));
+ return (char **) zshcalloc(sizeof(char *));
for (t = s, t0 = 0; *t; t++)
if (*t == '/')
t0++;
- q = r = (char **) zalloc(sizeof(char **) * (t0 + 2));
+ q = r = (char **) zalloc(sizeof(char *) * (t0 + 2));
while ((t = strchr(s, '/'))) {
*q++ = ztrduppfx(s, t - s);
@@ -719,7 +733,7 @@ slashsplit(char *s)
/**/
static int
-xsymlinks(char *s)
+xsymlinks(char *s, int full)
{
char **pp, **opp;
char xbuf2[PATH_MAX*3], xbuf3[PATH_MAX*2];
@@ -727,7 +741,7 @@ xsymlinks(char *s)
zulong xbuflen = strlen(xbuf);
opp = pp = slashsplit(s);
- for (; xbuflen < sizeof(xbuf) && *pp; pp++) {
+ for (; xbuflen < sizeof(xbuf) && *pp && ret >= 0; pp++) {
if (!strcmp(*pp, "."))
continue;
if (!strcmp(*pp, "..")) {
@@ -741,6 +755,8 @@ xsymlinks(char *s)
while (*--p != '/')
xbuflen--;
*p = '\0';
+ /* The \0 isn't included in the length */
+ xbuflen--;
continue;
}
sprintf(xbuf2, "%s/%s", xbuf, *pp);
@@ -758,13 +774,52 @@ xsymlinks(char *s)
} else {
ret = 1;
metafy(xbuf3, t0, META_NOALLOC);
+ if (!full) {
+ /*
+ * If only one expansion requested, ensure the
+ * full path is in xbuf.
+ */
+ zulong len = xbuflen;
+ if (*xbuf3 == '/')
+ strcpy(xbuf, xbuf3);
+ else if ((len += strlen(xbuf3) + 1) < sizeof(xbuf)) {
+ strcpy(xbuf + xbuflen, "/");
+ strcpy(xbuf + xbuflen + 1, xbuf3);
+ } else {
+ *xbuf = 0;
+ ret = -1;
+ break;
+ }
+
+ while (*++pp) {
+ zulong newlen = len + strlen(*pp) + 1;
+ if (newlen < sizeof(xbuf)) {
+ strcpy(xbuf + len, "/");
+ strcpy(xbuf + len + 1, *pp);
+ len = newlen;
+ } else {
+ *xbuf = 01;
+ ret = -1;
+ break;
+ }
+ }
+ /*
+ * No need to update xbuflen, we're finished
+ * the expansion (for now).
+ */
+ break;
+ }
if (*xbuf3 == '/') {
strcpy(xbuf, "");
- if (xsymlinks(xbuf3 + 1) < 0)
+ if (xsymlinks(xbuf3 + 1, 1) < 0)
ret = -1;
+ else
+ xbuflen = strlen(xbuf);
} else
- if (xsymlinks(xbuf3) < 0)
+ if (xsymlinks(xbuf3, 1) < 0)
ret = -1;
+ else
+ xbuflen = strlen(xbuf);
}
}
freearray(opp);
@@ -783,7 +838,7 @@ xsymlink(char *s)
if (*s != '/')
return NULL;
*xbuf = '\0';
- if (xsymlinks(s + 1) < 0)
+ if (xsymlinks(s + 1, 1) < 0)
zwarn("path expansion failed, using root directory");
if (!*xbuf)
return ztrdup("/");
@@ -792,12 +847,30 @@ xsymlink(char *s)
/**/
void
-print_if_link(char *s)
+print_if_link(char *s, int all)
{
if (*s == '/') {
*xbuf = '\0';
- if (xsymlinks(s + 1) > 0)
- printf(" -> "), zputs(*xbuf ? xbuf : "/", stdout);
+ if (all) {
+ char *start = s + 1;
+ char xbuflink[PATH_MAX];
+ for (;;) {
+ if (xsymlinks(start, 0) > 0) {
+ printf(" -> ");
+ zputs(*xbuf ? xbuf : "/", stdout);
+ if (!*xbuf)
+ break;
+ strcpy(xbuflink, xbuf);
+ start = xbuflink + 1;
+ *xbuf = '\0';
+ } else {
+ break;
+ }
+ }
+ } else {
+ if (xsymlinks(s + 1, 1) > 0)
+ printf(" -> "), zputs(*xbuf ? xbuf : "/", stdout);
+ }
}
}
@@ -1049,10 +1122,13 @@ getnameddir(char *name)
if ((pw = getpwnam(name))) {
char *dir = isset(CHASELINKS) ? xsymlink(pw->pw_dir)
: ztrdup(pw->pw_dir);
- adduserdir(name, dir, ND_USERNAME, 1);
- str = dupstring(dir);
- zsfree(dir);
- return str;
+ if (dir) {
+ adduserdir(name, dir, ND_USERNAME, 1);
+ str = dupstring(dir);
+ zsfree(dir);
+ return str;
+ } else
+ return dupstring(pw->pw_dir);
}
}
#endif /* HAVE_GETPWNAM */
@@ -1456,7 +1532,7 @@ checkmailpath(char **s)
setunderscore(*s);
u = dupstring(u);
- if (! parsestr(u)) {
+ if (!parsestr(&u)) {
singsub(&u);
zputs(u, shout);
fputc('\n', shout);
@@ -2078,6 +2154,8 @@ zstrtol_underscore(const char *s, char **t, int base, int underscore)
base = 10;
else if (*++s == 'x' || *s == 'X')
base = 16, s++;
+ else if (*s == 'b' || *s == 'B')
+ base = 2, s++;
else
base = 8;
}
@@ -2197,6 +2275,10 @@ setblock_stdin(void)
* Note that apart from setting (and restoring) non-blocking input,
* this function does not change the input mode. The calling function
* should have set cbreak mode if necessary.
+ *
+ * fd may be -1 to sleep until the timeout in microseconds. This is a
+ * fallback for old systems that don't have nanosleep(). Some very old
+ * systems might not have select: get with it, daddy-o.
*/
/**/
@@ -2218,6 +2300,8 @@ read_poll(int fd, int *readchar, int polltty, zlong microseconds)
struct ttyinfo ti;
#endif
+ if (fd < 0 || (polltty && !isatty(fd)))
+ polltty = 0; /* no tty to poll */
#if defined(HAS_TIO) && !defined(__CYGWIN__)
/*
@@ -2239,7 +2323,7 @@ read_poll(int fd, int *readchar, int polltty, zlong microseconds)
* as plausible as it sounds, but it seems the right way to guess.
* pws 2000/06/26
*/
- if (polltty) {
+ if (polltty && fd >= 0) {
gettyinfo(&ti);
if ((polltty = ti.tio.c_cc[VMIN])) {
ti.tio.c_cc[VMIN] = 0;
@@ -2255,16 +2339,24 @@ read_poll(int fd, int *readchar, int polltty, zlong microseconds)
expire_tv.tv_sec = (int) (microseconds / (zlong)1000000);
expire_tv.tv_usec = microseconds % (zlong)1000000;
FD_ZERO(&foofd);
- FD_SET(fd, &foofd);
- ret = select(fd+1, (SELECT_ARG_2_T) &foofd, NULL, NULL, &expire_tv);
+ if (fd > -1) {
+ FD_SET(fd, &foofd);
+ ret = select(fd+1, (SELECT_ARG_2_T) &foofd, NULL, NULL, &expire_tv);
+ } else
+ ret = select(0, NULL, NULL, NULL, &expire_tv);
#else
+ if (fd < 0) {
+ /* OK, can't do that. Just quietly sleep for a second. */
+ sleep(1);
+ return 1;
+ }
#ifdef FIONREAD
if (ioctl(fd, FIONREAD, (char *) &val) == 0)
ret = (val > 0);
#endif
#endif
- if (ret < 0) {
+ if (fd >= 0 && ret < 0) {
/*
* Final attempt: set non-blocking read and try to read a character.
* Praise Bill, this works under Cygwin (nothing else seems to).
@@ -2286,6 +2378,80 @@ read_poll(int fd, int *readchar, int polltty, zlong microseconds)
return (ret > 0);
}
+/*
+ * Sleep for the given number of microseconds --- must be within
+ * range of a long at the moment, but this is only used for
+ * limited internal purposes.
+ */
+
+/**/
+int
+zsleep(long us)
+{
+#ifdef HAVE_NANOSLEEP
+ struct timespec sleeptime;
+
+ sleeptime.tv_sec = (time_t)us / (time_t)1000000;
+ sleeptime.tv_nsec = (us % 1000000L) * 1000L;
+ for (;;) {
+ struct timespec rem;
+ int ret = nanosleep(&sleeptime, &rem);
+
+ if (ret == 0)
+ return 1;
+ else if (errno != EINTR)
+ return 0;
+ sleeptime = rem;
+ }
+#else
+ int dummy;
+ return read_poll(-1, &dummy, 0, us);
+#endif
+}
+
+/**
+ * Sleep for time (fairly) randomly up to max_us microseconds.
+ * Don't let the wallclock time extend beyond end_time.
+ * Return 1 if that seemed to work, else 0.
+ *
+ * For best results max_us should be a multiple of 2**16 or large
+ * enough that it doesn't matter.
+ */
+
+/**/
+int
+zsleep_random(long max_us, time_t end_time)
+{
+ long r;
+ time_t now = time(NULL);
+
+ /*
+ * Randomish backoff. Doesn't need to be fundamentally
+ * unpredictable, just probably unlike the value another
+ * exiting shell is using. On some systems the bottom 16
+ * bits aren't that random but the use here doesn't
+ * really care.
+ */
+ r = (long)(rand() & 0xFFFF);
+ /*
+ * Turn this into a fraction of sleep_us. Again, this
+ * doesn't need to be particularly accurate and the base time
+ * is sufficient that we can do the division first and not
+ * worry about the range.
+ */
+ r = (max_us >> 16) * r;
+ /*
+ * Don't sleep beyond timeout.
+ * Not that important as timeout is ridiculously long, but
+ * if there's an interface, interface to it...
+ */
+ while (r && now + (time_t)(r / 1000000) > end_time)
+ r >>= 1;
+ if (r) /* pedantry */
+ return zsleep(r);
+ return 0;
+}
+
/**/
int
checkrmall(char *s)
@@ -2294,7 +2460,7 @@ checkrmall(char *s)
return 1;
fprintf(shout, "zsh: sure you want to delete all the files in ");
if (*s != '/') {
- nicezputs(pwd[1] ? unmeta(pwd) : "", shout);
+ nicezputs(pwd[1] ? pwd : "", shout);
fputc('/', shout);
}
nicezputs(s, shout);
@@ -2949,7 +3115,7 @@ colonsplit(char *s, int uniq)
for (t = s, ct = 0; *t; t++) /* count number of colons */
if (*t == ':')
ct++;
- ptr = ret = (char **) zalloc(sizeof(char **) * (ct + 2));
+ ptr = ret = (char **) zalloc(sizeof(char *) * (ct + 2));
t = s;
do {
@@ -3482,7 +3648,7 @@ inittyptab(void)
for (t0 = (int)STOUC(Snull); t0 <= (int)STOUC(Nularg); t0++)
typtab[t0] |= ITOK | IMETA | INULL;
for (s = ifs ? ifs : EMULATION(EMULATE_KSH|EMULATE_SH) ?
- ztrdup(DEFAULT_IFS_SH) : ztrdup(DEFAULT_IFS); *s; s++) {
+ DEFAULT_IFS_SH : DEFAULT_IFS; *s; s++) {
int c = STOUC(*s == Meta ? *++s ^ 32 : *s);
#ifdef MULTIBYTE_SUPPORT
if (!isascii(c)) {
@@ -3517,7 +3683,7 @@ inittyptab(void)
#ifdef MULTIBYTE_SUPPORT
set_widearray(wordchars, &wordchars_wide);
set_widearray(ifs ? ifs : EMULATION(EMULATE_KSH|EMULATE_SH) ?
- ztrdup(DEFAULT_IFS_SH) : ztrdup(DEFAULT_IFS), &ifs_wide);
+ DEFAULT_IFS_SH : DEFAULT_IFS, &ifs_wide);
#endif
for (s = SPECCHARS; *s; s++)
typtab[STOUC(*s)] |= ISPECIAL;
@@ -4158,7 +4324,7 @@ unmetafy(char *s, int *len)
for (p = s; *p && *p != Meta; p++);
for (t = p; (*t = *p++);)
- if (*t++ == Meta)
+ if (*t++ == Meta && *p)
t[-1] = *p++ ^ 32;
if (len)
*len = t - s;
@@ -4242,7 +4408,7 @@ unmeta(const char *file_name)
}
for (t = file_name, p = fn; *t; p++)
- if ((*p = *t++) == Meta)
+ if ((*p = *t++) == Meta && *t)
*p = *t++ ^ 32;
*p = '\0';
return fn;
@@ -4631,6 +4797,14 @@ mb_metacharlenconv_r(const char *s, wint_t *wcp, mbstate_t *mbsp)
inchar = *++ptr ^ 32;
DPUTS(!*ptr,
"BUG: unexpected end of string in mb_metacharlen()\n");
+ } else if (imeta(*ptr)) {
+ /*
+ * As this is metafied input, this is a token --- this
+ * can't be a part of the string. It might be
+ * something on the end of an unbracketed parameter
+ * reference, for example.
+ */
+ break;
} else
inchar = *ptr;
ptr++;
diff --git a/Src/watch.c b/Src/watch.c
index 8dea0b495..fe409f91a 100644
--- a/Src/watch.c
+++ b/Src/watch.c
@@ -372,6 +372,27 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
return fmt;
}
+/* See if the watch entry matches */
+
+static int
+watchlog_match(char *teststr, char *actual, int len)
+{
+ int ret = 0;
+ Patprog pprog;
+ char *str = dupstring(teststr);
+
+ tokenize(str);
+
+ if ((pprog = patcompile(str, PAT_STATIC, 0))) {
+ queue_signals();
+ if (pattry(pprog, actual))
+ ret = 1;
+ unqueue_signals();
+ } else if (!strncmp(actual, teststr, len))
+ ret = 1;
+ return ret;
+}
+
/* check the List for login/logouts */
/**/
@@ -400,7 +421,7 @@ watchlog(int inout, WATCH_STRUCT_UTMP *u, char **w, char *fmt)
for (vv = v; *vv && *vv != '@' && *vv != '%'; vv++);
sav = *vv;
*vv = '\0';
- if (strncmp(u->ut_name, v, sizeof(u->ut_name)))
+ if (!watchlog_match(v, u->ut_name, sizeof(u->ut_name)))
bad = 1;
*vv = sav;
v = vv;
@@ -410,7 +431,7 @@ watchlog(int inout, WATCH_STRUCT_UTMP *u, char **w, char *fmt)
for (vv = ++v; *vv && *vv != '@'; vv++);
sav = *vv;
*vv = '\0';
- if (strncmp(u->ut_line, v, sizeof(u->ut_line)))
+ if (!watchlog_match(v, u->ut_line, sizeof(u->ut_line)))
bad = 1;
*vv = sav;
v = vv;
@@ -420,7 +441,7 @@ watchlog(int inout, WATCH_STRUCT_UTMP *u, char **w, char *fmt)
for (vv = ++v; *vv && *vv != '%'; vv++);
sav = *vv;
*vv = '\0';
- if (strncmp(u->ut_host, v, strlen(v)))
+ if (!watchlog_match(v, u->ut_host, strlen(v)))
bad = 1;
*vv = sav;
v = vv;
diff --git a/Src/zsh.h b/Src/zsh.h
index d284c7aa7..486ad800a 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -163,40 +163,42 @@ struct mathfunc {
#define Hat ((char) 0x86)
#define Star ((char) 0x87)
#define Inpar ((char) 0x88)
-#define Outpar ((char) 0x89)
-#define Qstring ((char) 0x8a)
-#define Equals ((char) 0x8b)
-#define Bar ((char) 0x8c)
-#define Inbrace ((char) 0x8d)
-#define Outbrace ((char) 0x8e)
-#define Inbrack ((char) 0x8f)
-#define Outbrack ((char) 0x90)
-#define Tick ((char) 0x91)
-#define Inang ((char) 0x92)
-#define Outang ((char) 0x93)
-#define OutangProc ((char) 0x94)
-#define Quest ((char) 0x95)
-#define Tilde ((char) 0x96)
-#define Qtick ((char) 0x97)
-#define Comma ((char) 0x98)
+#define Inparmath ((char) 0x89)
+#define Outpar ((char) 0x8a)
+#define Outparmath ((char) 0x8b)
+#define Qstring ((char) 0x8c)
+#define Equals ((char) 0x8d)
+#define Bar ((char) 0x8e)
+#define Inbrace ((char) 0x8f)
+#define Outbrace ((char) 0x90)
+#define Inbrack ((char) 0x91)
+#define Outbrack ((char) 0x92)
+#define Tick ((char) 0x93)
+#define Inang ((char) 0x94)
+#define Outang ((char) 0x95)
+#define OutangProc ((char) 0x96)
+#define Quest ((char) 0x97)
+#define Tilde ((char) 0x98)
+#define Qtick ((char) 0x99)
+#define Comma ((char) 0x9a)
/*
* Null arguments: placeholders for single and double quotes
* and backslashes.
*/
-#define Snull ((char) 0x99)
-#define Dnull ((char) 0x9a)
-#define Bnull ((char) 0x9b)
+#define Snull ((char) 0x9b)
+#define Dnull ((char) 0x9c)
+#define Bnull ((char) 0x9d)
/*
* Backslash which will be returned to "\" instead of being stripped
* when we turn the string into a printable format.
*/
-#define Bnullkeep ((char) 0x9c)
+#define Bnullkeep ((char) 0x9e)
/*
* Null argument that does not correspond to any character.
* This should be last as it does not appear in ztokens and
* is used to initialise the IMETA type in inittyptab().
*/
-#define Nularg ((char) 0x9d)
+#define Nularg ((char) 0x9f)
/*
* Take care to update the use of IMETA appropriately when adding
@@ -409,7 +411,9 @@ enum {
#define INP_HIST (1<<2) /* expanding history */
#define INP_CONT (1<<3) /* continue onto previously stacked input */
#define INP_ALCONT (1<<4) /* stack is continued from alias expn. */
-#define INP_LINENO (1<<5) /* update line number */
+#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 */
/* Flags for metafy */
#define META_REALLOC 0
@@ -421,6 +425,15 @@ enum {
#define META_HEAPDUP 6
#define META_HREALLOC 7
+/* Context to save and restore (bit fields) */
+enum {
+ /* History mechanism */
+ ZCONTEXT_HIST = (1<<0),
+ /* Lexical analyser */
+ ZCONTEXT_LEX = (1<<1),
+ /* Parser */
+ ZCONTEXT_PARSE = (1<<2)
+};
/**************************/
/* Abstract types for zsh */
@@ -1865,9 +1878,9 @@ typedef groupset *Groupset;
#define PRINT_TYPESET (1<<5)
/* flags for printing for the whence builtin */
-#define PRINT_WHENCE_CSH (1<<5)
-#define PRINT_WHENCE_VERBOSE (1<<6)
-#define PRINT_WHENCE_SIMPLE (1<<7)
+#define PRINT_WHENCE_CSH (1<<6)
+#define PRINT_WHENCE_VERBOSE (1<<7)
+#define PRINT_WHENCE_SIMPLE (1<<8)
#define PRINT_WHENCE_FUNCDEF (1<<9)
#define PRINT_WHENCE_WORD (1<<10)
@@ -2623,6 +2636,20 @@ enum trap_state {
#define IN_EVAL_TRAP() \
(intrap && !trapisfunc && traplocallevel == locallevel)
+/*
+ * Bits in the errflag variable.
+ */
+enum errflag_bits {
+ /*
+ * Standard internal error bit.
+ */
+ ERRFLAG_ERROR = 1,
+ /*
+ * User interrupt.
+ */
+ ERRFLAG_INT = 2
+};
+
/***********/
/* Sorting */
/***********/
@@ -2668,6 +2695,87 @@ struct sortelt {
typedef struct sortelt *SortElt;
+/*********************************************************/
+/* Structures to save and restore for individual modules */
+/*********************************************************/
+
+/* History */
+struct hist_stack {
+ int histactive;
+ int histdone;
+ int stophist;
+ int hlinesz;
+ char *hline;
+ char *hptr;
+ short *chwords;
+ int chwordlen;
+ int chwordpos;
+ int (*hgetc) _((void));
+ void (*hungetc) _((int));
+ void (*hwaddc) _((int));
+ void (*hwbegin) _((int));
+ void (*hwend) _((void));
+ void (*addtoline) _((int));
+ unsigned char *cstack;
+ int csp;
+};
+
+/*
+ * State of a lexical token buffer.
+ *
+ * It would be neater to include the pointer to the start of the buffer,
+ * however the current code structure means that the standard instance
+ * of this, tokstr, is visible in lots of places, so that's not
+ * convenient.
+ */
+
+struct lexbufstate {
+ /*
+ * Next character to be added.
+ * Set to NULL when the buffer is to be visible from elsewhere.
+ */
+ char *ptr;
+ /* Allocated buffer size */
+ int siz;
+ /* Length in use */
+ int len;
+};
+
+/* Lexical analyser */
+struct lex_stack {
+ int dbparens;
+ int isfirstln;
+ int isfirstch;
+ int lexflags;
+ enum lextok tok;
+ char *tokstr;
+ char *zshlextext;
+ struct lexbufstate lexbuf;
+ int lex_add_raw;
+ char *tokstr_raw;
+ struct lexbufstate lexbuf_raw;
+ int lexstop;
+ zlong toklineno;
+};
+
+/* Parser */
+struct parse_stack {
+ struct heredocs *hdocs;
+
+ int incmdpos;
+ int aliasspaceflag;
+ int incond;
+ int inredir;
+ int incasepat;
+ int isnewlin;
+ int infor;
+
+ int eclen, ecused, ecnpats;
+ Wordcode ecbuf;
+ Eccstr ecstrs;
+ int ecsoffs, ecssub, ecnfunc;
+};
+
/************************/
/* Flags to casemodifiy */
/************************/
diff --git a/Src/zsh.mdd b/Src/zsh.mdd
index 9a8c923f9..c2e59c910 100644
--- a/Src/zsh.mdd
+++ b/Src/zsh.mdd
@@ -9,7 +9,8 @@ alwayslink=1
# autobins not specified because of alwayslink
-objects="builtin.o compat.o cond.o exec.o glob.o hashtable.o hashnameddir.o \
+objects="builtin.o compat.o cond.o context.o \
+exec.o glob.o hashtable.o hashnameddir.o \
hist.o init.o input.o jobs.o lex.o linklist.o loop.o math.o \
mem.o module.o options.o params.o parse.o pattern.o prompt.o signals.o \
signames.o sort.o string.o subst.o text.o utils.o watch.o"
@@ -21,9 +22,18 @@ hdrdeps="zshcurses.h zshterm.h"
:<<\Make
@CONFIG_MK@
+# If we're using gcc as the preprocessor, get rid of the additional
+# lines generated by the preprocessor as they can confuse the script.
+# We don't need these in other cases either, but can't necessarily rely
+# on the option to remove them being the same.
signames.c: signames1.awk signames2.awk ../config.h @SIGNAL_H@
$(AWK) -f $(sdir)/signames1.awk @SIGNAL_H@ >sigtmp.c
- $(CPP) sigtmp.c >sigtmp.out
+ case "`$(CPP) --version </dev/null 2>&1`" in \
+ *"Free Software Foundation"*) \
+ $(CPP) -P sigtmp.c >sigtmp.out;; \
+ *) \
+ $(CPP) sigtmp.c >sigtmp.out;; \
+ esac
$(AWK) -f $(sdir)/signames2.awk sigtmp.out > $@
rm -f sigtmp.c sigtmp.out
diff --git a/Src/ztype.h b/Src/ztype.h
index eef0f23db..d1bef0a5a 100644
--- a/Src/ztype.h
+++ b/Src/ztype.h
@@ -75,3 +75,9 @@
#define WC_ZISTYPE(X,Y) zistype((X),(Y))
#define WC_ISPRINT(X) isprint(X)
#endif
+
+#if defined(__APPLE__) && defined(BROKEN_ISPRINT)
+#define ISPRINT(c) isprint_ascii(c)
+#else
+#define ISPRINT(c) isprint(c)
+#endif