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