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