Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / nvi / ex / ex_shell.c
1 /*-
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.
6  *
7  * See the LICENSE file for redistribution information.
8  */
9
10 #include "config.h"
11
12 #ifndef lint
13 static const char sccsid[] = "@(#)ex_shell.c    10.38 (Berkeley) 8/19/96";
14 #endif /* not lint */
15
16 #include <sys/param.h>
17 #include <sys/queue.h>
18 #include <sys/wait.h>
19
20 #include <bitstring.h>
21 #include <errno.h>
22 #include <limits.h>
23 #include <signal.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28
29 #include "../common/common.h"
30
31 static const char *sigmsg __P((int));
32
33 /*
34  * ex_shell -- :sh[ell]
35  *      Invoke the program named in the SHELL environment variable
36  *      with the argument -i.
37  *
38  * PUBLIC: int ex_shell __P((SCR *, EXCMD *));
39  */
40 int
41 ex_shell(sp, cmdp)
42         SCR *sp;
43         EXCMD *cmdp;
44 {
45         int rval;
46         char buf[MAXPATHLEN];
47
48         /* We'll need a shell. */
49         if (opts_empty(sp, O_SHELL, 0))
50                 return (1);
51
52         /*
53          * XXX
54          * Assumes all shells use -i.
55          */
56         (void)snprintf(buf, sizeof(buf), "%s -i", O_STR(sp, O_SHELL));
57
58         /* Restore the window name. */
59         (void)sp->gp->scr_rename(sp, NULL, 0);
60
61         /* If we're still in a vi screen, move out explicitly. */
62         rval = ex_exec_proc(sp, cmdp, buf, NULL, !F_ISSET(sp, SC_SCR_EXWROTE));
63
64         /* Set the window name. */
65         (void)sp->gp->scr_rename(sp, sp->frp->name, 1);
66
67         /*
68          * !!!
69          * Historically, vi didn't require a continue message after the
70          * return of the shell.  Match it.
71          */
72         F_SET(sp, SC_EX_WAIT_NO);
73
74         return (rval);
75 }
76
77 /*
78  * ex_exec_proc --
79  *      Run a separate process.
80  *
81  * PUBLIC: int ex_exec_proc __P((SCR *, EXCMD *, char *, const char *, int));
82  */
83 int
84 ex_exec_proc(sp, cmdp, cmd, msg, need_newline)
85         SCR *sp;
86         EXCMD *cmdp;
87         char *cmd;
88         const char *msg;
89         int need_newline;
90 {
91         GS *gp;
92         const char *name;
93         pid_t pid;
94
95         gp = sp->gp;
96
97         /* We'll need a shell. */
98         if (opts_empty(sp, O_SHELL, 0))
99                 return (1);
100
101         /* Enter ex mode. */
102         if (F_ISSET(sp, SC_VI)) {
103                 if (gp->scr_screen(sp, SC_EX)) {
104                         ex_emsg(sp, cmdp->cmd->name, EXM_NOCANON);
105                         return (1);
106                 }
107                 (void)gp->scr_attr(sp, SA_ALTERNATE, 0);
108                 F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE);
109         }
110
111         /* Put out additional newline, message. */
112         if (need_newline)
113                 (void)ex_puts(sp, "\n");
114         if (msg != NULL) {
115                 (void)ex_puts(sp, msg);
116                 (void)ex_puts(sp, "\n");
117         }
118         (void)ex_fflush(sp);
119
120         switch (pid = vfork()) {
121         case -1:                        /* Error. */
122                 msgq(sp, M_SYSERR, "vfork");
123                 return (1);
124         case 0:                         /* Utility. */
125                 if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL)
126                         name = O_STR(sp, O_SHELL);
127                 else
128                         ++name;
129                 execl(O_STR(sp, O_SHELL), name, "-c", cmd, NULL);
130                 msgq_str(sp, M_SYSERR, O_STR(sp, O_SHELL), "execl: %s");
131                 _exit(127);
132                 /* NOTREACHED */
133         default:                        /* Parent. */
134                 return (proc_wait(sp, (long)pid, cmd, 0, 0));
135         }
136         /* NOTREACHED */
137 }
138
139 /*
140  * proc_wait --
141  *      Wait for one of the processes.
142  *
143  * !!!
144  * The pid_t type varies in size from a short to a long depending on the
145  * system.  It has to be cast into something or the standard promotion
146  * rules get you.  I'm using a long based on the belief that nobody is
147  * going to make it unsigned and it's unlikely to be a quad.
148  *
149  * PUBLIC: int proc_wait __P((SCR *, long, const char *, int, int));
150  */
151 int
152 proc_wait(sp, pid, cmd, silent, okpipe)
153         SCR *sp;
154         long pid;
155         const char *cmd;
156         int silent, okpipe;
157 {
158         size_t len;
159         int nf, pstat;
160         char *p;
161
162         /* Wait for the utility, ignoring interruptions. */
163         for (;;) {
164                 errno = 0;
165                 if (waitpid((pid_t)pid, &pstat, 0) != -1)
166                         break;
167                 if (errno != EINTR) {
168                         msgq(sp, M_SYSERR, "waitpid");
169                         return (1);
170                 }
171         }
172
173         /*
174          * Display the utility's exit status.  Ignore SIGPIPE from the
175          * parent-writer, as that only means that the utility chose to
176          * exit before reading all of its input.
177          */
178         if (WIFSIGNALED(pstat) && (!okpipe || WTERMSIG(pstat) != SIGPIPE)) {
179                 for (; isblank(*cmd); ++cmd);
180                 p = msg_print(sp, cmd, &nf);
181                 len = strlen(p);
182                 msgq(sp, M_ERR, "%.*s%s: received signal: %s%s",
183                     MIN(len, 20), p, len > 20 ? " ..." : "",
184                     sigmsg(WTERMSIG(pstat)),
185                     WCOREDUMP(pstat) ? "; core dumped" : "");
186                 if (nf)
187                         FREE_SPACE(sp, p, 0);
188                 return (1);
189         }
190
191         if (WIFEXITED(pstat) && WEXITSTATUS(pstat)) {
192                 /*
193                  * Remain silent for "normal" errors when doing shell file
194                  * name expansions, they almost certainly indicate nothing
195                  * more than a failure to match.
196                  *
197                  * Remain silent for vi read filter errors.  It's historic
198                  * practice.
199                  */
200                 if (!silent) {
201                         for (; isblank(*cmd); ++cmd);
202                         p = msg_print(sp, cmd, &nf);
203                         len = strlen(p);
204                         msgq(sp, M_ERR, "%.*s%s: exited with status %d",
205                             MIN(len, 20), p, len > 20 ? " ..." : "",
206                             WEXITSTATUS(pstat));
207                         if (nf)
208                                 FREE_SPACE(sp, p, 0);
209                 }
210                 return (1);
211         }
212         return (0);
213 }
214
215 /*
216  * XXX
217  * The sys_siglist[] table in the C library has this information, but there's
218  * no portable way to get to it.  (Believe me, I tried.)
219  */
220 typedef struct _sigs {
221         int      number;                /* signal number */
222         char    *message;               /* related message */
223 } SIGS;
224
225 SIGS const sigs[] = {
226 #ifdef SIGABRT
227         SIGABRT,        "Abort trap",
228 #endif
229 #ifdef SIGALRM
230         SIGALRM,        "Alarm clock",
231 #endif
232 #ifdef SIGBUS
233         SIGBUS,         "Bus error",
234 #endif
235 #ifdef SIGCLD
236         SIGCLD,         "Child exited or stopped",
237 #endif
238 #ifdef SIGCHLD
239         SIGCHLD,        "Child exited",
240 #endif
241 #ifdef SIGCONT
242         SIGCONT,        "Continued",
243 #endif
244 #ifdef SIGDANGER
245         SIGDANGER,      "System crash imminent",
246 #endif
247 #ifdef SIGEMT
248         SIGEMT,         "EMT trap",
249 #endif
250 #ifdef SIGFPE
251         SIGFPE,         "Floating point exception",
252 #endif
253 #ifdef SIGGRANT
254         SIGGRANT,       "HFT monitor mode granted",
255 #endif
256 #ifdef SIGHUP
257         SIGHUP,         "Hangup",
258 #endif
259 #ifdef SIGILL
260         SIGILL,         "Illegal instruction",
261 #endif
262 #ifdef SIGINFO
263         SIGINFO,        "Information request",
264 #endif
265 #ifdef SIGINT
266         SIGINT,         "Interrupt",
267 #endif
268 #ifdef SIGIO
269         SIGIO,          "I/O possible",
270 #endif
271 #ifdef SIGIOT
272         SIGIOT,         "IOT trap",
273 #endif
274 #ifdef SIGKILL
275         SIGKILL,        "Killed",
276 #endif
277 #ifdef SIGLOST
278         SIGLOST,        "Record lock",
279 #endif
280 #ifdef SIGMIGRATE
281         SIGMIGRATE,     "Migrate process to another CPU",
282 #endif
283 #ifdef SIGMSG
284         SIGMSG,         "HFT input data pending",
285 #endif
286 #ifdef SIGPIPE
287         SIGPIPE,        "Broken pipe",
288 #endif
289 #ifdef SIGPOLL
290         SIGPOLL,        "I/O possible",
291 #endif
292 #ifdef SIGPRE
293         SIGPRE,         "Programming error",
294 #endif
295 #ifdef SIGPROF
296         SIGPROF,        "Profiling timer expired",
297 #endif
298 #ifdef SIGPWR
299         SIGPWR,         "Power failure imminent",
300 #endif
301 #ifdef SIGRETRACT
302         SIGRETRACT,     "HFT monitor mode retracted",
303 #endif
304 #ifdef SIGQUIT
305         SIGQUIT,        "Quit",
306 #endif
307 #ifdef SIGSAK
308         SIGSAK,         "Secure Attention Key",
309 #endif
310 #ifdef SIGSEGV
311         SIGSEGV,        "Segmentation fault",
312 #endif
313 #ifdef SIGSOUND
314         SIGSOUND,       "HFT sound sequence completed",
315 #endif
316 #ifdef SIGSTOP
317         SIGSTOP,        "Suspended (signal)",
318 #endif
319 #ifdef SIGSYS
320         SIGSYS,         "Bad system call",
321 #endif
322 #ifdef SIGTERM
323         SIGTERM,        "Terminated",
324 #endif
325 #ifdef SIGTRAP
326         SIGTRAP,        "Trace/BPT trap",
327 #endif
328 #ifdef SIGTSTP
329         SIGTSTP,        "Suspended",
330 #endif
331 #ifdef SIGTTIN
332         SIGTTIN,        "Stopped (tty input)",
333 #endif
334 #ifdef SIGTTOU
335         SIGTTOU,        "Stopped (tty output)",
336 #endif
337 #ifdef SIGURG
338         SIGURG,         "Urgent I/O condition",
339 #endif
340 #ifdef SIGUSR1
341         SIGUSR1,        "User defined signal 1",
342 #endif
343 #ifdef SIGUSR2
344         SIGUSR2,        "User defined signal 2",
345 #endif
346 #ifdef SIGVTALRM
347         SIGVTALRM,      "Virtual timer expired",
348 #endif
349 #ifdef SIGWINCH
350         SIGWINCH,       "Window size changes",
351 #endif
352 #ifdef SIGXCPU
353         SIGXCPU,        "Cputime limit exceeded",
354 #endif
355 #ifdef SIGXFSZ
356         SIGXFSZ,        "Filesize limit exceeded",
357 #endif
358 };
359
360 /*
361  * sigmsg --
362  *      Return a pointer to a message describing a signal.
363  */
364 static const char *
365 sigmsg(signo)
366         int signo;
367 {
368         static char buf[40];
369         const SIGS *sigp;
370         int n;
371
372         for (n = 0,
373             sigp = &sigs[0]; n < sizeof(sigs) / sizeof(sigs[0]); ++n, ++sigp)
374                 if (sigp->number == signo)
375                         return (sigp->message);
376         (void)snprintf(buf, sizeof(buf), "Unknown signal: %d", signo);
377         return (buf);
378 }