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