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