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) 06/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 } 1293