From 8c1240bff454dba4737ab990899c3a92758d3d4b Mon Sep 17 00:00:00 2001 From: Paul Ackersviller Date: Tue, 19 Jun 2007 03:16:36 +0000 Subject: Merge of 21770, 21760: fix test for sequence prefixes in the local keymap in getkeymapcmd(). --- Src/Zle/zle_keymap.c | 276 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 208 insertions(+), 68 deletions(-) (limited to 'Src/Zle') 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); +} -- cgit 1.4.1