1 /* popen.c
2 RunSilent() is by Steven Szelei,
3 and pt_popen()/pt_pclose() is by Kurt Keller
4 Modified and comments translated by Steve Donovan
5
6 Please note an extension; if your commmand contains '2>&1'
7 then any error output will be redirected as well to the pipe.
8
9 Put this file in scite\lua\src\lib and add to your project
10
11 to modify liolib.c in the same dir,
12 replace conditional at line 47 with:
13
14 #ifndef USE_POPEN
15 #ifdef __WINDOWS__
16 #define USE_POPEN 1
17 FILE* pt_popen(const char *cmd, const char*mode);
18 int pt_pclose(FILE *file);
19 uintptr_t RunSilent(const char* strCommand);
20 #define popen pt_popen
21 #define pclose pt_pclose
22 #define system RunSilent
23 #endif
24 #else
25 #define USE_POPEN 0
26 #endif
27
28 */
29
30 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
31 SWI-Prolog note:
32
33 This file is copied verbatim from
34 http://lua-users.org/wiki/PipesOnWindows, where it is contributed for
35 using pipes with the LUA programming language. LUA is distributed under
36 the MIT licence.
37
38 This version is heavily modified:
39
40 * Support Unicode commands (commands are specified in UTF-8)
41 * make popen()/pclose() thread-safe.
42 * Fix leak process-handles
43
44 If you find this file and know better, please contact info@swi-prolog.org.
45 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
46
47 #include <windows.h>
48 #include <stdio.h>
49 #include <fcntl.h>
50 #include <string.h>
51 #include <io.h>
52 #include "../pl-utf8.h"
53
RunSilent(const char * strCommand)54 DWORD RunSilent(const char* strCommand)
55 {
56 STARTUPINFO StartupInfo;
57 PROCESS_INFORMATION ProcessInfo;
58 char Args[4096];
59 char *pEnvCMD = NULL;
60 char *pDefaultCMD = "CMD.EXE";
61 ULONG rc;
62
63 memset(&StartupInfo, 0, sizeof(StartupInfo));
64 StartupInfo.cb = sizeof(STARTUPINFO);
65 StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
66 StartupInfo.wShowWindow = SW_HIDE;
67
68 Args[0] = 0;
69
70 pEnvCMD = getenv("COMSPEC");
71
72 if(pEnvCMD){
73 strcpy(Args, pEnvCMD);
74 } else{
75 strcpy(Args, pDefaultCMD);
76 }
77
78 /* "/c" option - Do the command then terminate the command window */
79 strcat(Args, " /c ");
80 /*the application you would like to run from the command window */
81 strcat(Args, strCommand);
82
83 if (!CreateProcess( NULL, Args, NULL, NULL, FALSE,
84 CREATE_NEW_CONSOLE,
85 NULL,
86 NULL,
87 &StartupInfo,
88 &ProcessInfo))
89 {
90 return GetLastError();
91 }
92
93 WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
94 if(!GetExitCodeProcess(ProcessInfo.hProcess, &rc))
95 rc = 0;
96
97 CloseHandle(ProcessInfo.hThread);
98 CloseHandle(ProcessInfo.hProcess);
99
100 return rc;
101
102 }
103
104 /*------------------------------------------------------------------------------
105 Globals for the Routines pt_popen() / pt_pclose()
106 ------------------------------------------------------------------------------*/
107
108 CRITICAL_SECTION lock;
109 #define LOCK() EnterCriticalSection(&lock);
110 #define UNLOCK() LeaveCriticalSection(&lock);
111
112 static void
pt_init()113 pt_init()
114 { InitializeCriticalSection(&lock);
115 }
116
117
118 typedef struct pipe_context
119 { struct pipe_context *next;
120 FILE *fd;
121 HANDLE in[2];
122 HANDLE out[2];
123 HANDLE err[2];
124 char mode; /* 'r' or 'w' */
125 } pipe_context;
126
127
128 static pipe_context *pipes = NULL;
129
130 static pipe_context *
allocPipeContext()131 allocPipeContext()
132 { pipe_context *pc = malloc(sizeof(*pc));
133
134 if ( !pc )
135 return NULL;
136
137 pc->in[0] = INVALID_HANDLE_VALUE;
138 pc->in[1] = INVALID_HANDLE_VALUE;
139 pc->out[0] = INVALID_HANDLE_VALUE;
140 pc->out[1] = INVALID_HANDLE_VALUE;
141 pc->err[0] = INVALID_HANDLE_VALUE;
142 pc->err[1] = INVALID_HANDLE_VALUE;
143
144 return pc;
145 }
146
147
148 static void
discardPipeContext(pipe_context * pc)149 discardPipeContext(pipe_context *pc)
150 { if (pc->in[0] != INVALID_HANDLE_VALUE)
151 CloseHandle(pc->in[0]);
152 if (pc->in[1] != INVALID_HANDLE_VALUE)
153 CloseHandle(pc->in[1]);
154 if (pc->out[0] != INVALID_HANDLE_VALUE)
155 CloseHandle(pc->out[0]);
156 if (pc->out[1] != INVALID_HANDLE_VALUE)
157 CloseHandle(pc->out[1]);
158 if (pc->err[0] != INVALID_HANDLE_VALUE)
159 CloseHandle(pc->err[0]);
160 if (pc->err[1] != INVALID_HANDLE_VALUE)
161 CloseHandle(pc->err[1]);
162
163 free(pc);
164 }
165
166
167
168 static void
linkPipeContext(pipe_context * pc)169 linkPipeContext(pipe_context *pc)
170 { LOCK();
171 pc->next = pipes;
172 pipes = pc;
173 UNLOCK();
174 }
175
176
177 static int
my_pipe(HANDLE * readwrite)178 my_pipe(HANDLE *readwrite)
179 {
180 SECURITY_ATTRIBUTES sa;
181
182 sa.nLength = sizeof(sa); /* Length in bytes */
183 sa.bInheritHandle = 1; /* the child must inherit these handles */
184 sa.lpSecurityDescriptor = NULL;
185
186 if (! CreatePipe (&readwrite[0],&readwrite[1],&sa,1 << 13))
187 {
188 errno = -1; /* EMFILE; que? */
189 return -1;
190 }
191
192 return 0;
193 }
194
195 /*------------------------------------------------------------------------------
196 Replacement for 'popen()' under Windows.
197
198 cmd is taken to be encoded in UTF-8 for compatibility with the Unix
199 version.
200
201 NOTE: if cmd contains '2>&1', we connect the standard error file handle
202 to the standard output file handle.
203 ------------------------------------------------------------------------------*/
204
205 static void
utf8towcs(wchar_t * o,const char * src)206 utf8towcs(wchar_t *o, const char *src)
207 { for( ; *src; )
208 { int wc;
209
210 src = utf8_get_char(src, &wc);
211 *o++ = wc;
212 }
213 *o = 0;
214 }
215
216
217 FILE *
pt_popen(const char * cmd,const char * mode)218 pt_popen(const char *cmd, const char *mode)
219 { FILE *fptr = NULL;
220 PROCESS_INFORMATION piProcInfo;
221 STARTUPINFOW siStartInfo;
222 int success, redirect_error = 0;
223 wchar_t *wcmd = NULL;
224 wchar_t *err2out;
225 pipe_context *pc;
226
227 size_t utf8len = utf8_strlen(cmd, strlen(cmd));
228 if ( !(wcmd = malloc((utf8len+1)*sizeof(wchar_t))) )
229 { return NULL;
230 }
231 utf8towcs(wcmd, cmd);
232
233 if ( !(pc=allocPipeContext()) )
234 goto finito;
235 if ( !mode || !*mode )
236 goto finito;
237 pc->mode = *mode;
238 if ( pc->mode != 'r' && pc->mode != 'w' )
239 goto finito;
240
241 /*
242 * Shall we redirect stderr to stdout ? */
243 if ( (err2out=wcsstr(wcmd, L"2>&1")) != NULL)
244 { /* this option doesn't apply to win32 shells, so we clear it out! */
245 wcsncpy(err2out, L" ", 4);
246 redirect_error = 1;
247 }
248
249 /*
250 * Create the Pipes... */
251 if (my_pipe(pc->in) == -1 ||
252 my_pipe(pc->out) == -1)
253 goto finito;
254 if ( !redirect_error )
255 { if ( my_pipe(pc->err) == -1)
256 goto finito;
257 }
258
259 /*
260 * Now create the child process */
261 ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
262 siStartInfo.cb = sizeof(STARTUPINFO);
263 siStartInfo.hStdInput = pc->in[0];
264 siStartInfo.hStdOutput = pc->out[1];
265 if ( redirect_error )
266 siStartInfo.hStdError = pc->out[1];
267 else
268 siStartInfo.hStdError = pc->err[1];
269 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
270
271 success = CreateProcessW(NULL,
272 wcmd, // command line
273 NULL, // process security attributes
274 NULL, // primary thread security attributes
275 TRUE, // handles are inherited
276 CREATE_NO_WINDOW, // creation flags: without window (?)
277 NULL, // use parent's environment
278 NULL, // use parent's current directory
279 &siStartInfo, // STARTUPINFO pointer
280 &piProcInfo); // receives PROCESS_INFORMATION
281
282 if ( !success )
283 goto finito;
284
285 CloseHandle(piProcInfo.hThread);
286 CloseHandle(piProcInfo.hProcess);
287
288 /*
289 * These handles listen to the Child process */
290 CloseHandle(pc->in[0]); pc->in[0] = INVALID_HANDLE_VALUE;
291 CloseHandle(pc->out[1]); pc->out[1] = INVALID_HANDLE_VALUE;
292 if ( pc->err[1] != INVALID_HANDLE_VALUE )
293 { CloseHandle(pc->err[1]);
294 pc->err[1] = INVALID_HANDLE_VALUE;
295 }
296
297 if ( pc->mode == 'r' )
298 fptr = _fdopen(_open_osfhandle((intptr_t)pc->out[0],_O_BINARY),"r");
299 else
300 fptr = _fdopen(_open_osfhandle((intptr_t)pc->in[1],_O_BINARY),"w");
301
302 finito:
303 if ( fptr )
304 { pc->fd = fptr;
305 linkPipeContext(pc);
306 } else
307 { if ( pc )
308 discardPipeContext(pc);
309 }
310 if ( wcmd )
311 free(wcmd);
312
313 return fptr;
314 }
315
316 /*------------------------------------------------------------------------------
317 Replacement for 'pclose()' under Win32
318 ------------------------------------------------------------------------------*/
319 int
pt_pclose(FILE * fd)320 pt_pclose(FILE *fd)
321 { pipe_context **ppc;
322 int rc;
323
324 if ( !fd )
325 { errno = EINVAL;
326 return -1;
327 }
328
329 rc = fclose(fd);
330 LOCK();
331 for(ppc = &pipes; *ppc; ppc=&(*ppc)->next)
332 { pipe_context *pc = *ppc;
333
334 if ( pc->fd == fd )
335 { *ppc = pc->next;
336
337 UNLOCK();
338 if ( pc->err[0] != INVALID_HANDLE_VALUE )
339 CloseHandle(pc->err[0]);
340 if ( pc->mode == 'r' )
341 { CloseHandle(pc->in[1]);
342 } else
343 { CloseHandle(pc->out[0]);
344 }
345
346 free(pc);
347
348 return rc;
349 }
350 }
351
352 UNLOCK();
353 errno = EINVAL;
354 return -1;
355 }
356
357
358