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