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