|
|
1.1 ! root 1: /* blk.c */ ! 2: ! 3: /* Author: ! 4: * Steve Kirkendall ! 5: * 14407 SW Teal Blvd. #C ! 6: * Beaverton, OR 97005 ! 7: * [email protected] ! 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 == 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: } ! 169: this->dirty = FALSE; ! 170: ! 171: /* update the header so it knows we put it there */ ! 172: hdr.n[this->logical] = physical; ! 173: } ! 174: ! 175: ! 176: /* This function sets a block's "dirty" flag or deletes empty blocks */ ! 177: void blkdirty(bp) ! 178: BLK *bp; /* buffer returned by blkget() */ ! 179: { ! 180: REG int i, j; ! 181: REG char *scan; ! 182: REG int k; ! 183: ! 184: /* find the buffer */ ! 185: for (i = 0; i < NBUFS && bp != &blk[i].buf; i++) ! 186: { ! 187: } ! 188: #ifdef DEBUG ! 189: if (i >= NBUFS) ! 190: { ! 191: msg("blkdirty() called with unknown buffer at 0x%lx", bp); ! 192: return; ! 193: } ! 194: if (blk[i].logical == 0) ! 195: { ! 196: msg("blkdirty called with freed buffer"); ! 197: return; ! 198: } ! 199: #endif ! 200: ! 201: /* if this block ends with line# INFINITY, then it must have been ! 202: * allocated unnecessarily during tmpstart(). Forget it. ! 203: */ ! 204: if (lnum[blk[i].logical] == INFINITY) ! 205: { ! 206: #ifdef DEBUG ! 207: if (blk[i].buf.c[0]) ! 208: { ! 209: msg("bkldirty called with non-empty extra BLK"); ! 210: } ! 211: #endif ! 212: blk[i].logical = 0; ! 213: blk[i].dirty = FALSE; ! 214: return; ! 215: } ! 216: ! 217: /* count lines in this block */ ! 218: for (j = 0, scan = bp->c; *scan && scan < bp->c + BLKSIZE; scan++) ! 219: { ! 220: if (*scan == '\n') ! 221: { ! 222: j++; ! 223: } ! 224: } ! 225: ! 226: /* adjust lnum, if necessary */ ! 227: k = blk[i].logical; ! 228: j += (lnum[k - 1] - lnum[k]); ! 229: if (j != 0) ! 230: { ! 231: nlines += j; ! 232: while (k < MAXBLKS && lnum[k] != INFINITY) ! 233: { ! 234: lnum[k++] += j; ! 235: } ! 236: } ! 237: ! 238: /* if it still has text, mark it as dirty */ ! 239: if (*bp->c) ! 240: { ! 241: blk[i].dirty = TRUE; ! 242: } ! 243: else /* empty block, so delete it */ ! 244: { ! 245: /* adjust the cache */ ! 246: k = blk[i].logical; ! 247: for (j = 0; j < NBUFS; j++) ! 248: { ! 249: if (blk[j].logical >= k) ! 250: { ! 251: blk[j].logical--; ! 252: } ! 253: } ! 254: ! 255: /* delete it from hdr.n[] and lnum[] */ ! 256: blk[i].logical = 0; ! 257: blk[i].dirty = FALSE; ! 258: while (k < MAXBLKS - 1) ! 259: { ! 260: hdr.n[k] = hdr.n[k + 1]; ! 261: lnum[k] = lnum[k + 1]; ! 262: k++; ! 263: } ! 264: hdr.n[MAXBLKS - 1] = 0; ! 265: lnum[MAXBLKS - 1] = INFINITY; ! 266: } ! 267: } ! 268: ! 269: ! 270: /* insert a new block into hdr, and adjust the cache */ ! 271: BLK *blkadd(logical) ! 272: int logical; /* where to insert the new block */ ! 273: { ! 274: REG int i; ! 275: ! 276: /* adjust hdr and lnum[] */ ! 277: for (i = MAXBLKS - 1; i > logical; i--) ! 278: { ! 279: hdr.n[i] = hdr.n[i - 1]; ! 280: lnum[i] = lnum[i - 1]; ! 281: } ! 282: hdr.n[logical] = 0; ! 283: lnum[logical] = lnum[logical - 1]; ! 284: ! 285: /* adjust the cache */ ! 286: for (i = 0; i < NBUFS; i++) ! 287: { ! 288: if (blk[i].logical >= logical) ! 289: { ! 290: blk[i].logical++; ! 291: } ! 292: } ! 293: ! 294: /* return the new block, via blkget() */ ! 295: return blkget(logical); ! 296: } ! 297: ! 298: ! 299: /* This function forces all dirty blocks out to disk */ ! 300: void blksync() ! 301: { ! 302: int i; ! 303: ! 304: for (i = 0; i < NBUFS; i++) ! 305: { ! 306: /* blk[i].dirty = TRUE; */ ! 307: blkflush(&blk[i]); ! 308: } ! 309: if (*o_sync) ! 310: { ! 311: sync(); ! 312: } ! 313: } ! 314: ! 315: /*------------------------------------------------------------------------*/ ! 316: ! 317: static MARK undocurs; /* where the cursor should go if undone */ ! 318: static long oldnlines; ! 319: static long oldlnum[MAXBLKS]; ! 320: ! 321: ! 322: /* This function should be called before each command that changes the text. ! 323: * It defines the state that undo() will reset the file to. ! 324: */ ! 325: void beforedo(forundo) ! 326: int forundo; /* boolean: is this for an undo? */ ! 327: { ! 328: REG int i; ! 329: REG long l; ! 330: ! 331: /* if this is a nested call to beforedo, quit! Use larger context */ ! 332: if (b4cnt++ > 0) ! 333: { ! 334: return; ! 335: } ! 336: ! 337: /* force all block buffers to disk */ ! 338: blksync(); ! 339: ! 340: #ifndef NO_RECYCLE ! 341: /* perform garbage collection on blocks from tmp file */ ! 342: garbage(); ! 343: #endif ! 344: ! 345: /* force the header out to disk */ ! 346: lseek(tmpfd, 0L, 0); ! 347: if (write(tmpfd, hdr.c, (unsigned)BLKSIZE) != BLKSIZE) ! 348: { ! 349: msg("Trouble writing header to tmp file "); ! 350: } ! 351: ! 352: /* copy or swap oldnlines <--> nlines, oldlnum <--> lnum */ ! 353: if (forundo) ! 354: { ! 355: for (i = 0; i < MAXBLKS; i++) ! 356: { ! 357: l = lnum[i]; ! 358: lnum[i] = oldlnum[i]; ! 359: oldlnum[i] = l; ! 360: } ! 361: l = nlines; ! 362: nlines = oldnlines; ! 363: oldnlines = l; ! 364: } ! 365: else ! 366: { ! 367: for (i = 0; i < MAXBLKS; i++) ! 368: { ! 369: oldlnum[i] = lnum[i]; ! 370: } ! 371: oldnlines = nlines; ! 372: } ! 373: ! 374: /* save the cursor position */ ! 375: undocurs = cursor; ! 376: ! 377: /* upon return, the calling function continues and makes changes... */ ! 378: } ! 379: ! 380: /* This function marks the end of a (nested?) change to the file */ ! 381: void afterdo() ! 382: { ! 383: if (--b4cnt) ! 384: { ! 385: /* after abortdo(), b4cnt may decribe nested beforedo/afterdo ! 386: * pairs incorrectly. If it is decremented to often, then ! 387: * keep b4cnt sane but don't do anything else. ! 388: */ ! 389: if (b4cnt < 0) ! 390: b4cnt = 0; ! 391: ! 392: return; ! 393: } ! 394: ! 395: /* make sure the cursor wasn't left stranded in deleted text */ ! 396: if (markline(cursor) > nlines) ! 397: { ! 398: cursor = MARK_LAST; ! 399: } ! 400: /* NOTE: it is still possible that markidx(cursor) is after the ! 401: * end of a line, so the Vi mode will have to take care of that ! 402: * itself */ ! 403: ! 404: /* if a significant change has been made to this file, then set the ! 405: * MODIFIED flag. ! 406: */ ! 407: if (significant) ! 408: { ! 409: setflag(file, MODIFIED); ! 410: setflag(file, UNDOABLE); ! 411: } ! 412: } ! 413: ! 414: /* This function cuts short the current set of changes. It is called after ! 415: * a SIGINT. ! 416: */ ! 417: void abortdo() ! 418: { ! 419: /* finish the operation immediately. */ ! 420: if (b4cnt > 0) ! 421: { ! 422: b4cnt = 1; ! 423: afterdo(); ! 424: } ! 425: ! 426: /* in visual mode, the screen is probably screwed up */ ! 427: if (mode == MODE_COLON) ! 428: { ! 429: mode = MODE_VI; ! 430: } ! 431: if (mode == MODE_VI) ! 432: { ! 433: redraw(MARK_UNSET, FALSE); ! 434: } ! 435: } ! 436: ! 437: /* This function discards all changes made since the last call to beforedo() */ ! 438: int undo() ! 439: { ! 440: BLK oldhdr; ! 441: ! 442: /* if beforedo() has never been run, fail */ ! 443: if (!tstflag(file, UNDOABLE)) ! 444: { ! 445: msg("You haven't modified this file yet."); ! 446: return FALSE; ! 447: } ! 448: ! 449: /* read the old header form the tmp file */ ! 450: lseek(tmpfd, 0L, 0); ! 451: if (read(tmpfd, oldhdr.c, (unsigned)BLKSIZE) != BLKSIZE) ! 452: { ! 453: msg("Trouble rereading the old header from tmp file"); ! 454: } ! 455: ! 456: /* "do" the changed version, so we can undo the "undo" */ ! 457: cursor = undocurs; ! 458: beforedo(TRUE); ! 459: afterdo(); ! 460: ! 461: /* wipe out the block buffers - we can't assume they're correct */ ! 462: blkinit(); ! 463: ! 464: /* use the old header -- and therefore the old text blocks */ ! 465: hdr = oldhdr; ! 466: ! 467: /* This is a change */ ! 468: significant = TRUE; ! 469: changes++; ! 470: ! 471: return TRUE; ! 472: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.