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