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