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