1 /* Copyright (c) 1981 Regents of the University of California */ 2 static char *sccsid = "@(#)ex_io.c 7.9 03/27/85"; 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 struct glob G; 281 getone() 282 { 283 register char *str; 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 struct stat statb; 417 418 deletenone(); 419 clrstats(); 420 first = addr2 + 1; 421 if (fstat(io, &statb) < 0) 422 bsize = LBSIZE; 423 else { 424 bsize = statb.st_blksize; 425 if (bsize <= 0) 426 bsize = LBSIZE; 427 } 428 ignore(append(getfile, addr2)); 429 last = dot; 430 /* 431 * if the modeline variable is set, 432 * check the first and last five lines of the file 433 * for a mode line. 434 */ 435 if (value(MODELINE)) { 436 for (a=first; a<=last; a++) { 437 if (a==first+5 && last-first > 10) 438 a = last - 4; 439 getline(*a); 440 checkmodeline(linebuf); 441 } 442 } 443 } 444 445 rop3(c) 446 int c; 447 { 448 449 if (iostats() == 0 && c == 'e') 450 edited++; 451 if (c == 'e') { 452 if (wasalt || firstpat) { 453 register line *addr = zero + oldadot; 454 455 if (addr > dol) 456 addr = dol; 457 if (firstpat) { 458 globp = (*firstpat) ? firstpat : "$"; 459 commands(1,1); 460 firstpat = 0; 461 } else if (addr >= one) { 462 if (inopen) 463 dot = addr; 464 markpr(addr); 465 } else 466 goto other; 467 } else 468 other: 469 if (dol > zero) { 470 if (inopen) 471 dot = one; 472 markpr(one); 473 } 474 if(FIXUNDO) 475 undkind = UNDNONE; 476 if (inopen) { 477 vcline = 0; 478 vreplace(0, LINES, lineDOL()); 479 } 480 } 481 } 482 483 /* 484 * Are these two really the same inode? 485 */ 486 samei(sp, cp) 487 struct stat *sp; 488 char *cp; 489 { 490 struct stat stb; 491 492 if (stat(cp, &stb) < 0 || sp->st_dev != stb.st_dev) 493 return (0); 494 return (sp->st_ino == stb.st_ino); 495 } 496 497 /* Returns from edited() */ 498 #define EDF 0 /* Edited file */ 499 #define NOTEDF -1 /* Not edited file */ 500 #define PARTBUF 1 /* Write of partial buffer to Edited file */ 501 502 /* 503 * Write a file. 504 */ 505 wop(dofname) 506 bool dofname; /* if 1 call filename, else use savedfile */ 507 { 508 register int c, exclam, nonexist; 509 line *saddr1, *saddr2; 510 struct stat stbuf; 511 512 c = 0; 513 exclam = 0; 514 if (dofname) { 515 if (peekchar() == '!') 516 exclam++, ignchar(); 517 ignore(skipwh()); 518 while (peekchar() == '>') 519 ignchar(), c++, ignore(skipwh()); 520 if (c != 0 && c != 2) 521 error("Write forms are 'w' and 'w>>'"); 522 filename('w'); 523 } else { 524 if (savedfile[0] == 0) 525 error("No file|No current filename"); 526 saddr1=addr1; 527 saddr2=addr2; 528 addr1=one; 529 addr2=dol; 530 CP(file, savedfile); 531 if (inopen) { 532 vclrech(0); 533 splitw++; 534 } 535 lprintf("\"%s\"", file); 536 } 537 nonexist = stat(file, &stbuf); 538 switch (c) { 539 540 case 0: 541 if (!exclam && (!value(WRITEANY) || value(READONLY))) 542 switch (edfile()) { 543 544 case NOTEDF: 545 if (nonexist) 546 break; 547 if ((stbuf.st_mode & S_IFMT) == S_IFCHR) { 548 if (samei(&stbuf, "/dev/null")) 549 break; 550 if (samei(&stbuf, "/dev/tty")) 551 break; 552 } 553 io = open(file, 1); 554 if (io < 0) 555 syserror(); 556 if (!isatty(io)) 557 serror(" File exists| File exists - use \"w! %s\" to overwrite", file); 558 close(io); 559 break; 560 561 case EDF: 562 if (value(READONLY)) 563 error(" File is read only"); 564 break; 565 566 case PARTBUF: 567 if (value(READONLY)) 568 error(" File is read only"); 569 error(" Use \"w!\" to write partial buffer"); 570 } 571 cre: 572 /* 573 synctmp(); 574 */ 575 #ifdef V6 576 io = creat(file, 0644); 577 #else 578 io = creat(file, 0666); 579 #endif 580 if (io < 0) 581 syserror(); 582 writing = 1; 583 if (hush == 0) 584 if (nonexist) 585 printf(" [New file]"); 586 else if (value(WRITEANY) && edfile() != EDF) 587 printf(" [Existing file]"); 588 break; 589 590 case 2: 591 io = open(file, 1); 592 if (io < 0) { 593 if (exclam || value(WRITEANY)) 594 goto cre; 595 syserror(); 596 } 597 lseek(io, 0l, 2); 598 break; 599 } 600 putfile(0); 601 ignore(iostats()); 602 if (c != 2 && addr1 == one && addr2 == dol) { 603 if (eq(file, savedfile)) 604 edited = 1; 605 sync(); 606 } 607 if (!dofname) { 608 addr1 = saddr1; 609 addr2 = saddr2; 610 } 611 writing = 0; 612 } 613 614 /* 615 * Is file the edited file? 616 * Work here is that it is not considered edited 617 * if this is a partial buffer, and distinguish 618 * all cases. 619 */ 620 edfile() 621 { 622 623 if (!edited || !eq(file, savedfile)) 624 return (NOTEDF); 625 return (addr1 == one && addr2 == dol ? EDF : PARTBUF); 626 } 627 628 /* 629 * Extract the next line from the io stream. 630 */ 631 char *nextip; 632 633 getfile() 634 { 635 register short c; 636 register char *lp, *fp; 637 638 lp = linebuf; 639 fp = nextip; 640 do { 641 if (--ninbuf < 0) { 642 ninbuf = read(io, genbuf, bsize) - 1; 643 if (ninbuf < 0) { 644 if (lp != linebuf) { 645 lp++; 646 printf(" [Incomplete last line]"); 647 break; 648 } 649 return (EOF); 650 } 651 #ifdef CRYPT 652 if (kflag) { 653 fp = genbuf; 654 while(fp < &genbuf[ninbuf]) { 655 if (*fp++ & 0200) { 656 crblock(perm, genbuf, ninbuf+1, 657 cntch); 658 break; 659 } 660 } 661 } 662 #endif 663 fp = genbuf; 664 cntch += ninbuf+1; 665 } 666 if (lp >= &linebuf[LBSIZE]) { 667 error(" Line too long"); 668 } 669 c = *fp++; 670 if (c == 0) { 671 cntnull++; 672 continue; 673 } 674 if (c & QUOTE) { 675 cntodd++; 676 c &= TRIM; 677 if (c == 0) 678 continue; 679 } 680 *lp++ = c; 681 } while (c != '\n'); 682 *--lp = 0; 683 nextip = fp; 684 cntln++; 685 return (0); 686 } 687 688 /* 689 * Write a range onto the io stream. 690 */ 691 putfile(isfilter) 692 int isfilter; 693 { 694 line *a1; 695 register char *fp, *lp; 696 register int nib; 697 struct stat statb; 698 699 a1 = addr1; 700 clrstats(); 701 cntln = addr2 - a1 + 1; 702 if (cntln == 0) 703 return; 704 if (fstat(io, &statb) < 0) 705 bsize = LBSIZE; 706 else { 707 bsize = statb.st_blksize; 708 if (bsize <= 0) 709 bsize = LBSIZE; 710 } 711 nib = bsize; 712 fp = genbuf; 713 do { 714 getline(*a1++); 715 lp = linebuf; 716 for (;;) { 717 if (--nib < 0) { 718 nib = fp - genbuf; 719 #ifdef CRYPT 720 if(kflag && !isfilter) 721 crblock(perm, genbuf, nib, cntch); 722 #endif 723 if (write(io, genbuf, nib) != nib) { 724 wrerror(); 725 } 726 cntch += nib; 727 nib = bsize - 1; 728 fp = genbuf; 729 } 730 if ((*fp++ = *lp++) == 0) { 731 fp[-1] = '\n'; 732 break; 733 } 734 } 735 } while (a1 <= addr2); 736 nib = fp - genbuf; 737 #ifdef CRYPT 738 if(kflag && !isfilter) 739 crblock(perm, genbuf, nib, cntch); 740 #endif 741 if (write(io, genbuf, nib) != nib) { 742 wrerror(); 743 } 744 cntch += nib; 745 } 746 747 /* 748 * A write error has occurred; if the file being written was 749 * the edited file then we consider it to have changed since it is 750 * now likely scrambled. 751 */ 752 wrerror() 753 { 754 755 if (eq(file, savedfile) && edited) 756 change(); 757 syserror(); 758 } 759 760 /* 761 * Source command, handles nested sources. 762 * Traps errors since it mungs unit 0 during the source. 763 */ 764 short slevel; 765 short ttyindes; 766 767 source(fil, okfail) 768 char *fil; 769 bool okfail; 770 { 771 jmp_buf osetexit; 772 register int saveinp, ointty, oerrno; 773 char *saveglobp; 774 short savepeekc; 775 776 signal(SIGINT, SIG_IGN); 777 saveinp = dup(0); 778 savepeekc = peekc; 779 saveglobp = globp; 780 peekc = 0; globp = 0; 781 if (saveinp < 0) 782 error("Too many nested sources"); 783 if (slevel <= 0) 784 ttyindes = saveinp; 785 close(0); 786 if (open(fil, 0) < 0) { 787 oerrno = errno; 788 setrupt(); 789 dup(saveinp); 790 close(saveinp); 791 errno = oerrno; 792 if (!okfail) 793 filioerr(fil); 794 return; 795 } 796 slevel++; 797 ointty = intty; 798 intty = isatty(0); 799 oprompt = value(PROMPT); 800 value(PROMPT) &= intty; 801 getexit(osetexit); 802 setrupt(); 803 if (setexit() == 0) 804 commands(1, 1); 805 else if (slevel > 1) { 806 close(0); 807 dup(saveinp); 808 close(saveinp); 809 slevel--; 810 resexit(osetexit); 811 reset(); 812 } 813 intty = ointty; 814 value(PROMPT) = oprompt; 815 close(0); 816 dup(saveinp); 817 close(saveinp); 818 globp = saveglobp; 819 peekc = savepeekc; 820 slevel--; 821 resexit(osetexit); 822 } 823 824 /* 825 * Clear io statistics before a read or write. 826 */ 827 clrstats() 828 { 829 830 ninbuf = 0; 831 cntch = 0; 832 cntln = 0; 833 cntnull = 0; 834 cntodd = 0; 835 } 836 837 /* 838 * Io is finished, close the unit and print statistics. 839 */ 840 iostats() 841 { 842 843 (void) fsync(io); 844 close(io); 845 io = -1; 846 if (hush == 0) { 847 if (value(TERSE)) 848 printf(" %d/%D", cntln, cntch); 849 else 850 printf(" %d line%s, %D character%s", cntln, plural((long) cntln), 851 cntch, plural(cntch)); 852 if (cntnull || cntodd) { 853 printf(" ("); 854 if (cntnull) { 855 printf("%D null", cntnull); 856 if (cntodd) 857 printf(", "); 858 } 859 if (cntodd) 860 printf("%D non-ASCII", cntodd); 861 putchar(')'); 862 } 863 noonl(); 864 flush(); 865 } 866 return (cntnull != 0 || cntodd != 0); 867 } 868 869 #if USG | USG3TTY 870 /* It's so wonderful how we all speak the same language... */ 871 # define index strchr 872 # define rindex strrchr 873 #endif 874 875 checkmodeline(line) 876 char *line; 877 { 878 char *beg, *end; 879 char cmdbuf[1024]; 880 char *index(), *rindex(); 881 882 beg = index(line, ':'); 883 if (beg == NULL) 884 return; 885 if (&beg[-3] < line) 886 return; 887 if (!( ( (beg[-3] == ' ' || beg[-3] == '\t') 888 && beg[-2] == 'e' 889 && beg[-1] == 'x') 890 || ( (beg[-3] == ' ' || beg[-3] == '\t') 891 && beg[-2] == 'v' 892 && beg[-1] == 'i'))) return; 893 strncpy(cmdbuf, beg+1, sizeof cmdbuf); 894 end = rindex(cmdbuf, ':'); 895 if (end == NULL) 896 return; 897 *end = 0; 898 globp = cmdbuf; 899 commands(1, 1); 900 } 901