📄 blk.c
字号:
/* 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/*------------------------------------------------------------------------*/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); setflag(file, UNDOABLE); } }/* 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, UNDOABLE)) { 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 */ significant = TRUE; changes++; return TRUE;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -