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.60 1993/12/28 12:28:40 bostic Exp $ (Berkeley) $Date: 1993/12/28 12:28:40 $";
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. */
64 u_int flags
, saved_vi_mode
;
65 int ch
, eval
, flagchk
, readonly
, silent
, snapshot
;
66 char *excmdarg
, *myname
, *p
, *rec_f
, *tag_f
, *trace_f
, *wsizearg
;
67 char path
[MAXPATHLEN
];
69 /* Stop if indirecting through a NULL pointer. */
73 /* Set screen type and mode based on the program name. */
75 if ((myname
= strrchr(*argv
, '/')) == NULL
)
79 if (!strcmp(myname
, "ex") || !strcmp(myname
, "nex"))
82 /* View is readonly. */
83 if (!strcmp(myname
, "view"))
87 saved_vi_mode
= S_VI_CURSES
;
89 /* Convert old-style arguments into new-style ones. */
92 /* Parse the arguments. */
94 excmdarg
= rec_f
= tag_f
= trace_f
= wsizearg
= NULL
;
97 while ((ch
= getopt(argc
, argv
, "c:eFlRr:sT:t:vw:x:")) != EOF
)
99 case 'c': /* Run the command. */
102 case 'e': /* Ex mode. */
106 case 'F': /* No snapshot. */
110 if (flagchk
!= '\0' && flagchk
!= 'l')
112 "only one of -%c and -l may be specified.",
116 case 'R': /* Readonly. */
119 case 'r': /* Recover. */
122 "only one recovery file may be specified.");
125 "only one of -%c and -r may be specified.",
132 errx(1, "-s only applicable to ex.");
135 case 'T': /* Trace. */
141 "only one tag file may be specified.");
144 "only one of -%c and -t may be specified.",
149 case 'v': /* Vi mode. */
157 if (!strcmp(optarg
, "aw")) {
160 saved_vi_mode
= S_VI_XAW
;
166 usage(LF_ISSET(S_EX
));
171 /* Build and initialize the GS structure. */
172 __global_list
= gp
= gs_init();
175 F_SET(gp
, G_SNAPSHOT
);
177 /* Build and initialize the first/current screen. */
178 if (screen_init(NULL
, &sp
, flags
))
180 sp
->saved_vi_mode
= saved_vi_mode
;
181 CIRCLEQ_INSERT_HEAD(&__global_list
->dq
, sp
, q
);
183 if (trace_f
!= NULL
) {
185 if ((gp
->tracefp
= fopen(optarg
, "w")) == NULL
)
186 err(1, "%s", optarg
);
187 (void)fprintf(gp
->tracefp
, "\n===\ntrace: open %s\n", optarg
);
189 msgq(sp
, M_ERR
, "-T support not compiled into this version.");
193 if (set_window_size(sp
, 0, 0)) /* Set the window size. */
196 if (opts_init(sp
)) /* Options initialization. */
199 O_SET(sp
, O_READONLY
);
201 O_CLR(sp
, O_AUTOPRINT
);
203 O_CLR(sp
, O_VERBOSE
);
205 F_SET(sp
, S_EXSILENT
);
207 if (wsizearg
!= NULL
) {
209 if (strtol(optarg
, &p
, 10) < 0 || *p
)
210 errx(1, "illegal window size -- %s", optarg
);
211 (void)snprintf(path
, sizeof(path
), "window=%s", optarg
);
213 a
.len
= strlen(path
);
218 if (opts_set(sp
, av
))
220 "Unable to set command line window option");
223 /* Keymaps, special keys, must follow option initializations. */
228 if (digraph_init(sp
)) /* Digraph initialization. */
233 * Source the system, environment, ~user and local .exrc values.
234 * Vi historically didn't check ~user/.exrc if the environment
235 * variable EXINIT was set. This is all done before the file is
236 * read in because things in the .exrc information can set, for
237 * example, the recovery directory.
240 * While nvi can handle any of the options settings of historic vi,
241 * the converse is not true. Since users are going to have to have
242 * files and environmental variables that work with both, we use nvi
243 * versions if they exist, otherwise the historic ones.
246 if (exrc_isok(sp
, _PATH_SYSEXRC
, 1))
247 (void)ex_cfile(sp
, NULL
, _PATH_SYSEXRC
);
249 /* Source the {N,}EXINIT environment variable. */
250 if ((p
= getenv("NEXINIT")) != NULL
||
251 (p
= getenv("EXINIT")) != NULL
)
252 if ((p
= strdup(p
)) == NULL
) {
253 msgq(sp
, M_SYSERR
, NULL
);
256 (void)ex_icmd(sp
, NULL
, p
, strlen(p
));
259 else if ((p
= getenv("HOME")) != NULL
&& *p
) {
261 sizeof(path
), "%s/%s", p
, _PATH_NEXRC
);
262 if (exrc_isok(sp
, path
, 0))
263 (void)ex_cfile(sp
, NULL
, path
);
266 sizeof(path
), "%s/%s", p
, _PATH_EXRC
);
267 if (exrc_isok(sp
, path
, 0))
268 (void)ex_cfile(sp
, NULL
, path
);
273 * According to O'Reilly ("Learning the VI Editor", Fifth Ed.,
274 * May 1992, page 106), System V release 3.2 and later, has an
275 * option "[no]exrc", causing vi to not "read .exrc files in
276 * the current directory unless you first set the exrc option
277 * in your home directory's .exrc file". Yeah, right. Did
278 * someone actually believe that users would change their home
279 * .exrc file based on whether or not they wanted to source the
280 * current local .exrc? Or that users would want ALL the local
281 * .exrc files on some systems, and none of them on others?
284 * Apply the same tests to local .exrc files that are applied
285 * to any other .exrc file.
287 if (exrc_isok(sp
, _PATH_EXRC
, 0))
288 (void)ex_cfile(sp
, NULL
, _PATH_EXRC
);
291 /* List recovery files if -l specified. */
295 /* Use a tag file or recovery file if specified. */
296 if (tag_f
!= NULL
&& ex_tagfirst(sp
, tag_f
))
298 else if (rec_f
!= NULL
&& rcv_read(sp
, rec_f
))
301 /* Append any remaining arguments as file names. */
303 for (; *argv
!= NULL
; ++argv
)
304 if (file_add(sp
, NULL
, *argv
, 0) == NULL
)
308 * If no recovery or tag file, get an EXF structure.
309 * If no argv file, use a temporary file.
311 if (tag_f
== NULL
&& rec_f
== NULL
) {
312 if ((frp
= file_first(sp
)) == NULL
&&
313 (frp
= file_add(sp
, NULL
, NULL
, 1)) == NULL
)
315 if (file_init(sp
, frp
, NULL
, 0))
319 /* Set up the argument pointer. */
323 * Initialize the signals. Use sigaction(2), not signal(3), because
324 * we don't want to always restart system calls on 4BSD systems. It
325 * would be nice in some cases to restart system calls, but SA_RESTART
326 * is a 4BSD extension so we can't use it.
328 * SIGWINCH, SIGHUP, SIGTERM:
329 * Catch and set a global bit.
331 act
.sa_handler
= h_hup
;
332 sigemptyset(&act
.sa_mask
);
334 (void)sigaction(SIGHUP
, &act
, NULL
);
335 act
.sa_handler
= h_term
;
336 sigemptyset(&act
.sa_mask
);
338 (void)sigaction(SIGTERM
, &act
, NULL
);
339 act
.sa_handler
= h_winch
;
340 sigemptyset(&act
.sa_mask
);
342 (void)sigaction(SIGWINCH
, &act
, NULL
);
348 act
.sa_handler
= SIG_IGN
;
349 sigemptyset(&act
.sa_mask
);
351 (void)sigaction(SIGQUIT
, &act
, NULL
);
354 * If there's an initial command, push it on the command stack.
355 * Historically, it was always an ex command, not vi in vi mode
356 * or ex in ex mode. So, make it look like an ex command to vi.
358 if (excmdarg
!= NULL
)
359 if (IN_EX_MODE(sp
)) {
360 if (term_push(sp
, excmdarg
, strlen(excmdarg
), 0, 0))
362 } else if (IN_VI_MODE(sp
)) {
363 if (term_push(sp
, "\n", 1, 0, 0))
365 if (term_push(sp
, excmdarg
, strlen(excmdarg
), 0, 0))
367 if (term_push(sp
, ":", 1, 0, 0))
371 /* Vi reads from the terminal. */
372 if (!F_ISSET(gp
, G_ISFROMTTY
) && !F_ISSET(sp
, S_EX
)) {
373 msgq(sp
, M_ERR
, "Vi's standard input must be a terminal.");
378 if (sp
->s_edit(sp
, sp
->ep
))
382 * Edit the next screen on the display queue, or, move
383 * a screen from the hidden queue to the display queue.
385 if ((sp
= __global_list
->dq
.cqh_first
) ==
386 (void *)&__global_list
->dq
)
387 if ((sp
= __global_list
->hq
.cqh_first
) !=
388 (void *)&__global_list
->hq
) {
389 CIRCLEQ_REMOVE(&sp
->gp
->hq
, sp
, q
);
390 CIRCLEQ_INSERT_TAIL(&sp
->gp
->dq
, sp
, q
);
395 * The screen type may have changed -- reinitialize the
396 * functions in case it has.
398 switch (F_ISSET(sp
, S_SCREENS
)) {
400 if (sex_screen_init(sp
))
404 if (svi_screen_init(sp
))
408 if (xaw_screen_init(sp
))
417 * Two error paths. The first means that something failed before
418 * we called a screen routine. Swap the message pointers between
419 * the SCR and the GS, so messages get displayed. The second is
420 * something failed in a screen. NOTE: sp may be GONE when the
421 * screen returns, so only the gp can be trusted.
425 err1
: gp
->msgq
.lh_first
= sp
->msgq
.lh_first
;
435 * Build and initialize the GS structure.
443 CALLOC_NOMSG(NULL
, gp
, GS
*, 1, sizeof(GS
));
447 CIRCLEQ_INIT(&gp
->dq
);
448 CIRCLEQ_INIT(&gp
->hq
);
449 LIST_INIT(&gp
->msgq
);
451 /* Structures shared by screens so stored in the GS structure. */
452 CALLOC_NOMSG(NULL
, gp
->tty
, IBUF
*, 1, sizeof(IBUF
));
456 LIST_INIT(&gp
->cutq
);
457 LIST_INIT(&gp
->seqq
);
459 /* Set a flag if we're reading from the tty. */
460 if (isatty(STDIN_FILENO
))
461 F_SET(gp
, G_ISFROMTTY
);
465 * Set a flag and don't do terminal sets/resets if the input isn't
466 * from a tty. Under all circumstances put reasonable things into
467 * the original_termios field, as some routines (seq.c:seq_save()
468 * and term.c:term_init()) want values for special characters.
470 if (F_ISSET(gp
, G_ISFROMTTY
)) {
471 if (tcgetattr(STDIN_FILENO
, &gp
->original_termios
))
474 if ((fd
= open(_PATH_TTY
, O_RDONLY
, 0)) == -1)
475 err(1, "%s", _PATH_TTY
);
476 if (tcgetattr(fd
, &gp
->original_termios
))
487 * End the GS structure.
497 /* Reset anything that needs resetting. */
498 if (gp
->flags
& G_SETMODE
) /* O_MESG */
499 if ((tty
= ttyname(STDERR_FILENO
)) == NULL
)
501 else if (chmod(tty
, gp
->origmode
) < 0)
504 /* Ring the bell if scheduled. */
505 if (F_ISSET(gp
, G_BELLSCHED
))
506 (void)fprintf(stderr
, "\07"); /* \a */
508 /* If there are any remaining screens, flush their messages. */
509 for (sp
= __global_list
->dq
.cqh_first
;
510 sp
!= (void *)&__global_list
->dq
; sp
= sp
->q
.cqe_next
)
511 for (mp
= sp
->msgq
.lh_first
;
512 mp
!= NULL
&& !(F_ISSET(mp
, M_EMPTY
)); mp
= mp
->q
.le_next
)
513 (void)fprintf(stderr
, "%.*s\n", (int)mp
->len
, mp
->mbuf
);
514 for (sp
= __global_list
->hq
.cqh_first
;
515 sp
!= (void *)&__global_list
->hq
; sp
= sp
->q
.cqe_next
)
516 for (mp
= sp
->msgq
.lh_first
;
517 mp
!= NULL
&& !(F_ISSET(mp
, M_EMPTY
)); mp
= mp
->q
.le_next
)
518 (void)fprintf(stderr
, "%.*s\n", (int)mp
->len
, mp
->mbuf
);
519 /* Flush messages on the global queue. */
520 for (mp
= gp
->msgq
.lh_first
;
521 mp
!= NULL
&& !(F_ISSET(mp
, M_EMPTY
)); mp
= mp
->q
.le_next
)
522 (void)fprintf(stderr
, "%.*s\n", (int)mp
->len
, mp
->mbuf
);
524 FREE(gp
->special_key
, MAX_FAST_KEY
);
527 * DON'T FREE THE GLOBAL STRUCTURE -- WE DIDN'T TURN
528 * OFF SIGNALS/TIMERS, SO IT MAY STILL BE REFERENCED.
540 F_SET(__global_list
, G_SIGHUP
);
543 * If we're asleep, just die.
546 * This isn't right if the windows are independent.
548 if (F_ISSET(__global_list
, G_SLEEPING
))
560 F_SET(__global_list
, G_SIGTERM
);
563 * If we're asleep, just die.
566 * This isn't right if the windows are independent.
568 if (F_ISSET(__global_list
, G_SLEEPING
))
580 F_SET(__global_list
, G_SIGWINCH
);
585 * Check a .exrc for source-ability.
588 exrc_isok(sp
, path
, rootok
)
595 char *emsg
, buf
[MAXPATHLEN
];
597 /* Check for the file's existence. */
603 * Historically, vi did not read the .exrc files if they were owned
604 * by someone other than the user, unless the undocumented option
605 * sourceany was set. We don't support the sourceany option. We
606 * check that the user (or root, for system files) owns the file and
607 * require that it not be writeable by anyone other than the owner.
610 /* Owned by the user or root. */
613 if (sb
.st_uid
!= uid
&& sb
.st_uid
!= 0) {
614 emsg
= "not owned by you or root";
618 if (sb
.st_uid
!= uid
) {
619 emsg
= "not owned by you";
623 /* Not writeable by anyone but the owner. */
624 if (sb
.st_mode
& (S_IWGRP
| S_IWOTH
)) {
625 emsg
= "writeable by a user other than the owner";
626 err
: if (strchr(path
, '/') == NULL
&&
627 getcwd(buf
, sizeof(buf
)) != NULL
)
629 "%s/%s: not sourced: %s.", buf
, path
, emsg
);
632 "%s: not sourced: %s.", path
, emsg
);
646 * Translate old style arguments into something getopt will like.
647 * Make sure it's not text space memory, because ex changes the
649 * Change "+" into "-c$".
650 * Change "+<anything else>" into "-c<anything else>".
651 * Change "-" into "-s"
652 * Change "-r" into "-l"
654 for (myname
= argv
[0]; *++argv
;)
655 if (argv
[0][0] == '+') {
656 if (argv
[0][1] == '\0') {
657 MALLOC_NOMSG(NULL
, argv
[0], char *, 4);
660 (void)strcpy(argv
[0], "-c$");
663 len
= strlen(argv
[0]);
664 MALLOC_NOMSG(NULL
, argv
[0], char *, len
+ 2);
669 (void)strcpy(argv
[0] + 2, p
+ 1);
671 } else if (argv
[0][0] == '-') {
672 if (argv
[0][1] == 'r') {
673 if (argv
[0][2] == '\0' && argv
[1] == NULL
)
675 } else if (argv
[0][1] == '\0') {
676 MALLOC_NOMSG(NULL
, argv
[0], char *, 3);
679 (void)strcpy(argv
[0], "-s");
689 "usage: ex [-eFlRsv] [-c command] [-r file] [-t tag] [-w size] [-x aw]"
691 "usage: vi [-eFlRv] [-c command] [-r file] [-t tag] [-w size] [-x aw]"
693 (void)fprintf(stderr
, "%s\n", is_ex
? EX_USAGE
: VI_USAGE
);