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