pike.git / src / fdlib.c

version» Context lines:

pike.git/src/fdlib.c:1:   /*   || This file is part of Pike. For copyright information see COPYRIGHT.   || Pike is distributed under GPL, LGPL and MPL. See the file COPYING   || for more information.   */    + /* +  * This file contains wrappers for NT system calls that +  * implement the corresponding POSIX system calls. +  * One major difference compared to the native wrappers +  * in crt.lib is that filenames are assumed to be UTF-8- +  * encoded, with a fallback to Latin-1. +  * +  * The UTF-8 filenames are recoded to UTF16 and used +  * with the wide versions of the NT system calls. +  */ +    #include "global.h"   #include "fdlib.h" -  + #include "backend.h"   #include "pike_error.h"   #include <math.h> - #include <ctype.h> + #include "port.h" + #include <assert.h>      #ifdef HAVE_DIRECT_H   #include <direct.h>   #endif      #if defined(HAVE_WINSOCK_H)    -  + #include <shlobj.h> + #include <objbase.h> +  + #include <wchar.h> + #include <io.h> +    #include <time.h>      /* Old versions of the headerfiles don't have this constant... */   #ifndef INVALID_SET_FILE_POINTER   #define INVALID_SET_FILE_POINTER ((DWORD)-1)   #endif    -  + #ifndef ENOTSOCK + #define ENOTSOCK WSAENOTSOCK + #endif +    #include "threads.h"    - static MUTEX_T fd_mutex; + /* Mutex protecting da_handle, fd_type, fd_busy and first_free_handle. */ + static PIKE_MUTEX_T fd_mutex;    - HANDLE da_handle[MAX_OPEN_FILEDESCRIPTORS]; - int fd_type[MAX_OPEN_FILEDESCRIPTORS]; + /* Condition indicating that some field of fd_busy has been cleared. */ + static COND_T fd_cond; +  + /* HANDLEs corresponding to the fd of the same index. */ + HANDLE da_handle[FD_SETSIZE]; +  + /* Next free fd when >= 0. +  * +  * When < 0: +  * FD_NO_MORE_FREE (-1) +  * End marker for the free list. +  * +  * FD_FILE (-2) +  * Handle from CreateFileW(). +  * +  * FD_CONSOLE (-3) +  * Handle from GetStdHandle(). +  * +  * FD_SOCKET (-4) +  * socket_fd from socket(). +  * +  * FD_PIPE (-5) +  * Handle from CreatePipe(). +  */ + int fd_type[FD_SETSIZE]; +  + /* Indication whether the corresponding fd is in use. */ + int fd_busy[FD_SETSIZE]; +  + /* Next free fd. FD_NO_MORE_FREE (-1) if all fds allocated. */   int first_free_handle;    -  + /* The root desktop folder. */ + static LPSHELLFOLDER isf = NULL; +    /* #define FD_DEBUG */   /* #define FD_STAT_DEBUG */      #ifdef FD_DEBUG   #define FDDEBUG(X) X   #else   #define FDDEBUG(X)   #endif      #ifdef FD_STAT_DEBUG   #define STATDEBUG(X) X   #else   #define STATDEBUG(X) do {} while (0)   #endif    - /* _dosmaperr is internal but still exported in the dll interface. */ - __declspec(dllimport) void __cdecl _dosmaperr(unsigned long); +     -  + #ifdef USE_DL_MALLOC + /* NB: We use some calls that allocate memory with the libc malloc(). */ + static inline void libc_free(void *ptr); + #else + #define libc_free(PTR) free(PTR) + #endif +  + /* Provide and use our own implementation of _dosmaperr(), +  * as it isn't provided by all CRT runtimes; eg both MinGW +  * and msvcr100.dll seem to not implement _dosmaperr()... +  */ + #undef _dosmaperr + #define _dosmaperr pike_dosmaperr +  + /* Mapping of Windows error codes to POSIX error codes. +  * +  * This table was generated by correlating the behavior of +  * _dosmaperr() with the <WinError.h> and <errno.h> header files. +  * +  * Entries not in the table map to EINVAL. +  */ + static const unsigned long pike_doserrtab[][2] = { +  { ERROR_INVALID_FUNCTION, EINVAL, }, /* 1 */ +  { ERROR_FILE_NOT_FOUND, ENOENT, }, /* 2 */ +  { ERROR_PATH_NOT_FOUND, ENOENT, }, /* 3 */ +  { ERROR_TOO_MANY_OPEN_FILES, EMFILE, }, /* 4 */ +  { ERROR_ACCESS_DENIED, EACCES, }, /* 5 */ +  { ERROR_INVALID_HANDLE, EBADF, }, /* 6 */ +  { ERROR_ARENA_TRASHED, ENOMEM, }, /* 7 */ +  { ERROR_NOT_ENOUGH_MEMORY, ENOMEM, }, /* 8 */ +  { ERROR_INVALID_BLOCK, ENOMEM, }, /* 9 */ +  { ERROR_BAD_ENVIRONMENT, E2BIG, }, /* 10 */ +  { ERROR_BAD_FORMAT, ENOEXEC, }, /* 11 */ +  { ERROR_INVALID_ACCESS, EINVAL, }, /* 12 */ +  { ERROR_INVALID_DATA, EINVAL, }, /* 13 */ +  { ERROR_INVALID_DRIVE, ENOENT, }, /* 15 */ +  { ERROR_CURRENT_DIRECTORY, EACCES, }, /* 16 */ +  { ERROR_NOT_SAME_DEVICE, EXDEV, }, /* 17 */ +  { ERROR_NO_MORE_FILES, ENOENT, }, /* 18 */ +  { ERROR_WRITE_PROTECT, EACCES, }, /* 19 */ +  { ERROR_BAD_UNIT, EACCES, }, /* 20 */ +  { ERROR_NOT_READY, EACCES, }, /* 21 */ +  { ERROR_BAD_COMMAND, EACCES }, /* 22 */ +  { ERROR_CRC, EACCES }, /* 23 */ +  { ERROR_BAD_LENGTH, EACCES }, /* 24 */ +  { ERROR_NOT_DOS_DISK, EACCES }, /* 26 */ +  { ERROR_SECTOR_NOT_FOUND, EACCES }, /* 27 */ +  { ERROR_OUT_OF_PAPER, EACCES }, /* 28 */ +  { ERROR_WRITE_FAULT, EACCES }, /* 29 */ +  { ERROR_READ_FAULT, EACCES }, /* 30 */ +  { ERROR_GEN_FAILURE, EACCES }, /* 31 */ +  { ERROR_SHARING_VIOLATION, EACCES }, /* 32 */ +  { ERROR_LOCK_VIOLATION, EACCES, }, /* 33 */ +  { ERROR_WRONG_DISK, EACCES }, /* 34 */ +  { ERROR_SHARING_BUFFER_EXCEEDED, EACCES }, /* 36 */ +  { ERROR_BAD_NETPATH, ENOENT, }, /* 53 */ +  { ERROR_NETWORK_ACCESS_DENIED, EACCES, }, /* 65 */ +  { ERROR_BAD_NET_NAME, ENOENT, }, /* 67 */ +  { ERROR_FILE_EXISTS, EEXIST, }, /* 80 */ +  { ERROR_CANNOT_MAKE, EACCES, }, /* 82 */ +  { ERROR_FAIL_I24, EACCES, }, /* 83 */ +  { ERROR_INVALID_PARAMETER, EINVAL, }, /* 87 */ +  { ERROR_NO_PROC_SLOTS, EAGAIN, }, /* 89 */ +  { ERROR_DRIVE_LOCKED, EACCES, }, /* 108 */ +  { ERROR_BROKEN_PIPE, EPIPE, }, /* 109 */ +  { ERROR_DISK_FULL, ENOSPC, }, /* 112 */ +  { ERROR_INVALID_TARGET_HANDLE, EBADF, }, /* 114 */ +  { ERROR_INVALID_HANDLE, EINVAL, }, /* 124 */ +  { ERROR_WAIT_NO_CHILDREN, ECHILD, }, /* 128 */ +  { ERROR_CHILD_NOT_COMPLETE, ECHILD, }, /* 129 */ +  { ERROR_DIRECT_ACCESS_HANDLE, EBADF, }, /* 130 */ +  { ERROR_NEGATIVE_SEEK, EINVAL, }, /* 131 */ +  { ERROR_SEEK_ON_DEVICE, EACCES, }, /* 132 */ +  { ERROR_DIR_NOT_EMPTY, ENOTEMPTY, }, /* 145 */ +  { ERROR_NOT_LOCKED, EACCES, }, /* 158 */ +  { ERROR_BAD_PATHNAME, ENOENT, }, /* 161 */ +  { ERROR_MAX_THRDS_REACHED, EAGAIN, }, /* 164 */ +  { ERROR_LOCK_FAILED, EACCES, }, /* 167 */ +  { ERROR_ALREADY_EXISTS, EEXIST, }, /* 183 */ +  { ERROR_INVALID_STARTING_CODESEG, ENOEXEC }, /* 188 */ +  { ERROR_INVALID_STACKSEG, ENOEXEC }, /* 189 */ +  { ERROR_INVALID_MODULETYPE, ENOEXEC }, /* 190 */ +  { ERROR_INVALID_EXE_SIGNATURE, ENOEXEC }, /* 191 */ +  { ERROR_EXE_MARKED_INVALID, ENOEXEC }, /* 192 */ +  { ERROR_BAD_EXE_FORMAT, ENOEXEC }, /* 193 */ +  { ERROR_ITERATED_DATA_EXCEEDS_64k, ENOEXEC }, /* 194 */ +  { ERROR_INVALID_MINALLOCSIZE, ENOEXEC }, /* 195 */ +  { ERROR_DYNLINK_FROM_INVALID_RING, ENOEXEC }, /* 196 */ +  { ERROR_IOPL_NOT_ENABLED, ENOEXEC }, /* 197 */ +  { ERROR_INVALID_SEGDPL, ENOEXEC }, /* 198 */ +  { ERROR_AUTODATASEG_EXCEEDS_64k, ENOEXEC }, /* 199 */ +  { ERROR_RING2SEG_MUST_BE_MOVABLE, ENOEXEC }, /* 200 */ +  { ERROR_RELOC_CHAIN_XEEDS_SEGLIM, ENOEXEC }, /* 201 */ +  { ERROR_INFLOOP_IN_RELOC_CHAIN, ENOEXEC }, /* 202 */ +  { ERROR_FILENAME_EXCED_RANGE, ENOENT, }, /* 206 */ +  { ERROR_NESTING_NOT_ALLOWED, EAGAIN, }, /* 215 */ +  { ERROR_NOT_ENOUGH_QUOTA, ENOMEM, }, /* 1816 */ + }; +  + static inline void _dosmaperr(unsigned long err) + { +  unsigned int l = 0, h = NELEM(pike_doserrtab); +  while (l < h) { +  unsigned int m = (l+h)>>1; +  unsigned long e = pike_doserrtab[m][0]; +  +  if (e == err) { +  errno = pike_doserrtab[m][1]; +  return; +  } +  +  if (e < err) { +  l = m+1; +  } else { +  h = m; +  } +  } +  errno = EINVAL; + } +    PMOD_EXPORT void set_errno_from_win32_error (unsigned long err)   { -  +  FDDEBUG(fprintf(stderr, "Win32Error: %ld\n", err)); +     /* _dosmaperr handles the common I/O errors from GetLastError, but    * not the winsock codes. */    _dosmaperr (err);       /* Let through any error that _dosmaperr didn't map, and which    * doesn't conflict with the errno range in msvcrt. */    if (errno == EINVAL && err > STRUNCATE /* 80 */) {    switch (err) {    /* Special cases for the error codes above STRUNCATE that    * _dosmaperr actively map to EINVAL. */    case ERROR_INVALID_PARAMETER: /* 87 */    case ERROR_INVALID_HANDLE: /* 124 */    case ERROR_NEGATIVE_SEEK: /* 131 */    return; -  +  case ERROR_DIRECTORY: /* 267 */ +  errno = ENOTDIR; /* [Bug 7271] */ +  return;    }       /* FIXME: This lets most winsock codes through as-is, e.g.    * WSAEWOULDBLOCK (10035) instead of EAGAIN. There are symbolic    * constants for all of them in the System module, but the    * duplicate values still complicates code on the Windows    * platform, so they ought to be mapped to the basic codes where    * possible, I think. That wouldn't be compatible, though.    * /mast */       errno = err;    }   }    -  + /* Dynamic load of functions that don't exist in all Windows versions. */ +  + #undef NTLIB + #define NTLIB(LIB) \ +  static HINSTANCE PIKE_CONCAT3(Pike_NT_, LIB, _lib); +  + #undef NTLIBFUNC + #define NTLIBFUNC(LIB, RET, NAME, ARGLIST) \ +  typedef RET (WINAPI * PIKE_CONCAT3(Pike_NT_, NAME, _type)) ARGLIST; \ +  static PIKE_CONCAT3(Pike_NT_, NAME, _type) PIKE_CONCAT(Pike_NT_, NAME) +  + #include "ntlibfuncs.h" +    #define ISSEPARATOR(a) ((a) == '\\' || (a) == '/')      #ifdef PIKE_DEBUG   static int IsValidHandle(HANDLE h)   {   #ifndef __GNUC__    __try {    HANDLE ret;    if(DuplicateHandle(GetCurrentProcess(),    h,
pike.git/src/fdlib.c:108: Inside #if defined(PIKE_DEBUG)
  }      PMOD_EXPORT HANDLE CheckValidHandle(HANDLE h)   {    if(!IsValidHandle(h))    Pike_fatal("Invalid handle!\n");    return h;   }   #endif    + /* Used by signal_handler.c:get_inheritable_handle(). */ + int fd_to_handle(int fd, int *type, HANDLE *handle) + { +  int ret = -1; +  +  FDDEBUG(fprintf(stderr, "fd_to_handle(%d, %p, %p)...\n", fd, type, handle)); +  +  if (fd >= FD_NO_MORE_FREE) { +  mt_lock(&fd_mutex); +  while (fd_busy[fd]) { +  FDDEBUG(fprintf(stderr, "fd %d is busy; waiting...\n", fd)); +  co_wait(&fd_cond, &fd_mutex); +  } +  if (fd_type[fd] < 0) { +  FDDEBUG(fprintf(stderr, "fd %d is valid.\n", fd)); +  if (type) *type = fd_type[fd]; +  if (handle) *handle = da_handle[fd]; +  fd_busy[fd] = 1; +  ret = 0; +  FDDEBUG(fprintf(stderr, "fd %d ==> handle: %ld (%d)\n", +  fd, (long)da_handle[fd], fd_type[fd])); +  } else { +  FDDEBUG(fprintf(stderr, "fd %d is invalid.\n", fd)); +  errno = EBADF; +  } +  mt_unlock(&fd_mutex); +  } else { +  FDDEBUG(fprintf(stderr, "fd %d is invalid (range).\n", fd)); +  errno = EBADF; +  } +  +  return ret; + } +  + static int fd_to_socket(int fd, SOCKET *socket) + { +  int ret = -1; +  +  FDDEBUG(fprintf(stderr, "fd_to_socket(%d, %p)...\n", fd, socket)); +  +  if (fd >= FD_NO_MORE_FREE) { +  mt_lock(&fd_mutex); +  while (fd_busy[fd]) { +  FDDEBUG(fprintf(stderr, "fd %d is busy; waiting...\n", fd)); +  co_wait(&fd_cond, &fd_mutex); +  } +  if (fd_type[fd] == FD_SOCKET) { +  FDDEBUG(fprintf(stderr, "fd %d is a valid socket.\n", fd)); +  if (socket) *socket = (SOCKET)da_handle[fd]; +  fd_busy[fd] = 1; +  ret = 0; +  } else if (fd_type[fd] < 0) { +  FDDEBUG(fprintf(stderr, "fd %d is not a socket.\n", fd)); +  errno = ENOTSOCK; +  } else { +  FDDEBUG(fprintf(stderr, "fd %d is invalid.\n", fd)); +  errno = EBADF; +  } +  mt_unlock(&fd_mutex); +  } else { +  FDDEBUG(fprintf(stderr, "fd %d is invalid (range).\n", fd)); +  errno = EBADF; +  } +  +  return ret; + } +  + static int allocate_fd(int type, HANDLE handle) + { +  int fd; +  +  if (type >= FD_NO_MORE_FREE) { +  errno = EINVAL; +  return -1; +  } +  +  mt_lock(&fd_mutex); +  +  fd = first_free_handle; +  +  if (fd >= 0) { +  FDDEBUG(fprintf(stderr, "allocating fd %d (busy: %d)\n", fd, fd_busy[fd])); +  +  assert(!fd_busy[fd]); +  assert(fd_type[fd] >= -1); +  +  first_free_handle = fd_type[fd]; +  fd_type[fd] = type; +  da_handle[fd] = handle; +  fd_busy[fd] = 1; +  } else { +  FDDEBUG(fprintf(stderr, "All fds are allocated.\n")); +  errno = EMFILE; +  } +  +  mt_unlock(&fd_mutex); +  +  return fd; + } +  + static int reallocate_fd(int fd, int type, HANDLE handle) + { +  int prev_fd; +  if ((fd < 0) || (fd >= FD_SETSIZE) || (type >= FD_NO_MORE_FREE)) { +  errno = EINVAL; +  return -1; +  } +  +  FDDEBUG(fprintf(stderr, "reallocate_fd(%d, %d, %p)...\n", +  fd, type, (void *)handle)); +  +  mt_lock(&fd_mutex); +  +  while (fd_busy[fd]) { +  FDDEBUG(fprintf(stderr, "fd %d is busy; waiting...\n", fd)); +  co_wait(&fd_cond, &fd_mutex); +  } +  +  if (fd_type[fd] < FD_NO_MORE_FREE) { +  FDDEBUG(fprintf(stderr, "fd %d was in use; reallocating.\n", fd)); +  goto reallocate; +  } +  +  FDDEBUG(fprintf(stderr, "fd %d was not i use. Allocating.\n", fd)); +  +  prev_fd = first_free_handle; +  +  if (prev_fd == fd) { +  assert(!fd_busy[fd]); +  assert(fd_type[fd] >= -1); +  first_free_handle = fd_type[fd]; +  goto found; +  } +  +  while (prev_fd != FD_NO_MORE_FREE) { +  if (fd_type[prev_fd] == fd) { +  /* Found. */ +  assert(!fd_busy[fd]); +  assert(fd_type[fd] >= -1); +  fd_type[prev_fd] = fd_type[fd]; +  goto found; +  } +  prev_fd = fd_type[prev_fd]; +  } +  +  FDDEBUG(fprintf(stderr, "fd %d was not on the free list!\n", fd)); +  +  errno = EMFILE; +  mt_unlock(&fd_mutex); +  return -1; +  +  reallocate: +  if (fd_type[fd] == FD_SOCKET) { +  FDDEBUG(fprintf(stderr, "Closing socket that was fd %d.\n", fd)); +  closesocket((SOCKET)da_handle[fd]); +  } else { +  FDDEBUG(fprintf(stderr, "Closing handle that was fd %d.\n", fd)); +  CloseHandle(da_handle[fd]); +  } +  +  /* FALLTHRU */ +  found: +  FDDEBUG(fprintf(stderr, "Allocating fd %d.\n", fd)); +  fd_type[fd] = type; +  da_handle[fd] = handle; +  fd_busy[fd] = 1; +  mt_unlock(&fd_mutex); +  return fd; + } +  + /* Used by signal_handler.c:get_inheritable_handle(). */ + void release_fd(int fd) + { +  if ((fd < 0) || (fd >= FD_SETSIZE)) { +  return; +  } +  +  mt_lock(&fd_mutex); +  +  FDDEBUG(fprintf(stderr, "Releasing fd %d. Busy: %d\n", fd, fd_busy[fd])); +  +  assert(fd_busy[fd]); +  +  fd_busy[fd] = 0; +  +  FDDEBUG(fprintf(stderr, "Broadcasting now that fd %d is no longer busy.\n", +  fd)); +  co_broadcast(&fd_cond); +  mt_unlock(&fd_mutex); + } +  + static void free_fd(int fd) + { +  if ((fd < 0) || (fd >= FD_SETSIZE)) { +  return; +  } +  +  mt_lock(&fd_mutex); +  +  FDDEBUG(fprintf(stderr, "Freeing fd %d. Busy: %d\n", fd, fd_busy[fd])); +  +  if (fd_type[fd] < FD_NO_MORE_FREE) { +  assert(fd_busy[fd]); +  +  fd_type[fd] = first_free_handle; +  first_free_handle = fd; +  fd_busy[fd] = 0; +  +  FDDEBUG(fprintf(stderr, "Broadcasting now that fd %d is free.\n", fd)); +  co_broadcast(&fd_cond); +  } else { +  FDDEBUG(fprintf(stderr, "Fd %d is already free!\n", fd)); +  FDDEBUG(fprintf(stderr, "fd_type[%d]: %d\n", fd, fd_type[fd])); +  FDDEBUG(fprintf(stderr, "fd_busy[%d]: %d\n", fd, fd_busy[fd])); +  } +  mt_unlock(&fd_mutex); + } +  + static void set_fd_handle(int fd, HANDLE handle) + { +  if (fd < 0) { +  return; +  } +  +  mt_lock(&fd_mutex); +  assert(fd_busy[fd]); +  +  da_handle[fd] = handle; +  mt_unlock(&fd_mutex); + } +    PMOD_EXPORT char *debug_fd_info(int fd)   {    if(fd<0)    return "BAD";    -  if(fd > MAX_OPEN_FILEDESCRIPTORS) +  if(fd > FD_SETSIZE)    return "OUT OF RANGE";       switch(fd_type[fd])    {    case FD_SOCKET: return "IS SOCKET";    case FD_CONSOLE: return "IS CONSOLE";    case FD_FILE: return "IS FILE";    case FD_PIPE: return "IS PIPE";    default: return "NOT OPEN";    }   }      PMOD_EXPORT int debug_fd_query_properties(int fd, int guess)   { -  switch(fd_type[fd]) +  int type; +  +  FDDEBUG(fprintf(stderr, "fd_query_properties(%d, %d)...\n", fd, guess)); +  +  if (fd_to_handle(fd, &type, NULL) < 0) return 0; +  release_fd(fd); +  +  switch(type)    {    case FD_SOCKET:    return fd_BUFFERED | fd_CAN_NONBLOCK | fd_CAN_SHUTDOWN;       case FD_FILE:    return fd_INTERPROCESSABLE;       case FD_CONSOLE:    return fd_CAN_NONBLOCK | fd_INTERPROCESSABLE;       case FD_PIPE:    return fd_INTERPROCESSABLE | fd_BUFFERED;    default: return 0;    }   }    - void fd_init() + void fd_init(void)   {    int e;    WSADATA wsadata; -  +  OSVERSIONINFO osversion;       mt_init(&fd_mutex); -  +  co_init(&fd_cond);    mt_lock(&fd_mutex); -  +     if(WSAStartup(MAKEWORD(1,1), &wsadata) != 0)    {    Pike_fatal("No winsock available.\n");    }    FDDEBUG(fprintf(stderr,"Using %s\n",wsadata.szDescription));    -  +  FDDEBUG(fprintf(stderr, "FD_SETSIZE: %ld\n", (long)FD_SETSIZE)); +  FDDEBUG(fprintf(stderr, "fd_busy: %ld bytes.\n", (long)sizeof(fd_busy))); +  +  memset(fd_busy, 0, sizeof(fd_busy)); +     fd_type[0] = FD_CONSOLE;    da_handle[0] = GetStdHandle(STD_INPUT_HANDLE);    fd_type[1] = FD_CONSOLE;    da_handle[1] = GetStdHandle(STD_OUTPUT_HANDLE);    fd_type[2] = FD_CONSOLE;    da_handle[2] = GetStdHandle(STD_ERROR_HANDLE);       first_free_handle=3; -  for(e=3;e<MAX_OPEN_FILEDESCRIPTORS-1;e++) +  for(e=3;e<FD_SETSIZE-1;e++)    fd_type[e]=e+1;    fd_type[e]=FD_NO_MORE_FREE;    mt_unlock(&fd_mutex); -  +  +  /* MoveFileEx doesn't exist in W98 and earlier. */ +  /* Correction, it exists but does not work -Hubbe */ +  osversion.dwOSVersionInfoSize = sizeof(osversion); +  if (GetVersionEx(&osversion) && +  (osversion.dwPlatformId != VER_PLATFORM_WIN32s) && /* !win32s */ +  (osversion.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS)) /* !win9x */ +  { + #undef NTLIB + #define NTLIB(LIBNAME) \ +  PIKE_CONCAT3(Pike_NT_, LIBNAME, _lib) = \ +  LoadLibrary(TOSTR(LIBNAME)) +  + #undef NTLIBFUNC + #define NTLIBFUNC(LIBNAME, RETTYPE, SYMBOL, ARGLIST) do { \ +  if (PIKE_CONCAT3(Pike_NT_, LIBNAME, _lib)) { \ +  PIKE_CONCAT(Pike_NT_, SYMBOL) = \ +  (PIKE_CONCAT3(Pike_NT_, SYMBOL, _type)) \ +  GetProcAddress(PIKE_CONCAT3(Pike_NT_, LIBNAME, _lib), \ +  TOSTR(SYMBOL)); \ +  } \ +  } while(0) + #include "ntlibfuncs.h"    }    - void fd_exit() +  if (SHGetDesktopFolder(&isf) != S_OK) { +  Pike_fatal("fdlib: No desktop folder!\n"); +  } + } +  + void fd_exit(void)   { -  + #undef NTLIB + #define NTLIB(LIBNAME) do { \ +  if (PIKE_CONCAT3(Pike_NT_, LIBNAME, _lib)) { \ +  if (FreeLibrary(PIKE_CONCAT3(Pike_NT_, LIBNAME, _lib))) { \ +  PIKE_CONCAT3(Pike_NT_, LIBNAME, _lib) = NULL; \ +  } \ +  } \ +  } while(0) + #undef NTLIBFUNC + #define NTLIBFUNC(LIBNAME, RETTYPE, SYMBOL, ARGLIST) do { \ +  if (!PIKE_CONCAT3(Pike_NT_, LIBNAME, _lib)) { \ +  PIKE_CONCAT(Pike_NT_, SYMBOL) = NULL; \ +  } \ +  } while(0) + #include "ntlibfuncs.h" +     WSACleanup();    mt_destroy(&fd_mutex);   }    - static INLINE time_t convert_filetime_to_time_t(FILETIME *tmp) + static inline time_t convert_filetime_to_time_t(FILETIME *tmp)   {    /* FILETIME is in 100ns since Jan 01, 1601 00:00 UTC.    *    * Offset to Jan 01, 1970 is thus 0x019db1ded53e8000 * 100ns.    */ - #ifdef INT64 +     return (((INT64) tmp->dwHighDateTime << 32)    + tmp->dwLowDateTime    - 0x019db1ded53e8000) / 10000000; - #else -  double t; -  if (tmp->dwLowDateTime < 0xd53e8000UL) { -  tmp->dwHighDateTime -= 0x019db1dfUL; /* Note: Carry! */ -  tmp->dwLowDateTime += 0x2ac18000UL; /* Note: 2-compl */ -  } else { -  tmp->dwHighDateTime -= 0x019db1deUL; -  tmp->dwLowDateTime -= 0xd53e8000UL; +    } -  t=tmp->dwHighDateTime * pow(2.0,32.0) + (double)tmp->dwLowDateTime; +     -  /* 1s == 10000000 * 100ns. */ -  t/=10000000.0; -  return DO_NOT_WARN((long)floor(t)); - #endif - } -  +    /* The following replaces _stat in MS CRT since it's buggy on ntfs.    * Axel Siebert explains:    *    * On NTFS volumes, the time is stored as UTC, so it's easy to    * calculate the difference to January 1, 1601 00:00 UTC directly.    *    * On FAT volumes, the time is stored as local time, as a heritage    * from MS-DOS days. This means that this local time must be    * converted to UTC first before calculating the FILETIME. During    * this conversion, all Windows versions exhibit the same bug, using
pike.git/src/fdlib.c:377:    * IsUncRoot - returns TRUE if the argument is a UNC name specifying a    * root share. That is, if it is of the form    * \\server\share\. This routine will also return true if    * the argument is of the form \\server\share (no trailing    * slash).    *    * Forward slashes ('/') may be used instead of    * backslashes ('\').    */    - static int IsUncRoot(char *path) + static int IsUncRoot(const p_wchar1 *path)   {    /* root UNC names start with 2 slashes */ -  if ( strlen(path) >= 5 && /* minimum string is "//x/y" */ +  if ( wcslen(path) >= 5 && /* minimum string is "//x/y" */    ISSEPARATOR(path[0]) &&    ISSEPARATOR(path[1]) )    { -  char * p = path + 2 ; +  const p_wchar1 *p = path + 2 ;       /* find the slash between the server name and share name */    while ( *++p )    if ( ISSEPARATOR(*p) )    break ;       if ( *p && p[1] )    {    /* is there a further slash? */    while ( *++p )
pike.git/src/fdlib.c:407:       /* final slash (if any) */    if ( !*p || !p[1])    return 1;    }    }       return 0 ;   }    + PMOD_EXPORT p_wchar1 *pike_dwim_utf8_to_utf16(const p_wchar0 *str) + { +  /* NB: Maximum expansion factor is 2. +  * +  * UTF8 UTF16 Range Expansion +  * 1 byte 2 bytes U000000 - U00007f 2 +  * 2 bytes 2 bytes U000080 - U0007ff 1 +  * 3 bytes 2 bytes U000800 - U00ffff 0.67 +  * 4 bytes 4 bytes U010000 - U10ffff 1 +  * +  * NB: Some extra padding at the end for NUL and adding +  * of terminating slashes, etc. +  */ +  size_t len = strlen(str); +  p_wchar1 *res = malloc((len + 4) * sizeof(p_wchar1)); +  size_t i = 0, j = 0; +  +  if (!res) { +  return NULL; +  } +  +  while (i < len) { +  p_wchar0 c = str[i++]; +  p_wchar2 ch, mask = 0x3f; +  if (!(c & 0x80)) { +  /* US-ASCII */ +  res[j++] = c; +  continue; +  } +  if (!(c & 0x40)) { +  /* Continuation character. Invalid. Retry as Latin-1. */ +  goto latin1_to_utf16; +  } +  ch = c; +  while (c & 0x40) { +  p_wchar0 cc = str[i++]; +  if ((cc & 0xc0) != 0x80) { +  /* Expected continuation character. */ +  goto latin1_to_utf16; +  } +  ch = ch<<6 | (cc & 0x3f); +  mask |= mask << 5; +  c <<= 1; +  } +  ch &= mask; +  if (ch < 0) { +  goto latin1_to_utf16; +  } +  if (ch < 0x10000) { +  res[j++] = ch; +  continue; +  } +  ch -= 0x10000; +  if (ch >= 0x100000) { +  goto latin1_to_utf16; +  } +  /* Encode with surrogates. */ +  res[j++] = 0xd800 | ch >> 10; +  res[j++] = 0xdc00 | (ch & 0x3ff); +  } +  goto done; +  +  latin1_to_utf16: +  /* DWIM: Assume Latin-1. Just widen the string. */ +  for (j = 0; j < len; j++) { +  res[j] = str[j]; +  } +  +  done: +  res[j++] = 0; /* NUL-termination. */ +  return res; + } +  + PMOD_EXPORT p_wchar0 *pike_utf16_to_utf8(const p_wchar1 *str) + { +  /* NB: Maximum expansion factor is 1.5. +  * +  * UTF16 UTF8 Range Expansion +  * 2 bytes 1 byte U000000 - U00007f 0.5 +  * 2 bytes 2 bytes U000080 - U0007ff 1 +  * 2 bytes 3 bytes U000800 - U00d7ff 1.5 +  * 2 bytes 2 bytes U00d800 - U00dfff 1 +  * 2 bytes 3 bytes U00e000 - U00ffff 1.5 +  * 4 bytes 4 bytes U010000 - U10ffff 1 +  * +  * NB: Some extra padding at the end for NUL and adding +  * of terminating slashes, etc. +  * +  * NB: According to Wikipedia, win32 doesn't enforce correct +  * pairing of surrogates. Encode such code units as-is. +  */ +  size_t i = 0, j = 0; +  size_t sz = 0; +  p_wchar1 c; +  p_wchar0 *ret; +  +  while ((c = str[i++])) { +  sz++; +  if (c < 0x80) continue; +  sz++; +  if (c < 0x0800) continue; + #if 0 +  /* NB: The following is only correct if all surrogates are paired correctly. */ +  if ((c & 0xf800) == 0xd800) { +  /* One half of a surrogate pair. */ +  continue; +  } + #endif /* 0 */ +  sz++; +  } +  sz++; /* NUL termination. */ +  +  ret = malloc(sz); +  if (!ret) return NULL; +  +  for (i = 0; (c = str[i]); i++) { +  if (c < 0x80) { +  ret[j++] = c & 0x7f; +  continue; +  } +  if (c < 0x800) { +  ret[j++] = 0xc0 | (c>>6); +  ret[j++] = 0x80 | (c & 0x3f); +  continue; +  } +  if ((c & 0xf800) == 0xd800) { +  /* Surrogate */ +  if ((c & 0xfc00) == 0xd800) { +  p_wchar2 ch = str[++i]; +  if ((ch & 0xfc00) == 0xdc00) { +  ch = 0x100000 | (ch & 0x3ff) | ((c & 0x3ff)<<10); +  ret[j++] = 0xf0 | (ch >> 18); +  ret[j++] = 0x80 | ((ch >> 12) & 0x3f); +  ret[j++] = 0x80 | ((ch >> 6) & 0x3f); +  ret[j++] = 0x80 | (ch & 0x3f); +  continue; +  } +  /* Invalid surrogate pair. +  * Unget the second code unit. +  */ +  i--; +  } +  /* Invalid or lone surrogate. +  * Encode as-is. +  */ +  } +  ret[j++] = 0xe0 | (c >> 12); +  ret[j++] = 0x80 | ((c >> 6) & 0x3f); +  ret[j++] = 0x80 | (c & 0x3f); +  } +  ret[j++] = 0; +  return ret; + } +    /* Note 1: s->st_mtime is the creation time for non-root directories.    *    * Note 2: Root directories (e.g. C:\) and network share roots (e.g.    * \\server\foo\) have no time information at all. All timestamps are    * set to one year past the start of the epoch for these.    *    * Note 3: s->st_ctime is set to the file creation time. It should    * probably be the last access time to be closer to the unix    * counterpart, but the creation time is admittedly more useful. */ - int debug_fd_stat(const char *file, PIKE_STAT_T *buf) + PMOD_EXPORT int debug_fd_stat(const char *file, PIKE_STAT_T *buf)   { -  ptrdiff_t l = strlen(file); -  char fname[MAX_PATH]; +  ptrdiff_t l; +  p_wchar1 *fname;    int drive; /* current = -1, A: = 0, B: = 1, ... */    HANDLE hFind; -  WIN32_FIND_DATA findbuf; +  WIN32_FIND_DATAW findbuf; +  int exec_bits = 0;    -  if(ISSEPARATOR (file[l-1])) +  /* Note: On NT the following characters are illegal in filenames: +  * \ / : * ? " < > | +  * +  * The first three are valid in paths, so check for the remaining 6. +  */ +  if (strpbrk(file, "*?\"<>|"))    { -  do l--; -  while(l && ISSEPARATOR (file[l])); -  l++; -  if(l+1 > sizeof(fname)) -  { -  errno=EINVAL; +  errno = ENOENT; +  return(-1); +  } +  +  fname = pike_dwim_utf8_to_utf16(file); +  if (!fname) { +  errno = ENOMEM;    return -1;    } -  MEMCPY(fname, file, l); -  fname[l]=0; -  file=fname; -  } +     -  /* don't allow wildcards */ -  if (strpbrk(file, "?*")) +  l = wcslen(fname); +  /* Get rid of terminating slashes. */ +  while (l && ISSEPARATOR(fname[l-1])) { +  fname[--l] = 0; +  } +  if( l==0 )    {    errno = ENOENT; -  return(-1); +  return -1;    }       /* get disk from file */ -  if (file[1] == ':') -  drive = toupper(*file) - 'A'; +  if (fname[1] == ':') +  drive = toupper(*fname) - 'A';    else    drive = -1;    -  +  +  /* Look at the extension to see if a file appears to be executable. */ +  if ((l >= 4) && (fname[l-4] == '.')) { +  char ext[4]; +  int i; +  for (i = 0; i < 3; i++) { +  p_wchar1 c = fname[l + i - 4]; +  ext[i] = (c > 128)?0:tolower(c); +  } +  ext[3] = 0; +  if (!strcmp (ext, "exe") || +  !strcmp (ext, "cmd") || +  !strcmp (ext, "bat") || +  !strcmp (ext, "com")) +  exec_bits = 0111; /* Execute perm for all. */ +  } +     STATDEBUG (fprintf (stderr, "fd_stat %s drive %d\n", file, drive));       /* get info for file */ -  hFind = FindFirstFile(file, &findbuf); +  hFind = FindFirstFileW(fname, &findbuf);       if ( hFind == INVALID_HANDLE_VALUE )    { -  char abspath[_MAX_PATH + 1]; +     UINT drive_type; -  +  p_wchar1 *abspath;       STATDEBUG (fprintf (stderr, "check root dir\n"));       if (!strpbrk(file, ":./\\")) {    STATDEBUG (fprintf (stderr, "no path separators\n")); -  +  free(fname);    errno = ENOENT;    return -1;    }    -  if (!_fullpath( abspath, file, _MAX_PATH ) || +  /* NB: One extra byte for the terminating separator. */ +  abspath = malloc((l + _MAX_PATH + 1 + 1) * sizeof(p_wchar1)); +  if (!abspath) { +  free(fname); +  errno = ENOMEM; +  return -1; +  } +  +  if (!_wfullpath( abspath, fname, l + _MAX_PATH ) ||    /* Neither root dir ('C:\') nor UNC root dir ('\\server\share\'). */ -  (strlen (abspath) > 3 && !IsUncRoot (abspath))) { -  STATDEBUG (fprintf (stderr, "not a root %s\n", abspath)); +  (wcslen(abspath) > 3 && !IsUncRoot (abspath))) { +  STATDEBUG (fprintf (stderr, "not a root %S\n", abspath)); +  free(abspath); +  free(fname);    errno = ENOENT;    return -1;    }    -  STATDEBUG (fprintf (stderr, "abspath: %s\n", abspath)); +  free(fname); +  fname = NULL;    -  l = strlen (abspath); +  STATDEBUG (fprintf (stderr, "abspath: %S\n", abspath)); +  +  l = wcslen(abspath);    if (!ISSEPARATOR (abspath[l - 1])) {    /* Ensure there's a slash at the end or else GetDriveType    * won't like it. */    abspath[l] = '\\';    abspath[l + 1] = 0;    }    -  drive_type = GetDriveType (abspath); +  drive_type = GetDriveTypeW (abspath);    if (drive_type == DRIVE_UNKNOWN || drive_type == DRIVE_NO_ROOT_DIR) {    STATDEBUG (fprintf (stderr, "invalid drive type: %u\n", drive_type)); -  +  free(abspath);    errno = ENOENT;    return -1;    }    -  +  free(abspath); +  abspath = NULL; +     STATDEBUG (fprintf (stderr, "faking root stat\n"));       /* Root directories (e.g. C:\ and \\server\share\) are faked */    findbuf.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;    findbuf.nFileSizeHigh = 0;    findbuf.nFileSizeLow = 0;    findbuf.cFileName[0] = '\0';       /* Don't have any timestamp info, so set it to some date. The    * following is ridiculously complex, but it's compatible with the
pike.git/src/fdlib.c:522:    t.tm_min = 0;    t.tm_sec = 0;    t.tm_isdst = -1;    buf->st_mtime = mktime (&t);    buf->st_atime = buf->st_mtime;    buf->st_ctime = buf->st_mtime;    }    }       else { -  char fstype[50]; +  p_wchar1 fstype[50];    /* Really only need room in this buffer for "FAT" and anything    * longer that begins with "FAT", but since GetVolumeInformation    * has shown to trig unreliable error codes for a too short buffer    * (see below), we allocate ample space. */       BOOL res;    -  if (drive >= 0) { -  char root[4]; /* Room for "X:\" */ -  root[0] = drive + 'A'; -  root[1] = ':', root[2] = '\\', root[3] = 0; -  res = GetVolumeInformation (root, NULL, 0, NULL, NULL,NULL, -  (LPSTR)&fstype, sizeof (fstype)); +  fstype[0] = '-'; +  fstype[1] = 0; +  +  if (l>=3 && fname[1] == ':') { +  /* Construct a string "X:\" in fname. */ +  fname[0] = toupper(fname[0]); +  fname[2] = '\\'; +  fname[3] = 0; +  } else { +  free(fname); +  fname = NULL;    } -  else -  res = GetVolumeInformation (NULL, NULL, 0, NULL, NULL,NULL, -  (LPSTR)&fstype, sizeof (fstype)); +     -  STATDEBUG (fprintf (stderr, "found, vol info: %d, %s\n", -  res, res ? fstype : "-")); +  res = GetVolumeInformationW (fname, NULL, 0, NULL, NULL,NULL, +  fstype, sizeof(fstype)/sizeof(fstype[0]));    -  +  if (fname) { +  free(fname); +  fname = NULL; +  } +  +  STATDEBUG (fprintf (stderr, "found, vol info: %d, %S\n", +  res, fstype)); +     if (!res) {    unsigned long w32_err = GetLastError();    /* Get ERROR_MORE_DATA if the fstype buffer wasn't long enough,    * so let's ignore it. That error is also known to be reported    * as ERROR_BAD_LENGTH in Vista and 7. */    if (w32_err != ERROR_MORE_DATA && w32_err != ERROR_BAD_LENGTH) {    STATDEBUG (fprintf (stderr, "GetVolumeInformation failure: %d\n",    w32_err));    set_errno_from_win32_error (w32_err);    FindClose (hFind);    return -1;    }    }    -  if (res && !strcmp (fstype, "FAT")) { +  if (res && (fstype[0] == 'F') && (fstype[1] == 'A') && (fstype[2] == 'T')) {    if (!fat_filetimes_to_stattimes (&findbuf.ftCreationTime,    &findbuf.ftLastAccessTime,    &findbuf.ftLastWriteTime,    buf)) {    STATDEBUG (fprintf (stderr, "fat_filetimes_to_stattimes failed.\n"));    FindClose (hFind);    return -1;    }    }    else
pike.git/src/fdlib.c:584:       FindClose(hFind);    }       if (findbuf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)    /* Always label a directory as executable. Note that this also    * catches special locations like root dirs and network share    * roots. */    buf->st_mode = S_IFDIR | 0111;    else { -  const char *p; -  buf->st_mode = S_IFREG; -  -  /* Look at the extension to see if a file appears to be executable. */ -  if ((p = strrchr (file, '.')) && strlen (p) == 4) { -  char ext[4]; -  ext[0] = tolower (p[1]); -  ext[1] = tolower (p[2]); -  ext[2] = tolower (p[3]); -  ext[3] = 0; -  if (!strcmp (ext, "exe") || -  !strcmp (ext, "cmd") || -  !strcmp (ext, "bat") || -  !strcmp (ext, "com")) -  buf->st_mode |= 0111; /* Execute perm for all. */ +  buf->st_mode = S_IFREG | exec_bits;    } -  } +        /* The file is read/write unless the read only flag is set. */    if (findbuf.dwFileAttributes & FILE_ATTRIBUTE_READONLY)    buf->st_mode |= 0444; /* Read perm for all. */    else    buf->st_mode |= 0666; /* Read and write perm for all. */       buf->st_nlink = 1; - #ifdef INT64 +     buf->st_size = ((INT64) findbuf.nFileSizeHigh << 32) + findbuf.nFileSizeLow; - #else -  if (findbuf.nFileSizeHigh) -  buf->st_size = MAXDWORD; -  else -  buf->st_size = findbuf.nFileSizeLow; - #endif +        buf->st_uid = buf->st_gid = buf->st_ino = 0; /* unused entries */    buf->st_rdev = buf->st_dev =    (_dev_t) (drive >= 0 ? drive : _getdrive() - 1); /* A=0, B=1, ... */       return(0);   }    -  + PMOD_EXPORT int debug_fd_truncate(const char *file, INT64 len) + { +  p_wchar1 *fname = pike_dwim_utf8_to_utf16(file); +  HANDLE h; +  LONG high; +  +  if (!fname) { +  errno = ENOMEM; +  return -1; +  } +  +  h = CreateFileW(fname, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, +  NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); +  free(fname); +  +  if (h == INVALID_HANDLE_VALUE) { +  errno = GetLastError(); +  return -1; +  } +  +  high = (LONG)(len >> 32); +  len &= (INT64)0xffffffffUL; +  +  if (SetFilePointer(h, (long)len, &high, FILE_BEGIN) == +  INVALID_SET_FILE_POINTER) { +  DWORD err = GetLastError(); +  if (err != NO_ERROR) { +  errno = err; +  CloseHandle(h); +  return -1; +  } +  } +  if (!SetEndOfFile(h)) { +  errno = GetLastError(); +  CloseHandle(h); +  return -1; +  } +  CloseHandle(h); +  return 0; + } +  + PMOD_EXPORT int debug_fd_rmdir(const char *dir) + { +  p_wchar1 *dname = pike_dwim_utf8_to_utf16(dir); +  int ret; +  +  if (!dname) { +  errno = ENOMEM; +  return -1; +  } +  +  ret = _wrmdir(dname); +  +  if (ret && (errno == EACCES)) { +  PIKE_STAT_T st; +  if (!fd_stat(dir, &st) && !(st.st_mode & _S_IWRITE) && +  !_wchmod(dname, st.st_mode | _S_IWRITE)) { +  /* Retry with write permission. */ +  ret = _wrmdir(dname); +  +  if (ret) { +  /* Failed anyway. Restore original permissions. */ +  int err = errno; +  _wchmod(dname, st.st_mode); +  errno = err; +  } +  } +  } +  +  free(dname); +  +  return ret; + } +  + PMOD_EXPORT int debug_fd_unlink(const char *file) + { +  p_wchar1 *fname = pike_dwim_utf8_to_utf16(file); +  int ret; +  +  if (!fname) { +  errno = ENOMEM; +  return -1; +  } +  +  ret = _wunlink(fname); +  +  if (ret && (errno == EACCES)) { +  PIKE_STAT_T st; +  if (!fd_stat(file, &st) && !(st.st_mode & _S_IWRITE) && +  !_wchmod(fname, st.st_mode | _S_IWRITE)) { +  /* Retry with write permission. */ +  ret = _wunlink(fname); +  +  if (ret) { +  /* Failed anyway. Restore original permissions. */ +  int err = errno; +  _wchmod(fname, st.st_mode); +  errno = err; +  } +  } +  } +  +  free(fname); +  +  return ret; + } +  + PMOD_EXPORT int debug_fd_mkdir(const char *dir, int mode) + { +  p_wchar1 *dname = pike_dwim_utf8_to_utf16(dir); +  int ret; +  int mask; +  +  if (!dname) { +  errno = ENOMEM; +  return -1; +  } +  +  mask = _umask(~mode & 0777); +  _umask(~mode & mask & 0777); +  ret = _wmkdir(dname); +  _wchmod(dname, mode & ~mask & 0777); +  _umask(mask); +  +  free(dname); +  +  return ret; + } +  + PMOD_EXPORT int debug_fd_rename(const char *old, const char *new) + { +  p_wchar1 *oname = pike_dwim_utf8_to_utf16(old); +  p_wchar1 *nname; +  int ret; +  +  if (!oname) { +  errno = ENOMEM; +  return -1; +  } +  +  nname = pike_dwim_utf8_to_utf16(new); +  if (!nname) { +  free(oname); +  errno = ENOMEM; +  return -1; +  } +  +  if (Pike_NT_MoveFileExW) { +  int retried = 0; +  PIKE_STAT_T st; +  ret = 0; +  retry: +  if (!Pike_NT_MoveFileExW(oname, nname, MOVEFILE_REPLACE_EXISTING)) { +  DWORD err = GetLastError(); +  if ((err == ERROR_ACCESS_DENIED) && !retried) { +  /* This happens when the destination is an already existing +  * directory. On POSIX this operation is valid if oname and +  * nname are directories, and the nname directory is empty. +  */ +  if (!fd_stat(old, &st) && ((st.st_mode & S_IFMT) == S_IFDIR) && +  !fd_stat(new, &st) && ((st.st_mode & S_IFMT) == S_IFDIR) && +  !_wrmdir(nname)) { +  /* Succeeded in removing the destination. This implies +  * that it was an empty directory. +  * +  * Retry. +  */ +  retried = 1; +  goto retry; +  } +  } +  if (retried) { +  /* Recreate the destination directory that we deleted above. */ +  _wmkdir(nname); +  /* NB: st still contains the flags from the original nname dir. */ +  _wchmod(nname, st.st_mode & 0777); +  } +  ret = -1; +  set_errno_from_win32_error(err); +  } +  } else { +  /* Fall back to rename() for W98 and earlier. Unlike MoveFileEx, +  * it can't move directories between directories. */ +  ret = _wrename(oname, nname); +  } +  +  free(oname); +  free(nname); +  +  return ret; + } +  + PMOD_EXPORT int debug_fd_chdir(const char *dir) + { +  p_wchar1 *dname = pike_dwim_utf8_to_utf16(dir); +  int ret; +  +  if (!dname) { +  errno = ENOMEM; +  return -1; +  } +  +  ret = _wchdir(dname); +  +  free(dname); +  +  return ret; + } +  + PMOD_EXPORT char *debug_fd_get_current_dir_name(void) + { +  /* NB: Windows CRT _wgetcwd() has a special case for buf == NULL, +  * where the buffer is instead allocated with malloc(), and +  * len is the minimum buffer size to allocate. +  */ +  p_wchar1 *utf16buffer = _wgetcwd(NULL, 0); +  p_wchar0 *utf8buffer; +  +  if (!utf16buffer) { +  errno = ENOMEM; +  return NULL; +  } +  +  utf8buffer = pike_utf16_to_utf8(utf16buffer); +  libc_free(utf16buffer); +  +  if (!utf8buffer) { +  errno = ENOMEM; +  return NULL; +  } +  +  return utf8buffer; + } +  + PMOD_EXPORT char *debug_fd_normalize_path(const char *path) + { +  p_wchar1 *pname = pike_dwim_utf8_to_utf16(path); +  p_wchar1 *buffer; +  char *res; +  size_t len; +  PIDLIST_ABSOLUTE idl; +  HRESULT hres; +  +  if (!pname) { +  errno = ENOMEM; +  return NULL; +  } +  +  /* First convert to an absolute path. */ +  len = MAX_PATH; +  do { +  size_t ret; +  +  buffer = malloc(len * sizeof(p_wchar1)); +  if (!buffer) { +  errno = ENOMEM; +  return NULL; +  } +  +  ret = GetFullPathNameW(pname, len, buffer, NULL); +  +  if (!ret) { +  set_errno_from_win32_error(GetLastError()); +  return NULL; +  } +  if (ret*2 < len) break; +  +  /* Buffer too small. Reallocate. +  * NB: We over allocate 100% to be able to reuse the +  * buffer safely with SHGetPathFromIDListW() below. +  */ +  free(buffer); +  len = (ret+1)*2; +  } while(1); +  free(pname); +  +  if ((hres = isf->lpVtbl->ParseDisplayName(isf, NULL, NULL, buffer, +  NULL, &idl, NULL)) != S_OK) { +  free(buffer); +  set_errno_from_win32_error(hres); +  return NULL; +  } +  +  if (!SHGetPathFromIDListW(idl, buffer)) { +  /* FIXME: Use SHGetPathFromIDListExW() (Vista/2008 and later), +  * to ensure that we don't overrun the buffer. +  * It however doesn't seem to be documented how +  * to detect whether it fails due to a too small +  * buffer or for some other reason, so we couldn't +  * use it to grow the res buffer and retry. +  */ +  free(buffer); +  CoTaskMemFree (idl); +  errno = EINVAL; +  return NULL; +  } +  CoTaskMemFree (idl); +  +  /* Remove trailing slashes, except after a drive letter. */ +  len = wcslen(buffer); +  while(len && buffer[len-1]=='\\') { +  len--; +  } +  if (!len || (len == 2 && buffer[len-1] == ':')) { +  buffer[len++] = '\\'; +  } +  buffer[len + 1] = 0; +  +  /* Convert host and share in an UNC path to lowercase since Windows +  * Shell doesn't do that consistently. +  */ +  if (len>1) +  { +  if (buffer[0] == '\\' && buffer[1] == '\\') { +  size_t i; +  int segments; +  p_wchar1 c; +  +  for (i = segments = 2; (c = buffer[i]) && segments; i++) { +  if (c >= 256) continue; +  buffer[i] = tolower(buffer[i]); +  if (c == '\\') { +  segments--; +  } +  } +  } else if ((buffer[1] == ':') && (buffer[0] < 256)) { +  /* Normalize the drive letter to upper-case. */ +  buffer[0] = toupper(buffer[0]); +  } +  } +  +  res = pike_utf16_to_utf8(buffer); +  free(buffer); +  if (!res) { +  errno = ENOMEM; +  return NULL; +  } +  +  return res; + } +    PMOD_EXPORT FD debug_fd_open(const char *file, int open_mode, int create_mode)   {    HANDLE x;    FD fd;    DWORD omode,cmode = 0,amode;    -  ptrdiff_t l = strlen(file); -  char fname[MAX_PATH]; +  ptrdiff_t l; +  p_wchar1 *fname;    -  if(ISSEPARATOR (file[l-1])) +  +  /* Note: On NT the following characters are illegal in filenames: +  * \ / : * ? " < > | +  * +  * The first three are valid in paths, so check for the remaining 6. +  */ +  if (strpbrk(file, "*?\"<>|"))    { -  do l--; -  while(l && ISSEPARATOR (file[l])); -  l++; -  if(l+1 > sizeof(fname)) -  { -  errno=EINVAL; +  /* ENXIO: +  * "The file is a device special file and no corresponding device +  * exists." +  */ +  errno = ENXIO;    return -1;    } -  MEMCPY(fname, file, l); -  fname[l]=0; -  file=fname; +  +  fname = pike_dwim_utf8_to_utf16(file); +  if (!fname) { +  errno = ENOMEM; +  return -1;    }    -  +  l = wcslen(fname); +  +  /* Get rid of terminating slashes. */ +  while (l && ISSEPARATOR(fname[l-1])) { +  fname[--l] = 0; +  } +     omode=0; -  FDDEBUG(fprintf(stderr,"fd_open(%s,0x%x,%o)\n",file,open_mode,create_mode)); -  if(first_free_handle == FD_NO_MORE_FREE) -  { -  errno=EMFILE; +  FDDEBUG(fprintf(stderr, "fd_open(%S, 0x%x, %o)\n", +  fname, open_mode, create_mode)); +  +  fd = allocate_fd(FD_FILE, INVALID_HANDLE_VALUE); +  if (fd < 0) { +  free(fname);    return -1;    }       if(open_mode & fd_RDONLY) omode|=GENERIC_READ;    if(open_mode & fd_WRONLY) omode|=GENERIC_WRITE;       switch(open_mode & (fd_CREAT | fd_TRUNC | fd_EXCL))    {    case fd_CREAT | fd_TRUNC:    cmode=CREATE_ALWAYS;
pike.git/src/fdlib.c:692:    break;    }       if(create_mode & 0222)    {    amode=FILE_ATTRIBUTE_NORMAL;    }else{    amode=FILE_ATTRIBUTE_READONLY;    }    -  x=CreateFile(file, +  x=CreateFileW(fname,    omode,    FILE_SHARE_READ | FILE_SHARE_WRITE,    NULL,    cmode,    amode,    NULL);       -  if(x == DO_NOT_WARN(INVALID_HANDLE_VALUE)) +  if(x == INVALID_HANDLE_VALUE)    {    unsigned long err = GetLastError(); -  +  free(fname);    if (err == ERROR_INVALID_NAME)    /* An invalid name means the file doesn't exist. This is    * consistent with fd_stat, opendir, and unix. */    errno = ENOENT;    else    set_errno_from_win32_error (err);    return -1;    }       SetHandleInformation(x,HANDLE_FLAG_INHERIT|HANDLE_FLAG_PROTECT_FROM_CLOSE,0);    -  mt_lock(&fd_mutex); +  set_fd_handle(fd, x);    -  fd=first_free_handle; -  first_free_handle=fd_type[fd]; -  fd_type[fd]=FD_FILE; -  da_handle[fd] = x; +  release_fd(fd);    -  mt_unlock(&fd_mutex); -  +     if(open_mode & fd_APPEND)    fd_lseek(fd,0,SEEK_END);    -  FDDEBUG(fprintf(stderr,"Opened %s file as %d (%d)\n",file,fd,x)); +  FDDEBUG(fprintf(stderr, "Opened %S file as %d (%d)\n", fname, fd, x));    -  +  free(fname); +     return fd;   }      PMOD_EXPORT FD debug_fd_socket(int domain, int type, int proto)   {    FD fd;    SOCKET s; -  mt_lock(&fd_mutex); -  if(first_free_handle == FD_NO_MORE_FREE) -  { -  mt_unlock(&fd_mutex); -  errno=EMFILE; -  return -1; -  } -  mt_unlock(&fd_mutex); +     -  +  fd = allocate_fd(FD_SOCKET, (HANDLE)INVALID_SOCKET); +  if (fd < 0) return -1; +     s=socket(domain, type, proto);       if(s==INVALID_SOCKET)    { -  set_errno_from_win32_error (WSAGetLastError()); +  DWORD err = WSAGetLastError(); +  free_fd(fd); +  set_errno_from_win32_error (err);    return -1;    }       SetHandleInformation((HANDLE)s,    HANDLE_FLAG_INHERIT|HANDLE_FLAG_PROTECT_FROM_CLOSE, 0); -  mt_lock(&fd_mutex); -  fd=first_free_handle; -  first_free_handle=fd_type[fd]; -  fd_type[fd] = FD_SOCKET; -  da_handle[fd] = (HANDLE)s; -  mt_unlock(&fd_mutex); +     -  +  set_fd_handle(fd, (HANDLE)s); +  +  release_fd(fd); +     FDDEBUG(fprintf(stderr,"New socket: %d (%d)\n",fd,s));       return fd;   }      PMOD_EXPORT int debug_fd_pipe(int fds[2] DMALLOC_LINE_ARGS)   { -  +  int tmp_fds[2];    HANDLE files[2]; -  mt_lock(&fd_mutex); -  if(first_free_handle == FD_NO_MORE_FREE) -  { -  mt_unlock(&fd_mutex); -  errno=EMFILE; +  +  tmp_fds[0] = allocate_fd(FD_PIPE, INVALID_HANDLE_VALUE); +  if (tmp_fds[0] < 0) {    return -1;    } -  mt_unlock(&fd_mutex); +  tmp_fds[1] = allocate_fd(FD_PIPE, INVALID_HANDLE_VALUE); +  if (tmp_fds[1] < 0) { +  free_fd(tmp_fds[0]); +  return -1; +  } +     if(!CreatePipe(&files[0], &files[1], NULL, 0))    { -  set_errno_from_win32_error (GetLastError()); +  DWORD err = GetLastError(); +  free_fd(tmp_fds[0]); +  free_fd(tmp_fds[1]); +  set_errno_from_win32_error (err);    return -1;    }    -  FDDEBUG(fprintf(stderr,"ReadHANDLE=%d WriteHANDLE=%d\n",files[0],files[1])); +  FDDEBUG(fprintf(stderr, "ReadHANDLE=%d WriteHANDLE=%d\n", +  files[0], files[1]));    -  SetHandleInformation(files[0],HANDLE_FLAG_INHERIT|HANDLE_FLAG_PROTECT_FROM_CLOSE,0); -  SetHandleInformation(files[1],HANDLE_FLAG_INHERIT|HANDLE_FLAG_PROTECT_FROM_CLOSE,0); -  mt_lock(&fd_mutex); -  fds[0]=first_free_handle; -  first_free_handle=fd_type[fds[0]]; -  fd_type[fds[0]]=FD_PIPE; -  da_handle[fds[0]] = files[0]; +  SetHandleInformation(files[0], +  HANDLE_FLAG_INHERIT|HANDLE_FLAG_PROTECT_FROM_CLOSE, 0); +  SetHandleInformation(files[1], +  HANDLE_FLAG_INHERIT|HANDLE_FLAG_PROTECT_FROM_CLOSE, 0);    -  fds[1]=first_free_handle; -  first_free_handle=fd_type[fds[1]]; -  fd_type[fds[1]]=FD_PIPE; -  da_handle[fds[1]] = files[1]; +  fds[0] = tmp_fds[0]; +  set_fd_handle(fds[0], files[0]); +  release_fd(fds[0]);    -  mt_unlock(&fd_mutex); -  FDDEBUG(fprintf(stderr,"New pipe: %d (%d) -> %d (%d)\n",fds[0],files[0], fds[1], fds[1]));; +  fds[1] = tmp_fds[1]; +  set_fd_handle(fds[1], files[1]); +  release_fd(fds[1]);    -  +  FDDEBUG(fprintf(stderr,"New pipe: %d (%d) -> %d (%d)\n", +  fds[0], files[0], fds[1], files[1])); +    #ifdef DEBUG_MALLOC    debug_malloc_register_fd( fds[0], DMALLOC_LOCATION());    debug_malloc_register_fd( fds[1], DMALLOC_LOCATION());   #endif       return 0;   }      PMOD_EXPORT FD debug_fd_accept(FD fd, struct sockaddr *addr,    ACCEPT_SIZE_T *addrlen)   {    FD new_fd;    SOCKET s; -  mt_lock(&fd_mutex); +  +  FDDEBUG(fprintf(stderr, "fd_accept(%d, %p, %p)...\n", fd, addr, addrlen)); +  +  if (fd_to_socket(fd, &s) < 0) return -1; +     FDDEBUG(fprintf(stderr,"Accept on %d (%ld)..\n", -  fd, PTRDIFF_T_TO_LONG((ptrdiff_t)da_handle[fd]))); -  if(first_free_handle == FD_NO_MORE_FREE) -  { -  mt_unlock(&fd_mutex); -  errno=EMFILE; -  return -1; -  } -  if(fd_type[fd]!=FD_SOCKET) -  { -  mt_unlock(&fd_mutex); -  errno=ENOTSUPP; -  return -1; -  } -  s=(SOCKET)da_handle[fd]; -  mt_unlock(&fd_mutex); -  s=accept(s, addr, addrlen); +  fd, PTRDIFF_T_TO_LONG((ptrdiff_t)s))); +  +  new_fd = allocate_fd(FD_SOCKET, (HANDLE)INVALID_SOCKET); +  if (new_fd < 0) return -1; +  +  s = accept(s, addr, addrlen); +  +  release_fd(fd); +     if(s==INVALID_SOCKET)    { -  set_errno_from_win32_error (WSAGetLastError()); +  DWORD err = WSAGetLastError(); +  free_fd(new_fd); +  set_errno_from_win32_error(err);    FDDEBUG(fprintf(stderr,"Accept failed with errno %d\n",errno));    return -1;    }       SetHandleInformation((HANDLE)s,    HANDLE_FLAG_INHERIT|HANDLE_FLAG_PROTECT_FROM_CLOSE, 0); -  mt_lock(&fd_mutex); -  new_fd=first_free_handle; -  first_free_handle=fd_type[new_fd]; -  fd_type[new_fd]=FD_SOCKET; -  da_handle[new_fd] = (HANDLE)s; +  set_fd_handle(new_fd, (HANDLE)s); +  release_fd(new_fd);       FDDEBUG(fprintf(stderr,"Accept on %d (%ld) returned new socket: %d (%ld)\n", -  fd, PTRDIFF_T_TO_LONG((ptrdiff_t)da_handle[fd]), -  new_fd, PTRDIFF_T_TO_LONG((ptrdiff_t)s))); +  fd, (long)(ptrdiff_t)da_handle[fd], +  new_fd, (long)(ptrdiff_t)s));    -  mt_unlock(&fd_mutex); -  +     return new_fd;   }         #define SOCKFUN(NAME,X1,X2) \   PMOD_EXPORT int PIKE_CONCAT(debug_fd_,NAME) X1 { \    SOCKET s; \    int ret; \ -  +  FDDEBUG(fprintf(stderr, "fd_" #NAME "(%d, ...)...\n", fd)); \ +  if (fd_to_socket(fd, &s) < 0) return -1; \    FDDEBUG(fprintf(stderr, #NAME " on %d (%ld)\n", \ -  fd, PTRDIFF_T_TO_LONG((ptrdiff_t)da_handle[fd]))); \ -  mt_lock(&fd_mutex); \ -  if(fd_type[fd] != FD_SOCKET) { \ -  mt_unlock(&fd_mutex); \ -  errno = ENOTSUPP; \ -  return -1; \ -  } \ -  s = (SOCKET)da_handle[fd]; \ -  mt_unlock(&fd_mutex); \ +  fd, (long)(ptrdiff_t)s)); \    ret = NAME X2; \ -  +  release_fd(fd); \    if(ret == SOCKET_ERROR) { \    set_errno_from_win32_error (WSAGetLastError()); \    ret = -1; \    } \    FDDEBUG(fprintf(stderr, #NAME " returned %d (%d)\n", ret, errno)); \    return ret; \   }      #define SOCKFUN1(NAME,T1) \    SOCKFUN(NAME, (FD fd, T1 a), (s, a) )
pike.git/src/fdlib.c:912:   SOCKFUN2(getpeername,struct sockaddr *,ACCEPT_SIZE_T *)   SOCKFUN5(recvfrom,void *,int,int,struct sockaddr *,ACCEPT_SIZE_T *)   SOCKFUN3(send,void *,int,int)   SOCKFUN5(sendto,void *,int,int,struct sockaddr *,unsigned int)   SOCKFUN1(shutdown, int)   SOCKFUN1(listen, int)      PMOD_EXPORT int debug_fd_connect (FD fd, struct sockaddr *a, int len)   {    SOCKET ret; -  mt_lock(&fd_mutex); +  +  FDDEBUG(fprintf(stderr, "fd_connect(%d, %p, %d)...\n", fd, a, len)); +  +  if (fd_to_socket(fd, &ret) < 0) return -1; +     FDDEBUG(fprintf(stderr, "connect on %d (%ld)\n", -  fd, PTRDIFF_T_TO_LONG((ptrdiff_t)da_handle[fd])); -  for(ret=0;ret<len;ret++) -  fprintf(stderr," %02x",((unsigned char *)a)[ret]); -  fprintf(stderr,"\n"); -  ) -  if(fd_type[fd] != FD_SOCKET) +  fd, (long)(ptrdiff_t)ret);    { -  mt_unlock(&fd_mutex); -  errno=ENOTSUPP; -  return -1; +  int i; +  for(i = 0 ; i < len ; i++) +  fprintf(stderr," %02x",((unsigned char *)a)[i]); +  fprintf(stderr,"\n");    } -  ret=(SOCKET)da_handle[fd]; -  mt_unlock(&fd_mutex); +  ); +     ret=connect(ret,a,len); -  +  +  release_fd(fd); +     if(ret == SOCKET_ERROR) set_errno_from_win32_error (WSAGetLastError());    FDDEBUG(fprintf(stderr, "connect returned %d (%d)\n",ret,errno)); -  return DO_NOT_WARN((int)ret); +  return (int)ret;   }      PMOD_EXPORT int debug_fd_close(FD fd)   {    HANDLE h;    int type; -  mt_lock(&fd_mutex); -  h = da_handle[fd]; +  +  FDDEBUG(fprintf(stderr, "fd_close(%d)...\n", fd)); +  +  if (fd_to_handle(fd, &type, &h) < 0) return -1; +     FDDEBUG(fprintf(stderr,"Closing %d (%ld)\n", -  fd, PTRDIFF_T_TO_LONG((ptrdiff_t)da_handle[fd]))); -  type=fd_type[fd]; -  mt_unlock(&fd_mutex); +  fd, (long)h)); +  +  free_fd(fd); +     switch(type)    {    case FD_SOCKET:    if(closesocket((SOCKET)h))    {    set_errno_from_win32_error (GetLastError());    FDDEBUG(fprintf(stderr,"Closing %d (%ld) failed with errno=%d\n", -  fd, PTRDIFF_T_TO_LONG((ptrdiff_t)da_handle[fd]), -  errno)); +  fd, (long)(ptrdiff_t)h, errno));    return -1;    }    break;       default:    if(!CloseHandle(h))    {    set_errno_from_win32_error (GetLastError());    return -1;    }    } -  FDDEBUG(fprintf(stderr,"%d (%ld) closed\n", -  fd, PTRDIFF_T_TO_LONG((ptrdiff_t)da_handle[fd]))); -  mt_lock(&fd_mutex); -  if(fd_type[fd]<FD_NO_MORE_FREE) -  { -  fd_type[fd]=first_free_handle; -  first_free_handle=fd; -  } -  mt_unlock(&fd_mutex); +     -  +  FDDEBUG(fprintf(stderr,"%d (%ld) closed\n", fd, (ptrdiff_t)h)); +     return 0;   }      PMOD_EXPORT ptrdiff_t debug_fd_write(FD fd, void *buf, ptrdiff_t len)   {    int kind;    HANDLE handle;    -  mt_lock(&fd_mutex); +  FDDEBUG(fprintf(stderr, "fd_write(%d, %p, %ld)...\n", fd, buf, (long)len)); +  +  if (fd_to_handle(fd, &kind, &handle) < 0) return -1; +     FDDEBUG(fprintf(stderr, "Writing %d bytes to %d (%d)\n", -  len, fd, da_handle[fd])); -  kind = fd_type[fd]; -  handle = da_handle[fd]; -  mt_unlock(&fd_mutex); +  len, fd, (long)(ptrdiff_t)handle));       switch(kind)    {    case FD_SOCKET:    { -  ptrdiff_t ret = send((SOCKET)handle, buf, -  DO_NOT_WARN((int)len), -  0); +  ptrdiff_t ret = send((SOCKET)handle, buf, (int)len, 0); +  release_fd(fd);    if(ret<0)    {    set_errno_from_win32_error (WSAGetLastError());    FDDEBUG(fprintf(stderr, "Write on %d failed (%d)\n", fd, errno));    if (errno == 1) {    /* UGLY kludge */    errno = WSAEWOULDBLOCK;    }    return -1;    }    FDDEBUG(fprintf(stderr, "Wrote %d bytes to %d)\n", len, fd));    return ret;    }       case FD_CONSOLE:    case FD_FILE:    case FD_PIPE:    {    DWORD ret = 0; -  if(!WriteFile(handle, buf, -  DO_NOT_WARN((DWORD)len), -  &ret,0) && ret<=0) +  if(!WriteFile(handle, buf, (DWORD)len, &ret,0) && ret<=0)    {    set_errno_from_win32_error (GetLastError());    FDDEBUG(fprintf(stderr, "Write on %d failed (%d)\n", fd, errno)); -  +  release_fd(fd);    return -1;    }    FDDEBUG(fprintf(stderr, "Wrote %ld bytes to %d)\n", (long)ret, fd)); -  +  release_fd(fd);    return ret;    }       default:    errno=ENOTSUPP; -  +  release_fd(fd);    return -1;    }   }      PMOD_EXPORT ptrdiff_t debug_fd_read(FD fd, void *to, ptrdiff_t len)   { -  +  int type;    DWORD ret;    ptrdiff_t rret;    HANDLE handle;    -  mt_lock(&fd_mutex); +  FDDEBUG(fprintf("fd_read(%d, %p, %ld)...\n", fd, to, (long)len)); +  +  if (fd_to_handle(fd, &type, &handle) < 0) return -1; +     FDDEBUG(fprintf(stderr,"Reading %d bytes from %d (%d) to %lx\n", -  len, fd, PTRDIFF_T_TO_LONG((ptrdiff_t)da_handle[fd]), -  PTRDIFF_T_TO_LONG((ptrdiff_t)to))); -  ret=fd_type[fd]; -  handle=da_handle[fd]; -  mt_unlock(&fd_mutex); +  len, fd, (long)(ptrdiff_t)h, +  (unsigned long)(ptrdiff_t)to));    -  switch(ret) +  switch(type)    {    case FD_SOCKET: -  rret=recv((SOCKET)handle, to, -  DO_NOT_WARN((int)len), -  0); +  rret=recv((SOCKET)handle, to, (int)len, 0); +  release_fd(fd);    if(rret<0)    {    set_errno_from_win32_error (WSAGetLastError());    FDDEBUG(fprintf(stderr,"Read on %d failed %ld\n",fd,errno));    return -1;    }    FDDEBUG(fprintf(stderr,"Read on %d returned %ld\n",fd,rret));    return rret;       case FD_CONSOLE:    case FD_FILE:    case FD_PIPE:    ret=0; -  if(len && !ReadFile(handle, to, -  DO_NOT_WARN((DWORD)len), -  &ret,0) && ret<=0) +  while(len && !ReadFile(handle, to, (DWORD)len, &ret, 0) && ret<=0)    {    unsigned int err = GetLastError(); -  +  release_fd(fd);    set_errno_from_win32_error (err);    switch(err)    { -  /* Pretend we reached the end of the file */ +  case ERROR_NOT_ENOUGH_MEMORY: +  /* This can happen when reading from stdin, and can be due +  * to running out of OS io-buffers. cf ReadConsole(): +  * +  * | lpBuffer [out] +  * | A pointer to a buffer that receives the data read from +  * | the console input buffer. +  * | +  * | The storage for this buffer is allocated from a shared +  * | heap for the process that is 64 KB in size. The maximum +  * | size of the buffer will depend on heap usage. +  * +  * The limit seems to be 26608 bytes on Windows Server 2003, +  * and 31004 bytes on some versions of Windows XP. +  * +  * The gdb people ran into the same bug in 2006. +  * cf http://permalink.gmane.org/gmane.comp.gdb.patches/29669 +  * +  * FIXME: We ought to attempt to fill the remainder of +  * the buffer on success and full read, but as +  * this failure mode usually only happens with +  * the console, we would risk blocking, so let +  * the higher levels of code deal with the +  * resulting short reads. +  */ +  /* Halve the size of the request and retry. */ +  len = len >> 1; +  if (len) continue; +  /* Total failure. Fall out to the generic error return. */ +  break;    case ERROR_BROKEN_PIPE: -  +  /* Pretend we reached the end of the file */    return 0;    }    FDDEBUG(fprintf(stderr,"Read failed %d\n",errno));    return -1;    }    FDDEBUG(fprintf(stderr,"Read on %d returned %ld\n",fd,ret)); -  +  release_fd(fd);    return ret;       default:    errno=ENOTSUPP; -  +  release_fd(fd);    return -1;    }   }      PMOD_EXPORT PIKE_OFF_T debug_fd_lseek(FD fd, PIKE_OFF_T pos, int where)   {    PIKE_OFF_T ret; -  +  int type;    HANDLE h;    -  mt_lock(&fd_mutex); -  if(fd_type[fd]!=FD_FILE) +  FDDEBUG(fprintf(stderr, "fd_lseek(%d)...\n", fd)); +  +  if (fd_to_handle(fd, &type, &h) < 0) return -1; +  if(type != FD_FILE)    { -  mt_unlock(&fd_mutex); +  release_fd(fd);    errno=ENOTSUPP;    return -1;    } -  +    #if FILE_BEGIN != SEEK_SET || FILE_CURRENT != SEEK_CUR || FILE_END != SEEK_END    switch(where)    {    case SEEK_SET: where=FILE_BEGIN; break;    case SEEK_CUR: where=FILE_CURRENT; break;    case SEEK_END: where=FILE_END; break;    }   #endif -  h = da_handle[fd]; -  mt_unlock(&fd_mutex); +        { - #ifdef INT64 +    #ifdef HAVE_SETFILEPOINTEREX    /* Windows NT based. */    LARGE_INTEGER li_pos;    LARGE_INTEGER li_ret;    li_pos.QuadPart = pos;    li_ret.QuadPart = 0;    if(!SetFilePointerEx(h, li_pos, &li_ret, where)) {    set_errno_from_win32_error (GetLastError()); -  +  release_fd(fd);    return -1;    }    ret = li_ret.QuadPart;   #else /* !HAVE_SETFILEPOINTEREX */    /* Windows 9x based. */ -  LONG high = DO_NOT_WARN((LONG)(pos >> 32)); +  LONG high = (LONG)(pos >> 32);    DWORD err;    pos &= ((INT64) 1 << 32) - 1; -  ret = SetFilePointer(h, DO_NOT_WARN((LONG)pos), &high, where); +  ret = SetFilePointer(h, (LONG)pos, &high, where);    if (ret == INVALID_SET_FILE_POINTER &&    (err = GetLastError()) != NO_ERROR) {    set_errno_from_win32_error (err); -  +  release_fd(fd);    return -1;    }    ret += (INT64) high << 32;   #endif /* HAVE_SETFILEPOINTEREX */ - #else /* !INT64 */ -  ret = SetFilePointer(h, (LONG)pos, NULL, where); -  if(ret == INVALID_SET_FILE_POINTER) -  { -  set_errno_from_win32_error (GetLastError()); -  return -1; +     } - #endif /* INT64 */ -  } +     -  +  release_fd(fd); +     return ret;   }      PMOD_EXPORT int debug_fd_ftruncate(FD fd, PIKE_OFF_T len)   { -  +  int type;    HANDLE h;    LONG oldfp_lo, oldfp_hi, len_hi;    DWORD err;    -  mt_lock(&fd_mutex); -  if(fd_type[fd]!=FD_FILE) +  FDDEBUG(fprintf(stderr, "fd_ftruncate(%d)...\n", fd)); +  +  if (fd_to_handle(fd, &type, &h) < 0) return -1; +  if(type != FD_FILE)    { -  mt_unlock(&fd_mutex); +  release_fd(fd);    errno=ENOTSUPP;    return -1;    } -  h = da_handle[fd]; -  mt_unlock(&fd_mutex); +        oldfp_hi = 0;    oldfp_lo = SetFilePointer(h, 0, &oldfp_hi, FILE_CURRENT);    if(!~oldfp_lo) {    err = GetLastError();    if(err != NO_ERROR) { -  +  release_fd(fd);    set_errno_from_win32_error (err);    return -1;    }    }    - #ifdef INT64 -  len_hi = DO_NOT_WARN ((LONG) (len >> 32)); +  len_hi = (LONG) (len >> 32);    len &= ((INT64) 1 << 32) - 1; - #else -  len_hi = 0; - #endif +     -  if (SetFilePointer (h, DO_NOT_WARN ((LONG) len), &len_hi, FILE_BEGIN) == +  if (SetFilePointer (h, (LONG) len, &len_hi, FILE_BEGIN) ==    INVALID_SET_FILE_POINTER &&    (err = GetLastError()) != NO_ERROR) {    SetFilePointer(h, oldfp_lo, &oldfp_hi, FILE_BEGIN); -  +  release_fd(fd);    set_errno_from_win32_error (err);    return -1;    }       if(!SetEndOfFile(h)) {    set_errno_from_win32_error (GetLastError());    SetFilePointer(h, oldfp_lo, &oldfp_hi, FILE_BEGIN); -  +  release_fd(fd);    return -1;    }       if (oldfp_hi < len_hi || (oldfp_hi == len_hi && oldfp_lo < len))    if(!~SetFilePointer(h, oldfp_lo, &oldfp_hi, FILE_BEGIN)) {    err = GetLastError();    if(err != NO_ERROR) { -  +  release_fd(fd);    set_errno_from_win32_error (err);    return -1;    }    }    -  +  release_fd(fd);    return 0;   }      PMOD_EXPORT int debug_fd_flock(FD fd, int oper)   {    long ret; -  +  int type;    HANDLE h; -  mt_lock(&fd_mutex); -  if(fd_type[fd]!=FD_FILE) +  +  FDDEBUG(fprintf(stderr, "fd_flock(%d)...\n", fd)); +  +  if (fd_to_handle(fd, &type, &h) < 0) return -1; +  if(type != FD_FILE)    { -  mt_unlock(&fd_mutex); +  release_fd(fd);    errno=ENOTSUPP;    return -1;    } -  h = da_handle[fd]; -  mt_unlock(&fd_mutex); +        if(oper & fd_LOCK_UN)    {    ret=UnlockFile(h,    0,    0,    0xffffffff,    0xffffffff);    }else{    DWORD flags = 0;    OVERLAPPED tmp; -  MEMSET(&tmp, 0, sizeof(tmp)); +  memset(&tmp, 0, sizeof(tmp));    tmp.Offset=0;    tmp.OffsetHigh=0;       if(oper & fd_LOCK_EX)    flags|=LOCKFILE_EXCLUSIVE_LOCK;       if(oper & fd_LOCK_UN)    flags|=LOCKFILE_FAIL_IMMEDIATELY;       ret=LockFileEx(h,    flags,    0,    0xffffffff,    0xffffffff,    &tmp);    } -  +  +  release_fd(fd); +     if(ret<0)    {    set_errno_from_win32_error (GetLastError());    return -1;    }       return 0;   }         /* Note: s->st_ctime is set to the file creation time. It should    * probably be the last access time to be closer to the unix    * counterpart, but the creation time is admittedly more useful. */   PMOD_EXPORT int debug_fd_fstat(FD fd, PIKE_STAT_T *s)   { -  +  int type; +  HANDLE h;    FILETIME c,a,m;    -  mt_lock(&fd_mutex); -  FDDEBUG(fprintf(stderr, "fstat on %d (%ld)\n", -  fd, PTRDIFF_T_TO_LONG((ptrdiff_t)da_handle[fd]))); -  if(fd_type[fd]!=FD_FILE) +  FDDEBUG(fprintf(stderr, "fd_fstat(%d, %p)\n", fd, s)); +  +  if (fd_to_handle(fd, &type, &h) < 0) return -1; +  if (type != FD_FILE)    { -  +  release_fd(fd);    errno=ENOTSUPP; -  mt_unlock(&fd_mutex); +     return -1;    }    -  MEMSET(s, 0, sizeof(PIKE_STAT_T)); +  FDDEBUG(fprintf(stderr, "fstat on %d (%ld)\n", +  fd, (long)(ptrdiff_t)h)); +  +  memset(s, 0, sizeof(PIKE_STAT_T));    s->st_nlink=1;    -  switch(fd_type[fd]) +  switch(type)    {    case FD_SOCKET:    s->st_mode=S_IFSOCK;    break;       default: -  switch(GetFileType(da_handle[fd])) +  switch(GetFileType(h))    {    default:    case FILE_TYPE_UNKNOWN: s->st_mode=0; break;       case FILE_TYPE_DISK:    s->st_mode=S_IFREG;    {    DWORD high, err; -  s->st_size=GetFileSize(da_handle[fd],&high); +  s->st_size=GetFileSize(h, &high);    if (s->st_size == INVALID_FILE_SIZE &&    (err = GetLastError()) != NO_ERROR) { -  +  release_fd(fd);    set_errno_from_win32_error (err); -  mt_unlock(&fd_mutex); +     return -1;    } - #ifdef INT64 +     s->st_size += (INT64) high << 32; - #else -  if (high) s->st_size = MAXDWORD; - #endif +     } -  if(!GetFileTime(da_handle[fd], &c, &a, &m)) +  if(!GetFileTime(h, &c, &a, &m))    {    set_errno_from_win32_error (GetLastError()); -  mt_unlock(&fd_mutex); +  release_fd(fd);    return -1;    }       /* FIXME: Determine the filesystem type to use    * fat_filetimes_to_stattimes when necessary. */       nonfat_filetimes_to_stattimes (&c, &a, &m, s);    break;       case FILE_TYPE_CHAR: s->st_mode=S_IFCHR; break;    case FILE_TYPE_PIPE: s->st_mode=S_IFIFO; break;    }    } -  +  release_fd(fd);    s->st_mode |= 0666; -  mt_unlock(&fd_mutex); +     return 0;   }         #ifdef FD_DEBUG   static void dump_FDSET(FD_SET *x, int fds)   {    if(x)    {    int e, first=1;    fprintf(stderr,"["); -  for(e=0;e<fds;e++) +  for(e = 0; e < FD_SETSIZE; e++)    {    if(FD_ISSET(da_handle[e],x))    {    if(!first) fprintf(stderr,",",e);    fprintf(stderr,"%d",e);    first=0;    }    }    fprintf(stderr,"]");    }else{
pike.git/src/fdlib.c:1397:    fprintf(stderr,",(%ld,%06ld));\n", (long) t->tv_sec,(long) t->tv_usec);    )       return ret;   }         PMOD_EXPORT int debug_fd_ioctl(FD fd, int cmd, void *data)   {    int ret; +  SOCKET s; +  +  FDDEBUG(fprintf(stderr, "fd_ioctl(%d, %d, %p)...\n", fd, cmd, data)); +  +  if (fd_to_socket(fd, &s) < 0) return -1; +     FDDEBUG(fprintf(stderr,"ioctl(%d (%ld,%d,%p)\n", -  fd, PTRDIFF_T_TO_LONG((ptrdiff_t)da_handle[fd]), cmd, data)); -  switch(fd_type[fd]) -  { -  case FD_SOCKET: -  ret=ioctlsocket((SOCKET)da_handle[fd], cmd, data); +  fd, (long)(ptrdiff_t)s, cmd, data)); +  +  ret = ioctlsocket(s, cmd, data); +     FDDEBUG(fprintf(stderr,"ioctlsocket returned %ld (%d)\n",ret,errno)); -  +  +  release_fd(fd); +     if(ret==SOCKET_ERROR)    {    set_errno_from_win32_error (WSAGetLastError());    return -1;    } -  return ret; +     -  default: -  errno=ENOTSUPP; -  return -1; +  return ret;   } - } +          PMOD_EXPORT FD debug_fd_dup(FD from)   {    FD fd; -  HANDLE x,p=GetCurrentProcess(); +  int type; +  HANDLE h,x,p=GetCurrentProcess();    -  mt_lock(&fd_mutex); - #ifdef DEBUG -  if(fd_type[from]>=FD_NO_MORE_FREE) -  Pike_fatal("fd_dup() on file which is not open!\n"); - #endif -  if(!DuplicateHandle(p,da_handle[from],p,&x,0,0,DUPLICATE_SAME_ACCESS)) +  FDDEBUG(fprintf(stderr, "fd_dup(%d)...\n", from)); +  +  if (fd_to_handle(from, &type, &h) < 0) return -1; +  +  fd = allocate_fd(type, +  (type == FD_SOCKET)? +  (HANDLE)INVALID_SOCKET:INVALID_HANDLE_VALUE); +  +  if(!DuplicateHandle(p, h, p, &x, 0, 0, DUPLICATE_SAME_ACCESS))    { -  set_errno_from_win32_error (GetLastError()); -  mt_unlock(&fd_mutex); +  DWORD err = GetLastError(); +  free_fd(fd); +  release_fd(from); +  set_errno_from_win32_error(err);    return -1;    }    -  fd=first_free_handle; -  first_free_handle=fd_type[fd]; -  fd_type[fd]=fd_type[from]; -  da_handle[fd] = x; -  mt_unlock(&fd_mutex); +  set_fd_handle(fd, x);       FDDEBUG(fprintf(stderr,"Dup %d (%ld) to %d (%d)\n", -  from, PTRDIFF_T_TO_LONG((ptrdiff_t)da_handle[from]), fd, x)); +  from, (long)(ptrdiff_t)h, fd, (long)(ptrdiff_t)x)); +  +  release_fd(from); +  release_fd(fd); +     return fd;   }      PMOD_EXPORT FD debug_fd_dup2(FD from, FD to)   { -  HANDLE x,p=GetCurrentProcess(); +  struct Backend_struct *backend = NULL; +  struct fd_callback_box *box = NULL; +  struct object *box_obj = NULL; +  int box_events = 0; +  int type; +  HANDLE h,x,p=GetCurrentProcess();    -  mt_lock(&fd_mutex); -  if(!DuplicateHandle(p,da_handle[from],p,&x,0,0,DUPLICATE_SAME_ACCESS)) -  { -  set_errno_from_win32_error (GetLastError()); -  mt_unlock(&fd_mutex); +  if ((from == to) || (to < 0) || (to >= FD_SETSIZE)) { +  errno = EINVAL;    return -1;    }    -  if(fd_type[to] < FD_NO_MORE_FREE) +  FDDEBUG(fprintf(stderr, "fd_dup2(%d, %d)...\n", from, to)); +  +  if (fd_to_handle(from, &type, &h) < 0) return -1; +  +  if(!DuplicateHandle(p, h, p, &x, 0, 0, DUPLICATE_SAME_ACCESS))    { -  if(!CloseHandle(da_handle[to])) -  { +  release_fd(from);    set_errno_from_win32_error (GetLastError()); -  mt_unlock(&fd_mutex); +     return -1;    } -  }else{ -  int *prev,next; -  for(prev=&first_free_handle;(next=*prev) != FD_NO_MORE_FREE;prev=fd_type+next) -  { -  if(next==to) -  { -  *prev=fd_type[next]; -  break; +  +  release_fd(from); +  +  backend = get_backend_for_fd(to); +  if (backend) { +  box = get_fd_callback_box_for_fd(backend, to); +  if (box) { +  /* Temporarily clear the events for the box, to make +  * sure no events are still registered for the old handle. +  * +  * NB: We need to keep a reference to the box object, in +  * case the only reference was held by the backend. +  */ +  box_obj = box->ref_obj; +  if (box_obj) add_ref(box_obj); +  box_events = box->events; +  set_fd_callback_events(box, 0, box->flags);    }    } -  +  +  /* NB: Dead-lock proofed by never holding the busy lock for +  * both from and to. +  */ +  +  if (reallocate_fd(to, type, x) < 0) { +  release_fd(to); +  +  if (box) { +  /* Restore the events for the box. */ +  set_fd_callback_events(box, box_events, box->flags); +  if (box_obj) free_object(box_obj);    } -  fd_type[to]=fd_type[from]; -  da_handle[to] = x; -  mt_unlock(&fd_mutex); +     -  +  if (type == FD_SOCKET) { +  closesocket((SOCKET)x); +  } else { +  CloseHandle(x); +  } +  return -1; +  } +  +  release_fd(to); +  +  if (box && (type == FD_SOCKET)) { +  /* Restore the events for the box, to register it with +  * the new handle. +  */ +  set_fd_callback_events(box, box_events, box->flags); +  } +  if (box_obj) free_object(box_obj); +     FDDEBUG(fprintf(stderr,"Dup2 %d (%d) to %d (%d)\n", -  from, PTRDIFF_T_TO_LONG((ptrdiff_t)da_handle[from]), to, x)); +  from, (long)(ptrdiff_t)h, to, x));       return to;   }      PMOD_EXPORT const char *debug_fd_inet_ntop(int af, const void *addr,    char *cp, size_t sz)   { -  static char *(*inet_ntop_funp)(int, void*, char *, size_t); +  static char *(*inet_ntop_funp)(int, const void*, char *, size_t);    static int tried;    static HINSTANCE ws2_32lib;       if (!inet_ntop_funp) {    if (!tried) {    tried = 1;    if ((ws2_32lib = LoadLibrary("Ws2_32"))) {    FARPROC proc;    if ((proc = GetProcAddress(ws2_32lib, "InetNtopA"))) { -  inet_ntop_funp = (char *(*)(int, void *, char *, size_t))proc; +  inet_ntop_funp = (char *(*)(int, const void *, char *, size_t))proc;    }    }    }    if (!inet_ntop_funp) {    const unsigned char *q = (const unsigned char *)addr;    if (af == AF_INET) {    snprintf(cp, sz, "%d.%d.%d.%d", q[0], q[1], q[2], q[3]);    return cp;   #ifdef AF_INET6    } else if (af == AF_INET6) {
pike.git/src/fdlib.c:1551:    return NULL;    }    }    return inet_ntop_funp(af, addr, cp, sz);   }   #endif /* HAVE_WINSOCK_H && !__GNUC__ */      #ifdef EMULATE_DIRECT   PMOD_EXPORT DIR *opendir(char *dir)   { -  ptrdiff_t len=strlen(dir); -  char *foo; -  DIR *ret=(DIR *)malloc(sizeof(DIR) + len+5); +  ptrdiff_t len; +  p_wchar1 *foo; +  DIR *ret = malloc(sizeof(DIR)); +     if(!ret)    {    errno=ENOMEM;    return 0;    } -  foo=sizeof(DIR) + (char *)ret; -  MEMCPY(foo, dir, len); +     -  if(len && foo[len-1]!='/') foo[len++]='/'; -  foo[len++]='*'; -  foo[len]=0; - /* fprintf(stderr,"opendir(%s)\n",foo); */ -  -  /* This may require appending a slash and a star... */ -  ret->h=FindFirstFile( (LPCTSTR) foo, & ret->find_data); -  if(ret->h == DO_NOT_WARN(INVALID_HANDLE_VALUE)) -  { -  errno=ENOENT; -  free((char *)ret); -  return 0; +  foo = pike_dwim_utf8_to_utf16(dir); +  if (!foo) { +  free(ret); +  errno = ENOMEM; +  return NULL;    } -  ret->first=1; -  return ret; - } +     - PMOD_EXPORT int readdir_r(DIR *dir, struct direct *tmp ,struct direct **d) - { -  if(dir->first) -  { -  *d=&dir->find_data; -  dir->first=0; -  return 0; -  }else{ -  if(FindNextFile(dir->h,tmp)) -  { -  *d=tmp; -  return 0; -  } -  *d=0; -  return 0; -  } - } +  len = wcslen(foo);    - PMOD_EXPORT void closedir(DIR *dir) - { -  FindClose(dir->h); -  free((char *)dir); - } - #endif +  /* This may require appending a slash and a star... */ +  if(len && !ISSEPARATOR(foo[len-1])) foo[len++]='/'; +  foo[len++]='*'; +  foo[len]=0;    - #if 0 + /* fprintf(stderr, "opendir(%S)\n", foo); */    - #ifdef FD_LINEAR - struct fd_mapper - { -  int size; -  void **data; - }; +  ret->h = FindFirstFileW(foo, &ret->find_data); +  free(foo);    - void init_fd_mapper(struct fd_mapper *x) +  if(ret->h == INVALID_HANDLE_VALUE)    { -  x->size=64; -  x->data=(void **)xalloc(x->size*sizeof(void *)); +  /* FIXME: Handle empty directories. */ +  errno=ENOENT; +  free(ret); +  return NULL;    }    - void exit_fd_mapper(struct fd_mapper *x) - { -  free((char *)x->data); +  ret->direct.d_name = NULL; +  ret->first=1; +  return ret;   }    - void fd_mapper_set(struct fd_mapper *x, FD fd, void *data) + PMOD_EXPORT struct dirent *readdir(DIR *dir)   { -  while(fd>=x->size) +  if(!dir->first)    { -  x->size*=2; -  x->data=(void **)realloc((char *)x->data, x->size*sizeof(void *)); -  if(!x->data) -  Pike_fatal("Out of memory.\n"); -  x->data=nd; -  } -  x->data[fd]=data; -  - } -  - void *fd_mapper_get(struct fd_mapper *x, FD fd) +  if(!FindNextFileW(dir->h, &dir->find_data))    { -  return x->data[fd]; +  errno = ENOENT; +  return NULL;    } - #else /* FD_LINEAR */ - struct fd_mapper_data - { -  FD x; -  void *data; - }; - struct fd_mapper - { -  int num; -  int hsize; -  struct fd_mapper_data *data; - }; -  - void init_fd_mapper(struct fd_mapper *x) - { -  int i; -  x->num=0; -  x->hsize=127; -  x->data=(struct fd_mapper_data *)xalloc(x->hsize*sizeof(struct fd_mapper_data)); -  for(i=0;i<x->hsize;i++) x->data[i].fd=-1; +  } else { +  dir->first = 0;    }    - void exit_fd_mapper(struct fd_mapper *x) - { -  free((char *)x->data); +  if (dir->direct.d_name) { +  free(dir->direct.d_name); +  dir->direct.d_name = NULL;    }    - void fd_mapper_set(struct fd_mapper *x, FD fd, void *data) - { -  int hval; -  x->num++; -  if(x->num*2 > x->hsize) -  { -  struct fd_mapper_data *old=x->data; -  int i,old_size=x->hsize; -  x->hsize*=3; -  x->num=0; -  x->data=(struct fd_mapper_data *)xalloc(x->size*sizeof(struct fd_mapper_data *)); -  for(i=0;i<x->size;i++) x->data[i].fd=-1; -  for(i=0;i<old_size;i++) -  if(old[i].fd!=-1) -  fd_mapper_set(x, old[i].fd, old[i].data); -  } +  dir->direct.d_name = pike_utf16_to_utf8(dir->find_data.cFileName);    -  hval=fd % x->hsize; -  while(x->data[hval].fd != -1) -  { -  hval++; -  if(hval==x->hsize) hval=0; +  if (dir->direct.d_name) return &dir->direct; +  errno = ENOMEM; +  return NULL;   } -  x->data[hval].fd=fd; -  x->data[hval].data=data; - } +     - void *fd_mapper_get(struct fd_mapper *x, FD fd) + PMOD_EXPORT void closedir(DIR *dir)   { -  int hval=fd % x->hsize; -  while(x->data[hval].fd != fd) -  { -  hval++; -  if(hval==x->hsize) hval=0; +  FindClose(dir->h); +  if (dir->direct.d_name) { +  free(dir->direct.d_name);    } -  return x->data[hval].data; +  free(dir);   } - #endif /* FD_LINEAR */ + #endif    -  - struct fd_data_hash + #if defined(HAVE_WINSOCK_H) && defined(USE_DL_MALLOC) + /* NB: We use some calls above that allocate memory with the libc malloc. */ + #undef free + static inline void libc_free(void *ptr)   { -  FD fd; -  int key; -  struct fd_data_hash *next; -  void *data; - }; -  - #define FD_DATA_PER_BLOCK 255 -  - struct fd_data_hash_block - { -  struct fd_data_hash_block *next; -  struct fd_data_hash data[FD_DATA_PER_BLOCk]; - }; -  - static int keynum=0; - static unsigned int num_stored_keys=0; - static unsigned int hash_size=0; - static struct fd_data_hash *free_blocks=0; - static struct fd_data_hash **htable=0; - static fd_data_hash_block *hash_blocks=0; -  - int get_fd_data_key(void) - { -  return ++keynum; +  if (ptr) free(ptr);   } -  - void store_fd_data(FD fd, int key, void *data) - { -  struct fd_data_hash *p,**last; -  unsigned int hval=(fd + key * 53) % hash_size; -  -  for(last=htable[e];p=*last;last=&p->next) -  { -  if(p->fd == fd && p->key == key) -  { -  if(data) -  { -  p->data=data; -  }else{ -  *last=p->next; -  p->next=free_blocks; -  free_blocks=p; -  num_stored_keys--; -  } -  return; -  } -  } -  if(!data) return; -  -  num_stored_keys++; -  -  if(num_stored_keys * 2 >= hash_size) -  { -  /* time to rehash */ -  unsigned int h; -  unsigned int old_hsize=hash_size; -  unsigned fd_data_hash **old_htable=htable; -  if(!hash_size) -  hash_size=127; -  else -  hash_size*=3; -  -  htable=(struct fd_data_hash **)xalloc(hash_size * sizeof(struct fd_data_hash *)); -  -  for(h=0;h<old_hsize;h++) -  { -  for(last=old_htable+e;p=*last;last=&p->next) -  store_fd_data(p->fd, p->key, p->data); -  *last=free_blocks; -  free_blocks=old_htable[h]; -  } -  if(old_htable) -  free((char *)old_htable); -  } -  -  -  if(!free_blocks) -  { -  struct fd_data_hash_block *n; -  int e; -  n=ALLOC_STRUCT(fd_data_hash_block); -  n->next=hash_blocks; -  hash_blocks=n; -  for(e=0;e<FD_DATA_PER_BLOCK;e++) -  { -  n->data[e].next=free_blocks; -  free_blocks=n->data+e; -  } -  } -  -  p=free_blocks; -  free_blocks=p->next; -  p->fd=fd; -  p->key=key; -  p->data=data; -  p->next=htable[hval]; -  htable[hval]=p; - } -  - void *get_fd_data(FD fd, int key) - { -  struct fd_data_hash *p,**last; -  unsigned int hval=(fd + key * 53) % hash_size; -  -  for(p=htable[hval];p;p=p->next) -  if(p->fd == fd && p->key == key) -  return p->data; -  -  return 0; - } -  -  - #define FD_EVENT_READ 1 - #define FD_EVENT_WRITE 2 - #define FD_EVENT_OOB 4 -  - struct event - { -  int fd; -  int events; - }; -  - #ifdef FDLIB_USE_SELECT -  - struct fd_waitor - { -  fd_FDSET rcustomers,wcustomers,xcustomers; -  fd_FDSET rtmp,wtmp,xtmp; -  FD last; -  int numleft; -  int max; - }; -  - #define init_waitor(X) do { (X)->numleft=0; (X)->max=0; \ -  fd_FDZERO(&X->rcustomers); \ -  fd_FDZERO(&X->wcustomers); \ -  fd_FDZERO(&X->xcustomers); \ -  } while(0) -  - void fd_waitor_set_customer(struct fd_waitor *x, FD customer, int flags) - { -  if(flags & FD_EVENT_READ) -  { -  fd_FD_SET(& x->rcustomer, customer); -  }else{ -  fd_FD_CLR(& x->rcustomer, customer); -  } -  -  if(flags & FD_EVENT_WRITE) -  { -  fd_FD_SET(& x->wcustomer, customer); -  }else{ -  fd_FD_CLR(& x->wcustomer, customer); -  } -  -  if(flags & FD_EVENT_OOB) -  { -  fd_FD_SET(& x->xcustomer, customer); -  }else{ -  fd_FD_CLR(& x->xcustomer, customer); -  } -  -  if(flags) -  if(customer>x->max) x->max=customer; -  else -  if(customer == x->max) -  { -  x->max--; -  while( -  !fd_ISSET(& x->rcustomers,x->max) && -  !fd_ISSET(& x->wcustomers,x->max) && -  !fd_ISSET(& x->xcustomers,x->max) -  ) -  x->max--; -  } - } -  - int fd_waitor_idle(fd_waitor *x, -  struct timeval *t, -  struct event *e) - { -  int tmp; -  if(!x->numleft) -  { -  x->rtmp=x->rcustomers; -  x->wtmp=x->wcustomers; -  x->xtmp=x->xcustomers; -  -  tmp=select(x->max, & x->rtmp, & x->wtmp, & x->xtmp, &t); -  if(tmp<0) return 0; -  -  x->last=0; -  x->numleft=tmp; -  } -  while(x->numleft) -  { -  while(x->last<x->max) -  { -  int flags= -  (fd_FD_ISSET(& x->rtmp, x->last) ? FD_EVENT_READ : 0) | -  (fd_FD_ISSET(& x->wtmp, x->last) ? FD_EVENT_WRITE : 0) | -  (fd_FD_ISSET(& x->xtmp, x->last) ? FD_EVENT_OOB: 0); -  -  if(flags) -  { -  numleft--; -  -  e->fd=x->last -  e->event=flags; -  return 1; -  } -  } -  } -  return 0; - } -  - #endif /* FDLIB_USE_SELECT */ -  - #ifdef FDLIB_USE_WAITFORMULTIPLEOBJECTS -  - #define FD_MAX 16384 -  - struct fd_waitor - { -  int occupied; -  HANDLE customers[FD_MAX]; -  FD pos_to_fd[FD_MAX]; -  int fd_to_pos_key; -  int last_swap; - }; -  - void fd_waitor_set_customer(fd_waitor *x, FD customer, int flags) - { -  HANDLE h=CreateEvent(); -  x->customers[x->occupied]=h; -  x->pos_to_fd[x->occupied]=customer; -  fd_mapper_store(customer, x->fd_to_pos_key, x->occupied); -  x->occupied++; - } -  - void fd_waitor_remove_customer(fd_waitor *x, FD customer) - { -  int pos=(int)fd_mapper_get(customer, x->fd_to_pos_key); -  -  CloseHandle(x->customers[pos]); -  -  fd_mapper_store(customer, x->fd_to_pos_key, NULL); -  x->occupied--; -  if(x->occupied != pos) -  { -  x->customer[pos]=x->customer[x->occupied]; -  x->pos_to_fd[pos]=x->pos_to_fd[x->occupied]; -  fd_mapper_store(x->pos_to_fd[pos], x->fd_to_pos_key, (void *)pos); -  } - } -  - FD fd_waitor_idle(fd_waitor *x, struct timeval delay) - { -  DWORD ret,d=delay.tv_usec/1000; -  d+=MINIMUM(100000,delay.tv_sec) *1000; -  -  ret=WaitForMultipleObjects(x->occupied, -  x->customers, -  0, -  delay); -  -  if(ret>= WAIT_OBJECT_0 && ret< WAIT_OBJECT_0 + x->occupied) -  { -  long tmp; -  ret-=WAIT_OBJECT_0; -  if(-- (x->last_swap) <= ret) -  { -  x->last_swap=x->occupied; -  if(x->occupied == ret) return ret; -  } -  tmp=customers[ret]; -  customers[ret]=customers[x->last_swap]; -  customers[x->last_swap]=tmp; -  -  tmp=pos_to_fd[ret]; -  pos_to_fd[ret]=pos_to_fd[x->last_swap]; -  pos_to_fd[x->last_swap]=tmp; -  -  fd_mapper_store(x->pos_to_fd[ret], x->fd_to_pos_key, ret); -  fd_mapper_store(x->pos_to_fd[x->last_swap], x->fd_to_pos_key, x->last_swap); -  return x->pos_to_fd[ret]; -  }else{ -  return -1; -  } - } -  - #endif /* FDLIB_USE_WAITFORMULTIPLEOBJECTS */ -  - #endif /* 0 */ + #endif