home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume14 / mush6.0 / part09 / loop.c next >
Encoding:
C/C++ Source or Header  |  1988-04-12  |  24.0 KB  |  921 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.  
  11. #ifdef BSD
  12. #include <sys/wait.h>
  13. #else
  14. #ifndef SYSV
  15. #include <wait.h>
  16. #endif /* SYSV */
  17. #endif /* BSD */
  18.  
  19. #define ever (;;)
  20. #define MAXARGS        100
  21. #define isdelimeter(c)    (index(" \t;|", c))
  22.  
  23. char *alias_expand(), *hist_expand(), *reference_hist(), *hist_from_str();
  24. char **calloc();
  25.  
  26. struct history {
  27.     int histno;
  28.     char **argv;
  29.     struct history *prev;
  30.     struct history *next;
  31. };
  32. static struct history *hist_head, *hist_tail;
  33. struct history *malloc();
  34. #define NULL_HIST    (struct history *)0
  35.  
  36. static char *last_aliased;
  37. static int hist_size, print_only;
  38.  
  39. do_loop()
  40. {
  41.     register char *p, **argv;
  42.     char      **last_argv = DUBL_NULL, line[256];
  43.     int         argc, c = (iscurses - 1);
  44.     struct history *new;
  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.     (void) signal(SIGTERM, catch);
  54.     (void) signal(SIGCHLD, sigchldcatcher);
  55.     (void) signal(SIGPIPE, SIG_IGN); /* if pager is terminated before end */
  56.  
  57.     turnoff(glob_flags, IGN_SIGS);
  58.     if (hist_size == 0) /* if user didn't set history in .rc file */
  59.     hist_size = 1;
  60.  
  61.     for ever {
  62.     if (setjmp(jmpbuf)) {
  63.         Debug("jumped back to main loop (%s: %d)\n", __FILE__,__LINE__);
  64. #ifdef CURSES
  65.         if (c > 0) { /* don't pass last command back to curses_command() */
  66.         iscurses = TRUE;
  67.         c = hit_return();
  68.         }
  69. #endif /* CURSES */
  70.     }
  71. #ifdef CURSES
  72.     if (iscurses || c > -1) {
  73.         /* if !iscurses, we know that we returned from a curses-based
  74.          * call and we really ARE still in curses. Reset tty modes!
  75.          */
  76.         if (ison(glob_flags, ECHO_FLAG)) {
  77.         turnoff(glob_flags, ECHO_FLAG);
  78.         echo_off();
  79.         save_echo_flg = TRUE;
  80.         }
  81.         if (!iscurses) {
  82.         iscurses = TRUE;
  83.         c = hit_return();
  84.         }
  85.         if ((c = curses_command(c)) == -1 && save_echo_flg) {
  86.         echo_on();
  87.         turnon(glob_flags, ECHO_FLAG);
  88.         save_echo_flg = FALSE;
  89.         }
  90.         continue;
  91.     }
  92. #endif /* CURSES */
  93.     clear_msg_list(msg_list);
  94.     (void) check_new_mail();
  95.  
  96.     /* print a prompt according to printf like format:
  97.      * (current message, deleted, unread, etc) are found in mail_status.
  98.      */
  99.     mail_status(1);
  100.     if (Getstr(line, 256, 0) > -1)
  101.         p = line;
  102.     else {
  103.         if (p = do_set(set_options, "ignoreeof")) {
  104.         if (!*p)
  105.             continue;
  106.         else
  107.             p = strcpy(line, p); /* so processing won't destroy var */
  108.         } else {
  109.         (void) quit(0, DUBL_NULL);
  110.         continue; /* quit may return if new mail arrives */
  111.         }
  112.     }
  113.  
  114.     skipspaces(0);
  115.     if (!*p && !(p = do_set(set_options, "newline"))) {
  116.         (void) readmsg(0, DUBL_NULL, msg_list);
  117.         continue;
  118.     }
  119.     if (!*p) /* if newline is set, but no value, then continue */
  120.         continue;
  121.  
  122.     /* upon error, argc = -1 -- still save in history so user can
  123.      * modify syntax error. if !argv, error is too severe.  We pass
  124.      * the last command typed in last_argv for history reference, and
  125.      * get back the current command _as typed_ (unexpanded by aliases
  126.      * or history) in last_argv.
  127.      */
  128.     if (!(argv = make_command(p, &last_argv, &argc)))
  129.         continue;
  130.     /* now save the new argv in the newly created history structure */
  131.     if (!(new = malloc(sizeof (struct history))))
  132.         error("can't increment history");
  133.     else {
  134.         new->histno = ++hist_no;
  135.         new->argv = last_argv; /* this is the command _as typed_ */
  136.         new->next = NULL_HIST;
  137.         new->prev = hist_head;
  138.         /* if first command, the tail of the list is "new" because
  139.          * nothing is in the list.  If not the first command, the
  140.          * head of the list's "next" pointer points to the new command.
  141.          */
  142.         if (hist_head)
  143.         hist_head->next = new;
  144.         else
  145.         hist_tail = new;
  146.         hist_head = new;
  147.     }
  148.     /*
  149.      * truncate the history list to the size of the history.
  150.      * Free the outdated command (argv) and move the tail closer to front.
  151.      * use a while loop in case the last command reset histsize to "small"
  152.      */
  153.     while (hist_head->histno - hist_tail->histno >= hist_size) {
  154.         hist_tail = hist_tail->next;
  155.         free_vec(hist_tail->prev->argv);
  156.         xfree(hist_tail->prev);
  157.         hist_tail->prev = NULL_HIST;
  158.     }
  159.  
  160.     if (print_only) {
  161.         print_only = 0;
  162.         free_vec(argv);
  163.     } else if (argc > -1)
  164.         (void) do_command(argc, argv, msg_list);
  165.     }
  166. }
  167.  
  168. /* make a command from "buf".
  169.  * first, expand history references. make an argv from that and save
  170.  * in last_argv (to be passed back and stored in history). After that,
  171.  * THEN expand aliases. return that argv to be executed as a command.
  172.  */
  173. char **
  174. make_command(start, last_argv, argc)
  175. register char *start, ***last_argv;
  176. int *argc;
  177. {
  178.     register char *p, **tmp;
  179.     char buf[BUFSIZ];
  180.  
  181.     if (!last_argv)
  182.     tmp = DUBL_NULL;
  183.     else
  184.     tmp = *last_argv;
  185.     /* first expand history -- (here's where argc gets set)
  186.      * pass the buffer, the history list to reference if \!* (or whatever)
  187.      * result in static buffer (pointed to by p) -- even if history parsing is
  188.      * ignored, do this to remove \'s behind !'s and verifying matching quotes
  189.      */
  190.     if (!(p = hist_expand(start, tmp, argc)) || Strcpy(buf, p) > BUFSIZ)
  191.     return DUBL_NULL;
  192.     /* if history was referenced in the command, echo new command */
  193.     if (*argc)
  194.     puts(buf);
  195.  
  196.     /* argc may == -1; ignore this error for now but catch it later */
  197.     if (!(tmp = mk_argv(buf, argc, 0)))
  198.     return DUBL_NULL;
  199.  
  200.     /* save this as the command typed */
  201.     if (last_argv)
  202.     *last_argv = tmp;
  203.  
  204.     /* expand all aliases (recursively)
  205.      * pass _this_ command (as typed and without aliases) to let aliases
  206.      * with "!*" be able to reference the command line just typed.
  207.      */
  208.     if (alias_stuff(buf, *argc, tmp) == -1)
  209.     return DUBL_NULL;
  210.  
  211.     /* now, expand variable references and make another argv */
  212.     if (!variable_expand(buf))
  213.     return DUBL_NULL;
  214.  
  215.     if (!last_argv)
  216.     free_vec(tmp);
  217.  
  218.     /* with everything expanded, build final argv from new buffer
  219.      * Note that backslashes and quotes still exist. Those are removed
  220.      * because argument final is 1.
  221.      */
  222.     tmp = mk_argv(buf, argc, 1);
  223.     return tmp;
  224. }
  225.  
  226. /*
  227.  * do the command specified by the argument vector, argv.
  228.  * First check to see if argc < 0. If so, someone called this
  229.  * command and they should not have! make_command() will return
  230.  * an argv but it will set argc to -1 if there's a sytanx error.
  231.  */
  232. do_command(argc, argv, list)
  233. char **argv, list[];
  234. {
  235.     register char *p;
  236.     char **tmp = argv;
  237.     int i, status;
  238.     long do_pipe = ison(glob_flags, DO_PIPE);
  239.  
  240.     turnoff(glob_flags, IS_PIPE);
  241.  
  242.     if (argc <= 0) {
  243.     turnoff(glob_flags, DO_PIPE);
  244.     return -1;
  245.     }
  246.  
  247.     clear_msg_list(list);
  248.  
  249.     for (i = 0; do_pipe >= 0 && argc; argc--) {
  250.     p = argv[i];
  251.     if (!strcmp(p, "|") || !strcmp(p, ";")) {
  252.         if (do_pipe = (*p == '|'))
  253.         turnon(glob_flags, DO_PIPE);
  254.         argv[i] = NULL;
  255.         /* if piping, then don't call next command if this one fails. */
  256.         if ((status = exec_argv(i, argv, list)) <= -1 && do_pipe) {
  257.         print("Broken pipe.\n");
  258.         do_pipe = -1, turnoff(glob_flags, DO_PIPE);
  259.         }
  260.         /* if command failed and piping, or command worked and not piping */
  261.         if (do_pipe <= 0)
  262.         status = 0, clear_msg_list(list);
  263.         /* else command worked and piping: set is_pipe */
  264.         else if (!status)
  265.         turnon(glob_flags, IS_PIPE), turnoff(glob_flags, DO_PIPE);
  266.         argv[i] = p;
  267.         argv += (i+1);
  268.         i = 0;
  269.     } else
  270.         i++;
  271.     }
  272.     if (do_pipe >= 0)
  273.     status = exec_argv(i, argv, list);
  274.     Debug("freeing: "), print_argv(tmp);
  275.     free_vec(tmp);
  276.     turnoff(glob_flags, DO_PIPE);
  277.     return status;
  278. }
  279.  
  280. exec_argv(argc, argv, list)
  281. register char **argv, list[];
  282. {
  283.     register int n;
  284.  
  285.     if (!argv || !*argv || **argv == '\\' && !*++*argv) {
  286.     if (ison(glob_flags, IS_PIPE) || ison(glob_flags, DO_PIPE))
  287.         print("Invalid null command.\n");
  288.     return -1;
  289.     }
  290.     Debug("executing: "), print_argv(argv);
  291.  
  292.     /* if interrupted during execution of a command, return -1 */
  293.     if (isoff(glob_flags, IGN_SIGS) && setjmp(jmpbuf)) {
  294.     Debug("jumped back to exec_argv (%s: %d)\n", __FILE__, __LINE__);
  295.     return -1;
  296.     }
  297.  
  298.     /* standard commands */
  299.     for (n = 0; cmds[n].command; n++)
  300.     if (!strcmp(argv[0], cmds[n].command))
  301.         return (*cmds[n].func)(argc, argv, list);
  302.  
  303.     /* ucb-Mail compatible commands */
  304.     for (n = 0; ucb_cmds[n].command; n++)
  305.     if (!strcmp(argv[0], ucb_cmds[n].command))
  306.         return (*ucb_cmds[n].func)(argc, argv, list);
  307.  
  308.     /* for hidden, undocumented commands */
  309.     for (n = 0; hidden_cmds[n].command; n++)
  310.     if (!strcmp(argv[0], hidden_cmds[n].command))
  311.         return (*hidden_cmds[n].func)(argc, argv, list);
  312.  
  313. #ifdef SUNTOOL
  314.     /* check tool-only commands */
  315.     if (istool)
  316.     for (n = 0; fkey_cmds[n].command; n++)
  317.         if (!strcmp(argv[0], fkey_cmds[n].command))
  318.         return (*fkey_cmds[n].func)(argc, argv);
  319. #endif /* SUNTOOL */
  320.  
  321.     if ((isdigit(**argv) || index("^.*$-`{}", **argv))
  322.             && (n = get_msg_list(argv, list)) != 0) {
  323.     if (n > 0 && isoff(glob_flags, DO_PIPE))
  324.         for (n = 0; n < msg_cnt; n++)
  325.         if (msg_bit(list, n)) {
  326.             display_msg((current_msg = n), (long)0);
  327.             unset_msg_bit(list, n);
  328.         }
  329.     return 0;
  330.     } else if (strlen(*argv) == 1 && index("$^.", **argv)) {
  331.     if (!msg_cnt)
  332.         print("No messages.");
  333.     else {
  334.         if (**argv != '.')
  335.         current_msg = (**argv == '$') ? msg_cnt-1 : 0;
  336.         set_msg_bit(list, current_msg);
  337.         display_msg(current_msg, (long)0);
  338.     }
  339.     return 0;
  340.     }
  341.     /* get_msg_list will set the current message bit if nothing parsed */
  342.     unset_msg_bit(list, current_msg);
  343.  
  344.     if (!istool && do_set(set_options, "unix")) {
  345.     if (ison(glob_flags, IS_PIPE) || ison(glob_flags, DO_PIPE))
  346.         print("There is no piping to or from UNIX commands.\n");
  347.     else
  348.         execute(argv);  /* try to execute a unix command */
  349.     return -1; /* doesn't affect messages! */
  350.     }
  351.  
  352.     print("%s: command not found.\n", *argv);
  353.     if (!istool)
  354.     print("type '?' for valid commands, or type `help'\n");
  355.     return -1;
  356. }
  357.  
  358. /* recursively look for aliases on a command line.  aliases may
  359.  * reference other aliases.
  360.  */
  361. alias_stuff(b, argc, Argv)
  362. register char     *b, **Argv;
  363. {
  364.     register char     *p, **argv = DUBL_NULL;
  365.     register int     n = 0, i = 0, Argc;
  366.     static int         loops;
  367.     int         dummy;
  368.  
  369.     if (++loops == 20) {
  370.     print("Alias loop.\n");
  371.     return -1;
  372.     }
  373.     for (Argc = 0; Argc < argc; Argc++) {
  374.     register char *h = Argv[n + ++i];
  375.     register char *p2 = "";
  376.  
  377.     /* we've hit a command separator or the end of the line */
  378.     if (h && strcmp(h, ";") && strcmp(h, "|"))
  379.         continue;
  380.  
  381.     /* create a new argv containing this (possible subset) of argv */
  382.     if (!(argv = calloc((unsigned)(i+1), sizeof (char *))))
  383.         continue;
  384.     while (i--)
  385.         strdup(argv[i], Argv[n+i]);
  386.  
  387.     if ((!last_aliased || strcmp(last_aliased, argv[0]))
  388.             && (p = alias_expand(argv[0]))) {
  389.         /* if history was referenced, ignore the rest of argv
  390.          * else copy all of argv onto the end of the buffer.
  391.          */
  392.         if (!(p2 = hist_expand(p, argv, &dummy)))
  393.         break;
  394.         if (!dummy)
  395.         (void) argv_to_string(p2+strlen(p2), argv+1);
  396.         if (Strcpy(b, p2) > BUFSIZ) {
  397.         print("Not enough buffer space.\n");
  398.         break;
  399.         }
  400.         /* release old argv and build a new one based on new string */
  401.         free_vec(argv);
  402.         if (!(argv = mk_argv(b, &dummy, 0)))
  403.         break;
  404.         if (alias_stuff(b, dummy, argv) == -1)
  405.         break;
  406.     } else
  407.         b = argv_to_string(b, argv);
  408.     xfree(last_aliased), last_aliased = NULL;
  409.     free_vec(argv);
  410.     b += strlen(b);
  411.     if (h) {
  412.         p2 = h;
  413.         while (++Argc < argc && (h = Argv[Argc]))
  414.         if (strcmp(h, ";") && strcmp(h, "|"))
  415.             break;
  416.         b += strlen(sprintf(b, " %s ", p2));
  417.         n = Argc--;
  418.     }
  419.     i = 0;
  420.     }
  421.     xfree(last_aliased), last_aliased = NULL;
  422.     --loops;
  423.     if (Argc < argc) {
  424.     free_vec(argv);
  425.     return -1;
  426.     }
  427.     return 0;
  428. }
  429.  
  430. char *
  431. alias_expand(cmd)
  432. register char *cmd;
  433. {
  434.     register char *p;
  435.     register int x;
  436.  
  437.     if (!(p = do_set(functions, cmd)))
  438.     return NULL;
  439.     last_aliased = savestr(cmd); /* to be freed elsewhere; don't strdup! */
  440.     if (isoff(glob_flags, WARNING))
  441.     return p;
  442.     for (x = 0; cmds[x].command; x++)
  443.     if (!strcmp(cmd, cmds[x].command)) {
  444.         wprint("(real command: \"%s\" aliased to \"%s\")\n", cmd, p);
  445.         return p;
  446.     }
  447.     for (x = 0; ucb_cmds[x].command; x++)
  448.     if (!strcmp(cmd, ucb_cmds[x].command)) {
  449.         wprint("(ucb-command: \"%s\" aliased to \"%s\")\n", cmd, p);
  450.         return p;
  451.     }
  452.     return p;
  453. }
  454.  
  455. /* expand history references and separate message lists from other tokens */
  456. char *
  457. hist_expand(str, argv, hist_was_referenced)
  458. register char *str, **argv;
  459. register int *hist_was_referenced;
  460. {
  461.     static char   buf[BUFSIZ];
  462.     register int  b = 0, inquotes = 0;
  463.     int       first_space = 0, ignore_bang;
  464.  
  465.     ignore_bang = (ison(glob_flags, IGN_BANG) ||
  466.            do_set(set_options, "ignore_bang"));
  467.  
  468.     if (hist_was_referenced)
  469.     *hist_was_referenced = 0;
  470.     while (*str) {
  471.     while (!inquotes && isspace(*str))
  472.         str++;
  473.     do  {
  474.         if (!*str)
  475.         break;
  476.         if (b >= BUFSIZ-1) {
  477.         print("argument list too long.\n");
  478.         return NULL;
  479.         }
  480.         if ((buf[b] = *str++) == '\'') {
  481.         /* make sure there's a match! */
  482.         inquotes = !inquotes;
  483.         }
  484.         if (!first_space && !inquotes && index("0123456789{}*$", buf[b])
  485.                  && b && !index("0123456789{}- \t", buf[b-1])) {
  486.         buf[b+1] = buf[b];
  487.         buf[b++] = ' ';
  488.         while ((buf[++b] = *str++) && index("0123456789-,${}", buf[b]))
  489.             ;
  490.         if (!buf[b])
  491.             str--;
  492.         first_space++;
  493.         }
  494.         /* check for (;) (|) or any other delimeter and separate it from
  495.          * other tokens.
  496.          */
  497.         if (!inquotes && buf[b] != '\0' && isdelimeter(buf[b]) &&
  498.         (b < 0 || buf[b-1] != '\\')) {
  499.         if (b && !isspace(buf[b-1]))
  500.             buf[b+1] = buf[b], buf[b++] = ' ';
  501.         b++;
  502.         break;
  503.         }
  504.         /* if double-quotes, just copy byte by byte, char by char ... */
  505.         if (buf[b] == '"') {
  506.         int B = b;
  507.         while ((buf[++B] = *str++) && buf[B] != '"')
  508.             ;
  509.         if (buf[B])
  510.             b = B;
  511.         else
  512.             str--;
  513.         b++;
  514.         continue;
  515.         }
  516.         if (buf[b] == '\\') {
  517.         if ((buf[++b] = *str) == '!')
  518.             buf[--b] = '!';
  519.         ++str;
  520.         } else if (buf[b] == '!' && *str && *str != '\\' && !isspace(*str)
  521.             && !ignore_bang) {
  522.         char word[BUFSIZ];
  523.         if (!(str = reference_hist(str, word, argv)))
  524.             return NULL;
  525.         if (hist_was_referenced)
  526.             *hist_was_referenced = 1;
  527.         if (strlen(word) + b >= BUFSIZ) {
  528.             print("argument list too long.\n");
  529.             return NULL;
  530.         }
  531.         b += Strcpy(&buf[b], word) - 1;
  532.         }
  533.         b++;
  534.     } while (*str && (!isdelimeter(*str) || str[-1] == '\\'));
  535.     if (!inquotes)
  536.         first_space++, buf[b++] = ' ';
  537.     }
  538.     buf[b] = 0;
  539.     return buf;
  540. }
  541.  
  542. /*
  543.  * find mush variable references and expand them to their values.
  544.  * variables are preceded by a '$' and cannot be within single
  545.  * quotes.  Only if expansion has been made do we copy buf back into str.
  546.  * RETURN 0 on failure, 1 on success.
  547.  */
  548. variable_expand(str)
  549. register char *str;
  550. {
  551.     register int     b = 0;
  552.     char             buf[BUFSIZ], *start = str;
  553.     int             expanded = 0;
  554.  
  555.     while (*str) {
  556.     if (*str == '~' && (str == start || isspace(*(str-1)))) {
  557.         register char *p = any(str, " \t"), *tmp;
  558.         int x = 1;
  559.         if (p)
  560.         *p = 0;
  561.         tmp = getpath(str, &x);
  562.         /* if error, print message and return 0 */
  563.         if (x == -1) {
  564.         wprint("%s: %s\n", str, tmp);
  565.         return 0;
  566.         }
  567.         b += Strcpy(buf+b, tmp);
  568.         if (p)
  569.         *p = ' ', str = p;
  570.         else
  571.         str += strlen(str);
  572.         expanded = 1;
  573.     }
  574.     /* if single-quotes, just copy byte by byte, char by char ... */
  575.     if ((buf[b] = *str++) == '\'') {
  576.         while ((buf[++b] = *str++) && buf[b] != '\'')
  577.         ;
  578.         if (!buf[b])
  579.         str--;
  580.     }
  581.     /* If $ is eoln, continue.  Variables must start with a `$'
  582.      * and continue with {, _, a-z, A-Z or it is not a variable.      }
  583.      */
  584.     if (buf[b] == '$' &&
  585.         (isalpha(*str) || *str == '{' || *str == '_'))  /* } */  {
  586.         register char c, *p, *var, *end;
  587.  
  588.         if (*(end = var = str) == '{')  /* } */   {
  589.         if (!isalpha(*++str) && *str != '_') {
  590.             print("Illegal variable name.\n");
  591.             return 0;
  592.         }
  593.         if (!(end = index(var, '}'))) /* { */   {
  594.             print("Unmatched '{'.\n"); /* } */
  595.             return 0;
  596.         }
  597.         *end++ = 0;
  598.         } else
  599.         while (isalnum(*++end) || *end == '_')
  600.             ;
  601.         /* advance "str" to the next parse-point, replace the end
  602.          * of "var" (end) with a null, and save char in `c'
  603.          */
  604.         c = *(str = end), *end = 0;
  605.  
  606.         /* get the value of the variable. */
  607.         if (p = do_set(set_options, var))
  608.         b += Strcpy(buf+b, p);
  609.         else {
  610.         print("%s: undefined variable\n", var);
  611.         return 0;
  612.         }
  613.         expanded = 1;
  614.         *str = c; /* replace the null with the old character */
  615.     } else
  616.         b++;
  617.     }
  618.     buf[b] = 0;
  619.     if (expanded) /* if any expansions were done, copy back into orig buf */
  620.     (void) strcpy(start, buf);
  621.     return 1;
  622. }
  623.  
  624. /* make an vector of space delimeted character strings out of string "str".
  625.  * place in "argc" the number of args made. If final is true, then remove
  626.  * quotes and backslants according to standard.
  627.  */
  628. char **
  629. mk_argv(str, argc, final)
  630. register char *str;
  631. register int *argc;
  632. {
  633.     register char    *s, *p;
  634.     register int    tmp, err = 0;
  635.     char        *newargv[MAXARGS], **argv, *p2, c;
  636.  
  637.     *argc = 0;
  638.     while (*str) {
  639.     while (isspace(*str))
  640.         ++str;
  641.     if (*str) {        /* found beginning of a word */
  642.         s = p = str;
  643.         do  {
  644.         if (p - s >= BUFSIZ-1) {
  645.             print("argument list too long.\n");
  646.             return DUBL_NULL;
  647.         }
  648.         if ((*p = *str++) == '\\') {
  649.             if (final && (*str == ';' || *str == '|'))
  650.             /* make ";" look like " ;" */
  651.             *p = ' ';
  652.             if (*++p = *str) /* assign and compare to NULL */
  653.             str++;
  654.             continue;
  655.         }
  656.         if (p2 = index("\"'", *p)) {
  657.             register char c2 = *p2;
  658.             /* you can't escape quotes inside quotes of the same type */
  659.             if (!(p2 = index(str, c2))) {
  660.             if (final)
  661.                 print("Unmatched %c.\n", c2);
  662.             err++;
  663.             p2 = str;
  664.             }
  665.             tmp = (int)(p2 - str) + 1; /* take upto & include quote */
  666.             (void) strncpy(p + !final, str, tmp);
  667.             p += tmp - 2 * final; /* change final to a boolean */
  668.             if (*(str = p2))
  669.             str++;
  670.         }
  671.         } while (++p, *str && (!isdelimeter(*str) || str[-1] == '\\'));
  672.         if (c = *str) /* set c = *str, check for null */
  673.         str++;
  674.         *p = 0;
  675.         if (*s) {
  676.         newargv[*argc] = savestr(s);
  677.         (*argc)++;
  678.         }
  679.         *p = c;
  680.     }
  681.     }
  682.     if (!*argc)
  683.     return DUBL_NULL;
  684.     /* newargv[*argc] = NULL; */
  685.     if (!(argv = calloc((unsigned)((*argc)+1), sizeof(char *)))) {
  686.     perror("mk_argv: calloc");
  687.     return DUBL_NULL;
  688.     }
  689.     for (tmp = 0; tmp < *argc; tmp++)
  690.     argv[tmp] = newargv[tmp];
  691.     if (err)
  692.     *argc = -1;
  693.     return argv;
  694. }
  695.  
  696. /*
  697.  * reference previous history from syntax of str and place result into buf
  698.  * We know we've got a history reference -- we're passed the string starting
  699.  * the first char AFTER the '!' (which indicates history reference)
  700.  */
  701. char *
  702. reference_hist(str, buf, hist_ref)
  703. register char *str, *buf, **hist_ref;
  704. {
  705.     int        relative = *str == '-'; /* relative from current hist_no */
  706.     int        old_hist, argstart = 0, lastarg, argend = 0, n = 0;
  707.     register char  *p, **argv = hist_ref;
  708.     struct history *hist;
  709.  
  710.     buf[0] = 0;
  711.     if (index("!:$*", *str)) {
  712.     old_hist = hist_no;
  713.     if (*str == '!')
  714.         str++;
  715.     } else if (isdigit(*(str + relative)))
  716.     str = my_atoi(str + relative, &old_hist);
  717.     else if (!(p = hist_from_str(str, &old_hist)))
  718.     return NULL;
  719.     else
  720.     str = p;
  721.     if (relative)
  722.     old_hist = (hist_no-old_hist) + 1;
  723.     if (old_hist == hist_no) {
  724.     if (!(argv = hist_ref))
  725.         print("You haven't done anything yet!\n");
  726.     } else {
  727.     if (old_hist <= hist_no-hist_size || old_hist > hist_no ||
  728.         old_hist <= 0) {
  729.         if (old_hist <= 0)
  730.         print("You haven't done that many commands, yet.\n");
  731.         else
  732.         print("Event %d %s.\n", old_hist,
  733.             (old_hist > hist_no)? "hasn't happened yet": "expired");
  734.         return NULL;
  735.     }
  736.     hist = hist_head;
  737.     while (hist && hist->histno != old_hist)
  738.         hist = hist->prev;
  739.     if (hist)
  740.         argv = hist->argv;
  741.     }
  742.     if (!argv)
  743.     return NULL;
  744.     while (argv[argend+1])
  745.     argend++;
  746.     lastarg = argend;
  747.     if (*str && index(":$*-", *str)) {
  748.     int isrange;
  749.     if (*str == ':' && isdigit(*++str))
  750.         str = my_atoi(str, &argstart);
  751.     if (isrange = (*str == '-'))
  752.         str++;
  753.     if (!isdigit(*str)) {
  754.         if (*str == 'p')
  755.         str++, print_only = 1;
  756.         else if (!*str || isdelimeter(*str))
  757.         if (isrange)
  758.             argend--; /* unspecified end of range implies last-1 arg */
  759.         else
  760.             argend = argstart; /* no range specified; use arg given */
  761.         else {
  762.         if (*str == '*')
  763.             if (argv[0])
  764.             argstart = 1, argend = ++lastarg;
  765.             else
  766.             argstart = 0;
  767.         else if (*str == '$' && !isrange)
  768.             argstart = argend;
  769.         else if (*str != '$')
  770.             print("%c: unknown arguement selector.\n", *str);
  771.         str++;
  772.         }
  773.     } else
  774.         str = my_atoi(str, &argend);
  775.     }
  776.     if (argstart > lastarg || argend > lastarg || argstart > argend) {
  777.     print("Bad argument selector.\n");
  778.     return NULL;
  779.     }
  780.     while (argstart <= argend) {
  781.     n += Strcpy(&buf[n], argv[argstart++]);
  782.     buf[n++] = ' ';
  783.     }
  784.     buf[--n] = 0;
  785.     return str;
  786. }
  787.  
  788. /* find a history command that contains the string "str"
  789.  * place that history number in "hist" and return the end of the string
  790.  * parsed: !?foo (find command with "foo" in it) !?foo?bar (same, but add "bar")
  791.  * in the second example, return the pointer to "bar"
  792.  */
  793. char *
  794. hist_from_str(str, hist_number)
  795. register char *str;
  796. register int *hist_number;
  797. {
  798.     register char *p = NULL, c = 0;
  799.     int       full_search = 0, len, found;
  800.     char       buf[BUFSIZ];
  801.     struct history *hist;
  802. #ifndef REGCMP
  803.     extern char   *re_comp();
  804. #else
  805.     extern char   *regcmp();
  806. #endif /* REGCMP */
  807.  
  808.     if (*str == '?') {
  809.     if (p = index(++str, '?'))
  810.         *p++ = 0;
  811.     else
  812.         p = str + strlen(str);
  813.     full_search = 1;
  814.     } else if (*str == '{')
  815.     if (!(p = index(str, '}'))) {   /* { */
  816.         print("Unmatched '}'");
  817.         return NULL;
  818.     } else
  819.         *p++ = 0, ++str;
  820.     else
  821.     p = str;
  822.     while (*p && *p != ':' && !isspace(*p))
  823.     p++;
  824.     c = *p, *p = 0;
  825.     if (*str) {
  826. #ifndef REGCMP
  827.     if (re_comp(str))
  828. #else
  829.     if (!regcmp(str, NULL))
  830. #endif /* REGCMP */
  831.         return NULL;
  832.     } else {
  833.     *hist_number = hist_no;
  834.     return p;
  835.     }
  836.     len = strlen(str);
  837.     /* move thru the history in reverse searching for a string match. */
  838.     for (hist = hist_head; hist; hist = hist->prev) {
  839.     if (full_search) {
  840.         (void) argv_to_string(buf, hist->argv);
  841.         Debug("Checking for (%s) in (#%d: %s)\n", str, hist->histno, buf);
  842.     }
  843.     if (!full_search) {
  844.         (void) strcpy(buf, hist->argv[0]);
  845.         Debug("Checking for (%s) in (#%d: %*s)\n",
  846.         str, hist->histno, len, buf);
  847.         found = !strncmp(buf, str, len);
  848.     } else
  849.         found =
  850. #ifndef REGCMP
  851.         re_exec(buf)
  852. #else
  853.         !!regex(str, buf, NULL) /* convert to boolean value */
  854. #endif /* REGCMP */
  855.                 == 1;
  856.     if (found) {
  857.         *hist_number = hist->histno;
  858.         Debug("Found it in history #%d\n", *hist_number);
  859.         *p = c;
  860.         return p;
  861.     }
  862.     }
  863.     print("%s: event not found\n", str);
  864.     return NULL;
  865. }
  866.  
  867. disp_hist(n, argv)  /* argc not used -- use space for the variable, "n" */
  868. register int n;
  869. char **argv;
  870. {
  871.     register int    list_num = TRUE, num_of_hists = hist_size;
  872.     register int    reverse = FALSE;
  873.     struct history    *hist = hist_tail;
  874.  
  875.     while (*++argv && *argv[0] == '-') {
  876.     n = 1;
  877.     do  switch(argv[0][n]) {
  878.         case 'h': list_num = FALSE;
  879.         when 'r': reverse = TRUE; hist = hist_head;
  880.         otherwise: print("usage: history [-h] [-r] [#histories]\n");
  881.                return -1;
  882.         }
  883.     while (argv[0][++n]);
  884.     }
  885.     if (*argv)
  886.     if (!isdigit(**argv)) {
  887.         print("history: badly formed number\n");
  888.         return -1;
  889.     } else
  890.         num_of_hists = atoi(*argv);
  891.  
  892.     if (num_of_hists > hist_size || num_of_hists > hist_no)
  893.     num_of_hists = min(hist_size, hist_no);
  894.  
  895.     if (!reverse)
  896.     while (hist_no - hist->histno > num_of_hists) {
  897.         printf("skipping %d\n", hist->histno);
  898.         hist = hist->next;
  899.     }
  900.  
  901.     do_pager(NULL, TRUE);
  902.     for (n = 0; n < num_of_hists && hist; n++) {
  903.     char buf[256];
  904.     if (list_num)
  905.         do_pager(sprintf(buf, "%4.d  ", hist->histno), FALSE);
  906.     (void) argv_to_string(buf, hist->argv);
  907.     (void) do_pager(buf, FALSE);
  908.     if (do_pager("\n", FALSE) == -1)
  909.         break;
  910.     hist = (reverse)? hist->prev : hist->next;
  911.     }
  912.     do_pager(NULL, FALSE);
  913.     return -1;
  914. }
  915.  
  916. init_history(newsize)
  917. {
  918.     if ((hist_size = newsize) < 1)
  919.     hist_size = 1;
  920. }
  921.