xref: /dragonfly/contrib/nvi2/ex/ex_shell.c (revision b1ac2ebb)
1e0b8e63eSJohn Marino /*-
2e0b8e63eSJohn Marino  * Copyright (c) 1992, 1993, 1994
3e0b8e63eSJohn Marino  *	The Regents of the University of California.  All rights reserved.
4e0b8e63eSJohn Marino  * Copyright (c) 1992, 1993, 1994, 1995, 1996
5e0b8e63eSJohn Marino  *	Keith Bostic.  All rights reserved.
6e0b8e63eSJohn Marino  *
7e0b8e63eSJohn Marino  * See the LICENSE file for redistribution information.
8e0b8e63eSJohn Marino  */
9e0b8e63eSJohn Marino 
10e0b8e63eSJohn Marino #include "config.h"
11e0b8e63eSJohn Marino 
12e0b8e63eSJohn Marino #include <sys/queue.h>
13e0b8e63eSJohn Marino #include <sys/time.h>
14e0b8e63eSJohn Marino #include <sys/wait.h>
15e0b8e63eSJohn Marino 
16e0b8e63eSJohn Marino #include <bitstring.h>
17e0b8e63eSJohn Marino #include <ctype.h>
18e0b8e63eSJohn Marino #include <errno.h>
19e0b8e63eSJohn Marino #include <limits.h>
20e0b8e63eSJohn Marino #include <signal.h>
21e0b8e63eSJohn Marino #include <stdio.h>
22e0b8e63eSJohn Marino #include <stdlib.h>
23e0b8e63eSJohn Marino #include <string.h>
24e0b8e63eSJohn Marino #include <unistd.h>
25e0b8e63eSJohn Marino 
26e0b8e63eSJohn Marino #include "../common/common.h"
27e0b8e63eSJohn Marino 
28e0b8e63eSJohn Marino static const char *sigmsg(int);
29e0b8e63eSJohn Marino 
30e0b8e63eSJohn Marino /*
31e0b8e63eSJohn Marino  * ex_shell -- :sh[ell]
32e0b8e63eSJohn Marino  *	Invoke the program named in the SHELL environment variable
33e0b8e63eSJohn Marino  *	with the argument -i.
34e0b8e63eSJohn Marino  *
35e0b8e63eSJohn Marino  * PUBLIC: int ex_shell(SCR *, EXCMD *);
36e0b8e63eSJohn Marino  */
37e0b8e63eSJohn Marino int
ex_shell(SCR * sp,EXCMD * cmdp)38e0b8e63eSJohn Marino ex_shell(SCR *sp, EXCMD *cmdp)
39e0b8e63eSJohn Marino {
40e0b8e63eSJohn Marino 	int rval;
41e0b8e63eSJohn Marino 	char *buf;
42e0b8e63eSJohn Marino 
43e0b8e63eSJohn Marino 	/* We'll need a shell. */
44e0b8e63eSJohn Marino 	if (opts_empty(sp, O_SHELL, 0))
45e0b8e63eSJohn Marino 		return (1);
46e0b8e63eSJohn Marino 
47e0b8e63eSJohn Marino 	/*
48e0b8e63eSJohn Marino 	 * XXX
49e0b8e63eSJohn Marino 	 * Assumes all shells use -i.
50e0b8e63eSJohn Marino 	 */
51*b1ac2ebbSDaniel Fojt 	if (asprintf(&buf, "%s -i", O_STR(sp, O_SHELL)) == -1) {
52e0b8e63eSJohn Marino 		msgq(sp, M_SYSERR, NULL);
53e0b8e63eSJohn Marino 		return (1);
54e0b8e63eSJohn Marino 	}
55e0b8e63eSJohn Marino 
56e0b8e63eSJohn Marino 	/* Restore the window name. */
57e0b8e63eSJohn Marino 	(void)sp->gp->scr_rename(sp, NULL, 0);
58e0b8e63eSJohn Marino 
59e0b8e63eSJohn Marino 	/* If we're still in a vi screen, move out explicitly. */
60e0b8e63eSJohn Marino 	rval = ex_exec_proc(sp, cmdp, buf, NULL, !F_ISSET(sp, SC_SCR_EXWROTE));
61e0b8e63eSJohn Marino 	free(buf);
62e0b8e63eSJohn Marino 
63e0b8e63eSJohn Marino 	/* Set the window name. */
64e0b8e63eSJohn Marino 	(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
65e0b8e63eSJohn Marino 
66e0b8e63eSJohn Marino 	/*
67e0b8e63eSJohn Marino 	 * !!!
68e0b8e63eSJohn Marino 	 * Historically, vi didn't require a continue message after the
69e0b8e63eSJohn Marino 	 * return of the shell.  Match it.
70e0b8e63eSJohn Marino 	 */
71e0b8e63eSJohn Marino 	F_SET(sp, SC_EX_WAIT_NO);
72e0b8e63eSJohn Marino 
73e0b8e63eSJohn Marino 	return (rval);
74e0b8e63eSJohn Marino }
75e0b8e63eSJohn Marino 
76e0b8e63eSJohn Marino /*
77e0b8e63eSJohn Marino  * ex_exec_proc --
78e0b8e63eSJohn Marino  *	Run a separate process.
79e0b8e63eSJohn Marino  *
80e0b8e63eSJohn Marino  * PUBLIC: int ex_exec_proc(SCR *, EXCMD *, char *, const char *, int);
81e0b8e63eSJohn Marino  */
82e0b8e63eSJohn Marino int
ex_exec_proc(SCR * sp,EXCMD * cmdp,char * cmd,const char * msg,int need_newline)83e0b8e63eSJohn Marino ex_exec_proc(SCR *sp, EXCMD *cmdp, char *cmd, const char *msg, int need_newline)
84e0b8e63eSJohn Marino {
85e0b8e63eSJohn Marino 	GS *gp;
86e0b8e63eSJohn Marino 	const char *name;
87e0b8e63eSJohn Marino 	pid_t pid;
88e0b8e63eSJohn Marino 
89e0b8e63eSJohn Marino 	gp = sp->gp;
90e0b8e63eSJohn Marino 
91e0b8e63eSJohn Marino 	/* We'll need a shell. */
92e0b8e63eSJohn Marino 	if (opts_empty(sp, O_SHELL, 0))
93e0b8e63eSJohn Marino 		return (1);
94e0b8e63eSJohn Marino 
95e0b8e63eSJohn Marino 	/* Enter ex mode. */
96e0b8e63eSJohn Marino 	if (F_ISSET(sp, SC_VI)) {
97e0b8e63eSJohn Marino 		if (gp->scr_screen(sp, SC_EX)) {
98e0b8e63eSJohn Marino 			ex_wemsg(sp, cmdp->cmd->name, EXM_NOCANON);
99e0b8e63eSJohn Marino 			return (1);
100e0b8e63eSJohn Marino 		}
101e0b8e63eSJohn Marino 		(void)gp->scr_attr(sp, SA_ALTERNATE, 0);
102e0b8e63eSJohn Marino 		F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE);
103e0b8e63eSJohn Marino 	}
104e0b8e63eSJohn Marino 
105e0b8e63eSJohn Marino 	/* Put out additional newline, message. */
106e0b8e63eSJohn Marino 	if (need_newline)
107e0b8e63eSJohn Marino 		(void)ex_puts(sp, "\n");
108e0b8e63eSJohn Marino 	if (msg != NULL) {
109e0b8e63eSJohn Marino 		(void)ex_puts(sp, msg);
110e0b8e63eSJohn Marino 		(void)ex_puts(sp, "\n");
111e0b8e63eSJohn Marino 	}
112e0b8e63eSJohn Marino 	(void)ex_fflush(sp);
113e0b8e63eSJohn Marino 
114e0b8e63eSJohn Marino 	switch (pid = vfork()) {
115e0b8e63eSJohn Marino 	case -1:			/* Error. */
116e0b8e63eSJohn Marino 		msgq(sp, M_SYSERR, "vfork");
117e0b8e63eSJohn Marino 		return (1);
118e0b8e63eSJohn Marino 	case 0:				/* Utility. */
119e0b8e63eSJohn Marino 		if (gp->scr_child)
120e0b8e63eSJohn Marino 			gp->scr_child(sp);
121e0b8e63eSJohn Marino 		if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL)
122e0b8e63eSJohn Marino 			name = O_STR(sp, O_SHELL);
123e0b8e63eSJohn Marino 		else
124e0b8e63eSJohn Marino 			++name;
125e0b8e63eSJohn Marino 		execl(O_STR(sp, O_SHELL), name, "-c", cmd, (char *)NULL);
126e0b8e63eSJohn Marino 		msgq_str(sp, M_SYSERR, O_STR(sp, O_SHELL), "execl: %s");
127e0b8e63eSJohn Marino 		_exit(127);
128e0b8e63eSJohn Marino 		/* NOTREACHED */
129e0b8e63eSJohn Marino 	default:			/* Parent. */
130e0b8e63eSJohn Marino 		return (proc_wait(sp, (long)pid, cmd, 0, 0));
131e0b8e63eSJohn Marino 	}
132e0b8e63eSJohn Marino 	/* NOTREACHED */
133e0b8e63eSJohn Marino }
134e0b8e63eSJohn Marino 
135e0b8e63eSJohn Marino /*
136e0b8e63eSJohn Marino  * proc_wait --
137e0b8e63eSJohn Marino  *	Wait for one of the processes.
138e0b8e63eSJohn Marino  *
139e0b8e63eSJohn Marino  * !!!
140e0b8e63eSJohn Marino  * The pid_t type varies in size from a short to a long depending on the
141e0b8e63eSJohn Marino  * system.  It has to be cast into something or the standard promotion
142e0b8e63eSJohn Marino  * rules get you.  I'm using a long based on the belief that nobody is
143e0b8e63eSJohn Marino  * going to make it unsigned and it's unlikely to be a quad.
144e0b8e63eSJohn Marino  *
145e0b8e63eSJohn Marino  * PUBLIC: int proc_wait(SCR *, long, const char *, int, int);
146e0b8e63eSJohn Marino  */
147e0b8e63eSJohn Marino int
proc_wait(SCR * sp,long int pid,const char * cmd,int silent,int okpipe)148e0b8e63eSJohn Marino proc_wait(SCR *sp, long int pid, const char *cmd, int silent, int okpipe)
149e0b8e63eSJohn Marino {
150e0b8e63eSJohn Marino 	size_t len;
151e0b8e63eSJohn Marino 	int nf, pstat;
152e0b8e63eSJohn Marino 	char *p;
153e0b8e63eSJohn Marino 
154e0b8e63eSJohn Marino 	/* Wait for the utility, ignoring interruptions. */
155e0b8e63eSJohn Marino 	for (;;) {
156e0b8e63eSJohn Marino 		errno = 0;
157e0b8e63eSJohn Marino 		if (waitpid((pid_t)pid, &pstat, 0) != -1)
158e0b8e63eSJohn Marino 			break;
159e0b8e63eSJohn Marino 		if (errno != EINTR) {
160e0b8e63eSJohn Marino 			msgq(sp, M_SYSERR, "waitpid");
161e0b8e63eSJohn Marino 			return (1);
162e0b8e63eSJohn Marino 		}
163e0b8e63eSJohn Marino 	}
164e0b8e63eSJohn Marino 
165e0b8e63eSJohn Marino 	/*
166e0b8e63eSJohn Marino 	 * Display the utility's exit status.  Ignore SIGPIPE from the
167e0b8e63eSJohn Marino 	 * parent-writer, as that only means that the utility chose to
168e0b8e63eSJohn Marino 	 * exit before reading all of its input.
169e0b8e63eSJohn Marino 	 */
170e0b8e63eSJohn Marino 	if (WIFSIGNALED(pstat) && (!okpipe || WTERMSIG(pstat) != SIGPIPE)) {
171e0b8e63eSJohn Marino 		for (; cmdskip(*cmd); ++cmd);
172e0b8e63eSJohn Marino 		p = msg_print(sp, cmd, &nf);
173e0b8e63eSJohn Marino 		len = strlen(p);
174e0b8e63eSJohn Marino 		msgq(sp, M_ERR, "%.*s%s: received signal: %s%s",
175e0b8e63eSJohn Marino 		    (int)MIN(len, 20), p, len > 20 ? " ..." : "",
176e0b8e63eSJohn Marino 		    sigmsg(WTERMSIG(pstat)),
177e0b8e63eSJohn Marino 		    WCOREDUMP(pstat) ? "; core dumped" : "");
178e0b8e63eSJohn Marino 		if (nf)
179e0b8e63eSJohn Marino 			FREE_SPACE(sp, p, 0);
180e0b8e63eSJohn Marino 		return (1);
181e0b8e63eSJohn Marino 	}
182e0b8e63eSJohn Marino 
183e0b8e63eSJohn Marino 	if (WIFEXITED(pstat) && WEXITSTATUS(pstat)) {
184e0b8e63eSJohn Marino 		/*
185e0b8e63eSJohn Marino 		 * Remain silent for "normal" errors when doing shell file
186e0b8e63eSJohn Marino 		 * name expansions, they almost certainly indicate nothing
187e0b8e63eSJohn Marino 		 * more than a failure to match.
188e0b8e63eSJohn Marino 		 *
189e0b8e63eSJohn Marino 		 * Remain silent for vi read filter errors.  It's historic
190e0b8e63eSJohn Marino 		 * practice.
191e0b8e63eSJohn Marino 		 */
192e0b8e63eSJohn Marino 		if (!silent) {
193e0b8e63eSJohn Marino 			for (; cmdskip(*cmd); ++cmd);
194e0b8e63eSJohn Marino 			p = msg_print(sp, cmd, &nf);
195e0b8e63eSJohn Marino 			len = strlen(p);
196e0b8e63eSJohn Marino 			msgq(sp, M_ERR, "%.*s%s: exited with status %d",
197e0b8e63eSJohn Marino 			    (int)MIN(len, 20), p, len > 20 ? " ..." : "",
198e0b8e63eSJohn Marino 			    WEXITSTATUS(pstat));
199e0b8e63eSJohn Marino 			if (nf)
200e0b8e63eSJohn Marino 				FREE_SPACE(sp, p, 0);
201e0b8e63eSJohn Marino 		}
202e0b8e63eSJohn Marino 		return (1);
203e0b8e63eSJohn Marino 	}
204e0b8e63eSJohn Marino 	return (0);
205e0b8e63eSJohn Marino }
206e0b8e63eSJohn Marino 
207e0b8e63eSJohn Marino /*
208e0b8e63eSJohn Marino  * sigmsg --
209e0b8e63eSJohn Marino  * 	Return a pointer to a message describing a signal.
210e0b8e63eSJohn Marino  */
211e0b8e63eSJohn Marino static const char *
sigmsg(int signo)212e0b8e63eSJohn Marino sigmsg(int signo)
213e0b8e63eSJohn Marino {
214e0b8e63eSJohn Marino 	static char buf[40];
215e0b8e63eSJohn Marino 	char *message;
216e0b8e63eSJohn Marino 
217e0b8e63eSJohn Marino 	/* POSIX.1-2008 leaves strsignal(3)'s return value unspecified. */
218e0b8e63eSJohn Marino 	if ((message = strsignal(signo)) != NULL)
219e0b8e63eSJohn Marino 		return message;
220e0b8e63eSJohn Marino 	(void)snprintf(buf, sizeof(buf), "Unknown signal: %d", signo);
221e0b8e63eSJohn Marino 	return (buf);
222e0b8e63eSJohn Marino }
223