+#endif
+
+ info = lookup_volume_info (root_dir);
+
+ if (info == NULL || ! VOLINFO_STILL_VALID (root_dir, info))
+ {
+ char name[ 256 ];
+ DWORD serialnum;
+ DWORD maxcomp;
+ DWORD flags;
+ char type[ 256 ];
+
+ /* Info is not cached, or is stale. */
+ if (!GetVolumeInformation (root_dir,
+ name, sizeof (name),
+ &serialnum,
+ &maxcomp,
+ &flags,
+ type, sizeof (type)))
+ return NULL;
+
+ /* Cache the volume information for future use, overwriting existing
+ entry if present. */
+ if (info == NULL)
+ {
+ info = (volume_info_data *) xmalloc (sizeof (volume_info_data));
+ add_volume_info (root_dir, info);
+ }
+ else
+ {
+ xfree (info->name);
+ xfree (info->type);
+ }
+
+ info->name = xstrdup (name);
+ info->serialnum = serialnum;
+ info->maxcomp = maxcomp;
+ info->flags = flags;
+ info->type = xstrdup (type);
+ info->timestamp = GetTickCount ();
+ }
+
+ return info;
+}
+
+/* Get information on the volume where name is held; set path pointer to
+ start of pathname in name (past UNC header\volume header if present). */
+int
+get_volume_info (const char * name, const char ** pPath)
+{
+ char temp[MAX_PATH];
+ char *rootname = NULL; /* default to current volume */
+ volume_info_data * info;
+
+ if (name == NULL)
+ return FALSE;
+
+ /* find the root name of the volume if given */
+ if (isalpha (name[0]) && name[1] == ':')
+ {
+ rootname = temp;
+ temp[0] = *name++;
+ temp[1] = *name++;
+ temp[2] = '\\';
+ temp[3] = 0;
+ }
+ else if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1]))
+ {
+ char *str = temp;
+ int slashes = 4;
+ rootname = temp;
+ do
+ {
+ if (IS_DIRECTORY_SEP (*name) && --slashes == 0)
+ break;
+ *str++ = *name++;
+ }
+ while ( *name );
+
+ *str++ = '\\';
+ *str = 0;
+ }
+
+ if (pPath)
+ *pPath = name;
+
+ info = GetCachedVolumeInformation (rootname);
+ if (info != NULL)
+ {
+ /* Set global referenced by other functions. */
+ volume_info = *info;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* Determine if volume is FAT format (ie. only supports short 8.3
+ names); also set path pointer to start of pathname in name. */
+int
+is_fat_volume (const char * name, const char ** pPath)
+{
+ if (get_volume_info (name, pPath))
+ return (volume_info.maxcomp == 12);
+ return FALSE;
+}
+
+/* Map filename to a legal 8.3 name if necessary. */
+const char *
+map_w32_filename (const char * name, const char ** pPath)
+{
+ static char shortname[MAX_PATH];
+ char * str = shortname;
+ char c;
+ char * path;
+ const char * save_name = name;
+
+ if (strlen (name) >= MAX_PATH)
+ {
+ /* Return a filename which will cause callers to fail. */
+ strcpy (shortname, "?");
+ return shortname;
+ }
+
+ if (is_fat_volume (name, (const char **)&path)) /* truncate to 8.3 */
+ {
+ register int left = 8; /* maximum number of chars in part */
+ register int extn = 0; /* extension added? */
+ register int dots = 2; /* maximum number of dots allowed */
+
+ while (name < path)
+ *str++ = *name++; /* skip past UNC header */
+
+ while ((c = *name++))
+ {
+ switch ( c )
+ {
+ case '\\':
+ case '/':
+ *str++ = '\\';
+ extn = 0; /* reset extension flags */
+ dots = 2; /* max 2 dots */
+ left = 8; /* max length 8 for main part */
+ break;
+ case ':':
+ *str++ = ':';
+ extn = 0; /* reset extension flags */
+ dots = 2; /* max 2 dots */
+ left = 8; /* max length 8 for main part */
+ break;
+ case '.':
+ if ( dots )
+ {
+ /* Convert path components of the form .xxx to _xxx,
+ but leave . and .. as they are. This allows .emacs
+ to be read as _emacs, for example. */
+
+ if (! *name ||
+ *name == '.' ||
+ IS_DIRECTORY_SEP (*name))
+ {
+ *str++ = '.';
+ dots--;
+ }
+ else
+ {
+ *str++ = '_';
+ left--;
+ dots = 0;
+ }
+ }
+ else if ( !extn )
+ {
+ *str++ = '.';
+ extn = 1; /* we've got an extension */
+ left = 3; /* 3 chars in extension */
+ }
+ else
+ {
+ /* any embedded dots after the first are converted to _ */
+ *str++ = '_';
+ }
+ break;
+ case '~':
+ case '#': /* don't lose these, they're important */
+ if ( ! left )
+ str[-1] = c; /* replace last character of part */
+ /* FALLTHRU */
+ default:
+ if ( left )
+ {
+ *str++ = tolower (c); /* map to lower case (looks nicer) */
+ left--;
+ dots = 0; /* started a path component */
+ }
+ break;
+ }
+ }
+ *str = '\0';
+ }
+ else
+ {
+ strcpy (shortname, name);
+ unixtodos_filename (shortname);
+ }
+
+ if (pPath)
+ *pPath = shortname + (path - save_name);
+
+ return shortname;
+}
+
+static int
+is_exec (const char * name)
+{
+ char * p = strrchr (name, '.');
+ return
+ (p != NULL
+ && (stricmp (p, ".exe") == 0 ||
+ stricmp (p, ".com") == 0 ||
+ stricmp (p, ".bat") == 0 ||
+ stricmp (p, ".cmd") == 0));
+}
+
+/* Emulate the Unix directory procedures opendir, closedir,
+ and readdir. We can't use the procedures supplied in sysdep.c,
+ so we provide them here. */
+
+struct direct dir_static; /* simulated directory contents */
+static HANDLE dir_find_handle = INVALID_HANDLE_VALUE;
+static int dir_is_fat;
+static char dir_pathname[MAXPATHLEN+1];
+static WIN32_FIND_DATA dir_find_data;
+
+/* Support shares on a network resource as subdirectories of a read-only
+ root directory. */
+static HANDLE wnet_enum_handle = INVALID_HANDLE_VALUE;
+HANDLE open_unc_volume (const char *);
+char *read_unc_volume (HANDLE, char *, int);
+void close_unc_volume (HANDLE);
+
+DIR *
+opendir (char *filename)
+{
+ DIR *dirp;
+
+ /* Opening is done by FindFirstFile. However, a read is inherent to
+ this operation, so we defer the open until read time. */
+
+ if (dir_find_handle != INVALID_HANDLE_VALUE)
+ return NULL;
+ if (wnet_enum_handle != INVALID_HANDLE_VALUE)
+ return NULL;
+
+ if (is_unc_volume (filename))
+ {
+ wnet_enum_handle = open_unc_volume (filename);
+ if (wnet_enum_handle == INVALID_HANDLE_VALUE)
+ return NULL;
+ }
+
+ if (!(dirp = (DIR *) malloc (sizeof (DIR))))
+ return NULL;
+
+ dirp->dd_fd = 0;
+ dirp->dd_loc = 0;
+ dirp->dd_size = 0;
+
+ strncpy (dir_pathname, map_w32_filename (filename, NULL), MAXPATHLEN);
+ dir_pathname[MAXPATHLEN] = '\0';
+ dir_is_fat = is_fat_volume (filename, NULL);
+
+ return dirp;
+}
+
+void
+closedir (DIR *dirp)
+{
+ /* If we have a find-handle open, close it. */
+ if (dir_find_handle != INVALID_HANDLE_VALUE)
+ {
+ FindClose (dir_find_handle);
+ dir_find_handle = INVALID_HANDLE_VALUE;
+ }
+ else if (wnet_enum_handle != INVALID_HANDLE_VALUE)
+ {
+ close_unc_volume (wnet_enum_handle);
+ wnet_enum_handle = INVALID_HANDLE_VALUE;
+ }
+ xfree ((char *) dirp);
+}
+
+struct direct *
+readdir (DIR *dirp)
+{
+ if (wnet_enum_handle != INVALID_HANDLE_VALUE)
+ {
+ if (!read_unc_volume (wnet_enum_handle,
+ dir_find_data.cFileName,
+ MAX_PATH))
+ return NULL;
+ }
+ /* If we aren't dir_finding, do a find-first, otherwise do a find-next. */
+ else if (dir_find_handle == INVALID_HANDLE_VALUE)
+ {
+ char filename[MAXNAMLEN + 3];
+ int ln;
+
+ strcpy (filename, dir_pathname);
+ ln = strlen (filename) - 1;
+ if (!IS_DIRECTORY_SEP (filename[ln]))
+ strcat (filename, "\\");
+ strcat (filename, "*");
+
+ dir_find_handle = FindFirstFile (filename, &dir_find_data);
+
+ if (dir_find_handle == INVALID_HANDLE_VALUE)
+ return NULL;
+ }
+ else
+ {
+ if (!FindNextFile (dir_find_handle, &dir_find_data))
+ return NULL;
+ }
+
+ /* Emacs never uses this value, so don't bother making it match
+ value returned by stat(). */
+ dir_static.d_ino = 1;
+
+ dir_static.d_reclen = sizeof (struct direct) - MAXNAMLEN + 3 +
+ dir_static.d_namlen - dir_static.d_namlen % 4;
+
+ dir_static.d_namlen = strlen (dir_find_data.cFileName);
+ strcpy (dir_static.d_name, dir_find_data.cFileName);
+ if (dir_is_fat)
+ _strlwr (dir_static.d_name);
+ else if (!NILP (Vw32_downcase_file_names))
+ {
+ register char *p;
+ for (p = dir_static.d_name; *p; p++)
+ if (*p >= 'a' && *p <= 'z')
+ break;
+ if (!*p)
+ _strlwr (dir_static.d_name);
+ }
+
+ return &dir_static;
+}
+
+HANDLE
+open_unc_volume (const char *path)
+{
+ NETRESOURCE nr;
+ HANDLE henum;
+ int result;
+
+ nr.dwScope = RESOURCE_GLOBALNET;
+ nr.dwType = RESOURCETYPE_DISK;
+ nr.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER;
+ nr.dwUsage = RESOURCEUSAGE_CONTAINER;
+ nr.lpLocalName = NULL;
+ nr.lpRemoteName = (LPSTR)map_w32_filename (path, NULL);
+ nr.lpComment = NULL;
+ nr.lpProvider = NULL;
+
+ result = WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_DISK,
+ RESOURCEUSAGE_CONNECTABLE, &nr, &henum);
+
+ if (result == NO_ERROR)
+ return henum;
+ else
+ return INVALID_HANDLE_VALUE;
+}
+
+char *
+read_unc_volume (HANDLE henum, char *readbuf, int size)
+{
+ DWORD count;
+ int result;
+ DWORD bufsize = 512;
+ char *buffer;
+ char *ptr;
+
+ count = 1;
+ buffer = alloca (bufsize);
+ result = WNetEnumResource (wnet_enum_handle, &count, buffer, &bufsize);
+ if (result != NO_ERROR)
+ return NULL;
+
+ /* WNetEnumResource returns \\resource\share...skip forward to "share". */
+ ptr = ((LPNETRESOURCE) buffer)->lpRemoteName;
+ ptr += 2;
+ while (*ptr && !IS_DIRECTORY_SEP (*ptr)) ptr++;
+ ptr++;
+
+ strncpy (readbuf, ptr, size);
+ return readbuf;
+}
+
+void
+close_unc_volume (HANDLE henum)
+{
+ if (henum != INVALID_HANDLE_VALUE)
+ WNetCloseEnum (henum);
+}
+
+DWORD
+unc_volume_file_attributes (const char *path)
+{
+ HANDLE henum;
+ DWORD attrs;
+
+ henum = open_unc_volume (path);
+ if (henum == INVALID_HANDLE_VALUE)
+ return -1;
+
+ attrs = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_DIRECTORY;
+
+ close_unc_volume (henum);
+
+ return attrs;
+}
+
+
+/* Shadow some MSVC runtime functions to map requests for long filenames
+ to reasonable short names if necessary. This was originally added to
+ permit running Emacs on NT 3.1 on a FAT partition, which doesn't support
+ long file names. */
+
+int
+sys_access (const char * path, int mode)
+{
+ DWORD attributes;
+
+ /* MSVC implementation doesn't recognize D_OK. */
+ path = map_w32_filename (path, NULL);
+ if (is_unc_volume (path))
+ {
+ attributes = unc_volume_file_attributes (path);
+ if (attributes == -1) {
+ errno = EACCES;
+ return -1;
+ }
+ }
+ else if ((attributes = GetFileAttributes (path)) == -1)
+ {
+ /* Should try mapping GetLastError to errno; for now just indicate
+ that path doesn't exist. */
+ errno = EACCES;
+ return -1;
+ }
+ if ((mode & X_OK) != 0 && !is_exec (path))
+ {
+ errno = EACCES;
+ return -1;
+ }
+ if ((mode & W_OK) != 0 && (attributes & FILE_ATTRIBUTE_READONLY) != 0)
+ {
+ errno = EACCES;
+ return -1;
+ }
+ if ((mode & D_OK) != 0 && (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
+ {
+ errno = EACCES;
+ return -1;
+ }
+ return 0;
+}
+
+int
+sys_chdir (const char * path)
+{
+ return _chdir (map_w32_filename (path, NULL));
+}
+
+int
+sys_chmod (const char * path, int mode)
+{
+ return _chmod (map_w32_filename (path, NULL), mode);
+}
+
+int
+sys_chown (const char *path, uid_t owner, gid_t group)
+{
+ if (sys_chmod (path, _S_IREAD) == -1) /* check if file exists */
+ return -1;
+ return 0;
+}
+
+int
+sys_creat (const char * path, int mode)
+{
+ return _creat (map_w32_filename (path, NULL), mode);
+}
+
+FILE *
+sys_fopen(const char * path, const char * mode)
+{
+ int fd;
+ int oflag;
+ const char * mode_save = mode;
+
+ /* Force all file handles to be non-inheritable. This is necessary to
+ ensure child processes don't unwittingly inherit handles that might
+ prevent future file access. */
+
+ if (mode[0] == 'r')
+ oflag = O_RDONLY;
+ else if (mode[0] == 'w' || mode[0] == 'a')
+ oflag = O_WRONLY | O_CREAT | O_TRUNC;
+ else
+ return NULL;
+
+ /* Only do simplistic option parsing. */
+ while (*++mode)
+ if (mode[0] == '+')
+ {
+ oflag &= ~(O_RDONLY | O_WRONLY);
+ oflag |= O_RDWR;
+ }
+ else if (mode[0] == 'b')
+ {
+ oflag &= ~O_TEXT;
+ oflag |= O_BINARY;
+ }
+ else if (mode[0] == 't')
+ {
+ oflag &= ~O_BINARY;
+ oflag |= O_TEXT;
+ }
+ else break;
+
+ fd = _open (map_w32_filename (path, NULL), oflag | _O_NOINHERIT, 0644);
+ if (fd < 0)
+ return NULL;
+
+ return _fdopen (fd, mode_save);
+}
+
+/* This only works on NTFS volumes, but is useful to have. */
+int
+sys_link (const char * old, const char * new)
+{
+ HANDLE fileh;
+ int result = -1;
+ char oldname[MAX_PATH], newname[MAX_PATH];
+
+ if (old == NULL || new == NULL)
+ {
+ errno = ENOENT;
+ return -1;
+ }
+
+ strcpy (oldname, map_w32_filename (old, NULL));
+ strcpy (newname, map_w32_filename (new, NULL));
+
+ fileh = CreateFile (oldname, 0, 0, NULL, OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ if (fileh != INVALID_HANDLE_VALUE)
+ {
+ int wlen;
+
+ /* Confusingly, the "alternate" stream name field does not apply
+ when restoring a hard link, and instead contains the actual
+ stream data for the link (ie. the name of the link to create).
+ The WIN32_STREAM_ID structure before the cStreamName field is
+ the stream header, which is then immediately followed by the
+ stream data. */
+
+ struct {
+ WIN32_STREAM_ID wid;
+ WCHAR wbuffer[MAX_PATH]; /* extra space for link name */
+ } data;
+
+ wlen = MultiByteToWideChar (CP_ACP, MB_PRECOMPOSED, newname, -1,
+ data.wid.cStreamName, MAX_PATH);
+ if (wlen > 0)
+ {
+ LPVOID context = NULL;
+ DWORD wbytes = 0;
+
+ data.wid.dwStreamId = BACKUP_LINK;
+ data.wid.dwStreamAttributes = 0;
+ data.wid.Size.LowPart = wlen * sizeof(WCHAR);
+ data.wid.Size.HighPart = 0;
+ data.wid.dwStreamNameSize = 0;
+
+ if (BackupWrite (fileh, (LPBYTE)&data,
+ offsetof (WIN32_STREAM_ID, cStreamName)
+ + data.wid.Size.LowPart,
+ &wbytes, FALSE, FALSE, &context)
+ && BackupWrite (fileh, NULL, 0, &wbytes, TRUE, FALSE, &context))
+ {
+ /* succeeded */
+ result = 0;
+ }
+ else
+ {
+ /* Should try mapping GetLastError to errno; for now just
+ indicate a general error (eg. links not supported). */
+ errno = EINVAL; // perhaps EMLINK?
+ }
+ }
+
+ CloseHandle (fileh);
+ }
+ else
+ errno = ENOENT;
+
+ return result;
+}
+
+int
+sys_mkdir (const char * path)
+{
+ return _mkdir (map_w32_filename (path, NULL));
+}
+
+/* Because of long name mapping issues, we need to implement this
+ ourselves. Also, MSVC's _mktemp returns NULL when it can't generate
+ a unique name, instead of setting the input template to an empty
+ string.
+
+ Standard algorithm seems to be use pid or tid with a letter on the
+ front (in place of the 6 X's) and cycle through the letters to find a
+ unique name. We extend that to allow any reasonable character as the
+ first of the 6 X's. */
+char *
+sys_mktemp (char * template)
+{
+ char * p;
+ int i;
+ unsigned uid = GetCurrentThreadId ();
+ static char first_char[] = "abcdefghijklmnopqrstuvwyz0123456789!%-_@#";
+
+ if (template == NULL)
+ return NULL;
+ p = template + strlen (template);
+ i = 5;
+ /* replace up to the last 5 X's with uid in decimal */
+ while (--p >= template && p[0] == 'X' && --i >= 0)
+ {
+ p[0] = '0' + uid % 10;
+ uid /= 10;
+ }
+
+ if (i < 0 && p[0] == 'X')
+ {
+ i = 0;
+ do
+ {
+ int save_errno = errno;
+ p[0] = first_char[i];
+ if (sys_access (template, 0) < 0)
+ {
+ errno = save_errno;
+ return template;
+ }
+ }
+ while (++i < sizeof (first_char));
+ }
+
+ /* Template is badly formed or else we can't generate a unique name,
+ so return empty string */
+ template[0] = 0;
+ return template;
+}
+
+int
+sys_open (const char * path, int oflag, int mode)
+{
+ const char* mpath = map_w32_filename (path, NULL);
+ /* Try to open file without _O_CREAT, to be able to write to hidden
+ and system files. Force all file handles to be
+ non-inheritable. */
+ int res = _open (mpath, (oflag & ~_O_CREAT) | _O_NOINHERIT, mode);
+ if (res >= 0)
+ return res;
+ return _open (mpath, oflag | _O_NOINHERIT, mode);
+}
+
+int
+sys_rename (const char * oldname, const char * newname)
+{
+ BOOL result;
+ char temp[MAX_PATH];
+
+ /* MoveFile on Windows 95 doesn't correctly change the short file name
+ alias in a number of circumstances (it is not easy to predict when
+ just by looking at oldname and newname, unfortunately). In these
+ cases, renaming through a temporary name avoids the problem.
+
+ A second problem on Windows 95 is that renaming through a temp name when
+ newname is uppercase fails (the final long name ends up in
+ lowercase, although the short alias might be uppercase) UNLESS the
+ long temp name is not 8.3.
+
+ So, on Windows 95 we always rename through a temp name, and we make sure
+ the temp name has a long extension to ensure correct renaming. */
+
+ strcpy (temp, map_w32_filename (oldname, NULL));
+
+ if (os_subtype == OS_WIN95)
+ {
+ char * o;
+ char * p;
+ int i = 0;
+
+ oldname = map_w32_filename (oldname, NULL);
+ if (o = strrchr (oldname, '\\'))
+ o++;
+ else
+ o = (char *) oldname;
+
+ if (p = strrchr (temp, '\\'))
+ p++;
+ else
+ p = temp;
+
+ do
+ {
+ /* Force temp name to require a manufactured 8.3 alias - this
+ seems to make the second rename work properly. */
+ sprintf (p, "_.%s.%u", o, i);
+ i++;
+ result = rename (oldname, temp);
+ }
+ /* This loop must surely terminate! */
+ while (result < 0 && errno == EEXIST);
+ if (result < 0)
+ return -1;
+ }
+
+ /* Emulate Unix behaviour - newname is deleted if it already exists
+ (at least if it is a file; don't do this for directories).
+
+ Since we mustn't do this if we are just changing the case of the
+ file name (we would end up deleting the file we are trying to
+ rename!), we let rename detect if the destination file already
+ exists - that way we avoid the possible pitfalls of trying to
+ determine ourselves whether two names really refer to the same
+ file, which is not always possible in the general case. (Consider
+ all the permutations of shared or subst'd drives, etc.) */
+
+ newname = map_w32_filename (newname, NULL);
+ result = rename (temp, newname);
+
+ if (result < 0
+ && errno == EEXIST
+ && _chmod (newname, 0666) == 0
+ && _unlink (newname) == 0)
+ result = rename (temp, newname);
+
+ return result;
+}
+
+int
+sys_rmdir (const char * path)
+{
+ return _rmdir (map_w32_filename (path, NULL));
+}
+
+int
+sys_unlink (const char * path)
+{
+ path = map_w32_filename (path, NULL);
+
+ /* On Unix, unlink works without write permission. */
+ _chmod (path, 0666);
+ return _unlink (path);
+}
+
+static FILETIME utc_base_ft;
+static long double utc_base;
+static int init = 0;
+
+static time_t
+convert_time (FILETIME ft)
+{
+ long double ret;
+
+ if (!init)
+ {
+ /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */
+ SYSTEMTIME st;
+
+ st.wYear = 1970;
+ st.wMonth = 1;
+ st.wDay = 1;
+ st.wHour = 0;
+ st.wMinute = 0;
+ st.wSecond = 0;
+ st.wMilliseconds = 0;
+
+ SystemTimeToFileTime (&st, &utc_base_ft);
+ utc_base = (long double) utc_base_ft.dwHighDateTime
+ * 4096 * 1024 * 1024 + utc_base_ft.dwLowDateTime;
+ init = 1;
+ }
+
+ if (CompareFileTime (&ft, &utc_base_ft) < 0)
+ return 0;
+
+ ret = (long double) ft.dwHighDateTime * 4096 * 1024 * 1024 + ft.dwLowDateTime;
+ ret -= utc_base;
+ return (time_t) (ret * 1e-7);
+}
+
+void
+convert_from_time_t (time_t time, FILETIME * pft)
+{
+ long double tmp;
+
+ if (!init)
+ {
+ /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */
+ SYSTEMTIME st;
+
+ st.wYear = 1970;
+ st.wMonth = 1;
+ st.wDay = 1;
+ st.wHour = 0;
+ st.wMinute = 0;
+ st.wSecond = 0;
+ st.wMilliseconds = 0;
+
+ SystemTimeToFileTime (&st, &utc_base_ft);
+ utc_base = (long double) utc_base_ft.dwHighDateTime
+ * 4096 * 1024 * 1024 + utc_base_ft.dwLowDateTime;
+ init = 1;
+ }
+
+ /* time in 100ns units since 1-Jan-1601 */
+ tmp = (long double) time * 1e7 + utc_base;
+ pft->dwHighDateTime = (DWORD) (tmp / (4096.0 * 1024 * 1024));
+ pft->dwLowDateTime = (DWORD) (tmp - (4096.0 * 1024 * 1024) * pft->dwHighDateTime);
+}
+
+#if 0
+/* No reason to keep this; faking inode values either by hashing or even
+ using the file index from GetInformationByHandle, is not perfect and
+ so by default Emacs doesn't use the inode values on Windows.
+ Instead, we now determine file-truename correctly (except for
+ possible drive aliasing etc). */
+
+/* Modified version of "PJW" algorithm (see the "Dragon" compiler book). */
+static unsigned
+hashval (const unsigned char * str)
+{
+ unsigned h = 0;
+ while (*str)
+ {
+ h = (h << 4) + *str++;
+ h ^= (h >> 28);
+ }
+ return h;
+}
+
+/* Return the hash value of the canonical pathname, excluding the
+ drive/UNC header, to get a hopefully unique inode number. */
+static DWORD
+generate_inode_val (const char * name)
+{
+ char fullname[ MAX_PATH ];
+ char * p;
+ unsigned hash;
+
+ /* Get the truly canonical filename, if it exists. (Note: this
+ doesn't resolve aliasing due to subst commands, or recognise hard
+ links. */
+ if (!w32_get_long_filename ((char *)name, fullname, MAX_PATH))
+ abort ();
+
+ parse_root (fullname, &p);
+ /* Normal W32 filesystems are still case insensitive. */
+ _strlwr (p);
+ return hashval (p);
+}
+
+#endif
+
+/* MSVC stat function can't cope with UNC names and has other bugs, so
+ replace it with our own. This also allows us to calculate consistent
+ inode values without hacks in the main Emacs code. */
+int
+stat (const char * path, struct stat * buf)
+{
+ char *name, *r;
+ WIN32_FIND_DATA wfd;
+ HANDLE fh;
+ DWORD fake_inode;
+ int permission;
+ int len;
+ int rootdir = FALSE;
+
+ if (path == NULL || buf == NULL)
+ {
+ errno = EFAULT;
+ return -1;
+ }
+
+ name = (char *) map_w32_filename (path, &path);
+ /* must be valid filename, no wild cards or other invalid characters */
+ if (strpbrk (name, "*?|<>\""))
+ {
+ errno = ENOENT;
+ return -1;
+ }
+
+ /* If name is "c:/.." or "/.." then stat "c:/" or "/". */
+ r = IS_DEVICE_SEP (name[1]) ? &name[2] : name;
+ if (IS_DIRECTORY_SEP (r[0]) && r[1] == '.' && r[2] == '.' && r[3] == '\0')
+ {
+ r[1] = r[2] = '\0';
+ }
+
+ /* Remove trailing directory separator, unless name is the root
+ directory of a drive or UNC volume in which case ensure there
+ is a trailing separator. */
+ len = strlen (name);
+ rootdir = (path >= name + len - 1
+ && (IS_DIRECTORY_SEP (*path) || *path == 0));
+ name = strcpy (alloca (len + 2), name);
+
+ if (is_unc_volume (name))
+ {
+ DWORD attrs = unc_volume_file_attributes (name);
+
+ if (attrs == -1)
+ return -1;
+
+ memset (&wfd, 0, sizeof (wfd));
+ wfd.dwFileAttributes = attrs;
+ wfd.ftCreationTime = utc_base_ft;
+ wfd.ftLastAccessTime = utc_base_ft;
+ wfd.ftLastWriteTime = utc_base_ft;
+ strcpy (wfd.cFileName, name);
+ }
+ else if (rootdir)
+ {
+ if (!IS_DIRECTORY_SEP (name[len-1]))
+ strcat (name, "\\");
+ if (GetDriveType (name) < 2)
+ {
+ errno = ENOENT;
+ return -1;
+ }
+ memset (&wfd, 0, sizeof (wfd));
+ wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
+ wfd.ftCreationTime = utc_base_ft;
+ wfd.ftLastAccessTime = utc_base_ft;
+ wfd.ftLastWriteTime = utc_base_ft;
+ strcpy (wfd.cFileName, name);
+ }
+ else
+ {
+ if (IS_DIRECTORY_SEP (name[len-1]))
+ name[len - 1] = 0;
+
+ /* (This is hacky, but helps when doing file completions on
+ network drives.) Optimize by using information available from
+ active readdir if possible. */
+ len = strlen (dir_pathname);
+ if (IS_DIRECTORY_SEP (dir_pathname[len-1]))
+ len--;
+ if (dir_find_handle != INVALID_HANDLE_VALUE
+ && strnicmp (name, dir_pathname, len) == 0
+ && IS_DIRECTORY_SEP (name[len])
+ && stricmp (name + len + 1, dir_static.d_name) == 0)
+ {
+ /* This was the last entry returned by readdir. */
+ wfd = dir_find_data;
+ }
+ else
+ {
+ fh = FindFirstFile (name, &wfd);
+ if (fh == INVALID_HANDLE_VALUE)
+ {
+ errno = ENOENT;
+ return -1;
+ }
+ FindClose (fh);
+ }
+ }
+
+ if (!NILP (Vw32_get_true_file_attributes)
+ /* No access rights required to get info. */
+ && (fh = CreateFile (name, 0, 0, NULL, OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS, NULL))
+ != INVALID_HANDLE_VALUE)
+ {
+ /* This is more accurate in terms of gettting the correct number
+ of links, but is quite slow (it is noticable when Emacs is
+ making a list of file name completions). */
+ BY_HANDLE_FILE_INFORMATION info;
+
+ if (GetFileInformationByHandle (fh, &info))
+ {
+ buf->st_nlink = info.nNumberOfLinks;
+ /* Might as well use file index to fake inode values, but this
+ is not guaranteed to be unique unless we keep a handle open
+ all the time (even then there are situations where it is
+ not unique). Reputedly, there are at most 48 bits of info
+ (on NTFS, presumably less on FAT). */
+ fake_inode = info.nFileIndexLow ^ info.nFileIndexHigh;
+ }
+ else
+ {
+ buf->st_nlink = 1;
+ fake_inode = 0;
+ }
+
+ if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ {
+ buf->st_mode = _S_IFDIR;
+ }
+ else
+ {
+ switch (GetFileType (fh))
+ {
+ case FILE_TYPE_DISK:
+ buf->st_mode = _S_IFREG;
+ break;
+ case FILE_TYPE_PIPE:
+ buf->st_mode = _S_IFIFO;
+ break;
+ case FILE_TYPE_CHAR:
+ case FILE_TYPE_UNKNOWN:
+ default:
+ buf->st_mode = _S_IFCHR;
+ }
+ }
+ CloseHandle (fh);
+ }
+ else
+ {
+ /* Don't bother to make this information more accurate. */
+ buf->st_mode = (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ?
+ _S_IFDIR : _S_IFREG;
+ buf->st_nlink = 1;
+ fake_inode = 0;
+ }
+
+#if 0
+ /* Not sure if there is any point in this. */
+ if (!NILP (Vw32_generate_fake_inodes))
+ fake_inode = generate_inode_val (name);
+ else if (fake_inode == 0)
+ {
+ /* For want of something better, try to make everything unique. */
+ static DWORD gen_num = 0;
+ fake_inode = ++gen_num;
+ }
+#endif
+
+ /* MSVC defines _ino_t to be short; other libc's might not. */
+ if (sizeof (buf->st_ino) == 2)
+ buf->st_ino = fake_inode ^ (fake_inode >> 16);
+ else
+ buf->st_ino = fake_inode;
+
+ /* consider files to belong to current user */
+ buf->st_uid = the_passwd.pw_uid;
+ buf->st_gid = the_passwd.pw_gid;
+
+ /* volume_info is set indirectly by map_w32_filename */
+ buf->st_dev = volume_info.serialnum;
+ buf->st_rdev = volume_info.serialnum;
+
+
+ buf->st_size = wfd.nFileSizeLow;
+
+ /* Convert timestamps to Unix format. */
+ buf->st_mtime = convert_time (wfd.ftLastWriteTime);
+ buf->st_atime = convert_time (wfd.ftLastAccessTime);
+ if (buf->st_atime == 0) buf->st_atime = buf->st_mtime;
+ buf->st_ctime = convert_time (wfd.ftCreationTime);
+ if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime;
+
+ /* determine rwx permissions */
+ if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
+ permission = _S_IREAD;
+ else
+ permission = _S_IREAD | _S_IWRITE;
+
+ if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ permission |= _S_IEXEC;
+ else if (is_exec (name))
+ permission |= _S_IEXEC;
+
+ buf->st_mode |= permission | (permission >> 3) | (permission >> 6);
+
+ return 0;
+}
+
+/* Provide fstat and utime as well as stat for consistent handling of
+ file timestamps. */
+int
+fstat (int desc, struct stat * buf)
+{
+ HANDLE fh = (HANDLE) _get_osfhandle (desc);
+ BY_HANDLE_FILE_INFORMATION info;
+ DWORD fake_inode;
+ int permission;
+
+ switch (GetFileType (fh) & ~FILE_TYPE_REMOTE)
+ {
+ case FILE_TYPE_DISK:
+ buf->st_mode = _S_IFREG;
+ if (!GetFileInformationByHandle (fh, &info))
+ {
+ errno = EACCES;
+ return -1;
+ }
+ break;
+ case FILE_TYPE_PIPE:
+ buf->st_mode = _S_IFIFO;
+ goto non_disk;
+ case FILE_TYPE_CHAR:
+ case FILE_TYPE_UNKNOWN:
+ default:
+ buf->st_mode = _S_IFCHR;
+ non_disk:
+ memset (&info, 0, sizeof (info));
+ info.dwFileAttributes = 0;
+ info.ftCreationTime = utc_base_ft;
+ info.ftLastAccessTime = utc_base_ft;
+ info.ftLastWriteTime = utc_base_ft;
+ }
+
+ if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ buf->st_mode = _S_IFDIR;
+
+ buf->st_nlink = info.nNumberOfLinks;
+ /* Might as well use file index to fake inode values, but this
+ is not guaranteed to be unique unless we keep a handle open
+ all the time (even then there are situations where it is
+ not unique). Reputedly, there are at most 48 bits of info
+ (on NTFS, presumably less on FAT). */
+ fake_inode = info.nFileIndexLow ^ info.nFileIndexHigh;
+
+ /* MSVC defines _ino_t to be short; other libc's might not. */
+ if (sizeof (buf->st_ino) == 2)
+ buf->st_ino = fake_inode ^ (fake_inode >> 16);
+ else
+ buf->st_ino = fake_inode;
+
+ /* consider files to belong to current user */
+ buf->st_uid = 0;
+ buf->st_gid = 0;
+
+ buf->st_dev = info.dwVolumeSerialNumber;
+ buf->st_rdev = info.dwVolumeSerialNumber;
+
+ buf->st_size = info.nFileSizeLow;
+
+ /* Convert timestamps to Unix format. */
+ buf->st_mtime = convert_time (info.ftLastWriteTime);
+ buf->st_atime = convert_time (info.ftLastAccessTime);
+ if (buf->st_atime == 0) buf->st_atime = buf->st_mtime;
+ buf->st_ctime = convert_time (info.ftCreationTime);
+ if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime;
+
+ /* determine rwx permissions */
+ if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
+ permission = _S_IREAD;
+ else
+ permission = _S_IREAD | _S_IWRITE;
+
+ if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ permission |= _S_IEXEC;
+ else
+ {
+#if 0 /* no way of knowing the filename */
+ char * p = strrchr (name, '.');
+ if (p != NULL &&
+ (stricmp (p, ".exe") == 0 ||
+ stricmp (p, ".com") == 0 ||
+ stricmp (p, ".bat") == 0 ||
+ stricmp (p, ".cmd") == 0))
+ permission |= _S_IEXEC;
+#endif
+ }
+
+ buf->st_mode |= permission | (permission >> 3) | (permission >> 6);
+
+ return 0;
+}
+
+int
+utime (const char *name, struct utimbuf *times)
+{
+ struct utimbuf deftime;
+ HANDLE fh;
+ FILETIME mtime;
+ FILETIME atime;
+
+ if (times == NULL)
+ {
+ deftime.modtime = deftime.actime = time (NULL);
+ times = &deftime;
+ }
+
+ /* Need write access to set times. */
+ fh = CreateFile (name, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
+ 0, OPEN_EXISTING, 0, NULL);
+ if (fh)
+ {
+ convert_from_time_t (times->actime, &atime);
+ convert_from_time_t (times->modtime, &mtime);
+ if (!SetFileTime (fh, NULL, &atime, &mtime))
+ {
+ CloseHandle (fh);
+ errno = EACCES;
+ return -1;
+ }
+ CloseHandle (fh);
+ }
+ else
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+#ifdef HAVE_SOCKETS
+
+/* Wrappers for winsock functions to map between our file descriptors
+ and winsock's handles; also set h_errno for convenience.
+
+ To allow Emacs to run on systems which don't have winsock support
+ installed, we dynamically link to winsock on startup if present, and
+ otherwise provide the minimum necessary functionality
+ (eg. gethostname). */
+
+/* function pointers for relevant socket functions */
+int (PASCAL *pfn_WSAStartup) (WORD wVersionRequired, LPWSADATA lpWSAData);
+void (PASCAL *pfn_WSASetLastError) (int iError);
+int (PASCAL *pfn_WSAGetLastError) (void);
+int (PASCAL *pfn_socket) (int af, int type, int protocol);
+int (PASCAL *pfn_bind) (SOCKET s, const struct sockaddr *addr, int namelen);
+int (PASCAL *pfn_connect) (SOCKET s, const struct sockaddr *addr, int namelen);
+int (PASCAL *pfn_ioctlsocket) (SOCKET s, long cmd, u_long *argp);
+int (PASCAL *pfn_recv) (SOCKET s, char * buf, int len, int flags);
+int (PASCAL *pfn_send) (SOCKET s, const char * buf, int len, int flags);
+int (PASCAL *pfn_closesocket) (SOCKET s);
+int (PASCAL *pfn_shutdown) (SOCKET s, int how);
+int (PASCAL *pfn_WSACleanup) (void);
+
+u_short (PASCAL *pfn_htons) (u_short hostshort);
+u_short (PASCAL *pfn_ntohs) (u_short netshort);
+unsigned long (PASCAL *pfn_inet_addr) (const char * cp);
+int (PASCAL *pfn_gethostname) (char * name, int namelen);
+struct hostent * (PASCAL *pfn_gethostbyname) (const char * name);
+struct servent * (PASCAL *pfn_getservbyname) (const char * name, const char * proto);
+int (PASCAL *pfn_getpeername) (SOCKET s, struct sockaddr *addr, int * namelen);
+int (PASCAL *pfn_setsockopt) (SOCKET s, int level, int optname,
+ const char * optval, int optlen);
+int (PASCAL *pfn_listen) (SOCKET s, int backlog);
+int (PASCAL *pfn_getsockname) (SOCKET s, struct sockaddr * name,
+ int * namelen);
+SOCKET (PASCAL *pfn_accept) (SOCKET s, struct sockaddr * addr, int * addrlen);
+int (PASCAL *pfn_recvfrom) (SOCKET s, char * buf, int len, int flags,
+ struct sockaddr * from, int * fromlen);
+int (PASCAL *pfn_sendto) (SOCKET s, const char * buf, int len, int flags,
+ const struct sockaddr * to, int tolen);
+
+/* SetHandleInformation is only needed to make sockets non-inheritable. */
+BOOL (WINAPI *pfn_SetHandleInformation) (HANDLE object, DWORD mask, DWORD flags);
+#ifndef HANDLE_FLAG_INHERIT
+#define HANDLE_FLAG_INHERIT 1
+#endif
+
+HANDLE winsock_lib;
+static int winsock_inuse;
+
+BOOL
+term_winsock (void)
+{
+ if (winsock_lib != NULL && winsock_inuse == 0)
+ {
+ /* Not sure what would cause WSAENETDOWN, or even if it can happen
+ after WSAStartup returns successfully, but it seems reasonable
+ to allow unloading winsock anyway in that case. */
+ if (pfn_WSACleanup () == 0 ||
+ pfn_WSAGetLastError () == WSAENETDOWN)
+ {
+ if (FreeLibrary (winsock_lib))
+ winsock_lib = NULL;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+BOOL
+init_winsock (int load_now)
+{
+ WSADATA winsockData;
+
+ if (winsock_lib != NULL)
+ return TRUE;
+
+ pfn_SetHandleInformation = NULL;
+ pfn_SetHandleInformation
+ = (void *) GetProcAddress (GetModuleHandle ("kernel32.dll"),
+ "SetHandleInformation");
+
+ winsock_lib = LoadLibrary ("wsock32.dll");
+
+ if (winsock_lib != NULL)
+ {
+ /* dynamically link to socket functions */
+
+#define LOAD_PROC(fn) \
+ if ((pfn_##fn = (void *) GetProcAddress (winsock_lib, #fn)) == NULL) \
+ goto fail;
+
+ LOAD_PROC( WSAStartup );
+ LOAD_PROC( WSASetLastError );
+ LOAD_PROC( WSAGetLastError );
+ LOAD_PROC( socket );
+ LOAD_PROC( bind );
+ LOAD_PROC( connect );
+ LOAD_PROC( ioctlsocket );
+ LOAD_PROC( recv );
+ LOAD_PROC( send );
+ LOAD_PROC( closesocket );
+ LOAD_PROC( shutdown );
+ LOAD_PROC( htons );
+ LOAD_PROC( ntohs );
+ LOAD_PROC( inet_addr );
+ LOAD_PROC( gethostname );
+ LOAD_PROC( gethostbyname );
+ LOAD_PROC( getservbyname );
+ LOAD_PROC( getpeername );
+ LOAD_PROC( WSACleanup );
+ LOAD_PROC( setsockopt );
+ LOAD_PROC( listen );
+ LOAD_PROC( getsockname );
+ LOAD_PROC( accept );
+ LOAD_PROC( recvfrom );
+ LOAD_PROC( sendto );
+#undef LOAD_PROC
+
+ /* specify version 1.1 of winsock */
+ if (pfn_WSAStartup (0x101, &winsockData) == 0)
+ {
+ if (winsockData.wVersion != 0x101)
+ goto fail;
+
+ if (!load_now)
+ {
+ /* Report that winsock exists and is usable, but leave
+ socket functions disabled. I am assuming that calling
+ WSAStartup does not require any network interaction,
+ and in particular does not cause or require a dial-up
+ connection to be established. */
+
+ pfn_WSACleanup ();
+ FreeLibrary (winsock_lib);
+ winsock_lib = NULL;
+ }
+ winsock_inuse = 0;
+ return TRUE;
+ }
+
+ fail:
+ FreeLibrary (winsock_lib);
+ winsock_lib = NULL;
+ }
+
+ return FALSE;
+}
+
+
+int h_errno = 0;
+
+/* function to set h_errno for compatability; map winsock error codes to
+ normal system codes where they overlap (non-overlapping definitions
+ are already in <sys/socket.h> */
+static void set_errno ()
+{
+ if (winsock_lib == NULL)
+ h_errno = EINVAL;
+ else
+ h_errno = pfn_WSAGetLastError ();
+
+ switch (h_errno)
+ {
+ case WSAEACCES: h_errno = EACCES; break;
+ case WSAEBADF: h_errno = EBADF; break;
+ case WSAEFAULT: h_errno = EFAULT; break;
+ case WSAEINTR: h_errno = EINTR; break;
+ case WSAEINVAL: h_errno = EINVAL; break;
+ case WSAEMFILE: h_errno = EMFILE; break;
+ case WSAENAMETOOLONG: h_errno = ENAMETOOLONG; break;
+ case WSAENOTEMPTY: h_errno = ENOTEMPTY; break;
+ }
+ errno = h_errno;
+}
+
+static void check_errno ()
+{
+ if (h_errno == 0 && winsock_lib != NULL)
+ pfn_WSASetLastError (0);
+}
+
+/* Extend strerror to handle the winsock-specific error codes. */
+struct {
+ int errnum;
+ char * msg;
+} _wsa_errlist[] = {
+ WSAEINTR , "Interrupted function call",
+ WSAEBADF , "Bad file descriptor",
+ WSAEACCES , "Permission denied",
+ WSAEFAULT , "Bad address",
+ WSAEINVAL , "Invalid argument",
+ WSAEMFILE , "Too many open files",
+
+ WSAEWOULDBLOCK , "Resource temporarily unavailable",
+ WSAEINPROGRESS , "Operation now in progress",
+ WSAEALREADY , "Operation already in progress",
+ WSAENOTSOCK , "Socket operation on non-socket",
+ WSAEDESTADDRREQ , "Destination address required",
+ WSAEMSGSIZE , "Message too long",
+ WSAEPROTOTYPE , "Protocol wrong type for socket",
+ WSAENOPROTOOPT , "Bad protocol option",
+ WSAEPROTONOSUPPORT , "Protocol not supported",
+ WSAESOCKTNOSUPPORT , "Socket type not supported",
+ WSAEOPNOTSUPP , "Operation not supported",
+ WSAEPFNOSUPPORT , "Protocol family not supported",
+ WSAEAFNOSUPPORT , "Address family not supported by protocol family",
+ WSAEADDRINUSE , "Address already in use",
+ WSAEADDRNOTAVAIL , "Cannot assign requested address",
+ WSAENETDOWN , "Network is down",
+ WSAENETUNREACH , "Network is unreachable",
+ WSAENETRESET , "Network dropped connection on reset",
+ WSAECONNABORTED , "Software caused connection abort",
+ WSAECONNRESET , "Connection reset by peer",
+ WSAENOBUFS , "No buffer space available",
+ WSAEISCONN , "Socket is already connected",
+ WSAENOTCONN , "Socket is not connected",
+ WSAESHUTDOWN , "Cannot send after socket shutdown",
+ WSAETOOMANYREFS , "Too many references", /* not sure */
+ WSAETIMEDOUT , "Connection timed out",
+ WSAECONNREFUSED , "Connection refused",
+ WSAELOOP , "Network loop", /* not sure */
+ WSAENAMETOOLONG , "Name is too long",
+ WSAEHOSTDOWN , "Host is down",
+ WSAEHOSTUNREACH , "No route to host",
+ WSAENOTEMPTY , "Buffer not empty", /* not sure */
+ WSAEPROCLIM , "Too many processes",
+ WSAEUSERS , "Too many users", /* not sure */
+ WSAEDQUOT , "Double quote in host name", /* really not sure */
+ WSAESTALE , "Data is stale", /* not sure */
+ WSAEREMOTE , "Remote error", /* not sure */
+
+ WSASYSNOTREADY , "Network subsystem is unavailable",
+ WSAVERNOTSUPPORTED , "WINSOCK.DLL version out of range",
+ WSANOTINITIALISED , "Winsock not initialized successfully",
+ WSAEDISCON , "Graceful shutdown in progress",
+#ifdef WSAENOMORE
+ WSAENOMORE , "No more operations allowed", /* not sure */
+ WSAECANCELLED , "Operation cancelled", /* not sure */
+ WSAEINVALIDPROCTABLE , "Invalid procedure table from service provider",
+ WSAEINVALIDPROVIDER , "Invalid service provider version number",
+ WSAEPROVIDERFAILEDINIT , "Unable to initialize a service provider",
+ WSASYSCALLFAILURE , "System call failured",
+ WSASERVICE_NOT_FOUND , "Service not found", /* not sure */
+ WSATYPE_NOT_FOUND , "Class type not found",
+ WSA_E_NO_MORE , "No more resources available", /* really not sure */
+ WSA_E_CANCELLED , "Operation already cancelled", /* really not sure */
+ WSAEREFUSED , "Operation refused", /* not sure */
+#endif
+
+ WSAHOST_NOT_FOUND , "Host not found",
+ WSATRY_AGAIN , "Authoritative host not found during name lookup",
+ WSANO_RECOVERY , "Non-recoverable error during name lookup",
+ WSANO_DATA , "Valid name, no data record of requested type",
+
+ -1, NULL
+};
+
+char *
+sys_strerror(int error_no)
+{
+ int i;
+ static char unknown_msg[40];
+
+ if (error_no >= 0 && error_no < sys_nerr)
+ return sys_errlist[error_no];
+
+ for (i = 0; _wsa_errlist[i].errnum >= 0; i++)
+ if (_wsa_errlist[i].errnum == error_no)
+ return _wsa_errlist[i].msg;
+
+ sprintf(unknown_msg, "Unidentified error: %d", error_no);
+ return unknown_msg;
+}
+
+/* [andrewi 3-May-96] I've had conflicting results using both methods,
+ but I believe the method of keeping the socket handle separate (and
+ insuring it is not inheritable) is the correct one. */
+
+//#define SOCK_REPLACE_HANDLE
+
+#ifdef SOCK_REPLACE_HANDLE
+#define SOCK_HANDLE(fd) ((SOCKET) _get_osfhandle (fd))
+#else
+#define SOCK_HANDLE(fd) ((SOCKET) fd_info[fd].hnd)
+#endif
+
+int socket_to_fd (SOCKET s);
+
+int
+sys_socket(int af, int type, int protocol)
+{
+ SOCKET s;
+
+ if (winsock_lib == NULL)
+ {
+ h_errno = ENETDOWN;
+ return INVALID_SOCKET;
+ }
+
+ check_errno ();
+
+ /* call the real socket function */
+ s = pfn_socket (af, type, protocol);
+
+ if (s != INVALID_SOCKET)
+ return socket_to_fd (s);
+
+ set_errno ();
+ return -1;
+}
+
+/* Convert a SOCKET to a file descriptor. */
+int
+socket_to_fd (SOCKET s)
+{
+ int fd;
+ child_process * cp;
+
+ /* Although under NT 3.5 _open_osfhandle will accept a socket
+ handle, if opened with SO_OPENTYPE == SO_SYNCHRONOUS_NONALERT,
+ that does not work under NT 3.1. However, we can get the same
+ effect by using a backdoor function to replace an existing
+ descriptor handle with the one we want. */
+
+ /* allocate a file descriptor (with appropriate flags) */
+ fd = _open ("NUL:", _O_RDWR);
+ if (fd >= 0)
+ {
+#ifdef SOCK_REPLACE_HANDLE
+ /* now replace handle to NUL with our socket handle */
+ CloseHandle ((HANDLE) _get_osfhandle (fd));
+ _free_osfhnd (fd);
+ _set_osfhnd (fd, s);
+ /* setmode (fd, _O_BINARY); */
+#else
+ /* Make a non-inheritable copy of the socket handle. Note
+ that it is possible that sockets aren't actually kernel
+ handles, which appears to be the case on Windows 9x when
+ the MS Proxy winsock client is installed. */
+ {
+ /* Apparently there is a bug in NT 3.51 with some service
+ packs, which prevents using DuplicateHandle to make a
+ socket handle non-inheritable (causes WSACleanup to
+ hang). The work-around is to use SetHandleInformation
+ instead if it is available and implemented. */
+ if (pfn_SetHandleInformation)
+ {
+ pfn_SetHandleInformation ((HANDLE) s, HANDLE_FLAG_INHERIT, 0);
+ }
+ else
+ {
+ HANDLE parent = GetCurrentProcess ();
+ HANDLE new_s = INVALID_HANDLE_VALUE;
+
+ if (DuplicateHandle (parent,
+ (HANDLE) s,
+ parent,
+ &new_s,
+ 0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS))
+ {
+ /* It is possible that DuplicateHandle succeeds even
+ though the socket wasn't really a kernel handle,
+ because a real handle has the same value. So
+ test whether the new handle really is a socket. */
+ long nonblocking = 0;
+ if (pfn_ioctlsocket ((SOCKET) new_s, FIONBIO, &nonblocking) == 0)
+ {
+ pfn_closesocket (s);
+ s = (SOCKET) new_s;
+ }
+ else
+ {
+ CloseHandle (new_s);
+ }
+ }
+ }
+ }
+ fd_info[fd].hnd = (HANDLE) s;
+#endif
+
+ /* set our own internal flags */
+ fd_info[fd].flags = FILE_SOCKET | FILE_BINARY | FILE_READ | FILE_WRITE;
+
+ cp = new_child ();
+ if (cp)
+ {
+ cp->fd = fd;
+ cp->status = STATUS_READ_ACKNOWLEDGED;
+
+ /* attach child_process to fd_info */
+ if (fd_info[ fd ].cp != NULL)
+ {
+ DebPrint (("sys_socket: fd_info[%d] apparently in use!\n", fd));
+ abort ();
+ }
+
+ fd_info[ fd ].cp = cp;
+
+ /* success! */
+ winsock_inuse++; /* count open sockets */
+ return fd;
+ }
+
+ /* clean up */
+ _close (fd);
+ }
+ pfn_closesocket (s);
+ h_errno = EMFILE;
+ return -1;
+}
+
+
+int
+sys_bind (int s, const struct sockaddr * addr, int namelen)
+{
+ if (winsock_lib == NULL)
+ {
+ h_errno = ENOTSOCK;
+ return SOCKET_ERROR;
+ }
+
+ check_errno ();
+ if (fd_info[s].flags & FILE_SOCKET)
+ {
+ int rc = pfn_bind (SOCK_HANDLE (s), addr, namelen);
+ if (rc == SOCKET_ERROR)
+ set_errno ();
+ return rc;
+ }
+ h_errno = ENOTSOCK;
+ return SOCKET_ERROR;
+}
+
+
+int
+sys_connect (int s, const struct sockaddr * name, int namelen)
+{
+ if (winsock_lib == NULL)
+ {
+ h_errno = ENOTSOCK;
+ return SOCKET_ERROR;
+ }
+
+ check_errno ();
+ if (fd_info[s].flags & FILE_SOCKET)
+ {
+ int rc = pfn_connect (SOCK_HANDLE (s), name, namelen);
+ if (rc == SOCKET_ERROR)
+ set_errno ();
+ return rc;
+ }
+ h_errno = ENOTSOCK;
+ return SOCKET_ERROR;
+}
+
+u_short
+sys_htons (u_short hostshort)
+{
+ return (winsock_lib != NULL) ?
+ pfn_htons (hostshort) : hostshort;
+}
+
+u_short
+sys_ntohs (u_short netshort)
+{
+ return (winsock_lib != NULL) ?
+ pfn_ntohs (netshort) : netshort;
+}
+
+unsigned long
+sys_inet_addr (const char * cp)
+{
+ return (winsock_lib != NULL) ?
+ pfn_inet_addr (cp) : INADDR_NONE;
+}
+
+int
+sys_gethostname (char * name, int namelen)
+{
+ if (winsock_lib != NULL)
+ return pfn_gethostname (name, namelen);
+
+ if (namelen > MAX_COMPUTERNAME_LENGTH)
+ return !GetComputerName (name, (DWORD *)&namelen);
+
+ h_errno = EFAULT;
+ return SOCKET_ERROR;
+}
+
+struct hostent *
+sys_gethostbyname(const char * name)
+{
+ struct hostent * host;
+
+ if (winsock_lib == NULL)
+ {
+ h_errno = ENETDOWN;
+ return NULL;
+ }
+
+ check_errno ();
+ host = pfn_gethostbyname (name);
+ if (!host)
+ set_errno ();
+ return host;
+}
+
+struct servent *
+sys_getservbyname(const char * name, const char * proto)
+{
+ struct servent * serv;
+
+ if (winsock_lib == NULL)
+ {
+ h_errno = ENETDOWN;
+ return NULL;
+ }
+
+ check_errno ();
+ serv = pfn_getservbyname (name, proto);
+ if (!serv)
+ set_errno ();
+ return serv;
+}
+
+int
+sys_getpeername (int s, struct sockaddr *addr, int * namelen)
+{
+ if (winsock_lib == NULL)
+ {
+ h_errno = ENETDOWN;
+ return SOCKET_ERROR;
+ }
+
+ check_errno ();
+ if (fd_info[s].flags & FILE_SOCKET)
+ {
+ int rc = pfn_getpeername (SOCK_HANDLE (s), addr, namelen);
+ if (rc == SOCKET_ERROR)
+ set_errno ();
+ return rc;
+ }
+ h_errno = ENOTSOCK;
+ return SOCKET_ERROR;
+}
+
+
+int
+sys_shutdown (int s, int how)
+{
+ if (winsock_lib == NULL)
+ {
+ h_errno = ENETDOWN;
+ return SOCKET_ERROR;
+ }
+
+ check_errno ();
+ if (fd_info[s].flags & FILE_SOCKET)
+ {
+ int rc = pfn_shutdown (SOCK_HANDLE (s), how);
+ if (rc == SOCKET_ERROR)
+ set_errno ();
+ return rc;
+ }
+ h_errno = ENOTSOCK;
+ return SOCKET_ERROR;
+}
+
+int
+sys_setsockopt (int s, int level, int optname, const void * optval, int optlen)
+{
+ if (winsock_lib == NULL)
+ {
+ h_errno = ENETDOWN;
+ return SOCKET_ERROR;
+ }
+
+ check_errno ();
+ if (fd_info[s].flags & FILE_SOCKET)
+ {
+ int rc = pfn_setsockopt (SOCK_HANDLE (s), level, optname,
+ (const char *)optval, optlen);
+ if (rc == SOCKET_ERROR)
+ set_errno ();
+ return rc;
+ }
+ h_errno = ENOTSOCK;
+ return SOCKET_ERROR;
+}
+
+int
+sys_listen (int s, int backlog)
+{
+ if (winsock_lib == NULL)
+ {
+ h_errno = ENETDOWN;
+ return SOCKET_ERROR;
+ }
+
+ check_errno ();
+ if (fd_info[s].flags & FILE_SOCKET)
+ {
+ int rc = pfn_listen (SOCK_HANDLE (s), backlog);
+ if (rc == SOCKET_ERROR)
+ set_errno ();
+ return rc;
+ }
+ h_errno = ENOTSOCK;
+ return SOCKET_ERROR;
+}
+
+int
+sys_getsockname (int s, struct sockaddr * name, int * namelen)
+{
+ if (winsock_lib == NULL)
+ {
+ h_errno = ENETDOWN;
+ return SOCKET_ERROR;
+ }
+
+ check_errno ();
+ if (fd_info[s].flags & FILE_SOCKET)
+ {
+ int rc = pfn_getsockname (SOCK_HANDLE (s), name, namelen);
+ if (rc == SOCKET_ERROR)
+ set_errno ();
+ return rc;
+ }
+ h_errno = ENOTSOCK;
+ return SOCKET_ERROR;
+}
+
+int
+sys_accept (int s, struct sockaddr * addr, int * addrlen)
+{
+ if (winsock_lib == NULL)
+ {
+ h_errno = ENETDOWN;
+ return -1;
+ }
+
+ check_errno ();
+ if (fd_info[s].flags & FILE_SOCKET)
+ {
+ SOCKET t = pfn_accept (SOCK_HANDLE (s), addr, addrlen);
+ if (t != INVALID_SOCKET)
+ return socket_to_fd (t);
+
+ set_errno ();
+ return -1;
+ }
+ h_errno = ENOTSOCK;
+ return -1;
+}
+
+int
+sys_recvfrom (int s, char * buf, int len, int flags,
+ struct sockaddr * from, int * fromlen)
+{
+ if (winsock_lib == NULL)
+ {
+ h_errno = ENETDOWN;
+ return SOCKET_ERROR;
+ }
+
+ check_errno ();
+ if (fd_info[s].flags & FILE_SOCKET)
+ {
+ int rc = pfn_recvfrom (SOCK_HANDLE (s), buf, len, flags, from, fromlen);
+ if (rc == SOCKET_ERROR)
+ set_errno ();
+ return rc;
+ }
+ h_errno = ENOTSOCK;
+ return SOCKET_ERROR;
+}
+
+int
+sys_sendto (int s, const char * buf, int len, int flags,
+ const struct sockaddr * to, int tolen)
+{
+ if (winsock_lib == NULL)
+ {
+ h_errno = ENETDOWN;
+ return SOCKET_ERROR;
+ }
+
+ check_errno ();
+ if (fd_info[s].flags & FILE_SOCKET)
+ {
+ int rc = pfn_sendto (SOCK_HANDLE (s), buf, len, flags, to, tolen);
+ if (rc == SOCKET_ERROR)
+ set_errno ();
+ return rc;
+ }
+ h_errno = ENOTSOCK;
+ return SOCKET_ERROR;
+}
+
+/* Windows does not have an fcntl function. Provide an implementation
+ solely for making sockets non-blocking. */
+int
+fcntl (int s, int cmd, int options)
+{
+ if (winsock_lib == NULL)
+ {
+ h_errno = ENETDOWN;
+ return -1;
+ }
+
+ check_errno ();
+ if (fd_info[s].flags & FILE_SOCKET)
+ {
+ if (cmd == F_SETFL && options == O_NDELAY)
+ {
+ unsigned long nblock = 1;
+ int rc = pfn_ioctlsocket (SOCK_HANDLE (s), FIONBIO, &nblock);
+ if (rc == SOCKET_ERROR)
+ set_errno();
+ /* Keep track of the fact that we set this to non-blocking. */
+ fd_info[s].flags |= FILE_NDELAY;
+ return rc;
+ }
+ else
+ {
+ h_errno = EINVAL;
+ return SOCKET_ERROR;
+ }
+ }
+ h_errno = ENOTSOCK;
+ return SOCKET_ERROR;
+}
+
+#endif /* HAVE_SOCKETS */
+
+
+/* Shadow main io functions: we need to handle pipes and sockets more
+ intelligently, and implement non-blocking mode as well. */
+
+int
+sys_close (int fd)
+{
+ int rc;
+
+ if (fd < 0 || fd >= MAXDESC)
+ {
+ errno = EBADF;
+ return -1;
+ }
+
+ if (fd_info[fd].cp)
+ {
+ child_process * cp = fd_info[fd].cp;
+
+ fd_info[fd].cp = NULL;
+
+ if (CHILD_ACTIVE (cp))
+ {
+ /* if last descriptor to active child_process then cleanup */
+ int i;
+ for (i = 0; i < MAXDESC; i++)
+ {
+ if (i == fd)
+ continue;
+ if (fd_info[i].cp == cp)
+ break;
+ }
+ if (i == MAXDESC)
+ {
+#ifdef HAVE_SOCKETS
+ if (fd_info[fd].flags & FILE_SOCKET)
+ {
+#ifndef SOCK_REPLACE_HANDLE
+ if (winsock_lib == NULL) abort ();
+
+ pfn_shutdown (SOCK_HANDLE (fd), 2);
+ rc = pfn_closesocket (SOCK_HANDLE (fd));
+#endif
+ winsock_inuse--; /* count open sockets */
+ }
+#endif
+ delete_child (cp);
+ }
+ }
+ }
+
+ /* Note that sockets do not need special treatment here (at least on
+ NT and Windows 95 using the standard tcp/ip stacks) - it appears that
+ closesocket is equivalent to CloseHandle, which is to be expected
+ because socket handles are fully fledged kernel handles. */
+ rc = _close (fd);
+
+ if (rc == 0)
+ fd_info[fd].flags = 0;
+
+ return rc;
+}
+
+int
+sys_dup (int fd)
+{
+ int new_fd;
+
+ new_fd = _dup (fd);
+ if (new_fd >= 0)
+ {
+ /* duplicate our internal info as well */
+ fd_info[new_fd] = fd_info[fd];
+ }
+ return new_fd;
+}
+
+
+int
+sys_dup2 (int src, int dst)
+{
+ int rc;
+
+ if (dst < 0 || dst >= MAXDESC)
+ {
+ errno = EBADF;
+ return -1;
+ }
+
+ /* make sure we close the destination first if it's a pipe or socket */
+ if (src != dst && fd_info[dst].flags != 0)
+ sys_close (dst);
+
+ rc = _dup2 (src, dst);
+ if (rc == 0)
+ {
+ /* duplicate our internal info as well */
+ fd_info[dst] = fd_info[src];
+ }
+ return rc;
+}
+
+/* Unix pipe() has only one arg */
+int
+sys_pipe (int * phandles)
+{
+ int rc;
+ unsigned flags;
+
+ /* make pipe handles non-inheritable; when we spawn a child, we
+ replace the relevant handle with an inheritable one. Also put
+ pipes into binary mode; we will do text mode translation ourselves
+ if required. */
+ rc = _pipe (phandles, 0, _O_NOINHERIT | _O_BINARY);
+
+ if (rc == 0)
+ {
+ /* Protect against overflow, since Windows can open more handles than
+ our fd_info array has room for. */
+ if (phandles[0] >= MAXDESC || phandles[1] >= MAXDESC)
+ {
+ _close (phandles[0]);
+ _close (phandles[1]);
+ rc = -1;
+ }
+ else
+ {
+ flags = FILE_PIPE | FILE_READ | FILE_BINARY;
+ fd_info[phandles[0]].flags = flags;
+
+ flags = FILE_PIPE | FILE_WRITE | FILE_BINARY;
+ fd_info[phandles[1]].flags = flags;
+ }
+ }
+
+ return rc;
+}
+
+/* From ntproc.c */
+extern int w32_pipe_read_delay;
+
+/* Function to do blocking read of one byte, needed to implement
+ select. It is only allowed on sockets and pipes. */
+int
+_sys_read_ahead (int fd)
+{
+ child_process * cp;
+ int rc;
+
+ if (fd < 0 || fd >= MAXDESC)
+ return STATUS_READ_ERROR;
+
+ cp = fd_info[fd].cp;
+
+ if (cp == NULL || cp->fd != fd || cp->status != STATUS_READ_READY)
+ return STATUS_READ_ERROR;
+
+ if ((fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET)) == 0
+ || (fd_info[fd].flags & FILE_READ) == 0)
+ {
+ DebPrint (("_sys_read_ahead: internal error: fd %d is not a pipe or socket!\n", fd));
+ abort ();
+ }
+
+ cp->status = STATUS_READ_IN_PROGRESS;
+
+ if (fd_info[fd].flags & FILE_PIPE)
+ {
+ rc = _read (fd, &cp->chr, sizeof (char));
+
+ /* Give subprocess time to buffer some more output for us before
+ reporting that input is available; we need this because Windows 95
+ connects DOS programs to pipes by making the pipe appear to be
+ the normal console stdout - as a result most DOS programs will
+ write to stdout without buffering, ie. one character at a
+ time. Even some W32 programs do this - "dir" in a command
+ shell on NT is very slow if we don't do this. */
+ if (rc > 0)
+ {
+ int wait = w32_pipe_read_delay;
+
+ if (wait > 0)
+ Sleep (wait);
+ else if (wait < 0)
+ while (++wait <= 0)
+ /* Yield remainder of our time slice, effectively giving a
+ temporary priority boost to the child process. */
+ Sleep (0);
+ }
+ }
+#ifdef HAVE_SOCKETS
+ else if (fd_info[fd].flags & FILE_SOCKET)
+ {
+ unsigned long nblock = 0;
+ /* We always want this to block, so temporarily disable NDELAY. */
+ if (fd_info[fd].flags & FILE_NDELAY)
+ pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
+
+ rc = pfn_recv (SOCK_HANDLE (fd), &cp->chr, sizeof (char), 0);
+
+ if (fd_info[fd].flags & FILE_NDELAY)
+ {
+ nblock = 1;
+ pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
+ }
+ }
+#endif
+
+ if (rc == sizeof (char))
+ cp->status = STATUS_READ_SUCCEEDED;
+ else
+ cp->status = STATUS_READ_FAILED;
+
+ return cp->status;
+}
+
+int
+sys_read (int fd, char * buffer, unsigned int count)
+{
+ int nchars;
+ int to_read;
+ DWORD waiting;
+ char * orig_buffer = buffer;
+
+ if (fd < 0 || fd >= MAXDESC)
+ {
+ errno = EBADF;
+ return -1;
+ }
+
+ if (fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET))
+ {
+ child_process *cp = fd_info[fd].cp;
+
+ if ((fd_info[fd].flags & FILE_READ) == 0)
+ {
+ errno = EBADF;
+ return -1;
+ }
+
+ nchars = 0;
+
+ /* re-read CR carried over from last read */
+ if (fd_info[fd].flags & FILE_LAST_CR)
+ {
+ if (fd_info[fd].flags & FILE_BINARY) abort ();
+ *buffer++ = 0x0d;
+ count--;
+ nchars++;
+ fd_info[fd].flags &= ~FILE_LAST_CR;
+ }
+
+ /* presence of a child_process structure means we are operating in
+ non-blocking mode - otherwise we just call _read directly.
+ Note that the child_process structure might be missing because
+ reap_subprocess has been called; in this case the pipe is
+ already broken, so calling _read on it is okay. */
+ if (cp)
+ {
+ int current_status = cp->status;
+
+ switch (current_status)
+ {
+ case STATUS_READ_FAILED:
+ case STATUS_READ_ERROR:
+ /* report normal EOF if nothing in buffer */
+ if (nchars <= 0)
+ fd_info[fd].flags |= FILE_AT_EOF;
+ return nchars;
+
+ case STATUS_READ_READY:
+ case STATUS_READ_IN_PROGRESS:
+ DebPrint (("sys_read called when read is in progress\n"));
+ errno = EWOULDBLOCK;
+ return -1;
+
+ case STATUS_READ_SUCCEEDED:
+ /* consume read-ahead char */
+ *buffer++ = cp->chr;
+ count--;
+ nchars++;
+ cp->status = STATUS_READ_ACKNOWLEDGED;
+ ResetEvent (cp->char_avail);
+
+ case STATUS_READ_ACKNOWLEDGED:
+ break;
+
+ default:
+ DebPrint (("sys_read: bad status %d\n", current_status));
+ errno = EBADF;
+ return -1;
+ }
+
+ if (fd_info[fd].flags & FILE_PIPE)
+ {
+ PeekNamedPipe ((HANDLE) _get_osfhandle (fd), NULL, 0, NULL, &waiting, NULL);
+ to_read = min (waiting, (DWORD) count);
+
+ if (to_read > 0)
+ nchars += _read (fd, buffer, to_read);
+ }
+#ifdef HAVE_SOCKETS
+ else /* FILE_SOCKET */
+ {
+ if (winsock_lib == NULL) abort ();
+
+ /* do the equivalent of a non-blocking read */
+ pfn_ioctlsocket (SOCK_HANDLE (fd), FIONREAD, &waiting);
+ if (waiting == 0 && nchars == 0)
+ {
+ h_errno = errno = EWOULDBLOCK;
+ return -1;
+ }
+
+ if (waiting)
+ {
+ /* always use binary mode for sockets */
+ int res = pfn_recv (SOCK_HANDLE (fd), buffer, count, 0);
+ if (res == SOCKET_ERROR)
+ {
+ DebPrint(("sys_read.recv failed with error %d on socket %ld\n",
+ pfn_WSAGetLastError (), SOCK_HANDLE (fd)));
+ set_errno ();
+ return -1;
+ }
+ nchars += res;
+ }
+ }
+#endif
+ }
+ else
+ {
+ int nread = _read (fd, buffer, count);
+ if (nread >= 0)
+ nchars += nread;
+ else if (nchars == 0)
+ nchars = nread;
+ }
+
+ if (nchars <= 0)
+ fd_info[fd].flags |= FILE_AT_EOF;
+ /* Perform text mode translation if required. */
+ else if ((fd_info[fd].flags & FILE_BINARY) == 0)
+ {
+ nchars = crlf_to_lf (nchars, orig_buffer);
+ /* If buffer contains only CR, return that. To be absolutely
+ sure we should attempt to read the next char, but in
+ practice a CR to be followed by LF would not appear by
+ itself in the buffer. */
+ if (nchars > 1 && orig_buffer[nchars - 1] == 0x0d)
+ {
+ fd_info[fd].flags |= FILE_LAST_CR;
+ nchars--;
+ }
+ }
+ }
+ else
+ nchars = _read (fd, buffer, count);
+
+ return nchars;
+}
+
+/* For now, don't bother with a non-blocking mode */
+int
+sys_write (int fd, const void * buffer, unsigned int count)
+{
+ int nchars;
+
+ if (fd < 0 || fd >= MAXDESC)
+ {
+ errno = EBADF;
+ return -1;
+ }
+
+ if (fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET))
+ {
+ if ((fd_info[fd].flags & FILE_WRITE) == 0)
+ {
+ errno = EBADF;
+ return -1;
+ }
+
+ /* Perform text mode translation if required. */
+ if ((fd_info[fd].flags & FILE_BINARY) == 0)
+ {
+ char * tmpbuf = alloca (count * 2);
+ unsigned char * src = (void *)buffer;
+ unsigned char * dst = tmpbuf;
+ int nbytes = count;
+
+ while (1)
+ {
+ unsigned char *next;
+ /* copy next line or remaining bytes */
+ next = _memccpy (dst, src, '\n', nbytes);
+ if (next)
+ {
+ /* copied one line ending with '\n' */
+ int copied = next - dst;
+ nbytes -= copied;
+ src += copied;
+ /* insert '\r' before '\n' */
+ next[-1] = '\r';
+ next[0] = '\n';
+ dst = next + 1;
+ count++;
+ }
+ else
+ /* copied remaining partial line -> now finished */
+ break;
+ }
+ buffer = tmpbuf;
+ }
+ }
+
+#ifdef HAVE_SOCKETS
+ if (fd_info[fd].flags & FILE_SOCKET)
+ {
+ unsigned long nblock = 0;
+ if (winsock_lib == NULL) abort ();
+
+ /* TODO: implement select() properly so non-blocking I/O works. */
+ /* For now, make sure the write blocks. */
+ if (fd_info[fd].flags & FILE_NDELAY)
+ pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
+
+ nchars = pfn_send (SOCK_HANDLE (fd), buffer, count, 0);
+
+ /* Set the socket back to non-blocking if it was before,
+ for other operations that support it. */
+ if (fd_info[fd].flags & FILE_NDELAY)
+ {
+ nblock = 1;
+ pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
+ }
+
+ if (nchars == SOCKET_ERROR)
+ {
+ DebPrint(("sys_write.send failed with error %d on socket %ld\n",
+ pfn_WSAGetLastError (), SOCK_HANDLE (fd)));
+ set_errno ();
+ }
+ }
+ else
+#endif
+ nchars = _write (fd, buffer, count);
+
+ return nchars;
+}
+
+static void
+check_windows_init_file ()
+{
+ extern int noninteractive, inhibit_window_system;
+
+ /* A common indication that Emacs is not installed properly is when
+ it cannot find the Windows installation file. If this file does
+ not exist in the expected place, tell the user. */
+
+ if (!noninteractive && !inhibit_window_system)
+ {
+ extern Lisp_Object Vwindow_system, Vload_path, Qfile_exists_p;
+ Lisp_Object objs[2];
+ Lisp_Object full_load_path;
+ Lisp_Object init_file;
+ int fd;
+
+ objs[0] = Vload_path;
+ objs[1] = decode_env_path (0, (getenv ("EMACSLOADPATH")));
+ full_load_path = Fappend (2, objs);
+ init_file = build_string ("term/w32-win");
+ fd = openp (full_load_path, init_file, Vload_suffixes, NULL, Qnil);
+ if (fd < 0)
+ {
+ Lisp_Object load_path_print = Fprin1_to_string (full_load_path, Qnil);
+ char *init_file_name = SDATA (init_file);
+ char *load_path = SDATA (load_path_print);
+ char *buffer = alloca (1024);
+
+ sprintf (buffer,
+ "The Emacs Windows initialization file \"%s.el\" "
+ "could not be found in your Emacs installation. "
+ "Emacs checked the following directories for this file:\n"
+ "\n%s\n\n"
+ "When Emacs cannot find this file, it usually means that it "
+ "was not installed properly, or its distribution file was "
+ "not unpacked properly.\nSee the README.W32 file in the "
+ "top-level Emacs directory for more information.",
+ init_file_name, load_path);
+ MessageBox (NULL,
+ buffer,
+ "Emacs Abort Dialog",
+ MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
+ /* Use the low-level Emacs abort. */
+#undef abort
+ abort ();
+ }
+ else
+ {
+ _close (fd);
+ }
+ }
+}
+
+void
+term_ntproc ()
+{
+#ifdef HAVE_SOCKETS
+ /* shutdown the socket interface if necessary */
+ term_winsock ();
+#endif
+
+ term_w32select ();
+}
+
+void
+init_ntproc ()
+{
+#ifdef HAVE_SOCKETS
+ /* Initialise the socket interface now if available and requested by
+ the user by defining PRELOAD_WINSOCK; otherwise loading will be
+ delayed until open-network-stream is called (w32-has-winsock can
+ also be used to dynamically load or reload winsock).
+
+ Conveniently, init_environment is called before us, so
+ PRELOAD_WINSOCK can be set in the registry. */
+
+ /* Always initialize this correctly. */
+ winsock_lib = NULL;
+
+ if (getenv ("PRELOAD_WINSOCK") != NULL)
+ init_winsock (TRUE);
+#endif
+
+ /* Initial preparation for subprocess support: replace our standard
+ handles with non-inheritable versions. */
+ {
+ HANDLE parent;
+ HANDLE stdin_save = INVALID_HANDLE_VALUE;
+ HANDLE stdout_save = INVALID_HANDLE_VALUE;
+ HANDLE stderr_save = INVALID_HANDLE_VALUE;
+
+ parent = GetCurrentProcess ();
+
+ /* ignore errors when duplicating and closing; typically the
+ handles will be invalid when running as a gui program. */
+ DuplicateHandle (parent,
+ GetStdHandle (STD_INPUT_HANDLE),
+ parent,
+ &stdin_save,
+ 0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS);
+
+ DuplicateHandle (parent,
+ GetStdHandle (STD_OUTPUT_HANDLE),
+ parent,
+ &stdout_save,
+ 0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS);
+
+ DuplicateHandle (parent,
+ GetStdHandle (STD_ERROR_HANDLE),
+ parent,
+ &stderr_save,
+ 0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS);
+
+ fclose (stdin);
+ fclose (stdout);
+ fclose (stderr);
+
+ if (stdin_save != INVALID_HANDLE_VALUE)
+ _open_osfhandle ((long) stdin_save, O_TEXT);
+ else
+ _open ("nul", O_TEXT | O_NOINHERIT | O_RDONLY);
+ _fdopen (0, "r");
+
+ if (stdout_save != INVALID_HANDLE_VALUE)
+ _open_osfhandle ((long) stdout_save, O_TEXT);
+ else
+ _open ("nul", O_TEXT | O_NOINHERIT | O_WRONLY);
+ _fdopen (1, "w");
+
+ if (stderr_save != INVALID_HANDLE_VALUE)
+ _open_osfhandle ((long) stderr_save, O_TEXT);
+ else
+ _open ("nul", O_TEXT | O_NOINHERIT | O_WRONLY);
+ _fdopen (2, "w");
+ }
+
+ /* unfortunately, atexit depends on implementation of malloc */
+ /* atexit (term_ntproc); */
+ signal (SIGABRT, term_ntproc);
+
+ /* determine which drives are fixed, for GetCachedVolumeInformation */
+ {
+ /* GetDriveType must have trailing backslash. */
+ char drive[] = "A:\\";
+
+ /* Loop over all possible drive letters */
+ while (*drive <= 'Z')
+ {
+ /* Record if this drive letter refers to a fixed drive. */
+ fixed_drives[DRIVE_INDEX (*drive)] =
+ (GetDriveType (drive) == DRIVE_FIXED);
+
+ (*drive)++;
+ }
+
+ /* Reset the volume info cache. */
+ volume_cache = NULL;
+ }
+
+ /* Check to see if Emacs has been installed correctly. */
+ check_windows_init_file ();
+}
+
+/*
+ globals_of_w32 is used to initialize those global variables that
+ must always be initialized on startup even when the global variable
+ initialized is non zero (see the function main in emacs.c).
+*/
+void globals_of_w32 ()
+{
+ g_b_init_is_windows_9x = 0;
+ g_b_init_open_process_token = 0;
+ g_b_init_get_token_information = 0;
+ g_b_init_lookup_account_sid = 0;
+ g_b_init_get_sid_identifier_authority = 0;
+}
+
+/* end of nt.c */
+
+/* arch-tag: 90442dd3-37be-482b-b272-ac752e3049f1
+ (do not change this comment) */