ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/win_popen.c
Revision: 1.4
Committed: Mon Oct 4 10:14:22 2004 UTC (19 years, 5 months ago) by schorsch
Content type: text/plain
Branch: MAIN
CVS Tags: rad3R7P2, rad3R7P1, rad4R0, rad3R6, rad3R6P1, rad3R8, rad3R9
Changes since 1.3: +3 -2 lines
Log Message:
Included paths.h for X_OK.

File Contents

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