about summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
authorPaul Ackersviller <packersv@users.sourceforge.net>2007-06-19 03:16:36 +0000
committerPaul Ackersviller <packersv@users.sourceforge.net>2007-06-19 03:16:36 +0000
commit8c1240bff454dba4737ab990899c3a92758d3d4b (patch)
tree1a13d76c1d04b292dbdc2fc07e9eace0e5c363db /Src
parent8cbf1adc64afc2c51dcfceaeb069bf916900c49f (diff)
downloadzsh-8c1240bff454dba4737ab990899c3a92758d3d4b.tar.gz
zsh-8c1240bff454dba4737ab990899c3a92758d3d4b.tar.xz
zsh-8c1240bff454dba4737ab990899c3a92758d3d4b.zip
Merge of 21770, 21760: fix test for sequence prefixes in the local keymap in getkeymapcmd().
Diffstat (limited to 'Src')
-rw-r--r--Src/Zle/zle_keymap.c276
1 files changed, 208 insertions, 68 deletions
diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
index 28203655e..0077ee1a6 100644
--- a/Src/Zle/zle_keymap.c
+++ b/Src/Zle/zle_keymap.c
@@ -86,6 +86,16 @@ struct bindstate {
     char *lastseq;
     Thingy bind;
     char *str;
+    char *prefix;
+    int prefixlen;
+};
+
+/* This structure is used when scanning for prefix bindings to remove */
+
+struct remprefstate {
+    Keymap km;
+    char *prefix;
+    int prefixlen;
 };
 
 #define BS_LIST (1<<0)
@@ -146,7 +156,7 @@ createkeymapnamtab(void)
 static KeymapName
 makekeymapnamnode(Keymap keymap)
 {
-    KeymapName kmn = (KeymapName) zcalloc(sizeof(*kmn));
+    KeymapName kmn = (KeymapName) zshcalloc(sizeof(*kmn));
 
     kmn->keymap = keymap;
     return kmn;
@@ -159,8 +169,7 @@ freekeymapnamnode(HashNode hn)
     KeymapName kmn = (KeymapName) hn;
 
     zsfree(kmn->nam);
-    if(!--kmn->keymap->rc)
-	deletekeymap(kmn->keymap);
+    unrefkeymap(kmn->keymap);
     zfree(kmn, sizeof(*kmn));
 }
 
@@ -191,7 +200,7 @@ newkeytab(char *kmname)
 static Key
 makekeynode(Thingy t, char *str)
 {
-    Key k = (Key) zcalloc(sizeof(*k));
+    Key k = (Key) zshcalloc(sizeof(*k));
 
     k->bind = t;
     k->str = str;
@@ -220,7 +229,7 @@ static HashTable copyto;
 mod_export Keymap
 newkeymap(Keymap tocopy, char *kmname)
 {
-    Keymap km = zcalloc(sizeof(*km));
+    Keymap km = zshcalloc(sizeof(*km));
     int i;
 
     km->rc = 0;
@@ -239,7 +248,7 @@ newkeymap(Keymap tocopy, char *kmname)
 
 /**/
 static void
-scancopykeys(HashNode hn, int flags)
+scancopykeys(HashNode hn, UNUSED(int flags))
 {
     Key k = (Key) hn;
     Key kn = zalloc(sizeof(*k));
@@ -292,7 +301,7 @@ scankeymap(Keymap km, int sort, KeyScanFunc func, void *magic)
 
 /**/
 static void
-scankeys(HashNode hn, int flags)
+scankeys(HashNode hn, UNUSED(int flags))
 {
     Key k = (Key) hn;
     int f = k->nam[0] == Meta ? STOUC(k->nam[1])^32 : STOUC(k->nam[0]);
@@ -345,8 +354,7 @@ linkkeymap(Keymap km, char *name, int imm)
 	    return 1;
 	if(n->keymap == km)
 	    return 0;
-	if(!--n->keymap->rc)
-	    deletekeymap(n->keymap);
+	unrefkeymap(n->keymap);
 	n->keymap = km;
     } else {
 	n = makekeymapnamnode(km);
@@ -354,10 +362,23 @@ linkkeymap(Keymap km, char *name, int imm)
 	    n->flags |= KMN_IMMORTAL;
 	keymapnamtab->addnode(keymapnamtab, ztrdup(name), n);
     }
-    km->rc++;
+    refkeymap(km);
     return 0;
 }
 
+/**/
+void refkeymap(Keymap km)
+{
+    km->rc++;
+}
+
+/**/
+void unrefkeymap(Keymap km)
+{
+    if (!--km->rc)
+	deletekeymap(km);
+}
+
 /* Select a keymap as the current ZLE keymap.  Can optionally fall back *
  * on the guaranteed safe keymap if it fails.                           */
 
@@ -378,7 +399,10 @@ selectkeymap(char *name, int fb)
 	    return 1;
 	km = openkeymap(name = ".safe");
     }
-    curkeymapname = name;
+    if(name != curkeymapname) {
+	zsfree(curkeymapname);
+	curkeymapname = ztrdup(name);
+    }
     curkeymap = km;
     return 0;
 }
@@ -591,12 +615,12 @@ keyisprefix(Keymap km, char *seq)
 
 /**/
 int
-bin_bindkey(char *name, char **argv, char *ops, int func)
+bin_bindkey(char *name, char **argv, Options ops, UNUSED(int func))
 {
     static struct opn {
 	char o;
 	char selp;
-	int (*func) _((char *, char *, Keymap, char **, char *, char));
+	int (*func) _((char *, char *, Keymap, char **, Options, char));
 	int min, max;
     } const opns[] = {
 	{ 'l', 0, bin_bindkey_lsmaps, 0,  0 },
@@ -615,15 +639,16 @@ bin_bindkey(char *name, char **argv, char *ops, int func)
     int n;
 
     /* select operation and ensure no clashing arguments */
-    for(op = opns; op->o && !ops[STOUC(op->o)]; op++) ;
+    for(op = opns; op->o && !OPT_ISSET(ops,STOUC(op->o)); op++) ;
     if(op->o)
 	for(opp = op; (++opp)->o; )
-	    if(ops[STOUC(opp->o)]) {
+	    if(OPT_ISSET(ops,STOUC(opp->o))) {
 		zwarnnam(name, "incompatible operation selection options",
 		    NULL, 0);
 		return 1;
 	    }
-    n = ops['e'] + ops['v'] + ops['a'] + ops['M'];
+    n = OPT_ISSET(ops,'e') + OPT_ISSET(ops,'v') + 
+	OPT_ISSET(ops,'a') + OPT_ISSET(ops,'M');
     if(!op->selp && n) {
 	zwarnnam(name, "keymap cannot be selected with -%c", NULL, op->o);
 	return 1;
@@ -635,18 +660,14 @@ bin_bindkey(char *name, char **argv, char *ops, int func)
 
     /* keymap selection */
     if(op->selp) {
-	if(ops['e'])
+	if(OPT_ISSET(ops,'e'))
 	    kmname = "emacs";
-	else if(ops['v'])
+	else if(OPT_ISSET(ops,'v'))
 	    kmname = "viins";
-	else if(ops['a'])
+	else if(OPT_ISSET(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 if(OPT_ISSET(ops,'M')) {
+	    kmname = OPT_ARG(ops,'M');
 	} else
 	    kmname = "main";
 	km = openkeymap(kmname);
@@ -654,7 +675,7 @@ bin_bindkey(char *name, char **argv, char *ops, int func)
 	    zwarnnam(name, "no such keymap `%s'", kmname, 0);
 	    return 1;
 	}
-	if(ops['e'] || ops['v'])
+	if(OPT_ISSET(ops,'e') || OPT_ISSET(ops,'v'))
 	    linkkeymap(km, "main", 0);
     } else {
 	kmname = NULL;
@@ -663,7 +684,7 @@ bin_bindkey(char *name, char **argv, char *ops, int func)
 
     /* listing is a special case */
     if(!op->o && (!argv[0] || !argv[1])) {
-	if(ops['e'] || ops['v'])
+	if(OPT_ISSET(ops,'e') || OPT_ISSET(ops,'v'))
 	    return 0;
 	return bin_bindkey_list(name, kmname, km, argv, ops, op->o);
     }
@@ -686,9 +707,9 @@ bin_bindkey(char *name, char **argv, char *ops, int func)
 
 /**/
 static int
-bin_bindkey_lsmaps(char *name, char *kmname, Keymap km, char **argv, char *ops, char func)
+bin_bindkey_lsmaps(UNUSED(char *name), UNUSED(char *kmname), UNUSED(Keymap km), UNUSED(char **argv), Options ops, UNUSED(char func))
 {
-    scanhashtable(keymapnamtab, 1, 0, 0, scanlistmaps, ops['L']);
+    scanhashtable(keymapnamtab, 1, 0, 0, scanlistmaps, OPT_ISSET(ops,'L'));
     return 0;
 }
 
@@ -712,7 +733,7 @@ scanlistmaps(HashNode hn, int list)
 
 /**/
 static int
-bin_bindkey_delall(char *name, char *kmname, Keymap km, char **argv, char *ops, char func)
+bin_bindkey_delall(UNUSED(char *name), UNUSED(char *kmname), UNUSED(Keymap km), UNUSED(char **argv), UNUSED(Options ops), UNUSED(char func))
 {
     keymapnamtab->emptytable(keymapnamtab);
     default_bindings();
@@ -723,7 +744,7 @@ bin_bindkey_delall(char *name, char *kmname, Keymap km, char **argv, char *ops,
 
 /**/
 static int
-bin_bindkey_del(char *name, char *kmname, Keymap km, char **argv, char *ops, char func)
+bin_bindkey_del(char *name, UNUSED(char *kmname), UNUSED(Keymap km), char **argv, UNUSED(Options ops), UNUSED(char func))
 {
     int ret = 0;
 
@@ -742,7 +763,7 @@ bin_bindkey_del(char *name, char *kmname, Keymap km, char **argv, char *ops, cha
 
 /**/
 static int
-bin_bindkey_link(char *name, char *kmname, Keymap km, char **argv, char *ops, char func)
+bin_bindkey_link(char *name, UNUSED(char *kmname), Keymap km, char **argv, UNUSED(Options ops), UNUSED(char func))
 {
     km = openkeymap(argv[0]);
     if(!km) {
@@ -759,7 +780,7 @@ bin_bindkey_link(char *name, char *kmname, Keymap km, char **argv, char *ops, ch
 
 /**/
 static int
-bin_bindkey_new(char *name, char *kmname, Keymap km, char **argv, char *ops, char func)
+bin_bindkey_new(char *name, UNUSED(char *kmname), Keymap km, char **argv, UNUSED(Options ops), UNUSED(char func))
 {
     KeymapName kmn = (KeymapName) keymapnamtab->getnode(keymapnamtab, argv[0]);
 
@@ -787,7 +808,7 @@ bin_bindkey_new(char *name, char *kmname, Keymap km, char **argv, char *ops, cha
 
 /**/
 static int
-bin_bindkey_meta(char *name, char *kmname, Keymap km, char **argv, char *ops, char func)
+bin_bindkey_meta(char *name, char *kmname, Keymap km, UNUSED(char **argv), UNUSED(Options ops), UNUSED(char func))
 {
     char m[3], *str;
     int i;
@@ -817,7 +838,7 @@ bin_bindkey_meta(char *name, char *kmname, Keymap km, char **argv, char *ops, ch
 
 /**/
 static int
-bin_bindkey_bind(char *name, char *kmname, Keymap km, char **argv, char *ops, char func)
+bin_bindkey_bind(char *name, char *kmname, Keymap km, char **argv, Options ops, char func)
 {
     int ret = 0;
 
@@ -834,6 +855,19 @@ bin_bindkey_bind(char *name, char *kmname, Keymap km, char **argv, char *ops, ch
 	zwarnnam(name, "keymap `%s' is protected", kmname, 0);
 	return 1;
     }
+    if (func == 'r' && OPT_ISSET(ops,'p')) {
+	char *useq, *bseq;
+	int len;
+	struct remprefstate rps;
+	rps.km = km;
+	while ((useq = *argv++)) {
+	    bseq = getkeystring(useq, &len, 2, NULL);
+	    rps.prefix = metafy(bseq, len, META_USEHEAP);
+	    rps.prefixlen = strlen(rps.prefix);
+	    scankeymap(km, 0, scanremoveprefix, &rps);
+	}
+	return 0;
+    }
     do {
 	char *useq = *argv, *bseq, *seq, *str;
 	int len;
@@ -852,7 +886,7 @@ bin_bindkey_bind(char *name, char *kmname, Keymap km, char **argv, char *ops, ch
 	}
 	bseq = getkeystring(useq, &len, 2, NULL);
 	seq = metafy(bseq, len, META_USEHEAP);
-	if(ops['R']) {
+	if(OPT_ISSET(ops,'R')) {
 	    int first, last;
 	    char m[3];
 
@@ -878,19 +912,33 @@ bin_bindkey_bind(char *name, char *kmname, Keymap km, char **argv, char *ops, ch
     return ret;
 }
 
+/* Remove bindings for key sequences which have the given (proper) prefix. */
+
+/**/
+static void
+scanremoveprefix(char *seq, UNUSED(Thingy bind), UNUSED(char *str), void *magic)
+{
+    struct remprefstate *rps = magic;
+
+    if (strncmp(seq, rps->prefix, rps->prefixlen) || !seq[rps->prefixlen])
+	return;
+
+    bindkey(rps->km, seq, refthingy(t_undefinedkey), NULL);
+}
+
 /* 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)
+bin_bindkey_list(char *name, char *kmname, Keymap km, char **argv, Options ops, UNUSED(char func))
 {
     struct bindstate bs;
 
-    bs.flags = ops['L'] ? BS_LIST : 0;
+    bs.flags = OPT_ISSET(ops,'L') ? BS_LIST : 0;
     bs.kmname = kmname;
-    if(argv[0]) {
+    if(argv[0] && !OPT_ISSET(ops,'p')) {
 	int len;
 	char *seq;
 
@@ -899,8 +947,23 @@ bin_bindkey_list(char *name, char *kmname, Keymap km, char **argv, char *ops, ch
 	bs.flags |= BS_ALL;
 	bs.firstseq = bs.lastseq = seq;
 	bs.bind = keybind(km, seq, &bs.str);
+	bs.prefix = NULL;
+	bs.prefixlen = 0;
 	bindlistout(&bs);
     } else {
+	/* empty prefix is equivalent to no prefix */
+	if (OPT_ISSET(ops,'p') && (!argv[0] || argv[0][0])) {
+	    if (!argv[0]) {
+		zwarnnam(name, "option -p requires a prefix string", NULL, 0);
+		return 1;
+	    }
+	    bs.prefix = getkeystring(argv[0], &bs.prefixlen, 2, NULL);
+	    bs.prefix = metafy(bs.prefix, bs.prefixlen, META_HREALLOC);
+	    bs.prefixlen = strlen(bs.prefix);
+	} else {
+	    bs.prefix = NULL;
+	    bs.prefixlen = 0;
+	}
 	bs.firstseq = ztrdup("");
 	bs.lastseq = ztrdup("");
 	bs.bind = t_undefinedkey;
@@ -919,6 +982,10 @@ scanbindlist(char *seq, Thingy bind, char *str, void *magic)
 {
     struct bindstate *bs = magic;
 
+    if (bs->prefixlen &&
+	(strncmp(seq, bs->prefix, bs->prefixlen) || !seq[bs->prefixlen]))
+	return;
+
     if(bind == bs->bind && (bind || !strcmp(str, bs->str)) &&
        ztrlen(seq) == 1 && ztrlen(bs->lastseq) == 1) {
 	int l = bs->lastseq[1] ?
@@ -1011,6 +1078,66 @@ cleanup_keymaps(void)
     zfree(keybuf, keybufsz);
 }
 
+static char *cursorptr;
+
+/* utility function for termcap output routine to add to string */
+
+static int 
+add_cursor_char(int c)
+{
+    *cursorptr++ = c;
+    return 0;
+}
+
+/* interrogate termcap for cursor keys and add bindings to keymap */
+
+/**/
+static void
+add_cursor_key(Keymap km, int tccode, Thingy thingy, int defchar)
+{
+    char buf[2048];
+    int ok = 0;
+
+    /*
+     * Be careful not to try too hard with bindings for dubious or
+     * dysfunctional terminals.
+     */
+    if (tccan(tccode) && !(termflags & (TERM_NOUP|TERM_BAD|TERM_UNKNOWN))) {
+	/*
+	 * We can use the real termcap sequence.  We need to
+	 * persuade termcap to output `move cursor 1 char' and capture it.
+	 */
+	cursorptr = buf;
+	tputs(tcstr[tccode], 1, add_cursor_char);
+	*cursorptr = '\0';
+
+	/*
+	 * Sanity checking.  If the cursor key is zero-length (unlikely,
+	 * but this is termcap we're talking about), or it's a single
+	 * character, then we don't bind it.
+	 */
+	if (buf[0] && buf[1] && (buf[0] != Meta || buf[2]))
+	    ok = 1;
+    }
+    if (!ok) {
+	/* Assume the normal VT100-like values. */
+	sprintf(buf, "\33[%c", defchar);
+    }
+    bindkey(km, buf, refthingy(thingy), NULL);
+
+    /*
+     * If the string looked like \e[? or \eO?, bind the other one, too.
+     * This is necessary to make cursor keys work on many xterms with
+     * both normal and application modes.
+     */
+    if (buf[0] == '\33' && (buf[1] == '[' || buf[1] == 'O') && 
+	buf[2] && !buf[3])
+    {
+	buf[1] = (buf[1] == '[') ? 'O' : '[';
+	bindkey(km, buf, refthingy(thingy), NULL);
+    }
+}
+
 /* 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.    */
@@ -1023,6 +1150,7 @@ default_bindings(void)
     Keymap emap = newkeymap(NULL, "emacs");
     Keymap amap = newkeymap(NULL, "vicmd");
     Keymap smap = newkeymap(NULL, ".safe");
+    Keymap vimaps[2], kptr;
     char buf[3], *ed;
     int i;
 
@@ -1066,25 +1194,22 @@ default_bindings(void)
     /* 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);
+    vimaps[0] = vmap;
+    vimaps[1] = amap;
+    for (i = 0; i < 2; i++) {
+	kptr = vimaps[i];
+	/* vi command and insert modes: arrow keys */
+	add_cursor_key(kptr, TCUPCURSOR, t_uplineorhistory, 'A');
+	add_cursor_key(kptr, TCDOWNCURSOR, t_downlineorhistory, 'B');
+	add_cursor_key(kptr, TCLEFTCURSOR, t_vibackwardchar, 'D');
+	add_cursor_key(kptr, TCRIGHTCURSOR, t_viforwardchar, 'C');
+    }
+
+    /* emacs mode: arrow keys */ 
+    add_cursor_key(emap, TCUPCURSOR, t_uplineorhistory, 'A');
+    add_cursor_key(emap, TCDOWNCURSOR, t_downlineorhistory, 'B');
+    add_cursor_key(emap, TCLEFTCURSOR, t_backwardchar, 'D');
+    add_cursor_key(emap, TCRIGHTCURSOR, t_forwardchar, 'C');
    
     /* emacs mode: ^X sequences */
     bindkey(emap, "\30*",   refthingy(t_expandword), NULL);
@@ -1143,32 +1268,37 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp)
 {
     Thingy func = t_undefinedkey;
     char *str = NULL;
-    int lastlen = 0, lastc = c;
+    int lastlen = 0, lastc = lastchar;
 
     keybuflen = 0;
     keybuf[0] = 0;
-    while((c = getkeybuf(!!lastlen)) != EOF) {
+    while((lastchar = getkeybuf(!!lastlen)) != EOF) {
 	char *s;
 	Thingy f;
-	int loc = 1;
+	int loc = !!localkeymap;
+	int ispfx = 0;
 
-	if (!localkeymap ||
-	    (f = keybind(localkeymap, keybuf, &s)) == t_undefinedkey)
-	    loc = 0, f = keybind(km, keybuf, &s);
+	if (loc) {
+	    loc = ((f = keybind(localkeymap, keybuf, &s)) != t_undefinedkey);
+	    ispfx = keyisprefix(localkeymap, keybuf);
+	}
+	if (!loc)
+	    f = keybind(km, keybuf, &s);
+	ispfx |= keyisprefix(km, keybuf);
 
-	if(f != t_undefinedkey) {
+	if (f != t_undefinedkey) {
 	    lastlen = keybuflen;
 	    func = f;
 	    str = s;
-	    lastc = c;
+	    lastc = lastchar;
 	}
-	if(!keyisprefix((loc ? localkeymap : km), keybuf))
+	if (!ispfx)
 	    break;
     }
     if(!lastlen && keybuflen)
 	lastlen = keybuflen;
     else
-	c = lastc;
+	lastchar = lastc;
     if(lastlen != keybuflen) {
 	unmetafy(keybuf + lastlen, &keybuflen);
 	ungetkeys(keybuf+lastlen, keybuflen);
@@ -1252,3 +1382,13 @@ getkeycmd(void)
 	func = lastnamed;
     return func;
 }
+
+/**/
+mod_export void
+zlesetkeymap(int mode)
+{
+    Keymap km = openkeymap((mode == VIMODE) ? "viins" : "emacs");
+    if (!km)
+	return;
+    linkkeymap(km, "main", 0);
+}