tc.bind.c revision 145479
1/* $Header: /src/pub/tcsh/tc.bind.c,v 3.39 2005/03/25 18:46:41 kim Exp $ */
2/*
3 * tc.bind.c: Key binding functions
4 */
5/*-
6 * Copyright (c) 1980, 1991 The Regents of the University of California.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33#include "sh.h"
34
35RCSID("$Id: tc.bind.c,v 3.39 2005/03/25 18:46:41 kim Exp $")
36
37#include "ed.h"
38#include "ed.defns.h"
39
40#ifdef OBSOLETE
41static	int    tocontrol	__P((int));
42static	char  *unparsekey	__P((int));
43static	KEYCMD getkeycmd	__P((Char **));
44static	int    parsekey		__P((Char **));
45static	void   pkeys		__P((int, int));
46#endif /* OBSOLETE */
47
48static	void   printkey		__P((KEYCMD *, CStr *));
49static	KEYCMD parsecmd		__P((Char *));
50static  void   bad_spec		__P((Char *));
51static	CStr  *parsestring	__P((Char *, CStr *));
52static	CStr  *parsebind	__P((Char *, CStr *));
53static	void   print_all_keys	__P((void));
54static	void   printkeys	__P((KEYCMD *, int, int));
55static	void   bindkey_usage	__P((void));
56static	void   list_functions	__P((void));
57
58extern int MapsAreInited;
59
60
61
62
63/*ARGSUSED*/
64void
65dobindkey(v, c)
66    Char  **v;
67    struct command *c;
68{
69    KEYCMD *map;
70    int     ntype, no, removeb, key, bindk;
71    Char   *par;
72    Char    p;
73    KEYCMD  cmd;
74    CStr    in;
75    CStr    out;
76    Char    inbuf[200];
77    Char    outbuf[200];
78    uChar   ch;
79    in.buf = inbuf;
80    out.buf = outbuf;
81    in.len = 0;
82    out.len = 0;
83
84    USE(c);
85    if (!MapsAreInited)
86	ed_InitMaps();
87
88    map = CcKeyMap;
89    ntype = XK_CMD;
90    key = removeb = bindk = 0;
91    for (no = 1, par = v[no];
92	 par != NULL && (*par++ & CHAR) == '-'; no++, par = v[no]) {
93	if ((p = (*par & CHAR)) == '-') {
94	    no++;
95	    break;
96	}
97	else
98	    switch (p) {
99	    case 'b':
100		bindk = 1;
101		break;
102	    case 'k':
103		key = 1;
104		break;
105	    case 'a':
106		map = CcAltMap;
107		break;
108	    case 's':
109		ntype = XK_STR;
110		break;
111	    case 'c':
112		ntype = XK_EXE;
113		break;
114	    case 'r':
115		removeb = 1;
116		break;
117	    case 'v':
118		ed_InitVIMaps();
119		return;
120	    case 'e':
121		ed_InitEmacsMaps();
122		return;
123	    case 'd':
124#ifdef VIDEFAULT
125		ed_InitVIMaps();
126#else /* EMACSDEFAULT */
127		ed_InitEmacsMaps();
128#endif /* VIDEFAULT */
129		return;
130	    case 'l':
131		list_functions();
132		return;
133	    default:
134		bindkey_usage();
135		return;
136	    }
137    }
138
139    if (!v[no]) {
140	print_all_keys();
141	return;
142    }
143
144    if (key) {
145	if (!IsArrowKey(v[no]))
146	    xprintf(CGETS(20, 1, "Invalid key name `%S'\n"), v[no]);
147	in.buf = v[no++];
148	in.len = Strlen(in.buf);
149    }
150    else {
151	if (bindk) {
152	    if (parsebind(v[no++], &in) == NULL)
153		return;
154	}
155	else {
156	    if (parsestring(v[no++], &in) == NULL)
157		return;
158	}
159    }
160
161#ifndef WINNT_NATIVE
162    if (in.buf[0] > 0xFF) {
163	bad_spec(in.buf);
164	return;
165    }
166#endif
167    ch = (uChar) in.buf[0];
168
169    if (removeb) {
170	if (key) {
171	    (void) ClearArrowKeys(&in);
172	    return;
173	}
174	if (in.len > 1) {
175	    (void) DeleteXkey(&in);
176	}
177	else if (map[ch] == F_XKEY) {
178	    (void) DeleteXkey(&in);
179	    map[ch] = F_UNASSIGNED;
180	}
181	else {
182	    map[ch] = F_UNASSIGNED;
183	}
184	return;
185    }
186    if (!v[no]) {
187	if (key)
188	    PrintArrowKeys(&in);
189	else
190	    printkey(map, &in);
191	return;
192    }
193    if (v[no + 1]) {
194	bindkey_usage();
195	return;
196    }
197    switch (ntype) {
198    case XK_STR:
199    case XK_EXE:
200	if (parsestring(v[no], &out) == NULL)
201	    return;
202	if (key) {
203	    if (SetArrowKeys(&in, XmapStr(&out), ntype) == -1)
204		xprintf(CGETS(20, 2, "Bad key name: %S\n"), in.buf);
205	}
206	else
207	    AddXkey(&in, XmapStr(&out), ntype);
208	map[ch] = F_XKEY;
209	break;
210    case XK_CMD:
211	if ((cmd = parsecmd(v[no])) == 0)
212	    return;
213	if (key)
214	    (void) SetArrowKeys(&in, XmapCmd((int) cmd), ntype);
215	else {
216	    if (in.len > 1) {
217		AddXkey(&in, XmapCmd((int) cmd), ntype);
218		map[ch] = F_XKEY;
219	    }
220	    else {
221		ClearXkey(map, &in);
222		map[ch] = cmd;
223	    }
224	}
225	break;
226    default:
227	abort();
228	break;
229    }
230    if (key)
231	BindArrowKeys();
232}
233
234static void
235printkey(map, in)
236    KEYCMD *map;
237    CStr   *in;
238{
239    unsigned char outbuf[100];
240    struct KeyFuncs *fp;
241
242    if (in->len < 2) {
243	(void) unparsestring(in, outbuf, STRQQ);
244	for (fp = FuncNames; fp->name; fp++) {
245	    if (fp->func == map[(uChar) *(in->buf)]) {
246		xprintf("%s\t->\t%s\n", outbuf, fp->name);
247	    }
248	}
249    }
250    else
251	PrintXkey(in);
252}
253
254static  KEYCMD
255parsecmd(str)
256    Char   *str;
257{
258    struct KeyFuncs *fp;
259
260    for (fp = FuncNames; fp->name; fp++) {
261	if (strcmp(short2str(str), fp->name) == 0) {
262	    return (KEYCMD) fp->func;
263	}
264    }
265    xprintf(CGETS(20, 3, "Bad command name: %S\n"), str);
266    return 0;
267}
268
269
270static void
271bad_spec(str)
272    Char *str;
273{
274    xprintf(CGETS(20, 4, "Bad key spec %S\n"), str);
275}
276
277static CStr *
278parsebind(s, str)
279    Char *s;
280    CStr *str;
281{
282    Char *b = str->buf;
283
284    if (Iscntrl(*s)) {
285	*b++ = *s;
286	*b = '\0';
287	str->len = (int) (b - str->buf);
288	return str;
289    }
290
291    switch (*s) {
292    case '^':
293	s++;
294#ifdef IS_ASCII
295	*b++ = (*s == '?') ? '\177' : ((*s & CHAR) & 0237);
296#else
297	*b++ = (*s == '?') ? CTL_ESC('\177') : _toebcdic[_toascii[*s & CHAR] & 0237];
298#endif
299	*b = '\0';
300	break;
301
302    case 'F':
303    case 'M':
304    case 'X':
305    case 'C':
306#ifdef WINNT_NATIVE
307    case 'N':
308#endif /* WINNT_NATIVE */
309	if (s[1] != '-' || s[2] == '\0') {
310	    bad_spec(s);
311	    return NULL;
312	}
313	s += 2;
314	switch (s[-2]) {
315	case 'F': case 'f':	/* Turn into ^[str */
316	    *b++ = CTL_ESC('\033');
317	    while ((*b++ = *s++) != '\0')
318		continue;
319	    b--;
320	    break;
321
322	case 'C': case 'c':	/* Turn into ^c */
323#ifdef IS_ASCII
324	    *b++ = (*s == '?') ? '\177' : ((*s & CHAR) & 0237);
325#else
326	    *b++ = (*s == '?') ? CTL_ESC('\177') : _toebcdic[_toascii[*s & CHAR] & 0237];
327#endif
328	    *b = '\0';
329	    break;
330
331	case 'X' : case 'x':	/* Turn into ^Xc */
332#ifdef IS_ASCII
333	    *b++ = 'X' & 0237;
334#else
335	    *b++ = _toebcdic[_toascii['X'] & 0237];
336#endif
337	    *b++ = *s;
338	    *b = '\0';
339	    break;
340
341	case 'M' : case 'm':	/* Turn into 0x80|c */
342	    if (!NoNLSRebind) {
343	    	*b++ = CTL_ESC('\033');
344	    	*b++ = *s;
345	    } else {
346#ifdef IS_ASCII
347	    *b++ = *s | 0x80;
348#else
349	    *b++ = _toebcdic[_toascii[*s] | 0x80];
350#endif
351	    }
352	    *b = '\0';
353	    break;
354#ifdef WINNT_NATIVE
355	case 'N' : case 'n':	/* NT */
356		{
357			Char bnt;
358
359			bnt = nt_translate_bindkey(s);
360			if (bnt != 0)
361				*b++ = bnt;
362			else
363				bad_spec(s);
364		}
365	    break;
366#endif /* WINNT_NATIVE */
367
368	default:
369	    abort();
370	    /*NOTREACHED*/
371	    return NULL;
372	}
373	break;
374
375    default:
376	bad_spec(s);
377	return NULL;
378    }
379
380    str->len = (int) (b - str->buf);
381    return str;
382}
383
384
385static CStr *
386parsestring(str, buf)
387    Char   *str;
388    CStr   *buf;
389{
390    Char   *b;
391    const Char   *p;
392    eChar  es;
393
394    b = buf->buf;
395    if (*str == 0) {
396	xprintf(CGETS(20, 5, "Null string specification\n"));
397	return NULL;
398    }
399
400    for (p = str; *p != 0; p++) {
401	if ((*p & CHAR) == '\\' || (*p & CHAR) == '^') {
402	    if ((es = parseescape(&p)) == CHAR_ERR)
403		return 0;
404	    else
405		*b++ = (Char) es;
406	}
407	else
408	    *b++ = *p & CHAR;
409    }
410    *b = 0;
411    buf->len = (int) (b - buf->buf);
412    return buf;
413}
414
415static void
416print_all_keys()
417{
418    int     prev, i;
419    CStr nilstr;
420    nilstr.buf = NULL;
421    nilstr.len = 0;
422
423
424    xprintf(CGETS(20, 6, "Standard key bindings\n"));
425    prev = 0;
426    for (i = 0; i < 256; i++) {
427	if (CcKeyMap[prev] == CcKeyMap[i])
428	    continue;
429	printkeys(CcKeyMap, prev, i - 1);
430	prev = i;
431    }
432    printkeys(CcKeyMap, prev, i - 1);
433
434    xprintf(CGETS(20, 7, "Alternative key bindings\n"));
435    prev = 0;
436    for (i = 0; i < 256; i++) {
437	if (CcAltMap[prev] == CcAltMap[i])
438	    continue;
439	printkeys(CcAltMap, prev, i - 1);
440	prev = i;
441    }
442    printkeys(CcAltMap, prev, i - 1);
443    xprintf(CGETS(20, 8, "Multi-character bindings\n"));
444    PrintXkey(NULL);	/* print all Xkey bindings */
445    xprintf(CGETS(20, 9, "Arrow key bindings\n"));
446    PrintArrowKeys(&nilstr);
447}
448
449static void
450printkeys(map, first, last)
451    KEYCMD *map;
452    int     first, last;
453{
454    struct KeyFuncs *fp;
455    Char    firstbuf[2], lastbuf[2];
456    CStr fb, lb;
457    unsigned char unparsbuf[10], extrabuf[10];
458    fb.buf = firstbuf;
459    lb.buf = lastbuf;
460
461    firstbuf[0] = (Char) first;
462    firstbuf[1] = 0;
463    lastbuf[0] = (Char) last;
464    lastbuf[1] = 0;
465    fb.len = 1;
466    lb.len = 1;
467
468    if (map[first] == F_UNASSIGNED) {
469	if (first == last)
470	    xprintf(CGETS(20, 10, "%-15s->  is undefined\n"),
471		    unparsestring(&fb, unparsbuf, STRQQ));
472	return;
473    }
474
475    for (fp = FuncNames; fp->name; fp++) {
476	if (fp->func == map[first]) {
477	    if (first == last) {
478		xprintf("%-15s->  %s\n",
479			unparsestring(&fb, unparsbuf, STRQQ), fp->name);
480	    }
481	    else {
482		xprintf("%-4s to %-7s->  %s\n",
483			unparsestring(&fb, unparsbuf, STRQQ),
484			unparsestring(&lb, extrabuf, STRQQ), fp->name);
485	    }
486	    return;
487	}
488    }
489    if (map == CcKeyMap) {
490	xprintf(CGETS(20, 11, "BUG!!! %s isn't bound to anything.\n"),
491		unparsestring(&fb, unparsbuf, STRQQ));
492	xprintf("CcKeyMap[%d] == %d\n", first, CcKeyMap[first]);
493    }
494    else {
495	xprintf(CGETS(20, 11, "BUG!!! %s isn't bound to anything.\n"),
496		unparsestring(&fb, unparsbuf, STRQQ));
497	xprintf("CcAltMap[%d] == %d\n", first, CcAltMap[first]);
498    }
499}
500
501static void
502bindkey_usage()
503{
504    xprintf(CGETS(20, 12,
505	    "Usage: bindkey [options] [--] [KEY [COMMAND]]\n"));
506    xprintf(CGETS(20, 13,
507    	    "    -a   list or bind KEY in alternative key map\n"));
508    xprintf(CGETS(20, 14,
509	    "    -b   interpret KEY as a C-, M-, F- or X- key name\n"));
510    xprintf(CGETS(20, 15,
511            "    -s   interpret COMMAND as a literal string to be output\n"));
512    xprintf(CGETS(20, 16,
513            "    -c   interpret COMMAND as a builtin or external command\n"));
514    xprintf(CGETS(20, 17,
515	    "    -v   bind all keys to vi bindings\n"));
516    xprintf(CGETS(20, 18,
517	    "    -e   bind all keys to emacs bindings\n"));
518    xprintf(CGETS(20, 19,
519	    "    -d   bind all keys to default editor's bindings\n"));
520    xprintf(CGETS(20, 20,
521	    "    -l   list editor commands with descriptions\n"));
522    xprintf(CGETS(20, 21,
523	    "    -r   remove KEY's binding\n"));
524    xprintf(CGETS(20, 22,
525	    "    -k   interpret KEY as a symbolic arrow-key name\n"));
526    xprintf(CGETS(20, 23,
527	    "    --   force a break from option processing\n"));
528    xprintf(CGETS(20, 24,
529	    "    -u   (or any invalid option) this message\n"));
530    xprintf("\n");
531    xprintf(CGETS(20, 25,
532	    "Without KEY or COMMAND, prints all bindings\n"));
533    xprintf(CGETS(20, 26,
534	    "Without COMMAND, prints the binding for KEY.\n"));
535}
536
537static void
538list_functions()
539{
540    struct KeyFuncs *fp;
541
542    for (fp = FuncNames; fp->name; fp++) {
543	xprintf("%s\n          %s\n", fp->name, fp->desc);
544    }
545}
546
547#ifdef OBSOLETE
548
549/*
550 * Unfortunately the apollo optimizer does not like & operations
551 * with 0377, and produces illegal instructions. So we make it
552 * an unsigned char, and hope for the best.
553 * Of-course the compiler is smart enough to produce bad assembly
554 * language instructions, but dumb when it comes to fold the constant :-)
555 */
556#ifdef apollo
557static unsigned char APOLLO_0377 = 0377;
558#else /* sane */
559# define APOLLO_0377    0377
560#endif /* apollo */
561
562static int
563tocontrol(c)
564    int    c;
565{
566    c &= CHAR;
567    if (Islower(c))
568	c = Toupper(c);
569    else if (c == ' ')
570	c = '@';
571    if (c == '?')
572	c = CTL_ESC('\177');
573    else
574#ifdef IS_ASCII
575	c &= 037;
576#else
577	/* EBCDIC: simulate ASCII-behavior by transforming to ASCII and back */
578	c  = _toebcdic[_toascii[c] & 037];
579#endif
580    return (c);
581}
582
583static char *
584unparsekey(c)			/* 'c' -> "c", '^C' -> "^" + "C" */
585    int c;
586{
587    char *cp;
588    static char tmp[10];
589
590    cp = tmp;
591
592    if (c & 0400) {
593	*cp++ = 'A';
594	*cp++ = '-';
595	c &= APOLLO_0377;
596    }
597    if ((c & META) && !(Isprint(c) || (Iscntrl(c) && Isprint(c | 0100)))) {
598	*cp++ = 'M';
599	*cp++ = '-';
600	c &= ASCII;
601    }
602    if (Isprint(c)) {
603	*cp++ = (char) c;
604	*cp = '\0';
605	return (tmp);
606    }
607    switch (c) {
608    case ' ':
609	(void) strcpy(cp, "Spc");
610	return (tmp);
611    case '\n':
612	(void) strcpy(cp, "Lfd");
613	return (tmp);
614    case '\r':
615	(void) strcpy(cp, "Ret");
616	return (tmp);
617    case '\t':
618	(void) strcpy(cp, "Tab");
619	return (tmp);
620#ifdef IS_ASCII
621    case '\033':
622	(void) strcpy(cp, "Esc");
623	return (tmp);
624    case '\177':
625	(void) strcpy(cp, "Del");
626	return (tmp);
627    default:
628	*cp++ = '^';
629	if (c == '\177') {
630	    *cp++ = '?';
631	}
632	else {
633	    *cp++ = c | 0100;
634	}
635	*cp = '\0';
636	return (tmp);
637#else /* IS_ASCII */
638    default:
639        if (*cp == CTL_ESC('\033')) {
640	    (void) strcpy(cp, "Esc");
641	    return (tmp);
642	}
643	else if (*cp == CTL_ESC('\177')) {
644	    (void) strcpy(cp, "Del");
645	    return (tmp);
646	}
647	else if (Isupper(_toebcdic[_toascii[c]|0100])
648		|| strchr("@[\\]^_", _toebcdic[_toascii[c]|0100]) != NULL) {
649	    *cp++ = '^';
650	    *cp++ = _toebcdic[_toascii[c]|0100]
651	}
652	else {
653	    xsnprintf(cp, 3, "\\%3.3o", c);
654	    cp += 4;
655	}
656#endif /* IS_ASCII */
657    }
658}
659
660static  KEYCMD
661getkeycmd(sp)
662    Char  **sp;
663{
664    Char *s = *sp;
665    char c;
666    KEYCMD keycmd = F_UNASSIGNED;
667    KEYCMD *map;
668    int     meta = 0;
669    Char   *ret_sp = s;
670
671    map = CcKeyMap;
672
673    while (*s) {
674	if (*s == '^' && s[1]) {
675	    s++;
676	    c = tocontrol(*s++);
677	}
678	else
679	    c = *s++;
680
681	if (*s == '\0')
682	    break;
683
684	switch (map[c | meta]) {
685	case F_METANEXT:
686	    meta = META;
687	    keycmd = F_METANEXT;
688	    ret_sp = s;
689	    break;
690
691	case F_XKEY:
692	    keycmd = F_XKEY;
693	    ret_sp = s;
694	    /* FALLTHROUGH */
695
696	default:
697	    *sp = ret_sp;
698	    return (keycmd);
699
700	}
701    }
702    *sp = ret_sp;
703    return (keycmd);
704}
705
706static int
707parsekey(sp)
708    Char  **sp;			/* Return position of first unparsed character
709				 * for return value -2 (xkeynext) */
710{
711    int c, meta = 0, control = 0, ctrlx = 0;
712    Char   *s = *sp;
713    KEYCMD  keycmd;
714
715    if (s == NULL) {
716	xprintf(CGETS(20, 27, "bad key specification -- null string\n"));
717	return -1;
718    }
719    if (*s == 0) {
720	xprintf(CGETS(20, 28, "bad key specification -- empty string\n"));
721	return -1;
722    }
723
724    (void) strip(s);		/* trim to 7 bits. */
725
726    if (s[1] == 0)		/* single char */
727	return (s[0] & APOLLO_0377);
728
729    if ((s[0] == 'F' || s[0] == 'f') && s[1] == '-') {
730	if (s[2] == 0) {
731	    xprintf(CGETS(20, 29,
732		   "Bad function-key specification.  Null key not allowed\n"));
733	    return (-1);
734	}
735	*sp = s + 2;
736	return (-2);
737    }
738
739    if (s[0] == '0' && s[1] == 'x') {	/* if 0xn, then assume number */
740	c = 0;
741	for (s += 2; *s; s++) {	/* convert to hex; skip the first 0 */
742	    c *= 16;
743	    if (!Isxdigit(*s)) {
744		xprintf(CGETS(20, 30,
745			"bad key specification -- malformed hex number\n"));
746		return -1;	/* error */
747	    }
748	    if (Isdigit(*s))
749		c += *s - '0';
750	    else if (*s >= 'a' && *s <= 'f')
751		c += *s - 'a' + 0xA;
752	    else if (*s >= 'F' && *s <= 'F')
753		c += *s - 'A' + 0xA;
754	}
755    }
756    else if (s[0] == '0' && Isdigit(s[1])) {	/* if 0n, then assume number */
757	c = 0;
758	for (s++; *s; s++) {	/* convert to octal; skip the first 0 */
759	    if (!Isdigit(*s) || *s == '8' || *s == '9') {
760		xprintf(CGETS(20, 31,
761			"bad key specification -- malformed octal number\n"));
762		return -1;	/* error */
763	    }
764	    c = (c * 8) + *s - '0';
765	}
766    }
767    else if (Isdigit(s[0]) && Isdigit(s[1])) {	/* decimal number */
768	c = 0;
769	for (; *s; s++) {	/* convert to octal; skip the first 0 */
770	    if (!Isdigit(*s)) {
771		xprintf(CGETS(20, 32,
772		       "bad key specification -- malformed decimal number\n"));
773		return -1;	/* error */
774	    }
775	    c = (c * 10) + *s - '0';
776	}
777    }
778    else {
779	keycmd = getkeycmd(&s);
780
781	if ((s[0] == 'X' || s[0] == 'x') && s[1] == '-') {	/* X- */
782	    ctrlx++;
783	    s += 2;
784	    keycmd = getkeycmd(&s);
785	}
786	if ((*s == 'm' || *s == 'M') && s[1] == '-') {	/* meta */
787	    meta++;
788	    s += 2;
789	    keycmd = getkeycmd(&s);
790	}
791	else if (keycmd == F_METANEXT && *s) {	/* meta */
792	    meta++;
793	    keycmd = getkeycmd(&s);
794	}
795	if (*s == '^' && s[1]) {
796	    control++;
797	    s++;
798	    keycmd = getkeycmd(&s);
799	}
800	else if ((*s == 'c' || *s == 'C') && s[1] == '-') {	/* control */
801	    control++;
802	    s += 2;
803	    keycmd = getkeycmd(&s);
804	}
805
806	if (keycmd == F_XKEY) {
807	    if (*s == 0) {
808		xprintf(CGETS(20, 33,
809			      "Bad function-key specification.\n"));
810		xprintf(CGETS(20, 34, "Null key not allowed\n"));
811		return (-1);
812	    }
813	    *sp = s;
814	    return (-2);
815	}
816
817	if (s[1] != 0) {	/* if symbolic name */
818	    char   *ts;
819
820	    ts = short2str(s);
821	    if (!strcmp(ts, "space") || !strcmp(ts, "Spc"))
822		c = ' ';
823	    else if (!strcmp(ts, "return") || !strcmp(ts, "Ret"))
824		c = '\r';
825	    else if (!strcmp(ts, "newline") || !strcmp(ts, "Lfd"))
826		c = '\n';
827	    else if (!strcmp(ts, "linefeed"))
828		c = '\n';
829	    else if (!strcmp(ts, "tab"))
830		c = '\t';
831	    else if (!strcmp(ts, "escape") || !strcmp(ts, "Esc"))
832		c = CTL_ESC('\033');
833	    else if (!strcmp(ts, "backspace"))
834		c = '\b';
835	    else if (!strcmp(ts, "delete"))
836		c = CTL_ESC('\177');
837	    else {
838		xprintf(CGETS(20, 35,
839			"bad key specification -- unknown name \"%S\"\n"), s);
840		return -1;	/* error */
841	    }
842	}
843	else
844	    c = *s;		/* just a single char */
845
846	if (control)
847	    c = tocontrol(c);
848	if (meta)
849	    c |= META;
850	if (ctrlx)
851	    c |= 0400;
852    }
853    return (c & 0777);
854}
855
856
857/*ARGSUSED*/
858void
859dobind(v, dummy)
860    Char **v;
861    struct command *dummy;
862{
863    int c;
864    struct KeyFuncs *fp;
865    int i, prev;
866    Char   *p, *l;
867    CStr    cstr;
868    Char    buf[1000];
869
870    USE(dummy);
871    /*
872     * Assume at this point that i'm given 2 or 3 args - 'bind', the f-name,
873     * and the key; or 'bind' key to print the func for that key.
874     */
875
876    if (!MapsAreInited)
877	ed_InitMaps();
878
879    if (v[1] && v[2] && v[3]) {
880	xprintf(CGETS(20, 36,
881	 "usage: bind [KEY | COMMAND KEY | \"emacs\" | \"vi\" | \"-a\"]\n"));
882	return;
883    }
884
885    if (v[1] && v[2]) {		/* if bind FUNCTION KEY */
886	for (fp = FuncNames; fp->name; fp++) {
887	    if (strcmp(short2str(v[1]), fp->name) == 0) {
888		Char   *s = v[2];
889
890		if ((c = parsekey(&s)) == -1)
891		    return;
892		if (c == -2) {	/* extended key */
893		    for (i = 0; i < 256; i++) {
894			if (i != CTL_ESC('\033') && (CcKeyMap[i] == F_XKEY ||
895					 CcAltMap[i] == F_XKEY)) {
896			    p = buf;
897#ifdef IS_ASCII
898			    if (i > 0177) {
899				*p++ = 033;
900				*p++ = i & ASCII;
901			    }
902			    else {
903				*p++ = (Char) i;
904			    }
905#else
906			    *p++ = (Char) i;
907#endif
908			    for (l = s; *l != 0; l++) {
909				*p++ = *l;
910			    }
911			    *p = 0;
912			    cstr.buf = buf;
913			    cstr.len = Strlen(buf);
914			    AddXkey(&cstr, XmapCmd(fp->func), XK_CMD);
915			}
916		    }
917		    return;
918		}
919		if (c & 0400) {
920		    if (VImode) {
921			CcAltMap[c & APOLLO_0377] = fp->func;
922			/* bind the vi cmd mode key */
923			if (c & META) {
924			    buf[0] = CTL_ESC('\033');
925			    buf[1] = c & ASCII;
926			    buf[2] = 0;
927			    cstr.buf = buf;
928			    cstr.len = Strlen(buf);
929			    AddXkey(&cstr, XmapCmd(fp->func), XK_CMD);
930			}
931		    }
932		    else {
933			buf[0] = CTL_ESC('\030');	/* ^X */
934			buf[1] = c & APOLLO_0377;
935			buf[2] = 0;
936			cstr.buf = buf;
937			cstr.len = Strlen(buf);
938			AddXkey(&cstr, XmapCmd(fp->func), XK_CMD);
939			CcKeyMap[CTL_ESC('\030')] = F_XKEY;
940		    }
941		}
942		else {
943		    CcKeyMap[c] = fp->func;	/* bind the key */
944		    if (c & META) {
945			buf[0] = CTL_ESC('\033');
946			buf[1] = c & ASCII;
947			buf[2] = 0;
948			cstr.buf = buf;
949			cstr.len = Strlen(buf);
950			AddXkey(&cstr, XmapCmd(fp->func), XK_CMD);
951		    }
952		}
953		return;
954	    }
955	}
956	stderror(ERR_NAME | ERR_STRING, CGETS(20, 37, "Invalid function"));
957    }
958    else if (v[1]) {
959	char   *cv = short2str(v[1]);
960
961	if (strcmp(cv, "list") == 0) {
962	    for (fp = FuncNames; fp->name; fp++) {
963		xprintf("%s\n", fp->name);
964	    }
965	    return;
966	}
967	if ((strcmp(cv, "emacs") == 0) ||
968#ifndef VIDEFAULT
969	    (strcmp(cv, "defaults") == 0) ||
970	    (strcmp(cv, "default") == 0) ||
971#endif
972	    (strcmp(cv, "mg") == 0) ||
973	    (strcmp(cv, "gnumacs") == 0)) {
974	    /* reset keys to default */
975	    ed_InitEmacsMaps();
976#ifdef VIDEFAULT
977	}
978	else if ((strcmp(cv, "vi") == 0)
979		 || (strcmp(cv, "default") == 0)
980		 || (strcmp(cv, "defaults") == 0)) {
981#else
982	}
983	else if (strcmp(cv, "vi") == 0) {
984#endif
985	    ed_InitVIMaps();
986	}
987	else {			/* want to know what this key does */
988	    Char   *s = v[1];
989
990	    if ((c = parsekey(&s)) == -1)
991		return;
992	    if (c == -2) {	/* extended key */
993		cstr.buf = s;
994		cstr.len = Strlen(s);
995		PrintXkey(&cstr);
996		return;
997	    }
998	    pkeys(c, c);	/* must be regular key */
999	}
1000    }
1001    else {			/* list all the bindings */
1002	prev = 0;
1003	for (i = 0; i < 256; i++) {
1004	    if (CcKeyMap[prev] == CcKeyMap[i])
1005		continue;
1006	    pkeys(prev, i - 1);
1007	    prev = i;
1008	}
1009	pkeys(prev, i - 1);
1010	prev = 0;
1011	for (i = 256; i < 512; i++) {
1012	    if (CcAltMap[prev & APOLLO_0377] == CcAltMap[i & APOLLO_0377])
1013		continue;
1014	    pkeys(prev, i - 1);
1015	    prev = i;
1016	}
1017	pkeys(prev, i - 1);
1018	cstr.buf = NULL;
1019	cstr.len = 0;
1020	PrintXkey(&cstr);	/* print all Xkey bindings */
1021    }
1022    return;
1023}
1024
1025static void
1026pkeys(first, last)
1027    int first, last;
1028{
1029    struct KeyFuncs *fp;
1030    KEYCMD *map;
1031    int mask;
1032    char    buf[8];
1033
1034    if (last & 0400) {
1035	map = CcAltMap;
1036	first &= APOLLO_0377;
1037	last &= APOLLO_0377;
1038	mask = 0400;
1039    }
1040    else {
1041	map = CcKeyMap;
1042	mask = 0;
1043    }
1044    if (map[first] == F_UNASSIGNED) {
1045	if (first == last)
1046	    xprintf(CGETS(20, 38, " %s\t\tis undefined\n"),
1047		    unparsekey(first | mask));
1048	return;
1049    }
1050
1051    for (fp = FuncNames; fp->name; fp++) {
1052	if (fp->func == map[first]) {
1053	    if (first == last)
1054		xprintf(" %s\t\t%s\n",
1055			unparsekey((first & APOLLO_0377) | mask), fp->name);
1056	    else {
1057		(void) strcpy(buf, unparsekey((first & APOLLO_0377) | mask));
1058		xprintf(" %s..%s\t\t%s\n", buf,
1059		        unparsekey((last & APOLLO_0377) | mask), fp->name);
1060	    }
1061	    return;
1062	}
1063    }
1064    if (map == CcKeyMap) {
1065	xprintf(CGETS(20, 11, "BUG!!! %s isn't bound to anything.\n"),
1066		unparsekey(first));
1067	xprintf("CcKeyMap[%d] == %d\n", first, CcKeyMap[first]);
1068    }
1069    else {
1070	xprintf(CGETS(20, 11, "BUG!!! %s isn't bound to anything.\n"),
1071		unparsekey(first & 0400));
1072	xprintf("CcAltMap[%d] == %d\n", first, CcAltMap[first]);
1073    }
1074}
1075#endif /* OBSOLETE */
1076