ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/win_popen.c
Revision: 1.3
Committed: Sun Mar 28 20:33:12 2004 UTC (20 years, 1 month ago) by schorsch
Content type: text/plain
Branch: MAIN
Changes since 1.2: +131 -20 lines
Log Message:
Continued ANSIfication, and other fixes and clarifications.

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: win_popen.c,v 1.2 2003/10/27 10:19:31 schorsch Exp $";
3 #endif
4 /*
5 Replacement for the posix popen() on Windows
6
7 We don't just let the shell do the work like the original.
8 Since per default there's no decent shell around, we implement
9 the most basic functionality right here.
10
11 currently supports mode "r" and | to chain several processes.
12 Substrings between matching quotes are considered one word,
13 ignoring any | within. Quotes don't nest.
14
15 */
16
17 #include <windows.h>
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <ctype.h>
22 #include <io.h> /* _open_osfhandle() */
23 #include <fcntl.h> /* _O_RDONLY */
24
25 #include "rtio.h"
26 #include "rterror.h"
27
28
29 #define RAD_MAX_PIPES 32 /* maximum number of pipes */
30
31 #define R_MODE 1
32 #define W_MODE 2
33 #define A_MODE 3
34
35 static int parse_pipes(char *s, char *lines[], char **infn, char **outfn,
36 int *append, int maxl);
37 static BOOL createPipes(HANDLE*, HANDLE*, HANDLE*, HANDLE*);
38 static BOOL runChild(char*, char*, HANDLE, HANDLE, HANDLE);
39 static void resetStdHandles(HANDLE stdoutOrig, HANDLE stdinOrig);
40 HANDLE newFile(char *fn, int mode);
41
42
43 int
44 win_pclose( /* posix pclose replacement */
45 FILE* p
46 )
47 {
48 fclose(p);
49 /* not sure if it's useful to wait for anything on Windows */
50 return 0;
51 }
52
53
54 FILE *
55 win_popen( /* posix popen replacement */
56 char* command,
57 char* type
58 )
59 {
60 char *execfile, *args;
61 char *cmdlines[RAD_MAX_PIPES];
62 char *infn = NULL, *outfn = NULL;
63 char executable[512], estr[512];
64 int n, i, mode = 0, append = 0;
65 int ncmds = 0;
66 FILE *inf = NULL;
67 HANDLE stdoutRd = NULL, stdoutWr = NULL;
68 HANDLE stdinRd = NULL, stdinWr = NULL;
69 HANDLE stderrWr = NULL;
70 HANDLE stdoutOrig, stdinOrig;
71
72 if (strchr(type, 'w')) {
73 mode = W_MODE;
74 } else if (strchr(type, 'r')) {
75 mode = R_MODE;
76 } else {
77 _snprintf(estr, sizeof(estr),
78 "Invalid mode \"%s\" for win_popen().", type);
79 eputs(estr);
80 return NULL;
81 }
82
83 stdoutOrig = GetStdHandle(STD_OUTPUT_HANDLE);
84 stdinOrig = GetStdHandle(STD_INPUT_HANDLE);
85 /* if we have a console, use it for error output */
86 stderrWr = GetStdHandle(STD_ERROR_HANDLE);
87
88 ncmds = parse_pipes(command,cmdlines,&infn,&outfn,&append,RAD_MAX_PIPES);
89 if(ncmds <= 0) {
90 eputs("Too many pipes or malformed command.");
91 goto error;
92 }
93
94 if (infn != NULL) {
95 stdoutRd = newFile(infn, mode);
96 }
97
98 for(n = 0; n < ncmds; ++n) {
99 if(!createPipes(&stdoutRd, &stdoutWr,
100 &stdinRd, &stdinWr)) {
101 eputs("Error creating pipe");
102 goto error;
103 }
104 if (outfn != NULL && n == ncmds - 1) {
105 CloseHandle(stdoutWr);
106 CloseHandle(stdoutRd);
107 stdoutWr = newFile(outfn, mode);
108 }
109 if (n == 0 && mode == W_MODE) {
110 /* create a standard C file pointer for writing to the input */
111 inf = _fdopen(_open_osfhandle((long)stdinWr, _O_RDONLY), "w");
112 }
113 /* find the executable on the PATH */
114 args = nextword(executable, sizeof(executable), cmdlines[n]);
115 if (args == NULL) {
116 eputs("Empty command.");
117 goto error;
118 }
119 execfile = getpath(executable, getenv("PATH"), X_OK);
120 if(execfile == NULL) {
121 _snprintf(estr, sizeof(estr),
122 "Can't find executable for \"%s\".", executable);
123 eputs(estr);
124 goto error;
125 }
126 if(!runChild(execfile, cmdlines[n], stdinRd, stdoutWr, stderrWr)) {
127 _snprintf(estr, sizeof(estr),
128 "Unable to execute executable \"%s\".", executable);
129 eputs(estr);
130 goto error;
131 }
132 /* close the stdout end just passed to the last process,
133 or the final read will block */
134 CloseHandle(stdoutWr);
135 }
136
137 /* clean up */
138 resetStdHandles(stdinOrig, stdoutOrig);
139 for (i = 0; i < ncmds; i++) free(cmdlines[i]);
140 if (infn != NULL) free(infn);
141 if (outfn != NULL) free(outfn);
142
143 if (mode == R_MODE) {
144 /* return a standard C file pointer for reading the output */
145 return _fdopen(_open_osfhandle((long)stdoutRd, _O_RDONLY), "r");
146 } else if (mode == W_MODE) {
147 /* return a standard C file pointer for writing to the input */
148 return inf;
149 }
150
151 error:
152 resetStdHandles(stdinOrig, stdoutOrig);
153 for (i = 0; i < ncmds; i++) free(cmdlines[i]);
154 if (infn != NULL) free(infn);
155 if (outfn != NULL) free(outfn);
156 return NULL;
157 }
158
159
160 HANDLE
161 newFile(char *fn, int mode)
162 {
163 SECURITY_ATTRIBUTES sAttr;
164 HANDLE fh;
165
166 sAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
167 sAttr.bInheritHandle = TRUE;
168 sAttr.lpSecurityDescriptor = NULL;
169
170 if (mode == R_MODE) {
171 fh = CreateFile(fn,
172 GENERIC_READ,
173 FILE_SHARE_READ,
174 &sAttr,
175 OPEN_EXISTING,
176 FILE_ATTRIBUTE_NORMAL,
177 NULL);
178 } else if (mode == W_MODE || mode == A_MODE) {
179 fh = CreateFile(fn,
180 GENERIC_WRITE,
181 FILE_SHARE_WRITE,
182 &sAttr,
183 mode==W_MODE?CREATE_ALWAYS:OPEN_ALWAYS,
184 FILE_ATTRIBUTE_NORMAL,
185 NULL);
186 }
187 if (fh == NULL) {
188 int e = GetLastError();
189 }
190 return fh;
191 }
192
193
194 static BOOL
195 createPipes( /* establish matching pipes for a subprocess */
196 HANDLE* stdoutRd,
197 HANDLE* stdoutWr,
198 HANDLE* stdinRd,
199 HANDLE* stdinWr
200 )
201 {
202 HANDLE stdoutRdInh = NULL;
203 HANDLE stdinWrInh = NULL;
204 HANDLE curproc;
205 SECURITY_ATTRIBUTES sAttr;
206
207 curproc = GetCurrentProcess();
208
209 /* The rules of inheritance for handles are a mess.
210 Just to be safe, make all handles we pass to the
211 child processes inheritable */
212 sAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
213 sAttr.bInheritHandle = TRUE;
214 sAttr.lpSecurityDescriptor = NULL;
215
216 if(*stdoutRd != NULL){
217 /* if we have a previous stdout pipe,
218 assign the other end to stdin for the new process */
219 CloseHandle(*stdinRd);
220 if(!DuplicateHandle(curproc, *stdoutRd,
221 curproc, stdinRd, 0,
222 TRUE, DUPLICATE_SAME_ACCESS))
223 return FALSE;
224 CloseHandle(*stdoutRd);
225 if(!SetStdHandle(STD_INPUT_HANDLE, *stdinRd))
226 return FALSE;
227 } else {
228 /* there's no previous stdout, create a new stdin pipe */
229 if(!CreatePipe(stdinRd, &stdinWrInh, &sAttr, 0))
230 return FALSE;
231 if(!SetStdHandle(STD_INPUT_HANDLE, *stdinRd))
232 return FALSE;
233 CloseHandle(stdinWrInh);
234 }
235
236 /* create the stdout pipe for the new process */
237 if(!CreatePipe(&stdoutRdInh, stdoutWr, &sAttr, 0))
238 return FALSE;
239 if(!SetStdHandle(STD_OUTPUT_HANDLE, *stdoutWr))
240 return FALSE;
241 if(!DuplicateHandle(curproc, stdoutRdInh,
242 curproc, stdoutRd, 0,
243 FALSE, DUPLICATE_SAME_ACCESS))
244 return FALSE;
245 CloseHandle(stdoutRdInh);
246
247 return TRUE;
248 }
249
250
251 static void
252 resetStdHandles( /* clean up our std streams */
253 HANDLE stdoutOrig,
254 HANDLE stdinOrig
255 )
256 {
257 SetStdHandle(STD_OUTPUT_HANDLE, stdoutOrig);
258 SetStdHandle(STD_INPUT_HANDLE, stdinOrig);
259 }
260
261
262 static int
263 parse_pipes( /* split a shell command pipe sequence */
264 char *s,
265 char *lines[],
266 char **infn,
267 char **outfn,
268 int *append,
269 int maxl
270 )
271 {
272 int n = 0, i;
273 char *se, *ws;
274 char *curs;
275 int llen = 0;
276 int quote = 0;
277 int last = 0;
278
279 if (maxl<= 0) return 0;
280 if (s == NULL) {
281 return 0;
282 }
283 *infn = *outfn = NULL;
284 while (isspace(*s)) s++; /* leading whitespace */
285 se = s;
286 while (n < maxl) {
287 switch (*se) {
288 case '"':
289 if (quote == '"') quote = 0;
290 else if (quote == 0) quote = '"';
291 break;
292 case '\'':
293 if (quote == '\'') quote = 0;
294 else if (quote == 0) quote = '\'';
295 break;
296 case '<':
297 case '>':
298 case '|':
299 case '\0':
300 if (*se != '\0' && quote)
301 break;
302 llen = se - s;
303 curs = malloc(llen+1);
304 strncpy(curs, s, llen);
305 /* remove unix style line-end escapes */
306 while((ws = strstr(curs, "\\\n")) != NULL)
307 *ws = *(ws+1) = ' ';
308 /* remove DOS style line-end escapes */
309 while((ws = strstr(curs, "\\\r\n")) != NULL)
310 *ws = *(ws+1) = *(ws+2) = ' ';
311 while (isspace(*(curs + llen - 1)))
312 llen--; /* trailing whitespace */
313 curs[llen] = '\0';
314
315 if (last == '|' || last == 0) { /* first or pipe */
316 lines[n] = curs;
317 n++;
318 curs = NULL;
319 } else if (last == '<') { /* input file */
320 if (*infn != NULL) {
321 eputs("win_popen(): ambiguous input redirection");
322 goto error;
323 }
324 *infn = curs;
325 curs = NULL;
326 } else if (last == '>') { /* output file */
327 if (*outfn != NULL) {
328 eputs("win_popen(): ambiguous out redirection");
329 goto error;
330 }
331 *outfn = curs;
332 curs = NULL;
333 if (*se != '\0' && *se+1 == '>') { /* >> */
334 *append = 1;
335 se++;
336 }
337 }
338 last = *se;
339
340 if (*se == '\0') return n;
341 s = se + 1;
342 while (isspace(*s)) s++; /* leading whitespace */
343 se = s;
344 break;
345 default:
346 break;
347 }
348 se++;
349 }
350 /* more jobs than slots */
351 error:
352 for (i = 0; i < n; i++) free(lines[i]);
353 if (*infn != NULL) free(*infn);
354 if (*outfn != NULL) free(*outfn);
355 if (curs != NULL) free(curs);
356 return -1;
357 }
358
359
360 static BOOL
361 runChild( /* start a child process with the right std streams */
362 char* executable,
363 char* cmdline,
364 HANDLE stdinRd,
365 HANDLE stdoutWr,
366 HANDLE stderrWr
367 )
368 {
369 PROCESS_INFORMATION procInfo;
370 STARTUPINFO startupInfo;
371
372 /* use the given handles and don't display the console window */
373 ZeroMemory(&startupInfo, sizeof(STARTUPINFO));
374 startupInfo.cb = sizeof(STARTUPINFO);
375 startupInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
376 startupInfo.wShowWindow = SW_HIDE;
377 startupInfo.hStdInput = stdinRd;
378 startupInfo.hStdOutput = stdoutWr;
379 startupInfo.hStdError = stderrWr;
380
381 return CreateProcess(executable, cmdline, NULL, NULL,
382 TRUE, 0,
383 NULL, NULL, &startupInfo, &procInfo);
384 }
385
386
387