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