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