1 /* popen/pclose:
2 *
3 * simple MS-DOS piping scheme to imitate UNIX pipes
4 */
5
6 #include "EXTERN.h"
7 #include "common.h"
8 #include <setjmp.h>
9 #include "util2.h"
10 #include "util3.h"
11
12 #ifndef _NFILE
13 # define _NFILE 5 /* Number of open files */
14 #endif _NFILE
15
16 #define READIT 1 /* Read pipe */
17 #define WRITEIT 2 /* Write pipe */
18
19 static char* prgname[_NFILE]; /* program name if write pipe */
20 static int pipetype[_NFILE]; /* 1=read 2=write */
21 static char* pipename[_NFILE]; /* pipe file name */
22
23 /*
24 *------------------------------------------------------------------------
25 * run: Execute command via SHELL or COMSPEC
26 *------------------------------------------------------------------------
27 */
28
29 static int
run(char * command)30 run(char* command)
31 {
32 jmp_buf panic; /* How to recover from errors */
33 char* shell; /* Command processor */
34 char* s = NULL; /* Holds the command */
35 int s_is_malloced = 0; /* True if need to free 's' */
36 static char* command_com = "COMMAND.COM";
37 int status; /* Return codes */
38 char* shellpath; /* Full command processor path */
39 char* bp; /* Generic string pointer */
40 static char dash_c[] = "/c";
41
42 s = savestr(command);
43 /* Determine the command processor */
44 if ((shell = getenv("SHELL")) == NULL
45 && (shell = getenv("COMSPEC")) == NULL)
46 shell = command_com;
47 strupr(shell);
48 shellpath = shell;
49 /* Strip off any leading backslash directories */
50 shell = rindex(shellpath, '\\');
51 if (shell != NULL)
52 shell++;
53 else
54 shell = shellpath;
55 /* Strip off any leading slash directories */
56 bp = rindex(shell, '/');
57 if (bp != NULL)
58 shell = ++bp;
59 if (strcmp(shell, command_com) != 0) {
60 /* MKS Shell needs quoted argument */
61 char* bp;
62 bp = s = safemalloc(strlen(command) + 3);
63 *bp++ = '\'';
64 while ((*bp++ = *command++) != '\0') ;
65 *(bp - 1) = '\'';
66 *bp = '\0';
67 s_is_malloced = 1;
68 } else
69 s = command;
70 /* Run the program */
71 status = spawnl(P_WAIT, shellpath, shell, dash_c, s, NULL);
72 if (s_is_malloced)
73 free(s);
74 return status;
75 }
76
77 /*
78 *------------------------------------------------------------------------
79 * uniquepipe: returns a unique file name
80 *------------------------------------------------------------------------
81 */
82
83 static char*
uniquepipe(void)84 uniquepipe(void)
85 {
86 static char name[14];
87 static short int num = 0;
88 (void) sprintf(name, "pipe%04d.tmp", num++);
89 return name;
90 }
91
92 /*
93 *------------------------------------------------------------------------
94 * resetpipe: Private routine to cancel a pipe
95 *------------------------------------------------------------------------
96 */
97 static void
resetpipe(int fd)98 resetpipe(int fd)
99 {
100 char* bp;
101 if (fd >= 0 && fd < _NFILE) {
102 pipetype[fd] = 0;
103 if ((bp = pipename[fd]) != NULL) {
104 (void) unlink(bp);
105 free(bp);
106 pipename[fd] = NULL;
107 }
108 if ((bp = prgname[fd]) != NULL) {
109 free(bp);
110 prgname[fd] = NULL;
111 }
112 }
113 }
114
115 /*
116 *------------------------------------------------------------------------
117 * popen: open a pipe
118 *------------------------------------------------------------------------
119 */
popen(prg,type)120 FILE* popen(prg, type)
121 char* prg; /* The command to be run */
122 char* type; /* "w" or "r" */
123 {
124 FILE* p = NULL; /* Where we open the pipe */
125 int ostdin; /* Where our stdin is now */
126 int pipefd = -1; /* fileno(p) -- for convenience */
127 char* tmpfile; /* Holds name of pipe file */
128 jmp_buf panic; /* Where to go if there's an error */
129 int lineno; /* Line number where panic happened */
130
131 /* Get a unique pipe file name */
132 tmpfile = filexp("%Y/");
133 strcat(tmpfile, uniquepipe());
134 if ((lineno = setjmp(panic)) != 0) {
135 /* An error has occurred, so clean up */
136 int E = errno;
137 if (p != NULL)
138 (void) fclose(p);
139 resetpipe(pipefd);
140 errno = E;
141 lineno = lineno;
142 return NULL;
143 }
144 if (strcmp(type, "w") == 0) {
145 /* for write style pipe, pclose handles program execution */
146 if ((p = fopen(tmpfile, "w")) != NULL) {
147 pipefd = fileno(p);
148 pipetype[pipefd] = WRITEIT;
149 pipename[pipefd] = savestr(tmpfile);
150 prgname[pipefd] = savestr(prg);
151 }
152 } else if (strcmp(type, "r") == 0) {
153 /* read pipe must create tmp file, set up stdout to point to the temp
154 * file, and run the program. note that if the pipe file cannot be
155 * opened, it'll return a condition indicating pipe failure, which is
156 * fine. */
157 if ((p = fopen(tmpfile, "w")) != NULL) {
158 int ostdout;
159 pipefd = fileno(p);
160 pipetype[pipefd]= READIT;
161 pipename[pipefd] = savestr(tmpfile);
162 /* Redirect stdin for the new command */
163 ostdout = dup(fileno(stdout));
164 if (dup2(fileno(stdout), pipefd) < 0) {
165 int E = errno;
166 (void) dup2(fileno(stdout), ostdout);
167 errno = E;
168 longjmp(panic, __LINE__);
169 }
170 if (run(prg) != 0)
171 longjmp(panic, __LINE__);
172 if (dup2(fileno(stdout), ostdout) < 0)
173 longjmp(panic, __LINE__);
174 if (fclose(p) < 0)
175 longjmp(panic, __LINE__);
176 if ((p = fopen(tmpfile, "r")) == NULL)
177 longjmp(panic, __LINE__);
178 }
179 } else {
180 /* screwy call or unsupported type */
181 errno = EINVFNC;
182 longjmp(panic, __LINE__);
183 }
184 return p;
185 }
186
187 /* close a pipe */
188 int
pclose(p)189 pclose(p)
190 FILE* p;
191 {
192 int pipefd = -1; /* Fildes where pipe is opened */
193 int ostdout; /* Where our stdout points now */
194 int ostdin; /* Where our stdin points now */
195 jmp_buf panic; /* Context to return to if error */
196 int lineno; /* Line number where panic happened */
197 if ((lineno = setjmp(panic)) != 0) {
198 /* An error has occurred, so clean up and return */
199 int E = errno;
200 if (p != NULL)
201 (void) fclose(p);
202 resetpipe(pipefd);
203 errno = E;
204 lineno = lineno;
205 return -1;
206 }
207 pipefd = fileno(p);
208 if (fclose(p) < 0)
209 longjmp(panic, __LINE__);
210 switch (pipetype[pipefd]) {
211 case WRITEIT:
212 /* open the temp file again as read, redirect stdin from that
213 * file, run the program, then clean up. */
214 if ((p = fopen(pipename[pipefd],"r")) == NULL)
215 longjmp(panic, __LINE__);
216 ostdin = dup(fileno(stdin));
217 if (dup2(fileno(stdin), fileno(p)) < 0)
218 longjmp(panic, __LINE__);
219 if (run(prgname[pipefd]) != 0)
220 longjmp(panic, __LINE__);
221 if (dup2(fileno(stdin), ostdin) < 0)
222 longjmp(panic, __LINE__);
223 if (fclose(p) < 0)
224 longjmp(panic, __LINE__);
225 resetpipe(pipefd);
226 break;
227 case READIT:
228 /* close the temp file and remove it */
229 resetpipe(pipefd);
230 break;
231 default:
232 errno = EINVFNC;
233 longjmp(panic, __LINE__);
234 /*NOTREACHED*/
235 }
236 return 0;
237 }
238