466ccb7acfcb184fbb09d37af39c0c8cf6532cd7
[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(const char * string);
44 void __ecereNameSpace__ecere__com__eSystem_Delete(void * memory);
45 unsigned short * __ecereNameSpace__ecere__sys__UTF8toUTF16(const 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, const 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, const 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, const char * commandLine, const 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       bool result = true;
301
302       if((mode & POM_error) || (mode & POM_output))
303          if(pipe(hOutput))
304             result = false;
305
306       if((mode & POM_input))
307          if(pipe(hInput))
308             result = false;
309       if(result)
310       {
311          pid = fork();
312          if(pid > 0)
313          {
314             // This process
315             if(hInput[PIPE_READ])
316             {
317                close(hInput[PIPE_READ]);
318                output = fdopen(hInput[PIPE_WRITE],"w");
319             }
320             if(hOutput[PIPE_WRITE])
321             {
322                close(hOutput[PIPE_WRITE]);
323                input = fdopen(hOutput[PIPE_READ],"r");
324             }
325          }
326          else if(pid == 0)
327          {
328             // Child process
329             char * tokens[129];
330             int numTokens;
331             char * commandLineCopy = __ecereNameSpace__ecere__sys__CopyString(commandLine);
332
333             if(hInput[PIPE_WRITE])
334                close(hInput[PIPE_WRITE]);
335             if(hOutput[PIPE_READ])
336                close(hOutput[PIPE_READ]);
337
338             if((mode & POM_error) && hOutput[PIPE_WRITE] != STDERR_FILENO)
339                dup2(hOutput[PIPE_WRITE], STDERR_FILENO);
340
341             if((mode & POM_output) && hOutput[PIPE_WRITE] != STDOUT_FILENO)
342                dup2(hOutput[PIPE_WRITE], STDOUT_FILENO);
343             if(hOutput[PIPE_WRITE] && hOutput[PIPE_WRITE] != STDOUT_FILENO)
344                close(hOutput[PIPE_WRITE]);
345
346             if((mode & POM_input) && hInput[PIPE_READ] != STDIN_FILENO)
347             {
348                dup2(hInput[PIPE_READ], STDIN_FILENO);
349                close(hInput[PIPE_READ]);
350             }
351
352    #if 0 //#ifdef _DEBUG
353             fprintf(stderr, "\n_DualPipeOpen (in child): %s\n\n", commandLineCopy);
354    #endif
355             numTokens = __ecereNameSpace__ecere__sys__Tokenize(commandLineCopy, sizeof(tokens) / sizeof(tokens[0]) - 1, tokens, forArgsPassing);
356    #if 0 //#ifdef _DEBUG
357             { int c; for(c=0; c<numTokens; c++) fprintf(stderr, "argv[%d]: %s\n", c, tokens[c]); fprintf(stderr, "\n"); }
358    #endif
359             tokens[numTokens] = null;
360             if(env)
361             {
362                char * envTokens[129];
363                char * envCopy = __ecereNameSpace__ecere__sys__CopyString(env);
364                int numEnvTokens = __ecereNameSpace__ecere__sys__Tokenize(envCopy, sizeof(envTokens) / sizeof(envTokens[0]) - 1, envTokens, false);
365                envTokens[numEnvTokens] = null;
366
367                if(execve(tokens[0], tokens, envTokens) < 0)
368                {
369                   __ecereNameSpace__ecere__com__eSystem_Delete(commandLineCopy);
370                   __ecereNameSpace__ecere__com__eSystem_Delete(envCopy);
371                   exit(1);
372                }
373                __ecereNameSpace__ecere__com__eSystem_Delete(commandLineCopy);
374                __ecereNameSpace__ecere__com__eSystem_Delete(envCopy);
375                exit(0);
376             }
377             else
378             {
379                if(execvp(tokens[0], (char **)tokens) < 0)
380                {
381                   __ecereNameSpace__ecere__com__eSystem_Delete(commandLineCopy);
382                   exit(1);
383                }
384                __ecereNameSpace__ecere__com__eSystem_Delete(commandLineCopy);
385                exit(0);
386             }
387          }
388          if(input || output)
389          {
390             f = calloc(1, sizeof(_DualPipe));
391             *inputPtr = f->input = input;
392             *outputPtr = f->output = output;
393             f->pid = pid;
394          }
395       }
396       else
397       {
398          if(hInput[PIPE_READ])
399             close(hInput[PIPE_READ]);
400          if(hInput[PIPE_WRITE])
401             close(hInput[PIPE_WRITE]);
402          if(hOutput[PIPE_WRITE])
403             close(hOutput[PIPE_WRITE]);
404          if(hOutput[PIPE_READ])
405             close(hOutput[PIPE_READ]);
406       }
407    }
408 #else
409    {
410       HANDLE hOutput[2] = { 0 },hOutputRead = 0;
411       HANDLE hInput[2] = { 0 }, hInputWrite = 0;
412       HANDLE hError[2] = { 0 }, hErrorRead = 0;
413       HANDLE hStdErr = 0, hStdIn = 0, hStdOut = 0;
414       SECURITY_ATTRIBUTES sa;
415       PROCESS_INFORMATION pi = { 0 };
416       STARTUPINFO si = { 0 };
417       uint16 * _wcommandLine = __ecereNameSpace__ecere__sys__UTF8toUTF16(commandLine, null);
418
419       sa.nLength = sizeof(SECURITY_ATTRIBUTES);
420       sa.lpSecurityDescriptor = null;
421       sa.bInheritHandle = TRUE;
422
423       // Force redirecting if GUI application
424       if(!(mode & POM_error))
425          hStdErr = GetStdHandle(STD_ERROR_HANDLE);
426       if(!(mode & POM_input))
427          hStdIn  = GetStdHandle(STD_INPUT_HANDLE);
428       if(!(mode & POM_output))
429          hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
430
431       if((mode & POM_output) || !hStdOut)
432          CreatePipe(&hOutput[PIPE_READ],&hOutput[PIPE_WRITE],&sa,0);
433
434       if(( (mode & POM_error) && !(mode & POM_output)) ||
435          (!(mode & POM_error) && !hStdErr))
436          CreatePipe(&hError[PIPE_READ], &hError[PIPE_WRITE],&sa,0);
437
438       if((mode & POM_input) || !hStdIn)
439          CreatePipe(&hInput[PIPE_READ], &hInput[PIPE_WRITE], &sa,0);
440
441       if(hInput[PIPE_READ])
442          DuplicateHandle(GetCurrentProcess(),hInput[PIPE_WRITE],GetCurrentProcess(),&hInputWrite,0,FALSE,DUPLICATE_SAME_ACCESS);
443
444       if((mode & POM_error) && (mode & POM_output))
445       {
446          DuplicateHandle(GetCurrentProcess(),hOutput[PIPE_READ],GetCurrentProcess(),&hOutputRead,0,FALSE,DUPLICATE_SAME_ACCESS);
447          DuplicateHandle(GetCurrentProcess(),hOutput[PIPE_WRITE],GetCurrentProcess(),&hError[PIPE_WRITE],0,TRUE,DUPLICATE_SAME_ACCESS);
448       }
449       else
450       {
451          if(hOutput[PIPE_WRITE])
452             DuplicateHandle(GetCurrentProcess(),hOutput[PIPE_READ],GetCurrentProcess(),&hOutputRead,0,FALSE,DUPLICATE_SAME_ACCESS);
453          if(hError[PIPE_WRITE])
454             DuplicateHandle(GetCurrentProcess(),hError[PIPE_READ],GetCurrentProcess(),&hErrorRead,0,FALSE,DUPLICATE_SAME_ACCESS);
455       }
456
457       if(hOutput[PIPE_READ])
458          CloseHandle(hOutput[PIPE_READ]);
459       if(hError[PIPE_READ])
460          CloseHandle(hError[PIPE_READ]);
461       if(hInput[PIPE_WRITE])
462          CloseHandle(hInput[PIPE_WRITE]);
463
464       // Set up the start up info struct.
465       si.cb = sizeof(STARTUPINFO);
466       si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
467       si.wShowWindow = (mode & POM_showWindow) ? SW_SHOW : SW_HIDE;
468       si.hStdOutput = hOutput[PIPE_WRITE] ? hOutput[PIPE_WRITE] : hStdOut;
469       si.hStdInput  = hInput [PIPE_READ] ? hInput [PIPE_READ]  : hStdIn;
470       if((mode & POM_error) && (mode & POM_output))
471          si.hStdError = hOutput[PIPE_WRITE];
472       else if((mode & POM_error))
473          si.hStdError = hError[PIPE_WRITE];
474       else
475          si.hStdError = hError[PIPE_WRITE] ? hError[PIPE_WRITE] : hStdErr;
476
477       if(CreateProcess(null,_wcommandLine,null,null,TRUE, 0,(void *)env,null ,&si,&pi))
478       {
479          CloseHandle(pi.hThread);
480
481          f = calloc(1, sizeof(_DualPipe));
482          f->inputHandle = hOutputRead;
483          f->outputHandle = hInputWrite;
484          f->hProcess = pi.hProcess;
485          f->pid = pi.dwProcessId;
486          *inputPtr = null;
487          *outputPtr = null;
488       }
489       else
490       {
491          if(hOutputRead)
492             CloseHandle(hOutputRead);
493          if(hInputWrite)
494             CloseHandle(hInputWrite);
495          if(hErrorRead)
496             CloseHandle(hErrorRead);
497       }
498
499       if(hInput [PIPE_READ])  CloseHandle(hInput [PIPE_READ]);
500       if(hOutput[PIPE_WRITE]) CloseHandle(hOutput[PIPE_WRITE]);
501       if(hError [PIPE_WRITE]) CloseHandle(hError [PIPE_WRITE]);
502       __ecereNameSpace__ecere__com__eSystem_Delete(_wcommandLine);
503    }
504 #endif
505    return f;
506 }