version 1.4.30b
[polyglot.git] / pipex_win32.c
diff --git a/pipex_win32.c b/pipex_win32.c
new file mode 100644 (file)
index 0000000..8d9e893
--- /dev/null
@@ -0,0 +1,446 @@
+// pipex_win32.c
+
+#ifdef _WIN32
+
+// includes
+
+#include <io.h>
+#include <fcntl.h>
+#include "pipex.h"
+#include "util.h"
+
+// defines
+
+#define ErrorBufferSize 4096
+#define dwMaxHandles      32
+
+// variables
+
+static char ErrorBuffer[ErrorBufferSize];
+
+// prototypes
+
+static bool pipex_eof_input(pipex_t *pipex);
+static void pipex_set_eof_input(pipex_t *pipex);
+static void pipex_set_active(pipex_t *pipex);
+static int  pipex_read_data(pipex_t *pipex);
+static void pipex_read_input(pipex_t *pipex);
+
+// functions
+
+// win32_error()
+
+static char * win32_error(){
+    FormatMessage(
+        FORMAT_MESSAGE_FROM_SYSTEM,
+        NULL,
+        GetLastError(),
+        LANG_USER_DEFAULT,
+        ErrorBuffer,
+        ErrorBufferSize,
+        NULL);
+    return ErrorBuffer;
+}
+
+// TreadProc()
+
+static DWORD WINAPI ThreadProc(LPVOID lpParam){ 
+    pipex_t *p=(pipex_t *) lpParam;
+    while(!pipex_eof_input(p)){
+        if(p->nReadEnd<LINE_INPUT_MAX_CHAR-1){
+            pipex_read_input(p);
+        }else{
+                // wait until there is room in buffer
+            Sleep(10);
+        }
+    }
+    return 0;
+}
+
+// pipex_open()
+
+void pipex_open(pipex_t *pipex, const char *szName, const char *szProcFile) {
+    DWORD dwMode, dwThreadId;
+    HANDLE hStdinRead, hStdinWrite, hStdoutRead, hStdoutWrite, hThread;
+    SECURITY_ATTRIBUTES sa;
+    STARTUPINFO si;
+    PROCESS_INFORMATION pi;
+    int fdInput;
+    pipex->state=0;
+    pipex->name=szName;
+    pipex->hProcess=NULL;
+    if (szProcFile == NULL) {
+        pipex->hInput = GetStdHandle(STD_INPUT_HANDLE);
+        pipex->hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+        pipex->bConsole = GetConsoleMode(pipex->hInput, &dwMode);
+        pipex->bPipe=FALSE;
+    } else {
+        sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+        sa.bInheritHandle = TRUE;
+        sa.lpSecurityDescriptor = NULL;
+        CreatePipe(&hStdinRead, &hStdinWrite, &sa, 0);
+        CreatePipe(&hStdoutRead, &hStdoutWrite, &sa, 0);
+        si.cb = sizeof(STARTUPINFO);
+        si.lpReserved = si.lpDesktop = si.lpTitle = NULL;
+        si.dwFlags = STARTF_USESTDHANDLES;
+        si.cbReserved2 = 0;
+        si.lpReserved2 = NULL;
+        si.hStdInput = hStdinRead;
+        si.hStdOutput = hStdoutWrite;
+        si.hStdError = hStdoutWrite;
+        if(CreateProcess(NULL,
+                         (LPSTR) szProcFile,
+                         NULL,
+                         NULL,
+                         TRUE,
+                         DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP,
+                         NULL,
+                         NULL,
+                         &si,
+                         &pi)){
+            pipex->hProcess=pi.hProcess;
+            CloseHandle(pi.hThread);
+            CloseHandle(hStdinRead);
+            CloseHandle(hStdoutWrite);
+            pipex->hInput = hStdoutRead;
+            pipex->hOutput = hStdinWrite;
+            pipex->bConsole = FALSE;
+            pipex->bPipe=TRUE;
+        }else{
+            my_fatal("pipex_open(): %s",win32_error());
+        }
+    }
+    if (pipex->bConsole) {
+        SetConsoleMode(pipex->hInput,
+                       dwMode & ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT));
+        FlushConsoleInputBuffer(pipex->hInput);
+    } 
+    fdInput=_open_osfhandle((long) pipex->hInput,_O_RDONLY);
+    if(fdInput==-1){
+        my_fatal("pipex_open(): %s",strerror(errno));
+    }
+    pipex->fpInput=fdopen(fdInput,"r");
+    if(pipex->fpInput==NULL){
+        my_fatal("pipex_open(): %s",strerror(errno));
+    }
+    pipex->nReadEnd = 0;
+    pipex->lpFeedEnd = NULL;
+    InitializeCriticalSection(&(pipex->CriticalSection));
+    pipex->hEvent=CreateEvent(NULL,           // default security
+                       FALSE,          // auto reset
+                       FALSE,          // not signaled
+                       NULL            // nameless
+                       );
+    if(!(pipex->hEvent)){
+        my_fatal("pipex_open(): %s",win32_error());
+    }
+    hThread=CreateThread(NULL,         // default security
+                         0,            // default stacksize
+                         ThreadProc,   // worker function
+                         pipex,        // tell worker about ourselves
+                         0,            // run immediately
+                         &dwThreadId   // dropped, but needed for the call to work in Win9x
+                         );          
+    if(!hThread){
+        my_fatal("pipex_open(): %s",win32_error());
+    }
+    pipex_set_active(pipex);
+}
+
+// pipex_wait_event(pipex)
+
+void pipex_wait_event(pipex_t *pipex[]){
+    HANDLE hHandles[dwMaxHandles]; 
+    DWORD dwHandleCount=0;
+    pipex_t *p;
+    while((p=pipex[dwHandleCount])!=NULL){
+        ASSERT((p->hEvent)!=0);
+        if(dwHandleCount>=dwMaxHandles){
+            my_fatal("pipex_wait_event(): Too many objects to wait for");
+        }
+        hHandles[dwHandleCount++]=p->hEvent;
+    }
+    WaitForMultipleObjects(dwHandleCount,   // count
+                           hHandles,        //
+                           FALSE,           // return if one object is signaled
+                           INFINITE         // no timeout
+                           );
+}
+
+
+// pipex_send_eof()
+
+void pipex_send_eof(pipex_t *pipex)  {
+    my_log("Adapter->%s: EOF\n",pipex->name);
+    CloseHandle(pipex->hOutput);
+}
+
+// pipex_exit()
+
+void pipex_exit(pipex_t *pipex) {
+    CloseHandle(pipex->hInput);
+    CloseHandle(pipex->hOutput);
+    DWORD lpexit;
+    
+    if(GetExitCodeProcess(pipex->hProcess,&lpexit)){
+        if(lpexit==STILL_ACTIVE)
+                //must be java,hammer it down!
+            TerminateProcess(pipex->hProcess,lpexit);
+    }
+       CloseHandle(pipex->hProcess);
+}
+
+// pipex_eof_input()
+
+static bool pipex_eof_input(pipex_t *pipex){ 
+    int ret;
+    EnterCriticalSection(&(pipex->CriticalSection));
+    ret=(pipex->state)&PIPEX_EOF;
+    LeaveCriticalSection(&(pipex->CriticalSection));
+    return ret;
+}
+
+// pipex_set_eof_input()
+
+static void pipex_set_eof_input(pipex_t *pipex){
+    EnterCriticalSection(&(pipex->CriticalSection));
+    (pipex->state)|=PIPEX_EOF;
+    LeaveCriticalSection(&(pipex->CriticalSection));
+        // not quit the right place
+    my_log("%s->Adapter: EOF\n",pipex->name);
+
+}
+
+// pipex_active()
+
+/*
+ * This function returns TRUE if and only if the pipes have succesfully
+ * been created and the client has been started.
+ *
+ */
+
+bool pipex_active(pipex_t *pipex){
+    int ret;
+    EnterCriticalSection(&(pipex->CriticalSection));
+    ret=(pipex->state)&PIPEX_ACTIVE;
+    LeaveCriticalSection(&(pipex->CriticalSection));
+    return ret;
+}
+
+// pipex_set_active()
+
+static void pipex_set_active(pipex_t *pipex){
+    EnterCriticalSection(&(pipex->CriticalSection));
+    (pipex->state)|=PIPEX_ACTIVE;
+    LeaveCriticalSection(&(pipex->CriticalSection));
+}
+
+// pipex_read_data()
+
+static int pipex_read_data(pipex_t *pipex){
+    DWORD dwBytes;
+    char * ret;
+        // No protection. Access to nReadEnd is atomic.
+        // It is not a problem that nReadEnd becomes smaller after the call.
+        // This just means we have read less than we could have. 
+    ret=fgets(pipex->lpReadBuffer,
+              LINE_INPUT_MAX_CHAR-(pipex->nReadEnd),
+              pipex->fpInput);
+    if(!ret){
+        pipex_set_eof_input(pipex);
+        (pipex->lpReadBuffer)[0]='\0';
+        return 0;
+    }
+    dwBytes=strlen(pipex->lpReadBuffer);
+    (pipex->lpReadBuffer)[dwBytes]='\0';
+    return dwBytes;
+}
+
+// pipex_read_input()
+
+static void pipex_read_input(pipex_t *pipex) {
+  int ret;
+  BOOL bSetEvent=FALSE;
+      // ReadData is outside the critical section otherwise everything
+      // would block during the blocking read
+  ret=pipex_read_data(pipex);
+  EnterCriticalSection(&(pipex->CriticalSection));
+  if(!pipex_eof_input(pipex)){
+      if(ret+(pipex->nReadEnd)>=LINE_INPUT_MAX_CHAR){
+          my_fatal("pipex_read_input(): Internal error: buffer overflow\n");
+      }
+      memcpy((pipex->lpBuffer)+(pipex->nReadEnd),(pipex->lpReadBuffer),ret+1);
+      (pipex->nReadEnd) += ret;
+      if(!(pipex->lpFeedEnd)){
+          (pipex->lpFeedEnd) =
+              (char *) memchr(pipex->lpBuffer,'\n',pipex->nReadEnd);
+      }
+      if(pipex->lpFeedEnd){
+          bSetEvent=TRUE;
+      }else if((pipex->nReadEnd)>=LINE_INPUT_MAX_CHAR-1){
+          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);
+      }
+  }
+  LeaveCriticalSection(&(pipex->CriticalSection));
+  if(pipex_eof_input(pipex) || bSetEvent){
+      SetEvent(pipex->hEvent);
+  }
+}
+
+// pipex_eof()
+
+/*
+ * This function returns TRUE if and only if the input buffer does not
+ * contain a full line of data and EOF was encountered by
+ * the working thread.
+ *
+ */
+
+bool pipex_eof(pipex_t *pipex){
+  int ret;
+  EnterCriticalSection(&(pipex->CriticalSection));
+  if((pipex->lpFeedEnd) != NULL){
+    ret=FALSE;
+  }else if(pipex_eof_input(pipex)){
+    ret=TRUE;
+  }else{
+    ret=FALSE;
+  }
+  LeaveCriticalSection(&(pipex->CriticalSection));
+  return ret;
+}
+
+// pipex_readln_nb()
+
+/*
+ * This function returns FALSE if and only if the asynchronously filled
+ * input buffer does not contain a full line of data.
+ * In other words it operates in non-blocking mode.
+ *
+ */
+
+bool pipex_readln_nb(pipex_t *pipex, char *szLineStr) {
+  int nFeedEnd;
+  int ret;
+  EnterCriticalSection(&(pipex->CriticalSection));
+  if ((pipex->lpFeedEnd) == NULL) {
+    ret=FALSE;
+  } else {
+    nFeedEnd = pipex->lpFeedEnd - pipex->lpBuffer;
+    memcpy(szLineStr, pipex->lpBuffer, nFeedEnd+1);
+    szLineStr[nFeedEnd] = '\0';
+        // temp hack: we use the fact that strtok modifies its first argument
+    strtok(szLineStr,"\r\n");
+    ASSERT(strchr(szLineStr,'\n')==NULL)
+    ASSERT(strchr(szLineStr,'\r')==NULL)
+    nFeedEnd ++;
+    pipex->nReadEnd -= nFeedEnd;
+    memcpy(pipex->lpBuffer, pipex->lpBuffer + nFeedEnd, pipex->nReadEnd+1);
+    pipex->lpFeedEnd =
+        (char *) memchr(pipex->lpBuffer, '\n', pipex->nReadEnd);
+    ret=TRUE;
+  }
+  LeaveCriticalSection(&(pipex->CriticalSection));
+  if(ret){
+      my_log("%s->Adapter: %s\n",pipex->name,szLineStr);
+  }
+  return ret;
+}
+
+// pipex_readln()
+
+/*
+ * This function returns FALSE if and only if EOF has been encountered by 
+ * the working thread and no full line of data is present in the input buffer.
+ *
+ * If there is a full line of data present in the input buffer it returns 
+ * TRUE. 
+ *
+ * If none of these conditions is satisfied it blocks.
+ *
+ * As the name say this function is strictly for line input. 
+ * An incomplete line of data (i.e. not ending with \n) is lost when EOF
+ * is encountered.
+ *
+ */
+
+bool pipex_readln(pipex_t *pipex, char *szLineStr) {
+  while(!pipex_eof(pipex)){
+      if (pipex_readln_nb(pipex,szLineStr)) {
+          return TRUE;
+      }else{
+          WaitForSingleObject(pipex->hEvent,INFINITE);
+      }
+  }
+  szLineStr[0]='\0';
+  return FALSE;
+}
+
+//  GetWin32Priority()
+
+static DWORD GetWin32Priority(int nice)
+{
+/*
+REALTIME_PRIORITY_CLASS     0x00000100
+HIGH_PRIORITY_CLASS         0x00000080
+ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
+NORMAL_PRIORITY_CLASS       0x00000020
+BELOW_NORMAL_PRIORITY_CLASS 0x00004000
+IDLE_PRIORITY_CLASS         0x00000040
+*/
+       if (nice < -15) return 0x00000080;
+       if (nice < 0)   return 0x00008000;
+       if (nice == 0)  return 0x00000020;
+       if (nice < 15)  return 0x00004000;
+       return 0x00000040;
+}
+
+// pipex_set_priority()
+
+void pipex_set_priority(pipex_t *pipex, int value){
+    if(pipex->hProcess){
+        if(!SetPriorityClass(pipex->hProcess,
+                             GetWin32Priority(value))){
+            my_log("POLYGLOT Unable to change priority\n");
+        }
+    }
+}
+
+// pipex_set_affinit()
+
+void pipex_set_affinity(pipex_t *pipex, int value){
+    if(pipex->hProcess) return;
+    if(value==-1) return;
+
+    typedef void (WINAPI *SPAM)(HANDLE, int);
+    SPAM pSPAM;
+    pSPAM = (SPAM) GetProcAddress(
+        GetModuleHandle(TEXT("kernel32.dll")), 
+        "SetProcessAffinityMask");
+    if(NULL != pSPAM){
+            // [HGM] avoid crash on Win95 by first checking if API call exists
+        pSPAM(pipex->hProcess,value);
+    }else{
+        my_log("POLYGLOT API call \"SetProcessAffinityMask\" not available\n");
+    }
+}
+
+// pipex_writeln()
+
+void pipex_writeln(pipex_t *pipex, const char *szLineStr) {
+  DWORD dwBytes;
+  int nStrLen;
+  char szWriteBuffer[LINE_INPUT_MAX_CHAR];
+  my_log("Adapter->%s: %s\n",pipex->name,szLineStr);
+  if(pipex->bPipe){
+      nStrLen = strlen(szLineStr);
+      memcpy(szWriteBuffer, szLineStr, nStrLen);
+      szWriteBuffer[nStrLen] = '\r';
+      szWriteBuffer[nStrLen + 1] = '\n';
+      WriteFile(pipex->hOutput, szWriteBuffer, nStrLen + 2, &dwBytes, NULL);
+  }else{
+      printf("%s\n",szLineStr);
+      fflush(stdout);
+  }
+}
+#endif