1 /*
2 * pt_popen/pt_pclose functions
3 * Written somewhere in the 90s by Kurt Keller
4 * Comments translated by Steve Donovan
5 * Modified for use in xmp by Mirko Buffoni and Claudio Matsuoka
6 * Reentrancy patch added for xmp by Alice Rowan
7 */
8
9 /*
10 * This piece of code is in the public domain. I do not claim any rights
11 * on it. Do whatever you want to do with it and I hope it will be still
12 * useful. -- Kurt Keller, Aug 2013
13 */
14
15 #ifdef _WIN32
16
17 #include "ptpopen.h"
18
19 /*
20 > Hello,
21 > I am currently porting a UNIX program to WINDOWS.
22 > Most difficulty time I have is to find the popen()-like function under
23 > WINDOWS. Any help and hints would be greatly appreciated.
24 >
25 > Thanks in advance
26 > Tianlin Wang
27
28 This is what I use instead of popen(): (Sorry for the comments in german ;-))
29 It is not an **EXACT** replacement for popen() but it is OK for me.
30
31 Kurt.
32
33 ---------------------------------------------------
34 Tel.: (49)7150/393394 Parity Software GmbH
35 Fax.: (49)7150/393351 Stuttgarter Strasse 42/3
36 E-Mail: kk@parity-soft.de D-71701 Schwieberdingen
37 Web: www.parity-soft.de
38 ---------------------------------------------------
39 */
40
41 /*----------------------------------------------------------------------------
42 Globals for the Routines pt_popen() / pt_pclose()
43 ----------------------------------------------------------------------------*/
44
45 #include <windows.h>
46 #include <io.h>
47 #include <fcntl.h>
48 #include <errno.h>
49
50 #if defined(_MSC_VER) && (_MSC_VER < 1300)
51 typedef LONG LONG_PTR;
52 #endif
53
54 struct pt_popen_data
55 {
56 HANDLE pipein[2];
57 HANDLE pipeout[2];
58 HANDLE pipeerr[2];
59 char popenmode;
60 BOOL is_open;
61 };
62
63 static struct pt_popen_data static_data;
64
my_pipe(HANDLE * readwrite)65 static int my_pipe(HANDLE *readwrite)
66 {
67 SECURITY_ATTRIBUTES sa;
68
69 sa.nLength = sizeof(sa); /* Length in bytes */
70 sa.bInheritHandle = 1; /* the child must inherit these handles */
71 sa.lpSecurityDescriptor = NULL;
72
73 if (! CreatePipe (&readwrite[0],&readwrite[1],&sa,1 << 13))
74 {
75 errno = EMFILE;
76 return -1;
77 }
78
79 return 0;
80 }
81
82
83 /*----------------------------------------------------------------------------
84 Replacement for 'popen()' under WIN32.
85 NOTE: if cmd contains '2>&1', we connect the standard error file handle
86 to the standard output file handle.
87 NOTE: a pointer to allocate a pt_popen_data struct to may be provided. If
88 this pointer is NULL, a static (non-reentrant) struct will be used instead.
89 ----------------------------------------------------------------------------*/
pt_popen(const char * cmd,const char * mode,struct pt_popen_data ** data)90 FILE * pt_popen(const char *cmd, const char *mode, struct pt_popen_data **data)
91 {
92 FILE *fptr = (FILE *)0;
93 PROCESS_INFORMATION piProcInfo;
94 STARTUPINFO siStartInfo;
95 int success, umlenkung;
96 struct pt_popen_data *my_data = &static_data;
97 BOOL user_data = FALSE;
98
99 if (data) {
100 my_data = malloc(sizeof(struct pt_popen_data));
101 if (!my_data)
102 return NULL;
103
104 user_data = TRUE;
105 } else if (static_data.is_open) {
106 return NULL;
107 }
108
109 my_data->pipein[0] = INVALID_HANDLE_VALUE;
110 my_data->pipein[1] = INVALID_HANDLE_VALUE;
111 my_data->pipeout[0] = INVALID_HANDLE_VALUE;
112 my_data->pipeout[1] = INVALID_HANDLE_VALUE;
113 my_data->pipeerr[0] = INVALID_HANDLE_VALUE;
114 my_data->pipeerr[1] = INVALID_HANDLE_VALUE;
115 my_data->is_open = TRUE;
116
117 if (!mode || !*mode)
118 goto finito;
119
120 my_data->popenmode = *mode;
121 if (my_data->popenmode != 'r' && my_data->popenmode != 'w')
122 goto finito;
123
124 /*
125 * Shall we redirect stderr to stdout ? */
126 umlenkung = strstr("2>&1",(char *)cmd) != 0;
127
128 /*
129 * Create the Pipes... */
130 if (my_pipe(my_data->pipein) == -1 ||
131 my_pipe(my_data->pipeout) == -1)
132 goto finito;
133 if (!umlenkung && my_pipe(my_data->pipeerr) == -1)
134 goto finito;
135
136 /*
137 * Now create the child process */
138 ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
139 siStartInfo.cb = sizeof(STARTUPINFO);
140 siStartInfo.hStdInput = my_data->pipein[0];
141 siStartInfo.hStdOutput = my_data->pipeout[1];
142 if (umlenkung)
143 siStartInfo.hStdError = my_data->pipeout[1];
144 else
145 siStartInfo.hStdError = my_data->pipeerr[1];
146 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
147
148 success = CreateProcess(NULL,
149 (LPTSTR)cmd, // command line
150 NULL, // process security attributes
151 NULL, // primary thread security attributes
152 TRUE, // handles are inherited
153 DETACHED_PROCESS, // creation flags: without window (?)
154 NULL, // use parent's environment
155 NULL, // use parent's current directory
156 &siStartInfo, // STARTUPINFO pointer
157 &piProcInfo); // receives PROCESS_INFORMATION
158
159 if (!success)
160 goto finito;
161
162 /*
163 * These handles listen to the child process */
164 CloseHandle(my_data->pipein[0]); my_data->pipein[0] = INVALID_HANDLE_VALUE;
165 CloseHandle(my_data->pipeout[1]); my_data->pipeout[1] = INVALID_HANDLE_VALUE;
166 CloseHandle(my_data->pipeerr[1]); my_data->pipeerr[1] = INVALID_HANDLE_VALUE;
167
168 if (my_data->popenmode == 'r')
169 fptr = _fdopen(_open_osfhandle((LONG_PTR)my_data->pipeout[0],_O_BINARY),"r");
170 else
171 fptr = _fdopen(_open_osfhandle((LONG_PTR)my_data->pipein[1],_O_BINARY),"w");
172
173 finito:
174 if (!fptr)
175 {
176 if (my_data->pipein[0] != INVALID_HANDLE_VALUE)
177 CloseHandle(my_data->pipein[0]);
178 if (my_data->pipein[1] != INVALID_HANDLE_VALUE)
179 CloseHandle(my_data->pipein[1]);
180 if (my_data->pipeout[0] != INVALID_HANDLE_VALUE)
181 CloseHandle(my_data->pipeout[0]);
182 if (my_data->pipeout[1] != INVALID_HANDLE_VALUE)
183 CloseHandle(my_data->pipeout[1]);
184 if (my_data->pipeerr[0] != INVALID_HANDLE_VALUE)
185 CloseHandle(my_data->pipeerr[0]);
186 if (my_data->pipeerr[1] != INVALID_HANDLE_VALUE)
187 CloseHandle(my_data->pipeerr[1]);
188 my_data->is_open = FALSE;
189
190 if (user_data)
191 {
192 free(my_data);
193 my_data = NULL;
194 }
195 }
196 if (user_data)
197 *data = my_data;
198 return fptr;
199 }
200
201 /*----------------------------------------------------------------------------
202 Replacement for 'pclose()' under WIN32
203 ----------------------------------------------------------------------------*/
pt_pclose(FILE * fle,struct pt_popen_data ** data)204 int pt_pclose(FILE *fle, struct pt_popen_data **data)
205 {
206 struct pt_popen_data *my_data = &static_data;
207 BOOL free_data = FALSE;
208 if (data)
209 {
210 if (!*data)
211 return -1;
212
213 my_data = *data;
214 free_data = TRUE;
215 }
216
217 if (fle && my_data->is_open)
218 {
219 (void)fclose(fle);
220
221 CloseHandle(my_data->pipeerr[0]);
222 if (my_data->popenmode == 'r')
223 CloseHandle(my_data->pipein[1]);
224 else
225 CloseHandle(my_data->pipeout[0]);
226
227 if (free_data)
228 {
229 free(my_data);
230 *data = NULL;
231 }
232 return 0;
233 }
234 return -1;
235 }
236
237 #endif /* !HAVE_POPEN && WIN32 */
238