1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright (c) 2016 by Delphix. All rights reserved. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 31 /* Copyright (c) 1981 Regents of the University of California */ 32 33 #include "ex.h" 34 #include "ex_argv.h" 35 #include "ex_temp.h" 36 #include "ex_tty.h" 37 #include "ex_vis.h" 38 #ifdef STDIO 39 #include <stdio.h> 40 #undef getchar 41 #undef putchar 42 #endif 43 44 45 /* 46 * Command mode subroutines implementing 47 * append, args, copy, delete, join, move, put, 48 * shift, tag, yank, z and undo 49 */ 50 51 bool endline = 1; 52 line *tad1; 53 static int jnoop(void); 54 static void splitit(void); 55 int putchar(), getchar(); 56 int tags_flag; 57 58 /* 59 * Append after line a lines returned by function f. 60 * Be careful about intermediate states to avoid scramble 61 * if an interrupt comes in. 62 */ 63 int 64 append(int (*f)(), line *a) 65 { 66 line *a1, *a2, *rdot; 67 int nline; 68 69 nline = 0; 70 dot = a; 71 if(FIXUNDO && !inopen && f!=getsub) { 72 undap1 = undap2 = dot + 1; 73 undkind = UNDCHANGE; 74 } 75 while ((*f)() == 0) { 76 if (truedol >= endcore) { 77 if (morelines() < 0) { 78 if (FIXUNDO && f == getsub) { 79 undap1 = addr1; 80 undap2 = addr2 + 1; 81 } 82 error(value(vi_TERSE) ? gettext("Out of memory") : 83 gettext("Out of memory- too many lines in file")); 84 } 85 } 86 nline++; 87 a1 = truedol + 1; 88 a2 = a1 + 1; 89 dot++; 90 undap2++; 91 dol++; 92 unddol++; 93 truedol++; 94 for (rdot = dot; a1 > rdot;) 95 *--a2 = *--a1; 96 *rdot = 0; 97 putmark(rdot); 98 if (f == gettty) { 99 dirtcnt++; 100 TSYNC(); 101 } 102 } 103 return (nline); 104 } 105 106 void 107 appendnone(void) 108 { 109 110 if(FIXUNDO) { 111 undkind = UNDCHANGE; 112 undap1 = undap2 = addr1; 113 } 114 } 115 116 /* 117 * Print out the argument list, with []'s around the current name. 118 */ 119 void 120 pargs(void) 121 { 122 unsigned char **av = argv0, *as = args0; 123 int ac; 124 125 for (ac = 0; ac < argc0; ac++) { 126 if (ac != 0) 127 putchar(' '); 128 if (ac + argc == argc0 - 1) 129 viprintf("["); 130 lprintf("%s", as); 131 if (ac + argc == argc0 - 1) 132 viprintf("]"); 133 as = av ? *++av : strend(as) + 1; 134 } 135 noonl(); 136 } 137 138 /* 139 * Delete lines; two cases are if we are really deleting, 140 * more commonly we are just moving lines to the undo save area. 141 */ 142 int 143 delete(bool hush) 144 { 145 line *a1, *a2; 146 147 nonzero(); 148 if(FIXUNDO) { 149 void (*dsavint)(); 150 151 #ifdef UNDOTRACE 152 if (trace) 153 vudump("before delete"); 154 #endif 155 change(); 156 dsavint = signal(SIGINT, SIG_IGN); 157 undkind = UNDCHANGE; 158 a1 = addr1; 159 squish(); 160 a2 = addr2; 161 if (a2++ != dol) { 162 reverse(a1, a2); 163 reverse(a2, dol + 1); 164 reverse(a1, dol + 1); 165 } 166 dol -= a2 - a1; 167 unddel = a1 - 1; 168 if (a1 > dol) 169 a1 = dol; 170 dot = a1; 171 pkill[0] = pkill[1] = 0; 172 signal(SIGINT, dsavint); 173 #ifdef UNDOTRACE 174 if (trace) 175 vudump("after delete"); 176 #endif 177 } else { 178 line *a3; 179 int i; 180 181 change(); 182 a1 = addr1; 183 a2 = addr2 + 1; 184 a3 = truedol; 185 i = a2 - a1; 186 unddol -= i; 187 undap2 -= i; 188 dol -= i; 189 truedol -= i; 190 do 191 *a1++ = *a2++; 192 while (a2 <= a3); 193 a1 = addr1; 194 if (a1 > dol) 195 a1 = dol; 196 dot = a1; 197 } 198 if (!hush) 199 killed(); 200 return (0); 201 } 202 203 void 204 deletenone(void) 205 { 206 207 if(FIXUNDO) { 208 undkind = UNDCHANGE; 209 squish(); 210 unddel = addr1; 211 } 212 } 213 214 /* 215 * Crush out the undo save area, moving the open/visual 216 * save area down in its place. 217 */ 218 void 219 squish(void) 220 { 221 line *a1 = dol + 1, *a2 = unddol + 1, *a3 = truedol + 1; 222 223 if(FIXUNDO) { 224 if (inopen == -1) 225 return; 226 if (a1 < a2 && a2 < a3) 227 do 228 *a1++ = *a2++; 229 while (a2 < a3); 230 truedol -= unddol - dol; 231 unddol = dol; 232 } 233 } 234 235 /* 236 * Join lines. Special hacks put in spaces, two spaces if 237 * preceding line ends with '.', or no spaces if next line starts with ). 238 */ 239 static int jcount; 240 241 int 242 join(int c) 243 { 244 line *a1; 245 unsigned char *cp, *cp1; 246 #ifndef PRESUNEUC 247 unsigned char *pcp; 248 wchar_t *delim; 249 wchar_t wc1, wc2; 250 int n; 251 #endif /* PRESUNEUC */ 252 253 cp = genbuf; 254 *cp = 0; 255 for (a1 = addr1; a1 <= addr2; a1++) { 256 getaline(*a1); 257 cp1 = linebuf; 258 if (a1 != addr1 && c == 0) { 259 while (*cp1 == ' ' || *cp1 == '\t') 260 cp1++; 261 if (*cp1 && cp > genbuf && cp[-1] != ' ' && cp[-1] != '\t') { 262 #ifndef PRESUNEUC 263 /* 264 * insert locale-specific word delimiter if 265 * either of end-of-former-line or 266 * top-of-latter-line is non-ASCII. 267 */ 268 if (wddlm && *cp1 != ')' && cp[-1] != '.') { 269 if ((pcp = cp - MB_CUR_MAX) < genbuf) 270 pcp = genbuf;; 271 for ( ; pcp <= cp-1; pcp++) { 272 if ((n = mbtowc(&wc1, 273 (char *)pcp, cp - pcp)) == 274 cp - pcp) 275 goto gotprev; 276 } 277 goto mberror; 278 gotprev: 279 if (!isascii(wc2 = *cp1)) { 280 if (mbtowc(&wc2, (char *) cp1, 281 MB_CUR_MAX) <= 0) 282 goto mberror; 283 } 284 delim = (*wddlm)(wc1,wc2,2); 285 while (*delim) 286 cp += wctomb((char *)cp, 287 *delim++); 288 *cp = 0; 289 } else 290 mberror: 291 #endif /* PRESUNEUC */ 292 if (*cp1 != ')') { 293 *cp++ = ' '; 294 if (cp[-2] == '.') 295 *cp++ = ' '; 296 } 297 } 298 } 299 while (*cp++ = *cp1++) 300 if (cp > &genbuf[LBSIZE-2]) 301 error(value(vi_TERSE) ? gettext("Line overflow") : 302 gettext("Result line of join would be too long")); 303 cp--; 304 } 305 strcLIN(genbuf); 306 (void) delete(0); 307 jcount = 1; 308 if (FIXUNDO) 309 undap1 = undap2 = addr1; 310 (void)append(jnoop, --addr1); 311 if (FIXUNDO) 312 vundkind = VMANY; 313 return (0); 314 } 315 316 static int 317 jnoop(void) 318 { 319 320 return(--jcount); 321 } 322 323 /* 324 * Move and copy lines. Hard work is done by move1 which 325 * is also called by undo. 326 */ 327 int getcopy(); 328 329 void 330 vi_move(void) 331 { 332 line *adt; 333 bool iscopy = 0; 334 335 if (Command[0] == 'm') { 336 setdot1(); 337 markpr(addr2 == dot ? addr1 - 1 : addr2 + 1); 338 } else { 339 iscopy++; 340 setdot(); 341 } 342 nonzero(); 343 adt = address((char*)0); 344 if (adt == 0) 345 serror(value(vi_TERSE) ? 346 (unsigned char *)gettext("%s where?") : 347 (unsigned char *)gettext("%s requires a trailing address"), 348 Command); 349 donewline(); 350 move1(iscopy, adt); 351 killed(); 352 } 353 354 void 355 move1(int cflag, line *addrt) 356 { 357 line *adt, *ad1, *ad2; 358 int nlines; 359 360 adt = addrt; 361 nlines = (addr2 - addr1) + 1; 362 if (cflag) { 363 tad1 = addr1; 364 ad1 = dol; 365 (void)append(getcopy, ad1++); 366 ad2 = dol; 367 } else { 368 ad2 = addr2; 369 for (ad1 = addr1; ad1 <= ad2;) 370 *ad1++ &= ~01; 371 ad1 = addr1; 372 } 373 ad2++; 374 if (adt < ad1) { 375 if (adt + 1 == ad1 && !cflag && !inglobal) 376 error(gettext("That move would do nothing!")); 377 dot = adt + (ad2 - ad1); 378 if (++adt != ad1) { 379 reverse(adt, ad1); 380 reverse(ad1, ad2); 381 reverse(adt, ad2); 382 } 383 } else if (adt >= ad2) { 384 dot = adt++; 385 reverse(ad1, ad2); 386 reverse(ad2, adt); 387 reverse(ad1, adt); 388 } else 389 error(gettext("Move to a moved line")); 390 change(); 391 if (!inglobal) 392 if(FIXUNDO) { 393 if (cflag) { 394 undap1 = addrt + 1; 395 undap2 = undap1 + nlines; 396 deletenone(); 397 } else { 398 undkind = UNDMOVE; 399 undap1 = addr1; 400 undap2 = addr2; 401 unddel = addrt; 402 squish(); 403 } 404 } 405 } 406 407 int 408 getcopy(void) 409 { 410 411 if (tad1 > addr2) 412 return (EOF); 413 getaline(*tad1++); 414 return (0); 415 } 416 417 /* 418 * Put lines in the buffer from the undo save area. 419 */ 420 int 421 getput(void) 422 { 423 424 if (tad1 > unddol) 425 return (EOF); 426 getaline(*tad1++); 427 tad1++; 428 return (0); 429 } 430 431 int 432 put(void) 433 { 434 int cnt; 435 436 if (!FIXUNDO) 437 error(gettext("Cannot put inside global/macro")); 438 cnt = unddol - dol; 439 if (cnt && inopen && pkill[0] && pkill[1]) { 440 pragged(1); 441 return (0); 442 } 443 tad1 = dol + 1; 444 (void)append(getput, addr2); 445 undkind = UNDPUT; 446 notecnt = cnt; 447 netchange(cnt); 448 return (0); 449 } 450 451 /* 452 * A tricky put, of a group of lines in the middle 453 * of an existing line. Only from open/visual. 454 * Argument says pkills have meaning, e.g. called from 455 * put; it is 0 on calls from putreg. 456 */ 457 void 458 pragged(bool kill) 459 { 460 extern unsigned char *cursor; 461 #ifdef XPG4 462 extern int P_cursor_offset; 463 #endif 464 unsigned char *gp = &genbuf[cursor - linebuf]; 465 466 /* 467 * Assume the editor has: 468 * 469 * cursor is on 'c' 470 * 471 * file is: 1) abcd 472 * 2) efgh 473 * 474 * undo area: 3) 1 475 * 4) 2 476 * 5) 3 477 */ 478 479 if (!kill) 480 getDOT(); 481 482 /* 483 * Copy "abcd" into genbuf. 484 * Note that gp points to 'c'. 485 */ 486 487 strcpy(genbuf, linebuf); 488 489 /* 490 * Get last line of undo area ("3") into linebuf. 491 */ 492 493 getaline(*unddol); 494 if (kill) 495 *pkill[1] = 0; 496 497 498 /* 499 * Concatenate trailing end of current line 500 * into the last line of undo area: 501 * linebuf = "3cd" 502 */ 503 504 strcat(linebuf, gp); 505 #ifdef XPG4 506 P_cursor_offset = strlen(linebuf) - strlen(gp) - 1; 507 #endif 508 509 /* 510 * Replace the last line with what is now in linebuf. 511 * So unddol = "3cd" 512 */ 513 514 putmark(unddol); 515 516 /* 517 * Get the first line of the undo save area into linebuf. 518 * So linebuf = "1" 519 */ 520 521 getaline(dol[1]); 522 if (kill) 523 strcLIN(pkill[0]); 524 525 /* 526 * Copy the first line of the undo save area 527 * over what is pointed to by sp. 528 * genbuf = "ab1" 529 */ 530 531 strcpy(gp, linebuf); 532 533 /* 534 * Now copy genbuf back into linebuf. 535 * linebuf = "ab1" 536 */ 537 538 strcLIN(genbuf); 539 540 /* 541 * Now put linebuf back into the first line 542 * of the undo save area. 543 */ 544 545 putmark(dol+1); 546 547 /* 548 * Prepare to perform an undo which will actually 549 * do a put of multiple lines in the middle of 550 * the current line. 551 */ 552 553 undkind = UNDCHANGE; 554 undap1 = dot; 555 undap2 = dot + 1; 556 unddel = dot - 1; 557 undo(1); 558 } 559 560 /* 561 * Shift lines, based on c. 562 * If c is neither < nor >, then this is a lisp aligning =. 563 */ 564 void 565 shift(int c, int cnt) 566 { 567 line *addr; 568 unsigned char *cp; 569 unsigned char *dp; 570 int i; 571 572 if(FIXUNDO) 573 save12(), undkind = UNDCHANGE; 574 cnt *= value(vi_SHIFTWIDTH); 575 for (addr = addr1; addr <= addr2; addr++) { 576 dot = addr; 577 if (c == '=' && addr == addr1 && addr != addr2) 578 continue; 579 getDOT(); 580 i = whitecnt(linebuf); 581 switch (c) { 582 583 case '>': 584 if (linebuf[0] == 0) 585 continue; 586 cp = genindent(i + cnt); 587 break; 588 589 case '<': 590 if (i == 0) 591 continue; 592 i -= cnt; 593 cp = i > 0 ? genindent(i) : genbuf; 594 break; 595 596 default: 597 i = lindent(addr); 598 getDOT(); 599 cp = genindent(i); 600 break; 601 } 602 if (cp + strlen(dp = vpastwh(linebuf)) >= &genbuf[LBSIZE - 2]) 603 error(value(vi_TERSE) ? gettext("Line too long") : 604 gettext("Result line after shift would be too long")); 605 CP(cp, dp); 606 strcLIN(genbuf); 607 putmark(addr); 608 } 609 killed(); 610 } 611 612 /* 613 * Find a tag in the tags file. 614 * Most work here is in parsing the tags file itself. 615 */ 616 void 617 tagfind(quick) 618 bool quick; 619 { 620 unsigned char cmdbuf[BUFSIZE]; 621 unsigned char filebuf[FNSIZE]; 622 unsigned char tagfbuf[BUFSIZE]; 623 int c, d; 624 bool samef = 1; 625 int tfcount = 0; 626 int omagic, tl; 627 unsigned char *fn, *fne; 628 #ifdef STDIO /* was VMUNIX */ 629 /* 630 * We have lots of room so we bring in stdio and do 631 * a binary search on the tags file. 632 */ 633 FILE *iof; 634 unsigned char iofbuf[BUFSIZE]; 635 off64_t mid; /* assumed byte offset */ 636 off64_t top, bot; /* length of tag file */ 637 struct stat64 sbuf; 638 #endif 639 640 omagic = value(vi_MAGIC); 641 tl = value(vi_TAGLENGTH); 642 if (!skipend()) { 643 unsigned char *lp = lasttag; 644 645 while (!iswhite(peekchar()) && !endcmd(peekchar())) 646 if (lp < &lasttag[sizeof lasttag - 2]) 647 *lp++ = getchar(); 648 else 649 ignchar(); 650 *lp++ = 0; 651 if (!endcmd(peekchar())) 652 badtag: 653 error(value(vi_TERSE) ? gettext("Bad tag") : 654 gettext("Give one tag per line")); 655 } else if (lasttag[0] == 0) 656 error(gettext("No previous tag")); 657 c = getchar(); 658 if (!endcmd(c)) 659 goto badtag; 660 if (c == EOF) 661 ungetchar(c); 662 clrstats(); 663 664 /* 665 * Loop once for each file in tags "path". 666 * 667 * System tags array limits to 4k (tags[ONMSZ]) long, 668 * therefore, tagfbuf should be able to hold all tags. 669 */ 670 671 CP(tagfbuf, svalue(vi_TAGS)); 672 fne = tagfbuf - 1; 673 while (fne) { 674 fn = ++fne; 675 while (*fne && *fne != ' ') 676 fne++; 677 if (*fne == 0) 678 fne = 0; /* done, quit after this time */ 679 else 680 *fne = 0; /* null terminate filename */ 681 #ifdef STDIO /* was VMUNIX */ 682 iof = fopen((char *)fn, "r"); 683 if (iof == NULL) 684 continue; 685 tfcount++; 686 setbuf(iof, (char *)iofbuf); 687 fstat64(fileno(iof), &sbuf); 688 top = sbuf.st_size; 689 if (top == 0L || iof == NULL) 690 top = -1L; 691 bot = 0L; 692 while (top >= bot) { 693 /* loop for each tags file entry */ 694 unsigned char *cp = linebuf; 695 unsigned char *lp = lasttag; 696 unsigned char *oglobp; 697 698 mid = (top + bot) / 2; 699 fseeko64(iof, mid, 0); 700 if (mid > 0) /* to get first tag in file to work */ 701 /* scan to next \n */ 702 if(fgets((char *)linebuf, sizeof linebuf, iof)==NULL) 703 goto goleft; 704 /* get the line itself */ 705 if(fgets((char *)linebuf, sizeof linebuf, iof)==NULL) 706 goto goleft; 707 linebuf[strlen(linebuf)-1] = 0; /* was '\n' */ 708 while (*cp && *lp == *cp) 709 cp++, lp++; 710 /* 711 * This if decides whether there is a tag match. 712 * A positive taglength means that a 713 * match is found if the tag given matches at least 714 * taglength chars of the tag found. 715 * A taglength of greater than 511 means that a 716 * match is found even if the tag given is a proper 717 * prefix of the tag found. i.e. "ab" matches "abcd" 718 */ 719 if ( *lp == 0 && (iswhite(*cp) || tl > 511 || tl > 0 && lp-lasttag >= tl) ) { 720 /* 721 * Found a match. Force selection to be 722 * the first possible. 723 */ 724 if ( mid == bot && mid == top ) { 725 ; /* found first possible match */ 726 } 727 else { 728 /* postpone final decision. */ 729 top = mid; 730 continue; 731 } 732 } 733 else { 734 if ((int)*lp > (int)*cp) 735 bot = mid + 1; 736 else 737 goleft: 738 top = mid - 1; 739 continue; 740 } 741 /* 742 * We found the tag. Decode the line in the file. 743 */ 744 fclose(iof); 745 746 /* Rest of tag if abbreviated */ 747 while (!iswhite(*cp)) 748 cp++; 749 750 /* name of file */ 751 while (*cp && iswhite(*cp)) 752 cp++; 753 if (!*cp) 754 badtags: 755 serror((unsigned char *) 756 gettext("%s: Bad tags file entry"), 757 lasttag); 758 lp = filebuf; 759 while (*cp && *cp != ' ' && *cp != '\t') { 760 if (lp < &filebuf[sizeof filebuf - 2]) 761 *lp++ = *cp; 762 cp++; 763 } 764 *lp++ = 0; 765 766 if (*cp == 0) 767 goto badtags; 768 if (dol != zero) { 769 /* 770 * Save current position in 't for ^^ in visual. 771 */ 772 names['t'-'a'] = *dot &~ 01; 773 if (inopen) { 774 extern unsigned char *ncols['z'-'a'+2]; 775 extern unsigned char *cursor; 776 777 ncols['t'-'a'] = cursor; 778 } 779 } 780 #ifdef TAG_STACK 781 if (*savedfile) { 782 savetag((char *)savedfile); 783 } 784 #endif 785 strcpy(cmdbuf, cp); 786 if (strcmp(filebuf, savedfile) || !edited) { 787 unsigned char cmdbuf2[sizeof filebuf + 10]; 788 789 /* Different file. Do autowrite & get it. */ 790 if (!quick) { 791 ckaw(); 792 if (chng && dol > zero) { 793 #ifdef TAG_STACK 794 unsavetag(); 795 #endif 796 error(value(vi_TERSE) ? 797 gettext("No write") : gettext("No write since last change (:tag! overrides)")); 798 } 799 } 800 oglobp = globp; 801 strcpy(cmdbuf2, "e! "); 802 strcat(cmdbuf2, filebuf); 803 globp = cmdbuf2; 804 d = peekc; ungetchar(0); 805 commands(1, 1); 806 peekc = d; 807 globp = oglobp; 808 value(vi_MAGIC) = omagic; 809 samef = 0; 810 } 811 812 /* 813 * Look for pattern in the current file. 814 */ 815 oglobp = globp; 816 globp = cmdbuf; 817 d = peekc; ungetchar(0); 818 if (samef) 819 markpr(dot); 820 /* 821 * BUG: if it isn't found (user edited header 822 * line) we get left in nomagic mode. 823 */ 824 value(vi_MAGIC) = 0; 825 commands(1, 1); 826 peekc = d; 827 globp = oglobp; 828 value(vi_MAGIC) = omagic; 829 return; 830 } /* end of "for each tag in file" */ 831 #endif /* STDIO */ 832 /* 833 * Binary search failed, so try linear search if -S is on. 834 * -S is needed for tags files that are not sorted. 835 */ 836 837 /* 838 * Avoid stdio and scan tag file linearly. 839 */ 840 if (tags_flag == 0) 841 continue; 842 io = open(fn, 0); 843 if (io < 0) 844 continue; 845 /* tfcount++; */ 846 while (getfile() == 0) { 847 /* loop for each tags file entry */ 848 unsigned char *cp = linebuf; 849 unsigned char *lp = lasttag; 850 unsigned char *oglobp; 851 852 while (*cp && *lp == *cp) 853 cp++, lp++; 854 /* 855 * This if decides whether there is a tag match. 856 * A positive taglength means that a 857 * match is found if the tag given matches at least 858 * taglength chars of the tag found. 859 * A taglength of greater than 511 means that a 860 * match is found even if the tag given is a proper 861 * prefix of the tag found. i.e. "ab" matches "abcd" 862 */ 863 if ( *lp == 0 && (iswhite(*cp) || tl > 511 || tl > 0 && lp-lasttag >= tl) ) { 864 ; /* Found it. */ 865 } 866 else { 867 /* Not this tag. Try the next */ 868 continue; 869 } 870 /* 871 * We found the tag. Decode the line in the file. 872 */ 873 close(io); 874 /* Rest of tag if abbreviated */ 875 while (!iswhite(*cp)) 876 cp++; 877 878 /* name of file */ 879 while (*cp && iswhite(*cp)) 880 cp++; 881 if (!*cp) 882 badtags2: 883 serror((unsigned char *) 884 gettext("%s: Bad tags file entry"), 885 lasttag); 886 lp = filebuf; 887 while (*cp && *cp != ' ' && *cp != '\t') { 888 if (lp < &filebuf[sizeof filebuf - 2]) 889 *lp++ = *cp; 890 cp++; 891 } 892 *lp++ = 0; 893 894 if (*cp == 0) 895 goto badtags2; 896 if (dol != zero) { 897 /* 898 * Save current position in 't for ^^ in visual. 899 */ 900 names['t'-'a'] = *dot &~ 01; 901 if (inopen) { 902 extern unsigned char *ncols['z'-'a'+2]; 903 extern unsigned char *cursor; 904 905 ncols['t'-'a'] = cursor; 906 } 907 } 908 #ifdef TAG_STACK 909 if (*savedfile) { 910 savetag((char *)savedfile); 911 } 912 #endif 913 strcpy(cmdbuf, cp); 914 if (strcmp(filebuf, savedfile) || !edited) { 915 unsigned char cmdbuf2[sizeof filebuf + 10]; 916 917 /* Different file. Do autowrite & get it. */ 918 if (!quick) { 919 ckaw(); 920 if (chng && dol > zero) { 921 #ifdef TAG_STACK 922 unsavetag(); 923 #endif 924 error(value(vi_TERSE) ? 925 gettext("No write") : gettext("No write since last change (:tag! overrides)")); 926 } 927 } 928 oglobp = globp; 929 strcpy(cmdbuf2, "e! "); 930 strcat(cmdbuf2, filebuf); 931 globp = cmdbuf2; 932 d = peekc; ungetchar(0); 933 commands(1, 1); 934 peekc = d; 935 globp = oglobp; 936 value(vi_MAGIC) = omagic; 937 samef = 0; 938 } 939 940 /* 941 * Look for pattern in the current file. 942 */ 943 oglobp = globp; 944 globp = cmdbuf; 945 d = peekc; ungetchar(0); 946 if (samef) 947 markpr(dot); 948 /* 949 * BUG: if it isn't found (user edited header 950 * line) we get left in nomagic mode. 951 */ 952 value(vi_MAGIC) = 0; 953 commands(1, 1); 954 peekc = d; 955 globp = oglobp; 956 value(vi_MAGIC) = omagic; 957 return; 958 } /* end of "for each tag in file" */ 959 960 /* 961 * No such tag in this file. Close it and try the next. 962 */ 963 #ifdef STDIO /* was VMUNIX */ 964 fclose(iof); 965 #else 966 close(io); 967 #endif 968 } /* end of "for each file in path" */ 969 if (tfcount <= 0) 970 error(gettext("No tags file")); 971 else 972 serror(value(vi_TERSE) ? 973 (unsigned char *)gettext("%s: No such tag") : 974 (unsigned char *)gettext("%s: No such tag in tags file"), 975 lasttag); 976 } 977 978 /* 979 * Save lines from addr1 thru addr2 as though 980 * they had been deleted. 981 */ 982 int 983 yank(void) 984 { 985 986 if (!FIXUNDO) 987 error(gettext("Can't yank inside global/macro")); 988 save12(); 989 undkind = UNDNONE; 990 killcnt(addr2 - addr1 + 1); 991 return (0); 992 } 993 994 /* 995 * z command; print windows of text in the file. 996 * 997 * If this seems unreasonably arcane, the reasons 998 * are historical. This is one of the first commands 999 * added to the first ex (then called en) and the 1000 * number of facilities here were the major advantage 1001 * of en over ed since they allowed more use to be 1002 * made of fast terminals w/o typing .,.22p all the time. 1003 */ 1004 bool zhadpr; 1005 bool znoclear; 1006 short zweight; 1007 1008 void 1009 zop(int hadpr) 1010 { 1011 int c, nlines, op; 1012 bool excl; 1013 1014 zhadpr = hadpr; 1015 notempty(); 1016 znoclear = 0; 1017 zweight = 0; 1018 excl = exclam(); 1019 switch (c = op = getchar()) { 1020 1021 case '^': 1022 zweight = 1; 1023 /* FALLTHROUGH */ 1024 case '-': 1025 case '+': 1026 while (peekchar() == op) { 1027 ignchar(); 1028 zweight++; 1029 } 1030 /* FALLTHROUGH */ 1031 case '=': 1032 case '.': 1033 c = getchar(); 1034 break; 1035 1036 case EOF: 1037 znoclear++; 1038 break; 1039 1040 default: 1041 op = 0; 1042 break; 1043 } 1044 if (isdigit(c)) { 1045 nlines = c - '0'; 1046 for(;;) { 1047 c = getchar(); 1048 if (!isdigit(c)) 1049 break; 1050 nlines *= 10; 1051 nlines += c - '0'; 1052 } 1053 if (nlines < lines) 1054 znoclear++; 1055 value(vi_WINDOW) = nlines; 1056 if (op == '=') 1057 nlines += 2; 1058 } 1059 else { 1060 nlines = op == EOF ? value(vi_SCROLL) : 1061 excl ? lines - 1 : value(vi_WINDOW); 1062 } 1063 if (inopen || c != EOF) { 1064 ungetchar(c); 1065 donewline(); 1066 } 1067 addr1 = addr2; 1068 if (addr2 == 0 && dot < dol && op == 0) 1069 addr1 = addr2 = dot+1; 1070 setdot(); 1071 zop2(nlines, op); 1072 } 1073 1074 void 1075 zop2(int nlines, int op) 1076 { 1077 line *split; 1078 1079 split = NULL; 1080 switch (op) { 1081 1082 case EOF: 1083 if (addr2 == dol) 1084 error(gettext("\nAt EOF")); 1085 /* FALLTHROUGH */ 1086 case '+': 1087 if (addr2 == dol) 1088 error(gettext("At EOF")); 1089 addr2 += nlines * zweight; 1090 if (addr2 > dol) 1091 error(gettext("Hit BOTTOM")); 1092 addr2++; 1093 /* FALLTHROUGH */ 1094 default: 1095 addr1 = addr2; 1096 addr2 += nlines-1; 1097 dot = addr2; 1098 break; 1099 1100 case '=': 1101 case '.': 1102 znoclear = 0; 1103 nlines--; 1104 nlines >>= 1; 1105 if (op == '=') 1106 nlines--; 1107 addr1 = addr2 - nlines; 1108 if (op == '=') 1109 dot = split = addr2; 1110 addr2 += nlines; 1111 if (op == '.') { 1112 markDOT(); 1113 dot = addr2; 1114 } 1115 break; 1116 1117 case '^': 1118 case '-': 1119 addr2 -= nlines * zweight; 1120 if (addr2 < one) 1121 error(gettext("Hit TOP")); 1122 nlines--; 1123 addr1 = addr2 - nlines; 1124 dot = addr2; 1125 break; 1126 } 1127 if (addr1 <= zero) 1128 addr1 = one; 1129 if (addr2 > dol) 1130 addr2 = dol; 1131 if (dot > dol) 1132 dot = dol; 1133 if (addr1 > addr2) 1134 return; 1135 if (op == EOF && zhadpr) { 1136 getaline(*addr1); 1137 putchar((int)('\r' | QUOTE)); 1138 shudclob = 1; 1139 } else if (znoclear == 0 && clear_screen != NOSTR && !inopen) { 1140 flush1(); 1141 vclear(); 1142 } 1143 if (addr2 - addr1 > 1) 1144 pstart(); 1145 if (split) { 1146 plines(addr1, split - 1, 0); 1147 splitit(); 1148 plines(split, split, 0); 1149 splitit(); 1150 addr1 = split + 1; 1151 } 1152 plines(addr1, addr2, 0); 1153 } 1154 1155 static void 1156 splitit(void) 1157 { 1158 int l; 1159 1160 for (l = columns > 80 ? 40 : columns / 2; l > 0; l--) 1161 putchar('-'); 1162 putnl(); 1163 } 1164 1165 void 1166 plines(line *adr1, line *adr2, bool movedot) 1167 { 1168 line *addr; 1169 1170 pofix(); 1171 for (addr = adr1; addr <= adr2; addr++) { 1172 getaline(*addr); 1173 pline(lineno(addr)); 1174 if (inopen) 1175 putchar((int)('\n' | QUOTE)); 1176 if (movedot) 1177 dot = addr; 1178 } 1179 } 1180 1181 void 1182 pofix(void) 1183 { 1184 1185 if (inopen && Outchar != termchar) { 1186 vnfl(); 1187 setoutt(); 1188 } 1189 } 1190 1191 /* 1192 * Command level undo works easily because 1193 * the editor has a unique temporary file 1194 * index for every line which ever existed. 1195 * We don't have to save large blocks of text, 1196 * only the indices which are small. We do this 1197 * by moving them to after the last line in the 1198 * line buffer array, and marking down info 1199 * about whence they came. 1200 * 1201 * Undo is its own inverse. 1202 */ 1203 void 1204 undo(bool c) 1205 { 1206 int i, k; 1207 line *jp, *kp, *j; 1208 line *dolp1, *newdol, *newadot; 1209 1210 #ifdef UNDOTRACE 1211 if (trace) 1212 vudump("before undo"); 1213 #endif 1214 if (inglobal && inopen <= 0) 1215 error(value(vi_TERSE) ? gettext("Can't undo in global") : 1216 gettext("Can't undo in global commands")); 1217 1218 /* 1219 * Unless flag indicates a forced undo, make sure 1220 * there really was a change before trying to undo it. 1221 */ 1222 1223 if (!c) 1224 somechange(); 1225 1226 /* 1227 * Update change flags. 1228 */ 1229 1230 pkill[0] = pkill[1] = 0; 1231 change(); 1232 if (undkind == UNDMOVE) { 1233 /* 1234 * Command to be undone is a move command. 1235 * This is handled as a special case by noting that 1236 * a move "a,b m c" can be inverted by another move. 1237 */ 1238 if ((i = (jp = unddel) - undap2) > 0) { 1239 /* 1240 * when c > b inverse is a+(c-b),c m a-1 1241 */ 1242 addr2 = jp; 1243 addr1 = (jp = undap1) + i; 1244 unddel = jp-1; 1245 } else { 1246 /* 1247 * when b > c inverse is c+1,c+1+(b-a) m b 1248 */ 1249 addr1 = ++jp; 1250 addr2 = jp + ((unddel = undap2) - undap1); 1251 } 1252 kp = undap1; 1253 move1(0, unddel); 1254 dot = kp; 1255 Command = (unsigned char *)"move"; 1256 killed(); 1257 } else { 1258 int cnt; 1259 1260 newadot = dot; 1261 cnt = lineDOL(); 1262 newdol = dol; 1263 dolp1 = dol + 1; 1264 /* 1265 * Command to be undone is a non-move. 1266 * All such commands are treated as a combination of 1267 * a delete command and a append command. 1268 * We first move the lines appended by the last command 1269 * from undap1 to undap2-1 so that they are just before the 1270 * saved deleted lines. 1271 * 1272 * Assume the editor has: 1273 * 1274 * cursor is on 'c' 1275 * 1276 * (just change lines 5-8) 1277 * 1278 * file is: 1) ab 1279 * 2) cd 1280 * 3) ef 1281 * 4) gh 1282 * undap1: 5) 12 1283 * 6) 34 1284 * 7) 56 1285 * 8) 78 1286 * undap2: 9) qr 1287 * 10) st 1288 * 11) uv 1289 * 12) wx 1290 * dol: 13) yz 1291 * 1292 * UNDO AREA: 1293 * dol+1: 5) ij 1294 * 6) kl 1295 * 7) mn 1296 * unddol: 8) op 1297 */ 1298 1299 /* 1300 * If this is a change (not a delete/put), 1301 * then we must move the text between undap1 and undap2 1302 * and it must not be at the bottom of the file 1303 */ 1304 1305 if ((i = (kp = undap2) - (jp = undap1)) > 0) { 1306 if (kp != dolp1) { 1307 1308 /* 1309 * FILE: LINE INITIAL REV1 REV2 REV3 1310 * 1311 * 1) ab ab ab ab 1312 * 2) cd cd cd cd 1313 * 3) ef ef ef ef 1314 * unddel: 4) gh gh gh gh 1315 * undap1: 5) 12 78 78 qr 1316 * 6) 34 56 56 st 1317 * 7) 56 34 34 uv 1318 * 8) 78 12 12 wx 1319 * undap2: 9) qr qr yz yz 1320 * 10) st st wx 12 1321 * 11) uv uv uv 34 1322 * 12) wx wx st 56 1323 * dol: 13) yz yz qr 78 1324 * 1325 * UNDO AREA: 1326 * dol+1: 5) ij ij ij ij 1327 * 6) kl kl kl kl 1328 * 7) mn mn mn mn 1329 * unddol: 8) op op op op 1330 */ 1331 1332 reverse(jp, kp); 1333 reverse(kp, dolp1); 1334 reverse(jp, dolp1); 1335 } 1336 /* 1337 * Unddel, the line just before the spot where this 1338 * test was deleted, may have moved. Account for 1339 * this in restoration of saved deleted lines. 1340 */ 1341 if (unddel >= jp) 1342 unddel -= i; 1343 1344 /* 1345 * The last line (dol) may have changed, 1346 * account for this. 1347 */ 1348 newdol -= i; 1349 1350 /* 1351 * For the case where no lines are restored, dot 1352 * is the line before the first line deleted. 1353 */ 1354 dot = jp-1; 1355 } 1356 /* 1357 * Now put the deleted lines, if any, back where they were. 1358 * Basic operation is: dol+1,unddol m unddel 1359 */ 1360 if (undkind == UNDPUT) { 1361 unddel = undap1 - 1; 1362 squish(); 1363 } 1364 1365 /* 1366 * Set jp to the line where deleted text is to be added. 1367 */ 1368 jp = unddel + 1; 1369 1370 /* 1371 * Set kp to end of undo save area. 1372 * 1373 * If there is any deleted text to be added, do reverses. 1374 */ 1375 1376 if ((i = (kp = unddol) - dol) > 0) { 1377 1378 /* 1379 * If deleted lines are not to be appended 1380 * to the bottom of the file... 1381 */ 1382 1383 if (jp != dolp1) { 1384 /* 1385 * FILE: LINE START REV1 REV2 REV3 1386 * 1) ab ab ab ab 1387 * 2) cd cd cd cd 1388 * 3) ef ef ef ef 1389 * unddel: 4) gh gh gh gh 1390 * undap1: 5) qr 78 78 ij 1391 * 6) st 56 56 kl 1392 * 7) uv 34 34 mn 1393 * 8) wx 12 12 op 1394 * undap2: 9) yz yz yz qr 1395 * 10) 12 wx wx st 1396 * 11) 34 uv uv uv 1397 * 12) 56 st st wx 1398 * dol: 13) 78 qr qr yz 1399 * 1400 * UNDO AREA: 1401 * dol+1: 5) ij ij op 12 1402 * 6) kl kl mn 34 1403 * 7) mn mn kl 56 1404 * unddol: 8) op op ij 78 1405 */ 1406 1407 reverse(jp, dolp1); 1408 reverse(dolp1, ++kp); 1409 reverse(jp, kp); 1410 } 1411 /* 1412 * Account for possible forward motion of the target 1413 * (where the deleted lines were restored) for after 1414 * restoration of the deleted lines. 1415 */ 1416 if (undap1 >= jp) 1417 undap1 += i; 1418 /* 1419 * Dot is the first resurrected line. 1420 */ 1421 dot = jp; 1422 1423 /* 1424 * Account for a shift in the last line (dol). 1425 */ 1426 1427 newdol += i; 1428 } 1429 /* 1430 * Clean up so we are invertible 1431 */ 1432 unddel = undap1 - 1; 1433 undap1 = jp; 1434 undap2 = jp + i; 1435 dol = newdol; 1436 netchHAD(cnt); 1437 if (undkind == UNDALL) { 1438 dot = undadot; 1439 undadot = newadot; 1440 } else 1441 undkind = UNDCHANGE; 1442 /* 1443 * Now relocate all marks for lines that were modified, 1444 * since the marks point to lines whose address has 1445 * been modified from the save area to the current 1446 * area 1447 */ 1448 1449 for (j=unddol; j> dol; j--) 1450 for (k=0; k<=25; k++) 1451 if (names[k] == *(j)) 1452 names[k]= *((undap1+(j-dolp1)) ); 1453 } 1454 /* 1455 * Defensive programming - after a munged undadot. 1456 * Also handle empty buffer case. 1457 */ 1458 if ((dot <= zero || dot > dol) && dot != dol) 1459 dot = one; 1460 #ifdef UNDOTRACE 1461 if (trace) 1462 vudump("after undo"); 1463 #endif 1464 } 1465 1466 /* 1467 * Be (almost completely) sure there really 1468 * was a change, before claiming to undo. 1469 */ 1470 void 1471 somechange(void) 1472 { 1473 line *ip, *jp; 1474 1475 switch (undkind) { 1476 1477 case UNDMOVE: 1478 return; 1479 1480 case UNDCHANGE: 1481 if (undap1 == undap2 && dol == unddol) 1482 break; 1483 return; 1484 1485 case UNDPUT: 1486 if (undap1 != undap2) 1487 return; 1488 break; 1489 1490 case UNDALL: 1491 if (unddol - dol != lineDOL()) 1492 return; 1493 for (ip = one, jp = dol + 1; ip <= dol; ip++, jp++) 1494 if ((*ip &~ 01) != (*jp &~ 01)) 1495 return; 1496 break; 1497 1498 case UNDNONE: 1499 error(gettext("Nothing to undo")); 1500 } 1501 error(value(vi_TERSE) ? gettext("Nothing changed") : 1502 gettext("Last undoable command didn't change anything")); 1503 } 1504 1505 /* 1506 * Map command: 1507 * map src dest 1508 * 1509 * un is true if this is unmap command 1510 * ab is true if this is abbr command 1511 */ 1512 void 1513 mapcmd(int un, int ab) 1514 { 1515 unsigned char lhs[100], rhs[100]; /* max sizes resp. */ 1516 unsigned char *p; 1517 int c; /* char --> int */ 1518 unsigned char *dname; 1519 struct maps *mp; /* the map structure we are working on */ 1520 1521 mp = ab ? abbrevs : exclam() ? immacs : arrows; 1522 if (skipend()) { 1523 int i; 1524 1525 /* print current mapping values */ 1526 if (peekchar() != EOF) 1527 ignchar(); 1528 if (un) 1529 error(gettext("Missing lhs")); 1530 if (inopen) 1531 pofix(); 1532 for (i=0; i< MAXNOMACS && mp[i].mapto; i++) 1533 if (mp[i].cap) { 1534 lprintf("%s", mp[i].descr); 1535 putchar('\t'); 1536 lprintf("%s", mp[i].cap); 1537 putchar('\t'); 1538 lprintf("%s", mp[i].mapto); 1539 putNFL(); 1540 } 1541 return; 1542 } 1543 1544 (void)skipwh(); 1545 for (p=lhs; ; ) { 1546 c = getchar(); 1547 if (c == CTRL('v')) { 1548 c = getchar(); 1549 } else if (!un && any(c, " \t")) { 1550 /* End of lhs */ 1551 break; 1552 } else if (endcmd(c) && c!='"') { 1553 ungetchar(c); 1554 if (un) { 1555 donewline(); 1556 *p = 0; 1557 addmac(lhs, (unsigned char *)NOSTR, 1558 (unsigned char *)NOSTR, mp); 1559 return; 1560 } else 1561 error(gettext("Missing rhs")); 1562 } 1563 *p++ = c; 1564 } 1565 *p = 0; 1566 1567 if (skipend()) 1568 error(gettext("Missing rhs")); 1569 for (p=rhs; ; ) { 1570 c = getchar(); 1571 if (c == CTRL('v')) { 1572 c = getchar(); 1573 } else if (endcmd(c) && c!='"') { 1574 ungetchar(c); 1575 break; 1576 } 1577 *p++ = c; 1578 } 1579 *p = 0; 1580 donewline(); 1581 /* 1582 * Special hack for function keys: #1 means key f1, etc. 1583 * If the terminal doesn't have function keys, we just use #1. 1584 */ 1585 if (lhs[0] == '#') { 1586 unsigned char *fnkey; 1587 unsigned char *fkey(); 1588 unsigned char funkey[3]; 1589 1590 fnkey = fkey(lhs[1] - '0'); 1591 funkey[0] = 'f'; funkey[1] = lhs[1]; funkey[2] = 0; 1592 if (fnkey) 1593 strcpy(lhs, fnkey); 1594 dname = funkey; 1595 } else { 1596 dname = lhs; 1597 } 1598 addmac(lhs,rhs,dname,mp); 1599 } 1600 1601 /* 1602 * Add a macro definition to those that already exist. The sequence of 1603 * chars "src" is mapped into "dest". If src is already mapped into something 1604 * this overrides the mapping. There is no recursion. Unmap is done by 1605 * using NOSTR for dest. Dname is what to show in listings. mp is 1606 * the structure to affect (arrows, etc). 1607 */ 1608 void 1609 addmac(unsigned char *src, unsigned char *dest, unsigned char *dname, 1610 struct maps *mp) 1611 { 1612 int slot, zer; 1613 1614 #ifdef UNDOTRACE 1615 if (trace) 1616 fprintf(trace, "addmac(src='%s', dest='%s', dname='%s', mp=%x\n", src, dest, dname, mp); 1617 #endif 1618 if (dest && mp==arrows) { 1619 /* 1620 * Prevent tail recursion. We really should be 1621 * checking to see if src is a suffix of dest 1622 * but this makes mapping involving escapes that 1623 * is reasonable mess up. 1624 */ 1625 if (src[1] == 0 && src[0] == dest[strlen(dest)-1]) 1626 error(gettext("No tail recursion")); 1627 /* 1628 * We don't let the user rob themself of ":", and making 1629 * multi char words is a bad idea so we don't allow it. 1630 * Note that if user sets mapinput and maps all of return, 1631 * linefeed, and escape, they can hurt themself. This is 1632 * so weird I don't bother to check for it. 1633 */ 1634 if (isalpha(src[0]) && isascii(src[0]) && src[1] || any(src[0],":")) 1635 error(gettext("Too dangerous to map that")); 1636 } 1637 else if (dest) { 1638 /* check for tail recursion in input mode: fussier */ 1639 if (eq(src, dest+strlen(dest)-strlen(src))) 1640 error(gettext("No tail recursion")); 1641 } 1642 /* 1643 * If the src were null it would cause the dest to 1644 * be mapped always forever. This is not good. 1645 */ 1646 if (src == (unsigned char *)NOSTR || src[0] == 0) 1647 error(gettext("Missing lhs")); 1648 1649 /* see if we already have a def for src */ 1650 zer = -1; 1651 for (slot=0; slot < MAXNOMACS && mp[slot].mapto; slot++) { 1652 if (mp[slot].cap) { 1653 if (eq(src, mp[slot].cap) || eq(src, mp[slot].mapto)) 1654 break; /* if so, reuse slot */ 1655 } else { 1656 zer = slot; /* remember an empty slot */ 1657 } 1658 } 1659 1660 if (slot >= MAXNOMACS) 1661 error(gettext("Too many macros")); 1662 1663 if (dest == (unsigned char *)NOSTR) { 1664 /* unmap */ 1665 if (mp[slot].cap) { 1666 mp[slot].cap = (unsigned char *)NOSTR; 1667 mp[slot].descr = (unsigned char *)NOSTR; 1668 } else { 1669 error(value(vi_TERSE) ? gettext("Not mapped") : 1670 gettext("That macro wasn't mapped")); 1671 } 1672 return; 1673 } 1674 1675 /* reuse empty slot, if we found one and src isn't already defined */ 1676 if (zer >= 0 && mp[slot].mapto == 0) 1677 slot = zer; 1678 1679 /* if not, append to end */ 1680 if (msnext == 0) /* first time */ 1681 msnext = mapspace; 1682 /* Check is a bit conservative, we charge for dname even if reusing src */ 1683 if (msnext - mapspace + strlen(dest) + strlen(src) + strlen(dname) + 3 > MAXCHARMACS) 1684 error(gettext("Too much macro text")); 1685 CP(msnext, src); 1686 mp[slot].cap = msnext; 1687 msnext += strlen(src) + 1; /* plus 1 for null on the end */ 1688 CP(msnext, dest); 1689 mp[slot].mapto = msnext; 1690 msnext += strlen(dest) + 1; 1691 if (dname) { 1692 CP(msnext, dname); 1693 mp[slot].descr = msnext; 1694 msnext += strlen(dname) + 1; 1695 } else { 1696 /* default descr to string user enters */ 1697 mp[slot].descr = src; 1698 } 1699 } 1700 1701 /* 1702 * Implements macros from command mode. c is the buffer to 1703 * get the macro from. 1704 */ 1705 void 1706 cmdmac(c) 1707 unsigned char c; 1708 { 1709 unsigned char macbuf[BUFSIZE]; 1710 line *ad, *a1, *a2; 1711 unsigned char *oglobp; 1712 short pk; 1713 bool oinglobal; 1714 1715 lastmac = c; 1716 oglobp = globp; 1717 oinglobal = inglobal; 1718 pk = peekc; peekc = 0; 1719 if (inglobal < 2) 1720 inglobal = 1; 1721 regbuf(c, macbuf, sizeof(macbuf)); 1722 a1 = addr1; a2 = addr2; 1723 for (ad=a1; ad<=a2; ad++) { 1724 globp = macbuf; 1725 dot = ad; 1726 commands(1,1); 1727 } 1728 globp = oglobp; 1729 inglobal = oinglobal; 1730 peekc = pk; 1731 } 1732 1733 unsigned char * 1734 vgetpass(prompt) 1735 char *prompt; 1736 { 1737 unsigned char *p; 1738 int c; 1739 static unsigned char pbuf[9]; 1740 1741 /* In ex mode, let the system hassle with setting no echo */ 1742 if (!inopen) 1743 return (unsigned char *)getpass(prompt); 1744 viprintf("%s", prompt); flush(); 1745 for (p=pbuf; (c = getkey())!='\n' && c!=EOF && c!='\r';) { 1746 if (p < &pbuf[8]) 1747 *p++ = c; 1748 } 1749 *p = '\0'; 1750 return(pbuf); 1751 } 1752 1753 1754 #ifdef TAG_STACK 1755 #define TSTACKSIZE 20 1756 struct tagstack { 1757 line *tag_line; 1758 char *tag_file; 1759 } tagstack[TSTACKSIZE]; 1760 static int tag_depth = 0; 1761 1762 static char tag_buf[ 1024 ]; 1763 static char *tag_end = tag_buf; 1764 1765 void 1766 savetag(char *name) /* saves location where we are */ 1767 { 1768 if( !value(vi_TAGSTACK) ) 1769 return; 1770 if(tag_depth >= TSTACKSIZE) { 1771 error(gettext("Tagstack too deep.")); 1772 } 1773 if( strlen( name ) + 1 + tag_end >= &tag_buf[1024]) { 1774 error(gettext("Too many tags.")); 1775 } 1776 tagstack[tag_depth].tag_line = dot; 1777 tagstack[tag_depth++].tag_file = tag_end; 1778 while(*tag_end++ = *name++) 1779 ; 1780 } 1781 1782 /* 1783 * Undo a "savetag". 1784 */ 1785 void 1786 unsavetag(void) 1787 { 1788 if (!value(vi_TAGSTACK)) 1789 return; 1790 if (tag_depth > 0) 1791 tag_end = tagstack[--tag_depth].tag_file; 1792 } 1793 1794 void 1795 poptag(quick) /* puts us back where we came from */ 1796 bool quick; 1797 { 1798 unsigned char cmdbuf[100]; 1799 unsigned char *oglobp; 1800 int d; 1801 1802 if (!value(vi_TAGSTACK)) { /* reset the stack */ 1803 tag_end = tag_buf; 1804 d = tag_depth; 1805 tag_depth = 0; 1806 if (d == 0) 1807 error(gettext("Tagstack not enabled.")); 1808 else 1809 return; 1810 } 1811 if (!tag_depth) 1812 error(gettext("Tagstack empty.")); 1813 1814 /* change to old file */ 1815 if (strcmp(tagstack[tag_depth-1].tag_file, savedfile) ) { 1816 if (!quick) { 1817 ckaw(); 1818 if (chng && dol > zero) 1819 error(value(vi_TERSE) ? 1820 gettext("No write") : gettext("No write since last change (:pop! overrides)")); 1821 } 1822 oglobp = globp; 1823 strcpy(cmdbuf, "e! "); 1824 strcat(cmdbuf, tagstack[tag_depth-1].tag_file); 1825 globp = cmdbuf; 1826 d = peekc; ungetchar(0); 1827 commands(1, 1); 1828 peekc = d; 1829 globp = oglobp; 1830 } 1831 markpr(dot); 1832 /* set line number */ 1833 dot = tagstack[--tag_depth].tag_line; 1834 tag_end = tagstack[tag_depth].tag_file; 1835 } 1836 #endif 1837