|
|
1.1 ! root 1: /* Copyright (c) 1979 Regents of the University of California */ ! 2: #include "ex.h" ! 3: #include "ex_argv.h" ! 4: #include "ex_temp.h" ! 5: #include "ex_tty.h" ! 6: ! 7: /* ! 8: * Command mode subroutines implementing ! 9: * append, args, copy, delete, join, move, put, ! 10: * shift, tag, yank, z and undo ! 11: */ ! 12: ! 13: bool endline = 1; ! 14: line *tad1; ! 15: ! 16: /* ! 17: * Append after line a lines returned by function f. ! 18: * Be careful about intermediate states to avoid scramble ! 19: * if an interrupt comes in. ! 20: */ ! 21: append(f, a) ! 22: int (*f)(); ! 23: line *a; ! 24: { ! 25: register line *a1, *a2, *rdot; ! 26: int nline; ! 27: ! 28: nline = 0; ! 29: dot = a; ! 30: /* ! 31: * This is probably a bug, since it's different than the other tests ! 32: * in appendnone, delete, and deletenone. It is known to fail for ! 33: * the command :g/foo/r xxx (where there is one foo and the file ! 34: * xxx exists) and you try to undo it. I'm leaving it in for now ! 35: * because I'm afraid if I change it I'll break something. ! 36: */ ! 37: if (!inglobal && !inopen && f != getsub) { ! 38: undap1 = undap2 = dot + 1; ! 39: undkind = UNDCHANGE; ! 40: } ! 41: while ((*f)() == 0) { ! 42: if (truedol >= endcore) { ! 43: if (morelines() < 0) { ! 44: if (!inglobal && f == getsub) { ! 45: undap1 = addr1; ! 46: undap2 = addr2 + 1; ! 47: } ! 48: error("Out of memory@- too many lines in file"); ! 49: } ! 50: } ! 51: nline++; ! 52: a1 = truedol + 1; ! 53: a2 = a1 + 1; ! 54: dot++; ! 55: undap2++; ! 56: dol++; ! 57: unddol++; ! 58: truedol++; ! 59: for (rdot = dot; a1 > rdot;) ! 60: *--a2 = *--a1; ! 61: *rdot = 0; ! 62: putmark(rdot); ! 63: if (f == gettty) { ! 64: dirtcnt++; ! 65: TSYNC(); ! 66: } ! 67: } ! 68: return (nline); ! 69: } ! 70: ! 71: appendnone() ! 72: { ! 73: ! 74: if (inopen >= 0 && (inopen || !inglobal)) { ! 75: undkind = UNDCHANGE; ! 76: undap1 = undap2 = addr1; ! 77: } ! 78: } ! 79: ! 80: /* ! 81: * Print out the argument list, with []'s around the current name. ! 82: */ ! 83: pargs() ! 84: { ! 85: register char **av = argv0, *as = args0; ! 86: register int ac; ! 87: ! 88: for (ac = 0; ac < argc0; ac++) { ! 89: if (ac != 0) ! 90: putchar(' '); ! 91: if (ac + argc == argc0 - 1) ! 92: printf("["); ! 93: lprintf("%s", as); ! 94: if (ac + argc == argc0 - 1) ! 95: printf("]"); ! 96: as = av ? *++av : strend(as) + 1; ! 97: } ! 98: noonl(); ! 99: } ! 100: ! 101: /* ! 102: * Delete lines; two cases are if we are really deleting, ! 103: * more commonly we are just moving lines to the undo save area. ! 104: */ ! 105: delete(hush) ! 106: bool hush; ! 107: { ! 108: register line *a1, *a2; ! 109: ! 110: nonzero(); ! 111: if (inopen >= 0 && (inopen || !inglobal)) { ! 112: register int (*dsavint)(); ! 113: ! 114: change(); ! 115: dsavint = signal(SIGINT, SIG_IGN); ! 116: undkind = UNDCHANGE; ! 117: a1 = addr1; ! 118: squish(); ! 119: a2 = addr2; ! 120: if (a2++ != dol) { ! 121: reverse(a1, a2); ! 122: reverse(a2, dol + 1); ! 123: reverse(a1, dol + 1); ! 124: } ! 125: dol -= a2 - a1; ! 126: unddel = a1 - 1; ! 127: if (a1 > dol) ! 128: a1 = dol; ! 129: dot = a1; ! 130: pkill[0] = pkill[1] = 0; ! 131: signal(SIGINT, dsavint); ! 132: } else { ! 133: register line *a3; ! 134: register int i; ! 135: ! 136: change(); ! 137: a1 = addr1; ! 138: a2 = addr2 + 1; ! 139: a3 = truedol; ! 140: i = a2 - a1; ! 141: unddol -= i; ! 142: undap2 -= i; ! 143: dol -= i; ! 144: truedol -= i; ! 145: do ! 146: *a1++ = *a2++; ! 147: while (a2 <= a3); ! 148: a1 = addr1; ! 149: if (a1 > dol) ! 150: a1 = dol; ! 151: dot = a1; ! 152: } ! 153: if (!hush) ! 154: killed(); ! 155: } ! 156: ! 157: deletenone() ! 158: { ! 159: ! 160: if (inopen >= 0 && (inopen || !inglobal)) { ! 161: undkind = UNDCHANGE; ! 162: squish(); ! 163: unddel = addr1; ! 164: } ! 165: } ! 166: ! 167: /* ! 168: * Crush out the undo save area, moving the open/visual ! 169: * save area down in its place. ! 170: */ ! 171: squish() ! 172: { ! 173: register line *a1 = dol + 1, *a2 = unddol + 1, *a3 = truedol + 1; ! 174: ! 175: if (inopen == -1) ! 176: return; ! 177: if (a1 < a2 && a2 < a3) ! 178: do ! 179: *a1++ = *a2++; ! 180: while (a2 < a3); ! 181: truedol -= unddol - dol; ! 182: unddol = dol; ! 183: } ! 184: ! 185: /* ! 186: * Join lines. Special hacks put in spaces, two spaces if ! 187: * preceding line ends with '.', or no spaces if next line starts with ). ! 188: */ ! 189: static int jcount, jnoop(); ! 190: ! 191: join(c) ! 192: int c; ! 193: { ! 194: register line *a1; ! 195: register char *cp, *cp1; ! 196: ! 197: cp = genbuf; ! 198: *cp = 0; ! 199: for (a1 = addr1; a1 <= addr2; a1++) { ! 200: getline(*a1); ! 201: cp1 = linebuf; ! 202: if (a1 != addr1 && c == 0) { ! 203: while (*cp1 == ' ' || *cp1 == '\t') ! 204: cp1++; ! 205: if (*cp1 && cp > genbuf && cp[-1] != ' ' && cp[-1] != '\t') { ! 206: if (*cp1 != ')') { ! 207: *cp++ = ' '; ! 208: if (cp[-2] == '.') ! 209: *cp++ = ' '; ! 210: } ! 211: } ! 212: } ! 213: while (*cp++ = *cp1++) ! 214: if (cp > &genbuf[LBSIZE-2]) ! 215: error("Line overflow|Result line of join would be too long"); ! 216: cp--; ! 217: } ! 218: strcLIN(genbuf); ! 219: delete(0); ! 220: jcount = 1; ! 221: ignore(append(jnoop, --addr1)); ! 222: } ! 223: ! 224: static ! 225: jnoop() ! 226: { ! 227: ! 228: return(--jcount); ! 229: } ! 230: ! 231: /* ! 232: * Move and copy lines. Hard work is done by move1 which ! 233: * is also called by undo. ! 234: */ ! 235: int getcopy(); ! 236: ! 237: move() ! 238: { ! 239: register line *adt; ! 240: bool iscopy = 0; ! 241: ! 242: if (Command[0] == 'm') { ! 243: setdot1(); ! 244: markpr(addr2 == dot ? addr1 - 1 : addr2 + 1); ! 245: } else { ! 246: iscopy++; ! 247: setdot(); ! 248: } ! 249: nonzero(); ! 250: adt = address(0); ! 251: if (adt == 0) ! 252: serror("%s where?|%s requires a trailing address", Command); ! 253: newline(); ! 254: move1(iscopy, adt); ! 255: killed(); ! 256: } ! 257: ! 258: move1(cflag, addrt) ! 259: int cflag; ! 260: line *addrt; ! 261: { ! 262: register line *adt, *ad1, *ad2; ! 263: int lines; ! 264: ! 265: adt = addrt; ! 266: lines = (addr2 - addr1) + 1; ! 267: if (cflag) { ! 268: tad1 = addr1; ! 269: ad1 = dol; ! 270: ignore(append(getcopy, ad1++)); ! 271: ad2 = dol; ! 272: } else { ! 273: ad2 = addr2; ! 274: for (ad1 = addr1; ad1 <= ad2;) ! 275: *ad1++ &= ~01; ! 276: ad1 = addr1; ! 277: } ! 278: ad2++; ! 279: if (adt < ad1) { ! 280: if (adt + 1 == ad1 && !cflag && !inglobal) ! 281: error("That move would do nothing!"); ! 282: dot = adt + (ad2 - ad1); ! 283: if (++adt != ad1) { ! 284: reverse(adt, ad1); ! 285: reverse(ad1, ad2); ! 286: reverse(adt, ad2); ! 287: } ! 288: } else if (adt >= ad2) { ! 289: dot = adt++; ! 290: reverse(ad1, ad2); ! 291: reverse(ad2, adt); ! 292: reverse(ad1, adt); ! 293: } else ! 294: error("Move to a moved line"); ! 295: change(); ! 296: if (!inglobal) ! 297: if (cflag) { ! 298: undap1 = addrt + 1; ! 299: undap2 = undap1 + lines; ! 300: deletenone(); ! 301: } else { ! 302: undkind = UNDMOVE; ! 303: undap1 = addr1; ! 304: undap2 = addr2; ! 305: unddel = addrt; ! 306: squish(); ! 307: } ! 308: } ! 309: ! 310: getcopy() ! 311: { ! 312: ! 313: if (tad1 > addr2) ! 314: return (EOF); ! 315: getline(*tad1++); ! 316: return (0); ! 317: } ! 318: ! 319: /* ! 320: * Put lines in the buffer from the undo save area. ! 321: */ ! 322: getput() ! 323: { ! 324: ! 325: if (tad1 > unddol) ! 326: return (EOF); ! 327: getline(*tad1++); ! 328: tad1++; ! 329: return (0); ! 330: } ! 331: ! 332: put() ! 333: { ! 334: register int cnt; ! 335: ! 336: cnt = unddol - dol; ! 337: if (cnt && inopen && pkill[0] && pkill[1]) { ! 338: pragged(1); ! 339: return; ! 340: } ! 341: tad1 = dol + 1; ! 342: ignore(append(getput, addr2)); ! 343: undkind = UNDPUT; ! 344: notecnt = cnt; ! 345: netchange(cnt); ! 346: } ! 347: ! 348: /* ! 349: * A tricky put, of a group of lines in the middle ! 350: * of an existing line. Only from open/visual. ! 351: * Argument says pkills have meaning, e.g. called from ! 352: * put; it is 0 on calls from putreg. ! 353: */ ! 354: pragged(kill) ! 355: bool kill; ! 356: { ! 357: extern char *cursor; ! 358: register char *gp = &genbuf[cursor - linebuf]; ! 359: ! 360: /* ! 361: * This kind of stuff is TECO's forte. ! 362: * We just grunge along, since it cuts ! 363: * across our line-oriented model of the world ! 364: * almost scrambling our addled brain. ! 365: */ ! 366: if (!kill) ! 367: getDOT(); ! 368: strcpy(genbuf, linebuf); ! 369: getline(*unddol); ! 370: if (kill) ! 371: *pkill[1] = 0; ! 372: strcat(linebuf, gp); ! 373: putmark(unddol); ! 374: getline(dol[1]); ! 375: if (kill) ! 376: strcLIN(pkill[0]); ! 377: strcpy(gp, linebuf); ! 378: strcLIN(genbuf); ! 379: putmark(dol+1); ! 380: undkind = UNDCHANGE; ! 381: undap1 = dot; ! 382: undap2 = dot + 1; ! 383: unddel = dot - 1; ! 384: undo(1); ! 385: } ! 386: ! 387: /* ! 388: * Shift lines, based on c. ! 389: * If c is neither < nor >, then this is a lisp aligning =. ! 390: */ ! 391: shift(c, cnt) ! 392: int c; ! 393: int cnt; ! 394: { ! 395: register line *addr; ! 396: register char *cp; ! 397: char *dp; ! 398: register int i; ! 399: ! 400: if (!inglobal) ! 401: save12(), undkind = UNDCHANGE; ! 402: cnt *= value(SHIFTWIDTH); ! 403: for (addr = addr1; addr <= addr2; addr++) { ! 404: dot = addr; ! 405: #ifdef LISPCODE ! 406: if (c == '=' && addr == addr1 && addr != addr2) ! 407: continue; ! 408: #endif ! 409: getDOT(); ! 410: i = whitecnt(linebuf); ! 411: switch (c) { ! 412: ! 413: case '>': ! 414: if (linebuf[0] == 0) ! 415: continue; ! 416: cp = genindent(i + cnt); ! 417: break; ! 418: ! 419: case '<': ! 420: if (i == 0) ! 421: continue; ! 422: i -= cnt; ! 423: cp = i > 0 ? genindent(i) : genbuf; ! 424: break; ! 425: ! 426: #ifdef LISPCODE ! 427: default: ! 428: i = lindent(addr); ! 429: getDOT(); ! 430: cp = genindent(i); ! 431: break; ! 432: #endif ! 433: } ! 434: if (cp + strlen(dp = vpastwh(linebuf)) >= &genbuf[LBSIZE - 2]) ! 435: error("Line too long|Result line after shift would be too long"); ! 436: CP(cp, dp); ! 437: strcLIN(genbuf); ! 438: putmark(addr); ! 439: } ! 440: killed(); ! 441: } ! 442: ! 443: /* ! 444: * Find a tag in the tags file. ! 445: * Most work here is in parsing the tags file itself. ! 446: */ ! 447: tagfind(quick) ! 448: bool quick; ! 449: { ! 450: char cmdbuf[BUFSIZ]; ! 451: char filebuf[FNSIZE]; ! 452: register int c, d; ! 453: bool samef = 1; ! 454: bool notagsfile = 0; ! 455: short master = -1; ! 456: short omagic; ! 457: ! 458: omagic = value(MAGIC); ! 459: if (!skipend()) { ! 460: register char *lp = lasttag; ! 461: ! 462: while (!iswhite(peekchar()) && !endcmd(peekchar())) ! 463: if (lp < &lasttag[sizeof lasttag - 2]) ! 464: *lp++ = getchar(); ! 465: else ! 466: ignchar(); ! 467: *lp++ = 0; ! 468: if (!endcmd(peekchar())) ! 469: badtag: ! 470: error("Bad tag|Give one tag per line"); ! 471: } else if (lasttag[0] == 0) ! 472: error("No previous tag"); ! 473: c = getchar(); ! 474: if (!endcmd(c)) ! 475: goto badtag; ! 476: if (c == EOF) ! 477: ungetchar(c); ! 478: clrstats(); ! 479: do { ! 480: io = open(master ? "tags" : MASTERTAGS, 0); ! 481: if (master && io < 0) ! 482: notagsfile = 1; ! 483: while (getfile() == 0) { ! 484: register char *cp = linebuf; ! 485: register char *lp = lasttag; ! 486: char *oglobp; ! 487: ! 488: while (*cp && *lp == *cp) ! 489: cp++, lp++; ! 490: if (*lp || !iswhite(*cp)) ! 491: continue; ! 492: close(io); ! 493: while (*cp && iswhite(*cp)) ! 494: cp++; ! 495: if (!*cp) ! 496: badtags: ! 497: serror("%s: Bad tags file entry", lasttag); ! 498: lp = filebuf; ! 499: while (*cp && *cp != ' ' && *cp != '\t') { ! 500: if (lp < &filebuf[sizeof filebuf - 2]) ! 501: *lp++ = *cp; ! 502: cp++; ! 503: } ! 504: *lp++ = 0; ! 505: if (*cp == 0) ! 506: goto badtags; ! 507: if (dol != zero) { ! 508: /* ! 509: * Save current position in 't for ^^ in visual. ! 510: */ ! 511: names['t'-'a'] = *dot &~ 01; ! 512: if (inopen) { ! 513: extern char *ncols['z'-'a'+1]; ! 514: extern char *cursor; ! 515: ! 516: ncols['t'-'a'] = cursor; ! 517: } ! 518: } ! 519: strcpy(cmdbuf, cp); ! 520: if (strcmp(filebuf, savedfile) || !edited) { ! 521: char cmdbuf2[sizeof filebuf + 10]; ! 522: ! 523: if (!quick) { ! 524: ckaw(); ! 525: if (chng && dol > zero) ! 526: error("No write@since last change (:tag! overrides)"); ! 527: } ! 528: oglobp = globp; ! 529: strcpy(cmdbuf2, "e! "); ! 530: strcat(cmdbuf2, filebuf); ! 531: globp = cmdbuf2; ! 532: d = peekc; ungetchar(0); ! 533: /* ! 534: * BUG: if it isn't found (user edited header ! 535: * line) we get left in nomagic mode. ! 536: */ ! 537: value(MAGIC) = 0; ! 538: commands(1, 1); ! 539: peekc = d; ! 540: globp = oglobp; ! 541: value(MAGIC) = omagic; ! 542: samef = 0; ! 543: } ! 544: oglobp = globp; ! 545: globp = cmdbuf; ! 546: d = peekc; ungetchar(0); ! 547: if (samef) ! 548: markpr(dot); ! 549: value(MAGIC) = 0; ! 550: commands(1, 1); ! 551: peekc = d; ! 552: globp = oglobp; ! 553: value(MAGIC) = omagic; ! 554: return; ! 555: } ! 556: } while (++master == 0); ! 557: if (notagsfile) ! 558: error("No tags file"); ! 559: serror("%s: No such tag@in tags file", lasttag); ! 560: } ! 561: ! 562: /* ! 563: * Save lines from addr1 thru addr2 as though ! 564: * they had been deleted. ! 565: */ ! 566: yank() ! 567: { ! 568: ! 569: save12(); ! 570: undkind = UNDNONE; ! 571: killcnt(addr2 - addr1 + 1); ! 572: } ! 573: ! 574: /* ! 575: * z command; print windows of text in the file. ! 576: * ! 577: * If this seems unreasonably arcane, the reasons ! 578: * are historical. This is one of the first commands ! 579: * added to the first ex (then called en) and the ! 580: * number of facilities here were the major advantage ! 581: * of en over ed since they allowed more use to be ! 582: * made of fast terminals w/o typing .,.22p all the time. ! 583: */ ! 584: bool zhadpr; ! 585: bool znoclear; ! 586: short zweight; ! 587: ! 588: zop(hadpr) ! 589: int hadpr; ! 590: { ! 591: register int c, lines, op; ! 592: bool excl; ! 593: ! 594: zhadpr = hadpr; ! 595: notempty(); ! 596: znoclear = 0; ! 597: zweight = 0; ! 598: excl = exclam(); ! 599: switch (c = op = getchar()) { ! 600: ! 601: case '^': ! 602: zweight = 1; ! 603: case '-': ! 604: case '+': ! 605: while (peekchar() == op) { ! 606: ignchar(); ! 607: zweight++; ! 608: } ! 609: case '=': ! 610: case '.': ! 611: c = getchar(); ! 612: break; ! 613: ! 614: case EOF: ! 615: znoclear++; ! 616: break; ! 617: ! 618: default: ! 619: op = 0; ! 620: break; ! 621: } ! 622: if (isdigit(c)) { ! 623: lines = c - '0'; ! 624: for(;;) { ! 625: c = getchar(); ! 626: if (!isdigit(c)) ! 627: break; ! 628: lines *= 10; ! 629: lines += c - '0'; ! 630: } ! 631: if (lines < LINES) ! 632: znoclear++; ! 633: value(WINDOW) = lines; ! 634: if (op == '=') ! 635: lines += 2; ! 636: } else ! 637: lines = op == EOF ? value(SCROLL) : excl ? LINES - 1 : 2*value(SCROLL); ! 638: if (inopen || c != EOF) { ! 639: ungetchar(c); ! 640: newline(); ! 641: } ! 642: addr1 = addr2; ! 643: if (addr2 == 0 && dot < dol && op == 0) ! 644: addr1 = addr2 = dot+1; ! 645: setdot(); ! 646: zop2(lines, op); ! 647: } ! 648: ! 649: zop2(lines, op) ! 650: register int lines; ! 651: register int op; ! 652: { ! 653: register line *split; ! 654: ! 655: split = NULL; ! 656: switch (op) { ! 657: ! 658: case EOF: ! 659: if (addr2 == dol) ! 660: error("\nAt EOF"); ! 661: case '+': ! 662: if (addr2 == dol) ! 663: error("At EOF"); ! 664: addr2 += lines * zweight; ! 665: if (addr2 > dol) ! 666: error("Hit BOTTOM"); ! 667: addr2++; ! 668: default: ! 669: addr1 = addr2; ! 670: addr2 += lines-1; ! 671: dot = addr2; ! 672: break; ! 673: ! 674: case '=': ! 675: case '.': ! 676: znoclear = 0; ! 677: lines--; ! 678: lines >>= 1; ! 679: if (op == '=') ! 680: lines--; ! 681: addr1 = addr2 - lines; ! 682: if (op == '=') ! 683: dot = split = addr2; ! 684: addr2 += lines; ! 685: if (op == '.') { ! 686: markDOT(); ! 687: dot = addr2; ! 688: } ! 689: break; ! 690: ! 691: case '^': ! 692: case '-': ! 693: addr2 -= lines * zweight; ! 694: if (addr2 < one) ! 695: error("Hit TOP"); ! 696: lines--; ! 697: addr1 = addr2 - lines; ! 698: dot = addr2; ! 699: break; ! 700: } ! 701: if (addr1 <= zero) ! 702: addr1 = one; ! 703: if (addr2 > dol) ! 704: addr2 = dol; ! 705: if (dot > dol) ! 706: dot = dol; ! 707: if (addr1 > addr2) ! 708: return; ! 709: if (op == EOF && zhadpr) { ! 710: getline(*addr1); ! 711: putchar('\r' | QUOTE); ! 712: shudclob = 1; ! 713: } else if (znoclear == 0 && CL != NOSTR && !inopen) { ! 714: flush1(); ! 715: vclear(); ! 716: } ! 717: if (addr2 - addr1 > 1) ! 718: pstart(); ! 719: if (split) { ! 720: plines(addr1, split - 1, 0); ! 721: splitit(); ! 722: plines(split, split, 0); ! 723: splitit(); ! 724: addr1 = split + 1; ! 725: } ! 726: plines(addr1, addr2, 0); ! 727: } ! 728: ! 729: static ! 730: splitit() ! 731: { ! 732: register int l; ! 733: ! 734: for (l = COLUMNS > 80 ? 40 : COLUMNS / 2; l > 0; l--) ! 735: putchar('-'); ! 736: putnl(); ! 737: } ! 738: ! 739: plines(adr1, adr2, movedot) ! 740: line *adr1; ! 741: register line *adr2; ! 742: bool movedot; ! 743: { ! 744: register line *addr; ! 745: ! 746: pofix(); ! 747: for (addr = adr1; addr <= adr2; addr++) { ! 748: getline(*addr); ! 749: pline(lineno(addr)); ! 750: if (inopen) ! 751: putchar('\n' | QUOTE); ! 752: if (movedot) ! 753: dot = addr; ! 754: } ! 755: } ! 756: ! 757: pofix() ! 758: { ! 759: ! 760: if (inopen && Outchar != termchar) { ! 761: vnfl(); ! 762: setoutt(); ! 763: } ! 764: } ! 765: ! 766: /* ! 767: * Dudley doright to the rescue. ! 768: * Undo saves the day again. ! 769: * A tip of the hatlo hat to Warren Teitleman ! 770: * who made undo as useful as do. ! 771: * ! 772: * Command level undo works easily because ! 773: * the editor has a unique temporary file ! 774: * index for every line which ever existed. ! 775: * We don't have to save large blocks of text, ! 776: * only the indices which are small. We do this ! 777: * by moving them to after the last line in the ! 778: * line buffer array, and marking down info ! 779: * about whence they came. ! 780: * ! 781: * Undo is its own inverse. ! 782: */ ! 783: undo(c) ! 784: bool c; ! 785: { ! 786: register int i; ! 787: register line *jp, *kp; ! 788: line *dolp1, *newdol, *newadot; ! 789: ! 790: if (inglobal && inopen <= 0) ! 791: error("Can't undo in global@commands"); ! 792: if (!c) ! 793: somechange(); ! 794: pkill[0] = pkill[1] = 0; ! 795: change(); ! 796: if (undkind == UNDMOVE) { ! 797: /* ! 798: * Command to be undone is a move command. ! 799: * This is handled as a special case by noting that ! 800: * a move "a,b m c" can be inverted by another move. ! 801: */ ! 802: if ((i = (jp = unddel) - undap2) > 0) { ! 803: /* ! 804: * when c > b inverse is a+(c-b),c m a-1 ! 805: */ ! 806: addr2 = jp; ! 807: addr1 = (jp = undap1) + i; ! 808: unddel = jp-1; ! 809: } else { ! 810: /* ! 811: * when b > c inverse is c+1,c+1+(b-a) m b ! 812: */ ! 813: addr1 = ++jp; ! 814: addr2 = jp + ((unddel = undap2) - undap1); ! 815: } ! 816: kp = undap1; ! 817: move1(0, unddel); ! 818: dot = kp; ! 819: Command = "move"; ! 820: killed(); ! 821: } else { ! 822: int cnt; ! 823: ! 824: newadot = dot; ! 825: cnt = lineDOL(); ! 826: newdol = dol; ! 827: dolp1 = dol + 1; ! 828: /* ! 829: * Command to be undone is a non-move. ! 830: * All such commands are treated as a combination of ! 831: * a delete command and a append command. ! 832: * We first move the lines appended by the last command ! 833: * from undap1 to undap2-1 so that they are just before the ! 834: * saved deleted lines. ! 835: */ ! 836: if ((i = (kp = undap2) - (jp = undap1)) > 0) { ! 837: if (kp != dolp1) { ! 838: reverse(jp, kp); ! 839: reverse(kp, dolp1); ! 840: reverse(jp, dolp1); ! 841: } ! 842: /* ! 843: * Account for possible backward motion of target ! 844: * for restoration of saved deleted lines. ! 845: */ ! 846: if (unddel >= jp) ! 847: unddel -= i; ! 848: newdol -= i; ! 849: /* ! 850: * For the case where no lines are restored, dot ! 851: * is the line before the first line deleted. ! 852: */ ! 853: dot = jp-1; ! 854: } ! 855: /* ! 856: * Now put the deleted lines, if any, back where they were. ! 857: * Basic operation is: dol+1,unddol m unddel ! 858: */ ! 859: if (undkind == UNDPUT) { ! 860: unddel = undap1 - 1; ! 861: squish(); ! 862: } ! 863: jp = unddel + 1; ! 864: if ((i = (kp = unddol) - dol) > 0) { ! 865: if (jp != dolp1) { ! 866: reverse(jp, dolp1); ! 867: reverse(dolp1, ++kp); ! 868: reverse(jp, kp); ! 869: } ! 870: /* ! 871: * Account for possible forward motion of the target ! 872: * for restoration of the deleted lines. ! 873: */ ! 874: if (undap1 >= jp) ! 875: undap1 += i; ! 876: /* ! 877: * Dot is the first resurrected line. ! 878: */ ! 879: dot = jp; ! 880: newdol += i; ! 881: } ! 882: /* ! 883: * Clean up so we are invertible ! 884: */ ! 885: unddel = undap1 - 1; ! 886: undap1 = jp; ! 887: undap2 = jp + i; ! 888: dol = newdol; ! 889: netchHAD(cnt); ! 890: if (undkind == UNDALL) { ! 891: dot = undadot; ! 892: undadot = newadot; ! 893: } ! 894: undkind = UNDCHANGE; ! 895: } ! 896: if (dot == zero && dot != dol) ! 897: dot = one; ! 898: } ! 899: ! 900: /* ! 901: * Be (almost completely) sure there really ! 902: * was a change, before claiming to undo. ! 903: */ ! 904: somechange() ! 905: { ! 906: register line *ip, *jp; ! 907: ! 908: switch (undkind) { ! 909: ! 910: case UNDMOVE: ! 911: return; ! 912: ! 913: case UNDCHANGE: ! 914: if (undap1 == undap2 && dol == unddol) ! 915: break; ! 916: return; ! 917: ! 918: case UNDPUT: ! 919: if (undap1 != undap2) ! 920: return; ! 921: break; ! 922: ! 923: case UNDALL: ! 924: if (unddol - dol != lineDOL()) ! 925: return; ! 926: for (ip = one, jp = dol + 1; ip <= dol; ip++, jp++) ! 927: if ((*ip &~ 01) != (*jp &~ 01)) ! 928: return; ! 929: break; ! 930: ! 931: case UNDNONE: ! 932: error("Nothing to undo"); ! 933: } ! 934: error("Nothing changed|Last undoable command didn't change anything"); ! 935: } ! 936: ! 937: /* ! 938: * Map command: ! 939: * map src dest ! 940: */ ! 941: mapcmd(un) ! 942: int un; /* true if this is unmap command */ ! 943: { ! 944: char lhs[10], rhs[100]; /* max sizes resp. */ ! 945: register char *p; ! 946: register char c; ! 947: char *dname; ! 948: ! 949: if (skipend()) { ! 950: int i; ! 951: ! 952: /* print current mapping values */ ! 953: if (peekchar() != EOF) ! 954: ignchar(); ! 955: if (inopen) ! 956: pofix(); ! 957: for (i=0; arrows[i].mapto; i++) ! 958: if (arrows[i].cap) { ! 959: lprintf("%s", arrows[i].descr); ! 960: putchar('\t'); ! 961: lprintf("%s", arrows[i].cap); ! 962: putchar('\t'); ! 963: lprintf("%s", arrows[i].mapto); ! 964: putNFL(); ! 965: } ! 966: return; ! 967: } ! 968: ! 969: ignore(skipwh()); ! 970: for (p=lhs; ; ) { ! 971: c = getchar(); ! 972: if (c == CTRL(v)) { ! 973: c = getchar(); ! 974: } else if (any(c, " \t")) { ! 975: if (un) ! 976: eol(); /* will usually cause an error */ ! 977: else ! 978: break; ! 979: } else if (endcmd(c)) { ! 980: ungetchar(c); ! 981: if (un) { ! 982: newline(); ! 983: addmac(lhs, NOSTR, NOSTR); ! 984: return; ! 985: } else ! 986: error("Missing rhs"); ! 987: } ! 988: *p++ = c; ! 989: } ! 990: *p = 0; ! 991: ! 992: if (skipend()) ! 993: error("Missing rhs"); ! 994: for (p=rhs; ; ) { ! 995: c = getchar(); ! 996: if (c == CTRL(v)) { ! 997: c = getchar(); ! 998: } else if (endcmd(c)) { ! 999: ungetchar(c); ! 1000: break; ! 1001: } ! 1002: *p++ = c; ! 1003: } ! 1004: *p = 0; ! 1005: newline(); ! 1006: /* ! 1007: * Special hack for function keys: #1 means key f1, etc. ! 1008: * If the terminal doesn't have function keys, we just use #1. ! 1009: */ ! 1010: if (lhs[0] == '#') { ! 1011: char *fnkey; ! 1012: char *fkey(); ! 1013: char funkey[3]; ! 1014: ! 1015: fnkey = fkey(lhs[1] - '0'); ! 1016: funkey[0] = 'f'; funkey[1] = lhs[1]; funkey[2] = 0; ! 1017: if (fnkey) ! 1018: strcpy(lhs, fnkey); ! 1019: dname = funkey; ! 1020: } else { ! 1021: dname = lhs; ! 1022: } ! 1023: addmac(lhs,rhs,dname); ! 1024: } ! 1025: ! 1026: /* ! 1027: * Add a macro definition to those that already exist. The sequence of ! 1028: * chars "src" is mapped into "dest". If src is already mapped into something ! 1029: * this overrides the mapping. There is no recursion. Unmap is done by ! 1030: * using NOSTR for dest. ! 1031: */ ! 1032: addmac(src,dest,dname) ! 1033: register char *src, *dest, *dname; ! 1034: { ! 1035: register int slot, zer; ! 1036: ! 1037: if (dest) { ! 1038: /* Make sure user doesn't screw himself */ ! 1039: /* ! 1040: * Prevent tail recursion. We really should be ! 1041: * checking to see if src is a suffix of dest ! 1042: * but we are too lazy here, so we don't bother unless ! 1043: * src is only 1 char long. ! 1044: */ ! 1045: if (src[1] == 0 && src[0] == dest[strlen(dest)-1]) ! 1046: error("No tail recursion"); ! 1047: /* ! 1048: * We don't let the user rob himself of ":", and making ! 1049: * multi char words is a bad idea so we don't allow it. ! 1050: * Note that if user sets mapinput and maps all of return, ! 1051: * linefeed, and escape, he can screw himself. This is ! 1052: * so weird I don't bother to check for it. ! 1053: */ ! 1054: if (isalpha(src[0]) && src[1] || any(src[0],":")) ! 1055: error("Too dangerous to map that"); ! 1056: /* ! 1057: * If the src were null it would cause the dest to ! 1058: * be mapped always forever. This is not good. ! 1059: */ ! 1060: if (src[0] == 0) ! 1061: error("Null lhs"); ! 1062: } ! 1063: ! 1064: /* see if we already have a def for src */ ! 1065: zer = -1; ! 1066: for (slot=0; arrows[slot].mapto; slot++) { ! 1067: if (arrows[slot].cap) { ! 1068: if (eq(src, arrows[slot].cap)) ! 1069: break; /* if so, reuse slot */ ! 1070: } else { ! 1071: zer = slot; /* remember an empty slot */ ! 1072: } ! 1073: } ! 1074: ! 1075: if (dest == NOSTR) { ! 1076: /* unmap */ ! 1077: if (arrows[slot].cap) { ! 1078: arrows[slot].cap = NOSTR; ! 1079: arrows[slot].descr = NOSTR; ! 1080: } else { ! 1081: error("Not mapped|That macro wasn't mapped"); ! 1082: } ! 1083: return; ! 1084: } ! 1085: ! 1086: /* reuse empty slot, if we found one and src isn't already defined */ ! 1087: if (zer >= 0 && arrows[slot].mapto == 0) ! 1088: slot = zer; ! 1089: ! 1090: /* if not, append to end */ ! 1091: if (slot >= MAXNOMACS) ! 1092: error("Too many macros"); ! 1093: if (msnext == 0) /* first time */ ! 1094: msnext = mapspace; ! 1095: /* Check is a bit conservative, we charge for dname even if reusing src */ ! 1096: if (msnext - mapspace + strlen(dest) + strlen(src) + strlen(dname) + 3 > MAXCHARMACS) ! 1097: error("Too much macro text"); ! 1098: CP(msnext, src); ! 1099: arrows[slot].cap = msnext; ! 1100: msnext += strlen(src) + 1; /* plus 1 for null on the end */ ! 1101: CP(msnext, dest); ! 1102: arrows[slot].mapto = msnext; ! 1103: msnext += strlen(dest) + 1; ! 1104: if (dname) { ! 1105: CP(msnext, dname); ! 1106: arrows[slot].descr = msnext; ! 1107: msnext += strlen(dname) + 1; ! 1108: } else { ! 1109: /* default descr to string user enters */ ! 1110: arrows[slot].descr = src; ! 1111: } ! 1112: } ! 1113: ! 1114: /* ! 1115: * Implements macros from command mode. c is the buffer to ! 1116: * get the macro from. ! 1117: */ ! 1118: cmdmac(c) ! 1119: char c; ! 1120: { ! 1121: char macbuf[BUFSIZ]; ! 1122: line *ad, *a1, *a2; ! 1123: char *oglobp; ! 1124: char pk; ! 1125: bool oinglobal; ! 1126: ! 1127: lastmac = c; ! 1128: oglobp = globp; ! 1129: oinglobal = inglobal; ! 1130: pk = peekc; peekc = 0; ! 1131: if (inglobal < 2) ! 1132: inglobal = 1; ! 1133: regbuf(c, macbuf, sizeof(macbuf)); ! 1134: a1 = addr1; a2 = addr2; ! 1135: for (ad=a1; ad<=a2; ad++) { ! 1136: globp = macbuf; ! 1137: dot = ad; ! 1138: commands(1,1); ! 1139: } ! 1140: globp = oglobp; ! 1141: inglobal = oinglobal; ! 1142: peekc = pk; ! 1143: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.