Mercurial > hg > xemacs-beta
comparison src/nt.c @ 428:3ecd8885ac67 r21-2-22
Import from CVS: tag r21-2-22
author | cvs |
---|---|
date | Mon, 13 Aug 2007 11:28:15 +0200 |
parents | |
children | a5df635868b2 |
comparison
equal
deleted
inserted
replaced
427:0a0253eac470 | 428:3ecd8885ac67 |
---|---|
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 | |
612 for (i = 0; i < countof (env_vars); i++) | |
613 { | |
614 if (!getenv (env_vars[i]) && | |
615 (lpval = nt_get_resource (env_vars[i], &dwType)) != NULL) | |
616 { | |
617 if (dwType == REG_EXPAND_SZ) | |
618 { | |
619 char buf1[500], buf2[500]; | |
620 | |
621 ExpandEnvironmentStrings ((LPSTR) lpval, buf1, 500); | |
622 _snprintf (buf2, 499, "%s=%s", env_vars[i], buf1); | |
623 putenv (strdup (buf2)); | |
624 } | |
625 else if (dwType == REG_SZ) | |
626 { | |
627 char buf[500]; | |
628 | |
629 _snprintf (buf, 499, "%s=%s", env_vars[i], lpval); | |
630 putenv (strdup (buf)); | |
631 } | |
632 | |
633 xfree (lpval); | |
634 } | |
635 } | |
636 } | |
637 | |
638 /* Another special case: on NT, the PATH variable is actually named | |
639 "Path" although cmd.exe (perhaps NT itself) arranges for | |
640 environment variable lookup and setting to be case insensitive. | |
641 However, Emacs assumes a fully case sensitive environment, so we | |
642 need to change "Path" to "PATH" to match the expectations of | |
643 various elisp packages. We do this by the sneaky method of | |
644 modifying the string in the C runtime environ entry. | |
645 | |
646 The same applies to COMSPEC. */ | |
647 { | |
648 char ** envp; | |
649 | |
650 for (envp = environ; *envp; envp++) | |
651 if (_strnicmp (*envp, "PATH=", 5) == 0) | |
652 memcpy (*envp, "PATH=", 5); | |
653 else if (_strnicmp (*envp, "COMSPEC=", 8) == 0) | |
654 memcpy (*envp, "COMSPEC=", 8); | |
655 } | |
656 | |
657 /* Remember the initial working directory for getwd, then make the | |
658 real wd be the location of emacs.exe to avoid conflicts when | |
659 renaming or deleting directories. (We also don't call chdir when | |
660 running subprocesses for the same reason.) */ | |
661 if (!GetCurrentDirectory (MAXPATHLEN, startup_dir)) | |
662 abort (); | |
663 | |
664 { | |
665 char *p; | |
666 char modname[MAX_PATH]; | |
667 | |
668 if (!GetModuleFileName (NULL, modname, MAX_PATH)) | |
669 abort (); | |
670 if ((p = strrchr (modname, '\\')) == NULL) | |
671 abort (); | |
672 *p = 0; | |
673 | |
674 SetCurrentDirectory (modname); | |
675 } | |
676 | |
677 init_user_info (); | |
678 } | |
679 | |
680 #ifndef HAVE_X_WINDOWS | |
681 /* X11R6 on NT provides the single parameter version of this command. */ | |
682 | |
683 #include <sys/timeb.h> | |
684 | |
685 /* Emulate gettimeofday (Ulrich Leodolter, 1/11/95). */ | |
686 void | |
687 gettimeofday (struct timeval *tv, struct timezone *tz) | |
688 { | |
689 struct _timeb tb; | |
690 _ftime (&tb); | |
691 | |
692 tv->tv_sec = tb.time; | |
693 tv->tv_usec = tb.millitm * 1000L; | |
694 if (tz) | |
695 { | |
696 tz->tz_minuteswest = tb.timezone; /* minutes west of Greenwich */ | |
697 tz->tz_dsttime = tb.dstflag; /* type of dst correction */ | |
698 } | |
699 } | |
700 | |
701 #endif /* HAVE_X_WINDOWS */ | |
702 | |
703 /* ------------------------------------------------------------------------- */ | |
704 /* IO support and wrapper functions for Win32 API. */ | |
705 /* ------------------------------------------------------------------------- */ | |
706 | |
707 /* Place a wrapper around the MSVC version of ctime. It returns NULL | |
708 on network directories, so we handle that case here. | |
709 (Ulrich Leodolter, 1/11/95). */ | |
710 char * | |
711 sys_ctime (const time_t *t) | |
712 { | |
713 char *str = (char *) ctime (t); | |
714 return (str ? str : "Sun Jan 01 00:00:00 1970"); | |
715 } | |
716 | |
717 /* Emulate sleep...we could have done this with a define, but that | |
718 would necessitate including windows.h in the files that used it. | |
719 This is much easier. */ | |
720 | |
721 #ifndef HAVE_X_WINDOWS | |
722 void | |
723 sys_sleep (int seconds) | |
724 { | |
725 Sleep (seconds * 1000); | |
726 } | |
727 #endif | |
728 | |
729 /* #### This is an evil dirty hack. We must get rid of it. | |
730 Word "munging" is not in XEmacs lexicon. - kkm */ | |
731 | |
732 /* Internal MSVC data and functions for low-level descriptor munging */ | |
733 #if (_MSC_VER == 900) | |
734 extern char _osfile[]; | |
735 #endif | |
736 extern int __cdecl _set_osfhnd (int fd, long h); | |
737 extern int __cdecl _free_osfhnd (int fd); | |
738 | |
739 /* parallel array of private info on file handles */ | |
740 filedesc fd_info [ MAXDESC ]; | |
741 | |
742 typedef struct volume_info_data { | |
743 struct volume_info_data * next; | |
744 | |
745 /* time when info was obtained */ | |
746 DWORD timestamp; | |
747 | |
748 /* actual volume info */ | |
749 char * root_dir; | |
750 DWORD serialnum; | |
751 DWORD maxcomp; | |
752 DWORD flags; | |
753 char * name; | |
754 char * type; | |
755 } volume_info_data; | |
756 | |
757 /* Global referenced by various functions. */ | |
758 static volume_info_data volume_info; | |
759 | |
760 /* Vector to indicate which drives are local and fixed (for which cached | |
761 data never expires). */ | |
762 static BOOL fixed_drives[26]; | |
763 | |
764 /* Consider cached volume information to be stale if older than 10s, | |
765 at least for non-local drives. Info for fixed drives is never stale. */ | |
766 #define DRIVE_INDEX( c ) ( (c) <= 'Z' ? (c) - 'A' : (c) - 'a' ) | |
767 #define VOLINFO_STILL_VALID( root_dir, info ) \ | |
768 ( ( isalpha (root_dir[0]) && \ | |
769 fixed_drives[ DRIVE_INDEX (root_dir[0]) ] ) \ | |
770 || GetTickCount () - info->timestamp < 10000 ) | |
771 | |
772 /* Cache support functions. */ | |
773 | |
774 /* Simple linked list with linear search is sufficient. */ | |
775 static volume_info_data *volume_cache = NULL; | |
776 | |
777 static volume_info_data * | |
778 lookup_volume_info (char * root_dir) | |
779 { | |
780 volume_info_data * info; | |
781 | |
782 for (info = volume_cache; info; info = info->next) | |
783 if (stricmp (info->root_dir, root_dir) == 0) | |
784 break; | |
785 return info; | |
786 } | |
787 | |
788 static void | |
789 add_volume_info (char * root_dir, volume_info_data * info) | |
790 { | |
791 info->root_dir = xstrdup (root_dir); | |
792 info->next = volume_cache; | |
793 volume_cache = info; | |
794 } | |
795 | |
796 | |
797 /* Wrapper for GetVolumeInformation, which uses caching to avoid | |
798 performance penalty (~2ms on 486 for local drives, 7.5ms for local | |
799 cdrom drive, ~5-10ms or more for remote drives on LAN). */ | |
800 volume_info_data * | |
801 GetCachedVolumeInformation (char * root_dir) | |
802 { | |
803 volume_info_data * info; | |
804 char default_root[ MAX_PATH ]; | |
805 | |
806 /* NULL for root_dir means use root from current directory. */ | |
807 if (root_dir == NULL) | |
808 { | |
809 if (GetCurrentDirectory (MAX_PATH, default_root) == 0) | |
810 return NULL; | |
811 parse_root (default_root, &root_dir); | |
812 *root_dir = 0; | |
813 root_dir = default_root; | |
814 } | |
815 | |
816 /* Local fixed drives can be cached permanently. Removable drives | |
817 cannot be cached permanently, since the volume name and serial | |
818 number (if nothing else) can change. Remote drives should be | |
819 treated as if they are removable, since there is no sure way to | |
820 tell whether they are or not. Also, the UNC association of drive | |
821 letters mapped to remote volumes can be changed at any time (even | |
822 by other processes) without notice. | |
823 | |
824 As a compromise, so we can benefit from caching info for remote | |
825 volumes, we use a simple expiry mechanism to invalidate cache | |
826 entries that are more than ten seconds old. */ | |
827 | |
828 #if 0 | |
829 /* No point doing this, because WNetGetConnection is even slower than | |
830 GetVolumeInformation, consistently taking ~50ms on a 486 (FWIW, | |
831 GetDriveType is about the only call of this type which does not | |
832 involve network access, and so is extremely quick). */ | |
833 | |
834 /* Map drive letter to UNC if remote. */ | |
835 if ( isalpha( root_dir[0] ) && !fixed[ DRIVE_INDEX( root_dir[0] ) ] ) | |
836 { | |
837 char remote_name[ 256 ]; | |
838 char drive[3] = { root_dir[0], ':' }; | |
839 | |
840 if (WNetGetConnection (drive, remote_name, sizeof (remote_name)) | |
841 == NO_ERROR) | |
842 /* do something */ ; | |
843 } | |
844 #endif | |
845 | |
846 info = lookup_volume_info (root_dir); | |
847 | |
848 if (info == NULL || ! VOLINFO_STILL_VALID (root_dir, info)) | |
849 { | |
850 char name[ 256 ]; | |
851 DWORD serialnum; | |
852 DWORD maxcomp; | |
853 DWORD flags; | |
854 char type[ 256 ]; | |
855 | |
856 /* Info is not cached, or is stale. */ | |
857 if (!GetVolumeInformation (root_dir, | |
858 name, sizeof (name), | |
859 &serialnum, | |
860 &maxcomp, | |
861 &flags, | |
862 type, sizeof (type))) | |
863 return NULL; | |
864 | |
865 /* Cache the volume information for future use, overwriting existing | |
866 entry if present. */ | |
867 if (info == NULL) | |
868 { | |
869 info = (volume_info_data *) xmalloc (sizeof (volume_info_data)); | |
870 add_volume_info (root_dir, info); | |
871 } | |
872 else | |
873 { | |
874 free (info->name); | |
875 free (info->type); | |
876 } | |
877 | |
878 info->name = xstrdup (name); | |
879 info->serialnum = serialnum; | |
880 info->maxcomp = maxcomp; | |
881 info->flags = flags; | |
882 info->type = xstrdup (type); | |
883 info->timestamp = GetTickCount (); | |
884 } | |
885 | |
886 return info; | |
887 } | |
888 | |
889 /* Get information on the volume where name is held; set path pointer to | |
890 start of pathname in name (past UNC header\volume header if present). */ | |
891 int | |
892 get_volume_info (const char * name, const char ** pPath) | |
893 { | |
894 char temp[MAX_PATH]; | |
895 char *rootname = NULL; /* default to current volume */ | |
896 volume_info_data * info; | |
897 | |
898 if (name == NULL) | |
899 return FALSE; | |
900 | |
901 /* find the root name of the volume if given */ | |
902 if (isalpha (name[0]) && name[1] == ':') | |
903 { | |
904 rootname = temp; | |
905 temp[0] = *name++; | |
906 temp[1] = *name++; | |
907 temp[2] = '\\'; | |
908 temp[3] = 0; | |
909 } | |
910 else if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1])) | |
911 { | |
912 char *str = temp; | |
913 int slashes = 4; | |
914 rootname = temp; | |
915 do | |
916 { | |
917 if (IS_DIRECTORY_SEP (*name) && --slashes == 0) | |
918 break; | |
919 *str++ = *name++; | |
920 } | |
921 while ( *name ); | |
922 | |
923 *str++ = '\\'; | |
924 *str = 0; | |
925 } | |
926 | |
927 if (pPath) | |
928 *pPath = name; | |
929 | |
930 info = GetCachedVolumeInformation (rootname); | |
931 if (info != NULL) | |
932 { | |
933 /* Set global referenced by other functions. */ | |
934 volume_info = *info; | |
935 return TRUE; | |
936 } | |
937 return FALSE; | |
938 } | |
939 | |
940 /* Determine if volume is FAT format (ie. only supports short 8.3 | |
941 names); also set path pointer to start of pathname in name. */ | |
942 int | |
943 is_fat_volume (const char * name, const char ** pPath) | |
944 { | |
945 if (get_volume_info (name, pPath)) | |
946 return (volume_info.maxcomp == 12); | |
947 return FALSE; | |
948 } | |
949 | |
950 /* Map filename to a legal 8.3 name if necessary. */ | |
951 const char * | |
952 map_win32_filename (const char * name, const char ** pPath) | |
953 { | |
954 static char shortname[MAX_PATH]; | |
955 char * str = shortname; | |
956 char c; | |
957 const char * path; | |
958 const char * save_name = name; | |
959 | |
960 if (is_fat_volume (name, &path)) /* truncate to 8.3 */ | |
961 { | |
962 REGISTER int left = 8; /* maximum number of chars in part */ | |
963 REGISTER int extn = 0; /* extension added? */ | |
964 REGISTER int dots = 2; /* maximum number of dots allowed */ | |
965 | |
966 while (name < path) | |
967 *str++ = *name++; /* skip past UNC header */ | |
968 | |
969 while ((c = *name++)) | |
970 { | |
971 switch ( c ) | |
972 { | |
973 case '\\': | |
974 case '/': | |
975 *str++ = '\\'; | |
976 extn = 0; /* reset extension flags */ | |
977 dots = 2; /* max 2 dots */ | |
978 left = 8; /* max length 8 for main part */ | |
979 break; | |
980 case ':': | |
981 *str++ = ':'; | |
982 extn = 0; /* reset extension flags */ | |
983 dots = 2; /* max 2 dots */ | |
984 left = 8; /* max length 8 for main part */ | |
985 break; | |
986 case '.': | |
987 if ( dots ) | |
988 { | |
989 /* Convert path components of the form .xxx to _xxx, | |
990 but leave . and .. as they are. This allows .emacs | |
991 to be read as _emacs, for example. */ | |
992 | |
993 if (! *name || | |
994 *name == '.' || | |
995 IS_DIRECTORY_SEP (*name)) | |
996 { | |
997 *str++ = '.'; | |
998 dots--; | |
999 } | |
1000 else | |
1001 { | |
1002 *str++ = '_'; | |
1003 left--; | |
1004 dots = 0; | |
1005 } | |
1006 } | |
1007 else if ( !extn ) | |
1008 { | |
1009 *str++ = '.'; | |
1010 extn = 1; /* we've got an extension */ | |
1011 left = 3; /* 3 chars in extension */ | |
1012 } | |
1013 else | |
1014 { | |
1015 /* any embedded dots after the first are converted to _ */ | |
1016 *str++ = '_'; | |
1017 } | |
1018 break; | |
1019 case '~': | |
1020 case '#': /* don't lose these, they're important */ | |
1021 if ( ! left ) | |
1022 str[-1] = c; /* replace last character of part */ | |
1023 /* FALLTHRU */ | |
1024 default: | |
1025 if ( left ) | |
1026 { | |
1027 *str++ = tolower (c); /* map to lower case (looks nicer) */ | |
1028 left--; | |
1029 dots = 0; /* started a path component */ | |
1030 } | |
1031 break; | |
1032 } | |
1033 } | |
1034 *str = '\0'; | |
1035 } | |
1036 else | |
1037 { | |
1038 strcpy (shortname, name); | |
1039 unixtodos_filename (shortname); | |
1040 } | |
1041 | |
1042 if (pPath) | |
1043 *pPath = shortname + (path - save_name); | |
1044 | |
1045 return shortname; | |
1046 } | |
1047 | |
1048 | |
1049 /* Emulate the Unix directory procedures opendir, closedir, | |
1050 and readdir. We can't use the procedures supplied in sysdep.c, | |
1051 so we provide them here. */ | |
1052 | |
1053 struct direct dir_static; /* simulated directory contents */ | |
1054 static HANDLE dir_find_handle = INVALID_HANDLE_VALUE; | |
1055 static int dir_is_fat; | |
1056 static char dir_pathname[MAXPATHLEN+1]; | |
1057 static WIN32_FIND_DATA dir_find_data; | |
1058 | |
1059 DIR * | |
1060 opendir (const char *filename) | |
1061 { | |
1062 DIR *dirp; | |
1063 | |
1064 /* Opening is done by FindFirstFile. However, a read is inherent to | |
1065 this operation, so we defer the open until read time. */ | |
1066 | |
1067 if (!(dirp = xnew_and_zero(DIR))) | |
1068 return NULL; | |
1069 if (dir_find_handle != INVALID_HANDLE_VALUE) | |
1070 return NULL; | |
1071 | |
1072 dirp->dd_fd = 0; | |
1073 dirp->dd_loc = 0; | |
1074 dirp->dd_size = 0; | |
1075 | |
1076 strncpy (dir_pathname, map_win32_filename (filename, NULL), MAXPATHLEN); | |
1077 dir_pathname[MAXPATHLEN] = '\0'; | |
1078 dir_is_fat = is_fat_volume (filename, NULL); | |
1079 | |
1080 return dirp; | |
1081 } | |
1082 | |
1083 void | |
1084 closedir (DIR *dirp) | |
1085 { | |
1086 /* If we have a find-handle open, close it. */ | |
1087 if (dir_find_handle != INVALID_HANDLE_VALUE) | |
1088 { | |
1089 FindClose (dir_find_handle); | |
1090 dir_find_handle = INVALID_HANDLE_VALUE; | |
1091 } | |
1092 xfree (dirp); | |
1093 } | |
1094 | |
1095 struct direct * | |
1096 readdir (DIR *dirp) | |
1097 { | |
1098 /* If we aren't dir_finding, do a find-first, otherwise do a find-next. */ | |
1099 if (dir_find_handle == INVALID_HANDLE_VALUE) | |
1100 { | |
1101 char filename[MAXNAMLEN + 3]; | |
1102 int ln; | |
1103 | |
1104 strcpy (filename, dir_pathname); | |
1105 ln = strlen (filename) - 1; | |
1106 if (!IS_DIRECTORY_SEP (filename[ln])) | |
1107 strcat (filename, "\\"); | |
1108 strcat (filename, "*"); | |
1109 | |
1110 dir_find_handle = FindFirstFile (filename, &dir_find_data); | |
1111 | |
1112 if (dir_find_handle == INVALID_HANDLE_VALUE) | |
1113 return NULL; | |
1114 } | |
1115 else | |
1116 { | |
1117 if (!FindNextFile (dir_find_handle, &dir_find_data)) | |
1118 return NULL; | |
1119 } | |
1120 | |
1121 /* Emacs never uses this value, so don't bother making it match | |
1122 value returned by stat(). */ | |
1123 dir_static.d_ino = 1; | |
1124 | |
1125 dir_static.d_reclen = sizeof (struct direct) - MAXNAMLEN + 3 + | |
1126 dir_static.d_namlen - dir_static.d_namlen % 4; | |
1127 | |
1128 dir_static.d_namlen = strlen (dir_find_data.cFileName); | |
1129 strcpy (dir_static.d_name, dir_find_data.cFileName); | |
1130 if (dir_is_fat) | |
1131 _strlwr (dir_static.d_name); | |
1132 else if (!NILP (Vmswindows_downcase_file_names)) | |
1133 { | |
1134 REGISTER char *p; | |
1135 for (p = dir_static.d_name; *p; p++) | |
1136 if (*p >= 'a' && *p <= 'z') | |
1137 break; | |
1138 if (!*p) | |
1139 _strlwr (dir_static.d_name); | |
1140 } | |
1141 | |
1142 return &dir_static; | |
1143 } | |
1144 | |
1145 #if 0 | |
1146 /* #### Have to check if all that sad story about '95 is true - kkm */ | |
1147 int | |
1148 sys_rename (const char * oldname, const char * newname) | |
1149 { | |
1150 char temp[MAX_PATH]; | |
1151 DWORD attr; | |
1152 | |
1153 /* MoveFile on Win95 doesn't correctly change the short file name | |
1154 alias in a number of circumstances (it is not easy to predict when | |
1155 just by looking at oldname and newname, unfortunately). In these | |
1156 cases, renaming through a temporary name avoids the problem. | |
1157 | |
1158 A second problem on Win95 is that renaming through a temp name when | |
1159 newname is uppercase fails (the final long name ends up in | |
1160 lowercase, although the short alias might be uppercase) UNLESS the | |
1161 long temp name is not 8.3. | |
1162 | |
1163 So, on Win95 we always rename through a temp name, and we make sure | |
1164 the temp name has a long extension to ensure correct renaming. */ | |
1165 | |
1166 strcpy (temp, map_win32_filename (oldname, NULL)); | |
1167 | |
1168 if (GetVersion () & 0x80000000) | |
1169 { | |
1170 char * p; | |
1171 | |
1172 if (p = strrchr (temp, '\\')) | |
1173 p++; | |
1174 else | |
1175 p = temp; | |
1176 /* Force temp name to require a manufactured 8.3 alias - this | |
1177 seems to make the second rename work properly. */ | |
1178 strcpy (p, "_rename_temp.XXXXXX"); | |
1179 sys_mktemp (temp); | |
1180 if (rename (map_win32_filename (oldname, NULL), temp) < 0) | |
1181 return -1; | |
1182 } | |
1183 | |
1184 /* Emulate Unix behavior - newname is deleted if it already exists | |
1185 (at least if it is a file; don't do this for directories). | |
1186 However, don't do this if we are just changing the case of the file | |
1187 name - we will end up deleting the file we are trying to rename! */ | |
1188 newname = map_win32_filename (newname, NULL); | |
1189 | |
1190 /* TODO: Use GetInformationByHandle (on NT) to ensure newname and temp | |
1191 do not refer to the same file, eg. through share aliases. */ | |
1192 if (stricmp (newname, temp) != 0 | |
1193 && (attr = GetFileAttributes (newname)) != -1 | |
1194 && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0) | |
1195 { | |
1196 _chmod (newname, 0666); | |
1197 _unlink (newname); | |
1198 } | |
1199 | |
1200 return rename (temp, newname); | |
1201 } | |
1202 #endif /* 0 */ | |
1203 | |
1204 static FILETIME utc_base_ft; | |
1205 static long double utc_base; | |
1206 static int init = 0; | |
1207 | |
1208 time_t | |
1209 convert_time (FILETIME ft) | |
1210 { | |
1211 long double ret; | |
1212 | |
1213 if (!init) | |
1214 { | |
1215 /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */ | |
1216 SYSTEMTIME st; | |
1217 | |
1218 st.wYear = 1970; | |
1219 st.wMonth = 1; | |
1220 st.wDay = 1; | |
1221 st.wHour = 0; | |
1222 st.wMinute = 0; | |
1223 st.wSecond = 0; | |
1224 st.wMilliseconds = 0; | |
1225 | |
1226 SystemTimeToFileTime (&st, &utc_base_ft); | |
1227 utc_base = (long double) utc_base_ft.dwHighDateTime | |
1228 * 4096 * 1024 * 1024 + utc_base_ft.dwLowDateTime; | |
1229 init = 1; | |
1230 } | |
1231 | |
1232 if (CompareFileTime (&ft, &utc_base_ft) < 0) | |
1233 return 0; | |
1234 | |
1235 ret = (long double) ft.dwHighDateTime * 4096 * 1024 * 1024 + ft.dwLowDateTime; | |
1236 ret -= utc_base; | |
1237 return (time_t) (ret * 1e-7); | |
1238 } | |
1239 | |
1240 #if 0 | |
1241 /* in case we ever have need of this */ | |
1242 void | |
1243 convert_from_time_t (time_t time, FILETIME * pft) | |
1244 { | |
1245 long double tmp; | |
1246 | |
1247 if (!init) | |
1248 { | |
1249 /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */ | |
1250 SYSTEMTIME st; | |
1251 | |
1252 st.wYear = 1970; | |
1253 st.wMonth = 1; | |
1254 st.wDay = 1; | |
1255 st.wHour = 0; | |
1256 st.wMinute = 0; | |
1257 st.wSecond = 0; | |
1258 st.wMilliseconds = 0; | |
1259 | |
1260 SystemTimeToFileTime (&st, &utc_base_ft); | |
1261 utc_base = (long double) utc_base_ft.dwHighDateTime | |
1262 * 4096 * 1024 * 1024 + utc_base_ft.dwLowDateTime; | |
1263 init = 1; | |
1264 } | |
1265 | |
1266 /* time in 100ns units since 1-Jan-1601 */ | |
1267 tmp = (long double) time * 1e7 + utc_base; | |
1268 pft->dwHighDateTime = (DWORD) (tmp / (4096.0 * 1024 * 1024)); | |
1269 pft->dwLowDateTime = (DWORD) (tmp - pft->dwHighDateTime); | |
1270 } | |
1271 #endif | |
1272 | |
1273 #if 0 | |
1274 /* No reason to keep this; faking inode values either by hashing or even | |
1275 using the file index from GetInformationByHandle, is not perfect and | |
1276 so by default Emacs doesn't use the inode values on Windows. | |
1277 Instead, we now determine file-truename correctly (except for | |
1278 possible drive aliasing etc). */ | |
1279 | |
1280 /* Modified version of "PJW" algorithm (see the "Dragon" compiler book). */ | |
1281 static unsigned | |
1282 hashval (const unsigned char * str) | |
1283 { | |
1284 unsigned h = 0; | |
1285 while (*str) | |
1286 { | |
1287 h = (h << 4) + *str++; | |
1288 h ^= (h >> 28); | |
1289 } | |
1290 return h; | |
1291 } | |
1292 | |
1293 /* Return the hash value of the canonical pathname, excluding the | |
1294 drive/UNC header, to get a hopefully unique inode number. */ | |
1295 static DWORD | |
1296 generate_inode_val (const char * name) | |
1297 { | |
1298 char fullname[ MAX_PATH ]; | |
1299 char * p; | |
1300 unsigned hash; | |
1301 | |
1302 /* Get the truly canonical filename, if it exists. (Note: this | |
1303 doesn't resolve aliasing due to subst commands, or recognize hard | |
1304 links. */ | |
1305 if (!win32_get_long_filename ((char *)name, fullname, MAX_PATH)) | |
1306 abort (); | |
1307 | |
1308 parse_root (fullname, &p); | |
1309 /* Normal Win32 filesystems are still case insensitive. */ | |
1310 _strlwr (p); | |
1311 return hashval (p); | |
1312 } | |
1313 | |
1314 #endif | |
1315 | |
1316 /* MSVC stat function can't cope with UNC names and has other bugs, so | |
1317 replace it with our own. This also allows us to calculate consistent | |
1318 inode values without hacks in the main Emacs code. */ | |
1319 int | |
1320 stat (const char * path, struct stat * buf) | |
1321 { | |
1322 char * name; | |
1323 WIN32_FIND_DATA wfd; | |
1324 HANDLE fh; | |
1325 DWORD fake_inode; | |
1326 int permission; | |
1327 int len; | |
1328 int rootdir = FALSE; | |
1329 | |
1330 if (path == NULL || buf == NULL) | |
1331 { | |
1332 errno = EFAULT; | |
1333 return -1; | |
1334 } | |
1335 | |
1336 name = (char *) map_win32_filename (path, &path); | |
1337 /* must be valid filename, no wild cards */ | |
1338 if (strchr (name, '*') || strchr (name, '?')) | |
1339 { | |
1340 errno = ENOENT; | |
1341 return -1; | |
1342 } | |
1343 | |
1344 /* Remove trailing directory separator, unless name is the root | |
1345 directory of a drive or UNC volume in which case ensure there | |
1346 is a trailing separator. */ | |
1347 len = strlen (name); | |
1348 rootdir = (path >= name + len - 1 | |
1349 && (IS_DIRECTORY_SEP (*path) || *path == 0)); | |
1350 name = strcpy (alloca (len + 2), name); | |
1351 | |
1352 if (rootdir) | |
1353 { | |
1354 if (!IS_DIRECTORY_SEP (name[len-1])) | |
1355 strcat (name, "\\"); | |
1356 if (GetDriveType (name) < 2) | |
1357 { | |
1358 errno = ENOENT; | |
1359 return -1; | |
1360 } | |
1361 memset (&wfd, 0, sizeof (wfd)); | |
1362 wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY; | |
1363 wfd.ftCreationTime = utc_base_ft; | |
1364 wfd.ftLastAccessTime = utc_base_ft; | |
1365 wfd.ftLastWriteTime = utc_base_ft; | |
1366 strcpy (wfd.cFileName, name); | |
1367 } | |
1368 else | |
1369 { | |
1370 if (IS_DIRECTORY_SEP (name[len-1])) | |
1371 name[len - 1] = 0; | |
1372 | |
1373 /* (This is hacky, but helps when doing file completions on | |
1374 network drives.) Optimize by using information available from | |
1375 active readdir if possible. */ | |
1376 if (dir_find_handle != INVALID_HANDLE_VALUE && | |
1377 (len = strlen (dir_pathname)), | |
1378 strnicmp (name, dir_pathname, len) == 0 && | |
1379 IS_DIRECTORY_SEP (name[len]) && | |
1380 stricmp (name + len + 1, dir_static.d_name) == 0) | |
1381 { | |
1382 /* This was the last entry returned by readdir. */ | |
1383 wfd = dir_find_data; | |
1384 } | |
1385 else | |
1386 { | |
1387 fh = FindFirstFile (name, &wfd); | |
1388 if (fh == INVALID_HANDLE_VALUE) | |
1389 { | |
1390 errno = ENOENT; | |
1391 return -1; | |
1392 } | |
1393 FindClose (fh); | |
1394 } | |
1395 } | |
1396 | |
1397 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) | |
1398 { | |
1399 buf->st_mode = _S_IFDIR; | |
1400 buf->st_nlink = 2; /* doesn't really matter */ | |
1401 fake_inode = 0; /* this doesn't either I think */ | |
1402 } | |
1403 else if (!NILP (Vmswindows_get_true_file_attributes)) | |
1404 { | |
1405 /* This is more accurate in terms of getting the correct number | |
1406 of links, but is quite slow (it is noticeable when Emacs is | |
1407 making a list of file name completions). */ | |
1408 BY_HANDLE_FILE_INFORMATION info; | |
1409 | |
1410 /* No access rights required to get info. */ | |
1411 fh = CreateFile (name, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, | |
1412 OPEN_EXISTING, 0, NULL); | |
1413 | |
1414 if (GetFileInformationByHandle (fh, &info)) | |
1415 { | |
1416 switch (GetFileType (fh)) | |
1417 { | |
1418 case FILE_TYPE_DISK: | |
1419 buf->st_mode = _S_IFREG; | |
1420 break; | |
1421 case FILE_TYPE_PIPE: | |
1422 buf->st_mode = _S_IFIFO; | |
1423 break; | |
1424 case FILE_TYPE_CHAR: | |
1425 case FILE_TYPE_UNKNOWN: | |
1426 default: | |
1427 buf->st_mode = _S_IFCHR; | |
1428 } | |
1429 buf->st_nlink = (short) info.nNumberOfLinks; | |
1430 /* Might as well use file index to fake inode values, but this | |
1431 is not guaranteed to be unique unless we keep a handle open | |
1432 all the time (even then there are situations where it is | |
1433 not unique). Reputedly, there are at most 48 bits of info | |
1434 (on NTFS, presumably less on FAT). */ | |
1435 fake_inode = info.nFileIndexLow ^ info.nFileIndexHigh; | |
1436 CloseHandle (fh); | |
1437 } | |
1438 else | |
1439 { | |
1440 errno = EACCES; | |
1441 return -1; | |
1442 } | |
1443 } | |
1444 else | |
1445 { | |
1446 /* Don't bother to make this information more accurate. */ | |
1447 buf->st_mode = _S_IFREG; | |
1448 buf->st_nlink = 1; | |
1449 fake_inode = 0; | |
1450 } | |
1451 | |
1452 #if 0 | |
1453 /* Not sure if there is any point in this. */ | |
1454 if (!NILP (Vwin32_generate_fake_inodes)) | |
1455 fake_inode = generate_inode_val (name); | |
1456 else if (fake_inode == 0) | |
1457 { | |
1458 /* For want of something better, try to make everything unique. */ | |
1459 static DWORD gen_num = 0; | |
1460 fake_inode = ++gen_num; | |
1461 } | |
1462 #endif | |
1463 | |
1464 /* #### MSVC defines _ino_t to be short; other libc's might not. */ | |
1465 buf->st_ino = (unsigned short) (fake_inode ^ (fake_inode >> 16)); | |
1466 | |
1467 /* consider files to belong to current user */ | |
1468 buf->st_uid = the_passwd.pw_uid; | |
1469 buf->st_gid = the_passwd.pw_gid; | |
1470 | |
1471 /* volume_info is set indirectly by map_win32_filename */ | |
1472 buf->st_dev = volume_info.serialnum; | |
1473 buf->st_rdev = volume_info.serialnum; | |
1474 | |
1475 | |
1476 buf->st_size = wfd.nFileSizeLow; | |
1477 | |
1478 /* Convert timestamps to Unix format. */ | |
1479 buf->st_mtime = convert_time (wfd.ftLastWriteTime); | |
1480 buf->st_atime = convert_time (wfd.ftLastAccessTime); | |
1481 if (buf->st_atime == 0) buf->st_atime = buf->st_mtime; | |
1482 buf->st_ctime = convert_time (wfd.ftCreationTime); | |
1483 if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime; | |
1484 | |
1485 /* determine rwx permissions */ | |
1486 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) | |
1487 permission = _S_IREAD; | |
1488 else | |
1489 permission = _S_IREAD | _S_IWRITE; | |
1490 | |
1491 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) | |
1492 permission |= _S_IEXEC; | |
1493 else | |
1494 { | |
1495 char * p = strrchr (name, '.'); | |
1496 if (p != NULL && | |
1497 (stricmp (p, ".exe") == 0 || | |
1498 stricmp (p, ".com") == 0 || | |
1499 stricmp (p, ".bat") == 0 || | |
1500 stricmp (p, ".cmd") == 0)) | |
1501 permission |= _S_IEXEC; | |
1502 } | |
1503 | |
1504 buf->st_mode |= permission | (permission >> 3) | (permission >> 6); | |
1505 | |
1506 return 0; | |
1507 } | |
1508 | |
1509 /* From callproc.c */ | |
1510 extern Lisp_Object Vbinary_process_input; | |
1511 extern Lisp_Object Vbinary_process_output; | |
1512 | |
1513 /* Unix pipe() has only one arg */ | |
1514 int | |
1515 sys_pipe (int * phandles) | |
1516 { | |
1517 int rc; | |
1518 unsigned flags; | |
1519 | |
1520 /* make pipe handles non-inheritable; when we spawn a child, we | |
1521 replace the relevant handle with an inheritable one. Also put | |
1522 pipes into binary mode; we will do text mode translation ourselves | |
1523 if required. */ | |
1524 rc = _pipe (phandles, 0, _O_NOINHERIT | _O_BINARY); | |
1525 | |
1526 if (rc == 0) | |
1527 { | |
1528 flags = FILE_PIPE | FILE_READ; | |
1529 if (!NILP (Vbinary_process_output)) | |
1530 flags |= FILE_BINARY; | |
1531 fd_info[phandles[0]].flags = flags; | |
1532 | |
1533 flags = FILE_PIPE | FILE_WRITE; | |
1534 if (!NILP (Vbinary_process_input)) | |
1535 flags |= FILE_BINARY; | |
1536 fd_info[phandles[1]].flags = flags; | |
1537 } | |
1538 | |
1539 return rc; | |
1540 } | |
1541 | |
1542 void | |
1543 term_ntproc (int unused) | |
1544 { | |
1545 } | |
1546 | |
1547 void | |
1548 init_ntproc () | |
1549 { | |
1550 /* Initial preparation for subprocess support: replace our standard | |
1551 handles with non-inheritable versions. */ | |
1552 { | |
1553 HANDLE parent; | |
1554 HANDLE stdin_save = INVALID_HANDLE_VALUE; | |
1555 HANDLE stdout_save = INVALID_HANDLE_VALUE; | |
1556 HANDLE stderr_save = INVALID_HANDLE_VALUE; | |
1557 | |
1558 parent = GetCurrentProcess (); | |
1559 | |
1560 /* ignore errors when duplicating and closing; typically the | |
1561 handles will be invalid when running as a gui program. */ | |
1562 DuplicateHandle (parent, | |
1563 GetStdHandle (STD_INPUT_HANDLE), | |
1564 parent, | |
1565 &stdin_save, | |
1566 0, | |
1567 FALSE, | |
1568 DUPLICATE_SAME_ACCESS); | |
1569 | |
1570 DuplicateHandle (parent, | |
1571 GetStdHandle (STD_OUTPUT_HANDLE), | |
1572 parent, | |
1573 &stdout_save, | |
1574 0, | |
1575 FALSE, | |
1576 DUPLICATE_SAME_ACCESS); | |
1577 | |
1578 DuplicateHandle (parent, | |
1579 GetStdHandle (STD_ERROR_HANDLE), | |
1580 parent, | |
1581 &stderr_save, | |
1582 0, | |
1583 FALSE, | |
1584 DUPLICATE_SAME_ACCESS); | |
1585 | |
1586 fclose (stdin); | |
1587 fclose (stdout); | |
1588 fclose (stderr); | |
1589 | |
1590 if (stdin_save != INVALID_HANDLE_VALUE) | |
1591 _open_osfhandle ((long) stdin_save, O_TEXT); | |
1592 else | |
1593 _open ("nul", O_TEXT | O_NOINHERIT | O_RDONLY); | |
1594 _fdopen (0, "r"); | |
1595 | |
1596 if (stdout_save != INVALID_HANDLE_VALUE) | |
1597 _open_osfhandle ((long) stdout_save, O_TEXT); | |
1598 else | |
1599 _open ("nul", O_TEXT | O_NOINHERIT | O_WRONLY); | |
1600 _fdopen (1, "w"); | |
1601 | |
1602 if (stderr_save != INVALID_HANDLE_VALUE) | |
1603 _open_osfhandle ((long) stderr_save, O_TEXT); | |
1604 else | |
1605 _open ("nul", O_TEXT | O_NOINHERIT | O_WRONLY); | |
1606 _fdopen (2, "w"); | |
1607 } | |
1608 | |
1609 /* unfortunately, atexit depends on implementation of malloc */ | |
1610 /* atexit (term_ntproc); */ | |
1611 signal (SIGABRT, term_ntproc); | |
1612 | |
1613 /* determine which drives are fixed, for GetCachedVolumeInformation */ | |
1614 { | |
1615 /* GetDriveType must have trailing backslash. */ | |
1616 char drive[] = "A:\\"; | |
1617 | |
1618 /* Loop over all possible drive letters */ | |
1619 while ( *drive <= 'Z' ) | |
1620 { | |
1621 /* Record if this drive letter refers to a fixed drive. */ | |
1622 fixed_drives[ DRIVE_INDEX (*drive) ] = | |
1623 (GetDriveType (drive) == DRIVE_FIXED); | |
1624 | |
1625 (*drive)++; | |
1626 } | |
1627 } | |
1628 } | |
1629 #ifndef HAVE_TTY | |
1630 Lisp_Object | |
1631 tty_semi_canonicalize_console_connection (Lisp_Object connection, | |
1632 Error_behavior errb) | |
1633 { | |
1634 return Vstdio_str; | |
1635 } | |
1636 | |
1637 Lisp_Object | |
1638 tty_canonicalize_console_connection (Lisp_Object connection, | |
1639 Error_behavior errb) | |
1640 { | |
1641 return Vstdio_str; | |
1642 } | |
1643 | |
1644 Lisp_Object | |
1645 tty_semi_canonicalize_device_connection (Lisp_Object connection, | |
1646 Error_behavior errb) | |
1647 { | |
1648 return Vstdio_str; | |
1649 } | |
1650 | |
1651 Lisp_Object | |
1652 tty_canonicalize_device_connection (Lisp_Object connection, | |
1653 Error_behavior errb) | |
1654 { | |
1655 return Vstdio_str; | |
1656 } | |
1657 #endif | |
1658 | |
1659 /*--------------------------------------------------------------------*/ | |
1660 /* Signal support */ | |
1661 /*--------------------------------------------------------------------*/ | |
1662 | |
1663 /* We need MS-defined signal and raise here */ | |
1664 #undef signal | |
1665 #undef raise | |
1666 | |
1667 #define sigmask(nsig) (1U << nsig) | |
1668 | |
1669 /* We can support as many signals as fit into word */ | |
1670 #define SIG_MAX 32 | |
1671 | |
1672 /* Signal handlers. Initial value = 0 = SIG_DFL */ | |
1673 static void (__cdecl *signal_handlers[SIG_MAX])(int) = {0}; | |
1674 | |
1675 /* Signal block mask: bit set to 1 means blocked */ | |
1676 unsigned signal_block_mask = 0; | |
1677 | |
1678 /* Signal pending mask: bit set to 1 means sig is pending */ | |
1679 unsigned signal_pending_mask = 0; | |
1680 | |
1681 msw_sighandler msw_sigset (int nsig, msw_sighandler handler) | |
1682 { | |
1683 /* We delegate some signals to the system function */ | |
1684 if (nsig == SIGFPE || nsig == SIGABRT || nsig == SIGINT) | |
1685 return signal (nsig, handler); | |
1686 | |
1687 if (nsig < 0 || nsig > SIG_MAX) | |
1688 { | |
1689 errno = EINVAL; | |
1690 return NULL; | |
1691 } | |
1692 | |
1693 /* Store handler ptr */ | |
1694 { | |
1695 msw_sighandler old_handler = signal_handlers[nsig]; | |
1696 signal_handlers[nsig] = handler; | |
1697 return old_handler; | |
1698 } | |
1699 } | |
1700 | |
1701 int msw_sighold (int nsig) | |
1702 { | |
1703 if (nsig < 0 || nsig > SIG_MAX) | |
1704 return errno = EINVAL; | |
1705 | |
1706 signal_block_mask |= sigmask(nsig); | |
1707 return 0; | |
1708 } | |
1709 | |
1710 int msw_sigrelse (int nsig) | |
1711 { | |
1712 if (nsig < 0 || nsig > SIG_MAX) | |
1713 return errno = EINVAL; | |
1714 | |
1715 signal_block_mask &= ~sigmask(nsig); | |
1716 | |
1717 if (signal_pending_mask & sigmask(nsig)) | |
1718 msw_raise (nsig); | |
1719 | |
1720 return 0; | |
1721 } | |
1722 | |
1723 int msw_sigpause (int nsig) | |
1724 { | |
1725 /* This is currently not called, because the only | |
1726 call to sigpause inside XEmacs is with SIGCHLD | |
1727 parameter. Just in case, we put an assert here, | |
1728 so anyone who will add a call to sigpause will | |
1729 be surprised (or surprise someone else...) */ | |
1730 assert (0); | |
1731 return 0; | |
1732 } | |
1733 | |
1734 int msw_raise (int nsig) | |
1735 { | |
1736 /* We delegate some raises to the system routine */ | |
1737 if (nsig == SIGFPE || nsig == SIGABRT || nsig == SIGINT) | |
1738 return raise (nsig); | |
1739 | |
1740 if (nsig < 0 || nsig > SIG_MAX) | |
1741 return errno = EINVAL; | |
1742 | |
1743 /* If the signal is blocked, remember to issue later */ | |
1744 if (signal_block_mask & sigmask(nsig)) | |
1745 { | |
1746 signal_pending_mask |= sigmask(nsig); | |
1747 return 0; | |
1748 } | |
1749 | |
1750 if (signal_handlers[nsig] == SIG_IGN) | |
1751 return 0; | |
1752 | |
1753 if (signal_handlers[nsig] != SIG_DFL) | |
1754 { | |
1755 (*signal_handlers[nsig])(nsig); | |
1756 return 0; | |
1757 } | |
1758 | |
1759 /* Default signal actions */ | |
1760 if (nsig == SIGALRM || nsig == SIGPROF) | |
1761 exit (3); | |
1762 | |
1763 /* Other signals are ignored by default */ | |
1764 return 0; | |
1765 } | |
1766 | |
1767 /*--------------------------------------------------------------------*/ | |
1768 /* Async timers */ | |
1769 /*--------------------------------------------------------------------*/ | |
1770 | |
1771 /* We emulate two timers, one for SIGALRM, another for SIGPROF. | |
1772 | |
1773 itimerproc() function has an implementation limitation: it does | |
1774 not allow to set *both* interval and period. If an attempt is | |
1775 made to set both, and then they are unequal, the function | |
1776 asserts. | |
1777 | |
1778 Minimum timer resolution on Win32 systems varies, and is greater | |
1779 than or equal than 1 ms. The resolution is always wrapped not to | |
1780 attempt to get below the system defined limit. | |
1781 */ | |
1782 | |
1783 /* Timer precision, denominator of one fraction: for 100 ms | |
1784 interval, request 10 ms precision | |
1785 */ | |
1786 const int timer_prec = 10; | |
1787 | |
1788 /* Last itimervals, as set by calls to setitimer */ | |
1789 static struct itimerval it_alarm; | |
1790 static struct itimerval it_prof; | |
1791 | |
1792 /* Timer IDs as returned by MM */ | |
1793 MMRESULT tid_alarm = 0; | |
1794 MMRESULT tid_prof = 0; | |
1795 | |
1796 static void CALLBACK timer_proc (UINT uID, UINT uMsg, DWORD dwUser, | |
1797 DWORD dw1, DWORD dw2) | |
1798 { | |
1799 /* Just raise a signal indicated by dwUser parameter */ | |
1800 msw_raise (dwUser); | |
1801 } | |
1802 | |
1803 /* Divide time in ms specified by IT by DENOM. Return 1 ms | |
1804 if division results in zero */ | |
1805 static UINT period (const struct itimerval* it, UINT denom) | |
1806 { | |
1807 static TIMECAPS time_caps; | |
1808 | |
1809 UINT res; | |
1810 const struct timeval* tv = | |
1811 (it->it_value.tv_sec == 0 && it->it_value.tv_usec == 0) | |
1812 ? &it->it_interval : &it->it_value; | |
1813 | |
1814 /* Zero means stop timer */ | |
1815 if (tv->tv_sec == 0 && tv->tv_usec == 0) | |
1816 return 0; | |
1817 | |
1818 /* Convert to ms and divide by denom */ | |
1819 res = (tv->tv_sec * 1000 + (tv->tv_usec + 500) / 1000) / denom; | |
1820 | |
1821 /* Converge to minimum timer resolution */ | |
1822 if (time_caps.wPeriodMin == 0) | |
1823 timeGetDevCaps (&time_caps, sizeof(time_caps)); | |
1824 | |
1825 if (res < time_caps.wPeriodMin) | |
1826 res = time_caps.wPeriodMin; | |
1827 | |
1828 return res; | |
1829 } | |
1830 | |
1831 static int setitimer_helper (const struct itimerval* itnew, | |
1832 struct itimerval* itold, struct itimerval* itcurrent, | |
1833 MMRESULT* tid, DWORD sigkind) | |
1834 { | |
1835 UINT delay, resolution, event_type; | |
1836 | |
1837 /* First stop the old timer */ | |
1838 if (*tid) | |
1839 { | |
1840 timeKillEvent (*tid); | |
1841 timeEndPeriod (period (itcurrent, timer_prec)); | |
1842 *tid = 0; | |
1843 } | |
1844 | |
1845 /* Return old itimerval if requested */ | |
1846 if (itold) | |
1847 *itold = *itcurrent; | |
1848 | |
1849 *itcurrent = *itnew; | |
1850 | |
1851 /* Determine if to start new timer */ | |
1852 delay = period (itnew, 1); | |
1853 if (delay) | |
1854 { | |
1855 resolution = period (itnew, timer_prec); | |
1856 event_type = (itnew->it_value.tv_sec == 0 && itnew->it_value.tv_usec == 0) | |
1857 ? TIME_ONESHOT : TIME_PERIODIC; | |
1858 timeBeginPeriod (resolution); | |
1859 *tid = timeSetEvent (delay, resolution, timer_proc, sigkind, event_type); | |
1860 } | |
1861 | |
1862 return !delay || *tid; | |
1863 } | |
1864 | |
1865 int setitimer (int kind, const struct itimerval* itnew, | |
1866 struct itimerval* itold) | |
1867 { | |
1868 /* In this version, both interval and value are allowed | |
1869 only if they are equal. */ | |
1870 assert ((itnew->it_value.tv_sec == 0 && itnew->it_value.tv_usec == 0) | |
1871 || (itnew->it_interval.tv_sec == 0 && itnew->it_interval.tv_usec == 0) | |
1872 || (itnew->it_value.tv_sec == itnew->it_interval.tv_sec && | |
1873 itnew->it_value.tv_usec == itnew->it_interval.tv_usec)); | |
1874 | |
1875 if (kind == ITIMER_REAL) | |
1876 return setitimer_helper (itnew, itold, &it_alarm, &tid_alarm, SIGALRM); | |
1877 else if (kind == ITIMER_PROF) | |
1878 return setitimer_helper (itnew, itold, &it_prof, &tid_prof, SIGPROF); | |
1879 else | |
1880 return errno = EINVAL; | |
1881 } | |
1882 | |
1883 int | |
1884 open_input_file (file_data *p_file, CONST char *filename) | |
1885 { | |
1886 HANDLE file; | |
1887 HANDLE file_mapping; | |
1888 void *file_base; | |
1889 DWORD size, upper_size; | |
1890 | |
1891 file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL, | |
1892 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); | |
1893 if (file == INVALID_HANDLE_VALUE) | |
1894 return FALSE; | |
1895 | |
1896 size = GetFileSize (file, &upper_size); | |
1897 file_mapping = CreateFileMapping (file, NULL, PAGE_READONLY, | |
1898 0, size, NULL); | |
1899 if (!file_mapping) | |
1900 return FALSE; | |
1901 | |
1902 file_base = MapViewOfFile (file_mapping, FILE_MAP_READ, 0, 0, size); | |
1903 if (file_base == 0) | |
1904 return FALSE; | |
1905 | |
1906 p_file->name = (char*)filename; | |
1907 p_file->size = size; | |
1908 p_file->file = file; | |
1909 p_file->file_mapping = file_mapping; | |
1910 p_file->file_base = file_base; | |
1911 | |
1912 return TRUE; | |
1913 } | |
1914 | |
1915 /* Close the system structures associated with the given file. */ | |
1916 void | |
1917 close_file_data (file_data *p_file) | |
1918 { | |
1919 UnmapViewOfFile (p_file->file_base); | |
1920 CloseHandle (p_file->file_mapping); | |
1921 CloseHandle (p_file->file); | |
1922 } | |
1923 | |
1924 /* end of nt.c */ |