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