home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / mush-7.1.1 / loop.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-04-30  |  34.1 KB  |  1,308 lines

  1. /* loop.c     (c) copyright 1986 (Dan Heller) */
  2.  
  3. /*
  4.  * Here is where the main loop for text mode exists. Also, all the
  5.  * history is kept here and all the command parsing and execution
  6.  * and alias expansion in or out of text/graphics mode is done here.
  7.  */
  8.  
  9. #include "mush.h"
  10. #include "version.h"
  11.  
  12. #ifdef BSD
  13. #include <sys/wait.h>
  14. #else
  15. #ifndef SYSV
  16. #include <wait.h>
  17. #endif /* SYSV */
  18. #endif /* BSD */
  19.  
  20. #define ever (;;)
  21. #define MAXARGS        100
  22. #define isdelimeter(c)    (index(" \t;|", c))
  23.  
  24. char *alias_expand(), *hist_expand(), *reference_hist(), *hist_from_str();
  25. char *calloc();
  26.  
  27. struct history {
  28.     int histno;
  29.     char **argv;
  30.     struct history *prev;
  31.     struct history *next;
  32. };
  33. static struct history *hist_head, *hist_tail;
  34. #define malloc(n)    (struct history *)calloc((unsigned)1,(unsigned)(n))
  35. #define NULL_HIST    (struct history *)0
  36.  
  37. static char *last_aliased;
  38. static int hist_size, print_only;
  39.  
  40. do_loop()
  41. {
  42.     register char *p, **argv;
  43.     char      **last_argv = DUBL_NULL, line[256];
  44.     int         argc, c = (iscurses - 1);
  45. #ifdef CURSES
  46.     int          save_echo_flg = FALSE;
  47. #endif /* CURSES */
  48.  
  49.     /* catch the right signals -- see main.c for other signal catching */
  50.     (void) signal(SIGINT, catch);
  51.     (void) signal(SIGQUIT, catch);
  52.     (void) signal(SIGHUP, catch);
  53. #ifndef SUNTOOL
  54.     (void) signal(SIGTERM, catch);
  55.     (void) signal(SIGCHLD,
  56. #ifndef SYSV
  57.                sigchldcatcher
  58. #else /* SYSV */
  59.                SIG_DFL
  60. #endif /* SYSV */
  61.                );
  62. #endif /* SUNTOOL*/
  63.  
  64.     turnoff(glob_flags, IGN_SIGS);
  65.     if (hist_size == 0) /* if user didn't set history in .rc file */
  66.     hist_size = 1;
  67.  
  68.     for ever {
  69.     if (setjmp(jmpbuf)) {
  70.         Debug("jumped back to main loop (%s: %d)\n", __FILE__,__LINE__);
  71. #ifdef CURSES
  72.         if (c > 0) { /* don't pass last command back to curses_command() */
  73.         iscurses = TRUE;
  74.         c = hit_return();
  75.         }
  76. #endif /* CURSES */
  77.     }
  78. #ifdef CURSES
  79.     if (iscurses || c > -1) {
  80.         /* if !iscurses, we know that we returned from a curses-based
  81.          * call and we really ARE still in curses. Reset tty modes!
  82.          */
  83.         if (ison(glob_flags, ECHO_FLAG)) {
  84.         turnoff(glob_flags, ECHO_FLAG);
  85.         echo_off();
  86.         save_echo_flg = TRUE;
  87.         }
  88.         if (!iscurses) {
  89.         iscurses = TRUE;
  90.         c = hit_return();
  91.         }
  92.         if (c < 0)
  93.         c = 0;
  94.         if ((c = curses_command(c)) == -1 && save_echo_flg) {
  95.         echo_on();
  96.         turnon(glob_flags, ECHO_FLAG);
  97.         save_echo_flg = FALSE;
  98.         }
  99.         continue;
  100.     }
  101. #endif /* CURSES */
  102.     clear_msg_list(msg_list);
  103.     (void) check_new_mail();
  104.  
  105.     /* print a prompt according to printf like format:
  106.      * (current message, deleted, unread, etc) are found in mail_status.
  107.      */
  108.     mail_status(1);
  109.     if (Getstr(line, sizeof(line), 0) > -1)
  110.         p = line;
  111.     else {
  112.         if (isatty(0) && (p = do_set(set_options, "ignoreeof"))) {
  113.         if (!*p)
  114.             continue;
  115.         else
  116.             p = strcpy(line, p); /* so processing won't destroy var */
  117.         } else {
  118.         putchar('\n');
  119.         (void) mush_quit(0, DUBL_NULL);
  120.         continue; /* quit may return if new mail arrives */
  121.         }
  122.     }
  123.  
  124.     skipspaces(0);
  125.     if (!*p && !(p = do_set(set_options, "newline"))) {
  126.         (void) readmsg(0, DUBL_NULL, msg_list);
  127.         continue;
  128.     }
  129.     if (!*p) /* if newline is set, but no value, then continue */
  130.         continue;
  131.  
  132.     /* upon error, argc = -1 -- still save in history so user can
  133.      * modify syntax error. if !argv, error is too severe.  We pass
  134.      * the last command typed in last_argv for history reference, and
  135.      * get back the current command _as typed_ (unexpanded by aliases
  136.      * or history) in last_argv.
  137.      */
  138.     if (!(argv = make_command(p, &last_argv, &argc)))
  139.         continue;
  140.     /* now save the old argv in a newly created history structure */
  141.     (void) add_history(0, last_argv); /* argc is currently ignored */
  142.  
  143.     if (print_only) {
  144.         print_only = 0;
  145.         free_vec(argv);
  146.     } else if (argc > -1)
  147.         (void) do_command(argc, argv, msg_list);
  148.     }
  149. }
  150.  
  151. /* Add a command to the history list
  152.  */
  153. /*ARGSUSED*/
  154. add_history(un_used, argv)
  155. char **argv;
  156. {
  157.     struct history *new;
  158.  
  159.     if (!(new = malloc(sizeof (struct history))))
  160.     error("can't increment history");
  161.     else {
  162.     new->histno = ++hist_no;
  163.     new->argv = argv;    /* this is the command _as typed_ */
  164.     new->next = NULL_HIST;
  165.     new->prev = hist_head;
  166.     /* if first command, the tail of the list is "new" because
  167.      * nothing is in the list.  If not the first command, the
  168.      * head of the list's "next" pointer points to the new command.
  169.      */
  170.     if (hist_head)
  171.         hist_head->next = new;
  172.     else
  173.         hist_tail = new;
  174.     hist_head = new;
  175.     }
  176.     /*
  177.      * truncate the history list to the size of the history.
  178.      * Free the outdated command (argv) and move the tail closer to front.
  179.      * use a while loop in case the last command reset histsize to "small"
  180.      */
  181.     while (hist_head->histno - hist_tail->histno >= hist_size) {
  182.     hist_tail = hist_tail->next;
  183.     free_vec(hist_tail->prev->argv);
  184.     xfree((char *) (hist_tail->prev));
  185.     hist_tail->prev = NULL_HIST;
  186.     }
  187. }
  188.  
  189. /* make a command from "buf".
  190.  * first, expand history references. make an argv from that and save
  191.  * in last_argv (to be passed back and stored in history). After that,
  192.  * THEN expand aliases. return that argv to be executed as a command.
  193.  */
  194. char **
  195. make_command(start, last_argv, argc)
  196. register char *start, ***last_argv;
  197. int *argc;
  198. {
  199.     register char *p, **tmp;
  200.     char buf[BUFSIZ];
  201.  
  202.     if (!last_argv)
  203.     tmp = DUBL_NULL;
  204.     else
  205.     tmp = *last_argv;
  206.     /* first expand history -- (here's where argc gets set)
  207.      * pass the buffer, the history list to reference if \!* (or whatever)
  208.      * result in static buffer (pointed to by p) -- even if history parsing is
  209.      * ignored, do this to remove \'s behind !'s and verifying matching quotes
  210.      */
  211.     if (!(p = hist_expand(start, tmp, argc)) || Strcpy(buf, p) > sizeof buf)
  212.     return DUBL_NULL;
  213.     /* if history was referenced in the command, echo new command */
  214.     if (*argc)
  215.     puts(buf);
  216.  
  217.     /* argc may == -1; ignore this error for now but catch it later */
  218.     if (!(tmp = mk_argv(buf, argc, 0)))
  219.     return DUBL_NULL;
  220.  
  221.     /* save this as the command typed */
  222.     if (last_argv)
  223.     *last_argv = tmp;
  224.  
  225.     /* expand all aliases (recursively)
  226.      * pass _this_ command (as typed and without aliases) to let aliases
  227.      * with "!*" be able to reference the command line just typed.
  228.      */
  229.     if (alias_stuff(buf, *argc, tmp) == -1)
  230.     return DUBL_NULL;
  231.  
  232.     if (!last_argv)
  233.     free_vec(tmp);
  234.  
  235.     /* with everything expanded, build final argv from new buffer
  236.      * Note that backslashes and quotes still exist. Those are removed
  237.      * because argument final is 1.
  238.      */
  239.     tmp = mk_argv(buf, argc, 1);
  240.     return tmp;
  241. }
  242.  
  243. /* Return values from commands, see check_internal() */
  244. static int last_status;            /* Changes after every command */
  245. static char last_output[MAXMSGS];    /* Changes after SUCCESSFUL command */
  246.  
  247. /*
  248.  * do the command specified by the argument vector, argv.
  249.  * First check to see if argc < 0. If so, someone called this
  250.  * command and they should not have! make_command() will return
  251.  * an argv but it will set argc to -1 if there's a syntax error.
  252.  */
  253. do_command(argc, argv, list)
  254. char **argv, list[];
  255. {
  256.     register char *p;
  257.     char **tmp = argv, *next_cmd = NULL;
  258.     int i, status = 0;
  259.     long do_pipe = ison(glob_flags, DO_PIPE);
  260.  
  261.     if (argc <= 0) {
  262.     turnoff(glob_flags, DO_PIPE);
  263.     return -1;
  264.     }
  265.  
  266.     clear_msg_list(list);
  267.  
  268.     for (i = 0; do_pipe >= 0 && argc; argc--) {
  269.     p = argv[i];
  270.     /* mk_argv inserts a boolean in argv[i][2] for separators */
  271.     if ((!strcmp(p, "|") || !strcmp(p, ";")) && p[2]) {
  272.         if (do_pipe = (*p == '|'))
  273.         turnon(glob_flags, DO_PIPE);
  274.         else if (next_cmd = argv[i+1])
  275.         argv[i+1] = NULL, argc--;
  276.         argv[i] = NULL;
  277.         if ((status = exec_argv(i, argv, list)) <= -1)
  278.         mac_flush();
  279.         else
  280.         list_to_str(list, last_output);
  281.         turnon(glob_flags, IGN_SIGS); /* prevent longjmp */
  282.         /* if piping, then don't call next command if this one failed. */
  283.         if (status <= -1 && do_pipe) {
  284.         print("Broken pipe.\n");
  285.         do_pipe = -1, turnoff(glob_flags, DO_PIPE);
  286.         }
  287.         last_status = status;
  288.         /* if command failed and piping, or command worked and not piping */
  289.         if (do_pipe <= 0)
  290.         status = 0, clear_msg_list(list);
  291.         /* else command worked and piping: set is_pipe */
  292.         else if (!status)
  293.         turnon(glob_flags, IS_PIPE), turnoff(glob_flags, DO_PIPE);
  294.         argv[i] = p;
  295.         argv += (i+1);
  296.         i = 0;
  297.         turnoff(glob_flags, IGN_SIGS);
  298.     } else
  299.         i++;
  300.     }
  301.     if (*argv && do_pipe >= 0) {
  302.     status = exec_argv(i, argv, list);
  303.     turnon(glob_flags, IGN_SIGS);
  304.     if (status < 0) {
  305.         mac_flush();
  306.     } else
  307.         list_to_str(list, last_output);
  308.     last_status = status;
  309.     }
  310.     Debug("freeing: "), print_argv(tmp);
  311.     free_vec(tmp);
  312.     turnoff(glob_flags, DO_PIPE), turnoff(glob_flags, IS_PIPE);
  313.     if (next_cmd) {
  314.     if (tmp = mk_argv(next_cmd, &argc, 1)) {
  315.         turnoff(glob_flags, IGN_SIGS);
  316.         status = do_command(argc, tmp, list);
  317.         turnon(glob_flags, IGN_SIGS);
  318.     } else
  319.         status = argc;
  320.     xfree(next_cmd);
  321.     }
  322.     turnoff(glob_flags, IGN_SIGS);
  323.     return status;
  324. }
  325.  
  326. exec_argv(argc, argv, list)
  327. register char **argv, list[];
  328. {
  329.     register int n;
  330.  
  331.     if (!argv || !*argv || argv[0][0] == '\\' && !argv[0][1]) {
  332.     if (ison(glob_flags, IS_PIPE))
  333.         print("Invalid null command.\n");
  334.     else if (ison(glob_flags, DO_PIPE)) {
  335.         set_msg_bit(list, current_msg);
  336.         return 0;
  337.     }
  338.     return -1;
  339.     } else if (argv[0][0] == '\\') {
  340.     /* Can't change *argv (breaks free_vec),
  341.      *  so shift to remove the backslash
  342.      */
  343.     for (n = 0; argv[0][n]; n++)
  344.         argv[0][n] = argv[0][n+1];
  345.     }
  346.     Debug("executing: "), print_argv(argv);
  347.  
  348.     /* if interrupted during execution of a command, return -1 */
  349.     if (isoff(glob_flags, IGN_SIGS) && setjmp(jmpbuf)) {
  350.     Debug("jumped back to exec_argv (%s: %d)\n", __FILE__, __LINE__);
  351.     return -1;
  352.     }
  353.  
  354.     /* standard commands */
  355.     for (n = 0; cmds[n].command; n++)
  356.     if (!strcmp(argv[0], cmds[n].command))
  357.         return (*cmds[n].func)(argc, argv, list);
  358.  
  359.     /* ucb-Mail compatible commands */
  360.     for (n = 0; ucb_cmds[n].command; n++)
  361.     if (!strcmp(argv[0], ucb_cmds[n].command))
  362.         return (*ucb_cmds[n].func)(argc, argv, list);
  363.  
  364.     /* for hidden, undocumented commands */
  365.     for (n = 0; hidden_cmds[n].command; n++)
  366.     if (!strcmp(argv[0], hidden_cmds[n].command))
  367.         return (*hidden_cmds[n].func)(argc, argv, list);
  368.  
  369.     n = -1; /* default to failure */
  370.     if ((isdigit(**argv) || index("^.*$-`{}", **argv))
  371.             && (n = get_msg_list(argv, list)) != 0) {
  372.     if (n < 0)
  373.         return -1;
  374.     else if (isoff(glob_flags, DO_PIPE))
  375.         for (n = 0; n < msg_cnt; n++)
  376.         if (msg_bit(list, n)) {
  377.             display_msg((current_msg = n), (long)0);
  378.             unset_msg_bit(list, n);
  379.         }
  380.     return 0;
  381.     } else {
  382.     /* get_msg_list will set the current message bit if nothing parsed */
  383.     if (n == 0)
  384.         unset_msg_bit(list, current_msg);
  385.     if (strlen(*argv) == 1 && index("$^.", **argv)) {
  386.         if (!msg_cnt) {
  387.         print("No messages.");
  388.         return -1;
  389.         } else {
  390.         if (**argv != '.')
  391.             current_msg = (**argv == '$') ? msg_cnt-1 : 0;
  392.         set_msg_bit(list, current_msg);
  393.         display_msg(current_msg, (long)0);
  394.         }
  395.         return 0;
  396.     }
  397.     }
  398.  
  399.     if (!istool && do_set(set_options, "unix")) {
  400.     if (ison(glob_flags, IS_PIPE)) {
  401.         return pipe_msg(argc, argv, list);
  402.     } else
  403.         execute(argv);  /* try to execute a unix command */
  404.     return -1; /* doesn't affect messages! */
  405.     }
  406.  
  407.     print("%s: command not found.\n", *argv);
  408.     if (!istool)
  409.     print("type '?' for valid commands, or type `help'\n");
  410.     return -1;
  411. }
  412.  
  413. /* recursively look for aliases on a command line.  aliases may
  414.  * reference other aliases.
  415.  */
  416. alias_stuff(b, argc, Argv)
  417. register char     *b, **Argv;
  418. {
  419.     register char     *p, **argv = DUBL_NULL;
  420.     register int     n = 0, i = 0, Argc;
  421.     static int         loops;
  422.     int         dummy;
  423.  
  424.     if (++loops == 20) {
  425.     print("Alias loop.\n");
  426.     return -1;
  427.     }
  428.     for (Argc = 0; Argc < argc; Argc++) {
  429.     register char *h = Argv[n + ++i];
  430.     register char *p2 = "";
  431.     int sep;
  432.  
  433.     /* we've hit a command separator or the end of the line */
  434.     if (h && strcmp(h, ";") && strcmp(h, "|"))
  435.         continue;
  436.  
  437.     /* create a new argv containing this (possible subset) of argv */
  438.     if (!(argv = (char **)calloc((unsigned)(i+1), sizeof (char *))))
  439.         continue;
  440.     sep = n + i;
  441.     while (i--)
  442.         strdup(argv[i], Argv[n+i]);
  443.  
  444.     if ((!last_aliased || strcmp(last_aliased, argv[0]))
  445.             && (p = alias_expand(argv[0]))) {
  446.         /* if history was referenced, ignore the rest of argv
  447.          * else copy all of argv onto the end of the buffer.
  448.          */
  449.         if (!(p2 = hist_expand(p, argv, &dummy)))
  450.         break;
  451.         if (!dummy)
  452.         (void) argv_to_string(p2+strlen(p2), argv+1);
  453.         if (Strcpy(b, p2) > BUFSIZ) {
  454.         print("Not enough buffer space.\n");
  455.         break;
  456.         }
  457.         /* release old argv and build a new one based on new string */
  458.         free_vec(argv);
  459.         if (!(argv = mk_argv(b, &dummy, 0)))
  460.         break;
  461.         if (alias_stuff(b, dummy, argv) == -1)
  462.         break;
  463.     } else
  464.         b = argv_to_string(b, argv);
  465.     xfree(last_aliased), last_aliased = NULL;
  466.     free_vec(argv);
  467.     b += strlen(b);
  468.     if (h) {
  469.         b += strlen(sprintf(b, " %s ", h));
  470.         while (++Argc < argc && (h = Argv[Argc]))
  471.         if (Argc > sep && strcmp(h, ";"))
  472.             break;
  473.         n = Argc--;
  474.     }
  475.     i = 0;
  476.     }
  477.     xfree(last_aliased), last_aliased = NULL;
  478.     --loops;
  479.     if (Argc < argc) {
  480.     free_vec(argv);
  481.     return -1;
  482.     }
  483.     return 0;
  484. }
  485.  
  486. char *
  487. alias_expand(cmd)
  488. register char *cmd;
  489. {
  490.     register char *p;
  491.     register int x;
  492.  
  493.     if (!(p = do_set(functions, cmd)))
  494.     return NULL;
  495.     last_aliased = savestr(cmd); /* to be freed elsewhere; don't strdup! */
  496.     if (isoff(glob_flags, WARNING))
  497.     return p;
  498.     for (x = 0; cmds[x].command; x++)
  499.     if (!strcmp(cmd, cmds[x].command)) {
  500.         wprint("(real command: \"%s\" aliased to \"%s\")\n", cmd, p);
  501.         return p;
  502.     }
  503.     for (x = 0; ucb_cmds[x].command; x++)
  504.     if (!strcmp(cmd, ucb_cmds[x].command)) {
  505.         wprint("(ucb-command: \"%s\" aliased to \"%s\")\n", cmd, p);
  506.         return p;
  507.     }
  508.     return p;
  509. }
  510.  
  511. static int nonobang;
  512.  
  513. /* expand history references and separate message lists from other tokens */
  514. char *
  515. hist_expand(str, argv, hist_was_referenced)
  516. register char *str, **argv;
  517. register int *hist_was_referenced;
  518. {
  519.     static char   buf[BUFSIZ];
  520.     register int  b = 0, inquotes = 0;
  521.     int       first_space = 0, ignore_bang;
  522.  
  523.     ignore_bang = (ison(glob_flags, IGN_BANG) ||
  524.            do_set(set_options, "ignore_bang"));
  525.     nonobang = !!do_set(set_options, "nonobang");
  526.  
  527.     if (hist_was_referenced)
  528.     *hist_was_referenced = 0;
  529.     while (*str) {
  530.     while (!inquotes && isspace(*str))
  531.         str++;
  532.     do  {
  533.         if (!*str)
  534.         break;
  535.         if (b >= sizeof(buf)-1) {
  536.         print("argument list too long.\n");
  537.         return NULL;
  538.         }
  539.         if ((buf[b] = *str++) == '\'') {
  540.         /* make sure there's a match! */
  541.         inquotes = !inquotes;
  542.         }
  543.         if (!first_space && !inquotes && index("0123456789{}*$^.", buf[b])
  544.                  && b && !index("0123456789{}-^. \t", buf[b-1])) {
  545.         buf[b+1] = buf[b];
  546.         buf[b++] = ' ';
  547.         while ((buf[++b] = *str++) && index("0123456789-,${}.", buf[b]))
  548.             ;
  549.         if (!buf[b])
  550.             str--;
  551.         first_space++;
  552.         }
  553.         /* check for (;) (|) or any other delimiter and separate it from
  554.          * other tokens.
  555.          */
  556.         if (!inquotes && buf[b] != '\0' && isdelimeter(buf[b]) &&
  557.             (b < 0 || buf[b-1] != '\\')) {
  558.         if (!isspace(buf[b]))
  559.             first_space = -1; /* resume msg-list separation */
  560.         if (b && !isspace(buf[b-1]))
  561.             buf[b+1] = buf[b], buf[b++] = ' ';
  562.         b++;
  563.         break;
  564.         }
  565.         /*
  566.          * If double-quotes, just copy byte by byte, char by char,
  567.          *  but do remove backslashes from in front of !s
  568.          */
  569.         if (!inquotes && buf[b] == '"') {
  570.         int B = b;
  571.         while ((buf[++B] = *str++) && buf[B] != '"')
  572.             if (*str == '!' && buf[B] == '\\')
  573.             buf[B] = '!', str++;
  574.         if (buf[B])
  575.             b = B;
  576.         else
  577.             str--;
  578.         b++;
  579.         continue;
  580.         }
  581.         if (buf[b] == '\\') {
  582.         first_space = 1;    /* don't split escaped words */
  583.         if ((buf[++b] = *str) == '!')
  584.             buf[--b] = '!';
  585.         ++str;
  586.         } else if (buf[b] == '!' && *str && *str != '\\' && !isspace(*str)
  587.                && !ignore_bang) {
  588.         char word[BUFSIZ], *s;
  589.         if (!(s = reference_hist(str, word, argv))) {
  590.             if (!nonobang)
  591.             return NULL;
  592.         } else {
  593.             str = s;
  594.             if (hist_was_referenced)
  595.             *hist_was_referenced = 1;
  596.             if (strlen(word) + b >= sizeof buf) {
  597.             print("argument list too long.\n");
  598.             return NULL;
  599.             }
  600.             b += Strcpy(&buf[b], word) - 1;
  601.         }
  602.         }
  603.         b++;
  604.     } while (*str && (!isdelimeter(*str) || str[-1] == '\\'));
  605.     if (!inquotes)
  606.         first_space++, buf[b++] = ' ';
  607.     }
  608.     buf[b] = 0;
  609.     return buf;
  610. }
  611.  
  612. /*
  613.  * expand references to internal variables.  This allows such things
  614.  * as $iscurses, $hdrs_only, etc. to work correctly.
  615.  */
  616. char *
  617. check_internal(str)
  618. register char *str;
  619. {
  620.     int ret_val = -1;
  621.     static char version[80], get_status[4];
  622.  
  623.     if (!strcmp(str, "iscurses"))
  624.     ret_val = (iscurses || ison(glob_flags, PRE_CURSES));
  625.     else if (!strcmp(str, "istool"))
  626.     ret_val = istool;
  627.     else if (!strcmp(str, "hdrs_only"))
  628.     ret_val = (hdrs_only && *hdrs_only);
  629.     else if (!strcmp(str, "is_shell"))
  630.     ret_val = is_shell;
  631.     else if (!strcmp(str, "is_sending"))
  632.     ret_val = (ison(glob_flags, IS_SENDING) != 0);
  633.     else if (!strcmp(str, "redirect"))
  634.     ret_val = (isatty(0) != 0);
  635.     else if (!strcmp(str, "thisfolder"))
  636.     return (mailfile && *mailfile) ? mailfile : NULL;
  637.     else if (!strcmp(str, "status"))
  638.     return sprintf(get_status, "%d", last_status);
  639.     else if (!strcmp(str, "output"))
  640.     return last_output;
  641.     else if (!strcmp(str, "version")) {
  642.     /* Create the version string ONCE, then re-use it. */
  643.     if (!*version)
  644.         (void) sprintf(version, "%s (%d.%s.%d %s)",
  645.               MUSHNAME, RELEASE, REVISION, PATCHLEVEL, RELEASE_DATE);
  646.     return version;
  647.     }
  648.  
  649.     return ret_val > 0 ? "1" : ret_val == 0? "0" : NULL;
  650. }
  651.  
  652. /*
  653.  * Structure for expansions
  654.  * (move to a header file?)
  655.  */
  656. struct expand {
  657.     char *orig;        /* string beginning with substring to be expanded */
  658.     char *exp;        /* result of expansion of substring */
  659.     char *rest;        /* rest of the original string beyond substring */
  660. };
  661.  
  662. /*
  663.  * Parse and expand a single variable reference.  Variable references
  664.  * begin with a '$' and thereafter look like any of:
  665.  *    $    $$ is the pid of the current process
  666.  *    [%x]    $[%x] expands %x as a hdr_format character ($%x is same)
  667.  *    (%x)    $(%x) expands %x as a prompt format character
  668.  *    name    Value of variable "name" (error if not set)
  669.  *    v:x    Modified expansion; v is any of above, x is any of
  670.  *            h    head of a file pathname
  671.  *            t    tail of a file pathname
  672.  *            l    value converted to lowercase
  673.  *            u    value converted to uppercase
  674.  *             q    quote against further expansion (not yet)
  675.  *              <num>    select the <num>th space-separated field
  676.  *    ?name    Set/unset truth value of "name"
  677.  *    {v}    Separate v (any of above) from surrounding text
  678.  * A variable name may include alphabetics, numbers, or underscores but
  679.  * must begin with an alphabetic or underscore.
  680.  */
  681. varexp(ref)
  682. struct expand *ref;
  683. {
  684.     char *str = ref->orig, c, *p, *var, *end = NULL, *op = NULL;
  685.     int do_bool, do_fmt = 0, expanded = 0;
  686.  
  687.     if (*str == '$') {
  688.     /* Allow a $ all by itself to stand */
  689.     if (!*++str || isspace(*str)) {
  690.         ref->exp = savestr("$");
  691.         ref->rest = str;
  692.         return 1;
  693.     }
  694.     /* Handle $?{name} for backwards compatibility */
  695.     if (do_bool = (*str == '?'))
  696.         str++;
  697.     if (*str == '{')
  698.         if (p = index(str + 1, '}')) {
  699.         var = str + 1;
  700.         end = p;
  701.         } else
  702.         goto bad_var;
  703.     else
  704.         var = str;
  705.     /* Handle $?name and ${?name} (normal cases) */
  706.     if (*var == '?') {
  707.         if (do_bool) /* backwards compatibility clash */
  708.         goto bad_var;
  709.         ++var, do_bool = 1;
  710.     }
  711.     switch (*var) {
  712.         case '$':
  713.         if (str[0] == '{' && str[2] != '}')
  714.             goto bad_var;
  715.         else {
  716.             char buf[16];
  717.             (void) sprintf(buf, "%d", getpid());
  718.             ref->exp = savestr(buf);
  719.             ref->rest = (end ? end : var) + 1;
  720.             return 1;
  721.         }
  722.         when '%':
  723.         for (p = var + 1; *p && !index(" \t\n;|\"'$", *p); p++)
  724.             if (*p == ':') {
  725.             if (!do_bool && !op) {
  726.                 op = p;
  727.                 do_fmt = p - var;
  728.             } else
  729.                 break;
  730.             }
  731.         if (!do_fmt)
  732.             do_fmt = p - var;
  733.         end = p;
  734.         when '[': case '(':  /*)*/
  735.         p = any(var, *var == '(' ? ") \t\n" : "] \t\n");
  736.         if (!p || isspace(*p))
  737.             goto bad_var;
  738.         if (end && p > end)
  739.             goto bad_var;
  740.         else {
  741.             var++;
  742.             do_fmt = p - var;
  743.             if (*++p == ':')
  744.             op = p;
  745.             else
  746.             end = p;
  747.         }
  748.         /* fall through */
  749.         default:
  750.         if (!do_fmt && !isalpha(*var) && *var != '_')
  751.             goto bad_var;
  752.         if (!end)
  753.             end = var + strlen(var);
  754.         for (p = (op ? op : var + do_fmt) + 1; p < end; p++)
  755.             if (!do_bool && !op && *p == ':') {
  756.             op = p;
  757.             } else if (!isalnum(*p) && *p != '_') {
  758.             if (*str == '{') /*}*/
  759.                 goto bad_var;
  760.             end = p;
  761.             break;
  762.             }
  763.         if (op && op > end)
  764.             op = NULL;
  765.     }
  766.     /* replace the end of "var" (end) with a nul,
  767.      * and save char in `c'.  Similarly chop at op.
  768.      */
  769.     c = *end, *end = 0;
  770.     if (op)
  771.         *op++ = 0;
  772.  
  773.     if (!do_fmt && debug > 3)
  774.         printf("expanding (%s) ", var);
  775.  
  776.     /* get the value of the variable. */
  777.     if (do_fmt) {
  778.         char c1 = var[do_fmt];
  779.         var[do_fmt] = 0;
  780.         if (debug > 3)
  781.         printf("expanding (%s) ", var);
  782.         if (/*(*/ ')' == c1)
  783.         p = format_prompt(current_msg, var);
  784.         else
  785.         p = format_hdr(current_msg, var, FALSE) + 9;
  786.         var[do_fmt] = c1;
  787.     } else if (!(p = check_internal(var)))
  788.         p = do_set(set_options, var);
  789.     if (do_bool) {
  790.         ref->exp = savestr((p && (*p || !do_fmt)) ? "1" : "0");
  791.         expanded = 1;
  792.         if (debug > 3)
  793.         printf("--> (%s)\n", p);
  794.     } else if (p) {
  795.         if (debug > 3)
  796.         printf("--> (%s)", p);
  797.         if (op && isdigit(*op)) {
  798.         int varc, ix = atoi(op) - 1;
  799.         char **varv = mk_argv(p, &varc, FALSE);
  800.         /* Ignore non-fatal errors like unmatched quotes */
  801.         if (varv && varc < 0)
  802.             for (varc = 0; varv[varc]; varc++)
  803.             ;
  804.         if (ix < 0 || varc <= ix || !varv)
  805.             ref->exp = savestr("");
  806.         else
  807.             ref->exp = savestr(varv[ix]);
  808.         expanded = 1;
  809.         free_vec(varv);
  810.         } else if (op) {
  811.         char *p2 = rindex(p, '/');
  812.         expanded = (*op == 'h' || *op == 't');
  813.         if (*op == 't' && p2)
  814.             p = p2 + 1;
  815.         else if (*op == 'h' && p2)
  816.             *p2 = 0;
  817.         ref->exp = savestr(p);
  818.         if (*op == 'h' && p2)
  819.             *p2 = '/';
  820.         else if (*op == 'l' || *op == 'u') {
  821.             expanded = 1;
  822.             for (p = ref->exp; *p; p++)
  823.             if (*op == 'u')
  824.                 Upper(*p);
  825.             else
  826.                 Lower(*p);
  827.         }
  828.         if (!expanded) {
  829.             print("Unknown colon modifier :%c.\n", *op);
  830.             xfree(ref->exp);
  831.         } else
  832.             if (debug > 3)
  833.             printf("--> (%s)\n", p);
  834.         } else {
  835.         ref->exp = savestr(p);
  836.         expanded = 1;
  837.         if (debug > 3)
  838.             printf("\n");
  839.         }
  840.     } else {
  841.         print("%s: undefined variable\n", var);
  842.         expanded = 0;
  843.     }
  844.     *end = c; /* replace the null with the old character */
  845.     if (op)
  846.         *--op = ':'; /* Put back the colon */
  847.     ref->rest = end + (*str == '{'); /* } */
  848.     }
  849.     return expanded;
  850. bad_var:
  851.     print("Illegal variable name.\n");
  852.     return 0;
  853. }
  854.  
  855. /*
  856.  * find mush variable references and expand them to their values.
  857.  * variables are preceded by a '$' and cannot be within single
  858.  * quotes.  Only if expansion has been made do we copy buf back into str.
  859.  * We expand only as far as the first unprotected `;' separator in str,
  860.  * to get the right behavior when multiple commands are on one line.
  861.  * RETURN 0 on failure, 1 on success.
  862.  */
  863. variable_expand(str)
  864. register char *str;
  865. {
  866.     register int     b = 0, inquotes = 0;
  867.     char             buf[BUFSIZ], *start = str;
  868.     int             expanded = 0;
  869.  
  870.     while (*str && b < sizeof buf - 1) {
  871.     if (*str == '~' && (str == start || isspace(*(str-1)))) {
  872.         register char *p = any(str, " \t"), *tmp;
  873.         int x = 1;
  874.         if (p)
  875.         *p = 0;
  876.         tmp = getpath(str, &x);
  877.         /* if error, print message and return 0 */
  878.         if (x == -1) {
  879.         wprint("%s: %s\n", str, tmp);
  880.         return 0;
  881.         }
  882.         b += Strcpy(buf+b, tmp);
  883.         if (p)
  884.         *p = ' ', str = p;
  885.         else
  886.         str += strlen(str);
  887.         expanded = 1;
  888.     }
  889.     /* if single-quotes, just copy byte by byte, char by char ... */
  890.     if ((buf[b] = *str++) == '\'' && !inquotes) {
  891.         while ((buf[++b] = *str++) && buf[b] != '\'')
  892.         ;
  893.         if (!buf[b])
  894.         str--;
  895.     } else if (!inquotes && buf[b] == '\\' && *str) {
  896.         buf[++b] = *str++;
  897.         b++;
  898.         continue;
  899.     } else if (buf[b] == '"')
  900.         inquotes = !inquotes;
  901.     /* If $ is eol, continue.  Variables must start with a `$'
  902.      * and continue with {, _, a-z, A-Z or it is not a variable.      }
  903.      */
  904.     if (buf[b] == '$' && *str) {
  905.         struct expand expansion;
  906.         expansion.orig = str - 1;
  907.         if (varexp(&expansion)) {
  908.         b += Strcpy(&buf[b], expansion.exp);
  909.         xfree(expansion.exp);
  910.         str = expansion.rest;
  911.         expanded = 1;
  912.         } else
  913.         return 0;
  914.     } else if (!inquotes && buf[b] == ';') {
  915.         while (buf[++b] = *str++)
  916.         ;
  917.         b++;
  918.         break;
  919.     } else
  920.         b++;
  921.     }
  922.     buf[b] = 0;
  923.     if (expanded) /* if any expansions were done, copy back into orig buf */
  924.     (void) strcpy(start, buf);
  925.     if (debug > 3)
  926.     printf("expanded to: %s\n", start);
  927.     return 1;
  928. }
  929.  
  930. /* make an argv of space delimited character strings out of string "str".
  931.  * place in "argc" the number of args made.  If final is true, then expand
  932.  * variables and file names and remove quotes and backslants according to
  933.  * standard.
  934.  */
  935. char **
  936. mk_argv(str, argc, final)
  937. register char *str;
  938. int *argc;
  939. {
  940.     register char    *s = NULL, *p;
  941.     register int    tmp, err = 0, unq_sep = 0;
  942.     char        *newargv[MAXARGS], **argv, *p2, c, buf[BUFSIZ];
  943.  
  944.     if (debug > 3)
  945.     (void) printf("Working on: %s\n",str);
  946.     /* If final is true, do variable expansions first */
  947.     if (final) {
  948.     (void) strcpy(buf, str);
  949.     str = buf;
  950.     if (!variable_expand(str))
  951.         return DUBL_NULL;
  952.     }
  953.     *argc = 0;
  954.     while (*str && *argc < MAXARGS) {
  955.     while (isspace(*str))
  956.         ++str;
  957.     /* When we have hit an unquoted `;', final must be true,
  958.      * so we're finished.  Stuff the rest of the string at the
  959.      * end of the argv -- do_command will pass it back later,
  960.      * for further processing -- and break out of the loop.
  961.      * NOTE: *s is not yet valid the first time through this
  962.      * loop, so unq_sep should always be initialized to 0.
  963.      */
  964.     if (unq_sep && s && *s == ';') {
  965.         if (*str) { /* Don't bother saving a null string */
  966.         newargv[*argc] = savestr(str);
  967.         (*argc)++;
  968.         }
  969.         break;
  970.     }
  971.     if (*str) {        /* found beginning of a word */
  972.         unq_sep = 0;    /* innocent until proven guilty */
  973.         s = p = str;
  974.         do  {
  975.         if (p - s >= sizeof buf-1) {
  976.             print("argument list too long.\n");
  977.             return DUBL_NULL;
  978.         }
  979.         if (*str == ';' || *str == '|')
  980.             unq_sep = final; /* Mark an unquoted separator */
  981.         if ((*p = *str++) == '\\') {
  982.             if (final && (*str == ';' || *str == '|'))
  983.             --p; /* Back up to overwrite the backslash */
  984.             if (*++p = *str) /* assign and compare to NUL */
  985.             str++;
  986.             continue;
  987.         }
  988.         if (p2 = index("\"'", *p)) {
  989.             register char c2 = *p2;
  990.             /* you can't escape quotes inside quotes of the same type */
  991.             if (!(p2 = index(str, c2))) {
  992.             if (final)
  993.                 print("Unmatched %c.\n", c2);
  994.             err++;
  995.             p2 = str;
  996.             }
  997.             tmp = (int)(p2 - str) + 1; /* take up to & include quote */
  998.             (void) strncpy(p + !final, str, tmp);
  999.             p += tmp - 2 * !!final; /* change final to a boolean */
  1000.             if (*(str = p2))
  1001.             str++;
  1002.         }
  1003.         } while (++p, *str && (!isdelimeter(*str) || str[-1] == '\\'));
  1004.         if (c = *str) /* set c = *str, check for null */
  1005.         str++;
  1006.         *p = 0;
  1007.         if (*s) {
  1008.         /* To differentiate real separators from quoted or
  1009.          * escaped ones, always store 3 chars:
  1010.          *  1) The separator character
  1011.          *  2) A nul (string terminator)
  1012.          *  3) An additional boolean (0 or 1)
  1013.          * The boolean is checked by do_command.  Note that this
  1014.          * applies only to "solitary" separators, i.e. those not
  1015.          * part of a larger word.
  1016.          */
  1017.         if (final && (!strcmp(s, ";") || !strcmp(s, "|"))) {
  1018.             char *sep = savestr("xx"); /* get 3 char slots */
  1019.             sep[0] = *s, sep[1] = '\0', sep[2] = unq_sep;
  1020.             newargv[*argc] = sep;
  1021.         } else
  1022.             newargv[*argc] = savestr(s);
  1023.         (*argc)++;
  1024.         }
  1025.         *p = c;
  1026.     }
  1027.     }
  1028.     if (!*argc)
  1029.     return DUBL_NULL;
  1030.     /* newargv[*argc] = NULL; */
  1031.     if (!(argv = (char **)calloc((unsigned)((*argc)+1), sizeof(char *)))) {
  1032.     perror("mk_argv: calloc");
  1033.     return DUBL_NULL;
  1034.     }
  1035.     for (tmp = 0; tmp < *argc; tmp++)
  1036.     argv[tmp] = newargv[tmp];
  1037.     if (err)
  1038.     *argc = -1;
  1039.     else if (debug > 3)
  1040.     (void) printf("Made argv: "), print_argv(argv);
  1041.     return argv;
  1042. }
  1043.  
  1044. /*
  1045.  * Report a history parsing error.
  1046.  * Suppress the message if nonobang is true.
  1047.  */
  1048. #define hist_error    if (nonobang) {;} else print
  1049.  
  1050. /*
  1051.  * reference previous history from syntax of str and place result into buf
  1052.  * We know we've got a history reference -- we're passed the string starting
  1053.  * the first char AFTER the '!' (which indicates history reference)
  1054.  */
  1055. char *
  1056. reference_hist(str, buf, hist_ref)
  1057. register char *str, **hist_ref;
  1058. char buf[];
  1059. {
  1060.     int        relative; /* relative from current hist_no */
  1061.     int        old_hist, argstart = 0, lastarg, argend = 0, n = 0;
  1062.     register char  *p, *rb = NULL, **argv = hist_ref;
  1063.     struct history *hist;
  1064.  
  1065.     buf[0] = 0;
  1066.     if (*str == '{')
  1067.     if (!(rb = index(str, '}'))) {   /* { */
  1068.         hist_error("Unmatched '}'");
  1069.         return NULL;
  1070.     } else
  1071.         *rb = 0, ++str;
  1072.     relative = *str == '-';
  1073.     if (index("!:$*", *str)) {
  1074.     old_hist = hist_no;
  1075.     if (*str == '!')
  1076.         str++;
  1077.     } else if (isdigit(*(str + relative)))
  1078.     str = my_atoi(str + relative, &old_hist);
  1079.     else if (!(p = hist_from_str(str, &old_hist))) {
  1080.     if (rb) /* { */
  1081.         *rb = '}';
  1082.     return NULL;
  1083.     } else
  1084.     str = p;
  1085.     if (relative)
  1086.     old_hist = (hist_no - old_hist) + 1;
  1087.     if (old_hist == hist_no) {
  1088.     if (!(argv = hist_ref))
  1089.         hist_error("You haven't done anything yet!\n");
  1090.     } else {
  1091.     if (old_hist <= hist_no-hist_size || old_hist > hist_no ||
  1092.         old_hist <= 0) {
  1093.         if (old_hist <= 0)
  1094.         hist_error("You haven't done that many commands, yet.\n");
  1095.         else
  1096.         hist_error("Event %d %s.\n", old_hist,
  1097.             (old_hist > hist_no)? "hasn't happened yet": "expired");
  1098.         if (rb) /* { */
  1099.         *rb = '}';
  1100.         return NULL;
  1101.     }
  1102.     hist = hist_head;
  1103.     while (hist && hist->histno != old_hist)
  1104.         hist = hist->prev;
  1105.     if (hist)
  1106.         argv = hist->argv;
  1107.     }
  1108.     if (!argv) {
  1109.     if (rb) /* { */
  1110.         *rb = '}';
  1111.     return NULL;
  1112.     }
  1113.     while (argv[argend+1])
  1114.     argend++;
  1115.     lastarg = argend;
  1116.     if (*str && index(":$*-", *str)) {
  1117.     int isrange;
  1118.     if (*str == ':' && isdigit(*++str))
  1119.         str = my_atoi(str, &argstart);
  1120.     if (isrange = (*str == '-'))
  1121.         str++;
  1122.     if (!isdigit(*str)) {
  1123.         if (*str == 'p')
  1124.         str++, print_only = 1;
  1125.         else if (*str == '*') {
  1126.         str++;
  1127.         if (!isrange) {
  1128.             if (argv[0]) {
  1129.             if (argv[1])
  1130.                 argstart = 1;
  1131.             else {
  1132.                 if (rb) /* { */
  1133.                 *rb = '}';
  1134.                 return (rb ? rb + 1 : str);
  1135.             }
  1136.             } else
  1137.             argstart = 0;
  1138.         }
  1139.         } else if (*str == '$') {
  1140.         if (!isrange)
  1141.             argstart = argend;
  1142.         str++;
  1143.         } else if (isrange && argend > argstart)
  1144.         argend--; /* unspecified end of range implies last-1 arg */
  1145.         else 
  1146.         argend = argstart; /* no range specified; use arg given */
  1147.     } else
  1148.         str = my_atoi(str, &argend);
  1149.     }
  1150.     if (argstart > lastarg || argend > lastarg || argstart > argend) {
  1151.     hist_error("Bad argument selector.\n");
  1152.     if (rb) /* { */
  1153.         *rb = '}';
  1154.     return (nonobang ? rb ? rb + 1 : str : NULL);
  1155.     }
  1156.     if (debug > 3)
  1157.     print("history expanding from "), print_argv(argv);
  1158.     while (argstart <= argend) {
  1159.     n += Strcpy(&buf[n], argv[argstart++]);
  1160.     buf[n++] = ' ';
  1161.     }
  1162.     buf[--n] = 0;
  1163.     if (rb) /* { */
  1164.     *rb = '}';
  1165.     return (rb ? rb + 1 : str);
  1166. }
  1167.  
  1168. /* find a history command that contains the string "str"
  1169.  * place that history number in "hist" and return the end of the string
  1170.  * parsed: !?foo (find command with "foo" in it) !?foo?bar (same, but add "bar")
  1171.  * in the second example, return the pointer to "bar"
  1172.  */
  1173. char *
  1174. hist_from_str(str, hist_number)
  1175. register char *str;
  1176. register int *hist_number;
  1177. {
  1178.     register char *p = NULL, c = 0;
  1179.     int       full_search = 0, len, found;
  1180.     char       buf[BUFSIZ];
  1181.     struct history *hist;
  1182. #ifndef REGCMP
  1183.     extern char   *re_comp();
  1184. #else
  1185.     extern char   *regcmp();
  1186. #endif /* REGCMP */
  1187.  
  1188.     /* For !{something}, the {} are stripped in reference_hist() */
  1189.     if (*str == '?') {
  1190.     if (p = index(++str, '?'))
  1191.         c = *p, *p = 0;
  1192.     else
  1193.         p = str + strlen(str);
  1194.     full_search = 1;
  1195.     } else {
  1196.     p = str;
  1197.     while (*p && *p != ':' && !isspace(*p))
  1198.         p++;
  1199.     c = *p, *p = 0;
  1200.     }
  1201.     if (*str) {
  1202. #ifndef REGCMP
  1203.     if (re_comp(str))
  1204. #else
  1205.     if (!regcmp(str, NULL))
  1206. #endif /* REGCMP */
  1207.     {
  1208.         if (c)
  1209.         *p = c;
  1210.         return NULL;
  1211.     }
  1212.     } else {
  1213.     *hist_number = hist_no;
  1214.     if (c)
  1215.         *p = c;
  1216.     return (*p == '?' ? p + 1 : p);
  1217.     }
  1218.     len = strlen(str);
  1219.     /* move thru the history in reverse searching for a string match. */
  1220.     for (hist = hist_head; hist; hist = hist->prev) {
  1221.     if (full_search) {
  1222.         (void) argv_to_string(buf, hist->argv);
  1223.         Debug("Checking for (%s) in (#%d: %s)\n", str, hist->histno, buf);
  1224.     }
  1225.     if (!full_search) {
  1226.         (void) strcpy(buf, hist->argv[0]);
  1227.         Debug("Checking for (%s) in (#%d: %*s)\n",
  1228.         str, hist->histno, len, buf);
  1229.         found = !strncmp(buf, str, len);
  1230.     } else
  1231.         found =
  1232. #ifndef REGCMP
  1233.         re_exec(buf)
  1234. #else
  1235.         !!regex(str, buf, NULL) /* convert to boolean value */
  1236. #endif /* REGCMP */
  1237.                 == 1;
  1238.     if (found) {
  1239.         *hist_number = hist->histno;
  1240.         Debug("Found it in history #%d\n", *hist_number);
  1241.         *p = c;
  1242.         return (*p == '?' ? p + 1 : p);
  1243.     }
  1244.     }
  1245.     hist_error("%s: event not found\n", str);
  1246.     *p = c;
  1247.     return NULL;
  1248. }
  1249.  
  1250. disp_hist(n, argv)  /* argc not used -- use space for the variable, "n" */
  1251. register int n;
  1252. char **argv;
  1253. {
  1254.     register int    list_num = TRUE, num_of_hists = hist_size;
  1255.     register int    reverse = FALSE;
  1256.     struct history    *hist = hist_tail;
  1257.  
  1258.     while (*++argv && *argv[0] == '-') {
  1259.     n = 1;
  1260.     do  switch(argv[0][n]) {
  1261.         case 'h': list_num = FALSE;
  1262.         when 'r': reverse = TRUE; hist = hist_head;
  1263.         otherwise: return help(0, "history", cmd_help);
  1264.         }
  1265.     while (argv[0][++n]);
  1266.     }
  1267.  
  1268.     if (!hist) {
  1269.     print("No history yet.\n");
  1270.     return -1;
  1271.     }
  1272.     if (*argv)
  1273.     if (!isdigit(**argv)) {
  1274.         print("history: badly formed number\n");
  1275.         return -1;
  1276.     } else
  1277.         num_of_hists = atoi(*argv);
  1278.  
  1279.     if (num_of_hists > hist_size || num_of_hists > hist_no)
  1280.     num_of_hists = min(hist_size, hist_no);
  1281.  
  1282.     if (!reverse)
  1283.     while (hist_no - hist->histno > num_of_hists) {
  1284.         (void) printf("skipping %d\n", hist->histno);
  1285.         hist = hist->next;
  1286.     }
  1287.  
  1288.     (void) do_pager(NULL, TRUE);
  1289.     for (n = 0; n < num_of_hists && hist; n++) {
  1290.     char buf[256];
  1291.     if (list_num)
  1292.         (void) do_pager(sprintf(buf, "%4.d  ", hist->histno), FALSE);
  1293.     (void) argv_to_string(buf, hist->argv);
  1294.     (void) do_pager(buf, FALSE);
  1295.     if (do_pager("\n", FALSE) == -1)
  1296.         break;
  1297.     hist = (reverse)? hist->prev : hist->next;
  1298.     }
  1299.     (void) do_pager(NULL, FALSE);
  1300.     return 0;
  1301. }
  1302.  
  1303. init_history(newsize)
  1304. {
  1305.     if ((hist_size = newsize) < 1)
  1306.     hist_size = 1;
  1307. }
  1308.