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