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
36static	void   printkey		(const KEYCMD *, CStr *);
37static	KEYCMD parsecmd		(Char *);
38static  void   bad_spec		(const Char *);
39static	CStr  *parsestring	(const Char *, CStr *);
40static	CStr  *parsebind	(const Char *, CStr *);
41static	void   print_all_keys	(void);
42static	void   printkeys	(KEYCMD *, int, int);
43static	void   bindkey_usage	(void);
44static	void   list_functions	(void);
45
46extern int MapsAreInited;
47
48
49
50
51/*ARGSUSED*/
52void
53dobindkey(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
225static void
226printkey(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
246static  KEYCMD
247parsecmd(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
261static void
262bad_spec(const Char *str)
263{
264    xprintf(CGETS(20, 4, "Bad key spec %S\n"), str);
265}
266
267static CStr *
268parsebind(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
373static CStr *
374parsestring(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
405static void
406print_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
439static void
440printkeys(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
488static void
489bindkey_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
530static void
531list_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