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 case '-': 1024 case '+': 1025 while (peekchar() == op) { 1026 ignchar(); 1027 zweight++; 1028 } 1029 case '=': 1030 case '.': 1031 c = getchar(); 1032 break; 1033 1034 case EOF: 1035 znoclear++; 1036 break; 1037 1038 default: 1039 op = 0; 1040 break; 1041 } 1042 if (isdigit(c)) { 1043 nlines = c - '0'; 1044 for(;;) { 1045 c = getchar(); 1046 if (!isdigit(c)) 1047 break; 1048 nlines *= 10; 1049 nlines += c - '0'; 1050 } 1051 if (nlines < lines) 1052 znoclear++; 1053 value(vi_WINDOW) = nlines; 1054 if (op == '=') 1055 nlines += 2; 1056 } 1057 else { 1058 nlines = op == EOF ? value(vi_SCROLL) : 1059 excl ? lines - 1 : value(vi_WINDOW); 1060 } 1061 if (inopen || c != EOF) { 1062 ungetchar(c); 1063 donewline(); 1064 } 1065 addr1 = addr2; 1066 if (addr2 == 0 && dot < dol && op == 0) 1067 addr1 = addr2 = dot+1; 1068 setdot(); 1069 zop2(nlines, op); 1070 } 1071 1072 void 1073 zop2(int nlines, int op) 1074 { 1075 line *split; 1076 1077 split = NULL; 1078 switch (op) { 1079 1080 case EOF: 1081 if (addr2 == dol) 1082 error(gettext("\nAt EOF")); 1083 case '+': 1084 if (addr2 == dol) 1085 error(gettext("At EOF")); 1086 addr2 += nlines * zweight; 1087 if (addr2 > dol) 1088 error(gettext("Hit BOTTOM")); 1089 addr2++; 1090 default: 1091 addr1 = addr2; 1092 addr2 += nlines-1; 1093 dot = addr2; 1094 break; 1095 1096 case '=': 1097 case '.': 1098 znoclear = 0; 1099 nlines--; 1100 nlines >>= 1; 1101 if (op == '=') 1102 nlines--; 1103 addr1 = addr2 - nlines; 1104 if (op == '=') 1105 dot = split = addr2; 1106 addr2 += nlines; 1107 if (op == '.') { 1108 markDOT(); 1109 dot = addr2; 1110 } 1111 break; 1112 1113 case '^': 1114 case '-': 1115 addr2 -= nlines * zweight; 1116 if (addr2 < one) 1117 error(gettext("Hit TOP")); 1118 nlines--; 1119 addr1 = addr2 - nlines; 1120 dot = addr2; 1121 break; 1122 } 1123 if (addr1 <= zero) 1124 addr1 = one; 1125 if (addr2 > dol) 1126 addr2 = dol; 1127 if (dot > dol) 1128 dot = dol; 1129 if (addr1 > addr2) 1130 return; 1131 if (op == EOF && zhadpr) { 1132 getaline(*addr1); 1133 putchar((int)('\r' | QUOTE)); 1134 shudclob = 1; 1135 } else if (znoclear == 0 && clear_screen != NOSTR && !inopen) { 1136 flush1(); 1137 vclear(); 1138 } 1139 if (addr2 - addr1 > 1) 1140 pstart(); 1141 if (split) { 1142 plines(addr1, split - 1, 0); 1143 splitit(); 1144 plines(split, split, 0); 1145 splitit(); 1146 addr1 = split + 1; 1147 } 1148 plines(addr1, addr2, 0); 1149 } 1150 1151 static void 1152 splitit(void) 1153 { 1154 int l; 1155 1156 for (l = columns > 80 ? 40 : columns / 2; l > 0; l--) 1157 putchar('-'); 1158 putnl(); 1159 } 1160 1161 void 1162 plines(line *adr1, line *adr2, bool movedot) 1163 { 1164 line *addr; 1165 1166 pofix(); 1167 for (addr = adr1; addr <= adr2; addr++) { 1168 getaline(*addr); 1169 pline(lineno(addr)); 1170 if (inopen) 1171 putchar((int)('\n' | QUOTE)); 1172 if (movedot) 1173 dot = addr; 1174 } 1175 } 1176 1177 void 1178 pofix(void) 1179 { 1180 1181 if (inopen && Outchar != termchar) { 1182 vnfl(); 1183 setoutt(); 1184 } 1185 } 1186 1187 /* 1188 * Command level undo works easily because 1189 * the editor has a unique temporary file 1190 * index for every line which ever existed. 1191 * We don't have to save large blocks of text, 1192 * only the indices which are small. We do this 1193 * by moving them to after the last line in the 1194 * line buffer array, and marking down info 1195 * about whence they came. 1196 * 1197 * Undo is its own inverse. 1198 */ 1199 void 1200 undo(bool c) 1201 { 1202 int i, k; 1203 line *jp, *kp, *j; 1204 line *dolp1, *newdol, *newadot; 1205 1206 #ifdef UNDOTRACE 1207 if (trace) 1208 vudump("before undo"); 1209 #endif 1210 if (inglobal && inopen <= 0) 1211 error(value(vi_TERSE) ? gettext("Can't undo in global") : 1212 gettext("Can't undo in global commands")); 1213 1214 /* 1215 * Unless flag indicates a forced undo, make sure 1216 * there really was a change before trying to undo it. 1217 */ 1218 1219 if (!c) 1220 somechange(); 1221 1222 /* 1223 * Update change flags. 1224 */ 1225 1226 pkill[0] = pkill[1] = 0; 1227 change(); 1228 if (undkind == UNDMOVE) { 1229 /* 1230 * Command to be undone is a move command. 1231 * This is handled as a special case by noting that 1232 * a move "a,b m c" can be inverted by another move. 1233 */ 1234 if ((i = (jp = unddel) - undap2) > 0) { 1235 /* 1236 * when c > b inverse is a+(c-b),c m a-1 1237 */ 1238 addr2 = jp; 1239 addr1 = (jp = undap1) + i; 1240 unddel = jp-1; 1241 } else { 1242 /* 1243 * when b > c inverse is c+1,c+1+(b-a) m b 1244 */ 1245 addr1 = ++jp; 1246 addr2 = jp + ((unddel = undap2) - undap1); 1247 } 1248 kp = undap1; 1249 move1(0, unddel); 1250 dot = kp; 1251 Command = (unsigned char *)"move"; 1252 killed(); 1253 } else { 1254 int cnt; 1255 1256 newadot = dot; 1257 cnt = lineDOL(); 1258 newdol = dol; 1259 dolp1 = dol + 1; 1260 /* 1261 * Command to be undone is a non-move. 1262 * All such commands are treated as a combination of 1263 * a delete command and a append command. 1264 * We first move the lines appended by the last command 1265 * from undap1 to undap2-1 so that they are just before the 1266 * saved deleted lines. 1267 * 1268 * Assume the editor has: 1269 * 1270 * cursor is on 'c' 1271 * 1272 * (just change lines 5-8) 1273 * 1274 * file is: 1) ab 1275 * 2) cd 1276 * 3) ef 1277 * 4) gh 1278 * undap1: 5) 12 1279 * 6) 34 1280 * 7) 56 1281 * 8) 78 1282 * undap2: 9) qr 1283 * 10) st 1284 * 11) uv 1285 * 12) wx 1286 * dol: 13) yz 1287 * 1288 * UNDO AREA: 1289 * dol+1: 5) ij 1290 * 6) kl 1291 * 7) mn 1292 * unddol: 8) op 1293 */ 1294 1295 /* 1296 * If this is a change (not a delete/put), 1297 * then we must move the text between undap1 and undap2 1298 * and it must not be at the bottom of the file 1299 */ 1300 1301 if ((i = (kp = undap2) - (jp = undap1)) > 0) { 1302 if (kp != dolp1) { 1303 1304 /* 1305 * FILE: LINE INITIAL REV1 REV2 REV3 1306 * 1307 * 1) ab ab ab ab 1308 * 2) cd cd cd cd 1309 * 3) ef ef ef ef 1310 * unddel: 4) gh gh gh gh 1311 * undap1: 5) 12 78 78 qr 1312 * 6) 34 56 56 st 1313 * 7) 56 34 34 uv 1314 * 8) 78 12 12 wx 1315 * undap2: 9) qr qr yz yz 1316 * 10) st st wx 12 1317 * 11) uv uv uv 34 1318 * 12) wx wx st 56 1319 * dol: 13) yz yz qr 78 1320 * 1321 * UNDO AREA: 1322 * dol+1: 5) ij ij ij ij 1323 * 6) kl kl kl kl 1324 * 7) mn mn mn mn 1325 * unddol: 8) op op op op 1326 */ 1327 1328 reverse(jp, kp); 1329 reverse(kp, dolp1); 1330 reverse(jp, dolp1); 1331 } 1332 /* 1333 * Unddel, the line just before the spot where this 1334 * test was deleted, may have moved. Account for 1335 * this in restoration of saved deleted lines. 1336 */ 1337 if (unddel >= jp) 1338 unddel -= i; 1339 1340 /* 1341 * The last line (dol) may have changed, 1342 * account for this. 1343 */ 1344 newdol -= i; 1345 1346 /* 1347 * For the case where no lines are restored, dot 1348 * is the line before the first line deleted. 1349 */ 1350 dot = jp-1; 1351 } 1352 /* 1353 * Now put the deleted lines, if any, back where they were. 1354 * Basic operation is: dol+1,unddol m unddel 1355 */ 1356 if (undkind == UNDPUT) { 1357 unddel = undap1 - 1; 1358 squish(); 1359 } 1360 1361 /* 1362 * Set jp to the line where deleted text is to be added. 1363 */ 1364 jp = unddel + 1; 1365 1366 /* 1367 * Set kp to end of undo save area. 1368 * 1369 * If there is any deleted text to be added, do reverses. 1370 */ 1371 1372 if ((i = (kp = unddol) - dol) > 0) { 1373 1374 /* 1375 * If deleted lines are not to be appended 1376 * to the bottom of the file... 1377 */ 1378 1379 if (jp != dolp1) { 1380 /* 1381 * FILE: LINE START REV1 REV2 REV3 1382 * 1) ab ab ab ab 1383 * 2) cd cd cd cd 1384 * 3) ef ef ef ef 1385 * unddel: 4) gh gh gh gh 1386 * undap1: 5) qr 78 78 ij 1387 * 6) st 56 56 kl 1388 * 7) uv 34 34 mn 1389 * 8) wx 12 12 op 1390 * undap2: 9) yz yz yz qr 1391 * 10) 12 wx wx st 1392 * 11) 34 uv uv uv 1393 * 12) 56 st st wx 1394 * dol: 13) 78 qr qr yz 1395 * 1396 * UNDO AREA: 1397 * dol+1: 5) ij ij op 12 1398 * 6) kl kl mn 34 1399 * 7) mn mn kl 56 1400 * unddol: 8) op op ij 78 1401 */ 1402 1403 reverse(jp, dolp1); 1404 reverse(dolp1, ++kp); 1405 reverse(jp, kp); 1406 } 1407 /* 1408 * Account for possible forward motion of the target 1409 * (where the deleted lines were restored) for after 1410 * restoration of the deleted lines. 1411 */ 1412 if (undap1 >= jp) 1413 undap1 += i; 1414 /* 1415 * Dot is the first resurrected line. 1416 */ 1417 dot = jp; 1418 1419 /* 1420 * Account for a shift in the last line (dol). 1421 */ 1422 1423 newdol += i; 1424 } 1425 /* 1426 * Clean up so we are invertible 1427 */ 1428 unddel = undap1 - 1; 1429 undap1 = jp; 1430 undap2 = jp + i; 1431 dol = newdol; 1432 netchHAD(cnt); 1433 if (undkind == UNDALL) { 1434 dot = undadot; 1435 undadot = newadot; 1436 } else 1437 undkind = UNDCHANGE; 1438 /* 1439 * Now relocate all marks for lines that were modified, 1440 * since the marks point to lines whose address has 1441 * been modified from the save area to the current 1442 * area 1443 */ 1444 1445 for (j=unddol; j> dol; j--) 1446 for (k=0; k<=25; k++) 1447 if (names[k] == *(j)) 1448 names[k]= *((undap1+(j-dolp1)) ); 1449 } 1450 /* 1451 * Defensive programming - after a munged undadot. 1452 * Also handle empty buffer case. 1453 */ 1454 if ((dot <= zero || dot > dol) && dot != dol) 1455 dot = one; 1456 #ifdef UNDOTRACE 1457 if (trace) 1458 vudump("after undo"); 1459 #endif 1460 } 1461 1462 /* 1463 * Be (almost completely) sure there really 1464 * was a change, before claiming to undo. 1465 */ 1466 void 1467 somechange(void) 1468 { 1469 line *ip, *jp; 1470 1471 switch (undkind) { 1472 1473 case UNDMOVE: 1474 return; 1475 1476 case UNDCHANGE: 1477 if (undap1 == undap2 && dol == unddol) 1478 break; 1479 return; 1480 1481 case UNDPUT: 1482 if (undap1 != undap2) 1483 return; 1484 break; 1485 1486 case UNDALL: 1487 if (unddol - dol != lineDOL()) 1488 return; 1489 for (ip = one, jp = dol + 1; ip <= dol; ip++, jp++) 1490 if ((*ip &~ 01) != (*jp &~ 01)) 1491 return; 1492 break; 1493 1494 case UNDNONE: 1495 error(gettext("Nothing to undo")); 1496 } 1497 error(value(vi_TERSE) ? gettext("Nothing changed") : 1498 gettext("Last undoable command didn't change anything")); 1499 } 1500 1501 /* 1502 * Map command: 1503 * map src dest 1504 * 1505 * un is true if this is unmap command 1506 * ab is true if this is abbr command 1507 */ 1508 void 1509 mapcmd(int un, int ab) 1510 { 1511 unsigned char lhs[100], rhs[100]; /* max sizes resp. */ 1512 unsigned char *p; 1513 int c; /* char --> int */ 1514 unsigned char *dname; 1515 struct maps *mp; /* the map structure we are working on */ 1516 1517 mp = ab ? abbrevs : exclam() ? immacs : arrows; 1518 if (skipend()) { 1519 int i; 1520 1521 /* print current mapping values */ 1522 if (peekchar() != EOF) 1523 ignchar(); 1524 if (un) 1525 error(gettext("Missing lhs")); 1526 if (inopen) 1527 pofix(); 1528 for (i=0; i< MAXNOMACS && mp[i].mapto; i++) 1529 if (mp[i].cap) { 1530 lprintf("%s", mp[i].descr); 1531 putchar('\t'); 1532 lprintf("%s", mp[i].cap); 1533 putchar('\t'); 1534 lprintf("%s", mp[i].mapto); 1535 putNFL(); 1536 } 1537 return; 1538 } 1539 1540 (void)skipwh(); 1541 for (p=lhs; ; ) { 1542 c = getchar(); 1543 if (c == CTRL('v')) { 1544 c = getchar(); 1545 } else if (!un && any(c, " \t")) { 1546 /* End of lhs */ 1547 break; 1548 } else if (endcmd(c) && c!='"') { 1549 ungetchar(c); 1550 if (un) { 1551 donewline(); 1552 *p = 0; 1553 addmac(lhs, (unsigned char *)NOSTR, 1554 (unsigned char *)NOSTR, mp); 1555 return; 1556 } else 1557 error(gettext("Missing rhs")); 1558 } 1559 *p++ = c; 1560 } 1561 *p = 0; 1562 1563 if (skipend()) 1564 error(gettext("Missing rhs")); 1565 for (p=rhs; ; ) { 1566 c = getchar(); 1567 if (c == CTRL('v')) { 1568 c = getchar(); 1569 } else if (endcmd(c) && c!='"') { 1570 ungetchar(c); 1571 break; 1572 } 1573 *p++ = c; 1574 } 1575 *p = 0; 1576 donewline(); 1577 /* 1578 * Special hack for function keys: #1 means key f1, etc. 1579 * If the terminal doesn't have function keys, we just use #1. 1580 */ 1581 if (lhs[0] == '#') { 1582 unsigned char *fnkey; 1583 unsigned char *fkey(); 1584 unsigned char funkey[3]; 1585 1586 fnkey = fkey(lhs[1] - '0'); 1587 funkey[0] = 'f'; funkey[1] = lhs[1]; funkey[2] = 0; 1588 if (fnkey) 1589 strcpy(lhs, fnkey); 1590 dname = funkey; 1591 } else { 1592 dname = lhs; 1593 } 1594 addmac(lhs,rhs,dname,mp); 1595 } 1596 1597 /* 1598 * Add a macro definition to those that already exist. The sequence of 1599 * chars "src" is mapped into "dest". If src is already mapped into something 1600 * this overrides the mapping. There is no recursion. Unmap is done by 1601 * using NOSTR for dest. Dname is what to show in listings. mp is 1602 * the structure to affect (arrows, etc). 1603 */ 1604 void 1605 addmac(unsigned char *src, unsigned char *dest, unsigned char *dname, 1606 struct maps *mp) 1607 { 1608 int slot, zer; 1609 1610 #ifdef UNDOTRACE 1611 if (trace) 1612 fprintf(trace, "addmac(src='%s', dest='%s', dname='%s', mp=%x\n", src, dest, dname, mp); 1613 #endif 1614 if (dest && mp==arrows) { 1615 /* 1616 * Prevent tail recursion. We really should be 1617 * checking to see if src is a suffix of dest 1618 * but this makes mapping involving escapes that 1619 * is reasonable mess up. 1620 */ 1621 if (src[1] == 0 && src[0] == dest[strlen(dest)-1]) 1622 error(gettext("No tail recursion")); 1623 /* 1624 * We don't let the user rob himself of ":", and making 1625 * multi char words is a bad idea so we don't allow it. 1626 * Note that if user sets mapinput and maps all of return, 1627 * linefeed, and escape, they can hurt themself. This is 1628 * so weird I don't bother to check for it. 1629 */ 1630 if (isalpha(src[0]) && isascii(src[0]) && src[1] || any(src[0],":")) 1631 error(gettext("Too dangerous to map that")); 1632 } 1633 else if (dest) { 1634 /* check for tail recursion in input mode: fussier */ 1635 if (eq(src, dest+strlen(dest)-strlen(src))) 1636 error(gettext("No tail recursion")); 1637 } 1638 /* 1639 * If the src were null it would cause the dest to 1640 * be mapped always forever. This is not good. 1641 */ 1642 if (src == (unsigned char *)NOSTR || src[0] == 0) 1643 error(gettext("Missing lhs")); 1644 1645 /* see if we already have a def for src */ 1646 zer = -1; 1647 for (slot=0; slot < MAXNOMACS && mp[slot].mapto; slot++) { 1648 if (mp[slot].cap) { 1649 if (eq(src, mp[slot].cap) || eq(src, mp[slot].mapto)) 1650 break; /* if so, reuse slot */ 1651 } else { 1652 zer = slot; /* remember an empty slot */ 1653 } 1654 } 1655 1656 if (slot >= MAXNOMACS) 1657 error(gettext("Too many macros")); 1658 1659 if (dest == (unsigned char *)NOSTR) { 1660 /* unmap */ 1661 if (mp[slot].cap) { 1662 mp[slot].cap = (unsigned char *)NOSTR; 1663 mp[slot].descr = (unsigned char *)NOSTR; 1664 } else { 1665 error(value(vi_TERSE) ? gettext("Not mapped") : 1666 gettext("That macro wasn't mapped")); 1667 } 1668 return; 1669 } 1670 1671 /* reuse empty slot, if we found one and src isn't already defined */ 1672 if (zer >= 0 && mp[slot].mapto == 0) 1673 slot = zer; 1674 1675 /* if not, append to end */ 1676 if (msnext == 0) /* first time */ 1677 msnext = mapspace; 1678 /* Check is a bit conservative, we charge for dname even if reusing src */ 1679 if (msnext - mapspace + strlen(dest) + strlen(src) + strlen(dname) + 3 > MAXCHARMACS) 1680 error(gettext("Too much macro text")); 1681 CP(msnext, src); 1682 mp[slot].cap = msnext; 1683 msnext += strlen(src) + 1; /* plus 1 for null on the end */ 1684 CP(msnext, dest); 1685 mp[slot].mapto = msnext; 1686 msnext += strlen(dest) + 1; 1687 if (dname) { 1688 CP(msnext, dname); 1689 mp[slot].descr = msnext; 1690 msnext += strlen(dname) + 1; 1691 } else { 1692 /* default descr to string user enters */ 1693 mp[slot].descr = src; 1694 } 1695 } 1696 1697 /* 1698 * Implements macros from command mode. c is the buffer to 1699 * get the macro from. 1700 */ 1701 void 1702 cmdmac(c) 1703 unsigned char c; 1704 { 1705 unsigned char macbuf[BUFSIZE]; 1706 line *ad, *a1, *a2; 1707 unsigned char *oglobp; 1708 short pk; 1709 bool oinglobal; 1710 1711 lastmac = c; 1712 oglobp = globp; 1713 oinglobal = inglobal; 1714 pk = peekc; peekc = 0; 1715 if (inglobal < 2) 1716 inglobal = 1; 1717 regbuf(c, macbuf, sizeof(macbuf)); 1718 a1 = addr1; a2 = addr2; 1719 for (ad=a1; ad<=a2; ad++) { 1720 globp = macbuf; 1721 dot = ad; 1722 commands(1,1); 1723 } 1724 globp = oglobp; 1725 inglobal = oinglobal; 1726 peekc = pk; 1727 } 1728 1729 unsigned char * 1730 vgetpass(prompt) 1731 char *prompt; 1732 { 1733 unsigned char *p; 1734 int c; 1735 static unsigned char pbuf[9]; 1736 1737 /* In ex mode, let the system hassle with setting no echo */ 1738 if (!inopen) 1739 return (unsigned char *)getpass(prompt); 1740 viprintf("%s", prompt); flush(); 1741 for (p=pbuf; (c = getkey())!='\n' && c!=EOF && c!='\r';) { 1742 if (p < &pbuf[8]) 1743 *p++ = c; 1744 } 1745 *p = '\0'; 1746 return(pbuf); 1747 } 1748 1749 1750 #ifdef TAG_STACK 1751 #define TSTACKSIZE 20 1752 struct tagstack { 1753 line *tag_line; 1754 char *tag_file; 1755 } tagstack[TSTACKSIZE]; 1756 static int tag_depth = 0; 1757 1758 static char tag_buf[ 1024 ]; 1759 static char *tag_end = tag_buf; 1760 1761 void 1762 savetag(char *name) /* saves location where we are */ 1763 { 1764 if( !value(vi_TAGSTACK) ) 1765 return; 1766 if(tag_depth >= TSTACKSIZE) { 1767 error(gettext("Tagstack too deep.")); 1768 } 1769 if( strlen( name ) + 1 + tag_end >= &tag_buf[1024]) { 1770 error(gettext("Too many tags.")); 1771 } 1772 tagstack[tag_depth].tag_line = dot; 1773 tagstack[tag_depth++].tag_file = tag_end; 1774 while(*tag_end++ = *name++) 1775 ; 1776 } 1777 1778 /* 1779 * Undo a "savetag". 1780 */ 1781 void 1782 unsavetag(void) 1783 { 1784 if (!value(vi_TAGSTACK)) 1785 return; 1786 if (tag_depth > 0) 1787 tag_end = tagstack[--tag_depth].tag_file; 1788 } 1789 1790 void 1791 poptag(quick) /* puts us back where we came from */ 1792 bool quick; 1793 { 1794 unsigned char cmdbuf[100]; 1795 unsigned char *oglobp; 1796 int d; 1797 1798 if (!value(vi_TAGSTACK)) { /* reset the stack */ 1799 tag_end = tag_buf; 1800 d = tag_depth; 1801 tag_depth = 0; 1802 if (d == 0) 1803 error(gettext("Tagstack not enabled.")); 1804 else 1805 return; 1806 } 1807 if (!tag_depth) 1808 error(gettext("Tagstack empty.")); 1809 1810 /* change to old file */ 1811 if (strcmp(tagstack[tag_depth-1].tag_file, savedfile) ) { 1812 if (!quick) { 1813 ckaw(); 1814 if (chng && dol > zero) 1815 error(value(vi_TERSE) ? 1816 gettext("No write") : gettext("No write since last change (:pop! overrides)")); 1817 } 1818 oglobp = globp; 1819 strcpy(cmdbuf, "e! "); 1820 strcat(cmdbuf, tagstack[tag_depth-1].tag_file); 1821 globp = cmdbuf; 1822 d = peekc; ungetchar(0); 1823 commands(1, 1); 1824 peekc = d; 1825 globp = oglobp; 1826 } 1827 markpr(dot); 1828 /* set line number */ 1829 dot = tagstack[--tag_depth].tag_line; 1830 tag_end = tagstack[tag_depth].tag_file; 1831 } 1832 #endif 1833