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