version 1.4.31b
[polyglot.git] / pipex_win32.c
1 // pipex_win32.c
2
3 #ifdef _WIN32
4
5 // includes
6
7 #include <io.h>
8 #include <fcntl.h>
9 #include "pipex.h"
10 #include "util.h"
11
12 // defines
13
14 #define ErrorBufferSize 4096
15 #define dwMaxHandles      32
16
17 // variables
18
19 static char ErrorBuffer[ErrorBufferSize];
20
21 // prototypes
22
23 static bool pipex_eof_input(pipex_t *pipex);
24 static void pipex_set_eof_input(pipex_t *pipex);
25 static void pipex_set_active(pipex_t *pipex);
26 static int  pipex_read_data(pipex_t *pipex);
27 static void pipex_read_input(pipex_t *pipex);
28
29 // functions
30
31 // win32_error()
32
33 static char * win32_error(){
34     FormatMessage(
35         FORMAT_MESSAGE_FROM_SYSTEM,
36         NULL,
37         GetLastError(),
38         LANG_USER_DEFAULT,
39         ErrorBuffer,
40         ErrorBufferSize,
41         NULL);
42     return ErrorBuffer;
43 }
44
45 // TreadProc()
46
47 static DWORD WINAPI ThreadProc(LPVOID lpParam){ 
48     pipex_t *p=(pipex_t *) lpParam;
49     while(!pipex_eof_input(p)){
50         if(p->nReadEnd<LINE_INPUT_MAX_CHAR-1){
51             pipex_read_input(p);
52         }else{
53                 // wait until there is room in buffer
54             Sleep(10);
55         }
56     }
57     return 0;
58 }
59
60 // pipex_open()
61
62 void pipex_open(pipex_t *pipex, const char *szName, const char *szProcFile) {
63     DWORD dwMode, dwThreadId;
64     HANDLE hStdinRead, hStdinWrite, hStdoutRead, hStdoutWrite, hThread;
65     SECURITY_ATTRIBUTES sa;
66     STARTUPINFO si;
67     PROCESS_INFORMATION pi;
68     int fdInput;
69     pipex->state=0;
70     pipex->name=szName;
71     pipex->hProcess=NULL;
72     if (szProcFile == NULL) {
73         pipex->hInput = GetStdHandle(STD_INPUT_HANDLE);
74         pipex->hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
75         pipex->bConsole = GetConsoleMode(pipex->hInput, &dwMode);
76         pipex->bPipe=FALSE;
77     } else {
78         sa.nLength = sizeof(SECURITY_ATTRIBUTES);
79         sa.bInheritHandle = TRUE;
80         sa.lpSecurityDescriptor = NULL;
81         CreatePipe(&hStdinRead, &hStdinWrite, &sa, 0);
82         CreatePipe(&hStdoutRead, &hStdoutWrite, &sa, 0);
83         si.cb = sizeof(STARTUPINFO);
84         si.lpReserved = si.lpDesktop = si.lpTitle = NULL;
85         si.dwFlags = STARTF_USESTDHANDLES;
86         si.cbReserved2 = 0;
87         si.lpReserved2 = NULL;
88         si.hStdInput = hStdinRead;
89         si.hStdOutput = hStdoutWrite;
90         si.hStdError = hStdoutWrite;
91         if(CreateProcess(NULL,
92                          (LPSTR) szProcFile,
93                          NULL,
94                          NULL,
95                          TRUE,
96                          DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP,
97                          NULL,
98                          NULL,
99                          &si,
100                          &pi)){
101             pipex->hProcess=pi.hProcess;
102             CloseHandle(pi.hThread);
103             CloseHandle(hStdinRead);
104             CloseHandle(hStdoutWrite);
105             pipex->hInput = hStdoutRead;
106             pipex->hOutput = hStdinWrite;
107             pipex->bConsole = FALSE;
108             pipex->bPipe=TRUE;
109         }else{
110             my_fatal("pipex_open(): %s",win32_error());
111         }
112     }
113     if (pipex->bConsole) {
114         SetConsoleMode(pipex->hInput,
115                        dwMode & ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT));
116         FlushConsoleInputBuffer(pipex->hInput);
117     } 
118     fdInput=_open_osfhandle((long) pipex->hInput,_O_RDONLY);
119     if(fdInput==-1){
120         my_fatal("pipex_open(): %s",strerror(errno));
121     }
122     pipex->fpInput=fdopen(fdInput,"r");
123     if(pipex->fpInput==NULL){
124         my_fatal("pipex_open(): %s",strerror(errno));
125     }
126     pipex->nReadEnd = 0;
127     pipex->lpFeedEnd = NULL;
128     InitializeCriticalSection(&(pipex->CriticalSection));
129     pipex->hEvent=CreateEvent(NULL,           // default security
130                        FALSE,          // auto reset
131                        FALSE,          // not signaled
132                        NULL            // nameless
133                        );
134     if(!(pipex->hEvent)){
135         my_fatal("pipex_open(): %s",win32_error());
136     }
137     hThread=CreateThread(NULL,         // default security
138                          0,            // default stacksize
139                          ThreadProc,   // worker function
140                          pipex,        // tell worker about ourselves
141                          0,            // run immediately
142                          &dwThreadId   // dropped, but needed for the call to work in Win9x
143                          );          
144     if(!hThread){
145         my_fatal("pipex_open(): %s",win32_error());
146     }
147     pipex_set_active(pipex);
148 }
149
150 // pipex_wait_event(pipex)
151
152 void pipex_wait_event(pipex_t *pipex[]){
153     HANDLE hHandles[dwMaxHandles]; 
154     DWORD dwHandleCount=0;
155     pipex_t *p;
156     while((p=pipex[dwHandleCount])!=NULL){
157         ASSERT((p->hEvent)!=0);
158         if(dwHandleCount>=dwMaxHandles){
159             my_fatal("pipex_wait_event(): Too many objects to wait for");
160         }
161         hHandles[dwHandleCount++]=p->hEvent;
162     }
163     WaitForMultipleObjects(dwHandleCount,   // count
164                            hHandles,        //
165                            FALSE,           // return if one object is signaled
166                            INFINITE         // no timeout
167                            );
168 }
169
170
171 // pipex_send_eof()
172
173 void pipex_send_eof(pipex_t *pipex)  {
174     my_log("Adapter->%s: EOF\n",pipex->name);
175     CloseHandle(pipex->hOutput);
176 }
177
178 // pipex_exit()
179
180 void pipex_exit(pipex_t *pipex) {
181     CloseHandle(pipex->hInput);
182     CloseHandle(pipex->hOutput);
183     DWORD lpexit;
184     
185     if(GetExitCodeProcess(pipex->hProcess,&lpexit)){
186         if(lpexit==STILL_ACTIVE)
187                 //must be java,hammer it down!
188             TerminateProcess(pipex->hProcess,lpexit);
189     }
190         CloseHandle(pipex->hProcess);
191 }
192
193 // pipex_eof_input()
194
195 static bool pipex_eof_input(pipex_t *pipex){ 
196     int ret;
197     EnterCriticalSection(&(pipex->CriticalSection));
198     ret=(pipex->state)&PIPEX_EOF;
199     LeaveCriticalSection(&(pipex->CriticalSection));
200     return ret;
201 }
202
203 // pipex_set_eof_input()
204
205 static void pipex_set_eof_input(pipex_t *pipex){
206     EnterCriticalSection(&(pipex->CriticalSection));
207     (pipex->state)|=PIPEX_EOF;
208     LeaveCriticalSection(&(pipex->CriticalSection));
209         // not quit the right place
210     my_log("%s->Adapter: EOF\n",pipex->name);
211
212 }
213
214 // pipex_active()
215
216 /*
217  * This function returns TRUE if and only if the pipes have succesfully
218  * been created and the client has been started.
219  *
220  */
221
222 bool pipex_active(pipex_t *pipex){
223     int ret;
224     EnterCriticalSection(&(pipex->CriticalSection));
225     ret=(pipex->state)&PIPEX_ACTIVE;
226     LeaveCriticalSection(&(pipex->CriticalSection));
227     return ret;
228 }
229
230 // pipex_set_active()
231
232 static void pipex_set_active(pipex_t *pipex){
233     EnterCriticalSection(&(pipex->CriticalSection));
234     (pipex->state)|=PIPEX_ACTIVE;
235     LeaveCriticalSection(&(pipex->CriticalSection));
236 }
237
238 // pipex_read_data()
239
240 static int pipex_read_data(pipex_t *pipex){
241     DWORD dwBytes;
242     char * ret;
243         // No protection. Access to nReadEnd is atomic.
244         // It is not a problem that nReadEnd becomes smaller after the call.
245         // This just means we have read less than we could have. 
246     ret=fgets(pipex->lpReadBuffer,
247               LINE_INPUT_MAX_CHAR-(pipex->nReadEnd),
248               pipex->fpInput);
249     if(!ret){
250         pipex_set_eof_input(pipex);
251         (pipex->lpReadBuffer)[0]='\0';
252         return 0;
253     }
254     dwBytes=strlen(pipex->lpReadBuffer);
255     (pipex->lpReadBuffer)[dwBytes]='\0';
256     return dwBytes;
257 }
258
259 // pipex_read_input()
260
261 static void pipex_read_input(pipex_t *pipex) {
262   int ret;
263   BOOL bSetEvent=FALSE;
264       // ReadData is outside the critical section otherwise everything
265       // would block during the blocking read
266   ret=pipex_read_data(pipex);
267   EnterCriticalSection(&(pipex->CriticalSection));
268   if(!pipex_eof_input(pipex)){
269       if(ret+(pipex->nReadEnd)>=LINE_INPUT_MAX_CHAR){
270           my_fatal("pipex_read_input(): Internal error: buffer overflow\n");
271       }
272       memcpy((pipex->lpBuffer)+(pipex->nReadEnd),(pipex->lpReadBuffer),ret+1);
273       (pipex->nReadEnd) += ret;
274       if(!(pipex->lpFeedEnd)){
275           (pipex->lpFeedEnd) =
276               (char *) memchr(pipex->lpBuffer,'\n',pipex->nReadEnd);
277       }
278       if(pipex->lpFeedEnd){
279           bSetEvent=TRUE;
280       }else if((pipex->nReadEnd)>=LINE_INPUT_MAX_CHAR-1){
281           my_fatal("pipex_read_input(): LINE_INPUT_MAX_CHAR is equal to %d which is too small to contain a full line of engine output or GUI input.\n",LINE_INPUT_MAX_CHAR);
282       }
283   }
284   LeaveCriticalSection(&(pipex->CriticalSection));
285   if(pipex_eof_input(pipex) || bSetEvent){
286       SetEvent(pipex->hEvent);
287   }
288 }
289
290 // pipex_eof()
291
292 /*
293  * This function returns TRUE if and only if the input buffer does not
294  * contain a full line of data and EOF was encountered by
295  * the working thread.
296  *
297  */
298
299 bool pipex_eof(pipex_t *pipex){
300   int ret;
301   EnterCriticalSection(&(pipex->CriticalSection));
302   if((pipex->lpFeedEnd) != NULL){
303     ret=FALSE;
304   }else if(pipex_eof_input(pipex)){
305     ret=TRUE;
306   }else{
307     ret=FALSE;
308   }
309   LeaveCriticalSection(&(pipex->CriticalSection));
310   return ret;
311 }
312
313 // pipex_readln_nb()
314
315 /*
316  * This function returns FALSE if and only if the asynchronously filled
317  * input buffer does not contain a full line of data.
318  * In other words it operates in non-blocking mode.
319  *
320  */
321
322 bool pipex_readln_nb(pipex_t *pipex, char *szLineStr) {
323   int nFeedEnd;
324   int ret;
325   EnterCriticalSection(&(pipex->CriticalSection));
326   if ((pipex->lpFeedEnd) == NULL) {
327     ret=FALSE;
328   } else {
329     nFeedEnd = pipex->lpFeedEnd - pipex->lpBuffer;
330     memcpy(szLineStr, pipex->lpBuffer, nFeedEnd+1);
331     szLineStr[nFeedEnd] = '\0';
332         // temp hack: we use the fact that strtok modifies its first argument
333     strtok(szLineStr,"\r\n");
334     ASSERT(strchr(szLineStr,'\n')==NULL)
335     ASSERT(strchr(szLineStr,'\r')==NULL)
336     nFeedEnd ++;
337     pipex->nReadEnd -= nFeedEnd;
338     memcpy(pipex->lpBuffer, pipex->lpBuffer + nFeedEnd, pipex->nReadEnd+1);
339     pipex->lpFeedEnd =
340         (char *) memchr(pipex->lpBuffer, '\n', pipex->nReadEnd);
341     ret=TRUE;
342   }
343   LeaveCriticalSection(&(pipex->CriticalSection));
344   if(ret){
345       my_log("%s->Adapter: %s\n",pipex->name,szLineStr);
346   }
347   return ret;
348 }
349
350 // pipex_readln()
351
352 /*
353  * This function returns FALSE if and only if EOF has been encountered by 
354  * the working thread and no full line of data is present in the input buffer.
355  *
356  * If there is a full line of data present in the input buffer it returns 
357  * TRUE. 
358  *
359  * If none of these conditions is satisfied it blocks.
360  *
361  * As the name say this function is strictly for line input. 
362  * An incomplete line of data (i.e. not ending with \n) is lost when EOF
363  * is encountered.
364  *
365  */
366
367 bool pipex_readln(pipex_t *pipex, char *szLineStr) {
368   while(!pipex_eof(pipex)){
369       if (pipex_readln_nb(pipex,szLineStr)) {
370           return TRUE;
371       }else{
372           WaitForSingleObject(pipex->hEvent,INFINITE);
373       }
374   }
375   szLineStr[0]='\0';
376   return FALSE;
377 }
378
379 //  GetWin32Priority()
380
381 static DWORD GetWin32Priority(int nice)
382 {
383 /*
384 REALTIME_PRIORITY_CLASS     0x00000100
385 HIGH_PRIORITY_CLASS         0x00000080
386 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
387 NORMAL_PRIORITY_CLASS       0x00000020
388 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
389 IDLE_PRIORITY_CLASS         0x00000040
390 */
391         if (nice < -15) return 0x00000080;
392         if (nice < 0)   return 0x00008000;
393         if (nice == 0)  return 0x00000020;
394         if (nice < 15)  return 0x00004000;
395         return 0x00000040;
396 }
397
398 // pipex_set_priority()
399
400 void pipex_set_priority(pipex_t *pipex, int value){
401     if(pipex->hProcess){
402         if(!SetPriorityClass(pipex->hProcess,
403                              GetWin32Priority(value))){
404             my_log("POLYGLOT Unable to change priority\n");
405         }
406     }
407 }
408
409 // pipex_set_affinit()
410
411 void pipex_set_affinity(pipex_t *pipex, int value){
412     if(pipex->hProcess) return;
413     if(value==-1) return;
414
415     typedef void (WINAPI *SPAM)(HANDLE, int);
416     SPAM pSPAM;
417     pSPAM = (SPAM) GetProcAddress(
418         GetModuleHandle(TEXT("kernel32.dll")), 
419         "SetProcessAffinityMask");
420     if(NULL != pSPAM){
421             // [HGM] avoid crash on Win95 by first checking if API call exists
422         pSPAM(pipex->hProcess,value);
423     }else{
424         my_log("POLYGLOT API call \"SetProcessAffinityMask\" not available\n");
425     }
426 }
427
428 // pipex_writeln()
429
430 void pipex_writeln(pipex_t *pipex, const char *szLineStr) {
431   DWORD dwBytes;
432   int nStrLen;
433   char szWriteBuffer[LINE_INPUT_MAX_CHAR];
434   my_log("Adapter->%s: %s\n",pipex->name,szLineStr);
435   if(pipex->bPipe){
436       nStrLen = strlen(szLineStr);
437       memcpy(szWriteBuffer, szLineStr, nStrLen);
438       szWriteBuffer[nStrLen] = '\r';
439       szWriteBuffer[nStrLen + 1] = '\n';
440       WriteFile(pipex->hOutput, szWriteBuffer, nStrLen + 2, &dwBytes, NULL);
441   }else{
442       printf("%s\n",szLineStr);
443       fflush(stdout);
444   }
445 }
446 #endif