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