5 * 14407 SW Teal Blvd. #C
11 /* This file contains function which manipulate the cut buffers. */
16 #include <process.h> /* needed for getpid */
20 #define rename(a,b) Frename(0,a,b)
23 # define NANONS 9 /* number of anonymous buffers */
27 short *phys
; /* pointer to an array of #s of BLKs containing text */
28 int nblks
; /* number of blocks in phys[] array */
29 int start
; /* offset into first block of start of cut */
30 int end
; /* offset into last block of end of cut */
31 int tmpnum
; /* ID number of the temp file */
32 char lnmode
; /* boolean: line-mode cut? (as opposed to char-mode) */
34 named
[27], /* cut buffers "a through "z and ". */
35 anon
[NANONS
]; /* anonymous cut buffers */
37 static char cbname
; /* name chosen for next cut/paste operation */
38 static char dotcb
; /* cut buffer to use if "doingdot" is set */
42 /* This function builds a list of all blocks needed in the current tmp file
43 * for the contents of cut buffers.
44 * !!! WARNING: if you have more than ~450000 bytes of text in all of the
45 * cut buffers, then this will fail disastrously, because buffer overflow
46 * is *not* allowed for.
49 BLK
*need
; /* this is where we deposit the list */
51 struct cutbuf
*cb
; /* used to count through cut buffers */
52 int i
; /* used to count through blocks of a cut buffer */
53 int n
; /* total number of blocks in list */
57 /* first the named buffers... */
58 for (cb
= named
; cb
< &named
[27]; cb
++)
60 if (cb
->tmpnum
!= tmpnum
)
63 for (i
= cb
->nblks
; i
-- > 0; )
65 need
->n
[n
++] = cb
->phys
[i
];
69 /* then the anonymous buffers */
70 for (cb
= anon
; cb
< &anon
[NANONS
]; cb
++)
72 if (cb
->tmpnum
!= tmpnum
)
75 for (i
= cb
->nblks
; i
-- > 0; )
77 need
->n
[n
++] = cb
->phys
[i
];
81 /* return the length of the list */
86 static void maybezap(num
)
87 int num
; /* the tmpnum of the temporary file to [maybe] delete */
92 /* if this is the current tmp file, then we'd better keep it! */
93 if (tmpfd
>= 0 && num
== tmpnum
)
98 /* see if anybody else needs this tmp file */
99 for (i
= 27; --i
>= 0; )
101 if (named
[i
].nblks
> 0 && named
[i
].tmpnum
== num
)
108 for (i
= NANONS
; --i
>= 0 ; )
110 if (anon
[i
].nblks
> 0 && anon
[i
].tmpnum
== num
)
117 /* if nobody else needs it, then discard the tmp file */
121 strcpy(cutfname
, o_directory
);
122 if ((i
= strlen(cutfname
)) && !strchr(":/\\", cutfname
[i
- 1]))
123 cutfname
[i
++] = SLASH
;
124 sprintf(cutfname
+ i
, TMPNAME
+ 3, getpid(), num
);
126 sprintf(cutfname
, TMPNAME
, o_directory
, getpid(), num
);
132 /* This function frees a cut buffer. If it was the last cut buffer that
133 * refered to an old temp file, then it will delete the temp file. */
134 static void cutfree(buf
)
139 /* return immediately if the buffer is already empty */
145 /* else free up stuff */
150 msg("cutfree() tried to free a NULL buf->phys pointer.");
153 free((char *)buf
->phys
);
155 /* maybe delete the temp file */
159 /* This function is called when we are about to abort a tmp file.
161 * To minimize the number of extra files lying around, only named cut buffers
162 * are preserved in a file switch; the anonymous buffers just go away.
168 /* mark the current temp file as being "obsolete", and close it. */
169 storename((char *)0);
173 /* discard all anonymous cut buffers */
174 for (i
= 0; i
< NANONS
; i
++)
179 /* delete the temp file, if we don't really need it */
183 /* This function should be called just before termination of vi */
188 /* free the anonymous buffers, if they aren't already free */
191 /* free all named cut buffers, since they might be forcing an older
192 * tmp file to be retained.
194 for (i
= 0; i
< 27; i
++)
199 /* delete the temp file */
204 /* This function is used to select the cut buffer to be used next */
206 int name
; /* a single character */
214 /* This function copies a selected segment of text to a cut buffer */
216 MARK from
; /* start of text to cut */
217 MARK to
; /* end of text to cut */
219 int first
; /* logical number of first block in cut */
220 int last
; /* logical number of last block used in cut */
221 long line
; /* a line number */
222 int lnmode
; /* boolean: will this be a line-mode cut? */
223 MARK delthru
;/* end of text temporarily inserted for apnd */
224 REG
struct cutbuf
*cb
;
230 /* detect whether this must be a line-mode cut or char-mode cut */
231 if (markidx(from
) == 0 && markidx(to
) == 0)
236 /* by default, we don't "delthru" anything */
237 delthru
= MARK_UNSET
;
239 /* handle the "doingdot" quirks */
247 else if (cbname
!= '.')
252 /* decide which cut buffer to use */
255 /* free up the last anonymous cut buffer */
256 cutfree(&anon
[NANONS
- 1]);
258 /* shift the anonymous cut buffers */
259 for (i
= NANONS
- 1; i
> 0; i
--)
261 anon
[i
] = anon
[i
- 1];
264 /* use the first anonymous cut buffer */
268 else if (cbname
>= 'a' && cbname
<= 'z')
270 cb
= &named
[cbname
- 'a'];
274 else if (cbname
>= 'A' && cbname
<= 'Z')
276 cb
= &named
[cbname
- 'A'];
279 /* resolve linemode/charmode differences */
280 if (!lnmode
&& cb
->lnmode
)
282 from
&= ~(BLKSIZE
- 1);
283 if (markidx(to
) != 0 || to
== from
)
285 to
= to
+ BLKSIZE
- markidx(to
);
290 /* insert the old cut-buffer before the new text */
292 delthru
= paste(from
, FALSE
, TRUE
);
293 if (delthru
== MARK_UNSET
)
302 #endif /* not CRUNCH */
303 else if (cbname
== '.')
310 msg("Invalid cut buffer name: \"%c", cbname
);
311 dotcb
= cbname
= '\0';
317 /* detect whether we're doing a line mode cut */
323 if (markidx(from
) == 0 && markidx(to
) == 0)
325 rptlines
= markline(to
) - markline(from
);
331 /* make sure each block has a physical disk address */
334 /* find the first block in the cut */
335 line
= markline(from
);
336 for (first
= 1; line
> lnum
[first
]; first
++)
340 /* fetch text of the block containing that line */
341 blkc
= scan
= blkget(first
)->c
;
343 /* find the mark in the block */
344 for (l
= lnum
[first
- 1]; ++l
< line
; )
346 while (*scan
++ != '\n')
350 scan
+= markidx(from
);
352 /* remember the offset of the start */
353 cb
->start
= scan
- blkc
;
357 /* find the last block in the cut */
359 for (last
= first
; line
> lnum
[last
]; last
++)
363 /* fetch text of the block containing that line */
366 blkc
= scan
= blkget(last
)->c
;
373 /* find the mark in the block */
374 for (l
= lnum
[last
- 1]; ++l
< line
; )
376 while (*scan
++ != '\n')
380 if (markline(to
) <= nlines
)
385 /* remember the offset of the end */
386 cb
->end
= scan
- blkc
;
390 /* remember the physical block numbers of all included blocks */
391 cb
->nblks
= last
- first
;
397 cb
->phys
= (short *)0;
399 cb
->phys
= (short *)malloc((unsigned)(cb
->nblks
* sizeof(short)));
401 for (i
= 0; i
< cb
->nblks
; i
++)
403 cb
->phys
[i
] = hdr
.n
[first
++];
407 /* if we temporarily inserted text for appending, then delete that
408 * text now -- before the user sees it.
413 delete(from
, delthru
);
417 #endif /* not CRUNCH */
421 static void readcutblk(cb
, blkno
)
425 char cutfname
[50];/* name of an old temp file */
426 int fd
; /* either tmpfd or the result of open() */
431 /* decide which fd to use */
432 if (cb
->tmpnum
== tmpnum
)
439 strcpy(cutfname
, o_directory
);
440 if ((i
= strlen(cutfname
)) && !strchr(":/\\", cutfname
[i
-1]))
442 sprintf(cutfname
+i
, TMPNAME
+3, getpid(), cb
->tmpnum
);
444 sprintf(cutfname
, TMPNAME
, o_directory
, getpid(), cb
->tmpnum
);
446 fd
= open(cutfname
, O_RDONLY
);
450 lseek(fd
, (long)cb
->phys
[blkno
] * (long)BLKSIZE
, 0);
451 if (read(fd
, tmpblk
.c
, (unsigned)BLKSIZE
) != BLKSIZE
)
453 msg("Error reading back from tmp file for pasting!");
456 /* close the fd, if it isn't tmpfd */
464 /* This function inserts text from a cut buffer, and returns the MARK where
465 * insertion ended. Return MARK_UNSET on errors.
467 MARK
paste(at
, after
, retend
)
468 MARK at
; /* where to insert the text */
469 int after
; /* boolean: insert after mark? (rather than before) */
470 int retend
; /* boolean: return end of text? (rather than start) */
472 REG
struct cutbuf
*cb
;
475 /* handle the "doingdot" quirks */
480 if (dotcb
>= '1' && dotcb
< '1' + NANONS
- 1)
487 else if (cbname
!= '.')
492 /* decide which cut buffer to use */
493 if (cbname
>= 'A' && cbname
<= 'Z')
495 cb
= &named
[cbname
- 'A'];
497 else if (cbname
>= 'a' && cbname
<= 'z')
499 cb
= &named
[cbname
- 'a'];
501 else if (cbname
>= '1' && cbname
<= '9')
503 cb
= &anon
[cbname
- '1'];
505 else if (cbname
== '.')
515 msg("Invalid cut buffer name: \"%c", cbname
);
520 /* make sure it isn't empty */
524 msg("Cut buffer \"%c is empty", cbname
);
526 msg("Cut buffer is empty");
532 /* adjust the insertion MARK for "after" and line-mode cuts */
535 at
&= ~(BLKSIZE
- 1);
543 /* careful! if markidx(at) == 0 we might be pasting into an
544 * empty line -- so we can't blindly increment "at".
546 if (markidx(at
) == 0)
548 pfetch(markline(at
));
560 /* put a copy of the "at" mark in the mark[] array, so it stays in
561 * sync with changes made via add().
565 /* simple one-block paste? */
571 /* isolate the text we need within it */
574 tmpblk
.c
[cb
->end
] = '\0';
580 add(at
, &tmpblk
.c
[cb
->start
]);
585 /* multi-block paste */
591 /* add text from the last block first */
595 tmpblk
.c
[cb
->end
] = '\0';
600 /* add intervening blocks */
608 /* add text from the first cut block */
610 add(at
, &tmpblk
.c
[cb
->start
]);
615 rptlines
= markline(mark
[27]) - markline(at
);
618 /* return the mark at the beginning/end of inserted text */
621 return mark
[27] - 1L;
631 /* This function copies characters from a cut buffer into a string.
632 * It returns the number of characters in the cut buffer. If the cut
633 * buffer is too large to fit in the string (i.e. if cb2str() returns
634 * a number >= size) then the characters will not have been copied.
635 * It returns 0 if the cut buffer is empty, and -1 for invalid cut buffers.
637 int cb2str(name
, buf
, size
)
638 int name
; /* the name of a cut-buffer to get: a-z only! */
639 char *buf
; /* where to put the string */
640 unsigned size
; /* size of buf */
642 REG
struct cutbuf
*cb
;
646 /* decide which cut buffer to use */
647 if (name
>= 'a' && name
<= 'z')
649 cb
= &named
[name
- 'a'];
656 /* if the buffer is empty, return 0 */
662 /* !!! if not a single-block cut, then fail */
668 /* if too big, return the size now, without doing anything */
669 if (cb
->end
- cb
->start
>= size
)
671 return cb
->end
- cb
->start
;
677 /* isolate the string within that blk */
680 tmpblk
.c
[cb
->end
] = '\0';
684 for (dest
= tmpblk
.c
, src
= dest
+ cb
->start
; src
< tmpblk
.c
+ cb
->end
; )
691 /* copy the string into the buffer */
694 strcpy(buf
, tmpblk
.c
);
697 /* return the length */
698 return cb
->end
- cb
->start
;