|
|
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.