159243Sobrien/*
259243Sobrien * tc.bind.c: Key binding functions
359243Sobrien */
459243Sobrien/*-
559243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California.
659243Sobrien * All rights reserved.
759243Sobrien *
859243Sobrien * Redistribution and use in source and binary forms, with or without
959243Sobrien * modification, are permitted provided that the following conditions
1059243Sobrien * are met:
1159243Sobrien * 1. Redistributions of source code must retain the above copyright
1259243Sobrien *    notice, this list of conditions and the following disclaimer.
1359243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1459243Sobrien *    notice, this list of conditions and the following disclaimer in the
1559243Sobrien *    documentation and/or other materials provided with the distribution.
16100616Smp * 3. Neither the name of the University nor the names of its contributors
1759243Sobrien *    may be used to endorse or promote products derived from this software
1859243Sobrien *    without specific prior written permission.
1959243Sobrien *
2059243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2159243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2259243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2359243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2459243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2559243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2659243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2759243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2859243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2959243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3059243Sobrien * SUCH DAMAGE.
3159243Sobrien */
3259243Sobrien#include "sh.h"
3359243Sobrien#include "ed.h"
3459243Sobrien#include "ed.defns.h"
3559243Sobrien
36167465Smpstatic	void   printkey		(const KEYCMD *, CStr *);
37167465Smpstatic	KEYCMD parsecmd		(Char *);
38167465Smpstatic  void   bad_spec		(const Char *);
39167465Smpstatic	CStr  *parsestring	(const Char *, CStr *);
40167465Smpstatic	CStr  *parsebind	(const Char *, CStr *);
41167465Smpstatic	void   print_all_keys	(void);
42167465Smpstatic	void   printkeys	(KEYCMD *, int, int);
43167465Smpstatic	void   bindkey_usage	(void);
44167465Smpstatic	void   list_functions	(void);
4559243Sobrien
4659243Sobrienextern int MapsAreInited;
4759243Sobrien
4859243Sobrien
4959243Sobrien
5059243Sobrien
5159243Sobrien/*ARGSUSED*/
5259243Sobrienvoid
53167465Smpdobindkey(Char **v, struct command *c)
5459243Sobrien{
5559243Sobrien    KEYCMD *map;
56145479Smp    int     ntype, no, removeb, key, bindk;
5759243Sobrien    Char   *par;
5859243Sobrien    Char    p;
5959243Sobrien    KEYCMD  cmd;
6059243Sobrien    CStr    in;
6159243Sobrien    CStr    out;
6259243Sobrien    uChar   ch;
6359243Sobrien
6459243Sobrien    USE(c);
6559243Sobrien    if (!MapsAreInited)
6659243Sobrien	ed_InitMaps();
6759243Sobrien
6859243Sobrien    map = CcKeyMap;
6959243Sobrien    ntype = XK_CMD;
70145479Smp    key = removeb = bindk = 0;
7159243Sobrien    for (no = 1, par = v[no];
7259243Sobrien	 par != NULL && (*par++ & CHAR) == '-'; no++, par = v[no]) {
7359243Sobrien	if ((p = (*par & CHAR)) == '-') {
7459243Sobrien	    no++;
7559243Sobrien	    break;
7659243Sobrien	}
7759243Sobrien	else
7859243Sobrien	    switch (p) {
7959243Sobrien	    case 'b':
80145479Smp		bindk = 1;
8159243Sobrien		break;
8259243Sobrien	    case 'k':
8359243Sobrien		key = 1;
8459243Sobrien		break;
8559243Sobrien	    case 'a':
8659243Sobrien		map = CcAltMap;
8759243Sobrien		break;
8859243Sobrien	    case 's':
8959243Sobrien		ntype = XK_STR;
9059243Sobrien		break;
9159243Sobrien	    case 'c':
9259243Sobrien		ntype = XK_EXE;
9359243Sobrien		break;
9459243Sobrien	    case 'r':
95145479Smp		removeb = 1;
9659243Sobrien		break;
9759243Sobrien	    case 'v':
9859243Sobrien		ed_InitVIMaps();
9959243Sobrien		return;
10059243Sobrien	    case 'e':
10159243Sobrien		ed_InitEmacsMaps();
10259243Sobrien		return;
10359243Sobrien	    case 'd':
10459243Sobrien#ifdef VIDEFAULT
10559243Sobrien		ed_InitVIMaps();
10659243Sobrien#else /* EMACSDEFAULT */
10759243Sobrien		ed_InitEmacsMaps();
10859243Sobrien#endif /* VIDEFAULT */
10959243Sobrien		return;
11059243Sobrien	    case 'l':
11159243Sobrien		list_functions();
11259243Sobrien		return;
11359243Sobrien	    default:
11459243Sobrien		bindkey_usage();
11559243Sobrien		return;
11659243Sobrien	    }
11759243Sobrien    }
11859243Sobrien
11959243Sobrien    if (!v[no]) {
12059243Sobrien	print_all_keys();
12159243Sobrien	return;
12259243Sobrien    }
12359243Sobrien
12459243Sobrien    if (key) {
12559243Sobrien	if (!IsArrowKey(v[no]))
12659243Sobrien	    xprintf(CGETS(20, 1, "Invalid key name `%S'\n"), v[no]);
127167465Smp	in.buf = Strsave(v[no++]);
12859243Sobrien	in.len = Strlen(in.buf);
12959243Sobrien    }
13059243Sobrien    else {
131145479Smp	if (bindk) {
13259243Sobrien	    if (parsebind(v[no++], &in) == NULL)
13359243Sobrien		return;
13459243Sobrien	}
13559243Sobrien	else {
13659243Sobrien	    if (parsestring(v[no++], &in) == NULL)
13759243Sobrien		return;
13859243Sobrien	}
13959243Sobrien    }
140167465Smp    cleanup_push(in.buf, xfree);
14159243Sobrien
142145479Smp#ifndef WINNT_NATIVE
143145479Smp    if (in.buf[0] > 0xFF) {
144145479Smp	bad_spec(in.buf);
145167465Smp	cleanup_until(in.buf);
146145479Smp	return;
147145479Smp    }
148145479Smp#endif
14959243Sobrien    ch = (uChar) in.buf[0];
15059243Sobrien
151145479Smp    if (removeb) {
152167465Smp	if (key)
15359243Sobrien	    (void) ClearArrowKeys(&in);
154167465Smp	else if (in.len > 1) {
15559243Sobrien	    (void) DeleteXkey(&in);
15659243Sobrien	}
15759243Sobrien	else if (map[ch] == F_XKEY) {
15859243Sobrien	    (void) DeleteXkey(&in);
15959243Sobrien	    map[ch] = F_UNASSIGNED;
16059243Sobrien	}
16159243Sobrien	else {
16259243Sobrien	    map[ch] = F_UNASSIGNED;
16359243Sobrien	}
164167465Smp	cleanup_until(in.buf);
16559243Sobrien	return;
16659243Sobrien    }
16759243Sobrien    if (!v[no]) {
16859243Sobrien	if (key)
16959243Sobrien	    PrintArrowKeys(&in);
17059243Sobrien	else
17159243Sobrien	    printkey(map, &in);
172167465Smp	cleanup_until(in.buf);
17359243Sobrien	return;
17459243Sobrien    }
17559243Sobrien    if (v[no + 1]) {
17659243Sobrien	bindkey_usage();
177167465Smp	cleanup_until(in.buf);
17859243Sobrien	return;
17959243Sobrien    }
18059243Sobrien    switch (ntype) {
18159243Sobrien    case XK_STR:
18259243Sobrien    case XK_EXE:
183167465Smp	if (parsestring(v[no], &out) == NULL) {
184167465Smp	    cleanup_until(in.buf);
18559243Sobrien	    return;
186167465Smp	}
187167465Smp	cleanup_push(out.buf, xfree);
18859243Sobrien	if (key) {
18959243Sobrien	    if (SetArrowKeys(&in, XmapStr(&out), ntype) == -1)
190145479Smp		xprintf(CGETS(20, 2, "Bad key name: %S\n"), in.buf);
191167465Smp	    else
192167465Smp		cleanup_ignore(out.buf);
19359243Sobrien	}
19459243Sobrien	else
19559243Sobrien	    AddXkey(&in, XmapStr(&out), ntype);
19659243Sobrien	map[ch] = F_XKEY;
19759243Sobrien	break;
19859243Sobrien    case XK_CMD:
199167465Smp	if ((cmd = parsecmd(v[no])) == 0) {
200167465Smp	    cleanup_until(in.buf);
20159243Sobrien	    return;
202167465Smp	}
20359243Sobrien	if (key)
20459243Sobrien	    (void) SetArrowKeys(&in, XmapCmd((int) cmd), ntype);
20559243Sobrien	else {
20659243Sobrien	    if (in.len > 1) {
20759243Sobrien		AddXkey(&in, XmapCmd((int) cmd), ntype);
20859243Sobrien		map[ch] = F_XKEY;
20959243Sobrien	    }
21059243Sobrien	    else {
21159243Sobrien		ClearXkey(map, &in);
21259243Sobrien		map[ch] = cmd;
21359243Sobrien	    }
21459243Sobrien	}
21559243Sobrien	break;
21659243Sobrien    default:
21759243Sobrien	abort();
21859243Sobrien	break;
21959243Sobrien    }
220167465Smp    cleanup_until(in.buf);
22159243Sobrien    if (key)
22259243Sobrien	BindArrowKeys();
22359243Sobrien}
22459243Sobrien
22559243Sobrienstatic void
226167465Smpprintkey(const KEYCMD *map, CStr *in)
22759243Sobrien{
228145479Smp    struct KeyFuncs *fp;
22959243Sobrien
23059243Sobrien    if (in->len < 2) {
231167465Smp	unsigned char *unparsed;
232167465Smp
233167465Smp	unparsed = unparsestring(in, STRQQ);
234167465Smp	cleanup_push(unparsed, xfree);
23559243Sobrien	for (fp = FuncNames; fp->name; fp++) {
23659243Sobrien	    if (fp->func == map[(uChar) *(in->buf)]) {
237167465Smp		xprintf("%s\t->\t%s\n", unparsed, fp->name);
23859243Sobrien	    }
23959243Sobrien	}
240167465Smp	cleanup_until(unparsed);
24159243Sobrien    }
242167465Smp    else
24359243Sobrien	PrintXkey(in);
24459243Sobrien}
24559243Sobrien
24659243Sobrienstatic  KEYCMD
247167465Smpparsecmd(Char *str)
24859243Sobrien{
249145479Smp    struct KeyFuncs *fp;
25059243Sobrien
25159243Sobrien    for (fp = FuncNames; fp->name; fp++) {
25259243Sobrien	if (strcmp(short2str(str), fp->name) == 0) {
25359243Sobrien	    return (KEYCMD) fp->func;
25459243Sobrien	}
25559243Sobrien    }
25659243Sobrien    xprintf(CGETS(20, 3, "Bad command name: %S\n"), str);
25759243Sobrien    return 0;
25859243Sobrien}
25959243Sobrien
26059243Sobrien
26159243Sobrienstatic void
262167465Smpbad_spec(const Char *str)
26359243Sobrien{
26459243Sobrien    xprintf(CGETS(20, 4, "Bad key spec %S\n"), str);
26559243Sobrien}
26659243Sobrien
26759243Sobrienstatic CStr *
268167465Smpparsebind(const Char *s, CStr *str)
26959243Sobrien{
270167465Smp    struct Strbuf b = Strbuf_INIT;
27159243Sobrien
272167465Smp    cleanup_push(&b, Strbuf_cleanup);
27359243Sobrien    if (Iscntrl(*s)) {
274167465Smp	Strbuf_append1(&b, *s);
275167465Smp	goto end;
27659243Sobrien    }
27759243Sobrien
27859243Sobrien    switch (*s) {
27959243Sobrien    case '^':
28059243Sobrien	s++;
28169408Sache#ifdef IS_ASCII
282167465Smp	Strbuf_append1(&b, (*s == '?') ? '\177' : ((*s & CHAR) & 0237));
28369408Sache#else
284167465Smp	Strbuf_append1(&b, (*s == '?') ? CTL_ESC('\177')
285167465Smp		       : _toebcdic[_toascii[*s & CHAR] & 0237]);
28669408Sache#endif
28759243Sobrien	break;
28859243Sobrien
28959243Sobrien    case 'F':
29059243Sobrien    case 'M':
29159243Sobrien    case 'X':
29259243Sobrien    case 'C':
29369408Sache#ifdef WINNT_NATIVE
29459243Sobrien    case 'N':
29569408Sache#endif /* WINNT_NATIVE */
296167465Smp	if (s[1] != '-' || s[2] == '\0')
297167465Smp	    goto bad_spec;
29859243Sobrien	s += 2;
29959243Sobrien	switch (s[-2]) {
30059243Sobrien	case 'F': case 'f':	/* Turn into ^[str */
301167465Smp	    Strbuf_append1(&b, CTL_ESC('\033'));
302167465Smp	    Strbuf_append(&b, s);
30359243Sobrien	    break;
30459243Sobrien
30559243Sobrien	case 'C': case 'c':	/* Turn into ^c */
30669408Sache#ifdef IS_ASCII
307167465Smp	    Strbuf_append1(&b, (*s == '?') ? '\177' : ((*s & CHAR) & 0237));
30869408Sache#else
309167465Smp	    Strbuf_append1(&b, (*s == '?') ? CTL_ESC('\177')
310167465Smp			   : _toebcdic[_toascii[*s & CHAR] & 0237]);
31169408Sache#endif
31259243Sobrien	    break;
31359243Sobrien
31459243Sobrien	case 'X' : case 'x':	/* Turn into ^Xc */
31569408Sache#ifdef IS_ASCII
316167465Smp	    Strbuf_append1(&b, 'X' & 0237);
31769408Sache#else
318167465Smp	    Strbuf_append1(&b, _toebcdic[_toascii['X'] & 0237]);
31969408Sache#endif
320167465Smp	    Strbuf_append1(&b, *s);
32159243Sobrien	    break;
32259243Sobrien
32359243Sobrien	case 'M' : case 'm':	/* Turn into 0x80|c */
32459243Sobrien	    if (!NoNLSRebind) {
325167465Smp		Strbuf_append1(&b, CTL_ESC('\033'));
326167465Smp	    	Strbuf_append1(&b, *s);
32759243Sobrien	    } else {
32869408Sache#ifdef IS_ASCII
329167465Smp		Strbuf_append1(&b, *s | 0x80);
33069408Sache#else
331167465Smp		Strbuf_append1(&b, _toebcdic[_toascii[*s] | 0x80]);
33269408Sache#endif
33359243Sobrien	    }
33459243Sobrien	    break;
33569408Sache#ifdef WINNT_NATIVE
33659243Sobrien	case 'N' : case 'n':	/* NT */
33759243Sobrien		{
33859243Sobrien			Char bnt;
33959243Sobrien
34059243Sobrien			bnt = nt_translate_bindkey(s);
34159243Sobrien			if (bnt != 0)
342167465Smp			        Strbuf_append1(&b, bnt);
34359243Sobrien			else
34459243Sobrien				bad_spec(s);
34559243Sobrien		}
34659243Sobrien	    break;
34769408Sache#endif /* WINNT_NATIVE */
34859243Sobrien
34959243Sobrien	default:
35059243Sobrien	    abort();
35159243Sobrien	}
35259243Sobrien	break;
35359243Sobrien
35459243Sobrien    default:
355167465Smp	goto bad_spec;
35659243Sobrien    }
35759243Sobrien
358167465Smp end:
359167465Smp    cleanup_ignore(&b);
360167465Smp    cleanup_until(&b);
361167465Smp    Strbuf_terminate(&b);
362167465Smp    str->buf = xrealloc(b.s, (b.len + 1) * sizeof (*str->buf));
363167465Smp    str->len = b.len;
36459243Sobrien    return str;
365167465Smp
366167465Smp bad_spec:
367167465Smp    bad_spec(s);
368167465Smp    cleanup_until(&b);
369167465Smp    return NULL;
37059243Sobrien}
37159243Sobrien
37259243Sobrien
37359243Sobrienstatic CStr *
374167465Smpparsestring(const Char *str, CStr *buf)
37559243Sobrien{
376167465Smp    struct Strbuf b = Strbuf_INIT;
37759243Sobrien    const Char   *p;
378145479Smp    eChar  es;
37959243Sobrien
38059243Sobrien    if (*str == 0) {
381195609Smp	xprintf("%s", CGETS(20, 5, "Null string specification\n"));
38259243Sobrien	return NULL;
38359243Sobrien    }
38459243Sobrien
385167465Smp    cleanup_push(&b, Strbuf_cleanup);
38659243Sobrien    for (p = str; *p != 0; p++) {
38759243Sobrien	if ((*p & CHAR) == '\\' || (*p & CHAR) == '^') {
388167465Smp	    if ((es = parseescape(&p)) == CHAR_ERR) {
389167465Smp		cleanup_until(&b);
39059243Sobrien		return 0;
391167465Smp	    } else
392167465Smp		Strbuf_append1(&b, es);
39359243Sobrien	}
39459243Sobrien	else
395167465Smp	    Strbuf_append1(&b, *p & CHAR);
39659243Sobrien    }
397167465Smp    cleanup_ignore(&b);
398167465Smp    cleanup_until(&b);
399167465Smp    Strbuf_terminate(&b);
400167465Smp    buf->buf = xrealloc(b.s, (b.len + 1) * sizeof (*buf->buf));
401167465Smp    buf->len = b.len;
40259243Sobrien    return buf;
40359243Sobrien}
40459243Sobrien
40559243Sobrienstatic void
406167465Smpprint_all_keys(void)
40759243Sobrien{
40859243Sobrien    int     prev, i;
40959243Sobrien    CStr nilstr;
41059243Sobrien    nilstr.buf = NULL;
41159243Sobrien    nilstr.len = 0;
41259243Sobrien
41359243Sobrien
414195609Smp    xprintf("%s", CGETS(20, 6, "Standard key bindings\n"));
41559243Sobrien    prev = 0;
41659243Sobrien    for (i = 0; i < 256; i++) {
41759243Sobrien	if (CcKeyMap[prev] == CcKeyMap[i])
41859243Sobrien	    continue;
41959243Sobrien	printkeys(CcKeyMap, prev, i - 1);
42059243Sobrien	prev = i;
42159243Sobrien    }
42259243Sobrien    printkeys(CcKeyMap, prev, i - 1);
42359243Sobrien
424195609Smp    xprintf("%s", CGETS(20, 7, "Alternative key bindings\n"));
42559243Sobrien    prev = 0;
42659243Sobrien    for (i = 0; i < 256; i++) {
42759243Sobrien	if (CcAltMap[prev] == CcAltMap[i])
42859243Sobrien	    continue;
42959243Sobrien	printkeys(CcAltMap, prev, i - 1);
43059243Sobrien	prev = i;
43159243Sobrien    }
43259243Sobrien    printkeys(CcAltMap, prev, i - 1);
433195609Smp    xprintf("%s", CGETS(20, 8, "Multi-character bindings\n"));
43459243Sobrien    PrintXkey(NULL);	/* print all Xkey bindings */
435195609Smp    xprintf("%s", CGETS(20, 9, "Arrow key bindings\n"));
43659243Sobrien    PrintArrowKeys(&nilstr);
43759243Sobrien}
43859243Sobrien
43959243Sobrienstatic void
440167465Smpprintkeys(KEYCMD *map, int first, int last)
44159243Sobrien{
442145479Smp    struct KeyFuncs *fp;
44359243Sobrien    Char    firstbuf[2], lastbuf[2];
44459243Sobrien    CStr fb, lb;
445167465Smp    unsigned char *unparsed;
44659243Sobrien    fb.buf = firstbuf;
44759243Sobrien    lb.buf = lastbuf;
44859243Sobrien
44959243Sobrien    firstbuf[0] = (Char) first;
45059243Sobrien    firstbuf[1] = 0;
45159243Sobrien    lastbuf[0] = (Char) last;
45259243Sobrien    lastbuf[1] = 0;
45359243Sobrien    fb.len = 1;
45459243Sobrien    lb.len = 1;
45559243Sobrien
456167465Smp    unparsed = unparsestring(&fb, STRQQ);
457167465Smp    cleanup_push(unparsed, xfree);
45859243Sobrien    if (map[first] == F_UNASSIGNED) {
45959243Sobrien	if (first == last)
460167465Smp	    xprintf(CGETS(20, 10, "%-15s->  is undefined\n"), unparsed);
461167465Smp	cleanup_until(unparsed);
46259243Sobrien	return;
46359243Sobrien    }
46459243Sobrien
46559243Sobrien    for (fp = FuncNames; fp->name; fp++) {
46659243Sobrien	if (fp->func == map[first]) {
467167465Smp	    if (first == last)
468167465Smp		xprintf("%-15s->  %s\n", unparsed, fp->name);
46959243Sobrien	    else {
470167465Smp		unsigned char *p;
471167465Smp
472167465Smp		p = unparsestring(&lb, STRQQ);
473167465Smp		cleanup_push(p, xfree);
474167465Smp		xprintf("%-4s to %-7s->  %s\n", unparsed, p, fp->name);
47559243Sobrien	    }
476167465Smp	    cleanup_until(unparsed);
47759243Sobrien	    return;
47859243Sobrien	}
47959243Sobrien    }
480167465Smp    xprintf(CGETS(20, 11, "BUG!!! %s isn't bound to anything.\n"), unparsed);
481167465Smp    if (map == CcKeyMap)
48259243Sobrien	xprintf("CcKeyMap[%d] == %d\n", first, CcKeyMap[first]);
483167465Smp    else
48459243Sobrien	xprintf("CcAltMap[%d] == %d\n", first, CcAltMap[first]);
485167465Smp    cleanup_until(unparsed);
48659243Sobrien}
48759243Sobrien
48859243Sobrienstatic void
489167465Smpbindkey_usage(void)
49059243Sobrien{
491195609Smp    xprintf("%s", CGETS(20, 12,
49259243Sobrien	    "Usage: bindkey [options] [--] [KEY [COMMAND]]\n"));
493195609Smp    xprintf("%s", CGETS(20, 13,
49459243Sobrien    	    "    -a   list or bind KEY in alternative key map\n"));
495195609Smp    xprintf("%s", CGETS(20, 14,
49659243Sobrien	    "    -b   interpret KEY as a C-, M-, F- or X- key name\n"));
497195609Smp    xprintf("%s", CGETS(20, 15,
49859243Sobrien            "    -s   interpret COMMAND as a literal string to be output\n"));
499195609Smp    xprintf("%s", CGETS(20, 16,
50059243Sobrien            "    -c   interpret COMMAND as a builtin or external command\n"));
501195609Smp    xprintf("%s", CGETS(20, 17,
50259243Sobrien	    "    -v   bind all keys to vi bindings\n"));
503195609Smp    xprintf("%s", CGETS(20, 18,
50459243Sobrien	    "    -e   bind all keys to emacs bindings\n"));
505316957Sdchagin    xprintf(CGETS(20, 19,
506316957Sdchagin	    "    -d   bind all keys to default editor's bindings (%s)\n"),
507316957Sdchagin#ifdef VIDEFAULT
508316957Sdchagin	    "vi"
509316957Sdchagin#else /* EMACSDEFAULT */
510316957Sdchagin	    "emacs"
511316957Sdchagin#endif /* VIDEFAULT */
512316957Sdchagin	    );
513195609Smp    xprintf("%s", CGETS(20, 20,
51459243Sobrien	    "    -l   list editor commands with descriptions\n"));
515195609Smp    xprintf("%s", CGETS(20, 21,
51659243Sobrien	    "    -r   remove KEY's binding\n"));
517195609Smp    xprintf("%s", CGETS(20, 22,
51859243Sobrien	    "    -k   interpret KEY as a symbolic arrow-key name\n"));
519195609Smp    xprintf("%s", CGETS(20, 23,
52059243Sobrien	    "    --   force a break from option processing\n"));
521195609Smp    xprintf("%s", CGETS(20, 24,
52259243Sobrien	    "    -u   (or any invalid option) this message\n"));
52359243Sobrien    xprintf("\n");
524195609Smp    xprintf("%s", CGETS(20, 25,
52559243Sobrien	    "Without KEY or COMMAND, prints all bindings\n"));
526195609Smp    xprintf("%s", CGETS(20, 26,
52759243Sobrien	    "Without COMMAND, prints the binding for KEY.\n"));
52859243Sobrien}
52959243Sobrien
53059243Sobrienstatic void
531167465Smplist_functions(void)
53259243Sobrien{
533145479Smp    struct KeyFuncs *fp;
53459243Sobrien
53559243Sobrien    for (fp = FuncNames; fp->name; fp++) {
53659243Sobrien	xprintf("%s\n          %s\n", fp->name, fp->desc);
53759243Sobrien    }
53859243Sobrien}
539