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