home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume11 / mush5.7 / part09 / loop.c
Encoding:
C/C++ Source or Header  |  1987-09-19  |  23.5 KB  |  912 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.         if (b && !isspace(buf[b-1]))
  499.             buf[b+1] = buf[b], buf[b++] = ' ';
  500.         b++;
  501.         break;
  502.         }
  503.         /* if double-quotes, just copy byte by byte, char by char ... */
  504.         if (buf[b] == '"') {
  505.         int B = b;
  506.         while ((buf[++B] = *str++) && buf[B] != '"')
  507.             ;
  508.         if (buf[B])
  509.             b = B;
  510.         else
  511.             str--;
  512.         b++;
  513.         continue;
  514.         }
  515.         if (buf[b] == '\\') {
  516.         if ((buf[++b] = *str) == '!')
  517.             buf[--b] = '!';
  518.         ++str;
  519.         } else if (buf[b] == '!' && *str && *str != '\\' && !isspace(*str)
  520.             && !ignore_bang) {
  521.         char word[BUFSIZ];
  522.         if (!(str = reference_hist(str, word, argv)))
  523.             return NULL;
  524.         if (hist_was_referenced)
  525.             *hist_was_referenced = 1;
  526.         if (strlen(word) + b >= BUFSIZ) {
  527.             print("argument list too long.\n");
  528.             return NULL;
  529.         }
  530.         b += Strcpy(&buf[b], word) - 1;
  531.         }
  532.         b++;
  533.     } while (*str && !isdelimeter(*str));
  534.     if (!inquotes)
  535.         first_space++, buf[b++] = ' ';
  536.     }
  537.     buf[b] = 0;
  538.     return buf;
  539. }
  540.  
  541. /*
  542.  * find mush variable references and expand them to their values.
  543.  * variables are preceded by a '$' and cannot be within single
  544.  * quotes.  Only if expansion has been made do we copy buf back into str.
  545.  * RETURN 0 on failure, 1 on success.
  546.  */
  547. variable_expand(str)
  548. register char *str;
  549. {
  550.     register int     b = 0;
  551.     char             buf[BUFSIZ], *start = str;
  552.     int             expanded = 0;
  553.  
  554.     while (*str) {
  555.     if (*str == '~' && (str == start || isspace(*(str-1)))) {
  556.         register char *p = any(str, " \t"), *tmp;
  557.         int x = 1;
  558.         if (p)
  559.         *p = 0;
  560.         tmp = getpath(str, &x);
  561.         /* if error, print message and return 0 */
  562.         if (x == -1) {
  563.         wprint("%s: %s\n", str, tmp);
  564.         return 0;
  565.         }
  566.         b += Strcpy(buf+b, tmp);
  567.         if (p)
  568.         *p = ' ', str = p;
  569.         else
  570.         str += strlen(str);
  571.         expanded = 1;
  572.     }
  573.     /* if single-quotes, just copy byte by byte, char by char ... */
  574.     if ((buf[b] = *str++) == '\'') {
  575.         while ((buf[++b] = *str++) && buf[b] != '\'')
  576.         ;
  577.         if (!buf[b])
  578.         str--;
  579.     }
  580.     /* If $ is eoln, continue.  Variables must start with a `$'
  581.      * and continue with {, _, a-z, A-Z or it is not a variable.      }
  582.      */
  583.     if (buf[b] == '$' &&
  584.         (isalpha(*str) || *str == '{' || *str == '_'))  /* } */  {
  585.         register char c, *p, *var, *end;
  586.  
  587.         if (*(end = var = str) == '{')  /* } */   {
  588.         if (!isalpha(*++str) && *str != '_') {
  589.             print("Illegal variable name.\n");
  590.             return 0;
  591.         }
  592.         if (!(end = index(var, '}'))) /* { */   {
  593.             print("Unmatched '{'.\n"); /* } */
  594.             return 0;
  595.         }
  596.         *end++ = 0;
  597.         } else
  598.         while (isalnum(*++end) || *end == '_')
  599.             ;
  600.         /* advance "str" to the next parse-point, replace the end
  601.          * of "var" (end) with a null, and save char in `c'
  602.          */
  603.         c = *(str = end), *end = 0;
  604.  
  605.         /* get the value of the variable. */
  606.         if (p = do_set(set_options, var))
  607.         b += Strcpy(buf+b, p);
  608.         else {
  609.         print("%s: undefined variable\n", var);
  610.         return 0;
  611.         }
  612.         expanded = 1;
  613.         *str = c; /* replace the null with the old character */
  614.     } else
  615.         b++;
  616.     }
  617.     buf[b] = 0;
  618.     if (expanded) /* if any expansions were done, copy back into orig buf */
  619.     (void) strcpy(start, buf);
  620.     return 1;
  621. }
  622.  
  623. /* make an vector of space delimeted character strings out of string "str".
  624.  * place in "argc" the number of args made. If final is true, then remove
  625.  * quotes and backslants according to standard.
  626.  */
  627. char **
  628. mk_argv(str, argc, final)
  629. register char *str;
  630. register int *argc;
  631. {
  632.     register char    *s, *p;
  633.     register int    tmp, err = 0;
  634.     char        *newargv[MAXARGS], **argv, *p2, c;
  635.  
  636.     *argc = 0;
  637.     while (*str) {
  638.     while (isspace(*str))
  639.         ++str;
  640.     if (*str) {        /* found beginning of a word */
  641.         s = p = str;
  642.         do  {
  643.         if (p - s >= BUFSIZ-1) {
  644.             print("argument list too long.\n");
  645.             return DUBL_NULL;
  646.         }
  647.         if ((*p = *str++) == '\\') {
  648.             if (final && (*str == ';' || *str == '|'))
  649.             /* make ";" look like " ;" */
  650.             *p = ' ';
  651.             if (*++p = *str) /* assign and compare to NULL */
  652.             str++;
  653.             continue;
  654.         }
  655.         if (p2 = index("\"'", *p)) {
  656.             register char c2 = *p2;
  657.             /* you can't escape quotes inside quotes of the same type */
  658.             if (!(p2 = index(str, c2))) {
  659.             if (final)
  660.                 print("Unmatched %c.\n", c2);
  661.             err++;
  662.             p2 = str;
  663.             }
  664.             tmp = (int)(p2 - str) + 1; /* take upto & include quote */
  665.             (void) strncpy(p + !final, str, tmp);
  666.             p += tmp - 2 * final; /* change final to a boolean */
  667.             if (*(str = p2))
  668.             str++;
  669.         }
  670.         } while (++p, *str && !isdelimeter(*str));
  671.         if (c = *str) /* set c = *str, check for null */
  672.         str++;
  673.         *p = 0;
  674.         if (*s) {
  675.         newargv[*argc] = savestr(s);
  676.         (*argc)++;
  677.         }
  678.         *p = c;
  679.     }
  680.     }
  681.     if (!*argc)
  682.     return DUBL_NULL;
  683.     /* newargv[*argc] = NULL; */
  684.     if (!(argv = calloc((unsigned)((*argc)+1), sizeof(char *)))) {
  685.     perror("mk_argv: calloc");
  686.     return DUBL_NULL;
  687.     }
  688.     for (tmp = 0; tmp < *argc; tmp++)
  689.     argv[tmp] = newargv[tmp];
  690.     if (err)
  691.     *argc = -1;
  692.     return argv;
  693. }
  694.  
  695. /*
  696.  * reference previous history from syntax of str and place result into buf
  697.  * We know we've got a history reference -- we're passed the string starting
  698.  * the first char AFTER the '!' (which indicates history reference)
  699.  */
  700. char *
  701. reference_hist(str, buf, hist_ref)
  702. register char *str, *buf, **hist_ref;
  703. {
  704.     int        relative = *str == '-'; /* relative from current hist_no */
  705.     int        old_hist, argstart = 0, lastarg, argend = 0, n = 0;
  706.     register char  *p, **argv = hist_ref;
  707.     struct history *hist;
  708.  
  709.     buf[0] = 0;
  710.     if (index("!:$*", *str)) {
  711.     old_hist = hist_no;
  712.     if (*str == '!')
  713.         str++;
  714.     } else if (isdigit(*(str + relative)))
  715.     str = my_atoi(str + relative, &old_hist);
  716.     else if (!(p = hist_from_str(str, &old_hist)))
  717.     return NULL;
  718.     else
  719.     str = p;
  720.     if (relative)
  721.     old_hist = (hist_no-old_hist) + 1;
  722.     if (old_hist == hist_no) {
  723.     if (!(argv = hist_ref))
  724.         print("You haven't done anything yet!\n");
  725.     } else {
  726.     if (old_hist <= hist_no-hist_size || old_hist > hist_no ||
  727.         old_hist <= 0) {
  728.         if (old_hist <= 0)
  729.         print("You haven't done that many commands, yet.\n");
  730.         else
  731.         print("Event %d %s.\n", old_hist,
  732.             (old_hist > hist_no)? "hasn't happened yet": "expired");
  733.         return NULL;
  734.     }
  735.     hist = hist_head;
  736.     while (hist && hist->histno != old_hist)
  737.         hist = hist->prev;
  738.     if (hist)
  739.         argv = hist->argv;
  740.     }
  741.     if (!argv)
  742.     return NULL;
  743.     while (argv[argend+1])
  744.     argend++;
  745.     lastarg = argend;
  746.     if (*str && index(":$*", *str)) {
  747.     int isrange;
  748.     if (*str == ':' && isdigit(*++str))
  749.         str = my_atoi(str, &argstart);
  750.     if (isrange = (*str == '-'))
  751.         str++;
  752.     if (!isdigit(*str)) {
  753.         if (*str == 'p')
  754.         str++, print_only = 1;
  755.         else if (!*str || isdelimeter(*str))
  756.         argend = argstart;
  757.         else {
  758.         if (*str == '*')
  759.             if (argv[0])
  760.             argstart = 1, argend = ++lastarg;
  761.             else
  762.             argstart = 0;
  763.         else if (*str == '$' || !isrange)
  764.             argstart = argend;
  765.         else
  766.             print("%c: unknown arguement selector.\n", *str);
  767.         str++;
  768.         }
  769.     } else
  770.         str = my_atoi(str, &argend);
  771.     }
  772.     if (argstart > lastarg || argend > lastarg || argstart > argend) {
  773.     print("Bad argument selector.\n");
  774.     return NULL;
  775.     }
  776.     while (argstart <= argend) {
  777.     n += Strcpy(&buf[n], argv[argstart++]);
  778.     buf[n++] = ' ';
  779.     }
  780.     buf[--n] = 0;
  781.     return str;
  782. }
  783.  
  784. /* find a history command that contains the string "str"
  785.  * place that history number in "hist" and return the end of the string
  786.  * parsed: !?foo (find command with "foo" in it) !?foo?bar (same, but add "bar")
  787.  * in the second example, return the pointer to "bar"
  788.  */
  789. char *
  790. hist_from_str(str, hist_number)
  791. register char *str;
  792. register int *hist_number;
  793. {
  794.     register char *p = NULL, c = 0;
  795.     int       full_search = 0, len, found;
  796.     char       buf[BUFSIZ];
  797.     struct history *hist;
  798. #ifndef SYSV
  799.     extern char   *re_comp();
  800. #else
  801.     extern char   *regcmp();
  802. #endif SYSV
  803.  
  804.     if (*str == '?') {
  805.     if (p = index(++str, '?'))
  806.         *p++ = 0;
  807.     else
  808.         p = str + strlen(str);
  809.     full_search = 1;
  810.     } else if (*str == '{')
  811.     if (!(p = index(str, '}'))) {   /* { */
  812.         print("Unmatched '}'");
  813.         return NULL;
  814.     } else
  815.         *p++ = 0, ++str;
  816.     else
  817.     p = str;
  818.     while (*p && *p != ':' && !isspace(*p))
  819.     p++;
  820.     c = *p, *p = 0;
  821.     if (*str) {
  822. #ifndef SYSV
  823.     if (re_comp(str))
  824. #else
  825.     if (!regcmp(str, NULL))
  826. #endif SYSV
  827.         return NULL;
  828.     } else {
  829.     *hist_number = hist_no;
  830.     return p;
  831.     }
  832.     len = strlen(str);
  833.     /* move thru the history in reverse searching for a string match. */
  834.     for (hist = hist_head; hist; hist = hist->prev) {
  835.     if (full_search) {
  836.         (void) argv_to_string(buf, hist->argv);
  837.         Debug("Checking for (%s) in (#%d: %s)\n", str, hist->histno, buf);
  838.     }
  839.     if (!full_search) {
  840.         (void) strcpy(buf, hist->argv[0]);
  841.         Debug("Checking for (%s) in (#%d: %*s)\n",
  842.         str, hist->histno, len, buf);
  843.         found = !strncmp(buf, str, len);
  844.     } else
  845.         found =
  846. #ifndef SYSV
  847.         re_exec(buf)
  848. #else
  849.         !!regex(buf, NULL) /* convert to boolean value */
  850. #endif SYSV
  851.                 == 1;
  852.     if (found) {
  853.         *hist_number = hist->histno;
  854.         Debug("Found it in history #%d\n", *hist_number);
  855.         *p = c;
  856.         return p;
  857.     }
  858.     }
  859.     print("%s: event not found\n", str);
  860.     return NULL;
  861. }
  862.  
  863. disp_hist(n, argv)  /* argc not used -- use space for the variable, "n" */
  864. register int n;
  865. char **argv;
  866. {
  867.     register int    list_num = TRUE, num_of_hists = hist_size;
  868.     register int    reverse = FALSE;
  869.     struct history    *hist = hist_tail;
  870.  
  871.     while (*++argv && *argv[0] == '-') {
  872.     n = 1;
  873.     do  switch(argv[0][n]) {
  874.         case 'h': list_num = FALSE;
  875.         when 'r': reverse = TRUE; hist = hist_head;
  876.         otherwise: print("usage: history [-h] [-r] [#histories]\n");
  877.                return -1;
  878.         }
  879.     while (argv[0][++n]);
  880.     }
  881.     if (*argv)
  882.     if (!isdigit(**argv)) {
  883.         print("history: badly formed number\n");
  884.         return -1;
  885.     } else
  886.         num_of_hists = atoi(*argv);
  887.  
  888.     if (num_of_hists > hist_size || num_of_hists > hist_no)
  889.     num_of_hists = min(hist_size, hist_no);
  890.  
  891.     if (!reverse)
  892.     while (hist_no - hist->histno > num_of_hists) {
  893.         printf("skipping %d\n", hist->histno);
  894.         hist = hist->next;
  895.     }
  896.  
  897.     for (n = 0; n < num_of_hists && hist; n++) {
  898.     if (list_num)
  899.         wprint("%4.d  ", hist->histno);
  900.     print_argv(hist->argv);
  901.     hist = (reverse)? hist->prev : hist->next;
  902.     }
  903.     return -1;
  904. }
  905.  
  906. init_history(newsize)
  907. {
  908.     if ((hist_size = newsize) < 1)
  909.     hist_size = 1;
  910. }
  911.  
  912.