tc.bind.c revision 59243
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
39RCSID("$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
45static	int    tocontrol	__P((int));
46static	char  *unparsekey	__P((int));
47static	KEYCMD getkeycmd	__P((Char **));
48static	int    parsekey		__P((Char **));
49static	void   pkeys		__P((int, int));
50#endif /* OBSOLETE */
51
52static	void   printkey		__P((KEYCMD *, CStr *));
53static	KEYCMD parsecmd		__P((Char *));
54static  void   bad_spec		__P((Char *));
55static	CStr  *parsestring	__P((Char *, CStr *));
56static	CStr  *parsebind	__P((Char *, CStr *));
57static	void   print_all_keys	__P((void));
58static	void   printkeys	__P((KEYCMD *, int, int));
59static	void   bindkey_usage	__P((void));
60static	void   list_functions	__P((void));
61
62extern int MapsAreInited;
63
64
65
66
67/*ARGSUSED*/
68void
69dobindkey(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
232static void
233printkey(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
252static  KEYCMD
253parsecmd(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
268static void
269bad_spec(str)
270    Char *str;
271{
272    xprintf(CGETS(20, 4, "Bad key spec %S\n"), str);
273}
274
275static CStr *
276parsebind(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
390static CStr *
391parsestring(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
420static void
421print_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
454static void
455printkeys(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
506static void
507bindkey_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
542static void
543list_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
562static unsigned char APOLLO_0377 = 0377;
563#else /* sane */
564# define APOLLO_0377    0377
565#endif /* apollo */
566
567static int
568tocontrol(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
587static char *
588unparsekey(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
664static  KEYCMD
665getkeycmd(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
710static int
711parsekey(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*/
862void
863dobind(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
1029static void
1030pkeys(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