Commit | Line | Data |
---|---|---|
1e64b3ba JH |
1 | /*- |
2 | * Copyright (c) 1992, 1993 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. All advertising materials mentioning features or use of this software | |
14 | * must display the following acknowledgement: | |
15 | * This product includes software developed by the University of | |
16 | * California, Berkeley and its contributors. | |
17 | * 4. Neither the name of the University nor the names of its contributors | |
18 | * may be used to endorse or promote products derived from this software | |
19 | * without specific prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
31 | * SUCH DAMAGE. | |
32 | */ | |
33 | ||
34 | #ifndef lint | |
35 | static char copyright[] = | |
36 | "@(#) Copyright (c) 1992, 1993\n\ | |
37 | The Regents of the University of California. All rights reserved.\n"; | |
38 | #endif /* not lint */ | |
39 | ||
40 | #ifndef lint | |
178c26c5 | 41 | static char sccsid[] = "@(#)main.c 8.65 (Berkeley) 1/23/94"; |
1e64b3ba JH |
42 | #endif /* not lint */ |
43 | ||
44 | #include <sys/param.h> | |
45 | #include <sys/stat.h> | |
46 | ||
47 | #include <ctype.h> | |
48 | #include <err.h> | |
49 | #include <errno.h> | |
50 | #include <fcntl.h> | |
51 | #include <stdlib.h> | |
52 | #include <string.h> | |
53 | #include <termios.h> | |
54 | #include <unistd.h> | |
55 | ||
56 | #ifdef __STDC__ | |
57 | #include <stdarg.h> | |
58 | #else | |
59 | #include <varargs.h> | |
60 | #endif | |
61 | ||
62 | #include "vi.h" | |
63 | #include "excmd.h" | |
64 | #include "pathnames.h" | |
65 | #include "tag.h" | |
66 | ||
67 | static int exrc_isok __P((SCR *, char *, int)); | |
68 | static void gs_end __P((GS *)); | |
69 | static GS *gs_init __P((void)); | |
70 | static void h_hup __P((int)); | |
71 | static void h_term __P((int)); | |
72 | static void h_winch __P((int)); | |
73 | static void obsolete __P((char *[])); | |
74 | static void usage __P((int)); | |
75 | ||
76 | GS *__global_list; /* GLOBAL: List of screens. */ | |
77 | ||
78 | int | |
79 | main(argc, argv) | |
80 | int argc; | |
81 | char *argv[]; | |
82 | { | |
83 | extern int optind; | |
84 | extern char *optarg; | |
85 | static int reenter; /* STATIC: Re-entrancy check. */ | |
86 | struct sigaction act; | |
87 | GS *gp; | |
88 | FREF *frp; | |
89 | SCR *sp; | |
90 | u_int flags, saved_vi_mode; | |
91 | int ch, eval, flagchk, readonly, silent, snapshot; | |
92 | char *excmdarg, *myname, *p, *rec_f, *tag_f, *trace_f, *wsizearg; | |
93 | char path[MAXPATHLEN]; | |
94 | ||
95 | /* Stop if indirecting through a NULL pointer. */ | |
96 | if (reenter++) | |
97 | abort(); | |
98 | ||
99 | /* Set screen type and mode based on the program name. */ | |
100 | readonly = 0; | |
101 | if ((myname = strrchr(*argv, '/')) == NULL) | |
102 | myname = *argv; | |
103 | else | |
104 | ++myname; | |
105 | if (!strcmp(myname, "ex") || !strcmp(myname, "nex")) | |
106 | LF_INIT(S_EX); | |
107 | else { | |
108 | /* View is readonly. */ | |
109 | if (!strcmp(myname, "view")) | |
110 | readonly = 1; | |
111 | LF_INIT(S_VI_CURSES); | |
112 | } | |
113 | saved_vi_mode = S_VI_CURSES; | |
114 | ||
115 | /* Convert old-style arguments into new-style ones. */ | |
116 | obsolete(argv); | |
117 | ||
118 | /* Parse the arguments. */ | |
119 | flagchk = '\0'; | |
120 | excmdarg = rec_f = tag_f = trace_f = wsizearg = NULL; | |
121 | silent = 0; | |
122 | snapshot = 1; | |
123 | while ((ch = getopt(argc, argv, "c:eFlRr:sT:t:vw:x:")) != EOF) | |
124 | switch (ch) { | |
125 | case 'c': /* Run the command. */ | |
126 | excmdarg = optarg; | |
127 | break; | |
128 | case 'e': /* Ex mode. */ | |
129 | LF_CLR(S_SCREENS); | |
130 | LF_SET(S_EX); | |
131 | break; | |
132 | case 'F': /* No snapshot. */ | |
133 | snapshot = 0; | |
134 | break; | |
135 | case 'l': | |
136 | if (flagchk != '\0' && flagchk != 'l') | |
137 | errx(1, | |
138 | "only one of -%c and -l may be specified.", | |
139 | flagchk); | |
140 | flagchk = 'l'; | |
141 | break; | |
142 | case 'R': /* Readonly. */ | |
143 | readonly = 1; | |
144 | break; | |
145 | case 'r': /* Recover. */ | |
146 | if (flagchk == 'r') | |
147 | errx(1, | |
148 | "only one recovery file may be specified."); | |
149 | if (flagchk != '\0') | |
150 | errx(1, | |
151 | "only one of -%c and -r may be specified.", | |
152 | flagchk); | |
153 | flagchk = 'r'; | |
154 | rec_f = optarg; | |
155 | break; | |
156 | case 's': | |
157 | if (!LF_ISSET(S_EX)) | |
158 | errx(1, "-s only applicable to ex."); | |
159 | silent = 1; | |
160 | break; | |
161 | case 'T': /* Trace. */ | |
162 | trace_f = optarg; | |
163 | break; | |
164 | case 't': /* Tag. */ | |
165 | if (flagchk == 't') | |
166 | errx(1, | |
167 | "only one tag file may be specified."); | |
168 | if (flagchk != '\0') | |
169 | errx(1, | |
170 | "only one of -%c and -t may be specified.", | |
171 | flagchk); | |
172 | flagchk = 't'; | |
173 | tag_f = optarg; | |
174 | break; | |
175 | case 'v': /* Vi mode. */ | |
176 | LF_CLR(S_SCREENS); | |
177 | LF_SET(S_VI_CURSES); | |
178 | break; | |
179 | case 'w': | |
180 | wsizearg = optarg; | |
181 | break; | |
182 | case 'x': | |
183 | if (!strcmp(optarg, "aw")) { | |
184 | LF_CLR(S_SCREENS); | |
185 | LF_SET(S_VI_XAW); | |
186 | saved_vi_mode = S_VI_XAW; | |
187 | break; | |
188 | } | |
189 | /* FALLTHROUGH */ | |
190 | case '?': | |
191 | default: | |
192 | usage(LF_ISSET(S_EX)); | |
193 | } | |
194 | argc -= optind; | |
195 | argv += optind; | |
196 | ||
197 | /* Build and initialize the GS structure. */ | |
198 | __global_list = gp = gs_init(); | |
199 | ||
200 | if (snapshot) | |
201 | F_SET(gp, G_SNAPSHOT); | |
202 | ||
178c26c5 JH |
203 | /* |
204 | * Build and initialize the first/current screen. This is a bit | |
205 | * tricky. If an error is returned, we may or may not have a | |
206 | * screen structure. If we have a screen structure, put it on a | |
207 | * display queue so that the error messages get displayed. | |
208 | */ | |
1e64b3ba | 209 | if (screen_init(NULL, &sp, flags)) { |
178c26c5 JH |
210 | if (sp != NULL) |
211 | CIRCLEQ_INSERT_HEAD(&__global_list->dq, sp, q); | |
212 | goto err; | |
1e64b3ba JH |
213 | } |
214 | sp->saved_vi_mode = saved_vi_mode; | |
215 | CIRCLEQ_INSERT_HEAD(&__global_list->dq, sp, q); | |
216 | ||
217 | if (trace_f != NULL) { | |
218 | #ifdef DEBUG | |
219 | if ((gp->tracefp = fopen(optarg, "w")) == NULL) | |
220 | err(1, "%s", optarg); | |
221 | (void)fprintf(gp->tracefp, "\n===\ntrace: open %s\n", optarg); | |
222 | #else | |
223 | msgq(sp, M_ERR, "-T support not compiled into this version."); | |
224 | #endif | |
225 | } | |
226 | ||
227 | if (set_window_size(sp, 0, 0)) /* Set the window size. */ | |
178c26c5 | 228 | goto err; |
1e64b3ba | 229 | if (opts_init(sp)) /* Options initialization. */ |
178c26c5 JH |
230 | goto err; |
231 | if (readonly) /* Global read-only bit. */ | |
1e64b3ba | 232 | O_SET(sp, O_READONLY); |
178c26c5 | 233 | if (silent) { /* Ex batch mode. */ |
1e64b3ba JH |
234 | O_CLR(sp, O_AUTOPRINT); |
235 | O_CLR(sp, O_PROMPT); | |
236 | O_CLR(sp, O_VERBOSE); | |
237 | O_CLR(sp, O_WARN); | |
238 | F_SET(sp, S_EXSILENT); | |
239 | } | |
240 | if (wsizearg != NULL) { | |
241 | ARGS *av[2], a, b; | |
242 | if (strtol(optarg, &p, 10) < 0 || *p) | |
243 | errx(1, "illegal window size -- %s", optarg); | |
244 | (void)snprintf(path, sizeof(path), "window=%s", optarg); | |
178c26c5 | 245 | a.bp = (CHAR_T *)path; |
1e64b3ba JH |
246 | a.len = strlen(path); |
247 | b.bp = NULL; | |
248 | b.len = 0; | |
249 | av[0] = &a; | |
250 | av[1] = &b; | |
251 | if (opts_set(sp, av)) | |
252 | msgq(sp, M_ERR, | |
253 | "Unable to set command line window option"); | |
254 | } | |
255 | ||
256 | /* Keymaps, special keys, must follow option initializations. */ | |
257 | if (term_init(sp)) | |
178c26c5 | 258 | goto err; |
1e64b3ba JH |
259 | |
260 | #ifdef DIGRAPHS | |
261 | if (digraph_init(sp)) /* Digraph initialization. */ | |
178c26c5 | 262 | goto err; |
1e64b3ba JH |
263 | #endif |
264 | ||
265 | /* | |
266 | * Source the system, environment, ~user and local .exrc values. | |
267 | * Vi historically didn't check ~user/.exrc if the environment | |
268 | * variable EXINIT was set. This is all done before the file is | |
269 | * read in because things in the .exrc information can set, for | |
270 | * example, the recovery directory. | |
271 | * | |
272 | * !!! | |
273 | * While nvi can handle any of the options settings of historic vi, | |
274 | * the converse is not true. Since users are going to have to have | |
275 | * files and environmental variables that work with both, we use nvi | |
276 | * versions if they exist, otherwise the historic ones. | |
277 | */ | |
278 | if (!silent) { | |
279 | if (exrc_isok(sp, _PATH_SYSEXRC, 1)) | |
280 | (void)ex_cfile(sp, NULL, _PATH_SYSEXRC); | |
281 | ||
282 | /* Source the {N,}EXINIT environment variable. */ | |
283 | if ((p = getenv("NEXINIT")) != NULL || | |
284 | (p = getenv("EXINIT")) != NULL) | |
285 | if ((p = strdup(p)) == NULL) { | |
286 | msgq(sp, M_SYSERR, NULL); | |
178c26c5 | 287 | goto err; |
1e64b3ba JH |
288 | } else { |
289 | (void)ex_icmd(sp, NULL, p, strlen(p)); | |
290 | free(p); | |
291 | } | |
292 | else if ((p = getenv("HOME")) != NULL && *p) { | |
293 | (void)snprintf(path, | |
294 | sizeof(path), "%s/%s", p, _PATH_NEXRC); | |
295 | if (exrc_isok(sp, path, 0)) | |
296 | (void)ex_cfile(sp, NULL, path); | |
297 | else { | |
298 | (void)snprintf(path, | |
299 | sizeof(path), "%s/%s", p, _PATH_EXRC); | |
300 | if (exrc_isok(sp, path, 0)) | |
301 | (void)ex_cfile(sp, NULL, path); | |
302 | } | |
303 | } | |
304 | /* | |
305 | * !!! | |
306 | * According to O'Reilly ("Learning the VI Editor", Fifth Ed., | |
307 | * May 1992, page 106), System V release 3.2 and later, has an | |
308 | * option "[no]exrc", causing vi to not "read .exrc files in | |
309 | * the current directory unless you first set the exrc option | |
310 | * in your home directory's .exrc file". Yeah, right. Did | |
311 | * someone actually believe that users would change their home | |
312 | * .exrc file based on whether or not they wanted to source the | |
313 | * current local .exrc? Or that users would want ALL the local | |
314 | * .exrc files on some systems, and none of them on others? | |
315 | * I think not. | |
316 | * | |
317 | * Apply the same tests to local .exrc files that are applied | |
318 | * to any other .exrc file. | |
319 | */ | |
320 | if (exrc_isok(sp, _PATH_EXRC, 0)) | |
321 | (void)ex_cfile(sp, NULL, _PATH_EXRC); | |
322 | } | |
323 | ||
324 | /* List recovery files if -l specified. */ | |
325 | if (flagchk == 'l') | |
326 | exit(rcv_list(sp)); | |
327 | ||
328 | /* Use a tag file or recovery file if specified. */ | |
329 | if (tag_f != NULL && ex_tagfirst(sp, tag_f)) | |
178c26c5 | 330 | goto err; |
1e64b3ba | 331 | else if (rec_f != NULL && rcv_read(sp, rec_f)) |
178c26c5 | 332 | goto err; |
1e64b3ba JH |
333 | |
334 | /* Append any remaining arguments as file names. */ | |
335 | if (*argv != NULL) | |
336 | for (; *argv != NULL; ++argv) | |
337 | if (file_add(sp, NULL, *argv, 0) == NULL) | |
178c26c5 | 338 | goto err; |
1e64b3ba JH |
339 | |
340 | /* | |
341 | * If no recovery or tag file, get an EXF structure. | |
342 | * If no argv file, use a temporary file. | |
343 | */ | |
344 | if (tag_f == NULL && rec_f == NULL) { | |
345 | if ((frp = file_first(sp)) == NULL && | |
346 | (frp = file_add(sp, NULL, NULL, 1)) == NULL) | |
178c26c5 | 347 | goto err; |
1e64b3ba | 348 | if (file_init(sp, frp, NULL, 0)) |
178c26c5 | 349 | goto err; |
1e64b3ba JH |
350 | } |
351 | ||
352 | /* Set up the argument pointer. */ | |
353 | sp->a_frp = sp->frp; | |
354 | ||
355 | /* | |
356 | * Initialize the signals. Use sigaction(2), not signal(3), because | |
357 | * we don't want to always restart system calls on 4BSD systems. It | |
358 | * would be nice in some cases to restart system calls, but SA_RESTART | |
359 | * is a 4BSD extension so we can't use it. | |
360 | * | |
361 | * SIGWINCH, SIGHUP, SIGTERM: | |
362 | * Catch and set a global bit. | |
363 | */ | |
364 | act.sa_handler = h_hup; | |
365 | sigemptyset(&act.sa_mask); | |
366 | act.sa_flags = 0; | |
367 | (void)sigaction(SIGHUP, &act, NULL); | |
368 | act.sa_handler = h_term; | |
369 | sigemptyset(&act.sa_mask); | |
370 | act.sa_flags = 0; | |
371 | (void)sigaction(SIGTERM, &act, NULL); | |
372 | act.sa_handler = h_winch; | |
373 | sigemptyset(&act.sa_mask); | |
374 | act.sa_flags = 0; | |
375 | (void)sigaction(SIGWINCH, &act, NULL); | |
376 | ||
377 | /* | |
378 | * SIGQUIT: | |
379 | * Always ignore. | |
380 | */ | |
381 | act.sa_handler = SIG_IGN; | |
382 | sigemptyset(&act.sa_mask); | |
383 | act.sa_flags = 0; | |
384 | (void)sigaction(SIGQUIT, &act, NULL); | |
385 | ||
386 | /* | |
387 | * If there's an initial command, push it on the command stack. | |
388 | * Historically, it was always an ex command, not vi in vi mode | |
389 | * or ex in ex mode. So, make it look like an ex command to vi. | |
390 | */ | |
391 | if (excmdarg != NULL) | |
392 | if (IN_EX_MODE(sp)) { | |
393 | if (term_push(sp, excmdarg, strlen(excmdarg), 0, 0)) | |
178c26c5 | 394 | goto err; |
1e64b3ba JH |
395 | } else if (IN_VI_MODE(sp)) { |
396 | if (term_push(sp, "\n", 1, 0, 0)) | |
178c26c5 | 397 | goto err; |
1e64b3ba | 398 | if (term_push(sp, excmdarg, strlen(excmdarg), 0, 0)) |
178c26c5 | 399 | goto err; |
1e64b3ba | 400 | if (term_push(sp, ":", 1, 0, 0)) |
178c26c5 | 401 | goto err; |
1e64b3ba JH |
402 | } |
403 | ||
404 | /* Vi reads from the terminal. */ | |
405 | if (!F_ISSET(gp, G_ISFROMTTY) && !F_ISSET(sp, S_EX)) { | |
406 | msgq(sp, M_ERR, "Vi's standard input must be a terminal."); | |
178c26c5 | 407 | goto err; |
1e64b3ba JH |
408 | } |
409 | ||
410 | for (;;) { | |
411 | if (sp->s_edit(sp, sp->ep)) | |
178c26c5 | 412 | goto err; |
1e64b3ba JH |
413 | |
414 | /* | |
415 | * Edit the next screen on the display queue, or, move | |
416 | * a screen from the hidden queue to the display queue. | |
417 | */ | |
418 | if ((sp = __global_list->dq.cqh_first) == | |
419 | (void *)&__global_list->dq) | |
420 | if ((sp = __global_list->hq.cqh_first) != | |
421 | (void *)&__global_list->hq) { | |
422 | CIRCLEQ_REMOVE(&sp->gp->hq, sp, q); | |
423 | CIRCLEQ_INSERT_TAIL(&sp->gp->dq, sp, q); | |
424 | } else | |
425 | break; | |
426 | ||
427 | /* | |
428 | * The screen type may have changed -- reinitialize the | |
429 | * functions in case it has. | |
430 | */ | |
431 | switch (F_ISSET(sp, S_SCREENS)) { | |
432 | case S_EX: | |
433 | if (sex_screen_init(sp)) | |
178c26c5 | 434 | goto err; |
1e64b3ba JH |
435 | break; |
436 | case S_VI_CURSES: | |
437 | if (svi_screen_init(sp)) | |
178c26c5 | 438 | goto err; |
1e64b3ba JH |
439 | break; |
440 | case S_VI_XAW: | |
441 | if (xaw_screen_init(sp)) | |
178c26c5 | 442 | goto err; |
1e64b3ba JH |
443 | break; |
444 | default: | |
445 | abort(); | |
446 | } | |
447 | } | |
448 | ||
1e64b3ba | 449 | eval = 0; |
178c26c5 JH |
450 | if (0) |
451 | err: eval = 1; | |
1e64b3ba | 452 | |
178c26c5 JH |
453 | /* |
454 | * NOTE: sp may be GONE when the screen returns, so only | |
455 | * the gp can be trusted. | |
456 | */ | |
1e64b3ba JH |
457 | gs_end(gp); |
458 | ||
459 | /* | |
460 | * XXX | |
461 | * Make absolutely sure that the modes are restored correctly. | |
462 | * | |
463 | * This should no longer be needed, and it's here to handle what I | |
464 | * believe are SunOS/Solaris curses problems. The problem is that | |
465 | * for some unknown reason, when endwin() is called in the svi | |
466 | * routines, it isn't resetting the terminal correctly. I have not | |
467 | * been able to figure it out, so this resets the terminal to the | |
468 | * right modes regardless. The problem is that, in most tty driver | |
469 | * implementations, you can only reset the terminal modes once | |
470 | * (changing from !ICANON to ICANON) without losing the re-parsing | |
471 | * effect on the pending input. This means that this "fix" will make | |
472 | * other systems mess up characters typed after the quit command to | |
473 | * vi but before vi actually exits. | |
474 | */ | |
178c26c5 JH |
475 | if (F_ISSET(gp, G_ISFROMTTY)) |
476 | (void)tcsetattr(STDIN_FILENO, TCSADRAIN, &gp->original_termios); | |
1e64b3ba JH |
477 | exit(eval); |
478 | } | |
479 | ||
480 | /* | |
481 | * gs_init -- | |
482 | * Build and initialize the GS structure. | |
483 | */ | |
484 | static GS * | |
485 | gs_init() | |
486 | { | |
487 | GS *gp; | |
488 | int fd; | |
489 | ||
490 | CALLOC_NOMSG(NULL, gp, GS *, 1, sizeof(GS)); | |
491 | if (gp == NULL) | |
492 | err(1, NULL); | |
493 | ||
494 | CIRCLEQ_INIT(&gp->dq); | |
495 | CIRCLEQ_INIT(&gp->hq); | |
496 | LIST_INIT(&gp->msgq); | |
497 | ||
498 | /* Structures shared by screens so stored in the GS structure. */ | |
499 | CALLOC_NOMSG(NULL, gp->tty, IBUF *, 1, sizeof(IBUF)); | |
500 | if (gp->tty == NULL) | |
501 | err(1, NULL); | |
502 | ||
503 | LIST_INIT(&gp->cutq); | |
504 | LIST_INIT(&gp->seqq); | |
505 | ||
506 | /* Set a flag if we're reading from the tty. */ | |
507 | if (isatty(STDIN_FILENO)) | |
508 | F_SET(gp, G_ISFROMTTY); | |
509 | ||
510 | /* | |
511 | * XXX | |
512 | * Set a flag and don't do terminal sets/resets if the input isn't | |
513 | * from a tty. Under all circumstances put reasonable things into | |
514 | * the original_termios field, as some routines (seq.c:seq_save() | |
515 | * and term.c:term_init()) want values for special characters. | |
516 | */ | |
517 | if (F_ISSET(gp, G_ISFROMTTY)) { | |
518 | if (tcgetattr(STDIN_FILENO, &gp->original_termios)) | |
519 | err(1, "tcgetattr"); | |
520 | } else { | |
521 | if ((fd = open(_PATH_TTY, O_RDONLY, 0)) == -1) | |
522 | err(1, "%s", _PATH_TTY); | |
523 | if (tcgetattr(fd, &gp->original_termios)) | |
524 | err(1, "tcgetattr"); | |
525 | (void)close(fd); | |
526 | } | |
527 | ||
528 | return (gp); | |
529 | } | |
530 | ||
531 | ||
532 | /* | |
533 | * gs_end -- | |
534 | * End the GS structure. | |
535 | */ | |
536 | static void | |
537 | gs_end(gp) | |
538 | GS *gp; | |
539 | { | |
540 | MSG *mp; | |
541 | SCR *sp; | |
542 | char *tty; | |
543 | ||
544 | /* Reset anything that needs resetting. */ | |
545 | if (gp->flags & G_SETMODE) /* O_MESG */ | |
546 | if ((tty = ttyname(STDERR_FILENO)) == NULL) | |
547 | warn("ttyname"); | |
548 | else if (chmod(tty, gp->origmode) < 0) | |
549 | warn("%s", tty); | |
550 | ||
551 | /* Ring the bell if scheduled. */ | |
552 | if (F_ISSET(gp, G_BELLSCHED)) | |
553 | (void)fprintf(stderr, "\07"); /* \a */ | |
554 | ||
555 | /* If there are any remaining screens, flush their messages. */ | |
556 | for (sp = __global_list->dq.cqh_first; | |
557 | sp != (void *)&__global_list->dq; sp = sp->q.cqe_next) | |
558 | for (mp = sp->msgq.lh_first; | |
559 | mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next) | |
560 | (void)fprintf(stderr, "%.*s\n", (int)mp->len, mp->mbuf); | |
561 | for (sp = __global_list->hq.cqh_first; | |
562 | sp != (void *)&__global_list->hq; sp = sp->q.cqe_next) | |
563 | for (mp = sp->msgq.lh_first; | |
564 | mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next) | |
565 | (void)fprintf(stderr, "%.*s\n", (int)mp->len, mp->mbuf); | |
566 | /* Flush messages on the global queue. */ | |
567 | for (mp = gp->msgq.lh_first; | |
568 | mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next) | |
569 | (void)fprintf(stderr, "%.*s\n", (int)mp->len, mp->mbuf); | |
570 | ||
571 | if (gp->special_key != NULL) | |
572 | FREE(gp->special_key, MAX_FAST_KEY); | |
573 | ||
574 | /* | |
575 | * DON'T FREE THE GLOBAL STRUCTURE -- WE DIDN'T TURN | |
576 | * OFF SIGNALS/TIMERS, SO IT MAY STILL BE REFERENCED. | |
577 | */ | |
578 | } | |
579 | ||
580 | /* | |
581 | * h_hup -- | |
582 | * Handle SIGHUP. | |
583 | */ | |
584 | static void | |
585 | h_hup(signo) | |
586 | int signo; | |
587 | { | |
588 | F_SET(__global_list, G_SIGHUP); | |
589 | ||
590 | /* | |
591 | * If we're asleep, just die. | |
592 | * | |
593 | * XXX | |
594 | * This isn't right if the windows are independent. | |
595 | */ | |
596 | if (F_ISSET(__global_list, G_SLEEPING)) | |
597 | rcv_hup(); | |
598 | } | |
599 | ||
600 | /* | |
601 | * h_term -- | |
602 | * Handle SIGTERM. | |
603 | */ | |
604 | static void | |
605 | h_term(signo) | |
606 | int signo; | |
607 | { | |
608 | F_SET(__global_list, G_SIGTERM); | |
609 | ||
610 | /* | |
611 | * If we're asleep, just die. | |
612 | * | |
613 | * XXX | |
614 | * This isn't right if the windows are independent. | |
615 | */ | |
616 | if (F_ISSET(__global_list, G_SLEEPING)) | |
617 | rcv_term(); | |
618 | } | |
619 | ||
620 | /* | |
621 | * h_winch -- | |
622 | * Handle SIGWINCH. | |
623 | */ | |
624 | static void | |
625 | h_winch(signo) | |
626 | int signo; | |
627 | { | |
628 | F_SET(__global_list, G_SIGWINCH); | |
629 | } | |
630 | ||
631 | /* | |
632 | * exrc_isok -- | |
633 | * Check a .exrc for source-ability. | |
634 | */ | |
635 | static int | |
636 | exrc_isok(sp, path, rootok) | |
637 | SCR *sp; | |
638 | char *path; | |
639 | int rootok; | |
640 | { | |
641 | struct stat sb; | |
642 | uid_t uid; | |
643 | char *emsg, buf[MAXPATHLEN]; | |
644 | ||
645 | /* Check for the file's existence. */ | |
646 | if (stat(path, &sb)) | |
647 | return (0); | |
648 | ||
649 | /* | |
650 | * !!! | |
651 | * Historically, vi did not read the .exrc files if they were owned | |
652 | * by someone other than the user, unless the undocumented option | |
653 | * sourceany was set. We don't support the sourceany option. We | |
654 | * check that the user (or root, for system files) owns the file and | |
655 | * require that it not be writeable by anyone other than the owner. | |
656 | */ | |
657 | ||
658 | /* Owned by the user or root. */ | |
659 | uid = getuid(); | |
660 | if (rootok) { | |
661 | if (sb.st_uid != uid && sb.st_uid != 0) { | |
662 | emsg = "not owned by you or root"; | |
663 | goto err; | |
664 | } | |
665 | } else | |
666 | if (sb.st_uid != uid) { | |
667 | emsg = "not owned by you"; | |
668 | goto err; | |
669 | } | |
670 | ||
671 | /* Not writeable by anyone but the owner. */ | |
672 | if (sb.st_mode & (S_IWGRP | S_IWOTH)) { | |
673 | emsg = "writeable by a user other than the owner"; | |
674 | err: if (strchr(path, '/') == NULL && | |
675 | getcwd(buf, sizeof(buf)) != NULL) | |
676 | msgq(sp, M_ERR, | |
677 | "%s/%s: not sourced: %s.", buf, path, emsg); | |
678 | else | |
679 | msgq(sp, M_ERR, | |
680 | "%s: not sourced: %s.", path, emsg); | |
681 | return (0); | |
682 | } | |
683 | return (1); | |
684 | } | |
685 | ||
686 | static void | |
687 | obsolete(argv) | |
688 | char *argv[]; | |
689 | { | |
690 | size_t len; | |
691 | char *p, *myname; | |
692 | ||
693 | /* | |
694 | * Translate old style arguments into something getopt will like. | |
695 | * Make sure it's not text space memory, because ex changes the | |
696 | * strings. | |
697 | * Change "+" into "-c$". | |
698 | * Change "+<anything else>" into "-c<anything else>". | |
699 | * Change "-" into "-s" | |
700 | * Change "-r" into "-l" | |
701 | */ | |
702 | for (myname = argv[0]; *++argv;) | |
703 | if (argv[0][0] == '+') { | |
704 | if (argv[0][1] == '\0') { | |
705 | MALLOC_NOMSG(NULL, argv[0], char *, 4); | |
706 | if (argv[0] == NULL) | |
707 | err(1, NULL); | |
708 | (void)strcpy(argv[0], "-c$"); | |
709 | } else { | |
710 | p = argv[0]; | |
711 | len = strlen(argv[0]); | |
712 | MALLOC_NOMSG(NULL, argv[0], char *, len + 2); | |
713 | if (argv[0] == NULL) | |
714 | err(1, NULL); | |
715 | argv[0][0] = '-'; | |
716 | argv[0][1] = 'c'; | |
717 | (void)strcpy(argv[0] + 2, p + 1); | |
718 | } | |
719 | } else if (argv[0][0] == '-') { | |
720 | if (argv[0][1] == 'r') { | |
721 | if (argv[0][2] == '\0' && argv[1] == NULL) | |
722 | argv[0][1] = 'l'; | |
723 | } else if (argv[0][1] == '\0') { | |
724 | MALLOC_NOMSG(NULL, argv[0], char *, 3); | |
725 | if (argv[0] == NULL) | |
726 | err(1, NULL); | |
727 | (void)strcpy(argv[0], "-s"); | |
728 | } | |
729 | } | |
730 | } | |
731 | ||
732 | static void | |
733 | usage(is_ex) | |
734 | int is_ex; | |
735 | { | |
736 | #define EX_USAGE \ | |
737 | "usage: ex [-eFlRsv] [-c command] [-r file] [-t tag] [-w size] [-x aw]" | |
738 | #define VI_USAGE \ | |
739 | "usage: vi [-eFlRv] [-c command] [-r file] [-t tag] [-w size] [-x aw]" | |
740 | ||
741 | (void)fprintf(stderr, "%s\n", is_ex ? EX_USAGE : VI_USAGE); | |
742 | exit(1); | |
743 | } |