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