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