xref: /onnv-gate/usr/src/cmd/vi/port/ex_io.c (revision 13093:48f2dbca79a2)
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
5*13093SRoger.Faulkner@Oracle.COM  * Common Development and Distribution License (the "License").
6*13093SRoger.Faulkner@Oracle.COM  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
21*13093SRoger.Faulkner@Oracle.COM 
22802Scf46844 /*
23*13093SRoger.Faulkner@Oracle.COM  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
24802Scf46844  */
25802Scf46844 
260Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
270Sstevel@tonic-gate /*	  All Rights Reserved  	*/
280Sstevel@tonic-gate 
290Sstevel@tonic-gate 
300Sstevel@tonic-gate /* Copyright (c) 1981 Regents of the University of California */
310Sstevel@tonic-gate 
320Sstevel@tonic-gate #include "ex.h"
330Sstevel@tonic-gate #include "ex_argv.h"
340Sstevel@tonic-gate #include "ex_temp.h"
350Sstevel@tonic-gate #include "ex_tty.h"
360Sstevel@tonic-gate #include "ex_vis.h"
370Sstevel@tonic-gate #include <stdlib.h>
38802Scf46844 #include <unistd.h>
390Sstevel@tonic-gate 
400Sstevel@tonic-gate /*
410Sstevel@tonic-gate  * File input/output, source, preserve and recover
420Sstevel@tonic-gate  */
430Sstevel@tonic-gate 
440Sstevel@tonic-gate /*
450Sstevel@tonic-gate  * Following remember where . was in the previous file for return
460Sstevel@tonic-gate  * on file switching.
470Sstevel@tonic-gate  */
480Sstevel@tonic-gate int	altdot;
490Sstevel@tonic-gate int	oldadot;
500Sstevel@tonic-gate bool	wasalt;
510Sstevel@tonic-gate short	isalt;
520Sstevel@tonic-gate 
530Sstevel@tonic-gate long	cntch;			/* Count of characters on unit io */
540Sstevel@tonic-gate #ifndef VMUNIX
550Sstevel@tonic-gate short	cntln;			/* Count of lines " */
560Sstevel@tonic-gate #else
570Sstevel@tonic-gate int	cntln;
580Sstevel@tonic-gate #endif
590Sstevel@tonic-gate long	cntnull;		/* Count of nulls " */
600Sstevel@tonic-gate long	cntodd;			/* Count of non-ascii characters " */
610Sstevel@tonic-gate 
620Sstevel@tonic-gate static void chkmdln();
63802Scf46844 extern int	getchar();
640Sstevel@tonic-gate 
650Sstevel@tonic-gate /*
660Sstevel@tonic-gate  * Parse file name for command encoded by comm.
670Sstevel@tonic-gate  * If comm is E then command is doomed and we are
680Sstevel@tonic-gate  * parsing just so user won't have to retype the name.
690Sstevel@tonic-gate  */
70802Scf46844 void
filename(int comm)71802Scf46844 filename(int comm)
720Sstevel@tonic-gate {
73802Scf46844 	int c = comm, d;
74802Scf46844 	int i;
750Sstevel@tonic-gate 
760Sstevel@tonic-gate 	d = getchar();
770Sstevel@tonic-gate 	if (endcmd(d)) {
780Sstevel@tonic-gate 		if (savedfile[0] == 0 && comm != 'f')
790Sstevel@tonic-gate 			error(value(vi_TERSE) ? gettext("No file") :
800Sstevel@tonic-gate gettext("No current filename"));
810Sstevel@tonic-gate 		CP(file, savedfile);
820Sstevel@tonic-gate 		wasalt = (isalt > 0) ? isalt-1 : 0;
830Sstevel@tonic-gate 		isalt = 0;
840Sstevel@tonic-gate 		oldadot = altdot;
850Sstevel@tonic-gate 		if (c == 'e' || c == 'E')
860Sstevel@tonic-gate 			altdot = lineDOT();
870Sstevel@tonic-gate 		if (d == EOF)
880Sstevel@tonic-gate 			ungetchar(d);
890Sstevel@tonic-gate 	} else {
900Sstevel@tonic-gate 		ungetchar(d);
910Sstevel@tonic-gate 		getone();
920Sstevel@tonic-gate 		eol();
930Sstevel@tonic-gate 		if (savedfile[0] == 0 && c != 'E' && c != 'e') {
940Sstevel@tonic-gate 			c = 'e';
950Sstevel@tonic-gate 			edited = 0;
960Sstevel@tonic-gate 		}
970Sstevel@tonic-gate 		wasalt = strcmp(file, altfile) == 0;
980Sstevel@tonic-gate 		oldadot = altdot;
990Sstevel@tonic-gate 		switch (c) {
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate 		case 'f':
1020Sstevel@tonic-gate 			edited = 0;
1030Sstevel@tonic-gate 			/* fall into ... */
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate 		case 'e':
1060Sstevel@tonic-gate 			if (savedfile[0]) {
1070Sstevel@tonic-gate 				altdot = lineDOT();
1080Sstevel@tonic-gate 				CP(altfile, savedfile);
1090Sstevel@tonic-gate 			}
1100Sstevel@tonic-gate 			CP(savedfile, file);
1110Sstevel@tonic-gate 			break;
1120Sstevel@tonic-gate 
1130Sstevel@tonic-gate 		default:
1140Sstevel@tonic-gate 			if (file[0]) {
1150Sstevel@tonic-gate 				if (c != 'E')
1160Sstevel@tonic-gate 					altdot = lineDOT();
1170Sstevel@tonic-gate 				CP(altfile, file);
1180Sstevel@tonic-gate 			}
1190Sstevel@tonic-gate 			break;
1200Sstevel@tonic-gate 		}
1210Sstevel@tonic-gate 	}
1220Sstevel@tonic-gate 	if (hush && comm != 'f' || comm == 'E')
1230Sstevel@tonic-gate 		return;
1240Sstevel@tonic-gate 	if (file[0] != 0) {
1250Sstevel@tonic-gate 		lprintf("\"%s\"", file);
1260Sstevel@tonic-gate 		if (comm == 'f') {
1270Sstevel@tonic-gate 			if (value(vi_READONLY))
128802Scf46844 				viprintf(gettext(" [Read only]"));
1290Sstevel@tonic-gate 			if (!edited)
130802Scf46844 				viprintf(gettext(" [Not edited]"));
1310Sstevel@tonic-gate 			if (tchng)
132802Scf46844 				viprintf(gettext(" [Modified]"));
1330Sstevel@tonic-gate 		}
1340Sstevel@tonic-gate 		flush();
1350Sstevel@tonic-gate 	} else
136802Scf46844 		viprintf(gettext("No file "));
1370Sstevel@tonic-gate 	if (comm == 'f') {
1380Sstevel@tonic-gate 		if (!(i = lineDOL()))
1390Sstevel@tonic-gate 			i++;
1400Sstevel@tonic-gate 		/*
1410Sstevel@tonic-gate 		 * TRANSLATION_NOTE
1420Sstevel@tonic-gate 		 *	Reference order of arguments must not
1430Sstevel@tonic-gate 		 *	be changed using '%digit$', since vi's
144802Scf46844 		 *	viprintf() does not support it.
1450Sstevel@tonic-gate 		 */
146802Scf46844 		viprintf(gettext(" line %d of %d --%ld%%--"), lineDOT(),
147802Scf46844 		    lineDOL(), (long)(100 * lineDOT() / i));
1480Sstevel@tonic-gate 	}
1490Sstevel@tonic-gate }
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate /*
1520Sstevel@tonic-gate  * Get the argument words for a command into genbuf
1530Sstevel@tonic-gate  * expanding # and %.
1540Sstevel@tonic-gate  */
155802Scf46844 int
getargs(void)156802Scf46844 getargs(void)
1570Sstevel@tonic-gate {
158802Scf46844 	int c;
159802Scf46844 	unsigned char *cp, *fp;
1600Sstevel@tonic-gate 	static unsigned char fpatbuf[32];	/* hence limit on :next +/pat */
1610Sstevel@tonic-gate 	char	multic[MB_LEN_MAX + 1];
1620Sstevel@tonic-gate 	int	len;
1630Sstevel@tonic-gate 	wchar_t	wc;
1640Sstevel@tonic-gate 
1650Sstevel@tonic-gate 	pastwh();
1660Sstevel@tonic-gate 	if (peekchar() == '+') {
1670Sstevel@tonic-gate 		for (cp = fpatbuf;;) {
1680Sstevel@tonic-gate 			if (!isascii(c = peekchar()) && (c != EOF)) {
1690Sstevel@tonic-gate 				if ((len = _mbftowc(multic, &wc, getchar, &peekc)) > 0) {
1700Sstevel@tonic-gate 					if ((cp + len) >= &fpatbuf[sizeof(fpatbuf)])
1710Sstevel@tonic-gate 						error(gettext("Pattern too long"));
1720Sstevel@tonic-gate 					strncpy(cp, multic, len);
1730Sstevel@tonic-gate 					cp += len;
1740Sstevel@tonic-gate 					continue;
1750Sstevel@tonic-gate 				}
1760Sstevel@tonic-gate 			}
1770Sstevel@tonic-gate 
1780Sstevel@tonic-gate 			c = getchar();
1790Sstevel@tonic-gate 			*cp++ = c;
1800Sstevel@tonic-gate 			if (cp >= &fpatbuf[sizeof(fpatbuf)])
1810Sstevel@tonic-gate 				error(gettext("Pattern too long"));
1820Sstevel@tonic-gate 			if (c == '\\' && isspace(peekchar()))
1830Sstevel@tonic-gate 				c = getchar();
1840Sstevel@tonic-gate 			if (c == EOF || isspace(c)) {
1850Sstevel@tonic-gate 				ungetchar(c);
1860Sstevel@tonic-gate 				*--cp = 0;
1870Sstevel@tonic-gate 				firstpat = &fpatbuf[1];
1880Sstevel@tonic-gate 				break;
1890Sstevel@tonic-gate 			}
1900Sstevel@tonic-gate 		}
1910Sstevel@tonic-gate 	}
1920Sstevel@tonic-gate 	if (skipend())
1930Sstevel@tonic-gate 		return (0);
1940Sstevel@tonic-gate 	CP(genbuf, "echo "); cp = &genbuf[5];
1950Sstevel@tonic-gate 	for (;;) {
1960Sstevel@tonic-gate 		if (!isascii(c = peekchar())) {
1970Sstevel@tonic-gate 			if (endcmd(c) && c != '"')
1980Sstevel@tonic-gate 				break;
1990Sstevel@tonic-gate 			if ((len = _mbftowc(multic, &wc, getchar, &peekc)) > 0) {
2000Sstevel@tonic-gate 				if ((cp + len) > &genbuf[LBSIZE - 2])
2010Sstevel@tonic-gate 					error(gettext("Argument buffer overflow"));
2020Sstevel@tonic-gate 				strncpy(cp, multic, len);
2030Sstevel@tonic-gate 				cp += len;
2040Sstevel@tonic-gate 				continue;
2050Sstevel@tonic-gate 			}
2060Sstevel@tonic-gate 		}
2070Sstevel@tonic-gate 
2080Sstevel@tonic-gate 		if (endcmd(c) && c != '"')
2090Sstevel@tonic-gate 			break;
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate 		c = getchar();
2120Sstevel@tonic-gate 		switch (c) {
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate 		case '\\':
2150Sstevel@tonic-gate 			if (any(peekchar(), "#%|"))
2160Sstevel@tonic-gate 				c = getchar();
2170Sstevel@tonic-gate 			/* fall into... */
2180Sstevel@tonic-gate 
2190Sstevel@tonic-gate 		default:
2200Sstevel@tonic-gate 			if (cp > &genbuf[LBSIZE - 2])
2210Sstevel@tonic-gate flong:
2220Sstevel@tonic-gate 				error(gettext("Argument buffer overflow"));
2230Sstevel@tonic-gate 			*cp++ = c;
2240Sstevel@tonic-gate 			break;
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate 		case '#':
2270Sstevel@tonic-gate 			fp = (unsigned char *)altfile;
2280Sstevel@tonic-gate 			if (*fp == 0)
2290Sstevel@tonic-gate 				error(value(vi_TERSE) ?
2300Sstevel@tonic-gate gettext("No alternate filename") :
2310Sstevel@tonic-gate gettext("No alternate filename to substitute for #"));
2320Sstevel@tonic-gate 			goto filexp;
2330Sstevel@tonic-gate 
2340Sstevel@tonic-gate 		case '%':
2350Sstevel@tonic-gate 			fp = savedfile;
2360Sstevel@tonic-gate 			if (*fp == 0)
2370Sstevel@tonic-gate 				error(value(vi_TERSE) ?
2380Sstevel@tonic-gate gettext("No current filename") :
2390Sstevel@tonic-gate gettext("No current filename to substitute for %%"));
2400Sstevel@tonic-gate filexp:
2410Sstevel@tonic-gate 			while (*fp) {
2420Sstevel@tonic-gate 				if (cp > &genbuf[LBSIZE - 2])
2430Sstevel@tonic-gate 					goto flong;
2440Sstevel@tonic-gate 				*cp++ = *fp++;
2450Sstevel@tonic-gate 			}
2460Sstevel@tonic-gate 			break;
2470Sstevel@tonic-gate 		}
2480Sstevel@tonic-gate 	}
2490Sstevel@tonic-gate 	*cp = 0;
2500Sstevel@tonic-gate 	return (1);
2510Sstevel@tonic-gate }
2520Sstevel@tonic-gate 
2530Sstevel@tonic-gate /*
2540Sstevel@tonic-gate  * Glob the argument words in genbuf, or if no globbing
2550Sstevel@tonic-gate  * is implied, just split them up directly.
2560Sstevel@tonic-gate  */
257802Scf46844 void
glob(struct glob * gp)258802Scf46844 glob(struct glob *gp)
2590Sstevel@tonic-gate {
2600Sstevel@tonic-gate 	int pvec[2];
261802Scf46844 	unsigned char **argv = gp->argv;
262802Scf46844 	unsigned char *cp = gp->argspac;
263802Scf46844 	int c;
2640Sstevel@tonic-gate 	unsigned char ch;
2650Sstevel@tonic-gate 	int nleft = NCARGS;
2660Sstevel@tonic-gate 
2670Sstevel@tonic-gate 	gp->argc0 = 0;
2680Sstevel@tonic-gate 	if (gscan() == 0) {
269802Scf46844 		unsigned char *v = genbuf + 5;		/* strlen("echo ") */
2700Sstevel@tonic-gate 
2710Sstevel@tonic-gate 		for (;;) {
2720Sstevel@tonic-gate 			while (isspace(*v))
2730Sstevel@tonic-gate 				v++;
2740Sstevel@tonic-gate 			if (!*v)
2750Sstevel@tonic-gate 				break;
2760Sstevel@tonic-gate 			*argv++ = cp;
2770Sstevel@tonic-gate 			while (*v && !isspace(*v))
2780Sstevel@tonic-gate 				*cp++ = *v++;
2790Sstevel@tonic-gate 			*cp++ = 0;
2800Sstevel@tonic-gate 			gp->argc0++;
2810Sstevel@tonic-gate 		}
2820Sstevel@tonic-gate 		*argv = 0;
2830Sstevel@tonic-gate 		return;
2840Sstevel@tonic-gate 	}
2850Sstevel@tonic-gate 	if (pipe(pvec) < 0)
2860Sstevel@tonic-gate 		error(gettext("Can't make pipe to glob"));
2870Sstevel@tonic-gate 	pid = fork();
2880Sstevel@tonic-gate 	io = pvec[0];
2890Sstevel@tonic-gate 	if (pid < 0) {
2900Sstevel@tonic-gate 		close(pvec[1]);
2910Sstevel@tonic-gate 		error(gettext("Can't fork to do glob"));
2920Sstevel@tonic-gate 	}
2930Sstevel@tonic-gate 	if (pid == 0) {
2940Sstevel@tonic-gate 		int oerrno;
2950Sstevel@tonic-gate 
2960Sstevel@tonic-gate 		close(1);
2970Sstevel@tonic-gate 		dup(pvec[1]);
2980Sstevel@tonic-gate 		close(pvec[0]);
2990Sstevel@tonic-gate 		close(2);	/* so errors don't mess up the screen */
3000Sstevel@tonic-gate 		open("/dev/null", 1);
301802Scf46844 		execlp((char *)svalue(vi_SHELL), "sh", "-c", genbuf, (char *)0);
3020Sstevel@tonic-gate 		oerrno = errno; close(1); dup(2); errno = oerrno;
3030Sstevel@tonic-gate 		filioerr(svalue(vi_SHELL));
3040Sstevel@tonic-gate 	}
3050Sstevel@tonic-gate 	close(pvec[1]);
3060Sstevel@tonic-gate 	do {
3070Sstevel@tonic-gate 		*argv = cp;
3080Sstevel@tonic-gate 		for (;;) {
3090Sstevel@tonic-gate 			if (read(io, &ch, 1) != 1) {
3100Sstevel@tonic-gate 				close(io);
3110Sstevel@tonic-gate 				c = -1;
3120Sstevel@tonic-gate 			} else
3130Sstevel@tonic-gate 				c = ch;
3140Sstevel@tonic-gate 			if (c <= 0 || isspace(c))
3150Sstevel@tonic-gate 				break;
3160Sstevel@tonic-gate 			*cp++ = c;
3170Sstevel@tonic-gate 			if (--nleft <= 0)
3180Sstevel@tonic-gate 				error(gettext("Arg list too long"));
3190Sstevel@tonic-gate 		}
3200Sstevel@tonic-gate 		if (cp != *argv) {
3210Sstevel@tonic-gate 			--nleft;
3220Sstevel@tonic-gate 			*cp++ = 0;
3230Sstevel@tonic-gate 			gp->argc0++;
3240Sstevel@tonic-gate 			if (gp->argc0 >= NARGS)
3250Sstevel@tonic-gate 				error(gettext("Arg list too long"));
3260Sstevel@tonic-gate 			argv++;
3270Sstevel@tonic-gate 		}
3280Sstevel@tonic-gate 	} while (c >= 0);
3290Sstevel@tonic-gate 	waitfor();
3300Sstevel@tonic-gate 	if (gp->argc0 == 0)
3310Sstevel@tonic-gate 		error(gettext("No match"));
3320Sstevel@tonic-gate }
3330Sstevel@tonic-gate 
3340Sstevel@tonic-gate /*
3350Sstevel@tonic-gate  * Scan genbuf for shell metacharacters.
3360Sstevel@tonic-gate  * Set is union of v7 shell and csh metas.
3370Sstevel@tonic-gate  */
338802Scf46844 int
gscan(void)339802Scf46844 gscan(void)
3400Sstevel@tonic-gate {
341802Scf46844 	unsigned char *cp;
3420Sstevel@tonic-gate 	int	len;
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate 	for (cp = genbuf; *cp; cp += len) {
3450Sstevel@tonic-gate 		if (any(*cp, "~{[*?$`'\"\\"))
3460Sstevel@tonic-gate 			return (1);
3470Sstevel@tonic-gate 		if ((len = mblen((char *)cp, MB_CUR_MAX)) <= 0)
3480Sstevel@tonic-gate 			len = 1;
3490Sstevel@tonic-gate 	}
3500Sstevel@tonic-gate 	return (0);
3510Sstevel@tonic-gate }
3520Sstevel@tonic-gate 
3530Sstevel@tonic-gate /*
3540Sstevel@tonic-gate  * Parse one filename into file.
3550Sstevel@tonic-gate  */
3560Sstevel@tonic-gate struct glob G;
357802Scf46844 void
getone(void)358802Scf46844 getone(void)
3590Sstevel@tonic-gate {
360802Scf46844 	unsigned char *str;
3610Sstevel@tonic-gate 
3620Sstevel@tonic-gate 	if (getargs() == 0)
3630Sstevel@tonic-gate 		error(gettext("Missing filename"));
3640Sstevel@tonic-gate 	glob(&G);
3650Sstevel@tonic-gate 	if (G.argc0 > 1)
3660Sstevel@tonic-gate 		error(value(vi_TERSE) ? gettext("Ambiguous") :
3670Sstevel@tonic-gate gettext("Too many file names"));
3680Sstevel@tonic-gate 	if (G.argc0 < 1)
3690Sstevel@tonic-gate 		error(gettext("Missing filename"));
3700Sstevel@tonic-gate 	str = G.argv[G.argc0 - 1];
3710Sstevel@tonic-gate 	if (strlen(str) > FNSIZE - 4)
3720Sstevel@tonic-gate 		error(gettext("Filename too long"));
3730Sstevel@tonic-gate samef:
3740Sstevel@tonic-gate 	CP(file, str);
3750Sstevel@tonic-gate }
3760Sstevel@tonic-gate 
3770Sstevel@tonic-gate /*
3780Sstevel@tonic-gate  * Read a file from the world.
3790Sstevel@tonic-gate  * C is command, 'e' if this really an edit (or a recover).
3800Sstevel@tonic-gate  */
381802Scf46844 void
rop(int c)382802Scf46844 rop(int c)
3830Sstevel@tonic-gate {
384802Scf46844 	int i;
3850Sstevel@tonic-gate 	struct stat64 stbuf;
3860Sstevel@tonic-gate 	short magic;
3870Sstevel@tonic-gate 	static int ovro;	/* old value(vi_READONLY) */
3880Sstevel@tonic-gate 	static int denied;	/* 1 if READONLY was set due to file permissions */
3890Sstevel@tonic-gate 
3900Sstevel@tonic-gate 	io = open(file, 0);
3910Sstevel@tonic-gate 	if (io < 0) {
3920Sstevel@tonic-gate 		if (c == 'e' && errno == ENOENT) {
3930Sstevel@tonic-gate 			edited++;
3940Sstevel@tonic-gate 			/*
3950Sstevel@tonic-gate 			 * If the user just did "ex foo" he is probably
3960Sstevel@tonic-gate 			 * creating a new file.  Don't be an error, since
3970Sstevel@tonic-gate 			 * this is ugly, and it messes up the + option.
3980Sstevel@tonic-gate 			 */
3990Sstevel@tonic-gate 			if (!seenprompt) {
400802Scf46844 				viprintf(gettext(" [New file]"));
4010Sstevel@tonic-gate 				noonl();
4020Sstevel@tonic-gate 				return;
4030Sstevel@tonic-gate 			}
4040Sstevel@tonic-gate 		}
4050Sstevel@tonic-gate 
4060Sstevel@tonic-gate 		if (value(vi_READONLY) && denied) {
4070Sstevel@tonic-gate 			value(vi_READONLY) = ovro;
4080Sstevel@tonic-gate 			denied = 0;
4090Sstevel@tonic-gate 		}
4100Sstevel@tonic-gate 		syserror(0);
4110Sstevel@tonic-gate 	}
4120Sstevel@tonic-gate 	if (fstat64(io, &stbuf))
4130Sstevel@tonic-gate 		syserror(0);
4140Sstevel@tonic-gate 	switch (FTYPE(stbuf) & S_IFMT) {
4150Sstevel@tonic-gate 
4160Sstevel@tonic-gate 	case S_IFBLK:
4170Sstevel@tonic-gate 		error(gettext(" Block special file"));
4180Sstevel@tonic-gate 
4190Sstevel@tonic-gate 	case S_IFCHR:
4200Sstevel@tonic-gate 		if (isatty(io))
4210Sstevel@tonic-gate 			error(gettext(" Teletype"));
4220Sstevel@tonic-gate 		if (samei(&stbuf, "/dev/null"))
4230Sstevel@tonic-gate 			break;
4240Sstevel@tonic-gate 		error(gettext(" Character special file"));
4250Sstevel@tonic-gate 
4260Sstevel@tonic-gate 	case S_IFDIR:
4270Sstevel@tonic-gate 		error(gettext(" Directory"));
4280Sstevel@tonic-gate 
4290Sstevel@tonic-gate 	}
4300Sstevel@tonic-gate 	if (c != 'r') {
4310Sstevel@tonic-gate 		if (value(vi_READONLY) && denied) {
4320Sstevel@tonic-gate 			value(vi_READONLY) = ovro;
4330Sstevel@tonic-gate 			denied = 0;
4340Sstevel@tonic-gate 		}
435802Scf46844 		if ((FMODE(stbuf) & 0222) == 0 || access((char *)file, 2) < 0) {
4360Sstevel@tonic-gate 			ovro = value(vi_READONLY);
4370Sstevel@tonic-gate 			denied = 1;
4380Sstevel@tonic-gate 			value(vi_READONLY) = 1;
4390Sstevel@tonic-gate 		}
4400Sstevel@tonic-gate 	}
4410Sstevel@tonic-gate 	if (hush == 0 && value(vi_READONLY)) {
442802Scf46844 		viprintf(gettext(" [Read only]"));
4430Sstevel@tonic-gate 		flush();
4440Sstevel@tonic-gate 	}
4450Sstevel@tonic-gate 	if (c == 'r')
4460Sstevel@tonic-gate 		setdot();
4470Sstevel@tonic-gate 	else
4480Sstevel@tonic-gate 		setall();
4490Sstevel@tonic-gate 
4500Sstevel@tonic-gate 	/* If it is a read command, then we must set dot to addr1
4510Sstevel@tonic-gate 	 * (value of N in :Nr ).  In the default case, addr1 will
4520Sstevel@tonic-gate 	 * already be set to dot.
4530Sstevel@tonic-gate 	 *
4540Sstevel@tonic-gate 	 * Next, it is necessary to mark the beginning (undap1) and
4550Sstevel@tonic-gate 	 * ending (undap2) addresses affected (for undo).  Note that
4560Sstevel@tonic-gate 	 * rop2() and rop3() will adjust the value of undap2.
4570Sstevel@tonic-gate 	 */
4580Sstevel@tonic-gate 	if (FIXUNDO && inopen && c == 'r') {
4590Sstevel@tonic-gate 		dot = addr1;
4600Sstevel@tonic-gate 		undap1 = undap2 = dot + 1;
4610Sstevel@tonic-gate 	}
4620Sstevel@tonic-gate 	rop2();
4630Sstevel@tonic-gate 	rop3(c);
4640Sstevel@tonic-gate }
4650Sstevel@tonic-gate 
466802Scf46844 void
rop2(void)467802Scf46844 rop2(void)
4680Sstevel@tonic-gate {
4690Sstevel@tonic-gate 	line *first, *last, *a;
4700Sstevel@tonic-gate 
4710Sstevel@tonic-gate 	deletenone();
4720Sstevel@tonic-gate 	clrstats();
4730Sstevel@tonic-gate 	first = addr2 + 1;
4740Sstevel@tonic-gate 	(void)append(getfile, addr2);
4750Sstevel@tonic-gate 	last = dot;
4760Sstevel@tonic-gate 	if (value(vi_MODELINES))
4770Sstevel@tonic-gate 		for (a=first; a<=last; a++) {
4780Sstevel@tonic-gate 			if (a==first+5 && last-first > 10)
4790Sstevel@tonic-gate 				a = last - 4;
480*13093SRoger.Faulkner@Oracle.COM 			getaline(*a);
4810Sstevel@tonic-gate 				chkmdln(linebuf);
4820Sstevel@tonic-gate 		}
4830Sstevel@tonic-gate }
4840Sstevel@tonic-gate 
485802Scf46844 void
rop3(int c)486802Scf46844 rop3(int c)
4870Sstevel@tonic-gate {
4880Sstevel@tonic-gate 
4890Sstevel@tonic-gate 	if (iostats() == 0 && c == 'e')
4900Sstevel@tonic-gate 		edited++;
4910Sstevel@tonic-gate 	if (c == 'e') {
4920Sstevel@tonic-gate 		if (wasalt || firstpat) {
493802Scf46844 			line *addr = zero + oldadot;
4940Sstevel@tonic-gate 
4950Sstevel@tonic-gate 			if (addr > dol)
4960Sstevel@tonic-gate 				addr = dol;
4970Sstevel@tonic-gate 			if (firstpat) {
4980Sstevel@tonic-gate 				globp = (*firstpat) ? firstpat : (unsigned char *)"$";
4990Sstevel@tonic-gate 				commands(1,1);
5000Sstevel@tonic-gate 				firstpat = 0;
5010Sstevel@tonic-gate 			} else if (addr >= one) {
5020Sstevel@tonic-gate 				if (inopen)
5030Sstevel@tonic-gate 					dot = addr;
5040Sstevel@tonic-gate 				markpr(addr);
5050Sstevel@tonic-gate 			} else
5060Sstevel@tonic-gate 				goto other;
5070Sstevel@tonic-gate 		} else
5080Sstevel@tonic-gate other:
5090Sstevel@tonic-gate 			if (dol > zero) {
5100Sstevel@tonic-gate 				if (inopen)
5110Sstevel@tonic-gate 					dot = one;
5120Sstevel@tonic-gate 				markpr(one);
5130Sstevel@tonic-gate 			}
5140Sstevel@tonic-gate 		if(FIXUNDO)
5150Sstevel@tonic-gate 			undkind = UNDNONE;
5160Sstevel@tonic-gate 		if (inopen) {
5170Sstevel@tonic-gate 			vcline = 0;
5180Sstevel@tonic-gate 			vreplace(0, lines, lineDOL());
5190Sstevel@tonic-gate 		}
5200Sstevel@tonic-gate 	}
5210Sstevel@tonic-gate 	if (laste) {
5220Sstevel@tonic-gate #ifdef VMUNIX
5230Sstevel@tonic-gate 		tlaste();
5240Sstevel@tonic-gate #endif
5250Sstevel@tonic-gate 		laste = 0;
5260Sstevel@tonic-gate 		sync();
5270Sstevel@tonic-gate 	}
5280Sstevel@tonic-gate }
5290Sstevel@tonic-gate 
5300Sstevel@tonic-gate /*
5310Sstevel@tonic-gate  * Are these two really the same inode?
5320Sstevel@tonic-gate  */
533802Scf46844 int
samei(struct stat64 * sp,unsigned char * cp)534802Scf46844 samei(struct stat64 *sp, unsigned char *cp)
5350Sstevel@tonic-gate {
5360Sstevel@tonic-gate 	struct stat64 stb;
5370Sstevel@tonic-gate 
5380Sstevel@tonic-gate 	if (stat64((char *)cp, &stb) < 0)
5390Sstevel@tonic-gate 		return (0);
5400Sstevel@tonic-gate 	return (IDENTICAL((*sp), stb));
5410Sstevel@tonic-gate }
5420Sstevel@tonic-gate 
5430Sstevel@tonic-gate /* Returns from edited() */
5440Sstevel@tonic-gate #define	EDF	0		/* Edited file */
5450Sstevel@tonic-gate #define	NOTEDF	-1		/* Not edited file */
5460Sstevel@tonic-gate #define	PARTBUF	1		/* Write of partial buffer to Edited file */
5470Sstevel@tonic-gate 
5480Sstevel@tonic-gate /*
5490Sstevel@tonic-gate  * Write a file.
5500Sstevel@tonic-gate  */
551802Scf46844 void
wop(dofname)5520Sstevel@tonic-gate wop(dofname)
5530Sstevel@tonic-gate bool dofname;	/* if 1 call filename, else use savedfile */
5540Sstevel@tonic-gate {
555802Scf46844 	int c, exclam, nonexist;
5560Sstevel@tonic-gate 	line *saddr1, *saddr2;
5570Sstevel@tonic-gate 	struct stat64 stbuf;
5580Sstevel@tonic-gate 	char *messagep;
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate 	c = 0;
5610Sstevel@tonic-gate 	exclam = 0;
5620Sstevel@tonic-gate 	if (dofname) {
5630Sstevel@tonic-gate 		if (peekchar() == '!')
5640Sstevel@tonic-gate 			exclam++, ignchar();
5650Sstevel@tonic-gate 		(void)skipwh();
5660Sstevel@tonic-gate 		while (peekchar() == '>')
5670Sstevel@tonic-gate 			ignchar(), c++, (void)skipwh();
5680Sstevel@tonic-gate 		if (c != 0 && c != 2)
5690Sstevel@tonic-gate 			error(gettext("Write forms are 'w' and 'w>>'"));
5700Sstevel@tonic-gate 		filename('w');
5710Sstevel@tonic-gate 	} else {
5720Sstevel@tonic-gate 		if (savedfile[0] == 0)
5730Sstevel@tonic-gate 			error(value(vi_TERSE) ? gettext("No file") :
5740Sstevel@tonic-gate gettext("No current filename"));
5750Sstevel@tonic-gate 		saddr1=addr1;
5760Sstevel@tonic-gate 		saddr2=addr2;
5770Sstevel@tonic-gate 		addr1=one;
5780Sstevel@tonic-gate 		addr2=dol;
5790Sstevel@tonic-gate 		CP(file, savedfile);
5800Sstevel@tonic-gate 		if (inopen) {
5810Sstevel@tonic-gate 			vclrech(0);
5820Sstevel@tonic-gate 			splitw++;
5830Sstevel@tonic-gate 		}
5840Sstevel@tonic-gate 		lprintf("\"%s\"", file);
5850Sstevel@tonic-gate 	}
5860Sstevel@tonic-gate 	nonexist = stat64((char *)file, &stbuf);
5870Sstevel@tonic-gate 	switch (c) {
5880Sstevel@tonic-gate 
5890Sstevel@tonic-gate 	case 0:
5900Sstevel@tonic-gate 		if (!exclam && (!value(vi_WRITEANY) || value(vi_READONLY)))
5910Sstevel@tonic-gate 		switch (edfile()) {
5920Sstevel@tonic-gate 
5930Sstevel@tonic-gate 		case NOTEDF:
5940Sstevel@tonic-gate 			if (nonexist)
5950Sstevel@tonic-gate 				break;
5960Sstevel@tonic-gate 			if (ISCHR(stbuf)) {
597802Scf46844 				if (samei(&stbuf, (unsigned char *)"/dev/null"))
5980Sstevel@tonic-gate 					break;
599802Scf46844 				if (samei(&stbuf, (unsigned char *)"/dev/tty"))
6000Sstevel@tonic-gate 					break;
6010Sstevel@tonic-gate 			}
6020Sstevel@tonic-gate 			io = open(file, 1);
6030Sstevel@tonic-gate 			if (io < 0)
6040Sstevel@tonic-gate 				syserror(0);
6050Sstevel@tonic-gate 			if (!isatty(io))
606802Scf46844 				serror(value(vi_TERSE) ?
607802Scf46844 				    (unsigned char *)gettext(" File exists") :
608802Scf46844 (unsigned char *)gettext(" File exists - use \"w! %s\" to overwrite"),
609802Scf46844 				    file);
6100Sstevel@tonic-gate 			close(io);
6110Sstevel@tonic-gate 			break;
6120Sstevel@tonic-gate 
6130Sstevel@tonic-gate 		case EDF:
6140Sstevel@tonic-gate 			if (value(vi_READONLY))
6150Sstevel@tonic-gate 				error(gettext(" File is read only"));
6160Sstevel@tonic-gate 			break;
6170Sstevel@tonic-gate 
6180Sstevel@tonic-gate 		case PARTBUF:
6190Sstevel@tonic-gate 			if (value(vi_READONLY))
6200Sstevel@tonic-gate 				error(gettext(" File is read only"));
6210Sstevel@tonic-gate 			error(gettext(" Use \"w!\" to write partial buffer"));
6220Sstevel@tonic-gate 		}
6230Sstevel@tonic-gate cre:
6240Sstevel@tonic-gate /*
6250Sstevel@tonic-gate 		synctmp();
6260Sstevel@tonic-gate */
6270Sstevel@tonic-gate 		io = creat(file, 0666);
6280Sstevel@tonic-gate 		if (io < 0)
6290Sstevel@tonic-gate 			syserror(0);
6300Sstevel@tonic-gate 		writing = 1;
6310Sstevel@tonic-gate 		if (hush == 0)
6320Sstevel@tonic-gate 			if (nonexist)
633802Scf46844 				viprintf(gettext(" [New file]"));
6340Sstevel@tonic-gate 			else if (value(vi_WRITEANY) && edfile() != EDF)
635802Scf46844 				viprintf(gettext(" [Existing file]"));
6360Sstevel@tonic-gate 		break;
6370Sstevel@tonic-gate 
6380Sstevel@tonic-gate 	case 2:
6390Sstevel@tonic-gate 		io = open(file, 1);
6400Sstevel@tonic-gate 		if (io < 0) {
6410Sstevel@tonic-gate 			if (exclam || value(vi_WRITEANY))
6420Sstevel@tonic-gate 				goto cre;
6430Sstevel@tonic-gate 			syserror(0);
6440Sstevel@tonic-gate 		}
6450Sstevel@tonic-gate 		lseek(io, 0l, 2);
6460Sstevel@tonic-gate 		break;
6470Sstevel@tonic-gate 	}
6480Sstevel@tonic-gate 	if (write_quit && inopen && (argc == 0 || morargc == argc))
6490Sstevel@tonic-gate 		setty(normf);
6500Sstevel@tonic-gate 	putfile(0);
6510Sstevel@tonic-gate 	if (fsync(io) < 0) {
652802Scf46844 		/*
653802Scf46844 		 * For NFS files write in putfile doesn't return error, but
654802Scf46844 		 * fsync does.  So, catch it here.
655802Scf46844 		 */
656802Scf46844 		messagep = (char *)gettext(
657802Scf46844 		    "\r\nYour file has been preserved\r\n");
658802Scf46844 		(void) preserve();
659802Scf46844 		write(1, messagep, strlen(messagep));
6600Sstevel@tonic-gate 
661802Scf46844 		wrerror();
6620Sstevel@tonic-gate 	}
6630Sstevel@tonic-gate 	(void)iostats();
6640Sstevel@tonic-gate 	if (c != 2 && addr1 == one && addr2 == dol) {
6650Sstevel@tonic-gate 		if (eq(file, savedfile))
6660Sstevel@tonic-gate 			edited = 1;
6670Sstevel@tonic-gate 		sync();
6680Sstevel@tonic-gate 	}
6690Sstevel@tonic-gate 	if (!dofname) {
6700Sstevel@tonic-gate 		addr1 = saddr1;
6710Sstevel@tonic-gate 		addr2 = saddr2;
6720Sstevel@tonic-gate 	}
6730Sstevel@tonic-gate 	writing = 0;
6740Sstevel@tonic-gate }
6750Sstevel@tonic-gate 
6760Sstevel@tonic-gate /*
6770Sstevel@tonic-gate  * Is file the edited file?
6780Sstevel@tonic-gate  * Work here is that it is not considered edited
6790Sstevel@tonic-gate  * if this is a partial buffer, and distinguish
6800Sstevel@tonic-gate  * all cases.
6810Sstevel@tonic-gate  */
682802Scf46844 int
edfile(void)683802Scf46844 edfile(void)
6840Sstevel@tonic-gate {
6850Sstevel@tonic-gate 
6860Sstevel@tonic-gate 	if (!edited || !eq(file, savedfile))
6870Sstevel@tonic-gate 		return (NOTEDF);
6880Sstevel@tonic-gate 	return (addr1 == one && addr2 == dol ? EDF : PARTBUF);
6890Sstevel@tonic-gate }
6900Sstevel@tonic-gate 
6910Sstevel@tonic-gate /*
6920Sstevel@tonic-gate  * Extract the next line from the io stream.
6930Sstevel@tonic-gate  */
6940Sstevel@tonic-gate unsigned char *nextip;
6950Sstevel@tonic-gate 
696802Scf46844 int
getfile(void)697802Scf46844 getfile(void)
6980Sstevel@tonic-gate {
699802Scf46844 	short c;
700802Scf46844 	unsigned char *lp;
701802Scf46844 	unsigned char *fp;
7020Sstevel@tonic-gate 
7030Sstevel@tonic-gate 	lp = linebuf;
7040Sstevel@tonic-gate 	fp = nextip;
7050Sstevel@tonic-gate 	do {
7060Sstevel@tonic-gate 		if (--ninbuf < 0) {
7070Sstevel@tonic-gate 			ninbuf = read(io, genbuf, LBSIZE) - 1;
7080Sstevel@tonic-gate 			if (ninbuf < 0) {
7090Sstevel@tonic-gate 				if (lp != linebuf) {
7100Sstevel@tonic-gate 					lp++;
711802Scf46844 					viprintf(
712802Scf46844 					    gettext(" [Incomplete last line]"));
7130Sstevel@tonic-gate 					break;
7140Sstevel@tonic-gate 				}
7150Sstevel@tonic-gate 				return (EOF);
7160Sstevel@tonic-gate 			}
7170Sstevel@tonic-gate 			if(crflag == -1) {
7180Sstevel@tonic-gate 				if(isencrypt(genbuf, ninbuf + 1))
7190Sstevel@tonic-gate 					crflag = 2;
7200Sstevel@tonic-gate 				else
7210Sstevel@tonic-gate 					crflag = -2;
7220Sstevel@tonic-gate 			}
7230Sstevel@tonic-gate 			if (crflag > 0 && run_crypt(cntch, genbuf, ninbuf+1, perm) == -1) {
7240Sstevel@tonic-gate 					smerror(gettext("Cannot decrypt block of text\n"));
7250Sstevel@tonic-gate 					break;
7260Sstevel@tonic-gate 			}
7270Sstevel@tonic-gate 			fp = genbuf;
7280Sstevel@tonic-gate 			cntch += ninbuf+1;
7290Sstevel@tonic-gate 		}
7300Sstevel@tonic-gate 		if (lp >= &linebuf[LBSIZE]) {
7310Sstevel@tonic-gate 			error(gettext(" Line too long"));
7320Sstevel@tonic-gate 		}
7330Sstevel@tonic-gate 		c = *fp++;
7340Sstevel@tonic-gate 		if (c == 0) {
7350Sstevel@tonic-gate 			cntnull++;
7360Sstevel@tonic-gate 			continue;
7370Sstevel@tonic-gate 		}
7380Sstevel@tonic-gate 		*lp++ = c;
7390Sstevel@tonic-gate 	} while (c != '\n');
7400Sstevel@tonic-gate 	*--lp = 0;
7410Sstevel@tonic-gate 	nextip = fp;
7420Sstevel@tonic-gate 	cntln++;
7430Sstevel@tonic-gate 	return (0);
7440Sstevel@tonic-gate }
7450Sstevel@tonic-gate 
7460Sstevel@tonic-gate /*
7470Sstevel@tonic-gate  * Write a range onto the io stream.
7480Sstevel@tonic-gate  */
749802Scf46844 void
putfile(int isfilter)7500Sstevel@tonic-gate putfile(int isfilter)
7510Sstevel@tonic-gate {
7520Sstevel@tonic-gate 	line *a1;
753802Scf46844 	unsigned char *lp;
754802Scf46844 	unsigned char *fp;
755802Scf46844 	int nib;
756802Scf46844 	bool ochng = chng;
7570Sstevel@tonic-gate 	char *messagep;
7580Sstevel@tonic-gate 
7590Sstevel@tonic-gate 	chng = 1;		/* set to force file recovery procedures in */
7600Sstevel@tonic-gate 				/* the event of an interrupt during write   */
7610Sstevel@tonic-gate 	a1 = addr1;
7620Sstevel@tonic-gate 	clrstats();
7630Sstevel@tonic-gate 	cntln = addr2 - a1 + 1;
7640Sstevel@tonic-gate 	nib = BUFSIZE;
7650Sstevel@tonic-gate 	fp = genbuf;
7660Sstevel@tonic-gate 	do {
767*13093SRoger.Faulkner@Oracle.COM 		getaline(*a1++);
7680Sstevel@tonic-gate 		lp = linebuf;
7690Sstevel@tonic-gate 		for (;;) {
7700Sstevel@tonic-gate 			if (--nib < 0) {
7710Sstevel@tonic-gate 				nib = fp - genbuf;
7720Sstevel@tonic-gate                 		if(kflag && !isfilter)
7730Sstevel@tonic-gate                                         if (run_crypt(cntch, genbuf, nib, perm) == -1)
7740Sstevel@tonic-gate 					  wrerror();
7750Sstevel@tonic-gate 				if (write(io, genbuf, nib) != nib) {
776802Scf46844 				    messagep = (char *)gettext(
777802Scf46844 					"\r\nYour file has been preserved\r\n");
778802Scf46844 				    (void) preserve();
779802Scf46844 				    write(1, messagep, strlen(messagep));
7800Sstevel@tonic-gate 
781802Scf46844 				    if (!isfilter)
782802Scf46844 					wrerror();
783802Scf46844 				    return;
7840Sstevel@tonic-gate 				}
7850Sstevel@tonic-gate 				cntch += nib;
7860Sstevel@tonic-gate 				nib = BUFSIZE - 1;
7870Sstevel@tonic-gate 				fp = genbuf;
7880Sstevel@tonic-gate 			}
7890Sstevel@tonic-gate 			if ((*fp++ = *lp++) == 0) {
7900Sstevel@tonic-gate 				fp[-1] = '\n';
7910Sstevel@tonic-gate 				break;
7920Sstevel@tonic-gate 			}
7930Sstevel@tonic-gate 		}
7940Sstevel@tonic-gate 	} while (a1 <= addr2);
7950Sstevel@tonic-gate 	nib = fp - genbuf;
7960Sstevel@tonic-gate 	if(kflag && !isfilter)
7970Sstevel@tonic-gate 		if (run_crypt(cntch, genbuf, nib, perm) == -1)
7980Sstevel@tonic-gate 			wrerror();
7990Sstevel@tonic-gate 	if ((cntch == 0) && (nib == 1)) {
8000Sstevel@tonic-gate 		cntln = 0;
8010Sstevel@tonic-gate 		return;
8020Sstevel@tonic-gate 	}
8030Sstevel@tonic-gate 	if (write(io, genbuf, nib) != nib) {
804802Scf46844 		messagep = (char *)gettext(
805802Scf46844 		    "\r\nYour file has been preserved\r\n");
806802Scf46844 		(void) preserve();
807802Scf46844 		write(1, messagep, strlen(messagep));
8080Sstevel@tonic-gate 
8090Sstevel@tonic-gate 		if(!isfilter)
8100Sstevel@tonic-gate 			wrerror();
8110Sstevel@tonic-gate 		return;
8120Sstevel@tonic-gate 	}
8130Sstevel@tonic-gate 	cntch += nib;
8140Sstevel@tonic-gate 	chng = ochng;			/* reset chng to original value */
8150Sstevel@tonic-gate }
8160Sstevel@tonic-gate 
8170Sstevel@tonic-gate /*
8180Sstevel@tonic-gate  * A write error has occurred;  if the file being written was
8190Sstevel@tonic-gate  * the edited file then we consider it to have changed since it is
8200Sstevel@tonic-gate  * now likely scrambled.
8210Sstevel@tonic-gate  */
822802Scf46844 void
wrerror(void)823802Scf46844 wrerror(void)
8240Sstevel@tonic-gate {
8250Sstevel@tonic-gate 
8260Sstevel@tonic-gate 	if (eq(file, savedfile) && edited)
8270Sstevel@tonic-gate 		change();
8280Sstevel@tonic-gate 	syserror(1);
8290Sstevel@tonic-gate }
8300Sstevel@tonic-gate 
8310Sstevel@tonic-gate /*
8320Sstevel@tonic-gate  * Source command, handles nested sources.
8330Sstevel@tonic-gate  * Traps errors since it mungs unit 0 during the source.
8340Sstevel@tonic-gate  */
8350Sstevel@tonic-gate short slevel;
8360Sstevel@tonic-gate short ttyindes;
8370Sstevel@tonic-gate 
838802Scf46844 void
source(fil,okfail)8390Sstevel@tonic-gate source(fil, okfail)
8400Sstevel@tonic-gate 	unsigned char *fil;
8410Sstevel@tonic-gate 	bool okfail;
8420Sstevel@tonic-gate {
8430Sstevel@tonic-gate 	jmp_buf osetexit;
844802Scf46844 	int saveinp, ointty, oerrno;
8450Sstevel@tonic-gate 	unsigned char *saveglobp;
8460Sstevel@tonic-gate 	short savepeekc;
8470Sstevel@tonic-gate 
8480Sstevel@tonic-gate 	signal(SIGINT, SIG_IGN);
8490Sstevel@tonic-gate 	saveinp = dup(0);
8500Sstevel@tonic-gate 	savepeekc = peekc;
8510Sstevel@tonic-gate 	saveglobp = globp;
8520Sstevel@tonic-gate 	peekc = 0; globp = 0;
8530Sstevel@tonic-gate 	if (saveinp < 0)
8540Sstevel@tonic-gate 		error(gettext("Too many nested sources"));
8550Sstevel@tonic-gate 	if (slevel <= 0)
8560Sstevel@tonic-gate 		ttyindes = saveinp;
8570Sstevel@tonic-gate 	close(0);
8580Sstevel@tonic-gate 	if (open(fil, 0) < 0) {
8590Sstevel@tonic-gate 		oerrno = errno;
8600Sstevel@tonic-gate 		setrupt();
8610Sstevel@tonic-gate 		dup(saveinp);
8620Sstevel@tonic-gate 		close(saveinp);
8630Sstevel@tonic-gate 		errno = oerrno;
8640Sstevel@tonic-gate 		if (!okfail)
8650Sstevel@tonic-gate 			filioerr(fil);
8660Sstevel@tonic-gate 		return;
8670Sstevel@tonic-gate 	}
8680Sstevel@tonic-gate 	slevel++;
8690Sstevel@tonic-gate 	ointty = intty;
8700Sstevel@tonic-gate 	intty = isatty(0);
8710Sstevel@tonic-gate 	oprompt = value(vi_PROMPT);
8720Sstevel@tonic-gate 	value(vi_PROMPT) &= intty;
8730Sstevel@tonic-gate 	getexit(osetexit);
8740Sstevel@tonic-gate 	setrupt();
8750Sstevel@tonic-gate 	if (setexit() == 0)
8760Sstevel@tonic-gate 		commands(1, 1);
8770Sstevel@tonic-gate 	else if (slevel > 1) {
8780Sstevel@tonic-gate 		close(0);
8790Sstevel@tonic-gate 		dup(saveinp);
8800Sstevel@tonic-gate 		close(saveinp);
8810Sstevel@tonic-gate 		slevel--;
8820Sstevel@tonic-gate 		resexit(osetexit);
8830Sstevel@tonic-gate 		reset();
8840Sstevel@tonic-gate 	}
8850Sstevel@tonic-gate 	intty = ointty;
8860Sstevel@tonic-gate 	value(vi_PROMPT) = oprompt;
8870Sstevel@tonic-gate 	close(0);
8880Sstevel@tonic-gate 	dup(saveinp);
8890Sstevel@tonic-gate 	close(saveinp);
8900Sstevel@tonic-gate 	globp = saveglobp;
8910Sstevel@tonic-gate 	peekc = savepeekc;
8920Sstevel@tonic-gate 	slevel--;
8930Sstevel@tonic-gate 	resexit(osetexit);
8940Sstevel@tonic-gate }
8950Sstevel@tonic-gate 
8960Sstevel@tonic-gate /*
8970Sstevel@tonic-gate  * Clear io statistics before a read or write.
8980Sstevel@tonic-gate  */
899802Scf46844 void
clrstats(void)900802Scf46844 clrstats(void)
9010Sstevel@tonic-gate {
9020Sstevel@tonic-gate 
9030Sstevel@tonic-gate 	ninbuf = 0;
9040Sstevel@tonic-gate 	cntch = 0;
9050Sstevel@tonic-gate 	cntln = 0;
9060Sstevel@tonic-gate 	cntnull = 0;
9070Sstevel@tonic-gate 	cntodd = 0;
9080Sstevel@tonic-gate }
9090Sstevel@tonic-gate 
9100Sstevel@tonic-gate /*
9110Sstevel@tonic-gate  * Io is finished, close the unit and print statistics.
9120Sstevel@tonic-gate  */
913802Scf46844 int
iostats(void)914802Scf46844 iostats(void)
9150Sstevel@tonic-gate {
9160Sstevel@tonic-gate 
9170Sstevel@tonic-gate 	close(io);
9180Sstevel@tonic-gate 	io = -1;
9190Sstevel@tonic-gate 	if (hush == 0) {
9200Sstevel@tonic-gate 		if (value(vi_TERSE))
921802Scf46844 			viprintf(" %d/%D", cntln, cntch);
9220Sstevel@tonic-gate 		else if (cntln == 1 && cntch == 1) {
923802Scf46844 			viprintf(gettext(" 1 line, 1 character"));
9240Sstevel@tonic-gate 		} else if (cntln == 1 && cntch != 1) {
925802Scf46844 			viprintf(gettext(" 1 line, %D characters"), cntch);
9260Sstevel@tonic-gate 		} else if (cntln != 1 && cntch != 1) {
9270Sstevel@tonic-gate 			/*
9280Sstevel@tonic-gate 			 * TRANSLATION_NOTE
9290Sstevel@tonic-gate 			 *	Reference order of arguments must not
9300Sstevel@tonic-gate 			 *	be changed using '%digit$', since vi's
931802Scf46844 			 *	viprintf() does not support it.
9320Sstevel@tonic-gate 			 */
933802Scf46844 			viprintf(gettext(" %d lines, %D characters"), cntln,
9340Sstevel@tonic-gate 			    cntch);
9350Sstevel@tonic-gate 		} else {
9360Sstevel@tonic-gate 			/* ridiculous */
937802Scf46844 			viprintf(gettext(" %d lines, 1 character"), cntln);
9380Sstevel@tonic-gate 		}
9390Sstevel@tonic-gate 		if (cntnull || cntodd) {
940802Scf46844 			viprintf(" (");
9410Sstevel@tonic-gate 			if (cntnull) {
942802Scf46844 				viprintf(gettext("%D null"), cntnull);
9430Sstevel@tonic-gate 				if (cntodd)
944802Scf46844 					viprintf(", ");
9450Sstevel@tonic-gate 			}
9460Sstevel@tonic-gate 			if (cntodd)
947802Scf46844 				viprintf(gettext("%D non-ASCII"), cntodd);
9480Sstevel@tonic-gate 			putchar(')');
9490Sstevel@tonic-gate 		}
9500Sstevel@tonic-gate 		noonl();
9510Sstevel@tonic-gate 		flush();
9520Sstevel@tonic-gate 	}
9530Sstevel@tonic-gate 	return (cntnull != 0 || cntodd != 0);
9540Sstevel@tonic-gate }
9550Sstevel@tonic-gate 
9560Sstevel@tonic-gate 
chkmdln(aline)9570Sstevel@tonic-gate static void chkmdln(aline)
9580Sstevel@tonic-gate unsigned char *aline;
9590Sstevel@tonic-gate {
9600Sstevel@tonic-gate 	unsigned char *beg, *end;
9610Sstevel@tonic-gate 	unsigned char cmdbuf[1024];
9620Sstevel@tonic-gate 	char *strchr(), *strrchr();
9630Sstevel@tonic-gate 	bool savetty;
9640Sstevel@tonic-gate 	int  savepeekc;
9650Sstevel@tonic-gate 	int  savechng;
9660Sstevel@tonic-gate 	unsigned char	*savefirstpat;
9670Sstevel@tonic-gate 	unsigned char	*p;
9680Sstevel@tonic-gate 	int	len;
9690Sstevel@tonic-gate 
9700Sstevel@tonic-gate 	beg = (unsigned char *)strchr((char *)aline, ':');
9710Sstevel@tonic-gate 	if (beg == NULL)
9720Sstevel@tonic-gate 		return;
9730Sstevel@tonic-gate 	if ((len = beg - aline) < 2)
9740Sstevel@tonic-gate 		return;
9750Sstevel@tonic-gate 
9760Sstevel@tonic-gate 	if ((beg - aline) != 2) {
9770Sstevel@tonic-gate 		if ((p = beg - ((unsigned int)MB_CUR_MAX * 2) - 2) < aline)
9780Sstevel@tonic-gate 			p = aline;
9790Sstevel@tonic-gate 		for ( ; p < (beg - 2); p += len) {
9800Sstevel@tonic-gate 			if ((len = mblen((char *)p, MB_CUR_MAX)) <= 0)
9810Sstevel@tonic-gate 				len = 1;
9820Sstevel@tonic-gate 		}
9830Sstevel@tonic-gate 		if (p !=  (beg - 2))
9840Sstevel@tonic-gate 			return;
9850Sstevel@tonic-gate 	}
9860Sstevel@tonic-gate 
9870Sstevel@tonic-gate 	if (!((beg[-2] == 'e' && p[-1] == 'x')
9880Sstevel@tonic-gate 	||    (beg[-2] == 'v' && beg[-1] == 'i')))
9890Sstevel@tonic-gate 	 	return;
9900Sstevel@tonic-gate 
9910Sstevel@tonic-gate 	strncpy(cmdbuf, beg+1, sizeof cmdbuf);
9920Sstevel@tonic-gate 	end = (unsigned char *)strrchr(cmdbuf, ':');
9930Sstevel@tonic-gate 	if (end == NULL)
9940Sstevel@tonic-gate 		return;
9950Sstevel@tonic-gate 	*end = 0;
9960Sstevel@tonic-gate 	globp = cmdbuf;
9970Sstevel@tonic-gate 	savepeekc = peekc;
9980Sstevel@tonic-gate 	peekc = 0;
9990Sstevel@tonic-gate 	savetty = intty;
10000Sstevel@tonic-gate 	intty = 0;
10010Sstevel@tonic-gate 	savechng = chng;
10020Sstevel@tonic-gate 	savefirstpat = firstpat;
10030Sstevel@tonic-gate 	firstpat = (unsigned char *)"";
10040Sstevel@tonic-gate 	commands(1, 1);
10050Sstevel@tonic-gate 	peekc = savepeekc;
10060Sstevel@tonic-gate 	globp = 0;
10070Sstevel@tonic-gate 	intty = savetty;
10080Sstevel@tonic-gate 	/* chng being increased indicates that text was changed */
10090Sstevel@tonic-gate 	if (savechng < chng)
10100Sstevel@tonic-gate 		laste = 0;
10110Sstevel@tonic-gate 	firstpat = savefirstpat;
10120Sstevel@tonic-gate }
1013