xref: /onnv-gate/usr/src/cmd/vi/port/ex_io.c (revision 802:73b56fb6544b)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
22*802Scf46844 /*
23*802Scf46844  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*802Scf46844  * Use is subject to license terms.
25*802Scf46844  */
26*802Scf46844 
270Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
280Sstevel@tonic-gate /*	  All Rights Reserved  	*/
290Sstevel@tonic-gate 
300Sstevel@tonic-gate 
310Sstevel@tonic-gate /* Copyright (c) 1981 Regents of the University of California */
320Sstevel@tonic-gate 
330Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
340Sstevel@tonic-gate 
350Sstevel@tonic-gate #include "ex.h"
360Sstevel@tonic-gate #include "ex_argv.h"
370Sstevel@tonic-gate #include "ex_temp.h"
380Sstevel@tonic-gate #include "ex_tty.h"
390Sstevel@tonic-gate #include "ex_vis.h"
400Sstevel@tonic-gate #include <stdlib.h>
41*802Scf46844 #include <unistd.h>
420Sstevel@tonic-gate 
430Sstevel@tonic-gate /*
440Sstevel@tonic-gate  * File input/output, source, preserve and recover
450Sstevel@tonic-gate  */
460Sstevel@tonic-gate 
470Sstevel@tonic-gate /*
480Sstevel@tonic-gate  * Following remember where . was in the previous file for return
490Sstevel@tonic-gate  * on file switching.
500Sstevel@tonic-gate  */
510Sstevel@tonic-gate int	altdot;
520Sstevel@tonic-gate int	oldadot;
530Sstevel@tonic-gate bool	wasalt;
540Sstevel@tonic-gate short	isalt;
550Sstevel@tonic-gate 
560Sstevel@tonic-gate long	cntch;			/* Count of characters on unit io */
570Sstevel@tonic-gate #ifndef VMUNIX
580Sstevel@tonic-gate short	cntln;			/* Count of lines " */
590Sstevel@tonic-gate #else
600Sstevel@tonic-gate int	cntln;
610Sstevel@tonic-gate #endif
620Sstevel@tonic-gate long	cntnull;		/* Count of nulls " */
630Sstevel@tonic-gate long	cntodd;			/* Count of non-ascii characters " */
640Sstevel@tonic-gate 
650Sstevel@tonic-gate static void chkmdln();
66*802Scf46844 extern int	getchar();
670Sstevel@tonic-gate 
680Sstevel@tonic-gate /*
690Sstevel@tonic-gate  * Parse file name for command encoded by comm.
700Sstevel@tonic-gate  * If comm is E then command is doomed and we are
710Sstevel@tonic-gate  * parsing just so user won't have to retype the name.
720Sstevel@tonic-gate  */
73*802Scf46844 void
74*802Scf46844 filename(int comm)
750Sstevel@tonic-gate {
76*802Scf46844 	int c = comm, d;
77*802Scf46844 	int i;
780Sstevel@tonic-gate 
790Sstevel@tonic-gate 	d = getchar();
800Sstevel@tonic-gate 	if (endcmd(d)) {
810Sstevel@tonic-gate 		if (savedfile[0] == 0 && comm != 'f')
820Sstevel@tonic-gate 			error(value(vi_TERSE) ? gettext("No file") :
830Sstevel@tonic-gate gettext("No current filename"));
840Sstevel@tonic-gate 		CP(file, savedfile);
850Sstevel@tonic-gate 		wasalt = (isalt > 0) ? isalt-1 : 0;
860Sstevel@tonic-gate 		isalt = 0;
870Sstevel@tonic-gate 		oldadot = altdot;
880Sstevel@tonic-gate 		if (c == 'e' || c == 'E')
890Sstevel@tonic-gate 			altdot = lineDOT();
900Sstevel@tonic-gate 		if (d == EOF)
910Sstevel@tonic-gate 			ungetchar(d);
920Sstevel@tonic-gate 	} else {
930Sstevel@tonic-gate 		ungetchar(d);
940Sstevel@tonic-gate 		getone();
950Sstevel@tonic-gate 		eol();
960Sstevel@tonic-gate 		if (savedfile[0] == 0 && c != 'E' && c != 'e') {
970Sstevel@tonic-gate 			c = 'e';
980Sstevel@tonic-gate 			edited = 0;
990Sstevel@tonic-gate 		}
1000Sstevel@tonic-gate 		wasalt = strcmp(file, altfile) == 0;
1010Sstevel@tonic-gate 		oldadot = altdot;
1020Sstevel@tonic-gate 		switch (c) {
1030Sstevel@tonic-gate 
1040Sstevel@tonic-gate 		case 'f':
1050Sstevel@tonic-gate 			edited = 0;
1060Sstevel@tonic-gate 			/* fall into ... */
1070Sstevel@tonic-gate 
1080Sstevel@tonic-gate 		case 'e':
1090Sstevel@tonic-gate 			if (savedfile[0]) {
1100Sstevel@tonic-gate 				altdot = lineDOT();
1110Sstevel@tonic-gate 				CP(altfile, savedfile);
1120Sstevel@tonic-gate 			}
1130Sstevel@tonic-gate 			CP(savedfile, file);
1140Sstevel@tonic-gate 			break;
1150Sstevel@tonic-gate 
1160Sstevel@tonic-gate 		default:
1170Sstevel@tonic-gate 			if (file[0]) {
1180Sstevel@tonic-gate 				if (c != 'E')
1190Sstevel@tonic-gate 					altdot = lineDOT();
1200Sstevel@tonic-gate 				CP(altfile, file);
1210Sstevel@tonic-gate 			}
1220Sstevel@tonic-gate 			break;
1230Sstevel@tonic-gate 		}
1240Sstevel@tonic-gate 	}
1250Sstevel@tonic-gate 	if (hush && comm != 'f' || comm == 'E')
1260Sstevel@tonic-gate 		return;
1270Sstevel@tonic-gate 	if (file[0] != 0) {
1280Sstevel@tonic-gate 		lprintf("\"%s\"", file);
1290Sstevel@tonic-gate 		if (comm == 'f') {
1300Sstevel@tonic-gate 			if (value(vi_READONLY))
131*802Scf46844 				viprintf(gettext(" [Read only]"));
1320Sstevel@tonic-gate 			if (!edited)
133*802Scf46844 				viprintf(gettext(" [Not edited]"));
1340Sstevel@tonic-gate 			if (tchng)
135*802Scf46844 				viprintf(gettext(" [Modified]"));
1360Sstevel@tonic-gate 		}
1370Sstevel@tonic-gate 		flush();
1380Sstevel@tonic-gate 	} else
139*802Scf46844 		viprintf(gettext("No file "));
1400Sstevel@tonic-gate 	if (comm == 'f') {
1410Sstevel@tonic-gate 		if (!(i = lineDOL()))
1420Sstevel@tonic-gate 			i++;
1430Sstevel@tonic-gate 		/*
1440Sstevel@tonic-gate 		 * TRANSLATION_NOTE
1450Sstevel@tonic-gate 		 *	Reference order of arguments must not
1460Sstevel@tonic-gate 		 *	be changed using '%digit$', since vi's
147*802Scf46844 		 *	viprintf() does not support it.
1480Sstevel@tonic-gate 		 */
149*802Scf46844 		viprintf(gettext(" line %d of %d --%ld%%--"), lineDOT(),
150*802Scf46844 		    lineDOL(), (long)(100 * lineDOT() / i));
1510Sstevel@tonic-gate 	}
1520Sstevel@tonic-gate }
1530Sstevel@tonic-gate 
1540Sstevel@tonic-gate /*
1550Sstevel@tonic-gate  * Get the argument words for a command into genbuf
1560Sstevel@tonic-gate  * expanding # and %.
1570Sstevel@tonic-gate  */
158*802Scf46844 int
159*802Scf46844 getargs(void)
1600Sstevel@tonic-gate {
161*802Scf46844 	int c;
162*802Scf46844 	unsigned char *cp, *fp;
1630Sstevel@tonic-gate 	static unsigned char fpatbuf[32];	/* hence limit on :next +/pat */
1640Sstevel@tonic-gate 	char	multic[MB_LEN_MAX + 1];
1650Sstevel@tonic-gate 	int	len;
1660Sstevel@tonic-gate 	wchar_t	wc;
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate 	pastwh();
1690Sstevel@tonic-gate 	if (peekchar() == '+') {
1700Sstevel@tonic-gate 		for (cp = fpatbuf;;) {
1710Sstevel@tonic-gate 			if (!isascii(c = peekchar()) && (c != EOF)) {
1720Sstevel@tonic-gate 				if ((len = _mbftowc(multic, &wc, getchar, &peekc)) > 0) {
1730Sstevel@tonic-gate 					if ((cp + len) >= &fpatbuf[sizeof(fpatbuf)])
1740Sstevel@tonic-gate 						error(gettext("Pattern too long"));
1750Sstevel@tonic-gate 					strncpy(cp, multic, len);
1760Sstevel@tonic-gate 					cp += len;
1770Sstevel@tonic-gate 					continue;
1780Sstevel@tonic-gate 				}
1790Sstevel@tonic-gate 			}
1800Sstevel@tonic-gate 
1810Sstevel@tonic-gate 			c = getchar();
1820Sstevel@tonic-gate 			*cp++ = c;
1830Sstevel@tonic-gate 			if (cp >= &fpatbuf[sizeof(fpatbuf)])
1840Sstevel@tonic-gate 				error(gettext("Pattern too long"));
1850Sstevel@tonic-gate 			if (c == '\\' && isspace(peekchar()))
1860Sstevel@tonic-gate 				c = getchar();
1870Sstevel@tonic-gate 			if (c == EOF || isspace(c)) {
1880Sstevel@tonic-gate 				ungetchar(c);
1890Sstevel@tonic-gate 				*--cp = 0;
1900Sstevel@tonic-gate 				firstpat = &fpatbuf[1];
1910Sstevel@tonic-gate 				break;
1920Sstevel@tonic-gate 			}
1930Sstevel@tonic-gate 		}
1940Sstevel@tonic-gate 	}
1950Sstevel@tonic-gate 	if (skipend())
1960Sstevel@tonic-gate 		return (0);
1970Sstevel@tonic-gate 	CP(genbuf, "echo "); cp = &genbuf[5];
1980Sstevel@tonic-gate 	for (;;) {
1990Sstevel@tonic-gate 		if (!isascii(c = peekchar())) {
2000Sstevel@tonic-gate 			if (endcmd(c) && c != '"')
2010Sstevel@tonic-gate 				break;
2020Sstevel@tonic-gate 			if ((len = _mbftowc(multic, &wc, getchar, &peekc)) > 0) {
2030Sstevel@tonic-gate 				if ((cp + len) > &genbuf[LBSIZE - 2])
2040Sstevel@tonic-gate 					error(gettext("Argument buffer overflow"));
2050Sstevel@tonic-gate 				strncpy(cp, multic, len);
2060Sstevel@tonic-gate 				cp += len;
2070Sstevel@tonic-gate 				continue;
2080Sstevel@tonic-gate 			}
2090Sstevel@tonic-gate 		}
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate 		if (endcmd(c) && c != '"')
2120Sstevel@tonic-gate 			break;
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate 		c = getchar();
2150Sstevel@tonic-gate 		switch (c) {
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate 		case '\\':
2180Sstevel@tonic-gate 			if (any(peekchar(), "#%|"))
2190Sstevel@tonic-gate 				c = getchar();
2200Sstevel@tonic-gate 			/* fall into... */
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate 		default:
2230Sstevel@tonic-gate 			if (cp > &genbuf[LBSIZE - 2])
2240Sstevel@tonic-gate flong:
2250Sstevel@tonic-gate 				error(gettext("Argument buffer overflow"));
2260Sstevel@tonic-gate 			*cp++ = c;
2270Sstevel@tonic-gate 			break;
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate 		case '#':
2300Sstevel@tonic-gate 			fp = (unsigned char *)altfile;
2310Sstevel@tonic-gate 			if (*fp == 0)
2320Sstevel@tonic-gate 				error(value(vi_TERSE) ?
2330Sstevel@tonic-gate gettext("No alternate filename") :
2340Sstevel@tonic-gate gettext("No alternate filename to substitute for #"));
2350Sstevel@tonic-gate 			goto filexp;
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate 		case '%':
2380Sstevel@tonic-gate 			fp = savedfile;
2390Sstevel@tonic-gate 			if (*fp == 0)
2400Sstevel@tonic-gate 				error(value(vi_TERSE) ?
2410Sstevel@tonic-gate gettext("No current filename") :
2420Sstevel@tonic-gate gettext("No current filename to substitute for %%"));
2430Sstevel@tonic-gate filexp:
2440Sstevel@tonic-gate 			while (*fp) {
2450Sstevel@tonic-gate 				if (cp > &genbuf[LBSIZE - 2])
2460Sstevel@tonic-gate 					goto flong;
2470Sstevel@tonic-gate 				*cp++ = *fp++;
2480Sstevel@tonic-gate 			}
2490Sstevel@tonic-gate 			break;
2500Sstevel@tonic-gate 		}
2510Sstevel@tonic-gate 	}
2520Sstevel@tonic-gate 	*cp = 0;
2530Sstevel@tonic-gate 	return (1);
2540Sstevel@tonic-gate }
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate /*
2570Sstevel@tonic-gate  * Glob the argument words in genbuf, or if no globbing
2580Sstevel@tonic-gate  * is implied, just split them up directly.
2590Sstevel@tonic-gate  */
260*802Scf46844 void
261*802Scf46844 glob(struct glob *gp)
2620Sstevel@tonic-gate {
2630Sstevel@tonic-gate 	int pvec[2];
264*802Scf46844 	unsigned char **argv = gp->argv;
265*802Scf46844 	unsigned char *cp = gp->argspac;
266*802Scf46844 	int c;
2670Sstevel@tonic-gate 	unsigned char ch;
2680Sstevel@tonic-gate 	int nleft = NCARGS;
2690Sstevel@tonic-gate 
2700Sstevel@tonic-gate 	gp->argc0 = 0;
2710Sstevel@tonic-gate 	if (gscan() == 0) {
272*802Scf46844 		unsigned char *v = genbuf + 5;		/* strlen("echo ") */
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate 		for (;;) {
2750Sstevel@tonic-gate 			while (isspace(*v))
2760Sstevel@tonic-gate 				v++;
2770Sstevel@tonic-gate 			if (!*v)
2780Sstevel@tonic-gate 				break;
2790Sstevel@tonic-gate 			*argv++ = cp;
2800Sstevel@tonic-gate 			while (*v && !isspace(*v))
2810Sstevel@tonic-gate 				*cp++ = *v++;
2820Sstevel@tonic-gate 			*cp++ = 0;
2830Sstevel@tonic-gate 			gp->argc0++;
2840Sstevel@tonic-gate 		}
2850Sstevel@tonic-gate 		*argv = 0;
2860Sstevel@tonic-gate 		return;
2870Sstevel@tonic-gate 	}
2880Sstevel@tonic-gate 	if (pipe(pvec) < 0)
2890Sstevel@tonic-gate 		error(gettext("Can't make pipe to glob"));
2900Sstevel@tonic-gate 	pid = fork();
2910Sstevel@tonic-gate 	io = pvec[0];
2920Sstevel@tonic-gate 	if (pid < 0) {
2930Sstevel@tonic-gate 		close(pvec[1]);
2940Sstevel@tonic-gate 		error(gettext("Can't fork to do glob"));
2950Sstevel@tonic-gate 	}
2960Sstevel@tonic-gate 	if (pid == 0) {
2970Sstevel@tonic-gate 		int oerrno;
2980Sstevel@tonic-gate 
2990Sstevel@tonic-gate 		close(1);
3000Sstevel@tonic-gate 		dup(pvec[1]);
3010Sstevel@tonic-gate 		close(pvec[0]);
3020Sstevel@tonic-gate 		close(2);	/* so errors don't mess up the screen */
3030Sstevel@tonic-gate 		open("/dev/null", 1);
304*802Scf46844 		execlp((char *)svalue(vi_SHELL), "sh", "-c", genbuf, (char *)0);
3050Sstevel@tonic-gate 		oerrno = errno; close(1); dup(2); errno = oerrno;
3060Sstevel@tonic-gate 		filioerr(svalue(vi_SHELL));
3070Sstevel@tonic-gate 	}
3080Sstevel@tonic-gate 	close(pvec[1]);
3090Sstevel@tonic-gate 	do {
3100Sstevel@tonic-gate 		*argv = cp;
3110Sstevel@tonic-gate 		for (;;) {
3120Sstevel@tonic-gate 			if (read(io, &ch, 1) != 1) {
3130Sstevel@tonic-gate 				close(io);
3140Sstevel@tonic-gate 				c = -1;
3150Sstevel@tonic-gate 			} else
3160Sstevel@tonic-gate 				c = ch;
3170Sstevel@tonic-gate 			if (c <= 0 || isspace(c))
3180Sstevel@tonic-gate 				break;
3190Sstevel@tonic-gate 			*cp++ = c;
3200Sstevel@tonic-gate 			if (--nleft <= 0)
3210Sstevel@tonic-gate 				error(gettext("Arg list too long"));
3220Sstevel@tonic-gate 		}
3230Sstevel@tonic-gate 		if (cp != *argv) {
3240Sstevel@tonic-gate 			--nleft;
3250Sstevel@tonic-gate 			*cp++ = 0;
3260Sstevel@tonic-gate 			gp->argc0++;
3270Sstevel@tonic-gate 			if (gp->argc0 >= NARGS)
3280Sstevel@tonic-gate 				error(gettext("Arg list too long"));
3290Sstevel@tonic-gate 			argv++;
3300Sstevel@tonic-gate 		}
3310Sstevel@tonic-gate 	} while (c >= 0);
3320Sstevel@tonic-gate 	waitfor();
3330Sstevel@tonic-gate 	if (gp->argc0 == 0)
3340Sstevel@tonic-gate 		error(gettext("No match"));
3350Sstevel@tonic-gate }
3360Sstevel@tonic-gate 
3370Sstevel@tonic-gate /*
3380Sstevel@tonic-gate  * Scan genbuf for shell metacharacters.
3390Sstevel@tonic-gate  * Set is union of v7 shell and csh metas.
3400Sstevel@tonic-gate  */
341*802Scf46844 int
342*802Scf46844 gscan(void)
3430Sstevel@tonic-gate {
344*802Scf46844 	unsigned char *cp;
3450Sstevel@tonic-gate 	int	len;
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate 	for (cp = genbuf; *cp; cp += len) {
3480Sstevel@tonic-gate 		if (any(*cp, "~{[*?$`'\"\\"))
3490Sstevel@tonic-gate 			return (1);
3500Sstevel@tonic-gate 		if ((len = mblen((char *)cp, MB_CUR_MAX)) <= 0)
3510Sstevel@tonic-gate 			len = 1;
3520Sstevel@tonic-gate 	}
3530Sstevel@tonic-gate 	return (0);
3540Sstevel@tonic-gate }
3550Sstevel@tonic-gate 
3560Sstevel@tonic-gate /*
3570Sstevel@tonic-gate  * Parse one filename into file.
3580Sstevel@tonic-gate  */
3590Sstevel@tonic-gate struct glob G;
360*802Scf46844 void
361*802Scf46844 getone(void)
3620Sstevel@tonic-gate {
363*802Scf46844 	unsigned char *str;
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate 	if (getargs() == 0)
3660Sstevel@tonic-gate 		error(gettext("Missing filename"));
3670Sstevel@tonic-gate 	glob(&G);
3680Sstevel@tonic-gate 	if (G.argc0 > 1)
3690Sstevel@tonic-gate 		error(value(vi_TERSE) ? gettext("Ambiguous") :
3700Sstevel@tonic-gate gettext("Too many file names"));
3710Sstevel@tonic-gate 	if (G.argc0 < 1)
3720Sstevel@tonic-gate 		error(gettext("Missing filename"));
3730Sstevel@tonic-gate 	str = G.argv[G.argc0 - 1];
3740Sstevel@tonic-gate 	if (strlen(str) > FNSIZE - 4)
3750Sstevel@tonic-gate 		error(gettext("Filename too long"));
3760Sstevel@tonic-gate samef:
3770Sstevel@tonic-gate 	CP(file, str);
3780Sstevel@tonic-gate }
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate /*
3810Sstevel@tonic-gate  * Read a file from the world.
3820Sstevel@tonic-gate  * C is command, 'e' if this really an edit (or a recover).
3830Sstevel@tonic-gate  */
384*802Scf46844 void
385*802Scf46844 rop(int c)
3860Sstevel@tonic-gate {
387*802Scf46844 	int i;
3880Sstevel@tonic-gate 	struct stat64 stbuf;
3890Sstevel@tonic-gate 	short magic;
3900Sstevel@tonic-gate 	static int ovro;	/* old value(vi_READONLY) */
3910Sstevel@tonic-gate 	static int denied;	/* 1 if READONLY was set due to file permissions */
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate 	io = open(file, 0);
3940Sstevel@tonic-gate 	if (io < 0) {
3950Sstevel@tonic-gate 		if (c == 'e' && errno == ENOENT) {
3960Sstevel@tonic-gate 			edited++;
3970Sstevel@tonic-gate 			/*
3980Sstevel@tonic-gate 			 * If the user just did "ex foo" he is probably
3990Sstevel@tonic-gate 			 * creating a new file.  Don't be an error, since
4000Sstevel@tonic-gate 			 * this is ugly, and it messes up the + option.
4010Sstevel@tonic-gate 			 */
4020Sstevel@tonic-gate 			if (!seenprompt) {
403*802Scf46844 				viprintf(gettext(" [New file]"));
4040Sstevel@tonic-gate 				noonl();
4050Sstevel@tonic-gate 				return;
4060Sstevel@tonic-gate 			}
4070Sstevel@tonic-gate 		}
4080Sstevel@tonic-gate 
4090Sstevel@tonic-gate 		if (value(vi_READONLY) && denied) {
4100Sstevel@tonic-gate 			value(vi_READONLY) = ovro;
4110Sstevel@tonic-gate 			denied = 0;
4120Sstevel@tonic-gate 		}
4130Sstevel@tonic-gate 		syserror(0);
4140Sstevel@tonic-gate 	}
4150Sstevel@tonic-gate 	if (fstat64(io, &stbuf))
4160Sstevel@tonic-gate 		syserror(0);
4170Sstevel@tonic-gate 	switch (FTYPE(stbuf) & S_IFMT) {
4180Sstevel@tonic-gate 
4190Sstevel@tonic-gate 	case S_IFBLK:
4200Sstevel@tonic-gate 		error(gettext(" Block special file"));
4210Sstevel@tonic-gate 
4220Sstevel@tonic-gate 	case S_IFCHR:
4230Sstevel@tonic-gate 		if (isatty(io))
4240Sstevel@tonic-gate 			error(gettext(" Teletype"));
4250Sstevel@tonic-gate 		if (samei(&stbuf, "/dev/null"))
4260Sstevel@tonic-gate 			break;
4270Sstevel@tonic-gate 		error(gettext(" Character special file"));
4280Sstevel@tonic-gate 
4290Sstevel@tonic-gate 	case S_IFDIR:
4300Sstevel@tonic-gate 		error(gettext(" Directory"));
4310Sstevel@tonic-gate 
4320Sstevel@tonic-gate 	}
4330Sstevel@tonic-gate 	if (c != 'r') {
4340Sstevel@tonic-gate 		if (value(vi_READONLY) && denied) {
4350Sstevel@tonic-gate 			value(vi_READONLY) = ovro;
4360Sstevel@tonic-gate 			denied = 0;
4370Sstevel@tonic-gate 		}
438*802Scf46844 		if ((FMODE(stbuf) & 0222) == 0 || access((char *)file, 2) < 0) {
4390Sstevel@tonic-gate 			ovro = value(vi_READONLY);
4400Sstevel@tonic-gate 			denied = 1;
4410Sstevel@tonic-gate 			value(vi_READONLY) = 1;
4420Sstevel@tonic-gate 		}
4430Sstevel@tonic-gate 	}
4440Sstevel@tonic-gate 	if (hush == 0 && value(vi_READONLY)) {
445*802Scf46844 		viprintf(gettext(" [Read only]"));
4460Sstevel@tonic-gate 		flush();
4470Sstevel@tonic-gate 	}
4480Sstevel@tonic-gate 	if (c == 'r')
4490Sstevel@tonic-gate 		setdot();
4500Sstevel@tonic-gate 	else
4510Sstevel@tonic-gate 		setall();
4520Sstevel@tonic-gate 
4530Sstevel@tonic-gate 	/* If it is a read command, then we must set dot to addr1
4540Sstevel@tonic-gate 	 * (value of N in :Nr ).  In the default case, addr1 will
4550Sstevel@tonic-gate 	 * already be set to dot.
4560Sstevel@tonic-gate 	 *
4570Sstevel@tonic-gate 	 * Next, it is necessary to mark the beginning (undap1) and
4580Sstevel@tonic-gate 	 * ending (undap2) addresses affected (for undo).  Note that
4590Sstevel@tonic-gate 	 * rop2() and rop3() will adjust the value of undap2.
4600Sstevel@tonic-gate 	 */
4610Sstevel@tonic-gate 	if (FIXUNDO && inopen && c == 'r') {
4620Sstevel@tonic-gate 		dot = addr1;
4630Sstevel@tonic-gate 		undap1 = undap2 = dot + 1;
4640Sstevel@tonic-gate 	}
4650Sstevel@tonic-gate 	rop2();
4660Sstevel@tonic-gate 	rop3(c);
4670Sstevel@tonic-gate }
4680Sstevel@tonic-gate 
469*802Scf46844 void
470*802Scf46844 rop2(void)
4710Sstevel@tonic-gate {
4720Sstevel@tonic-gate 	line *first, *last, *a;
4730Sstevel@tonic-gate 
4740Sstevel@tonic-gate 	deletenone();
4750Sstevel@tonic-gate 	clrstats();
4760Sstevel@tonic-gate 	first = addr2 + 1;
4770Sstevel@tonic-gate 	(void)append(getfile, addr2);
4780Sstevel@tonic-gate 	last = dot;
4790Sstevel@tonic-gate 	if (value(vi_MODELINES))
4800Sstevel@tonic-gate 		for (a=first; a<=last; a++) {
4810Sstevel@tonic-gate 			if (a==first+5 && last-first > 10)
4820Sstevel@tonic-gate 				a = last - 4;
4830Sstevel@tonic-gate 			getline(*a);
4840Sstevel@tonic-gate 				chkmdln(linebuf);
4850Sstevel@tonic-gate 		}
4860Sstevel@tonic-gate }
4870Sstevel@tonic-gate 
488*802Scf46844 void
489*802Scf46844 rop3(int c)
4900Sstevel@tonic-gate {
4910Sstevel@tonic-gate 
4920Sstevel@tonic-gate 	if (iostats() == 0 && c == 'e')
4930Sstevel@tonic-gate 		edited++;
4940Sstevel@tonic-gate 	if (c == 'e') {
4950Sstevel@tonic-gate 		if (wasalt || firstpat) {
496*802Scf46844 			line *addr = zero + oldadot;
4970Sstevel@tonic-gate 
4980Sstevel@tonic-gate 			if (addr > dol)
4990Sstevel@tonic-gate 				addr = dol;
5000Sstevel@tonic-gate 			if (firstpat) {
5010Sstevel@tonic-gate 				globp = (*firstpat) ? firstpat : (unsigned char *)"$";
5020Sstevel@tonic-gate 				commands(1,1);
5030Sstevel@tonic-gate 				firstpat = 0;
5040Sstevel@tonic-gate 			} else if (addr >= one) {
5050Sstevel@tonic-gate 				if (inopen)
5060Sstevel@tonic-gate 					dot = addr;
5070Sstevel@tonic-gate 				markpr(addr);
5080Sstevel@tonic-gate 			} else
5090Sstevel@tonic-gate 				goto other;
5100Sstevel@tonic-gate 		} else
5110Sstevel@tonic-gate other:
5120Sstevel@tonic-gate 			if (dol > zero) {
5130Sstevel@tonic-gate 				if (inopen)
5140Sstevel@tonic-gate 					dot = one;
5150Sstevel@tonic-gate 				markpr(one);
5160Sstevel@tonic-gate 			}
5170Sstevel@tonic-gate 		if(FIXUNDO)
5180Sstevel@tonic-gate 			undkind = UNDNONE;
5190Sstevel@tonic-gate 		if (inopen) {
5200Sstevel@tonic-gate 			vcline = 0;
5210Sstevel@tonic-gate 			vreplace(0, lines, lineDOL());
5220Sstevel@tonic-gate 		}
5230Sstevel@tonic-gate 	}
5240Sstevel@tonic-gate 	if (laste) {
5250Sstevel@tonic-gate #ifdef VMUNIX
5260Sstevel@tonic-gate 		tlaste();
5270Sstevel@tonic-gate #endif
5280Sstevel@tonic-gate 		laste = 0;
5290Sstevel@tonic-gate 		sync();
5300Sstevel@tonic-gate 	}
5310Sstevel@tonic-gate }
5320Sstevel@tonic-gate 
5330Sstevel@tonic-gate /*
5340Sstevel@tonic-gate  * Are these two really the same inode?
5350Sstevel@tonic-gate  */
536*802Scf46844 int
537*802Scf46844 samei(struct stat64 *sp, unsigned char *cp)
5380Sstevel@tonic-gate {
5390Sstevel@tonic-gate 	struct stat64 stb;
5400Sstevel@tonic-gate 
5410Sstevel@tonic-gate 	if (stat64((char *)cp, &stb) < 0)
5420Sstevel@tonic-gate 		return (0);
5430Sstevel@tonic-gate 	return (IDENTICAL((*sp), stb));
5440Sstevel@tonic-gate }
5450Sstevel@tonic-gate 
5460Sstevel@tonic-gate /* Returns from edited() */
5470Sstevel@tonic-gate #define	EDF	0		/* Edited file */
5480Sstevel@tonic-gate #define	NOTEDF	-1		/* Not edited file */
5490Sstevel@tonic-gate #define	PARTBUF	1		/* Write of partial buffer to Edited file */
5500Sstevel@tonic-gate 
5510Sstevel@tonic-gate /*
5520Sstevel@tonic-gate  * Write a file.
5530Sstevel@tonic-gate  */
554*802Scf46844 void
5550Sstevel@tonic-gate wop(dofname)
5560Sstevel@tonic-gate bool dofname;	/* if 1 call filename, else use savedfile */
5570Sstevel@tonic-gate {
558*802Scf46844 	int c, exclam, nonexist;
5590Sstevel@tonic-gate 	line *saddr1, *saddr2;
5600Sstevel@tonic-gate 	struct stat64 stbuf;
5610Sstevel@tonic-gate 	char *messagep;
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate 	c = 0;
5640Sstevel@tonic-gate 	exclam = 0;
5650Sstevel@tonic-gate 	if (dofname) {
5660Sstevel@tonic-gate 		if (peekchar() == '!')
5670Sstevel@tonic-gate 			exclam++, ignchar();
5680Sstevel@tonic-gate 		(void)skipwh();
5690Sstevel@tonic-gate 		while (peekchar() == '>')
5700Sstevel@tonic-gate 			ignchar(), c++, (void)skipwh();
5710Sstevel@tonic-gate 		if (c != 0 && c != 2)
5720Sstevel@tonic-gate 			error(gettext("Write forms are 'w' and 'w>>'"));
5730Sstevel@tonic-gate 		filename('w');
5740Sstevel@tonic-gate 	} else {
5750Sstevel@tonic-gate 		if (savedfile[0] == 0)
5760Sstevel@tonic-gate 			error(value(vi_TERSE) ? gettext("No file") :
5770Sstevel@tonic-gate gettext("No current filename"));
5780Sstevel@tonic-gate 		saddr1=addr1;
5790Sstevel@tonic-gate 		saddr2=addr2;
5800Sstevel@tonic-gate 		addr1=one;
5810Sstevel@tonic-gate 		addr2=dol;
5820Sstevel@tonic-gate 		CP(file, savedfile);
5830Sstevel@tonic-gate 		if (inopen) {
5840Sstevel@tonic-gate 			vclrech(0);
5850Sstevel@tonic-gate 			splitw++;
5860Sstevel@tonic-gate 		}
5870Sstevel@tonic-gate 		lprintf("\"%s\"", file);
5880Sstevel@tonic-gate 	}
5890Sstevel@tonic-gate 	nonexist = stat64((char *)file, &stbuf);
5900Sstevel@tonic-gate 	switch (c) {
5910Sstevel@tonic-gate 
5920Sstevel@tonic-gate 	case 0:
5930Sstevel@tonic-gate 		if (!exclam && (!value(vi_WRITEANY) || value(vi_READONLY)))
5940Sstevel@tonic-gate 		switch (edfile()) {
5950Sstevel@tonic-gate 
5960Sstevel@tonic-gate 		case NOTEDF:
5970Sstevel@tonic-gate 			if (nonexist)
5980Sstevel@tonic-gate 				break;
5990Sstevel@tonic-gate 			if (ISCHR(stbuf)) {
600*802Scf46844 				if (samei(&stbuf, (unsigned char *)"/dev/null"))
6010Sstevel@tonic-gate 					break;
602*802Scf46844 				if (samei(&stbuf, (unsigned char *)"/dev/tty"))
6030Sstevel@tonic-gate 					break;
6040Sstevel@tonic-gate 			}
6050Sstevel@tonic-gate 			io = open(file, 1);
6060Sstevel@tonic-gate 			if (io < 0)
6070Sstevel@tonic-gate 				syserror(0);
6080Sstevel@tonic-gate 			if (!isatty(io))
609*802Scf46844 				serror(value(vi_TERSE) ?
610*802Scf46844 				    (unsigned char *)gettext(" File exists") :
611*802Scf46844 (unsigned char *)gettext(" File exists - use \"w! %s\" to overwrite"),
612*802Scf46844 				    file);
6130Sstevel@tonic-gate 			close(io);
6140Sstevel@tonic-gate 			break;
6150Sstevel@tonic-gate 
6160Sstevel@tonic-gate 		case EDF:
6170Sstevel@tonic-gate 			if (value(vi_READONLY))
6180Sstevel@tonic-gate 				error(gettext(" File is read only"));
6190Sstevel@tonic-gate 			break;
6200Sstevel@tonic-gate 
6210Sstevel@tonic-gate 		case PARTBUF:
6220Sstevel@tonic-gate 			if (value(vi_READONLY))
6230Sstevel@tonic-gate 				error(gettext(" File is read only"));
6240Sstevel@tonic-gate 			error(gettext(" Use \"w!\" to write partial buffer"));
6250Sstevel@tonic-gate 		}
6260Sstevel@tonic-gate cre:
6270Sstevel@tonic-gate /*
6280Sstevel@tonic-gate 		synctmp();
6290Sstevel@tonic-gate */
6300Sstevel@tonic-gate 		io = creat(file, 0666);
6310Sstevel@tonic-gate 		if (io < 0)
6320Sstevel@tonic-gate 			syserror(0);
6330Sstevel@tonic-gate 		writing = 1;
6340Sstevel@tonic-gate 		if (hush == 0)
6350Sstevel@tonic-gate 			if (nonexist)
636*802Scf46844 				viprintf(gettext(" [New file]"));
6370Sstevel@tonic-gate 			else if (value(vi_WRITEANY) && edfile() != EDF)
638*802Scf46844 				viprintf(gettext(" [Existing file]"));
6390Sstevel@tonic-gate 		break;
6400Sstevel@tonic-gate 
6410Sstevel@tonic-gate 	case 2:
6420Sstevel@tonic-gate 		io = open(file, 1);
6430Sstevel@tonic-gate 		if (io < 0) {
6440Sstevel@tonic-gate 			if (exclam || value(vi_WRITEANY))
6450Sstevel@tonic-gate 				goto cre;
6460Sstevel@tonic-gate 			syserror(0);
6470Sstevel@tonic-gate 		}
6480Sstevel@tonic-gate 		lseek(io, 0l, 2);
6490Sstevel@tonic-gate 		break;
6500Sstevel@tonic-gate 	}
6510Sstevel@tonic-gate 	if (write_quit && inopen && (argc == 0 || morargc == argc))
6520Sstevel@tonic-gate 		setty(normf);
6530Sstevel@tonic-gate 	putfile(0);
6540Sstevel@tonic-gate 	if (fsync(io) < 0) {
655*802Scf46844 		/*
656*802Scf46844 		 * For NFS files write in putfile doesn't return error, but
657*802Scf46844 		 * fsync does.  So, catch it here.
658*802Scf46844 		 */
659*802Scf46844 		messagep = (char *)gettext(
660*802Scf46844 		    "\r\nYour file has been preserved\r\n");
661*802Scf46844 		(void) preserve();
662*802Scf46844 		write(1, messagep, strlen(messagep));
6630Sstevel@tonic-gate 
664*802Scf46844 		wrerror();
6650Sstevel@tonic-gate 	}
6660Sstevel@tonic-gate 	(void)iostats();
6670Sstevel@tonic-gate 	if (c != 2 && addr1 == one && addr2 == dol) {
6680Sstevel@tonic-gate 		if (eq(file, savedfile))
6690Sstevel@tonic-gate 			edited = 1;
6700Sstevel@tonic-gate 		sync();
6710Sstevel@tonic-gate 	}
6720Sstevel@tonic-gate 	if (!dofname) {
6730Sstevel@tonic-gate 		addr1 = saddr1;
6740Sstevel@tonic-gate 		addr2 = saddr2;
6750Sstevel@tonic-gate 	}
6760Sstevel@tonic-gate 	writing = 0;
6770Sstevel@tonic-gate }
6780Sstevel@tonic-gate 
6790Sstevel@tonic-gate /*
6800Sstevel@tonic-gate  * Is file the edited file?
6810Sstevel@tonic-gate  * Work here is that it is not considered edited
6820Sstevel@tonic-gate  * if this is a partial buffer, and distinguish
6830Sstevel@tonic-gate  * all cases.
6840Sstevel@tonic-gate  */
685*802Scf46844 int
686*802Scf46844 edfile(void)
6870Sstevel@tonic-gate {
6880Sstevel@tonic-gate 
6890Sstevel@tonic-gate 	if (!edited || !eq(file, savedfile))
6900Sstevel@tonic-gate 		return (NOTEDF);
6910Sstevel@tonic-gate 	return (addr1 == one && addr2 == dol ? EDF : PARTBUF);
6920Sstevel@tonic-gate }
6930Sstevel@tonic-gate 
6940Sstevel@tonic-gate /*
6950Sstevel@tonic-gate  * Extract the next line from the io stream.
6960Sstevel@tonic-gate  */
6970Sstevel@tonic-gate unsigned char *nextip;
6980Sstevel@tonic-gate 
699*802Scf46844 int
700*802Scf46844 getfile(void)
7010Sstevel@tonic-gate {
702*802Scf46844 	short c;
703*802Scf46844 	unsigned char *lp;
704*802Scf46844 	unsigned char *fp;
7050Sstevel@tonic-gate 
7060Sstevel@tonic-gate 	lp = linebuf;
7070Sstevel@tonic-gate 	fp = nextip;
7080Sstevel@tonic-gate 	do {
7090Sstevel@tonic-gate 		if (--ninbuf < 0) {
7100Sstevel@tonic-gate 			ninbuf = read(io, genbuf, LBSIZE) - 1;
7110Sstevel@tonic-gate 			if (ninbuf < 0) {
7120Sstevel@tonic-gate 				if (lp != linebuf) {
7130Sstevel@tonic-gate 					lp++;
714*802Scf46844 					viprintf(
715*802Scf46844 					    gettext(" [Incomplete last line]"));
7160Sstevel@tonic-gate 					break;
7170Sstevel@tonic-gate 				}
7180Sstevel@tonic-gate 				return (EOF);
7190Sstevel@tonic-gate 			}
7200Sstevel@tonic-gate 			if(crflag == -1) {
7210Sstevel@tonic-gate 				if(isencrypt(genbuf, ninbuf + 1))
7220Sstevel@tonic-gate 					crflag = 2;
7230Sstevel@tonic-gate 				else
7240Sstevel@tonic-gate 					crflag = -2;
7250Sstevel@tonic-gate 			}
7260Sstevel@tonic-gate 			if (crflag > 0 && run_crypt(cntch, genbuf, ninbuf+1, perm) == -1) {
7270Sstevel@tonic-gate 					smerror(gettext("Cannot decrypt block of text\n"));
7280Sstevel@tonic-gate 					break;
7290Sstevel@tonic-gate 			}
7300Sstevel@tonic-gate 			fp = genbuf;
7310Sstevel@tonic-gate 			cntch += ninbuf+1;
7320Sstevel@tonic-gate 		}
7330Sstevel@tonic-gate 		if (lp >= &linebuf[LBSIZE]) {
7340Sstevel@tonic-gate 			error(gettext(" Line too long"));
7350Sstevel@tonic-gate 		}
7360Sstevel@tonic-gate 		c = *fp++;
7370Sstevel@tonic-gate 		if (c == 0) {
7380Sstevel@tonic-gate 			cntnull++;
7390Sstevel@tonic-gate 			continue;
7400Sstevel@tonic-gate 		}
7410Sstevel@tonic-gate 		*lp++ = c;
7420Sstevel@tonic-gate 	} while (c != '\n');
7430Sstevel@tonic-gate 	*--lp = 0;
7440Sstevel@tonic-gate 	nextip = fp;
7450Sstevel@tonic-gate 	cntln++;
7460Sstevel@tonic-gate 	return (0);
7470Sstevel@tonic-gate }
7480Sstevel@tonic-gate 
7490Sstevel@tonic-gate /*
7500Sstevel@tonic-gate  * Write a range onto the io stream.
7510Sstevel@tonic-gate  */
752*802Scf46844 void
7530Sstevel@tonic-gate putfile(int isfilter)
7540Sstevel@tonic-gate {
7550Sstevel@tonic-gate 	line *a1;
756*802Scf46844 	unsigned char *lp;
757*802Scf46844 	unsigned char *fp;
758*802Scf46844 	int nib;
759*802Scf46844 	bool ochng = chng;
7600Sstevel@tonic-gate 	char *messagep;
7610Sstevel@tonic-gate 
7620Sstevel@tonic-gate 	chng = 1;		/* set to force file recovery procedures in */
7630Sstevel@tonic-gate 				/* the event of an interrupt during write   */
7640Sstevel@tonic-gate 	a1 = addr1;
7650Sstevel@tonic-gate 	clrstats();
7660Sstevel@tonic-gate 	cntln = addr2 - a1 + 1;
7670Sstevel@tonic-gate 	nib = BUFSIZE;
7680Sstevel@tonic-gate 	fp = genbuf;
7690Sstevel@tonic-gate 	do {
7700Sstevel@tonic-gate 		getline(*a1++);
7710Sstevel@tonic-gate 		lp = linebuf;
7720Sstevel@tonic-gate 		for (;;) {
7730Sstevel@tonic-gate 			if (--nib < 0) {
7740Sstevel@tonic-gate 				nib = fp - genbuf;
7750Sstevel@tonic-gate                 		if(kflag && !isfilter)
7760Sstevel@tonic-gate                                         if (run_crypt(cntch, genbuf, nib, perm) == -1)
7770Sstevel@tonic-gate 					  wrerror();
7780Sstevel@tonic-gate 				if (write(io, genbuf, nib) != nib) {
779*802Scf46844 				    messagep = (char *)gettext(
780*802Scf46844 					"\r\nYour file has been preserved\r\n");
781*802Scf46844 				    (void) preserve();
782*802Scf46844 				    write(1, messagep, strlen(messagep));
7830Sstevel@tonic-gate 
784*802Scf46844 				    if (!isfilter)
785*802Scf46844 					wrerror();
786*802Scf46844 				    return;
7870Sstevel@tonic-gate 				}
7880Sstevel@tonic-gate 				cntch += nib;
7890Sstevel@tonic-gate 				nib = BUFSIZE - 1;
7900Sstevel@tonic-gate 				fp = genbuf;
7910Sstevel@tonic-gate 			}
7920Sstevel@tonic-gate 			if ((*fp++ = *lp++) == 0) {
7930Sstevel@tonic-gate 				fp[-1] = '\n';
7940Sstevel@tonic-gate 				break;
7950Sstevel@tonic-gate 			}
7960Sstevel@tonic-gate 		}
7970Sstevel@tonic-gate 	} while (a1 <= addr2);
7980Sstevel@tonic-gate 	nib = fp - genbuf;
7990Sstevel@tonic-gate 	if(kflag && !isfilter)
8000Sstevel@tonic-gate 		if (run_crypt(cntch, genbuf, nib, perm) == -1)
8010Sstevel@tonic-gate 			wrerror();
8020Sstevel@tonic-gate 	if ((cntch == 0) && (nib == 1)) {
8030Sstevel@tonic-gate 		cntln = 0;
8040Sstevel@tonic-gate 		return;
8050Sstevel@tonic-gate 	}
8060Sstevel@tonic-gate 	if (write(io, genbuf, nib) != nib) {
807*802Scf46844 		messagep = (char *)gettext(
808*802Scf46844 		    "\r\nYour file has been preserved\r\n");
809*802Scf46844 		(void) preserve();
810*802Scf46844 		write(1, messagep, strlen(messagep));
8110Sstevel@tonic-gate 
8120Sstevel@tonic-gate 		if(!isfilter)
8130Sstevel@tonic-gate 			wrerror();
8140Sstevel@tonic-gate 		return;
8150Sstevel@tonic-gate 	}
8160Sstevel@tonic-gate 	cntch += nib;
8170Sstevel@tonic-gate 	chng = ochng;			/* reset chng to original value */
8180Sstevel@tonic-gate }
8190Sstevel@tonic-gate 
8200Sstevel@tonic-gate /*
8210Sstevel@tonic-gate  * A write error has occurred;  if the file being written was
8220Sstevel@tonic-gate  * the edited file then we consider it to have changed since it is
8230Sstevel@tonic-gate  * now likely scrambled.
8240Sstevel@tonic-gate  */
825*802Scf46844 void
826*802Scf46844 wrerror(void)
8270Sstevel@tonic-gate {
8280Sstevel@tonic-gate 
8290Sstevel@tonic-gate 	if (eq(file, savedfile) && edited)
8300Sstevel@tonic-gate 		change();
8310Sstevel@tonic-gate 	syserror(1);
8320Sstevel@tonic-gate }
8330Sstevel@tonic-gate 
8340Sstevel@tonic-gate /*
8350Sstevel@tonic-gate  * Source command, handles nested sources.
8360Sstevel@tonic-gate  * Traps errors since it mungs unit 0 during the source.
8370Sstevel@tonic-gate  */
8380Sstevel@tonic-gate short slevel;
8390Sstevel@tonic-gate short ttyindes;
8400Sstevel@tonic-gate 
841*802Scf46844 void
8420Sstevel@tonic-gate source(fil, okfail)
8430Sstevel@tonic-gate 	unsigned char *fil;
8440Sstevel@tonic-gate 	bool okfail;
8450Sstevel@tonic-gate {
8460Sstevel@tonic-gate 	jmp_buf osetexit;
847*802Scf46844 	int saveinp, ointty, oerrno;
8480Sstevel@tonic-gate 	unsigned char *saveglobp;
8490Sstevel@tonic-gate 	short savepeekc;
8500Sstevel@tonic-gate 
8510Sstevel@tonic-gate 	signal(SIGINT, SIG_IGN);
8520Sstevel@tonic-gate 	saveinp = dup(0);
8530Sstevel@tonic-gate 	savepeekc = peekc;
8540Sstevel@tonic-gate 	saveglobp = globp;
8550Sstevel@tonic-gate 	peekc = 0; globp = 0;
8560Sstevel@tonic-gate 	if (saveinp < 0)
8570Sstevel@tonic-gate 		error(gettext("Too many nested sources"));
8580Sstevel@tonic-gate 	if (slevel <= 0)
8590Sstevel@tonic-gate 		ttyindes = saveinp;
8600Sstevel@tonic-gate 	close(0);
8610Sstevel@tonic-gate 	if (open(fil, 0) < 0) {
8620Sstevel@tonic-gate 		oerrno = errno;
8630Sstevel@tonic-gate 		setrupt();
8640Sstevel@tonic-gate 		dup(saveinp);
8650Sstevel@tonic-gate 		close(saveinp);
8660Sstevel@tonic-gate 		errno = oerrno;
8670Sstevel@tonic-gate 		if (!okfail)
8680Sstevel@tonic-gate 			filioerr(fil);
8690Sstevel@tonic-gate 		return;
8700Sstevel@tonic-gate 	}
8710Sstevel@tonic-gate 	slevel++;
8720Sstevel@tonic-gate 	ointty = intty;
8730Sstevel@tonic-gate 	intty = isatty(0);
8740Sstevel@tonic-gate 	oprompt = value(vi_PROMPT);
8750Sstevel@tonic-gate 	value(vi_PROMPT) &= intty;
8760Sstevel@tonic-gate 	getexit(osetexit);
8770Sstevel@tonic-gate 	setrupt();
8780Sstevel@tonic-gate 	if (setexit() == 0)
8790Sstevel@tonic-gate 		commands(1, 1);
8800Sstevel@tonic-gate 	else if (slevel > 1) {
8810Sstevel@tonic-gate 		close(0);
8820Sstevel@tonic-gate 		dup(saveinp);
8830Sstevel@tonic-gate 		close(saveinp);
8840Sstevel@tonic-gate 		slevel--;
8850Sstevel@tonic-gate 		resexit(osetexit);
8860Sstevel@tonic-gate 		reset();
8870Sstevel@tonic-gate 	}
8880Sstevel@tonic-gate 	intty = ointty;
8890Sstevel@tonic-gate 	value(vi_PROMPT) = oprompt;
8900Sstevel@tonic-gate 	close(0);
8910Sstevel@tonic-gate 	dup(saveinp);
8920Sstevel@tonic-gate 	close(saveinp);
8930Sstevel@tonic-gate 	globp = saveglobp;
8940Sstevel@tonic-gate 	peekc = savepeekc;
8950Sstevel@tonic-gate 	slevel--;
8960Sstevel@tonic-gate 	resexit(osetexit);
8970Sstevel@tonic-gate }
8980Sstevel@tonic-gate 
8990Sstevel@tonic-gate /*
9000Sstevel@tonic-gate  * Clear io statistics before a read or write.
9010Sstevel@tonic-gate  */
902*802Scf46844 void
903*802Scf46844 clrstats(void)
9040Sstevel@tonic-gate {
9050Sstevel@tonic-gate 
9060Sstevel@tonic-gate 	ninbuf = 0;
9070Sstevel@tonic-gate 	cntch = 0;
9080Sstevel@tonic-gate 	cntln = 0;
9090Sstevel@tonic-gate 	cntnull = 0;
9100Sstevel@tonic-gate 	cntodd = 0;
9110Sstevel@tonic-gate }
9120Sstevel@tonic-gate 
9130Sstevel@tonic-gate /*
9140Sstevel@tonic-gate  * Io is finished, close the unit and print statistics.
9150Sstevel@tonic-gate  */
916*802Scf46844 int
917*802Scf46844 iostats(void)
9180Sstevel@tonic-gate {
9190Sstevel@tonic-gate 
9200Sstevel@tonic-gate 	close(io);
9210Sstevel@tonic-gate 	io = -1;
9220Sstevel@tonic-gate 	if (hush == 0) {
9230Sstevel@tonic-gate 		if (value(vi_TERSE))
924*802Scf46844 			viprintf(" %d/%D", cntln, cntch);
9250Sstevel@tonic-gate 		else if (cntln == 1 && cntch == 1) {
926*802Scf46844 			viprintf(gettext(" 1 line, 1 character"));
9270Sstevel@tonic-gate 		} else if (cntln == 1 && cntch != 1) {
928*802Scf46844 			viprintf(gettext(" 1 line, %D characters"), cntch);
9290Sstevel@tonic-gate 		} else if (cntln != 1 && cntch != 1) {
9300Sstevel@tonic-gate 			/*
9310Sstevel@tonic-gate 			 * TRANSLATION_NOTE
9320Sstevel@tonic-gate 			 *	Reference order of arguments must not
9330Sstevel@tonic-gate 			 *	be changed using '%digit$', since vi's
934*802Scf46844 			 *	viprintf() does not support it.
9350Sstevel@tonic-gate 			 */
936*802Scf46844 			viprintf(gettext(" %d lines, %D characters"), cntln,
9370Sstevel@tonic-gate 			    cntch);
9380Sstevel@tonic-gate 		} else {
9390Sstevel@tonic-gate 			/* ridiculous */
940*802Scf46844 			viprintf(gettext(" %d lines, 1 character"), cntln);
9410Sstevel@tonic-gate 		}
9420Sstevel@tonic-gate 		if (cntnull || cntodd) {
943*802Scf46844 			viprintf(" (");
9440Sstevel@tonic-gate 			if (cntnull) {
945*802Scf46844 				viprintf(gettext("%D null"), cntnull);
9460Sstevel@tonic-gate 				if (cntodd)
947*802Scf46844 					viprintf(", ");
9480Sstevel@tonic-gate 			}
9490Sstevel@tonic-gate 			if (cntodd)
950*802Scf46844 				viprintf(gettext("%D non-ASCII"), cntodd);
9510Sstevel@tonic-gate 			putchar(')');
9520Sstevel@tonic-gate 		}
9530Sstevel@tonic-gate 		noonl();
9540Sstevel@tonic-gate 		flush();
9550Sstevel@tonic-gate 	}
9560Sstevel@tonic-gate 	return (cntnull != 0 || cntodd != 0);
9570Sstevel@tonic-gate }
9580Sstevel@tonic-gate 
9590Sstevel@tonic-gate 
9600Sstevel@tonic-gate static void chkmdln(aline)
9610Sstevel@tonic-gate unsigned char *aline;
9620Sstevel@tonic-gate {
9630Sstevel@tonic-gate 	unsigned char *beg, *end;
9640Sstevel@tonic-gate 	unsigned char cmdbuf[1024];
9650Sstevel@tonic-gate 	char *strchr(), *strrchr();
9660Sstevel@tonic-gate 	bool savetty;
9670Sstevel@tonic-gate 	int  savepeekc;
9680Sstevel@tonic-gate 	int  savechng;
9690Sstevel@tonic-gate 	unsigned char	*savefirstpat;
9700Sstevel@tonic-gate 	unsigned char	*p;
9710Sstevel@tonic-gate 	int	len;
9720Sstevel@tonic-gate 
9730Sstevel@tonic-gate 	beg = (unsigned char *)strchr((char *)aline, ':');
9740Sstevel@tonic-gate 	if (beg == NULL)
9750Sstevel@tonic-gate 		return;
9760Sstevel@tonic-gate 	if ((len = beg - aline) < 2)
9770Sstevel@tonic-gate 		return;
9780Sstevel@tonic-gate 
9790Sstevel@tonic-gate 	if ((beg - aline) != 2) {
9800Sstevel@tonic-gate 		if ((p = beg - ((unsigned int)MB_CUR_MAX * 2) - 2) < aline)
9810Sstevel@tonic-gate 			p = aline;
9820Sstevel@tonic-gate 		for ( ; p < (beg - 2); p += len) {
9830Sstevel@tonic-gate 			if ((len = mblen((char *)p, MB_CUR_MAX)) <= 0)
9840Sstevel@tonic-gate 				len = 1;
9850Sstevel@tonic-gate 		}
9860Sstevel@tonic-gate 		if (p !=  (beg - 2))
9870Sstevel@tonic-gate 			return;
9880Sstevel@tonic-gate 	}
9890Sstevel@tonic-gate 
9900Sstevel@tonic-gate 	if (!((beg[-2] == 'e' && p[-1] == 'x')
9910Sstevel@tonic-gate 	||    (beg[-2] == 'v' && beg[-1] == 'i')))
9920Sstevel@tonic-gate 	 	return;
9930Sstevel@tonic-gate 
9940Sstevel@tonic-gate 	strncpy(cmdbuf, beg+1, sizeof cmdbuf);
9950Sstevel@tonic-gate 	end = (unsigned char *)strrchr(cmdbuf, ':');
9960Sstevel@tonic-gate 	if (end == NULL)
9970Sstevel@tonic-gate 		return;
9980Sstevel@tonic-gate 	*end = 0;
9990Sstevel@tonic-gate 	globp = cmdbuf;
10000Sstevel@tonic-gate 	savepeekc = peekc;
10010Sstevel@tonic-gate 	peekc = 0;
10020Sstevel@tonic-gate 	savetty = intty;
10030Sstevel@tonic-gate 	intty = 0;
10040Sstevel@tonic-gate 	savechng = chng;
10050Sstevel@tonic-gate 	savefirstpat = firstpat;
10060Sstevel@tonic-gate 	firstpat = (unsigned char *)"";
10070Sstevel@tonic-gate 	commands(1, 1);
10080Sstevel@tonic-gate 	peekc = savepeekc;
10090Sstevel@tonic-gate 	globp = 0;
10100Sstevel@tonic-gate 	intty = savetty;
10110Sstevel@tonic-gate 	/* chng being increased indicates that text was changed */
10120Sstevel@tonic-gate 	if (savechng < chng)
10130Sstevel@tonic-gate 		laste = 0;
10140Sstevel@tonic-gate 	firstpat = savefirstpat;
10150Sstevel@tonic-gate }
1016