xref: /freebsd/contrib/tcsh/tc.bind.c (revision 77a0943ded95b9e6438f7db70c4a28e4d93946d4)
1 /* $Header: /src/pub/tcsh/tc.bind.c,v 3.33 1998/11/24 18:17:40 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.33 1998/11/24 18:17:40 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 #ifndef _OSD_POSIX
296 	*b++ = (*s == '?') ? '\177' : ((*s & CHAR) & 0237);
297 #else /*_OSD_POSIX*/
298 	*b++ = (*s == '?') ? CTL_ESC('\177') : _toebcdic[_toascii[*s & CHAR] & 0237];
299 #endif /*_OSD_POSIX*/
300 	*b = '\0';
301 	break;
302 
303     case 'F':
304     case 'M':
305     case 'X':
306     case 'C':
307 #ifdef WINNT
308     case 'N':
309 #endif /* WINNT */
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 #ifndef _OSD_POSIX
325 	    *b++ = (*s == '?') ? '\177' : ((*s & CHAR) & 0237);
326 #else /*_OSD_POSIX*/
327 	    *b++ = (*s == '?') ? CTL_ESC('\177') : _toebcdic[_toascii[*s & CHAR] & 0237];
328 #endif /*_OSD_POSIX*/
329 	    *b = '\0';
330 	    break;
331 
332 	case 'X' : case 'x':	/* Turn into ^Xc */
333 #ifndef _OSD_POSIX
334 	    *b++ = 'X' & 0237;
335 #else /*_OSD_POSIX*/
336 	    *b++ = _toebcdic[_toascii['X'] & 0237];
337 #endif /*_OSD_POSIX*/
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 #ifndef _OSD_POSIX
350 	    *b++ = *s | 0x80;
351 #else /*_OSD_POSIX*/
352 	    *b++ = _toebcdic[_toascii[*s] | 0x80];
353 #endif /*_OSD_POSIX*/
354 #ifdef DSPMBYTE
355 	    }
356 #endif /* DSPMBYTE */
357 	    *b = '\0';
358 	    break;
359 #ifdef WINNT
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 */
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 #ifndef _OSD_POSIX
580 	c &= 037;
581 #else /* EBCDIC: simulate ASCII-behavior by transforming to ASCII and back */
582 	c  = _toebcdic[_toascii[c] & 037];
583 #endif
584     return (c);
585 }
586 
587 static char *
588 unparsekey(c)			/* 'c' -> "c", '^C' -> "^" + "C" */
589     register int c;
590 {
591     register char *cp;
592     static char tmp[10];
593 
594     cp = tmp;
595 
596     if (c & 0400) {
597 	*cp++ = 'A';
598 	*cp++ = '-';
599 	c &= APOLLO_0377;
600     }
601     if ((c & META) && !(Isprint(c) || (Iscntrl(c) && Isprint(c | 0100)))) {
602 	*cp++ = 'M';
603 	*cp++ = '-';
604 	c &= ASCII;
605     }
606     if (Isprint(c)) {
607 	*cp++ = (char) c;
608 	*cp = '\0';
609 	return (tmp);
610     }
611     switch (c) {
612     case ' ':
613 	(void) strcpy(cp, "Spc");
614 	return (tmp);
615     case '\n':
616 	(void) strcpy(cp, "Lfd");
617 	return (tmp);
618     case '\r':
619 	(void) strcpy(cp, "Ret");
620 	return (tmp);
621     case '\t':
622 	(void) strcpy(cp, "Tab");
623 	return (tmp);
624 #ifndef _OSD_POSIX
625     case '\033':
626 	(void) strcpy(cp, "Esc");
627 	return (tmp);
628     case '\177':
629 	(void) strcpy(cp, "Del");
630 	return (tmp);
631     default:
632 	*cp++ = '^';
633 	if (c == '\177') {
634 	    *cp++ = '?';
635 	}
636 	else {
637 	    *cp++ = c | 0100;
638 	}
639 	*cp = '\0';
640 	return (tmp);
641 #else /*_OSD_POSIX*/
642     default:
643         if (*cp == CTL_ESC('\033')) {
644 	    (void) strcpy(cp, "Esc");
645 	    return (tmp);
646 	}
647 	else if (*cp == CTL_ESC('\177')) {
648 	    (void) strcpy(cp, "Del");
649 	    return (tmp);
650 	}
651 	else if (Isupper(_toebcdic[_toascii[c]|0100])
652 		|| strchr("@[\\]^_", _toebcdic[_toascii[c]|0100]) != NULL) {
653 	    *cp++ = '^';
654 	    *cp++ = _toebcdic[_toascii[c]|0100]
655 	}
656 	else {
657 	    xsnprintf(cp, 3, "\\%3.3o", c);
658 	    cp += 4;
659 	}
660 #endif /*_OSD_POSIX*/
661     }
662 }
663 
664 static  KEYCMD
665 getkeycmd(sp)
666     Char  **sp;
667 {
668     register Char *s = *sp;
669     register char c;
670     register KEYCMD keycmd = F_UNASSIGNED;
671     KEYCMD *map;
672     int     meta = 0;
673     Char   *ret_sp = s;
674 
675     map = CcKeyMap;
676 
677     while (*s) {
678 	if (*s == '^' && s[1]) {
679 	    s++;
680 	    c = tocontrol(*s++);
681 	}
682 	else
683 	    c = *s++;
684 
685 	if (*s == '\0')
686 	    break;
687 
688 	switch (map[c | meta]) {
689 	case F_METANEXT:
690 	    meta = META;
691 	    keycmd = F_METANEXT;
692 	    ret_sp = s;
693 	    break;
694 
695 	case F_XKEY:
696 	    keycmd = F_XKEY;
697 	    ret_sp = s;
698 	    /* FALLTHROUGH */
699 
700 	default:
701 	    *sp = ret_sp;
702 	    return (keycmd);
703 
704 	}
705     }
706     *sp = ret_sp;
707     return (keycmd);
708 }
709 
710 static int
711 parsekey(sp)
712     Char  **sp;			/* Return position of first unparsed character
713 				 * for return value -2 (xkeynext) */
714 {
715     register int c, meta = 0, control = 0, ctrlx = 0;
716     Char   *s = *sp;
717     KEYCMD  keycmd;
718 
719     if (s == NULL) {
720 	xprintf(CGETS(20, 27, "bad key specification -- null string\n"));
721 	return -1;
722     }
723     if (*s == 0) {
724 	xprintf(CGETS(20, 28, "bad key specification -- empty string\n"));
725 	return -1;
726     }
727 
728     (void) strip(s);		/* trim to 7 bits. */
729 
730     if (s[1] == 0)		/* single char */
731 	return (s[0] & APOLLO_0377);
732 
733     if ((s[0] == 'F' || s[0] == 'f') && s[1] == '-') {
734 	if (s[2] == 0) {
735 	    xprintf(CGETS(20, 29,
736 		   "Bad function-key specification.  Null key not allowed\n"));
737 	    return (-1);
738 	}
739 	*sp = s + 2;
740 	return (-2);
741     }
742 
743     if (s[0] == '0' && s[1] == 'x') {	/* if 0xn, then assume number */
744 	c = 0;
745 	for (s += 2; *s; s++) {	/* convert to hex; skip the first 0 */
746 	    c *= 16;
747 	    if (!Isxdigit(*s)) {
748 		xprintf(CGETS(20, 30,
749 			"bad key specification -- malformed hex number\n"));
750 		return -1;	/* error */
751 	    }
752 	    if (Isdigit(*s))
753 		c += *s - '0';
754 	    else if (*s >= 'a' && *s <= 'f')
755 		c += *s - 'a' + 0xA;
756 	    else if (*s >= 'F' && *s <= 'F')
757 		c += *s - 'A' + 0xA;
758 	}
759     }
760     else if (s[0] == '0' && Isdigit(s[1])) {	/* if 0n, then assume number */
761 	c = 0;
762 	for (s++; *s; s++) {	/* convert to octal; skip the first 0 */
763 	    if (!Isdigit(*s) || *s == '8' || *s == '9') {
764 		xprintf(CGETS(20, 31,
765 			"bad key specification -- malformed octal number\n"));
766 		return -1;	/* error */
767 	    }
768 	    c = (c * 8) + *s - '0';
769 	}
770     }
771     else if (Isdigit(s[0]) && Isdigit(s[1])) {	/* decimal number */
772 	c = 0;
773 	for (; *s; s++) {	/* convert to octal; skip the first 0 */
774 	    if (!Isdigit(*s)) {
775 		xprintf(CGETS(20, 32,
776 		       "bad key specification -- malformed decimal number\n"));
777 		return -1;	/* error */
778 	    }
779 	    c = (c * 10) + *s - '0';
780 	}
781     }
782     else {
783 	keycmd = getkeycmd(&s);
784 
785 	if ((s[0] == 'X' || s[0] == 'x') && s[1] == '-') {	/* X- */
786 	    ctrlx++;
787 	    s += 2;
788 	    keycmd = getkeycmd(&s);
789 	}
790 	if ((*s == 'm' || *s == 'M') && s[1] == '-') {	/* meta */
791 	    meta++;
792 	    s += 2;
793 	    keycmd = getkeycmd(&s);
794 	}
795 	else if (keycmd == F_METANEXT && *s) {	/* meta */
796 	    meta++;
797 	    keycmd = getkeycmd(&s);
798 	}
799 	if (*s == '^' && s[1]) {
800 	    control++;
801 	    s++;
802 	    keycmd = getkeycmd(&s);
803 	}
804 	else if ((*s == 'c' || *s == 'C') && s[1] == '-') {	/* control */
805 	    control++;
806 	    s += 2;
807 	    keycmd = getkeycmd(&s);
808 	}
809 
810 	if (keycmd == F_XKEY) {
811 	    if (*s == 0) {
812 		xprintf(CGETS(20, 33,
813 			      "Bad function-key specification.\n"));
814 		xprintf(CGETS(20, 34, "Null key not allowed\n"));
815 		return (-1);
816 	    }
817 	    *sp = s;
818 	    return (-2);
819 	}
820 
821 	if (s[1] != 0) {	/* if symbolic name */
822 	    char   *ts;
823 
824 	    ts = short2str(s);
825 	    if (!strcmp(ts, "space") || !strcmp(ts, "Spc"))
826 		c = ' ';
827 	    else if (!strcmp(ts, "return") || !strcmp(ts, "Ret"))
828 		c = '\r';
829 	    else if (!strcmp(ts, "newline") || !strcmp(ts, "Lfd"))
830 		c = '\n';
831 	    else if (!strcmp(ts, "linefeed"))
832 		c = '\n';
833 	    else if (!strcmp(ts, "tab"))
834 		c = '\t';
835 	    else if (!strcmp(ts, "escape") || !strcmp(ts, "Esc"))
836 		c = CTL_ESC('\033');
837 	    else if (!strcmp(ts, "backspace"))
838 		c = '\b';
839 	    else if (!strcmp(ts, "delete"))
840 		c = CTL_ESC('\177');
841 	    else {
842 		xprintf(CGETS(20, 35,
843 			"bad key specification -- unknown name \"%S\"\n"), s);
844 		return -1;	/* error */
845 	    }
846 	}
847 	else
848 	    c = *s;		/* just a single char */
849 
850 	if (control)
851 	    c = tocontrol(c);
852 	if (meta)
853 	    c |= META;
854 	if (ctrlx)
855 	    c |= 0400;
856     }
857     return (c & 0777);
858 }
859 
860 
861 /*ARGSUSED*/
862 void
863 dobind(v, dummy)
864     register Char **v;
865     struct command *dummy;
866 {
867     register int c;
868     register struct KeyFuncs *fp;
869     register int i, prev;
870     Char   *p, *l;
871     CStr    cstr;
872     Char    buf[1000];
873 
874     USE(dummy);
875     /*
876      * Assume at this point that i'm given 2 or 3 args - 'bind', the f-name,
877      * and the key; or 'bind' key to print the func for that key.
878      */
879 
880     if (!MapsAreInited)
881 	ed_InitMaps();
882 
883     if (v[1] && v[2] && v[3]) {
884 	xprintf(CGETS(20, 36,
885 	 "usage: bind [KEY | COMMAND KEY | \"emacs\" | \"vi\" | \"-a\"]\n"));
886 	return;
887     }
888 
889     if (v[1] && v[2]) {		/* if bind FUNCTION KEY */
890 	for (fp = FuncNames; fp->name; fp++) {
891 	    if (strcmp(short2str(v[1]), fp->name) == 0) {
892 		Char   *s = v[2];
893 
894 		if ((c = parsekey(&s)) == -1)
895 		    return;
896 		if (c == -2) {	/* extended key */
897 		    for (i = 0; i < 256; i++) {
898 			if (i != CTL_ESC('\033') && (CcKeyMap[i] == F_XKEY ||
899 					 CcAltMap[i] == F_XKEY)) {
900 			    p = buf;
901 #ifndef _OSD_POSIX /* this is only for ASCII, not for EBCDIC */
902 			    if (i > 0177) {
903 				*p++ = 033;
904 				*p++ = i & ASCII;
905 			    }
906 			    else {
907 				*p++ = (Char) i;
908 			    }
909 #else /*_OSD_POSIX*/
910 			    *p++ = (Char) i;
911 #endif /*_OSD_POSIX*/
912 			    for (l = s; *l != 0; l++) {
913 				*p++ = *l;
914 			    }
915 			    *p = 0;
916 			    cstr.buf = buf;
917 			    cstr.len = Strlen(buf);
918 			    AddXkey(&cstr, XmapCmd(fp->func), XK_CMD);
919 			}
920 		    }
921 		    return;
922 		}
923 		if (c & 0400) {
924 		    if (VImode) {
925 			CcAltMap[c & APOLLO_0377] = fp->func;
926 			/* bind the vi cmd mode key */
927 			if (c & META) {
928 			    buf[0] = CTL_ESC('\033');
929 			    buf[1] = c & ASCII;
930 			    buf[2] = 0;
931 			    cstr.buf = buf;
932 			    cstr.len = Strlen(buf);
933 			    AddXkey(&cstr, XmapCmd(fp->func), XK_CMD);
934 			}
935 		    }
936 		    else {
937 			buf[0] = CTL_ESC('\030');	/* ^X */
938 			buf[1] = c & APOLLO_0377;
939 			buf[2] = 0;
940 			cstr.buf = buf;
941 			cstr.len = Strlen(buf);
942 			AddXkey(&cstr, XmapCmd(fp->func), XK_CMD);
943 			CcKeyMap[CTL_ESC('\030')] = F_XKEY;
944 		    }
945 		}
946 		else {
947 		    CcKeyMap[c] = fp->func;	/* bind the key */
948 		    if (c & META) {
949 			buf[0] = CTL_ESC('\033');
950 			buf[1] = c & ASCII;
951 			buf[2] = 0;
952 			cstr.buf = buf;
953 			cstr.len = Strlen(buf);
954 			AddXkey(&cstr, XmapCmd(fp->func), XK_CMD);
955 		    }
956 		}
957 		return;
958 	    }
959 	}
960 	stderror(ERR_NAME | ERR_STRING, CGETS(20, 37, "Invalid function"));
961     }
962     else if (v[1]) {
963 	char   *cv = short2str(v[1]);
964 
965 	if (strcmp(cv, "list") == 0) {
966 	    for (fp = FuncNames; fp->name; fp++) {
967 		xprintf("%s\n", fp->name);
968 	    }
969 	    return;
970 	}
971 	if ((strcmp(cv, "emacs") == 0) ||
972 #ifndef VIDEFAULT
973 	    (strcmp(cv, "defaults") == 0) ||
974 	    (strcmp(cv, "default") == 0) ||
975 #endif
976 	    (strcmp(cv, "mg") == 0) ||
977 	    (strcmp(cv, "gnumacs") == 0)) {
978 	    /* reset keys to default */
979 	    ed_InitEmacsMaps();
980 #ifdef VIDEFAULT
981 	}
982 	else if ((strcmp(cv, "vi") == 0)
983 		 || (strcmp(cv, "default") == 0)
984 		 || (strcmp(cv, "defaults") == 0)) {
985 #else
986 	}
987 	else if (strcmp(cv, "vi") == 0) {
988 #endif
989 	    ed_InitVIMaps();
990 	}
991 	else {			/* want to know what this key does */
992 	    Char   *s = v[1];
993 
994 	    if ((c = parsekey(&s)) == -1)
995 		return;
996 	    if (c == -2) {	/* extended key */
997 		cstr.buf = s;
998 		cstr.len = Strlen(s);
999 		PrintXkey(&cstr);
1000 		return;
1001 	    }
1002 	    pkeys(c, c);	/* must be regular key */
1003 	}
1004     }
1005     else {			/* list all the bindings */
1006 	prev = 0;
1007 	for (i = 0; i < 256; i++) {
1008 	    if (CcKeyMap[prev] == CcKeyMap[i])
1009 		continue;
1010 	    pkeys(prev, i - 1);
1011 	    prev = i;
1012 	}
1013 	pkeys(prev, i - 1);
1014 	prev = 0;
1015 	for (i = 256; i < 512; i++) {
1016 	    if (CcAltMap[prev & APOLLO_0377] == CcAltMap[i & APOLLO_0377])
1017 		continue;
1018 	    pkeys(prev, i - 1);
1019 	    prev = i;
1020 	}
1021 	pkeys(prev, i - 1);
1022 	cstr.buf = NULL;
1023 	cstr.len = 0;
1024 	PrintXkey(&cstr);	/* print all Xkey bindings */
1025     }
1026     return;
1027 }
1028 
1029 static void
1030 pkeys(first, last)
1031     register int first, last;
1032 {
1033     register struct KeyFuncs *fp;
1034     register KEYCMD *map;
1035     int mask;
1036     char    buf[8];
1037 
1038     if (last & 0400) {
1039 	map = CcAltMap;
1040 	first &= APOLLO_0377;
1041 	last &= APOLLO_0377;
1042 	mask = 0400;
1043     }
1044     else {
1045 	map = CcKeyMap;
1046 	mask = 0;
1047     }
1048     if (map[first] == F_UNASSIGNED) {
1049 	if (first == last)
1050 	    xprintf(CGETS(20, 38, " %s\t\tis undefined\n"),
1051 		    unparsekey(first | mask));
1052 	return;
1053     }
1054 
1055     for (fp = FuncNames; fp->name; fp++) {
1056 	if (fp->func == map[first]) {
1057 	    if (first == last)
1058 		xprintf(" %s\t\t%s\n",
1059 			unparsekey((first & APOLLO_0377) | mask), fp->name);
1060 	    else {
1061 		(void) strcpy(buf, unparsekey((first & APOLLO_0377) | mask));
1062 		xprintf(" %s..%s\t\t%s\n", buf,
1063 		        unparsekey((last & APOLLO_0377) | mask), fp->name);
1064 	    }
1065 	    return;
1066 	}
1067     }
1068     if (map == CcKeyMap) {
1069 	xprintf(CGETS(20, 11, "BUG!!! %s isn't bound to anything.\n"),
1070 		unparsekey(first));
1071 	xprintf("CcKeyMap[%d] == %d\n", first, CcKeyMap[first]);
1072     }
1073     else {
1074 	xprintf(CGETS(20, 11, "BUG!!! %s isn't bound to anything.\n"),
1075 		unparsekey(first & 0400));
1076 	xprintf("CcAltMap[%d] == %d\n", first, CcAltMap[first]);
1077     }
1078 }
1079 #endif /* OBSOLETE */
1080