2 * Copyright (c) 1992, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 1992, 1993, 1994, 1995, 1996
5 * Keith Bostic. All rights reserved.
7 * See the LICENSE file for redistribution information.
13 static const char sccsid
[] = "$Id: ex_shell.c,v 10.44 2012/07/06 06:51:26 zy Exp $";
16 #include <sys/queue.h>
20 #include <bitstring.h>
30 #include "../common/common.h"
32 static const char *sigmsg(int);
35 * ex_shell -- :sh[ell]
36 * Invoke the program named in the SHELL environment variable
37 * with the argument -i.
39 * PUBLIC: int ex_shell(SCR *, EXCMD *);
42 ex_shell(SCR
*sp
, EXCMD
*cmdp
)
47 /* We'll need a shell. */
48 if (opts_empty(sp
, O_SHELL
, 0))
53 * Assumes all shells use -i.
55 (void)asprintf(&buf
, "%s -i", O_STR(sp
, O_SHELL
));
57 msgq(sp
, M_SYSERR
, NULL
);
61 /* Restore the window name. */
62 (void)sp
->gp
->scr_rename(sp
, NULL
, 0);
64 /* If we're still in a vi screen, move out explicitly. */
65 rval
= ex_exec_proc(sp
, cmdp
, buf
, NULL
, !F_ISSET(sp
, SC_SCR_EXWROTE
));
68 /* Set the window name. */
69 (void)sp
->gp
->scr_rename(sp
, sp
->frp
->name
, 1);
73 * Historically, vi didn't require a continue message after the
74 * return of the shell. Match it.
76 F_SET(sp
, SC_EX_WAIT_NO
);
83 * Run a separate process.
85 * PUBLIC: int ex_exec_proc(SCR *, EXCMD *, char *, const char *, int);
88 ex_exec_proc(SCR
*sp
, EXCMD
*cmdp
, char *cmd
, const char *msg
, int need_newline
)
96 /* We'll need a shell. */
97 if (opts_empty(sp
, O_SHELL
, 0))
101 if (F_ISSET(sp
, SC_VI
)) {
102 if (gp
->scr_screen(sp
, SC_EX
)) {
103 ex_wemsg(sp
, cmdp
->cmd
->name
, EXM_NOCANON
);
106 (void)gp
->scr_attr(sp
, SA_ALTERNATE
, 0);
107 F_SET(sp
, SC_SCR_EX
| SC_SCR_EXWROTE
);
110 /* Put out additional newline, message. */
112 (void)ex_puts(sp
, "\n");
114 (void)ex_puts(sp
, msg
);
115 (void)ex_puts(sp
, "\n");
119 switch (pid
= vfork()) {
120 case -1: /* Error. */
121 msgq(sp
, M_SYSERR
, "vfork");
123 case 0: /* Utility. */
126 if ((name
= strrchr(O_STR(sp
, O_SHELL
), '/')) == NULL
)
127 name
= O_STR(sp
, O_SHELL
);
130 execl(O_STR(sp
, O_SHELL
), name
, "-c", cmd
, (char *)NULL
);
131 msgq_str(sp
, M_SYSERR
, O_STR(sp
, O_SHELL
), "execl: %s");
134 default: /* Parent. */
135 return (proc_wait(sp
, (long)pid
, cmd
, 0, 0));
142 * Wait for one of the processes.
145 * The pid_t type varies in size from a short to a long depending on the
146 * system. It has to be cast into something or the standard promotion
147 * rules get you. I'm using a long based on the belief that nobody is
148 * going to make it unsigned and it's unlikely to be a quad.
150 * PUBLIC: int proc_wait(SCR *, long, const char *, int, int);
153 proc_wait(SCR
*sp
, long int pid
, const char *cmd
, int silent
, int okpipe
)
159 /* Wait for the utility, ignoring interruptions. */
162 if (waitpid((pid_t
)pid
, &pstat
, 0) != -1)
164 if (errno
!= EINTR
) {
165 msgq(sp
, M_SYSERR
, "waitpid");
171 * Display the utility's exit status. Ignore SIGPIPE from the
172 * parent-writer, as that only means that the utility chose to
173 * exit before reading all of its input.
175 if (WIFSIGNALED(pstat
) && (!okpipe
|| WTERMSIG(pstat
) != SIGPIPE
)) {
176 for (; cmdskip(*cmd
); ++cmd
);
177 p
= msg_print(sp
, cmd
, &nf
);
179 msgq(sp
, M_ERR
, "%.*s%s: received signal: %s%s",
180 (int)MIN(len
, 20), p
, len
> 20 ? " ..." : "",
181 sigmsg(WTERMSIG(pstat
)),
182 WCOREDUMP(pstat
) ? "; core dumped" : "");
184 FREE_SPACE(sp
, p
, 0);
188 if (WIFEXITED(pstat
) && WEXITSTATUS(pstat
)) {
190 * Remain silent for "normal" errors when doing shell file
191 * name expansions, they almost certainly indicate nothing
192 * more than a failure to match.
194 * Remain silent for vi read filter errors. It's historic
198 for (; cmdskip(*cmd
); ++cmd
);
199 p
= msg_print(sp
, cmd
, &nf
);
201 msgq(sp
, M_ERR
, "%.*s%s: exited with status %d",
202 (int)MIN(len
, 20), p
, len
> 20 ? " ..." : "",
205 FREE_SPACE(sp
, p
, 0);
214 * Return a pointer to a message describing a signal.
222 /* POSIX.1-2008 leaves strsignal(3)'s return value unspecified. */
223 if ((message
= strsignal(signo
)) != NULL
)
225 (void)snprintf(buf
, sizeof(buf
), "Unknown signal: %d", signo
);