1 /* Copyright (c) 1981 Regents of the University of California */ 2 static char *sccsid = "@(#)ex_io.c 7.7 04/17/84"; 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 417 deletenone(); 418 clrstats(); 419 first = addr2 + 1; 420 ignore(append(getfile, addr2)); 421 last = dot; 422 /* 423 * if the modeline variable is set, 424 * check the first and last five lines of the file 425 * for a mode line. 426 */ 427 if (value(MODELINE)) { 428 for (a=first; a<=last; a++) { 429 if (a==first+5 && last-first > 10) 430 a = last - 4; 431 getline(*a); 432 checkmodeline(linebuf); 433 } 434 } 435 } 436 437 rop3(c) 438 int c; 439 { 440 441 if (iostats() == 0 && c == 'e') 442 edited++; 443 if (c == 'e') { 444 if (wasalt || firstpat) { 445 register line *addr = zero + oldadot; 446 447 if (addr > dol) 448 addr = dol; 449 if (firstpat) { 450 globp = (*firstpat) ? firstpat : "$"; 451 commands(1,1); 452 firstpat = 0; 453 } else if (addr >= one) { 454 if (inopen) 455 dot = addr; 456 markpr(addr); 457 } else 458 goto other; 459 } else 460 other: 461 if (dol > zero) { 462 if (inopen) 463 dot = one; 464 markpr(one); 465 } 466 if(FIXUNDO) 467 undkind = UNDNONE; 468 if (inopen) { 469 vcline = 0; 470 vreplace(0, LINES, lineDOL()); 471 } 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 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 *saveglobp; 757 short savepeekc; 758 759 signal(SIGINT, SIG_IGN); 760 saveinp = dup(0); 761 savepeekc = peekc; 762 saveglobp = globp; 763 peekc = 0; globp = 0; 764 if (saveinp < 0) 765 error("Too many nested sources"); 766 if (slevel <= 0) 767 ttyindes = saveinp; 768 close(0); 769 if (open(fil, 0) < 0) { 770 oerrno = errno; 771 setrupt(); 772 dup(saveinp); 773 close(saveinp); 774 errno = oerrno; 775 if (!okfail) 776 filioerr(fil); 777 return; 778 } 779 slevel++; 780 ointty = intty; 781 intty = isatty(0); 782 oprompt = value(PROMPT); 783 value(PROMPT) &= intty; 784 getexit(osetexit); 785 setrupt(); 786 if (setexit() == 0) 787 commands(1, 1); 788 else if (slevel > 1) { 789 close(0); 790 dup(saveinp); 791 close(saveinp); 792 slevel--; 793 resexit(osetexit); 794 reset(); 795 } 796 intty = ointty; 797 value(PROMPT) = oprompt; 798 close(0); 799 dup(saveinp); 800 close(saveinp); 801 globp = saveglobp; 802 peekc = savepeekc; 803 slevel--; 804 resexit(osetexit); 805 } 806 807 /* 808 * Clear io statistics before a read or write. 809 */ 810 clrstats() 811 { 812 813 ninbuf = 0; 814 cntch = 0; 815 cntln = 0; 816 cntnull = 0; 817 cntodd = 0; 818 } 819 820 /* 821 * Io is finished, close the unit and print statistics. 822 */ 823 iostats() 824 { 825 826 (void) fsync(io); 827 close(io); 828 io = -1; 829 if (hush == 0) { 830 if (value(TERSE)) 831 printf(" %d/%D", cntln, cntch); 832 else 833 printf(" %d line%s, %D character%s", cntln, plural((long) cntln), 834 cntch, plural(cntch)); 835 if (cntnull || cntodd) { 836 printf(" ("); 837 if (cntnull) { 838 printf("%D null", cntnull); 839 if (cntodd) 840 printf(", "); 841 } 842 if (cntodd) 843 printf("%D non-ASCII", cntodd); 844 putchar(')'); 845 } 846 noonl(); 847 flush(); 848 } 849 return (cntnull != 0 || cntodd != 0); 850 } 851 852 #if USG | USG3TTY 853 /* It's so wonderful how we all speak the same language... */ 854 # define index strchr 855 # define rindex strrchr 856 #endif 857 858 checkmodeline(line) 859 char *line; 860 { 861 char *beg, *end; 862 char cmdbuf[1024]; 863 char *index(), *rindex(); 864 865 beg = index(line, ':'); 866 if (beg == NULL) 867 return; 868 if (&beg[-3] < line) 869 return; 870 if (!( ( (beg[-3] == ' ' || beg[-3] == '\t') 871 && beg[-2] == 'e' 872 && beg[-1] == 'x') 873 || ( (beg[-3] == ' ' || beg[-3] == '\t') 874 && beg[-2] == 'v' 875 && beg[-1] == 'i'))) return; 876 strncpy(cmdbuf, beg+1, sizeof cmdbuf); 877 end = rindex(cmdbuf, ':'); 878 if (end == NULL) 879 return; 880 *end = 0; 881 globp = cmdbuf; 882 commands(1, 1); 883 } 884