Goto sanos source index

//
// kernel32.c
//
// Win32 KERNEL32 emulation
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
// Portions Copyright (C) 1999 Turchanov Sergey
// Portions Copyright (C) 1999 Alexandre Julliard
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 
// 1. Redistributions of source code must retain the above copyright 
//    notice, this list of conditions and the following disclaimer.  
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.  
// 3. Neither the name of the project nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission. 
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
// SUCH DAMAGE.
// 

#include <os.h>
#include <sys/types.h>
#include <os/trap.h>
#include <string.h>
#include <time.h>
#include <inifile.h>
#include <win32.h>

int sprintf(char *buf, const char *fmt, ...);

#define MAX_VADS        256

#define EPOC            116444736000000000     // 00:00:00 GMT on January 1, 1970

#define MICROTIMESCALE  10                     // 1 us resolution
#define MILLITIMESCALE  10000                  // 1 ms resolution
#define SECTIMESCALE    10000000               // 1 sec resolution

#define NOFINDHANDLE ((HANDLE) (-2))
#define PROCESSHEAP  ((HANDLE) (-3))

struct winfinddata {
  handle_t fhandle;
  char dir[MAXPATH];
  char *mask;
};

struct vad {
  void *addr;
  unsigned long size;
};

struct vad vads[MAX_VADS];
PTOP_LEVEL_EXCEPTION_FILTER topexcptfilter = NULL;
void (*old_globalhandler)(struct siginfo *info);

int convert_filename_to_unicode(const char *src, wchar_t *dst, int maxlen) {
  wchar_t *end = dst + maxlen;
  while (*src) {
    if (dst == end) {
      errno = ENAMETOOLONG;
      return -1;
    }

    *dst++ = (unsigned char) *src++;
  }
  
  if (dst == end) {
    errno = ENAMETOOLONG;
    return -1;
  }
  *dst = 0;
  return 0;
}

int convert_filename_from_unicode(const wchar_t *src, char *dst, int maxlen) {
  char *end = dst + maxlen;
  while (*src) {
    if (dst == end) {
      errno = ENAMETOOLONG;
      return -1;
    }
    if (*src & 0xFF00) {
      errno = EINVAL;
      return -1;
    }
    *dst++ = (unsigned char) *src++;
  }
  
  if (dst == end) {
    errno = ENAMETOOLONG;
    return -1;
  }
  *dst = 0;
  return 0;
}

void convert_from_win32_context(struct context *ctxt, CONTEXT *ctx) {
  if (ctx->ContextFlags & CONTEXT_CONTROL) {
    ctxt->ess = ctx->SegSs;
    ctxt->esp = ctx->Esp;
    ctxt->ecs = ctx->SegCs;
    ctxt->eip = ctx->Eip;
    ctxt->eflags = ctx->EFlags;
    ctxt->ebp = ctx->Ebp;
  }

  if (ctx->ContextFlags & CONTEXT_INTEGER) {
    ctxt->eax = ctx->Eax;
    ctxt->ebx = ctx->Ebx;
    ctxt->ecx = ctx->Ecx;
    ctxt->edx = ctx->Edx;
    ctxt->esi = ctx->Esi;
    ctxt->edi = ctx->Edi;
  }

  if (ctx->ContextFlags & CONTEXT_SEGMENTS) {
    ctxt->ds = ctx->SegDs;
    ctxt->es = ctx->SegEs;
    // fs missing
    // gs missing
  }

  if (ctx->ContextFlags & CONTEXT_FLOATING_POINT) {
    // fpu state missing
  }

  if (ctx->ContextFlags & CONTEXT_DEBUG_REGISTERS) {
    // debug registers missing
  }
}

void convert_to_win32_context(struct context *ctxt, CONTEXT *ctx) {
  memset(ctx, 0, sizeof(CONTEXT));
  ctx->ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS;

  ctx->SegSs = ctxt->ess;
  ctx->Esp = ctxt->esp;
  ctx->SegCs = ctxt->ecs;
  ctx->Eip = ctxt->eip;
  ctx->EFlags = ctxt->eflags;
  ctx->Ebp = ctxt->ebp;

  ctx->Eax = ctxt->eax;
  ctx->Ebx = ctxt->ebx;
  ctx->Ecx = ctxt->ecx;
  ctx->Edx = ctxt->edx;
  ctx->Esi = ctxt->esi;
  ctx->Edi = ctxt->edi;

  ctx->SegDs = ctxt->ds;
  ctx->SegEs = ctxt->es;
  ctx->SegFs = 0;
  ctx->SegGs = 0;
}

static __inline EXCEPTION_FRAME *push_frame(EXCEPTION_FRAME *frame) {
  struct tib *tib = gettib();
  frame->prev = (EXCEPTION_FRAME *) tib->except;
  tib->except = (struct xcptrec *) frame;
  return frame->prev;
}

static __inline EXCEPTION_FRAME *pop_frame(EXCEPTION_FRAME *frame) {
  struct tib *tib = gettib();
  tib->except = (struct xcptrec *) frame->prev;
  return frame->prev;
}

//
// Handler for exceptions happening inside a handler
//

static EXCEPTION_DISPOSITION raise_handler(EXCEPTION_RECORD *rec, EXCEPTION_FRAME *frame, CONTEXT *ctxt, EXCEPTION_FRAME **dispatcher) {
  if (rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND)) return ExceptionContinueSearch;

  // We shouldn't get here so we store faulty frame in dispatcher
  *dispatcher = ((NESTED_FRAME *) frame)->prev;
  return ExceptionNestedException;
}

//
// Handler for exceptions happening inside an unwind handler.
//

static EXCEPTION_DISPOSITION unwind_handler(EXCEPTION_RECORD *rec, EXCEPTION_FRAME *frame, CONTEXT *ctxt, EXCEPTION_FRAME **dispatcher) {
  if (!(rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))) return ExceptionContinueSearch;

  // We shouldn't get here so we store faulty frame in dispatcher
  *dispatcher = ((NESTED_FRAME *) frame)->prev;
  return ExceptionCollidedUnwind;
}

//
// Call an exception handler, setting up an exception frame to catch exceptions
// happening during the handler execution.
//

static EXCEPTION_DISPOSITION call_handler(
    EXCEPTION_RECORD *record, 
    EXCEPTION_FRAME *frame,
    CONTEXT *ctxt, 
    EXCEPTION_FRAME **dispatcher,
    PEXCEPTION_HANDLER handler, 
    PEXCEPTION_HANDLER nested_handler) {
  NESTED_FRAME newframe;
  EXCEPTION_DISPOSITION rc;

  newframe.frame.handler = nested_handler;
  newframe.prev = frame;
  push_frame(&newframe.frame);
  //syslog(LOG_DEBUG, "calling handler at %p code=%x flags=%x", handler, record->ExceptionCode, record->ExceptionFlags);
  rc = handler(record, frame, ctxt, dispatcher);
  //syslog(LOG_DEBUG, "handler returned %x", rc);
  pop_frame(&newframe.frame);

  return rc;
}

void raise_exception(EXCEPTION_RECORD *rec, CONTEXT *ctxt) {
  PEXCEPTION_FRAME frame, dispatch, nested_frame;
  EXCEPTION_RECORD newrec;
  EXCEPTION_DISPOSITION rc;
  struct tib *tib = gettib();

  //syslog(LOG_DEBUG, "raise_exception: code=%lx flags=%lx addr=%p", rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress);

  frame = (EXCEPTION_FRAME *) tib->except;
  nested_frame = NULL;
  while (frame != (PEXCEPTION_FRAME) 0xFFFFFFFF) {
    //syslog(LOG_DEBUG, "frame: %p handler: %p", frame, frame->handler);
    
    // Check frame address
    if ((void *) frame < tib->stacklimit || (void *)(frame + 1) > tib->stacktop || (int) frame & 3) {
      rec->ExceptionFlags |= EH_STACK_INVALID;
      break;
    }

    // Call handler
    rc = call_handler(rec, frame, ctxt, &dispatch, frame->handler, raise_handler);
    if (frame == nested_frame) {
      // No longer nested
      nested_frame = NULL;
      rec->ExceptionFlags &= ~EH_NESTED_CALL;
    }

    switch (rc) {
      case ExceptionContinueExecution:
        if (!(rec->ExceptionFlags & EH_NONCONTINUABLE)) return;
        newrec.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION;
        newrec.ExceptionFlags = EH_NONCONTINUABLE;
        newrec.ExceptionRecord  = rec;
        newrec.NumberParameters = 0;
        panic("kernel32: exception not continuable");
        //TODO: RtlRaiseException(&newrec);  // Never returns
        break;

      case ExceptionContinueSearch:
        break;

      case ExceptionNestedException:
        if (nested_frame < dispatch) nested_frame = dispatch;
        rec->ExceptionFlags |= EH_NESTED_CALL;
        break;

      default:
        newrec.ExceptionCode = STATUS_INVALID_DISPOSITION;
        newrec.ExceptionFlags = EH_NONCONTINUABLE;
        newrec.ExceptionRecord = rec;
        newrec.NumberParameters = 0;
        panic("kernel32: invalid disposition returned from exception handler");
        //TODO: RtlRaiseException(&newrec);  // Never returns
    }

    frame = frame->prev;
  }
  
  // TODO: perform default handling
  // DefaultHandling(rec, context);
}

void unwind(PEXCEPTION_FRAME endframe, LPVOID eip, PEXCEPTION_RECORD rec, DWORD retval, CONTEXT *ctxt) {
  EXCEPTION_RECORD record, newrec;
  PEXCEPTION_FRAME frame, dispatch;
  EXCEPTION_DISPOSITION rc;
  struct tib *tib = gettib();

  ctxt->Eax = retval;

  // Build an exception record, if we do not have one
  if (!rec) {
    record.ExceptionCode    = STATUS_UNWIND;
    record.ExceptionFlags   = 0;
    record.ExceptionRecord  = NULL;
    record.ExceptionAddress = (void *) ctxt->Eip;
    record.NumberParameters = 0;
    rec = &record;
  }

  rec->ExceptionFlags |= EH_UNWINDING | (endframe ? 0 : EH_EXIT_UNWIND);

  //syslog(LOG_DEBUG, "code=%lx flags=%lx", rec->ExceptionCode, rec->ExceptionFlags);

  // Get chain of exception frames
  frame = (EXCEPTION_FRAME *) tib->except;
  while (frame != (PEXCEPTION_FRAME) 0xffffffff && frame != endframe) {
    // Check frame address
    if (endframe && (frame > endframe)) {
      newrec.ExceptionCode = STATUS_INVALID_UNWIND_TARGET;
      newrec.ExceptionFlags = EH_NONCONTINUABLE;
      newrec.ExceptionRecord = rec;
      newrec.NumberParameters = 0;
      panic("kernel32: invalid unwind target");
      //TODO: RtlRaiseException(&newrec);  // Never returns
    }

    if ((void *) frame < tib->stacklimit || (void *)(frame + 1) > tib->stacktop || (int) frame & 3) {
      newrec.ExceptionCode = STATUS_BAD_STACK;
      newrec.ExceptionFlags = EH_NONCONTINUABLE;
      newrec.ExceptionRecord = rec;
      newrec.NumberParameters = 0;
      panic("kernel32: bad stack in unwind");
      //TODO: RtlRaiseException(&newrec);  // Never returns
    }

    // Call handler
    rc = call_handler(rec, frame, ctxt, &dispatch, frame->handler, unwind_handler);

    switch (rc) {
      case ExceptionContinueSearch:
        break;

      case ExceptionCollidedUnwind:
        frame = dispatch;
        break;

      default:
        newrec.ExceptionCode = STATUS_INVALID_DISPOSITION;
        newrec.ExceptionFlags = EH_NONCONTINUABLE;
        newrec.ExceptionRecord = rec;
        newrec.NumberParameters = 0;
        panic("kernel32: invalid disposition returned from exception handler in unwind");
        //TODO: RtlRaiseException(&newrec);  // Never returns
    }

    frame = pop_frame(frame);
  }
}

void win32_globalhandler(struct siginfo *info) {
  CONTEXT ctxt;
  EXCEPTION_RECORD rec;
  EXCEPTION_POINTERS ep;

  //syslog(LOG_DEBUG, "kernel32: caught signal %d", signum);

  ep.ContextRecord = &ctxt;
  ep.ExceptionRecord = &rec;

  memset(&rec, 0, sizeof(EXCEPTION_RECORD));
  rec.ExceptionAddress = (void *) info->si_ctxt->eip;

  convert_to_win32_context(info->si_ctxt, &ctxt);
  
  switch (info->si_ctxt->traptype) {
    case INTR_DIV:
      rec.ExceptionCode = EXCEPTION_INT_DIVIDE_BY_ZERO;
      break;

    case INTR_OVFL:
      rec.ExceptionCode = EXCEPTION_INT_OVERFLOW;
      break;

    case INTR_BOUND:
      rec.ExceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
      break;

    case INTR_INSTR:
      rec.ExceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
      break;

    case INTR_STACK:
      rec.ExceptionCode = EXCEPTION_STACK_OVERFLOW;
      break;

    case INTR_PGFLT:
      if (info->si_signo == SIGSTKFLT) {
        rec.ExceptionCode = STATUS_GUARD_PAGE_VIOLATION;
      } else {
        rec.ExceptionCode = EXCEPTION_ACCESS_VIOLATION;
      }
      rec.NumberParameters = 2;
      rec.ExceptionInformation[0] = (void *) ((info->si_ctxt->errcode & 2) ? 1 : 0);
      rec.ExceptionInformation[1] = info->si_addr;
      break;

    case INTR_ALIGN:
      rec.ExceptionCode = EXCEPTION_DATATYPE_MISALIGNMENT;
      break;

    default:
      // Unknown exception, dispatch signal to native handler
      old_globalhandler(info);
  }

  raise_exception(&rec, &ctxt);

  convert_from_win32_context(info->si_ctxt, &ctxt);

  //syslog(LOG_DEBUG, "kernel32: sigexit", signum);
  sigexit(info, 0);
}

BOOL WINAPI CloseHandle(
    HANDLE hObject) {
  TRACE("CloseHandle");
  if (close((handle_t) hObject) < 0) return FALSE;
  return TRUE;
}

LONG WINAPI CompareFileTime(
    CONST FILETIME *lpFileTime1,
    CONST FILETIME *lpFileTime2) {
  TRACE("CompareFileTime");

  if (lpFileTime1->dwHighDateTime > lpFileTime2->dwHighDateTime) {
    return 1;
  } else if (lpFileTime1->dwHighDateTime < lpFileTime2->dwHighDateTime) {
    return -1;
  } else if (lpFileTime1->dwLowDateTime > lpFileTime2->dwLowDateTime) {
    return 1;
  } else if (lpFileTime1->dwLowDateTime < lpFileTime2->dwLowDateTime) {
    return -1;
  } else {
    return 0;
  }
}

BOOL WINAPI CreateDirectoryA(
    LPCTSTR lpPathName,
    LPSECURITY_ATTRIBUTES lpSecurityAttributes) {
  int rc;

  TRACE("CreateDirectoryA");
  rc = mkdir(lpPathName, 0666);
  if (rc < 0) return FALSE;

  return TRUE;
}

BOOL WINAPI CreateDirectoryW(
    LPCWSTR lpPathName,
    LPSECURITY_ATTRIBUTES lpSecurityAttributes) {
  char fn[MAXPATH];
  int rc;

  TRACE("CreateDirectoryW");
  
  rc = convert_filename_from_unicode(lpPathName, fn, MAXPATH);
  if (rc < 0) return FALSE;

  return CreateDirectoryA(fn, lpSecurityAttributes);
}

HANDLE WINAPI CreateEventA(
    LPSECURITY_ATTRIBUTES lpEventAttributes,
    BOOL bManualReset,
    BOOL bInitialState,
    LPCTSTR lpName) {
  TRACE("CreateEventA");
  if (lpName != NULL) panic("named event not supported");
  return (HANDLE) mkevent(bManualReset, bInitialState);
}

HANDLE WINAPI CreateFileA(
    LPCTSTR lpFileName, 
    DWORD dwDesiredAccess, 
    DWORD dwShareMode, 
    LPSECURITY_ATTRIBUTES lpSecurityAttributes, 
    DWORD dwCreationDisposition, 
    DWORD dwFlagsAndAttributes, 
    HANDLE hTemplateFile) {
  int flags = 0;
  int mode = 0;
  int shflags = 0;
  handle_t h;

  TRACE("CreateFile");

  if ((dwDesiredAccess & (GENERIC_READ | GENERIC_WRITE)) == (GENERIC_READ | GENERIC_WRITE)) {
    flags = O_RDWR;
  } else if (dwDesiredAccess & GENERIC_READ) {
    flags = O_RDONLY;
  } else if (dwDesiredAccess & GENERIC_WRITE) {
    flags = O_WRONLY;
  } else if (dwDesiredAccess & GENERIC_ALL) {
    flags = O_RDWR;
  } else {
    flags = O_RDONLY;
  }

  if ((dwShareMode & (FILE_SHARE_READ | FILE_SHARE_WRITE)) == (FILE_SHARE_READ | FILE_SHARE_WRITE)) {
    shflags = SH_DENYNO;
  } else if (dwShareMode & FILE_SHARE_READ) {
    shflags = SH_DENYWR;
  } else if (dwShareMode & FILE_SHARE_WRITE) {
    shflags = SH_DENYRD;
  } else {
    shflags = SH_DENYRW;
  }

  switch (dwCreationDisposition) {
    case CREATE_NEW:
      flags |= O_CREAT | O_EXCL;
      break;

    case CREATE_ALWAYS:
      flags |= O_CREAT | O_TRUNC;
      break;

    case OPEN_EXISTING:
      flags |= 0;
      break;

    case OPEN_ALWAYS:
      flags |= O_CREAT;
      break;

    case TRUNCATE_EXISTING:
      flags |= O_TRUNC;
      break;

    default:
      errno = EINVAL;
      return FALSE;
  }

  if (dwFlagsAndAttributes & FILE_ATTRIBUTE_READONLY) {
    mode = S_IREAD;
  } else {
    mode = S_IREAD | S_IWRITE | S_IEXEC;
  }

  if (dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED) {
    syslog(LOG_WARNING, "CreateFile(%s): overlapped operation not supported", lpFileName);
    errno = EINVAL;
    return INVALID_HANDLE_VALUE;
  }

  if (dwFlagsAndAttributes & FILE_FLAG_NO_BUFFERING) flags |= O_DIRECT;
  if (dwFlagsAndAttributes & FILE_FLAG_RANDOM_ACCESS) flags |= O_RANDOM;
  if (dwFlagsAndAttributes & FILE_FLAG_SEQUENTIAL_SCAN) flags |= O_SEQUENTIAL;
  if (dwFlagsAndAttributes & FILE_FLAG_DELETE_ON_CLOSE) flags |= O_TEMPORARY;
  if (dwFlagsAndAttributes & FILE_ATTRIBUTE_TEMPORARY) flags |= O_SHORT_LIVED;

  h = sopen(lpFileName, flags, shflags, mode);
  if (h < 0) return INVALID_HANDLE_VALUE;

  return (HANDLE) h;
}

HANDLE WINAPI CreateFileMappingA(
    HANDLE hFile,
    LPSECURITY_ATTRIBUTES lpAttributes,
    DWORD flProtect,
    DWORD dwMaximumSizeHigh,
    DWORD dwMaximumSizeLow,
    LPCTSTR lpName) {
  TRACE("CreateFileMappingA");
  panic("CreateFileMappingA not implemented");
  return INVALID_HANDLE_VALUE;
}

HANDLE WINAPI CreateFileW(
    LPCWSTR lpFileName,
    DWORD dwDesiredAccess,
    DWORD dwShareMode,
    LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    DWORD dwCreationDisposition,
    DWORD dwFlagsAndAttributes,
    HANDLE hTemplateFile) {
  char fn[MAXPATH];
  int rc;

  TRACE("CreateFileW");
  
  rc = convert_filename_from_unicode(lpFileName, fn, MAXPATH);
  if (rc < 0) return INVALID_HANDLE_VALUE;

  return CreateFileA(fn, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
}

BOOL WINAPI CreatePipe(
    PHANDLE hReadPipe, 
    PHANDLE hWritePipe, 
    LPSECURITY_ATTRIBUTES lpPipeAttributes, 
    DWORD nSize) {
  TRACE("CreatePipe");
  panic("CreatePipe not implemented");
  return FALSE;
}

BOOL WINAPI CreateProcessA(
    LPCTSTR lpApplicationName,
    LPTSTR lpCommandLine,
    LPSECURITY_ATTRIBUTES lpProcessAttributes,
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    BOOL bInheritHandles,
    DWORD dwCreationFlags,
    LPVOID lpEnvironment,
    LPCTSTR lpCurrentDirectory,
    LPSTARTUPINFO lpStartupInfo,
    LPPROCESS_INFORMATION lpProcessInformation) {
  TRACE("CreateProcessA");
  panic("CreateProcessA not implemented");
  return FALSE;
}

HANDLE WINAPI CreateSemaphoreA(
    LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
    LONG lInitialCount,
    LONG lMaximumCount,
    LPCTSTR lpName) {
  TRACE("CreateSemaphoreA");
  if (lpName != NULL) panic("named semaphores not supported");
  return (HANDLE) mksem(lInitialCount);
}

VOID WINAPI DebugBreak(VOID) {
  TRACE("DebugBreak");
  dbgbreak();
}

VOID WINAPI DeleteCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection) {
  TRACE("DeleteCriticalSection");
  csfree(lpCriticalSection);
}

BOOL WINAPI DeleteFileA(
    LPCTSTR lpFileName) {
  TRACE("DeleteFileA");
  if (unlink(lpFileName) < 0) return FALSE;
  return TRUE;
}

BOOL WINAPI DeleteFileW(
    LPCWSTR lpFileName) {
  char fn[MAXPATH];
  int rc;

  TRACE("DeleteFileW");
  
  rc = convert_filename_from_unicode(lpFileName, fn, MAXPATH);
  if (rc < 0) return FALSE;
  
  return DeleteFileA(fn);
}

BOOL WINAPI DisableThreadLibraryCalls(
    HMODULE hModule) {
  TRACE("DisableThreadLibraryCalls");
  // ignore
  return TRUE;
}

BOOL WINAPI DuplicateHandle(
    HANDLE hSourceProcessHandle,
    HANDLE hSourceHandle,
    HANDLE hTargetProcessHandle,
    LPHANDLE lpTargetHandle,
    DWORD dwDesiredAccess,
    BOOL bInheritHandle,
    DWORD dwOptions) {
  TRACE("DuplicateHandle");
  *lpTargetHandle = (HANDLE) dup((handle_t) hSourceHandle);
  return TRUE;
}

VOID WINAPI EnterCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection) {
  TRACEX("EnterCriticalSection");
  enter(lpCriticalSection);
}

BOOL WINAPI FileTimeToLocalFileTime(
    CONST FILETIME *lpFileTime,
    LPFILETIME lpLocalFileTime) {
  TRACEX("FileTimeToLocalFileTime");
  *lpLocalFileTime = *lpFileTime;
  return TRUE;
}

BOOL WINAPI FileTimeToSystemTime(
    CONST FILETIME *lpFileTime,
    LPSYSTEMTIME lpSystemTime) {
  time_t t;
  struct tm tm;

  TRACEX("FileTimeToSystemTime");

  t = (time_t) ((*(unsigned __int64 *) lpFileTime) - EPOC) / SECTIMESCALE;

  gmtime_r(&t, &tm);

  lpSystemTime->wYear = tm.tm_year + 1900; 
  lpSystemTime->wMonth = tm.tm_mon + 1; 
  lpSystemTime->wDayOfWeek = tm.tm_wday;
  lpSystemTime->wDay = tm.tm_mday; 
  lpSystemTime->wHour = tm.tm_hour; 
  lpSystemTime->wMinute = tm.tm_min; 
  lpSystemTime->wSecond = tm.tm_sec; 
  lpSystemTime->wMilliseconds = 0;

  return TRUE;
}

static void fill_find_data(LPWIN32_FIND_DATA fd, char *filename, struct stat64 *statbuf) {
  FILETIME *ft;

  memset(fd, 0, sizeof(WIN32_FIND_DATA));
  strcpy(fd->cFileName, filename);

  if ((statbuf->st_mode & S_IFMT) == S_IFDIR) {
    fd->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
  } else {
    fd->dwFileAttributes |= FILE_ATTRIBUTE_NORMAL;
  }

  ft = (FILETIME *) &statbuf->st_size;
  fd->nFileSizeLow = ft->dwLowDateTime;
  fd->nFileSizeHigh = ft->dwHighDateTime;

  *(unsigned __int64 *) &(fd->ftCreationTime) = (unsigned __int64) statbuf->st_ctime * SECTIMESCALE + EPOC;
  *(unsigned __int64 *) &(fd->ftLastAccessTime) = (unsigned __int64) statbuf->st_atime * SECTIMESCALE + EPOC;
  *(unsigned __int64 *) &(fd->ftLastWriteTime) = (unsigned __int64) statbuf->st_mtime * SECTIMESCALE + EPOC;
}

static int like(char *str, char *mask) {
  if (!str) str = "";
  if (!mask) mask = "";

  while (*mask) {
    if (*mask == '*') {
      while (*mask == '*') mask++;
      if (*mask == 0) return 1;
      while (*str) {
        if (like(str, mask)) return 1;
        str++;
      }
      return 0;
    } else if (*mask == *str || *mask == '?' && *str != 0) {
      str++;
      mask++;
    } else {
      return 0;
    }
  }

  return *str == 0;
}

BOOL WINAPI FindClose(
    HANDLE hFindFile) {
  struct winfinddata *finddata;

  TRACE("FindClose");
  if (hFindFile == NOFINDHANDLE) {
    return TRUE;
  } else if (hFindFile == INVALID_HANDLE_VALUE) {
    return FALSE;
  } else {
    finddata = (struct winfinddata *) hFindFile;

    close(finddata->fhandle);
    free(finddata);

    return TRUE;
  }
}

HANDLE WINAPI FindFirstFileA(
    LPCTSTR lpFileName,
    LPWIN32_FIND_DATA lpFindFileData) {
  struct winfinddata *finddata;
  struct direntry dirent;
  struct stat64 statbuf;
  char *p;
  char *base;
  char fn[MAXPATH];

  TRACE("FindFirstFileA");
  //syslog(LOG_DEBUG | LOG_AUX, "FindFirstFile %s", lpFileName);

  p = (char *) lpFileName;
  while (*p != 0 && *p != '*' && *p != '?') p++;
  
  if (*p) {
    finddata = (struct winfinddata *) malloc(sizeof(struct winfinddata));
    if (!finddata) {
      errno = ENOMEM;
      return INVALID_HANDLE_VALUE;
    }

    strcpy(finddata->dir, lpFileName);
    base = NULL;
    p = finddata->dir;
    while (*p) {
      if (*p == PS1 || *p == PS2) base = p + 1;
      p++;
    }
    if (!base) {
      errno = ENOTDIR;
      free(finddata);
      return INVALID_HANDLE_VALUE;
    }
    base[-1] = 0;
    finddata->mask = base;

    if (finddata->mask[0] == '*' && finddata->mask[1] == '.' && finddata->mask[2] == '*' && finddata->mask[3] == 0) {
      finddata->mask[1] = 0;
    }

    finddata->fhandle = _opendir(finddata->dir);
    if (finddata->fhandle < 0) {
      free(finddata);
      return INVALID_HANDLE_VALUE;
    }

    while (_readdir(finddata->fhandle, &dirent, 1) > 0) {
      //syslog(LOG_DEBUG | LOG_AUX, "match %s with %s", dirent.name, finddata->mask);
      if (like(dirent.name, finddata->mask)) {
        strcpy(fn, finddata->dir);
        strcpy(fn + strlen(fn), "\\");
        strcpy(fn + strlen(fn), dirent.name);

        if (stat64(fn, &statbuf) < 0) {
          errno = ENOENT;
          free(finddata);
          return INVALID_HANDLE_VALUE;
        }

        fill_find_data(lpFindFileData, dirent.name, &statbuf);
        return (HANDLE) finddata;
      }
    }

    // Add dummy entry
    close(finddata->fhandle);
    free(finddata);
    if (stat64(finddata->dir, &statbuf) < 0) {
      errno = ENOENT;
      return INVALID_HANDLE_VALUE;
    }

    fill_find_data(lpFindFileData, ".", &statbuf);
    return NOFINDHANDLE;
  } else {
    if (stat64((char *) lpFileName, &statbuf) < 0) {
      errno = ENOENT;
      return INVALID_HANDLE_VALUE;
    }

    p = (char *) lpFileName;
    base = p;
    while (*p) {
      if (*p == PS1 || *p == PS2) base = p + 1;
      p++;
    }

    fill_find_data(lpFindFileData, base, &statbuf);

    return NOFINDHANDLE;
  }
}

HANDLE WINAPI FindFirstFileW(
    LPCWSTR lpFileName,
    LPWIN32_FIND_DATAW lpFindFileData) {
  char fn[MAXPATH];
  int rc;
  WIN32_FIND_DATA fd;
  HANDLE fh;

  rc = convert_filename_from_unicode(lpFileName, fn, MAXPATH);
  if (rc < 0) return INVALID_HANDLE_VALUE;

  fh = FindFirstFileA(fn, &fd);
  if (fh == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;

  memcpy(lpFindFileData, &fd, sizeof(WIN32_FIND_DATA) - MAX_PATH - 14);
  convert_filename_to_unicode(fd.cFileName, lpFindFileData->cFileName, MAXPATH);
  return fh;
}

BOOL WINAPI FindNextFileA(
    HANDLE hFindFile,
    LPWIN32_FIND_DATA lpFindFileData) {
  struct winfinddata *finddata;
  struct direntry dirent;
  struct stat64 statbuf;
  char fn[MAXPATH];

  TRACE("FindNextFileA");

  if (hFindFile == NOFINDHANDLE) {
    errno = ESRCH;
    return FALSE;
  } else {
    finddata = (struct winfinddata *) hFindFile;

    while (_readdir(finddata->fhandle, &dirent, 1) > 0) {
      //syslog(LOG_DEBUG | LOG_AUX, "match next %s with %s", dirent.name, finddata->mask);
      if (like(dirent.name, finddata->mask)) {
        strcpy(fn, finddata->dir);
        strcpy(fn + strlen(fn), "\\");
        strcpy(fn + strlen(fn), dirent.name);

        if (stat64(fn, &statbuf) < 0) {
          errno = ENOENT;
          return FALSE;
        }

        fill_find_data(lpFindFileData, dirent.name, &statbuf);
        return TRUE;
      }
    }

    errno = ESRCH;
    return FALSE;
  }
}

BOOL WINAPI FindNextFileW(
    HANDLE hFindFile,
    LPWIN32_FIND_DATAW lpFindFileData) {
  WIN32_FIND_DATA fd;
  BOOL ok;

  TRACE("FindNextFileW");

  ok = FindNextFileA(hFindFile, &fd);
  if (!ok) return FALSE;

  memcpy(lpFindFileData, &fd, sizeof(WIN32_FIND_DATA) - MAX_PATH - 14);
  convert_filename_to_unicode(fd.cFileName, lpFindFileData->cFileName, MAXPATH);

  return TRUE;
}

BOOL WINAPI FlushFileBuffers(
    HANDLE hFile) {
  TRACE("FlushFileBuffers");
  if (fsync((handle_t) hFile) < 0) return FALSE;
  return TRUE;
}

BOOL WINAPI FlushViewOfFile(
    LPCVOID lpBaseAddress,
    SIZE_T dwNumberOfBytesToFlush) {
  panic("FlushViewOfFile not implemented");
  return FALSE;
}

DWORD WINAPI FormatMessageA(
    DWORD dwFlags,
    LPCVOID lpSource,
    DWORD dwMessageId,
    DWORD dwLanguageId,
    LPTSTR lpBuffer,
    DWORD nSize,
    va_list *Arguments) {
  char *buf;

  TRACE("FormatMessageA");

  // TODO: generate more descriptive message
  if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) {
    buf = malloc(nSize > 256 ? nSize : 256);
    if (!buf) {
      errno = ENOMEM;
      return 0;
    }

    *(char **) lpBuffer = buf;
  } else {
    buf = lpBuffer;
  }

  sprintf(buf, "Error 0x%08X.\n", dwMessageId);
  return strlen(buf);
}

BOOL WINAPI FreeLibrary(
    HMODULE hModule) {
  TRACE("FreeLibrary");
  if (dlclose((hmodule_t) hModule) < 0) return FALSE;
  return TRUE;
}

DWORD WINAPI GetCurrentDirectoryA(
    DWORD nBufferLength,
    LPTSTR lpBuffer) {
  char curdir[MAXPATH];

  TRACE("GetCurrentDirectoryA");
  
  if (!getcwd(curdir, MAXPATH)) return 0;
  if (nBufferLength < strlen(curdir) + 3) return strlen(curdir) + 3;
  lpBuffer[0] = 'c';
  lpBuffer[1] = ':';
  strcpy(lpBuffer + 2, curdir);
  return strlen(lpBuffer);
}

HANDLE WINAPI GetCurrentProcess(VOID) {
  TRACE("GetCurrentProcess");
  return (HANDLE) 1;
}

HANDLE WINAPI GetCurrentThread(VOID) {
  TRACE("GetCurrentThread");
  return (HANDLE) self();
}

DWORD WINAPI GetCurrentThreadId(VOID) {
  TRACE("GetCurrentThreadId");
  return gettid();
}

DWORD WINAPI GetEnvironmentVariableA(
    LPCTSTR lpName,
    LPTSTR lpBuffer,
    DWORD nSize) {
  char *value;

  TRACE("GetEnvironmentVariableA");
  //syslog(LOG_DEBUG, "GetEnvironmentVariable(%s)", lpName);
  
  value = get_property(osconfig(), "env", (char *) lpName, NULL);
  if (value) {
    strcpy(lpBuffer, value);
    return strlen(value);
  } else {
    memset(lpBuffer, 0, nSize);
    return 0;
  }
}

BOOL WINAPI GetExitCodeProcess(
    HANDLE hProcess,
    LPDWORD lpExitCode) {
  TRACE("GetExitCodeProcess");
  panic("GetExitCodeProcess not implemented");
  return 0;
}

BOOL WINAPI GetExitCodeThread(
    HANDLE hThread,
    LPDWORD lpExitCode) {
  TRACE("GetExitCodeThread");
  panic("GetExitCodeThread not implemented");
  return FALSE;
}

DWORD WINAPI GetFileAttributesA(
    LPCTSTR lpFileName) {
  struct stat64 fs;
  unsigned long attrs;

  if (stat64((char *) lpFileName, &fs) < 0) return -1;

  switch (fs.st_mode & S_IFMT) {
    case S_IFREG:
      attrs = FILE_ATTRIBUTE_NORMAL;
      break;

    case S_IFDIR:
      attrs = FILE_ATTRIBUTE_DIRECTORY;
      break;

    case S_IFSOCK:
    case S_IFBLK:
    case S_IFCHR:
    case S_IFIFO:
      attrs = FILE_ATTRIBUTE_DEVICE;
      break;

    case S_IFLNK:
      attrs = 0;
      break;

    default:
      attrs = 0;
  }

  if (!(fs.st_mode & S_IWRITE)) attrs |= FILE_ATTRIBUTE_READONLY;
  // TODO: set other attributes for files (hidden, system, etc.)

  return attrs;
}

DWORD WINAPI GetFileAttributesW(
    LPCWSTR lpFileName) {
  char fn[MAXPATH];
  int rc;

  TRACE("GetFileAttributesW");

  rc = convert_filename_from_unicode(lpFileName, fn, MAXPATH);
  if (rc < 0) return -1;

  rc = GetFileAttributesA(fn);
  //syslog(LOG_DEBUG, "GetFileAttributesW(%s)=%08X", fn, rc);
  return rc;
}

DWORD WINAPI GetFileSize(
    HANDLE hFile,
    LPDWORD lpFileSizeHigh) {
  LARGE_INTEGER size;

  TRACE("GetFileSize");

  size.QuadPart = fstat64((handle_t) hFile, NULL);
  if (size.QuadPart < 0) return INVALID_FILE_SIZE;
  if (lpFileSizeHigh) *lpFileSizeHigh = size.HighPart;
  return size.LowPart;
}

BOOL WINAPI GetFileTime(
    HANDLE hFile,
    LPFILETIME lpCreationTime,
    LPFILETIME lpLastAccessTime,
    LPFILETIME lpLastWriteTime) {
  struct stat64 statbuf;

  TRACE("GetFileTime");

  if (fstat64((handle_t) hFile, &statbuf) < 0) return FALSE;

  if (lpCreationTime) {
    *(unsigned __int64 *) lpCreationTime = (unsigned __int64) statbuf.st_ctime * SECTIMESCALE + EPOC;
  }

  if (lpLastAccessTime) {
    *(unsigned __int64 *) lpLastAccessTime = (unsigned __int64) statbuf.st_atime * SECTIMESCALE + EPOC;
  }

  if (lpLastWriteTime) {
    *(unsigned __int64 *) lpLastWriteTime = (unsigned __int64) statbuf.st_mtime * SECTIMESCALE + EPOC;
  }

  return TRUE;
}

DWORD WINAPI GetFullPathNameA(
    LPCTSTR lpFileName,
    DWORD nBufferLength,
    LPTSTR lpBuffer,
    LPTSTR *lpFilePart) {
  char fn[MAXPATH];
  int rc;
  char *basename;
  char *p;

  TRACE("GetFullPathName");

  rc = canonicalize(lpFileName, fn, MAXPATH);
  if (rc < 0) return 0;

  if (strlen(fn) + 2 >= nBufferLength) return strlen(fn) + 3;

  strcpy(lpBuffer, "c:");
  strcat(lpBuffer, fn);

  p = basename = lpBuffer;
  while (*p) {
    if (*p == PS1 || *p == PS2) basename = p + 1;
    p++;
  }
  *lpFilePart = basename;

  return strlen(lpBuffer);
}

DWORD WINAPI GetLastError(VOID) {
  TRACE("GetLastError");
  // TODO: implement better error reporting
  if (errno == 0) return 0;
  if (errno == ENOENT) return ERROR_FILE_NOT_FOUND;
  if (errno == ESRCH) return ERROR_NO_MORE_FILES;
  return 0x80040000 | errno;
}

DWORD WINAPI GetLogicalDrives(VOID) {
  TRACE("GetLogicalDrives");
  return 4; // Drive C:
}

DWORD WINAPI GetModuleFileNameA(
    HMODULE hModule,
    LPTSTR lpFilename,
    DWORD nSize) {
  int rc;

  TRACE("GetModuleFileNameA");
  rc = getmodpath((hmodule_t) hModule, lpFilename, nSize);
  //syslog(LOG_DEBUG, "Module filename for %p is %s", hModule, lpFilename);
  return rc;
}

HMODULE WINAPI GetModuleHandleA(
    LPCTSTR lpModuleName) {
  hmodule_t hmod;

  TRACE("GetModuleHandleA");
  hmod = getmodule(lpModuleName);
  //syslog(LOG_DEBUG, "GetModuleHandleA(%s) returned %p", lpModuleName, hmod);
  return (HMODULE) hmod;
}

BOOL WINAPI GetNumberOfConsoleInputEvents(
    HANDLE hConsoleInput,
    LPDWORD lpcNumberOfEvents) {
  int rc;

  TRACE("GetNumberOfConsoleInputEvents");

  // TODO: Here we assume that the handle refers to /dev/console
  rc = ioctl(fdin, IOCTL_KBHIT, NULL, 0);
  if (rc < 0) return FALSE;
  if (rc > 0) {
    *lpcNumberOfEvents = 1;
  } else {
    *lpcNumberOfEvents = 0;
  }

  return TRUE;
}

BOOL WINAPI GetOverlappedResult(
    HANDLE hFile,
    LPOVERLAPPED lpOverlapped,
    LPDWORD lpNumberOfBytesTransferred,
    BOOL bWait) {
  TRACE("GetOverlappedResult");
  panic("GetOverlappedResult not implemented");
  return FALSE;
}

FARPROC WINAPI GetProcAddress(
    HMODULE hModule,
    LPCSTR lpProcName) {
  TRACE("GetProcAddress");
  //syslog(LOG_DEBUG, "GetProcAddress name: %s hmod: %08X", lpProcName, hModule);
  return dlsym((hmodule_t) hModule, (char *) lpProcName);
}

BOOL WINAPI GetProcessAffinityMask(
    HANDLE hProcess,
    LPDWORD lpProcessAffinityMask,
    LPDWORD lpSystemAffinityMask) {
  TRACE("GetProcessAffinityMask");
  panic("GetProcessAffinityMask not implemented");
  return TRUE;
}

HANDLE WINAPI GetProcessHeap(VOID) {
  TRACE("GetProcessHeap");
  return PROCESSHEAP;
}

HANDLE WINAPI GetStdHandle(
    DWORD nStdHandle) {
  TRACE("GetStdHandle");

  switch (nStdHandle) {
    case STD_INPUT_HANDLE:  return (HANDLE) fdin;
    case STD_OUTPUT_HANDLE: return (HANDLE) fdout;
    case STD_ERROR_HANDLE:  return (HANDLE) fderr;
  }

  return INVALID_HANDLE_VALUE;
}

UINT WINAPI GetSystemDirectoryA(
    LPTSTR lpBuffer,
    UINT uSize) {
  TRACE("GetSystemDirectoryA");
  strcpy(lpBuffer, get_property(osconfig(), "win32", "sysdir", "c:\\bin"));
  return strlen(lpBuffer);
}

VOID WINAPI GetSystemInfo(
    LPSYSTEM_INFO lpSystemInfo) {
  struct cpuinfo cpu;

  TRACE("GetSystemInfo");
  
  sysinfo(SYSINFO_CPU, &cpu, sizeof(cpu));
  memset(lpSystemInfo, 0, sizeof(SYSTEM_INFO));
  lpSystemInfo->dwNumberOfProcessors = 1;

  lpSystemInfo->wProcessorLevel = cpu.cpu_family;

  if (cpu.cpu_family == 3) {
    lpSystemInfo->dwProcessorType = 386;
    lpSystemInfo->wProcessorRevision = 0xFFA0;
  } else if (cpu.cpu_family == 4) {
    lpSystemInfo->dwProcessorType = 486;
    lpSystemInfo->wProcessorRevision = 0xFFA0;
  } else {
    lpSystemInfo->dwProcessorType = 586;
    lpSystemInfo->wProcessorRevision = (cpu.cpu_model << 16) | cpu.cpu_stepping;
  }

  lpSystemInfo->dwPageSize = cpu.pagesize;
  lpSystemInfo->dwAllocationGranularity = cpu.pagesize;
  lpSystemInfo->lpMinimumApplicationAddress = (void *) 0x10000;
  lpSystemInfo->lpMaximumApplicationAddress = (void *) 0x7FFFFFFF;
}

VOID WINAPI GetSystemTime(
    LPSYSTEMTIME lpSystemTime) {
  struct timeval tv;
  struct tm tm;

  TRACE("GetSystemTime");
  gettimeofday(&tv, NULL);
  gmtime_r(&tv.tv_sec, &tm);

  lpSystemTime->wYear = tm.tm_year + 1900; 
  lpSystemTime->wMonth = tm.tm_mon + 1; 
  lpSystemTime->wDayOfWeek = tm.tm_wday;
  lpSystemTime->wDay = tm.tm_mday; 
  lpSystemTime->wHour = tm.tm_hour; 
  lpSystemTime->wMinute = tm.tm_min; 
  lpSystemTime->wSecond = tm.tm_sec; 
  lpSystemTime->wMilliseconds = (WORD) (tv.tv_usec / 1000);
}

VOID WINAPI GetSystemTimeAsFileTime(
    LPFILETIME lpSystemTimeAsFileTime) {
  struct timeval tv;

  TRACE("GetSystemTimeAsFileTime");
  gettimeofday(&tv, NULL);
  *(unsigned __int64 *) lpSystemTimeAsFileTime = ((unsigned __int64) (tv.tv_sec) * 1000000 + (unsigned __int64) (tv.tv_usec)) * MICROTIMESCALE + EPOC;
}

DWORD WINAPI GetTempPathA(
    DWORD nBufferLength,
    LPTSTR lpBuffer) {
  TRACE("GetTempPathA");
  strcpy(lpBuffer, get_property(osconfig(), "win32", "tmpdir", "c:\\tmp"));
  return strlen(lpBuffer);
}

BOOL WINAPI GetThreadContext(
    HANDLE hThread,
    LPCONTEXT lpContext) {
  struct context ctxt;
  int rc;

  TRACE("GetThreadContext");
  rc = getcontext((handle_t) hThread, &ctxt);
  if (rc < 0) {
    if (errno == EPERM) {
      // Thread does not have a usermode context, just return dummy context
      memset(lpContext, 0, sizeof(CONTEXT));
      return TRUE;
    }

    syslog(LOG_DEBUG, "GetThreadContext(%d) failed", hThread);
    return FALSE;
  }

  convert_to_win32_context(&ctxt, lpContext);
  return TRUE;
}

LCID WINAPI GetThreadLocale(VOID) {
  TRACE("GetThreadLocale");
  return get_numeric_property(osconfig(), "win32", "locale", 0x0406);
}

int WINAPI GetThreadPriority(
    HANDLE hThread) {
  int prio;

  TRACE("GetThreadPriority");

  prio = getprio((handle_t) hThread);
  if (prio < 0) return 0x7FFFFFFF;

  // Return priority based on win32 normal priority class scheme
  switch (prio) {
    case 1: return THREAD_PRIORITY_IDLE;
    case 6: return THREAD_PRIORITY_LOWEST;
    case 7: return THREAD_PRIORITY_BELOW_NORMAL;
    case 8: return THREAD_PRIORITY_NORMAL;
    case 9: return THREAD_PRIORITY_ABOVE_NORMAL;
    case 10: return THREAD_PRIORITY_HIGHEST;
    case 15: return THREAD_PRIORITY_TIME_CRITICAL;
  }

  return 0x7FFFFFFF;
}

BOOL WINAPI GetThreadTimes(
    HANDLE hThread,
    LPFILETIME lpCreationTime,
    LPFILETIME lpExitTime,
    LPFILETIME lpKernelTime,
    LPFILETIME lpUserTime) {
  TRACE("GetThreadTimes");
  panic("GetThreadTimes not implemented");
  return FALSE;
}

DWORD WINAPI GetTickCount(VOID) {
  TRACE("GetTickCount");
  return clock();
}

DWORD WINAPI GetTimeZoneInformation(
    LPTIME_ZONE_INFORMATION lpTimeZoneInformation) {
  TRACE("GetTimeZoneInformation");
  // TODO: return real time zone information (used by win32\native\java\util\TimeZone_md.c)
  return -1;
}

BOOL WINAPI GetVersionExA(
    LPOSVERSIONINFO lpVersionInfo) {
  TRACE("GetVersionExA");
  lpVersionInfo->dwMajorVersion = 5;
  lpVersionInfo->dwMinorVersion = 0;
  lpVersionInfo->dwPlatformId = 2; // VER_PLATFORM_WIN32_NT
  strcpy(lpVersionInfo->szCSDVersion, "sanos");

  return TRUE;
}

BOOL WINAPI GetVolumeInformationA(
    LPCTSTR lpRootPathName,
    LPTSTR lpVolumeNameBuffer,
    DWORD nVolumeNameSize,
    LPDWORD lpVolumeSerialNumber,
    LPDWORD lpMaximumComponentLength,
    LPDWORD lpFileSystemFlags,
    LPTSTR lpFileSystemNameBuffer,
    DWORD nFileSystemNameSize) {
  TRACE("GetVolumeInformationA");

  if (lpVolumeNameBuffer) *lpVolumeNameBuffer = 0;
  if (lpVolumeSerialNumber) *lpVolumeSerialNumber = 0;
  if (lpMaximumComponentLength) *lpMaximumComponentLength = MAXPATH - 1;
  if (lpFileSystemFlags) *lpFileSystemFlags = 0;
  if (lpFileSystemNameBuffer) *lpFileSystemNameBuffer = 0;

  return TRUE;
}

UINT WINAPI GetWindowsDirectoryA(
    LPTSTR lpBuffer,
    UINT uSize) {
  TRACE("GetWindowsDirectoryA");
  strcpy(lpBuffer, get_property(osconfig(), "win32", "windir", "c:\\bin"));
  return strlen(lpBuffer);
}

VOID WINAPI GlobalMemoryStatus(
    LPMEMORYSTATUS lpBuffer) {
  struct meminfo mem;

  TRACE("GlobalMemoryStatus");

  sysinfo(SYSINFO_MEM, &mem, sizeof(mem));

  lpBuffer->dwLength = sizeof(MEMORYSTATUS);
  lpBuffer->dwMemoryLoad = ((mem.physmem_total - mem.physmem_avail) / mem.pagesize) / (mem.physmem_total / mem.pagesize);
  lpBuffer->dwTotalPhys = mem.physmem_total;
  lpBuffer->dwAvailPhys = mem.physmem_avail;
  lpBuffer->dwTotalPageFile = mem.physmem_total;
  lpBuffer->dwAvailPageFile = mem.physmem_avail;
  lpBuffer->dwTotalVirtual = mem.virtmem_total;
  lpBuffer->dwAvailVirtual = mem.virtmem_avail;
}

LPVOID WINAPI HeapAlloc(
    HANDLE hHeap,
    DWORD dwFlags,
    SIZE_T dwBytes) {
  TRACE("HeapAlloc");
  if (hHeap != PROCESSHEAP) {
    syslog(LOG_DEBUG, "warning: HeapAlloc only supported for process heap");
    return NULL;
  }

  return malloc(dwBytes);
}

BOOL WINAPI HeapFree(
    HANDLE hHeap,
    DWORD dwFlags,
    LPVOID lpMem) {
  TRACE("HeapFree");
  if (hHeap != PROCESSHEAP) {
    syslog(LOG_DEBUG, "warning: HeapFree only supported for process heap");
    return FALSE;
  }

  free(lpMem);
  return TRUE;
}


VOID WINAPI InitializeCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection) {
  TRACE("InitializeCriticalSection");
  mkcs((struct critsect *) lpCriticalSection);
}

#ifndef __TINYC__
__declspec(naked) LONG WINAPI InterlockedDecrement(
    LPLONG volatile lpAddend) {
  __asm {
    mov   ecx,dword ptr [esp+4]
    mov   eax,0FFFFFFFFh
    nop
    xadd  dword ptr [ecx],eax
    dec   eax
    ret   4
  }
}

__declspec(naked) LONG WINAPI InterlockedExchange(
    LPLONG volatile Target,
    LONG Value) {
  __asm {
    mov ecx,dword ptr [esp+4]
    mov edx,dword ptr [esp+8]
    mov eax,dword ptr [ecx]
ileagain:
    lock cmpxchg dword ptr [ecx],edx
    jne ileagain
    ret 8
  }
}

__declspec(naked) LONG WINAPI InterlockedIncrement(
    LPLONG volatile lpAddend) {
  __asm {
    mov   ecx,dword ptr [esp+4]
    mov   eax,1
    nop
    xadd  dword ptr [ecx],eax
    inc   eax
    ret   4
  }
}
#endif

BOOL WINAPI IsDBCSLeadByte(
    BYTE TestChar) {
  TRACEX("IsDBCSLeadByte");
  // TODO: test is lead byte
  //syslog(LOG_DEBUG, "IsDBCSLeadByte called");
  return FALSE;
}

BOOL WINAPI IsValidCodePage(
    UINT CodePage) {
  TRACE("IsValidCodePage");
  panic("IsValidCodePage not implemented");
  return FALSE;
}

VOID WINAPI LeaveCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection) {
  TRACEX("LeaveCriticalSection");
  leave(lpCriticalSection);
}

HMODULE WINAPI LoadLibraryA(
    LPCTSTR lpFileName) {
  TRACE("LoadLibraryA");
  return (HMODULE) dlopen((char *) lpFileName, 0);
}

BOOL WINAPI LockFile(
    HANDLE hFile,
    DWORD dwFileOffsetLow,
    DWORD dwFileOffsetHigh,
    DWORD nNumberOfBytesToLockLow,
    DWORD nNumberOfBytesToLockHigh) {
  panic("LockFile not implemented");
  return FALSE;
}

BOOL WINAPI LockFileEx(
    HANDLE hFile,
    DWORD dwFlags,
    DWORD dwReserved,
    DWORD nNumberOfBytesToLockLow,
    DWORD nNumberOfBytesToLockHigh,
    LPOVERLAPPED lpOverlapped) {
  panic("LockFileEx not implemented");
  return FALSE;
}

LPVOID WINAPI MapViewOfFile(
    HANDLE hFileMappingObject,
    DWORD dwDesiredAccess,
    DWORD dwFileOffsetHigh,
    DWORD dwFileOffsetLow,
    SIZE_T dwNumberOfBytesToMap) {
  TRACE("MapViewOfFile");
  panic("MapViewOfFile not implemented");
  return NULL;
}

BOOL WINAPI MoveFileA(
    LPCTSTR lpExistingFileName,
    LPCTSTR lpNewFileName) {
  TRACE("MoveFileA");
  if (rename(lpExistingFileName, lpNewFileName) < 0) return FALSE;

  return TRUE;
}

int WINAPI MultiByteToWideChar(
    UINT CodePage,         // code page
    DWORD dwFlags,         // character-type options
    LPCSTR lpMultiByteStr, // string to map
    int cbMultiByte,       // number of bytes in string
    LPWSTR lpWideCharStr,  // wide-character buffer
    int cchWideChar) {     // size of buffer
  TRACE("MultiByteToWideChar");
  panic("MultiByteToWideChar not implemented");
  return 0;
}

HANDLE WINAPI OpenFileMappingA(
    DWORD dwDesiredAccess,
    BOOL bInheritHandle,
    LPCTSTR lpName) {
  TRACE("OpenFileMappingA");
  panic("OpenFileMappingA not implemented");
  return INVALID_HANDLE_VALUE;
}

HANDLE WINAPI OpenProcess(
    DWORD dwDesiredAccess,
    BOOL bInheritHandle,
    DWORD dwProcessId) {
  TRACE("OpenProcess");
  panic("OpenProcess not implemented");
  return NULL;
}

BOOL WINAPI PeekConsoleInputA(
    HANDLE hConsoleInput,
    PINPUT_RECORD lpBuffer,
    DWORD nLength,
    LPDWORD lpNumberOfEventsRead) {
  int rc;

  TRACE("PeekConsoleInputA");

  // TODO: Here we assume that the handle refers to /dev/console
  // Just return a bogus key event record if keyboard buffer is not empty
  rc = ioctl(fdin, IOCTL_KBHIT, NULL, 0);
  if (rc < 0) return FALSE;
  if (rc > 0) {
    KEY_EVENT_RECORD *rec = (KEY_EVENT_RECORD *) lpBuffer;
    rec->bKeyDown = TRUE;
    rec->wRepeatCount = 1;
    rec->wVirtualKeyCode = 0;
    rec->wVirtualScanCode = 0;
    rec->uChar.AsciiChar = ' ';
    rec->dwControlKeyState = 0;
    *lpNumberOfEventsRead = 1;
  } else {
    *lpNumberOfEventsRead = 0;
  }

  return TRUE;
}

BOOL WINAPI PeekNamedPipe(
    HANDLE hNamedPipe,
    LPVOID lpBuffer,
    DWORD nBufferSize,
    LPDWORD lpBytesRead,
    LPDWORD lpTotalBytesAvail,
    LPDWORD lpBytesLeftThisMessage) {
  TRACE("PeekNamedPipe");
  panic("PeekNamedPipe not implemented");
  return FALSE;
}

BOOL WINAPI QueryPerformanceCounter(
    LARGE_INTEGER *lpPerformanceCount) {
  TRACEX("QueryPerformanceCounter");
  lpPerformanceCount->HighPart = 0;
  lpPerformanceCount->LowPart = clock();

  return TRUE;
}

BOOL WINAPI QueryPerformanceFrequency(
    LARGE_INTEGER *lpFrequency) {
  TRACE("QueryPerformanceFrequency");
  // Tick count is in milliseconds
  lpFrequency->HighPart = 0;
  lpFrequency->LowPart = 1000;

  return TRUE;
}

BOOL WINAPI ReadFile(
    HANDLE hFile,
    LPVOID lpBuffer,
    DWORD nNumberOfBytesToRead,
    LPDWORD lpNumberOfBytesRead,
    LPOVERLAPPED lpOverlapped) {
  int rc;

  TRACE("ReadFile");
  if (lpOverlapped) panic("Overlapped I/O not implemented in ReadFile");

  rc = read((handle_t) hFile, lpBuffer, nNumberOfBytesToRead);
  if (rc < 0) return FALSE;

  *lpNumberOfBytesRead = rc;
  return TRUE;
}

BOOL WINAPI ReleaseSemaphore(
    HANDLE hSemaphore,
    LONG lReleaseCount,
    LPLONG lpPreviousCount) {
  LONG dummy;

  TRACE("ReleaseSemaphore");
  if (!lpPreviousCount) lpPreviousCount = &dummy;
  *lpPreviousCount = semrel((handle_t) hSemaphore, lReleaseCount);
  return TRUE;
}

BOOL WINAPI RemoveDirectoryA(
    LPCTSTR lpPathName) {
  TRACE("RemoveDirectoryA");
  if (rmdir(lpPathName) < 0) return FALSE;
  return TRUE;
}

BOOL WINAPI RemoveDirectoryW(
    LPCWSTR lpPathName) {
  char path[MAXPATH];
  int rc;

  TRACE("RemoveDirectoryW");

  rc = convert_filename_from_unicode(lpPathName, path, MAXPATH);
  if (rc < 0) return FALSE;

  return RemoveDirectoryA(path);
}

BOOL WINAPI ResetEvent(
    HANDLE hEvent) {
  TRACE("ResetEvent");
  ereset((handle_t) hEvent);
  return TRUE;
}

DWORD WINAPI ResumeThread(
    HANDLE hThread) {
  TRACE("ResumeThread");
  return resume((handle_t) hThread);
}

VOID WINAPI RtlUnwind(
    PEXCEPTION_FRAME endframe, 
    LPVOID eip, 
    PEXCEPTION_RECORD rec, 
    DWORD retval) {
  CONTEXT ctxt;

  TRACE("RtlUnwind");
  memset(&ctxt, 0, sizeof(CONTEXT));
  unwind(endframe, eip, rec, retval, &ctxt);
}

DWORD WINAPI SearchPathA(
    LPCSTR lpPath,
    LPCSTR lpFileName,
    LPCSTR lpExtension,
    DWORD nBufferLength,
    LPSTR lpBuffer,
    LPSTR *lpFilePart) {
  TRACE("SearchPathA");
  panic("SearchPathA not implemented");
  return 0;
}

BOOL WINAPI SetConsoleCtrlHandler(
    PHANDLER_ROUTINE HandlerRoutine,
    BOOL Add) {
  TRACE("SetConsoleCtrlHandler");
  //syslog(LOG_DEBUG, "warning: SetConsoleCtrlHandler not implemented, ignored");
  return TRUE;
}

BOOL WINAPI SetConsoleTitleA(
    LPCTSTR lpConsoleTitle) {
  TRACE("SetConsoleTitle");
  return TRUE;
}

BOOL WINAPI SetEndOfFile(
    HANDLE hFile)
{
  TRACE("SetEndOfFile");
  if (ftruncate((handle_t) hFile, tell((handle_t) hFile)) < 0) return FALSE;
  return TRUE;
}

BOOL WINAPI SetEvent(
    HANDLE hEvent) {
  TRACE("SetEvent");
  eset((handle_t) hEvent);
  return TRUE;
}

BOOL WINAPI SetFileAttributesA(
    LPCTSTR lpFileName,
    DWORD dwFileAttributes) {
  TRACE("SetFileAttributesA");
  if (dwFileAttributes != 0) syslog(LOG_DEBUG, "warning: SetFileAttributesA(%s,%08x) not implemented, ignored", lpFileName, dwFileAttributes);
  return TRUE;
}

BOOL WINAPI SetFileAttributesW(
    LPCWSTR lpFileName,
    DWORD dwFileAttributes) {
  char fn[MAXPATH];
  int rc;

  TRACE("SetFileAttributesW");
  
  rc = convert_filename_from_unicode(lpFileName, fn, MAXPATH);
  if (rc < 0) return 0;

  return SetFileAttributesA(fn, dwFileAttributes);
}

DWORD WINAPI SetFilePointer(
    HANDLE hFile,
    LONG lDistanceToMove,
    PLONG lpDistanceToMoveHigh,
    DWORD dwMoveMethod) {
  int rc;
  TRACE("SetFilePointer");
  
  if (lpDistanceToMoveHigh) {
    LARGE_INTEGER offset;
    LARGE_INTEGER retval;

    offset.LowPart = lDistanceToMove;
    offset.HighPart = *lpDistanceToMoveHigh;

    retval.QuadPart = lseek64((handle_t) hFile, offset.QuadPart, dwMoveMethod);
    if (retval.QuadPart < 0) {
      rc = -1;
    } else {
      *lpDistanceToMoveHigh = retval.HighPart;
      rc = retval.LowPart;
    }
  } else {
    rc = lseek((handle_t) hFile, lDistanceToMove, dwMoveMethod);
    if (rc < 0) rc = -1;
  }

  return rc;
}

BOOL WINAPI SetFileTime(
    HANDLE hFile,
    CONST FILETIME *lpCreationTime,
    CONST FILETIME *lpLastAccessTime,
    CONST FILETIME *lpLastWriteTime) {
  struct utimbuf times;
  int rc;

  TRACE("SetFileTime");

  if (lpCreationTime != NULL) {
    times.ctime = (time_t)(((*(__int64 *) lpCreationTime) - EPOC) / SECTIMESCALE);
  } else {
    times.ctime = -1;
  }

  if (lpLastAccessTime != NULL) {
    times.actime = (time_t)(((*(__int64 *) lpLastAccessTime) - EPOC) / SECTIMESCALE);
  } else {
    times.actime = -1;
  }

  if (lpLastWriteTime != NULL) {
    times.modtime = (time_t)(((*(__int64 *) lpLastWriteTime) - EPOC) / SECTIMESCALE);
  } else {
    times.modtime = -1;
  }

  rc = futime((handle_t) hFile, &times);
  if (rc < 0) return FALSE;

  return TRUE;
}

BOOL WINAPI SetHandleInformation(
    HANDLE hObject,
    DWORD dwMask,
    DWORD dwFlags) {
  TRACE("SetHandleInformation");
  // Ignored, only used for setting handle inheritance
  //syslog(LOG_DEBUG, "warning: SetHandleInformation ignored");
  return TRUE;
}

BOOL WINAPI SetThreadContext(
    HANDLE hThread,
    CONST CONTEXT *lpContext) {
  struct context ctxt;

  TRACE("SetThreadContext");

  if (lpContext->ContextFlags != (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS)) {
    syslog(LOG_WARNING, "kernel32: incomplete context in SetContext, flags %lx", lpContext->ContextFlags); 
  }

  memset(&ctxt, 0, sizeof(struct context));
  convert_from_win32_context(&ctxt, (CONTEXT *) lpContext);
  if (setcontext((handle_t) hThread, &ctxt) < 0) return FALSE;

  return TRUE;
}

BOOL WINAPI SetThreadPriority(
    HANDLE hThread,
    int nPriority) {
  int prio;

  TRACE("SetThreadPriority");

  // Set new thread priority based on win32 normal priority class scheme
  switch (nPriority) {
    case THREAD_PRIORITY_IDLE:
      prio = 1;
      break;

    case THREAD_PRIORITY_LOWEST:
      prio = 6;
      break;

    case THREAD_PRIORITY_BELOW_NORMAL:
      prio = 7;
      break;

    case THREAD_PRIORITY_NORMAL:
      prio = 8;
      break;

    case THREAD_PRIORITY_ABOVE_NORMAL:
      prio = 9;
      break;

    case THREAD_PRIORITY_HIGHEST:
      prio = 10;
      break;

    case THREAD_PRIORITY_TIME_CRITICAL:
      prio = 15;
      break;

    default:
      return FALSE;
  }

  if (setprio((handle_t) hThread, prio) < 0) return FALSE;
  return TRUE;
}

LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter(
    LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter) {
  LPTOP_LEVEL_EXCEPTION_FILTER oldfilter = topexcptfilter;

  TRACE("SetUnhandledExceptionFilter");
  
  topexcptfilter = lpTopLevelExceptionFilter;
  return oldfilter;
}

VOID WINAPI Sleep(
    DWORD dwMilliseconds) {
  TRACEX("Sleep");
  msleep(dwMilliseconds);
}

DWORD WINAPI SuspendThread(
    HANDLE hThread) {
  TRACE("SuspendThread");
  return suspend((handle_t) hThread);
}

BOOL WINAPI SystemTimeToFileTime(
    CONST SYSTEMTIME *lpSystemTime,
    LPFILETIME lpFileTime) {
  struct tm tm;
  time_t time;

  TRACE("SystemTimeToFileTime");
  memset(&tm, 0, sizeof(tm));
  tm.tm_year = lpSystemTime->wYear - 1900;
  tm.tm_mon = lpSystemTime->wMonth - 1;
  tm.tm_mday = lpSystemTime->wDay;
  tm.tm_hour = lpSystemTime->wHour;
  tm.tm_min = lpSystemTime->wMinute;
  tm.tm_sec = lpSystemTime->wSecond;

  time = mktime(&tm);
    
  *(unsigned __int64 *) lpFileTime = ((unsigned __int64) time * 1000000 + (unsigned __int64) (lpSystemTime->wMilliseconds) * 1000) * MICROTIMESCALE + EPOC;
  return TRUE;
}

BOOL WINAPI TerminateProcess(
    HANDLE hProcess,
    UINT uExitCode) {
  TRACE("TerminateProcess");
  panic("TerminateProcess not implemented");
  return FALSE;
}

DWORD WINAPI TlsAlloc(VOID) {
  TRACE("TlsAlloc");
  return tlsalloc();
}

BOOL WINAPI TlsFree(
    DWORD dwTlsIndex) {
  TRACE("TlsFree");
  tlsfree(dwTlsIndex);
  return TRUE;
}

LPVOID WINAPI TlsGetValue(
    DWORD dwTlsIndex) {
  TRACEX("TlsGetValue");
  return tlsget(dwTlsIndex);
}

BOOL WINAPI TlsSetValue(
    DWORD dwTlsIndex,
    LPVOID lpTlsValue) {
  TRACEX("TlsSetValue");
  tlsset(dwTlsIndex, lpTlsValue);
  return TRUE;
}

BOOL WINAPI TryEnterCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection) {
  TRACE("TryEnterCriticalSection");
  panic("TryEnterCriticalSection not implemented");
  return TRUE;
}

BOOL WINAPI UnlockFile(
    HANDLE hFile,
    DWORD dwFileOffsetLow,
    DWORD dwFileOffsetHigh,
    DWORD nNumberOfBytesToUnlockLow,
    DWORD nNumberOfBytesToUnlockHigh) {
  panic("UnlockFile not implemented");
  return FALSE;
}

BOOL WINAPI UnlockFileEx(
    HANDLE hFile,
    DWORD dwReserved,
    DWORD nNumberOfBytesToUnlockLow,
    DWORD nNumberOfBytesToUnlockHigh,
    LPOVERLAPPED lpOverlapped) {
  panic("UnlockFileEx not implemented");
  return FALSE;
}

BOOL WINAPI UnmapViewOfFile(
    LPCVOID lpBaseAddress) {
  TRACE("UnmapViewOfFile");
  panic("UnmapViewOfFile not implemented");
  return FALSE;
}

LPVOID WINAPI VirtualAlloc(
    LPVOID lpAddress,
    SIZE_T dwSize,
    DWORD flAllocationType,
    DWORD flProtect) {
  void *addr;
  int n;
  //struct tib *tib = gettib();

  TRACE("VirtualAlloc");

  // Do not allow JVM to mess with the stack (this is a hack!)
  //if (lpAddress >= tib->stackbase && lpAddress < tib->stacktop) return lpAddress;

  addr = vmalloc(lpAddress, dwSize, flAllocationType | MEM_ALIGN64K, flProtect, 'VALO');

  //syslog(LOG_DEBUG, "VirtualAlloc %p %dKB (%p,%p) -> %p", lpAddress, dwSize / K, flAllocationType, flProtect, addr);

  if (addr != NULL && (flAllocationType & MEM_RESERVE) != 0) {
    for (n = 0; n < MAX_VADS; n++) if (vads[n].addr == NULL) break;
    if (n != MAX_VADS) {
      vads[n].addr = addr;
      vads[n].size = dwSize;
    } else {
      panic("no vad for VirtualAlloc");
    }
  }

  return addr;
}

BOOL WINAPI VirtualFree(
    LPVOID lpAddress,
    SIZE_T dwSize,
    DWORD dwFreeType) {
  int n;
  int rc;

  TRACE("VirtualFree");
  if (lpAddress != NULL && (dwFreeType & MEM_RELEASE) != 0 && dwSize == 0) {
    for (n = 0; n < MAX_VADS; n++) if (vads[n].addr == lpAddress) break;
    if (n != MAX_VADS) {
      vads[n].addr = NULL;
      dwSize = vads[n].size;
    } else {
      syslog(LOG_WARNING, "warning: vad not found for VitualFree");
    }
  }

  rc = vmfree(lpAddress, dwSize, dwFreeType);

  //syslog(LOG_DEBUG, "VirtualFree %p %d bytes (%p) -> %d", lpAddress, dwSize, dwFreeType, rc);

  return rc == 0;
}

BOOL WINAPI VirtualProtect(
    LPVOID lpAddress,
    SIZE_T dwSize,
    DWORD flNewProtect,
    LPDWORD lpflOldProtect) {
  int rc;
  //struct tib *tib = gettib();

  TRACE("VirtualProtect");

  // Do not allow JVM to mess with the stack (this is a hack!)
  //if (lpAddress >= tib->stackbase && lpAddress < tib->stacktop) return TRUE;

  rc = vmprotect(lpAddress, dwSize, flNewProtect);
  //syslog(LOG_DEBUG, "VirtualProtect %p %dKB %d %d", lpAddress, dwSize / K, flNewProtect, rc);
  if (rc < 0) return FALSE;

  if (lpflOldProtect) *lpflOldProtect = rc;
  return TRUE;
}

DWORD WINAPI VirtualQuery(
    LPCVOID lpAddress,
    PMEMORY_BASIC_INFORMATION lpBuffer,
    SIZE_T dwLength) {
  struct tib *tib = gettib();

  TRACE("VirtualQuery");

  // Used in win32\hpi\src\threads_md.c to check for stack guard pages
  // In JVM 1.4 also used for checking stack size in hotspot\src\os\win32\vm\os_win32.cpp
  if (lpAddress >= tib->stacklimit && lpAddress < tib->stacktop) {
    // Handle stack case
    //syslog(LOG_DEBUG, "VirtualQuery %p (in stack %p %p %p)", lpAddress, tib->stackbase, tib->stacklimit, tib->stacktop);
    lpBuffer->BaseAddress = tib->stacklimit;
    lpBuffer->RegionSize = (char *) tib->stacktop - (char *) tib->stacklimit;
    lpBuffer->AllocationBase = tib->stackbase;
    lpBuffer->Protect = PAGE_READWRITE;
  } else {
    //syslog(LOG_DEBUG, "VirtualQuery %p (not in stack!!!!)", lpAddress);
  
    // Return dummy result
    lpBuffer->BaseAddress = (void *) ((unsigned long) lpAddress & ~(PAGESIZE - 1));
    lpBuffer->Protect = PAGE_READWRITE;
  }

  return dwLength;
}

DWORD WINAPI WaitForMultipleObjects(
    DWORD nCount,
    CONST HANDLE *lpHandles,
    BOOL bWaitAll,
    DWORD dwMilliseconds) {
  int rc;

  TRACE("WaitForMultipleObjects");

  if (bWaitAll) {
    rc = waitall((handle_t *) lpHandles, nCount, dwMilliseconds);
  } else {
    rc = waitany((handle_t *) lpHandles, nCount, dwMilliseconds);
  }

  if (rc < 0) {
    if (errno == ETIMEOUT) {
      return WAIT_TIMEOUT;
    } else {
      return WAIT_FAILED;
    }
  }

  return rc;
}

DWORD WINAPI WaitForSingleObject(
    HANDLE hHandle,
    DWORD dwMilliseconds) {
  int rc;

  TRACEX("WaitForSingleObject");
  rc = waitone((handle_t) hHandle, dwMilliseconds);

  if (rc < 0) {
    if (errno == ETIMEOUT) {
      return WAIT_TIMEOUT;
    } else {
      return WAIT_FAILED;
    }
  }

  return rc;
}

int WINAPI WideCharToMultiByte(
    UINT CodePage,
    DWORD dwFlags,
    LPCWSTR lpWideCharStr,
    int cchWideChar,
    LPSTR lpMultiByteStr,
    int cbMultiByte,
    LPCSTR lpDefaultChar,
    LPBOOL lpUsedDefaultChar) {
  TRACE("WideCharToMultiByte");
  panic("WideCharToMultiByte not implemented");
  return 0;
}

BOOL WINAPI WriteFile(
    HANDLE hFile,
    LPCVOID lpBuffer,
    DWORD nNumberOfBytesToWrite,
    LPDWORD lpNumberOfBytesWritten,
    LPOVERLAPPED lpOverlapped) {
  int rc;

  TRACE("WriteFile");
  if (lpOverlapped) panic("Overlapped I/O not implemented in WriteFile");

  rc = write((handle_t) hFile, lpBuffer, nNumberOfBytesToWrite);
  if (rc < 0) return FALSE;

  *lpNumberOfBytesWritten = rc;
  return TRUE;
}

int __stdcall DllMain(handle_t hmod, int reason, void *reserved) {
  if (reason == DLL_PROCESS_ATTACH) {
    // Register global WIN32 handler for all signals
    struct peb *peb = gettib()->peb;
    old_globalhandler = peb->globalhandler;
    peb->globalhandler = win32_globalhandler;
  }

  return TRUE;
}