home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / APPS / elvis_1.4.tar.Z / elvis_1.4.tar / blk.c < prev    next >
C/C++ Source or Header  |  1990-12-06  |  9KB  |  469 lines

  1. /* blk.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    14407 SW Teal Blvd. #C
  6.  *    Beaverton, OR 97005
  7.  *    kirkenda@cs.pdx.edu
  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. void 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.     REG struct _blkbuf    *this;    /* used to step through blk[] */
  65.     REG 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, (unsigned)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. void blkflush(this)
  136.     REG 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, (unsigned)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. void blkdirty(bp)
  176.     BLK    *bp;    /* buffer returned by blkget() */
  177. {
  178.     REG int        i, j;
  179.     REG char    *scan;
  180.     REG 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.     REG 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. void 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. void beforedo(forundo)
  324.     int        forundo;    /* boolean: is this for an undo? */
  325. {
  326.     REG int        i;
  327.     REG 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, (unsigned)BLKSIZE) != BLKSIZE)
  346.     {
  347.         msg("Trouble writing header to tmp file ");
  348.     }
  349.  
  350.     /* copy or swap oldnlines <--> nlines, oldlnum <--> lnum */
  351.     if (forundo)
  352.     {
  353.         for (i = 0; i < MAXBLKS; i++)
  354.         {
  355.             l = lnum[i];
  356.             lnum[i] = oldlnum[i];
  357.             oldlnum[i] = l;
  358.         }
  359.         l = nlines;
  360.         nlines = oldnlines;
  361.         oldnlines = l;
  362.     }
  363.     else
  364.     {
  365.         for (i = 0; i < MAXBLKS; i++)
  366.         {
  367.             oldlnum[i] = lnum[i];
  368.         }
  369.         oldnlines = nlines;
  370.     }
  371.  
  372.     /* save the cursor position */
  373.     undocurs = cursor;
  374.  
  375.     /* upon return, the calling function continues and makes changes... */
  376. }
  377.  
  378. /* This function marks the end of a (nested?) change to the file */
  379. void afterdo()
  380. {
  381.     if (--b4cnt)
  382.     {
  383.         /* after abortdo(), b4cnt may decribe nested beforedo/afterdo
  384.          * pairs incorrectly.  If it is decremented to often, then
  385.          * keep b4cnt sane but don't do anything else.
  386.          */
  387.         if (b4cnt < 0)
  388.             b4cnt = 0;
  389.  
  390.         return;
  391.     }
  392.  
  393.     /* make sure the cursor wasn't left stranded in deleted text */
  394.     if (markline(cursor) > nlines)
  395.     {
  396.         cursor = MARK_LAST;
  397.     }
  398.     /* NOTE: it is still possible that markidx(cursor) is after the
  399.      * end of a line, so the Vi mode will have to take care of that
  400.      * itself */
  401.  
  402.     /* if a significant change has been made to this file, then set the
  403.      * MODIFIED flag.
  404.      */
  405.     if (significant)
  406.     {
  407.         setflag(file, MODIFIED);
  408.     }    
  409. }
  410.  
  411. /* This function cuts short the current set of changes.  It is called after
  412.  * a SIGINT.
  413.  */
  414. void abortdo()
  415. {
  416.     /* finish the operation immediately. */
  417.     if (b4cnt > 0)
  418.     {
  419.         b4cnt = 1;
  420.         afterdo();
  421.     }
  422.  
  423.     /* in visual mode, the screen is probably screwed up */
  424.     if (mode == MODE_COLON)
  425.     {
  426.         mode = MODE_VI;
  427.     }
  428.     if (mode == MODE_VI)
  429.     {
  430.         redraw(MARK_UNSET, FALSE);
  431.     }
  432. }
  433.  
  434. /* This function discards all changes made since the last call to beforedo() */
  435. int undo()
  436. {
  437.     BLK        oldhdr;
  438.  
  439.     /* if beforedo() has never been run, fail */
  440.     if (!tstflag(file, MODIFIED))
  441.     {
  442.         msg("You haven't modified this file yet.");
  443.         return FALSE;
  444.     }
  445.  
  446.     /* read the old header form the tmp file */
  447.     lseek(tmpfd, 0L, 0);
  448.     if (read(tmpfd, oldhdr.c, (unsigned)BLKSIZE) != BLKSIZE)
  449.     {
  450.         msg("Trouble rereading the old header from tmp file");
  451.     }
  452.  
  453.     /* "do" the changed version, so we can undo the "undo" */
  454.     cursor = undocurs;
  455.     beforedo(TRUE);
  456.     afterdo();
  457.  
  458.     /* wipe out the block buffers - we can't assume they're correct */
  459.     blkinit();
  460.  
  461.     /* use the old header -- and therefore the old text blocks */
  462.     hdr = oldhdr;
  463.  
  464.     /* This is a change */
  465.     changes++;
  466.  
  467.     return TRUE;
  468. }
  469.