home *** CD-ROM | disk | FTP | other *** search
- /* blk.c */
-
- /* Author:
- * Steve Kirkendall
- * 14407 SW Teal Blvd. #C
- * Beaverton, OR 97005
- * kirkenda@cs.pdx.edu
- */
-
-
- /* This file contains the functions that get/put blocks from the temp file.
- * It also contains the "do" and "undo" functions.
- */
-
- #include "config.h"
- #include "vi.h"
-
- #ifndef NBUFS
- # define NBUFS 5 /* must be at least 3 -- more is better */
- #endif
-
- extern long lseek();
-
- /*------------------------------------------------------------------------*/
-
- BLK hdr; /* buffer for the header block */
-
- static int b4cnt; /* used to count context of beforedo/afterdo */
- static struct _blkbuf
- {
- BLK buf; /* contents of a text block */
- unsigned short logical; /* logical block number */
- int dirty; /* must the buffer be rewritten? */
- }
- blk[NBUFS], /* buffers for text[?] blocks */
- *toonew, /* buffer which shouldn't be recycled yet */
- *newtoo, /* another buffer which should be recycled */
- *recycle = blk; /* next block to be recycled */
-
-
-
-
-
- /* This function wipes out all buffers */
- void blkinit()
- {
- int i;
-
- for (i = 0; i < NBUFS; i++)
- {
- blk[i].logical = 0;
- blk[i].dirty = FALSE;
- }
- for (i = 0; i < MAXBLKS; i++)
- {
- hdr.n[i] = 0;
- }
- }
-
- /* This function allocates a buffer and fills it with a given block's text */
- BLK *blkget(logical)
- int logical; /* logical block number to fetch */
- {
- REG struct _blkbuf *this; /* used to step through blk[] */
- REG int i;
-
- /* if logical is 0, just return the hdr buffer */
- if (logical == 0)
- {
- return &hdr;
- }
-
- /* see if we have that block in mem already */
- for (this = blk; this < &blk[NBUFS]; this++)
- {
- if (this->logical == logical)
- {
- newtoo = toonew;
- toonew = this;
- return &this->buf;
- }
- }
-
- /* choose a block to be recycled */
- do
- {
- this = recycle++;
- if (recycle == &blk[NBUFS])
- {
- recycle = blk;
- }
- } while (this == toonew || this == newtoo);
-
- /* if it contains a block, flush that block */
- blkflush(this);
-
- /* fill this buffer with the desired block */
- this->logical = logical;
- if (hdr.n[logical])
- {
- /* it has been used before - fill it from tmp file */
- lseek(tmpfd, (long)hdr.n[logical] * (long)BLKSIZE, 0);
- if (read(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE)
- {
- msg("Error reading back from tmp file!");
- }
- }
- else
- {
- /* it is new - zero it */
- for (i = 0; i < BLKSIZE; i++)
- {
- this->buf.c[i] = 0;
- }
- }
-
- /* This isn't really a change, but it does potentially invalidate
- * the kinds of shortcuts that the "changes" variable is supposed
- * to protect us from... so count it as a change.
- */
- changes++;
-
- /* mark it as being "not dirty" */
- this->dirty = 0;
-
- /* return it */
- newtoo = toonew;
- toonew = this;
- return &this->buf;
- }
-
-
-
- /* This function writes a block out to the temporary file */
- void blkflush(this)
- REG struct _blkbuf *this; /* the buffer to flush */
- {
- long seekpos; /* seek position of the new block */
- unsigned short physical; /* physical block number */
-
- /* if its empty (an orphan blkadd() maybe?) then make it dirty */
- if (this->logical && !*this->buf.c)
- {
- blkdirty(&this->buf);
- }
-
- /* if it's an empty buffer or a clean version is on disk, quit */
- if (!this->logical || hdr.n[this->logical] && !this->dirty)
- {
- return;
- }
-
- /* find a free place in the file */
- #ifndef NO_RECYCLE
- seekpos = allocate();
- lseek(tmpfd, seekpos, 0);
- #else
- seekpos = lseek(tmpfd, 0L, 2);
- #endif
- physical = seekpos / BLKSIZE;
-
- /* put the block there */
- if (write(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE)
- {
- msg("Trouble writing to tmp file");
- }
- this->dirty = FALSE;
-
- /* update the header so it knows we put it there */
- hdr.n[this->logical] = physical;
- }
-
-
- /* This function sets a block's "dirty" flag or deletes empty blocks */
- void blkdirty(bp)
- BLK *bp; /* buffer returned by blkget() */
- {
- REG int i, j;
- REG char *scan;
- REG int k;
-
- /* find the buffer */
- for (i = 0; i < NBUFS && bp != &blk[i].buf; i++)
- {
- }
- #ifdef DEBUG
- if (i >= NBUFS)
- {
- msg("blkdirty() called with unknown buffer at 0x%lx", bp);
- return;
- }
- if (blk[i].logical == 0)
- {
- msg("blkdirty called with freed buffer");
- return;
- }
- #endif
-
- /* if this block ends with line# INFINITY, then it must have been
- * allocated unnecessarily during tmpstart(). Forget it.
- */
- if (lnum[blk[i].logical] == INFINITY)
- {
- #ifdef DEBUG
- if (blk[i].buf.c[0])
- {
- msg("bkldirty called with non-empty extra BLK");
- }
- #endif
- blk[i].logical = 0;
- blk[i].dirty = FALSE;
- return;
- }
-
- /* count lines in this block */
- for (j = 0, scan = bp->c; *scan && scan < bp->c + BLKSIZE; scan++)
- {
- if (*scan == '\n')
- {
- j++;
- }
- }
-
- /* adjust lnum, if necessary */
- k = blk[i].logical;
- j += (lnum[k - 1] - lnum[k]);
- if (j != 0)
- {
- nlines += j;
- while (k < MAXBLKS && lnum[k] != INFINITY)
- {
- lnum[k++] += j;
- }
- }
-
- /* if it still has text, mark it as dirty */
- if (*bp->c)
- {
- blk[i].dirty = TRUE;
- }
- else /* empty block, so delete it */
- {
- /* adjust the cache */
- k = blk[i].logical;
- for (j = 0; j < NBUFS; j++)
- {
- if (blk[j].logical >= k)
- {
- blk[j].logical--;
- }
- }
-
- /* delete it from hdr.n[] and lnum[] */
- blk[i].logical = 0;
- blk[i].dirty = FALSE;
- while (k < MAXBLKS - 1)
- {
- hdr.n[k] = hdr.n[k + 1];
- lnum[k] = lnum[k + 1];
- k++;
- }
- hdr.n[MAXBLKS - 1] = 0;
- lnum[MAXBLKS - 1] = INFINITY;
- }
- }
-
-
- /* insert a new block into hdr, and adjust the cache */
- BLK *blkadd(logical)
- int logical; /* where to insert the new block */
- {
- REG int i;
-
- /* adjust hdr and lnum[] */
- for (i = MAXBLKS - 1; i > logical; i--)
- {
- hdr.n[i] = hdr.n[i - 1];
- lnum[i] = lnum[i - 1];
- }
- hdr.n[logical] = 0;
- lnum[logical] = lnum[logical - 1];
-
- /* adjust the cache */
- for (i = 0; i < NBUFS; i++)
- {
- if (blk[i].logical >= logical)
- {
- blk[i].logical++;
- }
- }
-
- /* return the new block, via blkget() */
- return blkget(logical);
- }
-
-
- /* This function forces all dirty blocks out to disk */
- void blksync()
- {
- int i;
-
- for (i = 0; i < NBUFS; i++)
- {
- /* blk[i].dirty = TRUE; */
- blkflush(&blk[i]);
- }
- if (*o_sync)
- {
- #ifndef DF_POSIX
- sync();
- #endif
- }
- }
-
- /*------------------------------------------------------------------------*/
-
- static MARK undocurs; /* where the cursor should go if undone */
- static long oldnlines;
- static long oldlnum[MAXBLKS];
-
-
- /* This function should be called before each command that changes the text.
- * It defines the state that undo() will reset the file to.
- */
- void beforedo(forundo)
- int forundo; /* boolean: is this for an undo? */
- {
- REG int i;
- REG long l;
-
- /* if this is a nested call to beforedo, quit! Use larger context */
- if (b4cnt++ > 0)
- {
- return;
- }
-
- /* force all block buffers to disk */
- blksync();
-
- #ifndef NO_RECYCLE
- /* perform garbage collection on blocks from tmp file */
- garbage();
- #endif
-
- /* force the header out to disk */
- lseek(tmpfd, 0L, 0);
- if (write(tmpfd, hdr.c, (unsigned)BLKSIZE) != BLKSIZE)
- {
- msg("Trouble writing header to tmp file ");
- }
-
- /* copy or swap oldnlines <--> nlines, oldlnum <--> lnum */
- if (forundo)
- {
- for (i = 0; i < MAXBLKS; i++)
- {
- l = lnum[i];
- lnum[i] = oldlnum[i];
- oldlnum[i] = l;
- }
- l = nlines;
- nlines = oldnlines;
- oldnlines = l;
- }
- else
- {
- for (i = 0; i < MAXBLKS; i++)
- {
- oldlnum[i] = lnum[i];
- }
- oldnlines = nlines;
- }
-
- /* save the cursor position */
- undocurs = cursor;
-
- /* upon return, the calling function continues and makes changes... */
- }
-
- /* This function marks the end of a (nested?) change to the file */
- void afterdo()
- {
- if (--b4cnt)
- {
- /* after abortdo(), b4cnt may decribe nested beforedo/afterdo
- * pairs incorrectly. If it is decremented to often, then
- * keep b4cnt sane but don't do anything else.
- */
- if (b4cnt < 0)
- b4cnt = 0;
-
- return;
- }
-
- /* make sure the cursor wasn't left stranded in deleted text */
- if (markline(cursor) > nlines)
- {
- cursor = MARK_LAST;
- }
- /* NOTE: it is still possible that markidx(cursor) is after the
- * end of a line, so the Vi mode will have to take care of that
- * itself */
-
- /* if a significant change has been made to this file, then set the
- * MODIFIED flag.
- */
- if (significant)
- {
- setflag(file, MODIFIED);
- }
- }
-
- /* This function cuts short the current set of changes. It is called after
- * a SIGINT.
- */
- void abortdo()
- {
- /* finish the operation immediately. */
- if (b4cnt > 0)
- {
- b4cnt = 1;
- afterdo();
- }
-
- /* in visual mode, the screen is probably screwed up */
- if (mode == MODE_COLON)
- {
- mode = MODE_VI;
- }
- if (mode == MODE_VI)
- {
- redraw(MARK_UNSET, FALSE);
- }
- }
-
- /* This function discards all changes made since the last call to beforedo() */
- int undo()
- {
- BLK oldhdr;
-
- /* if beforedo() has never been run, fail */
- if (!tstflag(file, MODIFIED))
- {
- msg("You haven't modified this file yet.");
- return FALSE;
- }
-
- /* read the old header form the tmp file */
- lseek(tmpfd, 0L, 0);
- if (read(tmpfd, oldhdr.c, (unsigned)BLKSIZE) != BLKSIZE)
- {
- msg("Trouble rereading the old header from tmp file");
- }
-
- /* "do" the changed version, so we can undo the "undo" */
- cursor = undocurs;
- beforedo(TRUE);
- afterdo();
-
- /* wipe out the block buffers - we can't assume they're correct */
- blkinit();
-
- /* use the old header -- and therefore the old text blocks */
- hdr = oldhdr;
-
- /* This is a change */
- changes++;
-
- return TRUE;
- }
-