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