|
|
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.