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