xref: /onnv-gate/usr/src/cmd/vi/port/ex.c (revision 597:6efbffba2e57)
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  */
220Sstevel@tonic-gate /*
230Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
280Sstevel@tonic-gate /*	  All Rights Reserved  	*/
290Sstevel@tonic-gate 
300Sstevel@tonic-gate /* Copyright (c) 1981 Regents of the University of California */
310Sstevel@tonic-gate 
320Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
330Sstevel@tonic-gate 
340Sstevel@tonic-gate #include "ex.h"
350Sstevel@tonic-gate #include "ex_argv.h"
360Sstevel@tonic-gate #include "ex_temp.h"
370Sstevel@tonic-gate #include "ex_tty.h"
380Sstevel@tonic-gate #include <stdlib.h>
390Sstevel@tonic-gate #include <locale.h>
400Sstevel@tonic-gate #include <stdio.h>
410Sstevel@tonic-gate #ifdef TRACE
420Sstevel@tonic-gate unsigned char	tttrace[BUFSIZ];
430Sstevel@tonic-gate #endif
440Sstevel@tonic-gate 
450Sstevel@tonic-gate #define	EQ(a, b)	(strcmp(a, b) == 0)
460Sstevel@tonic-gate 
470Sstevel@tonic-gate char	*strrchr();
480Sstevel@tonic-gate void	init_re(void);
490Sstevel@tonic-gate 
500Sstevel@tonic-gate /*
510Sstevel@tonic-gate  * The code for ex is divided as follows:
520Sstevel@tonic-gate  *
530Sstevel@tonic-gate  * ex.c			Entry point and routines handling interrupt, hangup
540Sstevel@tonic-gate  *			signals; initialization code.
550Sstevel@tonic-gate  *
560Sstevel@tonic-gate  * ex_addr.c		Address parsing routines for command mode decoding.
570Sstevel@tonic-gate  *			Routines to set and check address ranges on commands.
580Sstevel@tonic-gate  *
590Sstevel@tonic-gate  * ex_cmds.c		Command mode command decoding.
600Sstevel@tonic-gate  *
610Sstevel@tonic-gate  * ex_cmds2.c		Subroutines for command decoding and processing of
620Sstevel@tonic-gate  *			file names in the argument list.  Routines to print
630Sstevel@tonic-gate  *			messages and reset state when errors occur.
640Sstevel@tonic-gate  *
650Sstevel@tonic-gate  * ex_cmdsub.c		Subroutines which implement command mode functions
660Sstevel@tonic-gate  *			such as append, delete, join.
670Sstevel@tonic-gate  *
680Sstevel@tonic-gate  * ex_data.c		Initialization of options.
690Sstevel@tonic-gate  *
700Sstevel@tonic-gate  * ex_get.c		Command mode input routines.
710Sstevel@tonic-gate  *
720Sstevel@tonic-gate  * ex_io.c		General input/output processing: file i/o, unix
730Sstevel@tonic-gate  *			escapes, filtering, source commands, preserving
740Sstevel@tonic-gate  *			and recovering.
750Sstevel@tonic-gate  *
760Sstevel@tonic-gate  * ex_put.c		Terminal driving and optimizing routines for low-level
770Sstevel@tonic-gate  *			output (cursor-positioning); output line formatting
780Sstevel@tonic-gate  *			routines.
790Sstevel@tonic-gate  *
800Sstevel@tonic-gate  * ex_re.c		Global commands, substitute, regular expression
810Sstevel@tonic-gate  *			compilation and execution.
820Sstevel@tonic-gate  *
830Sstevel@tonic-gate  * ex_set.c		The set command.
840Sstevel@tonic-gate  *
850Sstevel@tonic-gate  * ex_subr.c		Loads of miscellaneous subroutines.
860Sstevel@tonic-gate  *
870Sstevel@tonic-gate  * ex_temp.c		Editor buffer routines for main buffer and also
880Sstevel@tonic-gate  *			for named buffers (Q registers if you will.)
890Sstevel@tonic-gate  *
900Sstevel@tonic-gate  * ex_tty.c		Terminal dependent initializations from termcap
910Sstevel@tonic-gate  *			data base, grabbing of tty modes (at beginning
920Sstevel@tonic-gate  *			and after escapes).
930Sstevel@tonic-gate  *
940Sstevel@tonic-gate  * ex_unix.c		Routines for the ! command and its variations.
950Sstevel@tonic-gate  *
960Sstevel@tonic-gate  * ex_v*.c		Visual/open mode routines... see ex_v.c for a
970Sstevel@tonic-gate  *			guide to the overall organization.
980Sstevel@tonic-gate  */
990Sstevel@tonic-gate 
1000Sstevel@tonic-gate /*
1010Sstevel@tonic-gate  * This sets the Version of ex/vi for both the exstrings file and
1020Sstevel@tonic-gate  * the version command (":ver").
1030Sstevel@tonic-gate  */
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate /* variable used by ":ver" command */
1060Sstevel@tonic-gate unsigned char *Version = (unsigned char *)"Version SVR4.0, Solaris 2.5.0";
1070Sstevel@tonic-gate 
1080Sstevel@tonic-gate /*
1090Sstevel@tonic-gate  * NOTE: when changing the Version number, it must be changed in the
1100Sstevel@tonic-gate  *	 following files:
1110Sstevel@tonic-gate  *
1120Sstevel@tonic-gate  *			  port/READ_ME
1130Sstevel@tonic-gate  *			  port/ex.c
1140Sstevel@tonic-gate  *			  port/ex.news
1150Sstevel@tonic-gate  *
1160Sstevel@tonic-gate  */
1170Sstevel@tonic-gate #ifdef XPG4
1180Sstevel@tonic-gate unsigned char *savepat = (unsigned char *) NULL; /* firstpat storage	*/
1190Sstevel@tonic-gate #endif /* XPG4 */
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate /*
1220Sstevel@tonic-gate  * Main procedure.  Process arguments and then
1230Sstevel@tonic-gate  * transfer control to the main command processing loop
1240Sstevel@tonic-gate  * in the routine commands.  We are entered as either "ex", "edit", "vi"
1250Sstevel@tonic-gate  * or "view" and the distinction is made here. For edit we just diddle options;
1260Sstevel@tonic-gate  * for vi we actually force an early visual command.
1270Sstevel@tonic-gate  */
1280Sstevel@tonic-gate static unsigned char cryptkey[19]; /* contents of encryption key */
1290Sstevel@tonic-gate 
1300Sstevel@tonic-gate static void usage(unsigned char *);
1310Sstevel@tonic-gate 
1320Sstevel@tonic-gate static int validate_exrc(unsigned char *);
1330Sstevel@tonic-gate 
1340Sstevel@tonic-gate int
1350Sstevel@tonic-gate main(int ac, unsigned char *av[])
1360Sstevel@tonic-gate {
1370Sstevel@tonic-gate 	extern 	char 	*optarg;
1380Sstevel@tonic-gate 	extern 	int	optind;
1390Sstevel@tonic-gate 	unsigned char	*rcvname = 0;
1400Sstevel@tonic-gate 	unsigned char *cp;
1410Sstevel@tonic-gate 	int c;
1420Sstevel@tonic-gate 	unsigned char	*cmdnam;
1430Sstevel@tonic-gate 	bool recov = 0;
1440Sstevel@tonic-gate 	bool ivis = 0;
1450Sstevel@tonic-gate 	bool itag = 0;
1460Sstevel@tonic-gate 	bool fast = 0;
1470Sstevel@tonic-gate 	extern int verbose;
1480Sstevel@tonic-gate 	int argcounter = 0;
1490Sstevel@tonic-gate 	extern int tags_flag;	/* Set if tag file is not sorted (-S flag) */
1500Sstevel@tonic-gate 	unsigned char scratch [PATH_MAX+1];   /* temp for sourcing rc file(s) */
1510Sstevel@tonic-gate 	int vret = 0;
1520Sstevel@tonic-gate 	unsigned char exrcpath [PATH_MAX+1]; /* temp for sourcing rc file(s) */
1530Sstevel@tonic-gate 	int toptseen = 0;
1540Sstevel@tonic-gate #ifdef TRACE
1550Sstevel@tonic-gate 	unsigned char *tracef;
1560Sstevel@tonic-gate #endif
1570Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
1580Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)
1590Sstevel@tonic-gate #define	TEXT_DOMAIN "SYS_TEST"
1600Sstevel@tonic-gate #endif
1610Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
1620Sstevel@tonic-gate 
1630Sstevel@tonic-gate 	/*
1640Sstevel@tonic-gate 	 * Immediately grab the tty modes so that we won't
1650Sstevel@tonic-gate 	 * get messed up if an interrupt comes in quickly.
1660Sstevel@tonic-gate 	 */
1670Sstevel@tonic-gate 	gTTY(2);
1680Sstevel@tonic-gate 	normf = tty;
1690Sstevel@tonic-gate 	ppid = getpid();
1700Sstevel@tonic-gate 	/* Note - this will core dump if you didn't -DSINGLE in CFLAGS */
1710Sstevel@tonic-gate 	lines = 24;
1720Sstevel@tonic-gate 	columns = 80;	/* until defined right by setupterm */
1730Sstevel@tonic-gate 	/*
1740Sstevel@tonic-gate 	 * Defend against d's, v's, w's, and a's in directories of
1750Sstevel@tonic-gate 	 * path leading to our true name.
1760Sstevel@tonic-gate 	 */
1770Sstevel@tonic-gate 	if ((cmdnam = (unsigned char *)strrchr(av[0], '/')) != 0)
1780Sstevel@tonic-gate 		cmdnam++;
1790Sstevel@tonic-gate 	else
1800Sstevel@tonic-gate 		cmdnam = av[0];
1810Sstevel@tonic-gate 
1820Sstevel@tonic-gate 	if (EQ(cmdnam, "vi"))
1830Sstevel@tonic-gate 		ivis = 1;
1840Sstevel@tonic-gate 	else if (EQ(cmdnam, "view")) {
1850Sstevel@tonic-gate 		ivis = 1;
1860Sstevel@tonic-gate 		value(vi_READONLY) = 1;
1870Sstevel@tonic-gate 	} else if (EQ(cmdnam, "vedit")) {
1880Sstevel@tonic-gate 		ivis = 1;
1890Sstevel@tonic-gate 		value(vi_NOVICE) = 1;
1900Sstevel@tonic-gate 		value(vi_REPORT) = 1;
1910Sstevel@tonic-gate 		value(vi_MAGIC) = 0;
1920Sstevel@tonic-gate 		value(vi_SHOWMODE) = 1;
1930Sstevel@tonic-gate 	} else if (EQ(cmdnam, "edit")) {
1940Sstevel@tonic-gate 		value(vi_NOVICE) = 1;
1950Sstevel@tonic-gate 		value(vi_REPORT) = 1;
1960Sstevel@tonic-gate 		value(vi_MAGIC) = 0;
1970Sstevel@tonic-gate 		value(vi_SHOWMODE) = 1;
1980Sstevel@tonic-gate 	}
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate #ifdef	XPG4
2010Sstevel@tonic-gate 	{
2020Sstevel@tonic-gate 		struct winsize jwin;
2030Sstevel@tonic-gate 		char *envptr;
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate 		envlines = envcolumns = -1;
2060Sstevel@tonic-gate 		oldlines = oldcolumns = -1;
2070Sstevel@tonic-gate 
2080Sstevel@tonic-gate 		if (ioctl(0, TIOCGWINSZ, &jwin) != -1) {
2090Sstevel@tonic-gate 			oldlines = jwin.ws_row;
2100Sstevel@tonic-gate 			oldcolumns = jwin.ws_col;
2110Sstevel@tonic-gate 		}
2120Sstevel@tonic-gate 
2130Sstevel@tonic-gate 		if ((envptr = getenv("LINES")) != NULL &&
2140Sstevel@tonic-gate 		    *envptr != '\0' && isdigit(*envptr)) {
2150Sstevel@tonic-gate 			if ((envlines = atoi(envptr)) <= 0) {
2160Sstevel@tonic-gate 				envlines = -1;
2170Sstevel@tonic-gate 			}
2180Sstevel@tonic-gate 		}
2190Sstevel@tonic-gate 
2200Sstevel@tonic-gate 		if ((envptr = getenv("COLUMNS")) != NULL &&
2210Sstevel@tonic-gate 		    *envptr != '\0' && isdigit(*envptr)) {
2220Sstevel@tonic-gate 			if ((envcolumns = atoi(envptr)) <= 0) {
2230Sstevel@tonic-gate 				envcolumns = -1;
2240Sstevel@tonic-gate 			}
2250Sstevel@tonic-gate 		}
2260Sstevel@tonic-gate 	}
2270Sstevel@tonic-gate #endif /* XPG4 */
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate 	draino();
2300Sstevel@tonic-gate 	pstop();
2310Sstevel@tonic-gate 
2320Sstevel@tonic-gate 	/*
2330Sstevel@tonic-gate 	 * Initialize interrupt handling.
2340Sstevel@tonic-gate 	 */
2350Sstevel@tonic-gate 	oldhup = signal(SIGHUP, SIG_IGN);
2360Sstevel@tonic-gate 	if (oldhup == SIG_DFL)
2370Sstevel@tonic-gate 		signal(SIGHUP, onhup);
2380Sstevel@tonic-gate 	oldquit = signal(SIGQUIT, SIG_IGN);
2390Sstevel@tonic-gate 	ruptible = signal(SIGINT, SIG_IGN) == SIG_DFL;
2400Sstevel@tonic-gate 	if (signal(SIGTERM, SIG_IGN) == SIG_DFL)
2410Sstevel@tonic-gate 		signal(SIGTERM, onhup);
2420Sstevel@tonic-gate 	if (signal(SIGEMT, SIG_IGN) == SIG_DFL)
2430Sstevel@tonic-gate 		signal(SIGEMT, onemt);
2440Sstevel@tonic-gate 	signal(SIGILL, oncore);
2450Sstevel@tonic-gate 	signal(SIGTRAP, oncore);
2460Sstevel@tonic-gate 	signal(SIGIOT, oncore);
2470Sstevel@tonic-gate 	signal(SIGFPE, oncore);
2480Sstevel@tonic-gate 	signal(SIGBUS, oncore);
2490Sstevel@tonic-gate 	signal(SIGSEGV, oncore);
2500Sstevel@tonic-gate 	signal(SIGPIPE, oncore);
2510Sstevel@tonic-gate 	init_re();
2520Sstevel@tonic-gate 	while (1) {
2530Sstevel@tonic-gate #ifdef TRACE
2540Sstevel@tonic-gate 		while ((c = getopt(ac, (char **)av, "VU:Lc:Tvt:rlw:xRCsS")) !=
2550Sstevel@tonic-gate 		    EOF)
2560Sstevel@tonic-gate #else
2570Sstevel@tonic-gate 		while ((c = getopt(ac, (char **)av,
258*597Sceastha 		    "VLc:vt:rlw:xRCsS")) != EOF)
2590Sstevel@tonic-gate #endif
2600Sstevel@tonic-gate 			switch (c) {
2610Sstevel@tonic-gate 			case 's':
2620Sstevel@tonic-gate 				hush = 1;
2630Sstevel@tonic-gate 				value(vi_AUTOPRINT) = 0;
2640Sstevel@tonic-gate 				fast++;
2650Sstevel@tonic-gate 				break;
2660Sstevel@tonic-gate 
2670Sstevel@tonic-gate 			case 'R':
2680Sstevel@tonic-gate 				value(vi_READONLY) = 1;
2690Sstevel@tonic-gate 				break;
2700Sstevel@tonic-gate 			case 'S':
2710Sstevel@tonic-gate 				tags_flag = 1;
2720Sstevel@tonic-gate 				break;
2730Sstevel@tonic-gate #ifdef TRACE
2740Sstevel@tonic-gate 			case 'T':
2750Sstevel@tonic-gate 				tracef = (unsigned char *)"trace";
2760Sstevel@tonic-gate 				goto trace;
2770Sstevel@tonic-gate 
2780Sstevel@tonic-gate 			case 'U':
2790Sstevel@tonic-gate 				tracef = tttrace;
2800Sstevel@tonic-gate 				strcpy(tracef, optarg);
2810Sstevel@tonic-gate 		trace:
2820Sstevel@tonic-gate 				trace = fopen((char *)tracef, "w");
2830Sstevel@tonic-gate #define	tracbuf	NULL
2840Sstevel@tonic-gate 				if (trace == NULL)
2850Sstevel@tonic-gate 					printf("Trace create error\n");
2860Sstevel@tonic-gate 				else
2870Sstevel@tonic-gate 					setbuf(trace, (char *)tracbuf);
2880Sstevel@tonic-gate 				break;
2890Sstevel@tonic-gate #endif
2900Sstevel@tonic-gate 			case 'c':
2910Sstevel@tonic-gate 				if (optarg != NULL)
2920Sstevel@tonic-gate 					firstpat = (unsigned char *)optarg;
2930Sstevel@tonic-gate 				else
2940Sstevel@tonic-gate 					firstpat = (unsigned char *)"";
2950Sstevel@tonic-gate 				break;
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate 			case 'l':
2980Sstevel@tonic-gate 				value(vi_LISP) = 1;
2990Sstevel@tonic-gate 				value(vi_SHOWMATCH) = 1;
3000Sstevel@tonic-gate 				break;
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 			case 'r':
3030Sstevel@tonic-gate 				if (av[optind] && (c = av[optind][0]) &&
3040Sstevel@tonic-gate 				    c != '-') {
3050Sstevel@tonic-gate 					if ((strlen(av[optind])) >=
3060Sstevel@tonic-gate 					    sizeof (savedfile)) {
3070Sstevel@tonic-gate 						(void) fprintf(stderr, gettext(
308*597Sceastha 						    "Recovered file name"
309*597Sceastha 						    " too long\n"));
3100Sstevel@tonic-gate 						exit(1);
3110Sstevel@tonic-gate 					}
3120Sstevel@tonic-gate 
3130Sstevel@tonic-gate 					rcvname = (unsigned char *)av[optind];
3140Sstevel@tonic-gate 					optind++;
3150Sstevel@tonic-gate 				}
3160Sstevel@tonic-gate 
3170Sstevel@tonic-gate 			case 'L':
3180Sstevel@tonic-gate 				recov++;
3190Sstevel@tonic-gate 				break;
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate 			case 'V':
3220Sstevel@tonic-gate 				verbose = 1;
3230Sstevel@tonic-gate 				break;
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 			case 't':
3260Sstevel@tonic-gate 				if (toptseen) {
3270Sstevel@tonic-gate 					usage(cmdnam);
3280Sstevel@tonic-gate 					exit(1);
3290Sstevel@tonic-gate 				} else {
3300Sstevel@tonic-gate 					toptseen++;
3310Sstevel@tonic-gate 				}
3320Sstevel@tonic-gate 				itag = 1;
3330Sstevel@tonic-gate 				if (strlcpy(lasttag, optarg,
3340Sstevel@tonic-gate 				    sizeof (lasttag)) >= sizeof (lasttag)) {
3350Sstevel@tonic-gate 					(void) fprintf(stderr, gettext("Tag"
3360Sstevel@tonic-gate 					    " file name too long\n"));
3370Sstevel@tonic-gate 					exit(1);
3380Sstevel@tonic-gate 				}
3390Sstevel@tonic-gate 				break;
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate 			case 'w':
3420Sstevel@tonic-gate 				defwind = 0;
3430Sstevel@tonic-gate 				if (optarg[0] == NULL)
3440Sstevel@tonic-gate 					defwind = 3;
3450Sstevel@tonic-gate 				else for (cp = (unsigned char *)optarg;
3460Sstevel@tonic-gate 				    isdigit(*cp); cp++)
3470Sstevel@tonic-gate 					defwind = 10*defwind + *cp - '0';
3480Sstevel@tonic-gate 				if (defwind < 0)
3490Sstevel@tonic-gate 					defwind = 3;
3500Sstevel@tonic-gate 				break;
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate 			case 'C':
3530Sstevel@tonic-gate 				crflag = 1;
3540Sstevel@tonic-gate 				xflag = 1;
3550Sstevel@tonic-gate 				break;
3560Sstevel@tonic-gate 
3570Sstevel@tonic-gate 			case 'x':
3580Sstevel@tonic-gate 				/* encrypted mode */
3590Sstevel@tonic-gate 				xflag = 1;
3600Sstevel@tonic-gate 				crflag = -1;
3610Sstevel@tonic-gate 				break;
3620Sstevel@tonic-gate 
3630Sstevel@tonic-gate 			case 'v':
3640Sstevel@tonic-gate 				ivis = 1;
3650Sstevel@tonic-gate 				break;
3660Sstevel@tonic-gate 
3670Sstevel@tonic-gate 			default:
3680Sstevel@tonic-gate 				usage(cmdnam);
3690Sstevel@tonic-gate 				exit(1);
3700Sstevel@tonic-gate 			}
3710Sstevel@tonic-gate 		if (av[optind] && av[optind][0] == '+' &&
3720Sstevel@tonic-gate 		    av[optind-1] && strcmp(av[optind-1], "--")) {
3730Sstevel@tonic-gate 				firstpat = &av[optind][1];
3740Sstevel@tonic-gate 				optind++;
3750Sstevel@tonic-gate 				continue;
3760Sstevel@tonic-gate 		} else if (av[optind] && av[optind][0] == '-' &&
3770Sstevel@tonic-gate 		    av[optind-1] && strcmp(av[optind-1], "--")) {
3780Sstevel@tonic-gate 			hush = 1;
3790Sstevel@tonic-gate 			value(vi_AUTOPRINT) = 0;
3800Sstevel@tonic-gate 			fast++;
3810Sstevel@tonic-gate 			optind++;
3820Sstevel@tonic-gate 			continue;
3830Sstevel@tonic-gate 		}
3840Sstevel@tonic-gate 		break;
3850Sstevel@tonic-gate 	}
3860Sstevel@tonic-gate 
387*597Sceastha 	if (isatty(0) == 0) {
388*597Sceastha 		/*
389*597Sceastha 		 * If -V option is set and input is coming in via
390*597Sceastha 		 * stdin then vi behavior should be ignored. The vi
391*597Sceastha 		 * command should act like ex and only process ex commands
392*597Sceastha 		 * and echo the input ex commands to stderr
393*597Sceastha 		 */
394*597Sceastha 		if (verbose == 1) {
395*597Sceastha 			ivis = 0;
396*597Sceastha 		}
397*597Sceastha 
398*597Sceastha 		/*
399*597Sceastha 		 * If the standard input is not a terminal device,
400*597Sceastha 		 * it is as if the -s option has been specified.
401*597Sceastha 		 */
402*597Sceastha 		if (ivis == 0) {
403*597Sceastha 			hush = 1;
404*597Sceastha 			value(vi_AUTOPRINT) = 0;
405*597Sceastha 			fast++;
406*597Sceastha 		}
4070Sstevel@tonic-gate 	}
4080Sstevel@tonic-gate 
4090Sstevel@tonic-gate 	ac -= optind;
4100Sstevel@tonic-gate 	av  = &av[optind];
4110Sstevel@tonic-gate 
4120Sstevel@tonic-gate 	for (argcounter = 0; argcounter < ac; argcounter++) {
4130Sstevel@tonic-gate 		if ((strlen(av[argcounter])) >= sizeof (savedfile)) {
4140Sstevel@tonic-gate 			(void) fprintf(stderr, gettext("File argument"
415*597Sceastha 			    " too long\n"));
4160Sstevel@tonic-gate 			exit(1);
4170Sstevel@tonic-gate 		}
4180Sstevel@tonic-gate 	}
4190Sstevel@tonic-gate 
4200Sstevel@tonic-gate #ifdef SIGTSTP
4210Sstevel@tonic-gate 	if (!hush && signal(SIGTSTP, SIG_IGN) == SIG_DFL)
4220Sstevel@tonic-gate 		signal(SIGTSTP, onsusp), dosusp++;
4230Sstevel@tonic-gate #endif
4240Sstevel@tonic-gate 
4250Sstevel@tonic-gate 	if (xflag) {
4260Sstevel@tonic-gate 		permflag = 1;
4270Sstevel@tonic-gate 		if ((kflag = run_setkey(perm,
4280Sstevel@tonic-gate 		    (key = (unsigned char *)getpass(
4290Sstevel@tonic-gate 		    gettext("Enter key:"))))) == -1) {
4300Sstevel@tonic-gate 			kflag = 0;
4310Sstevel@tonic-gate 			xflag = 0;
4320Sstevel@tonic-gate 			smerror(gettext("Encryption facility not available\n"));
4330Sstevel@tonic-gate 		}
4340Sstevel@tonic-gate 		if (kflag == 0)
4350Sstevel@tonic-gate 			crflag = 0;
4360Sstevel@tonic-gate 		else {
4370Sstevel@tonic-gate 			strcpy(cryptkey, "CrYpTkEy=XXXXXXXXX");
4380Sstevel@tonic-gate 			strcpy(cryptkey + 9, key);
4390Sstevel@tonic-gate 			if (putenv((char *)cryptkey) != 0)
4400Sstevel@tonic-gate 			smerror(gettext(" Cannot copy key to environment"));
4410Sstevel@tonic-gate 		}
4420Sstevel@tonic-gate 
4430Sstevel@tonic-gate 	}
4440Sstevel@tonic-gate #ifndef PRESUNEUC
4450Sstevel@tonic-gate 	/*
4460Sstevel@tonic-gate 	 * Perform locale-specific initialization
4470Sstevel@tonic-gate 	 */
4480Sstevel@tonic-gate 	(void) localize();
4490Sstevel@tonic-gate #endif /* PRESUNEUC */
4500Sstevel@tonic-gate 
4510Sstevel@tonic-gate 	/*
4520Sstevel@tonic-gate 	 * Initialize end of core pointers.
4530Sstevel@tonic-gate 	 * Normally we avoid breaking back to fendcore after each
4540Sstevel@tonic-gate 	 * file since this can be expensive (much core-core copying).
4550Sstevel@tonic-gate 	 * If your system can scatter load processes you could do
4560Sstevel@tonic-gate 	 * this as ed does, saving a little core, but it will probably
4570Sstevel@tonic-gate 	 * not often make much difference.
4580Sstevel@tonic-gate 	 */
4590Sstevel@tonic-gate 	fendcore = (line *) sbrk(0);
4600Sstevel@tonic-gate 	endcore = fendcore - 2;
4610Sstevel@tonic-gate 
4620Sstevel@tonic-gate 	/*
4630Sstevel@tonic-gate 	 * If we are doing a recover and no filename
4640Sstevel@tonic-gate 	 * was given, then execute an exrecover command with
4650Sstevel@tonic-gate 	 * the -r option to type out the list of saved file names.
4660Sstevel@tonic-gate 	 * Otherwise set the remembered file name to the first argument
4670Sstevel@tonic-gate 	 * file name so the "recover" initial command will find it.
4680Sstevel@tonic-gate 	 */
4690Sstevel@tonic-gate 	if (recov) {
4700Sstevel@tonic-gate 		if (ac == 0 && (rcvname == NULL || *rcvname == NULL)) {
4710Sstevel@tonic-gate 			ppid = 0;
4720Sstevel@tonic-gate 			setrupt();
4730Sstevel@tonic-gate 			execlp(EXRECOVER, "exrecover", "-r", (char *)0);
4740Sstevel@tonic-gate 			filioerr(EXRECOVER);
4750Sstevel@tonic-gate 			exit(++errcnt);
4760Sstevel@tonic-gate 		}
4770Sstevel@tonic-gate 		if (rcvname && *rcvname)
4780Sstevel@tonic-gate 			(void) strlcpy(savedfile, rcvname, sizeof (savedfile));
4790Sstevel@tonic-gate 		else {
4800Sstevel@tonic-gate 			(void) strlcpy(savedfile, *av++, sizeof (savedfile));
4810Sstevel@tonic-gate 			ac--;
4820Sstevel@tonic-gate 		}
4830Sstevel@tonic-gate 	}
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate 	/*
4860Sstevel@tonic-gate 	 * Initialize the argument list.
4870Sstevel@tonic-gate 	 */
4880Sstevel@tonic-gate 	argv0 = av;
4890Sstevel@tonic-gate 	argc0 = ac;
4900Sstevel@tonic-gate 	args0 = av[0];
4910Sstevel@tonic-gate 	erewind();
4920Sstevel@tonic-gate 
4930Sstevel@tonic-gate 	/*
4940Sstevel@tonic-gate 	 * Initialize a temporary file (buffer) and
4950Sstevel@tonic-gate 	 * set up terminal environment.  Read user startup commands.
4960Sstevel@tonic-gate 	 */
4970Sstevel@tonic-gate 	if (setexit() == 0) {
4980Sstevel@tonic-gate 		setrupt();
4990Sstevel@tonic-gate 		intty = isatty(0);
5000Sstevel@tonic-gate 		value(vi_PROMPT) = intty;
5010Sstevel@tonic-gate 		if (((cp = (unsigned char *)getenv("SHELL")) != NULL) &&
5020Sstevel@tonic-gate 		    (*cp != '\0')) {
5030Sstevel@tonic-gate 			if (strlen(cp) < sizeof (shell)) {
5040Sstevel@tonic-gate 				(void) strlcpy(shell, cp, sizeof (shell));
5050Sstevel@tonic-gate 			}
5060Sstevel@tonic-gate 		}
5070Sstevel@tonic-gate 		if (fast)
5080Sstevel@tonic-gate 			setterm("dumb");
5090Sstevel@tonic-gate 		else {
5100Sstevel@tonic-gate 			gettmode();
5110Sstevel@tonic-gate 			cp = (unsigned char *)getenv("TERM");
5120Sstevel@tonic-gate 			if (cp == NULL || *cp == '\0')
5130Sstevel@tonic-gate 				cp = (unsigned char *)"unknown";
5140Sstevel@tonic-gate 			setterm(cp);
5150Sstevel@tonic-gate 		}
5160Sstevel@tonic-gate 	}
5170Sstevel@tonic-gate 
5180Sstevel@tonic-gate 	/*
5190Sstevel@tonic-gate 	 * Bring up some code from init()
5200Sstevel@tonic-gate 	 * This is still done in init later. This
5210Sstevel@tonic-gate 	 * avoids null pointer problems
5220Sstevel@tonic-gate 	 */
5230Sstevel@tonic-gate 
5240Sstevel@tonic-gate 	dot = zero = truedol = unddol = dol = fendcore;
5250Sstevel@tonic-gate 	one = zero+1;
5260Sstevel@tonic-gate 	{
5270Sstevel@tonic-gate 	register int i;
5280Sstevel@tonic-gate 
5290Sstevel@tonic-gate 	for (i = 0; i <= 'z'-'a'+1; i++)
5300Sstevel@tonic-gate 		names[i] = 1;
5310Sstevel@tonic-gate 	}
5320Sstevel@tonic-gate 
5330Sstevel@tonic-gate 	if (setexit() == 0 && !fast) {
5340Sstevel@tonic-gate 		if ((globp =
535*597Sceastha 		    (unsigned char *) getenv("EXINIT")) && *globp) {
5360Sstevel@tonic-gate 			if (ivis)
5370Sstevel@tonic-gate 				inexrc = 1;
5380Sstevel@tonic-gate 			commands(1, 1);
5390Sstevel@tonic-gate 			inexrc = 0;
5400Sstevel@tonic-gate 		} else {
5410Sstevel@tonic-gate 			globp = 0;
5420Sstevel@tonic-gate 			if ((cp = (unsigned char *) getenv("HOME")) !=
5430Sstevel@tonic-gate 			    0 && *cp) {
5440Sstevel@tonic-gate 				strncpy(scratch, cp, sizeof (scratch) - 1);
5450Sstevel@tonic-gate 				strncat(scratch, "/.exrc",
5460Sstevel@tonic-gate 				    sizeof (scratch) - 1 - strlen(scratch));
5470Sstevel@tonic-gate 				if (ivis)
548*597Sceastha 					inexrc = 1;
5490Sstevel@tonic-gate 				if ((vret = validate_exrc(scratch)) == 0) {
5500Sstevel@tonic-gate 					source(scratch, 1);
5510Sstevel@tonic-gate 				} else {
5520Sstevel@tonic-gate 					if (vret == -1) {
5530Sstevel@tonic-gate 						error(gettext(
5540Sstevel@tonic-gate 						    "Not owner of .exrc "
5550Sstevel@tonic-gate 						    "or .exrc is group or "
5560Sstevel@tonic-gate 						    "world writable"));
5570Sstevel@tonic-gate 					}
5580Sstevel@tonic-gate 				}
5590Sstevel@tonic-gate 				inexrc = 0;
5600Sstevel@tonic-gate 			}
5610Sstevel@tonic-gate 		}
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate 		/*
5640Sstevel@tonic-gate 		 * Allow local .exrc if the "exrc" option was set. This
5650Sstevel@tonic-gate 		 * loses if . is $HOME, but nobody should notice unless
5660Sstevel@tonic-gate 		 * they do stupid things like putting a version command
5670Sstevel@tonic-gate 		 * in .exrc.
5680Sstevel@tonic-gate 		 * Besides, they should be using EXINIT, not .exrc, right?
5690Sstevel@tonic-gate 		 */
5700Sstevel@tonic-gate 
5710Sstevel@tonic-gate 		if (value(vi_EXRC)) {
5720Sstevel@tonic-gate 			if (ivis)
5730Sstevel@tonic-gate 				inexrc = 1;
5740Sstevel@tonic-gate 			if ((cp = (unsigned char *) getenv("PWD")) != 0 &&
5750Sstevel@tonic-gate 			    *cp) {
5760Sstevel@tonic-gate 				strncpy(exrcpath, cp, sizeof (exrcpath) - 1);
5770Sstevel@tonic-gate 				strncat(exrcpath, "/.exrc",
5780Sstevel@tonic-gate 				    sizeof (exrcpath) - 1 - strlen(exrcpath));
5790Sstevel@tonic-gate 				if (strcmp(scratch, exrcpath) != 0) {
5800Sstevel@tonic-gate 					if ((vret =
5810Sstevel@tonic-gate 					    validate_exrc(exrcpath)) == 0) {
5820Sstevel@tonic-gate 						source(exrcpath, 1);
5830Sstevel@tonic-gate 					} else {
5840Sstevel@tonic-gate 						if (vret == -1) {
5850Sstevel@tonic-gate 							error(gettext(
5860Sstevel@tonic-gate 							    "Not owner of "
5870Sstevel@tonic-gate 							    ".exrc or .exrc "
5880Sstevel@tonic-gate 							    "is group or world "
5890Sstevel@tonic-gate 							    "writable"));
5900Sstevel@tonic-gate 						}
5910Sstevel@tonic-gate 					}
5920Sstevel@tonic-gate 				}
5930Sstevel@tonic-gate 			}
5940Sstevel@tonic-gate 			inexrc = 0;
5950Sstevel@tonic-gate 		}
5960Sstevel@tonic-gate 	}
5970Sstevel@tonic-gate 
5980Sstevel@tonic-gate 	init();	/* moved after prev 2 chunks to fix directory option */
5990Sstevel@tonic-gate 
6000Sstevel@tonic-gate 	/*
6010Sstevel@tonic-gate 	 * Initial processing.  Handle tag, recover, and file argument
6020Sstevel@tonic-gate 	 * implied next commands.  If going in as 'vi', then don't do
6030Sstevel@tonic-gate 	 * anything, just set initev so we will do it later (from within
6040Sstevel@tonic-gate 	 * visual).
6050Sstevel@tonic-gate 	 */
6060Sstevel@tonic-gate 	if (setexit() == 0) {
6070Sstevel@tonic-gate 		if (recov)
6080Sstevel@tonic-gate 			globp = (unsigned char *)"recover";
6090Sstevel@tonic-gate 		else if (itag) {
6100Sstevel@tonic-gate 			globp = ivis ? (unsigned char *)"tag" :
6110Sstevel@tonic-gate 			    (unsigned char *)"tag|p";
6120Sstevel@tonic-gate #ifdef	XPG4
6130Sstevel@tonic-gate 			if (firstpat != NULL) {
6140Sstevel@tonic-gate 				/*
6150Sstevel@tonic-gate 				 * if the user specified the -t and -c
6160Sstevel@tonic-gate 				 * flags together, then we service these
6170Sstevel@tonic-gate 				 * commands here. -t is handled first.
6180Sstevel@tonic-gate 				 */
6190Sstevel@tonic-gate 				savepat = firstpat;
6200Sstevel@tonic-gate 				firstpat = NULL;
6210Sstevel@tonic-gate 				inglobal = 1;
6220Sstevel@tonic-gate 				commands(1, 1);
6230Sstevel@tonic-gate 
6240Sstevel@tonic-gate 				/* now handle the -c argument: */
6250Sstevel@tonic-gate 				globp = savepat;
6260Sstevel@tonic-gate 				commands(1, 1);
6270Sstevel@tonic-gate 				inglobal = 0;
6280Sstevel@tonic-gate 				globp = savepat = NULL;
6290Sstevel@tonic-gate 
6300Sstevel@tonic-gate 				/* the above isn't sufficient for ex mode: */
6310Sstevel@tonic-gate 				if (!ivis) {
6320Sstevel@tonic-gate 					setdot();
6330Sstevel@tonic-gate 					nonzero();
6340Sstevel@tonic-gate 					plines(addr1, addr2, 1);
6350Sstevel@tonic-gate 				}
6360Sstevel@tonic-gate 			}
6370Sstevel@tonic-gate #endif /* XPG4 */
6380Sstevel@tonic-gate 		} else if (argc)
6390Sstevel@tonic-gate 			globp = (unsigned char *)"next";
6400Sstevel@tonic-gate 		if (ivis)
6410Sstevel@tonic-gate 			initev = globp;
6420Sstevel@tonic-gate 		else if (globp) {
6430Sstevel@tonic-gate 			inglobal = 1;
6440Sstevel@tonic-gate 			commands(1, 1);
6450Sstevel@tonic-gate 			inglobal = 0;
6460Sstevel@tonic-gate 		}
6470Sstevel@tonic-gate 	}
6480Sstevel@tonic-gate 
6490Sstevel@tonic-gate 	/*
6500Sstevel@tonic-gate 	 * Vi command... go into visual.
6510Sstevel@tonic-gate 	 */
6520Sstevel@tonic-gate 	if (ivis) {
6530Sstevel@tonic-gate 		/*
6540Sstevel@tonic-gate 		 * Don't have to be upward compatible
6550Sstevel@tonic-gate 		 * by starting editing at line $.
6560Sstevel@tonic-gate 		 */
6570Sstevel@tonic-gate #ifdef	XPG4
6580Sstevel@tonic-gate 		if (!itag && (dol > zero))
6590Sstevel@tonic-gate #else /* XPG4 */
6600Sstevel@tonic-gate 		if (dol > zero)
6610Sstevel@tonic-gate #endif /* XPG4 */
6620Sstevel@tonic-gate 			dot = one;
6630Sstevel@tonic-gate 		globp = (unsigned char *)"visual";
6640Sstevel@tonic-gate 		if (setexit() == 0)
6650Sstevel@tonic-gate 			commands(1, 1);
6660Sstevel@tonic-gate 	}
6670Sstevel@tonic-gate 
6680Sstevel@tonic-gate 	/*
6690Sstevel@tonic-gate 	 * Clear out trash in state accumulated by startup,
6700Sstevel@tonic-gate 	 * and then do the main command loop for a normal edit.
6710Sstevel@tonic-gate 	 * If you quit out of a 'vi' command by doing Q or ^\,
6720Sstevel@tonic-gate 	 * you also fall through to here.
6730Sstevel@tonic-gate 	 */
6740Sstevel@tonic-gate 	seenprompt = 1;
6750Sstevel@tonic-gate 	ungetchar(0);
6760Sstevel@tonic-gate 	globp = 0;
6770Sstevel@tonic-gate 	initev = 0;
6780Sstevel@tonic-gate 	setlastchar('\n');
6790Sstevel@tonic-gate 	setexit();
6800Sstevel@tonic-gate 	commands(0, 0);
6810Sstevel@tonic-gate 	cleanup(1);
6820Sstevel@tonic-gate 	exit(errcnt);
6830Sstevel@tonic-gate }
6840Sstevel@tonic-gate 
6850Sstevel@tonic-gate /*
6860Sstevel@tonic-gate  * Initialization, before editing a new file.
6870Sstevel@tonic-gate  * Main thing here is to get a new buffer (in fileinit),
6880Sstevel@tonic-gate  * rest is peripheral state resetting.
6890Sstevel@tonic-gate  */
6900Sstevel@tonic-gate init()
6910Sstevel@tonic-gate {
6920Sstevel@tonic-gate 	register int i;
6930Sstevel@tonic-gate 	void (*pstat)();
6940Sstevel@tonic-gate 	fileinit();
6950Sstevel@tonic-gate 	dot = zero = truedol = unddol = dol = fendcore;
6960Sstevel@tonic-gate 	one = zero+1;
6970Sstevel@tonic-gate 	undkind = UNDNONE;
6980Sstevel@tonic-gate 	chng = 0;
6990Sstevel@tonic-gate 	edited = 0;
7000Sstevel@tonic-gate 	for (i = 0; i <= 'z'-'a'+1; i++)
7010Sstevel@tonic-gate 		names[i] = 1;
7020Sstevel@tonic-gate 	anymarks = 0;
7030Sstevel@tonic-gate 	if (xflag) {
7040Sstevel@tonic-gate 		xtflag = 1;
7050Sstevel@tonic-gate 		/* ignore SIGINT before crypt process */
7060Sstevel@tonic-gate 		pstat = signal(SIGINT, SIG_IGN);
7070Sstevel@tonic-gate 		if (tpermflag)
7080Sstevel@tonic-gate 			(void) crypt_close(tperm);
7090Sstevel@tonic-gate 		tpermflag = 1;
7100Sstevel@tonic-gate 		if (makekey(tperm) != 0) {
7110Sstevel@tonic-gate 			xtflag = 0;
7120Sstevel@tonic-gate 			smerror(gettext(
7130Sstevel@tonic-gate 			    "Warning--Cannot encrypt temporary buffer\n"));
7140Sstevel@tonic-gate 		}
7150Sstevel@tonic-gate 		signal(SIGINT, pstat);
7160Sstevel@tonic-gate 	}
7170Sstevel@tonic-gate }
7180Sstevel@tonic-gate 
7190Sstevel@tonic-gate /*
7200Sstevel@tonic-gate  * Return last component of unix path name p.
7210Sstevel@tonic-gate  */
7220Sstevel@tonic-gate unsigned char *
7230Sstevel@tonic-gate tailpath(p)
7240Sstevel@tonic-gate register unsigned char *p;
7250Sstevel@tonic-gate {
7260Sstevel@tonic-gate 	register unsigned char *r;
7270Sstevel@tonic-gate 
7280Sstevel@tonic-gate 	for (r = p; *p; p++)
7290Sstevel@tonic-gate 		if (*p == '/')
7300Sstevel@tonic-gate 			r = p+1;
7310Sstevel@tonic-gate 	return (r);
7320Sstevel@tonic-gate }
7330Sstevel@tonic-gate 
7340Sstevel@tonic-gate 
7350Sstevel@tonic-gate /*
7360Sstevel@tonic-gate  * validate_exrc - verify .exrc as belonging to the user.
7370Sstevel@tonic-gate  * The file uid should match the process ruid,
7380Sstevel@tonic-gate  * and the file should be writable only by the owner.
7390Sstevel@tonic-gate  */
7400Sstevel@tonic-gate static int
7410Sstevel@tonic-gate validate_exrc(unsigned char *exrc_path)
7420Sstevel@tonic-gate {
7430Sstevel@tonic-gate 	struct stat64 exrc_stat;
7440Sstevel@tonic-gate 	int process_uid;
7450Sstevel@tonic-gate 
7460Sstevel@tonic-gate 	if (stat64((char *)exrc_path, &exrc_stat) == -1)
7470Sstevel@tonic-gate 		return (0); /* ignore if .exrec is not found */
7480Sstevel@tonic-gate 	process_uid = geteuid();
7490Sstevel@tonic-gate 	/* if not root, uid must match file owner */
7500Sstevel@tonic-gate 	if (process_uid && process_uid != exrc_stat.st_uid)
7510Sstevel@tonic-gate 		return (-1);
7520Sstevel@tonic-gate 	if ((exrc_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0)
7530Sstevel@tonic-gate 		return (-1);
7540Sstevel@tonic-gate 	return (0);
7550Sstevel@tonic-gate }
7560Sstevel@tonic-gate 
7570Sstevel@tonic-gate /*
7580Sstevel@tonic-gate  * print usage message to stdout
7590Sstevel@tonic-gate  */
7600Sstevel@tonic-gate static void
7610Sstevel@tonic-gate usage(unsigned char *name)
7620Sstevel@tonic-gate {
7630Sstevel@tonic-gate 	char buf[160];
7640Sstevel@tonic-gate 
7650Sstevel@tonic-gate #ifdef TRACE
7660Sstevel@tonic-gate 	(void) snprintf(buf, sizeof (buf), gettext(
767*597Sceastha 	    "Usage: %s [- | -s] [-l] [-L] [-wn] "
768*597Sceastha 	    "[-R] [-S] [-r [file]] [-t tag] [-T] [-U tracefile]\n"
769*597Sceastha 	    "[-v] [-V] [-x] [-C] [+cmd | -c cmd] file...\n"), name);
7700Sstevel@tonic-gate #else
7710Sstevel@tonic-gate 	(void) snprintf(buf, sizeof (buf), gettext(
772*597Sceastha 	    "Usage: %s [- | -s] [-l] [-L] [-wn] "
773*597Sceastha 	    "[-R] [-S] [-r [file]] [-t tag]\n"
774*597Sceastha 	    "[-v] [-V] [-x] [-C] [+cmd | -c cmd] file...\n"), name);
7750Sstevel@tonic-gate #endif
7760Sstevel@tonic-gate 	(void) write(2, buf, strlen(buf));
7770Sstevel@tonic-gate }
778