* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
static char copyright
[] =
"@(#) Copyright (c) 1992, 1993\n\
The Regents of the University of California. All rights reserved.\n";
static char sccsid
[] = "@(#)main.c 8.65 (Berkeley) 1/23/94";
static int exrc_isok
__P((SCR
*, char *, int));
static void gs_end
__P((GS
*));
static GS
*gs_init
__P((void));
static void h_hup
__P((int));
static void h_term
__P((int));
static void h_winch
__P((int));
static void obsolete
__P((char *[]));
static void usage
__P((int));
GS
*__global_list
; /* GLOBAL: List of screens. */
static int reenter
; /* STATIC: Re-entrancy check. */
u_int flags
, saved_vi_mode
;
int ch
, eval
, flagchk
, readonly
, silent
, snapshot
;
char *excmdarg
, *myname
, *p
, *rec_f
, *tag_f
, *trace_f
, *wsizearg
;
/* Stop if indirecting through a NULL pointer. */
/* Set screen type and mode based on the program name. */
if ((myname
= strrchr(*argv
, '/')) == NULL
)
if (!strcmp(myname
, "ex") || !strcmp(myname
, "nex"))
if (!strcmp(myname
, "view"))
saved_vi_mode
= S_VI_CURSES
;
/* Convert old-style arguments into new-style ones. */
/* Parse the arguments. */
excmdarg
= rec_f
= tag_f
= trace_f
= wsizearg
= NULL
;
while ((ch
= getopt(argc
, argv
, "c:eFlRr:sT:t:vw:x:")) != EOF
)
case 'c': /* Run the command. */
case 'F': /* No snapshot. */
if (flagchk
!= '\0' && flagchk
!= 'l')
"only one of -%c and -l may be specified.",
case 'R': /* Readonly. */
"only one recovery file may be specified.");
"only one of -%c and -r may be specified.",
errx(1, "-s only applicable to ex.");
"only one tag file may be specified.");
"only one of -%c and -t may be specified.",
if (!strcmp(optarg
, "aw")) {
saved_vi_mode
= S_VI_XAW
;
/* Build and initialize the GS structure. */
__global_list
= gp
= gs_init();
* Build and initialize the first/current screen. This is a bit
* tricky. If an error is returned, we may or may not have a
* screen structure. If we have a screen structure, put it on a
* display queue so that the error messages get displayed.
if (screen_init(NULL
, &sp
, flags
)) {
CIRCLEQ_INSERT_HEAD(&__global_list
->dq
, sp
, q
);
sp
->saved_vi_mode
= saved_vi_mode
;
CIRCLEQ_INSERT_HEAD(&__global_list
->dq
, sp
, q
);
if ((gp
->tracefp
= fopen(optarg
, "w")) == NULL
)
(void)fprintf(gp
->tracefp
, "\n===\ntrace: open %s\n", optarg
);
msgq(sp
, M_ERR
, "-T support not compiled into this version.");
if (set_window_size(sp
, 0, 0)) /* Set the window size. */
if (opts_init(sp
)) /* Options initialization. */
if (readonly
) /* Global read-only bit. */
if (silent
) { /* Ex batch mode. */
if (strtol(optarg
, &p
, 10) < 0 || *p
)
errx(1, "illegal window size -- %s", optarg
);
(void)snprintf(path
, sizeof(path
), "window=%s", optarg
);
"Unable to set command line window option");
/* Keymaps, special keys, must follow option initializations. */
if (digraph_init(sp
)) /* Digraph initialization. */
* Source the system, environment, ~user and local .exrc values.
* Vi historically didn't check ~user/.exrc if the environment
* variable EXINIT was set. This is all done before the file is
* read in because things in the .exrc information can set, for
* example, the recovery directory.
* While nvi can handle any of the options settings of historic vi,
* the converse is not true. Since users are going to have to have
* files and environmental variables that work with both, we use nvi
* versions if they exist, otherwise the historic ones.
if (exrc_isok(sp
, _PATH_SYSEXRC
, 1))
(void)ex_cfile(sp
, NULL
, _PATH_SYSEXRC
);
/* Source the {N,}EXINIT environment variable. */
if ((p
= getenv("NEXINIT")) != NULL
||
(p
= getenv("EXINIT")) != NULL
)
if ((p
= strdup(p
)) == NULL
) {
msgq(sp
, M_SYSERR
, NULL
);
(void)ex_icmd(sp
, NULL
, p
, strlen(p
));
else if ((p
= getenv("HOME")) != NULL
&& *p
) {
sizeof(path
), "%s/%s", p
, _PATH_NEXRC
);
if (exrc_isok(sp
, path
, 0))
(void)ex_cfile(sp
, NULL
, path
);
sizeof(path
), "%s/%s", p
, _PATH_EXRC
);
if (exrc_isok(sp
, path
, 0))
(void)ex_cfile(sp
, NULL
, path
);
* According to O'Reilly ("Learning the VI Editor", Fifth Ed.,
* May 1992, page 106), System V release 3.2 and later, has an
* option "[no]exrc", causing vi to not "read .exrc files in
* the current directory unless you first set the exrc option
* in your home directory's .exrc file". Yeah, right. Did
* someone actually believe that users would change their home
* .exrc file based on whether or not they wanted to source the
* current local .exrc? Or that users would want ALL the local
* .exrc files on some systems, and none of them on others?
* Apply the same tests to local .exrc files that are applied
* to any other .exrc file.
if (exrc_isok(sp
, _PATH_EXRC
, 0))
(void)ex_cfile(sp
, NULL
, _PATH_EXRC
);
/* List recovery files if -l specified. */
/* Use a tag file or recovery file if specified. */
if (tag_f
!= NULL
&& ex_tagfirst(sp
, tag_f
))
else if (rec_f
!= NULL
&& rcv_read(sp
, rec_f
))
/* Append any remaining arguments as file names. */
for (; *argv
!= NULL
; ++argv
)
if (file_add(sp
, NULL
, *argv
, 0) == NULL
)
* If no recovery or tag file, get an EXF structure.
* If no argv file, use a temporary file.
if (tag_f
== NULL
&& rec_f
== NULL
) {
if ((frp
= file_first(sp
)) == NULL
&&
(frp
= file_add(sp
, NULL
, NULL
, 1)) == NULL
)
if (file_init(sp
, frp
, NULL
, 0))
/* Set up the argument pointer. */
* Initialize the signals. Use sigaction(2), not signal(3), because
* we don't want to always restart system calls on 4BSD systems. It
* would be nice in some cases to restart system calls, but SA_RESTART
* is a 4BSD extension so we can't use it.
* SIGWINCH, SIGHUP, SIGTERM:
* Catch and set a global bit.
sigemptyset(&act
.sa_mask
);
(void)sigaction(SIGHUP
, &act
, NULL
);
sigemptyset(&act
.sa_mask
);
(void)sigaction(SIGTERM
, &act
, NULL
);
act
.sa_handler
= h_winch
;
sigemptyset(&act
.sa_mask
);
(void)sigaction(SIGWINCH
, &act
, NULL
);
act
.sa_handler
= SIG_IGN
;
sigemptyset(&act
.sa_mask
);
(void)sigaction(SIGQUIT
, &act
, NULL
);
* If there's an initial command, push it on the command stack.
* Historically, it was always an ex command, not vi in vi mode
* or ex in ex mode. So, make it look like an ex command to vi.
if (term_push(sp
, excmdarg
, strlen(excmdarg
), 0, 0))
} else if (IN_VI_MODE(sp
)) {
if (term_push(sp
, "\n", 1, 0, 0))
if (term_push(sp
, excmdarg
, strlen(excmdarg
), 0, 0))
if (term_push(sp
, ":", 1, 0, 0))
/* Vi reads from the terminal. */
if (!F_ISSET(gp
, G_ISFROMTTY
) && !F_ISSET(sp
, S_EX
)) {
msgq(sp
, M_ERR
, "Vi's standard input must be a terminal.");
if (sp
->s_edit(sp
, sp
->ep
))
* Edit the next screen on the display queue, or, move
* a screen from the hidden queue to the display queue.
if ((sp
= __global_list
->dq
.cqh_first
) ==
(void *)&__global_list
->dq
)
if ((sp
= __global_list
->hq
.cqh_first
) !=
(void *)&__global_list
->hq
) {
CIRCLEQ_REMOVE(&sp
->gp
->hq
, sp
, q
);
CIRCLEQ_INSERT_TAIL(&sp
->gp
->dq
, sp
, q
);
* The screen type may have changed -- reinitialize the
* functions in case it has.
switch (F_ISSET(sp
, S_SCREENS
)) {
* NOTE: sp may be GONE when the screen returns, so only
* Make absolutely sure that the modes are restored correctly.
* This should no longer be needed, and it's here to handle what I
* believe are SunOS/Solaris curses problems. The problem is that
* for some unknown reason, when endwin() is called in the svi
* routines, it isn't resetting the terminal correctly. I have not
* been able to figure it out, so this resets the terminal to the
* right modes regardless. The problem is that, in most tty driver
* implementations, you can only reset the terminal modes once
* (changing from !ICANON to ICANON) without losing the re-parsing
* effect on the pending input. This means that this "fix" will make
* other systems mess up characters typed after the quit command to
* vi but before vi actually exits.
if (F_ISSET(gp
, G_ISFROMTTY
))
(void)tcsetattr(STDIN_FILENO
, TCSADRAIN
, &gp
->original_termios
);
* Build and initialize the GS structure.
CALLOC_NOMSG(NULL
, gp
, GS
*, 1, sizeof(GS
));
/* Structures shared by screens so stored in the GS structure. */
CALLOC_NOMSG(NULL
, gp
->tty
, IBUF
*, 1, sizeof(IBUF
));
/* Set a flag if we're reading from the tty. */
if (isatty(STDIN_FILENO
))
* Set a flag and don't do terminal sets/resets if the input isn't
* from a tty. Under all circumstances put reasonable things into
* the original_termios field, as some routines (seq.c:seq_save()
* and term.c:term_init()) want values for special characters.
if (F_ISSET(gp
, G_ISFROMTTY
)) {
if (tcgetattr(STDIN_FILENO
, &gp
->original_termios
))
if ((fd
= open(_PATH_TTY
, O_RDONLY
, 0)) == -1)
if (tcgetattr(fd
, &gp
->original_termios
))
/* Reset anything that needs resetting. */
if (gp
->flags
& G_SETMODE
) /* O_MESG */
if ((tty
= ttyname(STDERR_FILENO
)) == NULL
)
else if (chmod(tty
, gp
->origmode
) < 0)
/* Ring the bell if scheduled. */
if (F_ISSET(gp
, G_BELLSCHED
))
(void)fprintf(stderr
, "\07"); /* \a */
/* If there are any remaining screens, flush their messages. */
for (sp
= __global_list
->dq
.cqh_first
;
sp
!= (void *)&__global_list
->dq
; sp
= sp
->q
.cqe_next
)
for (mp
= sp
->msgq
.lh_first
;
mp
!= NULL
&& !(F_ISSET(mp
, M_EMPTY
)); mp
= mp
->q
.le_next
)
(void)fprintf(stderr
, "%.*s\n", (int)mp
->len
, mp
->mbuf
);
for (sp
= __global_list
->hq
.cqh_first
;
sp
!= (void *)&__global_list
->hq
; sp
= sp
->q
.cqe_next
)
for (mp
= sp
->msgq
.lh_first
;
mp
!= NULL
&& !(F_ISSET(mp
, M_EMPTY
)); mp
= mp
->q
.le_next
)
(void)fprintf(stderr
, "%.*s\n", (int)mp
->len
, mp
->mbuf
);
/* Flush messages on the global queue. */
for (mp
= gp
->msgq
.lh_first
;
mp
!= NULL
&& !(F_ISSET(mp
, M_EMPTY
)); mp
= mp
->q
.le_next
)
(void)fprintf(stderr
, "%.*s\n", (int)mp
->len
, mp
->mbuf
);
if (gp
->special_key
!= NULL
)
FREE(gp
->special_key
, MAX_FAST_KEY
);
* DON'T FREE THE GLOBAL STRUCTURE -- WE DIDN'T TURN
* OFF SIGNALS/TIMERS, SO IT MAY STILL BE REFERENCED.
F_SET(__global_list
, G_SIGHUP
);
* If we're asleep, just die.
* This isn't right if the windows are independent.
if (F_ISSET(__global_list
, G_SLEEPING
))
F_SET(__global_list
, G_SIGTERM
);
* If we're asleep, just die.
* This isn't right if the windows are independent.
if (F_ISSET(__global_list
, G_SLEEPING
))
F_SET(__global_list
, G_SIGWINCH
);
* Check a .exrc for source-ability.
exrc_isok(sp
, path
, rootok
)
char *emsg
, buf
[MAXPATHLEN
];
/* Check for the file's existence. */
* Historically, vi did not read the .exrc files if they were owned
* by someone other than the user, unless the undocumented option
* sourceany was set. We don't support the sourceany option. We
* check that the user (or root, for system files) owns the file and
* require that it not be writeable by anyone other than the owner.
/* Owned by the user or root. */
if (sb
.st_uid
!= uid
&& sb
.st_uid
!= 0) {
emsg
= "not owned by you or root";
emsg
= "not owned by you";
/* Not writeable by anyone but the owner. */
if (sb
.st_mode
& (S_IWGRP
| S_IWOTH
)) {
emsg
= "writeable by a user other than the owner";
err
: if (strchr(path
, '/') == NULL
&&
getcwd(buf
, sizeof(buf
)) != NULL
)
"%s/%s: not sourced: %s.", buf
, path
, emsg
);
"%s: not sourced: %s.", path
, emsg
);
* Translate old style arguments into something getopt will like.
* Make sure it's not text space memory, because ex changes the
* Change "+<anything else>" into "-c<anything else>".
for (myname
= argv
[0]; *++argv
;)
if (argv
[0][1] == '\0') {
MALLOC_NOMSG(NULL
, argv
[0], char *, 4);
(void)strcpy(argv
[0], "-c$");
MALLOC_NOMSG(NULL
, argv
[0], char *, len
+ 2);
(void)strcpy(argv
[0] + 2, p
+ 1);
} else if (argv
[0][0] == '-') {
if (argv
[0][2] == '\0' && argv
[1] == NULL
)
} else if (argv
[0][1] == '\0') {
MALLOC_NOMSG(NULL
, argv
[0], char *, 3);
(void)strcpy(argv
[0], "-s");
"usage: ex [-eFlRsv] [-c command] [-r file] [-t tag] [-w size] [-x aw]"
"usage: vi [-eFlRv] [-c command] [-r file] [-t tag] [-w size] [-x aw]"
(void)fprintf(stderr
, "%s\n", is_ex
? EX_USAGE
: VI_USAGE
);