xref: /freebsd/contrib/tcsh/tc.bind.c (revision 87569f75a91f298c52a71823c04d41cf53c88889)
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 
35 RCSID("$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
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, 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 
234 static void
235 printkey(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 
254 static  KEYCMD
255 parsecmd(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 
270 static void
271 bad_spec(str)
272     Char *str;
273 {
274     xprintf(CGETS(20, 4, "Bad key spec %S\n"), str);
275 }
276 
277 static CStr *
278 parsebind(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 
385 static CStr *
386 parsestring(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 
415 static void
416 print_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 
449 static void
450 printkeys(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 
501 static void
502 bindkey_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 
537 static void
538 list_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
557 static unsigned char APOLLO_0377 = 0377;
558 #else /* sane */
559 # define APOLLO_0377    0377
560 #endif /* apollo */
561 
562 static int
563 tocontrol(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 
583 static char *
584 unparsekey(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 
660 static  KEYCMD
661 getkeycmd(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 
706 static int
707 parsekey(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*/
858 void
859 dobind(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 
1025 static void
1026 pkeys(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