home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1705 / blk.c next >
Encoding:
C/C++ Source or Header  |  1990-12-28  |  8.7 KB  |  457 lines

  1. /* blk.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    16820 SW Tallac Way
  6.  *    Beaverton, OR 97006
  7.  *    kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda
  8.  */
  9.  
  10.  
  11. /* This file contains the functions that get/put blocks from the temp file.
  12.  * It also contains the "do" and "undo" functions.
  13.  */
  14.  
  15. #include "config.h"
  16. #include "vi.h"
  17.  
  18. #ifndef NBUFS
  19. # define NBUFS    5        /* must be at least 3 -- more is better */
  20. #endif
  21.  
  22. extern long lseek();
  23.  
  24. /*------------------------------------------------------------------------*/
  25.  
  26. BLK        hdr;        /* buffer for the header block */
  27.  
  28. static int    b4cnt;        /* used to count context of beforedo/afterdo */
  29. static struct _blkbuf
  30. {
  31.     BLK        buf;        /* contents of a text block */
  32.     unsigned short    logical;    /* logical block number */
  33.     int        dirty;        /* must the buffer be rewritten? */
  34. }
  35.         blk[NBUFS],    /* buffers for text[?] blocks */
  36.         *toonew,    /* buffer which shouldn't be recycled yet */
  37.         *newtoo,    /* another buffer which should be recycled */
  38.         *recycle = blk;    /* next block to be recycled */
  39.  
  40.  
  41.  
  42.  
  43.  
  44. /* This function wipes out all buffers */
  45. blkinit()
  46. {
  47.     int    i;
  48.  
  49.     for (i = 0; i < NBUFS; i++)
  50.     {
  51.         blk[i].logical = 0;
  52.         blk[i].dirty = FALSE;
  53.     }
  54.     for (i = 0; i < MAXBLKS; i++)
  55.     {
  56.         hdr.n[i] = 0;
  57.     }
  58. }
  59.  
  60. /* This function allocates a buffer and fills it with a given block's text */
  61. BLK *blkget(logical)
  62.     int    logical;    /* logical block number to fetch */
  63. {
  64.     register struct _blkbuf    *this;    /* used to step through blk[] */
  65.     register int        i;
  66.  
  67.     /* if logical is 0, just return the hdr buffer */
  68.     if (logical == 0)
  69.     {
  70.         return &hdr;
  71.     }
  72.  
  73.     /* see if we have that block in mem already */
  74.     for (this = blk; this < &blk[NBUFS]; this++)
  75.     {
  76.         if (this->logical == logical)
  77.         {
  78.             newtoo = toonew;
  79.             toonew = this;
  80.             return &this->buf;
  81.         }
  82.     }
  83.  
  84.     /* choose a block to be recycled */
  85.     do
  86.     {
  87.         this = recycle++;
  88.         if (recycle == &blk[NBUFS])
  89.         {
  90.             recycle = blk;
  91.         }
  92.     } while (this == toonew || this == newtoo);
  93.  
  94.     /* if it contains a block, flush that block */
  95.     blkflush(this);
  96.  
  97.     /* fill this buffer with the desired block */
  98.     this->logical = logical;
  99.     if (hdr.n[logical])
  100.     {
  101.         /* it has been used before - fill it from tmp file */
  102.         lseek(tmpfd, (long)hdr.n[logical] * (long)BLKSIZE, 0);
  103.         if (read(tmpfd, this->buf.c, BLKSIZE) != BLKSIZE)
  104.         {
  105.             msg("Error reading back from tmp file!");
  106.         }
  107.     }
  108.     else
  109.     {
  110.         /* it is new - zero it */
  111.         for (i = 0; i < BLKSIZE; i++)
  112.         {
  113.             this->buf.c[i] = 0;
  114.         }
  115.     }
  116.  
  117.     /* This isn't really a change, but it does potentially invalidate
  118.      * the kinds of shortcuts that the "changes" variable is supposed
  119.      * to protect us from... so count it as a change.
  120.      */
  121.     changes++;
  122.  
  123.     /* mark it as being "not dirty" */
  124.     this->dirty = 0;
  125.  
  126.     /* return it */
  127.     newtoo = toonew;
  128.     toonew = this;
  129.     return &this->buf;
  130. }
  131.  
  132.  
  133.  
  134. /* This function writes a block out to the temporary file */
  135. blkflush(this)
  136.     register struct _blkbuf    *this;    /* the buffer to flush */
  137. {
  138.     long        seekpos;    /* seek position of the new block */
  139.     unsigned short    physical;    /* physical block number */
  140.  
  141.     /* if its empty (an orphan blkadd() maybe?) then make it dirty */
  142.     if (this->logical && !*this->buf.c)
  143.     {
  144.         blkdirty(&this->buf);
  145.     }
  146.  
  147.     /* if it's an empty buffer or a clean version is on disk, quit */
  148.     if (!this->logical || hdr.n[this->logical] && !this->dirty)
  149.     {
  150.         return;
  151.     }
  152.  
  153.     /* find a free place in the file */
  154. #ifndef NO_RECYCLE
  155.     seekpos = allocate();
  156.     lseek(tmpfd, seekpos, 0);
  157. #else
  158.     seekpos = lseek(tmpfd, 0L, 2);
  159. #endif
  160.     physical = seekpos / BLKSIZE;
  161.  
  162.     /* put the block there */
  163.     if (write(tmpfd, this->buf.c, BLKSIZE) != BLKSIZE)
  164.     {
  165.         msg("Trouble writing to tmp file");
  166.     }
  167.     this->dirty = FALSE;
  168.  
  169.     /* update the header so it knows we put it there */
  170.     hdr.n[this->logical] = physical;
  171. }
  172.  
  173.  
  174. /* This function sets a block's "dirty" flag or deletes empty blocks */
  175. blkdirty(bp)
  176.     BLK    *bp;    /* buffer returned by blkget() */
  177. {
  178.     register int     i, j;
  179.     register char    *scan;
  180.     register int    k;
  181.  
  182.     /* find the buffer */
  183.     for (i = 0; i < NBUFS && bp != &blk[i].buf; i++)
  184.     {
  185.     }
  186. #ifdef DEBUG
  187.     if (i >= NBUFS)
  188.     {
  189.         msg("blkdirty() called with unknown buffer at 0x%lx", bp);
  190.         return;
  191.     }
  192.     if (blk[i].logical == 0)
  193.     {
  194.         msg("blkdirty called with freed buffer");
  195.         return;
  196.     }
  197. #endif
  198.  
  199.     /* if this block ends with line# INFINITY, then it must have been
  200.      * allocated unnecessarily during tmpstart().  Forget it.
  201.      */
  202.     if (lnum[blk[i].logical] == INFINITY)
  203.     {
  204. #ifdef DEBUG
  205.         if (blk[i].buf.c[0])
  206.         {
  207.             msg("bkldirty called with non-empty extra BLK");
  208.         }
  209. #endif
  210.         blk[i].logical = 0;
  211.         blk[i].dirty = FALSE;
  212.         return;
  213.     }
  214.  
  215.     /* count lines in this block */
  216.     for (j = 0, scan = bp->c; *scan && scan < bp->c + BLKSIZE; scan++)
  217.     {
  218.         if (*scan == '\n')
  219.         {
  220.             j++;
  221.         }
  222.     }
  223.  
  224.     /* adjust lnum, if necessary */
  225.     k = blk[i].logical;
  226.     j += (lnum[k - 1] - lnum[k]);
  227.     if (j != 0)
  228.     {
  229.         nlines += j;
  230.         while (k < MAXBLKS && lnum[k] != INFINITY)
  231.         {
  232.             lnum[k++] += j;
  233.         }
  234.     }
  235.  
  236.     /* if it still has text, mark it as dirty */
  237.     if (*bp->c)
  238.     {
  239.         blk[i].dirty = TRUE;
  240.     }
  241.     else /* empty block, so delete it */
  242.     {
  243.         /* adjust the cache */
  244.         k = blk[i].logical;
  245.         for (j = 0; j < NBUFS; j++)
  246.         {
  247.             if (blk[j].logical >= k)
  248.             {
  249.                 blk[j].logical--;
  250.             }
  251.         }
  252.  
  253.         /* delete it from hdr.n[] and lnum[] */
  254.         blk[i].logical = 0;
  255.         blk[i].dirty = FALSE;
  256.         while (k < MAXBLKS - 1)
  257.         {
  258.             hdr.n[k] = hdr.n[k + 1];
  259.             lnum[k] = lnum[k + 1];
  260.             k++;
  261.         }
  262.         hdr.n[MAXBLKS - 1] = 0;
  263.         lnum[MAXBLKS - 1] = INFINITY;
  264.     }
  265. }
  266.  
  267.  
  268. /* insert a new block into hdr, and adjust the cache */
  269. BLK *blkadd(logical)
  270.     int        logical;    /* where to insert the new block */
  271. {
  272.     register int    i;
  273.  
  274.     /* adjust hdr and lnum[] */
  275.     for (i = MAXBLKS - 1; i > logical; i--)
  276.     {
  277.         hdr.n[i] = hdr.n[i - 1];
  278.         lnum[i] = lnum[i - 1];
  279.     }
  280.     hdr.n[logical] = 0;
  281.     lnum[logical] = lnum[logical - 1];
  282.  
  283.     /* adjust the cache */
  284.     for (i = 0; i < NBUFS; i++)
  285.     {
  286.         if (blk[i].logical >= logical)
  287.         {
  288.             blk[i].logical++;
  289.         }
  290.     }
  291.  
  292.     /* return the new block, via blkget() */
  293.     return blkget(logical);
  294. }
  295.  
  296.  
  297. /* This function forces all dirty blocks out to disk */
  298. blksync()
  299. {
  300.     int    i;
  301.  
  302.     for (i = 0; i < NBUFS; i++)
  303.     {
  304.         /* blk[i].dirty = TRUE; */
  305.         blkflush(&blk[i]);
  306.     }
  307.     if (*o_sync)
  308.     {
  309.         sync();
  310.     }
  311. }
  312.  
  313. /*------------------------------------------------------------------------*/
  314.  
  315. static MARK    undocurs;    /* where the cursor should go if undone */
  316. static long    oldnlines;
  317. static long    oldlnum[MAXBLKS];
  318.  
  319.  
  320. /* This function should be called before each command that changes the text.
  321.  * It defines the state that undo() will reset the file to.
  322.  */
  323. beforedo(undo)
  324.     int        undo;    /* boolean: is this for an undo? */
  325. {
  326.     register int    i;
  327.     register long    l;
  328.  
  329.     /* if this is a nested call to beforedo, quit! Use larger context */
  330.     if (b4cnt++ > 0)
  331.     {
  332.         return;
  333.     }
  334.  
  335.     /* force all block buffers to disk */
  336.     blksync();
  337.  
  338. #ifndef NO_RECYCLE
  339.     /* perform garbage collection on blocks from tmp file */
  340.     garbage();
  341. #endif
  342.  
  343.     /* force the header out to disk */
  344.     lseek(tmpfd, 0L, 0);
  345.     if (write(tmpfd, hdr.c, BLKSIZE) != BLKSIZE)
  346.     {
  347.         msg("Trouble writing header to tmp file ");
  348.     }
  349.  
  350.     /* set the file's "dirty" flag */
  351.     setflag(file, MODIFIED);
  352.  
  353.     /* copy or swap oldnlines <--> nlines, oldlnum <--> lnum */
  354.     if (undo)
  355.     {
  356.         for (i = 0; i < MAXBLKS; i++)
  357.         {
  358.             l = lnum[i];
  359.             lnum[i] = oldlnum[i];
  360.             oldlnum[i] = l;
  361.         }
  362.         l = nlines;
  363.         nlines = oldnlines;
  364.         oldnlines = l;
  365.     }
  366.     else
  367.     {
  368.         for (i = 0; i < MAXBLKS; i++)
  369.         {
  370.             oldlnum[i] = lnum[i];
  371.         }
  372.         oldnlines = nlines;
  373.     }
  374.  
  375.     /* save the cursor position */
  376.     undocurs = cursor;
  377.  
  378.     /* upon return, the calling function continues and makes changes... */
  379. }
  380.  
  381. /* This function marks the end of a (nested?) change to the file */
  382. afterdo()
  383. {
  384.     if (--b4cnt)
  385.     {
  386.         return;
  387.     }
  388.  
  389.     /* make sure the cursor wasn't left stranded in deleted text */
  390.     if (markline(cursor) > nlines)
  391.     {
  392.         cursor = MARK_LAST;
  393.     }
  394.     /* NOTE: it is still possible that markidx(cursor) is after the
  395.      * end of a line, so the Vi mode will have to take care of that
  396.      * itself */
  397. }
  398.  
  399. /* This function cuts short the current set of changes.  It is called after
  400.  * a SIGINT.
  401.  */
  402. abortdo()
  403. {
  404.     /* if we aren't in the middle of a change, do nothing */
  405.     if (!b4cnt)
  406.     {
  407.         return;
  408.     }
  409.  
  410.     /* reset the b4cnt counter */
  411.     b4cnt = 0;
  412.  
  413.     /* in visual mode, the screen is probably screwed up */
  414.     if (mode == MODE_COLON)
  415.     {
  416.         mode = MODE_VI;
  417.     }
  418.     if (mode == MODE_VI)
  419.     {
  420.         redraw(MARK_UNSET, FALSE);
  421.     }
  422. }
  423.     
  424. /* This function discards all changes made since the last call to beforedo() */
  425. undo()
  426. {
  427.     BLK        oldhdr;
  428.  
  429.     /* if beforedo() has never been run, fail */
  430.     if (!tstflag(file, MODIFIED))
  431.     {
  432.         msg("You haven't modified this file yet.");
  433.         return;
  434.     }
  435.  
  436.     /* read the old header form the tmp file */
  437.     lseek(tmpfd, 0L, 0);
  438.     if (read(tmpfd, oldhdr.c, BLKSIZE) != BLKSIZE)
  439.     {
  440.         msg("Trouble rereading the old header from tmp file");
  441.     }
  442.  
  443.     /* "do" the changed version, so we can undo the "undo" */
  444.     cursor = undocurs;
  445.     beforedo(TRUE);
  446.     afterdo();
  447.  
  448.     /* wipe out the block buffers - we can't assume they're correct */
  449.     blkinit();
  450.  
  451.     /* use the old header -- and therefore the old text blocks */
  452.     hdr = oldhdr;
  453.  
  454.     /* This is a change */
  455.     changes++;
  456. }
  457.