summaryrefslogtreecommitdiffstats
path: root/Src/Zle
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>1999-04-15 18:05:35 +0000
committerTanaka Akira <akr@users.sourceforge.net>1999-04-15 18:05:35 +0000
commit32c2ebbaa5d7927f33ee0ecf98472a71cf902cf3 (patch)
tree212c29fe244d0222bab8efe3032e7fa965842945 /Src/Zle
parentInitial revision (diff)
downloadzsh-3.1.5.tar
zsh-3.1.5.tar.gz
zsh-3.1.5.tar.bz2
zsh-3.1.5.tar.lz
zsh-3.1.5.tar.xz
zsh-3.1.5.tar.zst
zsh-3.1.5.zip
zsh-3.1.5zsh-3.1.5
Diffstat (limited to 'Src/Zle')
-rw-r--r--Src/Zle/.cvsignore14
-rw-r--r--Src/Zle/.distfiles10
-rw-r--r--Src/Zle/.exrc2
-rw-r--r--Src/Zle/comp.h137
-rw-r--r--Src/Zle/comp1.c291
-rw-r--r--Src/Zle/comp1.mdd3
-rw-r--r--Src/Zle/compctl.c1085
-rw-r--r--Src/Zle/compctl.mdd5
-rw-r--r--Src/Zle/deltochar.c91
-rw-r--r--Src/Zle/deltochar.mdd3
-rw-r--r--Src/Zle/iwidgets.list172
-rw-r--r--Src/Zle/zle.h137
-rw-r--r--Src/Zle/zle.mdd70
-rw-r--r--Src/Zle/zle_bindings.c421
-rw-r--r--Src/Zle/zle_hist.c1139
-rw-r--r--Src/Zle/zle_keymap.c1238
-rw-r--r--Src/Zle/zle_main.c907
-rw-r--r--Src/Zle/zle_misc.c816
-rw-r--r--Src/Zle/zle_move.c502
-rw-r--r--Src/Zle/zle_params.c196
-rw-r--r--Src/Zle/zle_refresh.c1116
-rw-r--r--Src/Zle/zle_things.sed9
-rw-r--r--Src/Zle/zle_thingy.c491
-rw-r--r--Src/Zle/zle_tricky.c4015
-rw-r--r--Src/Zle/zle_utils.c650
-rw-r--r--Src/Zle/zle_vi.c925
-rw-r--r--Src/Zle/zle_widget.sed7
-rw-r--r--Src/Zle/zle_word.c477
28 files changed, 14929 insertions, 0 deletions
diff --git a/Src/Zle/.cvsignore b/Src/Zle/.cvsignore
new file mode 100644
index 000000000..b10adcc3b
--- /dev/null
+++ b/Src/Zle/.cvsignore
@@ -0,0 +1,14 @@
+Makefile
+Makefile.in
+*.pro
+*.o
+*.o.c
+*.so
+*.mdh
+*.mdhi
+*.mdhs
+*.mdh.tmp
+thingies.list
+widgets.list
+zle_things.h
+zle_widget.h
diff --git a/Src/Zle/.distfiles b/Src/Zle/.distfiles
new file mode 100644
index 000000000..42c62efe9
--- /dev/null
+++ b/Src/Zle/.distfiles
@@ -0,0 +1,10 @@
+DISTFILES_SRC='
+ .cvsignore .distfiles .exrc
+ comp1.mdd comp.h comp1.c
+ compctl.mdd compctl.c
+ deltochar.mdd deltochar.c
+ zle.mdd iwidgets.list zle.h zle_bindings.c zle_hist.c
+ zle_keymap.c zle_main.c zle_misc.c zle_move.c zle_params.c
+ zle_refresh.c zle_things.sed zle_thingy.c zle_tricky.c
+ zle_utils.c zle_vi.c zle_widget.sed zle_word.c
+'
diff --git a/Src/Zle/.exrc b/Src/Zle/.exrc
new file mode 100644
index 000000000..91d0b39ef
--- /dev/null
+++ b/Src/Zle/.exrc
@@ -0,0 +1,2 @@
+set ai
+set sw=4
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
new file mode 100644
index 000000000..5d7ef74e2
--- /dev/null
+++ b/Src/Zle/comp.h
@@ -0,0 +1,137 @@
+/*
+ * comp.h - header file for completion
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * 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 Paul Falstad 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 Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad 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 Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#undef compctlread
+
+typedef struct compctlp *Compctlp;
+typedef struct compctl *Compctl;
+typedef struct compcond *Compcond;
+
+/* node for compctl hash table (compctltab) */
+
+struct compctlp {
+ HashNode next; /* next in hash chain */
+ char *nam; /* command name */
+ int flags; /* CURRENTLY UNUSED */
+ Compctl cc; /* pointer to the compctl desc. */
+};
+
+/* compctl -x condition */
+
+struct compcond {
+ Compcond and, or; /* the next or'ed/and'ed conditions */
+ int type; /* the type (CCT_*) */
+ int n; /* the array length */
+ union { /* these structs hold the data used to */
+ struct { /* test this condition */
+ int *a, *b; /* CCT_POS, CCT_NUMWORDS */
+ }
+ r;
+ struct { /* CCT_CURSTR, CCT_CURPAT,... */
+ int *p;
+ char **s;
+ }
+ s;
+ struct { /* CCT_RANGESTR,... */
+ char **a, **b;
+ }
+ l;
+ }
+ u;
+};
+
+#define CCT_UNUSED 0
+#define CCT_POS 1
+#define CCT_CURSTR 2
+#define CCT_CURPAT 3
+#define CCT_WORDSTR 4
+#define CCT_WORDPAT 5
+#define CCT_CURSUF 6
+#define CCT_CURPRE 7
+#define CCT_CURSUB 8
+#define CCT_CURSUBC 9
+#define CCT_NUMWORDS 10
+#define CCT_RANGESTR 11
+#define CCT_RANGEPAT 12
+
+/* Contains the real description for compctls */
+
+struct compctl {
+ int refc; /* reference count */
+ Compctl next; /* next compctl for -x */
+ unsigned long mask; /* mask of things to complete (CC_*) */
+ char *keyvar; /* for -k (variable) */
+ char *glob; /* for -g (globbing) */
+ char *str; /* for -s (expansion) */
+ char *func; /* for -K (function) */
+ char *explain; /* for -X (explanation) */
+ char *ylist; /* for -y (user-defined desc. for listing) */
+ char *prefix, *suffix; /* for -P and -S (prefix, suffix) */
+ char *subcmd; /* for -l (command name to use) */
+ char *withd; /* for -w (with directory */
+ char *hpat; /* for -H (history pattern) */
+ int hnum; /* for -H (number of events to search) */
+ Compctl ext; /* for -x (first of the compctls after -x) */
+ Compcond cond; /* for -x (condition for this compctl) */
+ Compctl xor; /* for + (next of the xor'ed compctls) */
+};
+
+/* objects to complete */
+#define CC_FILES (1<<0)
+#define CC_COMMPATH (1<<1)
+#define CC_REMOVE (1<<2)
+#define CC_OPTIONS (1<<3)
+#define CC_VARS (1<<4)
+#define CC_BINDINGS (1<<5)
+#define CC_ARRAYS (1<<6)
+#define CC_INTVARS (1<<7)
+#define CC_SHFUNCS (1<<8)
+#define CC_PARAMS (1<<9)
+#define CC_ENVVARS (1<<10)
+#define CC_JOBS (1<<11)
+#define CC_RUNNING (1<<12)
+#define CC_STOPPED (1<<13)
+#define CC_BUILTINS (1<<14)
+#define CC_ALREG (1<<15)
+#define CC_ALGLOB (1<<16)
+#define CC_USERS (1<<17)
+#define CC_DISCMDS (1<<18)
+#define CC_EXCMDS (1<<19)
+#define CC_SCALARS (1<<20)
+#define CC_READONLYS (1<<21)
+#define CC_SPECIALS (1<<22)
+#define CC_DELETE (1<<23)
+#define CC_NAMED (1<<24)
+#define CC_QUOTEFLAG (1<<25)
+#define CC_EXTCMDS (1<<26)
+#define CC_RESWDS (1<<27)
+#define CC_DIRS (1<<28)
+
+#define CC_EXPANDEXPL (1<<30)
+#define CC_RESERVED (1<<31)
diff --git a/Src/Zle/comp1.c b/Src/Zle/comp1.c
new file mode 100644
index 000000000..acd1288a6
--- /dev/null
+++ b/Src/Zle/comp1.c
@@ -0,0 +1,291 @@
+/*
+ * comp1.c - base of the completion system
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * 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 Paul Falstad 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 Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad 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 Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "comp1.mdh"
+
+#include "comp1.pro"
+
+/* default completion infos */
+
+/**/
+struct compctl cc_compos, cc_default, cc_first, cc_dummy;
+
+/* hash table for completion info for commands */
+
+/**/
+HashTable compctltab;
+
+/* Words on the command line, for use in completion */
+
+/**/
+int clwsize, clwnum, clwpos;
+/**/
+char **clwords;
+
+/* != 0 if in a shell function called from completion, such that read -[cl] *
+ * will work (i.e., the line is metafied, and the above word arrays are OK). */
+
+/**/
+int incompctlfunc;
+
+/**/
+static void
+createcompctltable(void)
+{
+ compctltab = newhashtable(23, "compctltab", NULL);
+
+ compctltab->hash = hasher;
+ compctltab->emptytable = emptyhashtable;
+ compctltab->filltable = NULL;
+ compctltab->addnode = addhashnode;
+ compctltab->getnode = gethashnode2;
+ compctltab->getnode2 = gethashnode2;
+ compctltab->removenode = removehashnode;
+ compctltab->disablenode = NULL;
+ compctltab->enablenode = NULL;
+ compctltab->freenode = freecompctlp;
+ compctltab->printnode = NULL;
+}
+
+/**/
+static void
+freecompctlp(HashNode hn)
+{
+ Compctlp ccp = (Compctlp) hn;
+
+ zsfree(ccp->nam);
+ freecompctl(ccp->cc);
+ zfree(ccp, sizeof(struct compctlp));
+}
+
+/**/
+void
+freecompctl(Compctl cc)
+{
+ if (cc == &cc_default ||
+ cc == &cc_first ||
+ cc == &cc_compos ||
+ --cc->refc > 0)
+ return;
+
+ zsfree(cc->keyvar);
+ zsfree(cc->glob);
+ zsfree(cc->str);
+ zsfree(cc->func);
+ zsfree(cc->explain);
+ zsfree(cc->ylist);
+ zsfree(cc->prefix);
+ zsfree(cc->suffix);
+ zsfree(cc->hpat);
+ zsfree(cc->subcmd);
+ if (cc->cond)
+ freecompcond(cc->cond);
+ if (cc->ext) {
+ Compctl n, m;
+
+ n = cc->ext;
+ do {
+ m = (Compctl) (n->next);
+ freecompctl(n);
+ n = m;
+ }
+ while (n);
+ }
+ if (cc->xor && cc->xor != &cc_default)
+ freecompctl(cc->xor);
+ zfree(cc, sizeof(struct compctl));
+}
+
+/**/
+void
+freecompcond(void *a)
+{
+ Compcond cc = (Compcond) a;
+ Compcond and, or, c;
+ int n;
+
+ for (c = cc; c; c = or) {
+ or = c->or;
+ for (; c; c = and) {
+ and = c->and;
+ if (c->type == CCT_POS ||
+ c->type == CCT_NUMWORDS) {
+ free(c->u.r.a);
+ free(c->u.r.b);
+ } else if (c->type == CCT_CURSUF ||
+ c->type == CCT_CURPRE) {
+ for (n = 0; n < c->n; n++)
+ if (c->u.s.s[n])
+ zsfree(c->u.s.s[n]);
+ free(c->u.s.s);
+ } else if (c->type == CCT_RANGESTR ||
+ c->type == CCT_RANGEPAT) {
+ for (n = 0; n < c->n; n++)
+ if (c->u.l.a[n])
+ zsfree(c->u.l.a[n]);
+ free(c->u.l.a);
+ for (n = 0; n < c->n; n++)
+ if (c->u.l.b[n])
+ zsfree(c->u.l.b[n]);
+ free(c->u.l.b);
+ } else {
+ for (n = 0; n < c->n; n++)
+ if (c->u.s.s[n])
+ zsfree(c->u.s.s[n]);
+ free(c->u.s.p);
+ free(c->u.s.s);
+ }
+ zfree(c, sizeof(struct compcond));
+ }
+ }
+}
+
+/**/
+int
+compctlread(char *name, char **args, char *ops, char *reply)
+{
+ char *buf, *bptr;
+
+ /* only allowed to be called for completion */
+ if (!incompctlfunc) {
+ zwarnnam(name, "option valid only in functions called for completion",
+ NULL, 0);
+ return 1;
+ }
+
+ if (ops['l']) {
+ /* -ln gives the index of the word the cursor is currently on, which is
+ available in cs (but remember that Zsh counts from one, not zero!) */
+ if (ops['n']) {
+ char nbuf[14];
+
+ if (ops['e'] || ops['E'])
+ printf("%d\n", cs + 1);
+ if (!ops['e']) {
+ sprintf(nbuf, "%d", cs + 1);
+ setsparam(reply, ztrdup(nbuf));
+ }
+ return 0;
+ }
+ /* without -n, the current line is assigned to the given parameter as a
+ scalar */
+ if (ops['e'] || ops['E']) {
+ zputs((char *) line, stdout);
+ putchar('\n');
+ }
+ if (!ops['e'])
+ setsparam(reply, ztrdup((char *) line));
+ } else {
+ int i;
+
+ /* -cn gives the current cursor position within the current word, which
+ is available in clwpos (but remember that Zsh counts from one, not
+ zero!) */
+ if (ops['n']) {
+ char nbuf[14];
+
+ if (ops['e'] || ops['E'])
+ printf("%d\n", clwpos + 1);
+ if (!ops['e']) {
+ sprintf(nbuf, "%d", clwpos + 1);
+ setsparam(reply, ztrdup(nbuf));
+ }
+ return 0;
+ }
+ /* without -n, the words of the current line are assigned to the given
+ parameters separately */
+ if (ops['A'] && !ops['e']) {
+ /* the -A option means that one array is specified, instead of
+ many parameters */
+ char **p, **b = (char **)zcalloc((clwnum + 1) * sizeof(char *));
+
+ for (i = 0, p = b; i < clwnum; p++, i++)
+ *p = ztrdup(clwords[i]);
+
+ setaparam(reply, b);
+ return 0;
+ }
+ if (ops['e'] || ops['E']) {
+ for (i = 0; i < clwnum; i++) {
+ zputs(clwords[i], stdout);
+ putchar('\n');
+ }
+
+ if (ops['e'])
+ return 0;
+ }
+
+ for (i = 0; i < clwnum && *args; reply = *args++, i++)
+ setsparam(reply, ztrdup(clwords[i]));
+
+ if (i < clwnum) {
+ int j, len;
+
+ for (j = i, len = 0; j < clwnum; len += strlen(clwords[j++]));
+ bptr = buf = zalloc(len + j - i);
+ while (i < clwnum) {
+ strucpy(&bptr, clwords[i++]);
+ *bptr++ = ' ';
+ }
+ bptr[-1] = '\0';
+ } else
+ buf = ztrdup("");
+ setsparam(reply, buf);
+ }
+ return 0;
+}
+
+/**/
+int
+boot_comp1(Module m)
+{
+ compctlreadptr = compctlread;
+ clwords = (char **) zcalloc((clwsize = 16) * sizeof(char *));
+ createcompctltable();
+ cc_compos.mask = CC_COMMPATH;
+ cc_default.refc = 10000;
+ cc_default.mask = CC_FILES;
+ cc_first.refc = 10000;
+ cc_first.mask = 0;
+ return 0;
+}
+
+#ifdef MODULE
+
+/**/
+int
+cleanup_comp1(Module m)
+{
+ deletehashtable(compctltab);
+ zfree(clwords, clwsize * sizeof(char *));
+ compctlreadptr = fallback_compctlread;
+ return 0;
+}
+
+#endif /* MODULE */
diff --git a/Src/Zle/comp1.mdd b/Src/Zle/comp1.mdd
new file mode 100644
index 000000000..9037e568a
--- /dev/null
+++ b/Src/Zle/comp1.mdd
@@ -0,0 +1,3 @@
+objects="comp1.o"
+
+headers="comp.h"
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
new file mode 100644
index 000000000..658cf4161
--- /dev/null
+++ b/Src/Zle/compctl.c
@@ -0,0 +1,1085 @@
+/*
+ * compctl.c - the compctl builtin
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * 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 Paul Falstad 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 Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad 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 Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "compctl.mdh"
+#include "compctl.pro"
+
+#define COMP_LIST (1<<0) /* -L */
+#define COMP_COMMAND (1<<1) /* -C */
+#define COMP_DEFAULT (1<<2) /* -D */
+#define COMP_FIRST (1<<3) /* -T */
+#define COMP_REMOVE (1<<4)
+
+#define COMP_SPECIAL (COMP_COMMAND|COMP_DEFAULT|COMP_FIRST)
+
+/* Flag for listing, command, default, or first completion */
+static int cclist;
+
+/* Mask for determining what to print */
+static unsigned long showmask = 0;
+
+/* Parse the basic flags for `compctl' */
+
+/**/
+static int
+get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
+{
+ /* Parse the basic flags for completion:
+ * first is a flag that we are not in extended completion,
+ * while hx indicates or (+) completion (need to know for
+ * default and command completion as the initial compctl is special).
+ * cct is a temporary just to hold flags; it never needs freeing.
+ */
+ struct compctl cct;
+ char **argv = *av;
+ int ready = 0, hx = 0;
+
+ /* Handle `compctl + foo ...' specially: turn it into
+ * a default compctl by removing it from the hash table.
+ */
+ if (first && argv[0][0] == '+' && !argv[0][1] &&
+ !(argv[1] && argv[1][0] == '-' && argv[1][1])) {
+ argv++;
+ if(argv[0] && argv[0][0] == '-')
+ argv++;
+ *av = argv;
+ freecompctl(cc);
+ cclist = COMP_REMOVE;
+ return 0;
+ }
+
+ memset((void *)&cct, 0, sizeof(cct));
+
+ /* Loop through the flags until we have no more: *
+ * those with arguments are not properly allocated yet, *
+ * we just hang on to the argument that was passed. */
+ for (; !ready && argv[0] && argv[0][0] == '-' && (argv[0][1] || !first);) {
+ if (!argv[0][1])
+ *argv = "-+";
+ while (!ready && *++(*argv)) {
+ if(**argv == Meta)
+ *++*argv ^= 32;
+ switch (**argv) {
+ case 'f':
+ cct.mask |= CC_FILES;
+ break;
+ case 'c':
+ cct.mask |= CC_COMMPATH;
+ break;
+ case 'm':
+ cct.mask |= CC_EXTCMDS;
+ break;
+ case 'w':
+ cct.mask |= CC_RESWDS;
+ break;
+ case 'o':
+ cct.mask |= CC_OPTIONS;
+ break;
+ case 'v':
+ cct.mask |= CC_VARS;
+ break;
+ case 'b':
+ cct.mask |= CC_BINDINGS;
+ break;
+ case 'A':
+ cct.mask |= CC_ARRAYS;
+ break;
+ case 'I':
+ cct.mask |= CC_INTVARS;
+ break;
+ case 'F':
+ cct.mask |= CC_SHFUNCS;
+ break;
+ case 'p':
+ cct.mask |= CC_PARAMS;
+ break;
+ case 'E':
+ cct.mask |= CC_ENVVARS;
+ break;
+ case 'j':
+ cct.mask |= CC_JOBS;
+ break;
+ case 'r':
+ cct.mask |= CC_RUNNING;
+ break;
+ case 'z':
+ cct.mask |= CC_STOPPED;
+ break;
+ case 'B':
+ cct.mask |= CC_BUILTINS;
+ break;
+ case 'a':
+ cct.mask |= CC_ALREG | CC_ALGLOB;
+ break;
+ case 'R':
+ cct.mask |= CC_ALREG;
+ break;
+ case 'G':
+ cct.mask |= CC_ALGLOB;
+ break;
+ case 'u':
+ cct.mask |= CC_USERS;
+ break;
+ case 'd':
+ cct.mask |= CC_DISCMDS;
+ break;
+ case 'e':
+ cct.mask |= CC_EXCMDS;
+ break;
+ case 'N':
+ cct.mask |= CC_SCALARS;
+ break;
+ case 'O':
+ cct.mask |= CC_READONLYS;
+ break;
+ case 'Z':
+ cct.mask |= CC_SPECIALS;
+ break;
+ case 'q':
+ cct.mask |= CC_REMOVE;
+ break;
+ case 'U':
+ cct.mask |= CC_DELETE;
+ break;
+ case 'n':
+ cct.mask |= CC_NAMED;
+ break;
+ case 'Q':
+ cct.mask |= CC_QUOTEFLAG;
+ break;
+ case '/':
+ cct.mask |= CC_DIRS;
+ break;
+ case 'k':
+ if ((*argv)[1]) {
+ cct.keyvar = (*argv) + 1;
+ *argv = "" - 1;
+ } else if (!argv[1]) {
+ zwarnnam(name, "variable name expected after -%c", NULL,
+ **argv);
+ return 1;
+ } else {
+ cct.keyvar = *++argv;
+ *argv = "" - 1;
+ }
+ break;
+ case 'K':
+ if ((*argv)[1]) {
+ cct.func = (*argv) + 1;
+ *argv = "" - 1;
+ } else if (!argv[1]) {
+ zwarnnam(name, "function name expected after -%c", NULL,
+ **argv);
+ return 1;
+ } else {
+ cct.func = *++argv;
+ *argv = "" - 1;
+ }
+ break;
+ case 'Y':
+ cct.mask |= CC_EXPANDEXPL;
+ goto expl;
+ case 'X':
+ cct.mask &= ~CC_EXPANDEXPL;
+ expl:
+ if ((*argv)[1]) {
+ cct.explain = (*argv) + 1;
+ *argv = "" - 1;
+ } else if (!argv[1]) {
+ zwarnnam(name, "string expected after -%c", NULL, **argv);
+ return 1;
+ } else {
+ cct.explain = *++argv;
+ *argv = "" - 1;
+ }
+ break;
+ case 'y':
+ if ((*argv)[1]) {
+ cct.ylist = (*argv) + 1;
+ *argv = "" - 1;
+ } else if (!argv[1]) {
+ zwarnnam(name, "function/variable expected after -%c",
+ NULL, **argv);
+ } else {
+ cct.ylist = *++argv;
+ *argv = "" - 1;
+ }
+ break;
+ case 'P':
+ if ((*argv)[1]) {
+ cct.prefix = (*argv) + 1;
+ *argv = "" - 1;
+ } else if (!argv[1]) {
+ zwarnnam(name, "string expected after -%c", NULL, **argv);
+ return 1;
+ } else {
+ cct.prefix = *++argv;
+ *argv = "" - 1;
+ }
+ break;
+ case 'S':
+ if ((*argv)[1]) {
+ cct.suffix = (*argv) + 1;
+ *argv = "" - 1;
+ } else if (!argv[1]) {
+ zwarnnam(name, "string expected after -%c", NULL, **argv);
+ return 1;
+ } else {
+ cct.suffix = *++argv;
+ *argv = "" - 1;
+ }
+ break;
+ case 'g':
+ if ((*argv)[1]) {
+ cct.glob = (*argv) + 1;
+ *argv = "" - 1;
+ } else if (!argv[1]) {
+ zwarnnam(name, "glob pattern expected after -%c", NULL,
+ **argv);
+ return 1;
+ } else {
+ cct.glob = *++argv;
+ *argv = "" - 1;
+ }
+ break;
+ case 's':
+ if ((*argv)[1]) {
+ cct.str = (*argv) + 1;
+ *argv = "" - 1;
+ } else if (!argv[1]) {
+ zwarnnam(name, "command string expected after -%c", NULL,
+ **argv);
+ return 1;
+ } else {
+ cct.str = *++argv;
+ *argv = "" - 1;
+ }
+ break;
+ case 'l':
+ if ((*argv)[1]) {
+ cct.subcmd = (*argv) + 1;
+ *argv = "" - 1;
+ } else if (!argv[1]) {
+ zwarnnam(name, "command name expected after -%c", NULL,
+ **argv);
+ return 1;
+ } else {
+ cct.subcmd = *++argv;
+ *argv = "" - 1;
+ }
+ break;
+ case 'W':
+ if ((*argv)[1]) {
+ cct.withd = (*argv) + 1;
+ *argv = "" - 1;
+ } else if (!argv[1]) {
+ zwarnnam(name, "path expected after -%c", NULL,
+ **argv);
+ return 1;
+ } else {
+ cct.withd = *++argv;
+ *argv = "" - 1;
+ }
+ break;
+ case 'H':
+ if ((*argv)[1])
+ cct.hnum = atoi((*argv) + 1);
+ else if (argv[1])
+ cct.hnum = atoi(*++argv);
+ else {
+ zwarnnam(name, "number expected after -%c", NULL,
+ **argv);
+ return 1;
+ }
+ if (!argv[1]) {
+ zwarnnam(name, "missing pattern after -%c", NULL,
+ **argv);
+ return 1;
+ }
+ cct.hpat = *++argv;
+ if (cct.hnum < 1)
+ cct.hnum = 0;
+ if (*cct.hpat == '*' && !cct.hpat[1])
+ cct.hpat = "";
+ *argv = "" - 1;
+ break;
+ case 'C':
+ if (first && !hx) {
+ cclist |= COMP_COMMAND;
+ } else {
+ zwarnnam(name, "misplaced command completion (-C) flag",
+ NULL, 0);
+ return 1;
+ }
+ break;
+ case 'D':
+ if (first && !hx) {
+ isdef = 1;
+ cclist |= COMP_DEFAULT;
+ } else {
+ zwarnnam(name, "misplaced default completion (-D) flag",
+ NULL, 0);
+ return 1;
+ }
+ break;
+ case 'T':
+ if (first && !hx) {
+ cclist |= COMP_FIRST;
+ } else {
+ zwarnnam(name, "misplaced first completion (-T) flag",
+ NULL, 0);
+ return 1;
+ }
+ break;
+ case 'L':
+ if (!first || hx) {
+ zwarnnam(name, "illegal use of -L flag", NULL, 0);
+ return 1;
+ }
+ cclist |= COMP_LIST;
+ break;
+ case 'x':
+ if (!argv[1]) {
+ zwarnnam(name, "condition expected after -%c", NULL,
+ **argv);
+ return 1;
+ }
+ if (first) {
+ argv++;
+ if (get_xcompctl(name, &argv, &cct, isdef)) {
+ if (cct.ext)
+ freecompctl(cct.ext);
+ return 1;
+ }
+ ready = 2;
+ } else {
+ zwarnnam(name, "recursive extended completion not allowed",
+ NULL, 0);
+ return 1;
+ }
+ break;
+ default:
+ if (!first && (**argv == '-' || **argv == '+'))
+ (*argv)--, argv--, ready = 1;
+ else {
+ zwarnnam(name, "bad option: -%c", NULL, **argv);
+ return 1;
+ }
+ }
+ }
+
+ if (*++argv && (!ready || ready == 2) &&
+ **argv == '+' && !argv[0][1]) {
+ /* There's an alternative (+) completion: assign
+ * what we have so far before moving on to that.
+ */
+ if (cc_assign(name, &cc, &cct, first && !hx))
+ return 1;
+
+ hx = 1;
+ ready = 0;
+
+ if (!*++argv || **argv != '-' ||
+ (**argv == '-' && (!argv[0][1] ||
+ (argv[0][1] == '-' && !argv[0][2])))) {
+ /* No argument to +, which means do default completion */
+ if (isdef)
+ zwarnnam(name,
+ "recursive xor'd default completions not allowed",
+ NULL, 0);
+ else
+ cc->xor = &cc_default;
+ } else {
+ /* more flags follow: prepare to loop again */
+ cc->xor = (Compctl) zcalloc(sizeof(*cc));
+ cc = cc->xor;
+ memset((void *)&cct, 0, sizeof(cct));
+ }
+ }
+ }
+ if (!ready && *argv && **argv == '-')
+ argv++;
+
+ if (! (cct.mask & (CC_EXCMDS | CC_DISCMDS)))
+ cct.mask |= CC_EXCMDS;
+
+ /* assign the last set of flags we parsed */
+ if (cc_assign(name, &cc, &cct, first && !hx))
+ return 1;
+
+ *av = argv;
+
+ return 0;
+}
+
+/* Handle the -x ... -- part of compctl. */
+
+/**/
+static int
+get_xcompctl(char *name, char ***av, Compctl cc, int isdef)
+{
+ char **argv = *av, *t, *tt, sav;
+ int n, l = 0, ready = 0;
+ Compcond m, c, o;
+ Compctl *next = &(cc->ext);
+
+ while (!ready) {
+ /* o keeps track of or's, m remembers the starting condition,
+ * c is the current condition being parsed
+ */
+ o = m = c = (Compcond) zcalloc(sizeof(*c));
+ /* Loop over each condition: something like 's[...][...], p[...]' */
+ for (t = *argv; *t;) {
+ while (*t == ' ')
+ t++;
+ /* First get the condition code */
+ switch (*t) {
+ case 's':
+ c->type = CCT_CURSUF;
+ break;
+ case 'S':
+ c->type = CCT_CURPRE;
+ break;
+ case 'p':
+ c->type = CCT_POS;
+ break;
+ case 'c':
+ c->type = CCT_CURSTR;
+ break;
+ case 'C':
+ c->type = CCT_CURPAT;
+ break;
+ case 'w':
+ c->type = CCT_WORDSTR;
+ break;
+ case 'W':
+ c->type = CCT_WORDPAT;
+ break;
+ case 'n':
+ c->type = CCT_CURSUB;
+ break;
+ case 'N':
+ c->type = CCT_CURSUBC;
+ break;
+ case 'm':
+ c->type = CCT_NUMWORDS;
+ break;
+ case 'r':
+ c->type = CCT_RANGESTR;
+ break;
+ case 'R':
+ c->type = CCT_RANGEPAT;
+ break;
+ default:
+ t[1] = '\0';
+ zwarnnam(name, "unknown condition code: %s", t, 0);
+ zfree(m, sizeof(struct compcond));
+
+ return 1;
+ }
+ /* Now get the arguments in square brackets */
+ if (t[1] != '[') {
+ t[1] = '\0';
+ zwarnnam(name, "expected condition after condition code: %s", t, 0);
+ zfree(m, sizeof(struct compcond));
+
+ return 1;
+ }
+ t++;
+ /* First count how many or'd arguments there are,
+ * marking the active ]'s and ,'s with unprintable characters.
+ */
+ for (n = 0, tt = t; *tt == '['; n++) {
+ for (l = 1, tt++; *tt && l; tt++)
+ if (*tt == '\\' && tt[1])
+ tt++;
+ else if (*tt == '[')
+ l++;
+ else if (*tt == ']')
+ l--;
+ else if (l == 1 && *tt == ',')
+ *tt = '\201';
+ if (tt[-1] == ']')
+ tt[-1] = '\200';
+ }
+
+ if (l) {
+ t[1] = '\0';
+ zwarnnam(name, "error after condition code: %s", t, 0);
+ zfree(m, sizeof(struct compcond));
+
+ return 1;
+ }
+ c->n = n;
+
+ /* Allocate space for all the arguments of the conditions */
+ if (c->type == CCT_POS ||
+ c->type == CCT_NUMWORDS) {
+ c->u.r.a = (int *)zcalloc(n * sizeof(int));
+ c->u.r.b = (int *)zcalloc(n * sizeof(int));
+ } else if (c->type == CCT_CURSUF ||
+ c->type == CCT_CURPRE)
+ c->u.s.s = (char **)zcalloc(n * sizeof(char *));
+
+ else if (c->type == CCT_RANGESTR ||
+ c->type == CCT_RANGEPAT) {
+ c->u.l.a = (char **)zcalloc(n * sizeof(char *));
+ c->u.l.b = (char **)zcalloc(n * sizeof(char *));
+ } else {
+ c->u.s.p = (int *)zcalloc(n * sizeof(int));
+ c->u.s.s = (char **)zcalloc(n * sizeof(char *));
+ }
+ /* Now loop over the actual arguments */
+ for (l = 0; *t == '['; l++, t++) {
+ for (t++; *t && *t == ' '; t++);
+ tt = t;
+ if (c->type == CCT_POS ||
+ c->type == CCT_NUMWORDS) {
+ /* p[...] or m[...]: one or two numbers expected */
+ for (; *t && *t != '\201' && *t != '\200'; t++);
+ if (!(sav = *t)) {
+ zwarnnam(name, "error in condition", NULL, 0);
+ freecompcond(m);
+ return 1;
+ }
+ *t = '\0';
+ c->u.r.a[l] = atoi(tt);
+ /* Second argument is optional: see if it's there */
+ if (sav == '\200')
+ /* no: copy first argument */
+ c->u.r.b[l] = c->u.r.a[l];
+ else {
+ tt = ++t;
+ for (; *t && *t != '\200'; t++);
+ if (!*t) {
+ zwarnnam(name, "error in condition", NULL, 0);
+ freecompcond(m);
+ return 1;
+ }
+ *t = '\0';
+ c->u.r.b[l] = atoi(tt);
+ }
+ } else if (c->type == CCT_CURSUF ||
+ c->type == CCT_CURPRE) {
+ /* -s[..] or -S[..]: single string expected */
+ for (; *t && *t != '\200'; t++)
+ if (*t == '\201')
+ *t = ',';
+ if (!*t) {
+ zwarnnam(name, "error in condition", NULL, 0);
+ freecompcond(m);
+ return 1;
+ }
+ *t = '\0';
+ c->u.s.s[l] = ztrdup(tt);
+ } else if (c->type == CCT_RANGESTR ||
+ c->type == CCT_RANGEPAT) {
+ /* -r[..,..] or -R[..,..]: two strings expected */
+ for (; *t && *t != '\201'; t++);
+ if (!*t) {
+ zwarnnam(name, "error in condition", NULL, 0);
+ freecompcond(m);
+ return 1;
+ }
+ *t = '\0';
+ c->u.l.a[l] = ztrdup(tt);
+ tt = ++t;
+ /* any more commas are text, not active */
+ for (; *t && *t != '\200'; t++)
+ if (*t == '\201')
+ *t = ',';
+ if (!*t) {
+ zwarnnam(name, "error in condition", NULL, 0);
+ freecompcond(m);
+ return 1;
+ }
+ *t = '\0';
+ c->u.l.b[l] = ztrdup(tt);
+ } else {
+ /* remaining patterns are number followed by string */
+ for (; *t && *t != '\200' && *t != '\201'; t++);
+ if (!*t || *t == '\200') {
+ zwarnnam(name, "error in condition", NULL, 0);
+ freecompcond(m);
+ return 1;
+ }
+ *t = '\0';
+ c->u.s.p[l] = atoi(tt);
+ tt = ++t;
+ for (; *t && *t != '\200'; t++)
+ if (*t == '\201')
+ *t = ',';
+ if (!*t) {
+ zwarnnam(name, "error in condition", NULL, 0);
+ freecompcond(m);
+ return 1;
+ }
+ *t = '\0';
+ c->u.s.s[l] = ztrdup(tt);
+ }
+ }
+ while (*t == ' ')
+ t++;
+ if (*t == ',') {
+ /* Another condition to `or' */
+ o->or = c = (Compcond) zcalloc(sizeof(*c));
+ o = c;
+ t++;
+ } else if (*t) {
+ /* Another condition to `and' */
+ c->and = (Compcond) zcalloc(sizeof(*c));
+ c = c->and;
+ }
+ }
+ /* Assign condition to current compctl */
+ *next = (Compctl) zcalloc(sizeof(*cc));
+ (*next)->cond = m;
+ argv++;
+ /* End of the condition; get the flags that go with it. */
+ if (get_compctl(name, &argv, *next, 0, isdef))
+ return 1;
+ if ((!argv || !*argv) && (cclist & COMP_SPECIAL))
+ /* default, first, or command completion finished */
+ ready = 1;
+ else {
+ /* see if we are looking for more conditions or are
+ * ready to return (ready = 1)
+ */
+ if (!argv || !*argv || **argv != '-' ||
+ ((!argv[0][1] || argv[0][1] == '+') && !argv[1])) {
+ zwarnnam(name, "missing command names", NULL, 0);
+ return 1;
+ }
+ if (!strcmp(*argv, "--"))
+ ready = 1;
+ else if (!strcmp(*argv, "-+") && argv[1] &&
+ !strcmp(argv[1], "--")) {
+ ready = 1;
+ argv++;
+ }
+ argv++;
+ /* prepare to put the next lot of conditions on the end */
+ next = &((*next)->next);
+ }
+ }
+ /* save position at end of parsing */
+ *av = argv - 1;
+ return 0;
+}
+
+/**/
+static int
+cc_assign(char *name, Compctl *ccptr, Compctl cct, int reass)
+{
+ /* Copy over the details from the values in cct to those in *ccptr */
+ Compctl cc;
+
+ if (cct->subcmd && (cct->keyvar || cct->glob || cct->str ||
+ cct->func || cct->explain || cct->ylist ||
+ cct->prefix)) {
+ zwarnnam(name, "illegal combination of options", NULL, 0);
+ return 1;
+ }
+
+ /* Handle assignment of new default or command completion */
+ if (reass && !(cclist & COMP_LIST)) {
+ /* if not listing */
+ if (cclist == (COMP_COMMAND|COMP_DEFAULT)
+ || cclist == (COMP_COMMAND|COMP_FIRST)
+ || cclist == (COMP_DEFAULT|COMP_FIRST)
+ || cclist == COMP_SPECIAL) {
+ zwarnnam(name, "can't set -D, -T, and -C simultaneously", NULL, 0);
+ /* ... because the following code wouldn't work. */
+ return 1;
+ }
+ if (cclist & COMP_COMMAND) {
+ /* command */
+ *ccptr = &cc_compos;
+ cc_reassign(*ccptr);
+ } else if (cclist & COMP_DEFAULT) {
+ /* default */
+ *ccptr = &cc_default;
+ cc_reassign(*ccptr);
+ } else if (cclist & COMP_FIRST) {
+ /* first */
+ *ccptr = &cc_first;
+ cc_reassign(*ccptr);
+ }
+ }
+
+ /* Free the old compctl */
+ cc = *ccptr;
+ zsfree(cc->keyvar);
+ zsfree(cc->glob);
+ zsfree(cc->str);
+ zsfree(cc->func);
+ zsfree(cc->explain);
+ zsfree(cc->ylist);
+ zsfree(cc->prefix);
+ zsfree(cc->suffix);
+ zsfree(cc->subcmd);
+ zsfree(cc->withd);
+ zsfree(cc->hpat);
+
+ /* and copy over the new stuff, (permanently) allocating
+ * space for strings.
+ */
+ cc->mask = cct->mask;
+ cc->keyvar = ztrdup(cct->keyvar);
+ cc->glob = ztrdup(cct->glob);
+ cc->str = ztrdup(cct->str);
+ cc->func = ztrdup(cct->func);
+ cc->explain = ztrdup(cct->explain);
+ cc->ylist = ztrdup(cct->ylist);
+ cc->prefix = ztrdup(cct->prefix);
+ cc->suffix = ztrdup(cct->suffix);
+ cc->subcmd = ztrdup(cct->subcmd);
+ cc->withd = ztrdup(cct->withd);
+ cc->hpat = ztrdup(cct->hpat);
+ cc->hnum = cct->hnum;
+
+ /* careful with extended completion: it's already allocated */
+ cc->ext = cct->ext;
+
+ return 0;
+}
+
+/**/
+static void
+cc_reassign(Compctl cc)
+{
+ /* Free up a new default or command completion:
+ * this is a hack to free up the parts which should be deleted,
+ * without removing the basic variable which is statically allocated
+ */
+ Compctl c2;
+
+ c2 = (Compctl) zcalloc(sizeof *cc);
+ c2->xor = cc->xor;
+ c2->ext = cc->ext;
+ c2->refc = 1;
+
+ freecompctl(c2);
+
+ cc->ext = cc->xor = NULL;
+}
+
+/**/
+static void
+compctl_process_cc(char **s, Compctl cc)
+{
+ Compctlp ccp;
+
+ if (cclist & COMP_REMOVE) {
+ /* Delete entries for the commands listed */
+ for (; *s; s++) {
+ if ((ccp = (Compctlp) compctltab->removenode(compctltab, *s)))
+ compctltab->freenode((HashNode) ccp);
+ }
+ } else {
+ /* Add the compctl just read to the hash table */
+
+ cc->refc = 0;
+ for (; *s; s++) {
+ cc->refc++;
+ ccp = (Compctlp) zalloc(sizeof *ccp);
+ ccp->cc = cc;
+ compctltab->addnode(compctltab, ztrdup(*s), ccp);
+ }
+ }
+}
+
+/* Print a `compctl' */
+
+/**/
+static void
+printcompctl(char *s, Compctl cc, int printflags)
+{
+ Compctl cc2;
+ char *css = "fcqovbAIFpEjrzBRGudeNOZUnQmw/";
+ char *mss = " pcCwWsSnNmrR";
+ unsigned long t = 0x7fffffff;
+ unsigned long flags = cc->mask;
+ unsigned long oldshowmask;
+
+ if ((flags & CC_EXCMDS) && !(flags & CC_DISCMDS))
+ flags &= ~CC_EXCMDS;
+
+ /* If showmask is non-zero, then print only those *
+ * commands with that flag set. */
+ if (showmask && !(flags & showmask))
+ return;
+
+ /* Temporarily clear showmask in case we make *
+ * recursive calls to printcompctl. */
+ oldshowmask = showmask;
+ showmask = 0;
+
+ /* print either command name or start of compctl command itself */
+ if (s) {
+ if (cclist & COMP_LIST) {
+ printf("compctl");
+ if (cc == &cc_compos)
+ printf(" -C");
+ if (cc == &cc_default)
+ printf(" -D");
+ if (cc == &cc_first)
+ printf(" -T");
+ } else
+ quotedzputs(s, stdout);
+ }
+
+ /* loop through flags w/o args that are set, printing them if so */
+ if (flags & t) {
+ printf(" -");
+ if ((flags & (CC_ALREG | CC_ALGLOB)) == (CC_ALREG | CC_ALGLOB))
+ putchar('a'), flags &= ~(CC_ALREG | CC_ALGLOB);
+ while (*css) {
+ if (flags & t & 1)
+ putchar(*css);
+ css++;
+ flags >>= 1;
+ t >>= 1;
+ }
+ }
+
+ /* now flags with arguments */
+ flags = cc->mask;
+ printif(cc->keyvar, 'k');
+ printif(cc->func, 'K');
+ printif(cc->explain, (cc->mask & CC_EXPANDEXPL) ? 'Y' : 'X');
+ printif(cc->ylist, 'y');
+ printif(cc->prefix, 'P');
+ printif(cc->suffix, 'S');
+ printif(cc->glob, 'g');
+ printif(cc->str, 's');
+ printif(cc->subcmd, 'l');
+ printif(cc->withd, 'W');
+ if (cc->hpat) {
+ printf(" -H %d ", cc->hnum);
+ quotedzputs(cc->hpat, stdout);
+ }
+
+ /* now the -x ... -- extended completion part */
+ if (cc->ext) {
+ Compcond c, o;
+ int i;
+
+ cc2 = cc->ext;
+ printf(" -x");
+
+ while (cc2) {
+ /* loop over conditions */
+ c = cc2->cond;
+
+ printf(" '");
+ for (c = cc2->cond; c;) {
+ /* loop over or's */
+ o = c->or;
+ while (c) {
+ /* loop over and's */
+ putchar(mss[c->type]);
+
+ for (i = 0; i < c->n; i++) {
+ /* for all [...]'s of a given condition */
+ putchar('[');
+ switch (c->type) {
+ case CCT_POS:
+ case CCT_NUMWORDS:
+ printf("%d,%d", c->u.r.a[i], c->u.r.b[i]);
+ break;
+ case CCT_CURSUF:
+ case CCT_CURPRE:
+ printqt(c->u.s.s[i]);
+ break;
+ case CCT_RANGESTR:
+ case CCT_RANGEPAT:
+ printqt(c->u.l.a[i]);
+ putchar(',');
+ printqt(c->u.l.b[i]);
+ break;
+ default:
+ printf("%d,", c->u.s.p[i]);
+ printqt(c->u.s.s[i]);
+ }
+ putchar(']');
+ }
+ if ((c = c->and))
+ putchar(' ');
+ }
+ if ((c = o))
+ printf(" , ");
+ }
+ putchar('\'');
+ c = cc2->cond;
+ cc2->cond = NULL;
+ /* now print the flags for the current condition */
+ printcompctl(NULL, cc2, 0);
+ cc2->cond = c;
+ if ((cc2 = (Compctl) (cc2->next)))
+ printf(" -");
+ }
+ if (cclist & COMP_LIST)
+ printf(" --");
+ }
+ if (cc && cc->xor) {
+ /* print xor'd (+) completions */
+ printf(" +");
+ if (cc->xor != &cc_default)
+ printcompctl(NULL, cc->xor, 0);
+ }
+ if (s) {
+ if ((cclist & COMP_LIST) && (cc != &cc_compos)
+ && (cc != &cc_default) && (cc != &cc_first)) {
+ if(s[0] == '-' || s[0] == '+')
+ printf(" -");
+ putchar(' ');
+ quotedzputs(s, stdout);
+ }
+ putchar('\n');
+ }
+
+ showmask = oldshowmask;
+}
+
+/**/
+static void
+printcompctlp(HashNode hn, int printflags)
+{
+ Compctlp ccp = (Compctlp) hn;
+
+ /* Function needed for use by scanhashtable() */
+ printcompctl(ccp->nam, ccp->cc, printflags);
+}
+
+/* Main entry point for the `compctl' builtin */
+
+/**/
+static int
+bin_compctl(char *name, char **argv, char *ops, int func)
+{
+ Compctl cc = NULL;
+ int ret = 0;
+
+ /* clear static flags */
+ cclist = 0;
+ showmask = 0;
+
+ /* Parse all the arguments */
+ if (*argv) {
+ cc = (Compctl) zcalloc(sizeof(*cc));
+ if (get_compctl(name, &argv, cc, 1, 0)) {
+ freecompctl(cc);
+ return 1;
+ }
+
+ /* remember flags for printing */
+ showmask = cc->mask;
+ if ((showmask & CC_EXCMDS) && !(showmask & CC_DISCMDS))
+ showmask &= ~CC_EXCMDS;
+
+ /* if no command arguments or just listing, we don't want cc */
+ if (!*argv || (cclist & COMP_LIST))
+ freecompctl(cc);
+ }
+
+ /* If no commands and no -C, -T, or -D, print all the compctl's *
+ * If some flags (other than -C, -T, or -D) were given, then *
+ * only print compctl containing those flags. */
+ if (!*argv && !(cclist & COMP_SPECIAL)) {
+ scanhashtable(compctltab, 1, 0, 0, compctltab->printnode, 0);
+ printcompctl((cclist & COMP_LIST) ? "" : "COMMAND", &cc_compos, 0);
+ printcompctl((cclist & COMP_LIST) ? "" : "DEFAULT", &cc_default, 0);
+ printcompctl((cclist & COMP_LIST) ? "" : "FIRST", &cc_first, 0);
+ return ret;
+ }
+
+ /* If we're listing and we've made it to here, then there are arguments *
+ * or a COMP_SPECIAL flag (-D, -C, -T), so print only those. */
+ if (cclist & COMP_LIST) {
+ HashNode hn;
+ char **ptr;
+
+ showmask = 0;
+ for (ptr = argv; *ptr; ptr++) {
+ if ((hn = compctltab->getnode(compctltab, *ptr))) {
+ compctltab->printnode(hn, 0);
+ } else {
+ zwarnnam(name, "no compctl defined for %s", *ptr, 0);
+ ret = 1;
+ }
+ }
+ if (cclist & COMP_COMMAND)
+ printcompctl("", &cc_compos, 0);
+ if (cclist & COMP_DEFAULT)
+ printcompctl("", &cc_default, 0);
+ if (cclist & COMP_FIRST)
+ printcompctl("", &cc_first, 0);
+ return ret;
+ }
+
+ /* Assign the compctl to the commands given */
+ if (*argv) {
+ if(cclist & COMP_SPECIAL)
+ /* Ideally we'd handle this properly, setting both the *
+ * special and normal completions. For the moment, *
+ * this is better than silently failing. */
+ zwarnnam(name, "extraneous commands ignored", NULL, 0);
+ else
+ compctl_process_cc(argv, cc);
+ }
+
+ return ret;
+}
+
+static struct builtin bintab[] = {
+ BUILTIN("compctl", 0, bin_compctl, 0, -1, 0, NULL, NULL),
+};
+
+/**/
+int
+boot_compctl(Module m)
+{
+ if(!addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)))
+ return 1;
+ compctltab->printnode = printcompctlp;
+ return 0;
+}
+
+#ifdef MODULE
+
+/**/
+int
+cleanup_compctl(Module m)
+{
+ compctltab->printnode = NULL;
+ deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+ return 0;
+}
+#endif
diff --git a/Src/Zle/compctl.mdd b/Src/Zle/compctl.mdd
new file mode 100644
index 000000000..c83ecda29
--- /dev/null
+++ b/Src/Zle/compctl.mdd
@@ -0,0 +1,5 @@
+moddeps="comp1"
+
+autobins="compctl"
+
+objects="compctl.o"
diff --git a/Src/Zle/deltochar.c b/Src/Zle/deltochar.c
new file mode 100644
index 000000000..87f8593b8
--- /dev/null
+++ b/Src/Zle/deltochar.c
@@ -0,0 +1,91 @@
+/*
+ * deltochar.c - ZLE module implementing Emacs' zap-to-char
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1996-1997 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.
+ *
+ */
+
+#include "deltochar.mdh"
+#include "deltochar.pro"
+
+static Widget w_deletetochar;
+
+/**/
+static void
+deltochar(void)
+{
+ int c = getkey(0), dest = cs, ok = 0, n = zmult;
+
+ if (n > 0) {
+ while (n-- && dest != ll) {
+ while (dest != ll && line[dest] != c)
+ dest++;
+ if (dest != ll) {
+ dest++;
+ if (!n) {
+ foredel(dest - cs);
+ ok++;
+ }
+ }
+ }
+ } else {
+ /* ignore character cursor is on when scanning backwards */
+ if (dest)
+ dest--;
+ while (n++ && dest != 0) {
+ while (dest != 0 && line[dest] != c)
+ dest--;
+ if (line[dest] == c && !n) {
+ backdel(cs - dest);
+ ok++;
+ }
+ }
+ }
+ if (!ok)
+ feep();
+}
+
+/**/
+int
+boot_deltochar(Module m)
+{
+ w_deletetochar = addzlefunction("delete-to-char", deltochar, ZLE_KEEPSUFFIX);
+ if (w_deletetochar)
+ return 0;
+ zwarnnam(m->nam, "name clash when adding ZLE function `delete-to-char'",
+ NULL, 0);
+ return -1;
+}
+
+#ifdef MODULE
+
+/**/
+int
+cleanup_deltochar(Module m)
+{
+ deletezlefunction(w_deletetochar);
+ return 0;
+}
+#endif
diff --git a/Src/Zle/deltochar.mdd b/Src/Zle/deltochar.mdd
new file mode 100644
index 000000000..4d1f89d1c
--- /dev/null
+++ b/Src/Zle/deltochar.mdd
@@ -0,0 +1,3 @@
+moddeps="zle"
+
+objects="deltochar.o"
diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list
new file mode 100644
index 000000000..01862160e
--- /dev/null
+++ b/Src/Zle/iwidgets.list
@@ -0,0 +1,172 @@
+#
+# intwidgets.list - list of internally implemented ZLE widgets
+#
+# Each line has the form:
+#
+# "canonical-name" , functionname , ZLE_FLAGS
+#
+# `#' starts a comment. Blank lines are ignored.
+#
+
+"accept-and-hold", acceptandhold, 0
+"accept-and-infer-next-history", acceptandinfernexthistory, 0
+"accept-and-menu-complete", acceptandmenucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX
+"accept-line", acceptline, 0
+"accept-line-and-down-history", acceptlineanddownhistory, 0
+"backward-char", backwardchar, 0
+"backward-delete-char", backwarddeletechar, ZLE_KEEPSUFFIX
+"backward-delete-word", backwarddeleteword, ZLE_KEEPSUFFIX
+"backward-kill-line", backwardkillline, ZLE_KILL | ZLE_KEEPSUFFIX
+"backward-kill-word", backwardkillword, ZLE_KILL | ZLE_KEEPSUFFIX
+"backward-word", backwardword, 0
+"beginning-of-buffer-or-history", beginningofbufferorhistory, 0
+"beginning-of-history", beginningofhistory, 0
+"beginning-of-line", beginningofline, 0
+"beginning-of-line-hist", beginningoflinehist, 0
+"capitalize-word", capitalizeword, 0
+"clear-screen", clearscreen, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
+"complete-word", completeword, ZLE_MENUCMP | ZLE_KEEPSUFFIX
+"copy-prev-word", copyprevword, 0
+"copy-region-as-kill", copyregionaskill, ZLE_KEEPSUFFIX
+"delete-char", deletechar, ZLE_KEEPSUFFIX
+"delete-char-or-list", deletecharorlist, ZLE_MENUCMP | ZLE_KEEPSUFFIX
+"delete-word", deleteword, ZLE_KEEPSUFFIX
+"describe-key-briefly", describekeybriefly, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
+"digit-argument", digitargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
+"down-case-word", downcaseword, 0
+"down-history", downhistory, 0
+"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-history", endofhistory, 0
+"end-of-line", endofline, 0
+"end-of-line-hist", endoflinehist, 0
+"exchange-point-and-mark", exchangepointandmark, 0
+"execute-last-named-cmd", NULL, 0
+"execute-named-cmd", NULL, 0
+"expand-cmd-path", expandcmdpath, 0
+"expand-history", expandhistory, 0
+"expand-or-complete", expandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX
+"expand-or-complete-prefix", expandorcompleteprefix, ZLE_MENUCMP | ZLE_KEEPSUFFIX
+"expand-word", expandword, 0
+"forward-char", forwardchar, 0
+"forward-word", forwardword, 0
+"get-line", getline, 0
+"gosmacs-transpose-chars", gosmacstransposechars, 0
+"history-beginning-search-backward", historybeginningsearchbackward, 0
+"history-beginning-search-forward", historybeginningsearchforward, 0
+"history-incremental-search-backward", historyincrementalsearchbackward, 0
+"history-incremental-search-forward", historyincrementalsearchforward, 0
+"history-search-backward", historysearchbackward, 0
+"history-search-forward", historysearchforward, 0
+"infer-next-history", infernexthistory, 0
+"insert-last-word", insertlastword, ZLE_MENUCMP | ZLE_KEEPSUFFIX
+"kill-buffer", killbuffer, ZLE_KILL | ZLE_KEEPSUFFIX
+"kill-line", killline, ZLE_KILL | ZLE_KEEPSUFFIX
+"kill-region", killregion, ZLE_KILL | ZLE_KEEPSUFFIX
+"kill-whole-line", killwholeline, ZLE_KILL | ZLE_KEEPSUFFIX
+"kill-word", killword, ZLE_KILL | ZLE_KEEPSUFFIX
+"list-choices", listchoices, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
+"list-expand", listexpand, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
+"magic-space", magicspace, 0
+"menu-complete", menucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX
+"menu-expand-or-complete", menuexpandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX
+"neg-argument", negargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
+"overwrite-mode", overwritemode, 0
+"pound-insert", poundinsert, 0
+"push-input", pushinput, 0
+"push-line", pushline, 0
+"push-line-or-edit", pushlineoredit, 0
+"quoted-insert", quotedinsert, ZLE_MENUCMP | ZLE_KEEPSUFFIX
+"quote-line", quoteline, 0
+"quote-region", quoteregion, 0
+"redisplay", redisplay, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
+"redo", redo, 0
+"reverse-menu-complete", reversemenucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX
+"run-help", processcmd, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
+"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
+"spell-word", spellword, 0
+"transpose-chars", transposechars, 0
+"transpose-words", transposewords, 0
+"undefined-key", undefinedkey, 0
+"undo", undo, 0
+"universal-argument", universalargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
+"up-case-word", upcaseword, 0
+"up-history", uphistory, 0
+"up-line-or-history", uplineorhistory, ZLE_LINEMOVE | ZLE_LASTCOL
+"up-line-or-search", uplineorsearch, ZLE_LINEMOVE | ZLE_LASTCOL
+"vi-add-eol", viaddeol, 0
+"vi-add-next", viaddnext, 0
+"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-word", vibackwardword, 0
+"vi-beginning-of-line", vibeginningofline, 0
+"vi-caps-lock-panic", vicapslockpanic, 0
+"vi-change", vichange, 0
+"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-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-find-next-char", vifindnextchar, 0
+"vi-find-next-char-skip", vifindnextcharskip, 0
+"vi-find-prev-char", vifindprevchar, 0
+"vi-find-prev-char-skip", vifindprevcharskip, 0
+"vi-first-non-blank", vifirstnonblank, 0
+"vi-forward-blank-word", viforwardblankword, 0
+"vi-forward-blank-word-end", viforwardblankwordend, 0
+"vi-forward-char", viforwardchar, 0
+"vi-forward-word", viforwardword, 0
+"vi-forward-word-end", viforwardwordend, 0
+"vi-goto-column", vigotocolumn, 0
+"vi-goto-mark", vigotomark, 0
+"vi-goto-mark-line", vigotomarkline, 0
+"vi-history-search-backward", vihistorysearchbackward, 0
+"vi-history-search-forward", vihistorysearchforward, 0
+"vi-indent", viindent, 0
+"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-match-bracket", vimatchbracket, 0
+"vi-open-line-above", viopenlineabove, 0
+"vi-open-line-below", viopenlinebelow, 0
+"vi-oper-swap-case", vioperswapcase, 0
+"vi-pound-insert", vipoundinsert, 0
+"vi-put-after", viputafter, ZLE_YANK
+"vi-put-before", viputbefore, ZLE_YANK
+"vi-quoted-insert", viquotedinsert, ZLE_MENUCMP | ZLE_KEEPSUFFIX
+"vi-repeat-change", virepeatchange, 0
+"vi-repeat-find", virepeatfind, 0
+"vi-repeat-search", virepeatsearch, 0
+"vi-replace", vireplace, 0
+"vi-replace-chars", vireplacechars, 0
+"vi-rev-repeat-find", virevrepeatfind, 0
+"vi-rev-repeat-search", virevrepeatsearch, 0
+"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-undo-change", viundochange, 0
+"vi-unindent", viunindent, 0
+"vi-up-line-or-history", viuplineorhistory, ZLE_LINEMOVE
+"vi-yank", viyank, 0
+"vi-yank-eol", viyankeol, 0
+"vi-yank-whole-line", viyankwholeline, 0
+"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
+"yank-pop", yankpop, ZLE_YANK
diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h
new file mode 100644
index 000000000..faf6cf878
--- /dev/null
+++ b/Src/Zle/zle.h
@@ -0,0 +1,137 @@
+/*
+ * zle.h - header file for line editor
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * 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 Paul Falstad 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 Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad 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 Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#undef trashzle
+#undef zleread
+#undef spaceinline
+#undef gotword
+#undef refresh
+
+typedef struct widget *Widget;
+typedef struct thingy *Thingy;
+
+/* widgets (ZLE functions) */
+
+typedef void (*ZleIntFunc) _((void));
+
+struct widget {
+ int flags; /* flags (see below) */
+ Thingy first; /* `first' thingy that names this widget */
+ union {
+ ZleIntFunc fn; /* pointer to internally implemented widget */
+ char *fnnam; /* name of the shell function for user-defined widget */
+ } u;
+};
+
+#define WIDGET_INT (1<<0) /* widget is internally implemented */
+#define ZLE_MENUCMP (1<<1) /* 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<<9) /* DON'T remove added suffix */
+
+/* thingies */
+
+struct thingy {
+ HashNode next; /* next node in the hash chain */
+ char *nam; /* name of the thingy */
+ int flags; /* TH_* flags (see below) */
+ int rc; /* reference count */
+ Widget widget; /* widget named by this thingy */
+ Thingy samew; /* `next' thingy (circularly) naming the same widget */
+};
+
+/* DISABLED is (1<<0) */
+#define TH_IMMORTAL (1<<1) /* can't refer to a different widget */
+
+/* command modifier prefixes */
+
+struct modifier {
+ int flags; /* MOD_* flags (see below) */
+ int mult; /* repeat count */
+ int tmult; /* repeat count actually being edited */
+ int vibuf; /* vi cut buffer */
+};
+
+#define MOD_MULT (1<<0) /* a repeat count has been selected */
+#define MOD_TMULT (1<<1) /* a repeat count is being entered */
+#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 */
+
+/* current modifier status */
+
+#define zmult (zmod.mult)
+
+/* undo system */
+
+struct change {
+ struct change *prev, *next; /* adjacent changes */
+ int flags; /* see below */
+ int hist; /* history line being changed */
+ int off; /* offset of the text changes */
+ char *del; /* characters to delete (metafied) */
+ char *ins; /* characters to insert (metafied) */
+};
+
+#define CH_NEXT (1<<0) /* next structure is also part of this change */
+#define CH_PREV (1<<1) /* previous structure is also part of this change */
+
+/* known thingies */
+
+#define Th(X) (&thingies[X])
+
+/* opaque keymap type */
+
+typedef struct keymap *Keymap;
+
+typedef void (*KeyScanFunc) _((char *, Thingy, char *, void *));
+
+#define invicmdmode() (!strcmp(curkeymapname, "vicmd"))
+
+/* Standard type of suffix removal. */
+
+#define removesuffix() iremovesuffix(256)
+
+/* Cut/kill buffer type. The buffer itself is purely binary data, *
+ * not NUL-terminated. len is a length count. flags uses the *
+ * CUTBUFFER_* constants defined below. */
+
+struct cutbuffer {
+ char *buf;
+ size_t len;
+ char flags;
+};
+
+typedef struct cutbuffer *Cutbuffer;
+
+#define CUTBUFFER_LINE 1 /* for vi: buffer contains whole lines of data */
+
+#define KRINGCT 8 /* number of buffers in the kill ring */
diff --git a/Src/Zle/zle.mdd b/Src/Zle/zle.mdd
new file mode 100644
index 000000000..29f39d363
--- /dev/null
+++ b/Src/Zle/zle.mdd
@@ -0,0 +1,70 @@
+moddeps="comp1"
+
+autobins="bindkey vared 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"
+
+headers="zle.h zle_things.h"
+
+:<<\Make
+zle_things.h: thingies.list zle_things.sed
+ ( \
+ echo '/** zle_things.h **/'; \
+ echo '/** indices of and pointers to known thingies **/'; \
+ echo; \
+ echo 'enum {'; \
+ sed -n -f $(sdir)/zle_things.sed < thingies.list; \
+ echo ' ZLE_BUILTIN_THINGY_COUNT'; \
+ echo '};'; \
+ ) > $@
+
+zle_widget.h: widgets.list zle_widget.sed
+ ( \
+ echo '/** zle_widget.h **/'; \
+ echo '/** indices of and pointers to internal widgets **/'; \
+ echo; \
+ echo 'enum {'; \
+ sed -n -f $(sdir)/zle_widget.sed < widgets.list; \
+ echo ' ZLE_BUILTIN_WIDGET_COUNT'; \
+ echo '};'; \
+ ) > $@
+
+thingies.list: iwidgets.list
+ ( \
+ echo '/** thingies.list **/'; \
+ echo '/** thingy structures for the known thingies **/'; \
+ echo; \
+ echo '/* format: T("name", TH_FLAGS, w_widget, t_nextthingy) */'; \
+ echo; \
+ sed -e 's/#.*//; /^$$/d; s/" *,.*/"/' \
+ -e 's/^"/T("/; s/$$/, 0,/; h' \
+ -e 's/-//g; s/^.*"\(.*\)".*/w_\1, t_D\1)/' \
+ -e 'H; g; s/\n/ /' \
+ < $(sdir)/iwidgets.list; \
+ sed -e 's/#.*//; /^$$/d; s/" *,.*/"/' \
+ -e 's/^"/T("./; s/$$/, TH_IMMORTAL,/; h' \
+ -e 's/-//g; s/^.*"\.\(.*\)".*/w_\1, t_\1)/' \
+ -e 'H; g; s/\n/ /' \
+ < $(sdir)/iwidgets.list; \
+ ) > $@
+
+widgets.list: iwidgets.list
+ ( \
+ echo '/** widgets.list **/'; \
+ echo '/** widget structures for the internal widgets **/'; \
+ echo; \
+ echo '/* format: W(ZLE_FLAGS, t_firstname, functionname) */'; \
+ echo; \
+ sed -e 's/#.*//; /^$$/d; s/-//g' \
+ -e 's/^"\(.*\)" *, *\([^ ]*\) *, *\(.*\)/W(\3, t_\1, \2)/' \
+ < $(sdir)/iwidgets.list; \
+ ) > $@
+
+zle_bindings.o zle_bindings..o: zle_widget.h widgets.list thingies.list
+
+clean-here: clean.zle
+clean.zle:
+ rm -f zle_things.h zle_widget.h widgets.list thingies.list
+Make
diff --git a/Src/Zle/zle_bindings.c b/Src/Zle/zle_bindings.c
new file mode 100644
index 000000000..40e555ad1
--- /dev/null
+++ b/Src/Zle/zle_bindings.c
@@ -0,0 +1,421 @@
+/*
+ * zle_bindings.c - commands and keymaps
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * 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 Paul Falstad 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 Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad 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 Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "zle.mdh"
+#include "zle_widget.h"
+
+#include "zle_bindings.pro"
+
+/*
+ * widgets is the table of internally implemented widgets. This
+ * table is not used directly, but each widget in it is referenced
+ * by address from within the table of thingies (below). The only
+ * complication here is that not all systems support union
+ * initialisation.
+ */
+
+static
+#ifdef HAVE_UNION_INIT
+# define BR(X) {X}
+struct widget
+#else /* !HAVE_UNION_INIT */
+# define BR(X) X
+struct intwidget {
+ int flags;
+ Thingy first;
+ ZleIntFunc fn;
+}
+#endif /* !HAVE_UNION_INIT */
+widgets[] = {
+#define W(zle_flags, t_firstname, functionname) \
+ { WIDGET_INT | zle_flags, t_firstname, BR(functionname) },
+#include "widgets.list"
+#undef W
+};
+
+/*
+ * thingies is the table of `known thingies', that exist on startup.
+ * Some bits of ZLE rely on some of these thingies always being the
+ * ones in this table, rather than doing a name lookup and accepting
+ * any semantically identical thingy. The initial reference count of
+ * these thingies is 2: 1 for the widget they name, and 1 extra to
+ * make sure they never get deleted.
+ */
+
+/**/
+struct thingy thingies[] = {
+#define T(name, th_flags, w_idget, t_next) \
+ { NULL, name, th_flags, 2, w_idget, t_next },
+#include "thingies.list"
+#undef T
+ { NULL, NULL, 0, 0, NULL, NULL }
+};
+
+/*
+ * Default key binding tables:
+ *
+ * In these tables, each element is bound to a single thingy, the index
+ * of which in the above table is stored here.
+ */
+
+/**/
+int emacsbind[32] = {
+ /* ^@ */ z_setmarkcommand,
+ /* ^A */ z_beginningofline,
+ /* ^B */ z_backwardchar,
+ /* ^C */ z_undefinedkey,
+ /* ^D */ z_deletecharorlist,
+ /* ^E */ z_endofline,
+ /* ^F */ z_forwardchar,
+ /* ^G */ z_sendbreak,
+ /* ^H */ z_backwarddeletechar,
+ /* ^I */ z_expandorcomplete,
+ /* ^J */ z_acceptline,
+ /* ^K */ z_killline,
+ /* ^L */ z_clearscreen,
+ /* ^M */ z_acceptline,
+ /* ^N */ z_downlineorhistory,
+ /* ^O */ z_acceptlineanddownhistory,
+ /* ^P */ z_uplineorhistory,
+ /* ^Q */ z_pushline,
+ /* ^R */ z_historyincrementalsearchbackward,
+ /* ^S */ z_historyincrementalsearchforward,
+ /* ^T */ z_transposechars,
+ /* ^U */ z_killwholeline,
+ /* ^V */ z_quotedinsert,
+ /* ^W */ z_backwardkillword,
+ /* ^X */ z_undefinedkey,
+ /* ^Y */ z_yank,
+ /* ^Z */ z_undefinedkey,
+ /* ^[ */ z_undefinedkey,
+ /* ^\ */ z_undefinedkey,
+ /* ^] */ z_undefinedkey,
+ /* ^^ */ z_undefinedkey,
+ /* ^_ */ z_undo,
+};
+
+/**/
+int metabind[128] = {
+ /* M-^@ */ z_undefinedkey,
+ /* M-^A */ z_undefinedkey,
+ /* M-^B */ z_undefinedkey,
+ /* M-^C */ z_undefinedkey,
+ /* M-^D */ z_listchoices,
+ /* M-^E */ z_undefinedkey,
+ /* M-^F */ z_undefinedkey,
+ /* M-^G */ z_sendbreak,
+ /* M-^H */ z_backwardkillword,
+ /* M-^I */ z_selfinsertunmeta,
+ /* M-^J */ z_selfinsertunmeta,
+ /* M-^K */ z_undefinedkey,
+ /* M-^L */ z_clearscreen,
+ /* M-^M */ z_selfinsertunmeta,
+ /* M-^N */ z_undefinedkey,
+ /* M-^O */ z_undefinedkey,
+ /* M-^P */ z_undefinedkey,
+ /* M-^Q */ z_undefinedkey,
+ /* M-^R */ z_undefinedkey,
+ /* M-^S */ z_undefinedkey,
+ /* M-^T */ z_undefinedkey,
+ /* M-^U */ z_undefinedkey,
+ /* M-^V */ z_undefinedkey,
+ /* M-^W */ z_undefinedkey,
+ /* M-^X */ z_undefinedkey,
+ /* M-^Y */ z_undefinedkey,
+ /* M-^Z */ z_undefinedkey,
+ /* M-^[ */ z_undefinedkey,
+ /* M-^\ */ z_undefinedkey,
+ /* M-^] */ z_undefinedkey,
+ /* M-^^ */ z_undefinedkey,
+ /* M-^_ */ z_copyprevword,
+ /* M- */ z_expandhistory,
+ /* M-! */ z_expandhistory,
+ /* M-" */ z_quoteregion,
+ /* M-# */ z_undefinedkey,
+ /* M-$ */ z_spellword,
+ /* M-% */ z_undefinedkey,
+ /* M-& */ z_undefinedkey,
+ /* M-' */ z_quoteline,
+ /* M-( */ z_undefinedkey,
+ /* M-) */ z_undefinedkey,
+ /* M-* */ z_undefinedkey,
+ /* M-+ */ z_undefinedkey,
+ /* M-, */ z_undefinedkey,
+ /* M-- */ z_negargument,
+ /* M-. */ z_insertlastword,
+ /* M-/ */ z_undefinedkey,
+ /* M-0 */ z_digitargument,
+ /* M-1 */ z_digitargument,
+ /* M-2 */ z_digitargument,
+ /* M-3 */ z_digitargument,
+ /* M-4 */ z_digitargument,
+ /* M-5 */ z_digitargument,
+ /* M-6 */ z_digitargument,
+ /* M-7 */ z_digitargument,
+ /* M-8 */ z_digitargument,
+ /* M-9 */ z_digitargument,
+ /* M-: */ z_undefinedkey,
+ /* M-; */ z_undefinedkey,
+ /* M-< */ z_beginningofbufferorhistory,
+ /* M-= */ z_undefinedkey,
+ /* M-> */ z_endofbufferorhistory,
+ /* M-? */ z_whichcommand,
+ /* M-@ */ z_undefinedkey,
+ /* M-A */ z_acceptandhold,
+ /* M-B */ z_backwardword,
+ /* M-C */ z_capitalizeword,
+ /* M-D */ z_killword,
+ /* M-E */ z_undefinedkey,
+ /* M-F */ z_forwardword,
+ /* M-G */ z_getline,
+ /* M-H */ z_runhelp,
+ /* M-I */ z_undefinedkey,
+ /* M-J */ z_undefinedkey,
+ /* M-K */ z_undefinedkey,
+ /* M-L */ z_downcaseword,
+ /* M-M */ z_undefinedkey,
+ /* M-N */ z_historybeginningsearchforward,
+ /* M-O */ z_undefinedkey,
+ /* M-P */ z_historybeginningsearchbackward,
+ /* M-Q */ z_pushline,
+ /* M-R */ z_undefinedkey,
+ /* M-S */ z_spellword,
+ /* M-T */ z_transposewords,
+ /* M-U */ z_upcaseword,
+ /* M-V */ z_undefinedkey,
+ /* M-W */ z_copyregionaskill,
+ /* M-X */ z_undefinedkey,
+ /* M-Y */ z_undefinedkey,
+ /* M-Z */ z_undefinedkey,
+ /* M-[ */ z_undefinedkey,
+ /* M-\ */ z_undefinedkey,
+ /* M-] */ z_undefinedkey,
+ /* M-^ */ z_undefinedkey,
+ /* M-_ */ z_insertlastword,
+ /* M-` */ z_undefinedkey,
+ /* M-a */ z_acceptandhold,
+ /* M-b */ z_backwardword,
+ /* M-c */ z_capitalizeword,
+ /* M-d */ z_killword,
+ /* M-e */ z_undefinedkey,
+ /* M-f */ z_forwardword,
+ /* M-g */ z_getline,
+ /* M-h */ z_runhelp,
+ /* M-i */ z_undefinedkey,
+ /* M-j */ z_undefinedkey,
+ /* M-k */ z_undefinedkey,
+ /* M-l */ z_downcaseword,
+ /* M-m */ z_undefinedkey,
+ /* M-n */ z_historybeginningsearchforward,
+ /* M-o */ z_undefinedkey,
+ /* M-p */ z_historybeginningsearchbackward,
+ /* M-q */ z_pushline,
+ /* M-r */ z_undefinedkey,
+ /* M-s */ z_spellword,
+ /* M-t */ z_transposewords,
+ /* M-u */ z_upcaseword,
+ /* M-v */ z_undefinedkey,
+ /* M-w */ z_copyregionaskill,
+ /* M-x */ z_executenamedcmd,
+ /* M-y */ z_yankpop,
+ /* M-z */ z_executelastnamedcmd,
+ /* M-{ */ z_undefinedkey,
+ /* M-| */ z_vigotocolumn,
+ /* M-} */ z_undefinedkey,
+ /* M-~ */ z_undefinedkey,
+ /* M-^? */ z_backwardkillword,
+};
+
+/**/
+int viinsbind[32] = {
+ /* ^@ */ z_undefinedkey,
+ /* ^A */ z_selfinsert,
+ /* ^B */ z_selfinsert,
+ /* ^C */ z_selfinsert,
+ /* ^D */ z_listchoices,
+ /* ^E */ z_selfinsert,
+ /* ^F */ z_selfinsert,
+ /* ^G */ z_listexpand,
+ /* ^H */ z_vibackwarddeletechar,
+ /* ^I */ z_expandorcomplete,
+ /* ^J */ z_acceptline,
+ /* ^K */ z_selfinsert,
+ /* ^L */ z_clearscreen,
+ /* ^M */ z_acceptline,
+ /* ^N */ z_selfinsert,
+ /* ^O */ z_selfinsert,
+ /* ^P */ z_selfinsert,
+ /* ^Q */ z_viquotedinsert,
+ /* ^R */ z_redisplay,
+ /* ^S */ z_selfinsert,
+ /* ^T */ z_selfinsert,
+ /* ^U */ z_vikillline,
+ /* ^V */ z_viquotedinsert,
+ /* ^W */ z_vibackwardkillword,
+ /* ^X */ z_selfinsert,
+ /* ^Y */ z_selfinsert,
+ /* ^Z */ z_selfinsert,
+ /* ^[ */ z_vicmdmode,
+ /* ^\ */ z_selfinsert,
+ /* ^] */ z_selfinsert,
+ /* ^^ */ z_selfinsert,
+ /* ^_ */ z_selfinsert,
+};
+
+/**/
+int vicmdbind[128] = {
+ /* ^@ */ z_undefinedkey,
+ /* ^A */ z_undefinedkey,
+ /* ^B */ z_undefinedkey,
+ /* ^C */ z_undefinedkey,
+ /* ^D */ z_listchoices,
+ /* ^E */ z_undefinedkey,
+ /* ^F */ z_undefinedkey,
+ /* ^G */ z_listexpand,
+ /* ^H */ z_vibackwardchar,
+ /* ^I */ z_undefinedkey,
+ /* ^J */ z_acceptline,
+ /* ^K */ z_undefinedkey,
+ /* ^L */ z_clearscreen,
+ /* ^M */ z_acceptline,
+ /* ^N */ z_downhistory,
+ /* ^O */ z_undefinedkey,
+ /* ^P */ z_uphistory,
+ /* ^Q */ z_undefinedkey,
+ /* ^R */ z_redisplay,
+ /* ^S */ z_undefinedkey,
+ /* ^T */ z_undefinedkey,
+ /* ^U */ z_undefinedkey,
+ /* ^V */ z_undefinedkey,
+ /* ^W */ z_undefinedkey,
+ /* ^X */ z_undefinedkey,
+ /* ^Y */ z_undefinedkey,
+ /* ^Z */ z_undefinedkey,
+ /* ^[ */ z_undefinedkey,
+ /* ^\ */ z_undefinedkey,
+ /* ^] */ z_undefinedkey,
+ /* ^^ */ z_undefinedkey,
+ /* ^_ */ z_undefinedkey,
+ /* */ z_viforwardchar,
+ /* ! */ z_undefinedkey,
+ /* " */ z_visetbuffer,
+ /* # */ z_poundinsert,
+ /* $ */ z_viendofline,
+ /* % */ z_vimatchbracket,
+ /* & */ z_undefinedkey,
+ /* ' */ z_vigotomarkline,
+ /* ( */ z_undefinedkey,
+ /* ) */ z_undefinedkey,
+ /* * */ z_undefinedkey,
+ /* + */ z_vidownlineorhistory,
+ /* , */ z_virevrepeatfind,
+ /* - */ z_viuplineorhistory,
+ /* . */ z_virepeatchange,
+ /* / */ z_vihistorysearchbackward,
+ /* 0 */ z_vidigitorbeginningofline,
+ /* 1 */ z_digitargument,
+ /* 2 */ z_digitargument,
+ /* 3 */ z_digitargument,
+ /* 4 */ z_digitargument,
+ /* 5 */ z_digitargument,
+ /* 6 */ z_digitargument,
+ /* 7 */ z_digitargument,
+ /* 8 */ z_digitargument,
+ /* 9 */ z_digitargument,
+ /* : */ z_undefinedkey,
+ /* ; */ z_virepeatfind,
+ /* < */ z_viunindent,
+ /* = */ z_listchoices,
+ /* > */ z_viindent,
+ /* ? */ z_vihistorysearchforward,
+ /* @ */ z_undefinedkey,
+ /* A */ z_viaddeol,
+ /* B */ z_vibackwardblankword,
+ /* C */ z_vichangeeol,
+ /* D */ z_vikilleol,
+ /* E */ z_viforwardblankwordend,
+ /* F */ z_vifindprevchar,
+ /* G */ z_vifetchhistory,
+ /* H */ z_undefinedkey,
+ /* I */ z_viinsertbol,
+ /* J */ z_vijoin,
+ /* K */ z_undefinedkey,
+ /* L */ z_undefinedkey,
+ /* M */ z_undefinedkey,
+ /* N */ z_virevrepeatsearch,
+ /* O */ z_viopenlineabove,
+ /* P */ z_viputbefore,
+ /* Q */ z_undefinedkey,
+ /* R */ z_vireplace,
+ /* S */ z_vichangewholeline,
+ /* T */ z_vifindprevcharskip,
+ /* U */ z_undefinedkey,
+ /* V */ z_undefinedkey,
+ /* W */ z_viforwardblankword,
+ /* X */ z_vibackwarddeletechar,
+ /* Y */ z_viyankwholeline,
+ /* Z */ z_undefinedkey,
+ /* [ */ z_undefinedkey,
+ /* \ */ z_undefinedkey,
+ /* ] */ z_undefinedkey,
+ /* ^ */ z_vifirstnonblank,
+ /* _ */ z_undefinedkey,
+ /* ` */ z_vigotomark,
+ /* a */ z_viaddnext,
+ /* b */ z_vibackwardword,
+ /* c */ z_vichange,
+ /* d */ z_videlete,
+ /* e */ z_viforwardwordend,
+ /* f */ z_vifindnextchar,
+ /* g */ z_undefinedkey,
+ /* h */ z_vibackwardchar,
+ /* i */ z_viinsert,
+ /* j */ z_downlineorhistory,
+ /* k */ z_uplineorhistory,
+ /* l */ z_viforwardchar,
+ /* m */ z_visetmark,
+ /* n */ z_virepeatsearch,
+ /* o */ z_viopenlinebelow,
+ /* p */ z_viputafter,
+ /* q */ z_undefinedkey,
+ /* r */ z_vireplacechars,
+ /* s */ z_visubstitute,
+ /* t */ z_vifindnextcharskip,
+ /* u */ z_viundochange,
+ /* v */ z_undefinedkey,
+ /* w */ z_viforwardword,
+ /* x */ z_videletechar,
+ /* y */ z_viyank,
+ /* z */ z_undefinedkey,
+ /* { */ z_undefinedkey,
+ /* | */ z_vigotocolumn,
+ /* } */ z_undefinedkey,
+ /* ~ */ z_viswapcase,
+ /* ^? */ z_vibackwardchar,
+};
diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c
new file mode 100644
index 000000000..76e421c1c
--- /dev/null
+++ b/Src/Zle/zle_hist.c
@@ -0,0 +1,1139 @@
+/*
+ * zle_hist.c - history editing
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * 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 Paul Falstad 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 Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad 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 Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "zle.mdh"
+#include "zle_hist.pro"
+
+/* Are references to earlier history lines permitted? == 0 if *
+ * editing or reading a standalone line, such as in vared or select. */
+
+/**/
+int histallowed;
+
+/* Column position of vi ideal cursor. -1 if it is unknown -- most *
+ * movements and changes do this. */
+
+/**/
+int lastcol;
+
+/* current history line number */
+
+/**/
+int histline;
+
+/* the last line in the history (the current one), metafied */
+
+/**/
+char *curhistline;
+
+/**/
+void
+remember_edits(void)
+{
+ if (histline == curhist) {
+ zsfree(curhistline);
+ curhistline = metafy((char *) line, ll, META_DUP);
+ }
+ else {
+ Histent ent = gethistent(histline);
+
+ if (metadiffer(ent->zle_text ? ent->zle_text : ent->text,
+ (char *) line, ll)) {
+ zsfree(ent->zle_text);
+ ent->zle_text = metafy((char *) line, ll, META_DUP);
+ }
+ }
+}
+
+/**/
+void
+forget_edits(void)
+{
+ int i;
+
+ for (i = 0; i < histentct; i++) {
+ zsfree(histentarr[i].zle_text);
+ histentarr[i].zle_text = NULL;
+ }
+}
+
+/**/
+void
+uphistory(void)
+{
+ if (zmult < 0) {
+ zmult = -zmult;
+ downhistory();
+ zmult = -zmult;
+ } else if(!zle_goto_hist(histline - zmult) && isset(HISTBEEP))
+ feep();
+}
+
+/**/
+int
+upline(void)
+{
+ int n = zmult;
+
+ if (n < 0) {
+ zmult = -zmult;
+ n = downline();
+ zmult = -zmult;
+ return n;
+ }
+ if (lastcol == -1)
+ lastcol = cs - findbol();
+ cs = findbol();
+ while (n) {
+ if (!cs)
+ break;
+ cs--;
+ cs = findbol();
+ n--;
+ }
+ if (!n) {
+ int x = findeol();
+
+ if ((cs += lastcol) >= x) {
+ cs = x;
+ if (cs > findbol() && invicmdmode())
+ cs--;
+ }
+ }
+ return n;
+}
+
+/**/
+void
+uplineorhistory(void)
+{
+ int ocs = cs;
+ int n = upline();
+ if (n) {
+ int m = zmult;
+
+ cs = ocs;
+ if (virangeflag || !histallowed) {
+ feep();
+ return;
+ }
+ zmult = n;
+ uphistory();
+ zmult = m;
+ }
+}
+
+/**/
+void
+viuplineorhistory(void)
+{
+ int col = lastcol;
+ uplineorhistory();
+ lastcol = col;
+ vifirstnonblank();
+}
+
+
+/**/
+void
+uplineorsearch(void)
+{
+ int ocs = cs;
+ int n = upline();
+ if (n) {
+ int m = zmult;
+
+ cs = ocs;
+ if (virangeflag || !histallowed) {
+ feep();
+ return;
+ }
+ zmult = n;
+ historysearchbackward();
+ zmult = m;
+ }
+}
+
+/**/
+int
+downline(void)
+{
+ int n = zmult;
+
+ if (n < 0) {
+ zmult = -zmult;
+ n = upline();
+ zmult = -zmult;
+ return n;
+ }
+ if (lastcol == -1)
+ lastcol = cs - findbol();
+ while (n) {
+ int x = findeol();
+
+ if (x == ll)
+ break;
+ cs = x + 1;
+ n--;
+ }
+ if (!n) {
+ int x = findeol();
+
+ if ((cs += lastcol) >= x) {
+ cs = x;
+ if (cs > findbol() && invicmdmode())
+ cs--;
+ }
+ }
+ return n;
+}
+
+/**/
+void
+downlineorhistory(void)
+{
+ int ocs = cs;
+ int n = downline();
+ if (n) {
+ int m = zmult;
+
+ cs = ocs;
+ if (virangeflag || !histallowed) {
+ feep();
+ return;
+ }
+ zmult = n;
+ downhistory();
+ zmult = m;
+ }
+}
+
+/**/
+void
+vidownlineorhistory(void)
+{
+ int col = lastcol;
+ downlineorhistory();
+ lastcol = col;
+ vifirstnonblank();
+}
+
+/**/
+void
+downlineorsearch(void)
+{
+ int ocs = cs;
+ int n = downline();
+ if (n) {
+ int m = zmult;
+
+ cs = ocs;
+ if (virangeflag || !histallowed) {
+ feep();
+ return;
+ }
+ zmult = n;
+ historysearchforward();
+ zmult = m;
+ }
+}
+
+/**/
+void
+acceptlineanddownhistory(void)
+{
+ char *s;
+
+ if (!(s = zle_get_event(histline + 1))) {
+ feep();
+ return;
+ }
+ pushnode(bufstack, ztrdup(s));
+ done = 1;
+ stackhist = histline + 1;
+}
+
+/**/
+void
+downhistory(void)
+{
+ if (zmult < 0) {
+ zmult = -zmult;
+ uphistory();
+ zmult = -zmult;
+ } else if(!zle_goto_hist(histline + zmult) && isset(HISTBEEP))
+ feep();
+}
+
+/**/
+void
+historysearchbackward(void)
+{
+ int histpos, histmpos, hl = histline;
+ int n = zmult;
+ char *s;
+
+ if (!n)
+ return;
+ if (n < 0) {
+ zmult = -n;
+ historysearchforward();
+ zmult = n;
+ return;
+ }
+ for (histpos = histmpos = 0; histpos < ll && !iblank(line[histpos]);
+ histpos++, histmpos++)
+ if(imeta(line[histpos]))
+ histmpos++;
+ for (;;) {
+ hl--;
+ if (!(s = zle_get_event(hl))) {
+ feep();
+ return;
+ }
+ if (metadiffer(s, (char *) line, histpos) < 0 &&
+ iblank(s[histmpos] == Meta ? s[histmpos+1]^32 : s[histmpos]) &&
+ metadiffer(s, (char *) line, ll) && !--n)
+ break;
+ }
+ zle_goto_hist(hl);
+}
+
+/**/
+void
+historysearchforward(void)
+{
+ int histpos, histmpos, hl = histline;
+ int n = zmult;
+ char *s;
+
+ if (!n)
+ return;
+ if (n < 0) {
+ zmult = -n;
+ historysearchbackward();
+ zmult = n;
+ return;
+ }
+ for (histpos = histmpos = 0; histpos < ll && !iblank(line[histpos]);
+ histpos++, histmpos++)
+ if(imeta(line[histpos]))
+ histmpos++;
+ for (;;) {
+ hl++;
+ if (!(s = zle_get_event(hl))) {
+ feep();
+ return;
+ }
+ if (metadiffer(s, (char *) line, histpos) < (histline == curhist) &&
+ (!s[histmpos] ||
+ iblank(s[histmpos] == Meta ? s[histmpos+1]^32 : s[histmpos])) &&
+ metadiffer(s, (char *) line, ll) && !--n)
+ break;
+ }
+ zle_goto_hist(hl);
+}
+
+/**/
+void
+beginningofbufferorhistory(void)
+{
+ if (findbol())
+ cs = 0;
+ else
+ beginningofhistory();
+}
+
+/**/
+void
+beginningofhistory(void)
+{
+ if (!zle_goto_hist(firsthist()) && isset(HISTBEEP))
+ feep();
+}
+
+/**/
+void
+endofbufferorhistory(void)
+{
+ if (findeol() != ll)
+ cs = ll;
+ else
+ endofhistory();
+}
+
+/**/
+void
+endofhistory(void)
+{
+ zle_goto_hist(curhist);
+}
+
+/**/
+void
+insertlastword(void)
+{
+ int n;
+ char *s, *t;
+ Histent he;
+
+/* multiple calls will now search back through the history, pem */
+ static char *lastinsert;
+ static int lasthist, lastpos;
+ int evhist = curhist - 1, save;
+
+ if (lastinsert) {
+ int lastlen = ztrlen(lastinsert);
+ int pos = cs;
+
+ if (lastpos <= pos &&
+ lastlen == pos - lastpos &&
+ memcmp(lastinsert, (char *)&line[lastpos], lastlen) == 0) {
+ evhist = --lasthist;
+ cs = lastpos;
+ foredel(pos - cs);
+ }
+ zsfree(lastinsert);
+ lastinsert = NULL;
+ }
+ if (!(he = quietgethist(evhist)) || !he->nwords) {
+ feep();
+ return;
+ }
+ if (zmult > 0) {
+ n = he->nwords - (zmult - 1);
+ } else {
+ n = 1 - zmult;
+ }
+ if (n < 1 || n > he->nwords) {
+ feep();
+ return;
+ }
+ s = he->text + he->words[2*n-2];
+ t = he->text + he->words[2*n-1];
+ save = *t;
+ *t = '\0'; /* ignore trailing whitespace */
+
+ lasthist = evhist;
+ lastpos = cs;
+ lastinsert = ztrdup(s);
+ n = zmult;
+ zmult = 1;
+ doinsert(s);
+ zmult = n;
+ *t = save;
+}
+
+/**/
+char *
+qgetevent(int ev)
+{
+ return ((ev == curhist) ? curhistline : quietgetevent(ev));
+}
+
+/**/
+char *
+zle_get_event(int ev)
+{
+ Histent ent;
+
+ if (ev == curhist)
+ return curhistline;
+ if (! (ent = quietgethist(ev)))
+ return NULL;
+ if (ent->zle_text)
+ return ent->zle_text;
+ return ent->text;
+}
+
+/**/
+static int
+zle_goto_hist(int ev)
+{
+ char *t;
+
+ remember_edits();
+ if(!(t = zle_get_event(ev)))
+ return 0;
+ mkundoent();
+ histline = ev;
+ setline(t);
+ setlastline();
+ return 1;
+}
+
+/**/
+void
+pushline(void)
+{
+ int n = zmult;
+
+ if (n < 0)
+ return;
+ pushnode(bufstack, metafy((char *) line, ll, META_DUP));
+ while (--n)
+ pushnode(bufstack, ztrdup(""));
+ stackcs = cs;
+ *line = '\0';
+ ll = cs = 0;
+}
+
+/**/
+void
+pushlineoredit(void)
+{
+ int ics;
+ unsigned char *s;
+ char *hline = hgetline();
+
+ if (zmult < 0)
+ return;
+ if (hline && *hline) {
+ ics = ztrlen(hline);
+ sizeline(ics + ll + 1);
+ for (s = line + ll; --s >= line; *(s + ics) = *s);
+ for (s = line; *hline; hline++)
+ *s++ = *hline == Meta ? *++hline ^ 32 : *hline;
+ ll += ics;
+ cs += ics;
+ }
+ pushline();
+ if (!isfirstln) {
+ errflag = done = 1;
+ }
+}
+
+/**/
+void
+pushinput(void)
+{
+ int i;
+
+ if (zmult < 0)
+ return;
+ zmult += i = !isfirstln;
+ pushlineoredit();
+ zmult -= i;
+}
+
+/**/
+void
+getline(void)
+{
+ char *s = (char *)getlinknode(bufstack);
+
+ if (!s)
+ feep();
+ else {
+ int cc;
+
+ unmetafy(s, &cc);
+ spaceinline(cc);
+ memcpy((char *)line + cs, s, cc);
+ cs += cc;
+ free(s);
+ }
+}
+
+/**/
+void
+historyincrementalsearchbackward(void)
+{
+ doisearch(-1);
+}
+
+/**/
+void
+historyincrementalsearchforward(void)
+{
+ doisearch(1);
+}
+
+static struct isrch_spot {
+ int hl; /* This spot's histline */
+ unsigned short pos; /* The search position in our metafied str */
+ unsigned short cs; /* The visible search position to the user */
+ unsigned short len; /* The search string's length */
+ unsigned short flags; /* This spot's flags */
+#define ISS_FAILING 1
+#define ISS_FORWARD 2
+} *isrch_spots;
+
+static int max_spot = 0;
+
+#ifdef MODULE
+
+/**/
+void
+free_isrch_spots(void)
+{
+ zfree(isrch_spots, max_spot * sizeof(*isrch_spots));
+}
+
+#endif /* MODULE */
+
+/**/
+static void
+set_isrch_spot(int num, int hl, int pos, int cs, int len, int dir, int nomatch)
+{
+ if (num >= max_spot) {
+ if (!isrch_spots) {
+ isrch_spots = (struct isrch_spot*)
+ zalloc((max_spot = 64) * sizeof *isrch_spots);
+ } else {
+ isrch_spots = (struct isrch_spot*)realloc((char*)isrch_spots,
+ (max_spot += 64) * sizeof *isrch_spots);
+ }
+ }
+
+ isrch_spots[num].hl = hl;
+ isrch_spots[num].pos = (unsigned short)pos;
+ isrch_spots[num].cs = (unsigned short)cs;
+ isrch_spots[num].len = (unsigned short)len;
+ isrch_spots[num].flags = (dir > 0? ISS_FORWARD : 0)
+ + (nomatch? ISS_FAILING : 0);
+}
+
+/**/
+static void
+get_isrch_spot(int num, int *hlp, int *posp, int *csp, int *lenp, int *dirp, int *nomatch)
+{
+ *hlp = isrch_spots[num].hl;
+ *posp = (int)isrch_spots[num].pos;
+ *csp = (int)isrch_spots[num].cs;
+ *lenp = (int)isrch_spots[num].len;
+ *dirp = (isrch_spots[num].flags & ISS_FORWARD)? 1 : -1;
+ *nomatch = (isrch_spots[num].flags & ISS_FAILING);
+}
+
+#define ISEARCH_PROMPT "failing XXX-i-search: "
+#define NORM_PROMPT_POS 8
+#define FIRST_SEARCH_CHAR (NORM_PROMPT_POS + 14)
+
+/**/
+static void
+doisearch(int dir)
+{
+ char *s, *ibuf = halloc(80), *sbuf = ibuf + FIRST_SEARCH_CHAR;
+ int sbptr = 0, top_spot = 0, pos, sibuf = 80;
+ int nomatch = 0, skip_line = 0, skip_pos = 0;
+ int odir = dir, sens = zmult == 1 ? 3 : 1;
+ int hl = histline;
+ Thingy cmd;
+ char *okeymap = curkeymapname;
+ static char *previous_search = NULL;
+ static int previous_search_len = 0;
+
+ strcpy(ibuf, ISEARCH_PROMPT);
+ memcpy(ibuf + NORM_PROMPT_POS, (dir == 1) ? "fwd" : "bck", 3);
+ remember_edits();
+ s = zle_get_event(hl);
+ selectkeymap("main", 1);
+ pos = metalen(s, cs);
+ for (;;) {
+ /* Remember the current values in case search fails (doesn't push). */
+ set_isrch_spot(top_spot, hl, pos, cs, sbptr, dir, nomatch);
+ if (sbptr == 1 && sbuf[0] == '^') {
+ cs = 0;
+ nomatch = 0;
+ statusline = ibuf + NORM_PROMPT_POS;
+ } else if (sbptr > 0) {
+ char *last_line = s;
+
+ for (;;) {
+ char *t;
+
+ if (skip_pos) {
+ if (dir < 0) {
+ if (pos == 0)
+ skip_line = 1;
+ else
+ pos -= 1 + (pos != 1 && s[pos-2] == Meta);
+ } else if (sbuf[0] != '^') {
+ if (pos >= strlen(s+1))
+ skip_line = 1;
+ else
+ pos += 1 + (s[pos] == Meta);
+ } else
+ skip_line = 1;
+ skip_pos = 0;
+ }
+ if (!skip_line && ((sbuf[0] == '^') ?
+ (t = metadiffer(s, sbuf + 1, sbptr - 1) < sens ? s : NULL) :
+ (t = hstrnstr(s, pos, sbuf, sbptr, dir, sens)))) {
+ zle_goto_hist(hl);
+ pos = t - s;
+ cs = ztrsub(t, s) + (dir == 1? sbptr - (sbuf[0]=='^') : 0);
+ nomatch = 0;
+ statusline = ibuf + NORM_PROMPT_POS;
+ break;
+ }
+ hl += dir;
+ if (!(s = zle_get_event(hl))) {
+ if (sbptr == (int)isrch_spots[top_spot-1].len
+ && (isrch_spots[top_spot-1].flags & ISS_FAILING))
+ top_spot--;
+ get_isrch_spot(top_spot, &hl, &pos, &cs, &sbptr,
+ &dir, &nomatch);
+ if (!nomatch) {
+ feep();
+ nomatch = 1;
+ }
+ s = last_line;
+ skip_line = 0;
+ statusline = ibuf;
+ break;
+ }
+ pos = dir == 1? 0 : strlen(s);
+ skip_line = !strcmp(last_line, s);
+ }
+ } else {
+ top_spot = 0;
+ nomatch = 0;
+ statusline = ibuf + NORM_PROMPT_POS;
+ }
+ sbuf[sbptr] = '_';
+ statusll = sbuf - statusline + sbptr + 1;
+ ref:
+ refresh();
+ if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) {
+ int i;
+ get_isrch_spot(0, &hl, &pos, &i, &sbptr, &dir, &nomatch);
+ s = zle_get_event(hl);
+ zle_goto_hist(hl);
+ cs = i;
+ break;
+ }
+ if(cmd == Th(z_clearscreen)) {
+ clearscreen();
+ goto ref;
+ } else if(cmd == Th(z_redisplay)) {
+ redisplay();
+ goto ref;
+ } else if(cmd == Th(z_vicmdmode)) {
+ if(selectkeymap(invicmdmode() ? "main" : "vicmd", 0))
+ feep();
+ goto ref;
+ } else if(cmd == Th(z_vibackwarddeletechar) ||
+ cmd == Th(z_backwarddeletechar)) {
+ if (top_spot)
+ get_isrch_spot(--top_spot, &hl, &pos, &cs, &sbptr,
+ &dir, &nomatch);
+ else
+ feep();
+ if (nomatch) {
+ statusline = ibuf;
+ skip_pos = 1;
+ }
+ s = zle_get_event(hl);
+ if (nomatch || !sbptr || (sbptr == 1 && sbuf[0] == '^')) {
+ int i = cs;
+ zle_goto_hist(hl);
+ cs = i;
+ }
+ memcpy(ibuf + NORM_PROMPT_POS, (dir == 1) ? "fwd" : "bck", 3);
+ continue;
+ } else if(cmd == Th(z_acceptandhold)) {
+ acceptandhold();
+ break;
+ } else if(cmd == Th(z_acceptandinfernexthistory)) {
+ acceptandinfernexthistory();
+ break;
+ } else if(cmd == Th(z_acceptlineanddownhistory)) {
+ acceptlineanddownhistory();
+ break;
+ } else if(cmd == Th(z_acceptline)) {
+ acceptline();
+ break;
+ } else if(cmd == Th(z_historyincrementalsearchbackward)) {
+ set_isrch_spot(top_spot++, hl, pos, cs, sbptr, dir, nomatch);
+ if (dir != -1)
+ dir = -1;
+ else
+ skip_pos = 1;
+ goto rpt;
+ } else if(cmd == Th(z_historyincrementalsearchforward)) {
+ set_isrch_spot(top_spot++, hl, pos, cs, sbptr, dir, nomatch);
+ if (dir != 1)
+ dir = 1;
+ else
+ skip_pos = 1;
+ goto rpt;
+ } else if(cmd == Th(z_virevrepeatsearch)) {
+ set_isrch_spot(top_spot++, hl, pos, cs, sbptr, dir, nomatch);
+ dir = -odir;
+ skip_pos = 1;
+ goto rpt;
+ } else if(cmd == Th(z_virepeatsearch)) {
+ set_isrch_spot(top_spot++, hl, pos, cs, sbptr, dir, nomatch);
+ dir = odir;
+ skip_pos = 1;
+ rpt:
+ if (!sbptr && previous_search_len) {
+ if (previous_search_len > sibuf - FIRST_SEARCH_CHAR - 2) {
+ ibuf = hrealloc(ibuf, sibuf, sibuf + previous_search_len);
+ sbuf = ibuf + FIRST_SEARCH_CHAR;
+ sibuf += previous_search_len;
+ }
+ memcpy(sbuf, previous_search, sbptr = previous_search_len);
+ }
+ memcpy(ibuf + NORM_PROMPT_POS, (dir == 1) ? "fwd" : "bck", 3);
+ continue;
+ } else if(cmd == Th(z_viquotedinsert) ||
+ cmd == Th(z_quotedinsert)) {
+ if(cmd == Th(z_viquotedinsert)) {
+ sbuf[sbptr] = '^';
+ refresh();
+ }
+ if ((c = getkey(0)) == EOF)
+ feep();
+ else
+ goto ins;
+ } else {
+ if(cmd == Th(z_selfinsertunmeta)) {
+ c &= 0x7f;
+ if(c == '\r')
+ c = '\n';
+ } else if (cmd == Th(z_magicspace))
+ c = ' ';
+ else if (cmd != Th(z_selfinsert)) {
+ ungetkeycmd();
+ if (cmd == Th(z_sendbreak))
+ sbptr = 0;
+ break;
+ }
+ ins:
+ if (sbptr == PATH_MAX) {
+ feep();
+ continue;
+ }
+ set_isrch_spot(top_spot++, hl, pos, cs, sbptr, dir, nomatch);
+ if (sbptr == sibuf - FIRST_SEARCH_CHAR - 2) {
+ ibuf = hrealloc(ibuf, sibuf, sibuf * 2);
+ sbuf = ibuf + FIRST_SEARCH_CHAR;
+ sibuf *= 2;
+ }
+ sbuf[sbptr++] = c;
+ }
+ handlefeep();
+ }
+ if (sbptr) {
+ zfree(previous_search, previous_search_len);
+ previous_search = zalloc(sbptr);
+ memcpy(previous_search, sbuf, previous_search_len = sbptr);
+ }
+ statusline = NULL;
+ selectkeymap(okeymap, 1);
+}
+
+/**/
+void
+acceptandinfernexthistory(void)
+{
+ int t0;
+ char *s;
+
+ done = 1;
+ for (t0 = histline - 2;; t0--) {
+ if (!(s = qgetevent(t0)))
+ return;
+ if (!metadiffer(s, (char *) line, ll))
+ break;
+ }
+ if (!(s = qgetevent(t0 + 1)))
+ return;
+ pushnode(bufstack, ztrdup(s));
+ stackhist = t0 + 1;
+}
+
+/**/
+void
+infernexthistory(void)
+{
+ int t0;
+ char *s;
+
+ for (t0 = histline - 2;; t0--) {
+ if (!(s = qgetevent(t0))) {
+ feep();
+ return;
+ }
+ if (! metadiffer(s, (char *) line, ll))
+ break;
+ }
+ if (!(s = qgetevent(t0 + 1))) {
+ feep();
+ return;
+ }
+ zle_goto_hist(t0 + 1);
+}
+
+/**/
+void
+vifetchhistory(void)
+{
+ if (zmult < 0)
+ return;
+ if (histline == curhist) {
+ if (!(zmod.flags & MOD_MULT)) {
+ cs = ll;
+ cs = findbol();
+ return;
+ }
+ }
+ if (!zle_goto_hist((zmod.flags & MOD_MULT) ? zmult : curhist) &&
+ isset(HISTBEEP))
+ feep();
+}
+
+/* the last vi search */
+
+static char *visrchstr;
+static int visrchsense;
+
+/**/
+static int
+getvisrchstr(void)
+{
+ char *sbuf = halloc(80);
+ int sptr = 1, ret = 0, ssbuf = 80;
+ Thingy cmd;
+ char *okeymap = curkeymapname;
+
+ if (visrchstr) {
+ zsfree(visrchstr);
+ visrchstr = NULL;
+ }
+ statusline = sbuf;
+ sbuf[0] = (visrchsense == -1) ? '?' : '/';
+ selectkeymap("main", 1);
+ while (sptr) {
+ sbuf[sptr] = '_';
+ statusll = sptr + 1;
+ refresh();
+ if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) {
+ ret = 0;
+ break;
+ }
+ if(cmd == Th(z_magicspace)) {
+ c = ' ';
+ cmd = Th(z_selfinsert);
+ }
+ if(cmd == Th(z_redisplay)) {
+ redisplay();
+ } else if(cmd == Th(z_clearscreen)) {
+ clearscreen();
+ } else if(cmd == Th(z_acceptline) ||
+ cmd == Th(z_vicmdmode)) {
+ sbuf[sptr] = 0;
+ visrchstr = metafy(sbuf + 1, sptr - 1, META_DUP);
+ ret = 1;
+ sptr = 0;
+ } else if(cmd == Th(z_backwarddeletechar) ||
+ cmd == Th(z_vibackwarddeletechar)) {
+ sptr--;
+ } else if(cmd == Th(z_backwardkillword) ||
+ cmd == Th(z_vibackwardkillword)) {
+ while(sptr != 1 && iblank(sbuf[sptr - 1]))
+ sptr--;
+ if(iident(sbuf[sptr - 1]))
+ while(sptr != 1 && iident(sbuf[sptr - 1]))
+ sptr--;
+ else
+ while(sptr != 1 && !iident(sbuf[sptr - 1]) && !iblank(sbuf[sptr - 1]))
+ sptr--;
+ } else if(cmd == Th(z_viquotedinsert) || cmd == Th(z_quotedinsert)) {
+ if(cmd == Th(z_viquotedinsert)) {
+ sbuf[sptr] = '^';
+ refresh();
+ }
+ if ((c = getkey(0)) == EOF)
+ feep();
+ else
+ goto ins;
+ } else if(cmd == Th(z_selfinsertunmeta) || cmd == Th(z_selfinsert)) {
+ if(cmd == Th(z_selfinsertunmeta)) {
+ c &= 0x7f;
+ if(c == '\r')
+ c = '\n';
+ }
+ ins:
+ if(sptr == ssbuf - 1) {
+ char *newbuf = halloc(ssbuf *= 2);
+ strcpy(newbuf, sbuf);
+ statusline = sbuf = newbuf;
+ }
+ sbuf[sptr++] = c;
+ } else {
+ feep();
+ }
+ handlefeep();
+ }
+ statusline = NULL;
+ selectkeymap(okeymap, 1);
+ return ret;
+}
+
+/**/
+void
+vihistorysearchforward(void)
+{
+ visrchsense = 1;
+ if (getvisrchstr())
+ virepeatsearch();
+}
+
+/**/
+void
+vihistorysearchbackward(void)
+{
+ visrchsense = -1;
+ if (getvisrchstr())
+ virepeatsearch();
+}
+
+/**/
+void
+virepeatsearch(void)
+{
+ int hl = histline, t0;
+ int n = zmult;
+ char *s;
+
+ if (!visrchstr) {
+ feep();
+ return;
+ }
+ if (!n)
+ return;
+ if (n < 0) {
+ n = -n;
+ visrchsense = -visrchsense;
+ }
+ t0 = strlen(visrchstr);
+ for (;;) {
+ hl += visrchsense;
+ if (!(s = zle_get_event(hl))) {
+ feep();
+ return;
+ }
+ if (!metadiffer(s, (char *) line, ll))
+ continue;
+ if (*visrchstr == '^') {
+ if (strncmp(s, visrchstr + 1, t0 - 1) != 0)
+ continue;
+ } else if (!hstrnstr(s, 0, visrchstr, t0, 1, 1))
+ continue;
+ if (--n <= 0)
+ break;
+ }
+ zle_goto_hist(hl);
+}
+
+/**/
+void
+virevrepeatsearch(void)
+{
+ visrchsense = -visrchsense;
+ virepeatsearch();
+ visrchsense = -visrchsense;
+}
+
+/* Extra function added by A.R. Iano-Fletcher. */
+/*The extern variable "cs" is the position of the cursor. */
+/* history-beginning-search-backward */
+
+/**/
+void
+historybeginningsearchbackward(void)
+{
+ int cpos = cs; /* save cursor position */
+ int hl = histline;
+ int n = zmult;
+ char *s;
+
+ if (!n)
+ return;
+ if (n < 0) {
+ zmult = -n;
+ historybeginningsearchforward();
+ zmult = n;
+ return;
+ }
+ for (;;) {
+ hl--;
+ if (!(s = zle_get_event(hl))) {
+ feep();
+ return;
+ }
+ if (metadiffer(s, (char *)line, cs) < 0 &&
+ metadiffer(s, (char *)line, ll))
+ if (--n <= 0)
+ break;
+ }
+
+ zle_goto_hist(hl);
+ cs = cpos;
+}
+
+/* Extra function added by A.R. Iano-Fletcher. */
+
+/* history-beginning-search-forward */
+/**/
+void
+historybeginningsearchforward(void)
+{
+ int cpos = cs; /* save cursor position */
+ int hl = histline;
+ int n = zmult;
+ char *s;
+
+ if (!n)
+ return;
+ if (n < 0) {
+ zmult = -n;
+ historybeginningsearchbackward();
+ zmult = n;
+ return;
+ }
+ for (;;) {
+ hl++;
+ if (!(s = zle_get_event(hl))) {
+ feep();
+ return;
+ }
+ if (metadiffer(s, (char *)line, cs) < (hl == curhist) &&
+ metadiffer(s, (char *)line, ll))
+ if (--n <= 0)
+ break;
+ }
+
+ zle_goto_hist(hl);
+ cs = cpos;
+}
diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
new file mode 100644
index 000000000..7de96bd03
--- /dev/null
+++ b/Src/Zle/zle_keymap.c
@@ -0,0 +1,1238 @@
+/*
+ * zle_keymap.c - keymaps and key bindings
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * 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 Paul Falstad 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 Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad 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 Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "zle.mdh"
+
+/*
+ * Keymap structures:
+ *
+ * There is a hash table of keymap names. Each name just points to a keymap.
+ * More than one name may point to the same keymap.
+ *
+ * Each keymap consists of a table of bindings for each character, and a
+ * hash table of multi-character key bindings. The keymap has no individual
+ * name, but maintains a reference count.
+ *
+ * In a keymap's table of initial bindings, each character is either bound to
+ * a thingy, or is a prefix (in which case NULL is stored). Those prefix
+ * entries are matched by more complex entries in the multi-character
+ * binding hash table. Each entry in this hash table (which is indexed by
+ * metafied key sequence) either has a normal thingy binding or a string to
+ * send (in which case the NULL thingy is used). Each entry also has a count
+ * of other entries for which it is a prefix.
+ */
+
+typedef struct keymapname *KeymapName;
+typedef struct key *Key;
+
+struct keymapname {
+ HashNode next; /* next in the hash chain */
+ char *nam; /* name of the keymap */
+ int flags; /* various flags (see below) */
+ Keymap keymap; /* the keymap itsef */
+};
+
+#define KMN_IMMORTAL (1<<1)
+
+struct keymap {
+ Thingy first[256]; /* base binding of each character */
+ HashTable multi; /* multi-character bindings */
+ int flags; /* various flags (see below) */
+ int rc; /* reference count */
+};
+
+#define KM_IMMUTABLE (1<<1)
+
+struct key {
+ HashNode next; /* next in hash chain */
+ char *nam; /* key sequence (metafied) */
+ Thingy bind; /* binding of this key sequence */
+ char *str; /* string for send-string (metafied) */
+ int prefixct; /* number of sequences for which this is a prefix */
+};
+
+/* This structure is used when listing keymaps. */
+
+struct bindstate {
+ int flags;
+ char *kmname;
+ char *firstseq;
+ char *lastseq;
+ Thingy bind;
+ char *str;
+};
+
+#define BS_LIST (1<<0)
+#define BS_ALL (1<<1)
+
+/* local functions */
+
+#include "zle_keymap.pro"
+
+/* currently selected keymap, and its name */
+
+/**/
+Keymap curkeymap;
+/**/
+char *curkeymapname;
+
+/* the hash table of keymap names */
+
+static HashTable keymapnamtab;
+
+/* key sequence reading data */
+
+static char *keybuf;
+static int keybuflen, keybufsz = 20;
+
+/* last command executed with execute-named-command */
+
+static Thingy lastnamed;
+
+/**********************************/
+/* hashtable management functions */
+/**********************************/
+
+/**/
+static void
+createkeymapnamtab(void)
+{
+ keymapnamtab = newhashtable(7, "keymapnamtab", NULL);
+
+ keymapnamtab->hash = hasher;
+ keymapnamtab->emptytable = emptyhashtable;
+ keymapnamtab->filltable = NULL;
+ keymapnamtab->addnode = addhashnode;
+ keymapnamtab->getnode = gethashnode2;
+ keymapnamtab->getnode2 = gethashnode2;
+ keymapnamtab->removenode = removehashnode;
+ keymapnamtab->disablenode = NULL;
+ keymapnamtab->enablenode = NULL;
+ keymapnamtab->freenode = freekeymapnamnode;
+ keymapnamtab->printnode = NULL;
+}
+
+/**/
+static KeymapName
+makekeymapnamnode(Keymap keymap)
+{
+ KeymapName kmn = (KeymapName) zcalloc(sizeof(*kmn));
+
+ kmn->keymap = keymap;
+ return kmn;
+}
+
+/**/
+static void
+freekeymapnamnode(HashNode hn)
+{
+ KeymapName kmn = (KeymapName) hn;
+
+ zsfree(kmn->nam);
+ if(!--kmn->keymap->rc)
+ deletekeymap(kmn->keymap);
+ zfree(kmn, sizeof(*kmn));
+}
+
+/**/
+static HashTable
+newkeytab(char *kmname)
+{
+ HashTable ht = newhashtable(19,
+ kmname ? dyncat("keytab:", kmname) : "keytab:", NULL);
+
+ ht->hash = hasher;
+ ht->emptytable = emptyhashtable;
+ ht->filltable = NULL;
+ ht->addnode = addhashnode;
+ ht->getnode = gethashnode2;
+ ht->getnode2 = gethashnode2;
+ ht->removenode = removehashnode;
+ ht->disablenode = NULL;
+ ht->enablenode = NULL;
+ ht->freenode = freekeynode;
+ ht->printnode = NULL;
+
+ return ht;
+}
+
+/**/
+static Key
+makekeynode(Thingy t, char *str)
+{
+ Key k = (Key) zcalloc(sizeof(*k));
+
+ k->bind = t;
+ k->str = str;
+ return k;
+}
+
+/**/
+static void
+freekeynode(HashNode hn)
+{
+ Key k = (Key) hn;
+
+ zsfree(k->nam);
+ unrefthingy(k->bind);
+ zsfree(k->str);
+ zfree(k, sizeof(*k));
+}
+
+/**************************/
+/* main keymap operations */
+/**************************/
+
+static HashTable copyto;
+
+/**/
+static Keymap
+newkeymap(Keymap tocopy, char *kmname)
+{
+ Keymap km = zcalloc(sizeof(*km));
+ int i;
+
+ km->rc = 0;
+ km->multi = newkeytab(kmname);
+ if(tocopy) {
+ for(i = 256; i--; )
+ km->first[i] = refthingy(tocopy->first[i]);
+ copyto = km->multi;
+ scanhashtable(tocopy->multi, 0, 0, 0, scancopykeys, 0);
+ } else {
+ for(i = 256; i--; )
+ km->first[i] = refthingy(t_undefinedkey);
+ }
+ return km;
+}
+
+/**/
+static void
+scancopykeys(HashNode hn, int flags)
+{
+ Key k = (Key) hn;
+ Key kn = zalloc(sizeof(*k));
+
+ memcpy(kn, k, sizeof(*k));
+ refthingy(kn->bind);
+ kn->str = ztrdup(k->str);
+ copyto->addnode(copyto, ztrdup(k->nam), kn);
+}
+
+/**/
+static void
+deletekeymap(Keymap km)
+{
+ int i;
+
+ deletehashtable(km->multi);
+ for(i = 256; i--; )
+ unrefthingy(km->first[i]);
+ zfree(km, sizeof(*km));
+}
+
+static Keymap skm_km;
+static int skm_last;
+static KeyScanFunc skm_func;
+static void *skm_magic;
+
+/**/
+void
+scankeymap(Keymap km, int sort, KeyScanFunc func, void *magic)
+{
+ char m[3];
+
+ skm_km = km;
+ skm_last = sort ? -1 : 255;
+ skm_func = func;
+ skm_magic = magic;
+ scanhashtable(km->multi, sort, 0, 0, scankeys, 0);
+ if(!sort)
+ skm_last = -1;
+ while(skm_last < 255) {
+ skm_last++;
+ if(km->first[skm_last] && km->first[skm_last] != t_undefinedkey) {
+ m[0] = skm_last;
+ metafy(m, 1, META_NOALLOC);
+ func(m, km->first[skm_last], NULL, magic);
+ }
+ }
+}
+
+/**/
+static void
+scankeys(HashNode hn, int flags)
+{
+ Key k = (Key) hn;
+ int f = k->nam[0] == Meta ? STOUC(k->nam[1])^32 : STOUC(k->nam[0]);
+ char m[3];
+
+ while(skm_last < f) {
+ skm_last++;
+ if(skm_km->first[skm_last] &&
+ skm_km->first[skm_last] != t_undefinedkey) {
+ m[0] = skm_last;
+ metafy(m, 1, META_NOALLOC);
+ skm_func(m, skm_km->first[skm_last], NULL, skm_magic);
+ }
+ }
+ skm_func(k->nam, k->bind, k->str, skm_magic);
+}
+
+/**************************/
+/* keymap name operations */
+/**************************/
+
+/**/
+Keymap
+openkeymap(char *name)
+{
+ KeymapName n = (KeymapName) keymapnamtab->getnode(keymapnamtab, name);
+ return n ? n->keymap : NULL;
+}
+
+/**/
+static int
+unlinkkeymap(char *name)
+{
+ KeymapName n = (KeymapName) keymapnamtab->getnode(keymapnamtab, name);
+ if(!n)
+ return 2;
+ if(n->flags & KMN_IMMORTAL)
+ return 1;
+ keymapnamtab->freenode(keymapnamtab->removenode(keymapnamtab, name));
+ return 0;
+}
+
+/**/
+static int
+linkkeymap(Keymap km, char *name)
+{
+ KeymapName n = (KeymapName) keymapnamtab->getnode(keymapnamtab, name);
+ if(n) {
+ if(n->flags & KMN_IMMORTAL)
+ return 1;
+ if(n->keymap == km)
+ return 0;
+ if(!--n->keymap->rc)
+ deletekeymap(n->keymap);
+ n->keymap = km;
+ } else
+ keymapnamtab->addnode(keymapnamtab, ztrdup(name),
+ makekeymapnamnode(km));
+ km->rc++;
+ return 0;
+}
+
+/* Select a keymap as the current ZLE keymap. Can optionally fall back *
+ * on the guaranteed safe keymap if it fails. */
+
+/**/
+int
+selectkeymap(char *name, int fb)
+{
+ Keymap km = openkeymap(name);
+
+ if(!km) {
+ char *nm = niceztrdup(name);
+ char *msg = tricat("No such keymap `", nm, "'");
+
+ zsfree(nm);
+ showmsg(msg);
+ zsfree(msg);
+ if(!fb)
+ return 1;
+ km = openkeymap(name = ".safe");
+ }
+ curkeymapname = name;
+ curkeymap = km;
+ return 0;
+}
+
+/* Reopen the currently selected keymap, in case it got deleted. This *
+ * should be called after doing anything that might have run an *
+ * arbitrary user-specified command. */
+
+/**/
+void
+reselectkeymap(void)
+{
+ selectkeymap(curkeymapname, 1);
+}
+
+/******************************/
+/* operations on key bindings */
+/******************************/
+
+/* Add/delete/change a keybinding in some keymap. km is the keymap to be *
+ * altered. seq is the metafied key sequence whose binding is to change. *
+ * bind is the thingy to which the key sequence is to be bound. For *
+ * send-string, bind is NULL and str is the metafied key sequence to push *
+ * back onto the input. */
+
+/**/
+int
+bindkey(Keymap km, char *seq, Thingy bind, char *str)
+{
+ Key k;
+ int f = seq[0] == Meta ? STOUC(seq[1])^32 : STOUC(seq[0]);
+ char *buf, *ptr;
+
+ if(km->flags & KM_IMMUTABLE)
+ return 1;
+ if(!*seq)
+ return 2;
+ if(!bind || ztrlen(seq) > 1) {
+ /* key needs to become a prefix if isn't one already */
+ if(km->first[f]) {
+ char fs[3];
+ fs[0] = f;
+ metafy(fs, 1, META_NOALLOC);
+ km->multi->addnode(km->multi, ztrdup(fs),
+ makekeynode(km->first[f], NULL));
+ km->first[f] = NULL;
+ }
+ k = (Key) km->multi->getnode(km->multi, seq);
+ } else {
+ /* If the sequence is a prefix entry only due to being *
+ * a send-string binding, we can remove that entry. */
+ if(!km->first[f]) {
+ k = (Key) km->multi->getnode(km->multi, seq);
+ if(!k->prefixct)
+ km->multi->freenode(km->multi->removenode(km->multi, seq));
+ else
+ goto domulti;
+ } else
+ unrefthingy(km->first[f]);
+ /* Just replace the single-character binding. */
+ km->first[f] = bind;
+ return 0;
+ }
+ domulti:
+ buf = ztrdup(seq);
+ ptr = strchr(buf, 0);
+ if(bind == t_undefinedkey) {
+ if(k) {
+ zsfree(k->str);
+ unrefthingy(k->bind);
+ k->bind = t_undefinedkey;
+ k->str = NULL;
+ while(!k->prefixct && k->bind == t_undefinedkey) {
+ km->multi->freenode(km->multi->removenode(km->multi, buf));
+ *--ptr = 0;
+ if(ptr[-1] == Meta)
+ *--ptr = 0;
+ k = (Key) km->multi->getnode(km->multi, buf);
+ k->prefixct--;
+ if(!k->prefixct && k->bind &&
+ (!buf[1] || (buf[0] == Meta && !buf[2]))) {
+ km->first[f] = refthingy(k->bind);
+ km->multi->freenode(km->multi->removenode(km->multi, buf));
+ break;
+ }
+ }
+ }
+ } else {
+ if(!k) {
+ int added;
+
+ km->multi->addnode(km->multi, ztrdup(buf), makekeynode(bind, ztrdup(str)));
+ do {
+ *--ptr = 0;
+ if(ptr > buf && ptr[-1] == Meta)
+ *--ptr = 0;
+ k = (Key) km->multi->getnode(km->multi, buf);
+ if((added = !k))
+ km->multi->addnode(km->multi, ztrdup(buf),
+ k = makekeynode(refthingy(t_undefinedkey), NULL));
+ k->prefixct++;
+ } while(added);
+ } else {
+ unrefthingy(k->bind);
+ zsfree(k->str);
+ k->bind = bind;
+ k->str = bind ? NULL : ztrdup(str);
+ }
+ }
+ free(buf);
+ return 0;
+}
+
+/* Look up a key binding. The binding is returned. In the case of a *
+ * send-string, NULL is returned and *strp is modified to point to the *
+ * metafied string of characters to be pushed back. */
+
+/**/
+Thingy
+keybind(Keymap km, char *seq, char **strp)
+{
+ Key k;
+
+ if(ztrlen(seq) == 1) {
+ int f = seq[0] == Meta ? STOUC(seq[1])^32 : STOUC(seq[0]);
+ Thingy bind = km->first[f];
+
+ if(bind)
+ return bind;
+ }
+ k = (Key) km->multi->getnode(km->multi, seq);
+ if(!k)
+ return t_undefinedkey;
+ *strp = k->str;
+ return k->bind;
+}
+
+/* Check whether a key sequence is a prefix of a longer bound sequence. *
+ * One oddity: if *nothing* in the keymap is bound, this returns true *
+ * for the empty sequence, even though this is not strictly accurate. */
+
+/**/
+static int
+keyisprefix(Keymap km, char *seq)
+{
+ Key k;
+
+ if(!*seq)
+ return 1;
+ if(ztrlen(seq) == 1) {
+ int f = seq[0] == Meta ? STOUC(seq[1])^32 : STOUC(seq[0]);
+
+ if(km->first[f])
+ return 0;
+ }
+ k = (Key) km->multi->getnode(km->multi, seq);
+ return k && k->prefixct;
+}
+
+/*******************/
+/* bindkey builtin */
+/*******************/
+
+/*
+ * THE BINDKEY BUILTIN
+ *
+ * Keymaps can be specified to bindkey in the following ways:
+ *
+ * -e select "emacs", also link it to "main"
+ * -v select "viins", also link it to "main"
+ * -a select "vicmd"
+ * -M first argument gives map name
+ * defaults to "main"
+ *
+ * These operations cannot have a keymap selected in the normal way:
+ *
+ * -l list all the keymap names
+ * -d delete all keymaps and reset to the default state (no arguments)
+ * -D delete named keymaps
+ * -A link the two named keymaps (2 arguments)
+ * -N create new empty keymap (1 argument)
+ * -N create new keymap, copying the second named keymap (2 arguments)
+ *
+ * Other operations:
+ *
+ * -m add the meta bindings to the selected keymap (no arguments)
+ * -r unbind each named string in the selected keymap
+ * -s bind send-strings in the selected keymap (2+ arguments)
+ * bind commands in the selected keymap (2+ arguments)
+ * display one binding in the selected keymap (1 argument)
+ * display the entire selected keymap (no arguments)
+ *
+ * There is an exception that the entire keymap display will not be performed
+ * if the -e or -v options were used.
+ *
+ * Other options:
+ *
+ * -L do listings in the form of bindkey commands
+ * -R for the binding operations, accept ranges instead of sequences
+ */
+
+/**/
+int
+bin_bindkey(char *name, char **argv, char *ops, int func)
+{
+ static struct opn {
+ char o;
+ char selp;
+ int (*func) _((char *, char *, Keymap, char **, char *, char));
+ int min, max;
+ } const opns[] = {
+ { 'l', 0, bin_bindkey_lsmaps, 0, 0 },
+ { 'd', 0, bin_bindkey_delall, 0, 0 },
+ { 'D', 0, bin_bindkey_del, 1, -1 },
+ { 'A', 0, bin_bindkey_link, 2, 2 },
+ { 'N', 0, bin_bindkey_new, 1, 2 },
+ { 'm', 1, bin_bindkey_meta, 0, 0 },
+ { 'r', 1, bin_bindkey_bind, 1, -1 },
+ { 's', 1, bin_bindkey_bind, 2, -1 },
+ { 0, 1, bin_bindkey_bind, 0, -1 },
+ };
+ struct opn const *op, *opp;
+ char *kmname;
+ Keymap km;
+ int n;
+
+ /* select operation and ensure no clashing arguments */
+ for(op = opns; op->o && !ops[op->o]; op++) ;
+ if(op->o)
+ for(opp = op; (++opp)->o; )
+ if(ops[opp->o]) {
+ zwarnnam(name, "incompatible operation selection options",
+ NULL, 0);
+ return 1;
+ }
+ n = ops['e'] + ops['v'] + ops['a'] + ops['M'];
+ if(!op->selp && n) {
+ zwarnnam(name, "keymap cannot be selected with -%c", NULL, op->o);
+ return 1;
+ }
+ if(n > 1) {
+ zwarnnam(name, "incompatible keymap selection options", NULL, 0);
+ return 1;
+ }
+
+ /* keymap selection */
+ if(op->selp) {
+ if(ops['e'])
+ kmname = "emacs";
+ else if(ops['v'])
+ kmname = "viins";
+ else if(ops['a'])
+ kmname = "vicmd";
+ else if(ops['M']) {
+ kmname = *argv++;
+ if(!kmname) {
+ zwarnnam(name, "-M option requires a keymap argument", NULL, 0);
+ return 1;
+ }
+ } else
+ kmname = "main";
+ km = openkeymap(kmname);
+ if(!km) {
+ zwarnnam(name, "no such keymap `%s'", kmname, 0);
+ return 1;
+ }
+ if(ops['e'] || ops['v'])
+ linkkeymap(km, "main");
+ } else {
+ kmname = NULL;
+ km = NULL;
+ }
+
+ /* listing is a special case */
+ if(!op->o && (!argv[0] || !argv[1])) {
+ if(ops['e'] || ops['v'])
+ return 0;
+ return bin_bindkey_list(name, kmname, km, argv, ops, op->o);
+ }
+
+ /* check number of arguments */
+ for(n = 0; argv[n]; n++) ;
+ if(n < op->min) {
+ zwarnnam(name, "not enough arguments for -%c", NULL, op->o);
+ return 1;
+ } else if(op->max != -1 && n > op->max) {
+ zwarnnam(name, "too many arguments for -%c", NULL, op->o);
+ return 1;
+ }
+
+ /* pass on the work to the operation function */
+ return op->func(name, kmname, km, argv, ops, op->o);
+}
+
+/* list the available keymaps */
+
+/**/
+static int
+bin_bindkey_lsmaps(char *name, char *kmname, Keymap km, char **argv, char *ops, char func)
+{
+ scanhashtable(keymapnamtab, 1, 0, 0, scanlistmaps, ops['L']);
+ return 0;
+}
+
+/**/
+static void
+scanlistmaps(HashNode hn, int list)
+{
+ KeymapName n = (KeymapName) hn;
+
+ if(list) {
+ fputs("bindkey -N ", stdout);
+ if(n->nam[0] == '-')
+ fputs("-- ", stdout);
+ quotedzputs(n->nam, stdout);
+ } else
+ nicezputs(n->nam, stdout);
+ putchar('\n');
+}
+
+/* reset all keymaps to the default state */
+
+/**/
+static int
+bin_bindkey_delall(char *name, char *kmname, Keymap km, char **argv, char *ops, char func)
+{
+ keymapnamtab->emptytable(keymapnamtab);
+ default_bindings();
+ return 0;
+}
+
+/* delete named keymaps */
+
+/**/
+static int
+bin_bindkey_del(char *name, char *kmname, Keymap km, char **argv, char *ops, char func)
+{
+ int ret = 0;
+
+ do {
+ int r = unlinkkeymap(*argv);
+ if(r == 1)
+ zwarnnam(name, "keymap name `%s' is protected", *argv, 0);
+ else if(r == 2)
+ zwarnnam(name, "no such keymap `%s'", *argv, 0);
+ ret |= !!r;
+ } while(*++argv);
+ return ret;
+}
+
+/* link named keymaps */
+
+/**/
+static int
+bin_bindkey_link(char *name, char *kmname, Keymap km, char **argv, char *ops, char func)
+{
+ km = openkeymap(argv[0]);
+ if(!km) {
+ zwarnnam(name, "no such keymap `%s'", argv[0], 0);
+ return 1;
+ } else if(linkkeymap(km, argv[1])) {
+ zwarnnam(name, "keymap name `%s' is protected", argv[1], 0);
+ return 1;
+ }
+ return 0;
+}
+
+/* create a new keymap */
+
+/**/
+static int
+bin_bindkey_new(char *name, char *kmname, Keymap km, char **argv, char *ops, char func)
+{
+ KeymapName kmn = (KeymapName) keymapnamtab->getnode(keymapnamtab, argv[0]);
+
+ if(kmn && (kmn -> flags & KMN_IMMORTAL)) {
+ zwarnnam(name, "keymap name `%s' is protected", argv[0], 0);
+ return 1;
+ }
+ if(argv[1]) {
+ km = openkeymap(argv[1]);
+ if(!km) {
+ zwarnnam(name, "no such keymap `%s'", argv[0], 0);
+ return 1;
+ }
+ } else
+ km = NULL;
+ linkkeymap(newkeymap(km, argv[0]), argv[0]);
+ return 0;
+}
+
+/* Add standard meta bindings to a keymap. Only sequences currently either *
+ * unbound or bound to self-insert are affected. Note that the use of *
+ * bindkey() is quite necessary: if this function were to go through the *
+ * km->first table itself, it would miss any prefix sequences that should *
+ * be rebound. */
+
+/**/
+static int
+bin_bindkey_meta(char *name, char *kmname, Keymap km, char **argv, char *ops, char func)
+{
+ char m[3], *str;
+ int i;
+ Thingy fn;
+
+ if(km->flags & KM_IMMUTABLE) {
+ zwarnnam(name, "keymap `%s' is protected", kmname, 0);
+ return 1;
+ }
+ for(i = 128; i < 256; i++)
+ if(metabind[i - 128] != z_undefinedkey) {
+ m[0] = i;
+ metafy(m, 1, META_NOALLOC);
+ fn = keybind(km, m, &str);
+ if(fn == t_selfinsert || fn == t_undefinedkey)
+ bindkey(km, m, refthingy(Th(metabind[i - 128])), NULL);
+ }
+ return 0;
+}
+
+/* Change key bindings. func can be: *
+ * 'r' bind sequences to undefined-key *
+ * 's' bind sequneces to specified send-strings *
+ * 0 bind sequences to specified functions *
+ * If the -R option is used, bind to key ranges *
+ * instead of single key sequences. */
+
+/**/
+static int
+bin_bindkey_bind(char *name, char *kmname, Keymap km, char **argv, char *ops, char func)
+{
+ int ret = 0;
+
+ if(!func || func == 's') {
+ char **a;
+
+ for(a = argv+2; *a; a++)
+ if(!*++a) {
+ zwarnnam(name, "even number of arguments required", NULL, 0);
+ return 1;
+ }
+ }
+ if(km->flags & KM_IMMUTABLE) {
+ zwarnnam(name, "keymap `%s' is protected", kmname, 0);
+ return 1;
+ }
+ do {
+ char *useq = *argv, *bseq, *seq, *str;
+ int len;
+ Thingy fn;
+
+ if(func == 'r') {
+ fn = refthingy(t_undefinedkey);
+ str = NULL;
+ } else if(func == 's') {
+ str = getkeystring(*++argv, &len, 2, NULL);
+ fn = NULL;
+ str = metafy(str, len, META_HREALLOC);
+ } else {
+ fn = rthingy(*++argv);
+ str = NULL;
+ }
+ bseq = getkeystring(useq, &len, 2, NULL);
+ seq = metafy(bseq, len, META_USEHEAP);
+ if(ops['R']) {
+ int first, last;
+ char m[3];
+
+ if(len < 2 || len > 2 + (bseq[1] == '-') ||
+ (first = STOUC(bseq[0])) > (last = STOUC(bseq[len - 1]))) {
+ zwarnnam(name, "malformed key range `%s'", useq, 0);
+ ret = 1;
+ } else {
+ for(; first <= last; first++) {
+ m[0] = first;
+ metafy(m, 1, META_NOALLOC);
+ bindkey(km, m, refthingy(fn), str);
+ }
+ unrefthingy(fn);
+ }
+ } else {
+ if(bindkey(km, seq, fn, str)) {
+ zwarnnam(name, "cannot bind to an empty key sequence", NULL, 0);
+ ret = 1;
+ }
+ }
+ } while(*++argv);
+ return ret;
+}
+
+/* List key bindings. If an argument is given, list just that one *
+ * binding, otherwise list the entire keymap. If the -L option is *
+ * given, list in the form of bindkey commands. */
+
+/**/
+static int
+bin_bindkey_list(char *name, char *kmname, Keymap km, char **argv, char *ops, char func)
+{
+ struct bindstate bs;
+
+ bs.flags = ops['L'] ? BS_LIST : 0;
+ bs.kmname = kmname;
+ if(argv[0]) {
+ int len;
+ char *seq;
+
+ seq = getkeystring(argv[0], &len, 2, NULL);
+ seq = metafy(seq, len, META_HREALLOC);
+ bs.flags |= BS_ALL;
+ bs.firstseq = bs.lastseq = seq;
+ bs.bind = keybind(km, seq, &bs.str);
+ bindlistout(&bs);
+ } else {
+ bs.firstseq = ztrdup("");
+ bs.lastseq = ztrdup("");
+ bs.bind = t_undefinedkey;
+ bs.str = NULL;
+ scankeymap(km, 1, scanbindlist, &bs);
+ bindlistout(&bs);
+ zsfree(bs.firstseq);
+ zsfree(bs.lastseq);
+ }
+ return 0;
+}
+
+/**/
+static void
+scanbindlist(char *seq, Thingy bind, char *str, void *magic)
+{
+ struct bindstate *bs = magic;
+
+ if(bind == bs->bind && (bind || !strcmp(str, bs->str)) &&
+ ztrlen(seq) == 1 && ztrlen(bs->lastseq) == 1) {
+ int l = bs->lastseq[1] ?
+ STOUC(bs->lastseq[1]) ^ 32 : STOUC(bs->lastseq[0]);
+ int t = seq[1] ? STOUC(seq[1]) ^ 32 : STOUC(seq[0]);
+
+ if(t == l + 1) {
+ zsfree(bs->lastseq);
+ bs->lastseq = ztrdup(seq);
+ return;
+ }
+ }
+ bindlistout(bs);
+ zsfree(bs->firstseq);
+ bs->firstseq = ztrdup(seq);
+ zsfree(bs->lastseq);
+ bs->lastseq = ztrdup(seq);
+ bs->bind = bind;
+ bs->str = str;
+}
+
+/**/
+static void
+bindlistout(struct bindstate *bs)
+{
+ int range;
+
+ if(bs->bind == t_undefinedkey && !(bs->flags & BS_ALL))
+ return;
+ range = strcmp(bs->firstseq, bs->lastseq);
+ if(bs->flags & BS_LIST) {
+ int nodash = 1;
+
+ fputs("bindkey ", stdout);
+ if(range)
+ fputs("-R ", stdout);
+ if(!bs->bind)
+ fputs("-s ", stdout);
+ if(!strcmp(bs->kmname, "main"))
+ ;
+ else if(!strcmp(bs->kmname, "vicmd"))
+ fputs("-a ", stdout);
+ else {
+ fputs("-M ", stdout);
+ quotedzputs(bs->kmname, stdout);
+ putchar(' ');
+ nodash = 0;
+ }
+ if(nodash && bs->firstseq[0] == '-')
+ fputs("-- ", stdout);
+ }
+ printbind(bs->firstseq, stdout);
+ if(range) {
+ putchar('-');
+ printbind(bs->lastseq, stdout);
+ }
+ putchar(' ');
+ if(bs->bind) {
+ ((bs->flags & BS_LIST) ? quotedzputs : nicezputs)
+ (bs->bind->nam, stdout);
+ } else
+ printbind(bs->str, stdout);
+ putchar('\n');
+}
+
+/****************************/
+/* initialisation functions */
+/****************************/
+
+/* main initialisation entry point */
+
+/**/
+void
+init_keymaps(void)
+{
+ createkeymapnamtab();
+ default_bindings();
+ keybuf = (char *)zalloc(keybufsz);
+ lastnamed = refthingy(t_undefinedkey);
+}
+
+#ifdef MODULE
+
+/* cleanup entry point (for unloading the zle module) */
+
+/**/
+void
+cleanup_keymaps(void)
+{
+ unrefthingy(lastnamed);
+ deletehashtable(keymapnamtab);
+ zfree(keybuf, keybufsz);
+}
+
+#endif /* MODULE */
+
+/* Create the default keymaps. For efficiency reasons, this function *
+ * assigns directly to the km->first array. It knows that there are no *
+ * prefix bindings in the way, and that it is using a simple keymap. */
+
+/**/
+static void
+default_bindings(void)
+{
+ Keymap vmap = newkeymap(NULL, "viins");
+ Keymap emap = newkeymap(NULL, "emacs");
+ Keymap amap = newkeymap(NULL, "vicmd");
+ Keymap smap = newkeymap(NULL, ".safe");
+ char buf[3], *ed;
+ int i;
+
+ /* vi insert mode and emacs mode: *
+ * 0-31 taken from the tables *
+ * 32-126 self-insert *
+ * 127 same as entry[8] *
+ * 128-255 self-insert */
+ for (i = 0; i < 32; i++) {
+ vmap->first[i] = refthingy(Th(viinsbind[i]));
+ emap->first[i] = refthingy(Th(emacsbind[i]));
+ }
+ for (i = 32; i < 256; i++) {
+ vmap->first[i] = refthingy(t_selfinsert);
+ emap->first[i] = refthingy(t_selfinsert);
+ }
+ unrefthingy(t_selfinsert);
+ unrefthingy(t_selfinsert);
+ vmap->first[127] = refthingy(vmap->first[8]);
+ emap->first[127] = refthingy(emap->first[8]);
+
+ /* vi command mode: *
+ * 0-127 taken from the table *
+ * 128-255 undefined-key */
+ for (i = 0; i < 128; i++)
+ amap->first[i] = refthingy(Th(vicmdbind[i]));
+ for (i = 128; i < 256; i++)
+ amap->first[i] = refthingy(t_undefinedkey);
+
+ /* safe fallback keymap:
+ * 0-255 self-insert, except: *
+ * '\n' accept-line *
+ * '\r' accept-line */
+ for (i = 0; i < 256; i++)
+ smap->first[i] = refthingy(t_selfinsert);
+ unrefthingy(t_selfinsert);
+ unrefthingy(t_selfinsert);
+ smap->first['\n'] = refthingy(t_acceptline);
+ smap->first['\r'] = refthingy(t_acceptline);
+
+ /* vt100 arrow keys are bound by default, for historical reasons. *
+ * Both standard and keypad modes are supported. */
+
+ /* vi command mode: arrow keys */
+ bindkey(amap, "\33[A", refthingy(t_uplineorhistory), NULL);
+ bindkey(amap, "\33[B", refthingy(t_downlineorhistory), NULL);
+ bindkey(amap, "\33[C", refthingy(t_viforwardchar), NULL);
+ bindkey(amap, "\33[D", refthingy(t_vibackwardchar), NULL);
+ bindkey(amap, "\33OA", refthingy(t_uplineorhistory), NULL);
+ bindkey(amap, "\33OB", refthingy(t_downlineorhistory), NULL);
+ bindkey(amap, "\33OC", refthingy(t_viforwardchar), NULL);
+ bindkey(amap, "\33OD", refthingy(t_vibackwardchar), NULL);
+
+ /* emacs mode: arrow keys */
+ bindkey(emap, "\33[A", refthingy(t_uplineorhistory), NULL);
+ bindkey(emap, "\33[B", refthingy(t_downlineorhistory), NULL);
+ bindkey(emap, "\33[C", refthingy(t_forwardchar), NULL);
+ bindkey(emap, "\33[D", refthingy(t_backwardchar), NULL);
+ bindkey(emap, "\33OA", refthingy(t_uplineorhistory), NULL);
+ bindkey(emap, "\33OB", refthingy(t_downlineorhistory), NULL);
+ bindkey(emap, "\33OC", refthingy(t_forwardchar), NULL);
+ bindkey(emap, "\33OD", refthingy(t_backwardchar), NULL);
+
+ /* emacs mode: ^X sequences */
+ bindkey(emap, "\30*", refthingy(t_expandword), NULL);
+ bindkey(emap, "\30g", refthingy(t_listexpand), NULL);
+ bindkey(emap, "\30G", refthingy(t_listexpand), NULL);
+ bindkey(emap, "\30\16", refthingy(t_infernexthistory), NULL);
+ bindkey(emap, "\30\13", refthingy(t_killbuffer), NULL);
+ bindkey(emap, "\30\6", refthingy(t_vifindnextchar), NULL);
+ bindkey(emap, "\30\17", refthingy(t_overwritemode), NULL);
+ bindkey(emap, "\30\25", refthingy(t_undo), NULL);
+ bindkey(emap, "\30\26", refthingy(t_vicmdmode), NULL);
+ bindkey(emap, "\30\12", refthingy(t_vijoin), NULL);
+ bindkey(emap, "\30\2", refthingy(t_vimatchbracket), NULL);
+ bindkey(emap, "\30s", refthingy(t_historyincrementalsearchforward), NULL);
+ bindkey(emap, "\30r", refthingy(t_historyincrementalsearchbackward), NULL);
+ bindkey(emap, "\30u", refthingy(t_undo), NULL);
+ bindkey(emap, "\30\30", refthingy(t_exchangepointandmark), NULL);
+ bindkey(emap, "\30=", refthingy(t_whatcursorposition), NULL);
+
+ /* emacs mode: ESC sequences, all taken from the meta binding table */
+ buf[0] = '\33';
+ buf[2] = 0;
+ for (i = 0; i < 128; i++)
+ if (metabind[i] != z_undefinedkey) {
+ buf[1] = i;
+ bindkey(emap, buf, refthingy(Th(metabind[i])), NULL);
+ }
+
+ /* Put the keymaps in the right namespace. The "main" keymap *
+ * will be linked to the "emacs" keymap, except that if VISUAL *
+ * or EDITOR contain the string "vi" then it will be linked to *
+ * the "viins" keymap. */
+ linkkeymap(vmap, "viins");
+ linkkeymap(emap, "emacs");
+ linkkeymap(amap, "vicmd");
+ linkkeymap(smap, ".safe");
+ if (((ed = zgetenv("VISUAL")) && strstr(ed, "vi")) ||
+ ((ed = zgetenv("EDITOR")) && strstr(ed, "vi")))
+ linkkeymap(vmap, "main");
+ else
+ linkkeymap(emap, "main");
+
+ /* the .safe map cannot be modified or deleted */
+ smap->flags |= KM_IMMUTABLE;
+ ((KeymapName) keymapnamtab->getnode(keymapnamtab, ".safe"))->flags
+ |= KMN_IMMORTAL;
+}
+
+/*************************/
+/* reading key sequences */
+/*************************/
+
+/* read a sequence of keys that is bound to some command in a keymap */
+
+/**/
+char *
+getkeymapcmd(Keymap km, Thingy *funcp, char **strp)
+{
+ Thingy func = t_undefinedkey;
+ char *str = NULL;
+ int lastlen = 0, lastc = c;
+
+ keybuflen = 0;
+ keybuf[0] = 0;
+ while((c = getkeybuf(!!lastlen)) != EOF) {
+ char *s;
+ Thingy f = keybind(km, keybuf, &s);
+
+ if(f != t_undefinedkey) {
+ lastlen = keybuflen;
+ func = f;
+ str = s;
+ lastc = c;
+ }
+ if(!keyisprefix(km, keybuf))
+ break;
+ }
+ if(!lastlen && keybuflen)
+ lastlen = keybuflen;
+ else
+ c = lastc;
+ if(lastlen != keybuflen) {
+ unmetafy(keybuf + lastlen, &keybuflen);
+ ungetkeys(keybuf+lastlen, keybuflen);
+ if(vichgflag)
+ vichgbufptr -= keybuflen;
+ keybuf[lastlen] = 0;
+ }
+ *funcp = func;
+ *strp = str;
+ return keybuf;
+}
+
+/**/
+static int
+getkeybuf(int w)
+{
+ int c = getkey(w);
+
+ if(c < 0)
+ return EOF;
+ if(keybuflen + 3 > keybufsz)
+ keybuf = realloc(keybuf, keybufsz *= 2);
+ if(imeta(c)) {
+ keybuf[keybuflen++] = Meta;
+ keybuf[keybuflen++] = c ^ 32;
+ } else
+ keybuf[keybuflen++] = c;
+ keybuf[keybuflen] = 0;
+ return c;
+}
+
+/* Push back the last command sequence read by getkeymapcmd(). *
+ * Must be executed at most once after each getkeymapcmd(). */
+
+/**/
+void
+ungetkeycmd(void)
+{
+ ungetkeys(keybuf, keybuflen);
+}
+
+/* read a command from the current keymap, with widgets */
+
+/**/
+Thingy
+getkeycmd(void)
+{
+ Thingy func;
+ int hops = 0;
+ char *seq, *str;
+
+ sentstring:
+ seq = getkeymapcmd(curkeymap, &func, &str);
+ if(!*seq)
+ return NULL;
+ if(!func) {
+ int len;
+ char *pb;
+
+ if (++hops == 20) {
+ zerr("string inserting another one too many times", NULL, 0);
+ hops = 0;
+ return NULL;
+ }
+ pb = unmetafy(ztrdup(str), &len);
+ ungetkeys(pb, len);
+ zfree(pb, strlen(str) + 1);
+ goto sentstring;
+ }
+ if (func == Th(z_executenamedcmd) && !statusline) {
+ while(func == Th(z_executenamedcmd))
+ func = executenamedcommand("execute: ");
+ if(!func)
+ func = t_undefinedkey;
+ else if(func != Th(z_executelastnamedcmd)) {
+ unrefthingy(lastnamed);
+ lastnamed = refthingy(func);
+ }
+ }
+ if (func == Th(z_executelastnamedcmd))
+ func = lastnamed;
+ return func;
+}
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
new file mode 100644
index 000000000..338cdef41
--- /dev/null
+++ b/Src/Zle/zle_main.c
@@ -0,0 +1,907 @@
+/*
+ * zle_main.c - main routines for line editor
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * 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 Paul Falstad 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 Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad 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 Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "zle.mdh"
+#include "zle_main.pro"
+
+/* != 0 if we're done editing */
+
+/**/
+int done;
+
+/* location of mark */
+
+/**/
+int mark;
+
+/* last character pressed */
+
+/**/
+int c;
+
+/* the binding for this key */
+
+/**/
+Thingy bindk;
+
+/* insert mode/overwrite mode flag */
+
+/**/
+int insmode;
+
+static int eofchar, eofsent;
+static long keytimeout;
+
+#ifdef HAVE_SELECT
+/* Terminal baud rate */
+
+static int baud;
+#endif
+
+/* flags associated with last command */
+
+/**/
+int lastcmd;
+
+/* the status line, and its length */
+
+/**/
+char *statusline;
+/**/
+int statusll;
+
+/* The current history line and cursor position for the top line *
+ * on the buffer stack. */
+
+/**/
+int stackhist, stackcs;
+
+/* != 0 if we are making undo records */
+
+/**/
+int undoing;
+
+/* current modifier status */
+
+/**/
+struct modifier zmod;
+
+/* Current command prefix status. This is normally 0. Prefixes set *
+ * this to 1. Each time round the main loop, this is checked: if it *
+ * is 0, the modifier status is reset; if it is 1, the modifier *
+ * status is left unchanged, and this flag is reset to 0. The *
+ * effect is that several prefix commands can be executed, and have *
+ * cumulative effect, but any other command execution will clear the *
+ * modifiers. */
+
+/**/
+int prefixflag;
+
+/* != 0 if there is a pending beep (usually indicating an error) */
+
+/**/
+int feepflag;
+
+/* set up terminal */
+
+/**/
+void
+setterm(void)
+{
+ struct ttyinfo ti;
+
+#if defined(CLOBBERS_TYPEAHEAD) && defined(FIONREAD)
+ int val;
+
+ ioctl(SHTTY, FIONREAD, (char *)&val);
+ if (val)
+ return;
+#endif
+
+/* sanitize the tty */
+#ifdef HAS_TIO
+ shttyinfo.tio.c_lflag |= ICANON | ECHO;
+# ifdef FLUSHO
+ shttyinfo.tio.c_lflag &= ~FLUSHO;
+# endif
+#else /* not HAS_TIO */
+ shttyinfo.sgttyb.sg_flags = (shttyinfo.sgttyb.sg_flags & ~CBREAK) | ECHO;
+ shttyinfo.lmodes &= ~LFLUSHO;
+#endif
+
+ attachtty(mypgrp);
+ ti = shttyinfo;
+#ifdef HAS_TIO
+ if (unset(FLOWCONTROL))
+ ti.tio.c_iflag &= ~IXON;
+ ti.tio.c_lflag &= ~(ICANON | ECHO
+# ifdef FLUSHO
+ | FLUSHO
+# endif
+ );
+# ifdef TAB3
+ ti.tio.c_oflag &= ~TAB3;
+# else
+# ifdef OXTABS
+ ti.tio.c_oflag &= ~OXTABS;
+# else
+ ti.tio.c_oflag &= ~XTABS;
+# endif
+# endif
+ ti.tio.c_oflag |= ONLCR;
+ ti.tio.c_cc[VQUIT] =
+# ifdef VDISCARD
+ ti.tio.c_cc[VDISCARD] =
+# endif
+# ifdef VSUSP
+ ti.tio.c_cc[VSUSP] =
+# endif
+# ifdef VDSUSP
+ ti.tio.c_cc[VDSUSP] =
+# endif
+# ifdef VSWTCH
+ ti.tio.c_cc[VSWTCH] =
+# endif
+# ifdef VLNEXT
+ ti.tio.c_cc[VLNEXT] =
+# endif
+ VDISABLEVAL;
+# if defined(VSTART) && defined(VSTOP)
+ if (unset(FLOWCONTROL))
+ ti.tio.c_cc[VSTART] = ti.tio.c_cc[VSTOP] = VDISABLEVAL;
+# endif
+ eofchar = ti.tio.c_cc[VEOF];
+ ti.tio.c_cc[VMIN] = 1;
+ ti.tio.c_cc[VTIME] = 0;
+ ti.tio.c_iflag |= (INLCR | ICRNL);
+ /* this line exchanges \n and \r; it's changed back in getkey
+ so that the net effect is no change at all inside the shell.
+ This double swap is to allow typeahead in common cases, eg.
+
+ % bindkey -s '^J' 'echo foo^M'
+ % sleep 10
+ echo foo<return> <--- typed before sleep returns
+
+ The shell sees \n instead of \r, since it was changed by the kernel
+ while zsh wasn't looking. Then in getkey() \n is changed back to \r,
+ and it sees "echo foo<accept line>", as expected. Without the double
+ swap the shell would see "echo foo\n", which is translated to
+ "echo fooecho foo<accept line>" because of the binding.
+ Note that if you type <line-feed> during the sleep the shell just sees
+ \n, which is translated to \r in getkey(), and you just get another
+ prompt. For type-ahead to work in ALL cases you have to use
+ stty inlcr.
+
+ Unfortunately it's IMPOSSIBLE to have a general solution if both
+ <return> and <line-feed> are mapped to the same character. The shell
+ could check if there is input and read it before setting it's own
+ terminal modes but if we get a \n we don't know whether to keep it or
+ change to \r :-(
+ */
+
+#else /* not HAS_TIO */
+ ti.sgttyb.sg_flags = (ti.sgttyb.sg_flags | CBREAK) & ~ECHO & ~XTABS;
+ ti.lmodes &= ~LFLUSHO;
+ eofchar = ti.tchars.t_eofc;
+ ti.tchars.t_quitc =
+ ti.ltchars.t_suspc =
+ ti.ltchars.t_flushc =
+ ti.ltchars.t_dsuspc = ti.ltchars.t_lnextc = -1;
+#endif
+
+#if defined(TTY_NEEDS_DRAINING) && defined(TIOCOUTQ) && defined(HAVE_SELECT)
+ if (baud) { /**/
+ int n = 0;
+
+ while ((ioctl(SHTTY, TIOCOUTQ, (char *)&n) >= 0) && n) {
+ struct timeval tv;
+
+ tv.tv_sec = n / baud;
+ tv.tv_usec = ((n % baud) * 1000000) / baud;
+ select(0, NULL, NULL, NULL, &tv);
+ }
+ }
+#endif
+
+ settyinfo(&ti);
+}
+
+static char *kungetbuf;
+static int kungetct, kungetsz;
+
+/**/
+void
+ungetkey(int ch)
+{
+ if (kungetct == kungetsz)
+ kungetbuf = realloc(kungetbuf, kungetsz *= 2);
+ kungetbuf[kungetct++] = ch;
+}
+
+/**/
+void
+ungetkeys(char *s, int len)
+{
+ s += len;
+ while (len--)
+ ungetkey(*--s);
+}
+
+#if defined(pyr) && defined(HAVE_SELECT)
+static int
+breakread(int fd, char *buf, int n)
+{
+ fd_set f;
+
+ FD_ZERO(&f);
+ FD_SET(fd, &f);
+ return (select(fd + 1, (SELECT_ARG_2_T) & f, NULL, NULL, NULL) == -1 ?
+ EOF : read(fd, buf, n));
+}
+
+# define read breakread
+#endif
+
+/**/
+int
+getkey(int keytmout)
+{
+ char cc;
+ unsigned int ret;
+ long exp100ths;
+ int die = 0, r, icnt = 0;
+ int old_errno = errno;
+
+#ifdef HAVE_SELECT
+ fd_set foofd;
+
+#else
+# ifdef HAS_TIO
+ struct ttyinfo ti;
+
+# endif
+#endif
+
+ if (kungetct)
+ ret = STOUC(kungetbuf[--kungetct]);
+ else {
+ if (keytmout) {
+ if (keytimeout > 500)
+ exp100ths = 500;
+ else if (keytimeout > 0)
+ exp100ths = keytimeout;
+ else
+ exp100ths = 0;
+#ifdef HAVE_SELECT
+ if (exp100ths) {
+ struct timeval expire_tv;
+
+ expire_tv.tv_sec = exp100ths / 100;
+ expire_tv.tv_usec = (exp100ths % 100) * 10000L;
+ FD_ZERO(&foofd);
+ FD_SET(SHTTY, &foofd);
+ if (select(SHTTY+1, (SELECT_ARG_2_T) & foofd,
+ NULL, NULL, &expire_tv) <= 0)
+ return EOF;
+ }
+#else
+# ifdef HAS_TIO
+ ti = shttyinfo;
+ ti.tio.c_lflag &= ~ICANON;
+ ti.tio.c_cc[VMIN] = 0;
+ ti.tio.c_cc[VTIME] = exp100ths / 10;
+# ifdef HAVE_TERMIOS_H
+ tcsetattr(SHTTY, TCSANOW, &ti.tio);
+# else
+ ioctl(SHTTY, TCSETA, &ti.tio);
+# endif
+ r = read(SHTTY, &cc, 1);
+# ifdef HAVE_TERMIOS_H
+ tcsetattr(SHTTY, TCSANOW, &shttyinfo.tio);
+# else
+ ioctl(SHTTY, TCSETA, &shttyinfo.tio);
+# endif
+ return (r <= 0) ? EOF : cc;
+# endif
+#endif
+ }
+ while ((r = read(SHTTY, &cc, 1)) != 1) {
+ if (r == 0) {
+ /* The test for IGNOREEOF was added to make zsh ignore ^Ds
+ that were typed while commands are running. Unfortuantely
+ this caused trouble under at least one system (SunOS 4.1).
+ Here shells that lost their xterm (e.g. if it was killed
+ with -9) didn't fail to read from the terminal but instead
+ happily continued to read EOFs, so that the above read
+ returned with 0, and, with IGNOREEOF set, this caused
+ an infinite loop. The simple way around this was to add
+ the counter (icnt) so that this happens 20 times and than
+ the shell gives up (yes, this is a bit dirty...). */
+ if (isset(IGNOREEOF) && icnt++ < 20)
+ continue;
+ stopmsg = 1;
+ zexit(1, 0);
+ }
+ icnt = 0;
+ if (errno == EINTR) {
+ die = 0;
+ if (!errflag && !retflag && !breaks)
+ continue;
+ errflag = 0;
+ errno = old_errno;
+ return EOF;
+ } else if (errno == EWOULDBLOCK) {
+ fcntl(0, F_SETFL, 0);
+ } else if (errno == EIO && !die) {
+ ret = opts[MONITOR];
+ opts[MONITOR] = 1;
+ attachtty(mypgrp);
+ refresh(); /* kludge! */
+ opts[MONITOR] = ret;
+ die = 1;
+ } else if (errno != 0) {
+ zerr("error on TTY read: %e", NULL, errno);
+ stopmsg = 1;
+ zexit(1, 0);
+ }
+ }
+ if (cc == '\r') /* undo the exchange of \n and \r determined by */
+ cc = '\n'; /* setterm() */
+ else if (cc == '\n')
+ cc = '\r';
+
+ ret = STOUC(cc);
+ }
+ if (vichgflag) {
+ if (vichgbufptr == vichgbufsz)
+ vichgbuf = realloc(vichgbuf, vichgbufsz *= 2);
+ vichgbuf[vichgbufptr++] = ret;
+ }
+ errno = old_errno;
+ return ret;
+}
+
+/* Read a line. It is returned metafied. */
+
+/**/
+unsigned char *
+zleread(char *lp, char *rp, int ha)
+{
+ unsigned char *s;
+ int old_errno = errno;
+ int tmout = getiparam("TMOUT");
+
+#ifdef HAVE_SELECT
+ long costmult;
+ struct timeval tv;
+ fd_set foofd;
+
+ baud = getiparam("BAUD");
+ costmult = (baud) ? 3840000L / baud : 0;
+ tv.tv_sec = 0;
+#endif
+
+ /* ZLE doesn't currently work recursively. This is needed in case a *
+ * select loop is used in a function called from ZLE. vared handles *
+ * this differently itself. */
+ if(zleactive) {
+ char *pptbuf;
+ int pptlen;
+
+ pptbuf = unmetafy(promptexpand(lp, 0, NULL, NULL), &pptlen);
+ write(2, (WRITE_ARG_2_T)pptbuf, pptlen);
+ free(pptbuf);
+ return (unsigned char *)shingetline();
+ }
+
+ keytimeout = getiparam("KEYTIMEOUT");
+ if (!shout) {
+ if (SHTTY != -1)
+ init_shout();
+
+ if (!shout)
+ return NULL;
+ /* We could be smarter and default to a system read. */
+
+ /* If we just got a new shout, make sure the terminal is set up. */
+ if (termflags & TERM_UNKNOWN)
+ init_term();
+ }
+
+ fflush(shout);
+ fflush(stderr);
+ intr();
+ insmode = unset(OVERSTRIKE);
+ eofsent = 0;
+ resetneeded = 0;
+ lpptbuf = promptexpand(lp, 1, NULL, NULL);
+ pmpt_attr = txtchange;
+ rpptbuf = promptexpand(rp, 1, NULL, NULL);
+ rpmpt_attr = txtchange;
+ histallowed = ha;
+ PERMALLOC {
+ histline = curhist;
+#ifdef HAVE_SELECT
+ FD_ZERO(&foofd);
+#endif
+ undoing = 1;
+ line = (unsigned char *)zalloc((linesz = 256) + 2);
+ virangeflag = lastcmd = done = cs = ll = mark = 0;
+ curhistline = NULL;
+ vichgflag = 0;
+ viinsbegin = 0;
+ statusline = NULL;
+ selectkeymap("main", 1);
+ fixsuffix();
+ if ((s = (unsigned char *)getlinknode(bufstack))) {
+ setline((char *)s);
+ zsfree((char *)s);
+ if (stackcs != -1) {
+ cs = stackcs;
+ stackcs = -1;
+ if (cs > ll)
+ cs = ll;
+ }
+ if (stackhist != -1) {
+ histline = stackhist;
+ stackhist = -1;
+ }
+ }
+ initundo();
+ if (isset(PROMPTCR))
+ putc('\r', shout);
+ if (tmout)
+ alarm(tmout);
+ zleactive = 1;
+ resetneeded = 1;
+ errflag = retflag = 0;
+ lastcol = -1;
+ initmodifier(&zmod);
+ prefixflag = 0;
+ feepflag = 0;
+ refresh();
+ while (!done && !errflag) {
+
+ statusline = NULL;
+ vilinerange = 0;
+ reselectkeymap();
+ bindk = getkeycmd();
+ if (!ll && isfirstln && c == eofchar) {
+ eofsent = 1;
+ break;
+ }
+ if (bindk) {
+ execzlefunc(bindk);
+ handleprefixes();
+ /* for vi mode, make sure the cursor isn't somewhere illegal */
+ if (invicmdmode() && cs > findbol() &&
+ (cs == ll || line[cs] == '\n'))
+ cs--;
+ if (undoing)
+ handleundo();
+ } else {
+ errflag = 1;
+ break;
+ }
+#ifdef HAVE_SELECT
+ if (baud && !(lastcmd & ZLE_MENUCMP)) {
+ FD_SET(SHTTY, &foofd);
+ if ((tv.tv_usec = cost * costmult) > 500000)
+ tv.tv_usec = 500000;
+ if (!kungetct && select(SHTTY+1, (SELECT_ARG_2_T) & foofd,
+ NULL, NULL, &tv) <= 0)
+ refresh();
+ } else
+#endif
+ if (!kungetct)
+ refresh();
+ handlefeep();
+ }
+ statusline = NULL;
+ invalidatelist();
+ trashzle();
+ free(lpptbuf);
+ free(rpptbuf);
+ zleactive = 0;
+ alarm(0);
+ } LASTALLOC;
+ zsfree(curhistline);
+ freeundo();
+ if (eofsent) {
+ free(line);
+ line = NULL;
+ } else {
+ line[ll++] = '\n';
+ line = (unsigned char *) metafy((char *) line, ll, META_REALLOC);
+ }
+ forget_edits();
+ errno = old_errno;
+ return line;
+}
+
+/* execute a widget */
+
+/**/
+void
+execzlefunc(Thingy func)
+{
+ Widget w;
+
+ if(func->flags & DISABLED) {
+ /* this thingy is not the name of a widget */
+ char *nm = niceztrdup(func->nam);
+ char *msg = tricat("No such widget `", nm, "'");
+
+ zsfree(nm);
+ showmsg(msg);
+ zsfree(msg);
+ feep();
+ } else if((w = func->widget)->flags & WIDGET_INT) {
+ int wflags = w->flags;
+
+ if(!(wflags & ZLE_KEEPSUFFIX))
+ removesuffix();
+ if(!(wflags & ZLE_MENUCMP)) {
+ fixsuffix();
+ invalidatelist();
+ }
+ if (wflags & ZLE_LINEMOVE)
+ vilinerange = 1;
+ if(!(wflags & ZLE_LASTCOL))
+ lastcol = -1;
+ w->u.fn();
+ lastcmd = wflags;
+ } else {
+ List l = getshfunc(w->u.fnnam);
+
+ if(l == &dummy_list) {
+ /* the shell function doesn't exist */
+ char *nm = niceztrdup(w->u.fnnam);
+ char *msg = tricat("No such shell function `", nm, "'");
+
+ zsfree(nm);
+ showmsg(msg);
+ zsfree(msg);
+ feep();
+ } else {
+ startparamscope();
+ makezleparams();
+ doshfunc(l, NULL, 0, 1);
+ endparamscope();
+ lastcmd = 0;
+ }
+ }
+}
+
+/* initialise command modifiers */
+
+/**/
+static void
+initmodifier(struct modifier *mp)
+{
+ mp->flags = 0;
+ mp->mult = 1;
+ mp->tmult = 1;
+ mp->vibuf = 0;
+}
+
+/* Reset command modifiers, unless the command just executed was a prefix. *
+ * Also set zmult, if the multiplier has been amended. */
+
+/**/
+static void
+handleprefixes(void)
+{
+ if (prefixflag) {
+ prefixflag = 0;
+ if(zmod.flags & MOD_TMULT) {
+ zmod.flags |= MOD_MULT;
+ zmod.mult = zmod.tmult;
+ }
+ } else
+ initmodifier(&zmod);
+}
+
+/* vared: edit (literally) a parameter value */
+
+/**/
+static int
+bin_vared(char *name, char **args, char *ops, int func)
+{
+ char *s;
+ char *t;
+ Param pm;
+ int create = 0;
+ char *p1 = NULL, *p2 = NULL;
+
+ /* all options are handled as arguments */
+ while (*args && **args == '-') {
+ while (*++(*args))
+ switch (**args) {
+ case 'c':
+ /* -c option -- allow creation of the parameter if it doesn't
+ yet exist */
+ create = 1;
+ break;
+ case 'p':
+ /* -p option -- set main prompt string */
+ if ((*args)[1])
+ p1 = *args + 1, *args = "" - 1;
+ else if (args[1])
+ p1 = *(++args), *args = "" - 1;
+ else {
+ zwarnnam(name, "prompt string expected after -%c", NULL,
+ **args);
+ return 1;
+ }
+ break;
+ case 'r':
+ /* -r option -- set right prompt string */
+ if ((*args)[1])
+ p2 = *args + 1, *args = "" - 1;
+ else if (args[1])
+ p2 = *(++args), *args = "" - 1;
+ else {
+ zwarnnam(name, "prompt string expected after -%c", NULL,
+ **args);
+ return 1;
+ }
+ break;
+ case 'h':
+ /* -h option -- enable history */
+ ops['h'] = 1;
+ break;
+ default:
+ /* unrecognised option character */
+ zwarnnam(name, "unknown option: %s", *args, 0);
+ return 1;
+ }
+ args++;
+ }
+
+ /* check we have a parameter name */
+ if (!*args) {
+ zwarnnam(name, "missing variable", NULL, 0);
+ return 1;
+ }
+ /* handle non-existent parameter */
+ if (!(s = getsparam(args[0]))) {
+ if (create)
+ createparam(args[0], PM_SCALAR);
+ else {
+ zwarnnam(name, "no such variable: %s", args[0], 0);
+ return 1;
+ }
+ }
+
+ if(zleactive) {
+ zwarnnam(name, "ZLE cannot be used recursively (yet)", NULL, 0);
+ return 1;
+ }
+
+ /* edit the parameter value */
+ PERMALLOC {
+ pushnode(bufstack, ztrdup(s));
+ } LASTALLOC;
+ t = (char *) zleread(p1, p2, ops['h']);
+ if (!t || errflag) {
+ /* error in editing */
+ errflag = 0;
+ return 1;
+ }
+ /* strip off trailing newline, if any */
+ if (t[strlen(t) - 1] == '\n')
+ t[strlen(t) - 1] = '\0';
+ /* final assignment of parameter value */
+ pm = (Param) paramtab->getnode(paramtab, args[0]);
+ if (pm && PM_TYPE(pm->flags) == PM_ARRAY) {
+ char **a;
+
+ PERMALLOC {
+ a = spacesplit(t, 1);
+ } LASTALLOC;
+ setaparam(args[0], a);
+ } else
+ setsparam(args[0], t);
+ return 0;
+}
+
+/**/
+void
+describekeybriefly(void)
+{
+ char *seq, *str, *msg, *is;
+ Thingy func;
+
+ if (statusline)
+ return;
+ statusline = "Describe key briefly: _";
+ statusll = strlen(statusline);
+ refresh();
+ seq = getkeymapcmd(curkeymap, &func, &str);
+ statusline = NULL;
+ if(!*seq)
+ return;
+ msg = bindztrdup(seq);
+ msg = appstr(msg, " is ");
+ if (!func)
+ is = bindztrdup(str);
+ else
+ is = niceztrdup(func->nam);
+ msg = appstr(msg, is);
+ zsfree(is);
+ showmsg(msg);
+ zsfree(msg);
+}
+
+#define MAXFOUND 4
+
+struct findfunc {
+ Thingy func;
+ int found;
+ char *msg;
+};
+
+/**/
+static void
+scanfindfunc(char *seq, Thingy func, char *str, void *magic)
+{
+ struct findfunc *ff = magic;
+
+ if(func != ff->func)
+ return;
+ if (!ff->found++)
+ ff->msg = appstr(ff->msg, " is on");
+ if(ff->found <= MAXFOUND) {
+ char *b = bindztrdup(seq);
+
+ ff->msg = appstr(ff->msg, " ");
+ ff->msg = appstr(ff->msg, b);
+ zsfree(b);
+ }
+}
+
+/**/
+void
+whereis(void)
+{
+ struct findfunc ff;
+
+ if (!(ff.func = executenamedcommand("Where is: ")))
+ return;
+ ff.found = 0;
+ ff.msg = niceztrdup(ff.func->nam);
+ scankeymap(curkeymap, 1, scanfindfunc, &ff);
+ if (!ff.found)
+ ff.msg = appstr(ff.msg, " is not bound to any key");
+ else if(ff.found > MAXFOUND)
+ ff.msg = appstr(ff.msg, " et al");
+ showmsg(ff.msg);
+ zsfree(ff.msg);
+}
+
+/**/
+void
+trashzle(void)
+{
+ if (zleactive) {
+ /* This refresh() is just to get the main editor display right and *
+ * get the cursor in the right place. For that reason, we disable *
+ * list display (which would otherwise result in infinite *
+ * recursion [at least, it would if refresh() didn't have its *
+ * extra `inlist' check]). */
+ int sl = showinglist;
+ showinglist = 0;
+ refresh();
+ showinglist = sl;
+ moveto(nlnct, 0);
+ if (clearflag && tccan(TCCLEAREOD)) {
+ tcout(TCCLEAREOD);
+ clearflag = 0;
+ }
+ if (postedit)
+ fprintf(shout, "%s", postedit);
+ fflush(shout);
+ resetneeded = 1;
+ settyinfo(&shttyinfo);
+ }
+ if (errflag)
+ kungetct = 0;
+}
+
+static struct builtin bintab[] = {
+ BUILTIN("bindkey", 0, bin_bindkey, 0, -1, 0, "evaMldDANmrsLR", NULL),
+ BUILTIN("vared", 0, bin_vared, 1, 7, 0, NULL, NULL),
+ BUILTIN("zle", 0, bin_zle, 0, -1, 0, "lDANL", NULL),
+};
+
+/**/
+int
+boot_zle(Module m)
+{
+ /* Set up editor entry points */
+ trashzleptr = trashzle;
+ gotwordptr = gotword;
+ refreshptr = refresh;
+ spaceinlineptr = spaceinline;
+ zlereadptr = zleread;
+
+ /* initialise the thingies */
+ init_thingies();
+
+ /* miscellaneous initialisations */
+ stackhist = stackcs = -1;
+ kungetbuf = (char *) zalloc(kungetsz = 32);
+
+ /* initialise the keymap system */
+ init_keymaps();
+
+ addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+ return 0;
+}
+
+#ifdef MODULE
+
+/**/
+int
+cleanup_zle(Module m)
+{
+ int i;
+
+ if(zleactive) {
+ zerrnam(m->nam, "can't unload the zle module while zle is active",
+ NULL, 0);
+ return 1;
+ }
+
+ deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+ cleanup_keymaps();
+ deletehashtable(thingytab);
+
+ zfree(vichgbuf, vichgbufsz);
+ zfree(kungetbuf, kungetsz);
+ free_isrch_spots();
+
+ zfree(cutbuf.buf, cutbuf.len);
+ for(i = KRINGCT; i--; )
+ zfree(kring[i].buf, kring[i].len);
+ for(i = 35; i--; )
+ zfree(vibuf[i].buf, vibuf[i].len);
+
+ /* editor entry points */
+ trashzleptr = noop_function;
+ gotwordptr = noop_function;
+ refreshptr = noop_function;
+ spaceinlineptr = noop_function_int;
+ zlereadptr = fallback_zleread;
+
+ return 0;
+}
+
+#endif /* MODULE */
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
new file mode 100644
index 000000000..42953852f
--- /dev/null
+++ b/Src/Zle/zle_misc.c
@@ -0,0 +1,816 @@
+/*
+ * zle_misc.c - miscellaneous editor routines
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * 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 Paul Falstad 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 Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad 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 Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "zle.mdh"
+#include "zle_misc.pro"
+
+/* insert a metafied string, with repetition and suffix removal */
+
+/**/
+void
+doinsert(char *str)
+{
+ char *s;
+ int len = ztrlen(str);
+ int c1 = *str == Meta ? STOUC(str[1])^32 : STOUC(*str);/* first character */
+ int neg = zmult < 0; /* insert *after* the cursor? */
+ int m = neg ? -zmult : zmult; /* number of copies to insert */
+
+ iremovesuffix(c1);
+ invalidatelist();
+
+ if(insmode)
+ spaceinline(m * len);
+ else if(cs + m * len > ll)
+ spaceinline(cs + m * len - ll);
+ while(m--)
+ for(s = str; *s; s++)
+ line[cs++] = *s == Meta ? *++s ^ 32 : *s;
+ if(neg)
+ cs += zmult * len;
+}
+
+/**/
+void
+selfinsert(void)
+{
+ char s[3], *p = s;
+
+ if(imeta(c)) {
+ *p++ = Meta;
+ c ^= 32;
+ }
+ *p++ = c;
+ *p = 0;
+ doinsert(s);
+}
+
+/**/
+void
+selfinsertunmeta(void)
+{
+ c &= 0x7f;
+ if (c == '\r')
+ c = '\n';
+ selfinsert();
+}
+
+/**/
+void
+deletechar(void)
+{
+ if (zmult < 0) {
+ zmult = -zmult;
+ backwarddeletechar();
+ zmult = -zmult;
+ return;
+ }
+ if (cs + zmult <= ll) {
+ cs += zmult;
+ backdel(zmult);
+ } else
+ feep();
+}
+
+/**/
+void
+backwarddeletechar(void)
+{
+ if (zmult < 0) {
+ zmult = -zmult;
+ deletechar();
+ zmult = -zmult;
+ return;
+ }
+ backdel(zmult > cs ? cs : zmult);
+}
+
+/**/
+void
+killwholeline(void)
+{
+ int i, fg, n = zmult;
+
+ if (n < 0)
+ return;
+ while (n--) {
+ if ((fg = (cs && cs == ll)))
+ cs--;
+ while (cs && line[cs - 1] != '\n')
+ cs--;
+ for (i = cs; i != ll && line[i] != '\n'; i++);
+ forekill(i - cs + (i != ll), fg);
+ }
+}
+
+/**/
+void
+killbuffer(void)
+{
+ cs = 0;
+ forekill(ll, 0);
+}
+
+/**/
+void
+backwardkillline(void)
+{
+ int i = 0, n = zmult;
+
+ if (n < 0) {
+ zmult = -n;
+ killline();
+ zmult = n;
+ return;
+ }
+ while (n--) {
+ if (cs && line[cs - 1] == '\n')
+ cs--, i++;
+ else
+ while (cs && line[cs - 1] != '\n')
+ cs--, i++;
+ }
+ forekill(i, 1);
+}
+
+/**/
+void
+gosmacstransposechars(void)
+{
+ int cc;
+
+ if (cs < 2 || line[cs - 1] == '\n' || line[cs - 2] == '\n') {
+ if (cs == ll || line[cs] == '\n' ||
+ ((cs + 1 == ll || line[cs + 1] == '\n') &&
+ (!cs || line[cs - 1] == '\n'))) {
+ feep();
+ return;
+ }
+ cs += (cs == 0 || line[cs - 1] == '\n') ? 2 : 1;
+ }
+ cc = line[cs - 2];
+ line[cs - 2] = line[cs - 1];
+ line[cs - 1] = cc;
+}
+
+/**/
+void
+transposechars(void)
+{
+ int cc, ct;
+ int n = zmult;
+ int neg = n < 0;
+
+ if (neg)
+ n = -n;
+ while (n--) {
+ if (!(ct = cs) || line[cs - 1] == '\n') {
+ if (ll == cs || line[cs] == '\n') {
+ feep();
+ return;
+ }
+ if (!neg)
+ cs++;
+ ct++;
+ }
+ if (neg) {
+ if (cs && line[cs - 1] != '\n') {
+ cs--;
+ if (ct > 1 && line[ct - 2] != '\n')
+ ct--;
+ }
+ } else {
+ if (cs != ll && line[cs] != '\n')
+ cs++;
+ }
+ if (ct == ll || line[ct] == '\n')
+ ct--;
+ if (ct < 1 || line[ct - 1] == '\n') {
+ feep();
+ return;
+ }
+ cc = line[ct - 1];
+ line[ct - 1] = line[ct];
+ line[ct] = cc;
+ }
+}
+
+/**/
+void
+poundinsert(void)
+{
+ cs = 0;
+ vifirstnonblank();
+ if (line[cs] != '#') {
+ spaceinline(1);
+ line[cs] = '#';
+ cs = findeol();
+ while(cs != ll) {
+ cs++;
+ vifirstnonblank();
+ spaceinline(1);
+ line[cs] = '#';
+ cs = findeol();
+ }
+ } else {
+ foredel(1);
+ cs = findeol();
+ while(cs != ll) {
+ cs++;
+ vifirstnonblank();
+ if(line[cs] == '#')
+ foredel(1);
+ cs = findeol();
+ }
+ }
+ done = 1;
+}
+
+/**/
+void
+acceptline(void)
+{
+ done = 1;
+}
+
+/**/
+void
+acceptandhold(void)
+{
+ pushnode(bufstack, metafy((char *)line, ll, META_DUP));
+ stackcs = cs;
+ done = 1;
+}
+
+/**/
+void
+killline(void)
+{
+ int i = 0, n = zmult;
+
+ if (n < 0) {
+ zmult = -n;
+ backwardkillline();
+ zmult = n;
+ return;
+ }
+ while (n--) {
+ if (line[cs] == '\n')
+ cs++, i++;
+ else
+ while (cs != ll && line[cs] != '\n')
+ cs++, i++;
+ }
+ backkill(i, 0);
+}
+
+/**/
+void
+killregion(void)
+{
+ if (mark > ll)
+ mark = ll;
+ if (mark > cs)
+ forekill(mark - cs, 0);
+ else
+ backkill(cs - mark, 1);
+}
+
+/**/
+void
+copyregionaskill(void)
+{
+ if (mark > ll)
+ mark = ll;
+ if (mark > cs)
+ cut(cs, mark - cs, 0);
+ else
+ cut(mark, cs - mark, 1);
+}
+
+static int kct, yankb, yanke;
+
+/**/
+void
+yank(void)
+{
+ Cutbuffer buf = &cutbuf;
+ int n = zmult;
+
+ if (n < 0)
+ return;
+ if (zmod.flags & MOD_VIBUF)
+ buf = &vibuf[zmod.vibuf];
+ if (!buf->buf) {
+ feep();
+ return;
+ }
+ mark = cs;
+ yankb = cs;
+ while (n--) {
+ kct = kringnum;
+ spaceinline(buf->len);
+ memcpy((char *)line + cs, buf->buf, buf->len);
+ cs += buf->len;
+ yanke = cs;
+ }
+}
+
+/**/
+void
+yankpop(void)
+{
+ int cc;
+
+ if (!(lastcmd & ZLE_YANK) || !kring[kct].buf) {
+ feep();
+ return;
+ }
+ cs = yankb;
+ foredel(yanke - yankb);
+ cc = kring[kct].len;
+ spaceinline(cc);
+ memcpy((char *)line + cs, kring[kct].buf, cc);
+ cs += cc;
+ yanke = cs;
+ kct = (kct + KRINGCT - 1) % KRINGCT;
+}
+
+/**/
+void
+overwritemode(void)
+{
+ insmode ^= 1;
+}
+/**/
+void
+whatcursorposition(void)
+{
+ char msg[100];
+ char *s = msg;
+ int bol = findbol();
+ int c = STOUC(line[cs]);
+
+ if (cs == ll)
+ strucpy(&s, "EOF");
+ else {
+ strucpy(&s, "Char: ");
+ switch (c) {
+ case ' ':
+ strucpy(&s, "SPC");
+ break;
+ case '\t':
+ strucpy(&s, "TAB");
+ break;
+ case '\n':
+ strucpy(&s, "LFD");
+ break;
+ default:
+ if (imeta(c)) {
+ *s++ = Meta;
+ *s++ = c ^ 32;
+ } else
+ *s++ = c;
+ }
+ sprintf(s, " (0%o, %d, 0x%x)", c, c, c);
+ s += strlen(s);
+ }
+ sprintf(s, " point %d of %d(%d%%) column %d", cs+1, ll+1,
+ ll ? 100 * cs / ll : 0, cs - bol);
+ showmsg(msg);
+}
+
+/**/
+void
+undefinedkey(void)
+{
+ feep();
+}
+
+/**/
+void
+quotedinsert(void)
+{
+#ifndef HAS_TIO
+ struct sgttyb sob;
+
+ sob = shttyinfo.sgttyb;
+ sob.sg_flags = (sob.sg_flags | RAW) & ~ECHO;
+ ioctl(SHTTY, TIOCSETN, &sob);
+#endif
+ c = getkey(0);
+#ifndef HAS_TIO
+ setterm();
+#endif
+ if (c < 0)
+ feep();
+ else
+ selfinsert();
+}
+
+/**/
+void
+digitargument(void)
+{
+ int sign = (zmult < 0) ? -1 : 1;
+
+ if (!(zmod.flags & MOD_TMULT))
+ zmod.tmult = 0;
+ if (zmod.flags & MOD_NEG) {
+ /* If we just had a negative argument, this is the digit, *
+ * rather than the -1 assumed by negargument() */
+ zmod.tmult = sign * (c & 0xf);
+ zmod.flags &= ~MOD_NEG;
+ } else
+ zmod.tmult = zmod.tmult * 10 + sign * (c & 0xf);
+ zmod.flags |= MOD_TMULT;
+ prefixflag = 1;
+}
+
+/**/
+void
+negargument(void)
+{
+ if(zmod.flags & MOD_TMULT) {
+ feep();
+ return;
+ }
+ zmod.tmult = -1;
+ zmod.flags |= MOD_TMULT|MOD_NEG;
+ prefixflag = 1;
+}
+
+/**/
+void
+universalargument(void)
+{
+ int digcnt = 0, pref = 0, minus = 1, gotk;
+ while ((gotk = getkey(0)) != EOF) {
+ if (gotk == '-' && !digcnt) {
+ minus = -1;
+ digcnt++;
+ } else if (gotk >= '0' && gotk <= '9') {
+ pref = pref * 10 + (gotk & 0xf);
+ digcnt++;
+ } else {
+ ungetkey(gotk);
+ break;
+ }
+ }
+ if (digcnt)
+ zmod.tmult = minus * (pref ? pref : 1);
+ else
+ zmod.tmult *= 4;
+ zmod.flags |= MOD_TMULT;
+ prefixflag = 1;
+}
+
+/**/
+void
+copyprevword(void)
+{
+ int len, t0;
+
+ for (t0 = cs - 1; t0 >= 0; t0--)
+ if (iword(line[t0]))
+ break;
+ for (; t0 >= 0; t0--)
+ if (!iword(line[t0]))
+ break;
+ if (t0)
+ t0++;
+ len = cs - t0;
+ spaceinline(len);
+ memcpy((char *)&line[cs], (char *)&line[t0], len);
+ cs += len;
+}
+
+/**/
+void
+sendbreak(void)
+{
+ errflag = 1;
+}
+
+/**/
+void
+quoteregion(void)
+{
+ char *str;
+ size_t len;
+
+ if (mark > ll)
+ mark = ll;
+ if (mark < cs) {
+ int tmp = mark;
+ mark = cs;
+ cs = tmp;
+ }
+ str = (char *)hcalloc(len = mark - cs);
+ memcpy(str, (char *)&line[cs], len);
+ foredel(len);
+ str = makequote(str, &len);
+ spaceinline(len);
+ memcpy((char *)&line[cs], str, len);
+ mark = cs;
+ cs += len;
+}
+
+/**/
+void
+quoteline(void)
+{
+ char *str;
+ size_t len = ll;
+
+ str = makequote((char *)line, &len);
+ sizeline(len);
+ memcpy(line, str, len);
+ cs = ll = len;
+}
+
+/**/
+static char *
+makequote(char *str, size_t *len)
+{
+ int qtct = 0;
+ char *l, *ol;
+ char *end = str + *len;
+
+ for (l = str; l < end; l++)
+ if (*l == '\'')
+ qtct++;
+ *len += 2 + qtct*3;
+ l = ol = (char *)halloc(*len);
+ *l++ = '\'';
+ for (; str < end; str++)
+ if (*str == '\'') {
+ *l++ = '\'';
+ *l++ = '\\';
+ *l++ = '\'';
+ *l++ = '\'';
+ } else
+ *l++ = *str;
+ *l++ = '\'';
+ return ol;
+}
+
+static char *cmdbuf;
+static LinkList cmdll;
+static int cmdambig;
+
+/**/
+static void
+scancompcmd(HashNode hn, int flags)
+{
+ int l;
+ Thingy t = (Thingy) hn;
+
+ if(strpfx(cmdbuf, t->nam)) {
+ addlinknode(cmdll, t->nam);
+ l = pfxlen(peekfirst(cmdll), t->nam);
+ if (l < cmdambig)
+ cmdambig = l;
+ }
+
+}
+
+#define NAMLEN 60
+
+/**/
+Thingy
+executenamedcommand(char *prmt)
+{
+ Thingy cmd;
+ int len, l = strlen(prmt);
+ char *ptr;
+ char *okeymap = curkeymapname;
+
+ cmdbuf = halloc(l + NAMLEN + 2);
+ strcpy(cmdbuf, prmt);
+ statusline = cmdbuf;
+ selectkeymap("main", 1);
+ ptr = cmdbuf += l;
+ len = 0;
+ for (;;) {
+ *ptr = '_';
+ statusll = l + len + 1;
+ refresh();
+ if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) {
+ statusline = NULL;
+ selectkeymap(okeymap, 1);
+ return NULL;
+ }
+ if(cmd == Th(z_clearscreen)) {
+ clearscreen();
+ } else if(cmd == Th(z_redisplay)) {
+ redisplay();
+ } else if(cmd == Th(z_viquotedinsert)) {
+ *ptr = '^';
+ refresh();
+ c = getkey(0);
+ if(c == EOF || !c || len == NAMLEN)
+ feep();
+ else
+ *ptr++ = c, len++;
+ } else if(cmd == Th(z_quotedinsert)) {
+ if((c = getkey(0)) == EOF || !c || len == NAMLEN)
+ feep();
+ else
+ *ptr++ = c, len++;
+ } else if(cmd == Th(z_backwarddeletechar) ||
+ cmd == Th(z_vibackwarddeletechar)) {
+ if (len)
+ len--, ptr--;
+ } else if(cmd == Th(z_killregion) || cmd == Th(z_backwardkillword) ||
+ cmd == Th(z_vibackwardkillword)) {
+ while (len && (len--, *--ptr != '-'));
+ } else if(cmd == Th(z_killwholeline) || cmd == Th(z_vikillline) ||
+ cmd == Th(z_backwardkillline)) {
+ len = 0;
+ ptr = cmdbuf;
+ } else {
+ if(cmd == Th(z_acceptline) || cmd == Th(z_vicmdmode)) {
+ Thingy r;
+ unambiguous:
+ *ptr = 0;
+ r = rthingy(cmdbuf);
+ if (!(r->flags & DISABLED)) {
+ unrefthingy(r);
+ statusline = NULL;
+ selectkeymap(okeymap, 1);
+ return r;
+ }
+ unrefthingy(r);
+ }
+ if(cmd == Th(z_selfinsertunmeta)) {
+ c &= 0x7f;
+ if(c == '\r')
+ c = '\n';
+ cmd = Th(z_selfinsert);
+ }
+ if (cmd == Th(z_listchoices) || cmd == Th(z_deletecharorlist) ||
+ cmd == Th(z_expandorcomplete) || cmd == Th(z_completeword) ||
+ cmd == Th(z_expandorcompleteprefix) || cmd == Th(z_vicmdmode) ||
+ cmd == Th(z_acceptline) || c == ' ' || c == '\t') {
+ cmdambig = 100;
+
+ HEAPALLOC {
+ cmdll = newlinklist();
+ *ptr = 0;
+
+ scanhashtable(thingytab, 1, 0, DISABLED, scancompcmd, 0);
+ } LASTALLOC;
+ if (empty(cmdll))
+ feep();
+ else if (cmd == Th(z_listchoices) ||
+ cmd == Th(z_deletecharorlist)) {
+ int zmultsav = zmult;
+ *ptr = '_';
+ statusll = l + len + 1;
+ zmult = 1;
+ listlist(cmdll);
+ zmult = zmultsav;
+ } else if (!nextnode(firstnode(cmdll))) {
+ strcpy(ptr = cmdbuf, peekfirst(cmdll));
+ ptr += (len = strlen(ptr));
+ if(cmd == Th(z_acceptline) || cmd == Th(z_vicmdmode))
+ goto unambiguous;
+ } else {
+ strcpy(cmdbuf, peekfirst(cmdll));
+ ptr = cmdbuf + cmdambig;
+ *ptr = '_';
+ if (isset(AUTOLIST) &&
+ !(isset(LISTAMBIGUOUS) && cmdambig > len)) {
+ int zmultsav = zmult;
+ if (isset(LISTBEEP))
+ feep();
+ statusll = l + cmdambig + 1;
+ zmult = 1;
+ listlist(cmdll);
+ zmult = zmultsav;
+ }
+ len = cmdambig;
+ }
+ } else {
+ if (len == NAMLEN || icntrl(c) || cmd != Th(z_selfinsert))
+ feep();
+ else
+ *ptr++ = c, len++;
+ }
+ }
+ handlefeep();
+ }
+}
+
+/*****************/
+/* Suffix system */
+/*****************/
+
+/*
+ * The completion system sometimes tentatively adds a suffix to a word,
+ * which can be removed depending on what is inserted next. These
+ * functions provide the capability to handle a removable suffix.
+ *
+ * Any removable suffix consists of characters immediately before the
+ * cursor. Whether it is removed depends on the next editing action.
+ * There can be more than one suffix simultaneously present, with
+ * different actions deleting different numbers of characters.
+ *
+ * If the next editing action changes the buffer other than by inserting
+ * characters, normally the suffix should be removed so as to leave a
+ * meaningful complete word. The behaviour should be the same if the
+ * next character inserted is a word separator. If the next character
+ * reasonably belongs where it is typed, or if the next editing action
+ * is a deletion, the suffix should not be removed. Other reasons for
+ * suffix removal may have other behaviour.
+ *
+ * In order to maintain a consistent state, after a suffix has been added
+ * the table *must* be zeroed, one way or another, before the buffer is
+ * changed. If the suffix is not being removed, call fixsuffix() to
+ * indicate that it is being permanently fixed.
+ */
+
+/* Length of suffix to remove when inserting each possible character value. *
+ * suffixlen[256] is the length to remove for non-insertion editing actions. */
+
+/**/
+int suffixlen[257];
+
+/* Set up suffix: the last n characters are a suffix that should be *
+ * removed in the usual word end conditions. */
+
+/**/
+void
+makesuffix(int n)
+{
+ suffixlen[256] = suffixlen[' '] = suffixlen['\t'] = suffixlen['\n'] = n;
+}
+
+/* Set up suffix for parameter names: the last n characters are a suffix *
+ * that should be removed if the next character is one of the ones that *
+ * needs to go immediately after the parameter name. br indicates that *
+ * the name is in braces (${PATH} instead of $PATH), so the extra *
+ * characters that can only be used in braces are included. */
+
+/**/
+void
+makeparamsuffix(int br, int n)
+{
+ if(br || unset(KSHARRAYS))
+ suffixlen[':'] = suffixlen['['] = n;
+ if(br) {
+ suffixlen['#'] = suffixlen['%'] = suffixlen['?'] = n;
+ suffixlen['-'] = suffixlen['+'] = suffixlen['='] = n;
+ /*{*/ suffixlen['}'] = n;
+ }
+}
+
+/* Remove suffix, if there is one, when inserting character c. */
+
+/**/
+void
+iremovesuffix(int c)
+{
+ int sl = suffixlen[c];
+ if(sl) {
+ backdel(sl);
+ invalidatelist();
+ }
+ fixsuffix();
+}
+
+/* Fix the suffix in place, if there is one, making it non-removable. */
+
+/**/
+void
+fixsuffix(void)
+{
+ memset(suffixlen, 0, sizeof(suffixlen));
+}
diff --git a/Src/Zle/zle_move.c b/Src/Zle/zle_move.c
new file mode 100644
index 000000000..8ed4c657a
--- /dev/null
+++ b/Src/Zle/zle_move.c
@@ -0,0 +1,502 @@
+/*
+ * zle_move.c - editor movement
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * 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 Paul Falstad 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 Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad 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 Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "zle.mdh"
+#include "zle_move.pro"
+
+static vimarkcs[27], vimarkline[27];
+
+/**/
+void
+beginningofline(void)
+{
+ int n = zmult;
+
+ if (n < 0) {
+ zmult = -n;
+ endofline();
+ zmult = n;
+ return;
+ }
+ while (n--) {
+ if (cs == 0)
+ return;
+ if (line[cs - 1] == '\n')
+ if (!--cs)
+ return;
+ while (cs && line[cs - 1] != '\n')
+ cs--;
+ }
+}
+
+/**/
+void
+endofline(void)
+{
+ int n = zmult;
+
+ if (n < 0) {
+ zmult = -n;
+ beginningofline();
+ zmult = n;
+ return;
+ }
+ while (n--) {
+ if (cs >= ll) {
+ cs = ll;
+ return;
+ }
+ if (line[cs] == '\n')
+ if (++cs == ll)
+ return;
+ while (cs != ll && line[cs] != '\n')
+ cs++;
+ }
+}
+
+/**/
+void
+beginningoflinehist(void)
+{
+ int n = zmult;
+
+ if (n < 0) {
+ zmult = -n;
+ endoflinehist();
+ zmult = n;
+ return;
+ }
+ while (n) {
+ if (cs == 0)
+ break;
+ if (line[cs - 1] == '\n')
+ if (!--cs)
+ break;
+ while (cs && line[cs - 1] != '\n')
+ cs--;
+ n--;
+ }
+ if (n) {
+ int m = zmult;
+
+ zmult = n;
+ uphistory();
+ zmult = m;
+ cs = 0;
+ }
+}
+
+/**/
+void
+endoflinehist(void)
+{
+ int n = zmult;
+
+ if (n < 0) {
+ zmult = -n;
+ beginningoflinehist();
+ zmult = n;
+ return;
+ }
+ while (n) {
+ if (cs >= ll) {
+ cs = ll;
+ break;
+ }
+ if (line[cs] == '\n')
+ if (++cs == ll)
+ break;
+ while (cs != ll && line[cs] != '\n')
+ cs++;
+ n--;
+ }
+ if (n) {
+ int m = zmult;
+
+ zmult = n;
+ downhistory();
+ zmult = m;
+ }
+}
+
+/**/
+void
+forwardchar(void)
+{
+ cs += zmult;
+ if (cs > ll)
+ cs = ll;
+ if (cs < 0)
+ cs = 0;
+}
+
+/**/
+void
+backwardchar(void)
+{
+ cs -= zmult;
+ if (cs > ll)
+ cs = ll;
+ if (cs < 0)
+ cs = 0;
+}
+
+/**/
+void
+setmarkcommand(void)
+{
+ mark = cs;
+}
+
+/**/
+void
+exchangepointandmark(void)
+{
+ int x;
+
+ x = mark;
+ mark = cs;
+ cs = x;
+ if (cs > ll)
+ cs = ll;
+}
+
+/**/
+void
+vigotocolumn(void)
+{
+ int x, y;
+
+ findline(&x, &y);
+ if (zmult >= 0)
+ cs = x + zmult - (zmult > 0);
+ else
+ cs = y + zmult;
+ if (cs > y)
+ cs = y;
+ if (cs < x)
+ cs = x;
+}
+
+/**/
+void
+vimatchbracket(void)
+{
+ int ocs = cs, dir, ct;
+ unsigned char oth, me;
+
+ otog:
+ if (cs == ll || line[cs] == '\n') {
+ feep();
+ cs = ocs;
+ return;
+ }
+ switch (me = line[cs]) {
+ case '{':
+ dir = 1;
+ oth = '}';
+ break;
+ case /*{*/ '}':
+ virangeflag = -virangeflag;
+ dir = -1;
+ oth = '{'; /*}*/
+ break;
+ case '(':
+ dir = 1;
+ oth = ')';
+ break;
+ case ')':
+ virangeflag = -virangeflag;
+ dir = -1;
+ oth = '(';
+ break;
+ case '[':
+ dir = 1;
+ oth = ']';
+ break;
+ case ']':
+ virangeflag = -virangeflag;
+ dir = -1;
+ oth = '[';
+ break;
+ default:
+ cs++;
+ goto otog;
+ }
+ ct = 1;
+ while (cs >= 0 && cs < ll && ct) {
+ cs += dir;
+ if (line[cs] == oth)
+ ct--;
+ else if (line[cs] == me)
+ ct++;
+ }
+ if (cs < 0 || cs >= ll) {
+ feep();
+ cs = ocs;
+ } else if(dir > 0 && virangeflag)
+ cs++;
+}
+
+/**/
+void
+viforwardchar(void)
+{
+ int lim = findeol() - invicmdmode();
+ int n = zmult;
+
+ if (n < 0) {
+ zmult = -n;
+ vibackwardchar();
+ zmult = n;
+ return;
+ }
+ if (cs >= lim) {
+ feep();
+ return;
+ }
+ while (n-- && cs < lim)
+ cs++;
+}
+
+/**/
+void
+vibackwardchar(void)
+{
+ int n = zmult;
+
+ if (n < 0) {
+ zmult = -n;
+ viforwardchar();
+ zmult = n;
+ return;
+ }
+ if (cs == findbol()) {
+ feep();
+ return;
+ }
+ while (n--) {
+ cs--;
+ if (cs < 0 || line[cs] == '\n') {
+ cs++;
+ break;
+ }
+ }
+}
+
+/**/
+void
+viendofline(void)
+{
+ int oldcs = cs, n = zmult;
+
+ if (n < 1) {
+ feep();
+ return;
+ }
+ while(n--) {
+ if (cs > ll) {
+ cs = oldcs;
+ feep();
+ return;
+ }
+ cs = findeol() + 1;
+ }
+ cs--;
+ lastcol = 1<<30;
+}
+
+/**/
+void
+vibeginningofline(void)
+{
+ cs = findbol();
+}
+
+static int vfindchar, vfinddir, tailadd;
+
+/**/
+void
+vifindnextchar(void)
+{
+ if ((vfindchar = vigetkey()) != -1) {
+ vfinddir = 1;
+ tailadd = 0;
+ virepeatfind();
+ }
+}
+
+/**/
+void
+vifindprevchar(void)
+{
+ if ((vfindchar = vigetkey()) != -1) {
+ vfinddir = -1;
+ tailadd = 0;
+ virepeatfind();
+ }
+}
+
+/**/
+void
+vifindnextcharskip(void)
+{
+ if ((vfindchar = vigetkey()) != -1) {
+ vfinddir = 1;
+ tailadd = -1;
+ virepeatfind();
+ }
+}
+
+/**/
+void
+vifindprevcharskip(void)
+{
+ if ((vfindchar = vigetkey()) != -1) {
+ vfinddir = -1;
+ tailadd = 1;
+ virepeatfind();
+ }
+}
+
+/**/
+void
+virepeatfind(void)
+{
+ int ocs = cs, n = zmult;
+
+ if (!vfinddir) {
+ feep();
+ return;
+ }
+ if (n < 0) {
+ zmult = -n;
+ virevrepeatfind();
+ zmult = n;
+ return;
+ }
+ while (n--) {
+ do
+ cs += vfinddir;
+ while (cs >= 0 && cs < ll && line[cs] != vfindchar && line[cs] != '\n');
+ if (cs < 0 || cs >= ll || line[cs] == '\n') {
+ feep();
+ cs = ocs;
+ return;
+ }
+ }
+ cs += tailadd;
+ if (vfinddir == 1 && virangeflag)
+ cs++;
+}
+
+/**/
+void
+virevrepeatfind(void)
+{
+ if (zmult < 0) {
+ zmult = -zmult;
+ virepeatfind();
+ zmult = -zmult;
+ return;
+ }
+ vfinddir = -vfinddir;
+ virepeatfind();
+ vfinddir = -vfinddir;
+}
+
+/**/
+void
+vifirstnonblank(void)
+{
+ cs = findbol();
+ while (cs != ll && iblank(line[cs]))
+ cs++;
+}
+
+/**/
+void
+visetmark(void)
+{
+ int ch;
+
+ ch = getkey(0);
+ if (ch < 'a' || ch > 'z') {
+ feep();
+ return;
+ }
+ ch -= 'a';
+ vimarkcs[ch] = cs;
+ vimarkline[ch] = histline;
+}
+
+/**/
+void
+vigotomark(void)
+{
+ int ch;
+
+ ch = getkey(0);
+ if (ch == c)
+ ch = 26;
+ else {
+ if (ch < 'a' || ch > 'z') {
+ feep();
+ return;
+ }
+ ch -= 'a';
+ }
+ if (!vimarkline[ch]) {
+ feep();
+ return;
+ }
+ if (curhist != vimarkline[ch]) {
+ char *s;
+
+ remember_edits();
+ if (!(s = qgetevent(vimarkline[ch]))) {
+ vimarkline[ch] = 0;
+ feep();
+ return;
+ }
+ histline = vimarkline[ch];
+ setline(s);
+ }
+ cs = vimarkcs[ch];
+ if (cs > ll)
+ cs = ll;
+}
+
+/**/
+void
+vigotomarkline(void)
+{
+ vigotomark();
+ vifirstnonblank();
+}
diff --git a/Src/Zle/zle_params.c b/Src/Zle/zle_params.c
new file mode 100644
index 000000000..ed1420829
--- /dev/null
+++ b/Src/Zle/zle_params.c
@@ -0,0 +1,196 @@
+/*
+ * zle_params.c - ZLE special parameters
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * 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 Paul Falstad 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 Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad 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 Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "zle.mdh"
+
+#include "zle_params.pro"
+
+/*
+ * ZLE SPECIAL PARAMETERS:
+ *
+ * These special parameters are created, with a local scope, when
+ * running user-defined widget functions. Reading and writing them
+ * reads and writes bits of ZLE state. The parameters are:
+ *
+ * BUFFER (scalar) entire buffer contents
+ * CURSOR (integer) cursor position; 0 <= $CURSOR <= $#BUFFER
+ * LBUFFER (scalar) portion of buffer to the left of the cursor
+ * RBUFFER (scalar) portion of buffer to the right of the cursor
+ */
+
+#define FN(X) ( (void (*) _((void))) (X) )
+static struct zleparam {
+ char *name;
+ int type;
+ void (*setfn) _((void));
+ void (*getfn) _((void));
+ void (*unsetfn) _((Param, int));
+ void *data;
+} zleparams[] = {
+ { "BUFFER", PM_SCALAR, FN(set_buffer), FN(get_buffer),
+ zleunsetfn, NULL },
+ { "CURSOR", PM_INTEGER, FN(set_cursor), FN(get_cursor),
+ zleunsetfn, NULL },
+ { "LBUFFER", PM_SCALAR, FN(set_lbuffer), FN(get_lbuffer),
+ zleunsetfn, NULL },
+ { "RBUFFER", PM_SCALAR, FN(set_rbuffer), FN(get_rbuffer),
+ zleunsetfn, NULL },
+ { NULL, 0, NULL, NULL, NULL, NULL }
+};
+
+/**/
+void
+makezleparams(void)
+{
+ struct zleparam *zp;
+
+ for(zp = zleparams; zp->name; zp++) {
+ Param pm = createparam(zp->name, zp->type | PM_SPECIAL);
+
+ pm->level = locallevel;
+ pm->u.data = zp->data;
+ switch(PM_TYPE(zp->type)) {
+ case PM_SCALAR:
+ pm->sets.cfn = (void (*) _((Param, char *))) zp->setfn;
+ pm->gets.cfn = (char *(*) _((Param))) zp->getfn;
+ break;
+ case PM_ARRAY:
+ pm->sets.afn = (void (*) _((Param, char **))) zp->setfn;
+ pm->gets.afn = (char **(*) _((Param))) zp->getfn;
+ break;
+ case PM_INTEGER:
+ pm->sets.ifn = (void (*) _((Param, long))) zp->setfn;
+ pm->gets.ifn = (long (*) _((Param))) zp->getfn;
+ break;
+ }
+ pm->unsetfn = zp->unsetfn;
+ }
+}
+
+/* Special unset function for ZLE special parameters: act like the standard *
+ * unset function if this is a user-initiated unset, but nothing is done if *
+ * the parameter is merely going out of scope (which it will do). */
+
+/**/
+static void
+zleunsetfn(Param pm, int exp)
+{
+ if(exp)
+ stdunsetfn(pm, exp);
+}
+
+/**/
+static void
+set_buffer(Param pm, char *x)
+{
+ if(x) {
+ unmetafy(x, &ll);
+ sizeline(ll);
+ strcpy((char *)line, x);
+ zsfree(x);
+ if(cs > ll)
+ cs = ll;
+ } else
+ cs = ll = 0;
+}
+
+/**/
+static char *
+get_buffer(Param pm)
+{
+ return metafy((char *)line, ll, META_HEAPDUP);
+}
+
+/**/
+static void
+set_cursor(Param pm, long x)
+{
+ if(x < 0)
+ cs = 0;
+ else if(x > ll)
+ cs = ll;
+ else
+ cs = x;
+}
+
+/**/
+static long
+get_cursor(Param pm)
+{
+ return cs;
+}
+
+/**/
+static void
+set_lbuffer(Param pm, char *x)
+{
+ char *y;
+ int len;
+
+ if(x)
+ unmetafy(y = x, &len);
+ else
+ y = "", len = 0;
+ sizeline(ll - cs + len);
+ memmove(line + len, line + cs, ll - cs);
+ memcpy(line, y, len);
+ ll = ll - cs + len;
+ cs = len;
+ zsfree(x);
+}
+
+/**/
+static char *
+get_lbuffer(Param pm)
+{
+ return metafy((char *)line, cs, META_HEAPDUP);
+}
+
+/**/
+static void
+set_rbuffer(Param pm, char *x)
+{
+ char *y;
+ int len;
+
+ if(x)
+ unmetafy(y = x, &len);
+ else
+ y = "", len = 0;
+ sizeline(ll = cs + len);
+ memcpy(line + cs, y, len);
+ zsfree(x);
+}
+
+/**/
+static char *
+get_rbuffer(Param pm)
+{
+ return metafy((char *)line + cs, ll - cs, META_HEAPDUP);
+}
diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c
new file mode 100644
index 000000000..4621b5124
--- /dev/null
+++ b/Src/Zle/zle_refresh.c
@@ -0,0 +1,1116 @@
+/*
+ * zle_refresh.c - screen update
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * 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 Paul Falstad 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 Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad 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 Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "zle.mdh"
+#include "zle_refresh.pro"
+
+/* Expanded prompts */
+
+/**/
+char *lpptbuf, *rpptbuf;
+
+/* Text attributes after displaying prompts */
+
+/**/
+unsigned pmpt_attr, rpmpt_attr;
+
+/* number of lines displayed */
+
+/**/
+int nlnct;
+
+/* Most lines of the buffer we've shown at once with the current list *
+ * showing. == 0 if there is no list. == -1 if a new list has just *
+ * been put on the screen. == -2 if refresh() needs to put up a new *
+ * list. */
+
+/**/
+int showinglist;
+
+/* Non-zero if ALWAYS_LAST_PROMPT has been used, meaning that the *
+ * screen below the buffer display should not be cleared by *
+ * refresh(), but should be by trashzle(). */
+
+/**/
+int clearflag;
+
+#ifdef HAVE_SELECT
+/* cost of last update */
+/**/
+int cost;
+
+# define SELECT_ADD_COST(X) cost += X
+# define zputc(a, b) putc(a, b), cost++
+# define zwrite(a, b, c, d) fwrite(a, b, c, d), cost += (b * c)
+#else
+# define SELECT_ADD_COST(X)
+# define zputc(a, b) putc(a, b)
+# define zwrite(a, b, c, d) fwrite(a, b, c, d)
+#endif
+
+/* Oct/Nov 94: <mason> some code savagely redesigned to fix several bugs -
+ refreshline() & tc_rightcurs() majorly rewritten; refresh() fixed -
+ I've put my fingers into just about every routine in here -
+ any queries about updates to mason@werple.net.au */
+
+static char **nbuf = NULL, /* new video buffer line-by-line char array */
+ **obuf = NULL; /* old video buffer line-by-line char array */
+static int more_start, /* more text before start of screen? */
+ more_end, /* more stuff after end of screen? */
+ lppth, /* lines taken up by the prompt */
+ olnct, /* previous number of lines */
+ ovln, /* previous video cursor position line */
+ pptw, rpw, /* prompt widths on screen */
+ rppth, /* right prompt height */
+ vcs, vln, /* video cursor position column & line */
+ vmaxln, /* video maximum number of lines */
+ winw, winh, rwinh, /* window width & height */
+ winpos; /* singlelinezle: line's position in window */
+
+/**/
+void
+resetvideo(void)
+{
+ int ln;
+ static int lwinw = -1, lwinh = -1; /* last window width & height */
+
+ genprompts();
+ winw = columns; /* terminal width */
+ if (termflags & TERM_SHORT)
+ winh = 1;
+ else
+ winh = (lines < 2) ? 24 : lines;
+ rwinh = lines; /* keep the real number of lines */
+ winpos = vln = vmaxln = 0;
+ if (lwinw != winw || lwinh != winh) {
+ if (nbuf) {
+ for (ln = 0; ln != lwinh; ln++) {
+ zfree(nbuf[ln], lwinw + 2);
+ zfree(obuf[ln], lwinw + 2);
+ }
+ free(nbuf);
+ free(obuf);
+ }
+ nbuf = (char **)zcalloc((winh + 1) * sizeof(char *));
+ obuf = (char **)zcalloc((winh + 1) * sizeof(char *));
+ nbuf[0] = (char *)zalloc(winw + 2);
+ obuf[0] = (char *)zalloc(winw + 2);
+
+ lwinw = winw;
+ lwinh = winh;
+ }
+ for (ln = 0; ln != winh + 1; ln++) {
+ if (nbuf[ln])
+ *nbuf[ln] = '\0';
+ if (obuf[ln])
+ *obuf[ln] = '\0';
+ }
+
+ if (pptw) {
+ memset(nbuf[0], ' ', pptw);
+ memset(obuf[0], ' ', pptw);
+ nbuf[0][pptw] = obuf[0][pptw] = '\0';
+ }
+
+ vcs = pptw;
+ olnct = nlnct = 0;
+ if (showinglist > 0)
+ showinglist = -2;
+}
+
+/*
+ * Nov 96: <mason> changed to single line scroll
+ */
+
+/**/
+static void
+scrollwindow(int tline)
+{
+ int t0;
+ char *s;
+
+ s = nbuf[tline];
+ for (t0 = tline; t0 < winh - 1; t0++)
+ nbuf[t0] = nbuf[t0 + 1];
+ nbuf[winh - 1] = s;
+ if (!tline)
+ more_start = 1;
+ return;
+}
+
+/* this is the messy part. */
+/* this define belongs where it's used!!! */
+
+#define nextline \
+{ \
+ *s = '\0'; \
+ if (ln != winh - 1) \
+ ln++; \
+ else { \
+ if (!canscroll) { \
+ if (nvln != -1 && nvln != winh - 1 \
+ && (numscrolls != onumscrolls - 1 \
+ || nvln <= winh / 2)) \
+ break; \
+ numscrolls++; \
+ canscroll = winh / 2; \
+ } \
+ canscroll--; \
+ scrollwindow(0); \
+ if (nvln != -1) \
+ nvln--; \
+ } \
+ if (!nbuf[ln]) \
+ nbuf[ln] = (char *)zalloc(winw + 2); \
+ s = (unsigned char *)nbuf[ln]; \
+ sen = s + winw; \
+}
+
+#define snextline \
+{ \
+ *s = '\0'; \
+ if (ln != winh - 1) \
+ ln++; \
+ else \
+ if (tosln < 3) { \
+ more_status = 1; \
+ scrollwindow(tosln + 1); \
+ } else if (tosln - 1 <= nvln) { \
+ scrollwindow(0); \
+ if (nvln) \
+ nvln--, tosln--; \
+ } else { \
+ tosln--; \
+ scrollwindow(tosln); \
+ } \
+ if (!nbuf[ln]) \
+ nbuf[ln] = (char *)zalloc(winw + 2); \
+ s = (unsigned char *)nbuf[ln]; \
+ sen = s + winw; \
+}
+
+static int cleareol, /* clear to end-of-line (if can't cleareod) */
+ clearf, /* alwayslastprompt used immediately before */
+ put_rpmpt, /* whether we should display right-prompt */
+ oput_rpmpt, /* whether displayed right-prompt last time */
+ oxtabs, /* oxtabs - tabs expand to spaces if set */
+ numscrolls, onumscrolls;
+
+/**/
+void
+refresh(void)
+{
+ static int inlist; /* avoiding recursion */
+ int canscroll = 0, /* number of lines we are allowed to scroll */
+ ln = 0, /* current line we're working on */
+ more_status = 0, /* more stuff in status line */
+ nvcs = 0, nvln = -1, /* video cursor column and line */
+ t0 = -1, /* tmp */
+ tosln = 0; /* tmp in statusline stuff */
+ unsigned char *s, /* pointer into the video buffer */
+ *t, /* pointer into the real buffer */
+ *sen, /* pointer to end of the video buffer (eol) */
+ *scs; /* pointer to cursor position in real buffer */
+ char **qbuf; /* tmp */
+
+ /* If this is called from listmatches() (indirectly via trashzle()), and *
+ * that was called from the end of refresh(), then we don't need to do *
+ * anything. All this `inlist' code is actually unnecessary, but it *
+ * improves speed a little in a common case. */
+ if (inlist)
+ return;
+
+#ifdef HAVE_SELECT
+ cost = 0; /* reset */
+#endif
+
+/* Nov 96: <mason> I haven't checked how complete this is. sgtty stuff may
+ or may not work */
+ oxtabs = ((SGTTYFLAG & SGTABTYPE) == SGTABTYPE);
+
+ cleareol = 0; /* unset */
+ more_start = more_end = 0; /* unset */
+ if (isset(SINGLELINEZLE) || lines < 3
+ || (termflags & (TERM_NOUP | TERM_BAD | TERM_UNKNOWN)))
+ termflags |= TERM_SHORT;
+ else
+ termflags &= ~TERM_SHORT;
+ if (resetneeded) {
+ onumscrolls = 0;
+ setterm();
+#ifdef TIOCGWINSZ
+ if (winchanged) {
+ moveto(0, 0);
+ t0 = olnct; /* this is to clear extra lines even when */
+ winchanged = 0; /* the terminal cannot TCCLEAREOD */
+ }
+#endif
+ resetvideo();
+ resetneeded = 0; /* unset */
+ oput_rpmpt = 0; /* no right-prompt currently on screen */
+
+ /* we probably should only have explicitly set attributes */
+ tsetcap(TCALLATTRSOFF, 0);
+ tsetcap(TCSTANDOUTEND, 0);
+ tsetcap(TCUNDERLINEEND, 0);
+
+ if (!clearflag)
+ if (tccan(TCCLEAREOD))
+ tcout(TCCLEAREOD);
+ else
+ cleareol = 1; /* request: clear to end of line */
+ if (t0 > -1)
+ olnct = t0;
+ if (termflags & TERM_SHORT)
+ vcs = 0;
+ else if (!clearflag && lpptbuf[0])
+ zputs(lpptbuf, shout);
+ if (clearflag) {
+ zputc('\r', shout);
+ vcs = 0;
+ moveto(0, pptw);
+ }
+ fflush(shout);
+ clearf = clearflag;
+ } else if (winw != columns || rwinh != lines)
+ resetvideo();
+
+/* now winw equals columns and winh equals lines
+ width comparisons can be made with winw, height comparisons with winh */
+
+ if (termflags & TERM_SHORT) {
+ singlerefresh();
+ return;
+ }
+
+ if (cs < 0) {
+#ifdef DEBUG
+ fprintf(stderr, "BUG: negative cursor position\n");
+ fflush(stderr);
+#endif
+ cs = 0;
+ }
+ scs = line + cs;
+ numscrolls = 0;
+
+/* first, we generate the video line buffers so we know what to put on
+ the screen - also determine final cursor position (nvln, nvcs) */
+
+ /* Deemed necessary by PWS 1995/05/15 due to kill-line problems */
+ if (!*nbuf)
+ *nbuf = (char *)zalloc(winw + 2);
+
+ s = (unsigned char *)(nbuf[ln = 0] + pptw);
+ t = line;
+ sen = (unsigned char *)(*nbuf + winw);
+ for (; t < line+ll; t++) {
+ if (t == scs) /* if cursor is here, remember it */
+ nvcs = s - (unsigned char *)(nbuf[nvln = ln]);
+
+ if (*t == '\n') { /* newline */
+ nbuf[ln][winw + 1] = '\0'; /* text not wrapped */
+ nextline
+ } else if (*t == '\t') { /* tab */
+ t0 = (char *)s - nbuf[ln];
+ if ((t0 | 7) + 1 >= winw) {
+ nbuf[ln][winw + 1] = '\n'; /* text wrapped */
+ nextline
+ } else
+ do
+ *s++ = ' ';
+ while ((++t0) & 7);
+ } else if (icntrl(*t)) { /* other control character */
+ *s++ = '^';
+ if (s == sen) {
+ nbuf[ln][winw + 1] = '\n'; /* text wrapped */
+ nextline
+ }
+ *s++ = (*t == 127) ? '?' : (*t | '@');
+ } else /* normal character */
+ *s++ = *t;
+ if (s == sen) {
+ nbuf[ln][winw + 1] = '\n'; /* text wrapped */
+ nextline
+ }
+ }
+
+/* if we're really on the next line, don't fake it; do everything properly */
+ if (t == scs && (nvcs = s - (unsigned char *)(nbuf[nvln = ln])) == winw) {
+ nbuf[ln][winw + 1] = '\n'; /* text wrapped */
+ switch ('\0') { /* a sad hack to make the break */
+ case '\0': /* in nextline work */
+ nextline
+ }
+ *s = '\0';
+ nvcs = 0;
+ nvln++;
+ }
+
+ if (t != line + ll)
+ more_end = 1;
+
+ if (statusline) {
+ tosln = ln + 1;
+ if (ln == winh - 1) {
+ if (nvln > 0) {
+ scrollwindow(0);
+ nvln--;
+ }
+ tosln--;
+ }
+ nbuf[ln][winw + 1] = '\0'; /* text not wrapped */
+ snextline
+ t = (unsigned char *)statusline;
+ for (; t < (unsigned char *)statusline + statusll; t++) {
+ if (icntrl(*t)) { /* simplified processing in the status line */
+ *s++ = '^';
+ if (s == sen) {
+ nbuf[ln][winw + 1] = '\n'; /* text wrapped */
+ snextline
+ }
+ *s++ = (*t == 127) ? '?' : (*t | '@');
+ } else
+ *s++ = *t;
+ if (s == sen) {
+ nbuf[ln][winw + 1] = '\n'; /* text wrapped */
+ snextline
+ }
+ }
+ }
+
+/* insert <.... at end of last line if there is more text past end of screen */
+ if (more_end) {
+ if (!statusline)
+ tosln = winh;
+ strncpy(nbuf[tosln - 1] + winw - 7, " <.... ", 7);
+ nbuf[tosln - 1][winw] = nbuf[tosln - 1][winw + 1] = '\0';
+ }
+
+/* insert <....> at end of first status line if status is too big */
+ if (more_status) {
+ strncpy(nbuf[tosln] + winw - 8, " <....> ", 8);
+ nbuf[tosln][winw] = nbuf[tosln][winw + 1] = '\0';
+ }
+
+ *s = '\0';
+ nlnct = ln + 1;
+ for (ln = nlnct; ln < winh; ln++)
+ zfree(nbuf[ln], winw + 2), nbuf[ln] = NULL;
+
+/* determine whether the right-prompt exists and can fit on the screen */
+ if (!more_start)
+ put_rpmpt = rppth == 1 && rpptbuf[0] && !strchr(rpptbuf, '\t') &&
+ (int)strlen(nbuf[0]) + rpw < winw - 1;
+ else {
+/* insert >.... on first line if there is more text before start of screen */
+ memset(nbuf[0], ' ', pptw);
+ t0 = winw - pptw;
+ t0 = t0 > 5 ? 5 : t0;
+ strncpy(nbuf[0] + pptw, ">....", t0);
+ memset(nbuf[0] + pptw + t0, ' ', winw - t0 - pptw);
+ nbuf[0][winw] = nbuf[0][winw + 1] = '\0';
+ }
+
+ for (ln = 0; !clearf && (ln < nlnct); ln++) {
+ /* if we have more lines than last time, clear the newly-used lines */
+ if (ln >= olnct)
+ cleareol = 1;
+
+ /* if old line and new line are different,
+ see if we can insert/delete a line to speed up update */
+
+ if (ln < olnct - 1 && !(hasam && vcs == winw) &&
+ nbuf[ln] && obuf[ln] &&
+ strncmp(nbuf[ln], obuf[ln], 16)) {
+ if (tccan(TCDELLINE) && obuf[ln + 1] && obuf[ln + 1][0] &&
+ nbuf[ln] && !strncmp(nbuf[ln], obuf[ln + 1], 16)) {
+ moveto(ln, 0);
+ tcout(TCDELLINE);
+ zfree(obuf[ln], winw + 2);
+ for (t0 = ln; t0 != olnct; t0++)
+ obuf[t0] = obuf[t0 + 1];
+ obuf[--olnct] = NULL;
+ }
+ /* don't try to insert a line if olnct = vmaxln (vmaxln is the number
+ of lines that have been displayed by this routine) so that we don't
+ go off the end of the screen. */
+
+ else if (tccan(TCINSLINE) && olnct < vmaxln && nbuf[ln + 1] &&
+ obuf[ln] && !strncmp(nbuf[ln + 1], obuf[ln], 16)) {
+ moveto(ln, 0);
+ tcout(TCINSLINE);
+ for (t0 = olnct; t0 != ln; t0--)
+ obuf[t0] = obuf[t0 - 1];
+ obuf[ln] = NULL;
+ olnct++;
+ }
+ }
+
+ /* update the single line */
+ refreshline(ln);
+
+ /* output the right-prompt if appropriate */
+ if (put_rpmpt && !ln && !oput_rpmpt) {
+ moveto(0, winw - 1 - rpw);
+ zputs(rpptbuf, shout);
+ vcs = winw - 1;
+ /* reset character attributes to that set by the main prompt */
+ txtchange = pmpt_attr;
+ if (txtchangeisset(TXTNOBOLDFACE) && (rpmpt_attr & TXTBOLDFACE))
+ tsetcap(TCALLATTRSOFF, 0);
+ if (txtchangeisset(TXTNOSTANDOUT) && (rpmpt_attr & TXTSTANDOUT))
+ tsetcap(TCSTANDOUTEND, 0);
+ if (txtchangeisset(TXTNOUNDERLINE) && (rpmpt_attr & TXTUNDERLINE))
+ tsetcap(TCUNDERLINEEND, 0);
+ if (txtchangeisset(TXTBOLDFACE) && (rpmpt_attr & TXTNOBOLDFACE))
+ tsetcap(TCBOLDFACEBEG, 0);
+ if (txtchangeisset(TXTSTANDOUT) && (rpmpt_attr & TXTNOSTANDOUT))
+ tsetcap(TCSTANDOUTBEG, 0);
+ if (txtchangeisset(TXTUNDERLINE) && (rpmpt_attr & TXTNOUNDERLINE))
+ tsetcap(TCUNDERLINEBEG, 0);
+ }
+ }
+
+/* if old buffer had extra lines, set them to be cleared and refresh them
+individually */
+
+ if (olnct > nlnct) {
+ cleareol = 1;
+ for (ln = nlnct; ln < olnct; ln++)
+ refreshline(ln);
+ }
+
+/* reset character attributes */
+ if (clearf && postedit) {
+ if ((txtchange = pmpt_attr ? pmpt_attr : rpmpt_attr)) {
+ if (txtchangeisset(TXTNOBOLDFACE))
+ tsetcap(TCALLATTRSOFF, 0);
+ if (txtchangeisset(TXTNOSTANDOUT))
+ tsetcap(TCSTANDOUTEND, 0);
+ if (txtchangeisset(TXTNOUNDERLINE))
+ tsetcap(TCUNDERLINEEND, 0);
+ if (txtchangeisset(TXTBOLDFACE))
+ tsetcap(TCBOLDFACEBEG, 0);
+ if (txtchangeisset(TXTSTANDOUT))
+ tsetcap(TCSTANDOUTBEG, 0);
+ if (txtchangeisset(TXTUNDERLINE))
+ tsetcap(TCUNDERLINEBEG, 0);
+ }
+ }
+ clearf = 0;
+
+/* move to the new cursor position */
+ moveto(nvln, nvcs);
+
+/* swap old and new buffers - better than freeing/allocating every time */
+ qbuf = nbuf;
+ nbuf = obuf;
+ obuf = qbuf;
+/* store current values so we can use them next time */
+ ovln = nvln;
+ olnct = nlnct;
+ oput_rpmpt = put_rpmpt;
+ onumscrolls = numscrolls;
+ if (nlnct > vmaxln)
+ vmaxln = nlnct;
+ fflush(shout); /* make sure everything is written out */
+
+ /* if we have a new list showing, note it; if part of the list has been
+ overwritten, redisplay it. */
+ if (showinglist == -2 || (showinglist > 0 && showinglist < nlnct)) {
+ inlist = 1;
+ listmatches();
+ inlist = 0;
+ refresh();
+ }
+ if (showinglist == -1)
+ showinglist = nlnct;
+}
+
+#define tcinscost(X) (tccan(TCMULTINS) ? tclen[TCMULTINS] : (X)*tclen[TCINS])
+#define tcdelcost(X) (tccan(TCMULTDEL) ? tclen[TCMULTDEL] : (X)*tclen[TCDEL])
+#define tc_delchars(X) (void) tcmultout(TCDEL, TCMULTDEL, (X))
+#define tc_inschars(X) (void) tcmultout(TCINS, TCMULTINS, (X))
+#define tc_upcurs(X) (void) tcmultout(TCUP, TCMULTUP, (X))
+#define tc_leftcurs(X) (void) tcmultout(TCLEFT, TCMULTLEFT, (X))
+
+/* refresh one line, using whatever speed-up tricks are provided by the tty */
+
+/**/
+static void
+refreshline(int ln)
+{
+ char *nl, *ol, *p1; /* line buffer pointers */
+ int ccs = 0, /* temporary count for cursor position */
+ char_ins = 0, /* number of characters inserted/deleted */
+ col_cleareol, /* clear to end-of-line from this column */
+ i, j, /* tmp */
+ ins_last, /* insert pushed last character off line */
+ nllen, ollen, /* new and old line buffer lengths */
+ rnllen; /* real new line buffer length */
+
+/* 0: setup */
+ nl = nbuf[ln];
+ rnllen = nllen = nl ? strlen(nl) : 0;
+ ol = obuf[ln] ? obuf[ln] : "";
+ ollen = strlen(ol);
+
+/* optimisation: can easily happen for clearing old lines. If the terminal has
+ the capability, then this is the easiest way to skip unnecessary stuff */
+ if (cleareol && !nllen && !(hasam && ln < nlnct - 1)
+ && tccan(TCCLEAREOL)) {
+ moveto(ln, 0);
+ tcout(TCCLEAREOL);
+ return;
+ }
+
+/* 1: pad out the new buffer with spaces to contain _all_ of the characters
+ which need to be written. do this now to allow some pre-processing */
+
+ if (cleareol /* request to clear to end of line */
+ || !nllen /* no line buffer given */
+ || (ln == 0 && (put_rpmpt != oput_rpmpt))) { /* prompt changed */
+ p1 = halloc(winw + 2);
+ if (nllen)
+ strncpy(p1, nl, nllen);
+ memset(p1 + nllen, ' ', winw - nllen);
+ p1[winw] = '\0';
+ p1[winw + 1] = (nllen < winw) ? '\0' : nl[winw + 1];
+ if (ln && nbuf[ln])
+ memcpy(nl, p1, winw + 2); /* next time obuf will be up-to-date */
+ else
+ nl = p1; /* don't keep the padding for prompt line */
+ nllen = winw;
+ } else if (ollen > nllen) { /* make new line at least as long as old */
+ p1 = halloc(ollen + 1);
+ strncpy(p1, nl, nllen);
+ memset(p1 + nllen, ' ', ollen - nllen);
+ p1[ollen] = '\0';
+ nl = p1;
+ nllen = ollen;
+ }
+
+/* 2: see if we can clear to end-of-line, and if it's faster, work out where
+ to do it from - we can normally only do so if there's no right-prompt.
+ With automatic margins, we shouldn't do it if there is another line, in
+ case it messes up cut and paste. */
+
+ if (hasam && ln < nlnct - 1 && rnllen == winw)
+ col_cleareol = -2; /* clearing eol would be evil so don't */
+ else {
+ col_cleareol = -1;
+ if (tccan(TCCLEAREOL) && (nllen == winw || put_rpmpt != oput_rpmpt)) {
+ for (i = nllen; i && nl[i - 1] == ' '; i--);
+ for (j = ollen; j && ol[j - 1] == ' '; j--);
+ if ((j > i + tclen[TCCLEAREOL]) /* new buf has enough spaces */
+ || (nllen == winw && nl[winw - 1] == ' '))
+ col_cleareol = i;
+ }
+ }
+
+/* 2b: first a new trick for automargin niceness - good for cut and paste */
+
+ if (hasam && vcs == winw) {
+ if (nbuf[vln] && nbuf[vln][vcs + 1] == '\n') {
+ vln++, vcs = 1;
+ if (nbuf[vln] && *nbuf[vln])
+ zputc(*nbuf[vln], shout);
+ else
+ zputc(' ', shout); /* I don't think this should happen */
+ if (ln == vln) { /* better safe than sorry */
+ nl++;
+ if (*ol)
+ ol++;
+ ccs = 1;
+ } /* else hmmm... I wonder what happened */
+ } else {
+ vln++, vcs = 0;
+ zputc('\n', shout);
+ }
+ }
+ ins_last = 0;
+
+/* 2c: if we're on the first line, start checking at the end of the prompt;
+ we shouldn't be doing anything within the prompt */
+
+ if (ln == 0 && pptw) {
+ i = pptw - ccs;
+ j = strlen(ol);
+ nl += i;
+ ol += (i > j ? j : i); /* if ol is too short, point it to '\0' */
+ ccs = pptw;
+ }
+
+/* 3: main display loop - write out the buffer using whatever tricks we can */
+
+ for (;;) {
+ if (*nl && *ol && nl[1] == ol[1]) /* skip only if second chars match */
+ /* skip past all matching characters */
+ for (; *nl && (*nl == *ol); nl++, ol++, ccs++) ;
+
+ if (!*nl) {
+ if (ccs == winw && hasam && char_ins > 0 && ins_last
+ && vcs != winw) {
+ nl--; /* we can assume we can go back here */
+ moveto(ln, winw - 1);
+ zputc(*nl, shout);
+ vcs++;
+ return; /* write last character in line */
+ }
+ if ((char_ins <= 0) || (ccs >= winw)) /* written everything */
+ return;
+ if (tccan(TCCLEAREOL) && (char_ins >= tclen[TCCLEAREOL])
+ && col_cleareol != -2)
+ /* we've got junk on the right yet to clear */
+ col_cleareol = 0; /* force a clear to end of line */
+ }
+
+ moveto(ln, ccs); /* move to where we do all output from */
+
+ /* if we can finish quickly, do so */
+ if ((col_cleareol >= 0) && (ccs >= col_cleareol)) {
+ tcout(TCCLEAREOL);
+ return;
+ }
+
+ /* we've written out the new but yet to clear rubbish due to inserts */
+ if (!*nl) {
+ i = (winw - ccs < char_ins) ? (winw - ccs) : char_ins;
+ if (tccan(TCDEL) && (tcdelcost(i) <= i + 1))
+ tc_delchars(i);
+ else {
+ vcs += i;
+ while (i-- > 0)
+ zputc(' ', shout);
+ }
+ return;
+ }
+
+ /* if we've reached the end of the old buffer, then there are few tricks
+ we can do, so we just dump out what we must and clear if we can */
+ if (!*ol) {
+ i = (col_cleareol >= 0) ? col_cleareol : nllen;
+ i -= vcs;
+ zwrite(nl, i, 1, shout);
+ vcs += i;
+ if (col_cleareol >= 0)
+ tcout(TCCLEAREOL);
+ return;
+ }
+
+ /* inserting & deleting chars: we can if there's no right-prompt */
+ if ((ln || !put_rpmpt || !oput_rpmpt)
+ && (nl[1] && ol[1] && nl[1] != ol[1])) {
+
+ /* deleting characters - see if we can find a match series that
+ makes it cheaper to delete intermediate characters
+ eg. oldline: hifoobar \ hopefully cheaper here to delete two
+ newline: foobar / characters, then we have six matches */
+ if (tccan(TCDEL)) {
+ for (i = 1; *(ol + i); i++)
+ if (tcdelcost(i) < pfxlen(ol + i, nl)) {
+ tc_delchars(i);
+ ol += i;
+ char_ins -= i;
+ i = 0;
+ break;
+ }
+ if (!i)
+ continue;
+ }
+ /* inserting characters - characters pushed off the right should be
+ annihilated, but we don't do this if we're on the last line lest
+ undesired scrolling occurs due to `illegal' characters on screen */
+
+ if (tccan(TCINS) && (vln != lines - 1)) { /* not on last line */
+ for (i = 1; *(nl + i); i++)
+ if (tcinscost(i) < pfxlen(nl + i, ol)) {
+ tc_inschars(i);
+ zwrite(nl, i, 1, shout);
+ nl += i;
+ char_ins += i;
+ ccs = (vcs += i);
+ /* if we've pushed off the right, truncate oldline */
+ for (i = 0; *(ol + i) && i < winw - ccs; i++);
+ if (i == winw - ccs) {
+ *(ol + i) = '\0';
+ ins_last = 1;
+ }
+ i = 0;
+ break;
+ }
+ if (!i)
+ continue;
+ }
+ }
+ /* we can't do any fancy tricks, so just dump the single character
+ and keep on trying */
+ zputc(*nl, shout);
+ nl++, ol++;
+ ccs++, vcs++;
+ }
+}
+
+/* move the cursor to line ln (relative to the prompt line),
+ absolute column cl; update vln, vcs - video line and column */
+
+/**/
+void
+moveto(int ln, int cl)
+{
+ int c;
+
+ if (vcs == winw) {
+ vln++, vcs = 0;
+ if (!hasam) {
+ zputc('\r', shout);
+ zputc('\n', shout);
+ } else {
+ if ((vln < nlnct) && nbuf[vln] && *nbuf[vln])
+ c = *nbuf[vln];
+ else
+ c = ' ';
+ zputc(c, shout);
+ zputc('\r', shout);
+ if ((vln < olnct) && obuf[vln] && *obuf[vln])
+ *obuf[vln] = c;
+ }
+ }
+
+ if (ln == vln && cl == vcs)
+ return;
+
+/* move up */
+ if (ln < vln) {
+ tc_upcurs(vln - ln);
+ vln = ln;
+ }
+/* move down; if we might go off the end of the screen, use newlines
+ instead of TCDOWN */
+
+ while (ln > vln) {
+ if (vln < vmaxln - 1)
+ if (ln > vmaxln - 1) {
+ if (tc_downcurs(vmaxln - 1 - vln))
+ vcs = 0;
+ vln = vmaxln - 1;
+ } else {
+ if (tc_downcurs(ln - vln))
+ vcs = 0;
+ vln = ln;
+ continue;
+ }
+ zputc('\r', shout), vcs = 0; /* safety precaution */
+ while (ln > vln) {
+ zputc('\n', shout);
+ vln++;
+ }
+ }
+
+ if (cl == vcs)
+ return;
+
+/* choose cheapest movements for ttys without multiple movement capabilities -
+ do this now because it's easier (to code) */
+ if (cl <= vcs / 2) {
+ zputc('\r', shout);
+ vcs = 0;
+ }
+ if (vcs < cl)
+ tc_rightcurs(cl);
+ else if (vcs > cl)
+ tc_leftcurs(vcs - cl);
+ vcs = cl;
+}
+
+/**/
+int
+tcmultout(int cap, int multcap, int ct)
+{
+ if (tccan(multcap) && (!tccan(cap) || tclen[multcap] <= tclen[cap] * ct)) {
+ tcoutarg(multcap, ct);
+ return 1;
+ } else if (tccan(cap)) {
+ while (ct--)
+ tcout(cap);
+ return 1;
+ }
+ return 0;
+}
+
+/**/
+static void
+tc_rightcurs(int cl)
+{
+ int ct, /* number of characters to move across */
+ i = vcs, /* cursor position after initial movements */
+ j;
+ char *t;
+
+ ct = cl - vcs;
+
+/* do a multright if we can - it's the most reliable */
+ if (tccan(TCMULTRIGHT)) {
+ tcoutarg(TCMULTRIGHT, ct);
+ return;
+ }
+
+/* try tabs if tabs are non destructive and multright is not possible */
+ if (!oxtabs && tccan(TCNEXTTAB) && ((vcs | 7) < cl)) {
+ i = (vcs | 7) + 1;
+ tcout(TCNEXTTAB);
+ for ( ; i + 8 <= cl; i += 8)
+ tcout(TCNEXTTAB);
+ if ((ct = cl - i) == 0) /* number of chars still to move across */
+ return;
+ }
+
+/* otherwise _carefully_ write the contents of the video buffer.
+ if we're anywhere in the prompt, goto the left column and write the whole
+ prompt out unless ztrlen(lpptbuf) == pptw : we can cheat then */
+ if (vln == 0 && i < pptw) {
+ if (strlen(lpptbuf) == pptw)
+ fputs(lpptbuf + i, shout);
+ else if (tccan(TCRIGHT) && (tclen[TCRIGHT] * ct <= ztrlen(lpptbuf)))
+ /* it is cheaper to send TCRIGHT than reprint the whole prompt */
+ for (ct = pptw - i; ct--; )
+ tcout(TCRIGHT);
+ else {
+ if (i != 0)
+ zputc('\r', shout);
+ tc_upcurs(lppth - 1);
+ zputs(lpptbuf, shout);
+ }
+ i = pptw;
+ ct = cl - i;
+ }
+
+ if (nbuf[vln]) {
+ for (j = 0, t = nbuf[vln]; *t && (j < i); j++, t++);
+ if (j == i)
+ for ( ; *t && ct; ct--, t++)
+ zputc(*t, shout);
+ }
+ while (ct--)
+ zputc(' ', shout); /* not my fault your terminal can't go right */
+}
+
+/**/
+static int
+tc_downcurs(int ct)
+{
+ int ret = 0;
+
+ if (ct && !tcmultout(TCDOWN, TCMULTDOWN, ct)) {
+ while (ct--)
+ zputc('\n', shout);
+ zputc('\r', shout), ret = -1;
+ }
+ return ret;
+}
+
+/**/
+void
+tcout(int cap)
+{
+ tputs(tcstr[cap], 1, putshout);
+ SELECT_ADD_COST(tclen[cap]);
+}
+
+/**/
+static void
+tcoutarg(int cap, int arg)
+{
+ char *result;
+
+ result = tgoto(tcstr[cap], arg, arg);
+ tputs(result, 1, putshout);
+ SELECT_ADD_COST(strlen(result));
+}
+
+/**/
+void
+clearscreen(void)
+{
+ tcout(TCCLEARSCREEN);
+ resetneeded = 1;
+ clearflag = 0;
+}
+
+/**/
+void
+redisplay(void)
+{
+ moveto(0, 0);
+ zputc('\r', shout); /* extra care */
+ tc_upcurs(lppth - 1);
+ resetneeded = 1;
+ clearflag = 0;
+}
+
+/**/
+static void
+singlerefresh(void)
+{
+ char *vbuf, *vp, /* video buffer and pointer */
+ **qbuf, /* tmp */
+ *refreshop = *obuf; /* pointer to old video buffer */
+ int t0, /* tmp */
+ vsiz, /* size of new video buffer */
+ nvcs = 0; /* new video cursor column */
+
+ nlnct = 1;
+/* generate the new line buffer completely */
+ for (vsiz = 1 + pptw, t0 = 0; t0 != ll; t0++, vsiz++)
+ if (line[t0] == '\t')
+ vsiz = (vsiz | 7) + 1;
+ else if (icntrl(line[t0]))
+ vsiz++;
+ vbuf = (char *)zalloc(vsiz);
+
+ if (cs < 0) {
+#ifdef DEBUG
+ fprintf(stderr, "BUG: negative cursor position\n");
+ fflush(stderr);
+#endif
+ cs = 0;
+ }
+
+ memcpy(vbuf, strchr(lpptbuf, 0) - pptw, pptw); /* only use last part of prompt */
+ vbuf[pptw] = '\0';
+ vp = vbuf + pptw;
+
+ for (t0 = 0; t0 != ll; t0++) {
+ if (line[t0] == '\t')
+ for (*vp++ = ' '; (vp - vbuf) & 7; )
+ *vp++ = ' ';
+ else if (line[t0] == '\n') {
+ *vp++ = '\\';
+ *vp++ = 'n';
+ } else if (line[t0] == 0x7f) {
+ *vp++ = '^';
+ *vp++ = '?';
+ } else if (icntrl(line[t0])) {
+ *vp++ = '^';
+ *vp++ = line[t0] | '@';
+ } else
+ *vp++ = line[t0];
+ if (t0 == cs)
+ nvcs = vp - vbuf - 1;
+ }
+ if (t0 == cs)
+ nvcs = vp - vbuf;
+ *vp = '\0';
+
+/* determine which part of the new line buffer we want for the display */
+ if ((winpos && nvcs < winpos + 1) || (nvcs > winpos + winw - 2)) {
+ if ((winpos = nvcs - ((winw - hasam) / 2)) < 0)
+ winpos = 0;
+ }
+ if (winpos)
+ vbuf[winpos] = '<'; /* line continues to the left */
+ if ((int)strlen(vbuf + winpos) > (winw - hasam)) {
+ vbuf[winpos + winw - hasam - 1] = '>'; /* line continues to right */
+ vbuf[winpos + winw - hasam] = '\0';
+ }
+ strcpy(nbuf[0], vbuf + winpos);
+ zfree(vbuf, vsiz);
+ nvcs -= winpos;
+
+/* display the `visable' portion of the line buffer */
+ for (t0 = 0, vp = *nbuf;;) {
+ /* skip past all matching characters */
+ for (; *vp && *vp == *refreshop; t0++, vp++, refreshop++) ;
+
+ if (!*vp && !*refreshop)
+ break;
+
+ singmoveto(t0); /* move to where we do all output from */
+
+ if (!*refreshop) {
+ if ((t0 = strlen(vp)))
+ zwrite(vp, t0, 1, shout);
+ vcs += t0;
+ break;
+ }
+ if (!*vp) {
+ if (tccan(TCCLEAREOL))
+ tcout(TCCLEAREOL);
+ else
+ for (; *refreshop++; vcs++)
+ zputc(' ', shout);
+ break;
+ }
+ zputc(*vp, shout);
+ vcs++, t0++;
+ vp++, refreshop++;
+ }
+/* move to the new cursor position */
+ singmoveto(nvcs);
+
+ qbuf = nbuf;
+ nbuf = obuf;
+ obuf = qbuf;
+ fflush(shout); /* make sure everything is written out */
+}
+
+/**/
+static void
+singmoveto(int pos)
+{
+ if (pos == vcs)
+ return;
+ if (pos <= vcs / 2) {
+ zputc('\r', shout);
+ vcs = 0;
+ }
+ if (pos < vcs) {
+ tc_leftcurs(vcs - pos);
+ vcs = pos;
+ }
+ if (pos > vcs) {
+ if (tcmultout(TCRIGHT, TCMULTRIGHT, pos - vcs))
+ vcs = pos;
+ else
+ while (pos > vcs) {
+ zputc(nbuf[0][vcs], shout);
+ vcs++;
+ }
+ }
+}
+
+/* recheck size of prompts */
+
+/**/
+static void
+genprompts(void)
+{
+ countprompt(lpptbuf, &pptw, &lppth);
+ countprompt(rpptbuf, &rpw, &rppth);
+}
diff --git a/Src/Zle/zle_things.sed b/Src/Zle/zle_things.sed
new file mode 100644
index 000000000..781d23704
--- /dev/null
+++ b/Src/Zle/zle_things.sed
@@ -0,0 +1,9 @@
+/^ *T("/{
+ s/^[^"]*"/ z_/
+ s/".*$/,/
+ s/-//g
+ s/\./D/g
+ P
+ s/ z_\(.*\),/#define t_\1 (\&thingies[z_\1])/
+ P
+}
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
new file mode 100644
index 000000000..c4f2e25e1
--- /dev/null
+++ b/Src/Zle/zle_thingy.c
@@ -0,0 +1,491 @@
+/*
+ * zle_thingy.c - thingies
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * 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 Paul Falstad 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 Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad 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 Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "zle.mdh"
+#include "zle_thingy.pro"
+
+/*
+ * Thingies:
+ *
+ * From the user's point of view, a thingy is just a string. Internally,
+ * the thingy is a struct thingy; these structures are in a hash table
+ * indexed by the string the user sees. This hash table contains all
+ * thingies currently referenced anywhere; each has a reference count,
+ * and is deleted when it becomes unused. Being the name of a function
+ * counts as a reference.
+ *
+ * The DISABLED flag on a thingy indicates that it is not the name of a
+ * widget. This makes it easy to generate completion lists;
+ * looking only at the `enabled' nodes makes the thingy table look like
+ * a table of widgets.
+ */
+
+/* Hashtable of thingies. Enabled nodes are those that refer to widgets. */
+
+/**/
+HashTable thingytab;
+
+/**********************************/
+/* hashtable management functions */
+/**********************************/
+
+/**/
+static void
+createthingytab(void)
+{
+ thingytab = newhashtable(199, "thingytab", NULL);
+
+ thingytab->hash = hasher;
+ thingytab->emptytable = emptythingytab;
+ thingytab->filltable = NULL;
+ thingytab->addnode = addhashnode;
+ thingytab->getnode = gethashnode;
+ thingytab->getnode2 = gethashnode2;
+ thingytab->removenode = removehashnode;
+ thingytab->disablenode = NULL;
+ thingytab->enablenode = NULL;
+ thingytab->freenode = freethingynode;
+ thingytab->printnode = NULL;
+}
+
+/**/
+static void
+emptythingytab(HashTable ht)
+{
+ /* This will only be called when deleting the thingy table, which *
+ * is only done to unload the zle module. A normal emptytable() *
+ * function would free all the thingies, but we don't want to do *
+ * that because some of them are the known thingies in the fixed *
+ * `thingies' table. As the module cleanup code deletes all the *
+ * keymaps and so on before deleting the thingy table, we can *
+ * just remove the user-defined widgets and then be sure that *
+ * *all* the thingies left are the fixed ones. This has the side *
+ * effect of freeing all resources used by user-defined widgets. */
+ scanhashtable(thingytab, 0, 0, DISABLED, scanemptythingies, 0);
+}
+
+/**/
+static void
+scanemptythingies(HashNode hn, int flags)
+{
+ Thingy t = (Thingy) hn;
+
+ /* Mustn't unbind internal widgets -- we wouldn't want to free the *
+ * memory they use. */
+ if(!(t->widget->flags & WIDGET_INT))
+ unbindwidget(t, 1);
+}
+
+/**/
+static Thingy
+makethingynode(void)
+{
+ Thingy t = (Thingy) zcalloc(sizeof(*t));
+
+ t->flags = DISABLED;
+ return t;
+}
+
+/**/
+static void
+freethingynode(HashNode hn)
+{
+ Thingy th = (Thingy) hn;
+
+ zsfree(th->nam);
+ zfree(th, sizeof(*th));
+}
+
+/************************/
+/* referencing thingies */
+/************************/
+
+/* It is important to maintain the reference counts on thingies. When *
+ * copying a reference to a thingy, wrap the copy in refthingy(), to *
+ * increase its reference count. When removing a reference, *
+ * unrefthingy() it. Both of these functions handle NULL arguments *
+ * correctly. */
+
+/**/
+Thingy
+refthingy(Thingy th)
+{
+ if(th)
+ th->rc++;
+ return th;
+}
+
+/**/
+void
+unrefthingy(Thingy th)
+{
+ if(th && !--th->rc)
+ thingytab->freenode(thingytab->removenode(thingytab, th->nam));
+}
+
+/* Use rthingy() to turn a string into a thingy. It increases the reference *
+ * count, after creating the thingy structure if necessary. */
+
+/**/
+Thingy
+rthingy(char *nam)
+{
+ Thingy t = (Thingy) thingytab->getnode2(thingytab, nam);
+
+ if(!t)
+ thingytab->addnode(thingytab, ztrdup(nam), t = makethingynode());
+ return refthingy(t);
+}
+
+/***********/
+/* widgets */
+/***********/
+
+/*
+ * Each widget is attached to one or more thingies. Each thingy
+ * names either zero or one widgets. Thingies that name a widget
+ * are treated as being referenced. The widget type, flags and pointer
+ * are stored in a separate structure pointed to by the thingies. Each
+ * thingy also has a pointer to the `next' thingy (in a circular list)
+ * that references the same widget. The DISABLED flag is unset in these
+ * thingies.
+ */
+
+/* Bind a widget to a thingy. The thingy's reference count must already *
+ * have been incremented. The widget may already be bound to other *
+ * thingies; if it is not, then its `first' member must be NULL. Return *
+ * is 0 on success, or -1 if the thingy has the TH_IMMORTAL flag set. */
+
+/**/
+static int
+bindwidget(Widget w, Thingy t)
+{
+ if(t->flags & TH_IMMORTAL) {
+ unrefthingy(t);
+ return -1;
+ }
+ if(!(t->flags & DISABLED)) {
+ if(t->widget == w)
+ return 0;
+ unbindwidget(t, 1);
+ }
+ if(w->first) {
+ t->samew = w->first->samew;
+ w->first->samew = t;
+ } else {
+ w->first = t;
+ t->samew = t;
+ }
+ t->widget = w;
+ t->flags &= ~DISABLED;
+ return 0;
+}
+
+/* Unbind a widget from a thingy. This decrements the thingy's reference *
+ * count. The widget will be destroyed if this is its last name. *
+ * TH_IMMORTAL thingies won't be touched, unless override is non-zero. *
+ * Returns 0 on success, or -1 if the thingy is protected. If the thingy *
+ * doesn't actually reference a widget, this is considered successful. */
+
+/**/
+static int
+unbindwidget(Thingy t, int override)
+{
+ Widget w;
+
+ if(t->flags & DISABLED)
+ return 0;
+ if(!override && (t->flags & TH_IMMORTAL))
+ return -1;
+ w = t->widget;
+ if(t->samew == t)
+ freewidget(w);
+ else {
+ Thingy p;
+ for(p = w->first; p->samew != t; p = p->samew) ;
+ w->first = p; /* optimised for deletezlefunction() */
+ p->samew = t->samew;
+ }
+ t->flags &= ~TH_IMMORTAL;
+ t->flags |= DISABLED;
+ unrefthingy(t);
+ return 0;
+}
+
+/* Free a widget. */
+
+/**/
+static void
+freewidget(Widget w)
+{
+ if(!(w->flags & WIDGET_INT))
+ zsfree(w->u.fnnam);
+ zfree(w, sizeof(*w));
+}
+
+/* Add am internal widget provided by a module. The name given is the *
+ * canonical one, which must not begin with a dot. The widget is first *
+ * bound to the dotted canonical name; if that name is already taken by *
+ * an internal widget, failure is indicated. The same widget is then *
+ * bound to the canonical name, and a pointer to the widget structure *
+ * returned. */
+
+/**/
+Widget
+addzlefunction(char *name, ZleIntFunc ifunc, int flags)
+{
+ VARARR(char, dotn, strlen(name) + 2);
+ Widget w;
+ Thingy t;
+
+ if(name[0] == '.')
+ return NULL;
+ dotn[0] = '.';
+ strcpy(dotn + 1, name);
+ t = (Thingy) thingytab->getnode(thingytab, dotn);
+ if(t && (t->flags & TH_IMMORTAL))
+ return NULL;
+ w = zalloc(sizeof(*w));
+ w->flags = WIDGET_INT | flags;
+ w->first = NULL;
+ w->u.fn = ifunc;
+ t = rthingy(dotn);
+ bindwidget(w, t);
+ t->flags |= TH_IMMORTAL;
+ bindwidget(w, rthingy(name));
+ return w;
+}
+
+#ifdef DYNAMIC
+
+/* Delete an internal widget provided by a module. Don't try to delete *
+ * a widget from the fixed table -- it would be bad. (Thanks, Egon.) */
+
+/**/
+void
+deletezlefunction(Widget w)
+{
+ Thingy p, n;
+
+ p = w->first;
+ while(1) {
+ n = p->samew;
+ if(n == p) {
+ unbindwidget(p, 1);
+ return;
+ }
+ unbindwidget(p, 1);
+ p = n;
+ }
+}
+
+#endif /* DYNAMIC */
+
+/***************/
+/* zle builtin */
+/***************/
+
+/*
+ * The available operations are:
+ *
+ * -l list user-defined widgets (no arguments)
+ * -D delete widget names
+ * -A link the two named widgets (2 arguments)
+ * -N create new user-defined widget (1 or 2 arguments)
+ * invoke a widget (1 argument)
+ */
+
+/**/
+int
+bin_zle(char *name, char **args, char *ops, int func)
+{
+ static struct opn {
+ char o;
+ int (*func) _((char *, char **, char *, char));
+ int min, max;
+ } const opns[] = {
+ { 'l', bin_zle_list, 0, 0 },
+ { 'D', bin_zle_del, 1, -1 },
+ { 'A', bin_zle_link, 2, 2 },
+ { 'N', bin_zle_new, 1, 2 },
+ { 0, bin_zle_call, 0, -1 },
+ };
+ struct opn const *op, *opp;
+ int n;
+
+ /* select operation and ensure no clashing arguments */
+ for(op = opns; op->o && !ops[op->o]; op++) ;
+ if(op->o)
+ for(opp = op; (++opp)->o; )
+ if(ops[opp->o]) {
+ zerrnam(name, "incompatible operation selection options",
+ NULL, 0);
+ return 1;
+ }
+
+ /* check number of arguments */
+ for(n = 0; args[n]; n++) ;
+ if(!op->o && n != 1) {
+ zerrnam(name, "wrong number of arguments", NULL, 0);
+ return 1;
+ }
+ if(n < op->min) {
+ zerrnam(name, "not enough arguments for -%c", NULL, op->o);
+ return 1;
+ } else if(op->max != -1 && n > op->max) {
+ zerrnam(name, "too many arguments for -%c", NULL, op->o);
+ return 1;
+ }
+
+ /* pass on the work to the operation function */
+ return op->func(name, args, ops, op->o);
+}
+
+/**/
+static int
+bin_zle_list(char *name, char **args, char *ops, char func)
+{
+ scanhashtable(thingytab, 1, 0, DISABLED, scanlistwidgets, ops['L']);
+ return 0;
+}
+
+/**/
+static void
+scanlistwidgets(HashNode hn, int list)
+{
+ Thingy t = (Thingy) hn;
+ Widget w = t->widget;
+
+ if(w->flags & WIDGET_INT)
+ return;
+ if(list) {
+ fputs("zle -N ", stdout);
+ if(t->nam[0] == '-')
+ fputs("-- ", stdout);
+ quotedzputs(t->nam, stdout);
+ if(strcmp(t->nam, w->u.fnnam)) {
+ fputc(' ', stdout);
+ quotedzputs(w->u.fnnam, stdout);
+ }
+ } else {
+ nicezputs(t->nam, stdout);
+ if(strcmp(t->nam, w->u.fnnam)) {
+ fputs(" (", stdout);
+ nicezputs(w->u.fnnam, stdout);
+ fputc(')', stdout);
+ }
+ }
+ putchar('\n');
+}
+
+/**/
+static int
+bin_zle_del(char *name, char **args, char *ops, char func)
+{
+ int ret = 0;
+
+ do {
+ Thingy t = (Thingy) thingytab->getnode(thingytab, *args);
+ if(!t) {
+ zwarnnam(name, "no such widget `%s'", *args, 0);
+ ret = 1;
+ } else if(unbindwidget(t, 0)) {
+ zwarnnam(name, "widget name `%s' is protected", *args, 0);
+ ret = 1;
+ }
+ } while(*++args);
+ return ret;
+}
+
+/**/
+static int
+bin_zle_link(char *name, char **args, char *ops, char func)
+{
+ Thingy t = (Thingy) thingytab->getnode(thingytab, args[0]);
+
+ if(!t) {
+ zerrnam(name, "no such widget `%s'", args[0], 0);
+ return 1;
+ } else if(bindwidget(t->widget, rthingy(args[1]))) {
+ zerrnam(name, "widget name `%s' is protected", args[1], 0);
+ return 1;
+ }
+ return 0;
+
+}
+
+/**/
+static int
+bin_zle_new(char *name, char **args, char *ops, char func)
+{
+ Widget w = zalloc(sizeof(*w));
+
+ w->flags = 0;
+ w->first = NULL;
+ w->u.fnnam = ztrdup(args[1] ? args[1] : args[0]);
+ if(!bindwidget(w, rthingy(args[0])))
+ return 0;
+ freewidget(w);
+ zerrnam(name, "widget name `%s' is protected", args[0], 0);
+ return 1;
+}
+
+/**/
+static int
+bin_zle_call(char *name, char **args, char *ops, char func)
+{
+ Thingy t;
+
+ if(!zleactive || incompctlfunc) {
+ zerrnam(name, "widgets can only be called when ZLE is active",
+ NULL, 0);
+ return 1;
+ }
+ t = rthingy(args[0]);
+ PERMALLOC {
+ execzlefunc(t);
+ } LASTALLOC;
+ unrefthingy(t);
+ return 0;
+}
+
+/*******************/
+/* initialiasation */
+/*******************/
+
+/**/
+void
+init_thingies(void)
+{
+ Thingy t;
+
+ createthingytab();
+ for(t = thingies; t->nam; t++)
+ thingytab->addnode(thingytab, t->nam, t);
+}
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
new file mode 100644
index 000000000..1aa1a008c
--- /dev/null
+++ b/Src/Zle/zle_tricky.c
@@ -0,0 +1,4015 @@
+/*
+ * zle_tricky.c - expansion and completion
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * 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 Paul Falstad 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 Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad 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 Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "zle.mdh"
+#include "zle_tricky.pro"
+
+/* The main part of ZLE maintains the line being edited as binary data, *
+ * but here, where we interface with the lexer and other bits of zsh, *
+ * we need the line metafied. The technique used is quite simple: on *
+ * entry to the expansion/completion system, we metafy the line in *
+ * place, adjusting ll and cs to match. All completion and expansion *
+ * is done on the metafied line. Immediately before returning, the *
+ * line is unmetafied again, changing ll and cs back. (ll and cs might *
+ * have changed during completion, so they can't be merely saved and *
+ * restored.) The various indexes into the line that are used in this *
+ * file only are not translated: they remain indexes into the metafied *
+ * line. */
+
+#ifdef HAVE_NIS_PLUS
+# include <rpcsvc/nis.h>
+#else
+# ifdef HAVE_NIS
+# include <rpc/types.h>
+# include <rpc/rpc.h>
+# include <rpcsvc/ypclnt.h>
+# include <rpcsvc/yp_prot.h>
+
+/* This is used when getting usernames from the NIS. */
+typedef struct {
+ int len;
+ char *s;
+}
+dopestring;
+# endif
+#endif
+
+#define inststr(X) inststrlen((X),1,-1)
+
+/* wb and we hold the beginning/end position of the word we are completing. */
+
+static int wb, we;
+
+/* offs is the cursor position within the tokenized *
+ * current word after removing nulargs. */
+
+static int offs;
+
+/* These control the type of completion that will be done. They are *
+ * affected by the choice of ZLE command and by relevant shell options. */
+
+static int usemenu, useglob;
+
+/* != 0 if we are in the middle of a menu completion */
+
+static int menucmp;
+
+/* A pointer to the current position in the menu-completion array (the one *
+ * that was put in the command line last). */
+
+static char **menucur;
+
+/* menupos is the point (in the command line) where the menu-completion *
+ * strings are inserted. menulen is the length of the string that was *
+ * inserted last. menuend is the end position of this string in the *
+ * command line. menuwe is non-zero if the cursor was at the end of the *
+ * word (meaning that suffixes should go before the cursor). menuinsc is *
+ * the length of any suffix that has been temporarily added. */
+
+static int menupos, menulen, menuend, menuwe, menuinsc;
+
+/* This is used as a flag from get_comp_string() that we are doing *
+ * completion inside a brace expansion. */
+
+static int complinbrace;
+
+/* The list of matches. fmatches contains the matches we first ignore *
+ * because of fignore. */
+
+static LinkList matches, fmatches;
+
+/* The list of matches turned into an array. This is used to sort this *
+ * list and when menu-completion is used (directly or via automenu). */
+
+static char **amatches;
+
+/* The number of matches. */
+
+static int nmatches;
+
+/* A list of user-defined explanations for the completions to be shown *
+ * instead of amatches when listing completions. */
+
+static char **aylist;
+
+/* !=0 if we have a valid completion list. */
+
+static int validlist;
+
+/* This flag is non-zero if we are completing a pattern (with globcomplete) */
+
+static int ispattern;
+
+/* Two patterns used when doing glob-completion. The first one is built *
+ * from the whole word we are completing and the second one from that *
+ * part of the word that was identified as a possible filename. */
+
+static Comp patcomp, filecomp;
+
+/* We store the following prefixes/suffixes: *
+ * lpre/lsuf -- what's on the line *
+ * rpre/rsuf -- same as lpre/lsuf, but expanded *
+ * *
+ * ... and if we are completing files, too: *
+ * ppre/psuf -- the path prefix/suffix *
+ * fpre/fsuf -- prefix/suffix of the pathname component the cursor is in *
+ * prpre -- ppre in expanded form usable for opendir *
+ * *
+ * The integer variables hold the lengths of lpre, lsuf, rpre, rsuf, *
+ * fpre, and fsuf. noreal is non-zero if we have rpre/rsuf. */
+
+static char *lpre, *lsuf;
+static char *rpre, *rsuf;
+static char *ppre, *psuf, *prpre;
+static char *fpre, *fsuf;
+static int lpl, lsl, rpl, rsl, fpl, fsl;
+static int noreal;
+
+/* This is used when completing after `$' and holds the whole prefix, *
+ * used in do_single() to check whether the word expands to a directory *
+ * name (in that case and if autoparamslash is set, we add a `/'). *
+ * qparampre is the same but quoted. The length of it is in qparprelen. *
+ * parambr is != 0 if the parameter name is in braces. */
+
+static char *parampre = NULL, *qparampre = NULL;
+static int qparprelen, parambr;
+
+/* This is either zero or equal to the special character the word we are *
+ * trying to complete starts with (e.g. Tilde or Equals). */
+
+static char ic;
+
+/* These hold the minimum common prefix/suffix lengths (normal and for *
+ * fignore ignored). */
+
+static int ab, ae, fab, fae;
+
+/* This variable says what we are currently adding to the list of matches. */
+
+static int addwhat;
+
+/* firstm hold the first match we found, shortest contains the shortest *
+ * one (normal and for fignore ignored). */
+
+static char *firstm, *shortest, *ffirstm, *fshortest;
+
+/* This holds the word we are completing in quoted from. */
+
+static char *qword;
+
+/* This is the length of the shortest match we found (normal and for *
+ * fignore ignored). */
+
+static int shortl, fshortl;
+
+/* This is non-zero if we are doing a menu-completion and this is not the *
+ * first call (e.g. when automenu is set and menu-completion was entered *
+ * due to this). */
+
+static int amenu;
+
+/* Find out if we have to insert a tab (instead of trying to complete). */
+
+/**/
+static int
+usetab(void)
+{
+ unsigned char *s = line + cs - 1;
+
+ for (; s >= line && *s != '\n'; s--)
+ if (*s != '\t' && *s != ' ')
+ return 0;
+ return 1;
+}
+
+#define COMP_COMPLETE 0
+#define COMP_LIST_COMPLETE 1
+#define COMP_SPELL 2
+#define COMP_EXPAND 3
+#define COMP_EXPAND_COMPLETE 4
+#define COMP_LIST_EXPAND 5
+#define COMP_ISEXPAND(X) ((X) >= COMP_EXPAND)
+
+/**/
+void
+completeword(void)
+{
+ usemenu = isset(MENUCOMPLETE);
+ useglob = isset(GLOBCOMPLETE);
+ if (c == '\t' && usetab())
+ selfinsert();
+ else
+ docomplete(COMP_COMPLETE);
+}
+
+/**/
+void
+menucomplete(void)
+{
+ usemenu = 1;
+ useglob = isset(GLOBCOMPLETE);
+ if (c == '\t' && usetab())
+ selfinsert();
+ else
+ docomplete(COMP_COMPLETE);
+}
+
+/**/
+void
+listchoices(void)
+{
+ usemenu = isset(MENUCOMPLETE);
+ useglob = isset(GLOBCOMPLETE);
+ docomplete(COMP_LIST_COMPLETE);
+}
+
+/**/
+void
+spellword(void)
+{
+ usemenu = useglob = 0;
+ docomplete(COMP_SPELL);
+}
+
+/**/
+void
+deletecharorlist(void)
+{
+ char **mc = menucur;
+
+ usemenu = isset(MENUCOMPLETE);
+ useglob = isset(GLOBCOMPLETE);
+ if (cs != ll)
+ deletechar();
+ else
+ docomplete(COMP_LIST_COMPLETE);
+
+ menucur = mc;
+}
+
+/**/
+void
+expandword(void)
+{
+ usemenu = useglob = 0;
+ if (c == '\t' && usetab())
+ selfinsert();
+ else
+ docomplete(COMP_EXPAND);
+}
+
+/**/
+void
+expandorcomplete(void)
+{
+ usemenu = isset(MENUCOMPLETE);
+ useglob = isset(GLOBCOMPLETE);
+ if (c == '\t' && usetab())
+ selfinsert();
+ else
+ docomplete(COMP_EXPAND_COMPLETE);
+}
+
+/**/
+void
+menuexpandorcomplete(void)
+{
+ usemenu = 1;
+ useglob = isset(GLOBCOMPLETE);
+ if (c == '\t' && usetab())
+ selfinsert();
+ else
+ docomplete(COMP_EXPAND_COMPLETE);
+}
+
+/**/
+void
+listexpand(void)
+{
+ usemenu = isset(MENUCOMPLETE);
+ useglob = isset(GLOBCOMPLETE);
+ docomplete(COMP_LIST_EXPAND);
+}
+
+/**/
+void
+reversemenucomplete(void)
+{
+ if (!menucmp) {
+ menucomplete();
+ return;
+ }
+ HEAPALLOC {
+ if (menucur == amatches)
+ menucur = amatches + nmatches - 1;
+ else
+ menucur--;
+ metafy_line();
+ do_single(*menucur);
+ unmetafy_line();
+ } LASTALLOC;
+}
+
+/* Accepts the current completion and starts a new arg, *
+ * with the next completions. This gives you a way to *
+ * accept several selections from the list of matches. */
+
+/**/
+void
+acceptandmenucomplete(void)
+{
+ if (!menucmp) {
+ feep();
+ return;
+ }
+ cs = menuend + menuinsc;
+ inststrlen(" ", 1, 1);
+ if (qparampre)
+ inststrlen(qparampre, 1, qparprelen);
+ if (lpre && !ispattern)
+ inststrlen(lpre, 1, -1);
+ if (lsuf && !ispattern)
+ inststrlen(lsuf, 0, -1);
+ menupos = cs;
+ menuend = cs + (lsuf ? strlen(lsuf) : 0);
+ menulen = 0;
+ menuinsc = 0;
+ menuwe = 1;
+ menucomplete();
+}
+
+/* These are flags saying if we are completing in the command *
+ * position or in a redirection. */
+
+static int lincmd, linredir;
+
+/* Non-zero if the last completion done was ambiguous (used to find *
+ * out if AUTOMENU should start). More precisely, it's nonzero after *
+ * successfully doing any completion, unless the completion was *
+ * unambiguous and did not cause the display of a completion list. *
+ * From the other point of view, it's nonzero iff AUTOMENU (if set) *
+ * should kick in on another completion. */
+
+static int lastambig;
+
+/* This describes some important things collected during the last *
+ * completion. Its value is zero or the inclusive OR of some of *
+ * the HAS_* things below. */
+
+static int haswhat;
+
+/* We have a suffix to add (given with compctl -S). */
+
+#define HAS_SUFFIX 1
+
+/* We have filenames in the completion list. */
+
+#define HAS_FILES 2
+
+/* We have other things than files in the completion list. If this is *
+ * not set but HAS_FILES is, we probably put the file type characters *
+ * in the completion list (if listtypes is set) and we attempt to add *
+ * a slash to completed directories. */
+
+#define HAS_MISC 4
+
+/* This is set if we have filenames in the completion list that were *
+ * generated by a globcompletion pattern. */
+
+#define HAS_PATHPAT 8
+
+
+/* This holds the naem of the current command (used to find the right *
+ * compctl). */
+
+static char *cmdstr;
+
+
+/* Check if the given string is the name of a parameter and if this *
+ * parameter is one worth expanding. */
+
+/**/
+static int
+checkparams(char *p)
+{
+ int t0, n, l = strlen(p), e = 0;
+ struct hashnode *hn;
+
+ for (t0 = paramtab->hsize - 1, n = 0; n < 2 && t0 >= 0; t0--)
+ for (hn = paramtab->nodes[t0]; n < 2 && hn; hn = hn->next)
+ if (pfxlen(p, hn->nam) == l) {
+ n++;
+ if (strlen(hn->nam) == l)
+ e = 1;
+ }
+ return (n == 1) ? (getsparam(p) != NULL) :
+ (!menucmp && e && isset(RECEXACT));
+}
+
+/* Check if the given string has wildcards. The difficulty is that we *
+ * have to treat things like job specifications (%...) and parameter *
+ * expressions correctly. */
+
+/**/
+static int
+cmphaswilds(char *str)
+{
+ if ((*str == Inbrack || *str == Outbrack) && !str[1])
+ return 0;
+
+ /* If a leading % is immediately followed by ?, then don't *
+ * treat that ? as a wildcard. This is so you don't have *
+ * to escape job references such as %?foo. */
+ if (str[0] == '%' && str[1] ==Quest)
+ str += 2;
+
+ for (; *str;) {
+ if (*str == String || *str == Qstring) {
+ /* A parameter expression. */
+
+ if (*++str == Inbrace)
+ skipparens(Inbrace, Outbrace, &str);
+ else if (*str == String || *str == Qstring)
+ str++;
+ else {
+ /* Skip all the things a parameter expression might start *
+ * with (before we come to the parameter name). */
+ for (; *str; str++)
+ if (*str != '^' && *str != Hat &&
+ *str != '=' && *str != Equals &&
+ *str != '~' && *str != Tilde)
+ break;
+ if (*str == '#' || *str == Pound)
+ str++;
+ /* Star and Quest are parameter names here, not wildcards */
+ if (*str == Star || *str == Quest)
+ str++;
+ }
+ } else {
+ /* Not a parameter expression so we check for wildcards */
+ if (((*str == Pound || *str == Hat) && isset(EXTENDEDGLOB)) ||
+ *str == Star || *str == Bar || *str == Quest ||
+ !skipparens(Inbrack, Outbrack, &str) ||
+ !skipparens(Inang, Outang, &str) ||
+ (unset(IGNOREBRACES) &&
+ !skipparens(Inbrace, Outbrace, &str)) ||
+ (*str == Inpar && str[1] == ':' &&
+ !skipparens(Inpar, Outpar, &str)))
+ return 1;
+ if (*str)
+ str++;
+ }
+ }
+ return 0;
+}
+
+/* The main entry point for completion. */
+
+/**/
+static void
+docomplete(int lst)
+{
+ char *s, *ol;
+ int olst = lst, chl = 0, ne = noerrs, ocs;
+
+ /* If we are doing a menu-completion... */
+
+ if (menucmp && lst != COMP_LIST_EXPAND) {
+ do_menucmp(lst);
+ return;
+ }
+
+ /* Check if we have to start a menu-completion (via automenu). */
+
+ if ((amenu = (isset(AUTOMENU) && lastambig)))
+ usemenu = 1;
+
+ /* Expand history references before starting completion. If anything *
+ * changed, do no more. */
+
+ if (doexpandhist())
+ return;
+
+ metafy_line();
+
+ ocs = cs;
+ if (!isfirstln && chline != NULL) {
+ /* If we are completing in a multi-line buffer (which was not *
+ * taken from the history), we have to prepend the stuff saved *
+ * in chline to the contents of line. */
+
+ ol = dupstring((char *)line);
+ /* Make sure that chline is zero-terminated. */
+ *hptr = '\0';
+ cs = 0;
+ inststr(chline);
+ chl = cs;
+ cs += ocs;
+ } else
+ ol = NULL;
+ inwhat = IN_NOTHING;
+ qword = NULL;
+ /* Get the word to complete. */
+ noerrs = 1;
+ s = get_comp_string();
+ DPUTS(wb < 0 || cs < wb || cs > we,
+ "BUG: 0 <= wb <= cs <= we is not true!");
+ 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 *
+ * would never change the pointer in the middle of an insertion, but *
+ * then vi doesn't have completion. More to the point, this is only *
+ * an emulation. */
+ if (viinsbegin > ztrsub((char *) line + wb, (char *) line))
+ viinsbegin = ztrsub((char *) line + wb, (char *) line);
+ /* If we added chline to the line buffer, reset the original contents. */
+ if (ol) {
+ cs -= chl;
+ wb -= chl;
+ we -= chl;
+ if (wb < 0) {
+ strcpy((char *) line, ol);
+ ll = strlen((char *) line);
+ cs = ocs;
+ unmetafy_line();
+ feep();
+ return;
+ }
+ ocs = cs;
+ cs = 0;
+ foredel(chl);
+ cs = ocs;
+ }
+ freeheap();
+ /* Save the lexer state, in case the completion code uses the lexer *
+ * somewhere (e.g. when processing a compctl -s flag). */
+ lexsave();
+ if (inwhat == IN_ENV)
+ lincmd = 0;
+ if (s) {
+ if (lst == COMP_EXPAND_COMPLETE) {
+ /* Check if we have to do expansion or completion. */
+ char *q = s;
+
+ if (*q == Equals) {
+ /* The word starts with `=', see if we can expand it. */
+ q = s + 1;
+ if (cmdnamtab->getnode(cmdnamtab, q) || hashcmd(q, pathchecked))
+ if (isset(RECEXACT))
+ lst = COMP_EXPAND;
+ else {
+ int t0, n = 0;
+ char *fc;
+ struct hashnode *hn;
+
+ for (t0 = cmdnamtab->hsize - 1; t0 >= 0; t0--)
+ for (hn = cmdnamtab->nodes[t0]; hn;
+ hn = hn->next) {
+ if (strpfx(q, hn->nam) && (fc = findcmd(hn->nam))) {
+ zsfree(fc);
+ n++;
+ }
+ if (n == 2)
+ break;
+ }
+
+ if (n == 1)
+ lst = COMP_EXPAND;
+ }
+ }
+ if (lst == COMP_EXPAND_COMPLETE)
+ do {
+ /* check if there is a parameter expresiion. */
+ for (; *q && *q != String; q++);
+ if (*q == String && q[1] != Inpar && q[1] != Inbrack) {
+ if (*++q == Inbrace) {
+ if (! skipparens(Inbrace, Outbrace, &q) &&
+ q == s + cs - wb)
+ lst = COMP_EXPAND;
+ } else {
+ char *t, sav, sav2;
+
+ /* Skip the things parameter expressions might *
+ * start with (the things before the parameter *
+ * name). */
+ for (; *q; q++)
+ if (*q != '^' && *q != Hat &&
+ *q != '=' && *q != Equals &&
+ *q != '~' && *q != Tilde)
+ break;
+ if ((*q == '#' || *q == Pound || *q == '+') &&
+ q[1] != String)
+ q++;
+
+ sav2 = *(t = q);
+ if (*q == Quest || *q == Star || *q == String ||
+ *q == Qstring)
+ *q = ztokens[*q - Pound], ++q;
+ else if (*q == '?' || *q == '*' || *q == '$' ||
+ *q == '-' || *q == '!' || *q == '@')
+ q++;
+ else if (idigit(*q))
+ do q++; while (idigit(*q));
+ else
+ while (iident(*q))
+ q++;
+ sav = *q;
+ *q = '\0';
+ if (cs - wb == q - s &&
+ (idigit(sav2) || checkparams(t)))
+ lst = COMP_EXPAND;
+ *q = sav;
+ *t = sav2;
+ }
+ if (lst != COMP_EXPAND)
+ lst = COMP_COMPLETE;
+ } else
+ break;
+ } while (q < s + cs - wb);
+ if (lst == COMP_EXPAND_COMPLETE) {
+ /* If it is still not clear if we should use expansion or *
+ * completion and there is a `$' or a backtick in the word, *
+ * than do expansion. */
+ for (q = s; *q; q++)
+ if (*q == Tick || *q == Qtick ||
+ *q == String || *q == Qstring)
+ break;
+ lst = *q ? COMP_EXPAND : COMP_COMPLETE;
+ }
+ /* And do expansion if there are wildcards and globcomplete is *
+ * not used. */
+ if (unset(GLOBCOMPLETE) && cmphaswilds(s))
+ lst = COMP_EXPAND;
+ }
+ if (lincmd && (inwhat == IN_NOTHING))
+ inwhat = IN_CMD;
+
+ if (lst == COMP_SPELL) {
+ char *x, *q;
+
+ for (q = s; *q; q++)
+ if (INULL(*q))
+ *q = Nularg;
+ cs = wb;
+ foredel(we - wb);
+ HEAPALLOC {
+ untokenize(x = dupstring(s));
+ if (*s == Tilde || *s == Equals || *s == String)
+ *x = *s;
+ spckword(&x, 0, lincmd, 0);
+ } LASTALLOC;
+ untokenize(x);
+ inststr(x);
+ } else if (COMP_ISEXPAND(lst)) {
+ /* Do expansion. */
+ char *ol = (olst == COMP_EXPAND_COMPLETE) ?
+ dupstring((char *)line) : (char *)line;
+ int ocs = cs, ne = noerrs;
+
+ noerrs = 1;
+ doexpansion(s, lst, olst, lincmd);
+ lastambig = 0;
+ noerrs = ne;
+
+ /* If expandorcomplete was invoked and the expansion didn't *
+ * change the command line, do completion. */
+ if (olst == COMP_EXPAND_COMPLETE &&
+ !strcmp(ol, (char *)line)) {
+ char *p;
+
+ cs = ocs;
+ errflag = 0;
+
+ p = s;
+ if (*p == Tilde || *p == Equals)
+ p++;
+ for (; *p; p++)
+ if (itok(*p))
+ if (*p != String && *p != Qstring)
+ *p = ztokens[*p - Pound];
+ else if (p[1] == Inbrace)
+ p++, skipparens(Inbrace, Outbrace, &p);
+ docompletion(s, lst, lincmd, 1);
+ }
+ } else
+ /* Just do completion. */
+ docompletion(s, lst, lincmd, 0);
+ zsfree(s);
+ }
+ /* Reset the lexer state, pop the heap. */
+ lexrestore();
+ popheap();
+ zsfree(qword);
+ unmetafy_line();
+}
+
+/* Do completion, given that we are in the middle of a menu completion. We *
+ * don't need to generate a list of matches, because that's already been *
+ * done by previous commands. We will either list the completions, or *
+ * insert the next completion. */
+
+/**/
+static void
+do_menucmp(int lst)
+{
+ /* Just list the matches if the list was requested. */
+ if (lst == COMP_LIST_COMPLETE) {
+ showinglist = -2;
+ return;
+ }
+ /* Otherwise go to the next match in the array... */
+ HEAPALLOC {
+ if (!*++menucur)
+ menucur = amatches;
+ /* ... and insert it into the command line. */
+ metafy_line();
+ do_single(*menucur);
+ unmetafy_line();
+ } LASTALLOC;
+}
+
+/* 1 if we are completing in a string */
+static int instring;
+
+/* 1 if we are completing the prefix */
+static int comppref;
+
+/* This function inserts an `x' in the command line at the cursor position. *
+ * *
+ * Oh, you want to know why? Well, if completion is tried somewhere on an *
+ * empty part of the command line, the lexer code would normally not be *
+ * able to give us the `word' we want to complete, since there is no word. *
+ * But we need to call the lexer to find out where we are (and for which *
+ * command we are completing and such things). So we temporarily add a `x' *
+ * (any character without special meaning would do the job) at the cursor *
+ * position, than the lexer gives us the word `x' and its beginning and end *
+ * positions and we can remove the `x'. *
+ * *
+ * If we are just completing the prefix (comppref set), we also insert a *
+ * space after the x to end the word. We never need to remove the space: *
+ * anywhere we are able to retrieve a word for completion it will be *
+ * discarded as whitespace. It has the effect of making any suffix *
+ * referrable to as the next word on the command line when indexing *
+ * from a completion function. */
+
+/**/
+static void
+addx(char **ptmp)
+{
+ int addspace = 0;
+
+ if (!line[cs] || line[cs] == '\n' ||
+ (iblank(line[cs]) && (!cs || line[cs-1] != '\\')) ||
+ line[cs] == ')' || line[cs] == '`' ||
+ (instring && (line[cs] == '"' || line[cs] == '\'')) ||
+ (addspace = (comppref && !iblank(line[cs])))) {
+ *ptmp = (char *)line;
+ line = (unsigned char *)halloc(strlen((char *)line) + 3 + addspace);
+ memcpy(line, *ptmp, cs);
+ line[cs] = 'x';
+ if (addspace)
+ line[cs+1] = ' ';
+ strcpy((char *)line + cs + 1 + addspace, (*ptmp) + cs);
+ addedx = 1 + addspace;
+ } else {
+ addedx = 0;
+ *ptmp = NULL;
+ }
+}
+
+/* Like dupstring, but add an extra space at the end of the string. */
+
+/**/
+static char *
+dupstrspace(const char *str)
+{
+ int len = strlen((char *)str);
+ char *t = (char *)ncalloc(len + 2);
+ strcpy(t, str);
+ strcpy(t+len, " ");
+ return t;
+}
+
+/* These functions metafy and unmetafy the ZLE buffer, as described at the *
+ * top of this file. Note that ll and cs are translated. They *must* be *
+ * called in matching pairs, around all the expansion/completion code. *
+ * Currently, there are four pairs: in history expansion, in the main *
+ * completion function, and one in each of the middle-of-menu-completion *
+ * functions (there's one for each direction). */
+
+/**/
+static void
+metafy_line(void)
+{
+ int len = ll;
+ char *s;
+
+ for (s = (char *) line; s < (char *) line + ll;)
+ if (imeta(*s++))
+ len++;
+ sizeline(len);
+ (void) metafy((char *) line, ll, META_NOALLOC);
+ ll = len;
+ cs = metalen((char *) line, cs);
+}
+
+/**/
+static void
+unmetafy_line(void)
+{
+ cs = ztrsub((char *) line + cs, (char *) line);
+ (void) unmetafy((char *) line, &ll);
+}
+
+/* Lasciate ogni speranza. *
+ * This function is a nightmare. It works, but I'm sure that nobody really *
+ * understands why. The problem is: to make it cleaner we would need *
+ * changes in the lexer code (and then in the parser, and then...). */
+
+/**/
+static char *
+get_comp_string(void)
+{
+ int t0, tt0, i, j, k, cp, rd, sl, ocs;
+ char *s = NULL, *linptr, *tmp, *p, *tt = NULL;
+
+ complinbrace = 0;
+ /* This global flag is used to signal the lexer code if it should *
+ * expand aliases or not. */
+ noaliases = isset(COMPLETEALIASES);
+
+ /* Find out if we are somewhere in a `string', i.e. inside '...', *
+ * "...", `...`, or ((...)). */
+
+ for (i = j = k = 0, p = (char *)line; p < (char *)line + cs; p++)
+ if (*p == '`' && !(k & 1))
+ i++;
+ else if (*p == '\"' && !(k & 1) && !(i & 1))
+ j++;
+ else if (*p == '\'' && !(j & 1))
+ k++;
+ else if (*p == '\\' && p[1] && !(k & 1))
+ p++;
+ instring = (j & 1) ? 2 : (k & 1);
+ addx(&tmp);
+ if (instring) {
+ /* Yes, we are in a string. */
+ if (!tmp) {
+ tmp = (char *)line;
+ line = (unsigned char *) dupstring((char *) line);
+ }
+ /* Now remove the quotes. *
+ * What?? Why that?? Well, we want to be able to complete *
+ * inside strings. The lexer code gives us no help here, *
+ * so we have to cheat. We remove the quotes, the lexer *
+ * will than treat the words in the strings normally and we *
+ * can complete them. *
+ * This is completely the wrong thing to do, but it's *
+ * occasionally useful, and we can't handle quotes properly *
+ * yet anyway. */
+ for (p = (char *)line; *p; p++)
+ if (*p == '"' || *p == '\'')
+ *p = ' ';
+ }
+ linptr = (char *)line;
+ pushheap();
+ HEAPALLOC {
+ start:
+ inwhat = IN_NOTHING;
+ /* Now set up the lexer and start it. */
+ parbegin = parend = -1;
+ lincmd = incmdpos;
+ linredir = inredir;
+ zsfree(cmdstr);
+ cmdstr = NULL;
+ zleparse = 1;
+ clwpos = -1;
+ lexsave();
+ inpush(dupstrspace((char *) linptr), 0, NULL);
+ strinbeg();
+ stophist = 2;
+ i = tt0 = cp = rd = 0;
+
+ /* This loop is possibly the wrong way to do this. It goes through *
+ * the previously massaged command line using the lexer. It stores *
+ * each token in each command (commands being regarded, roughly, as *
+ * being separated by tokens | & &! |& || &&). The loop stops when *
+ * the end of the command containing the cursor is reached. It's a *
+ * simple way to do things, but suffers from an inability to *
+ * distinguish actual command arguments from, for example, *
+ * filenames in redirections. (But note that code elsewhere checks *
+ * if we are completing *in* a redirection.) The only way to fix *
+ * this would be to pass the command line through the parser too, *
+ * and get the arguments that way. Maybe in 3.1... */
+ do {
+ lincmd = incmdpos;
+ linredir = inredir;
+ /* Get the next token. */
+ ctxtlex();
+ if (tok == DINPAR)
+ tokstr = NULL;
+
+ /* We reached the end. */
+ if (tok == ENDINPUT)
+ break;
+ if (tok == BAR || tok == AMPER ||
+ tok == BARAMP || tok == AMPERBANG ||
+ ((tok == DBAR || tok == DAMPER) && !incond)) {
+ /* This is one of the things that separate commands. If we *
+ * already have the things we need (e.g. the token strings), *
+ * leave the loop. */
+ if (tt)
+ break;
+ /* Otherwise reset the variables we are collecting data in. */
+ i = tt0 = cp = rd = 0;
+ }
+ if (lincmd && tok == STRING) {
+ /* The lexer says, this token is in command position, so *
+ * store the token string (to find the right compctl). */
+ zsfree(cmdstr);
+ cmdstr = ztrdup(tokstr);
+ i = 0;
+ }
+ if (!zleparse && !tt0) {
+ /* This is done when the lexer reached the word the cursor is on. */
+ tt = tokstr ? dupstring(tokstr) : NULL;
+ /* If we added a `x', remove it. */
+ if (addedx && tt)
+ chuck(tt + cs - wb);
+ tt0 = tok;
+ /* Store the number of this word. */
+ clwpos = i;
+ cp = lincmd;
+ rd = linredir;
+ if (inwhat == IN_NOTHING && incond)
+ inwhat = IN_COND;
+ }
+ if (!tokstr)
+ continue;
+ /* We need to store the token strings of all words (for some of *
+ * the more complicated compctl -x things). They are stored in *
+ * the clwords array. Make this array big enough. */
+ if (i + 1 == clwsize) {
+ int n;
+ clwords = (char **)realloc(clwords,
+ (clwsize *= 2) * sizeof(char *));
+ for(n = clwsize; --n > i; )
+ clwords[n] = NULL;
+ }
+ zsfree(clwords[i]);
+ /* And store the current token string. */
+ clwords[i] = ztrdup(tokstr);
+ sl = strlen(tokstr);
+ /* Sometimes the lexer gives us token strings ending with *
+ * spaces we delete the spaces. */
+ while (sl && clwords[i][sl - 1] == ' ' &&
+ (sl < 2 || (clwords[i][sl - 2] != Bnull &&
+ clwords[i][sl - 2] != Meta)))
+ clwords[i][--sl] = '\0';
+ /* If this is the word the cursor is in and we added a `x', *
+ * remove it. */
+ if (clwpos == i++ && addedx)
+ chuck(&clwords[i - 1][((cs - wb) >= sl) ?
+ (sl - 1) : (cs - wb)]);
+ } while (tok != LEXERR && tok != ENDINPUT &&
+ (tok != SEPER || (zleparse && !tt0)));
+ /* Calculate the number of words stored in the clwords array. */
+ clwnum = (tt || !i) ? i : i - 1;
+ zsfree(clwords[clwnum]);
+ clwords[clwnum] = NULL;
+ t0 = tt0;
+ lincmd = cp;
+ linredir = rd;
+ strinend();
+ inpop();
+ errflag = zleparse = 0;
+ if (parbegin != -1) {
+ /* We are in command or process substitution */
+ if (parend >= 0 && !tmp)
+ line = (unsigned char *) dupstring(tmp = (char *)line);
+ linptr = (char *) line + ll + addedx - parbegin + 1;
+ if (parend >= 0) {
+ ll -= parend;
+ line[ll + addedx] = '\0';
+ }
+ lexrestore();
+ goto start;
+ }
+
+ if (inwhat == IN_MATH)
+ s = NULL;
+ else if (!t0 || t0 == ENDINPUT) {
+ /* There was no word (empty line). */
+ s = ztrdup("");
+ we = wb = cs;
+ clwpos = clwnum;
+ t0 = STRING;
+ } else if (t0 == STRING) {
+ /* We found a simple string. */
+ s = ztrdup(clwords[clwpos]);
+ } else if (t0 == ENVSTRING) {
+ /* The cursor was inside a parameter assignment. */
+ for (s = tt; iident(*s); s++);
+ if (skipparens(Inbrack, Outbrack, &s) > 0 || s > tt + cs - wb)
+ s = NULL, inwhat = IN_MATH;
+ else if (*s == '=') {
+ s++;
+ wb += s - tt;
+ t0 = STRING;
+ s = ztrdup(s);
+ inwhat = IN_ENV;
+ }
+ lincmd = 1;
+ }
+ if (we > ll)
+ we = ll;
+ tt = (char *)line;
+ if (tmp) {
+ line = (unsigned char *)tmp;
+ ll = strlen((char *)line);
+ }
+ if (t0 != STRING && inwhat != IN_MATH) {
+ if (tmp) {
+ tmp = NULL;
+ linptr = (char *)line;
+ lexrestore();
+ goto start;
+ }
+ feep();
+ noaliases = 0;
+ lexrestore();
+ LASTALLOC_RETURN NULL;
+ }
+
+ noaliases = 0;
+
+ /* Check if we are in an array subscript. We simply assume that *
+ * we are in a subscript if we are in brackets. Correct solution *
+ * is very difficult. This is quite close, but gets things like *
+ * foo[_ wrong (note no $). If we are in a subscript, treat it *
+ * as being in math. */
+ if (inwhat != IN_MATH) {
+ int i = 0;
+ for (tt = s; ++tt < s + cs - wb;)
+ if (*tt == Inbrack)
+ i++;
+ else if (i && *tt == Outbrack)
+ i--;
+ if (i)
+ inwhat = IN_MATH;
+ }
+ if (inwhat == IN_MATH) {
+ /* In mathematical expression, we complete parameter names (even *
+ * if they don't have a `$' in front of them). So we have to *
+ * find that name. */
+ for (we = cs; iident(line[we]); we++);
+ for (wb = cs; --wb >= 0 && iident(line[wb]););
+ wb++;
+ zsfree(s);
+ s = zalloc(we - wb + 1);
+ strncpy(s, (char *) line + wb, we - wb);
+ s[we - wb] = '\0';
+ }
+ /* This variable will hold the current word in quoted form. */
+ qword = ztrdup(s);
+ /* While building the quoted form, we also clean up the command line. */
+ offs = cs - wb;
+ for (p = s, tt = qword, i = wb; *p; p++, tt++, i++)
+ if (INULL(*p)) {
+ if (i < cs)
+ offs--;
+ if (p[1] || *p != Bnull) {
+ if (*p == Bnull) {
+ *tt = '\\';
+ if (cs == i + 1)
+ cs++, offs++;
+ } else {
+ ocs = cs;
+ cs = i;
+ foredel(1);
+ chuck(tt--);
+ if ((cs = ocs) > i--)
+ cs--;
+ we--;
+ }
+ } else {
+ ocs = cs;
+ *tt = '\0';
+ cs = we;
+ backdel(1);
+ if (ocs == we)
+ cs = we - 1;
+ else
+ cs = ocs;
+ we--;
+ }
+ chuck(p--);
+ }
+
+ if (!isset(IGNOREBRACES)) {
+ /* Try and deal with foo{xxx etc.; only simple cases
+ * (only one inbrace, completion after inbrace and before outbrace
+ * if present).
+ */
+ int myoffs = isset(COMPLETEINWORD) ? offs : strlen(s);
+ tt = NULL;
+ /* First check the conditions mentioned above
+ * and locate opening brace
+ */
+ for (i = 0, p = s; *p; p++, i++) {
+ /* careful, ${... is not a brace expansion...
+ * in fact, if it's got a substitution in it's too
+ * hard for us anyway. sorry.
+ */
+ if (*p == String || *p == Qstring) {
+ tt = NULL;
+ break;
+ } else if (*p == Inbrace) {
+ if (tt) {
+ /* too many inbraces */
+ tt = NULL;
+ break;
+ }
+ tt = p;
+ } else if (*p == Outbrace && i < myoffs) {
+ /* outbrace is before cursor pos, so nothing to complete */
+ tt = NULL;
+ break;
+ }
+ }
+
+ if (tt && tt < s + myoffs) {
+ /* Braces are go: delete opening brace */
+ char *com = NULL;
+ chuck(tt);
+ offs--;
+ myoffs--;
+
+ /* Look for text up to comma before cursor and delete it */
+ for (i = tt - s, p = tt; *p && i < myoffs; p++, i++)
+ if (*p == Comma)
+ com = p;
+ if (com) {
+ i = com - tt + 1;
+ while (i--)
+ chuck(tt), offs--, myoffs--;
+ }
+
+ /* Look for text between subsequent comma
+ * and closing brace or end of string and delete it
+ */
+ for (p = s + myoffs; *p && *p != Outbrace; p++)
+ if (*p == Comma) {
+ while (*p && *p != Outbrace)
+ chuck(p);
+ break;
+ }
+ if (*p == Outbrace)
+ chuck(p);
+ else {
+ /* we are still waiting for an outbrace and maybe commas */
+ complinbrace = 1;
+ }
+ }
+ }
+
+ } LASTALLOC;
+ lexrestore();
+
+ return (char *)s;
+}
+
+/* Expand the current word. */
+
+/**/
+static void
+doexpansion(char *s, int lst, int olst, int explincmd)
+{
+ LinkList vl;
+ char *ss;
+
+ DPUTS(useheap, "BUG: useheap in doexpansion()");
+ HEAPALLOC {
+ pushheap();
+ vl = newlinklist();
+ ss = dupstring(s);
+ addlinknode(vl, ss);
+ prefork(vl, 0);
+ if (errflag)
+ goto end;
+ if ((lst == COMP_LIST_EXPAND) || (lst == COMP_EXPAND)) {
+ int ng = opts[NULLGLOB];
+
+ opts[NULLGLOB] = 1;
+ globlist(vl);
+ opts[NULLGLOB] = ng;
+ }
+ if (errflag)
+ goto end;
+ if (empty(vl) || !*(char *)peekfirst(vl)) {
+ if (!noerrs)
+ feep();
+ goto end;
+ }
+ if (peekfirst(vl) == (void *) ss ||
+ (olst == COMP_EXPAND_COMPLETE &&
+ !nextnode(firstnode(vl)) && *s == Tilde &&
+ (ss = dupstring(s), filesubstr(&ss, 0)) &&
+ !strcmp(ss, (char *)peekfirst(vl)))) {
+ /* If expansion didn't change the word, try completion if *
+ * expandorcomplete was called, otherwise, just beep. */
+ if (lst == COMP_EXPAND_COMPLETE)
+ docompletion(s, COMP_COMPLETE, explincmd, 0);
+ else
+ feep();
+ goto end;
+ }
+ if (lst == COMP_LIST_EXPAND) {
+ /* Only the list of expansions was requested. */
+ listlist(vl);
+ goto end;
+ }
+ /* Remove the current word and put the expansions there. */
+ cs = wb;
+ foredel(we - wb);
+ while ((ss = (char *)ugetnode(vl))) {
+ untokenize(ss);
+ ss = quotename(ss, NULL, NULL, NULL);
+ inststr(ss);
+#if 0
+ if (nonempty(vl)) {
+ spaceinline(1);
+ line[cs++] = ' ';
+ }
+#endif
+ if (olst != COMP_EXPAND_COMPLETE || nonempty(vl) ||
+ (cs && line[cs-1] != '/')) {
+ spaceinline(1);
+ line[cs++] = ' ';
+ }
+ }
+ end:
+ popheap();
+ } LASTALLOC;
+}
+
+/* This is called from the lexer to give us word positions. */
+
+/**/
+void
+gotword(void)
+{
+ we = ll + 1 - inbufct + (addedx == 2 ? 1 : 0);
+ if (cs <= we) {
+ wb = ll - wordbeg + addedx;
+ zleparse = 0;
+ }
+}
+
+/* Insert the given string into the command line. If move is non-zero, *
+ * the cursor position is changed and len is the length of the string *
+ * to insert (if it is -1, the length is calculated here). */
+
+/**/
+static void
+inststrlen(char *str, int move, int len)
+{
+ if (!len)
+ return;
+ if (len == -1)
+ len = strlen(str);
+ spaceinline(len);
+ strncpy((char *)(line + cs), str, len);
+ if (move)
+ cs += len;
+}
+
+/* Quote the string s and return the result. If e is non-zero, it the *
+ * pointer it points to may point to aposition in s and in e the position *
+ * of the corresponding character in the quoted string is returned. Like *
+ * e, te may point to a position in the string and pl is used to return *
+ * the position of the character pointed to by te in the quoted string. *
+ * The string is metafied and may contain tokens. */
+
+/**/
+static char *
+quotename(const char *s, char **e, char *te, int *pl)
+{
+ const char *u, *tt;
+ char *v, buf[PATH_MAX * 2];
+ int sf = 0;
+
+ tt = v = buf;
+ u = s;
+ for (; *u; u++) {
+ if (e && *e == u)
+ *e = v, sf |= 1;
+ if (te == u)
+ *pl = v - tt, sf |= 2;
+ if (ispecial(*u) &&
+ (!instring || (isset(BANGHIST) &&
+ *u == (char)bangchar) ||
+ (instring == 2 &&
+ (*u == '$' || *u == '`' || *u == '\"')) ||
+ (instring == 1 && *u == '\'')))
+ if (*u == '\n' || (instring == 1 && *u == '\'')) {
+ if (unset(RCQUOTES)) {
+ *v++ = '\'';
+ if (*u == '\'')
+ *v++ = '\\';
+ *v++ = *u;
+ *v++ = '\'';
+ } else if (*u == '\n')
+ *v++ = '"', *v++ = '\n', *v++ = '"';
+ else
+ *v++ = '\'', *v++ = '\'';
+ continue;
+ } else
+ *v++ = '\\';
+ if(*u == Meta)
+ *v++ = *u++;
+ *v++ = *u;
+ }
+ *v = '\0';
+ if (strcmp(buf, s))
+ tt = dupstring(buf);
+ else
+ tt = s;
+ v += tt - buf;
+ if (e && (sf & 1))
+ *e += tt - buf;
+
+ if (e && *e == u)
+ *e = v;
+ if (te == u)
+ *pl = v - tt;
+
+ return (char *) tt;
+}
+
+/* This adds a match to the list of matches. The string to add is given *
+ * in s, the type of match is given in the global variable addwhat and *
+ * the parameter t (if not NULL) is a pointer to a hash node node which *
+ * may be used to give other information to this function. *
+ * *
+ * addwhat contains either one of the special values (negative, see below) *
+ * or the inclusive OR of some of the CC_* flags used for compctls. */
+
+/**/
+static void
+addmatch(char *s, char *t)
+{
+ int test = 0, sl = strlen(s), pl = rpl, cc = 0, *bp, *ep, *sp;
+ char *e = NULL, *tt, *te, *fc, **fm;
+ Comp cp = patcomp;
+ HashNode hn;
+ Param pm;
+ LinkList l = matches;
+
+/*
+ * addwhat: -5 is for files,
+ * -6 is for glob expansions,
+ * -8 is for executable files (e.g. command paths),
+ * -9 is for parameters
+ * -7 is for command names (from cmdnamtab)
+ * -4 is for a cdable parameter
+ * -3 is for executable command names.
+ * -2 is for anything unquoted
+ * -1 is for other file specifications
+ * (things with `~' of `=' at the beginning, ...).
+ */
+
+ /* Just to make the code cleaner */
+ hn = (HashNode) t;
+ pm = (Param) t;
+
+ if (!addwhat) {
+ test = 1;
+ } else if (addwhat == -1 || addwhat == -5 || addwhat == -6 ||
+ addwhat == CC_FILES || addwhat == -7 || addwhat == -8) {
+ if (sl < fpl + fsl)
+ return;
+
+ if ((addwhat == CC_FILES ||
+ addwhat == -5) && !*psuf && !*fsuf) {
+ /* If this is a filename, do the fignore check. */
+ char **pt = fignore;
+ int filell;
+
+ for (test = 1; test && *pt; pt++)
+ if ((filell = strlen(*pt)) < sl
+ && !strcmp(*pt, s + sl - filell))
+ test = 0;
+
+ if (!test)
+ l = fmatches;
+ }
+ pl = fpl;
+ if (addwhat == -5 || addwhat == -8) {
+ test = 1;
+ cp = filecomp;
+ cc = cp || ispattern;
+ e = s + sl - fsl;
+ } else {
+ if ((cp = filecomp)) {
+ if ((test = domatch(s, filecomp, 0)))
+ cc = 1;
+ } else {
+ e = s + sl - fsl;
+ if ((test = !strncmp(s, fpre, fpl)))
+ test = !strcmp(e, fsuf);
+ if (ispattern)
+ cc = 1;
+ }
+ }
+ if (test) {
+ fc = NULL;
+ if (addwhat == -7 && !(fc = findcmd(s)))
+ return;
+ if (fc)
+ zsfree(fc);
+ haswhat |= HAS_FILES;
+
+ if (addwhat == CC_FILES || addwhat == -6 ||
+ addwhat == -5 || addwhat == -8) {
+ te = s + pl;
+ s = quotename(s, &e, te, &pl);
+ sl = strlen(s);
+ } else if (!cc) {
+ s = dupstring(t = s);
+ e += s - t;
+ }
+ if (cc) {
+ tt = (char *)halloc(strlen(ppre) + strlen(psuf) + sl + 1);
+ strcpy(tt, ppre);
+ strcat(tt, s);
+ strcat(tt, psuf);
+ untokenize(s = tt);
+ }
+ }
+ } else if (addwhat == CC_QUOTEFLAG || addwhat == -2 ||
+ (addwhat == -3 && !(hn->flags & DISABLED)) ||
+ (addwhat == -4 && (PM_TYPE(pm->flags) == PM_SCALAR) &&
+ (tt = pm->gets.cfn(pm)) && *tt == '/') ||
+ (addwhat == -9 && !(hn->flags & PM_UNSET)) ||
+ (addwhat > 0 &&
+ ((!(hn->flags & PM_UNSET) &&
+ (((addwhat & CC_ARRAYS) && (hn->flags & PM_ARRAY)) ||
+ ((addwhat & CC_INTVARS) && (hn->flags & PM_INTEGER)) ||
+ ((addwhat & CC_ENVVARS) && (hn->flags & PM_EXPORTED)) ||
+ ((addwhat & CC_SCALARS) && (hn->flags & PM_SCALAR)) ||
+ ((addwhat & CC_READONLYS) && (hn->flags & PM_READONLY)) ||
+ ((addwhat & CC_SPECIALS) && (hn->flags & PM_SPECIAL)) ||
+ ((addwhat & CC_PARAMS) && !(hn->flags & PM_EXPORTED)))) ||
+ ((( addwhat & CC_SHFUNCS) ||
+ ( addwhat & CC_BUILTINS) ||
+ ( addwhat & CC_EXTCMDS) ||
+ ( addwhat & CC_RESWDS) ||
+ ((addwhat & CC_ALREG) && !(hn->flags & ALIAS_GLOBAL)) ||
+ ((addwhat & CC_ALGLOB) && (hn->flags & ALIAS_GLOBAL))) &&
+ (((addwhat & CC_DISCMDS) && (hn->flags & DISABLED)) ||
+ ((addwhat & CC_EXCMDS) && !(hn->flags & DISABLED)))) ||
+ ((addwhat & CC_BINDINGS) && !(hn->flags & DISABLED))))) {
+ if (sl >= rpl + rsl) {
+ if (cp)
+ test = domatch(s, patcomp, 0);
+ else {
+ e = s + sl - rsl;
+ if ((test = !strncmp(s, rpre, rpl)))
+ test = !strcmp(e, rsuf);
+ }
+ }
+ if (!test && sl < lpl + lsl)
+ return;
+ if (!test && lpre && lsuf && sl >= lpl + lsl) {
+ e = s + sl - lsl;
+ if ((test = !strncmp(s, lpre, lpl)))
+ test = !strcmp(e, lsuf);
+ pl = lpl;
+ }
+ if (addwhat == CC_QUOTEFLAG) {
+ te = s + pl;
+ s = quotename(s, &e, te, &pl);
+ sl = strlen(s);
+ }
+ if (test)
+ haswhat |= HAS_MISC;
+ }
+ if (!test)
+ return;
+
+ if (ispattern) {
+ t = s;
+ } else {
+ t = s += pl;
+ if (*e)
+ t = s = dupstrpfx(t, e - t);
+ }
+
+ if (l == fmatches) {
+ bp = &fab;
+ ep = &fae;
+ sp = &fshortl;
+ fm = &ffirstm;
+ } else {
+ bp = &ab;
+ ep = &ae;
+ sp = &shortl;
+ fm = &firstm;
+ }
+
+ if (!ispattern && *fm) {
+ if ((test = pfxlen(*fm, s)) < *bp)
+ *bp = test;
+ if ((test = sfxlen(*fm, s)) < *ep)
+ *ep = test;
+ if (*ep > *sp - *bp)
+ *ep = *sp - *bp;
+ }
+
+ /* If we are doing a glob completion we store the whole string in *
+ * the list. Otherwise only the part that fits between the prefix *
+ * and the suffix is stored. */
+ addlinknode(l, t);
+ if (!*fm) {
+ *bp = *ep = 10000;
+ *fm = t;
+ *sp = 100000;
+ }
+ if (!ispattern && (sl = strlen(t)) < *sp) {
+ *sp = sl;
+ if (l == fmatches)
+ fshortest = t;
+ else
+ shortest = t;
+ }
+}
+
+#ifdef HAVE_NIS_PLUS
+static int
+match_username(nis_name table, nis_object *object, void *userdata)
+{
+ if (errflag)
+ return 1;
+ else {
+ static char buf[40];
+ register entry_col *ec =
+ object->zo_data.objdata_u.en_data.en_cols.en_cols_val;
+ register int l = minimum(ec->ec_value.ec_value_len, 39);
+
+ memcpy(buf, ec->ec_value.ec_value_val, l);
+ buf[l] = '\0';
+
+ addmatch(dupstring(buf), NULL);
+ }
+ return 0;
+}
+#else
+# ifdef HAVE_NIS
+static int
+match_username(int status, char *key, int keylen, char *val, int vallen, dopestring *data)
+{
+ if (errflag || status != YP_TRUE)
+ return 1;
+
+ if (vallen > keylen && val[keylen] == ':') {
+ val[keylen] = '\0';
+ addmatch(dupstring(val), NULL);
+ }
+ return 0;
+}
+# endif /* HAVE_NIS */
+#endif /* HAVE_NIS_PLUS */
+
+/**/
+static void
+maketildelist(void)
+{
+#if defined(HAVE_NIS) || defined(HAVE_NIS_PLUS)
+ FILE *pwf;
+ char buf[BUFSIZ], *p;
+ int skipping;
+
+# ifndef HAVE_NIS_PLUS
+ char domain[YPMAXDOMAIN];
+ struct ypall_callback cb;
+ dopestring data;
+
+ data.s = fpre;
+ data.len = fpl;
+ /* Get potential matches from NIS and cull those without local accounts */
+ if (getdomainname(domain, YPMAXDOMAIN) == 0) {
+ cb.foreach = (int (*)()) match_username;
+ cb.data = (char *)&data;
+ yp_all(domain, PASSWD_MAP, &cb);
+ }
+# else /* HAVE_NIS_PLUS */
+ /* Maybe we should turn this string into a #define'd constant...? */
+
+ nis_list("passwd.org_dir", EXPAND_NAME|ALL_RESULTS|FOLLOW_LINKS|FOLLOW_PATH,
+ match_username, 0);
+# endif
+ /* Don't forget the non-NIS matches from the flat passwd file */
+ if ((pwf = fopen(PASSWD_FILE, "r")) != NULL) {
+ skipping = 0;
+ while (fgets(buf, BUFSIZ, pwf) != NULL) {
+ if (strchr(buf, '\n') != NULL) {
+ if (!skipping) {
+ if ((p = strchr(buf, ':')) != NULL) {
+ *p = '\0';
+ addmatch(dupstring(buf), NULL);
+ }
+ } else
+ skipping = 0;
+ } else
+ skipping = 1;
+ }
+ fclose(pwf);
+ }
+#else /* no NIS or NIS_PLUS */
+ /* add all the usernames to the named directory table */
+ nameddirtab->filltable(nameddirtab);
+#endif
+
+ scanhashtable(nameddirtab, 0, (addwhat==-1) ? 0 : ND_USERNAME, 0,
+ addhnmatch, 0);
+}
+
+/* Copy the given string and remove backslashes from the copy and return it. */
+
+/**/
+static char *
+rembslash(char *s)
+{
+ char *t = s = dupstring(s);
+
+ while (*s)
+ if (*s == '\\') {
+ chuck(s);
+ if (*s)
+ s++;
+ } else
+ s++;
+
+ return t;
+}
+
+/* This does the check for compctl -x `n' and `N' patterns. */
+
+/**/
+static int
+getcpat(char *wrd, int cpatindex, char *cpat, int class)
+{
+ char *str, *s, *t, *p;
+ int d = 0;
+
+ if (!wrd || !*wrd)
+ return -1;
+
+ cpat = rembslash(cpat);
+
+ str = ztrdup(wrd);
+ untokenize(str);
+ if (!cpatindex)
+ cpatindex++, d = 0;
+ else if ((d = (cpatindex < 0)))
+ cpatindex = -cpatindex;
+
+ for (s = d ? str + strlen(str) - 1 : str;
+ d ? (s >= str) : *s;
+ d ? s-- : s++) {
+ for (t = s, p = cpat; *t && *p; p++) {
+ if (class) {
+ if (*p == *s && !--cpatindex) {
+ zsfree(str);
+ return (int)(s - str + 1);
+ }
+ } else if (*t++ != *p)
+ break;
+ }
+ if (!class && !*p && !--cpatindex) {
+ zsfree(str);
+ t += wrd - str;
+ for (d = 0; --t >= wrd;)
+ if (! INULL(*t))
+ d++;
+ return d;
+ }
+ }
+ zsfree(str);
+ return -1;
+}
+
+/* This holds a pointer to the compctl we are using. */
+
+static Compctl ccmain;
+
+
+/* Find the compctl to use and return it. The first argument gives a *
+ * compctl to start searching with (if it is zero, the hash table is *
+ * searched). compadd is used to return a number of characters that *
+ * should be ignored at the beginning of the word and incmd is *
+ * non-zero if we are in command position. */
+
+/**/
+static Compctl
+get_ccompctl(Compctl occ, int *compadd, int incmd)
+{
+ Compctl compc, ret;
+ Compctlp ccp;
+ int t, i, a, b, tt, ra, rb, j, isf = 1;
+ Compcond or, cc;
+ char *s, *ss, *sc, *cmd = dupstring(cmdstr);
+ Comp comp;
+
+ first_rec:
+ *compadd = 0;
+ ra = 0;
+ rb = clwnum - 1;
+ sc = NULL;
+
+ if (!(ret = compc = occ)) {
+ if (isf) {
+ isf = 0;
+ ret = &cc_first;
+ }
+ else if (inwhat == IN_ENV)
+ /* Default completion for parameter values. */
+ ret = &cc_default;
+ else if (inwhat == IN_MATH) {
+ /* Parameter names inside mathematical expression. */
+ cc_dummy.mask = CC_PARAMS;
+ ret = &cc_dummy;
+ cc_dummy.refc = 10000;
+ } else if (inwhat == IN_COND) {
+ /* We try to be clever here: in conditions we complete option *
+ * names after a `-o', file names after `-nt', `-ot', and `-ef' *
+ * and file names and parameter names elsewhere. */
+ s = clwpos ? clwords[clwpos - 1] : "";
+ cc_dummy.mask = !strcmp("-o", s) ? CC_OPTIONS :
+ ((*s == '-' && s[1] && !s[2]) ||
+ !strcmp("-nt", s) ||
+ !strcmp("-ot", s) ||
+ !strcmp("-ef", s)) ? CC_FILES :
+ (CC_FILES | CC_PARAMS);
+ ret = &cc_dummy;
+ cc_dummy.refc = 10000;
+ } else if (incmd)
+ ret = &cc_compos;
+ /* And in redirections or if there is no command name (and we are *
+ * not in command position) or if no special compctl was given *
+ * for the command: use default completion. Note that we first *
+ * search the complete command name and than the trailing *
+ * pathname component. */
+ else if (linredir ||
+ !(cmd &&
+ (((ccp = (Compctlp) compctltab->getnode(compctltab, cmd)) &&
+ (compc = ret = ccp->cc)) ||
+ ((s = dupstring(cmd)) && remlpaths(&s) &&
+ (ccp = (Compctlp) compctltab->getnode(compctltab, s)) &&
+ (compc = ret = ccp->cc)))))
+ ret = &cc_default;
+
+ ccmain = compc = ret;
+ ccmain->refc++;
+ }
+ /* The compctl we found has extended completion patterns, check them. */
+ if (compc && compc->ext) {
+ compc = compc->ext;
+ /* This loops over the patterns separated by `--'. */
+ for (t = 0; compc && !t; compc = compc->next) {
+ /* This loops over OR'ed patterns. */
+ for (cc = compc->cond; cc && !t; cc = or) {
+ or = cc->or;
+ /* This loops over AND'ed patterns. */
+ for (t = 1; cc && t; cc = cc->and) {
+ /* And this loops of [...] pairs. */
+ for (t = i = 0; i < cc->n && !t; i++) {
+ s = NULL;
+ ra = 0;
+ rb = clwnum - 1;
+ switch (cc->type) {
+ case CCT_POS:
+ tt = clwpos;
+ goto cct_num;
+ case CCT_NUMWORDS:
+ tt = clwnum;
+ cct_num:
+ if ((a = cc->u.r.a[i]) < 0)
+ a += clwnum;
+ if ((b = cc->u.r.b[i]) < 0)
+ b += clwnum;
+ if (cc->type == CCT_POS)
+ ra = a, rb = b;
+ t = (tt >= a && tt <= b);
+ break;
+ case CCT_CURSUF:
+ case CCT_CURPRE:
+ s = ztrdup(clwpos < clwnum ? clwords[clwpos] : "");
+ untokenize(s);
+ sc = rembslash(cc->u.s.s[i]);
+ a = strlen(sc);
+ if (!strncmp(s, sc, a)) {
+ *compadd = (cc->type == CCT_CURSUF ? a : 0);
+ t = 1;
+ }
+ break;
+ case CCT_CURSUB:
+ case CCT_CURSUBC:
+ if (clwpos < 0 || clwpos > clwnum)
+ t = 0;
+ else {
+ a = getcpat(clwords[clwpos],
+ cc->u.s.p[i],
+ cc->u.s.s[i],
+ cc->type == CCT_CURSUBC);
+ if (a != -1)
+ *compadd = a, t = 1;
+ }
+ break;
+
+ case CCT_CURPAT:
+ case CCT_CURSTR:
+ tt = clwpos;
+ goto cct_str;
+ case CCT_WORDPAT:
+ case CCT_WORDSTR:
+ tt = 0;
+ cct_str:
+ if ((a = tt + cc->u.s.p[i]) < 0)
+ a += clwnum;
+ s = ztrdup((a < 0 || a >= clwnum) ? "" :
+ clwords[a]);
+ untokenize(s);
+
+ if (cc->type == CCT_CURPAT ||
+ cc->type == CCT_WORDPAT) {
+ tokenize(ss = dupstring(cc->u.s.s[i]));
+ t = ((comp = parsereg(ss)) &&
+ domatch(s, comp, 0));
+ } else
+ t = (!strcmp(s, rembslash(cc->u.s.s[i])));
+ break;
+ case CCT_RANGESTR:
+ case CCT_RANGEPAT:
+ if (cc->type == CCT_RANGEPAT)
+ tokenize(sc = dupstring(cc->u.l.a[i]));
+ for (j = clwpos; j; j--) {
+ untokenize(s = ztrdup(clwords[j]));
+ if (cc->type == CCT_RANGESTR)
+ sc = rembslash(cc->u.l.a[i]);
+ if (cc->type == CCT_RANGESTR ?
+ !strncmp(s, sc, strlen(sc)) :
+ ((comp = parsereg(sc)) &&
+ domatch(s, comp, 0))) {
+ zsfree(s);
+ ra = j + 1;
+ t = 1;
+ break;
+ }
+ zsfree(s);
+ }
+ if (t) {
+ if (cc->type == CCT_RANGEPAT)
+ tokenize(sc = dupstring(cc->u.l.b[i]));
+ for (j++; j < clwnum; j++) {
+ untokenize(s = ztrdup(clwords[j]));
+ if (cc->type == CCT_RANGESTR)
+ sc = rembslash(cc->u.l.b[i]);
+ if (cc->type == CCT_RANGESTR ?
+ !strncmp(s, sc, strlen(sc)) :
+ ((comp = parsereg(sc)) &&
+ domatch(s, comp, 0))) {
+ zsfree(s);
+ rb = j - 1;
+ t = clwpos <= rb;
+ break;
+ }
+ zsfree(s);
+ }
+ }
+ s = NULL;
+ }
+ zsfree(s);
+ }
+ }
+ }
+ if (t)
+ break;
+ }
+ if (compc)
+ /* We found a matching pattern, we may return it. */
+ ret = compc;
+ }
+ if (ret->subcmd) {
+ /* The thing we want to return has a subcmd flag (-l). */
+ char **ow = clwords, *os = cmdstr, *ops = NULL;
+ int oldn = clwnum, oldp = clwpos;
+
+ /* So we restrict the words-array. */
+ if (ra >= clwnum)
+ ra = clwnum - 1;
+ if (ra < 1)
+ ra = 1;
+ if (rb >= clwnum)
+ rb = clwnum - 1;
+ if (rb < 1)
+ rb = 1;
+ clwnum = rb - ra + 1;
+ clwpos = clwpos - ra;
+
+ if (ret->subcmd[0]) {
+ /* And probably put the command name given to the flag *
+ * in the array. */
+ clwpos++;
+ clwnum++;
+ incmd = 0;
+ ops = clwords[ra - 1];
+ clwords[ra - 1] = cmdstr = ret->subcmd;
+ clwords += ra - 1;
+ } else {
+ cmdstr = clwords[ra];
+ incmd = !clwpos;
+ clwords += ra;
+ }
+ *compadd = 0;
+ if (ccmain != &cc_dummy)
+ freecompctl(ccmain);
+ /* Then we call this function recursively. */
+
+ ret = get_ccompctl(NULL, compadd, incmd);
+ /* And restore the things we changed. */
+ clwords = ow;
+ cmdstr = os;
+ clwnum = oldn;
+ clwpos = oldp;
+ if (ops)
+ clwords[ra - 1] = ops;
+ }
+ if (ret == &cc_first)
+ goto first_rec;
+ return ret;
+}
+
+/* Dump a hash table (without sorting). For each element the addmatch *
+ * function is called and at the beginning the addwhat variable is set. *
+ * This could be done using scanhashtable(), but this is easy and much *
+ * more efficient. */
+
+/**/
+static void
+dumphashtable(HashTable ht, int what)
+{
+ HashNode hn;
+ int i;
+
+ addwhat = what;
+
+ for (i = 0; i < ht->hsize; i++)
+ for (hn = ht->nodes[i]; hn; hn = hn->next)
+ addmatch(hn->nam, (char *) hn);
+
+}
+
+/* ScanFunc used by maketildelist() et al. */
+
+/**/
+static void
+addhnmatch(HashNode hn, int flags)
+{
+ addmatch(hn->nam, NULL);
+}
+
+/* Perform expansion on the given string and return the result. *
+ * During this errors are not reported. */
+
+/**/
+static char *
+getreal(char *str)
+{
+ LinkList l = newlinklist();
+ int ne = noerrs;
+
+ noerrs = 1;
+ addlinknode(l, dupstring(str));
+ prefork(l, 0);
+ noerrs = ne;
+ if (!errflag && nonempty(l))
+ return ztrdup(peekfirst(l));
+ errflag = 0;
+
+ return ztrdup(str);
+}
+
+/* This reads a directory and adds the files to the list of *
+ * matches. The parameters say which files should be added. */
+
+/**/
+static void
+gen_matches_files(int dirs, int execs, int all)
+{
+ DIR *d;
+ struct stat buf;
+ char *n, p[PATH_MAX], *q = NULL, *e;
+ LinkList l = NULL;
+ int ns = 0, ng = opts[NULLGLOB], test, aw = addwhat;
+
+ addwhat = execs ? -8 : -5;
+ opts[NULLGLOB] = 1;
+
+ if (*psuf) {
+ /* If there is a path suffix, check if it doesn't have a `*' or *
+ * `)' at the end (this is used to determine if we should use *
+ * globbing). */
+ q = psuf + strlen(psuf) - 1;
+ ns = !(*q == Star || *q == Outpar);
+ l = newlinklist();
+ /* And generate only directory names. */
+ dirs = 1;
+ all = execs = 0;
+ }
+ /* Open directory. */
+ if ((d = opendir((prpre && *prpre) ? prpre : "."))) {
+ /* If we search only special files, prepare a path buffer for stat. */
+ if (!all && prpre) {
+ strcpy(p, prpre);
+ q = p + strlen(prpre);
+ }
+ /* Fine, now read the directory. */
+ while ((n = zreaddir(d, 1)) && !errflag) {
+ /* Ignore files beginning with `.' unless the thing we found on *
+ * the command line also starts with a dot or GLOBDOTS is set. */
+ if (*n != '.' || *fpre == '.' || isset(GLOBDOTS)) {
+ if (filecomp)
+ /* If we have a pattern for the filename check, use it. */
+ test = domatch(n, filecomp, 0);
+ else {
+ /* Otherwise use the prefix and suffix strings directly. */
+ e = n + strlen(n) - fsl;
+ if ((test = !strncmp(n, fpre, fpl)))
+ test = !strcmp(e, fsuf);
+ }
+ /* Filename didn't match? */
+ if (!test)
+ continue;
+ if (!all) {
+ /* We still have to check the file type, so prepare *
+ * the path buffer by appending the filename. */
+ strcpy(q, n);
+ /* And do the stat. */
+ if (stat(p, &buf) < 0)
+ continue;
+ }
+ if (all ||
+ (dirs && S_ISDIR(buf.st_mode)) ||
+ (execs && S_ISREG(buf.st_mode) && (buf.st_mode&S_IXUGO))) {
+ /* If we want all files or the file has the right type... */
+ if (*psuf) {
+ /* We have to test for a path suffix. */
+ int o = strlen(p), tt;
+
+ /* Append it to the path buffer. */
+ strcpy(p + o, psuf);
+
+ /* Do we have to use globbing? */
+ if (ispattern || (ns && isset(GLOBCOMPLETE))) {
+ /* Yes, so append a `*' if needed. */
+ if (ns) {
+ int tl = strlen(p);
+
+ p[tl] = Star;
+ p[tl + 1] = '\0';
+ }
+ /* Do the globbing... */
+ remnulargs(p);
+ addlinknode(l, p);
+ globlist(l);
+ /* And see if that produced a filename. */
+ tt = nonempty(l);
+ while (ugetnode(l));
+ } else
+ /* Otherwise just check, if we have access *
+ * to the file. */
+ tt = !access(p, F_OK);
+
+ p[o] = '\0';
+ if (tt)
+ /* Ok, we can add the filename to the *
+ * list of matches. */
+ addmatch(dupstring(n), NULL);
+ } else
+ /* We want all files, so just add the name *
+ * to the matches. */
+ addmatch(dupstring(n), NULL);
+ }
+ }
+ }
+ closedir(d);
+ }
+ opts[NULLGLOB] = ng;
+ addwhat = aw;
+}
+
+/* This holds the explanation string we have to print. */
+
+static char *expl;
+
+/* This holds the suffix to add (given with compctl -S). */
+
+static char *ccsuffix;
+
+/* This s non-zero if the compctl -q flag was given (the suffix should *
+ * be removed when a space or something like that is typed next). */
+
+static int remsuffix;
+
+/**/
+static void
+quotepresuf(char **ps)
+{
+ if (*ps) {
+ char *p = quotename(*ps, NULL, NULL, NULL);
+
+ if (p != *ps) {
+ zsfree(*ps);
+ *ps = ztrdup(p);
+ }
+ }
+}
+
+/**/
+static void
+docompletion(char *s, int lst, int incmd, int untokenized)
+{
+ static int delit, compadd;
+
+ fixsuffix();
+ HEAPALLOC {
+ pushheap();
+
+ /* Make sure we have the completion list and compctl. */
+ if(makecomplist(s, incmd, &delit, &compadd, untokenized)) {
+ /* Error condition: feeeeeeeeeeeeep(). */
+ feep();
+ goto compend;
+ }
+
+ if (lst == COMP_LIST_COMPLETE)
+ /* All this and the guy only wants to see the list, sigh. */
+ showinglist = -2;
+ else {
+ /* We have matches. */
+ if (delit) {
+ /* If we have to delete the word from the command line, *
+ * do it now. */
+ wb -= compadd;
+ strcpy((char *)line + wb, (char *)line + we);
+ we = cs = wb;
+ }
+ if (nmatches > 1)
+ /* There are more than one match. */
+ do_ambiguous();
+ else if (nmatches == 1) {
+ /* Only one match. */
+ do_single(amatches[0]);
+ invalidatelist();
+ }
+ }
+
+ /* Print the explanation string if needed. */
+ if (!showinglist && expl && nmatches != 1) {
+ int up;
+
+ if (!nmatches)
+ feep();
+ trashzle();
+
+ clearflag = (isset(USEZLE) && !termflags &&
+ (isset(ALWAYSLASTPROMPT) && zmult == 1)) ||
+ (unset(ALWAYSLASTPROMPT) && zmult != 1);
+
+ up = printfmt(expl, nmatches, 1);
+
+ if (clearflag)
+ tcmultout(TCUP, TCMULTUP, up + nlnct);
+ else
+ putc('\n', shout);
+ fflush(shout);
+ }
+ compend:
+ ll = strlen((char *)line);
+ if (cs > ll)
+ cs = ll;
+ popheap();
+ } LASTALLOC;
+}
+
+/* Create the completion list. This is called whenever some bit of *
+ * completion code needs the list. If the list is already available *
+ * (validlist!=0), this function doesn't do anything. Along with *
+ * the list is maintained the prefixes/suffixes etc. When any of *
+ * this becomes invalid -- e.g. if some text is changed on the *
+ * command line -- invalidatelist() should be called, to set *
+ * validlist to zero and free up the memory used. This function *
+ * returns non-zero on error. delit and compadd return information *
+ * about bits of the command line that need to be deleted. */
+
+/**/
+static int
+makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized)
+{
+ Compctl cc = NULL;
+ int oloffs = offs, owe = we, owb = wb, ocs = cs, oll = ll, isf = 1;
+ int t, sf1, sf2, ooffs;
+ char *p, *sd = NULL, *tt, *s1, *s2, *os = NULL;
+ unsigned char *ol = NULL;
+
+ /* If we already have a list from a previous execution of this *
+ * function, skip the list building code. */
+ if (validlist)
+ return !nmatches;
+
+ os = dupstring(s);
+ ol = (unsigned char *)dupstring((char *)line);
+
+ xorrec:
+
+ DPUTS(ll != strlen((char *) line), "BUG: xorrec: ll != strlen(line)");
+
+ /* Go to the end of the word if complete_in_word is not set. */
+ if (unset(COMPLETEINWORD) && cs != we)
+ cs = we, offs = strlen(s);
+
+ ispattern = haswhat = lastambig = 0;
+ patcomp = filecomp = NULL;
+ menucur = NULL;
+ shortest = NULL;
+ fshortest = NULL;
+ rpre = rsuf = lpre = lsuf = ppre = psuf = prpre =
+ fpre = fsuf = firstm = ffirstm = parampre = qparampre = NULL;
+
+ /* Blank out the lists. */
+ matches = newlinklist();
+ fmatches = newlinklist();
+
+ /* If we don't have a compctl definition yet or we have a compctl *
+ * with extended completion, get it (or the next one, resp.). */
+ if (!cc || cc->ext)
+ cc = get_ccompctl(cc, compadd, incmd);
+
+ /* *compadd is the number of characters we have to ignore at the *
+ * beginning of the word. */
+ wb += *compadd;
+ s += *compadd;
+ if ((offs -= *compadd) < 0)
+ /* It's bigger than our word prefix, so we can't help here... */
+ return 1;
+
+ /* Insert the prefix (compctl -P), if any. */
+ if (cc->prefix) {
+ int pl = 0, sl = strlen(cc->prefix);
+
+ if (*s) {
+ /* First find out how much of the prefix is already on the line. */
+ sd = dupstring(s);
+ untokenize(sd);
+ pl = pfxlen(cc->prefix, sd);
+ s += pl;
+ }
+ if (pl < sl) {
+ int savecs = cs;
+
+ /* Then insert the prefix. */
+ cs = wb + pl;
+ inststrlen(cc->prefix + pl, 0, sl - pl);
+ cs = savecs + sl - pl;
+ }
+ /* And adjust the word beginning/end variables. */
+ wb += sl;
+ we += sl - pl;
+ offs -= pl;
+ }
+ /* Does this compctl have a suffix (compctl -S)? */
+ if ((ccsuffix = cc->suffix) && *ccsuffix) {
+ char *sdup = dupstring(ccsuffix);
+ int sl = strlen(sdup), suffixll;
+
+ /* Ignore trailing spaces. */
+ for (p = sdup + sl - 1; p >= sdup && *p == ' '; p--, sl--);
+ p[1] = '\0';
+
+ if (!sd) {
+ sd = dupstring(s);
+ untokenize(sd);
+ }
+ /* If the suffix is already there, ignore it (and don't add *
+ * it again). */
+ if (*sd && (suffixll = strlen(sd)) >= sl &&
+ offs <= suffixll - sl && !strcmp(sdup, sd + suffixll - sl)) {
+ ccsuffix = NULL;
+ haswhat |= HAS_SUFFIX;
+ s[suffixll - sl] = '\0';
+ }
+ }
+ /* Do we have one of the special characters `~' and `=' at the beginning? */
+ if ((ic = *s) != Tilde && ic != Equals)
+ ic = 0;
+
+ /* Check if we have to complete a parameter name... */
+
+ /* Try to find a `$'. */
+ for (p = s + offs; p > s && *p != String; p--);
+ if (*p == String) {
+ /* Handle $$'s */
+ while (p > s && p[-1] == String)
+ p--;
+ while (p[1] == String && p[2] == String)
+ p += 2;
+ }
+ if (*p == String && p[1] != Inpar && p[1] != Inbrack) {
+ /* This is really a parameter expression (not $(...) or $[...]). */
+ char *b = p + 1, *e = b;
+ int n = 0, br = 1;
+
+ if (*b == Inbrace) {
+ /* If this is a ${...}, ignore the possible (...) flags. */
+ b++, br++;
+ n = skipparens(Inpar, Outpar, &b);
+ }
+
+ /* Ignore the stuff before the parameter name. */
+ for (; *b; b++)
+ if (*b != '^' && *b != Hat &&
+ *b != '=' && *b != Equals &&
+ *b != '~' && *b != Tilde)
+ break;
+ if (*b == '#' || *b == Pound || *b == '+')
+ b++;
+
+ e = b;
+ /* Find the end of the name. */
+ if (*e == Quest || *e == Star || *e == String || *e == Qstring ||
+ *e == '?' || *e == '*' || *e == '$' ||
+ *e == '-' || *e == '!' || *e == '@')
+ e++;
+ else if (idigit(*e))
+ while (idigit(*e))
+ e++;
+ else if (iident(*e))
+ while (iident(*e) ||
+ (useglob && (*e == Star || *e == Quest)))
+ e++;
+
+ /* Now make sure that the cursor is inside the name. */
+ if (offs <= e - s && offs >= b - s && n <= 0) {
+ /* It is. */
+ parambr = br - 1;
+ /* Get the prefix (anything up to the character before the name). */
+ *e = '\0';
+ parampre = ztrduppfx(s, b - s);
+ qparampre = ztrdup(quotename(parampre, NULL, NULL, NULL));
+ untokenize(qparampre);
+ qparprelen = strlen(qparampre);
+ /* And adjust wb, we, and offs again. */
+ offs -= b - s;
+ wb = cs - offs;
+ we = wb + e - b;
+ s = b;
+ /* And now make sure that we complete parameter names. */
+ cc = ccmain = &cc_dummy;
+ cc_dummy.refc = 10000;
+ cc_dummy.mask = CC_PARAMS | CC_ENVVARS;
+ }
+ }
+ ooffs = offs;
+ /* If we have to ignore the word, do that. */
+ if (cc->mask & CC_DELETE) {
+ *delit = 1;
+ *s = '\0';
+ offs = 0;
+ } else
+ *delit = 0;
+
+ /* Compute line prefix/suffix. */
+
+ lpl = offs;
+ lpre = zalloc(lpl + 1);
+ memcpy(lpre, s, lpl);
+ lpre[lpl] = '\0';
+ p = quotename(lpre, NULL, NULL, NULL);
+ if (strcmp(p, lpre) && !strpfx(p, qword)) {
+ int l1, l2;
+
+ backdel(l1 = cs - wb);
+ untokenize(p);
+ inststrlen(p, 1, l2 = strlen(p));
+ we += l2 - l1;
+ }
+ lsuf = ztrdup(s + offs);
+ lsl = strlen(lsuf);
+ if (lsl && (p = quotename(lsuf, NULL, NULL, NULL)) &&
+ (strcmp(p, lsuf) && !strsfx(p, qword))) {
+ int l1, l2;
+
+ foredel(l1 = strlen(s + offs));
+ untokenize(p);
+ inststrlen(p, 0, l2 = strlen(p));
+ we += l2 - l1;
+ }
+
+ /* First check for ~.../... */
+ if (ic == Tilde) {
+ for (p = lpre + lpl; p > lpre; p--)
+ if (*p == '/')
+ break;
+
+ if (*p == '/')
+ ic = 0;
+ }
+ /* Compute real prefix/suffix. */
+
+ noreal = !*delit;
+ for (p = lpre; *p && *p != String && *p != Tick; p++);
+ tt = ic && !parampre ? lpre + 1 : lpre;
+ rpre = (*p || *lpre == Tilde || *lpre == Equals) ?
+ (noreal = 0, getreal(tt)) :
+ ztrdup(tt);
+
+ for (p = lsuf; *p && *p != String && *p != Tick; p++);
+ rsuf = *p ? (noreal = 0, getreal(lsuf)) : ztrdup(lsuf);
+
+ /* Check if word is a pattern. */
+
+ for (s1 = NULL, sf1 = 0, p = rpre + (rpl = strlen(rpre)) - 1;
+ p >= rpre && (ispattern != 3 || !sf1);
+ p--)
+ if (itok(*p) && (p > rpre || (*p != Equals && *p != Tilde)))
+ ispattern |= sf1 ? 1 : 2;
+ else if (*p == '/') {
+ sf1++;
+ if (!s1)
+ s1 = p;
+ }
+ for (s2 = NULL, sf2 = t = 0, p = rsuf; *p && (!t || !sf2); p++)
+ if (itok(*p))
+ t |= sf2 ? 4 : 2;
+ else if (*p == '/') {
+ sf2++;
+ if (!s2)
+ s2 = p;
+ }
+ ispattern = ispattern | t;
+
+ /* But if we were asked not to do glob completion, we never treat the *
+ * thing as a pattern. */
+ if (!useglob)
+ ispattern = 0;
+
+ if (ispattern) {
+ /* The word should be treated as a pattern, so compute the matcher. */
+ p = (char *)ncalloc(rpl + rsl + 2);
+ strcpy(p, rpre);
+ if (rpl && p[rpl - 1] != Star) {
+ p[rpl] = Star;
+ strcpy(p + rpl + 1, rsuf);
+ } else
+ strcpy(p + rpl, rsuf);
+ patcomp = parsereg(p);
+ }
+ if (!patcomp) {
+ untokenize(rpre);
+ untokenize(rsuf);
+
+ rpl = strlen(rpre);
+ rsl = strlen(rsuf);
+ }
+ untokenize(lpre);
+ untokenize(lsuf);
+
+ /* Handle completion of files specially (of course). */
+
+ if ((cc->mask & (CC_FILES | CC_DIRS | CC_COMMPATH)) || cc->glob) {
+ /* s1 and s2 point to the last/first slash in the prefix/suffix. */
+ if (!s1)
+ s1 = rpre;
+ if (!s2)
+ s2 = rsuf + rsl;
+
+ /* Compute the path prefix/suffix. */
+ if (*s1 != '/')
+ ppre = ztrdup("");
+ else
+ ppre = ztrduppfx(rpre, s1 - rpre + 1);
+ psuf = ztrdup(s2);
+
+ /* And get the file prefix. */
+ fpre = ztrdup(((s1 == s || s1 == rpre || ic) &&
+ (*s != '/' || cs == wb)) ? s1 : s1 + 1);
+ /* And the suffix. */
+ fsuf = ztrduppfx(rsuf, s2 - rsuf);
+
+ if (useglob && (ispattern & 2)) {
+ int t2;
+
+ /* We have to use globbing, so compute the pattern from *
+ * the file prefix and suffix with a `*' between them. */
+ p = (char *)ncalloc((t2 = strlen(fpre)) + strlen(fsuf) + 2);
+ strcpy(p, fpre);
+ if ((!t2 || p[t2 - 1] != Star) && *fsuf != Star)
+ p[t2++] = Star;
+ strcpy(p + t2, fsuf);
+ filecomp = parsereg(p);
+ }
+ if (!filecomp) {
+ untokenize(fpre);
+ untokenize(fsuf);
+
+ fpl = strlen(fpre);
+ fsl = strlen(fsuf);
+ }
+ addwhat = -1;
+
+ /* Completion after `~', maketildelist adds the usernames *
+ * and named directories. */
+ if (ic == Tilde)
+ maketildelist();
+ else if (ic == Equals) {
+ /* Completion after `=', get the command names from *
+ * the cmdnamtab and aliases from aliastab. */
+ if (isset(HASHLISTALL))
+ cmdnamtab->filltable(cmdnamtab);
+ dumphashtable(cmdnamtab, -7);
+ dumphashtable(aliastab, -2);
+ } else {
+ /* Normal file completion... */
+ if (ispattern & 1) {
+ /* But with pattern matching. */
+ LinkList l = newlinklist();
+ LinkNode n;
+ int ng = opts[NULLGLOB];
+
+ opts[NULLGLOB] = 1;
+
+ addwhat = 0;
+ p = (char *)ncalloc(lpl + lsl + 3);
+ strcpy(p, lpre);
+ if (*lsuf != '*' && *lpre && lpre[lpl - 1] != '*')
+ strcat(p, "*");
+ strcat(p, lsuf);
+ if (*lsuf && lsuf[lsl - 1] != '*' && lsuf[lsl - 1] != ')')
+ strcat(p, "*");
+
+ /* Do the globbing. */
+ tokenize(p);
+ remnulargs(p);
+ addlinknode(l, p);
+ globlist(l);
+
+ if (nonempty(l)) {
+ /* And add the resulting words. */
+ haswhat |= HAS_PATHPAT;
+ for (n = firstnode(l); n; incnode(n))
+ addmatch(getdata(n), NULL);
+ }
+ opts[NULLGLOB] = ng;
+ } else {
+ /* No pattern matching. */
+ addwhat = CC_FILES;
+ if (cc->withd) {
+ prpre = tricat(cc->withd, "/", ppre);
+ } else
+ prpre = ztrdup(ppre);
+
+ if (sf2)
+ /* We are in the path, so add only directories. */
+ gen_matches_files(1, 0, 0);
+ else {
+ if (cc->mask & CC_FILES)
+ /* Add all files. */
+ gen_matches_files(0, 0, 1);
+ else if (cc->mask & CC_COMMPATH) {
+ /* Completion of command paths. */
+ if (sf1 || cc->withd)
+ /* There is a path prefix, so add *
+ * directories and executables. */
+ gen_matches_files(1, 1, 0);
+ else {
+ /* No path prefix, so add the things *
+ * reachable via the PATH variable. */
+ char **pc = path, *pp = prpre;
+
+ for (; *pc; pc++)
+ if (!**pc || (pc[0][0] == '.' && !pc[0][1]))
+ break;
+ if (*pc) {
+ prpre = "./";
+ gen_matches_files(1, 1, 0);
+ prpre = pp;
+ }
+ }
+ } else if (cc->mask & CC_DIRS)
+ gen_matches_files(1, 0, 0);
+ /* The compctl has a glob pattern (compctl -g). */
+ if (cc->glob) {
+ int ns, pl = strlen(prpre), o;
+ char *g = dupstring(cc->glob), pa[PATH_MAX];
+ char *p2, *p3;
+ int ne = noerrs, md = opts[MARKDIRS];
+
+ /* These are used in the globbing code to make *
+ * things a bit faster. */
+ glob_pre = fpre;
+ glob_suf = fsuf;
+
+ noerrs = 1;
+ addwhat = -6;
+ strcpy(pa, prpre);
+ o = strlen(pa);
+ opts[MARKDIRS] = 0;
+
+ /* The compctl -g string may contain more than *
+ * one pattern, so we need a loop. */
+ while (*g) {
+ LinkList l = newlinklist();
+ int ng;
+
+ /* Find the blank terminating the pattern. */
+ while (*g && inblank(*g))
+ g++;
+ /* Oops, we already reached the end of the
+ string. */
+ if (!*g)
+ break;
+ for (p = g + 1; *p && !inblank(*p); p++)
+ if (*p == '\\' && p[1])
+ p++;
+ /* Get the pattern string. */
+ tokenize(g = dupstrpfx(g, p - g));
+ if (*g == '=')
+ *g = Equals;
+ if (*g == '~')
+ *g = Tilde;
+ remnulargs(g);
+ if ((*g == Equals || *g == Tilde) && !cc->withd) {
+ /* The pattern has a `~' or `=' at the *
+ * beginning, so we expand this and use *
+ * the result. */
+ filesub(&g, 0);
+ addlinknode(l, dupstring(g));
+ } else if (*g == '/' && !cc->withd)
+ /* The pattern is a full path (starting *
+ * with '/'), so add it unchanged. */
+ addlinknode(l, dupstring(g));
+ else {
+ /* It's a simple pattern, so append it to *
+ * the path we have on the command line. */
+ strcpy(pa + o, g);
+ addlinknode(l, dupstring(pa));
+ }
+ /* Do the globbing. */
+ ng = opts[NULLGLOB];
+ opts[NULLGLOB] = 1;
+ globlist(l);
+ opts[NULLGLOB] = ng;
+ /* Get the results. */
+ if (nonempty(l) && peekfirst(l)) {
+ for (p2 = (char *)peekfirst(l); *p2; p2++)
+ if (itok(*p2))
+ break;
+ if (!*p2) {
+ if ((*g == Equals || *g == Tilde ||
+ *g == '/') || cc->withd) {
+ /* IF the pattern started with `~', *
+ * `=', or `/', add the result only, *
+ * if it really matches what we have *
+ * on the line. *
+ * Do this if an initial directory *
+ * was specified, too. */
+ while ((p2 = (char *)ugetnode(l)))
+ if (strpfx(prpre, p2))
+ addmatch(p2 + pl, NULL);
+ } else {
+ /* Otherwise ignore the path we *
+ * prepended to the pattern. */
+ while ((p2 = p3 =
+ (char *)ugetnode(l))) {
+ for (ns = sf1; *p3 && ns; p3++)
+ if (*p3 == '/')
+ ns--;
+
+ addmatch(p3, NULL);
+ }
+ }
+ }
+ }
+ pa[o] = '\0';
+ g = p;
+ }
+ glob_pre = glob_suf = NULL;
+ noerrs = ne;
+ opts[MARKDIRS] = md;
+ }
+ }
+ }
+ }
+ }
+ /* Use tricat() instead of dyncat() to get zalloc()'d memory. */
+ if (ic) {
+ /* Now change the `~' and `=' tokens to the real characters so *
+ * that things starting with these characters will be added. */
+ char *orpre = rpre;
+
+ rpre = tricat("", (ic == Tilde) ? "~" : "=", rpre);
+ rpl++;
+ zsfree(orpre);
+ }
+ if (!ic && (cc->mask & CC_COMMPATH) && !*ppre && !*psuf) {
+ /* If we have to complete commands, add alias names, *
+ * shell functions and builtins too. */
+ dumphashtable(aliastab, -3);
+ dumphashtable(reswdtab, -3);
+ dumphashtable(shfunctab, -3);
+ dumphashtable(builtintab, -3);
+ if (isset(HASHLISTALL))
+ cmdnamtab->filltable(cmdnamtab);
+ dumphashtable(cmdnamtab, -3);
+ /* And parameter names if autocd and cdablevars are set. */
+ if (isset(AUTOCD) && isset(CDABLEVARS))
+ dumphashtable(paramtab, -4);
+ }
+ addwhat = (cc->mask & CC_QUOTEFLAG) ? -2 : CC_QUOTEFLAG;
+
+ if (cc->mask & CC_NAMED)
+ /* Add named directories. */
+ dumphashtable(nameddirtab, addwhat);
+ if (cc->mask & CC_OPTIONS)
+ /* Add option names. */
+ dumphashtable(optiontab, addwhat);
+ if (cc->mask & CC_VARS)
+ /* And parameter names. */
+ dumphashtable(paramtab, -9);
+ if (cc->mask & CC_BINDINGS)
+ /* And zle function names... */
+ dumphashtable(thingytab, CC_BINDINGS);
+ if (cc->keyvar) {
+ /* This adds things given to the compctl -k flag *
+ * (from a parameter or a list of words). */
+ char **usr = get_user_var(cc->keyvar);
+
+ if (usr)
+ while (*usr)
+ addmatch(*usr++, NULL);
+ }
+ if (cc->mask & CC_USERS)
+ /* Add user names. */
+ maketildelist();
+ if (cc->func) {
+ /* This handles the compctl -K flag. */
+ List list;
+ char **r;
+ int lv = lastval;
+
+ /* Get the function. */
+ if ((list = getshfunc(cc->func)) != &dummy_list) {
+ /* We have it, so build a argument list. */
+ LinkList args = newlinklist();
+
+ addlinknode(args, cc->func);
+
+ if (*delit) {
+ p = dupstrpfx(os, ooffs);
+ untokenize(p);
+ addlinknode(args, p);
+ p = dupstring(os + ooffs);
+ untokenize(p);
+ addlinknode(args, p);
+ } else {
+ addlinknode(args, lpre);
+ addlinknode(args, lsuf);
+ }
+
+ /* This flag allows us to use read -l and -c. */
+ incompctlfunc = 1;
+ /* Call the function. */
+ doshfunc(list, args, 0, 1);
+ incompctlfunc = 0;
+ /* And get the result from the reply parameter. */
+ if ((r = get_user_var("reply")))
+ while (*r)
+ addmatch(*r++, NULL);
+ }
+ lastval = lv;
+ }
+ if (cc->mask & (CC_JOBS | CC_RUNNING | CC_STOPPED)) {
+ /* Get job names. */
+ int i;
+ char *j, *jj;
+
+ for (i = 0; i < MAXJOB; i++)
+ if (jobtab[i].stat & STAT_INUSE) {
+ int stopped = jobtab[i].stat & STAT_STOPPED;
+
+ j = jj = dupstring(jobtab[i].procs->text);
+ /* Find the first word. */
+ for (; *jj; jj++)
+ if (*jj == ' ') {
+ *jj = '\0';
+ break;
+ }
+ if ((cc->mask & CC_JOBS) ||
+ (stopped && (cc->mask & CC_STOPPED)) ||
+ (!stopped && (cc->mask & CC_RUNNING)))
+ addmatch(j, NULL);
+ }
+ }
+ if (cc->str) {
+ /* Get the stuff from a compctl -s. */
+ LinkList foo = newlinklist();
+ LinkNode n;
+ int first = 1, ng = opts[NULLGLOB], oowe = we, oowb = wb;
+ char *tmpbuf;
+
+ opts[NULLGLOB] = 1;
+
+ /* Put the strin in the lexer buffer and call the lexer to *
+ * get the words we have to expand. */
+ zleparse = 1;
+ lexsave();
+ tmpbuf = (char *)halloc(strlen(cc->str) + 5);
+ sprintf(tmpbuf, "foo %s", cc->str); /* KLUDGE! */
+ inpush(tmpbuf, 0, NULL);
+ strinbeg();
+ noaliases = 1;
+ do {
+ ctxtlex();
+ if (tok == ENDINPUT || tok == LEXERR)
+ break;
+ if (!first && tokstr && *tokstr)
+ addlinknode(foo, ztrdup(tokstr));
+ first = 0;
+ } while (tok != ENDINPUT && tok != LEXERR);
+ noaliases = 0;
+ strinend();
+ inpop();
+ errflag = zleparse = 0;
+ lexrestore();
+ /* Fine, now do full expansion. */
+ prefork(foo, 0);
+ if (!errflag) {
+ globlist(foo);
+ if (!errflag)
+ /* And add the resulting words as matches. */
+ for (n = firstnode(foo); n; incnode(n))
+ addmatch((char *)n->dat, NULL);
+ }
+ opts[NULLGLOB] = ng;
+ we = oowe;
+ wb = oowb;
+ }
+ if (cc->hpat) {
+ /* We have a pattern to take things from the history. */
+ Comp compc = NULL;
+ char *e, *h, hpatsav;
+ Histent he;
+ int i = curhist - 1, n = cc->hnum;
+
+ /* Parse the pattern, if it isn't the null string. */
+ if (*(cc->hpat)) {
+ char *thpat = dupstring(cc->hpat);
+
+ tokenize(thpat);
+ compc = parsereg(thpat);
+ }
+ /* n holds the number of history line we have to search. */
+ if (!n)
+ n = -1;
+
+ /* Now search the history. */
+ while (n-- && (he = quietgethist(i--))) {
+ int iwords;
+ for (iwords = 0; iwords < he->nwords; iwords++) {
+ h = he->text + he->words[iwords*2];
+ e = he->text + he->words[iwords*2+1];
+ hpatsav = *e;
+ *e = '\0';
+ /* We now have a word from the history, ignore it *
+ * if it begins with a quote or `$'. */
+ if (*h != '\'' && *h != '"' && *h != '`' && *h != '$' &&
+ (!compc || domatch(h, compc, 0)))
+ /* Otherwise add it if it was matched. */
+ addmatch(dupstring(h), NULL);
+ if (hpatsav)
+ *e = hpatsav;
+ }
+ }
+ }
+ if ((t = cc->mask & (CC_ARRAYS | CC_INTVARS | CC_ENVVARS | CC_SCALARS |
+ CC_READONLYS | CC_SPECIALS | CC_PARAMS)))
+ /* Add various flavours of parameters. */
+ dumphashtable(paramtab, t);
+ if ((t = cc->mask & CC_SHFUNCS))
+ /* Add shell functions. */
+ dumphashtable(shfunctab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
+ if ((t = cc->mask & CC_BUILTINS))
+ /* Add builtins. */
+ dumphashtable(builtintab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
+ if ((t = cc->mask & CC_EXTCMDS))
+ /* Add external commands */
+ dumphashtable(cmdnamtab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
+ if ((t = cc->mask & CC_RESWDS))
+ /* Add reserved words */
+ dumphashtable(reswdtab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
+ if ((t = cc->mask & (CC_ALREG | CC_ALGLOB)))
+ /* Add the two types of aliases. */
+ dumphashtable(aliastab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
+
+ /* If we have no matches, ignore fignore. */
+ if (empty(matches)) {
+ matches = fmatches;
+ firstm = ffirstm;
+ shortest = fshortest;
+ ab = fab;
+ ae = fae;
+ shortl = fshortl;
+ }
+
+ /* Make an array from the list of matches. */
+ makearray(matches);
+ PERMALLOC {
+ amatches = arrdup(amatches);
+ if (firstm)
+ firstm = ztrdup(firstm);
+ /* And quote the prefixes/suffixes. */
+ if (hasspecial(s)) {
+ zfree(lpre, lpl);
+ zfree(lsuf, lsl);
+ lpre = zalloc(lpl + 1);
+ memcpy(lpre, s, lpl);
+ lpre[lpl] = '\0';
+ lsuf = ztrdup(s + offs);
+ quotepresuf(&lpre);
+ quotepresuf(&lsuf);
+ untokenize(lpre);
+ untokenize(lsuf);
+ }
+ quotepresuf(&fpre);
+ quotepresuf(&fsuf);
+ quotepresuf(&ppre);
+ quotepresuf(&psuf);
+ } LASTALLOC;
+
+ if (!errflag && cc->ylist) {
+ /* generate the user-defined display list: if anything fails, *
+ * we silently allow the normal completion list to be used. */
+ char **yaptr, *uv = NULL;
+ List list;
+
+ if (cc->ylist[0] == '$' || cc->ylist[0] == '(') {
+ /* from variable */
+ uv = cc->ylist + (cc->ylist[0] == '$');
+ } else if ((list = getshfunc(cc->ylist)) != &dummy_list) {
+ /* from function: pass completions as arg list */
+ LinkList args = newlinklist();
+ int addlen = strlen(rpre) + strlen(rsuf) + 1;
+
+ addlinknode(args, cc->ylist);
+ for (yaptr = amatches; *yaptr; yaptr++) {
+ /* can't use tricat(). rats. */
+ char *ptr = (char *)halloc(addlen + strlen(*yaptr));
+ sprintf(ptr, "%s%s%s", rpre, *yaptr, rsuf);
+ addlinknode(args, ptr);
+ }
+
+ /* No harm in allowing read -l and -c here, too */
+ incompctlfunc = 1;
+ doshfunc(list, args, 0, 1);
+ incompctlfunc = 0;
+ uv = "reply";
+ }
+ if (uv && (yaptr = get_user_var(uv))) {
+ PERMALLOC {
+ aylist = arrdup(yaptr);
+ } LASTALLOC;
+ }
+ }
+
+ /* Get the explanation string we will have to print: *
+ * do this here in case a -y function alters the messge */
+ if ((expl = cc->explain)) {
+ if (cc->mask & CC_EXPANDEXPL && !parsestr(expl = dupstring(expl))) {
+ singsub(&expl);
+ untokenize(expl);
+ }
+ expl = ztrdup(expl);
+ }
+
+ remsuffix = (cc->mask & CC_REMOVE);
+ ccsuffix = cc->suffix;
+
+ validlist = 1;
+ if (nmatches && !errflag)
+ return 0;
+
+ if ((isf || cc->xor) && !parampre) {
+ /* We found no matches, but there is a xor'ed completion: *
+ * fine, so go back and continue with that compctl. */
+ errflag = 0;
+ cc = cc->xor;
+ isf = 0;
+ wb = owb;
+ we = owe;
+ cs = ocs;
+ ll = oll;
+ strcpy((char *)line, (char *)ol);
+ offs = oloffs;
+ s = dupstring(os);
+ free(amatches);
+ zsfree(rpre);
+ zsfree(rsuf);
+ zsfree(lpre);
+ zsfree(lsuf);
+ zsfree(ppre);
+ zsfree(psuf);
+ zsfree(fpre);
+ zsfree(fsuf);
+ zsfree(prpre);
+ zsfree(parampre);
+ zsfree(qparampre);
+ zsfree(firstm);
+ if (expl)
+ zsfree(expl);
+ expl = NULL;
+ if (aylist)
+ freearray(aylist);
+ aylist = NULL;
+ goto xorrec;
+ }
+
+ /* No matches and xor'ed completion: restore the command line if *
+ * it was alredy quoted, which is the case when s is untokenized. */
+ if (untokenized)
+ strcpy((char *)line, (char *)ol);
+ return 1;
+}
+
+/* Invalidate the completion list. */
+
+/**/
+void
+invalidatelist(void)
+{
+ if(showinglist == -2)
+ listmatches();
+ if(validlist) {
+ freearray(amatches);
+ if (aylist)
+ freearray(aylist);
+ aylist = NULL;
+ if (expl)
+ zsfree(expl);
+ expl = 0;
+ zsfree(rpre);
+ zsfree(rsuf);
+ zsfree(lpre);
+ zsfree(lsuf);
+ zsfree(ppre);
+ zsfree(psuf);
+ zsfree(fpre);
+ zsfree(fsuf);
+ zsfree(prpre);
+ zsfree(parampre);
+ zsfree(qparampre);
+ zsfree(firstm);
+ if (ccmain != &cc_dummy)
+ freecompctl(ccmain);
+ }
+ lastambig = menucmp = showinglist = validlist = 0;
+ menucur = NULL;
+}
+
+/* Get the words from a variable or a compctl -k list. */
+
+/**/
+static char **
+get_user_var(char *nam)
+{
+ if (!nam)
+ return NULL;
+ else if (*nam == '(') {
+ /* It's a (...) list, not a parameter name. */
+ char *ptr, *s, **uarr, **aptr;
+ int count = 0, notempty = 0, brk = 0;
+ LinkList arrlist = newlinklist();
+
+ ptr = dupstring(nam);
+ s = ptr + 1;
+ while (*++ptr) {
+ if (*ptr == '\\' && ptr[1])
+ chuck(ptr), notempty = 1;
+ else if (*ptr == ',' || inblank(*ptr) || *ptr == ')') {
+ if (*ptr == ')')
+ brk++;
+ if (notempty) {
+ *ptr = '\0';
+ count++;
+ if (*s == '\n')
+ s++;
+ addlinknode(arrlist, s);
+ }
+ s = ptr + 1;
+ notempty = 0;
+ } else {
+ notempty = 1;
+ if(*ptr == Meta)
+ ptr++;
+ }
+ if (brk)
+ break;
+ }
+ if (!brk || !count)
+ return NULL;
+ *ptr = '\0';
+ aptr = uarr = (char **)ncalloc(sizeof(char *) * (count + 1));
+
+ while ((*aptr++ = (char *)ugetnode(arrlist)));
+ uarr[count] = NULL;
+ return uarr;
+ } else {
+ /* Otherwise it should be a parameter name. */
+ char **arr = NULL, *val;
+ if (!(arr = getaparam(nam)) && (val = getsparam(nam))) {
+ arr = (char **)ncalloc(2*sizeof(char *));
+ arr[0] = val;
+ arr[1] = NULL;
+ }
+ return arr;
+ }
+
+}
+
+/* This is strcmp with ignoring backslashes. */
+
+/**/
+static int
+strbpcmp(const void *a, const void *b)
+{
+ char *aa = *((char **)a), *bb = *((char **)b);
+
+ while (*aa && *bb) {
+ if (*aa == '\\')
+ aa++;
+ if (*bb == '\\')
+ bb++;
+ if (*aa != *bb)
+ return (int)(*aa - *bb);
+ if (*aa)
+ aa++;
+ if (*bb)
+ bb++;
+ }
+ return (int)(*aa - *bb);
+}
+
+/* Make an array from a linked list */
+
+/**/
+static void
+makearray(LinkList l)
+{
+ char **ap, **bp, **cp;
+ LinkNode nod;
+
+ /* Build an array for the matches. */
+ ap = amatches = (char **)ncalloc(((nmatches = countlinknodes(l)) + 1) *
+ sizeof(char *));
+
+ /* And copy them into it. */
+ for (nod = firstnode(l); nod; incnode(nod))
+ *ap++ = (char *)getdata(nod);
+ *ap = NULL;
+
+ /* Now sort the array. */
+ qsort((void *) amatches, nmatches, sizeof(char *),
+ (int (*) _((const void *, const void *)))strbpcmp);
+
+ /* And delete the ones that occur more than once. */
+ for (ap = cp = amatches; *ap; ap++) {
+ *cp++ = *ap;
+ for (bp = ap; bp[1] && !strcmp(*ap, bp[1]); bp++);
+ ap = bp;
+ }
+ *cp = NULL;
+ nmatches = arrlen(amatches);
+}
+
+/* Handle the case were we found more than one match. */
+
+/**/
+static void
+do_ambiguous(void)
+{
+ int p = (usemenu || ispattern), atend = (cs == we);
+ int inv = 0;
+
+ menucmp = 0;
+
+ /* If we have to insert the first match, call do_single(). This is *
+ * how REC_EXACT takes effect. We effectively turn the ambiguous *
+ * completion into an unambiguous one. */
+ if (shortest && shortl == 0 && isset(RECEXACT) &&
+ (usemenu == 0 || unset(AUTOMENU))) {
+ do_single(shortest);
+ invalidatelist();
+ return;
+ }
+ /* Setting lastambig here means that the completion is ambiguous and *
+ * AUTO_MENU might want to start a menu completion next time round, *
+ * but this might be overridden below if we can complete an *
+ * unambiguous prefix. */
+ lastambig = 1;
+ if(p) {
+ /* p is set if we are in a position to start using menu completion *
+ * due to one of the menu completion options, or due to the *
+ * menu-complete-word command, or due to using GLOB_COMPLETE which *
+ * does menu-style completion regardless of the setting of the *
+ * normal menu completion options. */
+ do_ambig_menu();
+ } else {
+ /* Sort-of general case: we have an ambiguous completion, and aren't *
+ * starting menu completion or doing anything really weird. We need *
+ * to insert any unambiguous prefix and suffix, if possible. */
+ if(ab)
+ inststrlen(firstm, 1, ab);
+ if(ae && !atend)
+ inststrlen(firstm + strlen(firstm) - ae, 0, ae);
+ if(ab || (ae && !atend))
+ inv = 1;
+ /* If the LIST_AMBIGUOUS option (meaning roughly `show a list only *
+ * if the completion is completely ambiguous') is set, and some *
+ * prefix was inserted, return now, bypassing the list-displaying *
+ * code. On the way, invalidate the list and note that we don't *
+ * want to enter an AUTO_MENU imediately. */
+ if(isset(LISTAMBIGUOUS) && inv) {
+ invalidatelist();
+ lastambig = 0;
+ return;
+ }
+ }
+ /* At this point, we might want a completion listing. Show the listing *
+ * if it is needed. */
+ if (isset(LISTBEEP))
+ feep();
+ if (isset(AUTOLIST) && !amenu && !showinglist)
+ showinglist = -2;
+ if(inv)
+ invalidatelist();
+}
+
+/* This is a stat that ignores backslashes in the filename. The `ls' *
+ * parameter says if we have to do lstat() or stat(). I think this *
+ * should instead be done by use of a general function to expand a *
+ * filename (stripping backslashes), combined with the actual *
+ * (l)stat(). */
+
+/**/
+static int
+ztat(char *nam, struct stat *buf, int ls)
+{
+ char b[PATH_MAX], *p;
+
+ for (p = b; p < b + sizeof(b) - 1 && *nam; nam++)
+ if (*nam == '\\' && nam[1])
+ *p++ = *++nam;
+ else
+ *p++ = *nam;
+ *p = '\0';
+
+ return ls ? lstat(b, buf) : stat(b, buf);
+}
+
+/* Insert a single match in the command line. */
+
+/**/
+static void
+do_single(char *str)
+{
+ int l;
+ int havesuff = 0;
+
+ fixsuffix();
+
+ if (!menucur) {
+ /* We are currently not in a menu-completion, *
+ * so set the position variables. */
+ if (ispattern) {
+ cs = we;
+ menupos = wb;
+ } else
+ menupos = cs;
+ menuwe = (cs == we) || isset(ALWAYSTOEND);
+ menuend = we;
+ }
+ /* If we are already in a menu-completion or if we have done a *
+ * glob completion, we have to delete some of the stuff on the *
+ * command line. */
+ if (menucur) {
+ if (menuinsc) {
+ cs = menuend + lsl;
+ foredel(menuinsc);
+ }
+ l = menulen;
+ } else if (ispattern)
+ l = we - wb;
+ else
+ l = 0;
+
+ menuinsc = 0;
+ cs = menupos;
+ foredel(l);
+
+ /* And than we insert the new string. */
+ inststrlen(str, 1, menulen = strlen(str));
+ menuend = cs;
+
+ cs += lsl;
+
+ if (ccsuffix) {
+ /* There is a compctl -S suffix. Add it. */
+ if (!(haswhat & HAS_SUFFIX) && *ccsuffix) {
+ havesuff = 1;
+ inststr(ccsuffix);
+ menuinsc = ztrlen(ccsuffix);
+ if (remsuffix && menuwe)
+ makesuffix(menuinsc);
+ }
+ havesuff = 1;
+ } else {
+ /* There is no user-specified suffix, *
+ * so generate one automagically. */
+ if(parampre && parambr) {
+ /*{{*/
+ /* Completing a parameter in braces. Add a removable `}' suffix. */
+ inststrlen("}", 1, 1);
+ menuinsc++;
+ }
+ if(!(haswhat & HAS_MISC) ||
+ (parampre && isset(AUTOPARAMSLASH))) {
+ /* If we have only filenames or we completed a parameter name *
+ * and AUTO_PARAM_SLASH is set, lets see if it is a directory. *
+ * If it is, we append a slash. */
+ char *p;
+ struct stat buf;
+
+ /* Build the path name. */
+ if (ispattern || ic || parampre) {
+ int ne = noerrs;
+
+ noerrs = 1;
+
+ if (parampre) {
+ int pl = strlen(parampre);
+ p = (char *) ncalloc(pl + strlen(lpre) + strlen(str) +
+ strlen(lsuf) + 1);
+ sprintf(p, "%s%s%s%s", parampre, lpre, str, lsuf);
+ if (pl && p[pl-1] == Inbrace)
+ strcpy(p+pl-1, p+pl);
+ }
+ else if (ic) {
+ p = (char *) ncalloc(strlen(ppre) + strlen(fpre) + strlen(str) +
+ strlen(fsuf) + strlen(psuf) + 2);
+ sprintf(p, "%c%s%s%s%s%s", ic,
+ ppre, fpre, str, fsuf, psuf);
+ }
+ else
+ p = dupstring(str);
+ parsestr(p);
+ if (ic)
+ *p = ic;
+ singsub(&p);
+
+ noerrs = ne;
+ } else {
+ p = (char *) ncalloc((prpre ? strlen(prpre) : 0) + strlen(fpre) +
+ strlen(str) + strlen(fsuf) + strlen(psuf) + 3);
+ sprintf(p, "%s%s%s%s%s",
+ (prpre && *prpre) ? prpre : "./", fpre, str,
+ fsuf, psuf);
+ }
+ /* And do the stat. */
+ if (!ztat(p, &buf, 0) && S_ISDIR(buf.st_mode)) {
+ /* It is a directory, so add the slash. */
+ havesuff = 1;
+ inststrlen("/", 1, 1);
+ menuinsc++;
+ if(menuwe && isset(AUTOREMOVESLASH)) {
+ makesuffix(1);
+ suffixlen['/'] = 1;
+ }
+ }
+ }
+ }
+ /* If completing in a brace expansion... */
+ if(complinbrace) {
+ if(havesuff) {
+ /*{{*/
+ /* If a suffix was added, and is removable, let *
+ * `,' and `}' remove it. */
+ if(isset(AUTOPARAMKEYS))
+ suffixlen[','] = suffixlen['}'] = suffixlen[256];
+ } else {
+ /*{{*/
+ /* Otherwise, add a `,' suffix, and let `}' remove it. */
+ havesuff = 1;
+ inststrlen(",", 1, 1);
+ menuinsc++;
+ if(menuwe && isset(AUTOPARAMKEYS))
+ suffixlen[','] = suffixlen['}'] = 1;
+ }
+ } else if(!menucmp && !havesuff) {
+ /* If we didn't add a suffix, add a space, unless we are *
+ * doing menu completion. */
+ inststrlen(" ", 1, 1);
+ menuinsc++;
+ if(menuwe)
+ makesuffix(1);
+ }
+ if(menuwe && parampre && isset(AUTOPARAMKEYS))
+ makeparamsuffix(parambr, menuinsc);
+
+ if (!menuwe)
+ cs = menuend;
+}
+
+/* This handles the beginning of menu-completion. */
+
+/**/
+static void
+do_ambig_menu(void)
+{
+ menucmp = 1;
+ menucur = NULL;
+ do_single(amatches[0]);
+ menucur = amatches;
+}
+
+/* Return the length of the common prefix of s and t. */
+
+/**/
+int
+pfxlen(char *s, char *t)
+{
+ int i = 0;
+
+ while (*s && *s == *t)
+ s++, t++, i++;
+ return i;
+}
+
+/* Return the length of the common suffix of s and t. */
+
+/**/
+static int
+sfxlen(char *s, char *t)
+{
+ if (*s && *t) {
+ int i = 0;
+ char *s2 = s + strlen(s) - 1, *t2 = t + strlen(t) - 1;
+
+ while (s2 >= s && t2 >= t && *s2 == *t2)
+ s2--, t2--, i++;
+
+ return i;
+ } else
+ return 0;
+}
+
+/* This is used to print the explanation string. *
+ * It returns the number of lines printed. */
+
+/**/
+static int
+printfmt(char *fmt, int n, int dopr)
+{
+ char *p = fmt, nc[DIGBUFSIZE];
+ int l = 0, cc = 0;
+
+ for (; *p; p++) {
+ /* Handle the `%' stuff (%% == %, %n == <number of matches>). */
+ if (*p == '%') {
+ if (*++p) {
+ switch (*p) {
+ case '%':
+ if (dopr)
+ putc('%', shout);
+ cc++;
+ break;
+ case 'n':
+ sprintf(nc, "%d", n);
+ if (dopr)
+ fprintf(shout, nc);
+ cc += strlen(nc);
+ break;
+ }
+ } else
+ break;
+ } else {
+ cc++;
+ if (*p == '\n') {
+ l += 1 + (cc / columns);
+ cc = 0;
+ }
+ if (dopr)
+ putc(*p, shout);
+ }
+ }
+
+ return l + (cc / columns);
+}
+
+/* List the matches. Note that the list entries are metafied. */
+
+/**/
+void
+listmatches(void)
+{
+ int longest = 1, fct, fw, colsz, t0, t1, ct, up, cl, xup = 0;
+ int off = 0, boff = 0, nboff = 0;
+ int of = (!aylist && isset(LISTTYPES) && !(haswhat & HAS_MISC));
+ char **arr, **ap, sav;
+ int nfpl, nfsl, nlpl, nlsl;
+ int listmax = getiparam("LISTMAX"), litnl = 0;
+ size_t (*strlenfn) _((char const *));
+
+#ifdef DEBUG
+ /* Sanity check */
+ if(!validlist) {
+ showmsg("BUG: listmatches called with bogus list");
+ return;
+ }
+#endif
+
+ /* Calculate lengths of prefixes/suffixes to be added */
+ nfpl = fpre ? niceztrlen(fpre) : 0;
+ nfsl = fsuf ? niceztrlen(fsuf) : 0;
+ nlpl = lpre ? niceztrlen(lpre) : 0;
+ nlsl = lsuf ? niceztrlen(lsuf) : 0;
+
+ /* Calculate the lengths of the prefixes/suffixes we have to ignore
+ during printing. */
+ if (ispattern && !aylist && !(haswhat & (HAS_MISC | HAS_PATHPAT))) {
+ if (ppre && *ppre)
+ off = strlen(ppre);
+ if (psuf && *psuf) {
+ boff = strlen(psuf);
+ nboff = niceztrlen(psuf);
+ }
+ }
+
+ /* Set the cursor below the prompt. */
+ trashzle();
+ showinglist = 0;
+
+ clearflag = (isset(USEZLE) && !termflags &&
+ (isset(ALWAYSLASTPROMPT) && zmult == 1)) ||
+ (unset(ALWAYSLASTPROMPT) && zmult != 1);
+
+ /* just to keep gcc happy */
+ fw = colsz = up = 0;
+ if (aylist) {
+ arr = aylist;
+ /* If no literal newlines, the remaining code should use strlen() */
+ strlenfn = (size_t (*) _((char const *)))strlen;
+
+ /* The hard bit here is that we are handling newlines literally. *
+ * In fact, we are in principle handling all characters literally, *
+ * but it's quite enough work with just newlines. *
+ * If there are such, we give up trying to print the list as *
+ * columns and print as rows, counting the extra newlines. */
+ ct = 0;
+ for (ap = arr; *ap; ap++) {
+ ct++;
+ if (strchr(*ap, '\n'))
+ litnl++;
+ }
+ if (litnl) {
+ colsz = ct;
+ up = colsz + nlnct - clearflag;
+ /* Count real newlines, as well as overflowing lines. */
+ for (ap = arr; *ap; ap++) {
+ char *nlptr, *sptr = *ap;
+ while (sptr && *sptr) {
+ up += (nlptr = strchr(sptr, '\n'))
+ ? 1 + (nlptr-sptr)/columns
+ : strlen(sptr)/columns;
+ sptr = nlptr ? nlptr+1 : NULL;
+ }
+ }
+ }
+ } else {
+ arr = amatches;
+ ct = nmatches;
+ strlenfn = niceztrlen;
+ }
+
+
+ if (!litnl) {
+ /* Calculate the column width, the number of columns and the
+ number of lines. */
+ for (ap = arr; *ap; ap++)
+ if ((cl = strlenfn(*ap + off) - nboff +
+ ((ispattern || aylist) ? 0 :
+ (!(haswhat & HAS_MISC) ?
+ nfpl + nfsl : nlpl + nlsl))) > longest)
+ longest = cl;
+ if (of)
+ longest++;
+
+ fw = longest + 2;
+ fct = (columns + 1) / fw;
+ if (fct == 0) {
+ fct = 1;
+ colsz = ct;
+ up = colsz + nlnct - clearflag;
+ for (ap = arr; *ap; ap++)
+ up += (strlenfn(*ap + off) - nboff + of +
+ ((ispattern || aylist) ? 0 :
+ (!(haswhat & HAS_MISC) ?
+ nfpl + nfsl : nlpl + nlsl))) / columns;
+ } else {
+ colsz = (ct + fct - 1) / fct;
+ up = colsz + nlnct - clearflag + (ct == 0);
+ }
+ }
+
+ /* Print the explanation string, if any. */
+ if (expl) {
+ xup = printfmt(expl, ct, 1) + 1;
+ putc('\n', shout);
+ up += xup;
+ }
+
+ /* Maybe we have to ask if the user wants to see the list. */
+ if ((listmax && ct > listmax) || (!listmax && up >= lines)) {
+ int qup;
+ setterm();
+ qup = printfmt("zsh: do you wish to see all %n possibilities? ", ct, 1);
+ fflush(shout);
+ if (getzlequery() != 'y') {
+ if (clearflag) {
+ putc('\r', shout);
+ tcmultout(TCUP, TCMULTUP, qup);
+ if (tccan(TCCLEAREOD))
+ tcout(TCCLEAREOD);
+ tcmultout(TCUP, TCMULTUP, nlnct + xup);
+ } else
+ putc('\n', shout);
+ return;
+ }
+ if (clearflag) {
+ putc('\r', shout);
+ tcmultout(TCUP, TCMULTUP, qup);
+ if (tccan(TCCLEAREOD))
+ tcout(TCCLEAREOD);
+ } else
+ putc('\n', shout);
+ settyinfo(&shttyinfo);
+ }
+
+ /* Now print the matches. */
+ for (t1 = 0; t1 != colsz; t1++) {
+ ap = arr + t1;
+ if (of) {
+ /* We have to print the file types. */
+ while (*ap) {
+ int t2;
+ char *pb;
+ struct stat buf;
+
+ /* Build the path name for the stat. */
+ if (ispattern) {
+ int cut = strlen(*ap) - boff;
+
+ sav = ap[0][cut];
+ ap[0][cut] = '\0';
+ nicezputs(*ap + off, shout);
+ t2 = niceztrlen(*ap + off);
+ ap[0][cut] = sav;
+ pb = *ap;
+ } else {
+ nicezputs(fpre, shout);
+ nicezputs(*ap, shout);
+ nicezputs(fsuf, shout);
+ t2 = nfpl + niceztrlen(*ap) + nfsl;
+ pb = (char *) halloc((prpre ? strlen(prpre) : 0) + 3 +
+ strlen(fpre) + strlen(*ap) + strlen(fsuf));
+ sprintf(pb, "%s%s%s%s",
+ (prpre && *prpre) ? prpre : "./", fpre, *ap, fsuf);
+ }
+ if (ztat(pb, &buf, 1))
+ putc(' ', shout);
+ else
+ /* Print the file type character. */
+ putc(file_type(buf.st_mode), shout);
+ for (t0 = colsz; t0 && *ap; t0--, ap++);
+ if (*ap)
+ /* And add spaces to make the columns aligned. */
+ for (++t2; t2 < fw; t2++)
+ putc(' ', shout);
+ }
+ } else
+ while (*ap) {
+ int t2;
+
+ if (aylist) {
+ zputs(*ap, shout);
+ t2 = strlen(*ap);
+ } else if (ispattern) {
+ int cut = strlen(*ap) - boff;
+
+ sav = ap[0][cut];
+ ap[0][cut] = '\0';
+ nicezputs(*ap + off, shout);
+ t2 = niceztrlen(*ap + off);
+ ap[0][cut] = sav;
+ } else if (!(haswhat & HAS_MISC)) {
+ nicezputs(fpre, shout);
+ nicezputs(*ap, shout);
+ nicezputs(fsuf, shout);
+ t2 = nfpl + niceztrlen(*ap) + nfsl;
+ } else {
+ nicezputs(lpre, shout);
+ nicezputs(*ap, shout);
+ nicezputs(lsuf, shout);
+ t2 = nlpl + niceztrlen(*ap) + nlsl;
+ }
+ for (t0 = colsz; t0 && *ap; t0--, ap++);
+ if (*ap)
+ for (; t2 < fw; t2++)
+ putc(' ', shout);
+ }
+ if (t1 != colsz - 1 || !clearflag)
+ putc('\n', shout);
+ }
+ if (clearflag)
+ /* Move the cursor up to the prompt, if always_last_prompt *
+ * is set and all that... */
+ if (up < lines) {
+ tcmultout(TCUP, TCMULTUP, up);
+ showinglist = -1;
+ } else
+ clearflag = 0, putc('\n', shout);
+}
+
+/* This is used to print expansions. */
+
+/**/
+void
+listlist(LinkList l)
+{
+ int hw = haswhat, ip = ispattern;
+ char *lp = lpre, *ls = lsuf;
+ int nm = nmatches, vl = validlist;
+ char **am = amatches, **ay = aylist;
+ char *ex = expl;
+
+ haswhat = HAS_MISC;
+ ispattern = 0;
+ validlist = 1;
+ lpre = lsuf = "";
+ aylist = NULL;
+ expl = NULL;
+
+ makearray(l);
+ listmatches();
+ showinglist = 0;
+
+ expl = ex;
+ amatches = am;
+ aylist = ay;
+ nmatches = nm;
+ validlist = vl;
+ lpre = lp;
+ lsuf = ls;
+ ispattern = ip;
+ haswhat = hw;
+}
+
+/* Expand the history references. */
+
+/**/
+int
+doexpandhist(void)
+{
+ unsigned char *ol;
+ int oll, ocs, ne = noerrs, err;
+
+ DPUTS(useheap, "BUG: useheap in doexpandhist()");
+ HEAPALLOC {
+ pushheap();
+ metafy_line();
+ oll = ll;
+ ocs = cs;
+ ol = (unsigned char *)dupstring((char *)line);
+ expanding = 1;
+ excs = cs;
+ ll = cs = 0;
+ lexsave();
+ /* We push ol as it will remain unchanged */
+ inpush((char *) ol, 0, NULL);
+ strinbeg();
+ noaliases = 1;
+ noerrs = 1;
+ exlast = inbufct;
+ do {
+ ctxtlex();
+ } while (tok != ENDINPUT && tok != LEXERR);
+ stophist = 2;
+ while (!lexstop)
+ hgetc();
+ /* We have to save errflags because it's reset in lexrestore. Since *
+ * noerrs was set to 1 errflag is true if there was a habort() which *
+ * means that the expanded string is unusable. */
+ err = errflag;
+ noerrs = ne;
+ noaliases = 0;
+ strinend();
+ inpop();
+ zleparse = 0;
+ lexrestore();
+ expanding = 0;
+
+ if (!err) {
+ cs = excs;
+ if (strcmp((char *)line, (char *)ol)) {
+ unmetafy_line();
+ /* For vi mode -- reset the beginning-of-insertion pointer *
+ * to the beginning of the line. This seems a little silly, *
+ * if we are, for example, expanding "exec !!". */
+ if (viinsbegin > findbol())
+ viinsbegin = findbol();
+ popheap();
+ LASTALLOC_RETURN 1;
+ }
+ }
+
+ strcpy((char *)line, (char *)ol);
+ ll = oll;
+ cs = ocs;
+ unmetafy_line();
+
+ popheap();
+ } LASTALLOC;
+ return 0;
+}
+
+/**/
+void
+magicspace(void)
+{
+ c = ' ';
+ selfinsert();
+ doexpandhist();
+}
+
+/**/
+void
+expandhistory(void)
+{
+ if (!doexpandhist())
+ feep();
+}
+
+static int cmdwb, cmdwe;
+
+/**/
+static char *
+getcurcmd(void)
+{
+ int curlincmd;
+ char *s = NULL;
+
+ DPUTS(useheap, "BUG: useheap in getcurcmd()");
+ HEAPALLOC {
+ zleparse = 2;
+ lexsave();
+ metafy_line();
+ inpush(dupstrspace((char *) line), 0, NULL);
+ unmetafy_line();
+ strinbeg();
+ pushheap();
+ do {
+ curlincmd = incmdpos;
+ ctxtlex();
+ if (tok == ENDINPUT || tok == LEXERR)
+ break;
+ if (tok == STRING && curlincmd) {
+ zsfree(s);
+ s = ztrdup(tokstr);
+ cmdwb = ll - wordbeg;
+ cmdwe = ll + 1 - inbufct;
+ }
+ }
+ while (tok != ENDINPUT && tok != LEXERR && zleparse);
+ popheap();
+ strinend();
+ inpop();
+ errflag = zleparse = 0;
+ lexrestore();
+ } LASTALLOC;
+ return s;
+}
+
+/**/
+void
+processcmd(void)
+{
+ char *s;
+ int m = zmult;
+
+ s = getcurcmd();
+ if (!s) {
+ feep();
+ return;
+ }
+ zmult = 1;
+ pushline();
+ zmult = m;
+ inststr(bindk->nam);
+ inststr(" ");
+ untokenize(s);
+ HEAPALLOC {
+ inststr(quotename(s, NULL, NULL, NULL));
+ } LASTALLOC;
+ zsfree(s);
+ done = 1;
+}
+
+/**/
+void
+expandcmdpath(void)
+{
+ int oldcs = cs, na = noaliases;
+ char *s, *str;
+
+ noaliases = 1;
+ s = getcurcmd();
+ noaliases = na;
+ if (!s || cmdwb < 0 || cmdwe < cmdwb) {
+ feep();
+ return;
+ }
+ str = findcmd(s);
+ zsfree(s);
+ if (!str) {
+ feep();
+ return;
+ }
+ cs = cmdwb;
+ foredel(cmdwe - cmdwb);
+ spaceinline(strlen(str));
+ strncpy((char *)line + cs, str, strlen(str));
+ cs = oldcs;
+ if (cs >= cmdwe - 1)
+ cs += cmdwe - cmdwb + strlen(str);
+ if (cs > ll)
+ cs = ll;
+ zsfree(str);
+}
+
+/* Extra function added by AR Iano-Fletcher. */
+/* This is a expand/complete in the vein of wash. */
+
+/**/
+void
+expandorcompleteprefix(void)
+{
+ comppref = 1;
+ expandorcomplete();
+ comppref = 0;
+}
diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c
new file mode 100644
index 000000000..8fe3e7f0b
--- /dev/null
+++ b/Src/Zle/zle_utils.c
@@ -0,0 +1,650 @@
+/*
+ * zle_utils.c - miscellaneous line editor utilities
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * 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 Paul Falstad 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 Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad 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 Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "zle.mdh"
+#include "zle_utils.pro"
+
+/* Primary cut buffer */
+
+/**/
+struct cutbuffer cutbuf;
+
+/* Emacs-style kill buffer ring */
+
+/**/
+struct cutbuffer kring[KRINGCT];
+/**/
+int 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. */
+
+/**/
+struct cutbuffer vibuf[35];
+
+/* the line before last mod (for undo purposes) */
+
+/**/
+char *lastline;
+/**/
+int lastlinesz, lastll;
+
+/* size of line buffer */
+
+/**/
+int linesz;
+
+/* make sure that the line buffer has at least sz chars */
+
+/**/
+void
+sizeline(int sz)
+{
+ while (sz > linesz)
+ line = (unsigned char *)realloc(line, (linesz *= 4) + 2);
+}
+
+/* insert space for ct chars at cursor position */
+
+/**/
+void
+spaceinline(int ct)
+{
+ int i;
+
+ sizeline(ct + ll);
+ for (i = ll; --i >= cs;)
+ line[i + ct] = line[i];
+ ll += ct;
+ line[ll] = '\0';
+
+ if (mark > cs)
+ mark += ct;
+}
+
+/**/
+static void
+shiftchars(int to, int cnt)
+{
+ if (mark >= to + cnt)
+ mark -= cnt;
+ else if (mark > to)
+ mark = to;
+
+ while (to + cnt < ll) {
+ line[to] = line[to + cnt];
+ to++;
+ }
+ line[ll = to] = '\0';
+}
+
+/**/
+void
+backkill(int ct, int dir)
+{
+ int i = (cs -= ct);
+
+ cut(i, ct, dir);
+ shiftchars(i, ct);
+}
+
+/**/
+void
+forekill(int ct, int dir)
+{
+ int i = cs;
+
+ cut(i, ct, dir);
+ shiftchars(i, ct);
+}
+
+/**/
+void
+cut(int i, int ct, int dir)
+{
+ if (zmod.flags & MOD_VIBUF) {
+ struct cutbuffer *b = &vibuf[zmod.vibuf];
+
+ if (!(zmod.flags & MOD_VIAPP) || !b->buf) {
+ zfree(b->buf, b->len);
+ b->buf = (char *)zalloc(ct);
+ memcpy(b->buf, (char *) line + i, ct);
+ b->len = ct;
+ b->flags = vilinerange ? CUTBUFFER_LINE : 0;
+ } else {
+ int len = b->len;
+
+ if(vilinerange)
+ b->flags |= CUTBUFFER_LINE;
+ b->buf = realloc(b->buf, ct + len + !!(b->flags & CUTBUFFER_LINE));
+ if (b->flags & CUTBUFFER_LINE)
+ b->buf[len++] = '\n';
+ memcpy(b->buf + len, (char *) line + i, ct);
+ b->len = len + ct;
+ }
+ return;
+ } else {
+ /* Save in "1, shifting "1-"8 along to "2-"9 */
+ int n;
+ zfree(vibuf[34].buf, vibuf[34].len);
+ for(n=34; n>26; n--)
+ vibuf[n] = vibuf[n-1];
+ vibuf[26].buf = (char *)zalloc(ct);
+ memcpy(vibuf[26].buf, (char *) line + i, ct);
+ vibuf[26].len = ct;
+ vibuf[26].flags = vilinerange ? CUTBUFFER_LINE : 0;
+ }
+ if (!cutbuf.buf) {
+ cutbuf.buf = ztrdup("");
+ cutbuf.len = cutbuf.flags = 0;
+ } else if (!(lastcmd & ZLE_KILL)) {
+ kringnum = (kringnum + 1) % KRINGCT;
+ if (kring[kringnum].buf)
+ free(kring[kringnum].buf);
+ kring[kringnum] = cutbuf;
+ cutbuf.buf = ztrdup("");
+ cutbuf.len = cutbuf.flags = 0;
+ }
+ if (dir) {
+ char *s = (char *)zalloc(cutbuf.len + ct);
+
+ memcpy(s, (char *) line + i, ct);
+ memcpy(s + ct, cutbuf.buf, cutbuf.len);
+ free(cutbuf.buf);
+ cutbuf.buf = s;
+ cutbuf.len += ct;
+ } else {
+ cutbuf.buf = realloc(cutbuf.buf, cutbuf.len + ct);
+ memcpy(cutbuf.buf + cutbuf.len, (char *) line + i, ct);
+ cutbuf.len += ct;
+ }
+ if(vilinerange)
+ cutbuf.flags |= CUTBUFFER_LINE;
+ else
+ cutbuf.flags &= ~CUTBUFFER_LINE;
+}
+
+/**/
+void
+backdel(int ct)
+{
+ shiftchars(cs -= ct, ct);
+}
+
+/**/
+void
+foredel(int ct)
+{
+ shiftchars(cs, ct);
+}
+
+/**/
+void
+setline(char const *s)
+{
+ sizeline(strlen(s));
+ strcpy((char *) line, s);
+ unmetafy((char *) line, &ll);
+ if ((cs = ll) && invicmdmode())
+ cs--;
+}
+
+/**/
+int
+findbol(void)
+{
+ int x = cs;
+
+ while (x > 0 && line[x - 1] != '\n')
+ x--;
+ return x;
+}
+
+/**/
+int
+findeol(void)
+{
+ int x = cs;
+
+ while (x != ll && line[x] != '\n')
+ x++;
+ return x;
+}
+
+/**/
+void
+findline(int *a, int *b)
+{
+ *a = findbol();
+ *b = findeol();
+}
+
+/* Search for needle in haystack. Haystack is a metafied string while *
+ * needle is unmetafied and len-long. Start the search at position *
+ * pos. Search forward if dir > 0 otherwise search backward. */
+
+/**/
+char *
+hstrnstr(char *haystack, int pos, char *needle, int len, int dir, int sens)
+{
+ char *s = haystack + pos;
+
+ if (dir > 0) {
+ while (*s) {
+ if (metadiffer(s, needle, len) < sens)
+ return s;
+ s += 1 + (*s == Meta);
+ }
+ } else {
+ for (;;) {
+ if (metadiffer(s, needle, len) < sens)
+ return s;
+ if (s == haystack)
+ break;
+ s -= 1 + (s != haystack+1 && s[-2] == Meta);
+ }
+ }
+ return NULL;
+}
+
+/* Query the user, and return a single character response. The *
+ * question is assumed to have been printed already, and the *
+ * cursor is left immediately after the response echoed. *
+ * (Might cause a problem if this takes it onto the next line.) *
+ * <Tab> is interpreted as 'y'; any other control character is *
+ * interpreted as 'n'. If there are any characters in the *
+ * buffer, this is taken as a negative response, and no *
+ * characters are read. Case is folded. */
+
+/**/
+int
+getzlequery(void)
+{
+ int c;
+#ifdef FIONREAD
+ int val;
+
+ /* check for typeahead, which is treated as a negative response */
+ ioctl(SHTTY, FIONREAD, (char *)&val);
+ if (val) {
+ putc('n', shout);
+ return 'n';
+ }
+#endif
+
+ /* get a character from the tty and interpret it */
+ c = getkey(0);
+ if (c == '\t')
+ c = 'y';
+ else if (icntrl(c) || c == EOF)
+ c = 'n';
+ else
+ c = tulower(c);
+
+ /* echo response and return */
+ putc(c, shout);
+ return c;
+}
+
+/* Format a string, keybinding style. */
+
+/**/
+char *
+bindztrdup(char *str)
+{
+ int c, len = 1;
+ char *buf, *ptr, *ret;
+
+ for(ptr = str; *ptr; ptr++) {
+ c = *ptr == Meta ? STOUC(*++ptr) ^ 32 : STOUC(*ptr);
+ if(c & 0x80) {
+ len += 3;
+ c &= 0x7f;
+ }
+ if(c < 32 || c == 0x7f) {
+ len++;
+ c ^= 64;
+ }
+ len += c == '\\' || c == '^';
+ len++;
+ }
+ ptr = buf = zalloc(len);
+ for(; *str; str++) {
+ c = *str == Meta ? STOUC(*++str) ^ 32 : STOUC(*str);
+ if(c & 0x80) {
+ *ptr++ = '\\';
+ *ptr++ = 'M';
+ *ptr++ = '-';
+ c &= 0x7f;
+ }
+ if(c < 32 || c == 0x7f) {
+ *ptr++ = '^';
+ c ^= 64;
+ }
+ if(c == '\\' || c == '^')
+ *ptr++ = '\\';
+ *ptr++ = c;
+ }
+ *ptr = 0;
+ ret = dquotedztrdup(buf);
+ zsfree(buf);
+ return ret;
+}
+
+/* Display a metafied string, keybinding-style. */
+
+/**/
+int
+printbind(char *str, FILE *stream)
+{
+ char *b = bindztrdup(str);
+ int ret = zputs(b, stream);
+
+ zsfree(b);
+ return ret;
+}
+
+/* Display a message where the completion list normally goes. *
+ * The message must be metafied. */
+
+/**/
+void
+showmsg(char const *msg)
+{
+ char const *p;
+ int up = 0, cc = 0, c;
+
+ trashzle();
+ clearflag = isset(USEZLE) && !termflags && isset(ALWAYSLASTPROMPT);
+
+ for(p = msg; (c = *p); p++) {
+ if(c == Meta)
+ c = *++p ^ 32;
+ if(c == '\n') {
+ putc('\n', shout);
+ up += 1 + cc / columns;
+ cc = 0;
+ } else {
+ char const *n = nicechar(c);
+ fputs(n, shout);
+ cc += strlen(n);
+ }
+ }
+ up += cc / columns;
+
+ if (clearflag) {
+ putc('\r', shout);
+ tcmultout(TCUP, TCMULTUP, up + nlnct);
+ } else
+ putc('\n', shout);
+ showinglist = 0;
+}
+
+/* handle the error flag */
+
+/**/
+void
+feep(void)
+{
+ feepflag = 1;
+}
+
+/**/
+void
+handlefeep(void)
+{
+ if(feepflag)
+ beep();
+ feepflag = 0;
+}
+
+/***************/
+/* undo system */
+/***************/
+
+/* head of the undo list, and the current position */
+
+static struct change *changes, *curchange;
+
+/* list of pending changes, not yet in the undo system */
+
+static struct change *nextchanges, *endnextchanges;
+
+/**/
+void
+initundo(void)
+{
+ nextchanges = NULL;
+ changes = curchange = zalloc(sizeof(*curchange));
+ curchange->prev = curchange->next = NULL;
+ curchange->del = curchange->ins = NULL;
+ lastline = zalloc(lastlinesz = linesz);
+ memcpy(lastline, line, lastll = ll);
+}
+
+/**/
+void
+freeundo(void)
+{
+ freechanges(changes);
+ freechanges(nextchanges);
+ zfree(lastline, lastlinesz);
+}
+
+/**/
+static void
+freechanges(struct change *p)
+{
+ struct change *n;
+
+ for(; p; p = n) {
+ n = p->next;
+ zsfree(p->del);
+ zsfree(p->ins);
+ zfree(p, sizeof(*p));
+ }
+}
+
+/* register pending changes in the undo system */
+
+/**/
+void
+handleundo(void)
+{
+ mkundoent();
+ if(!nextchanges)
+ return;
+ setlastline();
+ if(curchange->next) {
+ freechanges(curchange->next);
+ curchange->next = NULL;
+ zsfree(curchange->del);
+ zsfree(curchange->ins);
+ curchange->del = curchange->ins = NULL;
+ }
+ nextchanges->prev = curchange->prev;
+ if(curchange->prev)
+ curchange->prev->next = nextchanges;
+ else
+ changes = nextchanges;
+ curchange->prev = endnextchanges;
+ endnextchanges->next = curchange;
+ nextchanges = endnextchanges = NULL;
+}
+
+/* add an entry to the undo system, if anything has changed */
+
+/**/
+void
+mkundoent(void)
+{
+ int pre, suf;
+ int sh = ll < lastll ? ll : lastll;
+ struct change *ch;
+
+ if(lastll == ll && !memcmp(lastline, line, ll))
+ return;
+ for(pre = 0; pre < sh && line[pre] == lastline[pre]; )
+ pre++;
+ for(suf = 0; suf < sh - pre &&
+ line[ll - 1 - suf] == lastline[lastll - 1 - suf]; )
+ suf++;
+ ch = zalloc(sizeof(*ch));
+ ch->next = NULL;
+ ch->hist = histline;
+ ch->off = pre;
+ if(suf + pre == lastll)
+ ch->del = NULL;
+ else
+ ch->del = metafy(lastline + pre, lastll - pre - suf, META_DUP);
+ if(suf + pre == ll)
+ ch->ins = NULL;
+ else
+ ch->ins = metafy((char *)line + pre, ll - pre - suf, META_DUP);
+ if(nextchanges) {
+ ch->flags = CH_PREV;
+ ch->prev = endnextchanges;
+ endnextchanges->flags |= CH_NEXT;
+ endnextchanges->next = ch;
+ } else {
+ nextchanges = ch;
+ ch->flags = 0;
+ ch->prev = NULL;
+ }
+ endnextchanges = ch;
+}
+
+/* set lastline to match line */
+
+/**/
+void
+setlastline(void)
+{
+ if(lastlinesz != linesz)
+ lastline = realloc(lastline, lastlinesz = linesz);
+ memcpy(lastline, line, lastll = ll);
+}
+
+/* move backwards through the change list */
+
+/**/
+void
+undo(void)
+{
+ handleundo();
+ do {
+ if(!curchange->prev) {
+ feep();
+ return;
+ }
+ unapplychange(curchange = curchange->prev);
+ } while(curchange->flags & CH_PREV);
+ setlastline();
+}
+
+/**/
+static void
+unapplychange(struct change *ch)
+{
+ if(ch->hist != histline) {
+ remember_edits();
+ setline(zle_get_event(histline = ch->hist));
+ }
+ cs = ch->off;
+ if(ch->ins)
+ foredel(ztrlen(ch->ins));
+ if(ch->del) {
+ char *c = ch->del;
+
+ spaceinline(ztrlen(c));
+ for(; *c; c++)
+ if(*c == Meta)
+ line[cs++] = STOUC(*++c) ^ 32;
+ else
+ line[cs++] = STOUC(*c);
+ }
+}
+
+/* move forwards through the change list */
+
+/**/
+void
+redo(void)
+{
+ handleundo();
+ do {
+ if(!curchange->next) {
+ feep();
+ return;
+ }
+ applychange(curchange);
+ curchange = curchange->next;
+ } while(curchange->prev->flags & CH_NEXT);
+ setlastline();
+}
+
+/**/
+static void
+applychange(struct change *ch)
+{
+ if(ch->hist != histline) {
+ remember_edits();
+ setline(zle_get_event(histline = ch->hist));
+ }
+ cs = ch->off;
+ if(ch->del)
+ foredel(ztrlen(ch->del));
+ if(ch->ins) {
+ char *c = ch->ins;
+
+ spaceinline(ztrlen(c));
+ for(; *c; c++)
+ if(*c == Meta)
+ line[cs++] = STOUC(*++c) ^ 32;
+ else
+ line[cs++] = STOUC(*c);
+ }
+}
+
+/* vi undo: toggle between the end of the undo list and the preceding point */
+
+/**/
+void
+viundochange(void)
+{
+ handleundo();
+ if(curchange->next) {
+ do {
+ applychange(curchange);
+ curchange = curchange->next;
+ } while(curchange->next);
+ setlastline();
+ } else
+ undo();
+}
diff --git a/Src/Zle/zle_vi.c b/Src/Zle/zle_vi.c
new file mode 100644
index 000000000..a599d8091
--- /dev/null
+++ b/Src/Zle/zle_vi.c
@@ -0,0 +1,925 @@
+/*
+ * zle_vi.c - vi-specific functions
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * 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 Paul Falstad 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 Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad 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 Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "zle.mdh"
+#include "zle_vi.pro"
+
+/* != 0 if we're getting a vi range */
+
+/**/
+int virangeflag;
+
+/* kludge to get cw and dw to work right */
+
+/**/
+int wordflag;
+
+/* != 0 if we're killing lines into a buffer, vi-style */
+
+/**/
+int vilinerange;
+
+/* last vi change buffer, for vi change repetition */
+
+/**/
+int vichgbufsz, vichgbufptr, vichgflag;
+
+/**/
+char *vichgbuf;
+
+/* point where vi insert mode was last entered */
+
+/**/
+int viinsbegin;
+
+static struct modifier lastmod;
+static int inrepeat, vichgrepeat;
+
+/**/
+static void
+startvichange(int im)
+{
+ if (im != -1) {
+ insmode = im;
+ vichgflag = 1;
+ }
+ if (inrepeat) {
+ zmod = lastmod;
+ inrepeat = vichgflag = 0;
+ vichgrepeat = 1;
+ } else {
+ lastmod = zmod;
+ if (vichgbuf)
+ free(vichgbuf);
+ vichgbuf = (char *)zalloc(vichgbufsz = 16);
+ vichgbuf[0] = c;
+ vichgbufptr = 1;
+ vichgrepeat = 0;
+ }
+}
+
+/**/
+static void
+startvitext(int im)
+{
+ startvichange(im);
+ selectkeymap("main", 1);
+ undoing = 0;
+ viinsbegin = cs;
+}
+
+/**/
+int
+vigetkey(void)
+{
+ Keymap mn = openkeymap("main");
+ char m[3], *str;
+ Thingy cmd;
+
+ if((c = getkey(0)) == EOF) {
+ feep();
+ return -1;
+ }
+
+ m[0] = c;
+ metafy(m, 1, META_NOALLOC);
+ if(mn)
+ cmd = keybind(mn, m, &str);
+ else
+ cmd = t_undefinedkey;
+
+ if (!cmd || cmd == Th(z_sendbreak)) {
+ feep();
+ return -1;
+ } else if (cmd == Th(z_quotedinsert)) {
+ if ((c = getkey(0)) == EOF) {
+ feep();
+ return -1;
+ }
+ } else if(cmd == Th(z_viquotedinsert)) {
+ char sav = line[cs];
+
+ line[cs] = '^';
+ refresh();
+ c = getkey(0);
+ line[cs] = sav;
+ if(c == EOF) {
+ feep();
+ return -1;
+ }
+ } else if (cmd == Th(z_vicmdmode))
+ return -1;
+ return c;
+}
+
+/**/
+static int
+getvirange(int wf)
+{
+ int pos = cs;
+ 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;
+ feep();
+ return -1;
+ }
+ if(k2 == bindk)
+ /* The command key is repeated: a number of lines is used. */
+ dovilinerange();
+ else
+ execzlefunc(k2);
+ if(vichgrepeat)
+ zmult = mult1;
+ else
+ zmult = mult1 * zmod.tmult;
+ } while(prefixflag);
+ 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 || ll != lastll || memcmp(line, lastline, ll)) {
+ histline = hist1;
+ memcpy(line, lastline, ll = lastll);
+ cs = pos;
+ feep();
+ return -1;
+ }
+
+ /* Can't handle an empty file. Also, if the movement command *
+ * failed, or didn't move, it is an error. */
+ if (!ll || (cs == pos && virangeflag != 2)) {
+ feep();
+ return -1;
+ }
+
+ /* 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)
+ pos++;
+
+ /* Get the range the right way round. cs is placed at the *
+ * start of the range, and pos (the return value of this *
+ * function) is the end. */
+ if (cs > pos) {
+ int tmp = cs;
+ cs = pos;
+ pos = tmp;
+ }
+
+ /* 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 cs. 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) {
+ int newcs = findbol();
+ cs = pos;
+ pos = findeol();
+ cs = newcs;
+ }
+ return pos;
+}
+
+/**/
+static void
+dovilinerange(void)
+{
+ int pos = cs, n = zmult;
+
+ /* A number of lines is taken as the range. The current line *
+ * is included. If the repeat count is positive the lines go *
+ * downward, otherwise upward. The repeat count gives the *
+ * number of lines. */
+ vilinerange = 1;
+ if (!n) {
+ feep();
+ return;
+ }
+ if (n > 0) {
+ while(n-- && cs <= ll)
+ cs = findeol() + 1;
+ if (n != -1) {
+ cs = pos;
+ feep();
+ return;
+ }
+ cs--;
+ } else {
+ while(n++ && cs >= 0)
+ cs = findbol() - 1;
+ if (n != 1) {
+ cs = pos;
+ feep();
+ return;
+ }
+ cs++;
+ }
+ virangeflag = 2;
+}
+
+/**/
+void
+viaddnext(void)
+{
+ if (cs != findeol())
+ cs++;
+ startvitext(1);
+}
+
+/**/
+void
+viaddeol(void)
+{
+ cs = findeol();
+ startvitext(1);
+}
+
+/**/
+void
+viinsert(void)
+{
+ startvitext(1);
+}
+
+/**/
+void
+viinsertbol(void)
+{
+ vifirstnonblank();
+ startvitext(1);
+}
+
+/**/
+void
+videlete(void)
+{
+ int c2;
+
+ startvichange(1);
+ if ((c2 = getvirange(0)) != -1) {
+ forekill(c2 - cs, 0);
+ if (vilinerange && ll) {
+ if (cs == ll)
+ cs--;
+ foredel(1);
+ vifirstnonblank();
+ }
+ }
+ vichgflag = 0;
+}
+
+/**/
+void
+videletechar(void)
+{
+ int n = zmult;
+
+ startvichange(-1);
+ /* handle negative argument */
+ if (n < 0) {
+ zmult = -n;
+ vibackwarddeletechar();
+ zmult = n;
+ return;
+ }
+ /* it is an error to be on the end of line */
+ if (cs == ll || line[cs] == '\n') {
+ feep();
+ return;
+ }
+ /* 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() - cs)
+ n = findeol() - cs;
+ /* do the deletion */
+ forekill(n, 0);
+}
+
+/**/
+void
+vichange(void)
+{
+ int c2;
+
+ startvichange(1);
+ if ((c2 = getvirange(1)) != -1) {
+ forekill(c2 - cs, 0);
+ selectkeymap("main", 1);
+ viinsbegin = cs;
+ undoing = 0;
+ }
+}
+
+/**/
+void
+visubstitute(void)
+{
+ int n = zmult;
+
+ startvichange(1);
+ if (n < 0) {
+ feep();
+ return;
+ }
+ /* it is an error to be on the end of line */
+ if (cs == ll || line[cs] == '\n') {
+ feep();
+ return;
+ }
+ /* 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() - cs)
+ n = findeol() - cs;
+ /* do the substitution */
+ forekill(n, 0);
+ startvitext(1);
+}
+
+/**/
+void
+vichangeeol(void)
+{
+ forekill(findeol() - cs, 0);
+ startvitext(1);
+}
+
+/**/
+void
+vichangewholeline(void)
+{
+ vifirstnonblank();
+ vichangeeol();
+}
+
+/**/
+void
+viyank(void)
+{
+ int oldcs = cs, c2;
+
+ startvichange(1);
+ if ((c2 = getvirange(0)) != -1)
+ cut(cs, c2 - cs, 0);
+ vichgflag = 0;
+ cs = oldcs;
+}
+
+/**/
+void
+viyankeol(void)
+{
+ int x = findeol();
+
+ startvichange(-1);
+ if (x == cs) {
+ feep();
+ return;
+ }
+ cut(cs, x - cs, 0);
+}
+
+/**/
+void
+viyankwholeline(void)
+{
+ int bol = findbol(), oldcs = cs;
+ int n = zmult;
+
+ startvichange(-1);
+ if (n < 1)
+ return;
+ while(n--) {
+ if (cs > ll) {
+ feep();
+ cs = oldcs;
+ return;
+ }
+ cs = findeol() + 1;
+ }
+ vilinerange = 1;
+ cut(bol, cs - bol - 1, 0);
+ cs = oldcs;
+}
+
+/**/
+void
+vireplace(void)
+{
+ startvitext(0);
+}
+
+/* vi-replace-chars has some oddities relating to vi-repeat-change. In *
+ * the real vi, if one does 3r at the end of a line, it feeps without *
+ * reading the argument, and won't repeat the action. A successful rx *
+ * followed by 3. at the end of a line (or 3rx followed by . at the end *
+ * of a line) will obviously feep after the ., even though it has the *
+ * argument available. Here repeating is tied very closely to argument *
+ * reading, so some trickery is needed to emulate this. When repeating *
+ * a change, we always read the argument normally, even if the count *
+ * was bad. When recording a change for repeating, and a bad count is *
+ * given, we squash the repeat buffer to avoid repeating the partial *
+ * command; we've lost the previous change, but that can't be avoided *
+ * without a rewrite of the repeat code. */
+
+/**/
+void
+vireplacechars(void)
+{
+ int ch, n = zmult;
+
+ startvichange(1);
+ /* check argument range */
+ if (n < 1 || n + cs > findeol()) {
+ if(vichgrepeat) {
+ int ofeep = feepflag;
+ vigetkey();
+ feepflag = ofeep;
+ }
+ if(vichgflag) {
+ free(vichgbuf);
+ vichgbuf = NULL;
+ vichgflag = 0;
+ }
+ feep();
+ return;
+ }
+ /* get key */
+ if((ch = vigetkey()) == -1) {
+ vichgflag = 0;
+ feep();
+ return;
+ }
+ /* do change */
+ if (ch == '\r' || ch == '\n') {
+ /* <return> handled specially */
+ cs += n - 1;
+ backkill(n - 1, 0);
+ line[cs++] = '\n';
+ } else {
+ while (n--)
+ line[cs++] = ch;
+ cs--;
+ }
+ vichgflag = 0;
+}
+
+/**/
+void
+vicmdmode(void)
+{
+ if (invicmdmode() || selectkeymap("vicmd", 0))
+ feep();
+ undoing = 1;
+ vichgflag = 0;
+ if (cs != findbol())
+ cs--;
+}
+
+/**/
+void
+viopenlinebelow(void)
+{
+ cs = findeol();
+ spaceinline(1);
+ line[cs++] = '\n';
+ startvitext(1);
+}
+
+/**/
+void
+viopenlineabove(void)
+{
+ cs = findbol();
+ spaceinline(1);
+ line[cs] = '\n';
+ startvitext(1);
+}
+
+/**/
+void
+vioperswapcase(void)
+{
+ int oldcs, c2;
+
+ /* get the range */
+ startvichange(1);
+ if ((c2 = getvirange(0)) != -1) {
+ oldcs = cs;
+ /* swap the case of all letters within range */
+ while (cs < c2) {
+ if (islower(line[cs]))
+ line[cs] = tuupper(line[cs]);
+ else if (isupper(line[cs]))
+ line[cs] = tulower(line[cs]);
+ cs++;
+ }
+ /* go back to the first line of the range */
+ cs = oldcs;
+ vifirstnonblank();
+ }
+ vichgflag = 0;
+}
+
+/**/
+void
+virepeatchange(void)
+{
+ /* make sure we have a change to repeat */
+ if (!vichgbuf || vichgflag) {
+ feep();
+ return;
+ }
+ /* restore or update the saved count and buffer */
+ if (zmod.flags & MOD_MULT) {
+ lastmod.mult = zmod.mult;
+ lastmod.flags |= MOD_MULT;
+ }
+ if (zmod.flags & MOD_VIBUF) {
+ lastmod.vibuf = zmod.vibuf;
+ lastmod.flags = (lastmod.flags & ~MOD_VIAPP) |
+ MOD_VIBUF | (zmod.flags & MOD_VIAPP);
+ }
+ /* repeat the command */
+ inrepeat = 1;
+ ungetkeys(vichgbuf, vichgbufptr);
+}
+
+/**/
+void
+viindent(void)
+{
+ int oldcs = cs, c2;
+
+ /* get the range */
+ startvichange(1);
+ if ((c2 = getvirange(0)) == -1) {
+ vichgflag = 0;
+ return;
+ }
+ vichgflag = 0;
+ /* must be a line range */
+ if (!vilinerange) {
+ feep();
+ cs = oldcs;
+ return;
+ }
+ oldcs = cs;
+ /* add a tab to the beginning of each line within range */
+ while (cs < c2) {
+ spaceinline(1);
+ line[cs] = '\t';
+ cs = findeol() + 1;
+ }
+ /* go back to the first line of the range */
+ cs = oldcs;
+ vifirstnonblank();
+}
+
+/**/
+void
+viunindent(void)
+{
+ int oldcs = cs, c2;
+
+ /* get the range */
+ startvichange(1);
+ if ((c2 = getvirange(0)) == -1) {
+ vichgflag = 0;
+ return;
+ }
+ vichgflag = 0;
+ /* must be a line range */
+ if (!vilinerange) {
+ feep();
+ cs = oldcs;
+ return;
+ }
+ oldcs = cs;
+ /* remove a tab from the beginning of each line within range */
+ while (cs < c2) {
+ if (line[cs] == '\t')
+ foredel(1);
+ cs = findeol() + 1;
+ }
+ /* go back to the first line of the range */
+ cs = oldcs;
+ vifirstnonblank();
+}
+
+/**/
+void
+vibackwarddeletechar(void)
+{
+ int n = zmult;
+
+ if (invicmdmode())
+ startvichange(-1);
+ /* handle negative argument */
+ if (n < 0) {
+ zmult = -n;
+ videletechar();
+ zmult = n;
+ return;
+ }
+ /* It is an error to be at the beginning of the line, or (in *
+ * insert mode) to delete past the beginning of insertion. */
+ if ((!invicmdmode() && cs - n < viinsbegin) || cs == findbol()) {
+ feep();
+ return;
+ }
+ /* Put argument into the acceptable range -- it is not an error to *
+ * specify a greater count than the number of available characters. */
+ if (n > cs - findbol())
+ n = cs - findbol();
+ /* do the deletion */
+ backkill(n, 1);
+}
+
+/**/
+void
+vikillline(void)
+{
+ if (viinsbegin > cs) {
+ feep();
+ return;
+ }
+ backdel(cs - viinsbegin);
+}
+
+/**/
+void
+viputbefore(void)
+{
+ Cutbuffer buf = &cutbuf;
+ int n = zmult;
+
+ startvichange(-1);
+ if (n < 0)
+ return;
+ if (zmod.flags & MOD_VIBUF)
+ buf = &vibuf[zmod.vibuf];
+ if (!buf->buf) {
+ feep();
+ return;
+ }
+ if(buf->flags & CUTBUFFER_LINE) {
+ cs = findbol();
+ spaceinline(buf->len + 1);
+ memcpy((char *)line + cs, buf->buf, buf->len);
+ line[cs + buf->len] = '\n';
+ vifirstnonblank();
+ } else {
+ while (n--) {
+ spaceinline(buf->len);
+ memcpy((char *)line + cs, buf->buf, buf->len);
+ cs += buf->len;
+ }
+ if (cs)
+ cs--;
+ }
+}
+
+/**/
+void
+viputafter(void)
+{
+ Cutbuffer buf = &cutbuf;
+ int n = zmult;
+
+ startvichange(-1);
+ if (n < 0)
+ return;
+ if (zmod.flags & MOD_VIBUF)
+ buf = &vibuf[zmod.vibuf];
+ if (!buf->buf) {
+ feep();
+ return;
+ }
+ if(buf->flags & CUTBUFFER_LINE) {
+ cs = findeol();
+ spaceinline(buf->len + 1);
+ line[cs++] = '\n';
+ memcpy((char *)line + cs, buf->buf, buf->len);
+ vifirstnonblank();
+ } else {
+ if (cs != findeol())
+ cs++;
+ while (n--) {
+ spaceinline(buf->len);
+ memcpy((char *)line + cs, buf->buf, buf->len);
+ cs += buf->len;
+ }
+ if (cs)
+ cs--;
+ }
+
+}
+
+/**/
+void
+vijoin(void)
+{
+ int x;
+
+ startvichange(-1);
+ if ((x = findeol()) == ll) {
+ feep();
+ return;
+ }
+ cs = x + 1;
+ for (x = 1; cs != ll && iblank(line[cs]); cs++, x++);
+ backdel(x);
+ if (cs && iblank(line[cs-1]))
+ cs--;
+ else {
+ spaceinline(1);
+ line[cs] = ' ';
+ }
+}
+
+/**/
+void
+viswapcase(void)
+{
+ int eol, n = zmult;
+
+ startvichange(-1);
+ if (n < 1)
+ return;
+ eol = findeol();
+ while (cs < eol && n--) {
+ if (islower(line[cs]))
+ line[cs] = tuupper(line[cs]);
+ else if (isupper(line[cs]))
+ line[cs] = tulower(line[cs]);
+ cs++;
+ }
+ if (cs && cs == eol)
+ cs--;
+}
+
+/**/
+void
+vicapslockpanic(void)
+{
+ beep();
+ statusline = "press a lowercase key to continue";
+ statusll = strlen(statusline);
+ refresh();
+ while (!islower(getkey(0)));
+ statusline = NULL;
+}
+
+/**/
+void
+visetbuffer(void)
+{
+ int ch;
+
+ if ((zmod.flags & MOD_VIBUF) ||
+ (((ch = getkey(0)) < '1' || ch > '9') &&
+ (ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z'))) {
+ feep();
+ return;
+ }
+ if (ch >= 'A' && ch <= 'Z') /* needed in cut() */
+ zmod.flags |= MOD_VIAPP;
+ else
+ zmod.flags &= ~MOD_VIAPP;
+ zmod.vibuf = tulower(ch) + (idigit(ch) ? -'1' + 26 : -'a');
+ zmod.flags |= MOD_VIBUF;
+ prefixflag = 1;
+}
+
+/**/
+void
+vikilleol(void)
+{
+ int n = findeol() - cs;
+
+ startvichange(-1);
+ if (!n) {
+ /* error -- line already empty */
+ feep();
+ return;
+ }
+ /* delete to end of line */
+ forekill(findeol() - cs, 0);
+}
+
+/**/
+void
+vipoundinsert(void)
+{
+ int oldcs = cs;
+
+ startvichange(-1);
+ vifirstnonblank();
+ if(line[cs] != '#') {
+ spaceinline(1);
+ line[cs] = '#';
+ if(cs <= viinsbegin)
+ viinsbegin++;
+ cs = oldcs + (cs <= oldcs);
+ } else {
+ foredel(1);
+ if (cs < viinsbegin)
+ viinsbegin--;
+ cs = oldcs - (cs < oldcs);
+ }
+}
+
+/**/
+void
+viquotedinsert(void)
+{
+#ifndef HAS_TIO
+ struct sgttyb sob;
+#endif
+
+ spaceinline(1);
+ line[cs] = '^';
+ refresh();
+#ifndef HAS_TIO
+ sob = shttyinfo.sgttyb;
+ sob.sg_flags = (sob.sg_flags | RAW) & ~ECHO;
+ ioctl(SHTTY, TIOCSETN, &sob);
+#endif
+ c = getkey(0);
+#ifndef HAS_TIO
+ setterm();
+#endif
+ foredel(1);
+ if(c < 0)
+ feep();
+ else
+ selfinsert();
+}
+
+/* the 0 key in vi: continue a repeat count in the manner of *
+ * digit-argument if possible, otherwise do vi-beginning-of-line. */
+
+/**/
+void
+vidigitorbeginningofline(void)
+{
+ if(zmod.flags & MOD_TMULT)
+ digitargument();
+ else {
+ removesuffix();
+ invalidatelist();
+ vibeginningofline();
+ }
+}
diff --git a/Src/Zle/zle_widget.sed b/Src/Zle/zle_widget.sed
new file mode 100644
index 000000000..635322b42
--- /dev/null
+++ b/Src/Zle/zle_widget.sed
@@ -0,0 +1,7 @@
+/^ *W(/{
+ s/[^,]*, *t_/ wi_/
+ s/ *,.*/,/
+ P
+ s/ wi_\(.*\),/#define w_\1 (\&widgets[wi_\1])/
+ P
+}
diff --git a/Src/Zle/zle_word.c b/Src/Zle/zle_word.c
new file mode 100644
index 000000000..923216ef8
--- /dev/null
+++ b/Src/Zle/zle_word.c
@@ -0,0 +1,477 @@
+/*
+ * zle_word.c - word-related editor functions
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * 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 Paul Falstad 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 Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad 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 Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "zle.mdh"
+#include "zle_word.pro"
+
+/**/
+void
+forwardword(void)
+{
+ int n = zmult;
+
+ if (n < 0) {
+ zmult = -n;
+ backwardword();
+ zmult = n;
+ return;
+ }
+ while (n--) {
+ while (cs != ll && iword(line[cs]))
+ cs++;
+ if (wordflag && !n)
+ return;
+ while (cs != ll && !iword(line[cs]))
+ cs++;
+ }
+}
+
+/**/
+void
+viforwardword(void)
+{
+ int n = zmult;
+
+ if (n < 0) {
+ zmult = -n;
+ backwardword();
+ zmult = n;
+ return;
+ }
+ while (n--) {
+ if (iident(line[cs]))
+ while (cs != ll && iident(line[cs]))
+ cs++;
+ else
+ while (cs != ll && !iident(line[cs]) && !iblank(line[cs]))
+ cs++;
+ if (wordflag && !n)
+ return;
+ while (cs != ll && iblank(line[cs]))
+ cs++;
+ }
+}
+
+/**/
+void
+viforwardblankword(void)
+{
+ int n = zmult;
+
+ if (n < 0) {
+ zmult = -n;
+ vibackwardblankword();
+ zmult = n;
+ return;
+ }
+ while (n--) {
+ while (cs != ll && !iblank(line[cs]))
+ cs++;
+ if (wordflag && !n)
+ return;
+ while (cs != ll && iblank(line[cs]))
+ cs++;
+ }
+}
+
+/**/
+void
+emacsforwardword(void)
+{
+ int n = zmult;
+
+ if (n < 0) {
+ zmult = -n;
+ emacsbackwardword();
+ zmult = n;
+ return;
+ }
+ while (n--) {
+ while (cs != ll && !iword(line[cs]))
+ cs++;
+ if (wordflag && !n)
+ return;
+ while (cs != ll && iword(line[cs]))
+ cs++;
+ }
+}
+
+/**/
+void
+viforwardblankwordend(void)
+{
+ int n = zmult;
+
+ if (n < 0)
+ return;
+ while (n--) {
+ while (cs != ll && iblank(line[cs + 1]))
+ cs++;
+ while (cs != ll && !iblank(line[cs + 1]))
+ cs++;
+ }
+ if (cs != ll && virangeflag)
+ cs++;
+}
+
+/**/
+void
+viforwardwordend(void)
+{
+ int n = zmult;
+
+ if (n < 0) {
+ zmult = -n;
+ backwardword();
+ zmult = n;
+ return;
+ }
+ while (n--) {
+ if (iblank(line[cs + 1]))
+ while (cs != ll && iblank(line[cs + 1]))
+ cs++;
+ if (iident(line[cs + 1]))
+ while (cs != ll && iident(line[cs + 1]))
+ cs++;
+ else
+ while (cs != ll && !iident(line[cs + 1]) && !iblank(line[cs + 1]))
+ cs++;
+ }
+ if (cs != ll && virangeflag)
+ cs++;
+}
+
+/**/
+void
+backwardword(void)
+{
+ int n = zmult;
+
+ if (n < 0) {
+ zmult = -n;
+ forwardword();
+ zmult = n;
+ return;
+ }
+ while (n--) {
+ while (cs && !iword(line[cs - 1]))
+ cs--;
+ while (cs && iword(line[cs - 1]))
+ cs--;
+ }
+}
+
+/**/
+void
+vibackwardword(void)
+{
+ int n = zmult;
+
+ if (n < 0) {
+ zmult = -n;
+ backwardword();
+ zmult = n;
+ return;
+ }
+ while (n--) {
+ while (cs && iblank(line[cs - 1]))
+ cs--;
+ if (iident(line[cs - 1]))
+ while (cs && iident(line[cs - 1]))
+ cs--;
+ else
+ while (cs && !iident(line[cs - 1]) && !iblank(line[cs - 1]))
+ cs--;
+ }
+}
+
+/**/
+void
+vibackwardblankword(void)
+{
+ int n = zmult;
+
+ if (n < 0) {
+ zmult = -n;
+ viforwardblankword();
+ zmult = n;
+ return;
+ }
+ while (n--) {
+ while (cs && iblank(line[cs - 1]))
+ cs--;
+ while (cs && !iblank(line[cs - 1]))
+ cs--;
+ }
+}
+
+/**/
+void
+emacsbackwardword(void)
+{
+ int n = zmult;
+
+ if (n < 0) {
+ zmult = -n;
+ emacsforwardword();
+ zmult = n;
+ return;
+ }
+ while (n--) {
+ while (cs && !iword(line[cs - 1]))
+ cs--;
+ while (cs && iword(line[cs - 1]))
+ cs--;
+ }
+}
+
+/**/
+void
+backwarddeleteword(void)
+{
+ int x = cs, n = zmult;
+
+ if (n < 0) {
+ zmult = -n;
+ deleteword();
+ zmult = n;
+ return;
+ }
+ while (n--) {
+ while (x && !iword(line[x - 1]))
+ x--;
+ while (x && iword(line[x - 1]))
+ x--;
+ }
+ backdel(cs - x);
+}
+
+/**/
+void
+vibackwardkillword(void)
+{