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.41 2001/06/25 15:19:19 skimo Exp $ (Berkeley) $Date: 2001/06/25 15:19:19 $";
16 #include <sys/param.h>
17 #include <sys/queue.h>
20 #include <bitstring.h>
29 #include "../common/common.h"
31 static const char *sigmsg
__P((int));
34 * ex_shell -- :sh[ell]
35 * Invoke the program named in the SHELL environment variable
36 * with the argument -i.
38 * PUBLIC: int ex_shell __P((SCR *, EXCMD *));
41 ex_shell(SCR
*sp
, EXCMD
*cmdp
)
46 /* We'll need a shell. */
47 if (opts_empty(sp
, O_SHELL
, 0))
52 * Assumes all shells use -i.
54 (void)snprintf(buf
, sizeof(buf
), "%s -i", O_STR(sp
, O_SHELL
));
56 /* Restore the window name. */
57 (void)sp
->gp
->scr_rename(sp
, NULL
, 0);
59 /* If we're still in a vi screen, move out explicitly. */
60 rval
= ex_exec_proc(sp
, cmdp
, buf
, NULL
, !F_ISSET(sp
, SC_SCR_EXWROTE
));
62 /* Set the window name. */
63 (void)sp
->gp
->scr_rename(sp
, sp
->frp
->name
, 1);
67 * Historically, vi didn't require a continue message after the
68 * return of the shell. Match it.
70 F_SET(sp
, SC_EX_WAIT_NO
);
77 * Run a separate process.
79 * PUBLIC: int ex_exec_proc __P((SCR *, EXCMD *, char *, const char *, int));
82 ex_exec_proc(SCR
*sp
, EXCMD
*cmdp
, char *cmd
, const char *msg
, int need_newline
)
90 /* We'll need a shell. */
91 if (opts_empty(sp
, O_SHELL
, 0))
95 if (F_ISSET(sp
, SC_VI
)) {
96 if (gp
->scr_screen(sp
, SC_EX
)) {
97 ex_wemsg(sp
, cmdp
->cmd
->name
, EXM_NOCANON
);
100 (void)gp
->scr_attr(sp
, SA_ALTERNATE
, 0);
101 F_SET(sp
, SC_SCR_EX
| SC_SCR_EXWROTE
);
104 /* Put out additional newline, message. */
106 (void)ex_puts(sp
, "\n");
108 (void)ex_puts(sp
, msg
);
109 (void)ex_puts(sp
, "\n");
113 switch (pid
= vfork()) {
114 case -1: /* Error. */
115 msgq(sp
, M_SYSERR
, "vfork");
117 case 0: /* Utility. */
120 if ((name
= strrchr(O_STR(sp
, O_SHELL
), '/')) == NULL
)
121 name
= O_STR(sp
, O_SHELL
);
124 execl(O_STR(sp
, O_SHELL
), name
, "-c", cmd
, NULL
);
125 msgq_str(sp
, M_SYSERR
, O_STR(sp
, O_SHELL
), "execl: %s");
128 default: /* Parent. */
129 return (proc_wait(sp
, (long)pid
, cmd
, 0, 0));
136 * Wait for one of the processes.
139 * The pid_t type varies in size from a short to a long depending on the
140 * system. It has to be cast into something or the standard promotion
141 * rules get you. I'm using a long based on the belief that nobody is
142 * going to make it unsigned and it's unlikely to be a quad.
144 * PUBLIC: int proc_wait __P((SCR *, long, const char *, int, int));
147 proc_wait(SCR
*sp
, long int pid
, const char *cmd
, int silent
, int okpipe
)
153 /* Wait for the utility, ignoring interruptions. */
156 if (waitpid((pid_t
)pid
, &pstat
, 0) != -1)
158 if (errno
!= EINTR
) {
159 msgq(sp
, M_SYSERR
, "waitpid");
165 * Display the utility's exit status. Ignore SIGPIPE from the
166 * parent-writer, as that only means that the utility chose to
167 * exit before reading all of its input.
169 if (WIFSIGNALED(pstat
) && (!okpipe
|| WTERMSIG(pstat
) != SIGPIPE
)) {
170 for (; isblank(*cmd
); ++cmd
);
171 p
= msg_print(sp
, cmd
, &nf
);
173 msgq(sp
, M_ERR
, "%.*s%s: received signal: %s%s",
174 MIN(len
, 20), p
, len
> 20 ? " ..." : "",
175 sigmsg(WTERMSIG(pstat
)),
176 WCOREDUMP(pstat
) ? "; core dumped" : "");
178 FREE_SPACE(sp
, p
, 0);
182 if (WIFEXITED(pstat
) && WEXITSTATUS(pstat
)) {
184 * Remain silent for "normal" errors when doing shell file
185 * name expansions, they almost certainly indicate nothing
186 * more than a failure to match.
188 * Remain silent for vi read filter errors. It's historic
192 for (; isblank(*cmd
); ++cmd
);
193 p
= msg_print(sp
, cmd
, &nf
);
195 msgq(sp
, M_ERR
, "%.*s%s: exited with status %d",
196 MIN(len
, 20), p
, len
> 20 ? " ..." : "",
199 FREE_SPACE(sp
, p
, 0);
208 * The sys_siglist[] table in the C library has this information, but there's
209 * no portable way to get to it. (Believe me, I tried.)
211 typedef struct _sigs
{
212 int number
; /* signal number */
213 char *message
; /* related message */
216 SIGS
const sigs
[] = {
218 SIGABRT
, "Abort trap",
221 SIGALRM
, "Alarm clock",
227 SIGCLD
, "Child exited or stopped",
230 SIGCHLD
, "Child exited",
233 SIGCONT
, "Continued",
236 SIGDANGER
, "System crash imminent",
242 SIGFPE
, "Floating point exception",
245 SIGGRANT
, "HFT monitor mode granted",
251 SIGILL
, "Illegal instruction",
254 SIGINFO
, "Information request",
260 SIGIO
, "I/O possible",
269 SIGLOST
, "Record lock",
272 SIGMIGRATE
, "Migrate process to another CPU",
275 SIGMSG
, "HFT input data pending",
278 SIGPIPE
, "Broken pipe",
281 SIGPOLL
, "I/O possible",
284 SIGPRE
, "Programming error",
287 SIGPROF
, "Profiling timer expired",
290 SIGPWR
, "Power failure imminent",
293 SIGRETRACT
, "HFT monitor mode retracted",
299 SIGSAK
, "Secure Attention Key",
302 SIGSEGV
, "Segmentation fault",
305 SIGSOUND
, "HFT sound sequence completed",
308 SIGSTOP
, "Suspended (signal)",
311 SIGSYS
, "Bad system call",
314 SIGTERM
, "Terminated",
317 SIGTRAP
, "Trace/BPT trap",
320 SIGTSTP
, "Suspended",
323 SIGTTIN
, "Stopped (tty input)",
326 SIGTTOU
, "Stopped (tty output)",
329 SIGURG
, "Urgent I/O condition",
332 SIGUSR1
, "User defined signal 1",
335 SIGUSR2
, "User defined signal 2",
338 SIGVTALRM
, "Virtual timer expired",
341 SIGWINCH
, "Window size changes",
344 SIGXCPU
, "Cputime limit exceeded",
347 SIGXFSZ
, "Filesize limit exceeded",
353 * Return a pointer to a message describing a signal.
363 sigp
= &sigs
[0]; n
< sizeof(sigs
) / sizeof(sigs
[0]); ++n
, ++sigp
)
364 if (sigp
->number
== signo
)
365 return (sigp
->message
);
366 (void)snprintf(buf
, sizeof(buf
), "Unknown signal: %d", signo
);