xref: /csrg-svn/usr.bin/ex/ex.c (revision 64299)
148255Sbostic /*-
263046Sbostic  * Copyright (c) 1980, 1993
363046Sbostic  *	The Regents of the University of California.  All rights reserved.
448255Sbostic  *
548255Sbostic  * %sccs.include.proprietary.c%
621653Sdist  */
721653Sdist 
821653Sdist #ifndef lint
963046Sbostic static char copyright[] =
1063046Sbostic "@(#) Copyright (c) 1980, 1993\n\
1163046Sbostic 	The Regents of the University of California.  All rights reserved.\n";
1248255Sbostic #endif /* not lint */
1321653Sdist 
1421653Sdist #ifndef lint
15*64299Smckusick static char sccsid[] = "@(#)ex.c	8.1.1.1 (Berkeley) 08/19/93";
1648255Sbostic #endif /* not lint */
1721653Sdist 
18427Smark #include "ex.h"
19427Smark #include "ex_argv.h"
20427Smark #include "ex_temp.h"
21427Smark #include "ex_tty.h"
2237882Sbostic #include "pathnames.h"
23427Smark 
24427Smark #ifdef TRACE
2530596Sconrad #ifdef	vms
2630596Sconrad char	tttrace[]	= { 't','r','a','c','e','.','l','i','s' };
2730596Sconrad #else
28427Smark char	tttrace[]	= { '/','d','e','v','/','t','t','y','x','x',0 };
29427Smark #endif
3030596Sconrad #endif
31427Smark 
32427Smark /*
33427Smark  * The code for ex is divided as follows:
34427Smark  *
35427Smark  * ex.c			Entry point and routines handling interrupt, hangup
36427Smark  *			signals; initialization code.
37427Smark  *
38427Smark  * ex_addr.c		Address parsing routines for command mode decoding.
39427Smark  *			Routines to set and check address ranges on commands.
40427Smark  *
41427Smark  * ex_cmds.c		Command mode command decoding.
42427Smark  *
43427Smark  * ex_cmds2.c		Subroutines for command decoding and processing of
44427Smark  *			file names in the argument list.  Routines to print
45427Smark  *			messages and reset state when errors occur.
46427Smark  *
47427Smark  * ex_cmdsub.c		Subroutines which implement command mode functions
48427Smark  *			such as append, delete, join.
49427Smark  *
50427Smark  * ex_data.c		Initialization of options.
51427Smark  *
52427Smark  * ex_get.c		Command mode input routines.
53427Smark  *
54427Smark  * ex_io.c		General input/output processing: file i/o, unix
55427Smark  *			escapes, filtering, source commands, preserving
56427Smark  *			and recovering.
57427Smark  *
58427Smark  * ex_put.c		Terminal driving and optimizing routines for low-level
59427Smark  *			output (cursor-positioning); output line formatting
60427Smark  *			routines.
61427Smark  *
62427Smark  * ex_re.c		Global commands, substitute, regular expression
63427Smark  *			compilation and execution.
64427Smark  *
65427Smark  * ex_set.c		The set command.
66427Smark  *
67427Smark  * ex_subr.c		Loads of miscellaneous subroutines.
68427Smark  *
69427Smark  * ex_temp.c		Editor buffer routines for main buffer and also
70427Smark  *			for named buffers (Q registers if you will.)
71427Smark  *
72427Smark  * ex_tty.c		Terminal dependent initializations from termcap
73427Smark  *			data base, grabbing of tty modes (at beginning
74427Smark  *			and after escapes).
75427Smark  *
76510Smark  * ex_unix.c		Routines for the ! command and its variations.
77510Smark  *
78427Smark  * ex_v*.c		Visual/open mode routines... see ex_v.c for a
79427Smark  *			guide to the overall organization.
80427Smark  */
81427Smark 
82427Smark /*
83427Smark  * Main procedure.  Process arguments and then
84427Smark  * transfer control to the main command processing loop
85510Smark  * in the routine commands.  We are entered as either "ex", "edit", "vi"
86510Smark  * or "view" and the distinction is made here.  Actually, we are "vi" if
87510Smark  * there is a 'v' in our name, "view" is there is a 'w', and "edit" if
88510Smark  * there is a 'd' in our name.  For edit we just diddle options;
89510Smark  * for vi we actually force an early visual command.
90427Smark  */
main(ac,av)91427Smark main(ac, av)
92427Smark 	register int ac;
93427Smark 	register char *av[];
94427Smark {
9530596Sconrad #ifdef EXSTRINGS
96427Smark 	char *erpath = EXSTRINGS;
97467Smark #endif
98427Smark 	register char *cp;
99427Smark 	register int c;
100427Smark 	bool recov = 0;
101427Smark 	bool ivis;
102427Smark 	bool itag = 0;
103427Smark 	bool fast = 0;
10446826Sbostic 	extern void onemt();
10530596Sconrad #ifdef UNIX_SBRK
10630596Sconrad 	extern char *sbrk();
10730596Sconrad #else
10830596Sconrad 	extern char *malloc();
10930596Sconrad #endif
110427Smark #ifdef TRACE
111427Smark 	register char *tracef;
112427Smark #endif
11330596Sconrad #ifdef	vms
11430596Sconrad 	char termtype[20];
11530596Sconrad #endif
116427Smark 
117427Smark 	/*
118427Smark 	 * Immediately grab the tty modes so that we wont
119427Smark 	 * get messed up if an interrupt comes in quickly.
120427Smark 	 */
12130596Sconrad 	ex_gTTY(1);
122510Smark #ifndef USG3TTY
123427Smark 	normf = tty.sg_flags;
124510Smark #else
125510Smark 	normf = tty;
126510Smark #endif
127427Smark 	ppid = getpid();
128427Smark 	/*
129510Smark 	 * Defend against d's, v's, w's, and a's in directories of
130427Smark 	 * path leading to our true name.
131427Smark 	 */
13230596Sconrad #ifndef	vms
133427Smark 	av[0] = tailpath(av[0]);
13430596Sconrad #else
13530596Sconrad 	/*
13630596Sconrad 	 * This program has to be invoked by using the following
13730596Sconrad 	 * string definitions:
13830596Sconrad 	 *
13930596Sconrad 	 * vi == "$dir:ex.exe vi"
14030596Sconrad 	 * view == "$dir:ex.exe view"
14130596Sconrad 	 * ex == "$dir:ex.exe ex"
14230596Sconrad 	 * edit == "$dir:ex.exe edit"
14330596Sconrad 	 */
14430596Sconrad 	ac--;
14530596Sconrad 	av++;
14630596Sconrad #endif
147427Smark 
148427Smark 	/*
149510Smark 	 * Figure out how we were invoked: ex, edit, vi, view.
150427Smark 	 */
151510Smark 	ivis = any('v', av[0]);	/* "vi" */
152510Smark 	if (any('w', av[0]))	/* "view" */
153510Smark 		value(READONLY) = 1;
154510Smark 	if (any('d', av[0])) {	/* "edit" */
155427Smark 		value(OPEN) = 0;
156427Smark 		value(REPORT) = 1;
157427Smark 		value(MAGIC) = 0;
158427Smark 	}
159427Smark 
16030596Sconrad #ifdef EXSTRINGS
161427Smark 	/*
162510Smark 	 * For debugging take files out of . if name is a.out.
163510Smark 	 */
164510Smark 	if (av[0][0] == 'a')
165510Smark 		erpath = tailpath(erpath);
166510Smark #endif
167510Smark 	/*
168427Smark 	 * Open the error message file.
169427Smark 	 */
170427Smark 	draino();
17130596Sconrad #ifdef EXSTRINGS
172427Smark 	erfile = open(erpath+4, 0);
173427Smark 	if (erfile < 0) {
174427Smark 		erfile = open(erpath, 0);
175427Smark 	}
176467Smark #endif
177427Smark 	pstop();
178427Smark 
179427Smark 	/*
180427Smark 	 * Initialize interrupt handling.
181427Smark 	 */
182427Smark 	oldhup = signal(SIGHUP, SIG_IGN);
183427Smark 	if (oldhup == SIG_DFL)
184427Smark 		signal(SIGHUP, onhup);
185427Smark 	oldquit = signal(SIGQUIT, SIG_IGN);
186427Smark 	ruptible = signal(SIGINT, SIG_IGN) == SIG_DFL;
187427Smark 	if (signal(SIGTERM, SIG_IGN) == SIG_DFL)
188427Smark 		signal(SIGTERM, onhup);
18921678Sdist 	if (signal(SIGEMT, SIG_IGN) == SIG_DFL)
19021678Sdist 		signal(SIGEMT, onemt);
191427Smark 
192427Smark 	/*
193427Smark 	 * Process flag arguments.
194427Smark 	 */
195427Smark 	ac--, av++;
196427Smark 	while (ac && av[0][0] == '-') {
197427Smark 		c = av[0][1];
198427Smark 		if (c == 0) {
199427Smark 			hush = 1;
200427Smark 			value(AUTOPRINT) = 0;
201427Smark 			fast++;
202427Smark 		} else switch (c) {
203427Smark 
204510Smark 		case 'R':
205510Smark 			value(READONLY) = 1;
206510Smark 			break;
207510Smark 
208427Smark #ifdef TRACE
209427Smark 		case 'T':
210427Smark 			if (av[0][2] == 0)
211427Smark 				tracef = "trace";
212427Smark 			else {
213427Smark 				tracef = tttrace;
214427Smark 				tracef[8] = av[0][2];
215427Smark 				if (tracef[8])
216427Smark 					tracef[9] = av[0][3];
217427Smark 				else
218427Smark 					tracef[9] = 0;
219427Smark 			}
220427Smark 			trace = fopen(tracef, "w");
22121678Sdist #define tracbuf NULL
222427Smark 			if (trace == NULL)
22330596Sconrad 				ex_printf("Trace create error\n");
22421678Sdist 			else
22521678Sdist 				setbuf(trace, tracbuf);
226427Smark 			break;
227427Smark 
228427Smark #endif
229427Smark 
230427Smark #ifdef LISPCODE
231427Smark 		case 'l':
232427Smark 			value(LISP) = 1;
233427Smark 			value(SHOWMATCH) = 1;
234427Smark 			break;
235427Smark #endif
236427Smark 
237427Smark 		case 'r':
238427Smark 			recov++;
239427Smark 			break;
240427Smark 
241427Smark 		case 't':
242427Smark 			if (ac > 1 && av[1][0] != '-') {
243427Smark 				ac--, av++;
244427Smark 				itag = 1;
245427Smark 				/* BUG: should check for too long tag. */
246427Smark 				CP(lasttag, av[0]);
247427Smark 			}
248427Smark 			break;
249427Smark 
250427Smark 		case 'v':
251427Smark 			ivis = 1;
252427Smark 			break;
253427Smark 
254427Smark 		case 'w':
255427Smark 			defwind = 0;
256427Smark 			if (av[0][2] == 0) defwind = 3;
257427Smark 			else for (cp = &av[0][2]; isdigit(*cp); cp++)
258427Smark 				defwind = 10*defwind + *cp - '0';
259427Smark 			break;
260427Smark 
261510Smark 
262427Smark 		default:
263427Smark 			smerror("Unknown option %s\n", av[0]);
264427Smark 			break;
265427Smark 		}
266427Smark 		ac--, av++;
267427Smark 	}
26821678Sdist 
26921678Sdist #ifdef SIGTSTP
27021678Sdist 	if (!hush && signal(SIGTSTP, SIG_IGN) == SIG_DFL)
27121678Sdist 		signal(SIGTSTP, onsusp), dosusp++;
27221678Sdist #endif
27321678Sdist 
274427Smark 	if (ac && av[0][0] == '+') {
275427Smark 		firstpat = &av[0][1];
276427Smark 		ac--, av++;
277427Smark 	}
278427Smark 
279510Smark 
280427Smark 	/*
281427Smark 	 * If we are doing a recover and no filename
282427Smark 	 * was given, then execute an exrecover command with
283427Smark 	 * the -r option to type out the list of saved file names.
284427Smark 	 * Otherwise set the remembered file name to the first argument
285427Smark 	 * file name so the "recover" initial command will find it.
286427Smark 	 */
287427Smark 	if (recov) {
288427Smark 		if (ac == 0) {
289427Smark 			ppid = 0;
290427Smark 			setrupt();
29137882Sbostic 			execl(_PATH_EXRECOVER, "exrecover", "-r", 0);
29237882Sbostic 			filioerr(_PATH_EXRECOVER);
29330596Sconrad 			ex_exit(1);
294427Smark 		}
295427Smark 		CP(savedfile, *av++), ac--;
296427Smark 	}
297427Smark 
298427Smark 	/*
299427Smark 	 * Initialize the argument list.
300427Smark 	 */
301427Smark 	argv0 = av;
302427Smark 	argc0 = ac;
303427Smark 	args0 = av[0];
304427Smark 	erewind();
305427Smark 
306427Smark 	/*
307427Smark 	 * Initialize a temporary file (buffer) and
308427Smark 	 * set up terminal environment.  Read user startup commands.
309427Smark 	 */
310427Smark 	if (setexit() == 0) {
311427Smark 		setrupt();
312427Smark 		intty = isatty(0);
313427Smark 		value(PROMPT) = intty;
31430596Sconrad #ifndef	vms
315510Smark 		if (cp = getenv("SHELL"))
31630596Sconrad #else
31730596Sconrad 		if (cp = getlog("SHELL"))
31830596Sconrad #endif
319510Smark 			CP(shell, cp);
320427Smark 		if (fast || !intty)
321427Smark 			setterm("dumb");
322427Smark 		else {
323427Smark 			gettmode();
32430596Sconrad #ifndef	vms
325510Smark 			if ((cp = getenv("TERM")) != 0 && *cp)
326427Smark 				setterm(cp);
32730596Sconrad #else
32830596Sconrad 			if ((cp = getlog("TERM")) != 0 && *cp) {
32930596Sconrad 				/*
33030596Sconrad 				 * Can't just use it directly since getlog
33130596Sconrad 				 * returns a pointer to a static buffer that
33230596Sconrad 				 * tgetent() will eventually use
33330596Sconrad 				 */
33430596Sconrad 				CP(termtype, cp);
33530596Sconrad 				setterm(termtype);
33630596Sconrad 			}
33730596Sconrad #endif
338427Smark 		}
339427Smark 	}
34021678Sdist 	if (setexit() == 0 && !fast && intty) {
34130596Sconrad #ifndef	vms
342510Smark 		if ((globp = getenv("EXINIT")) && *globp)
34330596Sconrad #else
34430596Sconrad 		if ((globp = getlog("EXINIT")) && *globp)
34530596Sconrad #endif
346427Smark 			commands(1,1);
347510Smark 		else {
348510Smark 			globp = 0;
34924486Sbloom 			if ((cp = getenv("HOME")) != 0 && *cp) {
35024486Sbloom 				(void) strcat(strcpy(genbuf, cp), "/.exrc");
35124486Sbloom 				if (iownit(genbuf))
35224486Sbloom 					source(genbuf, 1);
35324486Sbloom 			}
354510Smark 		}
35521678Sdist 		/*
35621678Sdist 		 * Allow local .exrc too.  This loses if . is $HOME,
35721678Sdist 		 * but nobody should notice unless they do stupid things
35821678Sdist 		 * like putting a version command in .exrc.  Besides,
35921678Sdist 		 * they should be using EXINIT, not .exrc, right?
36021678Sdist 		 */
36124486Sbloom 		 if (iownit(".exrc"))
36224486Sbloom 			source(".exrc", 1);
36321678Sdist 	}
36430596Sconrad #ifdef	UNIX_SBRK
36530596Sconrad 	/*
36630596Sconrad 	 * Initialize end of core pointers.
36730596Sconrad 	 * Normally we avoid breaking back to fendcore after each
36830596Sconrad 	 * file since this can be expensive (much core-core copying).
36930596Sconrad 	 * If your system can scatter load processes you could do
37030596Sconrad 	 * this as ed does, saving a little core, but it will probably
37130596Sconrad 	 * not often make much difference.
37230596Sconrad 	 */
37330596Sconrad 	fendcore = (line *) sbrk(0);
37430596Sconrad 	endcore = fendcore - 2;
37530596Sconrad #else
37630596Sconrad 	/*
37730596Sconrad 	 * Allocate all the memory we will ever use in one chunk.
37830596Sconrad 	 * This is for system such as VMS where sbrk() does not
37930596Sconrad 	 * guarantee that the memory allocated beyond the end is
38030596Sconrad 	 * consecutive.  VMS's RMS does all sorts of memory allocation
38130596Sconrad 	 * and screwed up ex royally because ex assumes that all
38230596Sconrad 	 * memory up to "endcore" belongs to it and RMS has different
38330596Sconrad 	 * ideas.
38430596Sconrad 	 */
38530596Sconrad 	fendcore = (line *) malloc((unsigned)
38630596Sconrad 		value(LINELIMIT) * sizeof (line *));
38730596Sconrad 	if (fendcore == NULL) {
38830596Sconrad 		lprintf("ex: cannot handle %d lines\n", value(LINELIMIT));
38930596Sconrad 		lprintf("ex: set \"linelimit\" lower\n");
39030596Sconrad 		flush();
39130596Sconrad 		ex_exit(1);
39230596Sconrad 	}
39330596Sconrad 	endcore = fendcore + (value(LINELIMIT) - 1);
39430596Sconrad #endif
39521678Sdist 	init();	/* moved after prev 2 chunks to fix directory option */
396427Smark 
397427Smark 	/*
398427Smark 	 * Initial processing.  Handle tag, recover, and file argument
399427Smark 	 * implied next commands.  If going in as 'vi', then don't do
400427Smark 	 * anything, just set initev so we will do it later (from within
401427Smark 	 * visual).
402427Smark 	 */
403427Smark 	if (setexit() == 0) {
404427Smark 		if (recov)
405427Smark 			globp = "recover";
406427Smark 		else if (itag)
407427Smark 			globp = ivis ? "tag" : "tag|p";
408427Smark 		else if (argc)
409427Smark 			globp = "next";
410427Smark 		if (ivis)
411427Smark 			initev = globp;
412427Smark 		else if (globp) {
413427Smark 			inglobal = 1;
414427Smark 			commands(1, 1);
415427Smark 			inglobal = 0;
416427Smark 		}
417427Smark 	}
418427Smark 
419427Smark 	/*
420427Smark 	 * Vi command... go into visual.
421427Smark 	 * Strange... everything in vi usually happens
422427Smark 	 * before we ever "start".
423427Smark 	 */
424427Smark 	if (ivis) {
425427Smark 		/*
426427Smark 		 * Don't have to be upward compatible with stupidity
427427Smark 		 * of starting editing at line $.
428427Smark 		 */
429427Smark 		if (dol > zero)
430427Smark 			dot = one;
431427Smark 		globp = "visual";
432427Smark 		if (setexit() == 0)
433427Smark 			commands(1, 1);
434427Smark 	}
435427Smark 
436427Smark 	/*
437427Smark 	 * Clear out trash in state accumulated by startup,
438427Smark 	 * and then do the main command loop for a normal edit.
439427Smark 	 * If you quit out of a 'vi' command by doing Q or ^\,
440427Smark 	 * you also fall through to here.
441427Smark 	 */
44221678Sdist 	seenprompt = 1;
443427Smark 	ungetchar(0);
444427Smark 	globp = 0;
445427Smark 	initev = 0;
446427Smark 	setlastchar('\n');
447427Smark 	setexit();
448427Smark 	commands(0, 0);
449427Smark 	cleanup(1);
45030596Sconrad 	ex_exit(0);
451427Smark }
452427Smark 
453427Smark /*
454427Smark  * Initialization, before editing a new file.
455427Smark  * Main thing here is to get a new buffer (in fileinit),
456427Smark  * rest is peripheral state resetting.
457427Smark  */
init()458427Smark init()
459427Smark {
460427Smark 	register int i;
461427Smark 
462427Smark 	fileinit();
463427Smark 	dot = zero = truedol = unddol = dol = fendcore;
464427Smark 	one = zero+1;
465427Smark 	undkind = UNDNONE;
466427Smark 	chng = 0;
467427Smark 	edited = 0;
468427Smark 	for (i = 0; i <= 'z'-'a'+1; i++)
469427Smark 		names[i] = 1;
470427Smark 	anymarks = 0;
471427Smark }
472427Smark 
473427Smark /*
474427Smark  * Return last component of unix path name p.
475427Smark  */
476427Smark char *
tailpath(p)477427Smark tailpath(p)
478427Smark register char *p;
479427Smark {
480427Smark 	register char *r;
481427Smark 
482427Smark 	for (r=p; *p; p++)
483427Smark 		if (*p == '/')
484427Smark 			r = p+1;
485427Smark 	return(r);
486427Smark }
48724486Sbloom 
48824486Sbloom /*
48924486Sbloom  * Check ownership of file.  Return nonzero if it exists and is owned by the
49024486Sbloom  * user or the option sourceany is used
49124486Sbloom  */
iownit(file)49224486Sbloom iownit(file)
49324486Sbloom char *file;
49424486Sbloom {
49524486Sbloom 	struct stat sb;
49624486Sbloom 
49724486Sbloom 	if (stat(file, &sb) == 0 && (value(SOURCEANY) || sb.st_uid == getuid()))
49824486Sbloom 		return(1);
49924486Sbloom 	else
50024486Sbloom 		return(0);
50124486Sbloom }
502