XEmacs 21.4.4 "Artificial Intelligence".
[chise/xemacs-chise.git.1] / src / nt.c
index e9d2961..2eac6d0 100644 (file)
--- a/src/nt.c
+++ b/src/nt.c
@@ -25,33 +25,21 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 /* 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 "buffer.h"
 
 #include "systime.h"
 #include "syssignal.h"
 #include "sysproc.h"
+#include "sysfile.h"
+#include "syspwd.h"
+#include "sysdir.h"
 
-#include <ctype.h>
-#include <direct.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <io.h>
-#include <pwd.h>
-#include <signal.h>
-#include <stddef.h> /* for offsetof */
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#include <windows.h>
-#include <mmsystem.h>
+#include "syswindows.h"
 
 #include "nt.h"
-#include <sys/dir.h>
 #include "ntheap.h"
 
 
@@ -61,7 +49,7 @@ extern Lisp_Object Vwin32_generate_fake_inodes;
 #endif
 extern Lisp_Object Vmswindows_get_true_file_attributes;
 
-extern char *get_home_directory(void);
+Fixnum nt_fake_unix_uid;
 
 static char startup_dir[ MAXPATHLEN ];
 
@@ -82,20 +70,6 @@ getwd (char *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
@@ -118,39 +92,40 @@ static struct passwd the_passwd =
   the_passwd_shell,
 };
 
-int 
-getuid () 
-{ 
-  return the_passwd.pw_uid;
+uid_t
+getuid (void) 
+{
+  return nt_fake_unix_uid;
 }
 
-int 
-geteuid () 
+uid_t 
+geteuid (void) 
 { 
-  /* 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 (); 
+  return nt_fake_unix_uid;
 }
 
-int 
-getgid () 
+gid_t
+getgid (void) 
 { 
   return the_passwd.pw_gid;
 }
 
-int 
-getegid () 
+gid_t
+getegid (void) 
 { 
   return getgid ();
 }
 
 struct passwd *
-getpwuid (int uid)
+getpwuid (uid_t uid)
 {
-  if (uid == the_passwd.pw_uid)
-    return &the_passwd;
-  return NULL;
+  if (uid == nt_fake_unix_uid)
+    {
+      the_passwd.pw_gid = the_passwd.pw_uid = uid;
+      return &the_passwd;
+    }
+  else
+    return NULL;
 }
 
 struct passwd *
@@ -169,8 +144,14 @@ getpwnam (const char *name)
 }
 
 void
-init_user_info ()
+init_user_info (void)
 {
+  /* This code is pretty much of ad hoc nature. There is no unix-like
+     UIDs under Windows NT. There is no concept of root user, because
+     all security is ACL-based. Instead, let's use a simple variable,
+     nt-fake-unix-uid, which would allow the user to have a uid of
+     choice. --kkm, 02/03/2000 */
+#if 0
   /* Find the user's real name by opening the process token and
      looking up the name associated with the user-sid in that token.
 
@@ -246,6 +227,18 @@ init_user_info ()
       the_passwd.pw_gid = 123;
     }
 
+  if (token)
+    CloseHandle (token);
+#else
+  /* Obtain only logon id here, uid part is moved to getuid */
+  char name[256];
+  DWORD length = sizeof (name);
+  if (GetUserName (name, &length))
+    strcpy (the_passwd.pw_name, name);
+  else
+    strcpy (the_passwd.pw_name, "unknown");
+#endif
+
   /* Ensure HOME and SHELL are defined. */
 #if 0
   /*
@@ -258,11 +251,8 @@ init_user_info ()
     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_dir, (char *)get_home_directory());
   strcpy (the_passwd.pw_shell, getenv ("SHELL"));
-
-  if (token)
-    CloseHandle (token);
 }
 
 /* Normalize filename by converting all path separators to
@@ -270,9 +260,7 @@ init_user_info ()
    case path name components to lower case.  */
 
 static void
-normalize_filename (fp, path_sep)
-     REGISTER char *fp;
-     char path_sep;
+normalize_filename (char *fp, char path_sep)
 {
   char sep;
   char *elem;
@@ -328,16 +316,14 @@ normalize_filename (fp, path_sep)
 
 /* Destructively turn backslashes into slashes.  */
 void
-dostounix_filename (p)
-     REGISTER char *p;
+dostounix_filename (char *p)
 {
   normalize_filename (p, '/');
 }
 
 /* Destructively turn slashes into backslashes.  */
 void
-unixtodos_filename (p)
-     REGISTER char *p;
+unixtodos_filename (char *p)
 {
   normalize_filename (p, '\\');
 }
@@ -346,10 +332,7 @@ unixtodos_filename (p)
    (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;
+crlf_to_lf (int n, unsigned char *buf, unsigned *lf_count)
 {
   unsigned char *np = buf;
   unsigned char *startp = buf;
@@ -526,14 +509,11 @@ request_sigio (void)
 #define REG_ROOT "SOFTWARE\\GNU\\XEmacs"
 
 LPBYTE 
-nt_get_resource (key, lpdwtype)
-    char *key;
-    LPDWORD lpdwtype;
+nt_get_resource (char *key, LPDWORD lpdwtype)
 {
   LPBYTE lpvalue;
   HKEY hrootkey = NULL;
   DWORD cbData;
-  BOOL ok = FALSE;
   
   /* Check both the current user and the local machine to see if 
      we have any resources.  */
@@ -574,7 +554,7 @@ nt_get_resource (key, lpdwtype)
 }
 
 void
-init_environment ()
+init_environment (void)
 {
   /* Check for environment variables and use registry if they don't exist */
   {
@@ -596,7 +576,9 @@ init_environment ()
       "EMACSLOCKDIR",
       "INFOPATH"
     };
-
+#if defined (HEAP_IN_DATA) && !defined(PDUMP)
+    cache_system_info ();
+#endif
     for (i = 0; i < countof (env_vars); i++) 
       {
        if (!getenv (env_vars[i]) &&
@@ -942,7 +924,7 @@ map_win32_filename (const char * name, const char ** pPath)
   static char shortname[MAX_PATH];
   char * str = shortname;
   char c;
-  char * path;
+  const char * path;
   const char * save_name = name;
 
   if (is_fat_volume (name, &path)) /* truncate to 8.3 */
@@ -1052,7 +1034,7 @@ opendir (const char *filename)
   /* Opening is done by FindFirstFile.  However, a read is inherent to
      this operation, so we defer the open until read time.  */
 
-  if (!(dirp = (DIR *) xmalloc (sizeof (DIR))))
+  if (!(dirp = xnew_and_zero(DIR)))
     return NULL;
   if (dir_find_handle != INVALID_HANDLE_VALUE)
     return NULL;
@@ -1068,16 +1050,22 @@ opendir (const char *filename)
   return dirp;
 }
 
-void
+int
 closedir (DIR *dirp)
 {
+  BOOL retval;
+
   /* If we have a find-handle open, close it.  */
   if (dir_find_handle != INVALID_HANDLE_VALUE)
     {
-      FindClose (dir_find_handle);
+      retval = FindClose (dir_find_handle);
       dir_find_handle = INVALID_HANDLE_VALUE;
     }
-  xfree ((char *) dirp);
+  xfree (dirp);
+  if (retval)
+    return 0;
+  else
+    return -1;
 }
 
 struct direct *
@@ -1107,7 +1095,7 @@ readdir (DIR *dirp)
     }
   
   /* Emacs never uses this value, so don't bother making it match
-     value returned by stat().  */
+     value returned by xemacs_stat().  */
   dir_static.d_ino = 1;
   
   dir_static.d_reclen = sizeof (struct direct) - MAXNAMLEN + 3 +
@@ -1193,6 +1181,8 @@ static FILETIME utc_base_ft;
 static long double utc_base;
 static int init = 0;
 
+#if 0
+
 time_t
 convert_time (FILETIME ft)
 {
@@ -1224,6 +1214,81 @@ convert_time (FILETIME ft)
   ret -= utc_base;
   return (time_t) (ret * 1e-7);
 }
+#else
+
+static LARGE_INTEGER utc_base_li;
+
+time_t
+convert_time (FILETIME uft)
+{
+  time_t ret;
+#ifndef MAXLONGLONG
+  SYSTEMTIME st;
+  struct tm t;
+  FILETIME ft;
+  TIME_ZONE_INFORMATION tzi;
+  DWORD tzid;
+#else
+  LARGE_INTEGER lft;
+#endif
+
+  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_li.LowPart = utc_base_ft.dwLowDateTime;
+      utc_base_li.HighPart = utc_base_ft.dwHighDateTime;
+
+      init = 1;
+    }
+
+#ifdef MAXLONGLONG
+
+  /* On a compiler that supports long integers, do it the easy way */
+  lft.LowPart = uft.dwLowDateTime;
+  lft.HighPart = uft.dwHighDateTime;
+  ret = (time_t) ((lft.QuadPart - utc_base_li.QuadPart) / 10000000);
+
+#else
+
+  /* Do it the hard way using mktime. */
+  FileTimeToLocalFileTime(&uft, &ft);
+  FileTimeToSystemTime (&ft, &st);
+  tzid = GetTimeZoneInformation (&tzi);
+  t.tm_year = st.wYear - 1900;
+  t.tm_mon = st.wMonth - 1;
+  t.tm_mday = st.wDay;
+  t.tm_hour = st.wHour;
+  t.tm_min = st.wMinute;
+  t.tm_sec = st.wSecond;
+  t.tm_isdst = (tzid == TIME_ZONE_ID_DAYLIGHT);
+  /* st.wMilliseconds not applicable */
+  ret = mktime(&t);
+  if (ret == -1)
+    {
+      ret = 0;
+    }
+
+#endif
+
+  return ret;
+}
+#endif
+#if defined(MINGW) && CYGWIN_VERSION_DLL_MAJOR <= 21
+#undef LowPart
+#undef HighPart
+#endif
 
 #if 0
 /* in case we ever have need of this */
@@ -1301,11 +1366,106 @@ generate_inode_val (const char * name)
 
 #endif
 
+/* #### aichner@ecf.teradyne.com reported that with the library
+   provided stat/fstat, (file-exist "d:\\tmp\\") =>> nil,
+   (file-exist "d:\\tmp") =>> t, when d:\tmp exists. Whenever
+   we opt to use non-encapsulated stat(), this should serve as
+   a compatibility test. --kkm */
+
+/* Since stat is encapsulated on Windows NT, we need to encapsulate
+   the equally broken fstat as well. FSFmacs also provides its own
+   utime. Is that necessary here too? */
+int
+mswindows_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 = 2;       /* doesn't really matter */
+      fake_inode = 0;          /* this doesn't either I think */
+    }
+  else
+    {
+      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;
+    }
+
+  /* MSVC defines _ino_t to be short; other libc's might not.  */
+  if (sizeof (buf->st_ino) == 2)
+    buf->st_ino = (unsigned short) (fake_inode ^ (fake_inode >> 16));
+  else
+    buf->st_ino = (unsigned short) 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;
+
+  buf->st_mode |= permission | (permission >> 3) | (permission >> 6);
+
+  return 0;
+}
+
 /* 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)
+mswindows_stat (const char * path, struct stat * buf)
 {
   char * name;
   WIN32_FIND_DATA wfd;
@@ -1335,7 +1495,7 @@ stat (const char * path, struct stat * buf)
   len = strlen (name);
   rootdir = (path >= name + len - 1
             && (IS_DIRECTORY_SEP (*path) || *path == 0));
-  name = strcpy (alloca (len + 2), name);
+  name = strcpy ((char *)alloca (len + 2), name);
 
   if (rootdir)
     {
@@ -1453,14 +1613,12 @@ stat (const char * path, struct stat * buf)
   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;
+  buf->st_uid = buf->st_gid = (short) nt_fake_unix_uid;
 
   /* 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. */
@@ -1527,74 +1685,13 @@ sys_pipe (int * phandles)
   return rc;
 }
 
-/* From ntproc.c */
-extern Lisp_Object Vwin32_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)
-    {
-      /* fd is not a pipe or socket */
-      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 Win95
-        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 Win32 programs do this - "dir" in a command
-        shell on NT is very slow if we don't do this. */
-      if (rc > 0)
-       {
-         int wait = XINT (Vwin32_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);
-       }
-    }
-
-  if (rc == sizeof (char))
-    cp->status = STATUS_READ_SUCCEEDED;
-  else
-    cp->status = STATUS_READ_FAILED;
-
-  return cp->status;
-}
-
 void
 term_ntproc (int unused)
 {
 }
 
 void
-init_ntproc ()
+init_ntproc (void)
 {
   /* Initial preparation for subprocess support: replace our standard
      handles with non-inheritable versions. */
@@ -1727,7 +1824,7 @@ 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)
+mswindows_sighandler mswindows_sigset (int nsig, mswindows_sighandler handler)
 {
   /* We delegate some signals to the system function */
   if (nsig == SIGFPE || nsig == SIGABRT || nsig == SIGINT)
@@ -1741,13 +1838,13 @@ msw_sighandler msw_sigset (int nsig, msw_sighandler handler)
 
   /* Store handler ptr */
   {
-    msw_sighandler old_handler = signal_handlers[nsig];
+    mswindows_sighandler old_handler = signal_handlers[nsig];
     signal_handlers[nsig] = handler;
     return old_handler;
   }
 }
   
-int msw_sighold (int nsig)
+int mswindows_sighold (int nsig)
 {
   if (nsig < 0 || nsig > SIG_MAX)
     return errno = EINVAL;
@@ -1756,7 +1853,7 @@ int msw_sighold (int nsig)
   return 0;
 }
 
-int msw_sigrelse (int nsig)
+int mswindows_sigrelse (int nsig)
 {
   if (nsig < 0 || nsig > SIG_MAX)
     return errno = EINVAL;
@@ -1764,12 +1861,12 @@ int msw_sigrelse (int nsig)
   signal_block_mask &= ~sigmask(nsig);
 
   if (signal_pending_mask & sigmask(nsig))
-    msw_raise (nsig);
+    mswindows_raise (nsig);
 
   return 0;
 }
 
-int msw_sigpause (int nsig)
+int mswindows_sigpause (int nsig)
 {
   /* This is currently not called, because the only
      call to sigpause inside XEmacs is with SIGCHLD
@@ -1780,7 +1877,7 @@ int msw_sigpause (int nsig)
   return 0;
 }
 
-int msw_raise (int nsig)
+int mswindows_raise (int nsig)
 {
   /* We delegate some raises to the system routine */
   if (nsig == SIGFPE || nsig == SIGABRT || nsig == SIGINT)
@@ -1810,6 +1907,7 @@ int msw_raise (int nsig)
     exit (3);
 
   /* Other signals are ignored by default */
+  return 0;
 }
 
 /*--------------------------------------------------------------------*/
@@ -1845,7 +1943,7 @@ 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);
+  mswindows_raise (dwUser);
 }
 
 /* Divide time in ms specified by IT by DENOM. Return 1 ms
@@ -1928,4 +2026,313 @@ int setitimer (int kind, const struct itimerval* itnew,
     return errno = EINVAL;
 }
 
+\f
+/*--------------------------------------------------------------------*/
+/*                        Memory-mapped files                         */
+/*--------------------------------------------------------------------*/
+
+int
+open_input_file (file_data *p_file, const char *filename)
+{
+  /* Synched with FSF 20.6.  We fixed some warnings. */
+  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 = (char *)file_base;
+
+  return TRUE;
+}
+
+int
+open_output_file (file_data *p_file, const char *filename, unsigned long size)
+{
+  /* Synched with FSF 20.6.  We fixed some warnings. */
+  HANDLE file;
+  HANDLE file_mapping;
+  void  *file_base;
+
+  file = CreateFile (filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+                    CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
+  if (file == INVALID_HANDLE_VALUE) 
+    return FALSE;
+
+  file_mapping = CreateFileMapping (file, NULL, PAGE_READWRITE, 
+                                   0, size, NULL);
+  if (!file_mapping) 
+    return FALSE;
+  
+  file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size);
+  if (file_base == NULL) 
+    return FALSE;
+  
+  p_file->name = filename;
+  p_file->size = size;
+  p_file->file = file;
+  p_file->file_mapping = file_mapping;
+  p_file->file_base = (char*) file_base;
+
+  return TRUE;
+}
+
+#if 1 /* !defined(MINGW) */
+/* Return pointer to section header for section containing the given
+   relative virtual address. */
+static IMAGE_SECTION_HEADER *
+rva_to_section (DWORD rva, IMAGE_NT_HEADERS * nt_header)
+{
+  /* Synched with FSF 20.6.  We added MINGW stuff. */
+  PIMAGE_SECTION_HEADER section;
+  int i;
+
+  section = IMAGE_FIRST_SECTION (nt_header);
+
+  for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
+    {
+      /* Some linkers (eg. the NT SDK linker I believe) swapped the
+        meaning of these two values - or rather, they ignored
+        VirtualSize entirely and always set it to zero.  This affects
+        some very old exes (eg. gzip dated Dec 1993).  Since
+        mswindows_executable_type relies on this function to work reliably,
+        we need to cope with this.  */
+      DWORD real_size = max (section->SizeOfRawData,
+                            section->Misc.VirtualSize);
+      if (rva >= section->VirtualAddress
+         && rva < section->VirtualAddress + real_size)
+       return section;
+      section++;
+    }
+  return NULL;
+}
+#endif
+
+void
+mswindows_executable_type (const char * filename, int * is_dos_app,
+                          int * is_cygnus_app)
+{
+  /* Synched with FSF 20.6.  We added MINGW stuff and casts. */
+  file_data executable;
+  char * p;
+
+  /* Default values in case we can't tell for sure.  */
+  *is_dos_app = FALSE;
+  *is_cygnus_app = FALSE;
+
+  if (!open_input_file (&executable, filename))
+    return;
+
+  p = strrchr (filename, '.');
+
+  /* We can only identify DOS .com programs from the extension. */
+  if (p && stricmp (p, ".com") == 0)
+    *is_dos_app = TRUE;
+  else if (p && (stricmp (p, ".bat") == 0 ||
+                stricmp (p, ".cmd") == 0))
+    {
+      /* A DOS shell script - it appears that CreateProcess is happy to
+        accept this (somewhat surprisingly); presumably it looks at
+        COMSPEC to determine what executable to actually invoke.
+        Therefore, we have to do the same here as well. */
+      /* Actually, I think it uses the program association for that
+        extension, which is defined in the registry.  */
+      p = egetenv ("COMSPEC");
+      if (p)
+       mswindows_executable_type (p, is_dos_app, is_cygnus_app);
+    }
+  else
+    {
+      /* Look for DOS .exe signature - if found, we must also check that
+        it isn't really a 16- or 32-bit Windows exe, since both formats
+        start with a DOS program stub.  Note that 16-bit Windows
+        executables use the OS/2 1.x format. */
+
+#if 0 /* defined( MINGW ) */
+      /* mingw32 doesn't have enough headers to detect cygwin
+        apps, just do what we can. */
+      FILHDR * exe_header;
+
+      exe_header = (FILHDR*) executable.file_base;
+      if (exe_header->e_magic != DOSMAGIC)
+       goto unwind;
+
+      if ((char*) exe_header->e_lfanew > (char*) executable.size)
+       {
+         /* Some dos headers (pkunzip) have bogus e_lfanew fields.  */
+         *is_dos_app = TRUE;
+       } 
+      else if (exe_header->nt_signature != NT_SIGNATURE)
+       {
+         *is_dos_app = TRUE;
+       }
+#else
+      IMAGE_DOS_HEADER * dos_header;
+      IMAGE_NT_HEADERS * nt_header;
+
+      dos_header = (PIMAGE_DOS_HEADER) executable.file_base;
+      if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
+       goto unwind;
+         
+      nt_header = (PIMAGE_NT_HEADERS) ((char*) dos_header + dos_header->e_lfanew);
+         
+      if ((char*) nt_header > (char*) dos_header + executable.size) 
+       {
+         /* Some dos headers (pkunzip) have bogus e_lfanew fields.  */
+         *is_dos_app = TRUE;
+       } 
+      else if (nt_header->Signature != IMAGE_NT_SIGNATURE &&
+              LOWORD (nt_header->Signature) != IMAGE_OS2_SIGNATURE)
+       {
+         *is_dos_app = TRUE;
+       }
+      else if (nt_header->Signature == IMAGE_NT_SIGNATURE)
+       {
+         /* Look for cygwin.dll in DLL import list. */
+         IMAGE_DATA_DIRECTORY import_dir =
+           nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
+         IMAGE_IMPORT_DESCRIPTOR * imports;
+         IMAGE_SECTION_HEADER * section;
+
+         section = rva_to_section (import_dir.VirtualAddress, nt_header);
+         imports = (IMAGE_IMPORT_DESCRIPTOR *) RVA_TO_PTR (import_dir.VirtualAddress,
+                                                           section, executable);
+             
+         for ( ; imports->Name; imports++)
+           {
+             char *dllname = (char*) RVA_TO_PTR (imports->Name, section, executable);
+
+             /* The exact name of the cygwin dll has changed with
+                various releases, but hopefully this will be reasonably
+                future proof.  */
+             if (strncmp (dllname, "cygwin", 6) == 0)
+               {
+                 *is_cygnus_app = TRUE;
+                 break;
+               }
+           }
+       }
+#endif
+    }
+
+ unwind:
+  close_file_data (&executable);
+}
+
+static 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);
+}
+
+int
+mswindows_utime (Lisp_Object path, struct utimbuf *times)
+{
+  struct utimbuf deftime;
+#if 0
+  HANDLE fh;
+#endif
+  static FILETIME mtime;
+  static FILETIME atime;
+  Extbyte *filename;
+
+  if (times == NULL)
+    {
+      deftime.modtime = deftime.actime = time (NULL);
+      times = &deftime;
+    }
+
+  LISP_STRING_TO_EXTERNAL (path, filename, Qmswindows_tstr);
+  /* APA: SetFileTime fails to set mtime correctly (always 1-Jan-1970) */
+#if 0
+  /* Need write access to set times.  */
+  fh = CreateFile (filename, 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;
+#else
+  return utime (filename, times);
+#endif
+}
+
+/* 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);
+}
+
+void
+vars_of_nt (void)
+{
+  DEFVAR_INT ("nt-fake-unix-uid", &nt_fake_unix_uid /*
+*Set uid returned by `user-uid' and `user-real-uid'.
+Under NT and 9x, there is no uids, and even no almighty user called root.
+By setting this variable, you can have any uid of choice. Default is 0.
+Changes to this variable take effect immediately.
+*/ );
+  nt_fake_unix_uid = 0;
+}
+
 /* end of nt.c */