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