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