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