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