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