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.1.1 (Berkeley) 08/19/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 */
filename(comm)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 */
getargs()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 */
gscan()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;
getone()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 */
rop(c)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 i = read(io, (char *)&head, sizeof(head));
421 (void)lseek(io, 0L, L_SET);
422 if (i != sizeof(head))
423 break;
424 #ifndef vms
425 switch ((int)head.a_magic) {
426
427 case 0405: /* data overlay on exec */
428 case OMAGIC: /* unshared */
429 case NMAGIC: /* shared text */
430 case 0411: /* separate I/D */
431 case ZMAGIC: /* VM/Unix demand paged */
432 case 0430: /* PDP-11 Overlay shared */
433 case 0431: /* PDP-11 Overlay sep I/D */
434 error(" Executable");
435
436 /*
437 * We do not forbid the editing of portable archives
438 * because it is reasonable to edit them, especially
439 * if they are archives of text files. This is
440 * especially useful if you archive source files together
441 * and copy them to another system with ~%take, since
442 * the files sometimes show up munged and must be fixed.
443 */
444 case 0177545:
445 case 0177555:
446 error(" Archive");
447 case 070707:
448 error(" Cpio file");
449
450 default:
451 {
452 char *bp = (char *)&head;
453 if ((u_char)bp[0] == (u_char)'\037' &&
454 (u_char)bp[1] == (u_char)'\235')
455 error(" Compressed file");
456 #ifdef ARCHIVES_ARE_OK
457 if (!strncmp(bp, "!<arch>\n__.SYMDEF", 17)
458 || !strncmp(bp, "!<arch>\n", 8))
459 error(" Archive");
460 #endif
461 }
462 break;
463 }
464 #endif
465 }
466 if (c != 'r') {
467 if (value(READONLY) && denied) {
468 value(READONLY) = ovro;
469 denied = 0;
470 }
471 if ((stbuf.st_mode & 0222) == 0 || access(file, 2) < 0) {
472 ovro = value(READONLY);
473 denied = 1;
474 value(READONLY) = 1;
475 }
476 }
477 if (value(READONLY)) {
478 ex_printf(" [Read only]");
479 flush();
480 }
481 #ifdef FLOCKFILE
482 /*
483 * Attempt to lock the file. We use an sharable lock if reading
484 * the file, and an exclusive lock if editting a file.
485 * The lock will be released when the file is no longer being
486 * referenced. At any time, the editor can have as many as
487 * three files locked, and with different lock statuses.
488 */
489 /*
490 * if this is either the saved or alternate file or current file,
491 * point to the appropriate descriptor and file lock status.
492 */
493 if (strcmp (file,savedfile) == 0) {
494 if (!io_savedfile) io_savedfile = dup(io) ;
495 lp = &lock_savedfile ; iop = &io_savedfile ;
496 } else if (strcmp (file,altfile) == 0) {
497 if (!io_altfile) io_altfile = dup(io) ;
498 lp = &lock_altfile ; iop = &io_altfile ;
499 } else {
500 /* throw away current lock, accquire new current lock */
501 if (io_curr) close (io_curr) ;
502 io_curr = dup(io) ;
503 lp = &lock_curr ; iop = &io_curr ;
504 lock_curr = 0 ;
505 }
506 if (c == 'r' || value(READONLY) || *lp == 0) {
507 /* if we have a lock already, don't bother */
508 if (!*lp) {
509 /* try for a shared lock */
510 if (flock(*iop, LOCK_SH|LOCK_NB) < 0
511 && errno == EWOULDBLOCK) {
512 ex_printf (
513 " [FILE BEING MODIFIED BY ANOTHER PROCESS]") ;
514 flush();
515 goto fail_lock ;
516 } else *lp = LOCK_SH ;
517 }
518 }
519 if ( c != 'r' && !value(READONLY) && *lp != LOCK_EX) {
520 /* if we are editting the file, upgrade to an exclusive lock. */
521 if (flock(*iop, LOCK_EX|LOCK_NB) < 0 && errno == EWOULDBLOCK) {
522 ex_printf (" [File open by another process]") ;
523 flush();
524 } else *lp = LOCK_EX ;
525 }
526 fail_lock:
527 #endif FLOCKFILE
528 if (c == 'r')
529 setdot();
530 else
531 setall();
532 if (FIXUNDO && inopen && c == 'r')
533 undap1 = undap2 = dot + 1;
534 rop2();
535 rop3(c);
536 }
537
rop2()538 rop2()
539 {
540 line *first, *last, *a;
541 struct stat statb;
542
543 deletenone();
544 clrstats();
545 first = addr2 + 1;
546 if (fstat(io, &statb) < 0)
547 bsize = LBSIZE;
548 else {
549 bsize = statb.st_blksize;
550 if (bsize <= 0)
551 bsize = LBSIZE;
552 }
553 ignore(append(getfile, addr2));
554 last = dot;
555 /*
556 * if the modeline variable is set,
557 * check the first and last five lines of the file
558 * for a mode line.
559 */
560 if (value(MODELINE)) {
561 for (a=first; a<=last; a++) {
562 if (a==first+5 && last-first > 10)
563 a = last - 4;
564 getline(*a);
565 checkmodeline(linebuf);
566 }
567 }
568 }
569
rop3(c)570 rop3(c)
571 int c;
572 {
573
574 if (iostats() == 0 && c == 'e')
575 edited++;
576 if (c == 'e') {
577 if (wasalt || firstpat) {
578 register line *addr = zero + oldadot;
579
580 if (addr > dol)
581 addr = dol;
582 if (firstpat) {
583 globp = (*firstpat) ? firstpat : "$";
584 commands(1,1);
585 firstpat = 0;
586 } else if (addr >= one) {
587 if (inopen)
588 dot = addr;
589 markpr(addr);
590 } else
591 goto other;
592 } else
593 other:
594 if (dol > zero) {
595 if (inopen)
596 dot = one;
597 markpr(one);
598 }
599 if(FIXUNDO)
600 undkind = UNDNONE;
601 if (inopen) {
602 vcline = 0;
603 vreplace(0, LINES, lineDOL());
604 }
605 }
606 }
607
608 /*
609 * Are these two really the same inode?
610 */
611 samei(sp, cp)
612 struct stat *sp;
613 char *cp;
614 {
615 struct stat stb;
616
617 if (stat(cp, &stb) < 0 || sp->st_dev != stb.st_dev)
618 return (0);
619 return (sp->st_ino == stb.st_ino);
620 }
621
622 /* Returns from edited() */
623 #define EDF 0 /* Edited file */
624 #define NOTEDF -1 /* Not edited file */
625 #define PARTBUF 1 /* Write of partial buffer to Edited file */
626
627 /*
628 * Write a file.
629 */
wop(dofname)630 wop(dofname)
631 bool dofname; /* if 1 call filename, else use savedfile */
632 {
633 register int c, exclam, nonexist;
634 line *saddr1, *saddr2;
635 struct stat stbuf;
636 #ifdef FLOCKFILE
637 int *lp, *iop ;
638 #endif FLOCKFILE
639
640 c = 0;
641 exclam = 0;
642 if (dofname) {
643 if (peekchar() == '!')
644 exclam++, ignchar();
645 ignore(skipwh());
646 while (peekchar() == '>')
647 ignchar(), c++, ignore(skipwh());
648 if (c != 0 && c != 2)
649 error("Write forms are 'w' and 'w>>'");
650 filename('w');
651 } else {
652 if (savedfile[0] == 0)
653 error("No file|No current filename");
654 saddr1=addr1;
655 saddr2=addr2;
656 addr1=one;
657 addr2=dol;
658 CP(file, savedfile);
659 if (inopen) {
660 vclrech(0);
661 splitw++;
662 }
663 lprintf("\"%s\"", file);
664 }
665 nonexist = stat(file, &stbuf);
666 #ifdef FLOCKFILE
667 /*
668 * if this is either the saved or alternate file or current file,
669 * point to the appropriate descriptor and file lock status.
670 */
671 if (strcmp (file,savedfile) == 0) {
672 lp = &lock_savedfile ; iop = &io_savedfile ;
673 } else if (strcmp (file,altfile) == 0) {
674 lp = &lock_altfile ; iop = &io_altfile ;
675 } else {
676 lp = &lock_curr ; iop = &io_curr ;
677 }
678 if (!*iop && !nonexist){
679 *lp = 0 ;
680 if ((*iop = open(file, 1)) < 0) *iop = 0 ;
681 }
682 #endif FLOCKFILE
683 switch (c) {
684
685 case 0:
686 if (!exclam && (!value(WRITEANY) || value(READONLY)))
687 switch (edfile()) {
688
689 case NOTEDF:
690 if (nonexist)
691 break;
692 if ((stbuf.st_mode & S_IFMT) == S_IFCHR) {
693 if (samei(&stbuf, _PATH_DEVNULL))
694 break;
695 if (samei(&stbuf, _PATH_TTY))
696 break;
697 }
698 io = open(file, 1);
699 if (io < 0)
700 syserror();
701 if (!isatty(io))
702 serror(" File exists| File exists - use \"w! %s\" to overwrite", file);
703 close(io);
704 break;
705
706 case EDF:
707 if (value(READONLY))
708 error(" File is read only");
709 break;
710
711 case PARTBUF:
712 if (value(READONLY))
713 error(" File is read only");
714 error(" Use \"w!\" to write partial buffer");
715 }
716 cre:
717 /*
718 synctmp();
719 */
720 #ifdef FLOCKFILE
721 if (*iop && !*lp != LOCK_EX && !exclam) {
722 /*
723 * upgrade to a exclusive lock. if can't get, someone else
724 * has the exclusive lock. bitch to the user.
725 */
726 if (flock(*iop, LOCK_EX|LOCK_NB) < 0 && errno == EWOULDBLOCK)
727 error (" File being modified by another process - use \"w!\" to write");
728 else *lp = LOCK_EX ;
729 }
730 #endif FLOCKFILE
731 #ifdef V6
732 io = creat(file, 0644);
733 #else
734 io = creat(file, 0666);
735 #ifdef vms /* to retain file protection modes on newer version of file */
736 if (!nonexist)
737 chmod(file, stbuf.st_mode & 0777);
738 #endif
739 #endif
740 if (io < 0)
741 syserror();
742 writing = 1;
743 if (hush == 0)
744 if (nonexist)
745 ex_printf(" [New file]");
746 else if (value(WRITEANY) && edfile() != EDF)
747 ex_printf(" [Existing file]");
748 #ifdef FLOCKFILE
749 if (!*iop)
750 *iop = dup(io) ;
751 #endif FLOCKFILE
752 break;
753
754 case 2:
755 io = open(file, 1);
756 if (io < 0) {
757 if (exclam || value(WRITEANY))
758 goto cre;
759 syserror();
760 }
761 lseek(io, 0l, 2);
762 #ifdef FLOCKFILE
763 if (!*iop) *iop = dup(io) ;
764 if (*lp != LOCK_EX && !exclam) {
765 /*
766 * upgrade to a exclusive lock. if can't get,
767 * someone else has the exclusive lock.
768 * bitch to the user.
769 */
770 if (flock(*iop, LOCK_SH|LOCK_NB) < 0
771 && errno == EWOULDBLOCK)
772 error (
773 " File being modified by another process - use \"w!>>\" to write");
774 else *lp = LOCK_EX ;
775 }
776 #endif FLOCKFILE
777 break;
778 }
779 #ifdef FLOCKFILE
780 if (flock(*iop, LOCK_EX|LOCK_NB) >= 0)
781 *lp = LOCK_EX ;
782 #endif FLOCKFILE
783 putfile(0);
784 #ifndef vms
785 (void) fsync(io);
786 #endif
787 ignore(iostats());
788 if (c != 2 && addr1 == one && addr2 == dol) {
789 if (eq(file, savedfile))
790 edited = 1;
791 ex_sync();
792 }
793 if (!dofname) {
794 addr1 = saddr1;
795 addr2 = saddr2;
796 }
797 writing = 0;
798 }
799
800 /*
801 * Is file the edited file?
802 * Work here is that it is not considered edited
803 * if this is a partial buffer, and distinguish
804 * all cases.
805 */
edfile()806 edfile()
807 {
808
809 if (!edited || !eq(file, savedfile))
810 return (NOTEDF);
811 return (addr1 == one && addr2 == dol ? EDF : PARTBUF);
812 }
813
814 /*
815 * Extract the next line from the io stream.
816 */
817 char *nextip;
818
getfile()819 getfile()
820 {
821 register short c;
822 register char *lp, *fp;
823
824 lp = linebuf;
825 fp = nextip;
826 do {
827 if (--ninbuf < 0) {
828 ninbuf = read(io, genbuf, (int) bsize) - 1;
829 if (ninbuf < 0) {
830 if (lp != linebuf) {
831 lp++;
832 ex_printf(" [Incomplete last line]");
833 break;
834 }
835 return (EOF);
836 }
837 fp = genbuf;
838 cntch += ninbuf+1;
839 }
840 if (lp >= &linebuf[LBSIZE]) {
841 error(" Line too long");
842 }
843 c = *fp++;
844 if (c == 0) {
845 cntnull++;
846 continue;
847 }
848 if (c & QUOTE) {
849 cntodd++;
850 c &= TRIM;
851 if (c == 0)
852 continue;
853 }
854 *lp++ = c;
855 } while (c != '\n');
856 *--lp = 0;
857 nextip = fp;
858 cntln++;
859 return (0);
860 }
861
862 /*
863 * Write a range onto the io stream.
864 */
865 /* ARGSUSED */
putfile(isfilter)866 putfile(isfilter)
867 int isfilter;
868 {
869 line *a1;
870 register char *fp, *lp;
871 register int nib;
872 struct stat statb;
873
874 a1 = addr1;
875 clrstats();
876 cntln = addr2 - a1 + 1;
877 if (cntln == 0)
878 return;
879 if (fstat(io, &statb) < 0)
880 bsize = LBSIZE;
881 else {
882 bsize = statb.st_blksize;
883 if (bsize <= 0)
884 bsize = LBSIZE;
885 }
886 nib = bsize;
887 fp = genbuf;
888 do {
889 getline(*a1++);
890 lp = linebuf;
891 for (;;) {
892 if (--nib < 0) {
893 nib = fp - genbuf;
894 if (write(io, genbuf, nib) != nib) {
895 wrerror();
896 }
897 cntch += nib;
898 nib = bsize - 1;
899 fp = genbuf;
900 }
901 if ((*fp++ = *lp++) == 0) {
902 fp[-1] = '\n';
903 break;
904 }
905 }
906 } while (a1 <= addr2);
907 nib = fp - genbuf;
908 if (write(io, genbuf, nib) != nib) {
909 wrerror();
910 }
911 cntch += nib;
912 }
913
914 /*
915 * A write error has occurred; if the file being written was
916 * the edited file then we consider it to have changed since it is
917 * now likely scrambled.
918 */
wrerror()919 wrerror()
920 {
921
922 if (eq(file, savedfile) && edited)
923 change();
924 syserror();
925 }
926
927 /*
928 * Source command, handles nested sources.
929 * Traps errors since it mungs unit 0 during the source.
930 */
931 short slevel;
932 short ttyindes;
933
source(fil,okfail)934 source(fil, okfail)
935 char *fil;
936 bool okfail;
937 {
938 jmp_buf osetexit;
939 register int saveinp, ointty, oerrno;
940 char *saveglobp;
941 short savepeekc;
942
943 signal(SIGINT, SIG_IGN);
944 saveinp = dup(0);
945 savepeekc = peekc;
946 saveglobp = globp;
947 peekc = 0; globp = 0;
948 if (saveinp < 0)
949 error("Too many nested sources");
950 if (slevel <= 0)
951 ttyindes = saveinp;
952 close(0);
953 if (open(fil, 0) < 0) {
954 oerrno = errno;
955 setrupt();
956 dup(saveinp);
957 close(saveinp);
958 errno = oerrno;
959 if (!okfail)
960 filioerr(fil);
961 return;
962 }
963 slevel++;
964 ointty = intty;
965 intty = isatty(0);
966 oprompt = value(PROMPT);
967 value(PROMPT) &= intty;
968 getexit(osetexit);
969 setrupt();
970 if (setexit() == 0)
971 commands(1, 1);
972 else if (slevel > 1) {
973 close(0);
974 dup(saveinp);
975 close(saveinp);
976 slevel--;
977 resexit(osetexit);
978 reset();
979 }
980 intty = ointty;
981 value(PROMPT) = oprompt;
982 close(0);
983 dup(saveinp);
984 close(saveinp);
985 globp = saveglobp;
986 peekc = savepeekc;
987 slevel--;
988 resexit(osetexit);
989 }
990
991 /*
992 * Clear io statistics before a read or write.
993 */
clrstats()994 clrstats()
995 {
996
997 ninbuf = 0;
998 cntch = 0;
999 cntln = 0;
1000 cntnull = 0;
1001 cntodd = 0;
1002 }
1003
1004 /*
1005 * Io is finished, close the unit and print statistics.
1006 */
iostats()1007 iostats()
1008 {
1009
1010 close(io);
1011 io = -1;
1012 if (hush == 0) {
1013 if (value(TERSE))
1014 ex_printf(" %d/%D", cntln, cntch);
1015 else
1016 ex_printf(" %d line%s, %D character%s", cntln, plural((long) cntln),
1017 cntch, plural(cntch));
1018 if (cntnull || cntodd) {
1019 ex_printf(" (");
1020 if (cntnull) {
1021 ex_printf("%D null", cntnull);
1022 if (cntodd)
1023 ex_printf(", ");
1024 }
1025 if (cntodd)
1026 ex_printf("%D non-ASCII", cntodd);
1027 ex_putchar(')');
1028 }
1029 noonl();
1030 flush();
1031 }
1032 return (cntnull != 0 || cntodd != 0);
1033 }
1034
1035 #ifdef USG
1036 # define index strchr
1037 # define rindex strrchr
1038 #endif
1039 #ifdef USG3TTY
1040 # define index strchr
1041 # define rindex strrchr
1042 #endif
1043 #ifdef vms
1044 # define index strchr
1045 # define rindex strrchr
1046 #endif
1047
checkmodeline(l)1048 checkmodeline(l)
1049 char *l;
1050 {
1051 char *beg, *end;
1052 char cmdbuf[1024];
1053 char *index(), *rindex(), *strncpy();
1054
1055 beg = index(l, ':');
1056 if (beg == NULL)
1057 return;
1058 if (&beg[-3] < l)
1059 return;
1060 if (!( ( (beg[-3] == ' ' || beg[-3] == '\t')
1061 && beg[-2] == 'e'
1062 && beg[-1] == 'x')
1063 || ( (beg[-3] == ' ' || beg[-3] == '\t')
1064 && beg[-2] == 'v'
1065 && beg[-1] == 'i'))) return;
1066 strncpy(cmdbuf, beg+1, sizeof cmdbuf);
1067 end = rindex(cmdbuf, ':');
1068 if (end == NULL)
1069 return;
1070 *end = 0;
1071 globp = cmdbuf;
1072 commands(1, 1);
1073 }
1074