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