xref: /original-bsd/usr.bin/ex/ex_io.c (revision 7ce13dd1)
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