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