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