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