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