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