version 1.4.33b
[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   int src, dst;
326   char c;
327   EnterCriticalSection(&(pipex->CriticalSection));
328   if ((pipex->lpFeedEnd) == NULL) {
329     ret=FALSE;
330   } else {
331     nFeedEnd = pipex->lpFeedEnd - pipex->lpBuffer;
332     memcpy(szLineStr, pipex->lpBuffer, nFeedEnd+1);
333     szLineStr[nFeedEnd] = '\0';
334     
335         // temp hack: stolen from util.c
336         // remove CRs and LFs
337     src = 0;
338     dst = 0;
339     while ((c=szLineStr[src++]) != '\0') {
340         if (c != '\r' && c != '\n') szLineStr[dst++] = c;
341     }
342     szLineStr[dst] = '\0';    
343     ASSERT(strchr(szLineStr,'\n')==NULL)
344     ASSERT(strchr(szLineStr,'\r')==NULL)
345         
346     nFeedEnd ++;
347     pipex->nReadEnd -= nFeedEnd;
348     memcpy(pipex->lpBuffer, pipex->lpBuffer + nFeedEnd, pipex->nReadEnd+1);
349     pipex->lpFeedEnd =
350         (char *) memchr(pipex->lpBuffer, '\n', pipex->nReadEnd);
351     ret=TRUE;
352   }
353   LeaveCriticalSection(&(pipex->CriticalSection));
354   if(ret){
355       my_log("%s->Adapter: %s\n",pipex->name,szLineStr);
356   }
357   return ret;
358 }
359
360 // pipex_readln()
361
362 /*
363  * This function returns FALSE if and only if EOF has been encountered by 
364  * the working thread and no full line of data is present in the input buffer.
365  *
366  * If there is a full line of data present in the input buffer it returns 
367  * TRUE. 
368  *
369  * If none of these conditions is satisfied it blocks.
370  *
371  * As the name say this function is strictly for line input. 
372  * An incomplete line of data (i.e. not ending with \n) is lost when EOF
373  * is encountered.
374  *
375  */
376
377 bool pipex_readln(pipex_t *pipex, char *szLineStr) {
378   while(!pipex_eof(pipex)){
379       if (pipex_readln_nb(pipex,szLineStr)) {
380           return TRUE;
381       }else{
382           WaitForSingleObject(pipex->hEvent,INFINITE);
383       }
384   }
385   szLineStr[0]='\0';
386   return FALSE;
387 }
388
389 //  GetWin32Priority()
390
391 static DWORD GetWin32Priority(int nice)
392 {
393 /*
394 REALTIME_PRIORITY_CLASS     0x00000100
395 HIGH_PRIORITY_CLASS         0x00000080
396 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
397 NORMAL_PRIORITY_CLASS       0x00000020
398 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
399 IDLE_PRIORITY_CLASS         0x00000040
400 */
401         if (nice < -15) return 0x00000080;
402         if (nice < 0)   return 0x00008000;
403         if (nice == 0)  return 0x00000020;
404         if (nice < 15)  return 0x00004000;
405         return 0x00000040;
406 }
407
408 // pipex_set_priority()
409
410 void pipex_set_priority(pipex_t *pipex, int value){
411     if(pipex->hProcess){
412         if(!SetPriorityClass(pipex->hProcess,
413                              GetWin32Priority(value))){
414             my_log("POLYGLOT Unable to change priority\n");
415         }
416     }
417 }
418
419 // pipex_set_affinit()
420
421 void pipex_set_affinity(pipex_t *pipex, int value){
422     if(pipex->hProcess) return;
423     if(value==-1) return;
424
425     typedef void (WINAPI *SPAM)(HANDLE, int);
426     SPAM pSPAM;
427     pSPAM = (SPAM) GetProcAddress(
428         GetModuleHandle(TEXT("kernel32.dll")), 
429         "SetProcessAffinityMask");
430     if(NULL != pSPAM){
431             // [HGM] avoid crash on Win95 by first checking if API call exists
432         pSPAM(pipex->hProcess,value);
433     }else{
434         my_log("POLYGLOT API call \"SetProcessAffinityMask\" not available\n");
435     }
436 }
437
438 // pipex_writeln()
439
440 void pipex_writeln(pipex_t *pipex, const char *szLineStr) {
441   DWORD dwBytes;
442   int nStrLen;
443   char szWriteBuffer[LINE_INPUT_MAX_CHAR];
444   my_log("Adapter->%s: %s\n",pipex->name,szLineStr);
445   if(pipex->bPipe){
446       nStrLen = strlen(szLineStr);
447       memcpy(szWriteBuffer, szLineStr, nStrLen);
448       szWriteBuffer[nStrLen] = '\r';
449       szWriteBuffer[nStrLen + 1] = '\n';
450       WriteFile(pipex->hOutput, szWriteBuffer, nStrLen + 2, &dwBytes, NULL);
451   }else{
452       printf("%s\n",szLineStr);
453       fflush(stdout);
454   }
455 }
456 #endif