]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/nvi/ex/ex_shell.c
bsddialog: import version 0.1
[FreeBSD/FreeBSD.git] / contrib / nvi / ex / ex_shell.c
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
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
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
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 *
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 }