source: trunk/minix/commands/elvis/cut.c@ 10

Last change on this file since 10 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 13.2 KB
Line 
1/* cut.c */
2
3/* Author:
4 * Steve Kirkendall
5 * 14407 SW Teal Blvd. #C
6 * Beaverton, OR 97005
7 * kirkenda@cs.pdx.edu
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
25static 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
37static char cbname; /* name chosen for next cut/paste operation */
38static 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 */
48int 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
86static 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. */
134static 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 */
164void 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 */
184void 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 */
205void 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 */
215void 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
421static 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 */
467MARK 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 */
637int 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
Note: See TracBrowser for help on using the repository browser.