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