1 /*- 2 * Copyright (c) 1980, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.proprietary.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)ex_subr.c 8.1 (Berkeley) 06/09/93"; 10 #endif /* not lint */ 11 12 #include "ex.h" 13 #include "ex_re.h" 14 #include "ex_tty.h" 15 #include "ex_vis.h" 16 #include "pathnames.h" 17 18 /* 19 * Random routines, in alphabetical order. 20 */ 21 22 any(c, s) 23 int c; 24 register char *s; 25 { 26 register int x; 27 28 while (x = *s++) 29 if (x == c) 30 return (1); 31 return (0); 32 } 33 34 backtab(i) 35 register int i; 36 { 37 register int j; 38 39 j = i % value(SHIFTWIDTH); 40 if (j == 0) 41 j = value(SHIFTWIDTH); 42 i -= j; 43 if (i < 0) 44 i = 0; 45 return (i); 46 } 47 48 change() 49 { 50 51 tchng++; 52 chng = tchng; 53 } 54 55 /* 56 * Column returns the number of 57 * columns occupied by printing the 58 * characters through position cp of the 59 * current line. 60 */ 61 column(cp) 62 register char *cp; 63 { 64 65 if (cp == 0) 66 cp = &linebuf[LBSIZE - 2]; 67 return (qcolumn(cp, (char *) 0)); 68 } 69 70 /* 71 * Ignore a comment to the end of the line. 72 * This routine eats the trailing newline so don't call newline(). 73 */ 74 comment() 75 { 76 register int c; 77 78 do { 79 c = ex_getchar(); 80 } while (c != '\n' && c != EOF); 81 if (c == EOF) 82 ungetchar(c); 83 } 84 85 Copy(to, from, size) 86 register char *from, *to; 87 register int size; 88 { 89 90 if (size > 0) 91 do 92 *to++ = *from++; 93 while (--size > 0); 94 } 95 96 copyw(to, from, size) 97 register line *from, *to; 98 register int size; 99 { 100 if (size > 0) 101 do 102 *to++ = *from++; 103 while (--size > 0); 104 } 105 106 copywR(to, from, size) 107 register line *from, *to; 108 register int size; 109 { 110 111 while (--size >= 0) 112 to[size] = from[size]; 113 } 114 115 ctlof(c) 116 int c; 117 { 118 119 return (c == TRIM ? '?' : c | ('A' - 1)); 120 } 121 122 dingdong() 123 { 124 125 if (VB) 126 putpad(VB); 127 else if (value(ERRORBELLS)) 128 putch('\207'); 129 } 130 131 fixindent(indent) 132 int indent; 133 { 134 register int i; 135 register char *cp; 136 137 i = whitecnt(genbuf); 138 cp = vpastwh(genbuf); 139 if (*cp == 0 && i == indent && linebuf[0] == 0) { 140 genbuf[0] = 0; 141 return (i); 142 } 143 CP(genindent(i), cp); 144 return (i); 145 } 146 147 filioerr(cp) 148 char *cp; 149 { 150 register int oerrno = errno; 151 152 lprintf("\"%s\"", cp); 153 errno = oerrno; 154 syserror(); 155 } 156 157 char * 158 genindent(indent) 159 register int indent; 160 { 161 register char *cp; 162 163 for (cp = genbuf; indent >= value(TABSTOP); indent -= value(TABSTOP)) 164 *cp++ = '\t'; 165 for (; indent > 0; indent--) 166 *cp++ = ' '; 167 return (cp); 168 } 169 170 getDOT() 171 { 172 173 getline(*dot); 174 } 175 176 line * 177 getmark(c) 178 register int c; 179 { 180 register line *addr; 181 182 for (addr = one; addr <= dol; addr++) 183 if (names[c - 'a'] == (*addr &~ 01)) { 184 return (addr); 185 } 186 return (0); 187 } 188 189 getn(cp) 190 register char *cp; 191 { 192 register int i = 0; 193 194 while (isdigit(*cp)) 195 i = i * 10 + *cp++ - '0'; 196 if (*cp) 197 return (0); 198 return (i); 199 } 200 201 ignnEOF() 202 { 203 register int c = ex_getchar(); 204 205 if (c == EOF) 206 ungetchar(c); 207 else if (c=='"') 208 comment(); 209 } 210 211 iswhite(c) 212 int c; 213 { 214 215 return (c == ' ' || c == '\t'); 216 } 217 218 junk(c) 219 register int c; 220 { 221 222 if (c && !value(BEAUTIFY)) 223 return (0); 224 if (c >= ' ' && c != TRIM) 225 return (0); 226 switch (c) { 227 228 case '\t': 229 case '\n': 230 case '\f': 231 return (0); 232 233 default: 234 return (1); 235 } 236 } 237 238 killed() 239 { 240 241 killcnt(addr2 - addr1 + 1); 242 } 243 244 killcnt(cnt) 245 register int cnt; 246 { 247 248 if (inopen) { 249 notecnt = cnt; 250 notenam = notesgn = ""; 251 return; 252 } 253 if (!notable(cnt)) 254 return; 255 ex_printf("%d lines", cnt); 256 if (value(TERSE) == 0) { 257 ex_printf(" %c%s", Command[0] | ' ', Command + 1); 258 if (Command[strlen(Command) - 1] != 'e') 259 ex_putchar('e'); 260 ex_putchar('d'); 261 } 262 putNFL(); 263 } 264 265 lineno(a) 266 line *a; 267 { 268 269 return (a - zero); 270 } 271 272 lineDOL() 273 { 274 275 return (lineno(dol)); 276 } 277 278 lineDOT() 279 { 280 281 return (lineno(dot)); 282 } 283 284 markDOT() 285 { 286 287 markpr(dot); 288 } 289 290 markpr(which) 291 line *which; 292 { 293 294 if ((inglobal == 0 || inopen) && which <= endcore) { 295 names['z'-'a'+1] = *which & ~01; 296 if (inopen) 297 ncols['z'-'a'+1] = cursor; 298 } 299 } 300 301 markreg(c) 302 register int c; 303 { 304 305 if (c == '\'' || c == '`') 306 return ('z' + 1); 307 if (c >= 'a' && c <= 'z') 308 return (c); 309 return (0); 310 } 311 312 /* 313 * Mesg decodes the terse/verbose strings. Thus 314 * 'xxx@yyy' -> 'xxx' if terse, else 'xxx yyy' 315 * 'xxx|yyy' -> 'xxx' if terse, else 'yyy' 316 * All others map to themselves. 317 */ 318 char * 319 mesg(str) 320 register char *str; 321 { 322 register char *cp; 323 324 str = strcpy(genbuf, str); 325 for (cp = str; *cp; cp++) 326 switch (*cp) { 327 328 case '@': 329 if (value(TERSE)) 330 *cp = 0; 331 else 332 *cp = ' '; 333 break; 334 335 case '|': 336 if (value(TERSE) == 0) 337 return (cp + 1); 338 *cp = 0; 339 break; 340 } 341 return (str); 342 } 343 344 /*VARARGS2*/ 345 merror(seekpt, i) 346 #ifndef EXSTRINGS 347 char *seekpt; 348 #else 349 # ifdef lint 350 char *seekpt; 351 # else 352 int seekpt; 353 # endif 354 #endif 355 int i; 356 { 357 register char *cp = linebuf; 358 359 if (seekpt == 0) 360 return; 361 merror1(seekpt); 362 if (*cp == '\n') 363 putnl(), cp++; 364 if (inopen > 0 && CE) 365 vclreol(); 366 if (SO && SE) 367 putpad(SO); 368 ex_printf(mesg(cp), i); 369 if (SO && SE) 370 putpad(SE); 371 } 372 373 merror1(seekpt) 374 #ifndef EXSTRINGS 375 char *seekpt; 376 #else 377 # ifdef lint 378 char *seekpt; 379 # else 380 int seekpt; 381 # endif 382 #endif 383 { 384 385 #ifndef EXSTRINGS 386 strcpy(linebuf, seekpt); 387 #else 388 lseek(erfile, (long) seekpt, 0); 389 if (read(erfile, linebuf, 128) < 2) 390 CP(linebuf, "ERROR"); 391 #endif 392 } 393 394 morelines() 395 { 396 #ifdef UNIX_SBRK 397 char *sbrk(); 398 399 if ((int) sbrk(1024 * sizeof (line)) == -1) 400 return (-1); 401 endcore += 1024; 402 return (0); 403 #else 404 /* 405 * We can never be guaranteed that we can get more memory 406 * beyond "endcore". So we just punt every time. 407 */ 408 return -1; 409 #endif 410 } 411 412 nonzero() 413 { 414 415 if (addr1 == zero) { 416 notempty(); 417 error("Nonzero address required@on this command"); 418 } 419 } 420 421 notable(i) 422 int i; 423 { 424 425 return (hush == 0 && !inglobal && i > value(REPORT)); 426 } 427 428 429 notempty() 430 { 431 432 if (dol == zero) 433 error("No lines@in the buffer"); 434 } 435 436 437 netchHAD(cnt) 438 int cnt; 439 { 440 441 netchange(lineDOL() - cnt); 442 } 443 444 netchange(i) 445 register int i; 446 { 447 register char *cp; 448 449 if (i > 0) 450 notesgn = cp = "more "; 451 else 452 notesgn = cp = "fewer ", i = -i; 453 if (inopen) { 454 notecnt = i; 455 notenam = ""; 456 return; 457 } 458 if (!notable(i)) 459 return; 460 ex_printf(mesg("%d %slines@in file after %s"), i, cp, Command); 461 putNFL(); 462 } 463 464 putmark(addr) 465 line *addr; 466 { 467 468 putmk1(addr, putline()); 469 } 470 471 putmk1(addr, n) 472 register line *addr; 473 int n; 474 { 475 register line *markp; 476 register oldglobmk; 477 478 oldglobmk = *addr & 1; 479 *addr &= ~1; 480 for (markp = (anymarks ? names : &names['z'-'a'+1]); 481 markp <= &names['z'-'a'+1]; markp++) 482 if (*markp == *addr) 483 *markp = n; 484 *addr = n | oldglobmk; 485 } 486 487 char * 488 plural(i) 489 long i; 490 { 491 492 return (i == 1 ? "" : "s"); 493 } 494 495 int qcount(); 496 short vcntcol; 497 498 qcolumn(lim, gp) 499 register char *lim, *gp; 500 { 501 register int x; 502 int (*OO)(); 503 504 OO = Outchar; 505 Outchar = qcount; 506 vcntcol = 0; 507 if (lim != NULL) 508 x = lim[1], lim[1] = 0; 509 pline(0); 510 if (lim != NULL) 511 lim[1] = x; 512 if (gp) 513 while (*gp) 514 ex_putchar(*gp++); 515 Outchar = OO; 516 return (vcntcol); 517 } 518 519 int 520 qcount(c) 521 int c; 522 { 523 524 if (c == '\t') { 525 vcntcol += value(TABSTOP) - vcntcol % value(TABSTOP); 526 return; 527 } 528 vcntcol++; 529 } 530 531 reverse(a1, a2) 532 register line *a1, *a2; 533 { 534 register line t; 535 536 for (;;) { 537 t = *--a2; 538 if (a2 <= a1) 539 return; 540 *a2 = *a1; 541 *a1++ = t; 542 } 543 } 544 545 save(a1, a2) 546 line *a1; 547 register line *a2; 548 { 549 register int more; 550 551 if (!FIXUNDO) 552 return; 553 #ifdef TRACE 554 if (trace) 555 vudump("before save"); 556 #endif 557 undkind = UNDNONE; 558 undadot = dot; 559 more = (a2 - a1 + 1) - (unddol - dol); 560 while (more > (endcore - truedol)) 561 if (morelines() < 0) 562 #ifdef UNIX_SBRK 563 error("Out of memory@saving lines for undo - try using ed"); 564 #else 565 error("Out of memory@saving lines for undo - try increasing linelimit"); 566 #endif 567 if (more) 568 (*(more > 0 ? copywR : copyw))(unddol + more + 1, unddol + 1, 569 (truedol - unddol)); 570 unddol += more; 571 truedol += more; 572 copyw(dol + 1, a1, a2 - a1 + 1); 573 undkind = UNDALL; 574 unddel = a1 - 1; 575 undap1 = a1; 576 undap2 = a2 + 1; 577 #ifdef TRACE 578 if (trace) 579 vudump("after save"); 580 #endif 581 } 582 583 save12() 584 { 585 586 save(addr1, addr2); 587 } 588 589 saveall() 590 { 591 592 save(one, dol); 593 } 594 595 span() 596 { 597 598 return (addr2 - addr1 + 1); 599 } 600 601 ex_sync() 602 { 603 604 chng = 0; 605 tchng = 0; 606 xchng = 0; 607 } 608 609 610 skipwh() 611 { 612 register int wh; 613 614 wh = 0; 615 while (iswhite(peekchar())) { 616 wh++; 617 ignchar(); 618 } 619 return (wh); 620 } 621 622 /*VARARGS2*/ 623 smerror(seekpt, cp) 624 #ifdef lint 625 char *seekpt; 626 #else 627 int seekpt; 628 #endif 629 char *cp; 630 { 631 632 if (seekpt == 0) 633 return; 634 merror1(seekpt); 635 if (inopen && CE) 636 vclreol(); 637 if (SO && SE) 638 putpad(SO); 639 lprintf(mesg(linebuf), cp); 640 if (SO && SE) 641 putpad(SE); 642 } 643 644 char * 645 strend(cp) 646 register char *cp; 647 { 648 649 while (*cp) 650 cp++; 651 return (cp); 652 } 653 654 strcLIN(dp) 655 char *dp; 656 { 657 658 CP(linebuf, dp); 659 } 660 661 syserror() 662 { 663 char *strerror(); 664 665 dirtcnt = 0; 666 ex_putchar(' '); 667 error(strerror(errno)); 668 } 669 670 /* 671 * Return the column number that results from being in column col and 672 * hitting a tab, where tabs are set every ts columns. Work right for 673 * the case where col > COLUMNS, even if ts does not divide COLUMNS. 674 */ 675 tabcol(col, ts) 676 int col, ts; 677 { 678 int offset, result; 679 680 if (col >= COLUMNS) { 681 offset = COLUMNS * (col/COLUMNS); 682 col -= offset; 683 } else 684 offset = 0; 685 result = col + ts - (col % ts) + offset; 686 return (result); 687 } 688 689 char * 690 vfindcol(i) 691 int i; 692 { 693 register char *cp; 694 register int (*OO)() = Outchar; 695 696 Outchar = qcount; 697 ignore(qcolumn(linebuf - 1, NOSTR)); 698 for (cp = linebuf; *cp && vcntcol < i; cp++) 699 ex_putchar(*cp); 700 if (cp != linebuf) 701 cp--; 702 Outchar = OO; 703 return (cp); 704 } 705 706 char * 707 vskipwh(cp) 708 register char *cp; 709 { 710 711 while (iswhite(*cp) && cp[1]) 712 cp++; 713 return (cp); 714 } 715 716 717 char * 718 vpastwh(cp) 719 register char *cp; 720 { 721 722 while (iswhite(*cp)) 723 cp++; 724 return (cp); 725 } 726 727 whitecnt(cp) 728 register char *cp; 729 { 730 register int i; 731 732 i = 0; 733 for (;;) 734 switch (*cp++) { 735 736 case '\t': 737 i += value(TABSTOP) - i % value(TABSTOP); 738 break; 739 740 case ' ': 741 i++; 742 break; 743 744 default: 745 return (i); 746 } 747 } 748 749 #ifdef lint 750 Ignore(a) 751 char *a; 752 { 753 754 a = a; 755 } 756 757 Ignorf(a) 758 int (*a)(); 759 { 760 761 a = a; 762 } 763 #endif 764 765 markit(addr) 766 line *addr; 767 { 768 769 if (addr != dot && addr >= one && addr <= dol) 770 markDOT(); 771 } 772 773 /* 774 * The following code is defensive programming against a bug in the 775 * pdp-11 overlay implementation. Sometimes it goes nuts and asks 776 * for an overlay with some garbage number, which generates an emt 777 * trap. This is a less than elegant solution, but it is somewhat 778 * better than core dumping and losing your work, leaving your tty 779 * in a weird state, etc. 780 */ 781 int _ovno; 782 void 783 onemt() 784 { 785 signal(SIGEMT, onemt); 786 /* 2 and 3 are valid on 11/40 type vi, so */ 787 if (_ovno < 0 || _ovno > 3) 788 _ovno = 0; 789 error("emt trap, _ovno is %d @ - try again"); 790 } 791 792 /* 793 * When a hangup occurs our actions are similar to a preserve 794 * command. If the buffer has not been [Modified], then we do 795 * nothing but remove the temporary files and exit. 796 * Otherwise, we sync the temp file and then attempt a preserve. 797 * If the preserve succeeds, we unlink our temp files. 798 * If the preserve fails, we leave the temp files as they are 799 * as they are a backup even without preservation if they 800 * are not removed. 801 */ 802 void 803 onhup() 804 { 805 806 /* 807 * USG tty driver can send multiple HUP's!! 808 */ 809 signal(SIGINT, SIG_IGN); 810 signal(SIGHUP, SIG_IGN); 811 if (chng == 0) { 812 cleanup(1); 813 ex_exit(0); 814 } 815 if (setexit() == 0) { 816 if (preserve()) { 817 cleanup(1); 818 ex_exit(0); 819 } 820 } 821 ex_exit(1); 822 } 823 824 /* 825 * An interrupt occurred. Drain any output which 826 * is still in the output buffering pipeline. 827 * Catch interrupts again. Unless we are in visual 828 * reset the output state (out of -nl mode, e.g). 829 * Then like a normal error (with the \n before Interrupt 830 * suppressed in visual mode). 831 */ 832 void 833 onintr() 834 { 835 836 #ifndef CBREAK 837 signal(SIGINT, onintr); 838 #else 839 signal(SIGINT, inopen ? vintr : onintr); 840 #endif 841 alarm(0); /* in case we were called from map */ 842 draino(); 843 if (!inopen) { 844 pstop(); 845 setlastchar('\n'); 846 #ifdef CBREAK 847 } 848 #else 849 } else 850 vraw(); 851 #endif 852 error("\nInterrupt" + inopen); 853 } 854 855 /* 856 * If we are interruptible, enable interrupts again. 857 * In some critical sections we turn interrupts off, 858 * but not very often. 859 */ 860 setrupt() 861 { 862 863 if (ruptible) { 864 #ifndef CBREAK 865 signal(SIGINT, onintr); 866 #else 867 signal(SIGINT, inopen ? vintr : onintr); 868 #endif 869 #ifdef SIGTSTP 870 if (dosusp) 871 signal(SIGTSTP, onsusp); 872 #endif 873 } 874 } 875 876 preserve() 877 { 878 879 #ifdef VMUNIX 880 tflush(); 881 #endif 882 synctmp(); 883 pid = vfork(); 884 if (pid < 0) 885 return (0); 886 if (pid == 0) { 887 close(0); 888 dup(tfile); 889 execl(_PATH_EXPRESERVE, "expreserve", (char *) 0); 890 ex_exit(1); 891 } 892 waitfor(); 893 if (rpid == pid && status == 0) 894 return (1); 895 return (0); 896 } 897 898 #ifndef V6 899 ex_exit(i) 900 int i; 901 { 902 903 # ifdef TRACE 904 if (trace) 905 fclose(trace); 906 # endif 907 _exit(i); 908 } 909 #endif 910 911 #ifdef SIGTSTP 912 /* 913 * We have just gotten a susp. Suspend and prepare to resume. 914 */ 915 void 916 onsusp() 917 { 918 ttymode f; 919 struct winsize win; 920 921 f = setty(normf); 922 vnfl(); 923 putpad(TE); 924 flush(); 925 926 (void) sigsetmask(0); 927 signal(SIGTSTP, SIG_DFL); 928 kill(0, SIGTSTP); 929 930 /* the pc stops here */ 931 932 signal(SIGTSTP, onsusp); 933 vcontin(0); 934 ignore(setty(f)); 935 if (!inopen) 936 error((char *) 0); 937 else { 938 #ifdef TIOCGWINSZ 939 if (ioctl(0, TIOCGWINSZ, &win) >= 0) 940 if (win.ws_row != winsz.ws_row || 941 win.ws_col != winsz.ws_col) 942 winch(); 943 #endif 944 if (vcnt < 0) { 945 vcnt = -vcnt; 946 if (state == VISUAL) 947 vclear(); 948 else if (state == CRTOPEN) 949 vcnt = 0; 950 } 951 vdirty(0, LINES); 952 vrepaint(cursor); 953 } 954 } 955 #endif 956