ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/win_popen.c
Revision: 1.5
Committed: Wed Oct 5 17:20:55 2011 UTC (12 years, 6 months ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: rad5R4, rad5R2, rad4R2P2, rad5R0, rad5R1, rad4R2, rad4R1, rad4R2P1, rad5R3, HEAD
Changes since 1.4: +6 -1 lines
Log Message:
Windows compatibility changes from Bill Hoffman

File Contents

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