BSD 3 development
[unix-history] / usr / src / cmd / ex / ex.c
CommitLineData
27704fe4
BJ
1/* Copyright (c) 1979 Regents of the University of California */
2#include "ex.h"
3#include "ex_argv.h"
4#include "ex_temp.h"
5#include "ex_tty.h"
6
7#ifdef TRACE
8char tttrace[] = { '/','d','e','v','/','t','t','y','x','x',0 };
9#endif
10
11/*
12 * The code for ex is divided as follows:
13 *
14 * ex.c Entry point and routines handling interrupt, hangup
15 * signals; initialization code.
16 *
17 * ex_addr.c Address parsing routines for command mode decoding.
18 * Routines to set and check address ranges on commands.
19 *
20 * ex_cmds.c Command mode command decoding.
21 *
22 * ex_cmds2.c Subroutines for command decoding and processing of
23 * file names in the argument list. Routines to print
24 * messages and reset state when errors occur.
25 *
26 * ex_cmdsub.c Subroutines which implement command mode functions
27 * such as append, delete, join.
28 *
29 * ex_data.c Initialization of options.
30 *
31 * ex_get.c Command mode input routines.
32 *
33 * ex_io.c General input/output processing: file i/o, unix
34 * escapes, filtering, source commands, preserving
35 * and recovering.
36 *
37 * ex_put.c Terminal driving and optimizing routines for low-level
38 * output (cursor-positioning); output line formatting
39 * routines.
40 *
41 * ex_re.c Global commands, substitute, regular expression
42 * compilation and execution.
43 *
44 * ex_set.c The set command.
45 *
46 * ex_subr.c Loads of miscellaneous subroutines.
47 *
48 * ex_temp.c Editor buffer routines for main buffer and also
49 * for named buffers (Q registers if you will.)
50 *
51 * ex_tty.c Terminal dependent initializations from termcap
52 * data base, grabbing of tty modes (at beginning
53 * and after escapes).
54 *
55 * ex_v*.c Visual/open mode routines... see ex_v.c for a
56 * guide to the overall organization.
57 */
58
59/*
60 * Main procedure. Process arguments and then
61 * transfer control to the main command processing loop
62 * in the routine commands. We are entered as either "ex", "edit" or "vi"
63 * and the distinction is made here. Actually, we are "vi" if
64 * there is a 'v' in our name, and "edit" if there is a 'd' in our
65 * name. For edit we just diddle options; for vi we actually
66 * force an early visual command, setting the external initev so
67 * the q command in visual doesn't give command mode.
68 */
69main(ac, av)
70 register int ac;
71 register char *av[];
72{
73#ifndef VMUNIX
74 char *erpath = EXSTRINGS;
75#endif
76 register char *cp;
77 register int c;
78 bool recov = 0;
79 bool ivis;
80 bool itag = 0;
81 bool fast = 0;
82#ifdef TRACE
83 register char *tracef;
84#endif
85
86 /*
87 * Immediately grab the tty modes so that we wont
88 * get messed up if an interrupt comes in quickly.
89 */
90 gTTY(1);
91 normf = tty.sg_flags;
92 ppid = getpid();
93 /*
94 * Defend against d's, v's, and a's in directories of
95 * path leading to our true name.
96 */
97 av[0] = tailpath(av[0]);
98 ivis = any('v', av[0]);
99
100 /*
101 * For debugging take files out of . if name is a.out.
102 * If a 'd' in our name, then set options for edit.
103 */
104#ifndef VMUNIX
105 if (av[0][0] == 'a')
106 erpath = tailpath(erpath);
107#endif
108 if (ivis) {
109#ifdef notdef
110 options[BEAUTIFY].odefault = value(BEAUTIFY) = 1;
111#endif
112 } else if (any('d', av[0])) {
113 value(OPEN) = 0;
114 value(REPORT) = 1;
115 value(MAGIC) = 0;
116 }
117
118 /*
119 * Open the error message file.
120 */
121 draino();
122#ifndef VMUNIX
123 erfile = open(erpath+4, 0);
124 if (erfile < 0) {
125 erfile = open(erpath, 0);
126 }
127#endif
128 pstop();
129
130 /*
131 * Initialize interrupt handling.
132 */
133 oldhup = signal(SIGHUP, SIG_IGN);
134 if (oldhup == SIG_DFL)
135 signal(SIGHUP, onhup);
136 oldquit = signal(SIGQUIT, SIG_IGN);
137 ruptible = signal(SIGINT, SIG_IGN) == SIG_DFL;
138 if (signal(SIGTERM, SIG_IGN) == SIG_DFL)
139 signal(SIGTERM, onhup);
140
141 /*
142 * Initialize end of core pointers.
143 * Normally we avoid breaking back to fendcore after each
144 * file since this can be expensive (much core-core copying).
145 * If your system can scatter load processes you could do
146 * this as ed does, saving a little core, but it will probably
147 * not often make much difference.
148 */
149 fendcore = (line *) sbrk(0);
150 endcore = fendcore - 2;
151
152 /*
153 * Process flag arguments.
154 */
155 ac--, av++;
156 while (ac && av[0][0] == '-') {
157 c = av[0][1];
158 if (c == 0) {
159 hush = 1;
160 value(AUTOPRINT) = 0;
161 fast++;
162 } else switch (c) {
163
164#ifdef TRACE
165 case 'T':
166 if (av[0][2] == 0)
167 tracef = "trace";
168 else {
169 tracef = tttrace;
170 tracef[8] = av[0][2];
171 if (tracef[8])
172 tracef[9] = av[0][3];
173 else
174 tracef[9] = 0;
175 }
176 trace = fopen(tracef, "w");
177 if (trace == NULL)
178 printf("Trace create error\n");
179 setbuf(trace, tracbuf);
180 break;
181
182#endif
183
184#ifdef LISPCODE
185 case 'l':
186 value(LISP) = 1;
187 value(SHOWMATCH) = 1;
188 break;
189#endif
190
191 case 'r':
192 recov++;
193 break;
194
195 case 't':
196 if (ac > 1 && av[1][0] != '-') {
197 ac--, av++;
198 itag = 1;
199 /* BUG: should check for too long tag. */
200 CP(lasttag, av[0]);
201 }
202 break;
203
204 case 'v':
205 ivis = 1;
206 break;
207
208 case 'w':
209 defwind = 0;
210 if (av[0][2] == 0) defwind = 3;
211 else for (cp = &av[0][2]; isdigit(*cp); cp++)
212 defwind = 10*defwind + *cp - '0';
213 break;
214
215 default:
216 smerror("Unknown option %s\n", av[0]);
217 break;
218 }
219 ac--, av++;
220 }
221 if (ac && av[0][0] == '+') {
222 firstpat = &av[0][1];
223 ac--, av++;
224 }
225
226 /*
227 * If we are doing a recover and no filename
228 * was given, then execute an exrecover command with
229 * the -r option to type out the list of saved file names.
230 * Otherwise set the remembered file name to the first argument
231 * file name so the "recover" initial command will find it.
232 */
233 if (recov) {
234 if (ac == 0) {
235 ppid = 0;
236 setrupt();
237 execl(EXRECOVER, "exrecover", "-r", 0);
238 filioerr(EXRECOVER);
239 exit(1);
240 }
241 CP(savedfile, *av++), ac--;
242 }
243
244 /*
245 * Initialize the argument list.
246 */
247 argv0 = av;
248 argc0 = ac;
249 args0 = av[0];
250 erewind();
251
252 /*
253 * Initialize a temporary file (buffer) and
254 * set up terminal environment. Read user startup commands.
255 */
256 init();
257 if (setexit() == 0) {
258 setrupt();
259 intty = isatty(0);
260 value(PROMPT) = intty;
261 if (fast || !intty)
262 setterm("dumb");
263 else {
264 gettmode();
265 if ((cp = getenv("TERM")) != 0)
266 setterm(cp);
267 }
268 }
269 if (setexit() == 0 && !fast && intty)
270 if (globp = getenv("EXINIT"))
271 commands(1,1);
272 else if ((cp = getenv("HOME")) != 0)
273 source(strcat(strcpy(genbuf, cp), "/.exrc"), 1);
274
275 /*
276 * Initial processing. Handle tag, recover, and file argument
277 * implied next commands. If going in as 'vi', then don't do
278 * anything, just set initev so we will do it later (from within
279 * visual).
280 */
281 if (setexit() == 0) {
282 if (recov)
283 globp = "recover";
284 else if (itag)
285 globp = ivis ? "tag" : "tag|p";
286 else if (argc)
287 globp = "next";
288 if (ivis)
289 initev = globp;
290 else if (globp) {
291 inglobal = 1;
292 commands(1, 1);
293 inglobal = 0;
294 }
295 }
296
297 /*
298 * Vi command... go into visual.
299 * Strange... everything in vi usually happens
300 * before we ever "start".
301 */
302 if (ivis) {
303 /*
304 * Don't have to be upward compatible with stupidity
305 * of starting editing at line $.
306 */
307 if (dol > zero)
308 dot = one;
309 globp = "visual";
310 if (setexit() == 0)
311 commands(1, 1);
312 }
313
314 /*
315 * Clear out trash in state accumulated by startup,
316 * and then do the main command loop for a normal edit.
317 * If you quit out of a 'vi' command by doing Q or ^\,
318 * you also fall through to here.
319 */
320 ungetchar(0);
321 globp = 0;
322 initev = 0;
323 setlastchar('\n');
324 setexit();
325 commands(0, 0);
326 cleanup(1);
327 exit(0);
328}
329
330/*
331 * Initialization, before editing a new file.
332 * Main thing here is to get a new buffer (in fileinit),
333 * rest is peripheral state resetting.
334 */
335init()
336{
337 register int i;
338
339 fileinit();
340 dot = zero = truedol = unddol = dol = fendcore;
341 one = zero+1;
342 undkind = UNDNONE;
343 chng = 0;
344 edited = 0;
345 for (i = 0; i <= 'z'-'a'+1; i++)
346 names[i] = 1;
347 anymarks = 0;
348}
349
350/*
351 * When a hangup occurs our actions are similar to a preserve
352 * command. If the buffer has not been [Modified], then we do
353 * nothing but remove the temporary files and exit.
354 * Otherwise, we sync the temp file and then attempt a preserve.
355 * If the preserve succeeds, we unlink our temp files.
356 * If the preserve fails, we leave the temp files as they are
357 * as they are a backup even without preservation if they
358 * are not removed.
359 */
360onhup()
361{
362
363 if (chng == 0) {
364 cleanup(1);
365 exit(0);
366 }
367 if (setexit() == 0) {
368 if (preserve()) {
369 cleanup(1);
370 exit(0);
371 }
372 }
373 exit(1);
374}
375
376/*
377 * An interrupt occurred. Drain any output which
378 * is still in the output buffering pipeline.
379 * Catch interrupts again. Unless we are in visual
380 * reset the output state (out of -nl mode, e.g).
381 * Then like a normal error (with the \n before Interrupt
382 * suppressed in visual mode).
383 */
384onintr()
385{
386
387#ifndef CBREAK
388 signal(SIGINT, onintr);
389#else
390 signal(SIGINT, inopen ? vintr : onintr);
391#endif
392 draino();
393 if (!inopen) {
394 pstop();
395 setlastchar('\n');
396#ifdef CBREAK
397 }
398#else
399 } else
400 vraw();
401#endif
402 error("\nInterrupt" + inopen);
403}
404
405/*
406 * If we are interruptible, enable interrupts again.
407 * In some critical sections we turn interrupts off,
408 * but not very often.
409 */
410setrupt()
411{
412
413 if (ruptible)
414#ifndef CBREAK
415 signal(SIGINT, onintr);
416#else
417 signal(SIGINT, inopen ? vintr : onintr);
418#endif
419}
420
421preserve()
422{
423
424 synctmp();
425 pid = fork();
426 if (pid < 0)
427 return (0);
428 if (pid == 0) {
429 close(0);
430 dup(tfile);
431 execl(EXPRESERVE, "expreserve", (char *) 0);
432 exit(1);
433 }
434 waitfor();
435 if (rpid == pid && status == 0)
436 return (1);
437 return (0);
438}
439
440#ifndef V6
441exit(i)
442 int i;
443{
444
445 _exit(i);
446}
447#endif
448
449/*
450 * Return last component of unix path name p.
451 */
452char *
453tailpath(p)
454register char *p;
455{
456 register char *r;
457
458 for (r=p; *p; p++)
459 if (*p == '/')
460 r = p+1;
461 return(r);
462}