ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/win_popen.c
Revision: 1.1
Committed: Tue Oct 21 19:20:37 2003 UTC (20 years, 6 months ago) by schorsch
Content type: text/plain
Branch: MAIN
Log Message:
Replacement for popen() on Windows, including some shell functionality.

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id$";
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 static int parse_pipes(char*, char**, int);
33 static BOOL createPipes(HANDLE*, HANDLE*, HANDLE*, HANDLE*);
34 static BOOL runChild(char*, char*, HANDLE, HANDLE, HANDLE);
35 static void resetStdHandles(HANDLE stdoutOrig, HANDLE stdinOrig);
36
37
38 int
39 pclose( /* posix pclose replacement */
40 FILE* p
41 )
42 {
43 fclose(p);
44 /* not sure if it's useful to wait for anything on Windows */
45 return 0;
46 }
47
48
49 FILE *
50 popen( /* posix popen replacement */
51 char* command,
52 char* type
53 )
54 {
55 char *execfile, *args;
56 char *cmdlines[RAD_MAX_PIPES];
57 char executable[512];
58 int n, i;
59 int ncmds = 0;
60 HANDLE stdoutRd = NULL, stdoutWr = NULL;
61 HANDLE stdinRd = NULL, stdinWr = NULL;
62 HANDLE stderrWr = NULL;
63 HANDLE stdoutOrig, stdinOrig;
64
65 stdoutOrig = GetStdHandle(STD_OUTPUT_HANDLE);
66 stdinOrig = GetStdHandle(STD_INPUT_HANDLE);
67 /* if we have a console, use it for error output */
68 stderrWr = GetStdHandle(STD_ERROR_HANDLE);
69
70 if((ncmds = parse_pipes(command, cmdlines, RAD_MAX_PIPES)) <= 0) {
71 eputs("Too many pipes or malformed command.");
72 goto error;
73 }
74
75 for(n = 0; n < ncmds; ++n) {
76 if(!createPipes(&stdoutRd, &stdoutWr,
77 &stdinRd, &stdinWr)) {
78 eputs("Error creating pipe");
79 goto error;
80 }
81 /* find the executable on the PATH */
82 args = nextword(executable, sizeof(executable), cmdlines[n]);
83 if (args == NULL) {
84 eputs("Empty command.");
85 goto error;
86 }
87 execfile = getpath(executable, getenv("PATH"), X_OK);
88 if(execfile == NULL) {
89 char estr[512];
90 _snprintf(estr, sizeof(estr),
91 "Can't find executable for \"%s\".", executable);
92 eputs(estr);
93 goto error;
94 }
95 if(!runChild(execfile, cmdlines[n], stdinRd, stdoutWr, stderrWr)) {
96 char estr[512];
97 _snprintf(estr, sizeof(estr),
98 "Unable to execute executable \"%s\".", executable);
99 eputs(estr);
100 goto error;
101 }
102 /* close the stdout end just passed to the last process,
103 or the final read will block */
104 CloseHandle(stdoutWr);
105 }
106 /* clean up */
107 resetStdHandles(stdinOrig, stdoutOrig);
108 for (i = 0; i < ncmds; i++) free(cmdlines[i]);
109
110 /* return a standard C file pointer for reading the output */
111 return _fdopen(_open_osfhandle((long)stdoutRd, _O_RDONLY), "r");
112
113 error:
114 resetStdHandles(stdinOrig, stdoutOrig);
115 for (i = 0; i < ncmds; i++) free(cmdlines[i]);
116 return NULL;
117 }
118
119
120 static BOOL
121 createPipes( /* establish matching pipes for a subprocess */
122 HANDLE* stdoutRd,
123 HANDLE* stdoutWr,
124 HANDLE* stdinRd,
125 HANDLE* stdinWr
126 )
127 {
128 HANDLE stdoutRdInh = NULL;
129 HANDLE stdinWrInh = NULL;
130 HANDLE curproc;
131 SECURITY_ATTRIBUTES sAttr;
132
133 curproc = GetCurrentProcess();
134
135 /* The rules of inheritance for handles are a mess.
136 Just to be safe, make all handles we pass to the
137 child processes inheritable */
138 sAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
139 sAttr.bInheritHandle = TRUE;
140 sAttr.lpSecurityDescriptor = NULL;
141
142 if(*stdoutRd != NULL){
143 /* if we have a previous stdout pipe,
144 assign the other end to stdin for the new process */
145 CloseHandle(*stdinRd);
146 if(!DuplicateHandle(curproc, *stdoutRd,
147 curproc, stdinRd, 0,
148 TRUE, DUPLICATE_SAME_ACCESS))
149 return FALSE;
150 CloseHandle(*stdoutRd);
151 if(!SetStdHandle(STD_INPUT_HANDLE, *stdinRd))
152 return FALSE;
153 } else {
154 /* there's no previous stdout, create a new stdin pipe */
155 if(!CreatePipe(stdinRd, &stdinWrInh, &sAttr, 0))
156 return FALSE;
157 if(!SetStdHandle(STD_INPUT_HANDLE, *stdinRd))
158 return FALSE;
159 CloseHandle(stdinWrInh);
160 }
161
162 /* create the stdout pipe for the new process */
163 if(!CreatePipe(&stdoutRdInh, stdoutWr, &sAttr, 0))
164 return FALSE;
165 if(!SetStdHandle(STD_OUTPUT_HANDLE, *stdoutWr))
166 return FALSE;
167 if(!DuplicateHandle(curproc, stdoutRdInh,
168 curproc, stdoutRd, 0,
169 FALSE, DUPLICATE_SAME_ACCESS))
170 return FALSE;
171 CloseHandle(stdoutRdInh);
172
173 return TRUE;
174 }
175
176
177 static void
178 resetStdHandles( /* clean up our std streams */
179 HANDLE stdoutOrig,
180 HANDLE stdinOrig
181 )
182 {
183 SetStdHandle(STD_OUTPUT_HANDLE, stdoutOrig);
184 SetStdHandle(STD_INPUT_HANDLE, stdinOrig);
185 }
186
187
188 static int
189 parse_pipes( /* split a shell command pipe sequence */
190 char* s,
191 char* lines[],
192 int maxl
193 )
194 {
195 int n = 0, i;
196 char *se, *ws;
197 int llen = 0;
198 int quote = 0;
199
200 if (maxl<= 0) return 0;
201 if (s == NULL) {
202 return 0;
203 }
204 while (isspace(*s)) s++; /* leading whitespace */
205 se = s;
206 while (n < maxl) {
207 switch (*se) {
208 case '"':
209 if (quote == '"') quote = 0;
210 else if (quote == 0) quote = '"';
211 break;
212 case '\'':
213 if (quote == '\'') quote = 0;
214 else if (quote == 0) quote = '\'';
215 break;
216 case '|':
217 case '\0':
218 if (*se == '|' && quote)
219 break;
220 llen = se - s;
221 lines[n] = malloc(llen+1);
222 strncpy(lines[n], s, llen);
223 /* remove unix style line-end escapes */
224 while((ws = strstr(lines[n], "\\\n")) != NULL)
225 *ws = *(ws+1) = ' ';
226 /* remove DOS style line-end escapes */
227 while((ws = strstr(lines[n], "\\\r\n")) != NULL)
228 *ws = *(ws+1) = *(ws+2) = ' ';
229 while (isspace(*(lines[n] + llen - 1)))
230 llen--; /* trailing whitespace */
231 lines[n][llen] = '\0';
232 n++;
233 if (*se == '\0') return n;
234 s = se + 1;
235 while (isspace(*s)) s++; /* leading whitespace */
236 se = s;
237 break;
238 default:
239 break;
240 }
241 se++;
242 }
243 /* more jobs than slots */
244 for (i = 0; i < n; i++) free(lines[i]);
245 return -1;
246 }
247
248
249 static BOOL
250 runChild( /* start a child process with the right std streams */
251 char* executable,
252 char* cmdline,
253 HANDLE stdinRd,
254 HANDLE stdoutWr,
255 HANDLE stderrWr
256 )
257 {
258 PROCESS_INFORMATION procInfo;
259 STARTUPINFO startupInfo;
260
261 /* use the given handles and don't display the console window */
262 ZeroMemory(&startupInfo, sizeof(STARTUPINFO));
263 startupInfo.cb = sizeof(STARTUPINFO);
264 startupInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
265 startupInfo.wShowWindow = SW_HIDE;
266 startupInfo.hStdInput = stdinRd;
267 startupInfo.hStdOutput = stdoutWr;
268 startupInfo.hStdError = stderrWr;
269
270 return CreateProcess(executable, cmdline, NULL, NULL,
271 TRUE, 0,
272 NULL, NULL, &startupInfo, &procInfo);
273 }
274
275
276