2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
5 * %sccs.include.redist.c%
9 static char copyright
[] =
10 "%Z% Copyright (c) 1992, 1993\n\
11 The Regents of the University of California. All rights reserved.\n";
15 static char sccsid
[] = "$Id: main.c,v 8.56 1993/12/20 17:29:51 bostic Exp $ (Berkeley) $Date: 1993/12/20 17:29:51 $";
18 #include <sys/param.h>
38 #include "pathnames.h"
41 static int exrc_isok
__P((SCR
*, char *, int));
42 static void gs_end
__P((GS
*));
43 static GS
*gs_init
__P((void));
44 static void h_hup
__P((int));
45 static void h_term
__P((int));
46 static void h_winch
__P((int));
47 static void obsolete
__P((char *[]));
48 static void usage
__P((int));
50 GS
*__global_list
; /* GLOBAL: List of screens. */
59 static int reenter
; /* STATIC: Re-entrancy check. */
65 u_int flags
, saved_vi_mode
;
66 int ch
, eval
, flagchk
, readonly
, silent
, snapshot
;
67 char *excmdarg
, *myname
, *p
, *rec_f
, *tag_f
, *trace_f
, *wsizearg
;
68 char path
[MAXPATHLEN
];
70 /* Stop if indirecting through a NULL pointer. */
74 /* Set screen type and mode based on the program name. */
76 if ((myname
= strrchr(*argv
, '/')) == NULL
)
80 if (!strcmp(myname
, "ex") || !strcmp(myname
, "nex"))
83 /* View is readonly. */
84 if (!strcmp(myname
, "view"))
88 saved_vi_mode
= S_VI_CURSES
;
90 /* Convert old-style arguments into new-style ones. */
93 /* Parse the arguments. */
95 excmdarg
= rec_f
= tag_f
= trace_f
= wsizearg
= NULL
;
98 while ((ch
= getopt(argc
, argv
, "c:eFlRr:sT:t:vw:x:")) != EOF
)
100 case 'c': /* Run the command. */
103 case 'e': /* Ex mode. */
107 case 'F': /* No snapshot. */
111 if (flagchk
!= '\0' && flagchk
!= 'l')
113 "only one of -%c and -l may be specified.",
117 case 'R': /* Readonly. */
120 case 'r': /* Recover. */
123 "only one recovery file may be specified.");
126 "only one of -%c and -r may be specified.",
133 errx(1, "-s only applicable to ex.");
136 case 'T': /* Trace. */
142 "only one tag file may be specified.");
145 "only one of -%c and -t may be specified.",
150 case 'v': /* Vi mode. */
158 if (!strcmp(optarg
, "aw")) {
161 saved_vi_mode
= S_VI_XAW
;
167 usage(LF_ISSET(S_EX
));
172 /* Build and initialize the GS structure. */
173 __global_list
= gp
= gs_init();
176 F_SET(gp
, G_SNAPSHOT
);
178 /* Build and initialize the first/current screen. */
179 if (screen_init(NULL
, &sp
, flags
))
181 sp
->saved_vi_mode
= saved_vi_mode
;
182 CIRCLEQ_INSERT_HEAD(&__global_list
->dq
, sp
, q
);
184 if (trace_f
!= NULL
) {
186 if ((gp
->tracefp
= fopen(optarg
, "w")) == NULL
)
187 err(1, "%s", optarg
);
188 (void)fprintf(gp
->tracefp
, "\n===\ntrace: open %s\n", optarg
);
190 msgq(sp
, M_ERR
, "-T support not compiled into this version.");
194 if (set_window_size(sp
, 0, 0)) /* Set the window size. */
197 if (opts_init(sp
)) /* Options initialization. */
200 O_SET(sp
, O_READONLY
);
202 O_CLR(sp
, O_AUTOPRINT
);
204 O_CLR(sp
, O_VERBOSE
);
206 F_SET(sp
, S_EXSILENT
);
208 if (wsizearg
!= NULL
) {
210 if (strtol(optarg
, &p
, 10) < 0 || *p
)
211 errx(1, "illegal window size -- %s", optarg
);
212 (void)snprintf(path
, sizeof(path
), "window=%s", optarg
);
214 a
.len
= strlen(path
);
219 if (opts_set(sp
, av
))
221 "Unable to set command line window option");
224 /* Keymaps, special keys, must follow option initializations. */
229 if (digraph_init(sp
)) /* Digraph initialization. */
234 * Source the system, environment, ~user and local .exrc values.
235 * If the environment exists, vi historically doesn't check ~user.
236 * This is done before the file is read in because things in the
237 * .exrc information can set, for example, the recovery directory.
240 if (exrc_isok(sp
, _PATH_SYSEXRC
, 1))
241 (void)ex_cfile(sp
, NULL
, _PATH_SYSEXRC
);
243 /* Source the EXINIT environment variable. */
244 if ((p
= getenv("EXINIT")) != NULL
)
245 if ((p
= strdup(p
)) == NULL
) {
246 msgq(sp
, M_SYSERR
, NULL
);
249 (void)ex_icmd(sp
, NULL
, p
, strlen(p
));
252 else if ((p
= getenv("HOME")) != NULL
&& *p
) {
254 sizeof(path
), "%s/%s", p
, _PATH_NEXRC
);
255 if (exrc_isok(sp
, path
, 0))
256 (void)ex_cfile(sp
, NULL
, path
);
259 sizeof(path
), "%s/%s", p
, _PATH_EXRC
);
260 if (exrc_isok(sp
, path
, 0))
261 (void)ex_cfile(sp
, NULL
, path
);
266 * According to O'Reilly ("Learning the VI Editor", Fifth
267 * Edition, May 1992), System V has an option, "[no]exrc"
268 * which "allows the execution of .exrc files that reside
269 * outside the user's home directory. My expectation is
270 * that this is intended to make sourcing "local" .exrc files
271 * a bit more secure. This isn't a reasonable fix, and it
272 * leaves the trojan horse door widely open. Instead we apply
273 * the same tests to local .exrc files that we apply to the
276 if (exrc_isok(sp
, _PATH_EXRC
, 0))
277 (void)ex_cfile(sp
, NULL
, _PATH_EXRC
);
280 /* List recovery files if -l specified. */
284 /* Use a tag file or recovery file if specified. */
285 if (tag_f
!= NULL
&& ex_tagfirst(sp
, tag_f
))
287 else if (rec_f
!= NULL
&& rcv_read(sp
, rec_f
))
290 /* Append any remaining arguments as file names. */
292 for (; *argv
!= NULL
; ++argv
)
293 if (file_add(sp
, NULL
, *argv
, 0) == NULL
)
297 * If no recovery or tag file, get an EXF structure.
298 * If no argv file, use a temporary file.
300 if (tag_f
== NULL
&& rec_f
== NULL
) {
301 if ((frp
= file_first(sp
)) == NULL
&&
302 (frp
= file_add(sp
, NULL
, NULL
, 1)) == NULL
)
304 if (file_init(sp
, frp
, NULL
, 0))
308 /* Set up the argument pointer. */
312 * Initialize the signals. Use sigaction(2), not signal(3), because
313 * we don't want to always restart system calls on 4BSD systems. It
314 * would be nice in some cases to restart system calls, but SA_RESTART
315 * is a 4BSD extension so we can't use it.
317 * SIGWINCH, SIGHUP, SIGTERM:
318 * Catch and set a global bit.
320 act
.sa_handler
= h_hup
;
321 sigemptyset(&act
.sa_mask
);
323 (void)sigaction(SIGHUP
, &act
, NULL
);
324 act
.sa_handler
= h_term
;
325 sigemptyset(&act
.sa_mask
);
327 (void)sigaction(SIGTERM
, &act
, NULL
);
328 act
.sa_handler
= h_winch
;
329 sigemptyset(&act
.sa_mask
);
331 (void)sigaction(SIGWINCH
, &act
, NULL
);
337 act
.sa_handler
= SIG_IGN
;
338 sigemptyset(&act
.sa_mask
);
340 (void)sigaction(SIGQUIT
, &act
, NULL
);
343 * If there's an initial command, push it on the command stack.
344 * Historically, it was always an ex command, not vi in vi mode
345 * or ex in ex mode. So, make it look like an ex command to vi.
347 if (excmdarg
!= NULL
)
348 if (IN_EX_MODE(sp
)) {
349 if (term_push(sp
, excmdarg
, strlen(excmdarg
), 0, 0))
351 } else if (IN_VI_MODE(sp
)) {
352 if (term_push(sp
, "\n", 1, 0, 0))
354 if (term_push(sp
, excmdarg
, strlen(excmdarg
), 0, 0))
356 if (term_push(sp
, ":", 1, 0, 0))
360 /* Vi reads from the terminal. */
361 if (!F_ISSET(gp
, G_ISFROMTTY
) && !F_ISSET(sp
, S_EX
)) {
362 msgq(sp
, M_ERR
, "Vi's standard input must be a terminal.");
367 if (sp
->s_edit(sp
, sp
->ep
))
371 * Edit the next screen on the display queue, or, move
372 * a screen from the hidden queue to the display queue.
374 if ((sp
= __global_list
->dq
.cqh_first
) ==
375 (void *)&__global_list
->dq
)
376 if ((sp
= __global_list
->hq
.cqh_first
) !=
377 (void *)&__global_list
->hq
) {
378 CIRCLEQ_REMOVE(&sp
->gp
->hq
, sp
, q
);
379 CIRCLEQ_INSERT_TAIL(&sp
->gp
->dq
, sp
, q
);
384 * The screen type may have changed -- reinitialize the
385 * functions in case it has.
387 switch (F_ISSET(sp
, S_SCREENS
)) {
389 if (sex_screen_init(sp
))
393 if (svi_screen_init(sp
))
397 if (xaw_screen_init(sp
))
406 * Two error paths. The first means that something failed before
407 * we called a screen routine. Swap the message pointers between
408 * the SCR and the GS, so messages get displayed. The second is
409 * something failed in a screen. NOTE: sp may be GONE when the
410 * screen returns, so only the gp can be trusted.
414 err1
: gp
->msgq
.lh_first
= sp
->msgq
.lh_first
;
420 /* Make absolutely sure that the modes are restored correctly. */
421 if (F_ISSET(gp
, G_ISFROMTTY
) &&
422 tcsetattr(STDIN_FILENO
, TCSADRAIN
, &gp
->original_termios
))
429 * Build and initialize the GS structure.
437 CALLOC_NOMSG(NULL
, gp
, GS
*, 1, sizeof(GS
));
441 CIRCLEQ_INIT(&gp
->dq
);
442 CIRCLEQ_INIT(&gp
->hq
);
443 LIST_INIT(&gp
->msgq
);
445 /* Structures shared by screens so stored in the GS structure. */
446 CALLOC_NOMSG(NULL
, gp
->tty
, IBUF
*, 1, sizeof(IBUF
));
450 LIST_INIT(&gp
->cutq
);
451 LIST_INIT(&gp
->seqq
);
453 /* Set a flag if we're reading from the tty. */
454 if (isatty(STDIN_FILENO
))
455 F_SET(gp
, G_ISFROMTTY
);
459 * Set a flag and don't do terminal sets/resets if the input isn't
460 * from a tty. Under all circumstances put reasonable things into
461 * the original_termios field, as some routines (seq.c:seq_save()
462 * and term.c:term_init()) want values for special characters.
464 if (F_ISSET(gp
, G_ISFROMTTY
)) {
465 if (tcgetattr(STDIN_FILENO
, &gp
->original_termios
))
468 if ((fd
= open(_PATH_TTY
, O_RDONLY
, 0)) == -1)
469 err(1, "%s", _PATH_TTY
);
470 if (tcgetattr(fd
, &gp
->original_termios
))
481 * End the GS structure.
491 /* Reset anything that needs resetting. */
492 if (gp
->flags
& G_SETMODE
) /* O_MESG */
493 if ((tty
= ttyname(STDERR_FILENO
)) == NULL
)
495 else if (chmod(tty
, gp
->origmode
) < 0)
498 /* Ring the bell if scheduled. */
499 if (F_ISSET(gp
, G_BELLSCHED
))
500 (void)fprintf(stderr
, "\07"); /* \a */
502 /* If there are any remaining screens, flush their messages. */
503 for (sp
= __global_list
->dq
.cqh_first
;
504 sp
!= (void *)&__global_list
->dq
; sp
= sp
->q
.cqe_next
)
505 for (mp
= sp
->msgq
.lh_first
;
506 mp
!= NULL
&& !(F_ISSET(mp
, M_EMPTY
)); mp
= mp
->q
.le_next
)
507 (void)fprintf(stderr
, "%.*s\n", (int)mp
->len
, mp
->mbuf
);
508 for (sp
= __global_list
->hq
.cqh_first
;
509 sp
!= (void *)&__global_list
->hq
; sp
= sp
->q
.cqe_next
)
510 for (mp
= sp
->msgq
.lh_first
;
511 mp
!= NULL
&& !(F_ISSET(mp
, M_EMPTY
)); mp
= mp
->q
.le_next
)
512 (void)fprintf(stderr
, "%.*s\n", (int)mp
->len
, mp
->mbuf
);
513 /* Flush messages on the global queue. */
514 for (mp
= gp
->msgq
.lh_first
;
515 mp
!= NULL
&& !(F_ISSET(mp
, M_EMPTY
)); mp
= mp
->q
.le_next
)
516 (void)fprintf(stderr
, "%.*s\n", (int)mp
->len
, mp
->mbuf
);
518 FREE(gp
->special_key
, MAX_FAST_KEY
);
521 * DON'T FREE THE GLOBAL STRUCTURE -- WE DIDN'T TURN
522 * OFF SIGNALS/TIMERS, SO IT MAY STILL BE REFERENCED.
534 F_SET(__global_list
, G_SIGHUP
);
537 * If we're asleep, just die.
540 * This isn't right if the windows are independent.
542 if (F_ISSET(__global_list
, G_SLEEPING
))
554 F_SET(__global_list
, G_SIGTERM
);
557 * If we're asleep, just die.
560 * This isn't right if the windows are independent.
562 if (F_ISSET(__global_list
, G_SLEEPING
))
574 F_SET(__global_list
, G_SIGWINCH
);
579 * Check a .exrc for source-ability.
582 exrc_isok(sp
, path
, rootok
)
590 /* Check for the file's existence. */
596 * Historically, vi did not read the .exrc files if they were owned
597 * by someone other than the user, unless the undocumented option
598 * sourceany was set. We don't support the sourceany option. We
599 * check that the user (or root, for system files) owns the file and
600 * require that it be unwriteable by anyone other than the owner.
603 /* Owned by the user or root. */
606 if (sb
.st_uid
!= uid
&& sb
.st_uid
!= 0) {
608 "%s not sourced: not owned by you or root.", path
);
612 if (sb
.st_uid
!= uid
) {
614 "%s not sourced: not owned by you.", path
);
618 /* Not writeable by anyone but the owner. */
619 if (sb
.st_mode
& (S_IWGRP
| S_IWOTH
)) {
621 "%s not sourced: writeable by a user other than the owner.",
636 * Translate old style arguments into something getopt will like.
637 * Make sure it's not text space memory, because ex changes the
639 * Change "+" into "-c$".
640 * Change "+<anything else>" into "-c<anything else>".
641 * Change "-" into "-s"
642 * Change "-r" into "-l"
644 for (myname
= argv
[0]; *++argv
;)
645 if (argv
[0][0] == '+') {
646 if (argv
[0][1] == '\0') {
647 MALLOC_NOMSG(NULL
, argv
[0], char *, 4);
650 (void)strcpy(argv
[0], "-c$");
653 len
= strlen(argv
[0]);
654 MALLOC_NOMSG(NULL
, argv
[0], char *, len
+ 2);
659 (void)strcpy(argv
[0] + 2, p
+ 1);
661 } else if (argv
[0][0] == '-') {
662 if (argv
[0][1] == 'r') {
663 if (argv
[0][2] == '\0' && argv
[1] == NULL
)
665 } else if (argv
[0][1] == '\0') {
666 MALLOC_NOMSG(NULL
, argv
[0], char *, 3);
669 (void)strcpy(argv
[0], "-s");
679 "usage: ex [-eFlRsv] [-c command] [-r file] [-t tag] [-w size] [-x aw]"
681 "usage: vi [-eFlRv] [-c command] [-r file] [-t tag] [-w size] [-x aw]"
683 (void)fprintf(stderr
, "%s\n", is_ex
? EX_USAGE
: VI_USAGE
);