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