import -ko -b 1.1.3 XEmacs XEmacs-21_2 r21-2-35
[chise/xemacs-chise.git.1] / src / nt.c
1 /* Utility and Unix shadow routines for XEmacs on Windows NT.
2    Copyright (C) 1994, 1995 Free Software Foundation, Inc.
3
4 This file is part of XEmacs.
5
6 XEmacs is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
9 later version.
10
11 XEmacs is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with XEmacs; see the file COPYING.  If not, write to the Free
18 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA.
20
21
22    Geoff Voelker (voelker@cs.washington.edu) 7-29-94 */
23
24 /* Adapted for XEmacs by David Hobley <david@spook-le0.cia.com.au> */
25 /* Sync'ed with Emacs 19.34.6 by Marc Paquette <marcpa@cam.org> */
26
27 #include <config.h>
28 #define getwd _getwd
29 #include "lisp.h"
30 #undef getwd
31
32 #include "systime.h"
33 #include "syssignal.h"
34 #include "sysproc.h"
35 #include "sysfile.h"
36 #include "syspwd.h"
37 #include "sysdir.h"
38
39 #include "syswindows.h"
40
41 #include "nt.h"
42 #include "ntheap.h"
43
44
45 extern Lisp_Object Vmswindows_downcase_file_names;
46 #if 0
47 extern Lisp_Object Vwin32_generate_fake_inodes;
48 #endif
49 extern Lisp_Object Vmswindows_get_true_file_attributes;
50
51 int nt_fake_unix_uid;
52
53 static char startup_dir[ MAXPATHLEN ];
54
55 /* Get the current working directory.  */
56 char *
57 getwd (char *dir)
58 {
59 #if 0
60   if (GetCurrentDirectory (MAXPATHLEN, dir) > 0)
61     return dir;
62   return NULL;
63 #else
64   /* Emacs doesn't actually change directory itself, and we want to
65      force our real wd to be where emacs.exe is to avoid unnecessary
66      conflicts when trying to rename or delete directories.  */
67   strcpy (dir, startup_dir);
68   return dir;
69 #endif
70 }
71
72 /* Emulate getloadavg.  */
73 int
74 getloadavg (double loadavg[], int nelem)
75 {
76   int i;
77
78   /* A faithful emulation is going to have to be saved for a rainy day.  */
79   for (i = 0; i < nelem; i++) 
80     {
81       loadavg[i] = 0.0;
82     }
83   return i;
84 }
85
86 /* Emulate getpwuid, getpwnam and others.  */
87
88 #define PASSWD_FIELD_SIZE 256
89
90 static char the_passwd_name[PASSWD_FIELD_SIZE];
91 static char the_passwd_passwd[PASSWD_FIELD_SIZE];
92 static char the_passwd_gecos[PASSWD_FIELD_SIZE];
93 static char the_passwd_dir[PASSWD_FIELD_SIZE];
94 static char the_passwd_shell[PASSWD_FIELD_SIZE];
95
96 static struct passwd the_passwd = 
97 {
98   the_passwd_name,
99   the_passwd_passwd,
100   0,
101   0,
102   0,
103   the_passwd_gecos,
104   the_passwd_dir,
105   the_passwd_shell,
106 };
107
108 uid_t
109 getuid (void) 
110 {
111   return nt_fake_unix_uid;
112 }
113
114 uid_t 
115 geteuid (void) 
116
117   return nt_fake_unix_uid;
118 }
119
120 gid_t
121 getgid (void) 
122
123   return the_passwd.pw_gid;
124 }
125
126 gid_t
127 getegid (void) 
128
129   return getgid ();
130 }
131
132 struct passwd *
133 getpwuid (uid_t uid)
134 {
135   if (uid == nt_fake_unix_uid)
136     {
137       the_passwd.pw_gid = the_passwd.pw_uid = uid;
138       return &the_passwd;
139     }
140   else
141     return NULL;
142 }
143
144 struct passwd *
145 getpwnam (const char *name)
146 {
147   struct passwd *pw;
148   
149   pw = getpwuid (getuid ());
150   if (!pw)
151     return pw;
152
153   if (stricmp (name, pw->pw_name))
154     return NULL;
155
156   return pw;
157 }
158
159 void
160 init_user_info (void)
161 {
162   /* This code is pretty much of ad hoc nature. There is no unix-like
163      UIDs under Windows NT. There is no concept of root user, because
164      all security is ACL-based. Instead, let's use a simple variable,
165      nt-fake-unix-uid, which would allow the user to have a uid of
166      choice. --kkm, 02/03/2000 */
167 #if 0
168   /* Find the user's real name by opening the process token and
169      looking up the name associated with the user-sid in that token.
170
171      Use the relative portion of the identifier authority value from
172      the user-sid as the user id value (same for group id using the
173      primary group sid from the process token). */
174
175   char            user_sid[256], name[256], domain[256];
176   DWORD           length = sizeof (name), dlength = sizeof (domain), trash;
177   HANDLE          token = NULL;
178   SID_NAME_USE    user_type;
179
180   if (OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY, &token)
181       && GetTokenInformation (token, TokenUser,
182                               (PVOID) user_sid, sizeof (user_sid), &trash)
183       && LookupAccountSid (NULL, *((PSID *) user_sid), name, &length,
184                            domain, &dlength, &user_type))
185     {
186       strcpy (the_passwd.pw_name, name);
187       /* Determine a reasonable uid value. */
188       if (stricmp ("administrator", name) == 0)
189         {
190           the_passwd.pw_uid = 0;
191           the_passwd.pw_gid = 0;
192         }
193       else
194         {
195           SID_IDENTIFIER_AUTHORITY * pSIA;
196
197           pSIA = GetSidIdentifierAuthority (*((PSID *) user_sid));
198           /* I believe the relative portion is the last 4 bytes (of 6)
199              with msb first. */
200           the_passwd.pw_uid = ((pSIA->Value[2] << 24) +
201                                (pSIA->Value[3] << 16) +
202                                (pSIA->Value[4] << 8)  +
203                                (pSIA->Value[5] << 0));
204           /* restrict to conventional uid range for normal users */
205           the_passwd.pw_uid = the_passwd.pw_uid % 60001;
206
207           /* Get group id */
208           if (GetTokenInformation (token, TokenPrimaryGroup,
209                                    (PVOID) user_sid, sizeof (user_sid), &trash))
210             {
211               SID_IDENTIFIER_AUTHORITY * pSIA;
212
213               pSIA = GetSidIdentifierAuthority (*((PSID *) user_sid));
214               the_passwd.pw_gid = ((pSIA->Value[2] << 24) +
215                                    (pSIA->Value[3] << 16) +
216                                    (pSIA->Value[4] << 8)  +
217                                    (pSIA->Value[5] << 0));
218               /* I don't know if this is necessary, but for safety... */
219               the_passwd.pw_gid = the_passwd.pw_gid % 60001;
220             }
221           else
222             the_passwd.pw_gid = the_passwd.pw_uid;
223         }
224     }
225   /* If security calls are not supported (presumably because we
226        are running under Windows 95), fallback to this. */
227   else if (GetUserName (name, &length))
228     {
229       strcpy (the_passwd.pw_name, name);
230       if (stricmp ("administrator", name) == 0)
231         the_passwd.pw_uid = 0;
232       else
233         the_passwd.pw_uid = 123;
234       the_passwd.pw_gid = the_passwd.pw_uid;
235     }
236   else
237     {
238       strcpy (the_passwd.pw_name, "unknown");
239       the_passwd.pw_uid = 123;
240       the_passwd.pw_gid = 123;
241     }
242
243   if (token)
244     CloseHandle (token);
245 #else
246   /* Obtain only logon id here, uid part is moved to getuid */
247   char name[256];
248   DWORD length = sizeof (name);
249   if (GetUserName (name, &length))
250     strcpy (the_passwd.pw_name, name);
251   else
252     strcpy (the_passwd.pw_name, "unknown");
253 #endif
254
255   /* Ensure HOME and SHELL are defined. */
256 #if 0
257   /*
258    * With XEmacs, setting $HOME is deprecated.
259    */
260   if (getenv ("HOME") == NULL)
261     putenv ("HOME=c:/");
262 #endif
263   if (getenv ("SHELL") == NULL)
264     putenv ((GetVersion () & 0x80000000) ? "SHELL=command" : "SHELL=cmd");
265
266   /* Set dir and shell from environment variables. */
267   strcpy (the_passwd.pw_dir, (char *)get_home_directory());
268   strcpy (the_passwd.pw_shell, getenv ("SHELL"));
269 }
270
271 /* Normalize filename by converting all path separators to
272    the specified separator.  Also conditionally convert upper
273    case path name components to lower case.  */
274
275 static void
276 normalize_filename (char *fp, char path_sep)
277 {
278   char sep;
279   char *elem;
280
281   /* Always lower-case drive letters a-z, even if the filesystem
282      preserves case in filenames.
283      This is so filenames can be compared by string comparison
284      functions that are case-sensitive.  Even case-preserving filesystems
285      do not distinguish case in drive letters.  */
286   if (fp[1] == ':' && *fp >= 'A' && *fp <= 'Z')
287     {
288       *fp += 'a' - 'A';
289       fp += 2;
290     }
291
292   if (NILP (Vmswindows_downcase_file_names))
293     {
294       while (*fp)
295         {
296           if (*fp == '/' || *fp == '\\')
297             *fp = path_sep;
298           fp++;
299         }
300       return;
301     }
302
303   sep = path_sep;               /* convert to this path separator */
304   elem = fp;                    /* start of current path element */
305
306   do {
307     if (*fp >= 'a' && *fp <= 'z')
308       elem = 0;                 /* don't convert this element */
309
310     if (*fp == 0 || *fp == ':')
311       {
312         sep = *fp;              /* restore current separator (or 0) */
313         *fp = '/';              /* after conversion of this element */
314       }
315
316     if (*fp == '/' || *fp == '\\')
317       {
318         if (elem && elem != fp)
319           {
320             *fp = 0;            /* temporary end of string */
321             _strlwr (elem);     /* while we convert to lower case */
322           }
323         *fp = sep;              /* convert (or restore) path separator */
324         elem = fp + 1;          /* next element starts after separator */
325         sep = path_sep;
326       }
327   } while (*fp++);
328 }
329
330 /* Destructively turn backslashes into slashes.  */
331 void
332 dostounix_filename (char *p)
333 {
334   normalize_filename (p, '/');
335 }
336
337 /* Destructively turn slashes into backslashes.  */
338 void
339 unixtodos_filename (char *p)
340 {
341   normalize_filename (p, '\\');
342 }
343
344 /* Remove all CR's that are followed by a LF.
345    (From msdos.c...probably should figure out a way to share it,
346    although this code isn't going to ever change.)  */
347 int
348 crlf_to_lf (int n, unsigned char *buf, unsigned *lf_count)
349 {
350   unsigned char *np = buf;
351   unsigned char *startp = buf;
352   unsigned char *endp = buf + n;
353
354   if (n == 0)
355     return n;
356   while (buf < endp - 1)
357     {
358       if (*buf == 0x0a)
359         (*lf_count)++;
360       if (*buf == 0x0d)
361         {
362           if (*(++buf) != 0x0a)
363             *np++ = 0x0d;
364         }
365       else
366         *np++ = *buf++;
367     }
368   if (buf < endp)
369     {
370       if (*buf == 0x0a)
371         (*lf_count)++;
372     *np++ = *buf++;
373     }
374   return np - startp;
375 }
376
377 /* Parse the root part of file name, if present.  Return length and
378     optionally store pointer to char after root.  */
379 static int
380 parse_root (char * name, char ** pPath)
381 {
382   char * start = name;
383
384   if (name == NULL)
385     return 0;
386
387   /* find the root name of the volume if given */
388   if (isalpha (name[0]) && name[1] == ':')
389     {
390       /* skip past drive specifier */
391       name += 2;
392       if (IS_DIRECTORY_SEP (name[0]))
393         name++;
394     }
395   else if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1]))
396     {
397       int slashes = 2;
398       name += 2;
399       do
400         {
401           if (IS_DIRECTORY_SEP (*name) && --slashes == 0)
402             break;
403           name++;
404         }
405       while ( *name );
406       if (IS_DIRECTORY_SEP (name[0]))
407         name++;
408     }
409
410   if (pPath)
411     *pPath = name;
412
413   return name - start;
414 }
415
416 /* Get long base name for name; name is assumed to be absolute.  */
417 static int
418 get_long_basename (char * name, char * buf, int size)
419 {
420   WIN32_FIND_DATA find_data;
421   HANDLE dir_handle;
422   int len = 0;
423 #ifdef PIGSFLY
424   char *p;
425
426   /* If the last component of NAME has a wildcard character, 
427      return it as the basename.  */
428   p = name + strlen (name);
429   while (*p != '\\' && *p != ':' && p > name) p--;
430   if (p > name) p++;
431   if (strchr (p, '*') || strchr (p, '?'))
432     {
433       if ((len = strlen (p)) < size)
434         memcpy (buf, p, len + 1);
435       else
436         len = 0;
437       return len;
438     }
439 #endif
440
441   dir_handle = FindFirstFile (name, &find_data);
442   if (dir_handle != INVALID_HANDLE_VALUE)
443     {
444       if ((len = strlen (find_data.cFileName)) < size)
445         memcpy (buf, find_data.cFileName, len + 1);
446       else
447         len = 0;
448       FindClose (dir_handle);
449     }
450   return len;
451 }
452
453 /* Get long name for file, if possible (assumed to be absolute).  */
454 BOOL
455 win32_get_long_filename (char * name, char * buf, int size)
456 {
457   char * o = buf;
458   char * p;
459   char * q;
460   char full[ MAX_PATH ];
461   int len;
462
463   len = strlen (name);
464   if (len >= MAX_PATH)
465     return FALSE;
466
467   /* Use local copy for destructive modification.  */
468   memcpy (full, name, len+1);
469   unixtodos_filename (full);
470
471   /* Copy root part verbatim.  */
472   len = parse_root (full, &p);
473   memcpy (o, full, len);
474   o += len;
475   size -= len;
476
477   do
478     {
479       q = p;
480       p = strchr (q, '\\');
481       if (p) *p = '\0';
482       len = get_long_basename (full, o, size);
483       if (len > 0)
484         {
485           o += len;
486           size -= len;
487           if (p != NULL)
488             {
489               *p++ = '\\';
490               if (size < 2)
491                 return FALSE;
492               *o++ = '\\';
493               size--;
494               *o = '\0';
495             }
496         }
497       else
498         return FALSE;
499     }
500   while (p != NULL && *p);
501
502   return TRUE;
503 }
504
505
506 /* Routines that are no-ops on NT but are defined to get Emacs to compile.  */
507
508 #if 0 /* #### We do not need those, do we? -kkm */
509 int 
510 unrequest_sigio (void) 
511
512   return 0;
513 }
514
515 int 
516 request_sigio (void) 
517
518   return 0;
519 }
520 #endif /* 0 */
521
522 #define REG_ROOT "SOFTWARE\\GNU\\XEmacs"
523
524 LPBYTE 
525 nt_get_resource (char *key, LPDWORD lpdwtype)
526 {
527   LPBYTE lpvalue;
528   HKEY hrootkey = NULL;
529   DWORD cbData;
530   
531   /* Check both the current user and the local machine to see if 
532      we have any resources.  */
533   
534   if (RegOpenKeyEx (HKEY_CURRENT_USER, REG_ROOT, 0, KEY_READ, &hrootkey) == ERROR_SUCCESS)
535     {
536       lpvalue = NULL;
537
538       if (RegQueryValueEx (hrootkey, key, NULL, NULL, NULL, &cbData) == ERROR_SUCCESS 
539           && (lpvalue = (LPBYTE) xmalloc (cbData)) != NULL 
540           && RegQueryValueEx (hrootkey, key, NULL, lpdwtype, lpvalue, &cbData) == ERROR_SUCCESS)
541         {
542           return (lpvalue);
543         }
544
545       if (lpvalue) xfree (lpvalue);
546         
547       RegCloseKey (hrootkey);
548     } 
549   
550   if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, REG_ROOT, 0, KEY_READ, &hrootkey) == ERROR_SUCCESS)
551     {
552       lpvalue = NULL;
553         
554       if (RegQueryValueEx (hrootkey, key, NULL, NULL, NULL, &cbData) == ERROR_SUCCESS &&
555           (lpvalue = (LPBYTE) xmalloc (cbData)) != NULL &&
556           RegQueryValueEx (hrootkey, key, NULL, lpdwtype, lpvalue, &cbData) == ERROR_SUCCESS)
557         {
558           return (lpvalue);
559         }
560         
561       if (lpvalue) xfree (lpvalue);
562         
563       RegCloseKey (hrootkey);
564     } 
565   
566   return (NULL);
567 }
568
569 void
570 init_environment (void)
571 {
572   /* Check for environment variables and use registry if they don't exist */
573   {
574     int i;
575     LPBYTE lpval;
576     DWORD dwType;
577
578     static char * env_vars[] = 
579     {
580       "HOME",
581       "emacs_dir",
582       "EMACSLOADPATH",
583       "EMACSDEBUGPATHS",
584       "SHELL",
585       "CMDPROXY",
586       "EMACSDATA",
587       "EMACSPATH",
588       "EMACSPACKAGEPATH",
589       "EMACSLOCKDIR",
590       "INFOPATH"
591     };
592 #if defined (HEAP_IN_DATA) && !defined(PDUMP)
593     cache_system_info ();
594 #endif
595     for (i = 0; i < countof (env_vars); i++) 
596       {
597         if (!getenv (env_vars[i]) &&
598             (lpval = nt_get_resource (env_vars[i], &dwType)) != NULL)
599           {
600             if (dwType == REG_EXPAND_SZ)
601               {
602                 char buf1[500], buf2[500];
603
604                 ExpandEnvironmentStrings ((LPSTR) lpval, buf1, 500);
605                 _snprintf (buf2, 499, "%s=%s", env_vars[i], buf1);
606                 putenv (strdup (buf2));
607               }
608             else if (dwType == REG_SZ)
609               {
610                 char buf[500];
611                   
612                 _snprintf (buf, 499, "%s=%s", env_vars[i], lpval);
613                 putenv (strdup (buf));
614               }
615
616             xfree (lpval);
617           }
618       }
619   }
620
621   /* Another special case: on NT, the PATH variable is actually named
622      "Path" although cmd.exe (perhaps NT itself) arranges for
623      environment variable lookup and setting to be case insensitive.
624      However, Emacs assumes a fully case sensitive environment, so we
625      need to change "Path" to "PATH" to match the expectations of
626      various elisp packages.  We do this by the sneaky method of
627      modifying the string in the C runtime environ entry.
628
629      The same applies to COMSPEC.  */
630   {
631     char ** envp;
632
633     for (envp = environ; *envp; envp++)
634       if (_strnicmp (*envp, "PATH=", 5) == 0)
635         memcpy (*envp, "PATH=", 5);
636       else if (_strnicmp (*envp, "COMSPEC=", 8) == 0)
637         memcpy (*envp, "COMSPEC=", 8);
638   }
639
640   /* Remember the initial working directory for getwd, then make the
641      real wd be the location of emacs.exe to avoid conflicts when
642      renaming or deleting directories.  (We also don't call chdir when
643      running subprocesses for the same reason.)  */
644   if (!GetCurrentDirectory (MAXPATHLEN, startup_dir))
645     abort ();
646
647   {
648     char *p;
649     char modname[MAX_PATH];
650
651     if (!GetModuleFileName (NULL, modname, MAX_PATH))
652       abort ();
653     if ((p = strrchr (modname, '\\')) == NULL)
654       abort ();
655     *p = 0;
656
657     SetCurrentDirectory (modname);
658   }
659
660   init_user_info ();
661 }
662
663 #ifndef HAVE_X_WINDOWS
664 /* X11R6 on NT provides the single parameter version of this command. */
665
666 #include <sys/timeb.h>
667
668 /* Emulate gettimeofday (Ulrich Leodolter, 1/11/95).  */
669 void 
670 gettimeofday (struct timeval *tv, struct timezone *tz)
671 {
672   struct _timeb tb;
673   _ftime (&tb);
674
675   tv->tv_sec = tb.time;
676   tv->tv_usec = tb.millitm * 1000L;
677   if (tz) 
678     {
679       tz->tz_minuteswest = tb.timezone; /* minutes west of Greenwich  */
680       tz->tz_dsttime = tb.dstflag;      /* type of dst correction  */
681     }
682 }
683
684 #endif /* HAVE_X_WINDOWS */
685
686 /* ------------------------------------------------------------------------- */
687 /* IO support and wrapper functions for Win32 API. */
688 /* ------------------------------------------------------------------------- */
689
690 /* Place a wrapper around the MSVC version of ctime.  It returns NULL
691    on network directories, so we handle that case here.  
692    (Ulrich Leodolter, 1/11/95).  */
693 char *
694 sys_ctime (const time_t *t)
695 {
696   char *str = (char *) ctime (t);
697   return (str ? str : "Sun Jan 01 00:00:00 1970");
698 }
699
700 /* Emulate sleep...we could have done this with a define, but that
701    would necessitate including windows.h in the files that used it.
702    This is much easier.  */
703
704 #ifndef HAVE_X_WINDOWS
705 void
706 sys_sleep (int seconds)
707 {
708   Sleep (seconds * 1000);
709 }
710 #endif
711
712 /* #### This is an evil dirty hack. We must get rid of it.
713    Word "munging" is not in XEmacs lexicon. - kkm */
714
715 /* Internal MSVC data and functions for low-level descriptor munging */
716 #if (_MSC_VER == 900)
717 extern char _osfile[];
718 #endif
719 extern int __cdecl _set_osfhnd (int fd, long h);
720 extern int __cdecl _free_osfhnd (int fd);
721
722 /* parallel array of private info on file handles */
723 filedesc fd_info [ MAXDESC ];
724
725 typedef struct volume_info_data {
726   struct volume_info_data * next;
727
728   /* time when info was obtained */
729   DWORD     timestamp;
730
731   /* actual volume info */
732   char *    root_dir;
733   DWORD     serialnum;
734   DWORD     maxcomp;
735   DWORD     flags;
736   char *    name;
737   char *    type;
738 } volume_info_data;
739
740 /* Global referenced by various functions.  */
741 static volume_info_data volume_info;
742
743 /* Vector to indicate which drives are local and fixed (for which cached
744    data never expires).  */
745 static BOOL fixed_drives[26];
746
747 /* Consider cached volume information to be stale if older than 10s,
748    at least for non-local drives.  Info for fixed drives is never stale.  */
749 #define DRIVE_INDEX( c ) ( (c) <= 'Z' ? (c) - 'A' : (c) - 'a' )
750 #define VOLINFO_STILL_VALID( root_dir, info )           \
751   ( ( isalpha (root_dir[0]) &&                          \
752       fixed_drives[ DRIVE_INDEX (root_dir[0]) ] )       \
753     || GetTickCount () - info->timestamp < 10000 )
754
755 /* Cache support functions.  */
756
757 /* Simple linked list with linear search is sufficient.  */
758 static volume_info_data *volume_cache = NULL;
759
760 static volume_info_data *
761 lookup_volume_info (char * root_dir)
762 {
763   volume_info_data * info;
764
765   for (info = volume_cache; info; info = info->next)
766     if (stricmp (info->root_dir, root_dir) == 0)
767       break;
768   return info;
769 }
770
771 static void
772 add_volume_info (char * root_dir, volume_info_data * info)
773 {
774   info->root_dir = xstrdup (root_dir);
775   info->next = volume_cache;
776   volume_cache = info;
777 }
778
779
780 /* Wrapper for GetVolumeInformation, which uses caching to avoid
781    performance penalty (~2ms on 486 for local drives, 7.5ms for local
782    cdrom drive, ~5-10ms or more for remote drives on LAN).  */
783 volume_info_data *
784 GetCachedVolumeInformation (char * root_dir)
785 {
786   volume_info_data * info;
787   char default_root[ MAX_PATH ];
788
789   /* NULL for root_dir means use root from current directory.  */
790   if (root_dir == NULL)
791     {
792       if (GetCurrentDirectory (MAX_PATH, default_root) == 0)
793         return NULL;
794       parse_root (default_root, &root_dir);
795       *root_dir = 0;
796       root_dir = default_root;
797     }
798
799   /* Local fixed drives can be cached permanently.  Removable drives
800      cannot be cached permanently, since the volume name and serial
801      number (if nothing else) can change.  Remote drives should be
802      treated as if they are removable, since there is no sure way to
803      tell whether they are or not.  Also, the UNC association of drive
804      letters mapped to remote volumes can be changed at any time (even
805      by other processes) without notice.
806    
807      As a compromise, so we can benefit from caching info for remote
808      volumes, we use a simple expiry mechanism to invalidate cache
809      entries that are more than ten seconds old.  */
810
811 #if 0
812   /* No point doing this, because WNetGetConnection is even slower than
813      GetVolumeInformation, consistently taking ~50ms on a 486 (FWIW,
814      GetDriveType is about the only call of this type which does not
815      involve network access, and so is extremely quick).  */
816
817   /* Map drive letter to UNC if remote. */
818   if ( isalpha( root_dir[0] ) && !fixed[ DRIVE_INDEX( root_dir[0] ) ] )
819     {
820       char remote_name[ 256 ];
821       char drive[3] = { root_dir[0], ':' };
822
823       if (WNetGetConnection (drive, remote_name, sizeof (remote_name))
824           == NO_ERROR)
825         /* do something */ ;
826     }
827 #endif
828
829   info = lookup_volume_info (root_dir);
830
831   if (info == NULL || ! VOLINFO_STILL_VALID (root_dir, info))
832   {
833     char  name[ 256 ];
834   DWORD     serialnum;
835   DWORD     maxcomp;
836   DWORD     flags;
837     char  type[ 256 ];
838
839     /* Info is not cached, or is stale. */
840     if (!GetVolumeInformation (root_dir,
841                                name, sizeof (name),
842                                &serialnum,
843                                &maxcomp,
844                                &flags,
845                                type, sizeof (type)))
846       return NULL;
847
848     /* Cache the volume information for future use, overwriting existing
849        entry if present.  */
850     if (info == NULL)
851       {
852         info = (volume_info_data *) xmalloc (sizeof (volume_info_data));
853         add_volume_info (root_dir, info);
854       }
855     else
856       {
857         free (info->name);
858         free (info->type);
859       }
860
861     info->name = xstrdup (name);
862     info->serialnum = serialnum;
863     info->maxcomp = maxcomp;
864     info->flags = flags;
865     info->type = xstrdup (type);
866     info->timestamp = GetTickCount ();
867   }
868
869   return info;
870 }
871
872 /* Get information on the volume where name is held; set path pointer to
873    start of pathname in name (past UNC header\volume header if present).  */
874 int
875 get_volume_info (const char * name, const char ** pPath)
876 {
877   char temp[MAX_PATH];
878   char *rootname = NULL;  /* default to current volume */
879   volume_info_data * info;
880
881   if (name == NULL)
882     return FALSE;
883
884   /* find the root name of the volume if given */
885   if (isalpha (name[0]) && name[1] == ':')
886     {
887       rootname = temp;
888       temp[0] = *name++;
889       temp[1] = *name++;
890       temp[2] = '\\';
891       temp[3] = 0;
892     }
893   else if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1]))
894     {
895       char *str = temp;
896       int slashes = 4;
897       rootname = temp;
898       do
899         {
900           if (IS_DIRECTORY_SEP (*name) && --slashes == 0)
901             break;
902           *str++ = *name++;
903         }
904       while ( *name );
905
906       *str++ = '\\';
907       *str = 0;
908     }
909
910   if (pPath)
911     *pPath = name;
912     
913   info = GetCachedVolumeInformation (rootname);
914   if (info != NULL)
915     {
916       /* Set global referenced by other functions.  */
917       volume_info = *info;
918       return TRUE;
919     }
920   return FALSE;
921 }
922
923 /* Determine if volume is FAT format (ie. only supports short 8.3
924    names); also set path pointer to start of pathname in name.  */
925 int
926 is_fat_volume (const char * name, const char ** pPath)
927 {
928   if (get_volume_info (name, pPath))
929     return (volume_info.maxcomp == 12);
930   return FALSE;
931 }
932
933 /* Map filename to a legal 8.3 name if necessary. */
934 const char *
935 map_win32_filename (const char * name, const char ** pPath)
936 {
937   static char shortname[MAX_PATH];
938   char * str = shortname;
939   char c;
940   const char * path;
941   const char * save_name = name;
942
943   if (is_fat_volume (name, &path)) /* truncate to 8.3 */
944     {
945       REGISTER int left = 8;    /* maximum number of chars in part */
946       REGISTER int extn = 0;    /* extension added? */
947       REGISTER int dots = 2;    /* maximum number of dots allowed */
948
949       while (name < path)
950         *str++ = *name++;       /* skip past UNC header */
951
952       while ((c = *name++))
953         {
954           switch ( c )
955             {
956             case '\\':
957             case '/':
958               *str++ = '\\';
959               extn = 0;         /* reset extension flags */
960               dots = 2;         /* max 2 dots */
961               left = 8;         /* max length 8 for main part */
962               break;
963             case ':':
964               *str++ = ':';
965               extn = 0;         /* reset extension flags */
966               dots = 2;         /* max 2 dots */
967               left = 8;         /* max length 8 for main part */
968               break;
969             case '.':
970               if ( dots )
971                 {
972                   /* Convert path components of the form .xxx to _xxx,
973                      but leave . and .. as they are.  This allows .emacs
974                      to be read as _emacs, for example.  */
975
976                   if (! *name ||
977                       *name == '.' ||
978                       IS_DIRECTORY_SEP (*name))
979                     {
980                       *str++ = '.';
981                       dots--;
982                     }
983                   else
984                     {
985                       *str++ = '_';
986                       left--;
987                       dots = 0;
988                     }
989                 }
990               else if ( !extn )
991                 {
992                   *str++ = '.';
993                   extn = 1;             /* we've got an extension */
994                   left = 3;             /* 3 chars in extension */
995                 }
996               else
997                 {
998                   /* any embedded dots after the first are converted to _ */
999                   *str++ = '_';
1000                 }
1001               break;
1002             case '~':
1003             case '#':                   /* don't lose these, they're important */
1004               if ( ! left )
1005                 str[-1] = c;            /* replace last character of part */
1006               /* FALLTHRU */
1007             default:
1008               if ( left )
1009                 {
1010                   *str++ = tolower (c); /* map to lower case (looks nicer) */
1011                   left--;
1012                   dots = 0;             /* started a path component */
1013                 }
1014               break;
1015             }
1016         }
1017       *str = '\0';
1018     }
1019   else
1020     {
1021       strcpy (shortname, name);
1022       unixtodos_filename (shortname);
1023     }
1024
1025   if (pPath)
1026     *pPath = shortname + (path - save_name);
1027
1028   return shortname;
1029 }
1030
1031
1032 /* Emulate the Unix directory procedures opendir, closedir, 
1033    and readdir.  We can't use the procedures supplied in sysdep.c,
1034    so we provide them here.  */
1035
1036 struct direct dir_static;       /* simulated directory contents */
1037 static HANDLE dir_find_handle = INVALID_HANDLE_VALUE;
1038 static int    dir_is_fat;
1039 static char   dir_pathname[MAXPATHLEN+1];
1040 static WIN32_FIND_DATA dir_find_data;
1041
1042 DIR *
1043 opendir (const char *filename)
1044 {
1045   DIR *dirp;
1046
1047   /* Opening is done by FindFirstFile.  However, a read is inherent to
1048      this operation, so we defer the open until read time.  */
1049
1050   if (!(dirp = xnew_and_zero(DIR)))
1051     return NULL;
1052   if (dir_find_handle != INVALID_HANDLE_VALUE)
1053     return NULL;
1054
1055   dirp->dd_fd = 0;
1056   dirp->dd_loc = 0;
1057   dirp->dd_size = 0;
1058
1059   strncpy (dir_pathname, map_win32_filename (filename, NULL), MAXPATHLEN);
1060   dir_pathname[MAXPATHLEN] = '\0';
1061   dir_is_fat = is_fat_volume (filename, NULL);
1062
1063   return dirp;
1064 }
1065
1066 int
1067 closedir (DIR *dirp)
1068 {
1069   BOOL retval;
1070
1071   /* If we have a find-handle open, close it.  */
1072   if (dir_find_handle != INVALID_HANDLE_VALUE)
1073     {
1074       retval = FindClose (dir_find_handle);
1075       dir_find_handle = INVALID_HANDLE_VALUE;
1076     }
1077   xfree (dirp);
1078   if (retval)
1079     return 0;
1080   else
1081     return -1;
1082 }
1083
1084 struct direct *
1085 readdir (DIR *dirp)
1086 {
1087   /* If we aren't dir_finding, do a find-first, otherwise do a find-next. */
1088   if (dir_find_handle == INVALID_HANDLE_VALUE)
1089     {
1090       char filename[MAXNAMLEN + 3];
1091       int ln;
1092
1093       strcpy (filename, dir_pathname);
1094       ln = strlen (filename) - 1;
1095       if (!IS_DIRECTORY_SEP (filename[ln]))
1096         strcat (filename, "\\");
1097       strcat (filename, "*");
1098
1099       dir_find_handle = FindFirstFile (filename, &dir_find_data);
1100
1101       if (dir_find_handle == INVALID_HANDLE_VALUE)
1102         return NULL;
1103     }
1104   else
1105     {
1106       if (!FindNextFile (dir_find_handle, &dir_find_data))
1107         return NULL;
1108     }
1109   
1110   /* Emacs never uses this value, so don't bother making it match
1111      value returned by stat().  */
1112   dir_static.d_ino = 1;
1113   
1114   dir_static.d_reclen = sizeof (struct direct) - MAXNAMLEN + 3 +
1115     dir_static.d_namlen - dir_static.d_namlen % 4;
1116   
1117   dir_static.d_namlen = strlen (dir_find_data.cFileName);
1118   strcpy (dir_static.d_name, dir_find_data.cFileName);
1119   if (dir_is_fat)
1120     _strlwr (dir_static.d_name);
1121   else if (!NILP (Vmswindows_downcase_file_names))
1122     {
1123       REGISTER char *p;
1124       for (p = dir_static.d_name; *p; p++)
1125         if (*p >= 'a' && *p <= 'z')
1126           break;
1127       if (!*p)
1128         _strlwr (dir_static.d_name);
1129     }
1130   
1131   return &dir_static;
1132 }
1133
1134 #if 0
1135 /* #### Have to check if all that sad story about '95 is true - kkm */
1136 int
1137 sys_rename (const char * oldname, const char * newname)
1138 {
1139   char temp[MAX_PATH];
1140   DWORD attr;
1141
1142   /* MoveFile on Win95 doesn't correctly change the short file name
1143      alias in a number of circumstances (it is not easy to predict when
1144      just by looking at oldname and newname, unfortunately).  In these
1145      cases, renaming through a temporary name avoids the problem.
1146
1147      A second problem on Win95 is that renaming through a temp name when
1148      newname is uppercase fails (the final long name ends up in
1149      lowercase, although the short alias might be uppercase) UNLESS the
1150      long temp name is not 8.3.
1151
1152      So, on Win95 we always rename through a temp name, and we make sure
1153      the temp name has a long extension to ensure correct renaming.  */
1154
1155   strcpy (temp, map_win32_filename (oldname, NULL));
1156
1157   if (GetVersion () & 0x80000000)
1158     {
1159       char * p;
1160
1161       if (p = strrchr (temp, '\\'))
1162         p++;
1163       else
1164         p = temp;
1165       /* Force temp name to require a manufactured 8.3 alias - this
1166          seems to make the second rename work properly. */
1167       strcpy (p, "_rename_temp.XXXXXX");
1168       sys_mktemp (temp);
1169       if (rename (map_win32_filename (oldname, NULL), temp) < 0)
1170         return -1;
1171     }
1172
1173   /* Emulate Unix behavior - newname is deleted if it already exists
1174      (at least if it is a file; don't do this for directories).
1175      However, don't do this if we are just changing the case of the file
1176      name - we will end up deleting the file we are trying to rename!  */
1177   newname = map_win32_filename (newname, NULL);
1178
1179   /* TODO: Use GetInformationByHandle (on NT) to ensure newname and temp
1180      do not refer to the same file, eg. through share aliases.  */
1181   if (stricmp (newname, temp) != 0
1182       && (attr = GetFileAttributes (newname)) != -1
1183       && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0)
1184     {
1185       _chmod (newname, 0666);
1186       _unlink (newname);
1187     }
1188
1189   return rename (temp, newname);
1190 }
1191 #endif /* 0 */
1192
1193 static FILETIME utc_base_ft;
1194 static int init = 0;
1195
1196 #if 0
1197
1198 static long double utc_base;
1199
1200 time_t
1201 convert_time (FILETIME ft)
1202 {
1203   long double ret;
1204
1205   if (!init)
1206     {
1207       /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */
1208       SYSTEMTIME st;
1209
1210       st.wYear = 1970;
1211       st.wMonth = 1;
1212       st.wDay = 1;
1213       st.wHour = 0;
1214       st.wMinute = 0;
1215       st.wSecond = 0;
1216       st.wMilliseconds = 0;
1217
1218       SystemTimeToFileTime (&st, &utc_base_ft);
1219       utc_base = (long double) utc_base_ft.dwHighDateTime
1220         * 4096 * 1024 * 1024 + utc_base_ft.dwLowDateTime;
1221       init = 1;
1222     }
1223
1224   if (CompareFileTime (&ft, &utc_base_ft) < 0)
1225     return 0;
1226
1227   ret = (long double) ft.dwHighDateTime * 4096 * 1024 * 1024 + ft.dwLowDateTime;
1228   ret -= utc_base;
1229   return (time_t) (ret * 1e-7);
1230 }
1231 #else
1232
1233 #if defined(MINGW) && CYGWIN_VERSION_DLL_MAJOR <= 21
1234 #define LowPart u.LowPart
1235 #define HighPart u.HighPart
1236 #endif
1237
1238 static LARGE_INTEGER utc_base_li;
1239
1240 time_t
1241 convert_time (FILETIME uft)
1242 {
1243   time_t ret;
1244 #ifndef MAXLONGLONG
1245   SYSTEMTIME st;
1246   struct tm t;
1247   FILETIME ft;
1248   TIME_ZONE_INFORMATION tzi;
1249   DWORD tzid;
1250 #else
1251   LARGE_INTEGER lft;
1252 #endif
1253
1254   if (!init)
1255     {
1256       /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */
1257       SYSTEMTIME st;
1258
1259       st.wYear = 1970;
1260       st.wMonth = 1;
1261       st.wDay = 1;
1262       st.wHour = 0;
1263       st.wMinute = 0;
1264       st.wSecond = 0;
1265       st.wMilliseconds = 0;
1266
1267       SystemTimeToFileTime (&st, &utc_base_ft);
1268
1269       utc_base_li.LowPart = utc_base_ft.dwLowDateTime;
1270       utc_base_li.HighPart = utc_base_ft.dwHighDateTime;
1271
1272       init = 1;
1273     }
1274
1275 #ifdef MAXLONGLONG
1276
1277   /* On a compiler that supports long integers, do it the easy way */
1278   lft.LowPart = uft.dwLowDateTime;
1279   lft.HighPart = uft.dwHighDateTime;
1280   ret = (time_t) ((lft.QuadPart - utc_base_li.QuadPart) / 10000000);
1281
1282 #else
1283
1284   /* Do it the hard way using mktime. */
1285   FileTimeToLocalFileTime(&uft, &ft);
1286   FileTimeToSystemTime (&ft, &st);
1287   tzid = GetTimeZoneInformation (&tzi);
1288   t.tm_year = st.wYear - 1900;
1289   t.tm_mon = st.wMonth - 1;
1290   t.tm_mday = st.wDay;
1291   t.tm_hour = st.wHour;
1292   t.tm_min = st.wMinute;
1293   t.tm_sec = st.wSecond;
1294   t.tm_isdst = (tzid == TIME_ZONE_ID_DAYLIGHT);
1295   /* st.wMilliseconds not applicable */
1296   ret = mktime(&t);
1297   if (ret == -1)
1298     {
1299       ret = 0;
1300     }
1301
1302 #endif
1303
1304   return ret;
1305 }
1306 #endif
1307 #if defined(MINGW) && CYGWIN_VERSION_DLL_MAJOR <= 21
1308 #undef LowPart
1309 #undef HighPart
1310 #endif
1311
1312 #if 0
1313 /* in case we ever have need of this */
1314 void
1315 convert_from_time_t (time_t time, FILETIME * pft)
1316 {
1317   long double tmp;
1318
1319   if (!init)
1320     {
1321       /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */
1322       SYSTEMTIME st;
1323
1324       st.wYear = 1970;
1325       st.wMonth = 1;
1326       st.wDay = 1;
1327       st.wHour = 0;
1328       st.wMinute = 0;
1329       st.wSecond = 0;
1330       st.wMilliseconds = 0;
1331
1332       SystemTimeToFileTime (&st, &utc_base_ft);
1333       utc_base = (long double) utc_base_ft.dwHighDateTime
1334         * 4096 * 1024 * 1024 + utc_base_ft.dwLowDateTime;
1335       init = 1;
1336     }
1337
1338   /* time in 100ns units since 1-Jan-1601 */
1339   tmp = (long double) time * 1e7 + utc_base;
1340   pft->dwHighDateTime = (DWORD) (tmp / (4096.0 * 1024 * 1024));
1341   pft->dwLowDateTime = (DWORD) (tmp - pft->dwHighDateTime);
1342 }
1343 #endif
1344
1345 #if 0
1346 /* No reason to keep this; faking inode values either by hashing or even
1347    using the file index from GetInformationByHandle, is not perfect and
1348    so by default Emacs doesn't use the inode values on Windows.
1349    Instead, we now determine file-truename correctly (except for
1350    possible drive aliasing etc).  */
1351
1352 /*  Modified version of "PJW" algorithm (see the "Dragon" compiler book). */
1353 static unsigned
1354 hashval (const unsigned char * str)
1355 {
1356   unsigned h = 0;
1357   while (*str)
1358     {
1359       h = (h << 4) + *str++;
1360       h ^= (h >> 28);
1361     }
1362   return h;
1363 }
1364
1365 /* Return the hash value of the canonical pathname, excluding the
1366    drive/UNC header, to get a hopefully unique inode number. */
1367 static DWORD
1368 generate_inode_val (const char * name)
1369 {
1370   char fullname[ MAX_PATH ];
1371   char * p;
1372   unsigned hash;
1373
1374   /* Get the truly canonical filename, if it exists.  (Note: this
1375      doesn't resolve aliasing due to subst commands, or recognize hard
1376      links.  */
1377   if (!win32_get_long_filename ((char *)name, fullname, MAX_PATH))
1378     abort ();
1379
1380   parse_root (fullname, &p);
1381   /* Normal Win32 filesystems are still case insensitive. */
1382   _strlwr (p);
1383   return hashval (p);
1384 }
1385
1386 #endif
1387
1388 /* #### aichner@ecf.teradyne.com reported that with the library
1389    provided stat/fstat, (file-exist "d:\\tmp\\") =>> nil,
1390    (file-exist "d:\\tmp") =>> t, when d:\tmp exists. Whenever
1391    we opt to use non-encapsulated stat(), this should serve as
1392    a compatibility test. --kkm */
1393
1394 /* Since stat is encapsulated on Windows NT, we need to encapsulate
1395    the equally broken fstat as well. */
1396 int
1397 mswindows_fstat (int handle, struct stat *buffer)
1398 {
1399   int ret;
1400   BY_HANDLE_FILE_INFORMATION lpFileInfo;
1401   /* Initialize values */
1402   buffer->st_mode = 0;
1403   buffer->st_size = 0;
1404   buffer->st_dev = 0;
1405   buffer->st_rdev = 0;
1406   buffer->st_atime = 0;
1407   buffer->st_ctime = 0;
1408   buffer->st_mtime = 0;
1409   buffer->st_nlink = 0;
1410   ret = GetFileInformationByHandle((HANDLE) _get_osfhandle(handle), &lpFileInfo);
1411   if (!ret)
1412     {
1413       return -1;
1414     }
1415   else
1416     {
1417       buffer->st_mtime = convert_time (lpFileInfo.ftLastWriteTime);
1418       buffer->st_atime = convert_time (lpFileInfo.ftLastAccessTime);
1419       if (buffer->st_atime == 0) buffer->st_atime = buffer->st_mtime;
1420       buffer->st_ctime = convert_time (lpFileInfo.ftCreationTime);
1421       if (buffer->st_ctime == 0) buffer->st_ctime = buffer->st_mtime;
1422       buffer->st_size = lpFileInfo.nFileSizeLow;
1423       buffer->st_nlink = (short) lpFileInfo.nNumberOfLinks;
1424       return 0;
1425     }
1426 }
1427
1428 /* MSVC stat function can't cope with UNC names and has other bugs, so
1429    replace it with our own.  This also allows us to calculate consistent
1430    inode values without hacks in the main Emacs code. */
1431 int
1432 mswindows_stat (const char * path, struct stat * buf)
1433 {
1434   char * name;
1435   WIN32_FIND_DATA wfd;
1436   HANDLE fh;
1437   DWORD fake_inode;
1438   int permission;
1439   int len;
1440   int rootdir = FALSE;
1441
1442   if (path == NULL || buf == NULL)
1443     {
1444       errno = EFAULT;
1445       return -1;
1446     }
1447
1448   name = (char *) map_win32_filename (path, &path);
1449   /* must be valid filename, no wild cards */
1450   if (strchr (name, '*') || strchr (name, '?'))
1451     {
1452       errno = ENOENT;
1453       return -1;
1454     }
1455
1456   /* Remove trailing directory separator, unless name is the root
1457      directory of a drive or UNC volume in which case ensure there
1458      is a trailing separator. */
1459   len = strlen (name);
1460   rootdir = (path >= name + len - 1
1461              && (IS_DIRECTORY_SEP (*path) || *path == 0));
1462   name = strcpy ((char *)alloca (len + 2), name);
1463
1464   if (rootdir)
1465     {
1466       if (!IS_DIRECTORY_SEP (name[len-1]))
1467         strcat (name, "\\");
1468       if (GetDriveType (name) < 2)
1469         {
1470           errno = ENOENT;
1471           return -1;
1472         }
1473       memset (&wfd, 0, sizeof (wfd));
1474       wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
1475       wfd.ftCreationTime = utc_base_ft;
1476       wfd.ftLastAccessTime = utc_base_ft;
1477       wfd.ftLastWriteTime = utc_base_ft;
1478       strcpy (wfd.cFileName, name);
1479     }
1480   else
1481     {
1482       if (IS_DIRECTORY_SEP (name[len-1]))
1483         name[len - 1] = 0;
1484
1485       /* (This is hacky, but helps when doing file completions on
1486          network drives.)  Optimize by using information available from
1487          active readdir if possible.  */
1488       if (dir_find_handle != INVALID_HANDLE_VALUE &&
1489           (len = strlen (dir_pathname)),
1490           strnicmp (name, dir_pathname, len) == 0 &&
1491           IS_DIRECTORY_SEP (name[len]) &&
1492           stricmp (name + len + 1, dir_static.d_name) == 0)
1493         {
1494           /* This was the last entry returned by readdir.  */
1495           wfd = dir_find_data;
1496         }
1497       else
1498         {
1499       fh = FindFirstFile (name, &wfd);
1500       if (fh == INVALID_HANDLE_VALUE)
1501         {
1502           errno = ENOENT;
1503           return -1;
1504         }
1505       FindClose (fh);
1506     }
1507     }
1508
1509   if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1510     {
1511       buf->st_mode = _S_IFDIR;
1512       buf->st_nlink = 2;        /* doesn't really matter */
1513       fake_inode = 0;           /* this doesn't either I think */
1514     }
1515   else if (!NILP (Vmswindows_get_true_file_attributes))
1516     {
1517       /* This is more accurate in terms of getting the correct number
1518          of links, but is quite slow (it is noticeable when Emacs is
1519          making a list of file name completions). */
1520       BY_HANDLE_FILE_INFORMATION info;
1521
1522       /* No access rights required to get info.  */
1523       fh = CreateFile (name, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
1524                        OPEN_EXISTING, 0, NULL);
1525
1526       if (GetFileInformationByHandle (fh, &info))
1527         {
1528           switch (GetFileType (fh))
1529             {
1530             case FILE_TYPE_DISK:
1531               buf->st_mode = _S_IFREG;
1532               break;
1533             case FILE_TYPE_PIPE:
1534               buf->st_mode = _S_IFIFO;
1535               break;
1536             case FILE_TYPE_CHAR:
1537             case FILE_TYPE_UNKNOWN:
1538             default:
1539               buf->st_mode = _S_IFCHR;
1540             }
1541           buf->st_nlink = (short) info.nNumberOfLinks;
1542           /* Might as well use file index to fake inode values, but this
1543              is not guaranteed to be unique unless we keep a handle open
1544              all the time (even then there are situations where it is
1545              not unique).  Reputedly, there are at most 48 bits of info
1546              (on NTFS, presumably less on FAT). */
1547           fake_inode = info.nFileIndexLow ^ info.nFileIndexHigh;
1548           CloseHandle (fh);
1549         }
1550       else
1551         {
1552           errno = EACCES;
1553           return -1;
1554         }
1555     }
1556   else
1557     {
1558       /* Don't bother to make this information more accurate.  */
1559       buf->st_mode = _S_IFREG;
1560       buf->st_nlink = 1;
1561       fake_inode = 0;
1562     }
1563
1564 #if 0
1565   /* Not sure if there is any point in this.  */
1566   if (!NILP (Vwin32_generate_fake_inodes))
1567     fake_inode = generate_inode_val (name);
1568   else if (fake_inode == 0)
1569     {
1570       /* For want of something better, try to make everything unique.  */
1571       static DWORD gen_num = 0;
1572       fake_inode = ++gen_num;
1573     }
1574 #endif
1575
1576   /* #### MSVC defines _ino_t to be short; other libc's might not.  */
1577   buf->st_ino = (unsigned short) (fake_inode ^ (fake_inode >> 16));
1578
1579   /* consider files to belong to current user */
1580   buf->st_uid = buf->st_gid = nt_fake_unix_uid;
1581
1582   /* volume_info is set indirectly by map_win32_filename */
1583   buf->st_dev = volume_info.serialnum;
1584   buf->st_rdev = volume_info.serialnum;
1585
1586   buf->st_size = wfd.nFileSizeLow;
1587
1588   /* Convert timestamps to Unix format. */
1589   buf->st_mtime = convert_time (wfd.ftLastWriteTime);
1590   buf->st_atime = convert_time (wfd.ftLastAccessTime);
1591   if (buf->st_atime == 0) buf->st_atime = buf->st_mtime;
1592   buf->st_ctime = convert_time (wfd.ftCreationTime);
1593   if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime;
1594
1595   /* determine rwx permissions */
1596   if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
1597     permission = _S_IREAD;
1598   else
1599     permission = _S_IREAD | _S_IWRITE;
1600   
1601   if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1602     permission |= _S_IEXEC;
1603   else
1604     {
1605       char * p = strrchr (name, '.');
1606       if (p != NULL &&
1607           (stricmp (p, ".exe") == 0 ||
1608            stricmp (p, ".com") == 0 ||
1609            stricmp (p, ".bat") == 0 ||
1610            stricmp (p, ".cmd") == 0))
1611         permission |= _S_IEXEC;
1612     }
1613
1614   buf->st_mode |= permission | (permission >> 3) | (permission >> 6);
1615
1616   return 0;
1617 }
1618
1619 /* From callproc.c  */
1620 extern Lisp_Object Vbinary_process_input;
1621 extern Lisp_Object Vbinary_process_output;
1622
1623 /* Unix pipe() has only one arg */
1624 int
1625 sys_pipe (int * phandles)
1626 {
1627   int rc;
1628   unsigned flags;
1629
1630   /* make pipe handles non-inheritable; when we spawn a child, we
1631      replace the relevant handle with an inheritable one.  Also put
1632      pipes into binary mode; we will do text mode translation ourselves
1633      if required.  */
1634   rc = _pipe (phandles, 0, _O_NOINHERIT | _O_BINARY);
1635
1636   if (rc == 0)
1637     {
1638       flags = FILE_PIPE | FILE_READ;
1639       if (!NILP (Vbinary_process_output))
1640           flags |= FILE_BINARY;
1641       fd_info[phandles[0]].flags = flags;
1642
1643       flags = FILE_PIPE | FILE_WRITE;
1644       if (!NILP (Vbinary_process_input))
1645           flags |= FILE_BINARY;
1646       fd_info[phandles[1]].flags = flags;
1647     }
1648
1649   return rc;
1650 }
1651
1652 void
1653 term_ntproc (int unused)
1654 {
1655 }
1656
1657 void
1658 init_ntproc (void)
1659 {
1660   /* Initial preparation for subprocess support: replace our standard
1661      handles with non-inheritable versions. */
1662   {
1663     HANDLE parent;
1664     HANDLE stdin_save =  INVALID_HANDLE_VALUE;
1665     HANDLE stdout_save = INVALID_HANDLE_VALUE;
1666     HANDLE stderr_save = INVALID_HANDLE_VALUE;
1667
1668     parent = GetCurrentProcess ();
1669
1670     /* ignore errors when duplicating and closing; typically the
1671        handles will be invalid when running as a gui program. */
1672     DuplicateHandle (parent, 
1673                      GetStdHandle (STD_INPUT_HANDLE), 
1674                      parent,
1675                      &stdin_save, 
1676                      0, 
1677                      FALSE, 
1678                      DUPLICATE_SAME_ACCESS);
1679     
1680     DuplicateHandle (parent,
1681                      GetStdHandle (STD_OUTPUT_HANDLE),
1682                      parent,
1683                      &stdout_save,
1684                      0,
1685                      FALSE,
1686                      DUPLICATE_SAME_ACCESS);
1687     
1688     DuplicateHandle (parent,
1689                      GetStdHandle (STD_ERROR_HANDLE),
1690                      parent,
1691                      &stderr_save,
1692                      0,
1693                      FALSE,
1694                      DUPLICATE_SAME_ACCESS);
1695     
1696     fclose (stdin);
1697     fclose (stdout);
1698     fclose (stderr);
1699
1700     if (stdin_save != INVALID_HANDLE_VALUE)
1701       _open_osfhandle ((long) stdin_save, O_TEXT);
1702     else
1703       _open ("nul", O_TEXT | O_NOINHERIT | O_RDONLY);
1704     _fdopen (0, "r");
1705
1706     if (stdout_save != INVALID_HANDLE_VALUE)
1707       _open_osfhandle ((long) stdout_save, O_TEXT);
1708     else
1709       _open ("nul", O_TEXT | O_NOINHERIT | O_WRONLY);
1710     _fdopen (1, "w");
1711
1712     if (stderr_save != INVALID_HANDLE_VALUE)
1713       _open_osfhandle ((long) stderr_save, O_TEXT);
1714     else
1715       _open ("nul", O_TEXT | O_NOINHERIT | O_WRONLY);
1716     _fdopen (2, "w");
1717   }
1718
1719   /* unfortunately, atexit depends on implementation of malloc */
1720   /* atexit (term_ntproc); */
1721   signal (SIGABRT, term_ntproc);
1722
1723   /* determine which drives are fixed, for GetCachedVolumeInformation */
1724   {
1725     /* GetDriveType must have trailing backslash. */
1726     char drive[] = "A:\\";
1727
1728     /* Loop over all possible drive letters */
1729     while ( *drive <= 'Z' )
1730     {
1731       /* Record if this drive letter refers to a fixed drive. */
1732       fixed_drives[ DRIVE_INDEX (*drive) ] =
1733         (GetDriveType (drive) == DRIVE_FIXED);
1734
1735       (*drive)++;
1736     }
1737   }
1738 }
1739 #ifndef HAVE_TTY
1740 Lisp_Object
1741 tty_semi_canonicalize_console_connection (Lisp_Object connection,
1742                                           Error_behavior errb)
1743 {
1744   return Vstdio_str;
1745 }
1746
1747 Lisp_Object
1748 tty_canonicalize_console_connection (Lisp_Object connection,
1749                                      Error_behavior errb)
1750 {
1751   return Vstdio_str;
1752 }
1753
1754 Lisp_Object
1755 tty_semi_canonicalize_device_connection (Lisp_Object connection,
1756                                          Error_behavior errb)
1757 {
1758   return Vstdio_str;
1759 }
1760
1761 Lisp_Object
1762 tty_canonicalize_device_connection (Lisp_Object connection,
1763                                     Error_behavior errb)
1764 {
1765   return Vstdio_str;
1766 }
1767 #endif
1768
1769 /*--------------------------------------------------------------------*/
1770 /* Signal support                                                     */
1771 /*--------------------------------------------------------------------*/
1772
1773 /* We need MS-defined signal and raise here */
1774 #undef signal
1775 #undef raise
1776
1777 #define sigmask(nsig) (1U << nsig)
1778
1779 /* We can support as many signals as fit into word */
1780 #define SIG_MAX 32
1781
1782 /* Signal handlers. Initial value = 0 = SIG_DFL */
1783 static void (__cdecl *signal_handlers[SIG_MAX])(int) = {0};
1784
1785 /* Signal block mask: bit set to 1 means blocked */
1786 unsigned signal_block_mask = 0;
1787
1788 /* Signal pending mask: bit set to 1 means sig is pending */
1789 unsigned signal_pending_mask = 0;
1790
1791 mswindows_sighandler mswindows_sigset (int nsig, mswindows_sighandler handler)
1792 {
1793   /* We delegate some signals to the system function */
1794   if (nsig == SIGFPE || nsig == SIGABRT || nsig == SIGINT)
1795     return signal (nsig, handler);
1796
1797   if (nsig < 0 || nsig > SIG_MAX)
1798     {
1799       errno = EINVAL;
1800       return NULL;
1801     }
1802
1803   /* Store handler ptr */
1804   {
1805     mswindows_sighandler old_handler = signal_handlers[nsig];
1806     signal_handlers[nsig] = handler;
1807     return old_handler;
1808   }
1809 }
1810   
1811 int mswindows_sighold (int nsig)
1812 {
1813   if (nsig < 0 || nsig > SIG_MAX)
1814     return errno = EINVAL;
1815
1816   signal_block_mask |= sigmask(nsig);
1817   return 0;
1818 }
1819
1820 int mswindows_sigrelse (int nsig)
1821 {
1822   if (nsig < 0 || nsig > SIG_MAX)
1823     return errno = EINVAL;
1824
1825   signal_block_mask &= ~sigmask(nsig);
1826
1827   if (signal_pending_mask & sigmask(nsig))
1828     mswindows_raise (nsig);
1829
1830   return 0;
1831 }
1832
1833 int mswindows_sigpause (int nsig)
1834 {
1835   /* This is currently not called, because the only
1836      call to sigpause inside XEmacs is with SIGCHLD
1837      parameter. Just in case, we put an assert here,
1838      so anyone who will add a call to sigpause will
1839      be surprised (or surprise someone else...) */
1840   assert (0);
1841   return 0;
1842 }
1843
1844 int mswindows_raise (int nsig)
1845 {
1846   /* We delegate some raises to the system routine */
1847   if (nsig == SIGFPE || nsig == SIGABRT || nsig == SIGINT)
1848     return raise (nsig);
1849
1850   if (nsig < 0 || nsig > SIG_MAX)
1851     return errno = EINVAL;
1852
1853   /* If the signal is blocked, remember to issue later */
1854   if (signal_block_mask & sigmask(nsig))
1855     {
1856       signal_pending_mask |= sigmask(nsig);
1857       return 0;
1858     }
1859
1860   if (signal_handlers[nsig] == SIG_IGN)
1861     return 0;
1862
1863   if (signal_handlers[nsig] != SIG_DFL)
1864     {
1865       (*signal_handlers[nsig])(nsig);
1866       return 0;
1867     }
1868
1869   /* Default signal actions */
1870   if (nsig == SIGALRM || nsig == SIGPROF)
1871     exit (3);
1872
1873   /* Other signals are ignored by default */
1874   return 0;
1875 }
1876
1877 /*--------------------------------------------------------------------*/
1878 /* Async timers                                                       */
1879 /*--------------------------------------------------------------------*/
1880
1881 /* We emulate two timers, one for SIGALRM, another for SIGPROF.
1882
1883    itimerproc() function has an implementation limitation: it does
1884    not allow to set *both* interval and period. If an attempt is
1885    made to set both, and then they are unequal, the function
1886    asserts.
1887
1888    Minimum timer resolution on Win32 systems varies, and is greater
1889    than or equal than 1 ms. The resolution is always wrapped not to
1890    attempt to get below the system defined limit.
1891    */
1892
1893 /* Timer precision, denominator of one fraction: for 100 ms
1894    interval, request 10 ms precision
1895    */
1896 const int timer_prec = 10;
1897
1898 /* Last itimervals, as set by calls to setitimer */
1899 static struct itimerval it_alarm;
1900 static struct itimerval it_prof;
1901
1902 /* Timer IDs as returned by MM */
1903 MMRESULT tid_alarm = 0;
1904 MMRESULT tid_prof = 0;
1905
1906 static void CALLBACK timer_proc (UINT uID, UINT uMsg, DWORD dwUser,
1907                                  DWORD dw1, DWORD dw2)
1908 {
1909   /* Just raise a signal indicated by dwUser parameter */
1910   mswindows_raise (dwUser);
1911 }
1912
1913 /* Divide time in ms specified by IT by DENOM. Return 1 ms
1914    if division results in zero */
1915 static UINT period (const struct itimerval* it, UINT denom)
1916 {
1917   static TIMECAPS time_caps;
1918
1919   UINT res;
1920   const struct timeval* tv = 
1921     (it->it_value.tv_sec == 0 && it->it_value.tv_usec == 0)
1922     ? &it->it_interval : &it->it_value;
1923   
1924   /* Zero means stop timer */
1925   if (tv->tv_sec == 0 && tv->tv_usec == 0)
1926     return 0;
1927   
1928   /* Convert to ms and divide by denom */
1929   res = (tv->tv_sec * 1000 + (tv->tv_usec + 500) / 1000) / denom;
1930   
1931   /* Converge to minimum timer resolution */
1932   if (time_caps.wPeriodMin == 0)
1933       timeGetDevCaps (&time_caps, sizeof(time_caps));
1934
1935   if (res < time_caps.wPeriodMin)
1936     res = time_caps.wPeriodMin;
1937
1938   return res;
1939 }
1940
1941 static int setitimer_helper (const struct itimerval* itnew,
1942                              struct itimerval* itold, struct itimerval* itcurrent,
1943                              MMRESULT* tid, DWORD sigkind)
1944 {
1945   UINT delay, resolution, event_type;
1946
1947   /* First stop the old timer */
1948   if (*tid)
1949     {
1950       timeKillEvent (*tid);
1951       timeEndPeriod (period (itcurrent, timer_prec));
1952       *tid = 0;
1953     }
1954
1955   /* Return old itimerval if requested */
1956   if (itold)
1957     *itold = *itcurrent;
1958
1959   *itcurrent = *itnew;
1960
1961   /* Determine if to start new timer */
1962   delay = period (itnew, 1);
1963   if (delay)
1964     {
1965       resolution = period (itnew, timer_prec);
1966       event_type = (itnew->it_value.tv_sec == 0 && itnew->it_value.tv_usec == 0)
1967         ? TIME_ONESHOT : TIME_PERIODIC;
1968       timeBeginPeriod (resolution);
1969       *tid = timeSetEvent (delay, resolution, timer_proc, sigkind, event_type);
1970     }
1971
1972   return !delay || *tid;
1973 }
1974  
1975 int setitimer (int kind, const struct itimerval* itnew,
1976                struct itimerval* itold)
1977 {
1978   /* In this version, both interval and value are allowed
1979      only if they are equal. */
1980   assert ((itnew->it_value.tv_sec == 0 && itnew->it_value.tv_usec == 0)
1981           || (itnew->it_interval.tv_sec == 0 && itnew->it_interval.tv_usec == 0)
1982           || (itnew->it_value.tv_sec == itnew->it_interval.tv_sec &&
1983               itnew->it_value.tv_usec == itnew->it_interval.tv_usec));
1984
1985   if (kind == ITIMER_REAL)
1986     return setitimer_helper (itnew, itold, &it_alarm, &tid_alarm, SIGALRM);
1987   else if (kind == ITIMER_PROF)
1988     return setitimer_helper (itnew, itold, &it_prof, &tid_prof, SIGPROF);
1989   else
1990     return errno = EINVAL;
1991 }
1992
1993 \f
1994 /*--------------------------------------------------------------------*/
1995 /*                        Memory-mapped files                         */
1996 /*--------------------------------------------------------------------*/
1997
1998 int
1999 open_input_file (file_data *p_file, const char *filename)
2000 {
2001   /* Synched with FSF 20.6.  We fixed some warnings. */
2002   HANDLE file;
2003   HANDLE file_mapping;
2004   void  *file_base;
2005   DWORD size, upper_size;
2006
2007   file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
2008                      OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
2009   if (file == INVALID_HANDLE_VALUE) 
2010     return FALSE;
2011
2012   size = GetFileSize (file, &upper_size);
2013   file_mapping = CreateFileMapping (file, NULL, PAGE_READONLY, 
2014                                     0, size, NULL);
2015   if (!file_mapping) 
2016     return FALSE;
2017
2018   file_base = MapViewOfFile (file_mapping, FILE_MAP_READ, 0, 0, size);
2019   if (file_base == 0) 
2020     return FALSE;
2021
2022   p_file->name = (char *)filename;
2023   p_file->size = size;
2024   p_file->file = file;
2025   p_file->file_mapping = file_mapping;
2026   p_file->file_base = (char *)file_base;
2027
2028   return TRUE;
2029 }
2030
2031 int
2032 open_output_file (file_data *p_file, const char *filename, unsigned long size)
2033 {
2034   /* Synched with FSF 20.6.  We fixed some warnings. */
2035   HANDLE file;
2036   HANDLE file_mapping;
2037   void  *file_base;
2038
2039   file = CreateFile (filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
2040                      CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
2041   if (file == INVALID_HANDLE_VALUE) 
2042     return FALSE;
2043
2044   file_mapping = CreateFileMapping (file, NULL, PAGE_READWRITE, 
2045                                     0, size, NULL);
2046   if (!file_mapping) 
2047     return FALSE;
2048   
2049   file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size);
2050   if (file_base == NULL) 
2051     return FALSE;
2052   
2053   p_file->name = filename;
2054   p_file->size = size;
2055   p_file->file = file;
2056   p_file->file_mapping = file_mapping;
2057   p_file->file_base = (char*) file_base;
2058
2059   return TRUE;
2060 }
2061
2062 #if 1 /* !defined(MINGW) */
2063 /* Return pointer to section header for section containing the given
2064    relative virtual address. */
2065 static IMAGE_SECTION_HEADER *
2066 rva_to_section (DWORD rva, IMAGE_NT_HEADERS * nt_header)
2067 {
2068   /* Synched with FSF 20.6.  We added MINGW stuff. */
2069   PIMAGE_SECTION_HEADER section;
2070   int i;
2071
2072   section = IMAGE_FIRST_SECTION (nt_header);
2073
2074   for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
2075     {
2076       /* Some linkers (eg. the NT SDK linker I believe) swapped the
2077          meaning of these two values - or rather, they ignored
2078          VirtualSize entirely and always set it to zero.  This affects
2079          some very old exes (eg. gzip dated Dec 1993).  Since
2080          mswindows_executable_type relies on this function to work reliably,
2081          we need to cope with this.  */
2082       DWORD real_size = max (section->SizeOfRawData,
2083                              section->Misc.VirtualSize);
2084       if (rva >= section->VirtualAddress
2085           && rva < section->VirtualAddress + real_size)
2086         return section;
2087       section++;
2088     }
2089   return NULL;
2090 }
2091 #endif
2092
2093 void
2094 mswindows_executable_type (const char * filename, int * is_dos_app,
2095                            int * is_cygnus_app)
2096 {
2097   /* Synched with FSF 20.6.  We added MINGW stuff and casts. */
2098   file_data executable;
2099   char * p;
2100
2101   /* Default values in case we can't tell for sure.  */
2102   *is_dos_app = FALSE;
2103   *is_cygnus_app = FALSE;
2104
2105   if (!open_input_file (&executable, filename))
2106     return;
2107
2108   p = strrchr (filename, '.');
2109
2110   /* We can only identify DOS .com programs from the extension. */
2111   if (p && stricmp (p, ".com") == 0)
2112     *is_dos_app = TRUE;
2113   else if (p && (stricmp (p, ".bat") == 0 ||
2114                  stricmp (p, ".cmd") == 0))
2115     {
2116       /* A DOS shell script - it appears that CreateProcess is happy to
2117          accept this (somewhat surprisingly); presumably it looks at
2118          COMSPEC to determine what executable to actually invoke.
2119          Therefore, we have to do the same here as well. */
2120       /* Actually, I think it uses the program association for that
2121          extension, which is defined in the registry.  */
2122       p = egetenv ("COMSPEC");
2123       if (p)
2124         mswindows_executable_type (p, is_dos_app, is_cygnus_app);
2125     }
2126   else
2127     {
2128       /* Look for DOS .exe signature - if found, we must also check that
2129          it isn't really a 16- or 32-bit Windows exe, since both formats
2130          start with a DOS program stub.  Note that 16-bit Windows
2131          executables use the OS/2 1.x format. */
2132
2133 #if 0 /* defined( MINGW ) */
2134       /* mingw32 doesn't have enough headers to detect cygwin
2135          apps, just do what we can. */
2136       FILHDR * exe_header;
2137
2138       exe_header = (FILHDR*) executable.file_base;
2139       if (exe_header->e_magic != DOSMAGIC)
2140         goto unwind;
2141
2142       if ((char*) exe_header->e_lfanew > (char*) executable.size)
2143         {
2144           /* Some dos headers (pkunzip) have bogus e_lfanew fields.  */
2145           *is_dos_app = TRUE;
2146         } 
2147       else if (exe_header->nt_signature != NT_SIGNATURE)
2148         {
2149           *is_dos_app = TRUE;
2150         }
2151 #else
2152       IMAGE_DOS_HEADER * dos_header;
2153       IMAGE_NT_HEADERS * nt_header;
2154
2155       dos_header = (PIMAGE_DOS_HEADER) executable.file_base;
2156       if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
2157         goto unwind;
2158           
2159       nt_header = (PIMAGE_NT_HEADERS) ((char*) dos_header + dos_header->e_lfanew);
2160           
2161       if ((char*) nt_header > (char*) dos_header + executable.size) 
2162         {
2163           /* Some dos headers (pkunzip) have bogus e_lfanew fields.  */
2164           *is_dos_app = TRUE;
2165         } 
2166       else if (nt_header->Signature != IMAGE_NT_SIGNATURE &&
2167                LOWORD (nt_header->Signature) != IMAGE_OS2_SIGNATURE)
2168         {
2169           *is_dos_app = TRUE;
2170         }
2171       else if (nt_header->Signature == IMAGE_NT_SIGNATURE)
2172         {
2173           /* Look for cygwin.dll in DLL import list. */
2174           IMAGE_DATA_DIRECTORY import_dir =
2175             nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
2176           IMAGE_IMPORT_DESCRIPTOR * imports;
2177           IMAGE_SECTION_HEADER * section;
2178
2179           section = rva_to_section (import_dir.VirtualAddress, nt_header);
2180           imports = (IMAGE_IMPORT_DESCRIPTOR *) RVA_TO_PTR (import_dir.VirtualAddress,
2181                                                             section, executable);
2182               
2183           for ( ; imports->Name; imports++)
2184             {
2185               char *dllname = (char*) RVA_TO_PTR (imports->Name, section, executable);
2186
2187               /* The exact name of the cygwin dll has changed with
2188                  various releases, but hopefully this will be reasonably
2189                  future proof.  */
2190               if (strncmp (dllname, "cygwin", 6) == 0)
2191                 {
2192                   *is_cygnus_app = TRUE;
2193                   break;
2194                 }
2195             }
2196         }
2197 #endif
2198     }
2199
2200  unwind:
2201   close_file_data (&executable);
2202 }
2203
2204 /* Close the system structures associated with the given file.  */
2205 void
2206 close_file_data (file_data *p_file)
2207 {
2208     UnmapViewOfFile (p_file->file_base);
2209     CloseHandle (p_file->file_mapping);
2210     CloseHandle (p_file->file);
2211 }
2212
2213 void
2214 vars_of_nt (void)
2215 {
2216   DEFVAR_INT ("nt-fake-unix-uid", &nt_fake_unix_uid /*
2217 *Set uid returned by `user-uid' and `user-real-uid'.
2218 Under NT and 9x, there is no uids, and even no almighty user called root.
2219 By setting this variable, you can have any uid of choice. Default is 0.
2220 Changes to this variable take effect immediately.
2221 */ );
2222   nt_fake_unix_uid = 0;
2223 }
2224
2225 /* end of nt.c */