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, 6 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

# User Rev Content
1 schorsch 1.1 #ifndef lint
2 schorsch 1.4 static const char RCSid[] = "$Id: win_popen.c,v 1.3 2004/03/28 20:33:12 schorsch Exp $";
3 schorsch 1.1 #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 schorsch 1.4 #include "paths.h"
26 schorsch 1.1 #include "rtio.h"
27     #include "rterror.h"
28    
29    
30     #define RAD_MAX_PIPES 32 /* maximum number of pipes */
31    
32 schorsch 1.3 #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 schorsch 1.1 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 schorsch 1.3 HANDLE newFile(char *fn, int mode);
42 schorsch 1.1
43    
44     int
45 schorsch 1.2 win_pclose( /* posix pclose replacement */
46 schorsch 1.1 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 schorsch 1.2 win_popen( /* posix popen replacement */
57 schorsch 1.1 char* command,
58     char* type
59     )
60     {
61     char *execfile, *args;
62     char *cmdlines[RAD_MAX_PIPES];
63 schorsch 1.3 char *infn = NULL, *outfn = NULL;
64     char executable[512], estr[512];
65     int n, i, mode = 0, append = 0;
66 schorsch 1.1 int ncmds = 0;
67 schorsch 1.3 FILE *inf = NULL;
68 schorsch 1.1 HANDLE stdoutRd = NULL, stdoutWr = NULL;
69     HANDLE stdinRd = NULL, stdinWr = NULL;
70     HANDLE stderrWr = NULL;
71     HANDLE stdoutOrig, stdinOrig;
72    
73 schorsch 1.3 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 schorsch 1.1 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 schorsch 1.3 ncmds = parse_pipes(command,cmdlines,&infn,&outfn,&append,RAD_MAX_PIPES);
90     if(ncmds <= 0) {
91 schorsch 1.1 eputs("Too many pipes or malformed command.");
92     goto error;
93     }
94    
95 schorsch 1.3 if (infn != NULL) {
96     stdoutRd = newFile(infn, mode);
97     }
98    
99 schorsch 1.1 for(n = 0; n < ncmds; ++n) {
100     if(!createPipes(&stdoutRd, &stdoutWr,
101     &stdinRd, &stdinWr)) {
102     eputs("Error creating pipe");
103     goto error;
104     }
105 schorsch 1.3 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 schorsch 1.1 /* 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 schorsch 1.3
138 schorsch 1.1 /* clean up */
139     resetStdHandles(stdinOrig, stdoutOrig);
140     for (i = 0; i < ncmds; i++) free(cmdlines[i]);
141 schorsch 1.3 if (infn != NULL) free(infn);
142     if (outfn != NULL) free(outfn);
143 schorsch 1.1
144 schorsch 1.3 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 schorsch 1.1
152     error:
153     resetStdHandles(stdinOrig, stdoutOrig);
154     for (i = 0; i < ncmds; i++) free(cmdlines[i]);
155 schorsch 1.3 if (infn != NULL) free(infn);
156     if (outfn != NULL) free(outfn);
157 schorsch 1.1 return NULL;
158     }
159    
160    
161 schorsch 1.3 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 schorsch 1.1 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 schorsch 1.3 char *s,
266     char *lines[],
267     char **infn,
268     char **outfn,
269     int *append,
270 schorsch 1.1 int maxl
271     )
272     {
273     int n = 0, i;
274     char *se, *ws;
275 schorsch 1.3 char *curs;
276 schorsch 1.1 int llen = 0;
277     int quote = 0;
278 schorsch 1.3 int last = 0;
279 schorsch 1.1
280     if (maxl<= 0) return 0;
281     if (s == NULL) {
282     return 0;
283     }
284 schorsch 1.3 *infn = *outfn = NULL;
285 schorsch 1.1 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 schorsch 1.3 case '<':
298     case '>':
299 schorsch 1.1 case '|':
300     case '\0':
301 schorsch 1.3 if (*se != '\0' && quote)
302 schorsch 1.1 break;
303     llen = se - s;
304 schorsch 1.3 curs = malloc(llen+1);
305     strncpy(curs, s, llen);
306 schorsch 1.1 /* remove unix style line-end escapes */
307 schorsch 1.3 while((ws = strstr(curs, "\\\n")) != NULL)
308 schorsch 1.1 *ws = *(ws+1) = ' ';
309     /* remove DOS style line-end escapes */
310 schorsch 1.3 while((ws = strstr(curs, "\\\r\n")) != NULL)
311 schorsch 1.1 *ws = *(ws+1) = *(ws+2) = ' ';
312 schorsch 1.3 while (isspace(*(curs + llen - 1)))
313 schorsch 1.1 llen--; /* trailing whitespace */
314 schorsch 1.3 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 schorsch 1.4 eputs("win_popen(): ambiguous output redirection");
330 schorsch 1.3 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 schorsch 1.1 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 schorsch 1.3 error:
353 schorsch 1.1 for (i = 0; i < n; i++) free(lines[i]);
354 schorsch 1.3 if (*infn != NULL) free(*infn);
355     if (*outfn != NULL) free(*outfn);
356     if (curs != NULL) free(curs);
357 schorsch 1.1 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