home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume1 / bourne / part1 / history.c < prev    next >
Encoding:
C/C++ Source or Header  |  1986-11-30  |  22.9 KB  |  1,249 lines

  1. /* history.c --- interacterive history mechanism for the Bourne shell */
  2.  
  3. /*
  4.  * Original design by Jeff Lee for the Software Tools Subsystem,
  5.  * This implementation by Arnold Robbins, based on Jeff's, but
  6.  * a little bit more capable.
  7.  */
  8.  
  9. #include "defs.h"    /* defines HISTSIZE */
  10. #include "sym.h"
  11.  
  12. #define MAXHIST        256        /* max no. saved commands */
  13. #define MAXLINE        257
  14. #define BIGBUF        (MAXLINE * 2)
  15.  
  16. #define HISTCHAR    '!'        /* history flag character */
  17. #define HISTLOOK    '?'        /* history global search command */
  18. #define HISTARG        '`'        /* history argument character */
  19. #define HISTSUB        '^'        /* history substitution character */
  20.  
  21. #define YES    (1)
  22. #define NO    (0)
  23.  
  24. #ifndef TAB    /* earlier version of the shell */
  25. #define    TAB    '\t'
  26. #endif
  27.  
  28. static char    Histbuf[HISTSIZE];    /* queue holding actual history */
  29. static int    Histptr[MAXHIST];    /* queue of pointers into buffer */
  30. static int    Hbuffirst = 0;        /* First pointer into Histbuf */
  31. static int    Hbuflast = 0;        /* Last pointer into Histbuf */
  32. static int    Hptrfirst = 0;        /* First pointer into Histptr */
  33. static int    Hptrlast = 0;        /* Last pointer into Histptr */
  34. static int    Histline = 0;        /* no. of cmd pointed to by Histptr[Hptrlast] */
  35.  
  36. static char h_badopt[] =    " unrecognized history option";
  37. static char badarg[] =    " illegal argument history";
  38. static char nohist[] =    " no history exists, yet";
  39. static char h_illegal[] =    " illegal history construct";
  40. static char bufover[] =    " history buffer overflow";
  41. static char bigtok[] =    " history token too large";
  42. static char internal[] =    " history internal error";
  43. static char badtoken[] =    " illegal history token";
  44. static char h_notfound[] =    " history item not found";
  45. static char bigexp[] =    " history expansion too big";
  46.  
  47. extern int    histsub ();        /* do a history substitution */
  48. static void    histinit ();        /* reinitialize history mechanism */
  49. static int    histexp ();        /* do a history expansion */
  50. static int    histque ();        /* save a command in the buffers */
  51. static void    histfree ();        /* free up some buffer storage */
  52. static int    histfind ();        /* find a history command */
  53. static int    histlook ();        /* get a previous command */
  54. static int    histget ();        /* get a string from the buffers */
  55. static void    histarg ();        /* get individual arguments */
  56. extern int    histrest ();        /* restore saved history */
  57. extern int    histsave ();        /* save current history */
  58.  
  59. static int    Bquote = 0;        /* count grave accents */
  60. static int    Dquote = 0;        /* count single quotes */
  61. static int    Squote = 0;        /* count double quotes */
  62.  
  63. #define errmsg(x, s)    { prs(x); prc(COLON); prs(s); newline(); return (FALSE); }
  64.  
  65. #define repeat        do    /* repeat ... until is easier to read */
  66. #define until(cond)    while (!(cond))
  67.  
  68. /* histsub --- perform a history substitution */
  69.  
  70. int histsub (in, out, outsize)
  71. char *in, *out;
  72. int outsize;
  73. {
  74.     if ((flags&prompt) == 0 || in == 0 || *in == '\0')
  75.         return (TRUE);    /* no history, pretend all ok */
  76.     
  77.     return (histexp (in, out, outsize) && histque (out));
  78. }
  79.  
  80. /* histexp --- perform history expansion on a command line */
  81.  
  82. static int histexp (in, out, outsize)
  83. char *in, *out;
  84. int outsize;
  85. {
  86.     int i;
  87.     int istart, ilen, ostart;
  88.     char buf[MAXLINE], result[BIGBUF];
  89.     auto int bangseen = NO;
  90.  
  91.     if (in[0] == NL || in[0] == '\0')
  92.         return (FALSE);
  93.  
  94.     istart = ostart = ilen = 0;
  95.     while (in[istart] && in[istart] != HISTCHAR)
  96.     {
  97.         if (ostart >= outsize)
  98.             errmsg (in, bigexp);
  99.         switch (in[istart]) {
  100.         case ESCAPE:
  101.             out[ostart++] = in[istart++];
  102.             if (in[istart] == HISTCHAR)
  103.             {
  104.                 bangseen = YES;
  105.                 if (Squote)
  106.                     out[ostart++] = in[istart++];
  107.                 else
  108.                     out[ostart - 1] = in[istart++];
  109.                     /* no quotes, nuke \ */
  110.                 continue;
  111.             }
  112.             break;
  113.         case '`':
  114.             if (Dquote == 0 && Squote == 0)
  115.                 Bquote = 1 - Bquote;
  116.             break;
  117.         case '\'':
  118.             if (Bquote == 0 && Dquote == 0)
  119.                 Squote = 1 - Squote;
  120.             break;
  121.         case '"':
  122.             if (Bquote == 0 && Squote == 0)
  123.                 Dquote = 1 - Dquote;
  124.             break;
  125.         }
  126.         if (ostart >= outsize)
  127.             errmsg (in, bigexp);
  128.         out[ostart++] = in[istart++];
  129.         if (Squote && in[istart] == HISTCHAR)
  130.             if (ostart >= outsize)
  131.             {
  132.                 errmsg (in, bigexp);
  133.             }
  134.             else
  135.                 out[ostart++] = in[istart++];
  136.     }
  137.  
  138.     if (in[istart] == '\0')
  139.     {
  140.         out[ostart] = '\0';
  141.         if (bangseen)
  142.             expanded = YES;        /* see comment below */
  143.         return (TRUE);    /* no history to do */
  144.     }
  145.  
  146.     expanded = NO;    /* this is a global flag */
  147.     while (histfind (in, &istart, &ilen))    /* we found something to do */
  148.     {
  149.         if (ilen >= MAXLINE)
  150.             errmsg (&in[istart], bigtok);
  151.  
  152.         /* save the history part */
  153.         strncpy (buf, & in[istart], ilen);
  154.         buf[ilen] = '\0';
  155.         istart += ilen;
  156.         if (buf[ilen-1] == HISTCHAR)
  157.             buf[--ilen] = '\0';
  158.         
  159.         /* actually make the substitution */
  160.         if (! histlook (buf, result))
  161.             return (FALSE);
  162.         
  163.         /* put it into generated line */
  164.         i = length (result) - 2;
  165.         if (result[i] == NL)
  166.             result[i] = '\0';
  167.         if (ostart + i + 1 >= outsize)
  168.             errmsg (&in[istart], bigexp);
  169.         movstr (result, & out[ostart]);
  170.         ostart += length (result) - 1;
  171.         expanded = YES;
  172.         while (in[istart] && in[istart] != HISTCHAR)
  173.         {
  174.             if (ostart >= outsize)
  175.                 errmsg (&in[istart], bigexp);
  176.             switch (in[istart]) {
  177.             case ESCAPE:
  178.                 out[ostart++] = in[istart++];
  179.                 if (in[istart] == HISTCHAR)
  180.                 {
  181.                     bangseen = YES;
  182.                     if (Squote)
  183.                         out[ostart++] = in[istart++];
  184.                     else
  185.                         out[ostart - 1] = in[istart++];
  186.                         /* no quotes, nuke \ */
  187.                     continue;
  188.                 }
  189.                 break;
  190.             case '`':
  191.                 if (Dquote == 0 && Squote == 0)
  192.                     Bquote = 1 - Bquote;
  193.                 break;
  194.             case '\'':
  195.                 if (Bquote == 0 && Dquote == 0)
  196.                     Squote = 1 - Squote;
  197.                 break;
  198.             case '"':
  199.                 if (Bquote == 0 && Squote == 0)
  200.                     Dquote = 1 - Dquote;
  201.                 break;
  202.             }
  203.             if (ostart >= outsize)
  204.                 errmsg (&in[istart], bigexp);
  205.             out[ostart++] = in[istart++];
  206.             if (Squote && in[istart] == HISTCHAR)
  207.                 if (ostart >= outsize)
  208.                 {
  209.                     errmsg (&in[istart], bigexp);
  210.                 }
  211.                 else
  212.                     out[ostart++] = in[istart++];
  213.         }
  214.     }
  215.  
  216.     out[ostart] = '\0';
  217.  
  218.     if (expanded)
  219.         prs (out);    /* should contain newline */
  220.     else if (bangseen)
  221.         expanded = YES;
  222.  
  223.     /*
  224.      * This is a KLUDGE, so that escaped !s work;
  225.      * it depends on knowledge of how readb() in word.c
  226.      * works, i.e., if expanded, use the generated buffer.
  227.      * This way, only expanded is needed as a global variable.
  228.      */
  229.  
  230.     return (TRUE);
  231. }
  232.  
  233. /* histque --- place the given command in the history queue */
  234.  
  235. static int histque (command)
  236. char *command;
  237. {
  238.     int c;
  239.     char *p;
  240.     static int Inaquote = FALSE;    /* in a quote across commands */
  241.  
  242.     for (; *command && (*command == SP || *command == TAB); command++)
  243.         ; /* skip leading white space */
  244.  
  245.     if (*command == NL && *(command+1) == '\0')
  246.         return (TRUE);    /* don't queue empty commands */
  247.                 /* or increment event_count */
  248.  
  249.     if (Inaquote)
  250.     {
  251.         /* clobber trailing \0 */
  252.         if (Hbuffirst == 0)
  253.             Hbuffirst = HISTSIZE - 1;
  254.         else
  255.             Hbuffirst--;
  256.  
  257.         event_count--;
  258.     }
  259.  
  260.     Histptr[Hptrfirst] = Hbuffirst;
  261.     if (! Inaquote)
  262.         Hptrfirst = (Hptrfirst + 1) % MAXHIST;
  263.  
  264.     if (Hptrfirst == Hptrlast)
  265.         histfree ();
  266.  
  267.     p = command;
  268.     c = *p++;
  269.     while (c != '\0' && Hptrfirst != Hptrlast)
  270.     {
  271.         repeat
  272.         {
  273.             Histbuf[Hbuffirst] = c;
  274.             c = *p++;
  275.             Hbuffirst = (Hbuffirst + 1) % HISTSIZE;
  276.         } until (c == '\0' || Hbuffirst == Hbuflast);
  277.  
  278.         if (Hbuffirst == Hbuflast)
  279.             histfree ();
  280.     }
  281.  
  282.     if (Hptrfirst != Hptrlast)
  283.     {
  284.         Histbuf[Hbuffirst] = '\0';
  285.         Hbuffirst = (Hbuffirst + 1) % HISTSIZE;
  286.  
  287.         if (Hbuffirst == Hbuflast)
  288.             histfree ();
  289.     }
  290.  
  291.     Inaquote = (Bquote || Dquote || Squote);
  292.  
  293.     if (Hptrfirst == Hptrlast)
  294.     {
  295.         histinit ();
  296.         errmsg (nullstr, bufover);
  297.         /* errmsg returns FALSE */
  298.     }
  299.  
  300.     event_count++;
  301.     return (TRUE);
  302. }
  303.  
  304. /* histfree --- free the next queue pointer */
  305.  
  306. static void histfree ()
  307. {
  308.     Hptrlast = (Hptrlast + 1) % MAXHIST;
  309.  
  310.     Hbuflast = Histptr[Hptrlast];
  311.     Histline++;
  312. }
  313.  
  314. /* histfind --- find the start and length of a history pattern */
  315.  
  316. static int histfind (command, start, len)
  317. char *command;
  318. int *start, *len;
  319. {
  320.     char *p, c;
  321.     int subseen;
  322.  
  323.     p = command + *start;
  324.     c = *p++;
  325.  
  326.     *len = 0;
  327.     if (c == NL || c == '\0')
  328.         return (FALSE);
  329.  
  330.     /* skip leading non-history */
  331.     while (c && c != HISTCHAR)
  332.     {
  333.         if (c == ESCAPE)
  334.         {
  335.             c = *p++;
  336.             *start += 1;
  337.         }
  338.  
  339.         if (c != '\0')
  340.         {
  341.             c = *p++;
  342.             *start += 1;
  343.         }
  344.     }
  345.  
  346.     if (c == NL || c == '\0')
  347.         return (FALSE);
  348.     
  349.     *len = 1;
  350.     c = *p++;
  351.     if (c == HISTLOOK)    /* !?...? */
  352.     {
  353.         *len += 1;
  354.         c = *p++;
  355.         while (c && c != HISTLOOK && c != NL)
  356.         {
  357.             if (c == ESCAPE)
  358.             {
  359.                 c = *p++;
  360.                 *len += 1;
  361.             }
  362.  
  363.             if (c != '\0')
  364.             {
  365.                 c = *p++;
  366.                 *len += 1;
  367.             }
  368.         }
  369.         if (c == HISTLOOK)
  370.         {
  371.             c = *p++;
  372.             *len += 1;
  373.         }
  374.     }
  375.     else if (digit (c) || c == '-')    /* !<num> */
  376.     {
  377.         if (c == '-')
  378.         {
  379.             c = *p++;
  380.             *len += 1;
  381.  
  382.             if (! digit(c))
  383.                 errmsg (command + *start, h_illegal);
  384.         }
  385.  
  386.         while (digit (c))
  387.         {
  388.             c = *p++;
  389.             *len += 1;
  390.         }
  391.     }
  392.     else    /* !<str> */
  393.         while (c && c != HISTARG && c != HISTSUB && c != SP &&
  394.                 c != TAB && c != NL && c != HISTCHAR)
  395.         {
  396.             if (c == ESCAPE)
  397.             {
  398.                 c = *p++;
  399.                 *len += 1;
  400.             }
  401.  
  402.             if (c != '\0')
  403.             {
  404.                 c = *p++;
  405.                 *len += 1;
  406.             }
  407.         }
  408.  
  409.     if (c == HISTARG)
  410.     {
  411.         *len += 1;
  412.         c = *p++;
  413.         while (c && digit (c))
  414.         {
  415.             *len += 1;
  416.             c = *p++;
  417.         }
  418.         if (c == '-')
  419.         {
  420.             *len += 1;
  421.             c = *p++;
  422.         }
  423.         if (c == '$')
  424.         {
  425.             *len += 1;
  426.             c = *p++;
  427.         }
  428.         else
  429.         {
  430.             while (c && digit (c))
  431.             {
  432.                 *len += 1;
  433.                 c = *p++;
  434.             }
  435.         }
  436.     }
  437.  
  438.     while (c == HISTSUB)
  439.     {
  440.         *len += 1;
  441.         subseen = 0;
  442.         c = *p++;
  443.  
  444.         while (subseen < 2 && c != NL && c != '\0')
  445.         {
  446.             if (c == ESCAPE)
  447.             {
  448.                 c = *p++;
  449.                 *len += 1;
  450.             }
  451.  
  452.             if (c != '\0')
  453.             {
  454.                 c = *p++;
  455.                 *len += 1;
  456.             }
  457.  
  458.             if (c == HISTSUB)
  459.                 subseen++;
  460.         }
  461.  
  462.         if (c == HISTSUB)
  463.         {
  464.             *len += 1;
  465.             c = *p++;
  466.             if (c == 'g' || c == 'G')
  467.             {
  468.                 *len += 1;
  469.                 c = *p++;
  470.             }
  471.         }
  472.     }
  473.  
  474.     if (c == HISTCHAR)
  475.         *len += 1;
  476.     
  477.     return (TRUE);
  478. }
  479.  
  480. /* histlook --- lookup the value of a history string */
  481.  
  482. static int histlook (str, sub)
  483. char *str, *sub;
  484. {
  485.     char c;
  486.     char *save, *p, *sp;
  487.     char buf[BIGBUF], rep[BIGBUF];
  488.     char new[BIGBUF];
  489.     int i, j, val, si, flag, len, last;
  490.     static int ctoi();
  491.  
  492.     save = sub;
  493.     /*
  494.      * first attempt to find which command on which we are to operate
  495.      *
  496.      * the entire hstory format is as follows
  497.      *
  498.      * ! [<str> | <num> | ?<str>?] [`<num> [- [<num>]]] {^<str>^<str>^ [g]}
  499.      */
  500.     
  501.     si = 0;
  502.  
  503.     if (str[si] == HISTCHAR)
  504.         si++;
  505.  
  506.     switch (str[si]) {
  507.     case '\0':    /* ! */
  508.     case NL:
  509.     case HISTARG:    /* on these, retrive previous line, then break */
  510.     case HISTSUB:
  511.         if (Hptrfirst == Hptrlast)
  512.             errmsg (nullstr, nohist);
  513.  
  514.         val = (Hptrfirst - Hptrlast + MAXHIST) % MAXHIST + Histline - 1;
  515.         if (! histget (val, sub))
  516.             errmsg (nullstr, internal);
  517.         break;
  518.  
  519.     case '-':
  520.     case '0':    /* !<num> */
  521.     case '1':
  522.     case '2':
  523.     case '3':
  524.     case '4':
  525.     case '5':
  526.     case '6':
  527.     case '7':
  528.     case '8':
  529.     case '9':
  530.         val = ctoi (str, &si) - 1;    /* for 0-based indexing */
  531.         if (! histget(val, sub))
  532.             errmsg (str, h_notfound);
  533.         break;
  534.     
  535.     case HISTLOOK:    /* ?<str>? */
  536.         i = 0;
  537.         si++;
  538.         while (str[si] && str[si] != HISTLOOK)
  539.         {
  540.             if (str[si] == ESCAPE)
  541.                 si++;
  542.             
  543.             if (str[si])
  544.                 buf[i++] = str[si++];
  545.         }
  546.         buf [i] = '\0';
  547.         if (str[si] == HISTLOOK)
  548.             si++;
  549.         if (buf[i-1] == NL)
  550.             buf[--i] = '\0';
  551.         
  552.         flag = FALSE;
  553.         val = (Hptrfirst - Hptrlast + MAXHIST) % MAXHIST + Histline - 1;
  554.         *sub = '\0';
  555.         while (histget (val, sub))
  556.         {
  557.             p = sub;
  558.             c = *p++;
  559.             while (c)
  560.             {
  561.                 i = 0;
  562.                 while (c != '\0' && buf[i] != '\0' && c != buf[i])
  563.                 {
  564.                     /* skip non matching */
  565.                     c = *p++;
  566.                     if (*p == '\0')
  567.                         break;
  568.                 }
  569.                 
  570.                 sp = p;
  571.  
  572.                 while (c && buf[i] && c == buf[i])
  573.                 {
  574.                     /* possibly matching */
  575.                     c = *p++;
  576.                     i++;
  577.                 }
  578.  
  579.                 if (buf[i] == '\0')
  580.                 {
  581.                     /* did match */
  582.                     flag = TRUE;
  583.                     goto out;
  584.                 }
  585.  
  586.                 p = sp;
  587.                 c = *p++;
  588.             }
  589.             val--;    /* search further back, next time around */
  590.             *sub = '\0';
  591.         }
  592.  
  593.     out:
  594.         if (flag == FALSE)
  595.             errmsg (str, h_notfound);
  596.         break;
  597.  
  598.     default:    /* !<str> */
  599.         i = 0;
  600.         while (str[si] && str[si] != HISTARG && str[si] != HISTSUB)
  601.         {
  602.             if (str[si] == ESCAPE)
  603.                 si++;
  604.             
  605.             if (str[si])
  606.                 buf[i++] = str[si++];
  607.         }
  608.         buf[i] = '\0';
  609.  
  610.         flag = FALSE;
  611.         val = (Hptrfirst - Hptrlast + MAXHIST) % MAXHIST + Histline - 1;
  612.         while (histget (val, sub))
  613.         {
  614.             p = sub;
  615.             c = *p++;
  616.             while (c == SP || c == TAB)
  617.                 c = *p++;
  618.  
  619.             i = 0;
  620.             while (buf[i] && buf[i] == c)
  621.             {
  622.                 c = *p++;
  623.                 i++;
  624.             }
  625.  
  626.             if (buf[i] == '\0')
  627.             {
  628.                 flag = TRUE;    /* found it */
  629.                 break;    /* while */
  630.             }
  631.             val--;
  632.         }
  633.         if (flag == FALSE)
  634.             errmsg (str, h_notfound);
  635.         break;
  636.     } /* end switch */
  637.  
  638.     j = length (sub) - 2;
  639.     if (sub[j] == NL)
  640.         sub[j] = '\0';
  641.  
  642.     /*
  643.      * ! [<str> | <num> | ? <str> ?] has now been parsed and the command
  644.      * line has been placed in "sub". Now see if the next character is a
  645.      * legal following character
  646.      */
  647.     
  648.     if (str[si] && str[si] != HISTARG && str[si] != HISTSUB && str[si] != NL)
  649.         errmsg (str, badtoken);
  650.  
  651.     /* if there is no more to the history string, we are done */
  652.     if (str[si] == NL || str[si] == '\0')
  653.         return (TRUE);
  654.     
  655.     /*
  656.      * now check for possible argument substitution. This section parses
  657.      * [` <num>] and turns "sub" into the appropriate argument
  658.      */
  659.     
  660.     if (str[si] == HISTARG)        /* `<num>-<num> */
  661.     {
  662.         si++;
  663.  
  664.         if (! digit(str[si]) && str[si] != '-' && str[si] != '$')
  665.             errmsg (str, badarg);
  666.         
  667.         /* determine the last argument */
  668.         p = sub;
  669.         last = -1;
  670.         /* count arguments, last will be val of $ */
  671.         histarg (p, &len);
  672.         while (len > 0)
  673.         {
  674.             last++;
  675.             p += len;
  676.             histarg (p, &len);
  677.         }
  678.  
  679.         if (str[si] == '-')    /* default to arg 1 */
  680.             val = 1;
  681.         else if (digit(str[si]))
  682.             val = min (ctoi(str, &si), last + 1);
  683.         else
  684.         {
  685.             /* $ */
  686.             val = last;
  687.  
  688.             if (str[si] != '$')
  689.             {
  690.                 errmsg (str, internal);
  691.             }
  692.             else
  693.                 si++;
  694.         }
  695.  
  696.         p = sub;
  697.         for (i = val; i > 0; i--)    /* delete preceding arguments */
  698.         {
  699.             histarg (p, & len);
  700.             p += len;
  701.         }
  702.  
  703.         /* p points to beginning of first wanted arg */
  704.         /* remove leading blanks */
  705.         c = *p++;
  706.         while (c == SP || c == TAB)
  707.             c = *p++;
  708.         
  709.         sub = p - 1;
  710.  
  711.  
  712.         if (str[si] == '-')
  713.         {
  714.             si++;
  715.             if (digit(str[si]))
  716.                 val = min (ctoi (str, &si), last) - val + 1;
  717.             else
  718.             {
  719.                 val = last - val + 1;
  720.  
  721.                 if (str[si] != '\0')
  722.                     si++;
  723.             }
  724.  
  725.             p = sub;
  726.             histarg (p, & len);
  727.             while (val > 0 && len > 0)
  728.             {
  729.                 val--;
  730.                 p += len;
  731.                 histarg (p, &len);
  732.             }
  733.             *p = '\0';
  734.         }
  735.         else
  736.         {
  737.             histarg (sub, & len);
  738.             sub [len] = '\0';
  739.         }
  740.     }
  741.  
  742.     /* move everything to beginning of buffer */
  743.     if (save != sub)
  744.     {
  745.         movstr (sub, save);
  746.         sub = save;
  747.     }
  748.  
  749.  
  750.     /*
  751.      * check that the remaining characters represent
  752.      * legal following characters
  753.      */
  754.  
  755.     if (str[si] && str[si] != HISTSUB && str[si] != NL)
  756.         errmsg (str, badtoken);
  757.     
  758.     /* check for no substitutions and return if we are done */
  759.     if (str[si] && str[si] != HISTSUB)
  760.         return (TRUE);
  761.     
  762.     /* keep performing substitutions until there are no more */
  763.  
  764.     while (str[si] == HISTSUB)
  765.     {
  766.         i = 0;
  767.         si++;
  768.         flag = FALSE;
  769.         /* buf is what to look for */
  770.         while (str[si] && str[si] != HISTSUB)
  771.         {
  772.             if (str[si] == ESCAPE)
  773.                 si++;
  774.             
  775.             if (str[si])
  776.                 buf[i++] = str[si++];
  777.         }
  778.         buf[i] = '\0';
  779.  
  780.         i = 0;
  781.         if (str[si])
  782.             si++;
  783.         
  784.         /* rep is replacement */
  785.         while (str[si] && str[si] != HISTSUB)
  786.         {
  787.             if (str[si] == ESCAPE)
  788.                 si++;
  789.             
  790.             if (str[si])
  791.                 rep[i++] = str[si++];
  792.         }
  793.         rep[i] = '\0';
  794.  
  795.         if (str[si] == HISTSUB)
  796.             si++;
  797.         
  798.         if (str[si] == 'g' || str[si] == 'G')
  799.         {
  800.             flag = TRUE;
  801.             si++;
  802.         }
  803.  
  804.         j = 0;        /* j indexes new */
  805.         p = sub;
  806.         c = *p++;
  807.         sp = p;        /* save position for backing up */
  808.         while (c != '\0')
  809.         {
  810.             i = 0;
  811.             while (c && c != buf[i])
  812.             {
  813.                 /* copy what doesn't match */
  814.                 new[j++] = c;
  815.                 c = *p++;
  816.                 sp = p;
  817.             }
  818.  
  819.             while (c && buf[i] && c == buf[i])
  820.             {
  821.                 /* partial matching */
  822.                 c = *p++;
  823.                 i++;
  824.             }
  825.  
  826.             if (buf[i] == '\0')
  827.             {
  828.                 /* successful match */
  829.                 char *cp = rep;
  830.  
  831.                 while (*cp)
  832.                     new[j++] = *cp++;
  833.                     /* put in replacement text */
  834.  
  835.                 if (flag == FALSE)    /* just 1 replacement */
  836.                 {
  837.                     new[j++] = c;
  838.                     while (*p)
  839.                         new[j++] = *p++;
  840.                         /* copy the rest */
  841.                     break;
  842.                 }
  843.             }
  844.             else if (c != '\0')
  845.             {
  846.                 /* back up and try again */
  847.                 new[j++] = *(sp - 1);
  848.                 p = sp;
  849.                 c = *p++;
  850.                 sp = p;
  851.             }
  852.         }
  853.         new[j] = '\0';
  854.         movstr (new, sub);
  855.         /* now look for next substitution */
  856.     }
  857.  
  858.     if (save != sub)
  859.     {
  860.         movstr (sub, save);
  861.         sub = save;
  862.     }
  863.     
  864.     j = length (sub) - 2;
  865.     if (sub[j] == NL)
  866.         sub[j] = '\0';
  867.  
  868.     return (TRUE);
  869. }
  870.  
  871. /* histget --- get a specified string from the history buffers */
  872.  
  873. static int histget (hp, sub)
  874. int hp;
  875. char *sub;
  876. {
  877.     char buf[BIGBUF];
  878.     int i, j, maxinx, hval;
  879.  
  880.     *sub = '\0';
  881.     maxinx = (Hptrfirst - Hptrlast + MAXHIST) % MAXHIST + Histline - 1;
  882.     if (hp < Histline || hp > maxinx)        /* out of range */
  883.         return (FALSE);
  884.     
  885.     hval = ((hp - Histline + Hptrlast - 1) % MAXHIST) + 1;
  886.     for (i = Histptr[hval]; Histbuf[i] != '\0'; )
  887.     {
  888.         int k; 
  889.  
  890.         j = 0;
  891.         while (Histbuf[i] != '\0' && j < sizeof(buf) - 1)
  892.         {
  893.             buf[j] = Histbuf[i];
  894.             i = (i + 1) % HISTSIZE;
  895.             j++;
  896.         }
  897.  
  898.         buf[j] = '\0';
  899.         /* strcat (sub, buf); */
  900.         movstr (buf,
  901.             sub + (((k = length (sub) - 1) <= 0 ? 0 : k)));
  902.     }
  903.  
  904.     return (TRUE);
  905. }
  906.  
  907. /* histarg --- return the last position of the next argument */
  908.  
  909. static void histarg (ptr, len)
  910. char *ptr;
  911. int *len;
  912. {
  913.     char *p;
  914.     char c;
  915.     int bracket, paren, brace, squote, dquote, bquote, skip;
  916.  
  917.     p = ptr;
  918.     *len = 0;
  919.     skip = FALSE;
  920.     bracket = paren = brace = squote = dquote = bquote = 0;
  921.  
  922.     repeat
  923.     {
  924.         *len += 1;
  925.         c = *p++;
  926.         while (skip == FALSE && (c == SP || c == TAB))
  927.         {
  928.             c = *p++;
  929.             *len += 1;
  930.         }
  931.  
  932.         skip = TRUE;
  933.         switch (c) {
  934.         case ESCAPE:
  935.             c = *p++;
  936.             *len += 1;
  937.             break;
  938.  
  939.         case '[':
  940.             if (squote == 0 && dquote == 0 && bquote == 0)
  941.                 bracket++;
  942.             break;
  943.  
  944.         case ']':
  945.             if (squote == 0 && dquote == 0 && bquote == 0)
  946.                 bracket--;
  947.             break;
  948.  
  949.         case '(':
  950.             if (squote == 0 && dquote == 0 && bquote == 0)
  951.                 paren++;
  952.             break;
  953.  
  954.         case ')':
  955.             if (squote == 0 && dquote == 0 && bquote == 0)
  956.                 paren--;
  957.             break;
  958.  
  959.         case '{':
  960.             if (squote == 0 && dquote == 0 && bquote == 0)
  961.                 brace++;
  962.             break;
  963.  
  964.         case '}':
  965.             if (squote == 0 && dquote == 0 && bquote == 0)
  966.                 brace--;
  967.             break;
  968.  
  969.         case '\'':
  970.             if (dquote == 0 && bquote == 0)
  971.                 squote = 1 - squote;
  972.             break;
  973.  
  974.         case '"':
  975.             if (squote == 0 && bquote == 0)
  976.                 dquote = 1 - dquote;
  977.             break;
  978.  
  979.         case '`':
  980.             if (dquote == 0 && squote == 0)
  981.                 bquote = 1 - bquote;
  982.             break;
  983.  
  984.         }
  985.     } until (c == '\0' ||
  986.         ((c == SP || c == TAB) && paren == 0 && brace == 0 &&
  987.         bracket == 0 && squote == 0 && dquote == 0 && bquote == 0));
  988.  
  989.     *len -= 1;
  990.     return;
  991. }
  992.  
  993. /* ctoi --- character to integer conversion, updates indices ala Fortrash */
  994.  
  995. static int ctoi (str, inx)
  996. register char *str;
  997. register int *inx;
  998. {
  999.     register int ret = 0;
  1000.     int neg = 0;
  1001.  
  1002.     if (str[*inx] == '-')
  1003.     {
  1004.         neg = 1;
  1005.         *inx += 1;
  1006.     }
  1007.  
  1008.     while (digit (str[*inx]))
  1009.     {
  1010.         ret = 10 * ret + str[*inx] - '0';
  1011.         *inx += 1;
  1012.     }
  1013.  
  1014.     return (neg ? -ret : ret);
  1015. }
  1016.  
  1017. /* min --- real function to return min of two numbers */
  1018.  
  1019. static int min (a, b)
  1020. register int a, b;
  1021. {
  1022.     return (a < b ? a : b);
  1023. }
  1024.  
  1025. /* histinit --- reinitialize history buffers */
  1026.  
  1027. static void histinit ()
  1028. {
  1029.     Hbuffirst = Hbuflast = Hptrfirst = Hptrlast = Histline = 0;
  1030.     event_count = 1;
  1031. }
  1032.  
  1033. /* histsave --- save history command lines */
  1034.  
  1035. histsave (file)
  1036. char *file;
  1037. {
  1038.     int fd, status, junk;
  1039.  
  1040.     if ((flags&nohistflg) != 0)
  1041.         return (FALSE);
  1042.  
  1043.     if ((flags&prompt) == 0)
  1044.         return (FALSE);
  1045.  
  1046.     if ((fd = creat (file, 0600)) < 0)    /* delete previous contents */
  1047.         return (FALSE);
  1048.     
  1049.     status = TRUE;
  1050.  
  1051.     junk = MAXHIST;
  1052.     if (write (fd, & junk, sizeof (junk)) != sizeof (junk))
  1053.         status = FALSE;
  1054.  
  1055.     junk = HISTSIZE;
  1056.     if (status == TRUE &&
  1057.         write (fd, & junk, sizeof (junk)) != sizeof (junk))
  1058.         status = FALSE;
  1059.  
  1060.     if (status == TRUE &&
  1061.         write (fd, &Hbuffirst, sizeof(Hbuffirst)) != sizeof (Hbuffirst))
  1062.         status = FALSE;
  1063.  
  1064.     if (status == TRUE &&
  1065.         write (fd, & Hbuflast, sizeof (Hbuflast)) != sizeof (Hbuflast))
  1066.         status = FALSE;
  1067.  
  1068.     if (status == TRUE &&
  1069.         write (fd, &Hptrfirst, sizeof(Hptrfirst)) != sizeof (Hptrfirst))
  1070.         status = FALSE;
  1071.  
  1072.     if (status == TRUE &&
  1073.         write (fd, & Hptrlast, sizeof (Hptrlast)) != sizeof (Hptrlast))
  1074.         status = FALSE;
  1075.  
  1076.     if (status == TRUE &&
  1077.         write (fd, Histptr, sizeof(Histptr)) != sizeof (Histptr))
  1078.         status = FALSE;
  1079.  
  1080.     if (status == TRUE &&
  1081.         write (fd, Histbuf, sizeof(Histbuf)) != sizeof (Histbuf))
  1082.         status = FALSE;
  1083.     
  1084.     close (fd);
  1085.  
  1086.     if (status == FALSE)
  1087.         unlink (file);    /* remove entirely */
  1088.     
  1089.     return (status);
  1090. }
  1091.  
  1092. /* histrest --- restore a history save file */
  1093.  
  1094. int histrest (file)
  1095. char *file;
  1096. {
  1097.     int fd, status, junk;
  1098.  
  1099.     if (flags&nohistflg)
  1100.         return (FALSE);
  1101.  
  1102.     if ((flags&prompt) == 0)
  1103.         return (FALSE);
  1104.  
  1105.     if ((fd = open (file, 0)) < 0)    /* open for reading */
  1106.         return (FALSE);
  1107.  
  1108.     status = TRUE;
  1109.     if (read (fd, & junk, sizeof (junk)) != sizeof (junk) ||
  1110.             junk != MAXHIST)
  1111.         status = FALSE;
  1112.  
  1113.     if (status == TRUE && read (fd, & junk, sizeof (junk)) != sizeof (junk)
  1114.             || junk != HISTSIZE)
  1115.         status = FALSE;
  1116.  
  1117.     if (status == TRUE &&
  1118.         read (fd, &Hbuffirst, sizeof (Hbuffirst)) != sizeof (Hbuffirst))
  1119.         status = FALSE;
  1120.  
  1121.     if (status == TRUE &&
  1122.         read (fd, & Hbuflast, sizeof (Hbuflast)) != sizeof (Hbuflast))
  1123.         status = FALSE;
  1124.  
  1125.     if (status == TRUE &&
  1126.         read (fd, &Hptrfirst, sizeof (Hptrfirst)) != sizeof (Hptrfirst))
  1127.         status = FALSE;
  1128.  
  1129.     if (status == TRUE &&
  1130.         read (fd, & Hptrlast, sizeof (Hptrlast)) != sizeof (Hptrlast))
  1131.         status = FALSE;
  1132.  
  1133.     if (status == TRUE &&
  1134.         read (fd, Histptr, sizeof(Histptr)) != sizeof (Histptr))
  1135.         status = FALSE;
  1136.  
  1137.     if (status == TRUE &&
  1138.         read (fd, Histbuf, sizeof(Histbuf)) != sizeof (Histbuf))
  1139.         status = FALSE;
  1140.     
  1141.     Histline = - (Hptrfirst - Hptrlast + MAXHIST) % MAXHIST;
  1142.     event_count = 1;
  1143.     close (fd);
  1144.     return (status);
  1145. }
  1146.  
  1147. /* history --- print history buffer, or save or restore buffer to file */
  1148.  
  1149. int history (argc, argv)
  1150. int argc;
  1151. char **argv;
  1152. {
  1153.     int i;
  1154.     char *hf;
  1155.     int (*hfp)();
  1156.  
  1157.     if ((flags&nohistflg) != 0)
  1158.     {
  1159.         if (flags&prompt)
  1160.             prs ("history processing not enabled\n");
  1161.         return 1;    /* failure */
  1162.     }
  1163.  
  1164.     if ((flags & prompt) == 0)    /* shell file */
  1165.         return 1;
  1166.  
  1167.     if (argc == 1)
  1168.     {
  1169.         int j = Histline + 1;
  1170.         int k, l, m; 
  1171.         int neg;
  1172.         char *cp;
  1173.  
  1174. #define outstr(s)    for (cp = s; *cp; cp++) \
  1175.                 if (*cp == NL && *(cp+1))  \
  1176.                     prs_buff ("\\n"); \
  1177.                 else \
  1178.                     prc_buff (*cp)
  1179.  
  1180.  
  1181.         for (i = Hptrlast; i != Hptrfirst; i = (i + 1) % MAXHIST)
  1182.         {
  1183.             neg = FALSE;
  1184.  
  1185.             k = j++;
  1186.             if (k < 0)
  1187.             {
  1188.                 neg = TRUE;
  1189.                 k = -k;
  1190.             }
  1191.             itos (k);
  1192.             l = length (numbuf) - 1;
  1193.             for (m = 3 - l; m > 0; m--)
  1194.                 prc_buff (SP);
  1195.             prc_buff (neg ? '-' : SP);
  1196.             prs_buff (numbuf);
  1197.             prc_buff (COLON);
  1198.             prc_buff (SP);
  1199.             /*
  1200.              * make sure that what we're printing
  1201.              * doesn't wrap around the history buffer.
  1202.              */
  1203.             k = (i % MAXHIST) + 1;
  1204.             if ((k != Hptrfirst && Histptr[i] < Histptr[k]) ||
  1205.                 (k == Hptrfirst && Histptr[i] < Hbuffirst))
  1206.                 outstr (& Histbuf[Histptr[i]]);
  1207.             else
  1208.             {
  1209.                 /* saved text wraps around */
  1210.                 for (l = Histptr[i]; l <= HISTSIZE - 1 &&
  1211.                         Histbuf[l] != '\0'; l++)
  1212.                     if (Histbuf[l] == NL
  1213.             && Histbuf[l + 1 <= HISTSIZE - 1 ? l + 1 : 0] != '\0')
  1214.                         prs_buff ("\\n");
  1215.                     else
  1216.                         prc_buff (Histbuf[l]);
  1217.                 
  1218.                 if (Histbuf[HISTSIZE - 1] != '\0')
  1219.                     outstr (Histbuf);
  1220.             }
  1221.         }
  1222.         return 0;
  1223.     }
  1224.     else if (eq (argv[1], dashi))
  1225.     {
  1226.         histinit ();
  1227.         return 0;
  1228.     }
  1229.     else if (eq (argv[1], dashr))
  1230.         hfp = histrest;
  1231.     else if (eq (argv[1], dashs))
  1232.         hfp = histsave;
  1233.     else
  1234.     {
  1235.         prs(argv[1]);
  1236.         prc(COLON);
  1237.         prs(h_badopt);
  1238.         newline();
  1239.         return 1;
  1240.     }
  1241.  
  1242.     if (argc >= 3)
  1243.         hf = argv[2];
  1244.     else
  1245.         hf = histfnod.namval;
  1246.     
  1247.     return ((*hfp)(hf) != 0);    /* do a save or restore */
  1248. }
  1249.