freebsd-dev/contrib/tcsh/tc.bind.c
2017-03-25 13:32:28 +00:00

543 lines
12 KiB
C

/* $Header: /p/tcsh/cvsroot/tcsh/tc.bind.c,v 3.46 2015/08/13 08:54:04 christos Exp $ */
/*
* tc.bind.c: Key binding functions
*/
/*-
* Copyright (c) 1980, 1991 The Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "sh.h"
RCSID("$tcsh: tc.bind.c,v 3.46 2015/08/13 08:54:04 christos Exp $")
#include "ed.h"
#include "ed.defns.h"
static void printkey (const KEYCMD *, CStr *);
static KEYCMD parsecmd (Char *);
static void bad_spec (const Char *);
static CStr *parsestring (const Char *, CStr *);
static CStr *parsebind (const Char *, CStr *);
static void print_all_keys (void);
static void printkeys (KEYCMD *, int, int);
static void bindkey_usage (void);
static void list_functions (void);
extern int MapsAreInited;
/*ARGSUSED*/
void
dobindkey(Char **v, struct command *c)
{
KEYCMD *map;
int ntype, no, removeb, key, bindk;
Char *par;
Char p;
KEYCMD cmd;
CStr in;
CStr out;
uChar ch;
USE(c);
if (!MapsAreInited)
ed_InitMaps();
map = CcKeyMap;
ntype = XK_CMD;
key = removeb = bindk = 0;
for (no = 1, par = v[no];
par != NULL && (*par++ & CHAR) == '-'; no++, par = v[no]) {
if ((p = (*par & CHAR)) == '-') {
no++;
break;
}
else
switch (p) {
case 'b':
bindk = 1;
break;
case 'k':
key = 1;
break;
case 'a':
map = CcAltMap;
break;
case 's':
ntype = XK_STR;
break;
case 'c':
ntype = XK_EXE;
break;
case 'r':
removeb = 1;
break;
case 'v':
ed_InitVIMaps();
return;
case 'e':
ed_InitEmacsMaps();
return;
case 'd':
#ifdef VIDEFAULT
ed_InitVIMaps();
#else /* EMACSDEFAULT */
ed_InitEmacsMaps();
#endif /* VIDEFAULT */
return;
case 'l':
list_functions();
return;
default:
bindkey_usage();
return;
}
}
if (!v[no]) {
print_all_keys();
return;
}
if (key) {
if (!IsArrowKey(v[no]))
xprintf(CGETS(20, 1, "Invalid key name `%S'\n"), v[no]);
in.buf = Strsave(v[no++]);
in.len = Strlen(in.buf);
}
else {
if (bindk) {
if (parsebind(v[no++], &in) == NULL)
return;
}
else {
if (parsestring(v[no++], &in) == NULL)
return;
}
}
cleanup_push(in.buf, xfree);
#ifndef WINNT_NATIVE
if (in.buf[0] > 0xFF) {
bad_spec(in.buf);
cleanup_until(in.buf);
return;
}
#endif
ch = (uChar) in.buf[0];
if (removeb) {
if (key)
(void) ClearArrowKeys(&in);
else if (in.len > 1) {
(void) DeleteXkey(&in);
}
else if (map[ch] == F_XKEY) {
(void) DeleteXkey(&in);
map[ch] = F_UNASSIGNED;
}
else {
map[ch] = F_UNASSIGNED;
}
cleanup_until(in.buf);
return;
}
if (!v[no]) {
if (key)
PrintArrowKeys(&in);
else
printkey(map, &in);
cleanup_until(in.buf);
return;
}
if (v[no + 1]) {
bindkey_usage();
cleanup_until(in.buf);
return;
}
switch (ntype) {
case XK_STR:
case XK_EXE:
if (parsestring(v[no], &out) == NULL) {
cleanup_until(in.buf);
return;
}
cleanup_push(out.buf, xfree);
if (key) {
if (SetArrowKeys(&in, XmapStr(&out), ntype) == -1)
xprintf(CGETS(20, 2, "Bad key name: %S\n"), in.buf);
else
cleanup_ignore(out.buf);
}
else
AddXkey(&in, XmapStr(&out), ntype);
map[ch] = F_XKEY;
break;
case XK_CMD:
if ((cmd = parsecmd(v[no])) == 0) {
cleanup_until(in.buf);
return;
}
if (key)
(void) SetArrowKeys(&in, XmapCmd((int) cmd), ntype);
else {
if (in.len > 1) {
AddXkey(&in, XmapCmd((int) cmd), ntype);
map[ch] = F_XKEY;
}
else {
ClearXkey(map, &in);
map[ch] = cmd;
}
}
break;
default:
abort();
break;
}
cleanup_until(in.buf);
if (key)
BindArrowKeys();
}
static void
printkey(const KEYCMD *map, CStr *in)
{
struct KeyFuncs *fp;
if (in->len < 2) {
unsigned char *unparsed;
unparsed = unparsestring(in, STRQQ);
cleanup_push(unparsed, xfree);
for (fp = FuncNames; fp->name; fp++) {
if (fp->func == map[(uChar) *(in->buf)]) {
xprintf("%s\t->\t%s\n", unparsed, fp->name);
}
}
cleanup_until(unparsed);
}
else
PrintXkey(in);
}
static KEYCMD
parsecmd(Char *str)
{
struct KeyFuncs *fp;
for (fp = FuncNames; fp->name; fp++) {
if (strcmp(short2str(str), fp->name) == 0) {
return (KEYCMD) fp->func;
}
}
xprintf(CGETS(20, 3, "Bad command name: %S\n"), str);
return 0;
}
static void
bad_spec(const Char *str)
{
xprintf(CGETS(20, 4, "Bad key spec %S\n"), str);
}
static CStr *
parsebind(const Char *s, CStr *str)
{
struct Strbuf b = Strbuf_INIT;
cleanup_push(&b, Strbuf_cleanup);
if (Iscntrl(*s)) {
Strbuf_append1(&b, *s);
goto end;
}
switch (*s) {
case '^':
s++;
#ifdef IS_ASCII
Strbuf_append1(&b, (*s == '?') ? '\177' : ((*s & CHAR) & 0237));
#else
Strbuf_append1(&b, (*s == '?') ? CTL_ESC('\177')
: _toebcdic[_toascii[*s & CHAR] & 0237]);
#endif
break;
case 'F':
case 'M':
case 'X':
case 'C':
#ifdef WINNT_NATIVE
case 'N':
#endif /* WINNT_NATIVE */
if (s[1] != '-' || s[2] == '\0')
goto bad_spec;
s += 2;
switch (s[-2]) {
case 'F': case 'f': /* Turn into ^[str */
Strbuf_append1(&b, CTL_ESC('\033'));
Strbuf_append(&b, s);
break;
case 'C': case 'c': /* Turn into ^c */
#ifdef IS_ASCII
Strbuf_append1(&b, (*s == '?') ? '\177' : ((*s & CHAR) & 0237));
#else
Strbuf_append1(&b, (*s == '?') ? CTL_ESC('\177')
: _toebcdic[_toascii[*s & CHAR] & 0237]);
#endif
break;
case 'X' : case 'x': /* Turn into ^Xc */
#ifdef IS_ASCII
Strbuf_append1(&b, 'X' & 0237);
#else
Strbuf_append1(&b, _toebcdic[_toascii['X'] & 0237]);
#endif
Strbuf_append1(&b, *s);
break;
case 'M' : case 'm': /* Turn into 0x80|c */
if (!NoNLSRebind) {
Strbuf_append1(&b, CTL_ESC('\033'));
Strbuf_append1(&b, *s);
} else {
#ifdef IS_ASCII
Strbuf_append1(&b, *s | 0x80);
#else
Strbuf_append1(&b, _toebcdic[_toascii[*s] | 0x80]);
#endif
}
break;
#ifdef WINNT_NATIVE
case 'N' : case 'n': /* NT */
{
Char bnt;
bnt = nt_translate_bindkey(s);
if (bnt != 0)
Strbuf_append1(&b, bnt);
else
bad_spec(s);
}
break;
#endif /* WINNT_NATIVE */
default:
abort();
}
break;
default:
goto bad_spec;
}
end:
cleanup_ignore(&b);
cleanup_until(&b);
Strbuf_terminate(&b);
str->buf = xrealloc(b.s, (b.len + 1) * sizeof (*str->buf));
str->len = b.len;
return str;
bad_spec:
bad_spec(s);
cleanup_until(&b);
return NULL;
}
static CStr *
parsestring(const Char *str, CStr *buf)
{
struct Strbuf b = Strbuf_INIT;
const Char *p;
eChar es;
if (*str == 0) {
xprintf("%s", CGETS(20, 5, "Null string specification\n"));
return NULL;
}
cleanup_push(&b, Strbuf_cleanup);
for (p = str; *p != 0; p++) {
if ((*p & CHAR) == '\\' || (*p & CHAR) == '^') {
if ((es = parseescape(&p)) == CHAR_ERR) {
cleanup_until(&b);
return 0;
} else
Strbuf_append1(&b, es);
}
else
Strbuf_append1(&b, *p & CHAR);
}
cleanup_ignore(&b);
cleanup_until(&b);
Strbuf_terminate(&b);
buf->buf = xrealloc(b.s, (b.len + 1) * sizeof (*buf->buf));
buf->len = b.len;
return buf;
}
static void
print_all_keys(void)
{
int prev, i;
CStr nilstr;
nilstr.buf = NULL;
nilstr.len = 0;
xprintf("%s", CGETS(20, 6, "Standard key bindings\n"));
prev = 0;
for (i = 0; i < 256; i++) {
if (CcKeyMap[prev] == CcKeyMap[i])
continue;
printkeys(CcKeyMap, prev, i - 1);
prev = i;
}
printkeys(CcKeyMap, prev, i - 1);
xprintf("%s", CGETS(20, 7, "Alternative key bindings\n"));
prev = 0;
for (i = 0; i < 256; i++) {
if (CcAltMap[prev] == CcAltMap[i])
continue;
printkeys(CcAltMap, prev, i - 1);
prev = i;
}
printkeys(CcAltMap, prev, i - 1);
xprintf("%s", CGETS(20, 8, "Multi-character bindings\n"));
PrintXkey(NULL); /* print all Xkey bindings */
xprintf("%s", CGETS(20, 9, "Arrow key bindings\n"));
PrintArrowKeys(&nilstr);
}
static void
printkeys(KEYCMD *map, int first, int last)
{
struct KeyFuncs *fp;
Char firstbuf[2], lastbuf[2];
CStr fb, lb;
unsigned char *unparsed;
fb.buf = firstbuf;
lb.buf = lastbuf;
firstbuf[0] = (Char) first;
firstbuf[1] = 0;
lastbuf[0] = (Char) last;
lastbuf[1] = 0;
fb.len = 1;
lb.len = 1;
unparsed = unparsestring(&fb, STRQQ);
cleanup_push(unparsed, xfree);
if (map[first] == F_UNASSIGNED) {
if (first == last)
xprintf(CGETS(20, 10, "%-15s-> is undefined\n"), unparsed);
cleanup_until(unparsed);
return;
}
for (fp = FuncNames; fp->name; fp++) {
if (fp->func == map[first]) {
if (first == last)
xprintf("%-15s-> %s\n", unparsed, fp->name);
else {
unsigned char *p;
p = unparsestring(&lb, STRQQ);
cleanup_push(p, xfree);
xprintf("%-4s to %-7s-> %s\n", unparsed, p, fp->name);
}
cleanup_until(unparsed);
return;
}
}
xprintf(CGETS(20, 11, "BUG!!! %s isn't bound to anything.\n"), unparsed);
if (map == CcKeyMap)
xprintf("CcKeyMap[%d] == %d\n", first, CcKeyMap[first]);
else
xprintf("CcAltMap[%d] == %d\n", first, CcAltMap[first]);
cleanup_until(unparsed);
}
static void
bindkey_usage(void)
{
xprintf("%s", CGETS(20, 12,
"Usage: bindkey [options] [--] [KEY [COMMAND]]\n"));
xprintf("%s", CGETS(20, 13,
" -a list or bind KEY in alternative key map\n"));
xprintf("%s", CGETS(20, 14,
" -b interpret KEY as a C-, M-, F- or X- key name\n"));
xprintf("%s", CGETS(20, 15,
" -s interpret COMMAND as a literal string to be output\n"));
xprintf("%s", CGETS(20, 16,
" -c interpret COMMAND as a builtin or external command\n"));
xprintf("%s", CGETS(20, 17,
" -v bind all keys to vi bindings\n"));
xprintf("%s", CGETS(20, 18,
" -e bind all keys to emacs bindings\n"));
xprintf(CGETS(20, 19,
" -d bind all keys to default editor's bindings (%s)\n"),
#ifdef VIDEFAULT
"vi"
#else /* EMACSDEFAULT */
"emacs"
#endif /* VIDEFAULT */
);
xprintf("%s", CGETS(20, 20,
" -l list editor commands with descriptions\n"));
xprintf("%s", CGETS(20, 21,
" -r remove KEY's binding\n"));
xprintf("%s", CGETS(20, 22,
" -k interpret KEY as a symbolic arrow-key name\n"));
xprintf("%s", CGETS(20, 23,
" -- force a break from option processing\n"));
xprintf("%s", CGETS(20, 24,
" -u (or any invalid option) this message\n"));
xprintf("\n");
xprintf("%s", CGETS(20, 25,
"Without KEY or COMMAND, prints all bindings\n"));
xprintf("%s", CGETS(20, 26,
"Without COMMAND, prints the binding for KEY.\n"));
}
static void
list_functions(void)
{
struct KeyFuncs *fp;
for (fp = FuncNames; fp->name; fp++) {
xprintf("%s\n %s\n", fp->name, fp->desc);
}
}