tc.bind.c revision 167465
1193323Sed/* $Header: /p/tcsh/cvsroot/tcsh/tc.bind.c,v 3.44 2006/03/02 18:46:44 christos Exp $ */
2193323Sed/*
3193323Sed * tc.bind.c: Key binding functions
4193323Sed */
5193323Sed/*-
6193323Sed * Copyright (c) 1980, 1991 The Regents of the University of California.
7193323Sed * All rights reserved.
8193323Sed *
9193323Sed * Redistribution and use in source and binary forms, with or without
10249423Sdim * modification, are permitted provided that the following conditions
11234353Sdim * are met:
12261991Sdim * 1. Redistributions of source code must retain the above copyright
13249423Sdim *    notice, this list of conditions and the following disclaimer.
14249423Sdim * 2. Redistributions in binary form must reproduce the above copyright
15249423Sdim *    notice, this list of conditions and the following disclaimer in the
16276479Sdim *    documentation and/or other materials provided with the distribution.
17193323Sed * 3. Neither the name of the University nor the names of its contributors
18193323Sed *    may be used to endorse or promote products derived from this software
19193323Sed *    without specific prior written permission.
20193323Sed *
21193323Sed * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22193323Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23193323Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24234353Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25193323Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26193323Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27193323Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28276479Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29193323Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30210299Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31193323Sed * SUCH DAMAGE.
32193323Sed */
33193323Sed#include "sh.h"
34276479Sdim
35198090SrdivackyRCSID("$tcsh: tc.bind.c,v 3.44 2006/03/02 18:46:44 christos Exp $")
36193323Sed
37193323Sed#include "ed.h"
38296417Sdim#include "ed.defns.h"
39296417Sdim
40193323Sedstatic	void   printkey		(const KEYCMD *, CStr *);
41210299Sedstatic	KEYCMD parsecmd		(Char *);
42288943Sdimstatic  void   bad_spec		(const Char *);
43288943Sdimstatic	CStr  *parsestring	(const Char *, CStr *);
44288943Sdimstatic	CStr  *parsebind	(const Char *, CStr *);
45288943Sdimstatic	void   print_all_keys	(void);
46288943Sdimstatic	void   printkeys	(KEYCMD *, int, int);
47288943Sdimstatic	void   bindkey_usage	(void);
48288943Sdimstatic	void   list_functions	(void);
49288943Sdim
50288943Sdimextern int MapsAreInited;
51288943Sdim
52288943Sdim
53288943Sdim
54288943Sdim
55234353Sdim/*ARGSUSED*/
56193323Sedvoid
57276479Sdimdobindkey(Char **v, struct command *c)
58193323Sed{
59193323Sed    KEYCMD *map;
60234353Sdim    int     ntype, no, removeb, key, bindk;
61193323Sed    Char   *par;
62193323Sed    Char    p;
63193323Sed    KEYCMD  cmd;
64234353Sdim    CStr    in;
65234353Sdim    CStr    out;
66234353Sdim    uChar   ch;
67234353Sdim
68234353Sdim    USE(c);
69193323Sed    if (!MapsAreInited)
70193323Sed	ed_InitMaps();
71193323Sed
72276479Sdim    map = CcKeyMap;
73193323Sed    ntype = XK_CMD;
74210299Sed    key = removeb = bindk = 0;
75193323Sed    for (no = 1, par = v[no];
76193323Sed	 par != NULL && (*par++ & CHAR) == '-'; no++, par = v[no]) {
77193323Sed	if ((p = (*par & CHAR)) == '-') {
78193323Sed	    no++;
79193323Sed	    break;
80193323Sed	}
81193323Sed	else
82193323Sed	    switch (p) {
83193323Sed	    case 'b':
84193323Sed		bindk = 1;
85193323Sed		break;
86193323Sed	    case 'k':
87234353Sdim		key = 1;
88234353Sdim		break;
89234353Sdim	    case 'a':
90193323Sed		map = CcAltMap;
91193323Sed		break;
92296417Sdim	    case 's':
93296417Sdim		ntype = XK_STR;
94288943Sdim		break;
95193323Sed	    case 'c':
96193323Sed		ntype = XK_EXE;
97288943Sdim		break;
98193323Sed	    case 'r':
99193323Sed		removeb = 1;
100296417Sdim		break;
101193323Sed	    case 'v':
102193323Sed		ed_InitVIMaps();
103193323Sed		return;
104234353Sdim	    case 'e':
105234353Sdim		ed_InitEmacsMaps();
106234353Sdim		return;
107234353Sdim	    case 'd':
108193323Sed#ifdef VIDEFAULT
109210299Sed		ed_InitVIMaps();
110276479Sdim#else /* EMACSDEFAULT */
111193323Sed		ed_InitEmacsMaps();
112193323Sed#endif /* VIDEFAULT */
113193323Sed		return;
114193323Sed	    case 'l':
115193323Sed		list_functions();
116276479Sdim		return;
117198090Srdivacky	    default:
118193323Sed		bindkey_usage();
119193323Sed		return;
120296417Sdim	    }
121296417Sdim    }
122193323Sed
123210299Sed    if (!v[no]) {
124234353Sdim	print_all_keys();
125193323Sed	return;
126193323Sed    }
127210299Sed
128218893Sdim    if (key) {
129193323Sed	if (!IsArrowKey(v[no]))
130210299Sed	    xprintf(CGETS(20, 1, "Invalid key name `%S'\n"), v[no]);
131193323Sed	in.buf = Strsave(v[no++]);
132193323Sed	in.len = Strlen(in.buf);
133210299Sed    }
134234353Sdim    else {
135296417Sdim	if (bindk) {
136249423Sdim	    if (parsebind(v[no++], &in) == NULL)
137296417Sdim		return;
138249423Sdim	}
139249423Sdim	else {
140296417Sdim	    if (parsestring(v[no++], &in) == NULL)
141193323Sed		return;
142210299Sed	}
143234353Sdim    }
144193323Sed    cleanup_push(in.buf, xfree);
145193323Sed
146193323Sed#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(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(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(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(CGETS(20, 8, "Multi-character bindings\n"));
438    PrintXkey(NULL);	/* print all Xkey bindings */
439    xprintf(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(CGETS(20, 12,
496	    "Usage: bindkey [options] [--] [KEY [COMMAND]]\n"));
497    xprintf(CGETS(20, 13,
498    	    "    -a   list or bind KEY in alternative key map\n"));
499    xprintf(CGETS(20, 14,
500	    "    -b   interpret KEY as a C-, M-, F- or X- key name\n"));
501    xprintf(CGETS(20, 15,
502            "    -s   interpret COMMAND as a literal string to be output\n"));
503    xprintf(CGETS(20, 16,
504            "    -c   interpret COMMAND as a builtin or external command\n"));
505    xprintf(CGETS(20, 17,
506	    "    -v   bind all keys to vi bindings\n"));
507    xprintf(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\n"));
511    xprintf(CGETS(20, 20,
512	    "    -l   list editor commands with descriptions\n"));
513    xprintf(CGETS(20, 21,
514	    "    -r   remove KEY's binding\n"));
515    xprintf(CGETS(20, 22,
516	    "    -k   interpret KEY as a symbolic arrow-key name\n"));
517    xprintf(CGETS(20, 23,
518	    "    --   force a break from option processing\n"));
519    xprintf(CGETS(20, 24,
520	    "    -u   (or any invalid option) this message\n"));
521    xprintf("\n");
522    xprintf(CGETS(20, 25,
523	    "Without KEY or COMMAND, prints all bindings\n"));
524    xprintf(CGETS(20, 26,
525	    "Without COMMAND, prints the binding for KEY.\n"));
526}
527
528static void
529list_functions(void)
530{
531    struct KeyFuncs *fp;
532
533    for (fp = FuncNames; fp->name; fp++) {
534	xprintf("%s\n          %s\n", fp->name, fp->desc);
535    }
536}
537