source: trunk/minix/commands/elvis/blk.c@ 20

Last change on this file since 20 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

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