1195609Smp/* $Header: /p/tcsh/cvsroot/tcsh/tc.bind.c,v 3.45 2009/06/25 21:15:37 christos Exp $ */
259243Sobrien/*
359243Sobrien * tc.bind.c: Key binding functions
459243Sobrien */
559243Sobrien/*-
659243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California.
759243Sobrien * All rights reserved.
859243Sobrien *
959243Sobrien * Redistribution and use in source and binary forms, with or without
1059243Sobrien * modification, are permitted provided that the following conditions
1159243Sobrien * are met:
1259243Sobrien * 1. Redistributions of source code must retain the above copyright
1359243Sobrien *    notice, this list of conditions and the following disclaimer.
1459243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1559243Sobrien *    notice, this list of conditions and the following disclaimer in the
1659243Sobrien *    documentation and/or other materials provided with the distribution.
17100616Smp * 3. Neither the name of the University nor the names of its contributors
1859243Sobrien *    may be used to endorse or promote products derived from this software
1959243Sobrien *    without specific prior written permission.
2059243Sobrien *
2159243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2259243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2359243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2459243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2559243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2659243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2759243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2859243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2959243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3059243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3159243Sobrien * SUCH DAMAGE.
3259243Sobrien */
3359243Sobrien#include "sh.h"
3459243Sobrien
35195609SmpRCSID("$tcsh: tc.bind.c,v 3.45 2009/06/25 21:15:37 christos Exp $")
3659243Sobrien
3759243Sobrien#include "ed.h"
3859243Sobrien#include "ed.defns.h"
3959243Sobrien
40167465Smpstatic	void   printkey		(const KEYCMD *, CStr *);
41167465Smpstatic	KEYCMD parsecmd		(Char *);
42167465Smpstatic  void   bad_spec		(const Char *);
43167465Smpstatic	CStr  *parsestring	(const Char *, CStr *);
44167465Smpstatic	CStr  *parsebind	(const Char *, CStr *);
45167465Smpstatic	void   print_all_keys	(void);
46167465Smpstatic	void   printkeys	(KEYCMD *, int, int);
47167465Smpstatic	void   bindkey_usage	(void);
48167465Smpstatic	void   list_functions	(void);
4959243Sobrien
5059243Sobrienextern int MapsAreInited;
5159243Sobrien
5259243Sobrien
5359243Sobrien
5459243Sobrien
5559243Sobrien/*ARGSUSED*/
5659243Sobrienvoid
57167465Smpdobindkey(Char **v, struct command *c)
5859243Sobrien{
5959243Sobrien    KEYCMD *map;
60145479Smp    int     ntype, no, removeb, key, bindk;
6159243Sobrien    Char   *par;
6259243Sobrien    Char    p;
6359243Sobrien    KEYCMD  cmd;
6459243Sobrien    CStr    in;
6559243Sobrien    CStr    out;
6659243Sobrien    uChar   ch;
6759243Sobrien
6859243Sobrien    USE(c);
6959243Sobrien    if (!MapsAreInited)
7059243Sobrien	ed_InitMaps();
7159243Sobrien
7259243Sobrien    map = CcKeyMap;
7359243Sobrien    ntype = XK_CMD;
74145479Smp    key = removeb = bindk = 0;
7559243Sobrien    for (no = 1, par = v[no];
7659243Sobrien	 par != NULL && (*par++ & CHAR) == '-'; no++, par = v[no]) {
7759243Sobrien	if ((p = (*par & CHAR)) == '-') {
7859243Sobrien	    no++;
7959243Sobrien	    break;
8059243Sobrien	}
8159243Sobrien	else
8259243Sobrien	    switch (p) {
8359243Sobrien	    case 'b':
84145479Smp		bindk = 1;
8559243Sobrien		break;
8659243Sobrien	    case 'k':
8759243Sobrien		key = 1;
8859243Sobrien		break;
8959243Sobrien	    case 'a':
9059243Sobrien		map = CcAltMap;
9159243Sobrien		break;
9259243Sobrien	    case 's':
9359243Sobrien		ntype = XK_STR;
9459243Sobrien		break;
9559243Sobrien	    case 'c':
9659243Sobrien		ntype = XK_EXE;
9759243Sobrien		break;
9859243Sobrien	    case 'r':
99145479Smp		removeb = 1;
10059243Sobrien		break;
10159243Sobrien	    case 'v':
10259243Sobrien		ed_InitVIMaps();
10359243Sobrien		return;
10459243Sobrien	    case 'e':
10559243Sobrien		ed_InitEmacsMaps();
10659243Sobrien		return;
10759243Sobrien	    case 'd':
10859243Sobrien#ifdef VIDEFAULT
10959243Sobrien		ed_InitVIMaps();
11059243Sobrien#else /* EMACSDEFAULT */
11159243Sobrien		ed_InitEmacsMaps();
11259243Sobrien#endif /* VIDEFAULT */
11359243Sobrien		return;
11459243Sobrien	    case 'l':
11559243Sobrien		list_functions();
11659243Sobrien		return;
11759243Sobrien	    default:
11859243Sobrien		bindkey_usage();
11959243Sobrien		return;
12059243Sobrien	    }
12159243Sobrien    }
12259243Sobrien
12359243Sobrien    if (!v[no]) {
12459243Sobrien	print_all_keys();
12559243Sobrien	return;
12659243Sobrien    }
12759243Sobrien
12859243Sobrien    if (key) {
12959243Sobrien	if (!IsArrowKey(v[no]))
13059243Sobrien	    xprintf(CGETS(20, 1, "Invalid key name `%S'\n"), v[no]);
131167465Smp	in.buf = Strsave(v[no++]);
13259243Sobrien	in.len = Strlen(in.buf);
13359243Sobrien    }
13459243Sobrien    else {
135145479Smp	if (bindk) {
13659243Sobrien	    if (parsebind(v[no++], &in) == NULL)
13759243Sobrien		return;
13859243Sobrien	}
13959243Sobrien	else {
14059243Sobrien	    if (parsestring(v[no++], &in) == NULL)
14159243Sobrien		return;
14259243Sobrien	}
14359243Sobrien    }
144167465Smp    cleanup_push(in.buf, xfree);
14559243Sobrien
146145479Smp#ifndef WINNT_NATIVE
147145479Smp    if (in.buf[0] > 0xFF) {
148145479Smp	bad_spec(in.buf);
149167465Smp	cleanup_until(in.buf);
150145479Smp	return;
151145479Smp    }
152145479Smp#endif
15359243Sobrien    ch = (uChar) in.buf[0];
15459243Sobrien
155145479Smp    if (removeb) {
156167465Smp	if (key)
15759243Sobrien	    (void) ClearArrowKeys(&in);
158167465Smp	else if (in.len > 1) {
15959243Sobrien	    (void) DeleteXkey(&in);
16059243Sobrien	}
16159243Sobrien	else if (map[ch] == F_XKEY) {
16259243Sobrien	    (void) DeleteXkey(&in);
16359243Sobrien	    map[ch] = F_UNASSIGNED;
16459243Sobrien	}
16559243Sobrien	else {
16659243Sobrien	    map[ch] = F_UNASSIGNED;
16759243Sobrien	}
168167465Smp	cleanup_until(in.buf);
16959243Sobrien	return;
17059243Sobrien    }
17159243Sobrien    if (!v[no]) {
17259243Sobrien	if (key)
17359243Sobrien	    PrintArrowKeys(&in);
17459243Sobrien	else
17559243Sobrien	    printkey(map, &in);
176167465Smp	cleanup_until(in.buf);
17759243Sobrien	return;
17859243Sobrien    }
17959243Sobrien    if (v[no + 1]) {
18059243Sobrien	bindkey_usage();
181167465Smp	cleanup_until(in.buf);
18259243Sobrien	return;
18359243Sobrien    }
18459243Sobrien    switch (ntype) {
18559243Sobrien    case XK_STR:
18659243Sobrien    case XK_EXE:
187167465Smp	if (parsestring(v[no], &out) == NULL) {
188167465Smp	    cleanup_until(in.buf);
18959243Sobrien	    return;
190167465Smp	}
191167465Smp	cleanup_push(out.buf, xfree);
19259243Sobrien	if (key) {
19359243Sobrien	    if (SetArrowKeys(&in, XmapStr(&out), ntype) == -1)
194145479Smp		xprintf(CGETS(20, 2, "Bad key name: %S\n"), in.buf);
195167465Smp	    else
196167465Smp		cleanup_ignore(out.buf);
19759243Sobrien	}
19859243Sobrien	else
19959243Sobrien	    AddXkey(&in, XmapStr(&out), ntype);
20059243Sobrien	map[ch] = F_XKEY;
20159243Sobrien	break;
20259243Sobrien    case XK_CMD:
203167465Smp	if ((cmd = parsecmd(v[no])) == 0) {
204167465Smp	    cleanup_until(in.buf);
20559243Sobrien	    return;
206167465Smp	}
20759243Sobrien	if (key)
20859243Sobrien	    (void) SetArrowKeys(&in, XmapCmd((int) cmd), ntype);
20959243Sobrien	else {
21059243Sobrien	    if (in.len > 1) {
21159243Sobrien		AddXkey(&in, XmapCmd((int) cmd), ntype);
21259243Sobrien		map[ch] = F_XKEY;
21359243Sobrien	    }
21459243Sobrien	    else {
21559243Sobrien		ClearXkey(map, &in);
21659243Sobrien		map[ch] = cmd;
21759243Sobrien	    }
21859243Sobrien	}
21959243Sobrien	break;
22059243Sobrien    default:
22159243Sobrien	abort();
22259243Sobrien	break;
22359243Sobrien    }
224167465Smp    cleanup_until(in.buf);
22559243Sobrien    if (key)
22659243Sobrien	BindArrowKeys();
22759243Sobrien}
22859243Sobrien
22959243Sobrienstatic void
230167465Smpprintkey(const KEYCMD *map, CStr *in)
23159243Sobrien{
232145479Smp    struct KeyFuncs *fp;
23359243Sobrien
23459243Sobrien    if (in->len < 2) {
235167465Smp	unsigned char *unparsed;
236167465Smp
237167465Smp	unparsed = unparsestring(in, STRQQ);
238167465Smp	cleanup_push(unparsed, xfree);
23959243Sobrien	for (fp = FuncNames; fp->name; fp++) {
24059243Sobrien	    if (fp->func == map[(uChar) *(in->buf)]) {
241167465Smp		xprintf("%s\t->\t%s\n", unparsed, fp->name);
24259243Sobrien	    }
24359243Sobrien	}
244167465Smp	cleanup_until(unparsed);
24559243Sobrien    }
246167465Smp    else
24759243Sobrien	PrintXkey(in);
24859243Sobrien}
24959243Sobrien
25059243Sobrienstatic  KEYCMD
251167465Smpparsecmd(Char *str)
25259243Sobrien{
253145479Smp    struct KeyFuncs *fp;
25459243Sobrien
25559243Sobrien    for (fp = FuncNames; fp->name; fp++) {
25659243Sobrien	if (strcmp(short2str(str), fp->name) == 0) {
25759243Sobrien	    return (KEYCMD) fp->func;
25859243Sobrien	}
25959243Sobrien    }
26059243Sobrien    xprintf(CGETS(20, 3, "Bad command name: %S\n"), str);
26159243Sobrien    return 0;
26259243Sobrien}
26359243Sobrien
26459243Sobrien
26559243Sobrienstatic void
266167465Smpbad_spec(const Char *str)
26759243Sobrien{
26859243Sobrien    xprintf(CGETS(20, 4, "Bad key spec %S\n"), str);
26959243Sobrien}
27059243Sobrien
27159243Sobrienstatic CStr *
272167465Smpparsebind(const Char *s, CStr *str)
27359243Sobrien{
274167465Smp    struct Strbuf b = Strbuf_INIT;
27559243Sobrien
276167465Smp    cleanup_push(&b, Strbuf_cleanup);
27759243Sobrien    if (Iscntrl(*s)) {
278167465Smp	Strbuf_append1(&b, *s);
279167465Smp	goto end;
28059243Sobrien    }
28159243Sobrien
28259243Sobrien    switch (*s) {
28359243Sobrien    case '^':
28459243Sobrien	s++;
28569408Sache#ifdef IS_ASCII
286167465Smp	Strbuf_append1(&b, (*s == '?') ? '\177' : ((*s & CHAR) & 0237));
28769408Sache#else
288167465Smp	Strbuf_append1(&b, (*s == '?') ? CTL_ESC('\177')
289167465Smp		       : _toebcdic[_toascii[*s & CHAR] & 0237]);
29069408Sache#endif
29159243Sobrien	break;
29259243Sobrien
29359243Sobrien    case 'F':
29459243Sobrien    case 'M':
29559243Sobrien    case 'X':
29659243Sobrien    case 'C':
29769408Sache#ifdef WINNT_NATIVE
29859243Sobrien    case 'N':
29969408Sache#endif /* WINNT_NATIVE */
300167465Smp	if (s[1] != '-' || s[2] == '\0')
301167465Smp	    goto bad_spec;
30259243Sobrien	s += 2;
30359243Sobrien	switch (s[-2]) {
30459243Sobrien	case 'F': case 'f':	/* Turn into ^[str */
305167465Smp	    Strbuf_append1(&b, CTL_ESC('\033'));
306167465Smp	    Strbuf_append(&b, s);
30759243Sobrien	    break;
30859243Sobrien
30959243Sobrien	case 'C': case 'c':	/* Turn into ^c */
31069408Sache#ifdef IS_ASCII
311167465Smp	    Strbuf_append1(&b, (*s == '?') ? '\177' : ((*s & CHAR) & 0237));
31269408Sache#else
313167465Smp	    Strbuf_append1(&b, (*s == '?') ? CTL_ESC('\177')
314167465Smp			   : _toebcdic[_toascii[*s & CHAR] & 0237]);
31569408Sache#endif
31659243Sobrien	    break;
31759243Sobrien
31859243Sobrien	case 'X' : case 'x':	/* Turn into ^Xc */
31969408Sache#ifdef IS_ASCII
320167465Smp	    Strbuf_append1(&b, 'X' & 0237);
32169408Sache#else
322167465Smp	    Strbuf_append1(&b, _toebcdic[_toascii['X'] & 0237]);
32369408Sache#endif
324167465Smp	    Strbuf_append1(&b, *s);
32559243Sobrien	    break;
32659243Sobrien
32759243Sobrien	case 'M' : case 'm':	/* Turn into 0x80|c */
32859243Sobrien	    if (!NoNLSRebind) {
329167465Smp		Strbuf_append1(&b, CTL_ESC('\033'));
330167465Smp	    	Strbuf_append1(&b, *s);
33159243Sobrien	    } else {
33269408Sache#ifdef IS_ASCII
333167465Smp		Strbuf_append1(&b, *s | 0x80);
33469408Sache#else
335167465Smp		Strbuf_append1(&b, _toebcdic[_toascii[*s] | 0x80]);
33669408Sache#endif
33759243Sobrien	    }
33859243Sobrien	    break;
33969408Sache#ifdef WINNT_NATIVE
34059243Sobrien	case 'N' : case 'n':	/* NT */
34159243Sobrien		{
34259243Sobrien			Char bnt;
34359243Sobrien
34459243Sobrien			bnt = nt_translate_bindkey(s);
34559243Sobrien			if (bnt != 0)
346167465Smp			        Strbuf_append1(&b, bnt);
34759243Sobrien			else
34859243Sobrien				bad_spec(s);
34959243Sobrien		}
35059243Sobrien	    break;
35169408Sache#endif /* WINNT_NATIVE */
35259243Sobrien
35359243Sobrien	default:
35459243Sobrien	    abort();
35559243Sobrien	}
35659243Sobrien	break;
35759243Sobrien
35859243Sobrien    default:
359167465Smp	goto bad_spec;
36059243Sobrien    }
36159243Sobrien
362167465Smp end:
363167465Smp    cleanup_ignore(&b);
364167465Smp    cleanup_until(&b);
365167465Smp    Strbuf_terminate(&b);
366167465Smp    str->buf = xrealloc(b.s, (b.len + 1) * sizeof (*str->buf));
367167465Smp    str->len = b.len;
36859243Sobrien    return str;
369167465Smp
370167465Smp bad_spec:
371167465Smp    bad_spec(s);
372167465Smp    cleanup_until(&b);
373167465Smp    return NULL;
37459243Sobrien}
37559243Sobrien
37659243Sobrien
37759243Sobrienstatic CStr *
378167465Smpparsestring(const Char *str, CStr *buf)
37959243Sobrien{
380167465Smp    struct Strbuf b = Strbuf_INIT;
38159243Sobrien    const Char   *p;
382145479Smp    eChar  es;
38359243Sobrien
38459243Sobrien    if (*str == 0) {
385195609Smp	xprintf("%s", CGETS(20, 5, "Null string specification\n"));
38659243Sobrien	return NULL;
38759243Sobrien    }
38859243Sobrien
389167465Smp    cleanup_push(&b, Strbuf_cleanup);
39059243Sobrien    for (p = str; *p != 0; p++) {
39159243Sobrien	if ((*p & CHAR) == '\\' || (*p & CHAR) == '^') {
392167465Smp	    if ((es = parseescape(&p)) == CHAR_ERR) {
393167465Smp		cleanup_until(&b);
39459243Sobrien		return 0;
395167465Smp	    } else
396167465Smp		Strbuf_append1(&b, es);
39759243Sobrien	}
39859243Sobrien	else
399167465Smp	    Strbuf_append1(&b, *p & CHAR);
40059243Sobrien    }
401167465Smp    cleanup_ignore(&b);
402167465Smp    cleanup_until(&b);
403167465Smp    Strbuf_terminate(&b);
404167465Smp    buf->buf = xrealloc(b.s, (b.len + 1) * sizeof (*buf->buf));
405167465Smp    buf->len = b.len;
40659243Sobrien    return buf;
40759243Sobrien}
40859243Sobrien
40959243Sobrienstatic void
410167465Smpprint_all_keys(void)
41159243Sobrien{
41259243Sobrien    int     prev, i;
41359243Sobrien    CStr nilstr;
41459243Sobrien    nilstr.buf = NULL;
41559243Sobrien    nilstr.len = 0;
41659243Sobrien
41759243Sobrien
418195609Smp    xprintf("%s", CGETS(20, 6, "Standard key bindings\n"));
41959243Sobrien    prev = 0;
42059243Sobrien    for (i = 0; i < 256; i++) {
42159243Sobrien	if (CcKeyMap[prev] == CcKeyMap[i])
42259243Sobrien	    continue;
42359243Sobrien	printkeys(CcKeyMap, prev, i - 1);
42459243Sobrien	prev = i;
42559243Sobrien    }
42659243Sobrien    printkeys(CcKeyMap, prev, i - 1);
42759243Sobrien
428195609Smp    xprintf("%s", CGETS(20, 7, "Alternative key bindings\n"));
42959243Sobrien    prev = 0;
43059243Sobrien    for (i = 0; i < 256; i++) {
43159243Sobrien	if (CcAltMap[prev] == CcAltMap[i])
43259243Sobrien	    continue;
43359243Sobrien	printkeys(CcAltMap, prev, i - 1);
43459243Sobrien	prev = i;
43559243Sobrien    }
43659243Sobrien    printkeys(CcAltMap, prev, i - 1);
437195609Smp    xprintf("%s", CGETS(20, 8, "Multi-character bindings\n"));
43859243Sobrien    PrintXkey(NULL);	/* print all Xkey bindings */
439195609Smp    xprintf("%s", CGETS(20, 9, "Arrow key bindings\n"));
44059243Sobrien    PrintArrowKeys(&nilstr);
44159243Sobrien}
44259243Sobrien
44359243Sobrienstatic void
444167465Smpprintkeys(KEYCMD *map, int first, int last)
44559243Sobrien{
446145479Smp    struct KeyFuncs *fp;
44759243Sobrien    Char    firstbuf[2], lastbuf[2];
44859243Sobrien    CStr fb, lb;
449167465Smp    unsigned char *unparsed;
45059243Sobrien    fb.buf = firstbuf;
45159243Sobrien    lb.buf = lastbuf;
45259243Sobrien
45359243Sobrien    firstbuf[0] = (Char) first;
45459243Sobrien    firstbuf[1] = 0;
45559243Sobrien    lastbuf[0] = (Char) last;
45659243Sobrien    lastbuf[1] = 0;
45759243Sobrien    fb.len = 1;
45859243Sobrien    lb.len = 1;
45959243Sobrien
460167465Smp    unparsed = unparsestring(&fb, STRQQ);
461167465Smp    cleanup_push(unparsed, xfree);
46259243Sobrien    if (map[first] == F_UNASSIGNED) {
46359243Sobrien	if (first == last)
464167465Smp	    xprintf(CGETS(20, 10, "%-15s->  is undefined\n"), unparsed);
465167465Smp	cleanup_until(unparsed);
46659243Sobrien	return;
46759243Sobrien    }
46859243Sobrien
46959243Sobrien    for (fp = FuncNames; fp->name; fp++) {
47059243Sobrien	if (fp->func == map[first]) {
471167465Smp	    if (first == last)
472167465Smp		xprintf("%-15s->  %s\n", unparsed, fp->name);
47359243Sobrien	    else {
474167465Smp		unsigned char *p;
475167465Smp
476167465Smp		p = unparsestring(&lb, STRQQ);
477167465Smp		cleanup_push(p, xfree);
478167465Smp		xprintf("%-4s to %-7s->  %s\n", unparsed, p, fp->name);
47959243Sobrien	    }
480167465Smp	    cleanup_until(unparsed);
48159243Sobrien	    return;
48259243Sobrien	}
48359243Sobrien    }
484167465Smp    xprintf(CGETS(20, 11, "BUG!!! %s isn't bound to anything.\n"), unparsed);
485167465Smp    if (map == CcKeyMap)
48659243Sobrien	xprintf("CcKeyMap[%d] == %d\n", first, CcKeyMap[first]);
487167465Smp    else
48859243Sobrien	xprintf("CcAltMap[%d] == %d\n", first, CcAltMap[first]);
489167465Smp    cleanup_until(unparsed);
49059243Sobrien}
49159243Sobrien
49259243Sobrienstatic void
493167465Smpbindkey_usage(void)
49459243Sobrien{
495195609Smp    xprintf("%s", CGETS(20, 12,
49659243Sobrien	    "Usage: bindkey [options] [--] [KEY [COMMAND]]\n"));
497195609Smp    xprintf("%s", CGETS(20, 13,
49859243Sobrien    	    "    -a   list or bind KEY in alternative key map\n"));
499195609Smp    xprintf("%s", CGETS(20, 14,
50059243Sobrien	    "    -b   interpret KEY as a C-, M-, F- or X- key name\n"));
501195609Smp    xprintf("%s", CGETS(20, 15,
50259243Sobrien            "    -s   interpret COMMAND as a literal string to be output\n"));
503195609Smp    xprintf("%s", CGETS(20, 16,
50459243Sobrien            "    -c   interpret COMMAND as a builtin or external command\n"));
505195609Smp    xprintf("%s", CGETS(20, 17,
50659243Sobrien	    "    -v   bind all keys to vi bindings\n"));
507195609Smp    xprintf("%s", CGETS(20, 18,
50859243Sobrien	    "    -e   bind all keys to emacs bindings\n"));
509195609Smp    xprintf("%s", CGETS(20, 19,
51059243Sobrien	    "    -d   bind all keys to default editor's bindings\n"));
511195609Smp    xprintf("%s", CGETS(20, 20,
51259243Sobrien	    "    -l   list editor commands with descriptions\n"));
513195609Smp    xprintf("%s", CGETS(20, 21,
51459243Sobrien	    "    -r   remove KEY's binding\n"));
515195609Smp    xprintf("%s", CGETS(20, 22,
51659243Sobrien	    "    -k   interpret KEY as a symbolic arrow-key name\n"));
517195609Smp    xprintf("%s", CGETS(20, 23,
51859243Sobrien	    "    --   force a break from option processing\n"));
519195609Smp    xprintf("%s", CGETS(20, 24,
52059243Sobrien	    "    -u   (or any invalid option) this message\n"));
52159243Sobrien    xprintf("\n");
522195609Smp    xprintf("%s", CGETS(20, 25,
52359243Sobrien	    "Without KEY or COMMAND, prints all bindings\n"));
524195609Smp    xprintf("%s", CGETS(20, 26,
52559243Sobrien	    "Without COMMAND, prints the binding for KEY.\n"));
52659243Sobrien}
52759243Sobrien
52859243Sobrienstatic void
529167465Smplist_functions(void)
53059243Sobrien{
531145479Smp    struct KeyFuncs *fp;
53259243Sobrien
53359243Sobrien    for (fp = FuncNames; fp->name; fp++) {
53459243Sobrien	xprintf("%s\n          %s\n", fp->name, fp->desc);
53559243Sobrien    }
53659243Sobrien}
537