0c7469c11163835346c38478b64d186abb165d50
[sdk] / ecere / src / sys / DualPipe.c
1 #if defined(__WIN32__)
2 #define WIN32_LEAN_AND_MEAN
3 #define UNICODE
4 #include <windows.h>
5 #else
6 #include <signal.h>
7 #include <unistd.h>
8 #include <sys/wait.h>
9 #include <fcntl.h>
10 #include <errno.h>
11 #endif
12
13 #include <stdarg.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17
18 #define POM_output   1
19 #define POM_error    2
20 #define POM_input    4
21
22 #define FSM_start    0
23 #define FSM_current  1
24 #define FSM_end      2
25
26
27 #define false  0
28 #define true   1
29
30 #define null   ((void *)0)
31
32 typedef unsigned int FileSeekMode;
33 typedef unsigned int PipeOpenMode;
34 typedef unsigned char byte;
35 typedef unsigned short uint16;
36 typedef unsigned int uint;
37 typedef int bool;
38
39 int __ecereNameSpace__ecere__sys__Tokenize(char * string, int maxTokens, char * tokens[], unsigned int escapeBackSlashes);
40 char * __ecereNameSpace__ecere__sys__CopyString(char * string);
41 void __ecereNameSpace__ecere__com__eSystem_Delete(void * memory);
42 unsigned short * __ecereNameSpace__ecere__sys__UTF8toUTF16(char * source, int * wordCount);
43
44 typedef struct
45 {
46 #if defined(__WIN32__)
47    HANDLE inputHandle, outputHandle;
48    HANDLE hProcess;
49 #else
50    FILE * input, * output;
51 #endif
52    int pid;
53    bool eof;
54 } _DualPipe;
55
56 void DualPipe_Destructor(_DualPipe * dp)
57 {
58 #if defined(__WIN32__)
59    if(dp->outputHandle)
60    {
61       CloseHandle(dp->outputHandle);
62       dp->outputHandle = null;
63    }
64    if(dp->inputHandle)
65    {
66       CloseHandle(dp->inputHandle);
67       dp->inputHandle = null;
68    }
69    if(dp->hProcess)
70       CloseHandle(dp->hProcess);
71 #endif
72    free(dp);
73 }
74
75 void DualPipe_CloseInput(_DualPipe * dp)
76 {
77 #if defined(__WIN32__)
78    if(dp->inputHandle)
79    {
80       CloseHandle(dp->inputHandle);
81       dp->inputHandle = null;
82    }
83 #endif
84 }
85
86 void DualPipe_CloseOutput(_DualPipe * dp)
87 {
88 #if defined(__WIN32__)
89    if(dp->outputHandle)
90    {
91       CloseHandle(dp->outputHandle);
92       dp->outputHandle = null;
93    }
94 #endif
95 }
96
97 int DualPipe_Read(_DualPipe * dp, byte * buffer, uint size, uint count)
98 {
99 #if defined(__WIN32__)
100    unsigned int read;
101    dp->eof = !ReadFile(dp->inputHandle, buffer, size*count, (DWORD *)&read, null);
102    return read / size;
103 #else
104    int result;
105    result = read(fileno(dp->input), buffer, size * count);
106    if(!result || (result < 0 && errno != EAGAIN))
107       dp->eof = true;
108    return (result > 0) ? (result / size) : 0;
109 #endif
110 }
111
112 int DualPipe_Write(_DualPipe * dp, byte * buffer, uint size, uint count)
113 {
114 #if defined(__WIN32__)
115    unsigned int written;
116    WriteFile(dp->outputHandle, buffer, size * count, (DWORD *)&written, null);
117    return written / size;
118 #endif
119    return 0;
120 }
121
122 bool DualPipe_Getc(_DualPipe * dp, char * ch)
123 {
124 #if defined(__WIN32__)
125    unsigned int read;
126    dp->eof = !ReadFile(dp->inputHandle, ch, 1, (DWORD *)&read, null);
127    return !dp->eof && read != 0;
128 #endif
129    return false;
130 }
131
132 bool DualPipe_Putc(_DualPipe * dp, char ch)
133 {
134 #if defined(__WIN32__)
135    unsigned int written;
136    WriteFile(dp->outputHandle, &ch, 1, (DWORD *)&written, null);
137    return written != 0;
138 #endif
139    return false;
140 }
141
142 bool DualPipe_Puts(_DualPipe * dp, char * string)
143 {
144 #if defined(__WIN32__)
145    unsigned int written;
146    int len = strlen(string);
147    WriteFile(dp->outputHandle, string, len, (DWORD *)&written, null);
148    return written == len;
149 #endif
150    return false;
151 }
152
153 bool DualPipe_Seek(_DualPipe * dp, int pos, FileSeekMode mode)
154 {
155 #if defined(__WIN32__)
156    bool result = false;
157    switch(mode)
158    {
159       case FSM_start:
160          result = SetFilePointer(dp->inputHandle, pos, null, FILE_BEGIN) != -1;
161          break;
162       case FSM_current:
163          result = SetFilePointer(dp->inputHandle, pos, null, FILE_CURRENT) != -1;
164          break;
165       case FSM_end:
166          result = SetFilePointer(dp->inputHandle, pos, null, FILE_END) != -1;
167          break;
168    }
169    return result;   
170 #endif
171    return false;
172 }
173
174 uint DualPipe_Tell(_DualPipe * dp)
175 {
176 #if defined(__WIN32__)
177    return (uint)SetFilePointer(dp->inputHandle, 0, null, FILE_CURRENT);
178 #endif
179    return 0;
180 }
181
182 bool DualPipe_Eof(_DualPipe * dp)
183 {
184 #if defined(__WIN32__)
185    return dp->eof;
186 #else
187    return !dp->input || dp->eof || feof(dp->input);
188 #endif
189 }
190
191 // Not yet supported... Will ever be?
192 bool DualPipe_GetSize(_DualPipe * dp)
193 {
194    return 0; 
195 }
196
197 bool DualPipe_Peek(_DualPipe * dp)
198 {
199 #if defined(__WIN32__)
200    uint avail = 0;
201    int result = PeekNamedPipe(dp->inputHandle, null, 0, null, (DWORD *)&avail, null);
202    if(!result)
203    {
204       unsigned int read;
205       char buffer[1];
206       dp->eof = !ReadFile(dp->inputHandle, buffer, 0, (DWORD *)&read, null);
207    }      
208    return avail ? true : false;
209 #else
210    bool result = false; //true; // false
211    if(!DualPipe_Eof(dp))
212    {
213       fd_set rs;
214       struct timeval tv;
215       int fd = fileno(dp->input);
216       FD_ZERO(&rs);
217       FD_SET(fd, &rs);
218       tv.tv_sec = 0;
219       tv.tv_usec = 0;
220       return select(fd+1, &rs, null, null, &tv) > 0;
221    }
222    return result;
223 #endif
224 }
225
226 void DualPipe_Terminate(_DualPipe * dp)
227 {
228 #if defined(__WIN32__)
229    TerminateProcess(dp->hProcess, 0);
230 #else
231    if(dp->pid)
232       kill(dp->pid, SIGTERM);
233 #endif
234 }
235
236 int DualPipe_GetExitCode(_DualPipe * dp)
237 {
238 #if !defined(__WIN32__)
239    int status = 0;
240    waitpid(dp->pid, &status, 0);
241    /*#if defined(__linux__)
242       // Until we support this _extension_ syntax: (moved this to a C file...)
243       // return (((( _extension_   (((union { __typeof(status) __in; int __i; }) { .__in = (status) }).__i))) & 0xff00) >> 8);
244       // return ((__extension__({ union { __typeof(status) __in;  int __i; } __u; __u.__in = (status); __u.__i; }) ) & 0xFF00) >> 8;
245    #else*/
246       return WEXITSTATUS(status);
247    //#endif
248    //return __WEXITSTATUS(status);
249 #else
250    int exitCode = 0;
251    // NOTE: This was inconsistent with Linux version waiting... Testing Suite would not return proper values
252    WaitForSingleObject(dp->hProcess, INFINITE);
253    GetExitCodeProcess(dp->hProcess, (DWORD *)&exitCode);
254    return exitCode;
255 #endif
256 }
257
258 int DualPipe_GetProcessID(_DualPipe * dp)
259 {
260    return dp->pid;
261    // Requires Windows Vista or Windows XP SP1.
262    // return GetProcessId(hProcess);
263 }
264
265 void DualPipe_Wait(_DualPipe * dp)
266 {
267 #if defined(__WIN32__)   
268    WaitForSingleObject(dp->hProcess, INFINITE);
269 #else
270    if(dp->pid)
271       waitpid(dp->pid, 0, 0);
272 #endif   
273 }
274
275 _DualPipe * _DualPipeOpen(PipeOpenMode mode, char * commandLine, char * env, void ** inputPtr, void ** outputPtr)
276 {
277    _DualPipe * f = null;
278 #define PIPE_READ    0
279 #define PIPE_WRITE   1
280
281 #if !defined(__WIN32__)
282    {
283       FILE * input = null, * output = null;
284       int hInput[2] = { 0 }, hOutput[2] = { 0 };
285       pid_t pid;
286       
287       if((mode & POM_error) || (mode & POM_output))
288          pipe(hOutput);
289
290       if((mode & POM_input))
291          pipe(hInput);
292
293       pid = fork();
294       if(pid > 0)
295       {
296          // This process
297          if(hInput[PIPE_READ])
298          {
299             close(hInput[PIPE_READ]);
300             output = fdopen(hInput[PIPE_WRITE],"w");
301          }
302          if(hOutput[PIPE_WRITE])
303          {
304             close(hOutput[PIPE_WRITE]);
305             input = fdopen(hOutput[PIPE_READ],"r");
306          }
307       }
308       else if(pid == 0)
309       {
310          // Child process
311          char * tokens[129];
312          int numTokens;
313          char * commandLineCopy = __ecereNameSpace__ecere__sys__CopyString(commandLine);
314
315          if(hInput[PIPE_WRITE])
316             close(hInput[PIPE_WRITE]);
317          if(hOutput[PIPE_READ])
318             close(hOutput[PIPE_READ]);
319          
320          if((mode & POM_error) && hOutput[PIPE_WRITE] != STDERR_FILENO)
321             dup2(hOutput[PIPE_WRITE], STDERR_FILENO);
322
323          if((mode & POM_output) && hOutput[PIPE_WRITE] != STDOUT_FILENO)
324             dup2(hOutput[PIPE_WRITE], STDOUT_FILENO);
325          if(hOutput[PIPE_WRITE] && hOutput[PIPE_WRITE] != STDOUT_FILENO)
326             close(hOutput[PIPE_WRITE]);            
327          
328          if((mode & POM_input) && hInput[PIPE_READ] != STDIN_FILENO)
329          {
330             dup2(hInput[PIPE_READ], STDIN_FILENO);
331             close(hInput[PIPE_READ]);
332          }
333          
334          numTokens = __ecereNameSpace__ecere__sys__Tokenize(commandLineCopy, sizeof(tokens) / sizeof(tokens[0]) - 1, tokens, false);
335          tokens[numTokens] = null;
336          if(env)
337          {
338             char * envTokens[129];
339             char * envCopy = __ecereNameSpace__ecere__sys__CopyString(env);
340             int numEnvTokens = __ecereNameSpace__ecere__sys__Tokenize(envCopy, sizeof(envTokens) / sizeof(envTokens[0]) - 1, envTokens, false);
341             envTokens[numEnvTokens] = null;
342
343             if(execve(tokens[0], tokens, envTokens) < 0)
344             {
345                __ecereNameSpace__ecere__com__eSystem_Delete(commandLineCopy);
346                __ecereNameSpace__ecere__com__eSystem_Delete(envCopy);
347                exit(1);
348             }
349             __ecereNameSpace__ecere__com__eSystem_Delete(commandLineCopy);
350             __ecereNameSpace__ecere__com__eSystem_Delete(envCopy);
351             exit(0);
352          }
353          else
354          {
355             if(execvp(tokens[0], (char **)tokens) < 0)
356             {
357                __ecereNameSpace__ecere__com__eSystem_Delete(commandLineCopy);
358                exit(1);
359             }
360             __ecereNameSpace__ecere__com__eSystem_Delete(commandLineCopy);
361             exit(0);
362          }
363       }
364       if(input || output)
365       {
366          f = calloc(1, sizeof(_DualPipe));
367          *inputPtr = f->input = input;
368          *outputPtr = f->output = output;
369          f->pid = pid;
370       }
371    }
372 #else
373    {
374       HANDLE hOutput[2] = { 0 },hOutputRead = 0;
375       HANDLE hInput[2] = { 0 }, hInputWrite = 0;
376       HANDLE hError[2] = { 0 }, hErrorRead = 0;
377       HANDLE hStdErr = 0, hStdIn = 0, hStdOut = 0;
378       SECURITY_ATTRIBUTES sa;
379       PROCESS_INFORMATION pi = { 0 };
380       STARTUPINFO si = { 0 };
381       uint16 * _wcommandLine = __ecereNameSpace__ecere__sys__UTF8toUTF16(commandLine, null);
382
383       sa.nLength = sizeof(SECURITY_ATTRIBUTES);
384       sa.lpSecurityDescriptor = null;
385       sa.bInheritHandle = TRUE;
386
387       // Force redirecting if GUI application
388       if(!(mode & POM_error))
389          hStdErr = GetStdHandle(STD_ERROR_HANDLE);
390       if(!(mode & POM_input))
391          hStdIn  = GetStdHandle(STD_INPUT_HANDLE);
392       if(!(mode & POM_output))
393          hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
394
395       if((mode & POM_output) || !hStdOut)
396          CreatePipe(&hOutput[PIPE_READ],&hOutput[PIPE_WRITE],&sa,0);
397
398       if(( (mode & POM_error) && !(mode & POM_output)) || 
399          (!(mode & POM_error) && !hStdErr))
400          CreatePipe(&hError[PIPE_READ], &hError[PIPE_WRITE],&sa,0);
401          
402       if((mode & POM_input) || !hStdIn)
403          CreatePipe(&hInput[PIPE_READ], &hInput[PIPE_WRITE], &sa,0);
404
405       if(hInput[PIPE_READ])
406          DuplicateHandle(GetCurrentProcess(),hInput[PIPE_WRITE],GetCurrentProcess(),&hInputWrite,0,FALSE,DUPLICATE_SAME_ACCESS);
407
408       if((mode & POM_error) && (mode & POM_output))
409       {
410          DuplicateHandle(GetCurrentProcess(),hOutput[PIPE_READ],GetCurrentProcess(),&hOutputRead,0,FALSE,DUPLICATE_SAME_ACCESS);
411          DuplicateHandle(GetCurrentProcess(),hOutput[PIPE_WRITE],GetCurrentProcess(),&hError[PIPE_WRITE],0,TRUE,DUPLICATE_SAME_ACCESS);
412       }
413       else 
414       {
415          if(hOutput[PIPE_WRITE])
416             DuplicateHandle(GetCurrentProcess(),hOutput[PIPE_READ],GetCurrentProcess(),&hOutputRead,0,FALSE,DUPLICATE_SAME_ACCESS);
417          if(hError[PIPE_WRITE])
418             DuplicateHandle(GetCurrentProcess(),hError[PIPE_READ],GetCurrentProcess(),&hErrorRead,0,FALSE,DUPLICATE_SAME_ACCESS);
419       }
420
421       if(hOutput[PIPE_READ])
422          CloseHandle(hOutput[PIPE_READ]);
423       if(hError[PIPE_READ])
424          CloseHandle(hError[PIPE_READ]);
425       if(hInput[PIPE_WRITE])
426          CloseHandle(hInput[PIPE_WRITE]);
427
428       // Set up the start up info struct.
429       si.cb = sizeof(STARTUPINFO);
430       si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
431       si.hStdOutput = hOutput[PIPE_WRITE] ? hOutput[PIPE_WRITE] : hStdOut;
432       si.hStdInput  = hInput [PIPE_READ] ? hInput [PIPE_READ]  : hStdIn;
433       if((mode & POM_error) && (mode & POM_output))
434          si.hStdError = hOutput[PIPE_WRITE];
435       else if((mode & POM_error))
436          si.hStdError = hError[PIPE_WRITE];
437       else
438          si.hStdError = hError[PIPE_WRITE] ? hError[PIPE_WRITE] : hStdErr;
439
440       if(CreateProcess(null,_wcommandLine,null,null,TRUE, 0,env,null ,&si,&pi))
441       {
442          CloseHandle(pi.hThread);
443
444          f = calloc(1, sizeof(_DualPipe));
445          f->inputHandle = hOutputRead;
446          f->outputHandle = hInputWrite;
447          f->hProcess = pi.hProcess;
448          f->pid = pi.dwProcessId;
449          *inputPtr = null;
450          *outputPtr = null;
451       }
452       else
453       {
454          if(hOutputRead)
455             CloseHandle(hOutputRead);
456          if(hInputWrite)
457             CloseHandle(hInputWrite);
458          if(hErrorRead)
459             CloseHandle(hErrorRead);
460       }
461
462       if(hInput [PIPE_READ])  CloseHandle(hInput [PIPE_READ]);
463       if(hOutput[PIPE_WRITE]) CloseHandle(hOutput[PIPE_WRITE]);
464       if(hError [PIPE_WRITE]) CloseHandle(hError [PIPE_WRITE]);
465       __ecereNameSpace__ecere__com__eSystem_Delete(_wcommandLine);
466    }
467 #endif
468    return f;
469 }