|
|
1.1 root 1: /* cut.c */
2:
3: /* Author:
4: * Steve Kirkendall
5: * 16820 SW Tallac Way
6: * Beaverton, OR 97006
7: * [email protected], or ...uunet!tektronix!psueea!jove!kirkenda
8: */
9:
10:
11: /* This file contains function which manipulate the cut buffers. */
12:
13: #include "config.h"
14: #include "vi.h"
15: #if TURBOC
16: #include <process.h> /* needed for getpid */
17: #endif
18: #if TOS
19: #include <osbind.h>
20: #define rename(a,b) Frename(0,a,b)
21: #endif
22:
23: # define NANNONS 9 /* number of annonymous buffers */
24:
25: static struct cutbuf
26: {
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 fd; /* fd of tmp file, or -1 to use tmpfd */
32: char lnmode; /* boolean: line-mode cut? (as opposed to char-mode) */
33: }
34: named[27], /* cut buffers "a through "z and ". */
35: annon[NANNONS]; /* annonymous cut buffers */
36:
37: static char cbname; /* name chosen for next cut/paste operation */
38:
39:
40: #ifndef NO_RECYCLE
41: /* This function builds a list of all blocks needed in the current tmp file
42: * for the contents of cut buffers.
43: * !!! WARNING: if you have more than ~450000 bytes of text in all of the
44: * cut buffers, then this will fail disastrously, because buffer overflow
45: * is *not* allowed for.
46: */
47: int cutneeds(need)
48: BLK *need; /* this is where we deposit the list */
49: {
50: struct cutbuf *cb; /* used to count through cut buffers */
51: int i; /* used to count through blocks of a cut buffer */
52: int n; /* total number of blocks in list */
53:
54: n = 0;
55:
56: /* first the named buffers... */
57: for (cb = named; cb < &named[27]; cb++)
58: {
59: if (cb->fd > 0)
60: continue;
61:
62: for (i = cb->nblks; i-- > 0; )
63: {
64: need->n[n++] = cb->phys[i];
65: }
66: }
67:
68: /* then the anonymous buffers */
69: for (cb = annon; cb < &annon[NANNONS]; cb++)
70: {
71: if (cb->fd > 0)
72: continue;
73:
74: for (i = cb->nblks; i-- > 0; )
75: {
76: need->n[n++] = cb->phys[i];
77: }
78: }
79:
80: return n;
81: }
82: #endif
83:
84: /* This function frees a cut buffer */
85: static void cutfree(buf)
86: struct cutbuf *buf;
87: {
88: char cutfname[50];
89: int i;
90:
91: /* return immediately if the buffer is already empty */
92: if (buf->nblks <= 0)
93: {
94: return;
95: }
96:
97: /* else free up stuff */
98: buf->nblks = 0;
99: #ifdef DEBUG
100: if (!buf->phys)
101: msg("cutfree() tried to free an NULL buf->phys pointer.");
102: #endif
103: free((char *)buf->phys);
104:
105: /* see if anybody else needs this tmp file */
106: if (buf->fd >= 0)
107: {
108: for (i = 0; i < 27; i++)
109: {
110: if (named[i].nblks > 0 && named[i].fd == buf->fd)
111: {
112: break;
113: }
114: }
115: }
116:
117: /* if nobody else needs it, then discard the tmp file */
118: if (buf->fd >= 0 && i == 27)
119: {
120: close(buf->fd);
121: #if MSDOS || TOS
122: strcpy(cutfname, o_directory);
123: if ((i = strlen(cutfname)) && !strchr(":/\\", cutfname[i-1]))
124: cutfname[i++]=SLASH;
125: sprintf(cutfname+i, CUTNAME+3, getpid(), buf->fd);
126: #else
127: sprintf(cutfname, CUTNAME, o_directory, getpid(), buf->fd);
128: #endif
129: unlink(cutfname);
130: }
131: }
132:
133: /* This function is called when we are about to abort a tmp file. If any
134: * cut buffers still need the file, then a copy of the file should be
135: * created for use by the cut buffers.
136: *
137: * To minimize the number of extra files lying around, only named cut buffers
138: * are preserved in a file switch; the annonymous buffers just go away.
139: */
140: void cutswitch(tmpname)
141: char *tmpname; /* name of the tmp file */
142: {
143: char cutfname[50]; /* used to build a new name for the tmp file */
144: int fd; /* a new fd for the current tmp file */
145: int i;
146: #if MSDOS || TOS
147: int j;
148: #endif
149:
150: /* discard all annonymous cut buffers */
151: for (i = 0; i < NANNONS; i++)
152: {
153: cutfree(&annon[i]);
154: }
155:
156: /* find the first named buffer that uses this tmp file */
157: for (i = 0; i < 27; i++)
158: {
159: if (named[i].nblks > 0 && named[i].fd < 0)
160: {
161: break;
162: }
163: }
164:
165: /* if none of them use this tmp file, then we're done */
166: if (i == 27)
167: {
168: return;
169: }
170:
171: /* else we'll need this file and an fd a little longer */
172: #if MSDOS || TOS
173: strcpy(cutfname, o_directory);
174: if ((j = strlen(cutfname)) && !strchr(":/\\", cutfname[j-1]))
175: cutfname[j++]=SLASH;
176: close(tmpfd);
177: fd = open(tmpname, O_RDONLY|O_BINARY);
178: close(fd);
179: sprintf(cutfname+j, CUTNAME+3, getpid(), fd);
180: rename(tmpname, cutfname);
181: fd = open(cutfname, O_RDONLY|O_BINARY);
182: tmpfd = -1; /* we'll try to close this in tmp.c, but who cares? */
183: #else
184: fd = dup(tmpfd);
185: # if OSK
186: sprintf(cutfname, CUTNAME, "", getpid(), fd);
187: if (!link(tmpname, &cutfname[1])) /* skip slash */
188: unlink(tmpname);
189: # else
190: sprintf(cutfname, CUTNAME, o_directory, getpid(), fd);
191: link(tmpname, cutfname) || unlink(tmpname);
192: # endif
193: #endif
194:
195: /* have all cut buffers use the new fd instead */
196: for (; i < 27; i++)
197: {
198: if (named[i].nblks > 0 && named[i].fd < 0)
199: {
200: named[i].fd = fd;
201: }
202: }
203: }
204:
205: /* This function should be called just before termination of vi */
206: void cutend()
207: {
208: int i;
209:
210: /* free all named cut buffers, since they might be forcing an older
211: * tmp file to be retained.
212: */
213: for (i = 0; i < 27; i++)
214: {
215: cutfree(&named[i]);
216: }
217: }
218:
219:
220: /* This function is used to select the cut buffer to be used next */
221: void cutname(name)
222: int name; /* a single character */
223: {
224: cbname = name;
225: }
226:
227:
228:
229:
230: /* This function copies a selected segment of text to a cut buffer */
231: void cut(from, to)
232: MARK from; /* start of text to cut */
233: MARK to; /* end of text to cut */
234: {
235: int first; /* logical number of first block in cut */
236: int last; /* logical number of last block used in cut */
237: long line; /* a line number */
238: int lnmode; /* boolean: will this be a line-mode cut? */
239: MARK delthru;/* end of text temporarily inserted for apnd */
240: REG struct cutbuf *cb;
241: REG long l;
242: REG int i;
243: REG char *scan;
244: char *blkc;
245:
246: /* detect whether this must be a line-mode cut or char-mode cut */
247: if (markidx(from) == 0 && markidx(to) == 0)
248: lnmode = TRUE;
249: else
250: lnmode = FALSE;
251:
252: /* by default, we don't "delthru" anything */
253: delthru = MARK_UNSET;
254:
255: /* decide which cut buffer to use */
256: if (!cbname)
257: {
258: /* free up the last annonymous cut buffer */
259: cutfree(&annon[NANNONS - 1]);
260:
261: /* shift the annonymous cut buffers */
262: for (i = NANNONS - 1; i > 0; i--)
263: {
264: annon[i] = annon[i - 1];
265: }
266:
267: /* use the first annonymous cut buffer */
268: cb = annon;
269: cb->nblks = 0;
270: }
271: else if (cbname >= 'a' && cbname <= 'z')
272: {
273: cb = &named[cbname - 'a'];
274: cutfree(cb);
275: }
276: #ifndef CRUNCH
277: else if (cbname >= 'A' && cbname <= 'Z')
278: {
279: cb = &named[cbname - 'A'];
280: if (cb->nblks > 0)
281: {
282: /* resolve linemode/charmode differences */
283: if (!lnmode && cb->lnmode)
284: {
285: from &= ~(BLKSIZE - 1);
286: if (markidx(to) != 0 || to == from)
287: {
288: to = to + BLKSIZE - markidx(to);
289: }
290: lnmode = TRUE;
291: }
292:
293: /* insert the old cut-buffer before the new text */
294: mark[28] = to;
295: delthru = paste(from, FALSE, TRUE);
296: if (delthru == MARK_UNSET)
297: {
298: return;
299: }
300: delthru++;
301: to = mark[28];
302: }
303: cutfree(cb);
304: }
305: #endif /* not CRUNCH */
306: else if (cbname == '.')
307: {
308: cb = &named[26];
309: cutfree(cb);
310: }
311: else
312: {
313: msg("Invalid cut buffer name: \"%c", cbname);
314: cbname = '\0';
315: return;
316: }
317: cbname = '\0';
318: cb->fd = -1;
319:
320: /* detect whether we're doing a line mode cut */
321: cb->lnmode = lnmode;
322:
323: /* ---------- */
324:
325: /* Reporting... */
326: if (markidx(from) == 0 && markidx(to) == 0)
327: {
328: rptlines = markline(to) - markline(from);
329: rptlabel = "yanked";
330: }
331:
332: /* ---------- */
333:
334: /* make sure each block has a physical disk address */
335: blksync();
336:
337: /* find the first block in the cut */
338: line = markline(from);
339: for (first = 1; line > lnum[first]; first++)
340: {
341: }
342:
343: /* fetch text of the block containing that line */
344: blkc = scan = blkget(first)->c;
345:
346: /* find the mark in the block */
347: for (l = lnum[first - 1]; ++l < line; )
348: {
349: while (*scan++ != '\n')
350: {
351: }
352: }
353: scan += markidx(from);
354:
355: /* remember the offset of the start */
356: cb->start = scan - blkc;
357:
358: /* ---------- */
359:
360: /* find the last block in the cut */
361: line = markline(to);
362: for (last = first; line > lnum[last]; last++)
363: {
364: }
365:
366: /* fetch text of the block containing that line */
367: if (last != first)
368: {
369: blkc = scan = blkget(last)->c;
370: }
371: else
372: {
373: scan = blkc;
374: }
375:
376: /* find the mark in the block */
377: for (l = lnum[last - 1]; ++l < line; )
378: {
379: while (*scan++ != '\n')
380: {
381: }
382: }
383: if (markline(to) <= nlines)
384: {
385: scan += markidx(to);
386: }
387:
388: /* remember the offset of the end */
389: cb->end = scan - blkc;
390:
391: /* ------- */
392:
393: /* remember the physical block numbers of all included blocks */
394: cb->nblks = last - first;
395: if (cb->end > 0)
396: {
397: cb->nblks++;
398: }
399: #ifdef lint
400: cb->phys = (short *)0;
401: #else
402: cb->phys = (short *)malloc((unsigned)(cb->nblks * sizeof(short)));
403: #endif
404: for (i = 0; i < cb->nblks; i++)
405: {
406: cb->phys[i] = hdr.n[first++];
407: }
408:
409: #ifndef CRUNCH
410: /* if we temporarily inserted text for appending, then delete that
411: * text now -- before the user sees it.
412: */
413: if (delthru)
414: {
415: line = rptlines;
416: delete(from, delthru);
417: rptlines = line;
418: rptlabel = "yanked";
419: }
420: #endif /* not CRUNCH */
421: }
422:
423:
424: static void readcutblk(cb, blkno)
425: struct cutbuf *cb;
426: int blkno;
427: {
428: int fd; /* either tmpfd or cb->fd */
429:
430: /* decide which fd to use */
431: if (cb->fd >= 0)
432: {
433: fd = cb->fd;
434: }
435: else
436: {
437: fd = tmpfd;
438: }
439:
440: /* get the block */
441: lseek(fd, (long)cb->phys[blkno] * (long)BLKSIZE, 0);
442: if (read(fd, tmpblk.c, (unsigned)BLKSIZE) != BLKSIZE)
443: {
444: msg("Error reading back from tmp file for pasting!");
445: }
446: }
447:
448:
449: /* This function inserts text from a cut buffer, and returns the MARK where
450: * insertion ended. Return MARK_UNSET on errors.
451: */
452: MARK paste(at, after, retend)
453: MARK at; /* where to insert the text */
454: int after; /* boolean: insert after mark? (rather than before) */
455: int retend; /* boolean: return end of text? (rather than start) */
456: {
457: REG struct cutbuf *cb;
458: REG int i;
459:
460: /* decide which cut buffer to use */
461: if (cbname >= 'A' && cbname <= 'Z')
462: {
463: cb = &named[cbname - 'A'];
464: }
465: else if (cbname >= 'a' && cbname <= 'z')
466: {
467: cb = &named[cbname - 'a'];
468: }
469: else if (cbname >= '1' && cbname <= '9')
470: {
471: cb = &annon[cbname - '1'];
472: }
473: else if (cbname == '.')
474: {
475: cb = &named[26];
476: }
477: else if (!cbname)
478: {
479: cb = annon;
480: }
481: else
482: {
483: msg("Invalid cut buffer name: \"%c", cbname);
484: cbname = '\0';
485: return MARK_UNSET;
486: }
487:
488: /* make sure it isn't empty */
489: if (cb->nblks == 0)
490: {
491: if (cbname)
492: msg("Cut buffer \"%c is empty", cbname);
493: else
494: msg("Cut buffer is empty");
495: cbname = '\0';
496: return MARK_UNSET;
497: }
498: cbname = '\0';
499:
500: /* adjust the insertion MARK for "after" and line-mode cuts */
501: if (cb->lnmode)
502: {
503: at &= ~(BLKSIZE - 1);
504: if (after)
505: {
506: at += BLKSIZE;
507: }
508: }
509: else if (after)
510: {
511: /* careful! if markidx(at) == 0 we might be pasting into an
512: * empty line -- so we can't blindly increment "at".
513: */
514: if (markidx(at) == 0)
515: {
516: pfetch(markline(at));
517: if (plen != 0)
518: {
519: at++;
520: }
521: }
522: else
523: {
524: at++;
525: }
526: }
527:
528: /* put a copy of the "at" mark in the mark[] array, so it stays in
529: * sync with changes made via add().
530: */
531: mark[27] = at;
532:
533: /* simple one-block paste? */
534: if (cb->nblks == 1)
535: {
536: /* get the block */
537: readcutblk(cb, 0);
538:
539: /* isolate the text we need within it */
540: if (cb->end)
541: {
542: tmpblk.c[cb->end] = '\0';
543: }
544:
545: /* insert it */
546: ChangeText
547: {
548: add(at, &tmpblk.c[cb->start]);
549: }
550: }
551: else
552: {
553: /* multi-block paste */
554:
555: ChangeText
556: {
557: i = cb->nblks - 1;
558:
559: /* add text from the last block first */
560: if (cb->end > 0)
561: {
562: readcutblk(cb, i);
563: tmpblk.c[cb->end] = '\0';
564: add(at, tmpblk.c);
565: i--;
566: }
567:
568: /* add intervening blocks */
569: while (i > 0)
570: {
571: readcutblk(cb, i);
572: add(at, tmpblk.c);
573: i--;
574: }
575:
576: /* add text from the first cut block */
577: readcutblk(cb, 0);
578: add(at, &tmpblk.c[cb->start]);
579: }
580: }
581:
582: /* Reporting... */
583: rptlines = markline(mark[27]) - markline(at);
584: rptlabel = "pasted";
585:
586: /* return the mark at the beginning/end of inserted text */
587: if (retend)
588: {
589: return mark[27] - 1L;
590: }
591: return at;
592: }
593:
594:
595:
596:
597: #ifndef NO_AT
598:
599: /* This function copies characters from a cut buffer into a string.
600: * It returns the number of characters in the cut buffer. If the cut
601: * buffer is too large to fit in the string (i.e. if cb2str() returns
602: * a number >= size) then the characters will not have been copied.
603: * It returns 0 if the cut buffer is empty, and -1 for invalid cut buffers.
604: */
605: int cb2str(name, buf, size)
606: int name; /* the name of a cut-buffer to get: a-z only! */
607: char *buf; /* where to put the string */
608: unsigned size; /* size of buf */
609: {
610: REG struct cutbuf *cb;
611: REG char *src;
612: REG char *dest;
613:
614: /* decide which cut buffer to use */
615: if (name >= 'a' && name <= 'z')
616: {
617: cb = &named[name - 'a'];
618: }
619: else
620: {
621: return -1;
622: }
623:
624: /* if the buffer is empty, return 0 */
625: if (cb->nblks == 0)
626: {
627: return 0;
628: }
629:
630: /* !!! if not a single-block cut, then fail */
631: if (cb->nblks != 1)
632: {
633: return size;
634: }
635:
636: /* if too big, return the size now, without doing anything */
637: if (cb->end - cb->start >= size)
638: {
639: return cb->end - cb->start;
640: }
641:
642: /* get the block */
643: readcutblk(cb, 0);
644:
645: /* isolate the string within that blk */
646: if (cb->start == 0)
647: {
648: tmpblk.c[cb->end] = '\0';
649: }
650: else
651: {
652: for (dest = tmpblk.c, src = dest + cb->start; src < tmpblk.c + cb->end; )
653: {
654: *dest++ = *src++;
655: }
656: *dest = '\0';
657: }
658:
659: /* copy the string into the buffer */
660: if (buf != tmpblk.c)
661: {
662: strcpy(buf, tmpblk.c);
663: }
664:
665: /* return the length */
666: return cb->end - cb->start;
667: }
668: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.