1 /*- 2 * Copyright (c) 1980, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.proprietary.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)ex_io.c 8.1 (Berkeley) 06/09/93"; 10 #endif /* not lint */ 11 12 #include "ex.h" 13 #include "ex_argv.h" 14 #include "ex_temp.h" 15 #include "ex_tty.h" 16 #include "ex_vis.h" 17 #include <sys/file.h> 18 #include <sys/exec.h> 19 #include "pathnames.h" 20 21 /* 22 * File input/output, source, preserve and recover 23 */ 24 25 /* 26 * Following remember where . was in the previous file for return 27 * on file switching. 28 */ 29 int altdot; 30 int oldadot; 31 bool wasalt; 32 short isalt; 33 34 long cntch; /* Count of characters on unit io */ 35 #ifndef VMUNIX 36 short cntln; /* Count of lines " */ 37 #else 38 int cntln; 39 #endif 40 long cntnull; /* Count of nulls " */ 41 long cntodd; /* Count of non-ascii characters " */ 42 43 #ifdef FLOCKFILE 44 /* 45 * The alternate, saved and current file are locked the extent of the 46 * time that they are active. If the saved file is exchanged 47 * with the alternate file, the file descriptors are exchanged 48 * and the lock is not released. 49 */ 50 int io_savedfile, io_altfile, io_curr ; 51 int lock_savedfile, lock_altfile, lock_curr ; 52 #endif FLOCKFILE 53 54 /* 55 * Parse file name for command encoded by comm. 56 * If comm is E then command is doomed and we are 57 * parsing just so user won't have to retype the name. 58 */ 59 filename(comm) 60 int comm; 61 { 62 register int c = comm, d; 63 register int i; 64 #ifdef FLOCKFILE 65 int lock ; 66 67 lock = 0 ; 68 #endif FLOCKFILE 69 70 d = ex_getchar(); 71 if (endcmd(d)) { 72 if (savedfile[0] == 0 && comm != 'f') 73 error("No file|No current filename"); 74 CP(file, savedfile); 75 #ifdef FLOCKFILE 76 if (io_curr && io_curr != io_savedfile) close(io_curr) ; 77 lock = lock_curr = lock_savedfile ; 78 io_curr = io_savedfile ; 79 #endif FLOCKFILE 80 wasalt = (isalt > 0) ? isalt-1 : 0; 81 isalt = 0; 82 oldadot = altdot; 83 if (c == 'e' || c == 'E') 84 altdot = lineDOT(); 85 if (d == EOF) 86 ungetchar(d); 87 } else { 88 ungetchar(d); 89 getone(); 90 eol(); 91 if (savedfile[0] == 0 && c != 'E' && c != 'e') { 92 c = 'e'; 93 edited = 0; 94 } 95 wasalt = strcmp(file, altfile) == 0; 96 oldadot = altdot; 97 switch (c) { 98 99 case 'f': 100 edited = 0; 101 /* fall into ... */ 102 103 case 'e': 104 if (savedfile[0]) { 105 #ifdef FLOCKFILE 106 if (strcmp(file,savedfile) == 0) break ; 107 #endif FLOCKFILE 108 altdot = lineDOT(); 109 CP(altfile, savedfile); 110 #ifdef FLOCKFILE 111 if (io_altfile) close (io_altfile) ; 112 io_altfile = io_savedfile ; 113 lock_altfile = lock_savedfile ; 114 io_savedfile = 0 ; 115 #endif FLOCKFILE 116 } 117 CP(savedfile, file); 118 #ifdef FLOCKFILE 119 io_savedfile = io_curr ; 120 lock_savedfile = lock_curr ; 121 io_curr = 0 ; lock = lock_curr = 0 ; 122 #endif FLOCKFILE 123 break; 124 125 default: 126 if (file[0]) { 127 #ifdef FLOCKFILE 128 if (wasalt) break ; 129 #endif 130 if (c != 'E') 131 altdot = lineDOT(); 132 CP(altfile, file); 133 #ifdef FLOCKFILE 134 if (io_altfile 135 && io_altfile != io_curr) close (io_altfile) ; 136 io_altfile = io_curr ; 137 lock_altfile = lock_curr ; 138 io_curr = 0 ; lock = lock_curr = 0 ; 139 #endif FLOCKFILE 140 } 141 break; 142 } 143 } 144 if (hush && comm != 'f' || comm == 'E') 145 return; 146 if (file[0] != 0) { 147 lprintf("\"%s\"", file); 148 if (comm == 'f') { 149 if (value(READONLY)) 150 ex_printf(" [Read only]"); 151 if (!edited) 152 ex_printf(" [Not edited]"); 153 if (tchng) 154 ex_printf(" [Modified]"); 155 #ifdef FLOCKFILE 156 if (lock == LOCK_SH) 157 ex_printf(" [Shared lock]") ; 158 else if (lock == LOCK_EX) 159 ex_printf(" [Exclusive lock]") ; 160 #endif FLOCKFILE 161 } 162 flush(); 163 } else 164 ex_printf("No file "); 165 if (comm == 'f') { 166 if (!(i = lineDOL())) 167 i++; 168 ex_printf(" line %d of %d --%ld%%--", lineDOT(), lineDOL(), 169 (long) 100 * lineDOT() / i); 170 } 171 } 172 173 /* 174 * Get the argument words for a command into genbuf 175 * expanding # and %. 176 */ 177 getargs() 178 { 179 register int c; 180 register char *cp, *fp; 181 static char fpatbuf[32]; /* hence limit on :next +/pat */ 182 183 pastwh(); 184 if (peekchar() == '+') { 185 for (cp = fpatbuf;;) { 186 c = *cp++ = ex_getchar(); 187 if (cp >= &fpatbuf[sizeof(fpatbuf)]) 188 error("Pattern too long"); 189 if (c == '\\' && isspace(peekchar())) 190 c = ex_getchar(); 191 if (c == EOF || isspace(c)) { 192 ungetchar(c); 193 *--cp = 0; 194 firstpat = &fpatbuf[1]; 195 break; 196 } 197 } 198 } 199 if (skipend()) 200 return (0); 201 CP(genbuf, "echo "); cp = &genbuf[5]; 202 for (;;) { 203 c = ex_getchar(); 204 if (endcmd(c)) { 205 ungetchar(c); 206 break; 207 } 208 switch (c) { 209 210 case '\\': 211 if (any(peekchar(), "#%|")) 212 c = ex_getchar(); 213 /* fall into... */ 214 215 default: 216 if (cp > &genbuf[LBSIZE - 2]) 217 flong: 218 error("Argument buffer overflow"); 219 *cp++ = c; 220 break; 221 222 case '#': 223 fp = altfile; 224 if (*fp == 0) 225 error("No alternate filename@to substitute for #"); 226 goto filexp; 227 228 case '%': 229 fp = savedfile; 230 if (*fp == 0) 231 error("No current filename@to substitute for %%"); 232 filexp: 233 while (*fp) { 234 if (cp > &genbuf[LBSIZE - 2]) 235 goto flong; 236 *cp++ = *fp++; 237 } 238 break; 239 } 240 } 241 *cp = 0; 242 return (1); 243 } 244 245 /* 246 * Glob the argument words in genbuf, or if no globbing 247 * is implied, just split them up directly. 248 */ 249 glob(gp) 250 struct glob *gp; 251 { 252 int pvec[2]; 253 register char **argv = gp->argv; 254 register char *cp = gp->argspac; 255 register int c; 256 char ch; 257 int nleft = NCARGS; 258 259 gp->argc0 = 0; 260 if (gscan() == 0) { 261 register char *v = genbuf + 5; /* strlen("echo ") */ 262 263 for (;;) { 264 while (isspace(*v)) 265 v++; 266 if (!*v) 267 break; 268 *argv++ = cp; 269 while (*v && !isspace(*v)) 270 *cp++ = *v++; 271 *cp++ = 0; 272 gp->argc0++; 273 } 274 *argv = 0; 275 return; 276 } 277 if (pipe(pvec) < 0) 278 error("Can't make pipe to glob"); 279 pid = vfork(); 280 io = pvec[0]; 281 if (pid < 0) { 282 close(pvec[1]); 283 error("Can't fork to do glob"); 284 } 285 if (pid == 0) { 286 int oerrno; 287 288 if (genbuf) { 289 register char *ccp = genbuf; 290 while (*ccp) 291 *ccp++ &= TRIM; 292 } 293 close(1); 294 dup(pvec[1]); 295 close(pvec[0]); 296 close(2); /* so errors don't mess up the screen */ 297 ignore(open(_PATH_DEVNULL, 1)); 298 execl(svalue(SHELL), "sh", "-c", genbuf, 0); 299 oerrno = errno; 300 close(1); 301 dup(2); 302 errno = oerrno; 303 filioerr(svalue(SHELL)); 304 } 305 close(pvec[1]); 306 do { 307 *argv = cp; 308 for (;;) { 309 if (read(io, &ch, 1) != 1) { 310 close(io); 311 c = -1; 312 } else 313 c = ch & TRIM; 314 if (c <= 0 || isspace(c)) 315 break; 316 *cp++ = c; 317 if (--nleft <= 0) 318 error("Arg list too long"); 319 } 320 if (cp != *argv) { 321 --nleft; 322 *cp++ = 0; 323 gp->argc0++; 324 if (gp->argc0 >= NARGS) 325 error("Arg list too long"); 326 argv++; 327 } 328 } while (c >= 0); 329 waitfor(); 330 if (gp->argc0 == 0) 331 error("No match"); 332 } 333 334 /* 335 * Scan genbuf for shell metacharacters. 336 * Set is union of v7 shell and csh metas. 337 */ 338 gscan() 339 { 340 #ifndef vms /* Never have meta-characters in vms */ 341 register char *cp; 342 343 for (cp = genbuf; *cp; cp++) 344 if (any(*cp, "~{[*?$`'\"\\")) 345 return (1); 346 #endif 347 return (0); 348 } 349 350 /* 351 * Parse one filename into file. 352 */ 353 struct glob G; 354 getone() 355 { 356 register char *str; 357 358 if (getargs() == 0) 359 error("Missing filename"); 360 glob(&G); 361 if (G.argc0 > 1) 362 error("Ambiguous|Too many file names"); 363 str = G.argv[G.argc0 - 1]; 364 if (strlen(str) > FNSIZE - 4) 365 error("Filename too long"); 366 CP(file, str); 367 } 368 369 /* 370 * Read a file from the world. 371 * C is command, 'e' if this really an edit (or a recover). 372 */ 373 rop(c) 374 int c; 375 { 376 register int i; 377 struct stat stbuf; 378 struct exec head; 379 static int ovro; /* old value(READONLY) */ 380 static int denied; /* 1 if READONLY was set due to file permissions */ 381 #ifdef FLOCKFILE 382 int *lp, *iop; 383 #endif FLOCKFILE 384 385 io = open(file, 0); 386 if (io < 0) { 387 if (c == 'e' && errno == ENOENT) { 388 edited++; 389 /* 390 * If the user just did "ex foo" he is probably 391 * creating a new file. Don't be an error, since 392 * this is ugly, and it screws up the + option. 393 */ 394 if (!seenprompt) { 395 ex_printf(" [New file]"); 396 noonl(); 397 return; 398 } 399 } 400 syserror(); 401 } 402 if (fstat(io, &stbuf)) 403 syserror(); 404 switch (stbuf.st_mode & S_IFMT) { 405 406 case S_IFBLK: 407 error(" Block special file"); 408 409 case S_IFCHR: 410 if (isatty(io)) 411 error(" Teletype"); 412 if (samei(&stbuf, _PATH_DEVNULL)) 413 break; 414 error(" Character special file"); 415 416 case S_IFDIR: 417 error(" Directory"); 418 419 case S_IFREG: 420 #ifdef CRYPT 421 if (xflag) 422 break; 423 #endif 424 i = read(io, (char *)&head, sizeof(head)); 425 (void)lseek(io, 0L, L_SET); 426 if (i != sizeof(head)) 427 break; 428 #ifndef vms 429 switch ((int)head.a_magic) { 430 431 case 0405: /* data overlay on exec */ 432 case OMAGIC: /* unshared */ 433 case NMAGIC: /* shared text */ 434 case 0411: /* separate I/D */ 435 case ZMAGIC: /* VM/Unix demand paged */ 436 case 0430: /* PDP-11 Overlay shared */ 437 case 0431: /* PDP-11 Overlay sep I/D */ 438 error(" Executable"); 439 440 /* 441 * We do not forbid the editing of portable archives 442 * because it is reasonable to edit them, especially 443 * if they are archives of text files. This is 444 * especially useful if you archive source files together 445 * and copy them to another system with ~%take, since 446 * the files sometimes show up munged and must be fixed. 447 */ 448 case 0177545: 449 case 0177555: 450 error(" Archive"); 451 case 070707: 452 error(" Cpio file"); 453 454 default: 455 { 456 char *bp = (char *)&head; 457 if ((u_char)bp[0] == (u_char)'\037' && 458 (u_char)bp[1] == (u_char)'\235') 459 error(" Compressed file"); 460 #ifdef ARCHIVES_ARE_OK 461 if (!strncmp(bp, "!<arch>\n__.SYMDEF", 17) 462 || !strncmp(bp, "!<arch>\n", 8)) 463 error(" Archive"); 464 #endif 465 } 466 break; 467 } 468 #endif 469 } 470 if (c != 'r') { 471 if (value(READONLY) && denied) { 472 value(READONLY) = ovro; 473 denied = 0; 474 } 475 if ((stbuf.st_mode & 0222) == 0 || access(file, 2) < 0) { 476 ovro = value(READONLY); 477 denied = 1; 478 value(READONLY) = 1; 479 } 480 } 481 if (value(READONLY)) { 482 ex_printf(" [Read only]"); 483 flush(); 484 } 485 #ifdef FLOCKFILE 486 /* 487 * Attempt to lock the file. We use an sharable lock if reading 488 * the file, and an exclusive lock if editting a file. 489 * The lock will be released when the file is no longer being 490 * referenced. At any time, the editor can have as many as 491 * three files locked, and with different lock statuses. 492 */ 493 /* 494 * if this is either the saved or alternate file or current file, 495 * point to the appropriate descriptor and file lock status. 496 */ 497 if (strcmp (file,savedfile) == 0) { 498 if (!io_savedfile) io_savedfile = dup(io) ; 499 lp = &lock_savedfile ; iop = &io_savedfile ; 500 } else if (strcmp (file,altfile) == 0) { 501 if (!io_altfile) io_altfile = dup(io) ; 502 lp = &lock_altfile ; iop = &io_altfile ; 503 } else { 504 /* throw away current lock, accquire new current lock */ 505 if (io_curr) close (io_curr) ; 506 io_curr = dup(io) ; 507 lp = &lock_curr ; iop = &io_curr ; 508 lock_curr = 0 ; 509 } 510 if (c == 'r' || value(READONLY) || *lp == 0) { 511 /* if we have a lock already, don't bother */ 512 if (!*lp) { 513 /* try for a shared lock */ 514 if (flock(*iop, LOCK_SH|LOCK_NB) < 0 515 && errno == EWOULDBLOCK) { 516 ex_printf ( 517 " [FILE BEING MODIFIED BY ANOTHER PROCESS]") ; 518 flush(); 519 goto fail_lock ; 520 } else *lp = LOCK_SH ; 521 } 522 } 523 if ( c != 'r' && !value(READONLY) && *lp != LOCK_EX) { 524 /* if we are editting the file, upgrade to an exclusive lock. */ 525 if (flock(*iop, LOCK_EX|LOCK_NB) < 0 && errno == EWOULDBLOCK) { 526 ex_printf (" [File open by another process]") ; 527 flush(); 528 } else *lp = LOCK_EX ; 529 } 530 fail_lock: 531 #endif FLOCKFILE 532 if (c == 'r') 533 setdot(); 534 else 535 setall(); 536 if (FIXUNDO && inopen && c == 'r') 537 undap1 = undap2 = dot + 1; 538 rop2(); 539 rop3(c); 540 } 541 542 rop2() 543 { 544 line *first, *last, *a; 545 struct stat statb; 546 547 deletenone(); 548 clrstats(); 549 first = addr2 + 1; 550 if (fstat(io, &statb) < 0) 551 bsize = LBSIZE; 552 else { 553 bsize = statb.st_blksize; 554 if (bsize <= 0) 555 bsize = LBSIZE; 556 } 557 ignore(append(getfile, addr2)); 558 last = dot; 559 /* 560 * if the modeline variable is set, 561 * check the first and last five lines of the file 562 * for a mode line. 563 */ 564 if (value(MODELINE)) { 565 for (a=first; a<=last; a++) { 566 if (a==first+5 && last-first > 10) 567 a = last - 4; 568 getline(*a); 569 checkmodeline(linebuf); 570 } 571 } 572 } 573 574 rop3(c) 575 int c; 576 { 577 578 if (iostats() == 0 && c == 'e') 579 edited++; 580 if (c == 'e') { 581 if (wasalt || firstpat) { 582 register line *addr = zero + oldadot; 583 584 if (addr > dol) 585 addr = dol; 586 if (firstpat) { 587 globp = (*firstpat) ? firstpat : "$"; 588 commands(1,1); 589 firstpat = 0; 590 } else if (addr >= one) { 591 if (inopen) 592 dot = addr; 593 markpr(addr); 594 } else 595 goto other; 596 } else 597 other: 598 if (dol > zero) { 599 if (inopen) 600 dot = one; 601 markpr(one); 602 } 603 if(FIXUNDO) 604 undkind = UNDNONE; 605 if (inopen) { 606 vcline = 0; 607 vreplace(0, LINES, lineDOL()); 608 } 609 } 610 } 611 612 /* 613 * Are these two really the same inode? 614 */ 615 samei(sp, cp) 616 struct stat *sp; 617 char *cp; 618 { 619 struct stat stb; 620 621 if (stat(cp, &stb) < 0 || sp->st_dev != stb.st_dev) 622 return (0); 623 return (sp->st_ino == stb.st_ino); 624 } 625 626 /* Returns from edited() */ 627 #define EDF 0 /* Edited file */ 628 #define NOTEDF -1 /* Not edited file */ 629 #define PARTBUF 1 /* Write of partial buffer to Edited file */ 630 631 /* 632 * Write a file. 633 */ 634 wop(dofname) 635 bool dofname; /* if 1 call filename, else use savedfile */ 636 { 637 register int c, exclam, nonexist; 638 line *saddr1, *saddr2; 639 struct stat stbuf; 640 #ifdef FLOCKFILE 641 int *lp, *iop ; 642 #endif FLOCKFILE 643 644 c = 0; 645 exclam = 0; 646 if (dofname) { 647 if (peekchar() == '!') 648 exclam++, ignchar(); 649 ignore(skipwh()); 650 while (peekchar() == '>') 651 ignchar(), c++, ignore(skipwh()); 652 if (c != 0 && c != 2) 653 error("Write forms are 'w' and 'w>>'"); 654 filename('w'); 655 } else { 656 if (savedfile[0] == 0) 657 error("No file|No current filename"); 658 saddr1=addr1; 659 saddr2=addr2; 660 addr1=one; 661 addr2=dol; 662 CP(file, savedfile); 663 if (inopen) { 664 vclrech(0); 665 splitw++; 666 } 667 lprintf("\"%s\"", file); 668 } 669 nonexist = stat(file, &stbuf); 670 #ifdef FLOCKFILE 671 /* 672 * if this is either the saved or alternate file or current file, 673 * point to the appropriate descriptor and file lock status. 674 */ 675 if (strcmp (file,savedfile) == 0) { 676 lp = &lock_savedfile ; iop = &io_savedfile ; 677 } else if (strcmp (file,altfile) == 0) { 678 lp = &lock_altfile ; iop = &io_altfile ; 679 } else { 680 lp = &lock_curr ; iop = &io_curr ; 681 } 682 if (!*iop && !nonexist){ 683 *lp = 0 ; 684 if ((*iop = open(file, 1)) < 0) *iop = 0 ; 685 } 686 #endif FLOCKFILE 687 switch (c) { 688 689 case 0: 690 if (!exclam && (!value(WRITEANY) || value(READONLY))) 691 switch (edfile()) { 692 693 case NOTEDF: 694 if (nonexist) 695 break; 696 if ((stbuf.st_mode & S_IFMT) == S_IFCHR) { 697 if (samei(&stbuf, _PATH_DEVNULL)) 698 break; 699 if (samei(&stbuf, _PATH_TTY)) 700 break; 701 } 702 io = open(file, 1); 703 if (io < 0) 704 syserror(); 705 if (!isatty(io)) 706 serror(" File exists| File exists - use \"w! %s\" to overwrite", file); 707 close(io); 708 break; 709 710 case EDF: 711 if (value(READONLY)) 712 error(" File is read only"); 713 break; 714 715 case PARTBUF: 716 if (value(READONLY)) 717 error(" File is read only"); 718 error(" Use \"w!\" to write partial buffer"); 719 } 720 cre: 721 /* 722 synctmp(); 723 */ 724 #ifdef FLOCKFILE 725 if (*iop && !*lp != LOCK_EX && !exclam) { 726 /* 727 * upgrade to a exclusive lock. if can't get, someone else 728 * has the exclusive lock. bitch to the user. 729 */ 730 if (flock(*iop, LOCK_EX|LOCK_NB) < 0 && errno == EWOULDBLOCK) 731 error (" File being modified by another process - use \"w!\" to write"); 732 else *lp = LOCK_EX ; 733 } 734 #endif FLOCKFILE 735 #ifdef V6 736 io = creat(file, 0644); 737 #else 738 io = creat(file, 0666); 739 #ifdef vms /* to retain file protection modes on newer version of file */ 740 if (!nonexist) 741 chmod(file, stbuf.st_mode & 0777); 742 #endif 743 #endif 744 if (io < 0) 745 syserror(); 746 writing = 1; 747 if (hush == 0) 748 if (nonexist) 749 ex_printf(" [New file]"); 750 else if (value(WRITEANY) && edfile() != EDF) 751 ex_printf(" [Existing file]"); 752 #ifdef FLOCKFILE 753 if (!*iop) 754 *iop = dup(io) ; 755 #endif FLOCKFILE 756 break; 757 758 case 2: 759 io = open(file, 1); 760 if (io < 0) { 761 if (exclam || value(WRITEANY)) 762 goto cre; 763 syserror(); 764 } 765 lseek(io, 0l, 2); 766 #ifdef FLOCKFILE 767 if (!*iop) *iop = dup(io) ; 768 if (*lp != LOCK_EX && !exclam) { 769 /* 770 * upgrade to a exclusive lock. if can't get, 771 * someone else has the exclusive lock. 772 * bitch to the user. 773 */ 774 if (flock(*iop, LOCK_SH|LOCK_NB) < 0 775 && errno == EWOULDBLOCK) 776 error ( 777 " File being modified by another process - use \"w!>>\" to write"); 778 else *lp = LOCK_EX ; 779 } 780 #endif FLOCKFILE 781 break; 782 } 783 #ifdef FLOCKFILE 784 if (flock(*iop, LOCK_EX|LOCK_NB) >= 0) 785 *lp = LOCK_EX ; 786 #endif FLOCKFILE 787 putfile(0); 788 #ifndef vms 789 (void) fsync(io); 790 #endif 791 ignore(iostats()); 792 if (c != 2 && addr1 == one && addr2 == dol) { 793 if (eq(file, savedfile)) 794 edited = 1; 795 ex_sync(); 796 } 797 if (!dofname) { 798 addr1 = saddr1; 799 addr2 = saddr2; 800 } 801 writing = 0; 802 } 803 804 /* 805 * Is file the edited file? 806 * Work here is that it is not considered edited 807 * if this is a partial buffer, and distinguish 808 * all cases. 809 */ 810 edfile() 811 { 812 813 if (!edited || !eq(file, savedfile)) 814 return (NOTEDF); 815 return (addr1 == one && addr2 == dol ? EDF : PARTBUF); 816 } 817 818 /* 819 * Extract the next line from the io stream. 820 */ 821 char *nextip; 822 823 getfile() 824 { 825 register short c; 826 register char *lp, *fp; 827 828 lp = linebuf; 829 fp = nextip; 830 do { 831 if (--ninbuf < 0) { 832 ninbuf = read(io, genbuf, (int) bsize) - 1; 833 if (ninbuf < 0) { 834 if (lp != linebuf) { 835 lp++; 836 ex_printf(" [Incomplete last line]"); 837 break; 838 } 839 return (EOF); 840 } 841 #ifdef CRYPT 842 if (kflag) { 843 fp = genbuf; 844 while(fp < &genbuf[ninbuf]) { 845 if (*fp++ & 0200) { 846 crblock(perm, genbuf, ninbuf+1, 847 cntch); 848 break; 849 } 850 } 851 } 852 #endif 853 fp = genbuf; 854 cntch += ninbuf+1; 855 } 856 if (lp >= &linebuf[LBSIZE]) { 857 error(" Line too long"); 858 } 859 c = *fp++; 860 if (c == 0) { 861 cntnull++; 862 continue; 863 } 864 if (c & QUOTE) { 865 cntodd++; 866 c &= TRIM; 867 if (c == 0) 868 continue; 869 } 870 *lp++ = c; 871 } while (c != '\n'); 872 *--lp = 0; 873 nextip = fp; 874 cntln++; 875 return (0); 876 } 877 878 /* 879 * Write a range onto the io stream. 880 */ 881 /* ARGSUSED */ 882 putfile(isfilter) 883 int isfilter; 884 { 885 line *a1; 886 register char *fp, *lp; 887 register int nib; 888 struct stat statb; 889 890 a1 = addr1; 891 clrstats(); 892 cntln = addr2 - a1 + 1; 893 if (cntln == 0) 894 return; 895 if (fstat(io, &statb) < 0) 896 bsize = LBSIZE; 897 else { 898 bsize = statb.st_blksize; 899 if (bsize <= 0) 900 bsize = LBSIZE; 901 } 902 nib = bsize; 903 fp = genbuf; 904 do { 905 getline(*a1++); 906 lp = linebuf; 907 for (;;) { 908 if (--nib < 0) { 909 nib = fp - genbuf; 910 #ifdef CRYPT 911 if(kflag && !isfilter) 912 crblock(perm, genbuf, nib, cntch); 913 #endif 914 if (write(io, genbuf, nib) != nib) { 915 wrerror(); 916 } 917 cntch += nib; 918 nib = bsize - 1; 919 fp = genbuf; 920 } 921 if ((*fp++ = *lp++) == 0) { 922 fp[-1] = '\n'; 923 break; 924 } 925 } 926 } while (a1 <= addr2); 927 nib = fp - genbuf; 928 #ifdef CRYPT 929 if(kflag && !isfilter) 930 crblock(perm, genbuf, nib, cntch); 931 #endif 932 if (write(io, genbuf, nib) != nib) { 933 wrerror(); 934 } 935 cntch += nib; 936 } 937 938 /* 939 * A write error has occurred; if the file being written was 940 * the edited file then we consider it to have changed since it is 941 * now likely scrambled. 942 */ 943 wrerror() 944 { 945 946 if (eq(file, savedfile) && edited) 947 change(); 948 syserror(); 949 } 950 951 /* 952 * Source command, handles nested sources. 953 * Traps errors since it mungs unit 0 during the source. 954 */ 955 short slevel; 956 short ttyindes; 957 958 source(fil, okfail) 959 char *fil; 960 bool okfail; 961 { 962 jmp_buf osetexit; 963 register int saveinp, ointty, oerrno; 964 char *saveglobp; 965 short savepeekc; 966 967 signal(SIGINT, SIG_IGN); 968 saveinp = dup(0); 969 savepeekc = peekc; 970 saveglobp = globp; 971 peekc = 0; globp = 0; 972 if (saveinp < 0) 973 error("Too many nested sources"); 974 if (slevel <= 0) 975 ttyindes = saveinp; 976 close(0); 977 if (open(fil, 0) < 0) { 978 oerrno = errno; 979 setrupt(); 980 dup(saveinp); 981 close(saveinp); 982 errno = oerrno; 983 if (!okfail) 984 filioerr(fil); 985 return; 986 } 987 slevel++; 988 ointty = intty; 989 intty = isatty(0); 990 oprompt = value(PROMPT); 991 value(PROMPT) &= intty; 992 getexit(osetexit); 993 setrupt(); 994 if (setexit() == 0) 995 commands(1, 1); 996 else if (slevel > 1) { 997 close(0); 998 dup(saveinp); 999 close(saveinp); 1000 slevel--; 1001 resexit(osetexit); 1002 reset(); 1003 } 1004 intty = ointty; 1005 value(PROMPT) = oprompt; 1006 close(0); 1007 dup(saveinp); 1008 close(saveinp); 1009 globp = saveglobp; 1010 peekc = savepeekc; 1011 slevel--; 1012 resexit(osetexit); 1013 } 1014 1015 /* 1016 * Clear io statistics before a read or write. 1017 */ 1018 clrstats() 1019 { 1020 1021 ninbuf = 0; 1022 cntch = 0; 1023 cntln = 0; 1024 cntnull = 0; 1025 cntodd = 0; 1026 } 1027 1028 /* 1029 * Io is finished, close the unit and print statistics. 1030 */ 1031 iostats() 1032 { 1033 1034 close(io); 1035 io = -1; 1036 if (hush == 0) { 1037 if (value(TERSE)) 1038 ex_printf(" %d/%D", cntln, cntch); 1039 else 1040 ex_printf(" %d line%s, %D character%s", cntln, plural((long) cntln), 1041 cntch, plural(cntch)); 1042 if (cntnull || cntodd) { 1043 ex_printf(" ("); 1044 if (cntnull) { 1045 ex_printf("%D null", cntnull); 1046 if (cntodd) 1047 ex_printf(", "); 1048 } 1049 if (cntodd) 1050 ex_printf("%D non-ASCII", cntodd); 1051 ex_putchar(')'); 1052 } 1053 noonl(); 1054 flush(); 1055 } 1056 return (cntnull != 0 || cntodd != 0); 1057 } 1058 1059 #ifdef USG 1060 # define index strchr 1061 # define rindex strrchr 1062 #endif 1063 #ifdef USG3TTY 1064 # define index strchr 1065 # define rindex strrchr 1066 #endif 1067 #ifdef vms 1068 # define index strchr 1069 # define rindex strrchr 1070 #endif 1071 1072 checkmodeline(l) 1073 char *l; 1074 { 1075 char *beg, *end; 1076 char cmdbuf[1024]; 1077 char *index(), *rindex(), *strncpy(); 1078 1079 beg = index(l, ':'); 1080 if (beg == NULL) 1081 return; 1082 if (&beg[-3] < l) 1083 return; 1084 if (!( ( (beg[-3] == ' ' || beg[-3] == '\t') 1085 && beg[-2] == 'e' 1086 && beg[-1] == 'x') 1087 || ( (beg[-3] == ' ' || beg[-3] == '\t') 1088 && beg[-2] == 'v' 1089 && beg[-1] == 'i'))) return; 1090 strncpy(cmdbuf, beg+1, sizeof cmdbuf); 1091 end = rindex(cmdbuf, ':'); 1092 if (end == NULL) 1093 return; 1094 *end = 0; 1095 globp = cmdbuf; 1096 commands(1, 1); 1097 } 1098