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