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