tc.bind.c revision 316957
1/* $Header: /p/tcsh/cvsroot/tcsh/tc.bind.c,v 3.46 2015/08/13 08:54:04 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. 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("$tcsh: tc.bind.c,v 3.46 2015/08/13 08:54:04 christos Exp $")
36
37#include "ed.h"
38#include "ed.defns.h"
39
40static	void   printkey		(const KEYCMD *, CStr *);
41static	KEYCMD parsecmd		(Char *);
42static  void   bad_spec		(const Char *);
43static	CStr  *parsestring	(const Char *, CStr *);
44static	CStr  *parsebind	(const Char *, CStr *);
45static	void   print_all_keys	(void);
46static	void   printkeys	(KEYCMD *, int, int);
47static	void   bindkey_usage	(void);
48static	void   list_functions	(void);
49
50extern int MapsAreInited;
51
52
53
54
55/*ARGSUSED*/
56void
57dobindkey(Char **v, struct command *c)
58{
59    KEYCMD *map;
60    int     ntype, no, removeb, key, bindk;
61    Char   *par;
62    Char    p;
63    KEYCMD  cmd;
64    CStr    in;
65    CStr    out;
66    uChar   ch;
67
68    USE(c);
69    if (!MapsAreInited)
70	ed_InitMaps();
71
72    map = CcKeyMap;
73    ntype = XK_CMD;
74    key = removeb = bindk = 0;
75    for (no = 1, par = v[no];
76	 par != NULL && (*par++ & CHAR) == '-'; no++, par = v[no]) {
77	if ((p = (*par & CHAR)) == '-') {
78	    no++;
79	    break;
80	}
81	else
82	    switch (p) {
83	    case 'b':
84		bindk = 1;
85		break;
86	    case 'k':
87		key = 1;
88		break;
89	    case 'a':
90		map = CcAltMap;
91		break;
92	    case 's':
93		ntype = XK_STR;
94		break;
95	    case 'c':
96		ntype = XK_EXE;
97		break;
98	    case 'r':
99		removeb = 1;
100		break;
101	    case 'v':
102		ed_InitVIMaps();
103		return;
104	    case 'e':
105		ed_InitEmacsMaps();
106		return;
107	    case 'd':
108#ifdef VIDEFAULT
109		ed_InitVIMaps();
110#else /* EMACSDEFAULT */
111		ed_InitEmacsMaps();
112#endif /* VIDEFAULT */
113		return;
114	    case 'l':
115		list_functions();
116		return;
117	    default:
118		bindkey_usage();
119		return;
120	    }
121    }
122
123    if (!v[no]) {
124	print_all_keys();
125	return;
126    }
127
128    if (key) {
129	if (!IsArrowKey(v[no]))
130	    xprintf(CGETS(20, 1, "Invalid key name `%S'\n"), v[no]);
131	in.buf = Strsave(v[no++]);
132	in.len = Strlen(in.buf);
133    }
134    else {
135	if (bindk) {
136	    if (parsebind(v[no++], &in) == NULL)
137		return;
138	}
139	else {
140	    if (parsestring(v[no++], &in) == NULL)
141		return;
142	}
143    }
144    cleanup_push(in.buf, xfree);
145
146#ifndef WINNT_NATIVE
147    if (in.buf[0] > 0xFF) {
148	bad_spec(in.buf);
149	cleanup_until(in.buf);
150	return;
151    }
152#endif
153    ch = (uChar) in.buf[0];
154
155    if (removeb) {
156	if (key)
157	    (void) ClearArrowKeys(&in);
158	else if (in.len > 1) {
159	    (void) DeleteXkey(&in);
160	}
161	else if (map[ch] == F_XKEY) {
162	    (void) DeleteXkey(&in);
163	    map[ch] = F_UNASSIGNED;
164	}
165	else {
166	    map[ch] = F_UNASSIGNED;
167	}
168	cleanup_until(in.buf);
169	return;
170    }
171    if (!v[no]) {
172	if (key)
173	    PrintArrowKeys(&in);
174	else
175	    printkey(map, &in);
176	cleanup_until(in.buf);
177	return;
178    }
179    if (v[no + 1]) {
180	bindkey_usage();
181	cleanup_until(in.buf);
182	return;
183    }
184    switch (ntype) {
185    case XK_STR:
186    case XK_EXE:
187	if (parsestring(v[no], &out) == NULL) {
188	    cleanup_until(in.buf);
189	    return;
190	}
191	cleanup_push(out.buf, xfree);
192	if (key) {
193	    if (SetArrowKeys(&in, XmapStr(&out), ntype) == -1)
194		xprintf(CGETS(20, 2, "Bad key name: %S\n"), in.buf);
195	    else
196		cleanup_ignore(out.buf);
197	}
198	else
199	    AddXkey(&in, XmapStr(&out), ntype);
200	map[ch] = F_XKEY;
201	break;
202    case XK_CMD:
203	if ((cmd = parsecmd(v[no])) == 0) {
204	    cleanup_until(in.buf);
205	    return;
206	}
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    cleanup_until(in.buf);
225    if (key)
226	BindArrowKeys();
227}
228
229static void
230printkey(const KEYCMD *map, CStr *in)
231{
232    struct KeyFuncs *fp;
233
234    if (in->len < 2) {
235	unsigned char *unparsed;
236
237	unparsed = unparsestring(in, STRQQ);
238	cleanup_push(unparsed, xfree);
239	for (fp = FuncNames; fp->name; fp++) {
240	    if (fp->func == map[(uChar) *(in->buf)]) {
241		xprintf("%s\t->\t%s\n", unparsed, fp->name);
242	    }
243	}
244	cleanup_until(unparsed);
245    }
246    else
247	PrintXkey(in);
248}
249
250static  KEYCMD
251parsecmd(Char *str)
252{
253    struct KeyFuncs *fp;
254
255    for (fp = FuncNames; fp->name; fp++) {
256	if (strcmp(short2str(str), fp->name) == 0) {
257	    return (KEYCMD) fp->func;
258	}
259    }
260    xprintf(CGETS(20, 3, "Bad command name: %S\n"), str);
261    return 0;
262}
263
264
265static void
266bad_spec(const Char *str)
267{
268    xprintf(CGETS(20, 4, "Bad key spec %S\n"), str);
269}
270
271static CStr *
272parsebind(const Char *s, CStr *str)
273{
274    struct Strbuf b = Strbuf_INIT;
275
276    cleanup_push(&b, Strbuf_cleanup);
277    if (Iscntrl(*s)) {
278	Strbuf_append1(&b, *s);
279	goto end;
280    }
281
282    switch (*s) {
283    case '^':
284	s++;
285#ifdef IS_ASCII
286	Strbuf_append1(&b, (*s == '?') ? '\177' : ((*s & CHAR) & 0237));
287#else
288	Strbuf_append1(&b, (*s == '?') ? CTL_ESC('\177')
289		       : _toebcdic[_toascii[*s & CHAR] & 0237]);
290#endif
291	break;
292
293    case 'F':
294    case 'M':
295    case 'X':
296    case 'C':
297#ifdef WINNT_NATIVE
298    case 'N':
299#endif /* WINNT_NATIVE */
300	if (s[1] != '-' || s[2] == '\0')
301	    goto bad_spec;
302	s += 2;
303	switch (s[-2]) {
304	case 'F': case 'f':	/* Turn into ^[str */
305	    Strbuf_append1(&b, CTL_ESC('\033'));
306	    Strbuf_append(&b, s);
307	    break;
308
309	case 'C': case 'c':	/* Turn into ^c */
310#ifdef IS_ASCII
311	    Strbuf_append1(&b, (*s == '?') ? '\177' : ((*s & CHAR) & 0237));
312#else
313	    Strbuf_append1(&b, (*s == '?') ? CTL_ESC('\177')
314			   : _toebcdic[_toascii[*s & CHAR] & 0237]);
315#endif
316	    break;
317
318	case 'X' : case 'x':	/* Turn into ^Xc */
319#ifdef IS_ASCII
320	    Strbuf_append1(&b, 'X' & 0237);
321#else
322	    Strbuf_append1(&b, _toebcdic[_toascii['X'] & 0237]);
323#endif
324	    Strbuf_append1(&b, *s);
325	    break;
326
327	case 'M' : case 'm':	/* Turn into 0x80|c */
328	    if (!NoNLSRebind) {
329		Strbuf_append1(&b, CTL_ESC('\033'));
330	    	Strbuf_append1(&b, *s);
331	    } else {
332#ifdef IS_ASCII
333		Strbuf_append1(&b, *s | 0x80);
334#else
335		Strbuf_append1(&b, _toebcdic[_toascii[*s] | 0x80]);
336#endif
337	    }
338	    break;
339#ifdef WINNT_NATIVE
340	case 'N' : case 'n':	/* NT */
341		{
342			Char bnt;
343
344			bnt = nt_translate_bindkey(s);
345			if (bnt != 0)
346			        Strbuf_append1(&b, bnt);
347			else
348				bad_spec(s);
349		}
350	    break;
351#endif /* WINNT_NATIVE */
352
353	default:
354	    abort();
355	}
356	break;
357
358    default:
359	goto bad_spec;
360    }
361
362 end:
363    cleanup_ignore(&b);
364    cleanup_until(&b);
365    Strbuf_terminate(&b);
366    str->buf = xrealloc(b.s, (b.len + 1) * sizeof (*str->buf));
367    str->len = b.len;
368    return str;
369
370 bad_spec:
371    bad_spec(s);
372    cleanup_until(&b);
373    return NULL;
374}
375
376
377static CStr *
378parsestring(const Char *str, CStr *buf)
379{
380    struct Strbuf b = Strbuf_INIT;
381    const Char   *p;
382    eChar  es;
383
384    if (*str == 0) {
385	xprintf("%s", CGETS(20, 5, "Null string specification\n"));
386	return NULL;
387    }
388
389    cleanup_push(&b, Strbuf_cleanup);
390    for (p = str; *p != 0; p++) {
391	if ((*p & CHAR) == '\\' || (*p & CHAR) == '^') {
392	    if ((es = parseescape(&p)) == CHAR_ERR) {
393		cleanup_until(&b);
394		return 0;
395	    } else
396		Strbuf_append1(&b, es);
397	}
398	else
399	    Strbuf_append1(&b, *p & CHAR);
400    }
401    cleanup_ignore(&b);
402    cleanup_until(&b);
403    Strbuf_terminate(&b);
404    buf->buf = xrealloc(b.s, (b.len + 1) * sizeof (*buf->buf));
405    buf->len = b.len;
406    return buf;
407}
408
409static void
410print_all_keys(void)
411{
412    int     prev, i;
413    CStr nilstr;
414    nilstr.buf = NULL;
415    nilstr.len = 0;
416
417
418    xprintf("%s", CGETS(20, 6, "Standard key bindings\n"));
419    prev = 0;
420    for (i = 0; i < 256; i++) {
421	if (CcKeyMap[prev] == CcKeyMap[i])
422	    continue;
423	printkeys(CcKeyMap, prev, i - 1);
424	prev = i;
425    }
426    printkeys(CcKeyMap, prev, i - 1);
427
428    xprintf("%s", CGETS(20, 7, "Alternative key bindings\n"));
429    prev = 0;
430    for (i = 0; i < 256; i++) {
431	if (CcAltMap[prev] == CcAltMap[i])
432	    continue;
433	printkeys(CcAltMap, prev, i - 1);
434	prev = i;
435    }
436    printkeys(CcAltMap, prev, i - 1);
437    xprintf("%s", CGETS(20, 8, "Multi-character bindings\n"));
438    PrintXkey(NULL);	/* print all Xkey bindings */
439    xprintf("%s", CGETS(20, 9, "Arrow key bindings\n"));
440    PrintArrowKeys(&nilstr);
441}
442
443static void
444printkeys(KEYCMD *map, int first, int last)
445{
446    struct KeyFuncs *fp;
447    Char    firstbuf[2], lastbuf[2];
448    CStr fb, lb;
449    unsigned char *unparsed;
450    fb.buf = firstbuf;
451    lb.buf = lastbuf;
452
453    firstbuf[0] = (Char) first;
454    firstbuf[1] = 0;
455    lastbuf[0] = (Char) last;
456    lastbuf[1] = 0;
457    fb.len = 1;
458    lb.len = 1;
459
460    unparsed = unparsestring(&fb, STRQQ);
461    cleanup_push(unparsed, xfree);
462    if (map[first] == F_UNASSIGNED) {
463	if (first == last)
464	    xprintf(CGETS(20, 10, "%-15s->  is undefined\n"), unparsed);
465	cleanup_until(unparsed);
466	return;
467    }
468
469    for (fp = FuncNames; fp->name; fp++) {
470	if (fp->func == map[first]) {
471	    if (first == last)
472		xprintf("%-15s->  %s\n", unparsed, fp->name);
473	    else {
474		unsigned char *p;
475
476		p = unparsestring(&lb, STRQQ);
477		cleanup_push(p, xfree);
478		xprintf("%-4s to %-7s->  %s\n", unparsed, p, fp->name);
479	    }
480	    cleanup_until(unparsed);
481	    return;
482	}
483    }
484    xprintf(CGETS(20, 11, "BUG!!! %s isn't bound to anything.\n"), unparsed);
485    if (map == CcKeyMap)
486	xprintf("CcKeyMap[%d] == %d\n", first, CcKeyMap[first]);
487    else
488	xprintf("CcAltMap[%d] == %d\n", first, CcAltMap[first]);
489    cleanup_until(unparsed);
490}
491
492static void
493bindkey_usage(void)
494{
495    xprintf("%s", CGETS(20, 12,
496	    "Usage: bindkey [options] [--] [KEY [COMMAND]]\n"));
497    xprintf("%s", CGETS(20, 13,
498    	    "    -a   list or bind KEY in alternative key map\n"));
499    xprintf("%s", CGETS(20, 14,
500	    "    -b   interpret KEY as a C-, M-, F- or X- key name\n"));
501    xprintf("%s", CGETS(20, 15,
502            "    -s   interpret COMMAND as a literal string to be output\n"));
503    xprintf("%s", CGETS(20, 16,
504            "    -c   interpret COMMAND as a builtin or external command\n"));
505    xprintf("%s", CGETS(20, 17,
506	    "    -v   bind all keys to vi bindings\n"));
507    xprintf("%s", CGETS(20, 18,
508	    "    -e   bind all keys to emacs bindings\n"));
509    xprintf(CGETS(20, 19,
510	    "    -d   bind all keys to default editor's bindings (%s)\n"),
511#ifdef VIDEFAULT
512	    "vi"
513#else /* EMACSDEFAULT */
514	    "emacs"
515#endif /* VIDEFAULT */
516	    );
517    xprintf("%s", CGETS(20, 20,
518	    "    -l   list editor commands with descriptions\n"));
519    xprintf("%s", CGETS(20, 21,
520	    "    -r   remove KEY's binding\n"));
521    xprintf("%s", CGETS(20, 22,
522	    "    -k   interpret KEY as a symbolic arrow-key name\n"));
523    xprintf("%s", CGETS(20, 23,
524	    "    --   force a break from option processing\n"));
525    xprintf("%s", CGETS(20, 24,
526	    "    -u   (or any invalid option) this message\n"));
527    xprintf("\n");
528    xprintf("%s", CGETS(20, 25,
529	    "Without KEY or COMMAND, prints all bindings\n"));
530    xprintf("%s", CGETS(20, 26,
531	    "Without COMMAND, prints the binding for KEY.\n"));
532}
533
534static void
535list_functions(void)
536{
537    struct KeyFuncs *fp;
538
539    for (fp = FuncNames; fp->name; fp++) {
540	xprintf("%s\n          %s\n", fp->name, fp->desc);
541    }
542}
543