Skip to content
Snippets Groups Projects
Select Git revision
  • f30663bbcee9e9dd35093817ecbfa98d96424f74
  • master default protected
  • 9.0
  • 8.0
  • 7.8
  • 7.6
  • 7.4
  • 7.2
  • 7.0
  • 0.6
  • rosuav/latex-markdown-renderer
  • rxnpatch/rxnpatch
  • marcus/gobject-introspection
  • rxnpatch/8.0
  • rosuav/pre-listening-ports
  • nt-tools
  • rosuav/async-annotations
  • rosuav/pgsql-ssl
  • rxnpatch/rxnpatch-broken/2023-10-06T094250
  • grubba/fdlib
  • grubba/wip/sakura/8.0
  • v8.0.2006
  • v8.0.2004
  • v8.0.2002
  • v8.0.2000
  • v8.0.1998
  • v8.0.1996
  • v8.0.1994
  • v8.0.1992
  • v8.0.1990
  • v8.0.1988
  • v8.0.1986
  • rxnpatch/clusters/8.0/2025-04-29T124414
  • rxnpatch/2025-04-29T124414
  • v8.0.1984
  • v8.0.1982
  • v8.0.1980
  • v8.0.1978
  • v8.0.1976
  • v8.0.1974
  • v8.0.1972
41 results

fdlib.c

Blame
  • fdlib.c 45.78 KiB
    /*
    || 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.
    */
    
    #include "global.h"
    #include "fdlib.h"
    #include "pike_error.h"
    #include <math.h>
    #include <ctype.h>
    
    #ifdef HAVE_DIRECT_H
    #include <direct.h>
    #endif
    
    #if defined(HAVE_WINSOCK_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
    
    #include "threads.h"
    
    static MUTEX_T fd_mutex;
    
    HANDLE da_handle[MAX_OPEN_FILEDESCRIPTORS];
    int fd_type[MAX_OPEN_FILEDESCRIPTORS];
    int first_free_handle;
    
    /* #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);
    
    PMOD_EXPORT void set_errno_from_win32_error (unsigned long 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;
        }
    
        /* 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;
      }
    }
    
    #define ISSEPARATOR(a) ((a) == '\\' || (a) == '/')
    
    #ifdef PIKE_DEBUG
    static int IsValidHandle(HANDLE h)
    {
    #ifndef __GNUC__
      __try {
        HANDLE ret;
        if(DuplicateHandle(GetCurrentProcess(),
    			h,
    			GetCurrentProcess(),
    			&ret,
    			0,
    			0,
    			DUPLICATE_SAME_ACCESS))
        {
          CloseHandle(ret);
        }
      }
    
      __except (1) {
        return 0;
      }
    #endif /* !__GNUC__ */
    
      return 1;
    }
    
    PMOD_EXPORT HANDLE CheckValidHandle(HANDLE h)
    {
      if(!IsValidHandle(h))
        Pike_fatal("Invalid handle!\n");
      return h;
    }
    #endif
    
    PMOD_EXPORT char *debug_fd_info(int fd)
    {
      if(fd<0)
        return "BAD";
    
      if(fd > MAX_OPEN_FILEDESCRIPTORS)
        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])
      {
        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()
    {
      int e;
      WSADATA wsadata;
      
      mt_init(&fd_mutex);
      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));
      
      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++)
        fd_type[e]=e+1;
      fd_type[e]=FD_NO_MORE_FREE;
      mt_unlock(&fd_mutex);
    }
    
    void fd_exit()
    {
      WSACleanup();
      mt_destroy(&fd_mutex);
    }
    
    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
     *   the *current* DST status instead of the DST status at that time.
     *   So the FILETIME value can be off by one hour for FAT volumes.
     *
     *   Now the CRT _stat implementation uses FileTimeToLocalFileTime to
     *   convert this value back to local time - which suffers from the
     *   same DST bug. Then the undocumented function __loctotime_t is
     *   used to convert this local time to a time_t, and this function
     *   correctly accounts for DST at that time(*).
     *
     *   On FAT volumes, the conversion error while retrieving the
     *   FILETIME, and the conversion error of FileTimeToLocalFileTime
     *   exactly cancel each other out, so the final result of the _stat
     *   implementation is always correct.
     *
     *   On NTFS volumes however, there is no conversion error while
     *   retrieving the FILETIME because the file system already stores it
     *   as UTC, so the conversion error of FileTimeToLocalFileTime can
     *   cause the final result to be 1 hour off.
     *
     * The following implementation tries to do the correct thing based in
     * the filesystem type.
     *
     * Note also that the UTC timestamps are cached by the OS. In the case
     * of FAT volumes that means that the current DST setting might be
     * different when FileTimeToLocalFileTime runs than it was when the
     * FILETIME was calculated, so the calculation errors might not cancel
     * each other out shortly after DST changes. A reboot is necessary
     * after a DST change to ensure that no cached timestamps remain.
     * There is nothing we can do to correct this error. :(
     *
     * See also http://msdn.microsoft.com/library/en-us/sysinfo/base/file_times.asp
     *
     * *) Actually it doesn't in all cases: The overlapping hour(s) when
     * going from DST to normal time are ambiguous and since there's no
     * DST flag it has to make an arbitrary choice.
     */
    
    static time_t local_time_to_utc (time_t t)
    /* Converts a time_t containing local time to a real time_t (i.e. UTC).
     *
     * Note that there's an ambiguity in the autumn transition from DST to
     * normal time: The returned timestamp can be one hour off in the hour
     * where the clock is "turned back". This function always consider
     * that hour to be in normal time.
     *
     * Might return -1 if the time is outside the valid range.
     */
    {
      int tz_secs, dl_secs;
    
      /* First adjust for the time zone. */
    #ifdef HAVE__GET_TIMEZONE
      _get_timezone (&tz_secs);
    #else
      tz_secs = _timezone;
    #endif
      t += tz_secs;
    
      /* Then DST. */
    #ifdef HAVE__GET_DAYLIGHT
      _get_daylight (&dl_secs);
    #else
      dl_secs = _daylight;
    #endif
      if (dl_secs) {
        /* See if localtime thinks this is in DST. */
        int isdst;
    #ifdef HAVE_LOCALTIME_S
        struct tm ts;
        if (localtime_s (&ts, &t)) return -1;
        isdst = ts.tm_isdst;
    #else
        struct tm *ts;
        /* FIXME: A mutex is necessary to avoid a race here, but we
         * can't protect all calls to localtime anyway. */
        if (!(ts = localtime (&t))) return -1;
        isdst = ts->tm_isdst;
    #endif
        if (isdst) t += dl_secs;
      }
    
      return t;
    }
    
    static time_t fat_filetime_to_time_t (FILETIME *in)
    {
      FILETIME local_ft;
      if (!FileTimeToLocalFileTime (in, &local_ft)) {
        set_errno_from_win32_error (GetLastError());
        return -1;
      }
      else {
        time_t t = convert_filetime_to_time_t (&local_ft);
        /* Now we have a strange creature, namely a time_t that contains
         * local time. */
        return local_time_to_utc (t);
      }
    }
    
    static int fat_filetimes_to_stattimes (FILETIME *creation,
    				       FILETIME *last_access,
    				       FILETIME *last_write,
    				       PIKE_STAT_T *stat)
    {
      time_t t;
    
      if ((t = fat_filetime_to_time_t (last_write)) < 0)
        return -1;
      stat->st_mtime = t;
    
      if (last_access->dwLowDateTime || last_access->dwHighDateTime) {
        if ((t = fat_filetime_to_time_t (last_access)) < 0)
          return -1;
        stat->st_atime = t;
      }
      else
        stat->st_atime = stat->st_mtime;
    
      /* Putting the creation time in the last status change field.. :\ */
      if (creation->dwLowDateTime || creation->dwHighDateTime) {
        if ((t = fat_filetime_to_time_t (creation)) < 0)
          return -1;
        stat->st_ctime = t;
      }
      else
        stat->st_ctime = stat->st_mtime;
    
      return 0;
    }
    
    static void nonfat_filetimes_to_stattimes (FILETIME *creation,
    					   FILETIME *last_access,
    					   FILETIME *last_write,
    					   PIKE_STAT_T *stat)
    {
      stat->st_mtime = convert_filetime_to_time_t (last_write);
    
      if (last_access->dwLowDateTime ||
          last_access->dwHighDateTime)
        stat->st_atime = convert_filetime_to_time_t(last_access);
      else
        stat->st_atime = stat->st_mtime;
    
      /* Putting the creation time in the last status change field.. :\ */
      if (creation->dwLowDateTime ||
          creation->dwHighDateTime)
        stat->st_ctime = convert_filetime_to_time_t (creation);
      else
        stat->st_ctime = stat->st_mtime;
    }
    
    /*
     * 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)
    {
      /* root UNC names start with 2 slashes */
      if ( strlen(path) >= 5 &&     /* minimum string is "//x/y" */
           ISSEPARATOR(path[0]) &&
           ISSEPARATOR(path[1]) )
      {
        char * 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 )
            if ( ISSEPARATOR(*p) )
              break ;
          
          /* final slash (if any) */
          if ( !*p || !p[1])
            return 1;
        }
      }
      
      return 0 ;
    }
    
    /* 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)
    {
      ptrdiff_t l = strlen(file);
      char fname[MAX_PATH];
      int drive;       		/* current = -1, A: = 0, B: = 1, ... */
      HANDLE hFind;
      WIN32_FIND_DATA findbuf;
    
      if(ISSEPARATOR (file[l-1]))
      {
        do l--;
        while(l && ISSEPARATOR (file[l]));
        l++;
        if(l+1 > sizeof(fname))
        {
          errno=EINVAL;
          return -1;
        }
        MEMCPY(fname, file, l);
        fname[l]=0;
        file=fname;
      }
    
      /* don't allow wildcards */
      if (strpbrk(file, "?*"))
      {
        errno = ENOENT;
        return(-1);
      }
      
      /* get disk from file */
      if (file[1] == ':')
        drive = toupper(*file) - 'A';
      else
        drive = -1;
    
      STATDEBUG (fprintf (stderr, "fd_stat %s drive %d\n", file, drive));
    
      /* get info for file */
      hFind = FindFirstFile(file, &findbuf);
    
      if ( hFind == INVALID_HANDLE_VALUE )
      {
        char abspath[_MAX_PATH + 1];
        UINT drive_type;
    
        STATDEBUG (fprintf (stderr, "check root dir\n"));
    
        if (!strpbrk(file, ":./\\")) {
          STATDEBUG (fprintf (stderr, "no path separators\n"));
          errno = ENOENT;
          return -1;
        }
    
        if (!_fullpath( abspath, file, _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));
          errno = ENOENT;
          return -1;
        }
    
        STATDEBUG (fprintf (stderr, "abspath: %s\n", abspath));
    
        l = strlen (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);
        if (drive_type == DRIVE_UNKNOWN || drive_type == DRIVE_NO_ROOT_DIR) {
          STATDEBUG (fprintf (stderr, "invalid drive type: %u\n", drive_type));
          errno = ENOENT;
          return -1;
        }
    
        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
         * stat function in MS CRT. */
        {
          struct tm t;
          t.tm_year = 1980 - 1900;
          t.tm_mon  = 0;
          t.tm_mday = 1;
          t.tm_hour = 0;
          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];
        /* 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));
        }
        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 : "-"));
    
        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 (!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
          /* Any non-fat filesystem is assumed to have sane timestamps. */
          nonfat_filetimes_to_stattimes (&findbuf.ftCreationTime,
    				     &findbuf.ftLastAccessTime,
    				     &findbuf.ftLastWriteTime,
    				     buf);
    
        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. */
        }
      }
    
      /* 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 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];
    
      if(ISSEPARATOR (file[l-1]))
      {
        do l--;
        while(l && ISSEPARATOR (file[l]));
        l++;
        if(l+1 > sizeof(fname))
        {
          errno=EINVAL;
          return -1;
        }
        MEMCPY(fname, file, l);
        fname[l]=0;
        file=fname;
      }
    
      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;
        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;
          break;
    
        case fd_TRUNC:
        case fd_TRUNC | fd_EXCL:
          cmode=TRUNCATE_EXISTING;
          break;
    
        case fd_CREAT:
          cmode=OPEN_ALWAYS; break;
    
        case fd_CREAT | fd_EXCL:
        case fd_CREAT | fd_EXCL | fd_TRUNC:
          cmode=CREATE_NEW;
          break;
    
        case 0:
        case fd_EXCL:
          cmode=OPEN_EXISTING;
          break;
      }
    
      if(create_mode & 0222)
      {
        amode=FILE_ATTRIBUTE_NORMAL;
      }else{
        amode=FILE_ATTRIBUTE_READONLY;
      }
        
      x=CreateFile(file,
    	       omode,
    	       FILE_SHARE_READ | FILE_SHARE_WRITE,
    	       NULL,
    	       cmode,
    	       amode,
    	       NULL);
    
      
      if(x == DO_NOT_WARN(INVALID_HANDLE_VALUE))
      {
        unsigned long err = GetLastError();
        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);
    
      fd=first_free_handle;
      first_free_handle=fd_type[fd];
      fd_type[fd]=FD_FILE;
      da_handle[fd] = x;
    
      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));
    
      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);
    
      s=socket(domain, type, proto);
    
      if(s==INVALID_SOCKET)
      {
        set_errno_from_win32_error (WSAGetLastError());
        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);
    
      FDDEBUG(fprintf(stderr,"New socket: %d (%d)\n",fd,s));
    
      return fd;
    }
    
    PMOD_EXPORT int debug_fd_pipe(int fds[2] DMALLOC_LINE_ARGS)
    {
      HANDLE files[2];
      mt_lock(&fd_mutex);
      if(first_free_handle == FD_NO_MORE_FREE)
      {
        mt_unlock(&fd_mutex);
        errno=EMFILE;
        return -1;
      }
      mt_unlock(&fd_mutex);
      if(!CreatePipe(&files[0], &files[1], NULL, 0))
      {
        set_errno_from_win32_error (GetLastError());
        return -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];
    
      fds[1]=first_free_handle;
      first_free_handle=fd_type[fds[1]];
      fd_type[fds[1]]=FD_PIPE;
      da_handle[fds[1]] = files[1];
    
      mt_unlock(&fd_mutex);
      FDDEBUG(fprintf(stderr,"New pipe: %d (%d) -> %d (%d)\n",fds[0],files[0], fds[1], fds[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,"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);
      if(s==INVALID_SOCKET)
      {
        set_errno_from_win32_error (WSAGetLastError());
        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;
    
      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)));
    
      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, #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); \
      ret = NAME X2; \
      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) )
    
    #define SOCKFUN2(NAME,T1,T2) \
       SOCKFUN(NAME, (FD fd, T1 a, T2 b), (s, a, b) )
    
    #define SOCKFUN3(NAME,T1,T2,T3) \
       SOCKFUN(NAME, (FD fd, T1 a, T2 b, T3 c), (s, a, b, c) )
    
    #define SOCKFUN4(NAME,T1,T2,T3,T4) \
       SOCKFUN(NAME, (FD fd, T1 a, T2 b, T3 c, T4 d), (s, a, b, c, d) )
    
    #define SOCKFUN5(NAME,T1,T2,T3,T4,T5) \
       SOCKFUN(NAME, (FD fd, T1 a, T2 b, T3 c, T4 d, T5 e), (s, a, b, c, d, e))
    
    
    SOCKFUN2(bind, struct sockaddr *, int)
    SOCKFUN4(getsockopt,int,int,void*,ACCEPT_SIZE_T *)
    SOCKFUN4(setsockopt,int,int,void*,int)
    SOCKFUN3(recv,void *,int,int)
    SOCKFUN2(getsockname,struct sockaddr *,ACCEPT_SIZE_T *)
    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, "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)
      {
        mt_unlock(&fd_mutex);
        errno=ENOTSUPP;
        return -1; 
      } 
      ret=(SOCKET)da_handle[fd];
      mt_unlock(&fd_mutex);
      ret=connect(ret,a,len); 
      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);
    }
    
    PMOD_EXPORT int debug_fd_close(FD fd)
    {
      HANDLE h;
      int type;
      mt_lock(&fd_mutex);
      h = da_handle[fd];
      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);
      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));
    	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);
    
      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, "Writing %d bytes to %d (%d)\n",
    		  len, fd, da_handle[fd]));
      kind = fd_type[fd];
      handle = da_handle[fd];
      mt_unlock(&fd_mutex);
      
      switch(kind)
      {
        case FD_SOCKET:
          {
    	ptrdiff_t ret = send((SOCKET)handle, buf,
    			     DO_NOT_WARN((int)len),
    			     0);
    	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)
    	{
    	  set_errno_from_win32_error (GetLastError());
    	  FDDEBUG(fprintf(stderr, "Write on %d failed (%d)\n", fd, errno));
    	  return -1;
    	}
    	FDDEBUG(fprintf(stderr, "Wrote %ld bytes to %d)\n", (long)ret, fd));
    	return ret;
          }
    
        default:
          errno=ENOTSUPP;
          return -1;
      }
    }
    
    PMOD_EXPORT ptrdiff_t debug_fd_read(FD fd, void *to, ptrdiff_t len)
    {
      DWORD ret;
      ptrdiff_t rret;
      HANDLE handle;
    
      mt_lock(&fd_mutex);
      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);
    
      switch(ret)
      {
        case FD_SOCKET:
          rret=recv((SOCKET)handle, to,
    		DO_NOT_WARN((int)len),
    		0);
          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)
          {
    	unsigned int err = GetLastError();
    	set_errno_from_win32_error (err);
    	switch(err)
    	{
    	  /* Pretend we reached the end of the file */
    	  case ERROR_BROKEN_PIPE:
    	    return 0;
    	}
    	FDDEBUG(fprintf(stderr,"Read failed %d\n",errno));
    	return -1;
          }
          FDDEBUG(fprintf(stderr,"Read on %d returned %ld\n",fd,ret));
          return ret;
    
        default:
          errno=ENOTSUPP;
          return -1;
      }
    }
    
    PMOD_EXPORT PIKE_OFF_T debug_fd_lseek(FD fd, PIKE_OFF_T pos, int where)
    {
      PIKE_OFF_T ret;
      HANDLE h;
    
      mt_lock(&fd_mutex);
      if(fd_type[fd]!=FD_FILE)
      {
        mt_unlock(&fd_mutex);
        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());
          return -1;
        }
        ret = li_ret.QuadPart;
    #else /* !HAVE_SETFILEPOINTEREX */
        /* Windows 9x based. */
        LONG high = DO_NOT_WARN((LONG)(pos >> 32));
        DWORD err;
        pos &= ((INT64) 1 << 32) - 1;
        ret = SetFilePointer(h, DO_NOT_WARN((LONG)pos), &high, where);
        if (ret == INVALID_SET_FILE_POINTER &&
    	(err = GetLastError()) != NO_ERROR) {
          set_errno_from_win32_error (err);
          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 */
      }
    
      return ret;
    }
    
    PMOD_EXPORT int debug_fd_ftruncate(FD fd, PIKE_OFF_T len)
    {
      HANDLE h;
      LONG oldfp_lo, oldfp_hi, len_hi;
      DWORD err;
    
      mt_lock(&fd_mutex);
      if(fd_type[fd]!=FD_FILE)
      {
        mt_unlock(&fd_mutex);
        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) {
          set_errno_from_win32_error (err);
          return -1;
        }
      }
    
    #ifdef INT64
      len_hi = DO_NOT_WARN ((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) ==
          INVALID_SET_FILE_POINTER &&
          (err = GetLastError()) != NO_ERROR) {
        SetFilePointer(h, oldfp_lo, &oldfp_hi, FILE_BEGIN);
        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);
        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) {
    	set_errno_from_win32_error (err);
    	return -1;
          }
        }
    
      return 0;
    }
    
    PMOD_EXPORT int debug_fd_flock(FD fd, int oper)
    {
      long ret;
      HANDLE h;
      mt_lock(&fd_mutex);
      if(fd_type[fd]!=FD_FILE)
      {
        mt_unlock(&fd_mutex);
        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));
        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);
      }
      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)
    {
      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)
      {
        errno=ENOTSUPP;
        mt_unlock(&fd_mutex);
        return -1;
      }
    
      MEMSET(s, 0, sizeof(PIKE_STAT_T));
      s->st_nlink=1;
    
      switch(fd_type[fd])
      {
        case FD_SOCKET:
          s->st_mode=S_IFSOCK;
          break;
    
        default:
          switch(GetFileType(da_handle[fd]))
          {
    	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);
    	    if (s->st_size == INVALID_FILE_SIZE &&
    		(err = GetLastError()) != NO_ERROR) {
    	      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))
    	  {
    	    set_errno_from_win32_error (GetLastError());
    	    mt_unlock(&fd_mutex);
    	    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;
          }
      }
      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++)
        {
          if(FD_ISSET(da_handle[e],x))
          {
    	if(!first) fprintf(stderr,",",e);
    	fprintf(stderr,"%d",e);
    	first=0;
          }
        }
        fprintf(stderr,"]");
      }else{
        fprintf(stderr,"0");
      }
    }
    #endif
    
    /* FIXME:
     * select with no fds should call Sleep()
     * If the backend works correctly, fds is zero when there are no fds.
     * /Hubbe
     */
    PMOD_EXPORT int debug_fd_select(int fds, FD_SET *a, FD_SET *b, FD_SET *c, struct timeval *t)
    {
      int ret;
    
      FDDEBUG(
        int e;
        fprintf(stderr,"Select(%d,",fds);
        dump_FDSET(a,fds);
        dump_FDSET(b,fds);
        dump_FDSET(c,fds);
        fprintf(stderr,",(%ld,%06ld));\n", (long) t->tv_sec,(long) t->tv_usec);
        )
    
      ret=select(fds,a,b,c,t);
      if(ret==SOCKET_ERROR)
      {
        set_errno_from_win32_error (WSAGetLastError());
        FDDEBUG(fprintf(stderr,"select->%d, errno=%d\n",ret,errno));
        return -1;
      }
    
      FDDEBUG(
        fprintf(stderr,"    ->(%d,",fds);
        dump_FDSET(a,fds);
        dump_FDSET(b,fds);
        dump_FDSET(c,fds);
        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;
      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);
          FDDEBUG(fprintf(stderr,"ioctlsocket returned %ld (%d)\n",ret,errno));
          if(ret==SOCKET_ERROR)
          {
    	set_errno_from_win32_error (WSAGetLastError());
    	return -1;
          }
          return ret;
    
        default:
          errno=ENOTSUPP;
          return -1;
      }
    }
    
    
    PMOD_EXPORT FD debug_fd_dup(FD from)
    {
      FD fd;
      HANDLE 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))
      {
        set_errno_from_win32_error (GetLastError());
        mt_unlock(&fd_mutex);
        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);
      
      FDDEBUG(fprintf(stderr,"Dup %d (%ld) to %d (%d)\n",
    		  from, PTRDIFF_T_TO_LONG((ptrdiff_t)da_handle[from]), fd, x));
      return fd;
    }
    
    PMOD_EXPORT FD debug_fd_dup2(FD from, FD to)
    {
      HANDLE 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);
        return -1;
      }
    
      if(fd_type[to] < FD_NO_MORE_FREE)
      {
        if(!CloseHandle(da_handle[to]))
        {
          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;
          }
        }
      }
      fd_type[to]=fd_type[from];
      da_handle[to] = x;
      mt_unlock(&fd_mutex);
    
      FDDEBUG(fprintf(stderr,"Dup2 %d (%d) to %d (%d)\n",
    		  from, PTRDIFF_T_TO_LONG((ptrdiff_t)da_handle[from]), 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 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;
    	}
          }
        }
        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) {
    	int i;
    	char *buf = cp;
    	int got_zeros = 0;
    	for (i=0; i < 8; i++) {
    	  size_t val = (q[0]<<8) | q[1];
    	  if (!val) {
    	    if (!got_zeros) {
    	      snprintf(buf, sz, ":");
    	      got_zeros = 1;
    	      goto next;
    	    } else if (got_zeros == 1) goto next;
    	  }
    	  got_zeros |= got_zeros << 1;
    	  if (i) {
    	    snprintf(buf, sz, ":%x", val);
    	  } else {
    	    snprintf(buf, sz, "%x", val);
    	  }
    	next:
    	  sz -= strlen(buf);
    	  buf += strlen(buf);
    	  q += 2;
    	}
    	if (got_zeros == 1) {
    	  snprintf(buf, sz, ":");
    	  sz -= strlen(buf);
    	}
    	return cp;
    #endif
          }
          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);
      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;
      }
      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;
      }
    }
    
    PMOD_EXPORT void closedir(DIR *dir)
    {
      FindClose(dir->h);
      free((char *)dir);
    }
    #endif
    
    #if 0
    
    #ifdef FD_LINEAR
    struct fd_mapper
    {
      int size;
      void **data;
    };
    
    void init_fd_mapper(struct fd_mapper *x)
    {
      x->size=64;
      x->data=(void **)xalloc(x->size*sizeof(void *));
    }
    
    void exit_fd_mapper(struct fd_mapper *x)
    {
      free((char *)x->data);
    }
    
    void fd_mapper_set(struct fd_mapper *x, FD fd, void *data)
    {
      while(fd>=x->size)
      {
        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)
    {
      return x->data[fd];
    }
    #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;
    }
    
    void exit_fd_mapper(struct fd_mapper *x)
    {
      free((char *)x->data);
    }
    
    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);
      }
    
      hval=fd % x->hsize;
      while(x->data[hval].fd != -1)
      {
        hval++;
        if(hval==x->hsize) hval=0;
      }
      x->data[hval].fd=fd;
      x->data[hval].data=data;
    }
    
    void *fd_mapper_get(struct fd_mapper *x, FD fd)
    {
      int hval=fd % x->hsize;
      while(x->data[hval].fd != fd)
      {
        hval++;
        if(hval==x->hsize) hval=0;
      }
      return x->data[hval].data;
    }
    #endif /* FD_LINEAR */
    
    
    struct fd_data_hash
    {
      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;
    }
    
    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 */