|
|
1.1 ! root 1: /* blk.c */ ! 2: ! 3: /* Author: ! 4: * Steve Kirkendall ! 5: * 16820 SW Tallac Way ! 6: * Beaverton, OR 97006 ! 7: * [email protected], or ...uunet!tektronix!psueea!jove!kirkenda ! 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: extern long lseek(); ! 23: ! 24: /*------------------------------------------------------------------------*/ ! 25: ! 26: BLK hdr; /* buffer for the header block */ ! 27: ! 28: static int b4cnt; /* used to count context of beforedo/afterdo */ ! 29: static struct _blkbuf ! 30: { ! 31: BLK buf; /* contents of a text block */ ! 32: unsigned short logical; /* logical block number */ ! 33: int dirty; /* must the buffer be rewritten? */ ! 34: } ! 35: blk[NBUFS], /* buffers for text[?] blocks */ ! 36: *toonew, /* buffer which shouldn't be recycled yet */ ! 37: *newtoo, /* another buffer which should be recycled */ ! 38: *recycle = blk; /* next block to be recycled */ ! 39: ! 40: ! 41: ! 42: ! 43: ! 44: /* This function wipes out all buffers */ ! 45: void blkinit() ! 46: { ! 47: int i; ! 48: ! 49: for (i = 0; i < NBUFS; i++) ! 50: { ! 51: blk[i].logical = 0; ! 52: blk[i].dirty = FALSE; ! 53: } ! 54: for (i = 0; i < MAXBLKS; i++) ! 55: { ! 56: hdr.n[i] = 0; ! 57: } ! 58: } ! 59: ! 60: /* This function allocates a buffer and fills it with a given block's text */ ! 61: BLK *blkget(logical) ! 62: int logical; /* logical block number to fetch */ ! 63: { ! 64: REG struct _blkbuf *this; /* used to step through blk[] */ ! 65: REG int i; ! 66: ! 67: /* if logical is 0, just return the hdr buffer */ ! 68: if (logical == 0) ! 69: { ! 70: return &hdr; ! 71: } ! 72: ! 73: /* see if we have that block in mem already */ ! 74: for (this = blk; this < &blk[NBUFS]; this++) ! 75: { ! 76: if (this->logical == logical) ! 77: { ! 78: newtoo = toonew; ! 79: toonew = this; ! 80: return &this->buf; ! 81: } ! 82: } ! 83: ! 84: /* choose a block to be recycled */ ! 85: do ! 86: { ! 87: this = recycle++; ! 88: if (recycle == &blk[NBUFS]) ! 89: { ! 90: recycle = blk; ! 91: } ! 92: } while (this == toonew || this == newtoo); ! 93: ! 94: /* if it contains a block, flush that block */ ! 95: blkflush(this); ! 96: ! 97: /* fill this buffer with the desired block */ ! 98: this->logical = logical; ! 99: if (hdr.n[logical]) ! 100: { ! 101: /* it has been used before - fill it from tmp file */ ! 102: lseek(tmpfd, (long)hdr.n[logical] * (long)BLKSIZE, 0); ! 103: if (read(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE) ! 104: { ! 105: msg("Error reading back from tmp file!"); ! 106: } ! 107: } ! 108: else ! 109: { ! 110: /* it is new - zero it */ ! 111: for (i = 0; i < BLKSIZE; i++) ! 112: { ! 113: this->buf.c[i] = 0; ! 114: } ! 115: } ! 116: ! 117: /* This isn't really a change, but it does potentially invalidate ! 118: * the kinds of shortcuts that the "changes" variable is supposed ! 119: * to protect us from... so count it as a change. ! 120: */ ! 121: changes++; ! 122: ! 123: /* mark it as being "not dirty" */ ! 124: this->dirty = 0; ! 125: ! 126: /* return it */ ! 127: newtoo = toonew; ! 128: toonew = this; ! 129: return &this->buf; ! 130: } ! 131: ! 132: ! 133: ! 134: /* This function writes a block out to the temporary file */ ! 135: void blkflush(this) ! 136: REG struct _blkbuf *this; /* the buffer to flush */ ! 137: { ! 138: long seekpos; /* seek position of the new block */ ! 139: unsigned short physical; /* physical block number */ ! 140: ! 141: /* if its empty (an orphan blkadd() maybe?) then make it dirty */ ! 142: if (this->logical && !*this->buf.c) ! 143: { ! 144: blkdirty(&this->buf); ! 145: } ! 146: ! 147: /* if it's an empty buffer or a clean version is on disk, quit */ ! 148: if (!this->logical || hdr.n[this->logical] && !this->dirty) ! 149: { ! 150: return; ! 151: } ! 152: ! 153: /* find a free place in the file */ ! 154: #ifndef NO_RECYCLE ! 155: seekpos = allocate(); ! 156: lseek(tmpfd, seekpos, 0); ! 157: #else ! 158: seekpos = lseek(tmpfd, 0L, 2); ! 159: #endif ! 160: physical = seekpos / BLKSIZE; ! 161: ! 162: /* put the block there */ ! 163: if (write(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE) ! 164: { ! 165: msg("Trouble writing to tmp file"); ! 166: } ! 167: this->dirty = FALSE; ! 168: ! 169: /* update the header so it knows we put it there */ ! 170: hdr.n[this->logical] = physical; ! 171: } ! 172: ! 173: ! 174: /* This function sets a block's "dirty" flag or deletes empty blocks */ ! 175: void blkdirty(bp) ! 176: BLK *bp; /* buffer returned by blkget() */ ! 177: { ! 178: REG int i, j; ! 179: REG char *scan; ! 180: REG int k; ! 181: ! 182: /* find the buffer */ ! 183: for (i = 0; i < NBUFS && bp != &blk[i].buf; i++) ! 184: { ! 185: } ! 186: #ifdef DEBUG ! 187: if (i >= NBUFS) ! 188: { ! 189: msg("blkdirty() called with unknown buffer at 0x%lx", bp); ! 190: return; ! 191: } ! 192: if (blk[i].logical == 0) ! 193: { ! 194: msg("blkdirty called with freed buffer"); ! 195: return; ! 196: } ! 197: #endif ! 198: ! 199: /* if this block ends with line# INFINITY, then it must have been ! 200: * allocated unnecessarily during tmpstart(). Forget it. ! 201: */ ! 202: if (lnum[blk[i].logical] == INFINITY) ! 203: { ! 204: #ifdef DEBUG ! 205: if (blk[i].buf.c[0]) ! 206: { ! 207: msg("bkldirty called with non-empty extra BLK"); ! 208: } ! 209: #endif ! 210: blk[i].logical = 0; ! 211: blk[i].dirty = FALSE; ! 212: return; ! 213: } ! 214: ! 215: /* count lines in this block */ ! 216: for (j = 0, scan = bp->c; *scan && scan < bp->c + BLKSIZE; scan++) ! 217: { ! 218: if (*scan == '\n') ! 219: { ! 220: j++; ! 221: } ! 222: } ! 223: ! 224: /* adjust lnum, if necessary */ ! 225: k = blk[i].logical; ! 226: j += (lnum[k - 1] - lnum[k]); ! 227: if (j != 0) ! 228: { ! 229: nlines += j; ! 230: while (k < MAXBLKS && lnum[k] != INFINITY) ! 231: { ! 232: lnum[k++] += j; ! 233: } ! 234: } ! 235: ! 236: /* if it still has text, mark it as dirty */ ! 237: if (*bp->c) ! 238: { ! 239: blk[i].dirty = TRUE; ! 240: } ! 241: else /* empty block, so delete it */ ! 242: { ! 243: /* adjust the cache */ ! 244: k = blk[i].logical; ! 245: for (j = 0; j < NBUFS; j++) ! 246: { ! 247: if (blk[j].logical >= k) ! 248: { ! 249: blk[j].logical--; ! 250: } ! 251: } ! 252: ! 253: /* delete it from hdr.n[] and lnum[] */ ! 254: blk[i].logical = 0; ! 255: blk[i].dirty = FALSE; ! 256: while (k < MAXBLKS - 1) ! 257: { ! 258: hdr.n[k] = hdr.n[k + 1]; ! 259: lnum[k] = lnum[k + 1]; ! 260: k++; ! 261: } ! 262: hdr.n[MAXBLKS - 1] = 0; ! 263: lnum[MAXBLKS - 1] = INFINITY; ! 264: } ! 265: } ! 266: ! 267: ! 268: /* insert a new block into hdr, and adjust the cache */ ! 269: BLK *blkadd(logical) ! 270: int logical; /* where to insert the new block */ ! 271: { ! 272: REG int i; ! 273: ! 274: /* adjust hdr and lnum[] */ ! 275: for (i = MAXBLKS - 1; i > logical; i--) ! 276: { ! 277: hdr.n[i] = hdr.n[i - 1]; ! 278: lnum[i] = lnum[i - 1]; ! 279: } ! 280: hdr.n[logical] = 0; ! 281: lnum[logical] = lnum[logical - 1]; ! 282: ! 283: /* adjust the cache */ ! 284: for (i = 0; i < NBUFS; i++) ! 285: { ! 286: if (blk[i].logical >= logical) ! 287: { ! 288: blk[i].logical++; ! 289: } ! 290: } ! 291: ! 292: /* return the new block, via blkget() */ ! 293: return blkget(logical); ! 294: } ! 295: ! 296: ! 297: /* This function forces all dirty blocks out to disk */ ! 298: void blksync() ! 299: { ! 300: int i; ! 301: ! 302: for (i = 0; i < NBUFS; i++) ! 303: { ! 304: /* blk[i].dirty = TRUE; */ ! 305: blkflush(&blk[i]); ! 306: } ! 307: if (*o_sync) ! 308: { ! 309: sync(); ! 310: } ! 311: } ! 312: ! 313: /*------------------------------------------------------------------------*/ ! 314: ! 315: static MARK undocurs; /* where the cursor should go if undone */ ! 316: static long oldnlines; ! 317: static long oldlnum[MAXBLKS]; ! 318: ! 319: ! 320: /* This function should be called before each command that changes the text. ! 321: * It defines the state that undo() will reset the file to. ! 322: */ ! 323: void beforedo(forundo) ! 324: int forundo; /* boolean: is this for an undo? */ ! 325: { ! 326: REG int i; ! 327: REG long l; ! 328: ! 329: /* if this is a nested call to beforedo, quit! Use larger context */ ! 330: if (b4cnt++ > 0) ! 331: { ! 332: return; ! 333: } ! 334: ! 335: /* force all block buffers to disk */ ! 336: blksync(); ! 337: ! 338: #ifndef NO_RECYCLE ! 339: /* perform garbage collection on blocks from tmp file */ ! 340: garbage(); ! 341: #endif ! 342: ! 343: /* force the header out to disk */ ! 344: lseek(tmpfd, 0L, 0); ! 345: if (write(tmpfd, hdr.c, (unsigned)BLKSIZE) != BLKSIZE) ! 346: { ! 347: msg("Trouble writing header to tmp file "); ! 348: } ! 349: ! 350: /* copy or swap oldnlines <--> nlines, oldlnum <--> lnum */ ! 351: if (forundo) ! 352: { ! 353: for (i = 0; i < MAXBLKS; i++) ! 354: { ! 355: l = lnum[i]; ! 356: lnum[i] = oldlnum[i]; ! 357: oldlnum[i] = l; ! 358: } ! 359: l = nlines; ! 360: nlines = oldnlines; ! 361: oldnlines = l; ! 362: } ! 363: else ! 364: { ! 365: for (i = 0; i < MAXBLKS; i++) ! 366: { ! 367: oldlnum[i] = lnum[i]; ! 368: } ! 369: oldnlines = nlines; ! 370: } ! 371: ! 372: /* save the cursor position */ ! 373: undocurs = cursor; ! 374: ! 375: /* upon return, the calling function continues and makes changes... */ ! 376: } ! 377: ! 378: /* This function marks the end of a (nested?) change to the file */ ! 379: void afterdo() ! 380: { ! 381: if (--b4cnt) ! 382: { ! 383: return; ! 384: } ! 385: ! 386: /* make sure the cursor wasn't left stranded in deleted text */ ! 387: if (markline(cursor) > nlines) ! 388: { ! 389: cursor = MARK_LAST; ! 390: } ! 391: /* NOTE: it is still possible that markidx(cursor) is after the ! 392: * end of a line, so the Vi mode will have to take care of that ! 393: * itself */ ! 394: ! 395: /* if a significant change has been made to this file, then set the ! 396: * MODIFIED flag. ! 397: */ ! 398: if (significant) ! 399: { ! 400: setflag(file, MODIFIED); ! 401: } ! 402: } ! 403: ! 404: /* This function cuts short the current set of changes. It is called after ! 405: * a SIGINT. ! 406: */ ! 407: void abortdo() ! 408: { ! 409: /* finish the operation immediately. */ ! 410: if (b4cnt > 0) ! 411: { ! 412: b4cnt = 1; ! 413: afterdo(); ! 414: } ! 415: ! 416: /* in visual mode, the screen is probably screwed up */ ! 417: if (mode == MODE_COLON) ! 418: { ! 419: mode = MODE_VI; ! 420: } ! 421: if (mode == MODE_VI) ! 422: { ! 423: redraw(MARK_UNSET, FALSE); ! 424: } ! 425: } ! 426: ! 427: /* This function discards all changes made since the last call to beforedo() */ ! 428: int undo() ! 429: { ! 430: BLK oldhdr; ! 431: ! 432: /* if beforedo() has never been run, fail */ ! 433: if (!tstflag(file, MODIFIED)) ! 434: { ! 435: msg("You haven't modified this file yet."); ! 436: return FALSE; ! 437: } ! 438: ! 439: /* read the old header form the tmp file */ ! 440: lseek(tmpfd, 0L, 0); ! 441: if (read(tmpfd, oldhdr.c, (unsigned)BLKSIZE) != BLKSIZE) ! 442: { ! 443: msg("Trouble rereading the old header from tmp file"); ! 444: } ! 445: ! 446: /* "do" the changed version, so we can undo the "undo" */ ! 447: cursor = undocurs; ! 448: beforedo(TRUE); ! 449: afterdo(); ! 450: ! 451: /* wipe out the block buffers - we can't assume they're correct */ ! 452: blkinit(); ! 453: ! 454: /* use the old header -- and therefore the old text blocks */ ! 455: hdr = oldhdr; ! 456: ! 457: /* This is a change */ ! 458: changes++; ! 459: ! 460: return TRUE; ! 461: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.