/* This file contains the functions that get/put blocks from the temp file.
* It also contains the "do" and "undo" functions.
# define NBUFS 5 /* must be at least 3 -- more is better */
/*------------------------------------------------------------------------*/
BLK hdr
; /* buffer for the header block */
static int b4cnt
; /* used to count context of beforedo/afterdo */
BLK buf
; /* contents of a text block */
unsigned short logical
; /* logical block number */
int dirty
; /* must the buffer be rewritten? */
blk
[NBUFS
], /* buffers for text[?] blocks */
*toonew
, /* buffer which shouldn't be recycled yet */
*newtoo
, /* another buffer which should be recycled */
*recycle
= blk
; /* next block to be recycled */
/* This function wipes out all buffers */
for (i
= 0; i
< NBUFS
; i
++)
for (i
= 0; i
< MAXBLKS
; i
++)
/* This function allocates a buffer and fills it with a given block's text */
int logical
; /* logical block number to fetch */
REG
struct _blkbuf
*this; /* used to step through blk[] */
/* if logical is 0, just return the hdr buffer */
/* see if we have that block in mem already */
for (this = blk
; this < &blk
[NBUFS
]; this++)
if (this->logical
== logical
)
/* choose a block to be recycled */
if (recycle
== &blk
[NBUFS
])
} while (this == toonew
|| this == newtoo
);
/* if it contains a block, flush that block */
/* fill this buffer with the desired block */
/* it has been used before - fill it from tmp file */
lseek(tmpfd
, (long)hdr
.n
[logical
] * (long)BLKSIZE
, 0);
if (read(tmpfd
, this->buf
.c
, (unsigned)BLKSIZE
) != BLKSIZE
)
msg("Error reading back from tmp file!");
/* it is new - zero it */
for (i
= 0; i
< BLKSIZE
; i
++)
/* This isn't really a change, but it does potentially invalidate
* the kinds of shortcuts that the "changes" variable is supposed
* to protect us from... so count it as a change.
/* mark it as being "not dirty" */
/* This function writes a block out to the temporary file */
REG
struct _blkbuf
*this; /* the buffer to flush */
long seekpos
; /* seek position of the new block */
unsigned short physical
; /* physical block number */
/* if its empty (an orphan blkadd() maybe?) then make it dirty */
if (this->logical
&& !*this->buf
.c
)
/* if it's an empty buffer or a clean version is on disk, quit */
if (!this->logical
|| hdr
.n
[this->logical
] && !this->dirty
)
/* find a free place in the file */
lseek(tmpfd
, seekpos
, 0);
seekpos
= lseek(tmpfd
, 0L, 2);
physical
= seekpos
/ BLKSIZE
;
/* put the block there */
if (write(tmpfd
, this->buf
.c
, (unsigned)BLKSIZE
) != BLKSIZE
)
msg("Trouble writing to tmp file");
/* update the header so it knows we put it there */
hdr
.n
[this->logical
] = physical
;
/* This function sets a block's "dirty" flag or deletes empty blocks */
BLK
*bp
; /* buffer returned by blkget() */
for (i
= 0; i
< NBUFS
&& bp
!= &blk
[i
].buf
; i
++)
msg("blkdirty() called with unknown buffer at 0x%lx", bp
);
msg("blkdirty called with freed buffer");
/* if this block ends with line# INFINITY, then it must have been
* allocated unnecessarily during tmpstart(). Forget it.
if (lnum
[blk
[i
].logical
] == INFINITY
)
msg("bkldirty called with non-empty extra BLK");
/* count lines in this block */
for (j
= 0, scan
= bp
->c
; *scan
&& scan
< bp
->c
+ BLKSIZE
; scan
++)
/* adjust lnum, if necessary */
j
+= (lnum
[k
- 1] - lnum
[k
]);
while (k
< MAXBLKS
&& lnum
[k
] != INFINITY
)
/* if it still has text, mark it as dirty */
else /* empty block, so delete it */
for (j
= 0; j
< NBUFS
; j
++)
/* delete it from hdr.n[] and lnum[] */
lnum
[MAXBLKS
- 1] = INFINITY
;
/* insert a new block into hdr, and adjust the cache */
int logical
; /* where to insert the new block */
/* adjust hdr and lnum[] */
for (i
= MAXBLKS
- 1; i
> logical
; i
--)
lnum
[logical
] = lnum
[logical
- 1];
for (i
= 0; i
< NBUFS
; i
++)
if (blk
[i
].logical
>= logical
)
/* return the new block, via blkget() */
/* This function forces all dirty blocks out to disk */
for (i
= 0; i
< NBUFS
; i
++)
/* blk[i].dirty = TRUE; */
/*------------------------------------------------------------------------*/
static MARK undocurs
; /* where the cursor should go if undone */
static long oldlnum
[MAXBLKS
];
/* This function should be called before each command that changes the text.
* It defines the state that undo() will reset the file to.
int forundo
; /* boolean: is this for an undo? */
/* if this is a nested call to beforedo, quit! Use larger context */
/* force all block buffers to disk */
/* perform garbage collection on blocks from tmp file */
/* force the header out to disk */
if (write(tmpfd
, hdr
.c
, (unsigned)BLKSIZE
) != BLKSIZE
)
msg("Trouble writing header to tmp file ");
/* copy or swap oldnlines <--> nlines, oldlnum <--> lnum */
for (i
= 0; i
< MAXBLKS
; i
++)
for (i
= 0; i
< MAXBLKS
; i
++)
/* save the cursor position */
/* upon return, the calling function continues and makes changes... */
/* This function marks the end of a (nested?) change to the file */
/* after abortdo(), b4cnt may decribe nested beforedo/afterdo
* pairs incorrectly. If it is decremented to often, then
* keep b4cnt sane but don't do anything else.
/* make sure the cursor wasn't left stranded in deleted text */
if (markline(cursor
) > nlines
)
/* NOTE: it is still possible that markidx(cursor) is after the
* end of a line, so the Vi mode will have to take care of that
/* if a significant change has been made to this file, then set the
/* This function cuts short the current set of changes. It is called after
/* finish the operation immediately. */
/* in visual mode, the screen is probably screwed up */
redraw(MARK_UNSET
, FALSE
);
/* This function discards all changes made since the last call to beforedo() */
/* if beforedo() has never been run, fail */
if (!tstflag(file
, UNDOABLE
))
msg("You haven't modified this file yet.");
/* read the old header form the tmp file */
if (read(tmpfd
, oldhdr
.c
, (unsigned)BLKSIZE
) != BLKSIZE
)
msg("Trouble rereading the old header from tmp file");
/* "do" the changed version, so we can undo the "undo" */
/* wipe out the block buffers - we can't assume they're correct */
/* use the old header -- and therefore the old text blocks */