xref: /freebsd/contrib/nvi/ex/ex_shell.c (revision 1e413cf93298b5b97441a21d9a50fdcd0ee9945e)
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 }
379