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