home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / k / ksh48.zip / sh / history.c < prev    next >
C/C++ Source or Header  |  1993-01-12  |  19KB  |  978 lines

  1. /*
  2.  * command history
  3.  *
  4.  * only implements in-memory history.
  5.  */
  6.  
  7. #ifndef lint
  8. static char *RCSid = "$Id: history.c,v 1.5 1992/12/05 13:15:34 sjg Exp $";
  9. #endif
  10. /*
  11.  *    This file contains
  12.  *    a)    the original in-memory history  mechanism
  13.  *    b)    a simple file saving history mechanism done by  sjg@zen
  14.  *        define EASY_HISTORY to get this
  15.  *    c)    a more complicated mechanism done by  pc@hillside.co.uk
  16.  *        that more closely follows the real ksh way of doing
  17.  *        things. You need to have the mmap system call for this
  18.  *        to work on your system
  19.  */
  20.  
  21. #ifndef HISTFILE
  22. # ifdef OS2
  23. #  define HISTFILE "history.ksh"
  24. # else
  25. #  define HISTFILE ".pdksh_hist"
  26. # endif
  27. #endif
  28.  
  29. #include "config.h"
  30. #include "stdh.h"
  31.  
  32. #ifdef EASY_HISTORY
  33.  
  34. #include <errno.h>
  35. #include <setjmp.h>
  36. #include "sh.h"
  37.  
  38. static FILE *hist_fh = NULL;
  39. static FILE *hist_open ARGS((char *mode));
  40.  
  41. #else
  42. /*    Defines and includes for the complicated case */
  43.  
  44. #include <sys/types.h>
  45. #include <sys/stat.h>
  46. #include <sys/file.h>
  47. #include <sys/mman.h>
  48. #include <errno.h>
  49. #include <setjmp.h>
  50. #include "sh.h"
  51.  
  52. /*
  53.  *    variables for handling the data file
  54.  */
  55. static char    *hname;
  56. static int    histfd;
  57. static int    hsize;
  58. static int    hstarted;
  59.  
  60. static int hist_count_lines ARGS((unsigned char *, int));
  61. static int hist_shrink ARGS((unsigned char *, int));
  62. static unsigned char *hist_skip_back ARGS((unsigned char *,int *,int));
  63. static void histload ARGS((Source *, unsigned char *, int));
  64. static void histinsert ARGS((Source *, int, unsigned char *));
  65. static void writehistfile ARGS((int, char *));
  66. static int sprinkle ARGS((int));
  67.  
  68. #ifdef MAP_FILE
  69. #define    MAP_FLAGS    MAP_FILE|MAP_PRIVATE
  70. #else
  71. #define    MAP_FLAGS    MAP_PRIVATE
  72. #endif
  73.  
  74. #endif    /* of EASY_HISTORY */
  75.  
  76.  
  77. char   *histrpl();
  78. char  **current;
  79. int    curpos;
  80.  
  81. c_fc(wp)
  82.     register char **wp;
  83. {
  84.     register char *id;
  85.     FILE *f;
  86.     struct temp *tf;
  87.     register char **hp;
  88.     char **hbeg, **hend;
  89.     char *p, *cmd = NULL;
  90.     int lflag = 0, nflag = 0, sflag = 0, rflag = 0, gflag = 0;
  91.     int done = 0;
  92.     void histbackup();
  93.  
  94.     for (wp++; (id = *wp) != NULL && *id++ == '-' && !done; wp++)
  95.         while (*id && !done) {
  96.             switch (*id++) {
  97.               case 'l':
  98.                 lflag++;
  99.                 break;
  100.               case 'n':
  101.                 nflag++;
  102.                 break;
  103.               case 'r':
  104.                 rflag++;
  105.                 break;
  106.               case 'g':
  107.                 gflag++;
  108.                 break;
  109.               case 'e':
  110.                 if (++wp && (p = *wp)) {
  111.                     if (p[0] == '-' && !p[1]) {
  112.                         sflag++;
  113.                     } else {
  114.                         cmd = alloc((size_t)(strlen(p)+4),ATEMP);
  115.                         strcpy(cmd, p);
  116.                         strcat(cmd, " $_");
  117.                     }
  118.                 } else
  119.                     errorf("argument expected\n");
  120.                 id = "";
  121.                 break;
  122.               default:
  123.                 wp--;
  124.                 done++;
  125.                 break;
  126.             }
  127.         }
  128.  
  129.     if (sflag) {
  130.         char *pat = NULL, *rep = NULL;
  131.  
  132.         hp = histptr - 1;
  133.         while ((id = *wp++) != NULL) {
  134.             /* todo: multiple substitutions */
  135.             if ((p = strchr(id, '=')) != NULL) {
  136.                 pat = id;
  137.                 rep = p;
  138.                 *rep++ = '\0';
  139.             } else
  140.                 hp = histget(id);
  141.         }
  142.  
  143.         if (hp == NULL || hp < history)
  144.             errorf("cannot find history\n");
  145.         if (pat == NULL)
  146.             strcpy(line, *hp);
  147.         else
  148.             histrpl(*hp, pat, rep, gflag);
  149.         histbackup();
  150. #ifdef EASY_HISTORY
  151.         histsave(line); 
  152. #else
  153.         histsave(source->line+1, line, 1);
  154. #endif
  155.         histpush--;
  156.         line[0] = '\0';
  157.         return 0;
  158.     }
  159.  
  160.     if (*wp != NULL) {
  161.         hbeg = histget(*wp++); /* first */
  162.         if (*wp != NULL)
  163.             hend = histget(*wp++); /* last */
  164.         else if (lflag)
  165.             hend = histptr;
  166.         else
  167.             hend = hbeg;
  168.     } else {
  169.         if (lflag)
  170.             hbeg = histptr - 16, hend = histptr;
  171.         else
  172.             hbeg = hend = histptr - 1;
  173.         if (hbeg < history)
  174.             hbeg = history;
  175.     }
  176.     if (hbeg == NULL || hend == NULL)
  177.         errorf("can't find history\n");
  178.  
  179.     if (lflag)
  180.         f = stdout;
  181.     else {
  182.         nflag++;
  183.         tf = maketemp(ATEMP);
  184.         tf->next = e.temps; e.temps = tf;
  185.         f = fopen(tf->name, "w");
  186.         if (f == NULL)
  187.             errorf("cannot create temp file %s", tf->name);
  188.         setvbuf(f, (char *)NULL, _IOFBF, BUFSIZ);
  189.     }
  190.  
  191.     for (hp = (rflag ? hend : hbeg); rflag ? (hp >= hbeg) : (hp <= hend);
  192.           rflag ? hp-- : hp++) {
  193.         if (!nflag)
  194.             fprintf(f, "%3d: ", source->line - (int)(histptr-hp));
  195.         fprintf(f, "%s\n", *hp);
  196.     }
  197.  
  198.     if (lflag)
  199.         return 0;
  200.     else
  201.         fclose(f);
  202.  
  203.     setstr(local("_"), tf->name);
  204.     if (cmd) {
  205.         command(cmd); /* edit temp file */
  206.         afree(cmd, ATEMP);
  207.     } else
  208.         command("${FCEDIT:-/bin/ed} $_");
  209.  
  210.     f = fopen(tf->name, "r");
  211.     if (f == NULL)
  212.         errorf("cannot open temp file %s\n", tf->name);
  213.     setvbuf(f, (char *)NULL, _IOFBF, BUFSIZ);
  214.     /* we push the editted lines onto the history list */
  215.     while (fgets(line, sizeof(line), f) != NULL) {
  216. #ifdef EASY_HISTORY
  217.         histsave(line); 
  218. #else
  219.         histsave(source->line, line, 1); 
  220. #endif
  221.         histpush--;
  222.     }
  223.     line[0] = '\0';
  224.     fclose(f);
  225.  
  226.     return 0;
  227. }
  228.  
  229. /******************************/
  230. /* Back up over last histsave */
  231. /******************************/
  232. void
  233. histbackup()
  234. {
  235.     static int last_line = -1;
  236.  
  237.     if (histptr > history && last_line != source->line) {
  238.         source->line--;
  239.         afree((void*)*histptr, APERM);
  240.         histptr--;
  241.         last_line = source->line;
  242.     }
  243. }
  244.  
  245. /*
  246.  * get pointer to history given pattern
  247.  * pattern is a number or string
  248.  */
  249. char **
  250. histget(str)
  251.     char *str;
  252. {
  253.     register char **hp = NULL;
  254.  
  255.     if (*str == '-')
  256.         hp = histptr + getn(str);
  257.     else
  258.     if (digit(*str))
  259.         hp = histptr + (getn(str) - source->line);
  260.     else
  261.     if (*str == '?') {    /* unanchored match */
  262.         for (hp = histptr-1; hp >= history; hp--)
  263.             if (strstr(*hp, str+1) != NULL)
  264.                 break;
  265.     } else {        /* anchored match */
  266.         for (hp = histptr; hp >= history; hp--)
  267.             if (strncmp(*hp, str, strlen(str)) == 0)
  268.                 break;
  269.     }
  270.  
  271.     return (history <= hp && hp <= histptr) ? hp : NULL;
  272. }
  273.  
  274. char *
  275. histrpl(s, pat, rep, global)
  276.     char *s;
  277.     char *pat, *rep;
  278.     int global;
  279. {
  280.     char *s1, *p, *last = NULL;
  281.     int len = strlen(pat);
  282.  
  283.     if (strlen(s) - strlen(pat) + strlen(rep) >= LINE)
  284.         errorf("substitution too long\n");
  285.     line[0] = '\0';
  286.     p = line;
  287.     while (s1 = strstr(s, pat)) {
  288.         strncpy(p, s, s1 - s);        /* first part */
  289.         strcpy(p + (s1 - s), rep);    /* replacement */
  290.         s = s1 + len;
  291.         last = s1;
  292.         p = strchr(p, 0);
  293.         if (!global)
  294.             s = "";
  295.     }
  296.     if (last)
  297.         strcpy(p, last + len);        /* last part */
  298.     else
  299.         errorf("substitution failed\n");
  300.     return line;
  301. }
  302.  
  303. /*
  304.  * Return the current position.
  305.  */
  306. char **
  307. histpos()
  308. {
  309.     return current;
  310. }
  311.  
  312. int
  313. histN()
  314. {
  315.     return curpos;
  316. }
  317.  
  318. int
  319. histnum(n)
  320. {
  321.     int    last = histptr - history;
  322.  
  323.     if (n < 0 || n >= last) {
  324.         current = histptr;
  325.         curpos = last;
  326.         return last;
  327.     }  else {
  328.         current = &history[n];
  329.         curpos = n;
  330.         return n;
  331.     }
  332. }
  333.  
  334. /*
  335.  * This will become unecessary if histget is modified to allow
  336.  * searching from positions other than the end, and in either 
  337.  * direction.
  338.  */
  339. char *
  340. findhist(start, fwd, str)
  341.     int    start;
  342.     int    fwd;
  343.     char     *str;
  344. {
  345.     int     pos = start;
  346.     char     *line, *last;
  347.  
  348.     /* XXX check that we are valid after this */
  349.     if (fwd)
  350.         pos++;
  351.     else
  352.         pos--;
  353.     histnum(pos);
  354.     line = *histpos();
  355.     do {
  356.         last = line;
  357.         if (strstr(line, str) != 0) {
  358.             /* keep position current */
  359.             return (line);
  360.         }
  361.         if (fwd)
  362.             pos++;
  363.         else
  364.             pos--;
  365.         histnum(pos);
  366.         line = *histpos();
  367.     } while (line && *line && line != last && pos>0);
  368.  
  369.     histnum(start);
  370.     if (pos <= 0)
  371.         return (char*)-1; /* TODO */
  372.     return NULL;
  373. }
  374.  
  375. #ifdef EASY_HISTORY
  376. /*
  377.  * save command in history
  378.  */
  379. void
  380. histsave(cmd)
  381.     char *cmd;
  382. {
  383.     register char **hp = histptr;
  384.     char *cp;
  385.  
  386.     if (++hp >= history + HISTORY) { /* remove oldest command */
  387.         afree((void*)*history, APERM);
  388.         for (hp = history; hp < history + HISTORY - 1; hp++)
  389.             hp[0] = hp[1];
  390.     }
  391.     *hp = strsave(cmd, APERM);
  392.     if ((cp = strchr(*hp, '\n')) != NULL)
  393.         *cp = '\0';
  394.     histptr = hp;
  395. }
  396.  
  397. /*
  398.  * 92-04-25 <sjg@zen>
  399.  * A simple history file implementation.
  400.  * At present we only save the history when we exit.
  401.  * This can cause problems when there are multiple shells are 
  402.  * running under the same user-id.  The last shell to exit gets 
  403.  * to save its history.
  404.  */
  405. void
  406. hist_init(s)
  407.   Source *s;
  408. {
  409.   static int once = 0;
  410.   FILE *fh;
  411.   
  412.   if (once++)
  413.     return;
  414.  
  415.   if (fh = hist_open("r"))
  416.   {
  417.     while (fgets(line, sizeof(line), fh) != NULL)
  418.     {
  419.       histsave(line); 
  420.       s->line++;
  421.     }
  422.     line[0] = '\0';
  423.     fclose(fh);
  424. #if 0    /* this might be a good idea? */
  425.     hist_fh = hist_open("a");
  426. #endif
  427.   }
  428.   
  429. }
  430.  
  431. void
  432. init_histvec()
  433. {    ;    }
  434.  
  435. /*
  436.  * save our history.
  437.  * We check that we do not have more than we are allowed.
  438.  * If the history file is read-only we do nothing.
  439.  * Handy for having all shells start with a useful history set.
  440.  */
  441.  
  442. void
  443. hist_finish()
  444. {
  445.   static int once = 0;
  446.   FILE *fh;
  447.   register int i, mx;
  448.   register char **hp, *mode = "w";
  449.   
  450.   if (once++)
  451.     return;
  452.   if ((mx = atoi(strval(global("HISTSIZE")))) > HISTORY || mx <= 0)
  453.     mx = HISTORY;
  454.   /* check how many we have */
  455.   i = histptr - history;
  456.   if (i >= mx)
  457.   {
  458.     hp = &histptr[-mx];
  459.   }
  460.   else
  461.   {
  462.     hp = history;
  463.   }
  464.   if (fh = hist_open(mode))
  465.   {
  466.     for (i = 0; i < mx && hp[i]; i++)
  467.       fprintf(fh, "%s\n", hp[i]);
  468.     fclose(fh);
  469.   }
  470. }
  471.  
  472.  
  473. /*
  474.  * simply grab the nominated history file.
  475.  */
  476. static FILE *
  477. hist_open(mode)
  478.   char *mode;
  479. {
  480.   register char *rcp;
  481.   FILE *fh;
  482.   char name[128];
  483.   
  484.   if ((rcp = strval(global("HISTFILE"))) == NULL || *rcp == '\0')
  485.   {
  486.     (void) sprintf(name, "%s/%s", strval(global("HOME")), HISTFILE);
  487.     rcp = name;
  488.   }
  489.   return fopen(rcp, mode);
  490. }
  491.  
  492. #else /* EASY_HISTORY */
  493.  
  494. /*
  495.  *    Routines added by Peter Collinson BSDI(Europe)/Hillside Systems to
  496.  *    a) permit HISTSIZE to control number of lines of history stored
  497.  *    b) maintain a physical history file
  498.  *
  499.  *    It turns out that there is a lot of ghastly hackery here
  500.  */
  501.  
  502.  
  503. /*
  504.  * save command in history
  505.  */
  506. void
  507. histsave(lno, cmd, dowrite)
  508.     int lno;
  509.     char *cmd;
  510.     int dowrite;
  511. {
  512.     register char **hp;
  513.     char *cp;
  514.  
  515.     cmd = strsave(cmd, APERM);
  516.     if ((cp = strchr(cmd, '\n')) != NULL)
  517.         *cp = '\0';
  518.  
  519.     if (histfd && dowrite)
  520.         writehistfile(lno, cmd);
  521.  
  522.     hp = histptr;
  523.         
  524.     if (++hp >= history + histsize) { /* remove oldest command */
  525.         afree((void*)*history, APERM);
  526.         for (hp = history; hp < history + histsize - 1; hp++)
  527.             hp[0] = hp[1];
  528.     }
  529.     *hp = cmd;
  530.     histptr = hp;
  531. }
  532.  
  533. /*
  534.  *    set history
  535.  *    this means reallocating the dataspace
  536.  */
  537. void
  538. sethistsize(n)
  539.     int n;
  540. {
  541.     int    offset;
  542.     
  543.     if (n != histsize) {
  544.         offset = histptr - history;
  545.         history = (char **)aresize(history, n*sizeof(char *), APERM);
  546.  
  547.         if (n < histsize && offset > histsize)
  548.             offset = histsize;
  549.  
  550.         histsize = n;
  551.         histptr = history + offset;
  552.     }
  553. }
  554.  
  555. /*
  556.  *    set history file
  557.  *    This can mean reloading/resetting/starting history file
  558.  *    maintenance
  559.  */
  560. void
  561. sethistfile(name)
  562.     char *name;
  563. {
  564.     /* if not started then nothing to do */
  565.     if (hstarted == 0)
  566.         return;
  567.  
  568.     /* if the name is the same as the name we have */
  569.     if (hname && strcmp(hname, name) == 0)
  570.         return;
  571.  
  572.     /*
  573.      * its a new name - possibly
  574.      */
  575.     if (histfd) {
  576.         /* yes the file is open */
  577.         (void) close(histfd);
  578.         histfd = 0;
  579.         hsize = 0;
  580.         afree(hname, APERM);
  581.         hname = NULL;
  582.         /* let's reset the history */
  583.         histptr = history - 1;
  584.         source->line = 0;
  585.     }
  586.  
  587.     hist_init(source);
  588. }
  589.  
  590. /*
  591.  *    initialise the history vector
  592.  */
  593. void
  594. init_histvec()
  595. {
  596.     if (history == (char **)NULL) {
  597.         history = (char **)alloc(histsize*sizeof (char *), APERM);
  598.         histptr = history-1;
  599.     }
  600. }
  601.     
  602. /*
  603.  *    Write history data to a file nominated by HISTFILE
  604.  *    if HISTFILE is unset then history still happens, but
  605.  *    the data is not written to a file
  606.  *    All copies of ksh looking at the file will maintain the
  607.  *    same history. This is ksh behaviour.
  608.  *
  609.  *    This stuff uses mmap()
  610.  *    if your system ain't got it - then you'll have to undef HISTORYFILE
  611.  */
  612.     
  613. /*
  614.  *    Open a history file
  615.  *    Format is:
  616.  *    Bytes 1, 2: HMAGIC - just to check that we are dealing with
  617.  *            the correct object
  618.  *    Then follows a number of stored commands
  619.  *    Each command is
  620.  *    <command byte><command number(4 bytes)><bytes><null>
  621.  */
  622. #define HMAGIC1        0xab
  623. #define HMAGIC2        0xcd
  624. #define COMMAND        0xff
  625.  
  626. void
  627. hist_init(s)
  628.     Source *s;
  629. {
  630.     unsigned char    *base;
  631.     int    lines;
  632.     int    bytes;
  633.     
  634.     hstarted = 1;
  635.     
  636.     if (flag[FTALKING] == 0)
  637.         return;
  638.  
  639.     hname = strval(global("HISTFILE"));
  640.     if (hname == NULL)
  641.         return;
  642.     hname = strsave(hname, APERM);
  643.  
  644.   retry:
  645.     /* we have a file and are interactive */
  646.     if ((histfd = open(hname, O_RDWR|O_CREAT|O_APPEND, 0600)) < 0)
  647.         return;
  648.  
  649.     histfd = fcntl(histfd, F_DUPFD, FDBASE);
  650.     
  651.     (void) fd_clexec(histfd);
  652.  
  653.     (void) flock(histfd, LOCK_EX);
  654.  
  655.     hsize = lseek(histfd, 0L, L_XTND);
  656.  
  657.     if (hsize == 0) {
  658.         /* add magic */
  659.         if (sprinkle(histfd)) {
  660.             hist_finish();
  661.             return;
  662.         }
  663.     }
  664.     else if (hsize > 0) {
  665.         /*
  666.          * we have some data
  667.          */
  668.         base = (unsigned char *)mmap(0, hsize, PROT_READ, MAP_FLAGS, histfd, 0);
  669.         /*
  670.          * check on its validity
  671.          */
  672.         if ((int)base == -1 || *base != HMAGIC1 || base[1] != HMAGIC2) {
  673.             if ((int)base !=  -1)
  674.                 munmap((caddr_t)base, hsize);
  675.             hist_finish();
  676.             unlink(hname);
  677.             goto retry;
  678.         }
  679.         if (hsize > 2) {
  680.             lines = hist_count_lines(base+2, hsize-2);
  681.             if (lines > histsize) {
  682.                 /* we need to make the file smaller */
  683.                 if (hist_shrink(base, hsize))
  684.                     unlink(hname);
  685.                 munmap((caddr_t)base, hsize);
  686.                 hist_finish();
  687.                 goto retry;
  688.             }
  689.         }
  690.         histload(s, base+2, hsize-2);
  691.         munmap((caddr_t)base, hsize);
  692.     }
  693.     (void) flock(histfd, LOCK_UN);
  694.     hsize = lseek(histfd, 0L, L_XTND);
  695. }
  696.  
  697. typedef enum state {
  698.     shdr,        /* expecting a header */
  699.     sline,        /* looking for a null byte to end the line */
  700.     sn1,        /* bytes 1 to 4 of a line no */
  701.     sn2, sn3, sn4,
  702. } State;
  703.  
  704. static int
  705. hist_count_lines(base, bytes)
  706.     register unsigned char *base;
  707.     register int bytes;
  708. {
  709.     State state = shdr;
  710.     register lines = 0;
  711.     
  712.     while (bytes--) {
  713.         switch (state)
  714.         {
  715.         case shdr:
  716.             if (*base == COMMAND)
  717.                 state = sn1;
  718.             break;
  719.         case sn1:
  720.             state = sn2; break;
  721.         case sn2:
  722.             state = sn3; break;
  723.         case sn3:
  724.             state = sn4; break;
  725.         case sn4:
  726.             state = sline; break;
  727.         case sline:
  728.             if (*base == '\0')
  729.                 lines++, state = shdr;
  730.         }
  731.         base++;
  732.     }
  733.     return lines;
  734. }
  735.  
  736. /*
  737.  *    Shrink the history file to histsize lines
  738.  */
  739. static int
  740. hist_shrink(oldbase, oldbytes)
  741.     unsigned char *oldbase;
  742.     int oldbytes;
  743. {
  744.     int fd;
  745.     char    nfile[1024];
  746.     struct    stat statb;
  747.     unsigned char *nbase = oldbase;
  748.     int nbytes = oldbytes;
  749.  
  750.     nbase = hist_skip_back(nbase, &nbytes, histsize);
  751.     if (nbase == NULL)
  752.         return 1;
  753.     if (nbase == oldbase)
  754.         return 0;
  755.     
  756.     /*
  757.      *    create temp file
  758.      */
  759.     (void) sprintf(nfile, "%s.%d", hname, getpid());
  760.     if ((fd = creat(nfile, 0600)) < 0)
  761.         return 1;
  762.  
  763.     if (sprinkle(fd)) {
  764.         close(fd);
  765.         unlink(nfile);
  766.         return 1;
  767.     }
  768.     if (write(fd, nbase, nbytes) != nbytes) {
  769.         close(fd);
  770.         unlink(nfile);
  771.         return 1;
  772.     }
  773.     /*
  774.      *    worry about who owns this file
  775.      */
  776.     if (fstat(histfd, &statb) >= 0)
  777.         fchown(fd, statb.st_uid, statb.st_gid);
  778.     close(fd);
  779.     
  780.     /*
  781.      *    rename
  782.      */
  783.     if (rename(nfile, hname) < 0)
  784.         return 1;
  785.     return 0;
  786. }
  787.     
  788.  
  789. /*
  790.  *    find a pointer to the data `no' back from the end of the file
  791.  *    return the pointer and the number of bytes left
  792.  */
  793. static unsigned char *
  794. hist_skip_back(base, bytes, no)
  795.     unsigned char *base;
  796.     int *bytes;
  797.     int no;
  798. {
  799.     register int lines = 0;
  800.     register unsigned char *ep;
  801.  
  802.     
  803.  
  804.     for (ep = base + *bytes; ep > base; ep--)
  805.     {
  806.         while (*ep != COMMAND) {
  807.             if (--ep == base)
  808.                 break;
  809.         }
  810.         if (++lines == no) {
  811.             *bytes = *bytes - ((char *)ep - (char *)base);
  812.             return ep;
  813.         }
  814.     }
  815.     if (ep > base)
  816.         return base;
  817.     return NULL;
  818. }
  819.  
  820. /*
  821.  *    load the history structure from the stored data
  822.  */
  823. static void
  824. histload(s, base, bytes)
  825.     Source *s;
  826.     register unsigned char *base;
  827.     register int bytes;
  828. {
  829.     State state;
  830.     int    lno;
  831.     unsigned char    *line;
  832.     
  833.     for (state = shdr; bytes-- > 0; base++) {
  834.         switch (state) {
  835.         case shdr:
  836.             if (*base == COMMAND)
  837.                 state = sn1;
  838.             break;
  839.         case sn1:
  840.             lno = (((*base)&0xff)<<24);
  841.             state = sn2;
  842.             break;
  843.         case sn2:
  844.             lno |= (((*base)&0xff)<<16);
  845.             state = sn3;
  846.             break;
  847.         case sn3:
  848.             lno |= (((*base)&0xff)<<8);
  849.             state = sn4;
  850.             break;
  851.         case sn4:
  852.             lno |= (*base)&0xff;
  853.             line = base+1;
  854.             state = sline;
  855.             break;
  856.         case sline:
  857.             if (*base == '\0') {
  858.                 /* worry about line numbers */
  859.                 if (histptr >= history && lno-1 != s->line) {
  860.                     /* a replacement ? */
  861.                     histinsert(s, lno, line);
  862.                 }
  863.                 else {
  864.                     s->line = lno;
  865.                     histsave(lno, (char *)line, 0);
  866.                 }
  867.                 state = shdr;
  868.             }
  869.         }
  870.     }
  871. }
  872.                 
  873. /*
  874.  *    Insert a line into the history at a specified number
  875.  */
  876. static void
  877. histinsert(s, lno, line)
  878.     Source *s;
  879.     int lno;
  880.     unsigned char *line;
  881. {
  882.     register char **hp;
  883.     
  884.     if (lno >= s->line-(histptr-history) && lno <= s->line) {
  885.         hp = &histptr[lno-s->line];
  886.         if (*hp)
  887.             afree((void*)*hp, APERM);
  888.         *hp = strsave((char *)line, APERM);
  889.     }
  890. }
  891.  
  892. /*
  893.  *    write a command to the end of the history file
  894.  *    This *MAY* seem easy but it's also necessary to check
  895.  *    that the history file has not changed in size.
  896.  *    If it has - then some other shell has written to it
  897.  *    and we should read those commands to update our history
  898.  */
  899. static void
  900. writehistfile(lno, cmd)
  901.     int lno;
  902.     char *cmd;
  903. {
  904.     int    sizenow;
  905.     unsigned char    *base;
  906.     unsigned char    *new;
  907.     int    bytes;
  908.     char    hdr[5];
  909.     
  910.     (void) flock(histfd, LOCK_EX);
  911.     sizenow = lseek(histfd, 0L, L_XTND);
  912.     if (sizenow != hsize) {
  913.         /*
  914.          *    Things have changed
  915.          */
  916.         if (sizenow > hsize) {
  917.             /* someone has added some lines */
  918.             bytes = sizenow - hsize;
  919.             base = (unsigned char *)mmap(0, sizenow, PROT_READ, MAP_FLAGS, histfd, 0);
  920.             if ((int)base == -1)
  921.                 goto bad;
  922.             new = base + hsize;
  923.             if (*new != COMMAND) {
  924.                 munmap((caddr_t)base, sizenow);
  925.                 goto bad;
  926.             }
  927.             source->line--;
  928.             histload(source, new, bytes);
  929.             source->line++;
  930.             lno = source->line;
  931.             munmap((caddr_t)base, sizenow);
  932.             hsize = sizenow;
  933.         } else {
  934.             /* it has shrunk */
  935.             /* but to what? */
  936.             /* we'll give up for now */
  937.             goto bad;
  938.         }
  939.     }
  940.     /*
  941.      *    we can write our bit now
  942.      */
  943.     hdr[0] = COMMAND;
  944.     hdr[1] = (lno>>24)&0xff;
  945.     hdr[2] = (lno>>16)&0xff;
  946.     hdr[3] = (lno>>8)&0xff;
  947.     hdr[4] = lno&0xff;
  948.     (void) write(histfd, hdr, 5);
  949.     (void) write(histfd, cmd, strlen(cmd)+1);
  950.     hsize = lseek(histfd, 0L, L_XTND);
  951.     (void) flock(histfd, LOCK_UN);
  952.     return;
  953. bad:
  954.     hist_finish();
  955. }
  956.  
  957. void
  958. hist_finish()
  959. {
  960.     (void) flock(histfd, LOCK_UN);
  961.     (void) close(histfd);
  962.     histfd = 0;
  963. }
  964.  
  965. /*
  966.  *    add magic to the history file
  967.  */
  968. static int
  969. sprinkle(fd)
  970.     int fd;
  971. {
  972.     static char mag[] = { HMAGIC1, HMAGIC2 };
  973.  
  974.     return(write(fd, mag, 2) != 2);
  975. }
  976.  
  977. #endif
  978.