5 * 14407 SW Teal Blvd. #C
11 /* This file contains the functions that get/put blocks from the temp file.
12 * It also contains the "do" and "undo" functions.
19 # define NBUFS 5 /* must be at least 3 -- more is better */
23 /*------------------------------------------------------------------------*/
25 BLK hdr
; /* buffer for the header block */
27 static int b4cnt
; /* used to count context of beforedo/afterdo */
30 BLK buf
; /* contents of a text block */
31 unsigned short logical
; /* logical block number */
32 int dirty
; /* must the buffer be rewritten? */
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 */
43 /* This function wipes out all buffers */
48 for (i
= 0; i
< NBUFS
; i
++)
53 for (i
= 0; i
< MAXBLKS
; i
++)
59 /* This function allocates a buffer and fills it with a given block's text */
61 int logical
; /* logical block number to fetch */
63 REG
struct _blkbuf
*this; /* used to step through blk[] */
66 /* if logical is 0, just return the hdr buffer */
72 /* see if we have that block in mem already */
73 for (this = blk
; this < &blk
[NBUFS
]; this++)
75 if (this->logical
== logical
)
83 /* choose a block to be recycled */
87 if (recycle
== &blk
[NBUFS
])
91 } while (this == toonew
|| this == newtoo
);
93 /* if it contains a block, flush that block */
96 /* fill this buffer with the desired block */
97 this->logical
= logical
;
100 /* it has been used before - fill it from tmp file */
101 lseek(tmpfd
, (long)hdr
.n
[logical
] * (long)BLKSIZE
, 0);
102 if (read(tmpfd
, this->buf
.c
, (unsigned)BLKSIZE
) != BLKSIZE
)
104 msg("Error reading back from tmp file!");
109 /* it is new - zero it */
110 for (i
= 0; i
< BLKSIZE
; i
++)
116 /* This isn't really a change, but it does potentially invalidate
117 * the kinds of shortcuts that the "changes" variable is supposed
118 * to protect us from... so count it as a change.
122 /* mark it as being "not dirty" */
133 /* This function writes a block out to the temporary file */
135 REG
struct _blkbuf
*this; /* the buffer to flush */
137 long seekpos
; /* seek position of the new block */
138 unsigned short physical
; /* physical block number */
140 /* if its empty (an orphan blkadd() maybe?) then make it dirty */
141 if (this->logical
&& !*this->buf
.c
)
143 blkdirty(&this->buf
);
146 /* if it's an empty buffer or a clean version is on disk, quit */
147 if (!this->logical
|| hdr
.n
[this->logical
] && !this->dirty
)
152 /* find a free place in the file */
154 seekpos
= allocate();
155 lseek(tmpfd
, seekpos
, 0);
157 seekpos
= lseek(tmpfd
, 0L, 2);
159 physical
= seekpos
/ BLKSIZE
;
161 /* put the block there */
162 if (write(tmpfd
, this->buf
.c
, (unsigned)BLKSIZE
) != BLKSIZE
)
164 msg("Trouble writing to tmp file");
168 /* update the header so it knows we put it there */
169 hdr
.n
[this->logical
] = physical
;
173 /* This function sets a block's "dirty" flag or deletes empty blocks */
175 BLK
*bp
; /* buffer returned by blkget() */
181 /* find the buffer */
182 for (i
= 0; i
< NBUFS
&& bp
!= &blk
[i
].buf
; i
++)
188 msg("blkdirty() called with unknown buffer at 0x%lx", bp
);
191 if (blk
[i
].logical
== 0)
193 msg("blkdirty called with freed buffer");
198 /* if this block ends with line# INFINITY, then it must have been
199 * allocated unnecessarily during tmpstart(). Forget it.
201 if (lnum
[blk
[i
].logical
] == INFINITY
)
206 msg("bkldirty called with non-empty extra BLK");
210 blk
[i
].dirty
= FALSE
;
214 /* count lines in this block */
215 for (j
= 0, scan
= bp
->c
; *scan
&& scan
< bp
->c
+ BLKSIZE
; scan
++)
223 /* adjust lnum, if necessary */
225 j
+= (lnum
[k
- 1] - lnum
[k
]);
229 while (k
< MAXBLKS
&& lnum
[k
] != INFINITY
)
235 /* if it still has text, mark it as dirty */
240 else /* empty block, so delete it */
242 /* adjust the cache */
244 for (j
= 0; j
< NBUFS
; j
++)
246 if (blk
[j
].logical
>= k
)
252 /* delete it from hdr.n[] and lnum[] */
254 blk
[i
].dirty
= FALSE
;
255 while (k
< MAXBLKS
- 1)
257 hdr
.n
[k
] = hdr
.n
[k
+ 1];
258 lnum
[k
] = lnum
[k
+ 1];
261 hdr
.n
[MAXBLKS
- 1] = 0;
262 lnum
[MAXBLKS
- 1] = INFINITY
;
267 /* insert a new block into hdr, and adjust the cache */
269 int logical
; /* where to insert the new block */
273 /* adjust hdr and lnum[] */
274 for (i
= MAXBLKS
- 1; i
> logical
; i
--)
276 hdr
.n
[i
] = hdr
.n
[i
- 1];
277 lnum
[i
] = lnum
[i
- 1];
280 lnum
[logical
] = lnum
[logical
- 1];
282 /* adjust the cache */
283 for (i
= 0; i
< NBUFS
; i
++)
285 if (blk
[i
].logical
>= logical
)
291 /* return the new block, via blkget() */
292 return blkget(logical
);
296 /* This function forces all dirty blocks out to disk */
301 for (i
= 0; i
< NBUFS
; i
++)
303 /* blk[i].dirty = TRUE; */
312 /*------------------------------------------------------------------------*/
314 static MARK undocurs
; /* where the cursor should go if undone */
315 static long oldnlines
;
316 static long oldlnum
[MAXBLKS
];
319 /* This function should be called before each command that changes the text.
320 * It defines the state that undo() will reset the file to.
322 void beforedo(forundo
)
323 int forundo
; /* boolean: is this for an undo? */
328 /* if this is a nested call to beforedo, quit! Use larger context */
334 /* force all block buffers to disk */
338 /* perform garbage collection on blocks from tmp file */
342 /* force the header out to disk */
344 if (write(tmpfd
, hdr
.c
, (unsigned)BLKSIZE
) != BLKSIZE
)
346 msg("Trouble writing header to tmp file ");
349 /* copy or swap oldnlines <--> nlines, oldlnum <--> lnum */
352 for (i
= 0; i
< MAXBLKS
; i
++)
355 lnum
[i
] = oldlnum
[i
];
364 for (i
= 0; i
< MAXBLKS
; i
++)
366 oldlnum
[i
] = lnum
[i
];
371 /* save the cursor position */
374 /* upon return, the calling function continues and makes changes... */
377 /* This function marks the end of a (nested?) change to the file */
382 /* after abortdo(), b4cnt may decribe nested beforedo/afterdo
383 * pairs incorrectly. If it is decremented to often, then
384 * keep b4cnt sane but don't do anything else.
392 /* make sure the cursor wasn't left stranded in deleted text */
393 if (markline(cursor
) > nlines
)
397 /* NOTE: it is still possible that markidx(cursor) is after the
398 * end of a line, so the Vi mode will have to take care of that
401 /* if a significant change has been made to this file, then set the
406 setflag(file
, MODIFIED
);
407 setflag(file
, UNDOABLE
);
411 /* This function cuts short the current set of changes. It is called after
416 /* finish the operation immediately. */
423 /* in visual mode, the screen is probably screwed up */
424 if (mode
== MODE_COLON
)
430 redraw(MARK_UNSET
, FALSE
);
434 /* This function discards all changes made since the last call to beforedo() */
439 /* if beforedo() has never been run, fail */
440 if (!tstflag(file
, UNDOABLE
))
442 msg("You haven't modified this file yet.");
446 /* read the old header form the tmp file */
448 if (read(tmpfd
, oldhdr
.c
, (unsigned)BLKSIZE
) != BLKSIZE
)
450 msg("Trouble rereading the old header from tmp file");
453 /* "do" the changed version, so we can undo the "undo" */
458 /* wipe out the block buffers - we can't assume they're correct */
461 /* use the old header -- and therefore the old text blocks */
464 /* This is a change */