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.57 1993/12/22 17:57:42 bostic Exp $ (Berkeley) $Date: 1993/12/22 17:57:42 $";
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 Ed.,
267 * May 1992, page 106), System V release 3.2 and later, has an
268 * option "[no]exrc", causing vi to not "read .exrc files in
269 * the current directory unless you first set the exrc option
270 * in your home directory's .exrc file". Yeah, right. Did
271 * someone actually believe that users would change their home
272 * .exrc file based on whether or not they wanted to source the
273 * current local .exrc? Or that users would want ALL the local
274 * .exrc files on some systems, and none of them on others?
277 * Apply the same tests to local .exrc files that are applied
278 * to any other .exrc file.
280 if (exrc_isok(sp
, _PATH_EXRC
, 0))
281 (void)ex_cfile(sp
, NULL
, _PATH_EXRC
);
284 /* List recovery files if -l specified. */
288 /* Use a tag file or recovery file if specified. */
289 if (tag_f
!= NULL
&& ex_tagfirst(sp
, tag_f
))
291 else if (rec_f
!= NULL
&& rcv_read(sp
, rec_f
))
294 /* Append any remaining arguments as file names. */
296 for (; *argv
!= NULL
; ++argv
)
297 if (file_add(sp
, NULL
, *argv
, 0) == NULL
)
301 * If no recovery or tag file, get an EXF structure.
302 * If no argv file, use a temporary file.
304 if (tag_f
== NULL
&& rec_f
== NULL
) {
305 if ((frp
= file_first(sp
)) == NULL
&&
306 (frp
= file_add(sp
, NULL
, NULL
, 1)) == NULL
)
308 if (file_init(sp
, frp
, NULL
, 0))
312 /* Set up the argument pointer. */
316 * Initialize the signals. Use sigaction(2), not signal(3), because
317 * we don't want to always restart system calls on 4BSD systems. It
318 * would be nice in some cases to restart system calls, but SA_RESTART
319 * is a 4BSD extension so we can't use it.
321 * SIGWINCH, SIGHUP, SIGTERM:
322 * Catch and set a global bit.
324 act
.sa_handler
= h_hup
;
325 sigemptyset(&act
.sa_mask
);
327 (void)sigaction(SIGHUP
, &act
, NULL
);
328 act
.sa_handler
= h_term
;
329 sigemptyset(&act
.sa_mask
);
331 (void)sigaction(SIGTERM
, &act
, NULL
);
332 act
.sa_handler
= h_winch
;
333 sigemptyset(&act
.sa_mask
);
335 (void)sigaction(SIGWINCH
, &act
, NULL
);
341 act
.sa_handler
= SIG_IGN
;
342 sigemptyset(&act
.sa_mask
);
344 (void)sigaction(SIGQUIT
, &act
, NULL
);
347 * If there's an initial command, push it on the command stack.
348 * Historically, it was always an ex command, not vi in vi mode
349 * or ex in ex mode. So, make it look like an ex command to vi.
351 if (excmdarg
!= NULL
)
352 if (IN_EX_MODE(sp
)) {
353 if (term_push(sp
, excmdarg
, strlen(excmdarg
), 0, 0))
355 } else if (IN_VI_MODE(sp
)) {
356 if (term_push(sp
, "\n", 1, 0, 0))
358 if (term_push(sp
, excmdarg
, strlen(excmdarg
), 0, 0))
360 if (term_push(sp
, ":", 1, 0, 0))
364 /* Vi reads from the terminal. */
365 if (!F_ISSET(gp
, G_ISFROMTTY
) && !F_ISSET(sp
, S_EX
)) {
366 msgq(sp
, M_ERR
, "Vi's standard input must be a terminal.");
371 if (sp
->s_edit(sp
, sp
->ep
))
375 * Edit the next screen on the display queue, or, move
376 * a screen from the hidden queue to the display queue.
378 if ((sp
= __global_list
->dq
.cqh_first
) ==
379 (void *)&__global_list
->dq
)
380 if ((sp
= __global_list
->hq
.cqh_first
) !=
381 (void *)&__global_list
->hq
) {
382 CIRCLEQ_REMOVE(&sp
->gp
->hq
, sp
, q
);
383 CIRCLEQ_INSERT_TAIL(&sp
->gp
->dq
, sp
, q
);
388 * The screen type may have changed -- reinitialize the
389 * functions in case it has.
391 switch (F_ISSET(sp
, S_SCREENS
)) {
393 if (sex_screen_init(sp
))
397 if (svi_screen_init(sp
))
401 if (xaw_screen_init(sp
))
410 * Two error paths. The first means that something failed before
411 * we called a screen routine. Swap the message pointers between
412 * the SCR and the GS, so messages get displayed. The second is
413 * something failed in a screen. NOTE: sp may be GONE when the
414 * screen returns, so only the gp can be trusted.
418 err1
: gp
->msgq
.lh_first
= sp
->msgq
.lh_first
;
424 /* Make absolutely sure that the modes are restored correctly. */
425 if (F_ISSET(gp
, G_ISFROMTTY
) &&
426 tcsetattr(STDIN_FILENO
, TCSADRAIN
, &gp
->original_termios
))
433 * Build and initialize the GS structure.
441 CALLOC_NOMSG(NULL
, gp
, GS
*, 1, sizeof(GS
));
445 CIRCLEQ_INIT(&gp
->dq
);
446 CIRCLEQ_INIT(&gp
->hq
);
447 LIST_INIT(&gp
->msgq
);
449 /* Structures shared by screens so stored in the GS structure. */
450 CALLOC_NOMSG(NULL
, gp
->tty
, IBUF
*, 1, sizeof(IBUF
));
454 LIST_INIT(&gp
->cutq
);
455 LIST_INIT(&gp
->seqq
);
457 /* Set a flag if we're reading from the tty. */
458 if (isatty(STDIN_FILENO
))
459 F_SET(gp
, G_ISFROMTTY
);
463 * Set a flag and don't do terminal sets/resets if the input isn't
464 * from a tty. Under all circumstances put reasonable things into
465 * the original_termios field, as some routines (seq.c:seq_save()
466 * and term.c:term_init()) want values for special characters.
468 if (F_ISSET(gp
, G_ISFROMTTY
)) {
469 if (tcgetattr(STDIN_FILENO
, &gp
->original_termios
))
472 if ((fd
= open(_PATH_TTY
, O_RDONLY
, 0)) == -1)
473 err(1, "%s", _PATH_TTY
);
474 if (tcgetattr(fd
, &gp
->original_termios
))
485 * End the GS structure.
495 /* Reset anything that needs resetting. */
496 if (gp
->flags
& G_SETMODE
) /* O_MESG */
497 if ((tty
= ttyname(STDERR_FILENO
)) == NULL
)
499 else if (chmod(tty
, gp
->origmode
) < 0)
502 /* Ring the bell if scheduled. */
503 if (F_ISSET(gp
, G_BELLSCHED
))
504 (void)fprintf(stderr
, "\07"); /* \a */
506 /* If there are any remaining screens, flush their messages. */
507 for (sp
= __global_list
->dq
.cqh_first
;
508 sp
!= (void *)&__global_list
->dq
; sp
= sp
->q
.cqe_next
)
509 for (mp
= sp
->msgq
.lh_first
;
510 mp
!= NULL
&& !(F_ISSET(mp
, M_EMPTY
)); mp
= mp
->q
.le_next
)
511 (void)fprintf(stderr
, "%.*s\n", (int)mp
->len
, mp
->mbuf
);
512 for (sp
= __global_list
->hq
.cqh_first
;
513 sp
!= (void *)&__global_list
->hq
; sp
= sp
->q
.cqe_next
)
514 for (mp
= sp
->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
);
517 /* Flush messages on the global queue. */
518 for (mp
= gp
->msgq
.lh_first
;
519 mp
!= NULL
&& !(F_ISSET(mp
, M_EMPTY
)); mp
= mp
->q
.le_next
)
520 (void)fprintf(stderr
, "%.*s\n", (int)mp
->len
, mp
->mbuf
);
522 FREE(gp
->special_key
, MAX_FAST_KEY
);
525 * DON'T FREE THE GLOBAL STRUCTURE -- WE DIDN'T TURN
526 * OFF SIGNALS/TIMERS, SO IT MAY STILL BE REFERENCED.
538 F_SET(__global_list
, G_SIGHUP
);
541 * If we're asleep, just die.
544 * This isn't right if the windows are independent.
546 if (F_ISSET(__global_list
, G_SLEEPING
))
558 F_SET(__global_list
, G_SIGTERM
);
561 * If we're asleep, just die.
564 * This isn't right if the windows are independent.
566 if (F_ISSET(__global_list
, G_SLEEPING
))
578 F_SET(__global_list
, G_SIGWINCH
);
583 * Check a .exrc for source-ability.
586 exrc_isok(sp
, path
, rootok
)
593 char *emsg
, buf
[MAXPATHLEN
];
595 /* Check for the file's existence. */
601 * Historically, vi did not read the .exrc files if they were owned
602 * by someone other than the user, unless the undocumented option
603 * sourceany was set. We don't support the sourceany option. We
604 * check that the user (or root, for system files) owns the file and
605 * require that it not be writeable by anyone other than the owner.
608 /* Owned by the user or root. */
611 if (sb
.st_uid
!= uid
&& sb
.st_uid
!= 0) {
612 emsg
= "not owned by you or root";
616 if (sb
.st_uid
!= uid
) {
617 emsg
= "not owned by you";
621 /* Not writeable by anyone but the owner. */
622 if (sb
.st_mode
& (S_IWGRP
| S_IWOTH
)) {
623 emsg
= "writeable by a user other than the owner";
624 err
: if (strchr(path
, '/') == NULL
&&
625 getcwd(buf
, sizeof(buf
)) != NULL
)
627 "%s/%s: not sourced: %s.", buf
, path
, emsg
);
630 "%s: not sourced: %s.", path
, emsg
);
644 * Translate old style arguments into something getopt will like.
645 * Make sure it's not text space memory, because ex changes the
647 * Change "+" into "-c$".
648 * Change "+<anything else>" into "-c<anything else>".
649 * Change "-" into "-s"
650 * Change "-r" into "-l"
652 for (myname
= argv
[0]; *++argv
;)
653 if (argv
[0][0] == '+') {
654 if (argv
[0][1] == '\0') {
655 MALLOC_NOMSG(NULL
, argv
[0], char *, 4);
658 (void)strcpy(argv
[0], "-c$");
661 len
= strlen(argv
[0]);
662 MALLOC_NOMSG(NULL
, argv
[0], char *, len
+ 2);
667 (void)strcpy(argv
[0] + 2, p
+ 1);
669 } else if (argv
[0][0] == '-') {
670 if (argv
[0][1] == 'r') {
671 if (argv
[0][2] == '\0' && argv
[1] == NULL
)
673 } else if (argv
[0][1] == '\0') {
674 MALLOC_NOMSG(NULL
, argv
[0], char *, 3);
677 (void)strcpy(argv
[0], "-s");
687 "usage: ex [-eFlRsv] [-c command] [-r file] [-t tag] [-w size] [-x aw]"
689 "usage: vi [-eFlRv] [-c command] [-r file] [-t tag] [-w size] [-x aw]"
691 (void)fprintf(stderr
, "%s\n", is_ex
? EX_USAGE
: VI_USAGE
);