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