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.58 1993/12/26 11:22:45 bostic Exp $ (Berkeley) $Date: 1993/12/26 11:22:45 $";
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 * If the environment exists, vi historically doesn't check ~user.
235 * This is done before the file is read in because things in the
236 * .exrc information can set, for example, the recovery directory.
239 if (exrc_isok(sp
, _PATH_SYSEXRC
, 1))
240 (void)ex_cfile(sp
, NULL
, _PATH_SYSEXRC
);
242 /* Source the EXINIT environment variable. */
243 if ((p
= getenv("EXINIT")) != NULL
)
244 if ((p
= strdup(p
)) == NULL
) {
245 msgq(sp
, M_SYSERR
, NULL
);
248 (void)ex_icmd(sp
, NULL
, p
, strlen(p
));
251 else if ((p
= getenv("HOME")) != NULL
&& *p
) {
253 sizeof(path
), "%s/%s", p
, _PATH_NEXRC
);
254 if (exrc_isok(sp
, path
, 0))
255 (void)ex_cfile(sp
, NULL
, path
);
258 sizeof(path
), "%s/%s", p
, _PATH_EXRC
);
259 if (exrc_isok(sp
, path
, 0))
260 (void)ex_cfile(sp
, NULL
, path
);
265 * According to O'Reilly ("Learning the VI Editor", Fifth Ed.,
266 * May 1992, page 106), System V release 3.2 and later, has an
267 * option "[no]exrc", causing vi to not "read .exrc files in
268 * the current directory unless you first set the exrc option
269 * in your home directory's .exrc file". Yeah, right. Did
270 * someone actually believe that users would change their home
271 * .exrc file based on whether or not they wanted to source the
272 * current local .exrc? Or that users would want ALL the local
273 * .exrc files on some systems, and none of them on others?
276 * Apply the same tests to local .exrc files that are applied
277 * to any other .exrc file.
279 if (exrc_isok(sp
, _PATH_EXRC
, 0))
280 (void)ex_cfile(sp
, NULL
, _PATH_EXRC
);
283 /* List recovery files if -l specified. */
287 /* Use a tag file or recovery file if specified. */
288 if (tag_f
!= NULL
&& ex_tagfirst(sp
, tag_f
))
290 else if (rec_f
!= NULL
&& rcv_read(sp
, rec_f
))
293 /* Append any remaining arguments as file names. */
295 for (; *argv
!= NULL
; ++argv
)
296 if (file_add(sp
, NULL
, *argv
, 0) == NULL
)
300 * If no recovery or tag file, get an EXF structure.
301 * If no argv file, use a temporary file.
303 if (tag_f
== NULL
&& rec_f
== NULL
) {
304 if ((frp
= file_first(sp
)) == NULL
&&
305 (frp
= file_add(sp
, NULL
, NULL
, 1)) == NULL
)
307 if (file_init(sp
, frp
, NULL
, 0))
311 /* Set up the argument pointer. */
315 * Initialize the signals. Use sigaction(2), not signal(3), because
316 * we don't want to always restart system calls on 4BSD systems. It
317 * would be nice in some cases to restart system calls, but SA_RESTART
318 * is a 4BSD extension so we can't use it.
320 * SIGWINCH, SIGHUP, SIGTERM:
321 * Catch and set a global bit.
323 act
.sa_handler
= h_hup
;
324 sigemptyset(&act
.sa_mask
);
326 (void)sigaction(SIGHUP
, &act
, NULL
);
327 act
.sa_handler
= h_term
;
328 sigemptyset(&act
.sa_mask
);
330 (void)sigaction(SIGTERM
, &act
, NULL
);
331 act
.sa_handler
= h_winch
;
332 sigemptyset(&act
.sa_mask
);
334 (void)sigaction(SIGWINCH
, &act
, NULL
);
340 act
.sa_handler
= SIG_IGN
;
341 sigemptyset(&act
.sa_mask
);
343 (void)sigaction(SIGQUIT
, &act
, NULL
);
346 * If there's an initial command, push it on the command stack.
347 * Historically, it was always an ex command, not vi in vi mode
348 * or ex in ex mode. So, make it look like an ex command to vi.
350 if (excmdarg
!= NULL
)
351 if (IN_EX_MODE(sp
)) {
352 if (term_push(sp
, excmdarg
, strlen(excmdarg
), 0, 0))
354 } else if (IN_VI_MODE(sp
)) {
355 if (term_push(sp
, "\n", 1, 0, 0))
357 if (term_push(sp
, excmdarg
, strlen(excmdarg
), 0, 0))
359 if (term_push(sp
, ":", 1, 0, 0))
363 /* Vi reads from the terminal. */
364 if (!F_ISSET(gp
, G_ISFROMTTY
) && !F_ISSET(sp
, S_EX
)) {
365 msgq(sp
, M_ERR
, "Vi's standard input must be a terminal.");
370 if (sp
->s_edit(sp
, sp
->ep
))
374 * Edit the next screen on the display queue, or, move
375 * a screen from the hidden queue to the display queue.
377 if ((sp
= __global_list
->dq
.cqh_first
) ==
378 (void *)&__global_list
->dq
)
379 if ((sp
= __global_list
->hq
.cqh_first
) !=
380 (void *)&__global_list
->hq
) {
381 CIRCLEQ_REMOVE(&sp
->gp
->hq
, sp
, q
);
382 CIRCLEQ_INSERT_TAIL(&sp
->gp
->dq
, sp
, q
);
387 * The screen type may have changed -- reinitialize the
388 * functions in case it has.
390 switch (F_ISSET(sp
, S_SCREENS
)) {
392 if (sex_screen_init(sp
))
396 if (svi_screen_init(sp
))
400 if (xaw_screen_init(sp
))
409 * Two error paths. The first means that something failed before
410 * we called a screen routine. Swap the message pointers between
411 * the SCR and the GS, so messages get displayed. The second is
412 * something failed in a screen. NOTE: sp may be GONE when the
413 * screen returns, so only the gp can be trusted.
417 err1
: gp
->msgq
.lh_first
= sp
->msgq
.lh_first
;
423 /* Make absolutely sure that the modes are restored correctly. */
424 if (F_ISSET(gp
, G_ISFROMTTY
) &&
425 tcsetattr(STDIN_FILENO
, TCSADRAIN
, &gp
->original_termios
))
432 * Build and initialize the GS structure.
440 CALLOC_NOMSG(NULL
, gp
, GS
*, 1, sizeof(GS
));
444 CIRCLEQ_INIT(&gp
->dq
);
445 CIRCLEQ_INIT(&gp
->hq
);
446 LIST_INIT(&gp
->msgq
);
448 /* Structures shared by screens so stored in the GS structure. */
449 CALLOC_NOMSG(NULL
, gp
->tty
, IBUF
*, 1, sizeof(IBUF
));
453 LIST_INIT(&gp
->cutq
);
454 LIST_INIT(&gp
->seqq
);
456 /* Set a flag if we're reading from the tty. */
457 if (isatty(STDIN_FILENO
))
458 F_SET(gp
, G_ISFROMTTY
);
462 * Set a flag and don't do terminal sets/resets if the input isn't
463 * from a tty. Under all circumstances put reasonable things into
464 * the original_termios field, as some routines (seq.c:seq_save()
465 * and term.c:term_init()) want values for special characters.
467 if (F_ISSET(gp
, G_ISFROMTTY
)) {
468 if (tcgetattr(STDIN_FILENO
, &gp
->original_termios
))
471 if ((fd
= open(_PATH_TTY
, O_RDONLY
, 0)) == -1)
472 err(1, "%s", _PATH_TTY
);
473 if (tcgetattr(fd
, &gp
->original_termios
))
484 * End the GS structure.
494 /* Reset anything that needs resetting. */
495 if (gp
->flags
& G_SETMODE
) /* O_MESG */
496 if ((tty
= ttyname(STDERR_FILENO
)) == NULL
)
498 else if (chmod(tty
, gp
->origmode
) < 0)
501 /* Ring the bell if scheduled. */
502 if (F_ISSET(gp
, G_BELLSCHED
))
503 (void)fprintf(stderr
, "\07"); /* \a */
505 /* If there are any remaining screens, flush their messages. */
506 for (sp
= __global_list
->dq
.cqh_first
;
507 sp
!= (void *)&__global_list
->dq
; sp
= sp
->q
.cqe_next
)
508 for (mp
= sp
->msgq
.lh_first
;
509 mp
!= NULL
&& !(F_ISSET(mp
, M_EMPTY
)); mp
= mp
->q
.le_next
)
510 (void)fprintf(stderr
, "%.*s\n", (int)mp
->len
, mp
->mbuf
);
511 for (sp
= __global_list
->hq
.cqh_first
;
512 sp
!= (void *)&__global_list
->hq
; sp
= sp
->q
.cqe_next
)
513 for (mp
= sp
->msgq
.lh_first
;
514 mp
!= NULL
&& !(F_ISSET(mp
, M_EMPTY
)); mp
= mp
->q
.le_next
)
515 (void)fprintf(stderr
, "%.*s\n", (int)mp
->len
, mp
->mbuf
);
516 /* Flush messages on the global queue. */
517 for (mp
= gp
->msgq
.lh_first
;
518 mp
!= NULL
&& !(F_ISSET(mp
, M_EMPTY
)); mp
= mp
->q
.le_next
)
519 (void)fprintf(stderr
, "%.*s\n", (int)mp
->len
, mp
->mbuf
);
521 FREE(gp
->special_key
, MAX_FAST_KEY
);
524 * DON'T FREE THE GLOBAL STRUCTURE -- WE DIDN'T TURN
525 * OFF SIGNALS/TIMERS, SO IT MAY STILL BE REFERENCED.
537 F_SET(__global_list
, G_SIGHUP
);
540 * If we're asleep, just die.
543 * This isn't right if the windows are independent.
545 if (F_ISSET(__global_list
, G_SLEEPING
))
557 F_SET(__global_list
, G_SIGTERM
);
560 * If we're asleep, just die.
563 * This isn't right if the windows are independent.
565 if (F_ISSET(__global_list
, G_SLEEPING
))
577 F_SET(__global_list
, G_SIGWINCH
);
582 * Check a .exrc for source-ability.
585 exrc_isok(sp
, path
, rootok
)
592 char *emsg
, buf
[MAXPATHLEN
];
594 /* Check for the file's existence. */
600 * Historically, vi did not read the .exrc files if they were owned
601 * by someone other than the user, unless the undocumented option
602 * sourceany was set. We don't support the sourceany option. We
603 * check that the user (or root, for system files) owns the file and
604 * require that it not be writeable by anyone other than the owner.
607 /* Owned by the user or root. */
610 if (sb
.st_uid
!= uid
&& sb
.st_uid
!= 0) {
611 emsg
= "not owned by you or root";
615 if (sb
.st_uid
!= uid
) {
616 emsg
= "not owned by you";
620 /* Not writeable by anyone but the owner. */
621 if (sb
.st_mode
& (S_IWGRP
| S_IWOTH
)) {
622 emsg
= "writeable by a user other than the owner";
623 err
: if (strchr(path
, '/') == NULL
&&
624 getcwd(buf
, sizeof(buf
)) != NULL
)
626 "%s/%s: not sourced: %s.", buf
, path
, emsg
);
629 "%s: not sourced: %s.", path
, emsg
);
643 * Translate old style arguments into something getopt will like.
644 * Make sure it's not text space memory, because ex changes the
646 * Change "+" into "-c$".
647 * Change "+<anything else>" into "-c<anything else>".
648 * Change "-" into "-s"
649 * Change "-r" into "-l"
651 for (myname
= argv
[0]; *++argv
;)
652 if (argv
[0][0] == '+') {
653 if (argv
[0][1] == '\0') {
654 MALLOC_NOMSG(NULL
, argv
[0], char *, 4);
657 (void)strcpy(argv
[0], "-c$");
660 len
= strlen(argv
[0]);
661 MALLOC_NOMSG(NULL
, argv
[0], char *, len
+ 2);
666 (void)strcpy(argv
[0] + 2, p
+ 1);
668 } else if (argv
[0][0] == '-') {
669 if (argv
[0][1] == 'r') {
670 if (argv
[0][2] == '\0' && argv
[1] == NULL
)
672 } else if (argv
[0][1] == '\0') {
673 MALLOC_NOMSG(NULL
, argv
[0], char *, 3);
676 (void)strcpy(argv
[0], "-s");
686 "usage: ex [-eFlRsv] [-c command] [-r file] [-t tag] [-w size] [-x aw]"
688 "usage: vi [-eFlRv] [-c command] [-r file] [-t tag] [-w size] [-x aw]"
690 (void)fprintf(stderr
, "%s\n", is_ex
? EX_USAGE
: VI_USAGE
);