home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume18 / mush6.4 / part09 / bind.c next >
C/C++ Source or Header  |  1989-03-12  |  20KB  |  653 lines

  1. /* bind.c */
  2.  
  3. #include "bindings.h"
  4. #include "mush.h"
  5.  
  6. extern char *c_macro();
  7. static un_bind();
  8.  
  9. struct cmd_map *cmd_map, *line_map, *bang_map;
  10.  
  11. /*
  12.  * Bindings are added here in REVERSE of the order that
  13.  * they will be displayed!  Display order is based on a
  14.  * guess about the frequency of use and (to a lesser
  15.  * extent) how hard they are to remember.
  16.  *
  17.  * The user's own new bindings, if any, will be displayed
  18.  * before any of these default bindings.
  19.  */
  20. init_bindings()
  21. {
  22. #ifdef CURSES
  23.     /* Help gets displayed last */
  24.     add_bind("?", C_HELP, NULL, &cmd_map);
  25.     add_bind("V", C_VERSION, NULL, &cmd_map);
  26.  
  27.     /* Miscellaneous shell commands */
  28.     add_bind("%", C_CHDIR, NULL, &cmd_map);
  29.     add_bind("|", C_PRINT_MSG, NULL, &cmd_map);
  30.     add_bind("!", C_SHELL_ESC, NULL, &cmd_map);
  31.     add_bind(":", C_CURSES_ESC, NULL, &cmd_map);
  32.  
  33.     /* Mush customization commands */
  34.     /* NOTE: No default C_MACRO bindings */
  35.     add_bind(")", C_SAVEOPTS, NULL, &cmd_map);
  36.     add_bind("(", C_SOURCE, NULL, &cmd_map);
  37.     add_bind("&!", C_MAP_BANG, NULL, &cmd_map);
  38.     add_bind("&:", C_MAP, NULL, &cmd_map);
  39.     add_bind("&&", C_BIND_MACRO, NULL, &cmd_map);
  40.     add_bind("v", C_VAR_SET, NULL, &cmd_map);
  41.     add_bind("i", C_IGNORE, NULL, &cmd_map);
  42.     add_bind("h", C_OWN_HDR, NULL, &cmd_map);
  43.     add_bind("B", C_UNBIND, NULL, &cmd_map);
  44.     add_bind("b", C_BIND, NULL, &cmd_map);
  45.     add_bind("a", C_ALIAS, NULL, &cmd_map);
  46.  
  47.     /* Display modification commands */
  48.     add_bind("\022", C_REVERSE, NULL, &cmd_map);    /* ^R */
  49.     add_bind("\014", C_REDRAW, NULL, &cmd_map);        /* ^L */
  50.     add_bind("Z", C_PREV_SCREEN, NULL, &cmd_map);
  51.     add_bind("z", C_NEXT_SCREEN, NULL, &cmd_map);
  52.  
  53.     /* Searching and sorting commands */
  54.     add_bind("\016", C_CONT_SEARCH, NULL, &cmd_map);    /* ^N */
  55.     add_bind("\037", C_PREV_SEARCH, NULL, &cmd_map);    /* ^/ */
  56.     add_bind("/", C_NEXT_SEARCH, NULL, &cmd_map);
  57.     add_bind("O", C_REV_SORT, NULL, &cmd_map);
  58.     add_bind("o", C_SORT, NULL, &cmd_map);
  59.  
  60.     /* Ways to get out */
  61.     add_bind("X", C_EXIT_HARD, NULL, &cmd_map);
  62.     add_bind("x", C_EXIT, NULL, &cmd_map);
  63.     add_bind("Q", C_QUIT_HARD, NULL, &cmd_map);
  64.     add_bind("q", C_QUIT, NULL, &cmd_map);
  65.  
  66.     /* Folder modification commands */
  67.     add_bind("\025", C_UPDATE, NULL, &cmd_map);        /* ^U */
  68.     add_bind("\020", C_PRESERVE, NULL, &cmd_map);    /* ^P */
  69.     add_bind("W", C_WRITE_LIST, NULL, &cmd_map);
  70.     add_bind("w", C_WRITE_MSG, NULL, &cmd_map);
  71.     add_bind("U", C_UNDEL_LIST, NULL, &cmd_map);
  72.     add_bind("u", C_UNDEL_MSG, NULL, &cmd_map);
  73.     add_bind("S", C_SAVE_LIST, NULL, &cmd_map);
  74.     add_bind("s", C_SAVE_MSG, NULL, &cmd_map);
  75.     add_bind("f", C_FOLDER, NULL, &cmd_map);
  76.     add_bind("D", C_DELETE_LIST, NULL, &cmd_map);
  77.     add_bind("d", C_DELETE_MSG, NULL, &cmd_map);
  78.     add_bind("C", C_COPY_LIST, NULL, &cmd_map);
  79.     add_bind("c", C_COPY_MSG, NULL, &cmd_map);
  80.  
  81.     /* Cursor movement and message selection */
  82.     add_bind("g", C_GOTO_MSG, NULL, &cmd_map);
  83.     add_bind("}", C_BOTTOM_PAGE, NULL, &cmd_map);
  84.     add_bind("{", C_TOP_PAGE, NULL, &cmd_map);
  85.     add_bind("$", C_LAST_MSG, NULL, &cmd_map);
  86.     add_bind("^", C_FIRST_MSG, NULL, &cmd_map);
  87.     add_bind("\013",C_PREV_MSG, NULL, &cmd_map);    /* ^K */
  88.     add_bind("\012", C_NEXT_MSG, NULL, &cmd_map);    /* ^J */
  89.     add_bind("-",C_PREV_MSG, NULL, &cmd_map);
  90.     add_bind("+",C_NEXT_MSG, NULL, &cmd_map);
  91.     add_bind("K", C_PREV_MSG, NULL, &cmd_map);
  92.     add_bind("k", C_PREV_MSG, NULL, &cmd_map);
  93.     add_bind("J", C_NEXT_MSG, NULL, &cmd_map);
  94.     add_bind("j", C_NEXT_MSG, NULL, &cmd_map);
  95.  
  96.     /* Mail-sending commands */
  97.     add_bind("R", C_REPLY_ALL, NULL, &cmd_map);
  98.     add_bind("r", C_REPLY_SENDER, NULL, &cmd_map);
  99.     add_bind("M", C_MAIL_FLAGS, NULL, &cmd_map);
  100.     add_bind("m", C_MAIL, NULL, &cmd_map);
  101.  
  102.     /* Mail-reading commands */
  103.     add_bind(".", C_DISPLAY_MSG, NULL, &cmd_map);
  104.     add_bind("T", C_TOP_MSG, NULL, &cmd_map);
  105.     add_bind("t", C_DISPLAY_MSG, NULL, &cmd_map);
  106.     add_bind("p", C_DISPLAY_MSG, NULL, &cmd_map);
  107.     add_bind("n", C_DISPLAY_NEXT, NULL, &cmd_map);
  108.  
  109. #endif /* CURSES */
  110. }
  111.  
  112. /* Bindable function names.
  113.  *  Most of these can't be used if CURSES is not defined,
  114.  *  but help and lookups get confused if they aren't all here.
  115.  */
  116. struct cmd_map map_func_names[] = {
  117.     /* These MUST be in numerical order; see bindings.h */
  118.     { C_NULL,        "no-op",        NULL, NULL_MAP },
  119.     { C_GOTO_MSG,    "goto-msg",        NULL, NULL_MAP },
  120.     { C_WRITE_LIST,    "write-list",        NULL, NULL_MAP },
  121.     { C_WRITE_MSG,    "write",        NULL, NULL_MAP },
  122.     { C_SAVE_LIST,    "save-list",        NULL, NULL_MAP },
  123.     { C_SAVE_MSG,    "save",            NULL, NULL_MAP },
  124.     { C_COPY_LIST,    "copy-list",        NULL, NULL_MAP },
  125.     { C_COPY_MSG,    "copy",            NULL, NULL_MAP },
  126.     { C_DELETE_LIST,    "delete-list",        NULL, NULL_MAP },
  127.     { C_DELETE_MSG,    "delete",        NULL, NULL_MAP },
  128.     { C_UNDEL_LIST,    "undelete-list",    NULL, NULL_MAP },
  129.     { C_UNDEL_MSG,    "undelete",        NULL, NULL_MAP },
  130.     { C_REDRAW,        "redraw",        NULL, NULL_MAP },
  131.     { C_REVERSE,    "reverse-video",    NULL, NULL_MAP },
  132.     { C_NEXT_MSG,    "next-msg",        NULL, NULL_MAP },
  133.     { C_PREV_MSG,    "back-msg",        NULL, NULL_MAP },
  134.     { C_FIRST_MSG,    "first-msg",        NULL, NULL_MAP },
  135.     { C_LAST_MSG,    "last-msg",        NULL, NULL_MAP },
  136.     { C_TOP_PAGE,    "top-page",        NULL, NULL_MAP },
  137.     { C_BOTTOM_PAGE,    "bottom-page",        NULL, NULL_MAP },
  138.     { C_NEXT_SCREEN,    "screen-next",        NULL, NULL_MAP },
  139.     { C_PREV_SCREEN,    "screen-back",        NULL, NULL_MAP },
  140.     { C_SOURCE,        "source",        NULL, NULL_MAP },
  141.     { C_SAVEOPTS,    "saveopts",        NULL, NULL_MAP },
  142.     { C_NEXT_SEARCH,    "search-next",        NULL, NULL_MAP },
  143.     { C_PREV_SEARCH,    "search-back",        NULL, NULL_MAP },
  144.     { C_CONT_SEARCH,    "search-again",        NULL, NULL_MAP },
  145.     { C_PRESERVE,    "preserve",        NULL, NULL_MAP },
  146.     { C_REV_SORT,    "sort-reverse",        NULL, NULL_MAP },
  147.     { C_SORT,        "sort",            NULL, NULL_MAP },
  148.     { C_QUIT_HARD,    "quit!",        NULL, NULL_MAP },
  149.     { C_QUIT,        "quit",            NULL, NULL_MAP },
  150.     { C_EXIT_HARD,    "exit!",        NULL, NULL_MAP },
  151.     { C_EXIT,        "exit",            NULL, NULL_MAP },
  152.     { C_UPDATE,        "update",        NULL, NULL_MAP },
  153.     { C_FOLDER,        "folder",        NULL, NULL_MAP },
  154.     { C_SHELL_ESC,    "shell-escape",        NULL, NULL_MAP },
  155.     { C_CURSES_ESC,    "line-mode",        NULL, NULL_MAP },
  156.     { C_PRINT_MSG,    "lpr",            NULL, NULL_MAP },
  157.     { C_CHDIR,        "chdir",        NULL, NULL_MAP },
  158.     { C_VAR_SET,    "variable",        NULL, NULL_MAP },
  159.     { C_IGNORE,        "ignore",        NULL, NULL_MAP },
  160.     { C_ALIAS,        "alias",        NULL, NULL_MAP },
  161.     { C_OWN_HDR,    "my-hdrs",        NULL, NULL_MAP },
  162.     { C_VERSION,    "version",        NULL, NULL_MAP },
  163.     { C_MAIL_FLAGS,    "mail-flags",        NULL, NULL_MAP },
  164.     { C_MAIL,        "mail",            NULL, NULL_MAP },
  165.     { C_REPLY_ALL,    "reply-all",        NULL, NULL_MAP },
  166.     { C_REPLY_SENDER,    "reply",        NULL, NULL_MAP },
  167.     { C_DISPLAY_NEXT,    "display-next",        NULL, NULL_MAP },
  168.     { C_DISPLAY_MSG,    "display",        NULL, NULL_MAP },
  169.     { C_TOP_MSG,    "top",            NULL, NULL_MAP },
  170.     { C_BIND_MACRO,    "bind-macro",        NULL, NULL_MAP },
  171.     { C_BIND,        "bind",            NULL, NULL_MAP },
  172.     { C_UNBIND,        "unbind",        NULL, NULL_MAP },
  173.     { C_MAP_BANG,    "map!",            NULL, NULL_MAP },
  174.     { C_MAP,        "map",            NULL, NULL_MAP },
  175.     { C_MACRO,        "macro",        NULL, NULL_MAP },
  176.     /* C_HELP Must be the last one! */
  177.     { C_HELP,        "help",            NULL, NULL_MAP }
  178. };
  179.  
  180. #ifdef CURSES
  181.  
  182. /*
  183.  * getcmd() is called from curses mode only.  It waits for char input from
  184.  * the user via m_getchar() (which means that a macro could provide input)
  185.  * and then compares the chars input against the "bind"ings set up by the
  186.  * user (or the defaults).  For example, 'j' could bind to "next msg" which
  187.  * is interpreted by the big switch statement in curses_command() (curses.c).
  188.  * getcmd() returns the int-value of the curses command the input is "bound"
  189.  * to.  If the input is unrecognized, C_NULL is returned (curses_command()
  190.  * might require some cleanup, so this is valid, too).
  191.  *
  192.  * Since the input could originate from a macro rather than the terminal,
  193.  * check to see if this is the case and search for a '[' char which indicates
  194.  * that there is a curses command or other "long" command to be executed.
  195.  */
  196. getcmd()
  197. {
  198.     char         buf[MAX_BIND_LEN * 3];
  199.     register int     c, m, match;
  200.     register char    *p = buf;
  201.     register struct cmd_map *list;
  202.  
  203.     bzero(buf, MAX_BIND_LEN);
  204.     active_cmd = NULL_MAP;
  205.     c = m_getchar();
  206.     /* If user did job control (^Z), then the interrupt flag will be
  207.      * set.  Be sure it's unset before continuing.
  208.      */
  209.     turnoff(glob_flags, WAS_INTR);
  210.     if (isdigit(c)) {
  211.     buf[0] = c;
  212.     buf[1] = '\0';
  213.     Ungetstr(buf); /* So mac_flush can clear on error */
  214.     return C_GOTO_MSG;
  215.     }
  216.     for (;;) {
  217.     if (ison(glob_flags, IN_MACRO) && c == MAC_LONG_CMD)
  218.         return long_mac_cmd(c, TRUE);
  219.     else
  220.         *p++ = c;
  221.     m = 0;
  222.     for (list = cmd_map; list; list = list->m_next) {
  223.         if ((match = prefix(buf, list->m_str)) == MATCH) {
  224.         if (debug)
  225.             print("\"%s\" ",
  226.             ctrl_strcpy(buf,
  227.                     map_func_names[list->m_cmd].m_str,
  228.                     TRUE));
  229.         if (list->m_cmd == C_MACRO) {
  230.             curs_macro(list->x_str);
  231.             return getcmd();
  232.         }
  233.         active_cmd = list;
  234.         return (int)list->m_cmd;
  235.         } else if (match != NO_MATCH)
  236.         m++;
  237.     }
  238.     if (m == 0) {
  239.         if (debug) {
  240.         char tmp[sizeof buf];
  241.         print("No binding for \"%s\" found.",
  242.             ctrl_strcpy(tmp, buf, TRUE));
  243.         }
  244.         return C_NULL;
  245.     }
  246.     c = m_getchar();
  247.     }
  248. }
  249.  
  250. #endif /* CURSES */
  251.  
  252. /*
  253.  * bind_it() is used to set or unset bind, map and map! settings.
  254.  * bind is used to accelerate curses commands by mapping key sequences
  255.  * to curses commands.  map is used to accelerate command mode keysequences
  256.  * by simulating stdin.  map! is the same, but used when in compose mode.
  257.  *
  258.  * bind_it() doesn't touch messages; return -1 for curses mode.
  259.  * return -2 to have curses command set CNTD_CMD to prevent screen refresh
  260.  * to allow user to read output in case of multiple lines.
  261.  *
  262.  * Since this routine deals with a lot of binding and unbinding of things
  263.  * like line-mode "map"s and is interactive (calls Getstr()), be very careful
  264.  * not to allow expansions during interaction.
  265.  */
  266. bind_it(len, argv)
  267. char **argv;
  268. {
  269.     char string[MAX_BIND_LEN], buf[256], *name = NULL;
  270.     char *rawstr; /* raw format of string (ptr to string if no argv avail) */
  271.     char ascii[MAX_BIND_LEN*2]; /* printable ascii version of string */
  272.     register int x;
  273.     SIGRET (*oldint)(), (*oldquit)();
  274.     struct cmd_map **map_list;
  275.     int unbind = (argv && **argv == 'u');
  276.     int map = 0, is_bind_macro = 0;
  277.     int ret = 0 - iscurses; /* return value */
  278.  
  279.     if (argv && !strcmp(name = *argv, "bind-macro"))
  280.     is_bind_macro++;
  281.  
  282.     if (map = (argv && (!strcmp(name, "map!") || !strcmp(name, "unmap!"))))
  283.     map_list = &bang_map;
  284.     else if (map = (argv && (!strcmp(name, "map") || !strcmp(name, "unmap"))))
  285.     map_list = &line_map;
  286.     else
  287.     map_list = &cmd_map;
  288.  
  289.     if (argv && *++argv && !strcmp(*argv, "-?"))
  290.     /* Subtract ret and iscurses to signal output */
  291.     return help(0, name, cmd_help) - ret - iscurses;
  292.  
  293.     if (iscurses)
  294.     on_intr();
  295.  
  296.     if (unbind) {
  297.     if (!*argv) {
  298.         print("%s what? ", name);
  299.         len = Getstr(buf, sizeof buf, 0);
  300.         if (len <= 0) {
  301.         if (iscurses)
  302.             off_intr();
  303.         return -1;
  304.         }
  305.         rawstr = m_xlate(buf);
  306.     } else
  307.         rawstr = m_xlate(*argv);
  308.     if (!un_bind(rawstr, map_list)) {
  309.         ctrl_strcpy(ascii, rawstr, TRUE);
  310.         print("\"%s\" isn't bound to a command.\n", ascii);
  311.     }
  312.     if (iscurses)
  313.         off_intr();
  314.     return ret;
  315.     }
  316.     if (argv && *argv) {
  317.     rawstr = m_xlate(*argv);
  318.     (void) ctrl_strcpy(ascii, rawstr, TRUE);
  319.     if (!*++argv) {
  320.         /*
  321.          * determine whether "argv" references a "map" or a "bind"
  322.          */
  323.         int binding = c_bind(rawstr, *map_list);
  324.         if (binding == C_MACRO) {
  325.         char *mapping = c_macro(NULL, rawstr, *map_list);
  326.         if (mapping) {
  327.             print("\"%s\" is mapped to ", ascii);
  328.             print_more("\"%s\".\n",
  329.             ctrl_strcpy(buf, mapping, FALSE));
  330.         } else
  331.             print("\"%s\" isn't mapped.\n", ascii);
  332.         } else if (binding)
  333.         print("\"%s\" is %s to \"%s\".\n", ascii,
  334.             map? "mapped" : "bound", map_func_names[binding].m_str);
  335.         else if (map)
  336.         print("\"%s\" isn't mapped.\n", ascii);
  337.         else
  338.         print("\"%s\" isn't bound to a command.\n", ascii);
  339.         if (iscurses)
  340.         off_intr();
  341.         return ret;
  342.     }
  343.     } else {
  344.     print("%s [<CR>=all, -?=help]: ", name);
  345.     len = Getstr(string, MAX_BIND_LEN-1, 0);
  346.     if (len == 0) {
  347.         int add_to_ret = iscurses;
  348. #ifdef CURSES
  349.         if (iscurses)
  350.         move(LINES-1, 0), refresh();
  351. #endif
  352.         if (map || is_bind_macro)
  353.         add_to_ret = !c_macro(name, NULL, *map_list);
  354.         else
  355.         add_to_ret = !c_bind(NULL, *map_list);
  356.         if (iscurses)
  357.         off_intr();
  358.         /* signal CTND_CMD if there was output */
  359.         return ret - add_to_ret;
  360.     }
  361.     if (len < 0) {
  362.         if (iscurses)
  363.         off_intr();
  364.         return ret;
  365.     }
  366.     rawstr = m_xlate(string);
  367.     (void) ctrl_strcpy(ascii, rawstr, TRUE);
  368.     }
  369.     /* if a binding was given on the command line */
  370.     if (argv && *argv && !map)
  371.     if (is_bind_macro)
  372.         (void) strcpy(buf, "macro");
  373.     else
  374.         (void) strcpy(buf, *argv++);
  375.     else {
  376.     /* at this point, "rawstr" and "ascii" should both be set */
  377.     int binding;
  378.  
  379.     if (!strcmp(ascii, "-?")) {
  380.         if (iscurses)
  381.         clr_bot_line();
  382.         ret -= help(0, name, cmd_help);
  383.         if (iscurses)
  384.         off_intr();
  385.         /* Subtract iscurses to signal CNTD_CMD */
  386.         return ret - iscurses;
  387.     }
  388.  
  389.     if (!map && !is_bind_macro) {
  390.         binding = c_bind(rawstr, *map_list);
  391.  
  392.         for (len = 0; len == 0; ) {
  393.         print("\"%s\" = <%s>: New binding [<CR> for list]: ",
  394.             ascii, (binding? map_func_names[binding].m_str : "unset"));
  395.         len = Getstr(buf, sizeof buf, 0);
  396.         if (iscurses)
  397.             clr_bot_line();
  398.         /* strip any trailing whitespace */
  399.         if (len > 0)
  400.             len = no_newln(buf) - buf;
  401.         if (len == 0) {
  402.             (void) do_pager(NULL, TRUE);
  403.             if (iscurses)
  404.             putchar('\n');
  405.             for (x = 1; x <= C_HELP; x++) {
  406.             if (!(x % 4))
  407.                 if (do_pager("\n", FALSE) == EOF)
  408.                 break;
  409.             (void) do_pager(sprintf(buf, "%-15.15s  ",
  410.                         map_func_names[x].m_str), FALSE);
  411.             }
  412.             (void) do_pager("\n", FALSE);
  413.             (void) do_pager(NULL, FALSE);
  414.             ret -= iscurses;
  415.         }
  416.         }
  417.     } else /* map */
  418.         (void) strcpy(buf, "macro"), len = 5;
  419.     /* if list was printed, ret < -1 -- tells CNTD_CMD to be set and
  420.      * prevents screen from being refreshed (lets user read output
  421.      */
  422.     if (len == -1) {
  423.         if (iscurses)
  424.         off_intr();
  425.         return ret;
  426.     }
  427.     }
  428.     for (x = 1; x <= C_HELP; x++) {
  429.     if (prefix(buf, map_func_names[x].m_str) == MATCH) {
  430.         int add_to_ret;
  431.         if (debug)
  432.         print("\"%s\" will execute \"%s\".\n", ascii, buf);
  433.         if (map_func_names[x].m_cmd == C_MACRO) {
  434.         if (argv && *argv) {
  435.             (void) argv_to_string(buf, argv);
  436.             (void) m_xlate(buf); /* Convert buf to raw chars */
  437.             add_to_ret =
  438.             do_bind(rawstr, map_func_names[x].m_cmd, buf, map_list);
  439.         } else {
  440.             char exp[MAX_MACRO_LEN*2]; /* printable expansion */
  441.             char *mapping = c_macro(NULL, rawstr, *map_list);
  442.  
  443.             if (mapping)
  444.             (void) ctrl_strcpy(exp, mapping, TRUE);
  445.             print("\"%s\" = <%s>", ascii, mapping ? exp : "unset");
  446.             putchar('\n'), print("New macro: ");
  447.             ret -= iscurses; /* To signal screen messed up */
  448.             /* we are done with buf, so we can trash over it */
  449.             len = Getstr(buf, MAX_MACRO_LEN, 0);
  450.             if (len > 0) {
  451.             if (iscurses)
  452.                 clr_bot_line();
  453.             (void) m_xlate(buf); /* Convert buf to raw chars */
  454.             add_to_ret =
  455.                 do_bind(rawstr, C_MACRO, buf, map_list);
  456.             if (debug) {
  457.                 (void) ctrl_strcpy(exp, buf, TRUE);
  458.                 print("\"%s\" will execute \"%s\".\n", ascii, exp);
  459.             }
  460.             } else if (len < 0) {
  461.             if (iscurses)
  462.                 off_intr();
  463.             return ret - add_to_ret;
  464.             } else
  465.             print("Can't bind to null macro"), putchar('\n');
  466.         }
  467.         } else /* not a macro */ {
  468.         (void) argv_to_string(buf, argv);
  469.         add_to_ret =
  470.             do_bind(rawstr, map_func_names[x].m_cmd, buf, map_list);
  471.         }
  472.         /* if do_bind had no errors, it returned -1.  If we already
  473.          * messed up the screen, then ret is less than -1.  return the
  474.          * lesser of the two to make sure that CNTD_CMD gets set right
  475.          */
  476.         if (iscurses)
  477.         off_intr();
  478.         return min(add_to_ret, ret);
  479.     }
  480.     }
  481.     print("\"%s\": Unknown function.\n", buf);
  482.     if (iscurses)
  483.     off_intr();
  484.     return ret;
  485. }
  486.  
  487. /*
  488.  * print current key to command bindings if "str" is NULL.
  489.  * else return the integer "m_cmd" which the str is bound to.
  490.  */
  491. c_bind(str, opts)
  492. register char *str;
  493. register struct cmd_map *opts;
  494. {
  495.     register int    incurses = iscurses;
  496.  
  497.     if (!str) {
  498.     if (!opts) {
  499.         print("No command bindings.\n");
  500.         return C_ERROR;
  501.     }
  502.     if (incurses)
  503.         clr_bot_line(), iscurses = FALSE;
  504.     (void) do_pager(NULL, TRUE);
  505.     (void) do_pager("Current key to command bindings:\n", FALSE);
  506.     (void) do_pager("\n", FALSE);
  507.     }
  508.  
  509.     for (; opts; opts = opts->m_next) {
  510.     char buf[BUFSIZ], buf2[MAX_BIND_LEN], exp[MAX_MACRO_LEN*2], *xp;
  511.     if (!str) {
  512.         (void) ctrl_strcpy(buf2, opts->m_str, FALSE);
  513.         if ((xp = opts->x_str) && opts->m_cmd == C_MACRO)
  514.         xp = ctrl_strcpy(exp, opts->x_str, TRUE);
  515.         if (do_pager(sprintf(buf, "%s\t%-15.15s %s\n",
  516.              buf2, map_func_names[opts->m_cmd].m_str,
  517.              xp? xp : ""),
  518.              FALSE) == EOF)
  519.         break;
  520.     } else
  521.         if (strcmp(str, opts->m_str))
  522.         continue;
  523.         else
  524.         return opts->m_cmd;
  525.     }
  526.  
  527.     iscurses = incurses;
  528.     if (!str)
  529.     (void) do_pager(NULL, FALSE);
  530.     return C_NULL;
  531. }
  532.  
  533. /*
  534.  * Doesn't touch messages, but changes macros: return -1.
  535.  * Error output causes return < -1.
  536.  *  args is currently the execute string of a macro mapping, but may be
  537.  *  used in the future as an argument string for any curses command.
  538.  */
  539. do_bind(str, func, args, map_list)
  540. register char *str, *args;
  541. struct cmd_map **map_list;
  542. long func;
  543. {
  544.     register int ret = -1;
  545.     register struct cmd_map *list;
  546.     int match;
  547.  
  548.     if (func == C_MACRO && !check_mac_bindings(args))
  549.     --ret;
  550.     (void) un_bind(str, map_list);
  551.     for (list = *map_list; list; list = list->m_next)
  552.     if ((match = prefix(str, list->m_str)) != NO_MATCH) {
  553.         ret--;
  554.         switch (match) {
  555.         case MATCH:
  556.             puts("Something impossible just happened.");
  557.         when A_PREFIX_B:
  558.             wprint("Warning: \"%s\" prefixes \"%s\" (%s)\n", str,
  559.             list->m_str, map_func_names[list->m_cmd].m_str);
  560.         when B_PREFIX_A:
  561.             wprint("Warning: \"%s\" (%s) prefixes: \"%s\"\n",
  562.             list->m_str, map_func_names[list->m_cmd].m_str, str);
  563.         }
  564.     }
  565.     add_bind(str, func, args, map_list);
  566.     /* errors decrement ret.  If ret returns less than -1, CNTD_CMD is set
  567.      * and no redrawing is done so user can see the warning signs
  568.      */
  569.     return ret;
  570. }
  571.  
  572. /*
  573.  * add a binding to a list.  This may include "map"s or other mappings since
  574.  * the map_list argument can control that.  The "func" is an int defined in
  575.  * bindings.h ... the "str" passed is the string the user would have to type
  576.  * to get the macro/map/binding expanded.  This must in in raw format: no
  577.  * \n's to mean \015.  Convert first using m_xlate().
  578.  */
  579. add_bind(str, func, args, map_list)
  580. register char *str, *args;
  581. struct cmd_map **map_list;
  582. long func;
  583. {
  584.     register struct cmd_map *tmp;
  585.     struct cmd_map *calloc();
  586.  
  587.     if (!str || !*str)
  588.     return;
  589.  
  590.     /* now make a new option struct and set fields */
  591.     if (!(tmp = calloc((unsigned)1, sizeof(struct cmd_map)))) {
  592.     error("calloc");
  593.     return;
  594.     }
  595.     tmp->m_next = *map_list;
  596.     *map_list = tmp;
  597.  
  598.     tmp->m_str = savestr(str);
  599.     tmp->m_cmd = func; /* strdup handles the NULL case */
  600.     if (args && *args)
  601.     tmp->x_str = savestr(args);
  602.     else
  603.     tmp->x_str = NULL;
  604. }
  605.  
  606. static
  607. un_bind(p, map_list)
  608. register char *p;
  609. struct cmd_map **map_list;
  610. {
  611.     register struct cmd_map *list = *map_list, *tmp;
  612.  
  613.     if (!list || !*list->m_str || !p || !*p)
  614.     return 0;
  615.  
  616.     if (!strcmp(p, (*map_list)->m_str)) {
  617.     *map_list = (*map_list)->m_next;
  618.     xfree (list->m_str);
  619.     if (list->x_str)
  620.         xfree (list->x_str);
  621.     xfree((char *)list);
  622.     return 1;
  623.     }
  624.     for ( ; list->m_next; list = list->m_next)
  625.     if (!strcmp(p, list->m_next->m_str)) {
  626.         tmp = list->m_next;
  627.         list->m_next = list->m_next->m_next;
  628.         xfree (tmp->m_str);
  629.         if (tmp->x_str)
  630.         xfree (tmp->x_str);
  631.         xfree ((char *)tmp);
  632.         return 1;
  633.     }
  634.     return 0;
  635. }
  636.  
  637. prefix(a, b)
  638. register char *a, *b;
  639. {
  640.     if (!a || !b)
  641.     return NO_MATCH;
  642.  
  643.     while (*a && *b && *a == *b)
  644.     a++, b++;
  645.     if (!*a && !*b)
  646.     return MATCH;
  647.     if (!*a && *b)
  648.     return A_PREFIX_B;
  649.     if (*a && !*b)
  650.     return B_PREFIX_A;
  651.     return NO_MATCH;
  652. }
  653.