diff src/nt.c @ 428:3ecd8885ac67 r21-2-22

Import from CVS: tag r21-2-22
author cvs
date Mon, 13 Aug 2007 11:28:15 +0200
parents
children a5df635868b2
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/nt.c	Mon Aug 13 11:28:15 2007 +0200
@@ -0,0 +1,1924 @@
+/* Utility and Unix shadow routines for XEmacs on Windows NT.
+   Copyright (C) 1994, 1995 Free Software Foundation, Inc.
+
+This file is part of XEmacs.
+
+XEmacs is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+XEmacs is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with XEmacs; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.
+
+
+   Geoff Voelker (voelker@cs.washington.edu) 7-29-94 */
+
+/* Adapted for XEmacs by David Hobley <david@spook-le0.cia.com.au> */
+/* Sync'ed with Emacs 19.34.6 by Marc Paquette <marcpa@cam.org> */
+
+#include <config.h>
+
+#undef signal
+#define getwd _getwd
+#include "lisp.h"
+#undef getwd
+
+#include "systime.h"
+#include "syssignal.h"
+#include "sysproc.h"
+#include "sysfile.h"
+
+#include <ctype.h>
+#include <direct.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <io.h>
+#include <pwd.h>
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <windows.h>
+#ifndef __MINGW32__
+#include <mmsystem.h>
+#else
+typedef void (CALLBACK TIMECALLBACK)(UINT uTimerID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2);
+
+typedef TIMECALLBACK FAR *LPTIMECALLBACK;
+DWORD WINAPI timeGetTime(void);
+MMRESULT WINAPI timeSetEvent(UINT uDelay, UINT uResolution,
+    LPTIMECALLBACK fptc, DWORD dwUser, UINT fuEvent);
+MMRESULT WINAPI timeKillEvent(UINT uTimerID);
+MMRESULT WINAPI timeGetDevCaps(TIMECAPS* ptc, UINT cbtc);
+MMRESULT WINAPI timeBeginPeriod(UINT uPeriod);
+MMRESULT WINAPI timeEndPeriod(UINT uPeriod);
+#endif
+
+#include "nt.h"
+#include <sys/dir.h>
+#include "ntheap.h"
+
+
+extern Lisp_Object Vmswindows_downcase_file_names;
+#if 0
+extern Lisp_Object Vwin32_generate_fake_inodes;
+#endif
+extern Lisp_Object Vmswindows_get_true_file_attributes;
+
+extern char *get_home_directory(void);
+
+static char startup_dir[ MAXPATHLEN ];
+
+/* Get the current working directory.  */
+char *
+getwd (char *dir)
+{
+#if 0
+  if (GetCurrentDirectory (MAXPATHLEN, dir) > 0)
+    return dir;
+  return NULL;
+#else
+  /* Emacs doesn't actually change directory itself, and we want to
+     force our real wd to be where emacs.exe is to avoid unnecessary
+     conflicts when trying to rename or delete directories.  */
+  strcpy (dir, startup_dir);
+  return dir;
+#endif
+}
+
+/* Emulate getloadavg.  */
+int
+getloadavg (double loadavg[], int nelem)
+{
+  int i;
+
+  /* A faithful emulation is going to have to be saved for a rainy day.  */
+  for (i = 0; i < nelem; i++) 
+    {
+      loadavg[i] = 0.0;
+    }
+  return i;
+}
+
+/* Emulate getpwuid, getpwnam and others.  */
+
+#define PASSWD_FIELD_SIZE 256
+
+static char the_passwd_name[PASSWD_FIELD_SIZE];
+static char the_passwd_passwd[PASSWD_FIELD_SIZE];
+static char the_passwd_gecos[PASSWD_FIELD_SIZE];
+static char the_passwd_dir[PASSWD_FIELD_SIZE];
+static char the_passwd_shell[PASSWD_FIELD_SIZE];
+
+static struct passwd the_passwd = 
+{
+  the_passwd_name,
+  the_passwd_passwd,
+  0,
+  0,
+  0,
+  the_passwd_gecos,
+  the_passwd_dir,
+  the_passwd_shell,
+};
+
+uid_t
+getuid () 
+{ 
+  return the_passwd.pw_uid;
+}
+
+uid_t 
+geteuid () 
+{ 
+  /* I could imagine arguing for checking to see whether the user is
+     in the Administrators group and returning a UID of 0 for that
+     case, but I don't know how wise that would be in the long run.  */
+  return getuid (); 
+}
+
+gid_t
+getgid () 
+{ 
+  return the_passwd.pw_gid;
+}
+
+gid_t
+getegid () 
+{ 
+  return getgid ();
+}
+
+struct passwd *
+getpwuid (uid_t uid)
+{
+  if (uid == the_passwd.pw_uid)
+    return &the_passwd;
+  return NULL;
+}
+
+struct passwd *
+getpwnam (const char *name)
+{
+  struct passwd *pw;
+  
+  pw = getpwuid (getuid ());
+  if (!pw)
+    return pw;
+
+  if (stricmp (name, pw->pw_name))
+    return NULL;
+
+  return pw;
+}
+
+void
+init_user_info ()
+{
+  /* Find the user's real name by opening the process token and
+     looking up the name associated with the user-sid in that token.
+
+     Use the relative portion of the identifier authority value from
+     the user-sid as the user id value (same for group id using the
+     primary group sid from the process token). */
+
+  char            user_sid[256], name[256], domain[256];
+  DWORD           length = sizeof (name), dlength = sizeof (domain), trash;
+  HANDLE          token = NULL;
+  SID_NAME_USE    user_type;
+
+  if (OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY, &token)
+      && GetTokenInformation (token, TokenUser,
+			      (PVOID) user_sid, sizeof (user_sid), &trash)
+      && LookupAccountSid (NULL, *((PSID *) user_sid), name, &length,
+			   domain, &dlength, &user_type))
+    {
+      strcpy (the_passwd.pw_name, name);
+      /* Determine a reasonable uid value. */
+      if (stricmp ("administrator", name) == 0)
+	{
+	  the_passwd.pw_uid = 0;
+	  the_passwd.pw_gid = 0;
+	}
+      else
+	{
+	  SID_IDENTIFIER_AUTHORITY * pSIA;
+
+	  pSIA = GetSidIdentifierAuthority (*((PSID *) user_sid));
+	  /* I believe the relative portion is the last 4 bytes (of 6)
+	     with msb first. */
+	  the_passwd.pw_uid = ((pSIA->Value[2] << 24) +
+			       (pSIA->Value[3] << 16) +
+			       (pSIA->Value[4] << 8)  +
+			       (pSIA->Value[5] << 0));
+	  /* restrict to conventional uid range for normal users */
+	  the_passwd.pw_uid = the_passwd.pw_uid % 60001;
+
+	  /* Get group id */
+	  if (GetTokenInformation (token, TokenPrimaryGroup,
+				   (PVOID) user_sid, sizeof (user_sid), &trash))
+	    {
+	      SID_IDENTIFIER_AUTHORITY * pSIA;
+
+	      pSIA = GetSidIdentifierAuthority (*((PSID *) user_sid));
+	      the_passwd.pw_gid = ((pSIA->Value[2] << 24) +
+				   (pSIA->Value[3] << 16) +
+				   (pSIA->Value[4] << 8)  +
+				   (pSIA->Value[5] << 0));
+	      /* I don't know if this is necessary, but for safety... */
+	      the_passwd.pw_gid = the_passwd.pw_gid % 60001;
+	    }
+	  else
+	    the_passwd.pw_gid = the_passwd.pw_uid;
+	}
+    }
+  /* If security calls are not supported (presumably because we
+       are running under Windows 95), fallback to this. */
+  else if (GetUserName (name, &length))
+    {
+      strcpy (the_passwd.pw_name, name);
+      if (stricmp ("administrator", name) == 0)
+	the_passwd.pw_uid = 0;
+      else
+	the_passwd.pw_uid = 123;
+      the_passwd.pw_gid = the_passwd.pw_uid;
+    }
+  else
+    {
+      strcpy (the_passwd.pw_name, "unknown");
+      the_passwd.pw_uid = 123;
+      the_passwd.pw_gid = 123;
+    }
+
+  /* Ensure HOME and SHELL are defined. */
+#if 0
+  /*
+   * With XEmacs, setting $HOME is deprecated.
+   */
+  if (getenv ("HOME") == NULL)
+    putenv ("HOME=c:/");
+#endif
+  if (getenv ("SHELL") == NULL)
+    putenv ((GetVersion () & 0x80000000) ? "SHELL=command" : "SHELL=cmd");
+
+  /* Set dir and shell from environment variables. */
+  strcpy (the_passwd.pw_dir, get_home_directory());
+  strcpy (the_passwd.pw_shell, getenv ("SHELL"));
+
+  if (token)
+    CloseHandle (token);
+}
+
+/* Normalize filename by converting all path separators to
+   the specified separator.  Also conditionally convert upper
+   case path name components to lower case.  */
+
+static void
+normalize_filename (fp, path_sep)
+     REGISTER char *fp;
+     char path_sep;
+{
+  char sep;
+  char *elem;
+
+  /* Always lower-case drive letters a-z, even if the filesystem
+     preserves case in filenames.
+     This is so filenames can be compared by string comparison
+     functions that are case-sensitive.  Even case-preserving filesystems
+     do not distinguish case in drive letters.  */
+  if (fp[1] == ':' && *fp >= 'A' && *fp <= 'Z')
+    {
+      *fp += 'a' - 'A';
+      fp += 2;
+    }
+
+  if (NILP (Vmswindows_downcase_file_names))
+    {
+      while (*fp)
+	{
+	  if (*fp == '/' || *fp == '\\')
+	    *fp = path_sep;
+	  fp++;
+	}
+      return;
+    }
+
+  sep = path_sep;		/* convert to this path separator */
+  elem = fp;			/* start of current path element */
+
+  do {
+    if (*fp >= 'a' && *fp <= 'z')
+      elem = 0;			/* don't convert this element */
+
+    if (*fp == 0 || *fp == ':')
+      {
+	sep = *fp;		/* restore current separator (or 0) */
+	*fp = '/';		/* after conversion of this element */
+      }
+
+    if (*fp == '/' || *fp == '\\')
+      {
+	if (elem && elem != fp)
+	  {
+	    *fp = 0;		/* temporary end of string */
+	    _strlwr (elem);	/* while we convert to lower case */
+	  }
+	*fp = sep;		/* convert (or restore) path separator */
+	elem = fp + 1;		/* next element starts after separator */
+	sep = path_sep;
+      }
+  } while (*fp++);
+}
+
+/* Destructively turn backslashes into slashes.  */
+void
+dostounix_filename (p)
+     REGISTER char *p;
+{
+  normalize_filename (p, '/');
+}
+
+/* Destructively turn slashes into backslashes.  */
+void
+unixtodos_filename (p)
+     REGISTER char *p;
+{
+  normalize_filename (p, '\\');
+}
+
+/* Remove all CR's that are followed by a LF.
+   (From msdos.c...probably should figure out a way to share it,
+   although this code isn't going to ever change.)  */
+int
+crlf_to_lf (n, buf, lf_count)
+     REGISTER int n;
+     REGISTER unsigned char *buf;
+     REGISTER unsigned *lf_count;
+{
+  unsigned char *np = buf;
+  unsigned char *startp = buf;
+  unsigned char *endp = buf + n;
+
+  if (n == 0)
+    return n;
+  while (buf < endp - 1)
+    {
+      if (*buf == 0x0a)
+	(*lf_count)++;
+      if (*buf == 0x0d)
+	{
+	  if (*(++buf) != 0x0a)
+	    *np++ = 0x0d;
+	}
+      else
+	*np++ = *buf++;
+    }
+  if (buf < endp)
+    {
+      if (*buf == 0x0a)
+	(*lf_count)++;
+    *np++ = *buf++;
+    }
+  return np - startp;
+}
+
+/* Parse the root part of file name, if present.  Return length and
+    optionally store pointer to char after root.  */
+static int
+parse_root (char * name, char ** pPath)
+{
+  char * start = name;
+
+  if (name == NULL)
+    return 0;
+
+  /* find the root name of the volume if given */
+  if (isalpha (name[0]) && name[1] == ':')
+    {
+      /* skip past drive specifier */
+      name += 2;
+      if (IS_DIRECTORY_SEP (name[0]))
+	name++;
+    }
+  else if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1]))
+    {
+      int slashes = 2;
+      name += 2;
+      do
+        {
+	  if (IS_DIRECTORY_SEP (*name) && --slashes == 0)
+	    break;
+	  name++;
+	}
+      while ( *name );
+      if (IS_DIRECTORY_SEP (name[0]))
+	name++;
+    }
+
+  if (pPath)
+    *pPath = name;
+
+  return name - start;
+}
+
+/* Get long base name for name; name is assumed to be absolute.  */
+static int
+get_long_basename (char * name, char * buf, int size)
+{
+  WIN32_FIND_DATA find_data;
+  HANDLE dir_handle;
+  int len = 0;
+#ifdef PIGSFLY
+  char *p;
+
+  /* If the last component of NAME has a wildcard character, 
+     return it as the basename.  */
+  p = name + strlen (name);
+  while (*p != '\\' && *p != ':' && p > name) p--;
+  if (p > name) p++;
+  if (strchr (p, '*') || strchr (p, '?'))
+    {
+      if ((len = strlen (p)) < size)
+	memcpy (buf, p, len + 1);
+      else
+	len = 0;
+      return len;
+    }
+#endif
+
+  dir_handle = FindFirstFile (name, &find_data);
+  if (dir_handle != INVALID_HANDLE_VALUE)
+    {
+      if ((len = strlen (find_data.cFileName)) < size)
+	memcpy (buf, find_data.cFileName, len + 1);
+      else
+	len = 0;
+      FindClose (dir_handle);
+    }
+  return len;
+}
+
+/* Get long name for file, if possible (assumed to be absolute).  */
+BOOL
+win32_get_long_filename (char * name, char * buf, int size)
+{
+  char * o = buf;
+  char * p;
+  char * q;
+  char full[ MAX_PATH ];
+  int len;
+
+  len = strlen (name);
+  if (len >= MAX_PATH)
+    return FALSE;
+
+  /* Use local copy for destructive modification.  */
+  memcpy (full, name, len+1);
+  unixtodos_filename (full);
+
+  /* Copy root part verbatim.  */
+  len = parse_root (full, &p);
+  memcpy (o, full, len);
+  o += len;
+  size -= len;
+
+  do
+    {
+      q = p;
+      p = strchr (q, '\\');
+      if (p) *p = '\0';
+      len = get_long_basename (full, o, size);
+      if (len > 0)
+	{
+	  o += len;
+	  size -= len;
+	  if (p != NULL)
+	    {
+	      *p++ = '\\';
+	      if (size < 2)
+		return FALSE;
+	      *o++ = '\\';
+	      size--;
+	      *o = '\0';
+	    }
+	}
+      else
+	return FALSE;
+    }
+  while (p != NULL && *p);
+
+  return TRUE;
+}
+
+
+/* Routines that are no-ops on NT but are defined to get Emacs to compile.  */
+
+#if 0 /* #### We do not need those, do we? -kkm */
+int 
+unrequest_sigio (void) 
+{ 
+  return 0;
+}
+
+int 
+request_sigio (void) 
+{ 
+  return 0;
+}
+#endif /* 0 */
+
+#define REG_ROOT "SOFTWARE\\GNU\\XEmacs"
+
+LPBYTE 
+nt_get_resource (key, lpdwtype)
+    char *key;
+    LPDWORD lpdwtype;
+{
+  LPBYTE lpvalue;
+  HKEY hrootkey = NULL;
+  DWORD cbData;
+  
+  /* Check both the current user and the local machine to see if 
+     we have any resources.  */
+  
+  if (RegOpenKeyEx (HKEY_CURRENT_USER, REG_ROOT, 0, KEY_READ, &hrootkey) == ERROR_SUCCESS)
+    {
+      lpvalue = NULL;
+
+      if (RegQueryValueEx (hrootkey, key, NULL, NULL, NULL, &cbData) == ERROR_SUCCESS 
+	  && (lpvalue = (LPBYTE) xmalloc (cbData)) != NULL 
+	  && RegQueryValueEx (hrootkey, key, NULL, lpdwtype, lpvalue, &cbData) == ERROR_SUCCESS)
+	{
+	  return (lpvalue);
+	}
+
+      if (lpvalue) xfree (lpvalue);
+	
+      RegCloseKey (hrootkey);
+    } 
+  
+  if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, REG_ROOT, 0, KEY_READ, &hrootkey) == ERROR_SUCCESS)
+    {
+      lpvalue = NULL;
+	
+      if (RegQueryValueEx (hrootkey, key, NULL, NULL, NULL, &cbData) == ERROR_SUCCESS &&
+	  (lpvalue = (LPBYTE) xmalloc (cbData)) != NULL &&
+	  RegQueryValueEx (hrootkey, key, NULL, lpdwtype, lpvalue, &cbData) == ERROR_SUCCESS)
+	{
+	  return (lpvalue);
+	}
+	
+      if (lpvalue) xfree (lpvalue);
+	
+      RegCloseKey (hrootkey);
+    } 
+  
+  return (NULL);
+}
+
+void
+init_environment ()
+{
+  /* Check for environment variables and use registry if they don't exist */
+  {
+    int i;
+    LPBYTE lpval;
+    DWORD dwType;
+
+    static char * env_vars[] = 
+    {
+      "HOME",
+      "emacs_dir",
+      "EMACSLOADPATH",
+      "EMACSDEBUGPATHS",
+      "SHELL",
+      "CMDPROXY",
+      "EMACSDATA",
+      "EMACSPATH",
+      "EMACSPACKAGEPATH",
+      "EMACSLOCKDIR",
+      "INFOPATH"
+    };
+
+    for (i = 0; i < countof (env_vars); i++) 
+      {
+	if (!getenv (env_vars[i]) &&
+	    (lpval = nt_get_resource (env_vars[i], &dwType)) != NULL)
+	  {
+	    if (dwType == REG_EXPAND_SZ)
+	      {
+		char buf1[500], buf2[500];
+
+		ExpandEnvironmentStrings ((LPSTR) lpval, buf1, 500);
+		_snprintf (buf2, 499, "%s=%s", env_vars[i], buf1);
+		putenv (strdup (buf2));
+	      }
+	    else if (dwType == REG_SZ)
+	      {
+		char buf[500];
+		  
+		_snprintf (buf, 499, "%s=%s", env_vars[i], lpval);
+		putenv (strdup (buf));
+	      }
+
+	    xfree (lpval);
+	  }
+      }
+  }
+
+  /* Another special case: on NT, the PATH variable is actually named
+     "Path" although cmd.exe (perhaps NT itself) arranges for
+     environment variable lookup and setting to be case insensitive.
+     However, Emacs assumes a fully case sensitive environment, so we
+     need to change "Path" to "PATH" to match the expectations of
+     various elisp packages.  We do this by the sneaky method of
+     modifying the string in the C runtime environ entry.
+
+     The same applies to COMSPEC.  */
+  {
+    char ** envp;
+
+    for (envp = environ; *envp; envp++)
+      if (_strnicmp (*envp, "PATH=", 5) == 0)
+	memcpy (*envp, "PATH=", 5);
+      else if (_strnicmp (*envp, "COMSPEC=", 8) == 0)
+	memcpy (*envp, "COMSPEC=", 8);
+  }
+
+  /* Remember the initial working directory for getwd, then make the
+     real wd be the location of emacs.exe to avoid conflicts when
+     renaming or deleting directories.  (We also don't call chdir when
+     running subprocesses for the same reason.)  */
+  if (!GetCurrentDirectory (MAXPATHLEN, startup_dir))
+    abort ();
+
+  {
+    char *p;
+    char modname[MAX_PATH];
+
+    if (!GetModuleFileName (NULL, modname, MAX_PATH))
+      abort ();
+    if ((p = strrchr (modname, '\\')) == NULL)
+      abort ();
+    *p = 0;
+
+    SetCurrentDirectory (modname);
+  }
+
+  init_user_info ();
+}
+
+#ifndef HAVE_X_WINDOWS
+/* X11R6 on NT provides the single parameter version of this command. */
+
+#include <sys/timeb.h>
+
+/* Emulate gettimeofday (Ulrich Leodolter, 1/11/95).  */
+void 
+gettimeofday (struct timeval *tv, struct timezone *tz)
+{
+  struct _timeb tb;
+  _ftime (&tb);
+
+  tv->tv_sec = tb.time;
+  tv->tv_usec = tb.millitm * 1000L;
+  if (tz) 
+    {
+      tz->tz_minuteswest = tb.timezone;	/* minutes west of Greenwich  */
+      tz->tz_dsttime = tb.dstflag;	/* type of dst correction  */
+    }
+}
+
+#endif /* HAVE_X_WINDOWS */
+
+/* ------------------------------------------------------------------------- */
+/* IO support and wrapper functions for Win32 API. */
+/* ------------------------------------------------------------------------- */
+
+/* Place a wrapper around the MSVC version of ctime.  It returns NULL
+   on network directories, so we handle that case here.  
+   (Ulrich Leodolter, 1/11/95).  */
+char *
+sys_ctime (const time_t *t)
+{
+  char *str = (char *) ctime (t);
+  return (str ? str : "Sun Jan 01 00:00:00 1970");
+}
+
+/* Emulate sleep...we could have done this with a define, but that
+   would necessitate including windows.h in the files that used it.
+   This is much easier.  */
+
+#ifndef HAVE_X_WINDOWS
+void
+sys_sleep (int seconds)
+{
+  Sleep (seconds * 1000);
+}
+#endif
+
+/* #### This is an evil dirty hack. We must get rid of it.
+   Word "munging" is not in XEmacs lexicon. - kkm */
+
+/* Internal MSVC data and functions for low-level descriptor munging */
+#if (_MSC_VER == 900)
+extern char _osfile[];
+#endif
+extern int __cdecl _set_osfhnd (int fd, long h);
+extern int __cdecl _free_osfhnd (int fd);
+
+/* parallel array of private info on file handles */
+filedesc fd_info [ MAXDESC ];
+
+typedef struct volume_info_data {
+  struct volume_info_data * next;
+
+  /* time when info was obtained */
+  DWORD     timestamp;
+
+  /* actual volume info */
+  char *    root_dir;
+  DWORD     serialnum;
+  DWORD     maxcomp;
+  DWORD     flags;
+  char *    name;
+  char *    type;
+} volume_info_data;
+
+/* Global referenced by various functions.  */
+static volume_info_data volume_info;
+
+/* Vector to indicate which drives are local and fixed (for which cached
+   data never expires).  */
+static BOOL fixed_drives[26];
+
+/* Consider cached volume information to be stale if older than 10s,
+   at least for non-local drives.  Info for fixed drives is never stale.  */
+#define DRIVE_INDEX( c ) ( (c) <= 'Z' ? (c) - 'A' : (c) - 'a' )
+#define VOLINFO_STILL_VALID( root_dir, info )		\
+  ( ( isalpha (root_dir[0]) &&				\
+      fixed_drives[ DRIVE_INDEX (root_dir[0]) ] )	\
+    || GetTickCount () - info->timestamp < 10000 )
+
+/* Cache support functions.  */
+
+/* Simple linked list with linear search is sufficient.  */
+static volume_info_data *volume_cache = NULL;
+
+static volume_info_data *
+lookup_volume_info (char * root_dir)
+{
+  volume_info_data * info;
+
+  for (info = volume_cache; info; info = info->next)
+    if (stricmp (info->root_dir, root_dir) == 0)
+      break;
+  return info;
+}
+
+static void
+add_volume_info (char * root_dir, volume_info_data * info)
+{
+  info->root_dir = xstrdup (root_dir);
+  info->next = volume_cache;
+  volume_cache = info;
+}
+
+
+/* Wrapper for GetVolumeInformation, which uses caching to avoid
+   performance penalty (~2ms on 486 for local drives, 7.5ms for local
+   cdrom drive, ~5-10ms or more for remote drives on LAN).  */
+volume_info_data *
+GetCachedVolumeInformation (char * root_dir)
+{
+  volume_info_data * info;
+  char default_root[ MAX_PATH ];
+
+  /* NULL for root_dir means use root from current directory.  */
+  if (root_dir == NULL)
+    {
+      if (GetCurrentDirectory (MAX_PATH, default_root) == 0)
+	return NULL;
+      parse_root (default_root, &root_dir);
+      *root_dir = 0;
+      root_dir = default_root;
+    }
+
+  /* Local fixed drives can be cached permanently.  Removable drives
+     cannot be cached permanently, since the volume name and serial
+     number (if nothing else) can change.  Remote drives should be
+     treated as if they are removable, since there is no sure way to
+     tell whether they are or not.  Also, the UNC association of drive
+     letters mapped to remote volumes can be changed at any time (even
+     by other processes) without notice.
+   
+     As a compromise, so we can benefit from caching info for remote
+     volumes, we use a simple expiry mechanism to invalidate cache
+     entries that are more than ten seconds old.  */
+
+#if 0
+  /* No point doing this, because WNetGetConnection is even slower than
+     GetVolumeInformation, consistently taking ~50ms on a 486 (FWIW,
+     GetDriveType is about the only call of this type which does not
+     involve network access, and so is extremely quick).  */
+
+  /* Map drive letter to UNC if remote. */
+  if ( isalpha( root_dir[0] ) && !fixed[ DRIVE_INDEX( root_dir[0] ) ] )
+    {
+      char remote_name[ 256 ];
+      char drive[3] = { root_dir[0], ':' };
+
+      if (WNetGetConnection (drive, remote_name, sizeof (remote_name))
+	  == NO_ERROR)
+	/* do something */ ;
+    }
+#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
+      {
+	free (info->name);
+	free (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_win32_filename (const char * name, const char ** pPath)
+{
+  static char shortname[MAX_PATH];
+  char * str = shortname;
+  char c;
+  const char * path;
+  const char * save_name = name;
+
+  if (is_fat_volume (name, &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;
+}
+
+
+/* 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;
+
+DIR *
+opendir (const 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 (!(dirp = xnew_and_zero(DIR)))
+    return NULL;
+  if (dir_find_handle != INVALID_HANDLE_VALUE)
+    return NULL;
+
+  dirp->dd_fd = 0;
+  dirp->dd_loc = 0;
+  dirp->dd_size = 0;
+
+  strncpy (dir_pathname, map_win32_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;
+    }
+  xfree (dirp);
+}
+
+struct direct *
+readdir (DIR *dirp)
+{
+  /* If we aren't dir_finding, do a find-first, otherwise do a find-next. */
+  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 (Vmswindows_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;
+}
+
+#if 0
+/* #### Have to check if all that sad story about '95 is true - kkm */
+int
+sys_rename (const char * oldname, const char * newname)
+{
+  char temp[MAX_PATH];
+  DWORD attr;
+
+  /* MoveFile on Win95 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 Win95 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 Win95 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_win32_filename (oldname, NULL));
+
+  if (GetVersion () & 0x80000000)
+    {
+      char * p;
+
+      if (p = strrchr (temp, '\\'))
+	p++;
+      else
+	p = temp;
+      /* Force temp name to require a manufactured 8.3 alias - this
+	 seems to make the second rename work properly. */
+      strcpy (p, "_rename_temp.XXXXXX");
+      sys_mktemp (temp);
+      if (rename (map_win32_filename (oldname, NULL), temp) < 0)
+	return -1;
+    }
+
+  /* Emulate Unix behavior - newname is deleted if it already exists
+     (at least if it is a file; don't do this for directories).
+     However, don't do this if we are just changing the case of the file
+     name - we will end up deleting the file we are trying to rename!  */
+  newname = map_win32_filename (newname, NULL);
+
+  /* TODO: Use GetInformationByHandle (on NT) to ensure newname and temp
+     do not refer to the same file, eg. through share aliases.  */
+  if (stricmp (newname, temp) != 0
+      && (attr = GetFileAttributes (newname)) != -1
+      && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0)
+    {
+      _chmod (newname, 0666);
+      _unlink (newname);
+    }
+
+  return rename (temp, newname);
+}
+#endif /* 0 */
+
+static FILETIME utc_base_ft;
+static long double utc_base;
+static int init = 0;
+
+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);
+}
+
+#if 0
+/* in case we ever have need of this */
+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 - pft->dwHighDateTime);
+}
+#endif
+
+#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 recognize hard
+     links.  */
+  if (!win32_get_long_filename ((char *)name, fullname, MAX_PATH))
+    abort ();
+
+  parse_root (fullname, &p);
+  /* Normal Win32 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;
+  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_win32_filename (path, &path);
+  /* must be valid filename, no wild cards */
+  if (strchr (name, '*') || strchr (name, '?'))
+    {
+      errno = ENOENT;
+      return -1;
+    }
+
+  /* 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 (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.  */
+      if (dir_find_handle != INVALID_HANDLE_VALUE &&
+	  (len = strlen (dir_pathname)),
+	  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 (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+    {
+      buf->st_mode = _S_IFDIR;
+      buf->st_nlink = 2;	/* doesn't really matter */
+      fake_inode = 0;		/* this doesn't either I think */
+    }
+  else if (!NILP (Vmswindows_get_true_file_attributes))
+    {
+      /* This is more accurate in terms of getting the correct number
+	 of links, but is quite slow (it is noticeable when Emacs is
+	 making a list of file name completions). */
+      BY_HANDLE_FILE_INFORMATION info;
+
+      /* No access rights required to get info.  */
+      fh = CreateFile (name, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
+		       OPEN_EXISTING, 0, NULL);
+
+      if (GetFileInformationByHandle (fh, &info))
+	{
+	  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;
+	    }
+	  buf->st_nlink = (short) 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;
+	  CloseHandle (fh);
+	}
+      else
+	{
+	  errno = EACCES;
+	  return -1;
+	}
+    }
+  else
+    {
+      /* Don't bother to make this information more accurate.  */
+      buf->st_mode = _S_IFREG;
+      buf->st_nlink = 1;
+      fake_inode = 0;
+    }
+
+#if 0
+  /* Not sure if there is any point in this.  */
+  if (!NILP (Vwin32_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.  */
+  buf->st_ino = (unsigned short) (fake_inode ^ (fake_inode >> 16));
+
+  /* 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_win32_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
+    {
+      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;
+    }
+
+  buf->st_mode |= permission | (permission >> 3) | (permission >> 6);
+
+  return 0;
+}
+
+/* From callproc.c  */
+extern Lisp_Object Vbinary_process_input;
+extern Lisp_Object Vbinary_process_output;
+
+/* 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)
+    {
+      flags = FILE_PIPE | FILE_READ;
+      if (!NILP (Vbinary_process_output))
+	  flags |= FILE_BINARY;
+      fd_info[phandles[0]].flags = flags;
+
+      flags = FILE_PIPE | FILE_WRITE;
+      if (!NILP (Vbinary_process_input))
+	  flags |= FILE_BINARY;
+      fd_info[phandles[1]].flags = flags;
+    }
+
+  return rc;
+}
+
+void
+term_ntproc (int unused)
+{
+}
+
+void
+init_ntproc ()
+{
+  /* 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)++;
+    }
+  }
+}
+#ifndef HAVE_TTY
+Lisp_Object
+tty_semi_canonicalize_console_connection (Lisp_Object connection,
+					  Error_behavior errb)
+{
+  return Vstdio_str;
+}
+
+Lisp_Object
+tty_canonicalize_console_connection (Lisp_Object connection,
+				     Error_behavior errb)
+{
+  return Vstdio_str;
+}
+
+Lisp_Object
+tty_semi_canonicalize_device_connection (Lisp_Object connection,
+					 Error_behavior errb)
+{
+  return Vstdio_str;
+}
+
+Lisp_Object
+tty_canonicalize_device_connection (Lisp_Object connection,
+				    Error_behavior errb)
+{
+  return Vstdio_str;
+}
+#endif
+
+/*--------------------------------------------------------------------*/
+/* Signal support                                                     */
+/*--------------------------------------------------------------------*/
+
+/* We need MS-defined signal and raise here */
+#undef signal
+#undef raise
+
+#define sigmask(nsig) (1U << nsig)
+
+/* We can support as many signals as fit into word */
+#define SIG_MAX 32
+
+/* Signal handlers. Initial value = 0 = SIG_DFL */
+static void (__cdecl *signal_handlers[SIG_MAX])(int) = {0};
+
+/* Signal block mask: bit set to 1 means blocked */
+unsigned signal_block_mask = 0;
+
+/* Signal pending mask: bit set to 1 means sig is pending */
+unsigned signal_pending_mask = 0;
+
+msw_sighandler msw_sigset (int nsig, msw_sighandler handler)
+{
+  /* We delegate some signals to the system function */
+  if (nsig == SIGFPE || nsig == SIGABRT || nsig == SIGINT)
+    return signal (nsig, handler);
+
+  if (nsig < 0 || nsig > SIG_MAX)
+    {
+      errno = EINVAL;
+      return NULL;
+    }
+
+  /* Store handler ptr */
+  {
+    msw_sighandler old_handler = signal_handlers[nsig];
+    signal_handlers[nsig] = handler;
+    return old_handler;
+  }
+}
+  
+int msw_sighold (int nsig)
+{
+  if (nsig < 0 || nsig > SIG_MAX)
+    return errno = EINVAL;
+
+  signal_block_mask |= sigmask(nsig);
+  return 0;
+}
+
+int msw_sigrelse (int nsig)
+{
+  if (nsig < 0 || nsig > SIG_MAX)
+    return errno = EINVAL;
+
+  signal_block_mask &= ~sigmask(nsig);
+
+  if (signal_pending_mask & sigmask(nsig))
+    msw_raise (nsig);
+
+  return 0;
+}
+
+int msw_sigpause (int nsig)
+{
+  /* This is currently not called, because the only
+     call to sigpause inside XEmacs is with SIGCHLD
+     parameter. Just in case, we put an assert here,
+     so anyone who will add a call to sigpause will
+     be surprised (or surprise someone else...) */
+  assert (0);
+  return 0;
+}
+
+int msw_raise (int nsig)
+{
+  /* We delegate some raises to the system routine */
+  if (nsig == SIGFPE || nsig == SIGABRT || nsig == SIGINT)
+    return raise (nsig);
+
+  if (nsig < 0 || nsig > SIG_MAX)
+    return errno = EINVAL;
+
+  /* If the signal is blocked, remember to issue later */
+  if (signal_block_mask & sigmask(nsig))
+    {
+      signal_pending_mask |= sigmask(nsig);
+      return 0;
+    }
+
+  if (signal_handlers[nsig] == SIG_IGN)
+    return 0;
+
+  if (signal_handlers[nsig] != SIG_DFL)
+    {
+      (*signal_handlers[nsig])(nsig);
+      return 0;
+    }
+
+  /* Default signal actions */
+  if (nsig == SIGALRM || nsig == SIGPROF)
+    exit (3);
+
+  /* Other signals are ignored by default */
+  return 0;
+}
+
+/*--------------------------------------------------------------------*/
+/* Async timers                                                       */
+/*--------------------------------------------------------------------*/
+
+/* We emulate two timers, one for SIGALRM, another for SIGPROF.
+
+   itimerproc() function has an implementation limitation: it does
+   not allow to set *both* interval and period. If an attempt is
+   made to set both, and then they are unequal, the function
+   asserts.
+
+   Minimum timer resolution on Win32 systems varies, and is greater
+   than or equal than 1 ms. The resolution is always wrapped not to
+   attempt to get below the system defined limit.
+   */
+
+/* Timer precision, denominator of one fraction: for 100 ms
+   interval, request 10 ms precision
+   */
+const int timer_prec = 10;
+
+/* Last itimervals, as set by calls to setitimer */
+static struct itimerval it_alarm;
+static struct itimerval it_prof;
+
+/* Timer IDs as returned by MM */
+MMRESULT tid_alarm = 0;
+MMRESULT tid_prof = 0;
+
+static void CALLBACK timer_proc (UINT uID, UINT uMsg, DWORD dwUser,
+				 DWORD dw1, DWORD dw2)
+{
+  /* Just raise a signal indicated by dwUser parameter */
+  msw_raise (dwUser);
+}
+
+/* Divide time in ms specified by IT by DENOM. Return 1 ms
+   if division results in zero */
+static UINT period (const struct itimerval* it, UINT denom)
+{
+  static TIMECAPS time_caps;
+
+  UINT res;
+  const struct timeval* tv = 
+    (it->it_value.tv_sec == 0 && it->it_value.tv_usec == 0)
+    ? &it->it_interval : &it->it_value;
+  
+  /* Zero means stop timer */
+  if (tv->tv_sec == 0 && tv->tv_usec == 0)
+    return 0;
+  
+  /* Convert to ms and divide by denom */
+  res = (tv->tv_sec * 1000 + (tv->tv_usec + 500) / 1000) / denom;
+  
+  /* Converge to minimum timer resolution */
+  if (time_caps.wPeriodMin == 0)
+      timeGetDevCaps (&time_caps, sizeof(time_caps));
+
+  if (res < time_caps.wPeriodMin)
+    res = time_caps.wPeriodMin;
+
+  return res;
+}
+
+static int setitimer_helper (const struct itimerval* itnew,
+			     struct itimerval* itold, struct itimerval* itcurrent,
+			     MMRESULT* tid, DWORD sigkind)
+{
+  UINT delay, resolution, event_type;
+
+  /* First stop the old timer */
+  if (*tid)
+    {
+      timeKillEvent (*tid);
+      timeEndPeriod (period (itcurrent, timer_prec));
+      *tid = 0;
+    }
+
+  /* Return old itimerval if requested */
+  if (itold)
+    *itold = *itcurrent;
+
+  *itcurrent = *itnew;
+
+  /* Determine if to start new timer */
+  delay = period (itnew, 1);
+  if (delay)
+    {
+      resolution = period (itnew, timer_prec);
+      event_type = (itnew->it_value.tv_sec == 0 && itnew->it_value.tv_usec == 0)
+	? TIME_ONESHOT : TIME_PERIODIC;
+      timeBeginPeriod (resolution);
+      *tid = timeSetEvent (delay, resolution, timer_proc, sigkind, event_type);
+    }
+
+  return !delay || *tid;
+}
+ 
+int setitimer (int kind, const struct itimerval* itnew,
+	       struct itimerval* itold)
+{
+  /* In this version, both interval and value are allowed
+     only if they are equal. */
+  assert ((itnew->it_value.tv_sec == 0 && itnew->it_value.tv_usec == 0)
+	  || (itnew->it_interval.tv_sec == 0 && itnew->it_interval.tv_usec == 0)
+	  || (itnew->it_value.tv_sec == itnew->it_interval.tv_sec &&
+	      itnew->it_value.tv_usec == itnew->it_interval.tv_usec));
+
+  if (kind == ITIMER_REAL)
+    return setitimer_helper (itnew, itold, &it_alarm, &tid_alarm, SIGALRM);
+  else if (kind == ITIMER_PROF)
+    return setitimer_helper (itnew, itold, &it_prof, &tid_prof, SIGPROF);
+  else
+    return errno = EINVAL;
+}
+
+int
+open_input_file (file_data *p_file, CONST char *filename)
+{
+  HANDLE file;
+  HANDLE file_mapping;
+  void  *file_base;
+  DWORD size, upper_size;
+
+  file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
+		     OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+  if (file == INVALID_HANDLE_VALUE) 
+    return FALSE;
+
+  size = GetFileSize (file, &upper_size);
+  file_mapping = CreateFileMapping (file, NULL, PAGE_READONLY, 
+				    0, size, NULL);
+  if (!file_mapping) 
+    return FALSE;
+
+  file_base = MapViewOfFile (file_mapping, FILE_MAP_READ, 0, 0, size);
+  if (file_base == 0) 
+    return FALSE;
+
+  p_file->name = (char*)filename;
+  p_file->size = size;
+  p_file->file = file;
+  p_file->file_mapping = file_mapping;
+  p_file->file_base = file_base;
+
+  return TRUE;
+}
+
+/* Close the system structures associated with the given file.  */
+void
+close_file_data (file_data *p_file)
+{
+    UnmapViewOfFile (p_file->file_base);
+    CloseHandle (p_file->file_mapping);
+    CloseHandle (p_file->file);
+}
+
+/* end of nt.c */