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