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

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