blk.c

Go to the documentation of this file.
00001 /* blk.c */
00002 
00003 /* Author:
00004  *      Steve Kirkendall
00005  *      14407 SW Teal Blvd. #C
00006  *      Beaverton, OR 97005
00007  *      kirkenda@cs.pdx.edu
00008  */
00009 
00010 
00011 /* This file contains the functions that get/put blocks from the temp file.
00012  * It also contains the "do" and "undo" functions.
00013  */
00014 
00015 #include "config.h"
00016 #include "vi.h"
00017 
00018 #ifndef NBUFS
00019 # define NBUFS  5               /* must be at least 3 -- more is better */
00020 #endif
00021 
00022 
00023 /*------------------------------------------------------------------------*/
00024 
00025 BLK             hdr;            /* buffer for the header block */
00026 
00027 static int      b4cnt;          /* used to count context of beforedo/afterdo */
00028 static struct _blkbuf
00029 {
00030         BLK             buf;            /* contents of a text block */
00031         unsigned short  logical;        /* logical block number */
00032         int             dirty;          /* must the buffer be rewritten? */
00033 }
00034                 blk[NBUFS],     /* buffers for text[?] blocks */
00035                 *toonew,        /* buffer which shouldn't be recycled yet */
00036                 *newtoo,        /* another buffer which should be recycled */
00037                 *recycle = blk; /* next block to be recycled */
00038 
00039 
00040 
00041 
00042 
00043 /* This function wipes out all buffers */
00044 void blkinit()
00045 {
00046         int     i;
00047 
00048         for (i = 0; i < NBUFS; i++)
00049         {
00050                 blk[i].logical = 0;
00051                 blk[i].dirty = FALSE;
00052         }
00053         for (i = 0; i < MAXBLKS; i++)
00054         {
00055                 hdr.n[i] = 0;
00056         }
00057 }
00058 
00059 /* This function allocates a buffer and fills it with a given block's text */
00060 BLK *blkget(logical)
00061         int     logical;        /* logical block number to fetch */
00062 {
00063         REG struct _blkbuf      *this;  /* used to step through blk[] */
00064         REG int i;
00065 
00066         /* if logical is 0, just return the hdr buffer */
00067         if (logical == 0)
00068         {
00069                 return &hdr;
00070         }
00071 
00072         /* see if we have that block in mem already */
00073         for (this = blk; this < &blk[NBUFS]; this++)
00074         {
00075                 if (this->logical == logical)
00076                 {
00077                         newtoo = toonew;
00078                         toonew = this;
00079                         return &this->buf;
00080                 }
00081         }
00082 
00083         /* choose a block to be recycled */
00084         do
00085         {
00086                 this = recycle++;
00087                 if (recycle == &blk[NBUFS])
00088                 {
00089                         recycle = blk;
00090                 }
00091         } while (this == toonew || this == newtoo);
00092 
00093         /* if it contains a block, flush that block */
00094         blkflush(this);
00095 
00096         /* fill this buffer with the desired block */
00097         this->logical = logical;
00098         if (hdr.n[logical])
00099         {
00100                 /* it has been used before - fill it from tmp file */
00101                 lseek(tmpfd, (long)hdr.n[logical] * (long)BLKSIZE, 0);
00102                 if (read(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE)
00103                 {
00104                         msg("Error reading back from tmp file!");
00105                 }
00106         }
00107         else
00108         {
00109                 /* it is new - zero it */
00110                 for (i = 0; i < BLKSIZE; i++)
00111                 {
00112                         this->buf.c[i] = 0;
00113                 }
00114         }
00115 
00116         /* This isn't really a change, but it does potentially invalidate
00117          * the kinds of shortcuts that the "changes" variable is supposed
00118          * to protect us from... so count it as a change.
00119          */
00120         changes++;
00121 
00122         /* mark it as being "not dirty" */
00123         this->dirty = 0;
00124 
00125         /* return it */
00126         newtoo = toonew;
00127         toonew = this;
00128         return &this->buf;
00129 }
00130 
00131 
00132 
00133 /* This function writes a block out to the temporary file */
00134 void blkflush(this)
00135         REG struct _blkbuf      *this;  /* the buffer to flush */
00136 {
00137         long            seekpos;        /* seek position of the new block */
00138         unsigned short  physical;       /* physical block number */
00139 
00140         /* if its empty (an orphan blkadd() maybe?) then make it dirty */
00141         if (this->logical && !*this->buf.c)
00142         {
00143                 blkdirty(&this->buf);
00144         }
00145 
00146         /* if it's an empty buffer or a clean version is on disk, quit */
00147         if (!this->logical || hdr.n[this->logical] && !this->dirty)
00148         {
00149                 return;
00150         }
00151 
00152         /* find a free place in the file */
00153 #ifndef NO_RECYCLE
00154         seekpos = allocate();
00155         lseek(tmpfd, seekpos, 0);
00156 #else
00157         seekpos = lseek(tmpfd, 0L, 2);
00158 #endif
00159         physical = seekpos / BLKSIZE;
00160 
00161         /* put the block there */
00162         if (write(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE)
00163         {
00164                 msg("Trouble writing to tmp file");
00165         }
00166         this->dirty = FALSE;
00167 
00168         /* update the header so it knows we put it there */
00169         hdr.n[this->logical] = physical;
00170 }
00171 
00172 
00173 /* This function sets a block's "dirty" flag or deletes empty blocks */
00174 void blkdirty(bp)
00175         BLK     *bp;    /* buffer returned by blkget() */
00176 {
00177         REG int         i, j;
00178         REG char        *scan;
00179         REG int         k;
00180 
00181         /* find the buffer */
00182         for (i = 0; i < NBUFS && bp != &blk[i].buf; i++)
00183         {
00184         }
00185 #ifdef DEBUG
00186         if (i >= NBUFS)
00187         {
00188                 msg("blkdirty() called with unknown buffer at 0x%lx", bp);
00189                 return;
00190         }
00191         if (blk[i].logical == 0)
00192         {
00193                 msg("blkdirty called with freed buffer");
00194                 return;
00195         }
00196 #endif
00197 
00198         /* if this block ends with line# INFINITY, then it must have been
00199          * allocated unnecessarily during tmpstart().  Forget it.
00200          */
00201         if (lnum[blk[i].logical] == INFINITY)
00202         {
00203 #ifdef DEBUG
00204                 if (blk[i].buf.c[0])
00205                 {
00206                         msg("bkldirty called with non-empty extra BLK");
00207                 }
00208 #endif
00209                 blk[i].logical = 0;
00210                 blk[i].dirty = FALSE;
00211                 return;
00212         }
00213 
00214         /* count lines in this block */
00215         for (j = 0, scan = bp->c; *scan && scan < bp->c + BLKSIZE; scan++)
00216         {
00217                 if (*scan == '\n')
00218                 {
00219                         j++;
00220                 }
00221         }
00222 
00223         /* adjust lnum, if necessary */
00224         k = blk[i].logical;
00225         j += (lnum[k - 1] - lnum[k]);
00226         if (j != 0)
00227         {
00228                 nlines += j;
00229                 while (k < MAXBLKS && lnum[k] != INFINITY)
00230                 {
00231                         lnum[k++] += j;
00232                 }
00233         }
00234 
00235         /* if it still has text, mark it as dirty */
00236         if (*bp->c)
00237         {
00238                 blk[i].dirty = TRUE;
00239         }
00240         else /* empty block, so delete it */
00241         {
00242                 /* adjust the cache */
00243                 k = blk[i].logical;
00244                 for (j = 0; j < NBUFS; j++)
00245                 {
00246                         if (blk[j].logical >= k)
00247                         {
00248                                 blk[j].logical--;
00249                         }
00250                 }
00251 
00252                 /* delete it from hdr.n[] and lnum[] */
00253                 blk[i].logical = 0;
00254                 blk[i].dirty = FALSE;
00255                 while (k < MAXBLKS - 1)
00256                 {
00257                         hdr.n[k] = hdr.n[k + 1];
00258                         lnum[k] = lnum[k + 1];
00259                         k++;
00260                 }
00261                 hdr.n[MAXBLKS - 1] = 0;
00262                 lnum[MAXBLKS - 1] = INFINITY;
00263         }
00264 }
00265 
00266 
00267 /* insert a new block into hdr, and adjust the cache */
00268 BLK *blkadd(logical)
00269         int     logical;        /* where to insert the new block */
00270 {
00271         REG int i;
00272 
00273         /* adjust hdr and lnum[] */
00274         for (i = MAXBLKS - 1; i > logical; i--)
00275         {
00276                 hdr.n[i] = hdr.n[i - 1];
00277                 lnum[i] = lnum[i - 1];
00278         }
00279         hdr.n[logical] = 0;
00280         lnum[logical] = lnum[logical - 1];
00281 
00282         /* adjust the cache */
00283         for (i = 0; i < NBUFS; i++)
00284         {
00285                 if (blk[i].logical >= logical)
00286                 {
00287                         blk[i].logical++;
00288                 }
00289         }
00290 
00291         /* return the new block, via blkget() */
00292         return blkget(logical);
00293 }
00294 
00295 
00296 /* This function forces all dirty blocks out to disk */
00297 void blksync()
00298 {
00299         int     i;
00300 
00301         for (i = 0; i < NBUFS; i++)
00302         {
00303                 /* blk[i].dirty = TRUE; */
00304                 blkflush(&blk[i]);
00305         }
00306         if (*o_sync)
00307         {
00308                 sync();
00309         }
00310 }
00311 
00312 /*------------------------------------------------------------------------*/
00313 
00314 static MARK     undocurs;       /* where the cursor should go if undone */
00315 static long     oldnlines;
00316 static long     oldlnum[MAXBLKS];
00317 
00318 
00319 /* This function should be called before each command that changes the text.
00320  * It defines the state that undo() will reset the file to.
00321  */
00322 void beforedo(forundo)
00323         int             forundo;        /* boolean: is this for an undo? */
00324 {
00325         REG int         i;
00326         REG long        l;
00327 
00328         /* if this is a nested call to beforedo, quit! Use larger context */
00329         if (b4cnt++ > 0)
00330         {
00331                 return;
00332         }
00333 
00334         /* force all block buffers to disk */
00335         blksync();
00336 
00337 #ifndef NO_RECYCLE
00338         /* perform garbage collection on blocks from tmp file */
00339         garbage();
00340 #endif
00341 
00342         /* force the header out to disk */
00343         lseek(tmpfd, 0L, 0);
00344         if (write(tmpfd, hdr.c, (unsigned)BLKSIZE) != BLKSIZE)
00345         {
00346                 msg("Trouble writing header to tmp file ");
00347         }
00348 
00349         /* copy or swap oldnlines <--> nlines, oldlnum <--> lnum */
00350         if (forundo)
00351         {
00352                 for (i = 0; i < MAXBLKS; i++)
00353                 {
00354                         l = lnum[i];
00355                         lnum[i] = oldlnum[i];
00356                         oldlnum[i] = l;
00357                 }
00358                 l = nlines;
00359                 nlines = oldnlines;
00360                 oldnlines = l;
00361         }
00362         else
00363         {
00364                 for (i = 0; i < MAXBLKS; i++)
00365                 {
00366                         oldlnum[i] = lnum[i];
00367                 }
00368                 oldnlines = nlines;
00369         }
00370 
00371         /* save the cursor position */
00372         undocurs = cursor;
00373 
00374         /* upon return, the calling function continues and makes changes... */
00375 }
00376 
00377 /* This function marks the end of a (nested?) change to the file */
00378 void afterdo()
00379 {
00380         if (--b4cnt)
00381         {
00382                 /* after abortdo(), b4cnt may decribe nested beforedo/afterdo
00383                  * pairs incorrectly.  If it is decremented to often, then
00384                  * keep b4cnt sane but don't do anything else.
00385                  */
00386                 if (b4cnt < 0)
00387                         b4cnt = 0;
00388 
00389                 return;
00390         }
00391 
00392         /* make sure the cursor wasn't left stranded in deleted text */
00393         if (markline(cursor) > nlines)
00394         {
00395                 cursor = MARK_LAST;
00396         }
00397         /* NOTE: it is still possible that markidx(cursor) is after the
00398          * end of a line, so the Vi mode will have to take care of that
00399          * itself */
00400 
00401         /* if a significant change has been made to this file, then set the
00402          * MODIFIED flag.
00403          */
00404         if (significant)
00405         {
00406                 setflag(file, MODIFIED);
00407                 setflag(file, UNDOABLE);
00408         }       
00409 }
00410 
00411 /* This function cuts short the current set of changes.  It is called after
00412  * a SIGINT.
00413  */
00414 void abortdo()
00415 {
00416         /* finish the operation immediately. */
00417         if (b4cnt > 0)
00418         {
00419                 b4cnt = 1;
00420                 afterdo();
00421         }
00422 
00423         /* in visual mode, the screen is probably screwed up */
00424         if (mode == MODE_COLON)
00425         {
00426                 mode = MODE_VI;
00427         }
00428         if (mode == MODE_VI)
00429         {
00430                 redraw(MARK_UNSET, FALSE);
00431         }
00432 }
00433 
00434 /* This function discards all changes made since the last call to beforedo() */
00435 int undo()
00436 {
00437         BLK             oldhdr;
00438 
00439         /* if beforedo() has never been run, fail */
00440         if (!tstflag(file, UNDOABLE))
00441         {
00442                 msg("You haven't modified this file yet.");
00443                 return FALSE;
00444         }
00445 
00446         /* read the old header form the tmp file */
00447         lseek(tmpfd, 0L, 0);
00448         if (read(tmpfd, oldhdr.c, (unsigned)BLKSIZE) != BLKSIZE)
00449         {
00450                 msg("Trouble rereading the old header from tmp file");
00451         }
00452 
00453         /* "do" the changed version, so we can undo the "undo" */
00454         cursor = undocurs;
00455         beforedo(TRUE);
00456         afterdo();
00457 
00458         /* wipe out the block buffers - we can't assume they're correct */
00459         blkinit();
00460 
00461         /* use the old header -- and therefore the old text blocks */
00462         hdr = oldhdr;
00463 
00464         /* This is a change */
00465         significant = TRUE;
00466         changes++;
00467 
00468         return TRUE;
00469 }

Generated on Fri Apr 14 22:56:52 2006 for minix by  doxygen 1.4.6