xref: /freebsd/contrib/tcsh/tc.bind.c (revision cf4c5a533126ca1ddb1f070af73f8f53b9e77fd4)
1 /* $Header: /p/tcsh/cvsroot/tcsh/tc.bind.c,v 3.44 2006/03/02 18:46:44 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 
35 RCSID("$tcsh: tc.bind.c,v 3.44 2006/03/02 18:46:44 christos Exp $")
36 
37 #include "ed.h"
38 #include "ed.defns.h"
39 
40 static	void   printkey		(const KEYCMD *, CStr *);
41 static	KEYCMD parsecmd		(Char *);
42 static  void   bad_spec		(const Char *);
43 static	CStr  *parsestring	(const Char *, CStr *);
44 static	CStr  *parsebind	(const Char *, CStr *);
45 static	void   print_all_keys	(void);
46 static	void   printkeys	(KEYCMD *, int, int);
47 static	void   bindkey_usage	(void);
48 static	void   list_functions	(void);
49 
50 extern int MapsAreInited;
51 
52 
53 
54 
55 /*ARGSUSED*/
56 void
57 dobindkey(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 
229 static void
230 printkey(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 
250 static  KEYCMD
251 parsecmd(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 
265 static void
266 bad_spec(const Char *str)
267 {
268     xprintf(CGETS(20, 4, "Bad key spec %S\n"), str);
269 }
270 
271 static CStr *
272 parsebind(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 
377 static CStr *
378 parsestring(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 
409 static void
410 print_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 
443 static void
444 printkeys(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 
492 static void
493 bindkey_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 
528 static void
529 list_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