home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume18
/
mush6.4
/
part09
/
bind.c
next >
Wrap
C/C++ Source or Header
|
1989-03-12
|
20KB
|
653 lines
/* bind.c */
#include "bindings.h"
#include "mush.h"
extern char *c_macro();
static un_bind();
struct cmd_map *cmd_map, *line_map, *bang_map;
/*
* Bindings are added here in REVERSE of the order that
* they will be displayed! Display order is based on a
* guess about the frequency of use and (to a lesser
* extent) how hard they are to remember.
*
* The user's own new bindings, if any, will be displayed
* before any of these default bindings.
*/
init_bindings()
{
#ifdef CURSES
/* Help gets displayed last */
add_bind("?", C_HELP, NULL, &cmd_map);
add_bind("V", C_VERSION, NULL, &cmd_map);
/* Miscellaneous shell commands */
add_bind("%", C_CHDIR, NULL, &cmd_map);
add_bind("|", C_PRINT_MSG, NULL, &cmd_map);
add_bind("!", C_SHELL_ESC, NULL, &cmd_map);
add_bind(":", C_CURSES_ESC, NULL, &cmd_map);
/* Mush customization commands */
/* NOTE: No default C_MACRO bindings */
add_bind(")", C_SAVEOPTS, NULL, &cmd_map);
add_bind("(", C_SOURCE, NULL, &cmd_map);
add_bind("&!", C_MAP_BANG, NULL, &cmd_map);
add_bind("&:", C_MAP, NULL, &cmd_map);
add_bind("&&", C_BIND_MACRO, NULL, &cmd_map);
add_bind("v", C_VAR_SET, NULL, &cmd_map);
add_bind("i", C_IGNORE, NULL, &cmd_map);
add_bind("h", C_OWN_HDR, NULL, &cmd_map);
add_bind("B", C_UNBIND, NULL, &cmd_map);
add_bind("b", C_BIND, NULL, &cmd_map);
add_bind("a", C_ALIAS, NULL, &cmd_map);
/* Display modification commands */
add_bind("\022", C_REVERSE, NULL, &cmd_map); /* ^R */
add_bind("\014", C_REDRAW, NULL, &cmd_map); /* ^L */
add_bind("Z", C_PREV_SCREEN, NULL, &cmd_map);
add_bind("z", C_NEXT_SCREEN, NULL, &cmd_map);
/* Searching and sorting commands */
add_bind("\016", C_CONT_SEARCH, NULL, &cmd_map); /* ^N */
add_bind("\037", C_PREV_SEARCH, NULL, &cmd_map); /* ^/ */
add_bind("/", C_NEXT_SEARCH, NULL, &cmd_map);
add_bind("O", C_REV_SORT, NULL, &cmd_map);
add_bind("o", C_SORT, NULL, &cmd_map);
/* Ways to get out */
add_bind("X", C_EXIT_HARD, NULL, &cmd_map);
add_bind("x", C_EXIT, NULL, &cmd_map);
add_bind("Q", C_QUIT_HARD, NULL, &cmd_map);
add_bind("q", C_QUIT, NULL, &cmd_map);
/* Folder modification commands */
add_bind("\025", C_UPDATE, NULL, &cmd_map); /* ^U */
add_bind("\020", C_PRESERVE, NULL, &cmd_map); /* ^P */
add_bind("W", C_WRITE_LIST, NULL, &cmd_map);
add_bind("w", C_WRITE_MSG, NULL, &cmd_map);
add_bind("U", C_UNDEL_LIST, NULL, &cmd_map);
add_bind("u", C_UNDEL_MSG, NULL, &cmd_map);
add_bind("S", C_SAVE_LIST, NULL, &cmd_map);
add_bind("s", C_SAVE_MSG, NULL, &cmd_map);
add_bind("f", C_FOLDER, NULL, &cmd_map);
add_bind("D", C_DELETE_LIST, NULL, &cmd_map);
add_bind("d", C_DELETE_MSG, NULL, &cmd_map);
add_bind("C", C_COPY_LIST, NULL, &cmd_map);
add_bind("c", C_COPY_MSG, NULL, &cmd_map);
/* Cursor movement and message selection */
add_bind("g", C_GOTO_MSG, NULL, &cmd_map);
add_bind("}", C_BOTTOM_PAGE, NULL, &cmd_map);
add_bind("{", C_TOP_PAGE, NULL, &cmd_map);
add_bind("$", C_LAST_MSG, NULL, &cmd_map);
add_bind("^", C_FIRST_MSG, NULL, &cmd_map);
add_bind("\013",C_PREV_MSG, NULL, &cmd_map); /* ^K */
add_bind("\012", C_NEXT_MSG, NULL, &cmd_map); /* ^J */
add_bind("-",C_PREV_MSG, NULL, &cmd_map);
add_bind("+",C_NEXT_MSG, NULL, &cmd_map);
add_bind("K", C_PREV_MSG, NULL, &cmd_map);
add_bind("k", C_PREV_MSG, NULL, &cmd_map);
add_bind("J", C_NEXT_MSG, NULL, &cmd_map);
add_bind("j", C_NEXT_MSG, NULL, &cmd_map);
/* Mail-sending commands */
add_bind("R", C_REPLY_ALL, NULL, &cmd_map);
add_bind("r", C_REPLY_SENDER, NULL, &cmd_map);
add_bind("M", C_MAIL_FLAGS, NULL, &cmd_map);
add_bind("m", C_MAIL, NULL, &cmd_map);
/* Mail-reading commands */
add_bind(".", C_DISPLAY_MSG, NULL, &cmd_map);
add_bind("T", C_TOP_MSG, NULL, &cmd_map);
add_bind("t", C_DISPLAY_MSG, NULL, &cmd_map);
add_bind("p", C_DISPLAY_MSG, NULL, &cmd_map);
add_bind("n", C_DISPLAY_NEXT, NULL, &cmd_map);
#endif /* CURSES */
}
/* Bindable function names.
* Most of these can't be used if CURSES is not defined,
* but help and lookups get confused if they aren't all here.
*/
struct cmd_map map_func_names[] = {
/* These MUST be in numerical order; see bindings.h */
{ C_NULL, "no-op", NULL, NULL_MAP },
{ C_GOTO_MSG, "goto-msg", NULL, NULL_MAP },
{ C_WRITE_LIST, "write-list", NULL, NULL_MAP },
{ C_WRITE_MSG, "write", NULL, NULL_MAP },
{ C_SAVE_LIST, "save-list", NULL, NULL_MAP },
{ C_SAVE_MSG, "save", NULL, NULL_MAP },
{ C_COPY_LIST, "copy-list", NULL, NULL_MAP },
{ C_COPY_MSG, "copy", NULL, NULL_MAP },
{ C_DELETE_LIST, "delete-list", NULL, NULL_MAP },
{ C_DELETE_MSG, "delete", NULL, NULL_MAP },
{ C_UNDEL_LIST, "undelete-list", NULL, NULL_MAP },
{ C_UNDEL_MSG, "undelete", NULL, NULL_MAP },
{ C_REDRAW, "redraw", NULL, NULL_MAP },
{ C_REVERSE, "reverse-video", NULL, NULL_MAP },
{ C_NEXT_MSG, "next-msg", NULL, NULL_MAP },
{ C_PREV_MSG, "back-msg", NULL, NULL_MAP },
{ C_FIRST_MSG, "first-msg", NULL, NULL_MAP },
{ C_LAST_MSG, "last-msg", NULL, NULL_MAP },
{ C_TOP_PAGE, "top-page", NULL, NULL_MAP },
{ C_BOTTOM_PAGE, "bottom-page", NULL, NULL_MAP },
{ C_NEXT_SCREEN, "screen-next", NULL, NULL_MAP },
{ C_PREV_SCREEN, "screen-back", NULL, NULL_MAP },
{ C_SOURCE, "source", NULL, NULL_MAP },
{ C_SAVEOPTS, "saveopts", NULL, NULL_MAP },
{ C_NEXT_SEARCH, "search-next", NULL, NULL_MAP },
{ C_PREV_SEARCH, "search-back", NULL, NULL_MAP },
{ C_CONT_SEARCH, "search-again", NULL, NULL_MAP },
{ C_PRESERVE, "preserve", NULL, NULL_MAP },
{ C_REV_SORT, "sort-reverse", NULL, NULL_MAP },
{ C_SORT, "sort", NULL, NULL_MAP },
{ C_QUIT_HARD, "quit!", NULL, NULL_MAP },
{ C_QUIT, "quit", NULL, NULL_MAP },
{ C_EXIT_HARD, "exit!", NULL, NULL_MAP },
{ C_EXIT, "exit", NULL, NULL_MAP },
{ C_UPDATE, "update", NULL, NULL_MAP },
{ C_FOLDER, "folder", NULL, NULL_MAP },
{ C_SHELL_ESC, "shell-escape", NULL, NULL_MAP },
{ C_CURSES_ESC, "line-mode", NULL, NULL_MAP },
{ C_PRINT_MSG, "lpr", NULL, NULL_MAP },
{ C_CHDIR, "chdir", NULL, NULL_MAP },
{ C_VAR_SET, "variable", NULL, NULL_MAP },
{ C_IGNORE, "ignore", NULL, NULL_MAP },
{ C_ALIAS, "alias", NULL, NULL_MAP },
{ C_OWN_HDR, "my-hdrs", NULL, NULL_MAP },
{ C_VERSION, "version", NULL, NULL_MAP },
{ C_MAIL_FLAGS, "mail-flags", NULL, NULL_MAP },
{ C_MAIL, "mail", NULL, NULL_MAP },
{ C_REPLY_ALL, "reply-all", NULL, NULL_MAP },
{ C_REPLY_SENDER, "reply", NULL, NULL_MAP },
{ C_DISPLAY_NEXT, "display-next", NULL, NULL_MAP },
{ C_DISPLAY_MSG, "display", NULL, NULL_MAP },
{ C_TOP_MSG, "top", NULL, NULL_MAP },
{ C_BIND_MACRO, "bind-macro", NULL, NULL_MAP },
{ C_BIND, "bind", NULL, NULL_MAP },
{ C_UNBIND, "unbind", NULL, NULL_MAP },
{ C_MAP_BANG, "map!", NULL, NULL_MAP },
{ C_MAP, "map", NULL, NULL_MAP },
{ C_MACRO, "macro", NULL, NULL_MAP },
/* C_HELP Must be the last one! */
{ C_HELP, "help", NULL, NULL_MAP }
};
#ifdef CURSES
/*
* getcmd() is called from curses mode only. It waits for char input from
* the user via m_getchar() (which means that a macro could provide input)
* and then compares the chars input against the "bind"ings set up by the
* user (or the defaults). For example, 'j' could bind to "next msg" which
* is interpreted by the big switch statement in curses_command() (curses.c).
* getcmd() returns the int-value of the curses command the input is "bound"
* to. If the input is unrecognized, C_NULL is returned (curses_command()
* might require some cleanup, so this is valid, too).
*
* Since the input could originate from a macro rather than the terminal,
* check to see if this is the case and search for a '[' char which indicates
* that there is a curses command or other "long" command to be executed.
*/
getcmd()
{
char buf[MAX_BIND_LEN * 3];
register int c, m, match;
register char *p = buf;
register struct cmd_map *list;
bzero(buf, MAX_BIND_LEN);
active_cmd = NULL_MAP;
c = m_getchar();
/* If user did job control (^Z), then the interrupt flag will be
* set. Be sure it's unset before continuing.
*/
turnoff(glob_flags, WAS_INTR);
if (isdigit(c)) {
buf[0] = c;
buf[1] = '\0';
Ungetstr(buf); /* So mac_flush can clear on error */
return C_GOTO_MSG;
}
for (;;) {
if (ison(glob_flags, IN_MACRO) && c == MAC_LONG_CMD)
return long_mac_cmd(c, TRUE);
else
*p++ = c;
m = 0;
for (list = cmd_map; list; list = list->m_next) {
if ((match = prefix(buf, list->m_str)) == MATCH) {
if (debug)
print("\"%s\" ",
ctrl_strcpy(buf,
map_func_names[list->m_cmd].m_str,
TRUE));
if (list->m_cmd == C_MACRO) {
curs_macro(list->x_str);
return getcmd();
}
active_cmd = list;
return (int)list->m_cmd;
} else if (match != NO_MATCH)
m++;
}
if (m == 0) {
if (debug) {
char tmp[sizeof buf];
print("No binding for \"%s\" found.",
ctrl_strcpy(tmp, buf, TRUE));
}
return C_NULL;
}
c = m_getchar();
}
}
#endif /* CURSES */
/*
* bind_it() is used to set or unset bind, map and map! settings.
* bind is used to accelerate curses commands by mapping key sequences
* to curses commands. map is used to accelerate command mode keysequences
* by simulating stdin. map! is the same, but used when in compose mode.
*
* bind_it() doesn't touch messages; return -1 for curses mode.
* return -2 to have curses command set CNTD_CMD to prevent screen refresh
* to allow user to read output in case of multiple lines.
*
* Since this routine deals with a lot of binding and unbinding of things
* like line-mode "map"s and is interactive (calls Getstr()), be very careful
* not to allow expansions during interaction.
*/
bind_it(len, argv)
char **argv;
{
char string[MAX_BIND_LEN], buf[256], *name = NULL;
char *rawstr; /* raw format of string (ptr to string if no argv avail) */
char ascii[MAX_BIND_LEN*2]; /* printable ascii version of string */
register int x;
SIGRET (*oldint)(), (*oldquit)();
struct cmd_map **map_list;
int unbind = (argv && **argv == 'u');
int map = 0, is_bind_macro = 0;
int ret = 0 - iscurses; /* return value */
if (argv && !strcmp(name = *argv, "bind-macro"))
is_bind_macro++;
if (map = (argv && (!strcmp(name, "map!") || !strcmp(name, "unmap!"))))
map_list = &bang_map;
else if (map = (argv && (!strcmp(name, "map") || !strcmp(name, "unmap"))))
map_list = &line_map;
else
map_list = &cmd_map;
if (argv && *++argv && !strcmp(*argv, "-?"))
/* Subtract ret and iscurses to signal output */
return help(0, name, cmd_help) - ret - iscurses;
if (iscurses)
on_intr();
if (unbind) {
if (!*argv) {
print("%s what? ", name);
len = Getstr(buf, sizeof buf, 0);
if (len <= 0) {
if (iscurses)
off_intr();
return -1;
}
rawstr = m_xlate(buf);
} else
rawstr = m_xlate(*argv);
if (!un_bind(rawstr, map_list)) {
ctrl_strcpy(ascii, rawstr, TRUE);
print("\"%s\" isn't bound to a command.\n", ascii);
}
if (iscurses)
off_intr();
return ret;
}
if (argv && *argv) {
rawstr = m_xlate(*argv);
(void) ctrl_strcpy(ascii, rawstr, TRUE);
if (!*++argv) {
/*
* determine whether "argv" references a "map" or a "bind"
*/
int binding = c_bind(rawstr, *map_list);
if (binding == C_MACRO) {
char *mapping = c_macro(NULL, rawstr, *map_list);
if (mapping) {
print("\"%s\" is mapped to ", ascii);
print_more("\"%s\".\n",
ctrl_strcpy(buf, mapping, FALSE));
} else
print("\"%s\" isn't mapped.\n", ascii);
} else if (binding)
print("\"%s\" is %s to \"%s\".\n", ascii,
map? "mapped" : "bound", map_func_names[binding].m_str);
else if (map)
print("\"%s\" isn't mapped.\n", ascii);
else
print("\"%s\" isn't bound to a command.\n", ascii);
if (iscurses)
off_intr();
return ret;
}
} else {
print("%s [<CR>=all, -?=help]: ", name);
len = Getstr(string, MAX_BIND_LEN-1, 0);
if (len == 0) {
int add_to_ret = iscurses;
#ifdef CURSES
if (iscurses)
move(LINES-1, 0), refresh();
#endif
if (map || is_bind_macro)
add_to_ret = !c_macro(name, NULL, *map_list);
else
add_to_ret = !c_bind(NULL, *map_list);
if (iscurses)
off_intr();
/* signal CTND_CMD if there was output */
return ret - add_to_ret;
}
if (len < 0) {
if (iscurses)
off_intr();
return ret;
}
rawstr = m_xlate(string);
(void) ctrl_strcpy(ascii, rawstr, TRUE);
}
/* if a binding was given on the command line */
if (argv && *argv && !map)
if (is_bind_macro)
(void) strcpy(buf, "macro");
else
(void) strcpy(buf, *argv++);
else {
/* at this point, "rawstr" and "ascii" should both be set */
int binding;
if (!strcmp(ascii, "-?")) {
if (iscurses)
clr_bot_line();
ret -= help(0, name, cmd_help);
if (iscurses)
off_intr();
/* Subtract iscurses to signal CNTD_CMD */
return ret - iscurses;
}
if (!map && !is_bind_macro) {
binding = c_bind(rawstr, *map_list);
for (len = 0; len == 0; ) {
print("\"%s\" = <%s>: New binding [<CR> for list]: ",
ascii, (binding? map_func_names[binding].m_str : "unset"));
len = Getstr(buf, sizeof buf, 0);
if (iscurses)
clr_bot_line();
/* strip any trailing whitespace */
if (len > 0)
len = no_newln(buf) - buf;
if (len == 0) {
(void) do_pager(NULL, TRUE);
if (iscurses)
putchar('\n');
for (x = 1; x <= C_HELP; x++) {
if (!(x % 4))
if (do_pager("\n", FALSE) == EOF)
break;
(void) do_pager(sprintf(buf, "%-15.15s ",
map_func_names[x].m_str), FALSE);
}
(void) do_pager("\n", FALSE);
(void) do_pager(NULL, FALSE);
ret -= iscurses;
}
}
} else /* map */
(void) strcpy(buf, "macro"), len = 5;
/* if list was printed, ret < -1 -- tells CNTD_CMD to be set and
* prevents screen from being refreshed (lets user read output
*/
if (len == -1) {
if (iscurses)
off_intr();
return ret;
}
}
for (x = 1; x <= C_HELP; x++) {
if (prefix(buf, map_func_names[x].m_str) == MATCH) {
int add_to_ret;
if (debug)
print("\"%s\" will execute \"%s\".\n", ascii, buf);
if (map_func_names[x].m_cmd == C_MACRO) {
if (argv && *argv) {
(void) argv_to_string(buf, argv);
(void) m_xlate(buf); /* Convert buf to raw chars */
add_to_ret =
do_bind(rawstr, map_func_names[x].m_cmd, buf, map_list);
} else {
char exp[MAX_MACRO_LEN*2]; /* printable expansion */
char *mapping = c_macro(NULL, rawstr, *map_list);
if (mapping)
(void) ctrl_strcpy(exp, mapping, TRUE);
print("\"%s\" = <%s>", ascii, mapping ? exp : "unset");
putchar('\n'), print("New macro: ");
ret -= iscurses; /* To signal screen messed up */
/* we are done with buf, so we can trash over it */
len = Getstr(buf, MAX_MACRO_LEN, 0);
if (len > 0) {
if (iscurses)
clr_bot_line();
(void) m_xlate(buf); /* Convert buf to raw chars */
add_to_ret =
do_bind(rawstr, C_MACRO, buf, map_list);
if (debug) {
(void) ctrl_strcpy(exp, buf, TRUE);
print("\"%s\" will execute \"%s\".\n", ascii, exp);
}
} else if (len < 0) {
if (iscurses)
off_intr();
return ret - add_to_ret;
} else
print("Can't bind to null macro"), putchar('\n');
}
} else /* not a macro */ {
(void) argv_to_string(buf, argv);
add_to_ret =
do_bind(rawstr, map_func_names[x].m_cmd, buf, map_list);
}
/* if do_bind had no errors, it returned -1. If we already
* messed up the screen, then ret is less than -1. return the
* lesser of the two to make sure that CNTD_CMD gets set right
*/
if (iscurses)
off_intr();
return min(add_to_ret, ret);
}
}
print("\"%s\": Unknown function.\n", buf);
if (iscurses)
off_intr();
return ret;
}
/*
* print current key to command bindings if "str" is NULL.
* else return the integer "m_cmd" which the str is bound to.
*/
c_bind(str, opts)
register char *str;
register struct cmd_map *opts;
{
register int incurses = iscurses;
if (!str) {
if (!opts) {
print("No command bindings.\n");
return C_ERROR;
}
if (incurses)
clr_bot_line(), iscurses = FALSE;
(void) do_pager(NULL, TRUE);
(void) do_pager("Current key to command bindings:\n", FALSE);
(void) do_pager("\n", FALSE);
}
for (; opts; opts = opts->m_next) {
char buf[BUFSIZ], buf2[MAX_BIND_LEN], exp[MAX_MACRO_LEN*2], *xp;
if (!str) {
(void) ctrl_strcpy(buf2, opts->m_str, FALSE);
if ((xp = opts->x_str) && opts->m_cmd == C_MACRO)
xp = ctrl_strcpy(exp, opts->x_str, TRUE);
if (do_pager(sprintf(buf, "%s\t%-15.15s %s\n",
buf2, map_func_names[opts->m_cmd].m_str,
xp? xp : ""),
FALSE) == EOF)
break;
} else
if (strcmp(str, opts->m_str))
continue;
else
return opts->m_cmd;
}
iscurses = incurses;
if (!str)
(void) do_pager(NULL, FALSE);
return C_NULL;
}
/*
* Doesn't touch messages, but changes macros: return -1.
* Error output causes return < -1.
* args is currently the execute string of a macro mapping, but may be
* used in the future as an argument string for any curses command.
*/
do_bind(str, func, args, map_list)
register char *str, *args;
struct cmd_map **map_list;
long func;
{
register int ret = -1;
register struct cmd_map *list;
int match;
if (func == C_MACRO && !check_mac_bindings(args))
--ret;
(void) un_bind(str, map_list);
for (list = *map_list; list; list = list->m_next)
if ((match = prefix(str, list->m_str)) != NO_MATCH) {
ret--;
switch (match) {
case MATCH:
puts("Something impossible just happened.");
when A_PREFIX_B:
wprint("Warning: \"%s\" prefixes \"%s\" (%s)\n", str,
list->m_str, map_func_names[list->m_cmd].m_str);
when B_PREFIX_A:
wprint("Warning: \"%s\" (%s) prefixes: \"%s\"\n",
list->m_str, map_func_names[list->m_cmd].m_str, str);
}
}
add_bind(str, func, args, map_list);
/* errors decrement ret. If ret returns less than -1, CNTD_CMD is set
* and no redrawing is done so user can see the warning signs
*/
return ret;
}
/*
* add a binding to a list. This may include "map"s or other mappings since
* the map_list argument can control that. The "func" is an int defined in
* bindings.h ... the "str" passed is the string the user would have to type
* to get the macro/map/binding expanded. This must in in raw format: no
* \n's to mean \015. Convert first using m_xlate().
*/
add_bind(str, func, args, map_list)
register char *str, *args;
struct cmd_map **map_list;
long func;
{
register struct cmd_map *tmp;
struct cmd_map *calloc();
if (!str || !*str)
return;
/* now make a new option struct and set fields */
if (!(tmp = calloc((unsigned)1, sizeof(struct cmd_map)))) {
error("calloc");
return;
}
tmp->m_next = *map_list;
*map_list = tmp;
tmp->m_str = savestr(str);
tmp->m_cmd = func; /* strdup handles the NULL case */
if (args && *args)
tmp->x_str = savestr(args);
else
tmp->x_str = NULL;
}
static
un_bind(p, map_list)
register char *p;
struct cmd_map **map_list;
{
register struct cmd_map *list = *map_list, *tmp;
if (!list || !*list->m_str || !p || !*p)
return 0;
if (!strcmp(p, (*map_list)->m_str)) {
*map_list = (*map_list)->m_next;
xfree (list->m_str);
if (list->x_str)
xfree (list->x_str);
xfree((char *)list);
return 1;
}
for ( ; list->m_next; list = list->m_next)
if (!strcmp(p, list->m_next->m_str)) {
tmp = list->m_next;
list->m_next = list->m_next->m_next;
xfree (tmp->m_str);
if (tmp->x_str)
xfree (tmp->x_str);
xfree ((char *)tmp);
return 1;
}
return 0;
}
prefix(a, b)
register char *a, *b;
{
if (!a || !b)
return NO_MATCH;
while (*a && *b && *a == *b)
a++, b++;
if (!*a && !*b)
return MATCH;
if (!*a && *b)
return A_PREFIX_B;
if (*a && !*b)
return B_PREFIX_A;
return NO_MATCH;
}