1 /* Copyright (c) 1981 Regents of the University of California */ 2 static char *sccsid = "@(#)ex_io.c 7.2 07/11/81"; 3 #include "ex.h" 4 #include "ex_argv.h" 5 #include "ex_temp.h" 6 #include "ex_tty.h" 7 #include "ex_vis.h" 8 9 /* 10 * File input/output, source, preserve and recover 11 */ 12 13 /* 14 * Following remember where . was in the previous file for return 15 * on file switching. 16 */ 17 int altdot; 18 int oldadot; 19 bool wasalt; 20 short isalt; 21 22 long cntch; /* Count of characters on unit io */ 23 #ifndef VMUNIX 24 short cntln; /* Count of lines " */ 25 #else 26 int cntln; 27 #endif 28 long cntnull; /* Count of nulls " */ 29 long cntodd; /* Count of non-ascii characters " */ 30 31 /* 32 * Parse file name for command encoded by comm. 33 * If comm is E then command is doomed and we are 34 * parsing just so user won't have to retype the name. 35 */ 36 filename(comm) 37 int comm; 38 { 39 register int c = comm, d; 40 register int i; 41 42 d = getchar(); 43 if (endcmd(d)) { 44 if (savedfile[0] == 0 && comm != 'f') 45 error("No file|No current filename"); 46 CP(file, savedfile); 47 wasalt = (isalt > 0) ? isalt-1 : 0; 48 isalt = 0; 49 oldadot = altdot; 50 if (c == 'e' || c == 'E') 51 altdot = lineDOT(); 52 if (d == EOF) 53 ungetchar(d); 54 } else { 55 ungetchar(d); 56 getone(); 57 eol(); 58 if (savedfile[0] == 0 && c != 'E' && c != 'e') { 59 c = 'e'; 60 edited = 0; 61 } 62 wasalt = strcmp(file, altfile) == 0; 63 oldadot = altdot; 64 switch (c) { 65 66 case 'f': 67 edited = 0; 68 /* fall into ... */ 69 70 case 'e': 71 if (savedfile[0]) { 72 altdot = lineDOT(); 73 CP(altfile, savedfile); 74 } 75 CP(savedfile, file); 76 break; 77 78 default: 79 if (file[0]) { 80 if (c != 'E') 81 altdot = lineDOT(); 82 CP(altfile, file); 83 } 84 break; 85 } 86 } 87 if (hush && comm != 'f' || comm == 'E') 88 return; 89 if (file[0] != 0) { 90 lprintf("\"%s\"", file); 91 if (comm == 'f') { 92 if (value(READONLY)) 93 printf(" [Read only]"); 94 if (!edited) 95 printf(" [Not edited]"); 96 if (tchng) 97 printf(" [Modified]"); 98 } 99 flush(); 100 } else 101 printf("No file "); 102 if (comm == 'f') { 103 if (!(i = lineDOL())) 104 i++; 105 printf(" line %d of %d --%ld%%--", lineDOT(), lineDOL(), 106 (long) 100 * lineDOT() / i); 107 } 108 } 109 110 /* 111 * Get the argument words for a command into genbuf 112 * expanding # and %. 113 */ 114 getargs() 115 { 116 register int c; 117 register char *cp, *fp; 118 static char fpatbuf[32]; /* hence limit on :next +/pat */ 119 120 pastwh(); 121 if (peekchar() == '+') { 122 for (cp = fpatbuf;;) { 123 c = *cp++ = getchar(); 124 if (cp >= &fpatbuf[sizeof(fpatbuf)]) 125 error("Pattern too long"); 126 if (c == '\\' && isspace(peekchar())) 127 c = getchar(); 128 if (c == EOF || isspace(c)) { 129 ungetchar(c); 130 *--cp = 0; 131 firstpat = &fpatbuf[1]; 132 break; 133 } 134 } 135 } 136 if (skipend()) 137 return (0); 138 CP(genbuf, "echo "); cp = &genbuf[5]; 139 for (;;) { 140 c = getchar(); 141 if (endcmd(c)) { 142 ungetchar(c); 143 break; 144 } 145 switch (c) { 146 147 case '\\': 148 if (any(peekchar(), "#%|")) 149 c = getchar(); 150 /* fall into... */ 151 152 default: 153 if (cp > &genbuf[LBSIZE - 2]) 154 flong: 155 error("Argument buffer overflow"); 156 *cp++ = c; 157 break; 158 159 case '#': 160 fp = altfile; 161 if (*fp == 0) 162 error("No alternate filename@to substitute for #"); 163 goto filexp; 164 165 case '%': 166 fp = savedfile; 167 if (*fp == 0) 168 error("No current filename@to substitute for %%"); 169 filexp: 170 while (*fp) { 171 if (cp > &genbuf[LBSIZE - 2]) 172 goto flong; 173 *cp++ = *fp++; 174 } 175 break; 176 } 177 } 178 *cp = 0; 179 return (1); 180 } 181 182 /* 183 * Glob the argument words in genbuf, or if no globbing 184 * is implied, just split them up directly. 185 */ 186 glob(gp) 187 struct glob *gp; 188 { 189 int pvec[2]; 190 register char **argv = gp->argv; 191 register char *cp = gp->argspac; 192 register int c; 193 char ch; 194 int nleft = NCARGS; 195 196 gp->argc0 = 0; 197 if (gscan() == 0) { 198 register char *v = genbuf + 5; /* strlen("echo ") */ 199 200 for (;;) { 201 while (isspace(*v)) 202 v++; 203 if (!*v) 204 break; 205 *argv++ = cp; 206 while (*v && !isspace(*v)) 207 *cp++ = *v++; 208 *cp++ = 0; 209 gp->argc0++; 210 } 211 *argv = 0; 212 return; 213 } 214 if (pipe(pvec) < 0) 215 error("Can't make pipe to glob"); 216 pid = fork(); 217 io = pvec[0]; 218 if (pid < 0) { 219 close(pvec[1]); 220 error("Can't fork to do glob"); 221 } 222 if (pid == 0) { 223 int oerrno; 224 225 close(1); 226 dup(pvec[1]); 227 close(pvec[0]); 228 close(2); /* so errors don't mess up the screen */ 229 open("/dev/null", 1); 230 execl(svalue(SHELL), "sh", "-c", genbuf, 0); 231 oerrno = errno; close(1); dup(2); errno = oerrno; 232 filioerr(svalue(SHELL)); 233 } 234 close(pvec[1]); 235 do { 236 *argv = cp; 237 for (;;) { 238 if (read(io, &ch, 1) != 1) { 239 close(io); 240 c = -1; 241 } else 242 c = ch & TRIM; 243 if (c <= 0 || isspace(c)) 244 break; 245 *cp++ = c; 246 if (--nleft <= 0) 247 error("Arg list too long"); 248 } 249 if (cp != *argv) { 250 --nleft; 251 *cp++ = 0; 252 gp->argc0++; 253 if (gp->argc0 >= NARGS) 254 error("Arg list too long"); 255 argv++; 256 } 257 } while (c >= 0); 258 waitfor(); 259 if (gp->argc0 == 0) 260 error("No match"); 261 } 262 263 /* 264 * Scan genbuf for shell metacharacters. 265 * Set is union of v7 shell and csh metas. 266 */ 267 gscan() 268 { 269 register char *cp; 270 271 for (cp = genbuf; *cp; cp++) 272 if (any(*cp, "~{[*?$`'\"\\")) 273 return (1); 274 return (0); 275 } 276 277 /* 278 * Parse one filename into file. 279 */ 280 getone() 281 { 282 register char *str; 283 struct glob G; 284 285 if (getargs() == 0) 286 error("Missing filename"); 287 glob(&G); 288 if (G.argc0 > 1) 289 error("Ambiguous|Too many file names"); 290 str = G.argv[G.argc0 - 1]; 291 if (strlen(str) > FNSIZE - 4) 292 error("Filename too long"); 293 samef: 294 CP(file, str); 295 } 296 297 /* 298 * Read a file from the world. 299 * C is command, 'e' if this really an edit (or a recover). 300 */ 301 rop(c) 302 int c; 303 { 304 register int i; 305 struct stat stbuf; 306 short magic; 307 static int ovro; /* old value(READONLY) */ 308 static int denied; /* 1 if READONLY was set due to file permissions */ 309 310 io = open(file, 0); 311 if (io < 0) { 312 if (c == 'e' && errno == ENOENT) { 313 edited++; 314 /* 315 * If the user just did "ex foo" he is probably 316 * creating a new file. Don't be an error, since 317 * this is ugly, and it screws up the + option. 318 */ 319 if (!seenprompt) { 320 printf(" [New file]"); 321 noonl(); 322 return; 323 } 324 } 325 syserror(); 326 } 327 if (fstat(io, &stbuf)) 328 syserror(); 329 switch (stbuf.st_mode & S_IFMT) { 330 331 case S_IFBLK: 332 error(" Block special file"); 333 334 case S_IFCHR: 335 if (isatty(io)) 336 error(" Teletype"); 337 if (samei(&stbuf, "/dev/null")) 338 break; 339 error(" Character special file"); 340 341 case S_IFDIR: 342 error(" Directory"); 343 344 case S_IFREG: 345 #ifdef CRYPT 346 if (xflag) 347 break; 348 #endif 349 i = read(io, (char *) &magic, sizeof(magic)); 350 lseek(io, 0l, 0); 351 if (i != sizeof(magic)) 352 break; 353 switch (magic) { 354 355 case 0405: /* data overlay on exec */ 356 case 0407: /* unshared */ 357 case 0410: /* shared text */ 358 case 0411: /* separate I/D */ 359 case 0413: /* VM/Unix demand paged */ 360 case 0430: /* PDP-11 Overlay shared */ 361 case 0431: /* PDP-11 Overlay sep I/D */ 362 error(" Executable"); 363 364 /* 365 * We do not forbid the editing of portable archives 366 * because it is reasonable to edit them, especially 367 * if they are archives of text files. This is 368 * especially useful if you archive source files together 369 * and copy them to another system with ~%take, since 370 * the files sometimes show up munged and must be fixed. 371 */ 372 case 0177545: 373 case 0177555: 374 error(" Archive"); 375 376 default: 377 #ifdef mbb 378 /* C/70 has a 10 bit byte */ 379 if (magic & 03401600) 380 #else 381 /* Everybody else has an 8 bit byte */ 382 if (magic & 0100200) 383 #endif 384 error(" Non-ascii file"); 385 break; 386 } 387 } 388 if (c != 'r') { 389 if (value(READONLY) && denied) { 390 value(READONLY) = ovro; 391 denied = 0; 392 } 393 if ((stbuf.st_mode & 0222) == 0 || access(file, 2) < 0) { 394 ovro = value(READONLY); 395 denied = 1; 396 value(READONLY) = 1; 397 } 398 } 399 if (value(READONLY)) { 400 printf(" [Read only]"); 401 flush(); 402 } 403 if (c == 'r') 404 setdot(); 405 else 406 setall(); 407 if (FIXUNDO && inopen && c == 'r') 408 undap1 = undap2 = dot + 1; 409 rop2(); 410 rop3(c); 411 } 412 413 rop2() 414 { 415 line *first, *last, *a; 416 417 deletenone(); 418 clrstats(); 419 first = addr2 + 1; 420 ignore(append(getfile, addr2)); 421 last = dot; 422 for (a=first; a<=last; a++) { 423 if (a==first+5 && last-first > 10) 424 a = last - 4; 425 getline(*a); 426 checkmodeline(linebuf); 427 } 428 } 429 430 rop3(c) 431 int c; 432 { 433 434 if (iostats() == 0 && c == 'e') 435 edited++; 436 if (c == 'e') { 437 if (wasalt || firstpat) { 438 register line *addr = zero + oldadot; 439 440 if (addr > dol) 441 addr = dol; 442 if (firstpat) { 443 globp = (*firstpat) ? firstpat : "$"; 444 commands(1,1); 445 firstpat = 0; 446 } else if (addr >= one) { 447 if (inopen) 448 dot = addr; 449 markpr(addr); 450 } else 451 goto other; 452 } else 453 other: 454 if (dol > zero) { 455 if (inopen) 456 dot = one; 457 markpr(one); 458 } 459 if(FIXUNDO) 460 undkind = UNDNONE; 461 if (inopen) { 462 vcline = 0; 463 vreplace(0, LINES, lineDOL()); 464 } 465 } 466 if (laste) { 467 #ifdef VMUNIX 468 tlaste(); 469 #endif 470 laste = 0; 471 sync(); 472 } 473 } 474 475 /* 476 * Are these two really the same inode? 477 */ 478 samei(sp, cp) 479 struct stat *sp; 480 char *cp; 481 { 482 struct stat stb; 483 484 if (stat(cp, &stb) < 0 || sp->st_dev != stb.st_dev) 485 return (0); 486 return (sp->st_ino == stb.st_ino); 487 } 488 489 /* Returns from edited() */ 490 #define EDF 0 /* Edited file */ 491 #define NOTEDF -1 /* Not edited file */ 492 #define PARTBUF 1 /* Write of partial buffer to Edited file */ 493 494 /* 495 * Write a file. 496 */ 497 wop(dofname) 498 bool dofname; /* if 1 call filename, else use savedfile */ 499 { 500 register int c, exclam, nonexist; 501 line *saddr1, *saddr2; 502 struct stat stbuf; 503 504 c = 0; 505 exclam = 0; 506 if (dofname) { 507 if (peekchar() == '!') 508 exclam++, ignchar(); 509 ignore(skipwh()); 510 while (peekchar() == '>') 511 ignchar(), c++, ignore(skipwh()); 512 if (c != 0 && c != 2) 513 error("Write forms are 'w' and 'w>>'"); 514 filename('w'); 515 } else { 516 if (savedfile[0] == 0) 517 error("No file|No current filename"); 518 saddr1=addr1; 519 saddr2=addr2; 520 addr1=one; 521 addr2=dol; 522 CP(file, savedfile); 523 if (inopen) { 524 vclrech(0); 525 splitw++; 526 } 527 lprintf("\"%s\"", file); 528 } 529 nonexist = stat(file, &stbuf); 530 switch (c) { 531 532 case 0: 533 if (!exclam && (!value(WRITEANY) || value(READONLY))) 534 switch (edfile()) { 535 536 case NOTEDF: 537 if (nonexist) 538 break; 539 if ((stbuf.st_mode & S_IFMT) == S_IFCHR) { 540 if (samei(&stbuf, "/dev/null")) 541 break; 542 if (samei(&stbuf, "/dev/tty")) 543 break; 544 } 545 io = open(file, 1); 546 if (io < 0) 547 syserror(); 548 if (!isatty(io)) 549 serror(" File exists| File exists - use \"w! %s\" to overwrite", file); 550 close(io); 551 break; 552 553 case EDF: 554 if (value(READONLY)) 555 error(" File is read only"); 556 break; 557 558 case PARTBUF: 559 if (value(READONLY)) 560 error(" File is read only"); 561 error(" Use \"w!\" to write partial buffer"); 562 } 563 cre: 564 /* 565 synctmp(); 566 */ 567 #ifdef V6 568 io = creat(file, 0644); 569 #else 570 io = creat(file, 0666); 571 #endif 572 if (io < 0) 573 syserror(); 574 writing = 1; 575 if (hush == 0) 576 if (nonexist) 577 printf(" [New file]"); 578 else if (value(WRITEANY) && edfile() != EDF) 579 printf(" [Existing file]"); 580 break; 581 582 case 2: 583 io = open(file, 1); 584 if (io < 0) { 585 if (exclam || value(WRITEANY)) 586 goto cre; 587 syserror(); 588 } 589 lseek(io, 0l, 2); 590 break; 591 } 592 putfile(0); 593 ignore(iostats()); 594 if (c != 2 && addr1 == one && addr2 == dol) { 595 if (eq(file, savedfile)) 596 edited = 1; 597 sync(); 598 } 599 if (!dofname) { 600 addr1 = saddr1; 601 addr2 = saddr2; 602 } 603 writing = 0; 604 } 605 606 /* 607 * Is file the edited file? 608 * Work here is that it is not considered edited 609 * if this is a partial buffer, and distinguish 610 * all cases. 611 */ 612 edfile() 613 { 614 615 if (!edited || !eq(file, savedfile)) 616 return (NOTEDF); 617 return (addr1 == one && addr2 == dol ? EDF : PARTBUF); 618 } 619 620 /* 621 * Extract the next line from the io stream. 622 */ 623 static char *nextip; 624 625 getfile() 626 { 627 register short c; 628 register char *lp, *fp; 629 630 lp = linebuf; 631 fp = nextip; 632 do { 633 if (--ninbuf < 0) { 634 ninbuf = read(io, genbuf, LBSIZE) - 1; 635 if (ninbuf < 0) { 636 if (lp != linebuf) { 637 lp++; 638 printf(" [Incomplete last line]"); 639 break; 640 } 641 return (EOF); 642 } 643 #ifdef CRYPT 644 fp = genbuf; 645 while(fp < &genbuf[ninbuf]) { 646 if (*fp++ & 0200) { 647 if (kflag) 648 crblock(perm, genbuf, ninbuf+1, 649 cntch); 650 break; 651 } 652 } 653 #endif 654 fp = genbuf; 655 cntch += ninbuf+1; 656 } 657 if (lp >= &linebuf[LBSIZE]) { 658 error(" Line too long"); 659 } 660 c = *fp++; 661 if (c == 0) { 662 cntnull++; 663 continue; 664 } 665 if (c & QUOTE) { 666 cntodd++; 667 c &= TRIM; 668 if (c == 0) 669 continue; 670 } 671 *lp++ = c; 672 } while (c != '\n'); 673 *--lp = 0; 674 nextip = fp; 675 cntln++; 676 return (0); 677 } 678 679 /* 680 * Write a range onto the io stream. 681 */ 682 putfile(isfilter) 683 int isfilter; 684 { 685 line *a1; 686 register char *fp, *lp; 687 register int nib; 688 689 a1 = addr1; 690 clrstats(); 691 cntln = addr2 - a1 + 1; 692 if (cntln == 0) 693 return; 694 nib = BUFSIZ; 695 fp = genbuf; 696 do { 697 getline(*a1++); 698 lp = linebuf; 699 for (;;) { 700 if (--nib < 0) { 701 nib = fp - genbuf; 702 #ifdef CRYPT 703 if(kflag && !isfilter) 704 crblock(perm, genbuf, nib, cntch); 705 #endif 706 if (write(io, genbuf, nib) != nib) { 707 wrerror(); 708 } 709 cntch += nib; 710 nib = BUFSIZ - 1; 711 fp = genbuf; 712 } 713 if ((*fp++ = *lp++) == 0) { 714 fp[-1] = '\n'; 715 break; 716 } 717 } 718 } while (a1 <= addr2); 719 nib = fp - genbuf; 720 #ifdef CRYPT 721 if(kflag && !isfilter) 722 crblock(perm, genbuf, nib, cntch); 723 #endif 724 if (write(io, genbuf, nib) != nib) { 725 wrerror(); 726 } 727 cntch += nib; 728 } 729 730 /* 731 * A write error has occurred; if the file being written was 732 * the edited file then we consider it to have changed since it is 733 * now likely scrambled. 734 */ 735 wrerror() 736 { 737 738 if (eq(file, savedfile) && edited) 739 change(); 740 syserror(); 741 } 742 743 /* 744 * Source command, handles nested sources. 745 * Traps errors since it mungs unit 0 during the source. 746 */ 747 short slevel; 748 short ttyindes; 749 750 source(fil, okfail) 751 char *fil; 752 bool okfail; 753 { 754 jmp_buf osetexit; 755 register int saveinp, ointty, oerrno; 756 char savepeekc, *saveglobp; 757 758 signal(SIGINT, SIG_IGN); 759 saveinp = dup(0); 760 savepeekc = peekc; 761 saveglobp = globp; 762 peekc = 0; globp = 0; 763 if (saveinp < 0) 764 error("Too many nested sources"); 765 if (slevel <= 0) 766 ttyindes = saveinp; 767 close(0); 768 if (open(fil, 0) < 0) { 769 oerrno = errno; 770 setrupt(); 771 dup(saveinp); 772 close(saveinp); 773 errno = oerrno; 774 if (!okfail) 775 filioerr(fil); 776 return; 777 } 778 slevel++; 779 ointty = intty; 780 intty = isatty(0); 781 oprompt = value(PROMPT); 782 value(PROMPT) &= intty; 783 getexit(osetexit); 784 setrupt(); 785 if (setexit() == 0) 786 commands(1, 1); 787 else if (slevel > 1) { 788 close(0); 789 dup(saveinp); 790 close(saveinp); 791 slevel--; 792 resexit(osetexit); 793 reset(); 794 } 795 intty = ointty; 796 value(PROMPT) = oprompt; 797 close(0); 798 dup(saveinp); 799 close(saveinp); 800 globp = saveglobp; 801 peekc = savepeekc; 802 slevel--; 803 resexit(osetexit); 804 } 805 806 /* 807 * Clear io statistics before a read or write. 808 */ 809 clrstats() 810 { 811 812 ninbuf = 0; 813 cntch = 0; 814 cntln = 0; 815 cntnull = 0; 816 cntodd = 0; 817 } 818 819 /* 820 * Io is finished, close the unit and print statistics. 821 */ 822 iostats() 823 { 824 825 close(io); 826 io = -1; 827 if (hush == 0) { 828 if (value(TERSE)) 829 printf(" %d/%D", cntln, cntch); 830 else 831 printf(" %d line%s, %D character%s", cntln, plural((long) cntln), 832 cntch, plural(cntch)); 833 if (cntnull || cntodd) { 834 printf(" ("); 835 if (cntnull) { 836 printf("%D null", cntnull); 837 if (cntodd) 838 printf(", "); 839 } 840 if (cntodd) 841 printf("%D non-ASCII", cntodd); 842 putchar(')'); 843 } 844 noonl(); 845 flush(); 846 } 847 return (cntnull != 0 || cntodd != 0); 848 } 849 850 #if USG | USG3TTY 851 /* It's so wonderful how we all speak the same language... */ 852 # define index strchr 853 # define rindex strrchr 854 #endif 855 856 checkmodeline(line) 857 char *line; 858 { 859 char *beg, *end; 860 char cmdbuf[1024]; 861 char *index(), *rindex(); 862 863 beg = index(line, ':'); 864 if (beg == NULL) 865 return; 866 if (beg[-2] != 'e' && beg[-2] != 'v') return; 867 if (beg[-1] != 'x' && beg[-1] != 'i') return; 868 869 strncpy(cmdbuf, beg+1, sizeof cmdbuf); 870 end = rindex(cmdbuf, ':'); 871 if (end == NULL) 872 return; 873 *end = 0; 874 globp = cmdbuf; 875 commands(1, 1); 876 } 877