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