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