home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / elvis184.zip / src / blk.c < prev    next >
C/C++ Source or Header  |  1995-05-26  |  10KB  |  498 lines

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