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

# User Rev Content
1 schorsch 1.1 #ifndef lint
2 greg 1.5 static const char RCSid[] = "$Id: win_popen.c,v 1.4 2004/10/04 10:14:22 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 greg 1.5 char *s = NULL;
69 schorsch 1.1 HANDLE stdoutRd = NULL, stdoutWr = NULL;
70     HANDLE stdinRd = NULL, stdinWr = NULL;
71     HANDLE stderrWr = NULL;
72     HANDLE stdoutOrig, stdinOrig;
73    
74 schorsch 1.3 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 greg 1.5 /* single quotes aren't removed, so change to double-quotes */
85     for (s=command; *s; s++)
86     if (*s == '\'')
87     *s = '"';
88 schorsch 1.3
89 schorsch 1.1 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 schorsch 1.3 ncmds = parse_pipes(command,cmdlines,&infn,&outfn,&append,RAD_MAX_PIPES);
95     if(ncmds <= 0) {
96 schorsch 1.1 eputs("Too many pipes or malformed command.");
97     goto error;
98     }
99    
100 schorsch 1.3 if (infn != NULL) {
101     stdoutRd = newFile(infn, mode);
102     }
103    
104 schorsch 1.1 for(n = 0; n < ncmds; ++n) {
105     if(!createPipes(&stdoutRd, &stdoutWr,
106     &stdinRd, &stdinWr)) {
107     eputs("Error creating pipe");
108     goto error;
109     }
110 schorsch 1.3 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 schorsch 1.1 /* 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 schorsch 1.3
143 schorsch 1.1 /* clean up */
144     resetStdHandles(stdinOrig, stdoutOrig);
145     for (i = 0; i < ncmds; i++) free(cmdlines[i]);
146 schorsch 1.3 if (infn != NULL) free(infn);
147     if (outfn != NULL) free(outfn);
148 schorsch 1.1
149 schorsch 1.3 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 schorsch 1.1
157     error:
158     resetStdHandles(stdinOrig, stdoutOrig);
159     for (i = 0; i < ncmds; i++) free(cmdlines[i]);
160 schorsch 1.3 if (infn != NULL) free(infn);
161     if (outfn != NULL) free(outfn);
162 schorsch 1.1 return NULL;
163     }
164    
165    
166 schorsch 1.3 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 schorsch 1.1 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 schorsch 1.3 char *s,
271     char *lines[],
272     char **infn,
273     char **outfn,
274     int *append,
275 schorsch 1.1 int maxl
276     )
277     {
278     int n = 0, i;
279     char *se, *ws;
280 schorsch 1.3 char *curs;
281 schorsch 1.1 int llen = 0;
282     int quote = 0;
283 schorsch 1.3 int last = 0;
284 schorsch 1.1
285     if (maxl<= 0) return 0;
286     if (s == NULL) {
287     return 0;
288     }
289 schorsch 1.3 *infn = *outfn = NULL;
290 schorsch 1.1 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 schorsch 1.3 case '<':
303     case '>':
304 schorsch 1.1 case '|':
305     case '\0':
306 schorsch 1.3 if (*se != '\0' && quote)
307 schorsch 1.1 break;
308     llen = se - s;
309 schorsch 1.3 curs = malloc(llen+1);
310     strncpy(curs, s, llen);
311 schorsch 1.1 /* remove unix style line-end escapes */
312 schorsch 1.3 while((ws = strstr(curs, "\\\n")) != NULL)
313 schorsch 1.1 *ws = *(ws+1) = ' ';
314     /* remove DOS style line-end escapes */
315 schorsch 1.3 while((ws = strstr(curs, "\\\r\n")) != NULL)
316 schorsch 1.1 *ws = *(ws+1) = *(ws+2) = ' ';
317 schorsch 1.3 while (isspace(*(curs + llen - 1)))
318 schorsch 1.1 llen--; /* trailing whitespace */
319 schorsch 1.3 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 schorsch 1.4 eputs("win_popen(): ambiguous output redirection");
335 schorsch 1.3 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 schorsch 1.1 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 schorsch 1.3 error:
358 schorsch 1.1 for (i = 0; i < n; i++) free(lines[i]);
359 schorsch 1.3 if (*infn != NULL) free(*infn);
360     if (*outfn != NULL) free(*outfn);
361     if (curs != NULL) free(curs);
362 schorsch 1.1 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