home *** CD-ROM | disk | FTP | other *** search
/ ftp.freefriends.org / ftp.freefriends.org.tar / ftp.freefriends.org / arnold / Source / mush.rstevens.tar.gz / mush.tar / bind.c < prev    next >
C/C++ Source or Header  |  1992-10-30  |  20KB  |  649 lines

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