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