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