1 /* Copyright (c) 1980 Regents of the University of California */ 2 static char *sccsid = "@(#)ex_io.c 5.6 09/17/80"; 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 syserror(); 315 } 316 if (fstat(io, &stbuf)) 317 syserror(); 318 switch (stbuf.st_mode & S_IFMT) { 319 320 case S_IFBLK: 321 error(" Block special file"); 322 323 case S_IFCHR: 324 if (isatty(io)) 325 error(" Teletype"); 326 if (samei(&stbuf, "/dev/null")) 327 break; 328 error(" Character special file"); 329 330 case S_IFDIR: 331 error(" Directory"); 332 333 case S_IFREG: 334 #ifdef CRYPT 335 if (xflag) 336 break; 337 #endif 338 i = read(io, (char *) &magic, sizeof(magic)); 339 lseek(io, 0l, 0); 340 if (i != sizeof(magic)) 341 break; 342 switch (magic) { 343 344 case 0405: 345 case 0407: 346 case 0410: 347 case 0411: 348 error(" Executable"); 349 350 case 0177545: 351 case 0177555: 352 error(" Archive"); 353 354 default: 355 if (magic & 0100200) 356 error(" Non-ascii file"); 357 break; 358 } 359 } 360 if (value(READONLY) && denied) { 361 value(READONLY) = ovro; 362 denied = 0; 363 } 364 if (c != 'r' && ((stbuf.st_mode & 0222) == 0 || access(file, 2) < 0)) { 365 ovro = value(READONLY); 366 denied = 1; 367 value(READONLY) = 1; 368 } 369 if (value(READONLY)) { 370 printf(" [Read only]"); 371 flush(); 372 } 373 if (c == 'r') 374 setdot(); 375 else 376 setall(); 377 if (FIXUNDO && inopen && c == 'r') 378 undap1 = undap2 = dot + 1; 379 rop2(); 380 rop3(c); 381 } 382 383 rop2() 384 { 385 386 deletenone(); 387 clrstats(); 388 ignore(append(getfile, addr2)); 389 } 390 391 rop3(c) 392 int c; 393 { 394 395 if (iostats() == 0 && c == 'e') 396 edited++; 397 if (c == 'e') { 398 if (wasalt || firstpat) { 399 register line *addr = zero + oldadot; 400 401 if (addr > dol) 402 addr = dol; 403 if (firstpat) { 404 globp = (*firstpat) ? firstpat : "$"; 405 commands(1,1); 406 firstpat = 0; 407 } else if (addr >= one) { 408 if (inopen) 409 dot = addr; 410 markpr(addr); 411 } else 412 goto other; 413 } else 414 other: 415 if (dol > zero) { 416 if (inopen) 417 dot = one; 418 markpr(one); 419 } 420 if(FIXUNDO) 421 undkind = UNDNONE; 422 if (inopen) { 423 vcline = 0; 424 vreplace(0, LINES, lineDOL()); 425 } 426 } 427 if (laste) { 428 #ifdef VMUNIX 429 tlaste(); 430 #endif 431 laste = 0; 432 sync(); 433 } 434 } 435 436 /* 437 * Are these two really the same inode? 438 */ 439 samei(sp, cp) 440 struct stat *sp; 441 char *cp; 442 { 443 struct stat stb; 444 445 if (stat(cp, &stb) < 0 || sp->st_dev != stb.st_dev) 446 return (0); 447 return (sp->st_ino == stb.st_ino); 448 } 449 450 /* Returns from edited() */ 451 #define EDF 0 /* Edited file */ 452 #define NOTEDF -1 /* Not edited file */ 453 #define PARTBUF 1 /* Write of partial buffer to Edited file */ 454 455 /* 456 * Write a file. 457 */ 458 wop(dofname) 459 bool dofname; /* if 1 call filename, else use savedfile */ 460 { 461 register int c, exclam, nonexist; 462 line *saddr1, *saddr2; 463 struct stat stbuf; 464 465 c = 0; 466 exclam = 0; 467 if (dofname) { 468 if (peekchar() == '!') 469 exclam++, ignchar(); 470 ignore(skipwh()); 471 while (peekchar() == '>') 472 ignchar(), c++, ignore(skipwh()); 473 if (c != 0 && c != 2) 474 error("Write forms are 'w' and 'w>>'"); 475 filename('w'); 476 } else { 477 if (savedfile[0] == 0) 478 error("No file|No current filename"); 479 saddr1=addr1; 480 saddr2=addr2; 481 addr1=one; 482 addr2=dol; 483 CP(file, savedfile); 484 if (inopen) { 485 vclrech(0); 486 splitw++; 487 } 488 lprintf("\"%s\"", file); 489 } 490 nonexist = stat(file, &stbuf); 491 switch (c) { 492 493 case 0: 494 if (!exclam && (!value(WRITEANY) || value(READONLY))) 495 switch (edfile()) { 496 497 case NOTEDF: 498 if (nonexist) 499 break; 500 if ((stbuf.st_mode & S_IFMT) == S_IFCHR) { 501 if (samei(&stbuf, "/dev/null")) 502 break; 503 if (samei(&stbuf, "/dev/tty")) 504 break; 505 } 506 io = open(file, 1); 507 if (io < 0) 508 syserror(); 509 if (!isatty(io)) 510 serror(" File exists| File exists - use \"w! %s\" to overwrite", file); 511 close(io); 512 break; 513 514 case EDF: 515 if (value(READONLY)) 516 error(" File is read only"); 517 break; 518 519 case PARTBUF: 520 if (value(READONLY)) 521 error(" File is read only"); 522 error(" Use \"w!\" to write partial buffer"); 523 } 524 cre: 525 /* 526 synctmp(); 527 */ 528 #ifdef V6 529 io = creat(file, 0644); 530 #else 531 io = creat(file, 0666); 532 #endif 533 if (io < 0) 534 syserror(); 535 if (hush == 0) 536 if (nonexist) 537 printf(" [New file]"); 538 else if (value(WRITEANY) && edfile() != EDF) 539 printf(" [Existing file]"); 540 break; 541 542 case 2: 543 io = open(file, 1); 544 if (io < 0) { 545 if (exclam || value(WRITEANY)) 546 goto cre; 547 syserror(); 548 } 549 lseek(io, 0l, 2); 550 break; 551 } 552 putfile(); 553 ignore(iostats()); 554 if (c != 2 && addr1 == one && addr2 == dol) { 555 if (eq(file, savedfile)) 556 edited = 1; 557 sync(); 558 } 559 if (!dofname) { 560 addr1 = saddr1; 561 addr2 = saddr2; 562 } 563 } 564 565 /* 566 * Is file the edited file? 567 * Work here is that it is not considered edited 568 * if this is a partial buffer, and distinguish 569 * all cases. 570 */ 571 edfile() 572 { 573 574 if (!edited || !eq(file, savedfile)) 575 return (NOTEDF); 576 return (addr1 == one && addr2 == dol ? EDF : PARTBUF); 577 } 578 579 /* 580 * Extract the next line from the io stream. 581 */ 582 static char *nextip; 583 584 getfile() 585 { 586 register short c; 587 register char *lp, *fp; 588 589 lp = linebuf; 590 fp = nextip; 591 do { 592 if (--ninbuf < 0) { 593 ninbuf = read(io, genbuf, LBSIZE) - 1; 594 if (ninbuf < 0) { 595 if (lp != linebuf) { 596 lp++; 597 printf(" [Incomplete last line]"); 598 break; 599 } 600 return (EOF); 601 } 602 #ifdef CRYPT 603 fp = genbuf; 604 while(fp < &genbuf[ninbuf]) { 605 if (*fp++ & 0200) { 606 if (kflag) 607 crblock(perm, genbuf, ninbuf+1, 608 cntch); 609 break; 610 } 611 } 612 #endif 613 fp = genbuf; 614 cntch += ninbuf+1; 615 } 616 if (lp >= &linebuf[LBSIZE]) { 617 error(" Line too long"); 618 } 619 c = *fp++; 620 if (c == 0) { 621 cntnull++; 622 continue; 623 } 624 if (c & QUOTE) { 625 cntodd++; 626 c &= TRIM; 627 if (c == 0) 628 continue; 629 } 630 *lp++ = c; 631 } while (c != '\n'); 632 *--lp = 0; 633 nextip = fp; 634 cntln++; 635 return (0); 636 } 637 638 /* 639 * Write a range onto the io stream. 640 */ 641 putfile() 642 { 643 line *a1; 644 register char *fp, *lp; 645 register int nib; 646 647 a1 = addr1; 648 clrstats(); 649 cntln = addr2 - a1 + 1; 650 if (cntln == 0) 651 return; 652 nib = BUFSIZ; 653 fp = genbuf; 654 do { 655 getline(*a1++); 656 lp = linebuf; 657 for (;;) { 658 if (--nib < 0) { 659 nib = fp - genbuf; 660 #ifdef CRYPT 661 if(kflag) 662 crblock(perm, genbuf, nib, cntch); 663 #endif 664 if (write(io, genbuf, nib) != nib) { 665 wrerror(); 666 } 667 cntch += nib; 668 nib = BUFSIZ - 1; 669 fp = genbuf; 670 } 671 if ((*fp++ = *lp++) == 0) { 672 fp[-1] = '\n'; 673 break; 674 } 675 } 676 } while (a1 <= addr2); 677 nib = fp - genbuf; 678 #ifdef CRYPT 679 if(kflag) 680 crblock(perm, genbuf, nib, cntch); 681 #endif 682 if (write(io, genbuf, nib) != nib) { 683 wrerror(); 684 } 685 cntch += nib; 686 } 687 688 /* 689 * A write error has occurred; if the file being written was 690 * the edited file then we consider it to have changed since it is 691 * now likely scrambled. 692 */ 693 wrerror() 694 { 695 696 if (eq(file, savedfile) && edited) 697 change(); 698 syserror(); 699 } 700 701 /* 702 * Source command, handles nested sources. 703 * Traps errors since it mungs unit 0 during the source. 704 */ 705 short slevel; 706 short ttyindes; 707 708 source(fil, okfail) 709 char *fil; 710 bool okfail; 711 { 712 jmp_buf osetexit; 713 register int saveinp, ointty, oerrno; 714 715 signal(SIGINT, SIG_IGN); 716 saveinp = dup(0); 717 if (saveinp < 0) 718 error("Too many nested sources"); 719 if (slevel <= 0) 720 ttyindes = saveinp; 721 close(0); 722 if (open(fil, 0) < 0) { 723 oerrno = errno; 724 setrupt(); 725 dup(saveinp); 726 close(saveinp); 727 errno = oerrno; 728 if (!okfail) 729 filioerr(fil); 730 return; 731 } 732 slevel++; 733 ointty = intty; 734 intty = isatty(0); 735 oprompt = value(PROMPT); 736 value(PROMPT) &= intty; 737 getexit(osetexit); 738 setrupt(); 739 if (setexit() == 0) 740 commands(1, 1); 741 else if (slevel > 1) { 742 close(0); 743 dup(saveinp); 744 close(saveinp); 745 slevel--; 746 resexit(osetexit); 747 reset(); 748 } 749 intty = ointty; 750 value(PROMPT) = oprompt; 751 close(0); 752 dup(saveinp); 753 close(saveinp); 754 slevel--; 755 resexit(osetexit); 756 } 757 758 /* 759 * Clear io statistics before a read or write. 760 */ 761 clrstats() 762 { 763 764 ninbuf = 0; 765 cntch = 0; 766 cntln = 0; 767 cntnull = 0; 768 cntodd = 0; 769 } 770 771 /* 772 * Io is finished, close the unit and print statistics. 773 */ 774 iostats() 775 { 776 777 close(io); 778 io = -1; 779 if (hush == 0) { 780 if (value(TERSE)) 781 printf(" %d/%D", cntln, cntch); 782 else 783 printf(" %d line%s, %D character%s", cntln, plural((long) cntln), 784 cntch, plural(cntch)); 785 if (cntnull || cntodd) { 786 printf(" ("); 787 if (cntnull) { 788 printf("%D null", cntnull); 789 if (cntodd) 790 printf(", "); 791 } 792 if (cntodd) 793 printf("%D non-ASCII", cntodd); 794 putchar(')'); 795 } 796 noonl(); 797 flush(); 798 } 799 return (cntnull != 0 || cntodd != 0); 800 } 801