diff options
Diffstat (limited to 'blk.c')
-rw-r--r-- | blk.c | 468 |
1 files changed, 468 insertions, 0 deletions
@@ -0,0 +1,468 @@ +/* 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) + { + sync(); + } +} + +/*------------------------------------------------------------------------*/ + +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; +} |