/* This file contains function which manipulate the cut buffers. */
#include <process.h> /* needed for getpid */
#define rename(a,b) Frename(0,a,b)
# define NANONS 9 /* number of anonymous buffers */
short *phys
; /* pointer to an array of #s of BLKs containing text */
int nblks
; /* number of blocks in phys[] array */
int start
; /* offset into first block of start of cut */
int end
; /* offset into last block of end of cut */
int tmpnum
; /* ID number of the temp file */
char lnmode
; /* boolean: line-mode cut? (as opposed to char-mode) */
named
[27], /* cut buffers "a through "z and ". */
anon
[NANONS
]; /* anonymous cut buffers */
static char cbname
; /* name chosen for next cut/paste operation */
static char dotcb
; /* cut buffer to use if "doingdot" is set */
/* This function builds a list of all blocks needed in the current tmp file
* for the contents of cut buffers.
* !!! WARNING: if you have more than ~450000 bytes of text in all of the
* cut buffers, then this will fail disastrously, because buffer overflow
BLK
*need
; /* this is where we deposit the list */
struct cutbuf
*cb
; /* used to count through cut buffers */
int i
; /* used to count through blocks of a cut buffer */
int n
; /* total number of blocks in list */
/* first the named buffers... */
for (cb
= named
; cb
< &named
[27]; cb
++)
if (cb
->tmpnum
!= tmpnum
)
for (i
= cb
->nblks
; i
-- > 0; )
need
->n
[n
++] = cb
->phys
[i
];
/* then the anonymous buffers */
for (cb
= anon
; cb
< &anon
[NANONS
]; cb
++)
if (cb
->tmpnum
!= tmpnum
)
for (i
= cb
->nblks
; i
-- > 0; )
need
->n
[n
++] = cb
->phys
[i
];
/* return the length of the list */
static void maybezap(num
)
int num
; /* the tmpnum of the temporary file to [maybe] delete */
/* if this is the current tmp file, then we'd better keep it! */
if (tmpfd
>= 0 && num
== tmpnum
)
/* see if anybody else needs this tmp file */
if (named
[i
].nblks
> 0 && named
[i
].tmpnum
== num
)
for (i
= NANONS
; --i
>= 0 ; )
if (anon
[i
].nblks
> 0 && anon
[i
].tmpnum
== num
)
/* if nobody else needs it, then discard the tmp file */
strcpy(cutfname
, o_directory
);
if ((i
= strlen(cutfname
)) && !strchr(":/\\", cutfname
[i
- 1]))
sprintf(cutfname
+ i
, TMPNAME
+ 3, getpid(), num
);
sprintf(cutfname
, TMPNAME
, o_directory
, getpid(), num
);
/* This function frees a cut buffer. If it was the last cut buffer that
* refered to an old temp file, then it will delete the temp file. */
/* return immediately if the buffer is already empty */
msg("cutfree() tried to free a NULL buf->phys pointer.");
/* maybe delete the temp file */
/* This function is called when we are about to abort a tmp file.
* To minimize the number of extra files lying around, only named cut buffers
* are preserved in a file switch; the anonymous buffers just go away.
/* mark the current temp file as being "obsolete", and close it. */
/* discard all anonymous cut buffers */
for (i
= 0; i
< NANONS
; i
++)
/* delete the temp file, if we don't really need it */
/* This function should be called just before termination of vi */
/* free the anonymous buffers, if they aren't already free */
/* free all named cut buffers, since they might be forcing an older
* tmp file to be retained.
/* delete the temp file */
/* This function is used to select the cut buffer to be used next */
int name
; /* a single character */
/* This function copies a selected segment of text to a cut buffer */
MARK from
; /* start of text to cut */
MARK to
; /* end of text to cut */
int first
; /* logical number of first block in cut */
int last
; /* logical number of last block used in cut */
long line
; /* a line number */
int lnmode
; /* boolean: will this be a line-mode cut? */
MARK delthru
;/* end of text temporarily inserted for apnd */
/* detect whether this must be a line-mode cut or char-mode cut */
if (markidx(from
) == 0 && markidx(to
) == 0)
/* by default, we don't "delthru" anything */
/* handle the "doingdot" quirks */
/* decide which cut buffer to use */
/* free up the last anonymous cut buffer */
cutfree(&anon
[NANONS
- 1]);
/* shift the anonymous cut buffers */
for (i
= NANONS
- 1; i
> 0; i
--)
/* use the first anonymous cut buffer */
else if (cbname
>= 'a' && cbname
<= 'z')
cb
= &named
[cbname
- 'a'];
else if (cbname
>= 'A' && cbname
<= 'Z')
cb
= &named
[cbname
- 'A'];
/* resolve linemode/charmode differences */
if (!lnmode
&& cb
->lnmode
)
if (markidx(to
) != 0 || to
== from
)
to
= to
+ BLKSIZE
- markidx(to
);
/* insert the old cut-buffer before the new text */
delthru
= paste(from
, FALSE
, TRUE
);
if (delthru
== MARK_UNSET
)
msg("Invalid cut buffer name: \"%c", cbname
);
/* detect whether we're doing a line mode cut */
if (markidx(from
) == 0 && markidx(to
) == 0)
rptlines
= markline(to
) - markline(from
);
/* make sure each block has a physical disk address */
/* find the first block in the cut */
for (first
= 1; line
> lnum
[first
]; first
++)
/* fetch text of the block containing that line */
blkc
= scan
= blkget(first
)->c
;
/* find the mark in the block */
for (l
= lnum
[first
- 1]; ++l
< line
; )
/* remember the offset of the start */
/* find the last block in the cut */
for (last
= first
; line
> lnum
[last
]; last
++)
/* fetch text of the block containing that line */
blkc
= scan
= blkget(last
)->c
;
/* find the mark in the block */
for (l
= lnum
[last
- 1]; ++l
< line
; )
if (markline(to
) <= nlines
)
/* remember the offset of the end */
/* remember the physical block numbers of all included blocks */
cb
->nblks
= last
- first
;
cb
->phys
= (short *)malloc((unsigned)(cb
->nblks
* sizeof(short)));
for (i
= 0; i
< cb
->nblks
; i
++)
cb
->phys
[i
] = hdr
.n
[first
++];
/* if we temporarily inserted text for appending, then delete that
* text now -- before the user sees it.
static void readcutblk(cb
, blkno
)
char cutfname
[50];/* name of an old temp file */
int fd
; /* either tmpfd or the result of open() */
/* decide which fd to use */
if (cb
->tmpnum
== tmpnum
)
strcpy(cutfname
, o_directory
);
if ((i
= strlen(cutfname
)) && !strchr(":/\\", cutfname
[i
-1]))
sprintf(cutfname
+i
, TMPNAME
+3, getpid(), cb
->tmpnum
);
sprintf(cutfname
, TMPNAME
, o_directory
, getpid(), cb
->tmpnum
);
fd
= open(cutfname
, O_RDONLY
);
lseek(fd
, (long)cb
->phys
[blkno
] * (long)BLKSIZE
, 0);
if (read(fd
, tmpblk
.c
, (unsigned)BLKSIZE
) != BLKSIZE
)
msg("Error reading back from tmp file for pasting!");
/* close the fd, if it isn't tmpfd */
/* This function inserts text from a cut buffer, and returns the MARK where
* insertion ended. Return MARK_UNSET on errors.
MARK
paste(at
, after
, retend
)
MARK at
; /* where to insert the text */
int after
; /* boolean: insert after mark? (rather than before) */
int retend
; /* boolean: return end of text? (rather than start) */
/* handle the "doingdot" quirks */
if (dotcb
>= '1' && dotcb
< '1' + NANONS
- 1)
/* decide which cut buffer to use */
if (cbname
>= 'A' && cbname
<= 'Z')
cb
= &named
[cbname
- 'A'];
else if (cbname
>= 'a' && cbname
<= 'z')
cb
= &named
[cbname
- 'a'];
else if (cbname
>= '1' && cbname
<= '9')
cb
= &anon
[cbname
- '1'];
msg("Invalid cut buffer name: \"%c", cbname
);
/* make sure it isn't empty */
msg("Cut buffer \"%c is empty", cbname
);
msg("Cut buffer is empty");
/* adjust the insertion MARK for "after" and line-mode cuts */
/* careful! if markidx(at) == 0 we might be pasting into an
* empty line -- so we can't blindly increment "at".
/* put a copy of the "at" mark in the mark[] array, so it stays in
* sync with changes made via add().
/* simple one-block paste? */
/* isolate the text we need within it */
tmpblk
.c
[cb
->end
] = '\0';
add(at
, &tmpblk
.c
[cb
->start
]);
/* add text from the last block first */
tmpblk
.c
[cb
->end
] = '\0';
/* add intervening blocks */
/* add text from the first cut block */
add(at
, &tmpblk
.c
[cb
->start
]);
rptlines
= markline(mark
[27]) - markline(at
);
/* return the mark at the beginning/end of inserted text */
/* This function copies characters from a cut buffer into a string.
* It returns the number of characters in the cut buffer. If the cut
* buffer is too large to fit in the string (i.e. if cb2str() returns
* a number >= size) then the characters will not have been copied.
* It returns 0 if the cut buffer is empty, and -1 for invalid cut buffers.
int cb2str(name
, buf
, size
)
int name
; /* the name of a cut-buffer to get: a-z only! */
char *buf
; /* where to put the string */
unsigned size
; /* size of buf */
/* decide which cut buffer to use */
if (name
>= 'a' && name
<= 'z')
/* if the buffer is empty, return 0 */
/* !!! if not a single-block cut, then fail */
/* if too big, return the size now, without doing anything */
if (cb
->end
- cb
->start
>= size
)
return cb
->end
- cb
->start
;
/* isolate the string within that blk */
tmpblk
.c
[cb
->end
] = '\0';
for (dest
= tmpblk
.c
, src
= dest
+ cb
->start
; src
< tmpblk
.c
+ cb
->end
; )
/* copy the string into the buffer */
return cb
->end
- cb
->start
;