2fa031924d9b0bf474ceae49196845bdcf0895a5
[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 /* MSVC stat function can't cope with UNC names and has other bugs, so
1319    replace it with our own.  This also allows us to calculate consistent
1320    inode values without hacks in the main Emacs code. */
1321 int
1322 stat (const char * path, struct stat * buf)
1323 {
1324   char * name;
1325   WIN32_FIND_DATA wfd;
1326   HANDLE fh;
1327   DWORD fake_inode;
1328   int permission;
1329   int len;
1330   int rootdir = FALSE;
1331
1332   if (path == NULL || buf == NULL)
1333     {
1334       errno = EFAULT;
1335       return -1;
1336     }
1337
1338   name = (char *) map_win32_filename (path, &path);
1339   /* must be valid filename, no wild cards */
1340   if (strchr (name, '*') || strchr (name, '?'))
1341     {
1342       errno = ENOENT;
1343       return -1;
1344     }
1345
1346   /* Remove trailing directory separator, unless name is the root
1347      directory of a drive or UNC volume in which case ensure there
1348      is a trailing separator. */
1349   len = strlen (name);
1350   rootdir = (path >= name + len - 1
1351              && (IS_DIRECTORY_SEP (*path) || *path == 0));
1352   name = strcpy (alloca (len + 2), name);
1353
1354   if (rootdir)
1355     {
1356       if (!IS_DIRECTORY_SEP (name[len-1]))
1357         strcat (name, "\\");
1358       if (GetDriveType (name) < 2)
1359         {
1360           errno = ENOENT;
1361           return -1;
1362         }
1363       memset (&wfd, 0, sizeof (wfd));
1364       wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
1365       wfd.ftCreationTime = utc_base_ft;
1366       wfd.ftLastAccessTime = utc_base_ft;
1367       wfd.ftLastWriteTime = utc_base_ft;
1368       strcpy (wfd.cFileName, name);
1369     }
1370   else
1371     {
1372       if (IS_DIRECTORY_SEP (name[len-1]))
1373         name[len - 1] = 0;
1374
1375       /* (This is hacky, but helps when doing file completions on
1376          network drives.)  Optimize by using information available from
1377          active readdir if possible.  */
1378       if (dir_find_handle != INVALID_HANDLE_VALUE &&
1379           (len = strlen (dir_pathname)),
1380           strnicmp (name, dir_pathname, len) == 0 &&
1381           IS_DIRECTORY_SEP (name[len]) &&
1382           stricmp (name + len + 1, dir_static.d_name) == 0)
1383         {
1384           /* This was the last entry returned by readdir.  */
1385           wfd = dir_find_data;
1386         }
1387       else
1388         {
1389       fh = FindFirstFile (name, &wfd);
1390       if (fh == INVALID_HANDLE_VALUE)
1391         {
1392           errno = ENOENT;
1393           return -1;
1394         }
1395       FindClose (fh);
1396     }
1397     }
1398
1399   if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1400     {
1401       buf->st_mode = _S_IFDIR;
1402       buf->st_nlink = 2;        /* doesn't really matter */
1403       fake_inode = 0;           /* this doesn't either I think */
1404     }
1405   else if (!NILP (Vmswindows_get_true_file_attributes))
1406     {
1407       /* This is more accurate in terms of getting the correct number
1408          of links, but is quite slow (it is noticeable when Emacs is
1409          making a list of file name completions). */
1410       BY_HANDLE_FILE_INFORMATION info;
1411
1412       /* No access rights required to get info.  */
1413       fh = CreateFile (name, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
1414                        OPEN_EXISTING, 0, NULL);
1415
1416       if (GetFileInformationByHandle (fh, &info))
1417         {
1418           switch (GetFileType (fh))
1419             {
1420             case FILE_TYPE_DISK:
1421               buf->st_mode = _S_IFREG;
1422               break;
1423             case FILE_TYPE_PIPE:
1424               buf->st_mode = _S_IFIFO;
1425               break;
1426             case FILE_TYPE_CHAR:
1427             case FILE_TYPE_UNKNOWN:
1428             default:
1429               buf->st_mode = _S_IFCHR;
1430             }
1431           buf->st_nlink = (short) info.nNumberOfLinks;
1432           /* Might as well use file index to fake inode values, but this
1433              is not guaranteed to be unique unless we keep a handle open
1434              all the time (even then there are situations where it is
1435              not unique).  Reputedly, there are at most 48 bits of info
1436              (on NTFS, presumably less on FAT). */
1437           fake_inode = info.nFileIndexLow ^ info.nFileIndexHigh;
1438           CloseHandle (fh);
1439         }
1440       else
1441         {
1442           errno = EACCES;
1443           return -1;
1444         }
1445     }
1446   else
1447     {
1448       /* Don't bother to make this information more accurate.  */
1449       buf->st_mode = _S_IFREG;
1450       buf->st_nlink = 1;
1451       fake_inode = 0;
1452     }
1453
1454 #if 0
1455   /* Not sure if there is any point in this.  */
1456   if (!NILP (Vwin32_generate_fake_inodes))
1457     fake_inode = generate_inode_val (name);
1458   else if (fake_inode == 0)
1459     {
1460       /* For want of something better, try to make everything unique.  */
1461       static DWORD gen_num = 0;
1462       fake_inode = ++gen_num;
1463     }
1464 #endif
1465
1466   /* #### MSVC defines _ino_t to be short; other libc's might not.  */
1467   buf->st_ino = (unsigned short) (fake_inode ^ (fake_inode >> 16));
1468
1469   /* consider files to belong to current user */
1470   buf->st_uid = the_passwd.pw_uid;
1471   buf->st_gid = the_passwd.pw_gid;
1472
1473   /* volume_info is set indirectly by map_win32_filename */
1474   buf->st_dev = volume_info.serialnum;
1475   buf->st_rdev = volume_info.serialnum;
1476
1477
1478   buf->st_size = wfd.nFileSizeLow;
1479
1480   /* Convert timestamps to Unix format. */
1481   buf->st_mtime = convert_time (wfd.ftLastWriteTime);
1482   buf->st_atime = convert_time (wfd.ftLastAccessTime);
1483   if (buf->st_atime == 0) buf->st_atime = buf->st_mtime;
1484   buf->st_ctime = convert_time (wfd.ftCreationTime);
1485   if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime;
1486
1487   /* determine rwx permissions */
1488   if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
1489     permission = _S_IREAD;
1490   else
1491     permission = _S_IREAD | _S_IWRITE;
1492   
1493   if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1494     permission |= _S_IEXEC;
1495   else
1496     {
1497       char * p = strrchr (name, '.');
1498       if (p != NULL &&
1499           (stricmp (p, ".exe") == 0 ||
1500            stricmp (p, ".com") == 0 ||
1501            stricmp (p, ".bat") == 0 ||
1502            stricmp (p, ".cmd") == 0))
1503         permission |= _S_IEXEC;
1504     }
1505
1506   buf->st_mode |= permission | (permission >> 3) | (permission >> 6);
1507
1508   return 0;
1509 }
1510
1511 /* From callproc.c  */
1512 extern Lisp_Object Vbinary_process_input;
1513 extern Lisp_Object Vbinary_process_output;
1514
1515 /* Unix pipe() has only one arg */
1516 int
1517 sys_pipe (int * phandles)
1518 {
1519   int rc;
1520   unsigned flags;
1521
1522   /* make pipe handles non-inheritable; when we spawn a child, we
1523      replace the relevant handle with an inheritable one.  Also put
1524      pipes into binary mode; we will do text mode translation ourselves
1525      if required.  */
1526   rc = _pipe (phandles, 0, _O_NOINHERIT | _O_BINARY);
1527
1528   if (rc == 0)
1529     {
1530       flags = FILE_PIPE | FILE_READ;
1531       if (!NILP (Vbinary_process_output))
1532           flags |= FILE_BINARY;
1533       fd_info[phandles[0]].flags = flags;
1534
1535       flags = FILE_PIPE | FILE_WRITE;
1536       if (!NILP (Vbinary_process_input))
1537           flags |= FILE_BINARY;
1538       fd_info[phandles[1]].flags = flags;
1539     }
1540
1541   return rc;
1542 }
1543
1544 void
1545 term_ntproc (int unused)
1546 {
1547 }
1548
1549 void
1550 init_ntproc ()
1551 {
1552   /* Initial preparation for subprocess support: replace our standard
1553      handles with non-inheritable versions. */
1554   {
1555     HANDLE parent;
1556     HANDLE stdin_save =  INVALID_HANDLE_VALUE;
1557     HANDLE stdout_save = INVALID_HANDLE_VALUE;
1558     HANDLE stderr_save = INVALID_HANDLE_VALUE;
1559
1560     parent = GetCurrentProcess ();
1561
1562     /* ignore errors when duplicating and closing; typically the
1563        handles will be invalid when running as a gui program. */
1564     DuplicateHandle (parent, 
1565                      GetStdHandle (STD_INPUT_HANDLE), 
1566                      parent,
1567                      &stdin_save, 
1568                      0, 
1569                      FALSE, 
1570                      DUPLICATE_SAME_ACCESS);
1571     
1572     DuplicateHandle (parent,
1573                      GetStdHandle (STD_OUTPUT_HANDLE),
1574                      parent,
1575                      &stdout_save,
1576                      0,
1577                      FALSE,
1578                      DUPLICATE_SAME_ACCESS);
1579     
1580     DuplicateHandle (parent,
1581                      GetStdHandle (STD_ERROR_HANDLE),
1582                      parent,
1583                      &stderr_save,
1584                      0,
1585                      FALSE,
1586                      DUPLICATE_SAME_ACCESS);
1587     
1588     fclose (stdin);
1589     fclose (stdout);
1590     fclose (stderr);
1591
1592     if (stdin_save != INVALID_HANDLE_VALUE)
1593       _open_osfhandle ((long) stdin_save, O_TEXT);
1594     else
1595       _open ("nul", O_TEXT | O_NOINHERIT | O_RDONLY);
1596     _fdopen (0, "r");
1597
1598     if (stdout_save != INVALID_HANDLE_VALUE)
1599       _open_osfhandle ((long) stdout_save, O_TEXT);
1600     else
1601       _open ("nul", O_TEXT | O_NOINHERIT | O_WRONLY);
1602     _fdopen (1, "w");
1603
1604     if (stderr_save != INVALID_HANDLE_VALUE)
1605       _open_osfhandle ((long) stderr_save, O_TEXT);
1606     else
1607       _open ("nul", O_TEXT | O_NOINHERIT | O_WRONLY);
1608     _fdopen (2, "w");
1609   }
1610
1611   /* unfortunately, atexit depends on implementation of malloc */
1612   /* atexit (term_ntproc); */
1613   signal (SIGABRT, term_ntproc);
1614
1615   /* determine which drives are fixed, for GetCachedVolumeInformation */
1616   {
1617     /* GetDriveType must have trailing backslash. */
1618     char drive[] = "A:\\";
1619
1620     /* Loop over all possible drive letters */
1621     while ( *drive <= 'Z' )
1622     {
1623       /* Record if this drive letter refers to a fixed drive. */
1624       fixed_drives[ DRIVE_INDEX (*drive) ] =
1625         (GetDriveType (drive) == DRIVE_FIXED);
1626
1627       (*drive)++;
1628     }
1629   }
1630 }
1631 #ifndef HAVE_TTY
1632 Lisp_Object
1633 tty_semi_canonicalize_console_connection (Lisp_Object connection,
1634                                           Error_behavior errb)
1635 {
1636   return Vstdio_str;
1637 }
1638
1639 Lisp_Object
1640 tty_canonicalize_console_connection (Lisp_Object connection,
1641                                      Error_behavior errb)
1642 {
1643   return Vstdio_str;
1644 }
1645
1646 Lisp_Object
1647 tty_semi_canonicalize_device_connection (Lisp_Object connection,
1648                                          Error_behavior errb)
1649 {
1650   return Vstdio_str;
1651 }
1652
1653 Lisp_Object
1654 tty_canonicalize_device_connection (Lisp_Object connection,
1655                                     Error_behavior errb)
1656 {
1657   return Vstdio_str;
1658 }
1659 #endif
1660
1661 /*--------------------------------------------------------------------*/
1662 /* Signal support                                                     */
1663 /*--------------------------------------------------------------------*/
1664
1665 /* We need MS-defined signal and raise here */
1666 #undef signal
1667 #undef raise
1668
1669 #define sigmask(nsig) (1U << nsig)
1670
1671 /* We can support as many signals as fit into word */
1672 #define SIG_MAX 32
1673
1674 /* Signal handlers. Initial value = 0 = SIG_DFL */
1675 static void (__cdecl *signal_handlers[SIG_MAX])(int) = {0};
1676
1677 /* Signal block mask: bit set to 1 means blocked */
1678 unsigned signal_block_mask = 0;
1679
1680 /* Signal pending mask: bit set to 1 means sig is pending */
1681 unsigned signal_pending_mask = 0;
1682
1683 msw_sighandler msw_sigset (int nsig, msw_sighandler handler)
1684 {
1685   /* We delegate some signals to the system function */
1686   if (nsig == SIGFPE || nsig == SIGABRT || nsig == SIGINT)
1687     return signal (nsig, handler);
1688
1689   if (nsig < 0 || nsig > SIG_MAX)
1690     {
1691       errno = EINVAL;
1692       return NULL;
1693     }
1694
1695   /* Store handler ptr */
1696   {
1697     msw_sighandler old_handler = signal_handlers[nsig];
1698     signal_handlers[nsig] = handler;
1699     return old_handler;
1700   }
1701 }
1702   
1703 int msw_sighold (int nsig)
1704 {
1705   if (nsig < 0 || nsig > SIG_MAX)
1706     return errno = EINVAL;
1707
1708   signal_block_mask |= sigmask(nsig);
1709   return 0;
1710 }
1711
1712 int msw_sigrelse (int nsig)
1713 {
1714   if (nsig < 0 || nsig > SIG_MAX)
1715     return errno = EINVAL;
1716
1717   signal_block_mask &= ~sigmask(nsig);
1718
1719   if (signal_pending_mask & sigmask(nsig))
1720     msw_raise (nsig);
1721
1722   return 0;
1723 }
1724
1725 int msw_sigpause (int nsig)
1726 {
1727   /* This is currently not called, because the only
1728      call to sigpause inside XEmacs is with SIGCHLD
1729      parameter. Just in case, we put an assert here,
1730      so anyone who will add a call to sigpause will
1731      be surprised (or surprise someone else...) */
1732   assert (0);
1733   return 0;
1734 }
1735
1736 int msw_raise (int nsig)
1737 {
1738   /* We delegate some raises to the system routine */
1739   if (nsig == SIGFPE || nsig == SIGABRT || nsig == SIGINT)
1740     return raise (nsig);
1741
1742   if (nsig < 0 || nsig > SIG_MAX)
1743     return errno = EINVAL;
1744
1745   /* If the signal is blocked, remember to issue later */
1746   if (signal_block_mask & sigmask(nsig))
1747     {
1748       signal_pending_mask |= sigmask(nsig);
1749       return 0;
1750     }
1751
1752   if (signal_handlers[nsig] == SIG_IGN)
1753     return 0;
1754
1755   if (signal_handlers[nsig] != SIG_DFL)
1756     {
1757       (*signal_handlers[nsig])(nsig);
1758       return 0;
1759     }
1760
1761   /* Default signal actions */
1762   if (nsig == SIGALRM || nsig == SIGPROF)
1763     exit (3);
1764
1765   /* Other signals are ignored by default */
1766   return 0;
1767 }
1768
1769 /*--------------------------------------------------------------------*/
1770 /* Async timers                                                       */
1771 /*--------------------------------------------------------------------*/
1772
1773 /* We emulate two timers, one for SIGALRM, another for SIGPROF.
1774
1775    itimerproc() function has an implementation limitation: it does
1776    not allow to set *both* interval and period. If an attempt is
1777    made to set both, and then they are unequal, the function
1778    asserts.
1779
1780    Minimum timer resolution on Win32 systems varies, and is greater
1781    than or equal than 1 ms. The resolution is always wrapped not to
1782    attempt to get below the system defined limit.
1783    */
1784
1785 /* Timer precision, denominator of one fraction: for 100 ms
1786    interval, request 10 ms precision
1787    */
1788 const int timer_prec = 10;
1789
1790 /* Last itimervals, as set by calls to setitimer */
1791 static struct itimerval it_alarm;
1792 static struct itimerval it_prof;
1793
1794 /* Timer IDs as returned by MM */
1795 MMRESULT tid_alarm = 0;
1796 MMRESULT tid_prof = 0;
1797
1798 static void CALLBACK timer_proc (UINT uID, UINT uMsg, DWORD dwUser,
1799                                  DWORD dw1, DWORD dw2)
1800 {
1801   /* Just raise a signal indicated by dwUser parameter */
1802   msw_raise (dwUser);
1803 }
1804
1805 /* Divide time in ms specified by IT by DENOM. Return 1 ms
1806    if division results in zero */
1807 static UINT period (const struct itimerval* it, UINT denom)
1808 {
1809   static TIMECAPS time_caps;
1810
1811   UINT res;
1812   const struct timeval* tv = 
1813     (it->it_value.tv_sec == 0 && it->it_value.tv_usec == 0)
1814     ? &it->it_interval : &it->it_value;
1815   
1816   /* Zero means stop timer */
1817   if (tv->tv_sec == 0 && tv->tv_usec == 0)
1818     return 0;
1819   
1820   /* Convert to ms and divide by denom */
1821   res = (tv->tv_sec * 1000 + (tv->tv_usec + 500) / 1000) / denom;
1822   
1823   /* Converge to minimum timer resolution */
1824   if (time_caps.wPeriodMin == 0)
1825       timeGetDevCaps (&time_caps, sizeof(time_caps));
1826
1827   if (res < time_caps.wPeriodMin)
1828     res = time_caps.wPeriodMin;
1829
1830   return res;
1831 }
1832
1833 static int setitimer_helper (const struct itimerval* itnew,
1834                              struct itimerval* itold, struct itimerval* itcurrent,
1835                              MMRESULT* tid, DWORD sigkind)
1836 {
1837   UINT delay, resolution, event_type;
1838
1839   /* First stop the old timer */
1840   if (*tid)
1841     {
1842       timeKillEvent (*tid);
1843       timeEndPeriod (period (itcurrent, timer_prec));
1844       *tid = 0;
1845     }
1846
1847   /* Return old itimerval if requested */
1848   if (itold)
1849     *itold = *itcurrent;
1850
1851   *itcurrent = *itnew;
1852
1853   /* Determine if to start new timer */
1854   delay = period (itnew, 1);
1855   if (delay)
1856     {
1857       resolution = period (itnew, timer_prec);
1858       event_type = (itnew->it_value.tv_sec == 0 && itnew->it_value.tv_usec == 0)
1859         ? TIME_ONESHOT : TIME_PERIODIC;
1860       timeBeginPeriod (resolution);
1861       *tid = timeSetEvent (delay, resolution, timer_proc, sigkind, event_type);
1862     }
1863
1864   return !delay || *tid;
1865 }
1866  
1867 int setitimer (int kind, const struct itimerval* itnew,
1868                struct itimerval* itold)
1869 {
1870   /* In this version, both interval and value are allowed
1871      only if they are equal. */
1872   assert ((itnew->it_value.tv_sec == 0 && itnew->it_value.tv_usec == 0)
1873           || (itnew->it_interval.tv_sec == 0 && itnew->it_interval.tv_usec == 0)
1874           || (itnew->it_value.tv_sec == itnew->it_interval.tv_sec &&
1875               itnew->it_value.tv_usec == itnew->it_interval.tv_usec));
1876
1877   if (kind == ITIMER_REAL)
1878     return setitimer_helper (itnew, itold, &it_alarm, &tid_alarm, SIGALRM);
1879   else if (kind == ITIMER_PROF)
1880     return setitimer_helper (itnew, itold, &it_prof, &tid_prof, SIGPROF);
1881   else
1882     return errno = EINVAL;
1883 }
1884
1885 int
1886 open_input_file (file_data *p_file, CONST char *filename)
1887 {
1888   HANDLE file;
1889   HANDLE file_mapping;
1890   void  *file_base;
1891   DWORD size, upper_size;
1892
1893   file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
1894                      OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
1895   if (file == INVALID_HANDLE_VALUE) 
1896     return FALSE;
1897
1898   size = GetFileSize (file, &upper_size);
1899   file_mapping = CreateFileMapping (file, NULL, PAGE_READONLY, 
1900                                     0, size, NULL);
1901   if (!file_mapping) 
1902     return FALSE;
1903
1904   file_base = MapViewOfFile (file_mapping, FILE_MAP_READ, 0, 0, size);
1905   if (file_base == 0) 
1906     return FALSE;
1907
1908   p_file->name = (char*)filename;
1909   p_file->size = size;
1910   p_file->file = file;
1911   p_file->file_mapping = file_mapping;
1912   p_file->file_base = file_base;
1913
1914   return TRUE;
1915 }
1916
1917 /* Close the system structures associated with the given file.  */
1918 void
1919 close_file_data (file_data *p_file)
1920 {
1921     UnmapViewOfFile (p_file->file_base);
1922     CloseHandle (p_file->file_mapping);
1923     CloseHandle (p_file->file);
1924 }
1925
1926 /* end of nt.c */