Mercurial > hg > xemacs-beta
annotate src/nt.c @ 4539:061e030e3270
Fix some bugs in load-history construction, built-in symbol file names.
lib-src/ChangeLog addition:
2008-12-27 Aidan Kehoe <kehoea@parhasard.net>
* make-docfile.c (main): Allow more than one -d argument, followed
by a directory to change to.
(put_filename): Don't strip directory information; with previous
change, allows retrieval of Lisp function and variable origin
files from #'built-in-symbol-file relative to lisp-directory.
(scan_lisp_file): Don't add an extraneous newline after the file
name, put_filename has added the newline already.
lisp/ChangeLog addition:
2008-12-27 Aidan Kehoe <kehoea@parhasard.net>
* loadup.el (load-history):
Add the contents of current-load-list to load-history before
clearing it. Move the variable declarations earlier in the file to
a format understood by make-docfile.c.
* custom.el (custom-declare-variable): Add the variable's symbol
to the current file's load history entry correctly, don't use a
cons. Eliminate a comment that we don't need to worry about, we
don't need to check the `initialized' C variable in Lisp.
* bytecomp.el (byte-compile-output-file-form):
Merge Andreas Schwab's pre-GPLv3 GNU change of 19970831 here;
treat #'custom-declare-variable correctly, generating the
docstrings in a format understood by make-docfile.c.
* loadhist.el (symbol-file): Correct behaviour for checking
autoloaded macros and functions when supplied with a TYPE
argument. Accept fully-qualified paths from
#'built-in-symbol-file; if a path is not fully-qualified, return
it relative to lisp-directory if the filename corresponds to a
Lisp file, and relative to (concat source-directory "/src/")
otherwise.
* make-docfile.el (preloaded-file-list):
Rationalise some let bindings a little. Use the "-d" argument to
make-docfile.c to supply Lisp paths relative to lisp-directory,
not absolutely. Add in loadup.el explicitly to the list of files
to be processed by make-docfile.c--it doesn't make sense to add it
to preloaded-file-list, since that is used for purposes of
byte-compilation too.
src/ChangeLog addition:
2008-12-27 Aidan Kehoe <kehoea@parhasard.net>
* doc.c (Fbuilt_in_symbol_file):
Return a subr's filename immediately if we've found it. Check for
compiled function and compiled macro docstrings in DOC too, and
return them if they exist.
The branch of the if statement focused on functions may have
executed, but we may still want to check variable bindings; an
else clause isn't appropriate.
author | Aidan Kehoe <kehoea@parhasard.net> |
---|---|
date | Sat, 27 Dec 2008 14:05:50 +0000 |
parents | 866b84b7c97e |
children | 5bbff3553494 |
rev | line source |
---|---|
771 | 1 /* Utility and Unix shadow routines under MS Windows (WIN32_NATIVE defined). |
428 | 2 Copyright (C) 1994, 1995 Free Software Foundation, Inc. |
2957 | 3 Copyright (C) 2000, 2001, 2002, 2004, 2005 Ben Wing. |
428 | 4 |
5 This file is part of XEmacs. | |
6 | |
7 XEmacs is free software; you can redistribute it and/or modify it | |
8 under the terms of the GNU General Public License as published by the | |
9 Free Software Foundation; either version 2, or (at your option) any | |
10 later version. | |
11 | |
12 XEmacs is distributed in the hope that it will be useful, but WITHOUT | |
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
15 for more details. | |
16 | |
17 You should have received a copy of the GNU General Public License | |
18 along with XEmacs; see the file COPYING. If not, write to the Free | |
19 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | |
20 02111-1307, USA. | |
21 | |
771 | 22 */ |
428 | 23 |
771 | 24 /* Authorship: |
428 | 25 |
771 | 26 Geoff Voelker (voelker@cs.washington.edu) 7-29-94 |
27 Adapted for XEmacs by David Hobley <david@spook-le0.cia.com.au> | |
28 Sync'ed with Emacs 19.34.6 by Marc Paquette <marcpa@cam.org> | |
29 (Note: Sync messages from Marc Paquette may indicate | |
30 incomplete synching, so beware.) | |
31 Synched (completely!) with Emacs 20.6 by Ben Wing, 6-23-00. | |
32 Largely rewritten by Ben Wing for XEmacs Mule support. | |
2526 | 33 Synched (completely!) with Emacs 21.0.103 by Ben Wing, 6-13-01. |
771 | 34 */ |
35 | |
36 /* This file Mule-ized by Ben Wing, 6-23-00. */ | |
428 | 37 |
38 #include <config.h> | |
39 #include "lisp.h" | |
40 | |
592 | 41 #include "buffer.h" |
872 | 42 #include "process.h" |
592 | 43 |
859 | 44 #include "sysdir.h" |
45 #include "sysfile.h" | |
428 | 46 #include "sysproc.h" |
442 | 47 #include "syspwd.h" |
859 | 48 #include "syssignal.h" |
49 #include "systime.h" | |
428 | 50 |
442 | 51 #include "syswindows.h" |
428 | 52 |
771 | 53 /* Control whether stat() attempts to determine file type and link count |
54 exactly, at the expense of slower operation. Since true hard links | |
55 are supported on NTFS volumes, this is only relevant on NT. */ | |
56 Lisp_Object Vmswindows_get_true_file_attributes; | |
428 | 57 |
771 | 58 /* Vmswindows_generate_fake_inodes; deleted */ |
59 | |
60 Fixnum mswindows_fake_unix_uid; | |
428 | 61 |
62 /* Emulate getpwuid, getpwnam and others. */ | |
63 | |
771 | 64 static struct passwd the_passwd = |
428 | 65 { |
771 | 66 "", |
67 "", | |
428 | 68 0, |
69 0, | |
70 0, | |
771 | 71 "", |
72 "", | |
73 "", | |
428 | 74 }; |
75 | |
76 uid_t | |
442 | 77 getuid (void) |
440 | 78 { |
771 | 79 return mswindows_fake_unix_uid; |
428 | 80 } |
81 | |
82 uid_t | |
442 | 83 geteuid (void) |
428 | 84 { |
771 | 85 /* Emacs 20.6 says: [[I could imagine arguing for checking to see |
86 whether the user is in the Administrators group and returning a | |
87 UID of 0 for that case, but I don't know how wise that would be | |
88 in the long run.]] */ | |
89 return mswindows_fake_unix_uid; | |
428 | 90 } |
91 | |
92 gid_t | |
442 | 93 getgid (void) |
428 | 94 { |
95 return the_passwd.pw_gid; | |
96 } | |
97 | |
98 gid_t | |
442 | 99 getegid (void) |
428 | 100 { |
101 return getgid (); | |
102 } | |
103 | |
104 struct passwd * | |
105 getpwuid (uid_t uid) | |
106 { | |
771 | 107 if (uid == mswindows_fake_unix_uid) |
440 | 108 { |
109 the_passwd.pw_gid = the_passwd.pw_uid = uid; | |
110 return &the_passwd; | |
111 } | |
112 else | |
113 return NULL; | |
428 | 114 } |
115 | |
116 struct passwd * | |
867 | 117 getpwnam (const Ibyte *name) |
428 | 118 { |
119 struct passwd *pw; | |
120 | |
121 pw = getpwuid (getuid ()); | |
122 if (!pw) | |
123 return pw; | |
124 | |
1204 | 125 if (qxestrcasecmp_i18n (name, (Ibyte *) pw->pw_name)) |
428 | 126 return NULL; |
127 | |
128 return pw; | |
129 } | |
130 | |
771 | 131 static void |
442 | 132 init_user_info (void) |
428 | 133 { |
440 | 134 /* This code is pretty much of ad hoc nature. There is no unix-like |
135 UIDs under Windows NT. There is no concept of root user, because | |
136 all security is ACL-based. Instead, let's use a simple variable, | |
137 nt-fake-unix-uid, which would allow the user to have a uid of | |
138 choice. --kkm, 02/03/2000 */ | |
139 #if 0 | |
428 | 140 /* Find the user's real name by opening the process token and |
141 looking up the name associated with the user-sid in that token. | |
142 | |
143 Use the relative portion of the identifier authority value from | |
144 the user-sid as the user id value (same for group id using the | |
145 primary group sid from the process token). */ | |
146 | |
771 | 147 TOKEN_USER sidinfo; |
148 Extbyte name[256], domain[256]; | |
149 Charcount length = sizeof (name) / XETCHAR_SIZE; | |
150 Charcount dlength = sizeof (domain) / XETCHAR_SIZE; | |
151 DWORD trash; | |
152 HANDLE token = NULL; | |
153 SID_NAME_USE user_type; | |
428 | 154 |
155 if (OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY, &token) | |
771 | 156 && GetTokenInformation (token, TokenUser, &sidinfo, sizeof (sidinfo), |
157 &trash) | |
158 && qxeLookupAccountSid (NULL, sidinfo.User.Sid, name, &length, | |
159 domain, &dlength, &user_type)) | |
428 | 160 { |
771 | 161 TSTR_TO_C_STRING_MALLOC (name, the_passwd.pw_name); |
428 | 162 /* Determine a reasonable uid value. */ |
771 | 163 if (qxestrcasecmp ("administrator", the_passwd.pw_name) == 0) |
428 | 164 { |
165 the_passwd.pw_uid = 0; | |
166 the_passwd.pw_gid = 0; | |
167 } | |
168 else | |
169 { | |
170 SID_IDENTIFIER_AUTHORITY * pSIA; | |
771 | 171 TOKEN_PRIMARY_GROUP group; |
428 | 172 |
771 | 173 pSIA = GetSidIdentifierAuthority (sidinfo.User.Sid); |
428 | 174 /* I believe the relative portion is the last 4 bytes (of 6) |
175 with msb first. */ | |
176 the_passwd.pw_uid = ((pSIA->Value[2] << 24) + | |
177 (pSIA->Value[3] << 16) + | |
178 (pSIA->Value[4] << 8) + | |
179 (pSIA->Value[5] << 0)); | |
180 /* restrict to conventional uid range for normal users */ | |
181 the_passwd.pw_uid = the_passwd.pw_uid % 60001; | |
182 | |
183 /* Get group id */ | |
184 if (GetTokenInformation (token, TokenPrimaryGroup, | |
771 | 185 &group, sizeof (group), &trash)) |
428 | 186 { |
187 SID_IDENTIFIER_AUTHORITY * pSIA; | |
188 | |
771 | 189 pSIA = GetSidIdentifierAuthority (group.PrimaryGroup); |
428 | 190 the_passwd.pw_gid = ((pSIA->Value[2] << 24) + |
191 (pSIA->Value[3] << 16) + | |
192 (pSIA->Value[4] << 8) + | |
193 (pSIA->Value[5] << 0)); | |
194 /* I don't know if this is necessary, but for safety... */ | |
195 the_passwd.pw_gid = the_passwd.pw_gid % 60001; | |
196 } | |
197 else | |
198 the_passwd.pw_gid = the_passwd.pw_uid; | |
199 } | |
200 } | |
201 /* If security calls are not supported (presumably because we | |
202 are running under Windows 95), fallback to this. */ | |
771 | 203 else if (qxeGetUserName (name, &length)) |
428 | 204 { |
771 | 205 TSTR_TO_C_STRING_MALLOC (name, the_passwd.pw_name); |
206 if (qxestrcasecmp ("administrator", the_passwd.pw_name) == 0) | |
428 | 207 the_passwd.pw_uid = 0; |
208 else | |
209 the_passwd.pw_uid = 123; | |
210 the_passwd.pw_gid = the_passwd.pw_uid; | |
211 } | |
212 else | |
213 { | |
771 | 214 the_passwd.pw_name = "unknown"; |
428 | 215 the_passwd.pw_uid = 123; |
216 the_passwd.pw_gid = 123; | |
217 } | |
218 | |
440 | 219 if (token) |
220 CloseHandle (token); | |
221 #else | |
222 /* Obtain only logon id here, uid part is moved to getuid */ | |
771 | 223 DWORD length = UNLEN + 1; |
224 Extbyte name[MAX_XETCHAR_SIZE * (UNLEN + 1)]; | |
225 if (qxeGetUserName (name, &length)) | |
226 TSTR_TO_C_STRING_MALLOC (name, the_passwd.pw_name); | |
440 | 227 else |
771 | 228 the_passwd.pw_name = "unknown"; |
440 | 229 #endif |
230 | |
771 | 231 #if 0 |
428 | 232 /* Ensure HOME and SHELL are defined. */ |
233 /* | |
234 * With XEmacs, setting $HOME is deprecated. | |
235 */ | |
771 | 236 if (egetenv ("HOME") == NULL) |
237 eputenv ("HOME=c:/"); | |
428 | 238 #endif |
239 | |
611 | 240 /* Set dir from environment variables. */ |
771 | 241 the_passwd.pw_dir = (char *) qxestrdup (get_home_directory ()); |
611 | 242 /* We used to set pw_shell here, but the order is wrong (SHELL gets |
853 | 243 initted in process.c, called later in the init process) and pw_shell |
611 | 244 is not used anywhere. */ |
428 | 245 } |
246 | |
771 | 247 /* Parse the root part of file name, if present. Return length and |
867 | 248 optionally store pointer to Ibyte after root. */ |
771 | 249 static Bytecount |
867 | 250 parse_root (Ibyte *name, Ibyte **pPath) |
428 | 251 { |
867 | 252 Ibyte *start = name; |
428 | 253 |
254 if (name == NULL) | |
255 return 0; | |
256 | |
257 /* find the root name of the volume if given */ | |
258 if (isalpha (name[0]) && name[1] == ':') | |
259 { | |
260 /* skip past drive specifier */ | |
261 name += 2; | |
262 if (IS_DIRECTORY_SEP (name[0])) | |
263 name++; | |
264 } | |
265 else if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1])) | |
266 { | |
267 int slashes = 2; | |
268 name += 2; | |
269 do | |
270 { | |
271 if (IS_DIRECTORY_SEP (*name) && --slashes == 0) | |
272 break; | |
273 name++; | |
274 } | |
771 | 275 while (*name); |
428 | 276 if (IS_DIRECTORY_SEP (name[0])) |
277 name++; | |
278 } | |
279 | |
280 if (pPath) | |
281 *pPath = name; | |
282 | |
283 return name - start; | |
284 } | |
285 | |
286 /* Get long base name for name; name is assumed to be absolute. */ | |
867 | 287 static Ibyte * |
288 get_long_basename (Ibyte *name) | |
428 | 289 { |
771 | 290 WIN32_FIND_DATAW find_data; |
428 | 291 HANDLE dir_handle; |
771 | 292 Extbyte *nameext; |
428 | 293 |
771 | 294 /* must be valid filename, no wild cards or other invalid characters */ |
295 if (qxestrpbrk (name, "*?|<>\"")) | |
296 return 0; | |
428 | 297 |
2526 | 298 PATHNAME_CONVERT_OUT (name, nameext); |
771 | 299 dir_handle = qxeFindFirstFile (nameext, &find_data); |
428 | 300 if (dir_handle != INVALID_HANDLE_VALUE) |
301 { | |
867 | 302 Ibyte *fileint; |
771 | 303 |
304 TSTR_TO_C_STRING_MALLOC (find_data.cFileName, fileint); | |
428 | 305 FindClose (dir_handle); |
771 | 306 return fileint; |
428 | 307 } |
771 | 308 return 0; |
428 | 309 } |
310 | |
311 /* Get long name for file, if possible (assumed to be absolute). */ | |
867 | 312 Ibyte * |
313 mswindows_get_long_filename (Ibyte *name) | |
428 | 314 { |
867 | 315 Ibyte *full = mswindows_canonicalize_filename (name); |
316 Ibyte *p; | |
317 Ibyte *q; | |
771 | 318 DECLARE_EISTRING (o); |
319 Bytecount len; | |
428 | 320 |
321 /* Copy root part verbatim. */ | |
322 len = parse_root (full, &p); | |
771 | 323 eicpy_raw (o, full, len); |
428 | 324 |
771 | 325 while (p != NULL && *p) |
428 | 326 { |
867 | 327 Ibyte *component; |
771 | 328 |
428 | 329 q = p; |
771 | 330 p = qxestrchr (q, '\\'); |
428 | 331 if (p) *p = '\0'; |
771 | 332 component = get_long_basename (full); |
333 if (component) | |
428 | 334 { |
771 | 335 eicat_rawz (o, component); |
428 | 336 if (p != NULL) |
337 { | |
338 *p++ = '\\'; | |
771 | 339 eicat_ch (o, '\\'); |
428 | 340 } |
1726 | 341 xfree (component, Ibyte *); |
428 | 342 } |
343 else | |
771 | 344 { |
1726 | 345 xfree (full, Ibyte *); |
771 | 346 return 0; |
347 } | |
428 | 348 } |
349 | |
1726 | 350 xfree (full, Ibyte *); |
771 | 351 return eicpyout_malloc (o, 0); |
428 | 352 } |
353 | |
771 | 354 static int |
867 | 355 is_unc_volume (const Ibyte *filename) |
771 | 356 { |
867 | 357 const Ibyte *ptr = filename; |
428 | 358 |
771 | 359 if (!IS_DIRECTORY_SEP (ptr[0]) || !IS_DIRECTORY_SEP (ptr[1]) || !ptr[2]) |
360 return 0; | |
361 | |
362 if (qxestrpbrk (ptr + 2, "*?|<>\"\\/")) | |
363 return 0; | |
364 | |
365 return 1; | |
428 | 366 } |
367 | |
771 | 368 /* NOTE: Value returned is still in external format. Callers need to |
369 convert. */ | |
707 | 370 #define REG_ROOT "SOFTWARE\\XEmacs\\XEmacs" |
428 | 371 |
771 | 372 static LPBYTE |
867 | 373 nt_get_resource (Ibyte *key, LPDWORD lpdwtype) |
428 | 374 { |
375 LPBYTE lpvalue; | |
376 HKEY hrootkey = NULL; | |
377 DWORD cbData; | |
771 | 378 Extbyte *keyext; |
379 | |
380 C_STRING_TO_TSTR (key, keyext); | |
428 | 381 |
382 /* Check both the current user and the local machine to see if | |
383 we have any resources. */ | |
384 | |
771 | 385 if (qxeRegOpenKeyEx (HKEY_CURRENT_USER, XETEXT (REG_ROOT), 0, KEY_READ, |
386 &hrootkey) == ERROR_SUCCESS) | |
428 | 387 { |
388 lpvalue = NULL; | |
389 | |
771 | 390 if (qxeRegQueryValueEx (hrootkey, keyext, NULL, NULL, NULL, |
391 &cbData) == ERROR_SUCCESS | |
2367 | 392 && (lpvalue = xnew_array (BYTE, cbData)) != NULL |
771 | 393 && qxeRegQueryValueEx (hrootkey, keyext, NULL, lpdwtype, lpvalue, |
394 &cbData) == ERROR_SUCCESS) | |
395 return (lpvalue); | |
428 | 396 |
1726 | 397 if (lpvalue) |
398 xfree (lpvalue, LPBYTE); | |
428 | 399 |
400 RegCloseKey (hrootkey); | |
401 } | |
402 | |
771 | 403 if (qxeRegOpenKeyEx (HKEY_LOCAL_MACHINE, XETEXT (REG_ROOT), 0, KEY_READ, |
404 &hrootkey) == ERROR_SUCCESS) | |
428 | 405 { |
406 lpvalue = NULL; | |
407 | |
771 | 408 if (qxeRegQueryValueEx (hrootkey, keyext, NULL, NULL, NULL, |
409 &cbData) == ERROR_SUCCESS && | |
2367 | 410 (lpvalue = xnew_array (BYTE, cbData)) != NULL && |
771 | 411 qxeRegQueryValueEx (hrootkey, keyext, NULL, lpdwtype, lpvalue, |
412 &cbData) == ERROR_SUCCESS) | |
413 return (lpvalue); | |
428 | 414 |
1726 | 415 if (lpvalue) |
416 xfree (lpvalue, LPBYTE); | |
428 | 417 |
418 RegCloseKey (hrootkey); | |
419 } | |
420 | |
421 return (NULL); | |
422 } | |
423 | |
424 void | |
814 | 425 init_mswindows_environment (void) |
428 | 426 { |
427 /* Check for environment variables and use registry if they don't exist */ | |
771 | 428 /* Emacs 20.6 sets default values for these; not necessary here because |
429 we already supply them. (except SHELL, which is set in init_user_info().) | |
430 Emacs 20.6 messes with TMPDIR; not necessary here. */ | |
428 | 431 { |
432 int i; | |
433 LPBYTE lpval; | |
434 DWORD dwType; | |
435 | |
2367 | 436 static Ascbyte *env_vars[] = |
428 | 437 { |
438 "HOME", | |
439 "EMACSLOADPATH", | |
440 "EMACSDEBUGPATHS", | |
441 "SHELL", | |
442 "CMDPROXY", | |
443 "EMACSDATA", | |
444 "EMACSPATH", | |
445 "EMACSPACKAGEPATH", | |
3179 | 446 "EMACSEARLYPACKAGES", |
447 "EMACSLATEPACKAGES", | |
448 "EMACSLASTPACKAGES", | |
771 | 449 "EMACSLOCKMETHOD", |
428 | 450 "INFOPATH" |
451 }; | |
771 | 452 #if defined (HEAP_IN_DATA) && !defined (PDUMP) |
430 | 453 cache_system_info (); |
454 #endif | |
771 | 455 |
456 #if 0 /* FSF 21.1 */ | |
457 /* !!#### i think i already do the equivalent elsewhere. | |
458 delete when i'm sure i do. | |
459 (but maybe i should be playing with LANG when the user changes | |
460 the locale, so that subprocesses get it right.) */ | |
461 /* Get default locale info and use it for LANG. */ | |
462 if (GetLocaleInfo (LOCALE_USER_DEFAULT, | |
463 LOCALE_SABBREVLANGNAME | LOCALE_USE_CP_ACP, | |
464 locale_name, sizeof (locale_name))) | |
465 { | |
466 for (i = 0; i < (sizeof (env_vars) / sizeof (env_vars[0])); i++) | |
467 { | |
468 if (strcmp (env_vars[i].name, "LANG") == 0) | |
469 { | |
470 env_vars[i].def_value = locale_name; | |
471 break; | |
472 } | |
473 } | |
474 } | |
475 #endif /* 0 */ | |
476 | |
428 | 477 for (i = 0; i < countof (env_vars); i++) |
478 { | |
771 | 479 if (!egetenv (env_vars[i]) && |
1204 | 480 (lpval = nt_get_resource ((Ibyte *) env_vars[i], &dwType)) != NULL) |
428 | 481 { |
482 if (dwType == REG_EXPAND_SZ) | |
483 { | |
771 | 484 Extbyte *buf = NULL; |
867 | 485 Ibyte *envval; |
771 | 486 Charcount cch; |
428 | 487 |
771 | 488 cch = qxeExpandEnvironmentStrings ((Extbyte *) lpval, buf, 0); |
2367 | 489 buf = alloca_extbytes (cch * XETCHAR_SIZE); |
771 | 490 qxeExpandEnvironmentStrings ((Extbyte *) lpval, buf, cch); |
491 TSTR_TO_C_STRING (buf, envval); | |
1204 | 492 eputenv (env_vars[i], (CIbyte *) envval); |
428 | 493 } |
494 else if (dwType == REG_SZ) | |
495 { | |
867 | 496 Ibyte *envval; |
771 | 497 |
498 TSTR_TO_C_STRING (lpval, envval); | |
1204 | 499 eputenv (env_vars[i], (CIbyte *) envval); |
428 | 500 } |
501 | |
1726 | 502 xfree (lpval, LPBYTE); |
428 | 503 } |
504 } | |
505 } | |
506 | |
507 /* Another special case: on NT, the PATH variable is actually named | |
508 "Path" although cmd.exe (perhaps NT itself) arranges for | |
509 environment variable lookup and setting to be case insensitive. | |
510 However, Emacs assumes a fully case sensitive environment, so we | |
511 need to change "Path" to "PATH" to match the expectations of | |
771 | 512 various elisp packages. |
428 | 513 |
514 The same applies to COMSPEC. */ | |
515 { | |
2367 | 516 EXTERNAL_LIST_LOOP_2 (str, Vprocess_environment) |
771 | 517 { |
518 if (STRINGP (str)) | |
519 { | |
867 | 520 Ibyte *dat = XSTRING_DATA (str); |
2367 | 521 if (qxestrncasecmp_ascii (dat, "PATH=", 5) == 0) |
771 | 522 memcpy (dat, "PATH=", 5); |
2367 | 523 else if (qxestrncasecmp_ascii (dat, "COMSPEC=", 8) == 0) |
771 | 524 memcpy (dat, "COMSPEC=", 8); |
525 } | |
526 } | |
428 | 527 } |
528 | |
529 init_user_info (); | |
530 } | |
531 | |
771 | 532 /* Emacs 20.6 contains a routine get_emacs_configuration() here to set |
533 EMACS_CONFIGURATION. */ | |
428 | 534 #ifndef HAVE_X_WINDOWS |
535 /* X11R6 on NT provides the single parameter version of this command. */ | |
536 | |
537 #include <sys/timeb.h> | |
538 | |
539 /* Emulate gettimeofday (Ulrich Leodolter, 1/11/95). */ | |
540 void | |
541 gettimeofday (struct timeval *tv, struct timezone *tz) | |
542 { | |
543 struct _timeb tb; | |
544 _ftime (&tb); | |
545 | |
546 tv->tv_sec = tb.time; | |
547 tv->tv_usec = tb.millitm * 1000L; | |
548 if (tz) | |
549 { | |
550 tz->tz_minuteswest = tb.timezone; /* minutes west of Greenwich */ | |
551 tz->tz_dsttime = tb.dstflag; /* type of dst correction */ | |
552 } | |
553 } | |
554 | |
555 #endif /* HAVE_X_WINDOWS */ | |
556 | |
771 | 557 |
428 | 558 /* ------------------------------------------------------------------------- */ |
771 | 559 /* IO support and wrapper functions for Win32 API. */ |
428 | 560 /* ------------------------------------------------------------------------- */ |
561 | |
771 | 562 typedef struct volume_info_data |
428 | 563 { |
771 | 564 struct volume_info_data *next; |
428 | 565 |
566 /* time when info was obtained */ | |
771 | 567 DWORD timestamp; |
428 | 568 |
569 /* actual volume info */ | |
867 | 570 Ibyte *root_dir; |
771 | 571 DWORD serialnum; |
572 DWORD maxcomp; | |
573 DWORD flags; | |
867 | 574 Ibyte *name; |
575 Ibyte *type; | |
428 | 576 } volume_info_data; |
577 | |
578 /* Global referenced by various functions. */ | |
579 static volume_info_data volume_info; | |
580 | |
581 /* Vector to indicate which drives are local and fixed (for which cached | |
582 data never expires). */ | |
583 static BOOL fixed_drives[26]; | |
584 | |
585 /* Consider cached volume information to be stale if older than 10s, | |
586 at least for non-local drives. Info for fixed drives is never stale. */ | |
587 #define DRIVE_INDEX( c ) ( (c) <= 'Z' ? (c) - 'A' : (c) - 'a' ) | |
588 #define VOLINFO_STILL_VALID( root_dir, info ) \ | |
589 ( ( isalpha (root_dir[0]) && \ | |
590 fixed_drives[ DRIVE_INDEX (root_dir[0]) ] ) \ | |
591 || GetTickCount () - info->timestamp < 10000 ) | |
592 | |
593 /* Cache support functions. */ | |
594 | |
595 /* Simple linked list with linear search is sufficient. */ | |
596 static volume_info_data *volume_cache = NULL; | |
597 | |
598 static volume_info_data * | |
867 | 599 lookup_volume_info (Ibyte *root_dir) |
428 | 600 { |
771 | 601 volume_info_data *info; |
428 | 602 |
603 for (info = volume_cache; info; info = info->next) | |
771 | 604 if (qxestrcasecmp_i18n (info->root_dir, root_dir) == 0) |
428 | 605 break; |
606 return info; | |
607 } | |
608 | |
609 static void | |
867 | 610 add_volume_info (Ibyte *root_dir, volume_info_data *info) |
428 | 611 { |
771 | 612 info->root_dir = qxestrdup (root_dir); |
428 | 613 info->next = volume_cache; |
614 volume_cache = info; | |
615 } | |
616 | |
617 | |
618 /* Wrapper for GetVolumeInformation, which uses caching to avoid | |
619 performance penalty (~2ms on 486 for local drives, 7.5ms for local | |
620 cdrom drive, ~5-10ms or more for remote drives on LAN). */ | |
771 | 621 static volume_info_data * |
867 | 622 get_cached_volume_information (Ibyte *root_dir) |
428 | 623 { |
771 | 624 volume_info_data *info; |
867 | 625 Ibyte *default_root; |
428 | 626 |
627 /* NULL for root_dir means use root from current directory. */ | |
628 if (root_dir == NULL) | |
629 { | |
771 | 630 Charcount nchars = qxeGetCurrentDirectory (0, NULL); |
631 Extbyte *rootext; | |
632 | |
633 if (!nchars) | |
428 | 634 return NULL; |
771 | 635 rootext = alloca_extbytes (nchars * XETCHAR_SIZE); |
636 if (!qxeGetCurrentDirectory (nchars, rootext)) | |
637 return NULL; | |
638 TSTR_TO_C_STRING (rootext, default_root); | |
428 | 639 parse_root (default_root, &root_dir); |
640 *root_dir = 0; | |
641 root_dir = default_root; | |
642 } | |
643 | |
644 /* Local fixed drives can be cached permanently. Removable drives | |
645 cannot be cached permanently, since the volume name and serial | |
646 number (if nothing else) can change. Remote drives should be | |
647 treated as if they are removable, since there is no sure way to | |
648 tell whether they are or not. Also, the UNC association of drive | |
649 letters mapped to remote volumes can be changed at any time (even | |
650 by other processes) without notice. | |
651 | |
652 As a compromise, so we can benefit from caching info for remote | |
653 volumes, we use a simple expiry mechanism to invalidate cache | |
654 entries that are more than ten seconds old. */ | |
655 | |
656 #if 0 | |
657 /* No point doing this, because WNetGetConnection is even slower than | |
658 GetVolumeInformation, consistently taking ~50ms on a 486 (FWIW, | |
659 GetDriveType is about the only call of this type which does not | |
660 involve network access, and so is extremely quick). */ | |
661 | |
662 /* Map drive letter to UNC if remote. */ | |
771 | 663 if (isalpha (root_dir[0]) && !fixed [DRIVE_INDEX (root_dir[0])]) |
428 | 664 { |
771 | 665 Extbyte remote_name[256 * XETCHAR_SIZE]; |
867 | 666 Ibyte drive[3] = { root_dir[0], ':' }; |
771 | 667 Extbyte *driveext; |
428 | 668 |
2526 | 669 PATHNAME_CONVERT_OUT (drive, driveext); |
771 | 670 if (qxeWNetGetConnection (driveext, remote_name, |
671 sizeof (remote_name) / XETCHAR_SIZE) | |
428 | 672 == NO_ERROR) |
673 /* do something */ ; | |
674 } | |
675 #endif | |
676 | |
677 info = lookup_volume_info (root_dir); | |
678 | |
679 if (info == NULL || ! VOLINFO_STILL_VALID (root_dir, info)) | |
771 | 680 { |
681 Extbyte name[256 * MAX_XETCHAR_SIZE]; | |
682 DWORD serialnum; | |
683 DWORD maxcomp; | |
684 DWORD flags; | |
685 Extbyte type[256 * MAX_XETCHAR_SIZE]; | |
1204 | 686 Extbyte *rootdirext; |
687 | |
2526 | 688 PATHNAME_CONVERT_OUT (root_dir, rootdirext); |
428 | 689 |
771 | 690 /* Info is not cached, or is stale. */ |
1204 | 691 if (!qxeGetVolumeInformation (rootdirext, |
771 | 692 name, sizeof (name) / XETCHAR_SIZE, |
693 &serialnum, | |
694 &maxcomp, | |
695 &flags, | |
696 type, sizeof (type) / XETCHAR_SIZE)) | |
697 return NULL; | |
428 | 698 |
771 | 699 /* Cache the volume information for future use, overwriting existing |
700 entry if present. */ | |
701 if (info == NULL) | |
702 { | |
2367 | 703 info = xnew (volume_info_data); |
771 | 704 add_volume_info (root_dir, info); |
705 } | |
706 else | |
707 { | |
1726 | 708 xfree (info->name, Ibyte *); |
709 xfree (info->type, Ibyte *); | |
771 | 710 } |
428 | 711 |
771 | 712 TSTR_TO_C_STRING_MALLOC (name, info->name); |
713 info->serialnum = serialnum; | |
714 info->maxcomp = maxcomp; | |
715 info->flags = flags; | |
716 TSTR_TO_C_STRING_MALLOC (type, info->type); | |
717 info->timestamp = GetTickCount (); | |
718 } | |
428 | 719 |
720 return info; | |
721 } | |
722 | |
723 /* Get information on the volume where name is held; set path pointer to | |
724 start of pathname in name (past UNC header\volume header if present). */ | |
771 | 725 static int |
867 | 726 get_volume_info (const Ibyte *name, const Ibyte **pPath) |
428 | 727 { |
771 | 728 /* We probably only need a couple of bytes, but let's be generous in |
729 case this function gets changed */ | |
2367 | 730 Ibyte *temp = alloca_ibytes (qxestrlen (name) + 10); |
867 | 731 Ibyte *rootname = NULL; /* default to current volume */ |
771 | 732 volume_info_data *info; |
428 | 733 |
734 if (name == NULL) | |
735 return FALSE; | |
736 | |
737 /* find the root name of the volume if given */ | |
738 if (isalpha (name[0]) && name[1] == ':') | |
739 { | |
740 rootname = temp; | |
741 temp[0] = *name++; | |
742 temp[1] = *name++; | |
743 temp[2] = '\\'; | |
744 temp[3] = 0; | |
745 } | |
746 else if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1])) | |
747 { | |
867 | 748 Ibyte *str = temp; |
428 | 749 int slashes = 4; |
750 rootname = temp; | |
751 do | |
752 { | |
753 if (IS_DIRECTORY_SEP (*name) && --slashes == 0) | |
754 break; | |
755 *str++ = *name++; | |
756 } | |
771 | 757 while (*name); |
428 | 758 |
759 *str++ = '\\'; | |
760 *str = 0; | |
761 } | |
762 | |
763 if (pPath) | |
764 *pPath = name; | |
765 | |
771 | 766 info = get_cached_volume_information (rootname); |
428 | 767 if (info != NULL) |
768 { | |
769 /* Set global referenced by other functions. */ | |
770 volume_info = *info; | |
771 return TRUE; | |
772 } | |
773 return FALSE; | |
774 } | |
775 | |
771 | 776 /* XEmacs: Everything referring to map_win32_filename() aka map_w32_filename() |
777 removed; it was only for NT 3.1, which we hereby do not support. (NT 3.5 | |
778 predates Windows 95!) */ | |
428 | 779 |
1204 | 780 int |
781 mswindows_is_executable (const Ibyte *name) | |
771 | 782 { |
867 | 783 Ibyte *p = qxestrrchr (name, '.'); |
2367 | 784 return (p != NULL && (qxestrcasecmp_ascii (p, ".exe") == 0 || |
785 qxestrcasecmp_ascii (p, ".com") == 0 || | |
786 qxestrcasecmp_ascii (p, ".bat") == 0 || | |
787 qxestrcasecmp_ascii (p, ".cmd") == 0)); | |
428 | 788 } |
789 | |
790 /* Emulate the Unix directory procedures opendir, closedir, | |
791 and readdir. We can't use the procedures supplied in sysdep.c, | |
792 so we provide them here. */ | |
793 | |
794 struct direct dir_static; /* simulated directory contents */ | |
795 static HANDLE dir_find_handle = INVALID_HANDLE_VALUE; | |
771 | 796 /* dir_is_fat deleted */ |
867 | 797 static Ibyte *dir_pathname; |
771 | 798 static WIN32_FIND_DATAW dir_find_data; |
799 | |
800 /* Support shares on a network resource as subdirectories of a read-only | |
801 root directory. */ | |
802 static HANDLE wnet_enum_handle = INVALID_HANDLE_VALUE; | |
867 | 803 static HANDLE open_unc_volume (const Ibyte *); |
804 static Ibyte *read_unc_volume (HANDLE); | |
771 | 805 static int close_unc_volume (HANDLE); |
428 | 806 |
807 DIR * | |
867 | 808 mswindows_opendir (const Ibyte *filename) |
428 | 809 { |
810 DIR *dirp; | |
811 | |
812 /* Opening is done by FindFirstFile. However, a read is inherent to | |
813 this operation, so we defer the open until read time. */ | |
814 | |
771 | 815 if (dir_find_handle != INVALID_HANDLE_VALUE) |
428 | 816 return NULL; |
771 | 817 if (wnet_enum_handle != INVALID_HANDLE_VALUE) |
428 | 818 return NULL; |
819 | |
771 | 820 if (is_unc_volume (filename)) |
821 { | |
822 wnet_enum_handle = open_unc_volume (filename); | |
823 if (wnet_enum_handle == INVALID_HANDLE_VALUE) | |
824 return NULL; | |
825 } | |
428 | 826 |
771 | 827 if (!(dirp = xnew_and_zero (DIR))) |
828 return NULL; | |
829 | |
830 if (dir_pathname) | |
1726 | 831 xfree (dir_pathname, Ibyte *); |
771 | 832 dir_pathname = qxestrdup (filename); |
428 | 833 |
834 return dirp; | |
835 } | |
836 | |
442 | 837 int |
771 | 838 mswindows_closedir (DIR *dirp) |
428 | 839 { |
2957 | 840 int retval = -1; |
442 | 841 |
428 | 842 /* If we have a find-handle open, close it. */ |
843 if (dir_find_handle != INVALID_HANDLE_VALUE) | |
844 { | |
771 | 845 retval = FindClose (dir_find_handle) ? 0 : -1; |
428 | 846 dir_find_handle = INVALID_HANDLE_VALUE; |
847 } | |
771 | 848 else if (wnet_enum_handle != INVALID_HANDLE_VALUE) |
849 { | |
850 retval = close_unc_volume (wnet_enum_handle); | |
851 wnet_enum_handle = INVALID_HANDLE_VALUE; | |
852 } | |
1726 | 853 xfree (dirp, DIR *); |
771 | 854 |
855 return retval; | |
856 } | |
857 | |
858 struct direct * | |
2286 | 859 mswindows_readdir (DIR *UNUSED (dirp)) |
771 | 860 { |
867 | 861 Ibyte *val; |
771 | 862 int need_to_free = 0; |
863 | |
864 if (wnet_enum_handle != INVALID_HANDLE_VALUE) | |
865 { | |
866 if (!(val = read_unc_volume (wnet_enum_handle))) | |
867 return NULL; | |
868 need_to_free = 1; | |
869 } | |
870 /* If we aren't dir_finding, do a find-first, otherwise do a find-next. */ | |
871 else if (dir_find_handle == INVALID_HANDLE_VALUE) | |
872 { | |
873 DECLARE_EISTRING (filename); | |
867 | 874 Ichar lastch; |
2526 | 875 Extbyte *fileext; |
771 | 876 |
877 eicpy_rawz (filename, dir_pathname); | |
878 lastch = eigetch_char (filename, eicharlen (filename) - 1); | |
879 if (!IS_DIRECTORY_SEP (lastch)) | |
880 eicat_ch (filename, '\\'); | |
881 eicat_ch (filename, '*'); | |
2526 | 882 PATHNAME_CONVERT_OUT (eidata (filename), fileext); |
771 | 883 |
2526 | 884 dir_find_handle = qxeFindFirstFile (fileext, &dir_find_data); |
771 | 885 |
886 if (dir_find_handle == INVALID_HANDLE_VALUE) | |
887 return NULL; | |
888 TSTR_TO_C_STRING (dir_find_data.cFileName, val); | |
889 } | |
890 else | |
891 { | |
892 if (!qxeFindNextFile (dir_find_handle, &dir_find_data)) | |
893 return NULL; | |
894 TSTR_TO_C_STRING (dir_find_data.cFileName, val); | |
895 } | |
896 | |
897 /* XEmacs never uses this value, so don't bother making it match | |
898 value returned by qxe_stat(). */ | |
899 dir_static.d_ino = 1; | |
900 | |
901 dir_static.d_reclen = sizeof (struct direct) - MAXNAMLEN + 3 + | |
902 dir_static.d_namlen - dir_static.d_namlen % 4; | |
903 | |
904 { | |
905 DECLARE_EISTRING (found); | |
906 Bytecount namlen; | |
907 | |
2526 | 908 if (mswindows_shortcuts_are_symlinks) |
909 { | |
910 int len = qxestrlen (val); | |
911 if (len > 4 && !qxestrcasecmp_ascii (val + len - 4, ".LNK")) | |
912 { | |
913 /* If we've found a valid link, then chop off the .LNK ending */ | |
914 DECLARE_EISTRING (linkname); | |
915 Ichar lastch; | |
916 Ibyte *resolved; | |
917 | |
918 /* First check if link is valid */ | |
919 PATHNAME_RESOLVE_LINKS (dir_pathname, resolved); | |
920 eicpy_rawz (linkname, resolved); | |
921 lastch = eigetch_char (linkname, eicharlen (linkname) - 1); | |
922 if (!IS_DIRECTORY_SEP (lastch)) | |
923 eicat_ch (linkname, '\\'); | |
924 eicat_rawz (linkname, val); | |
925 resolved = mswindows_read_link (eidata (linkname)); | |
926 if (resolved) | |
927 { | |
928 xfree (resolved, Ibyte *); | |
929 len -= 4; | |
930 val[len] = '\0'; | |
931 } | |
932 } | |
933 } | |
934 | |
771 | 935 eicpy_rawz (found, val); |
936 if (need_to_free) | |
1726 | 937 xfree (val, Ibyte *); |
771 | 938 |
939 if (!NILP (Vmswindows_downcase_file_names)) | |
940 eilwr (found); | |
941 | |
942 namlen = min (eilen (found), sizeof (dir_static.d_name) - 1); | |
943 strncpy (dir_static.d_name, (char *) eidata (found), namlen); | |
944 dir_static.d_name[namlen] = '\0'; | |
945 dir_static.d_namlen = (unsigned short) namlen; | |
946 } | |
947 | |
948 return &dir_static; | |
949 } | |
950 | |
951 static HANDLE | |
867 | 952 open_unc_volume (const Ibyte *path) |
771 | 953 { |
954 NETRESOURCEW nr; | |
955 HANDLE henum; | |
956 int result; | |
957 | |
958 nr.dwScope = RESOURCE_GLOBALNET; | |
959 nr.dwType = RESOURCETYPE_DISK; | |
960 nr.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER; | |
961 nr.dwUsage = RESOURCEUSAGE_CONTAINER; | |
962 nr.lpLocalName = NULL; | |
2526 | 963 PATHNAME_CONVERT_OUT (path, nr.lpRemoteName); |
771 | 964 nr.lpComment = NULL; |
965 nr.lpProvider = NULL; | |
966 | |
967 result = qxeWNetOpenEnum (RESOURCE_GLOBALNET, RESOURCETYPE_DISK, | |
968 RESOURCEUSAGE_CONNECTABLE, &nr, &henum); | |
969 | |
970 if (result == NO_ERROR) | |
971 return henum; | |
972 else | |
973 return INVALID_HANDLE_VALUE; | |
974 } | |
975 | |
867 | 976 static Ibyte * |
2286 | 977 read_unc_volume (HANDLE UNUSED (henum)) |
771 | 978 { |
1204 | 979 DWORD count; |
771 | 980 int result; |
981 Extbyte buf[16384]; | |
867 | 982 Ibyte *ptr; |
1204 | 983 DWORD bufsize = sizeof (buf); |
771 | 984 |
985 count = 1; | |
986 /* #### we should just be querying the size and then allocating the | |
987 right amount, like for all similar API's. but the docs say this ?! | |
988 | |
989 An application cannot set the lpBuffer parameter to NULL and | |
990 retrieve the required buffer size from the lpBufferSize | |
991 parameter. Instead, the application should allocate a buffer of a | |
992 reasonable size -- 16 kilobytes (K) is typical -- and use the value | |
993 of lpBufferSize for error detection. | |
994 */ | |
995 | |
996 result = qxeWNetEnumResource (wnet_enum_handle, &count, buf, &bufsize); | |
997 if (result != NO_ERROR) | |
998 return NULL; | |
999 | |
1000 /* WNetEnumResource returns \\resource\share...skip forward to "share". */ | |
1001 TSTR_TO_C_STRING (((LPNETRESOURCEW) buf)->lpRemoteName, ptr); | |
867 | 1002 INC_IBYTEPTR (ptr); |
1003 INC_IBYTEPTR (ptr); | |
1004 while (*ptr && !IS_DIRECTORY_SEP (itext_ichar (ptr))) | |
1005 INC_IBYTEPTR (ptr); | |
1006 INC_IBYTEPTR (ptr); | |
771 | 1007 |
1008 return qxestrdup (ptr); | |
1009 } | |
1010 | |
1011 static int | |
1012 close_unc_volume (HANDLE henum) | |
1013 { | |
1014 if (henum != INVALID_HANDLE_VALUE) | |
1015 return WNetCloseEnum (henum) == NO_ERROR ? 0 : -1; | |
442 | 1016 else |
1017 return -1; | |
428 | 1018 } |
1019 | |
771 | 1020 static DWORD |
867 | 1021 unc_volume_file_attributes (const Ibyte *path) |
428 | 1022 { |
771 | 1023 HANDLE henum; |
1024 DWORD attrs; | |
1025 | |
1026 henum = open_unc_volume (path); | |
1027 if (henum == INVALID_HANDLE_VALUE) | |
1028 return -1; | |
1029 | |
1030 attrs = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_DIRECTORY; | |
1031 | |
1032 close_unc_volume (henum); | |
428 | 1033 |
771 | 1034 return attrs; |
1035 } | |
1036 | |
1037 int | |
867 | 1038 mswindows_access (const Ibyte *path, int mode) |
771 | 1039 { |
1040 DWORD attributes; | |
428 | 1041 |
771 | 1042 /* MSVC implementation doesn't recognize D_OK. */ |
1043 if (is_unc_volume (path)) | |
1044 { | |
1045 attributes = unc_volume_file_attributes (path); | |
1046 if (attributes == -1) | |
1047 { | |
1048 errno = EACCES; | |
1049 return -1; | |
1050 } | |
428 | 1051 } |
1052 else | |
1053 { | |
771 | 1054 Extbyte *pathext; |
1055 | |
2526 | 1056 PATHNAME_CONVERT_OUT (path, pathext); |
771 | 1057 if ((attributes = qxeGetFileAttributes (pathext)) == -1) |
1058 { | |
1059 /* Should try mapping GetLastError to errno; for now just indicate | |
1060 that path doesn't exist. */ | |
1061 errno = EACCES; | |
1062 return -1; | |
1063 } | |
428 | 1064 } |
1204 | 1065 if ((mode & X_OK) != 0 && !mswindows_is_executable (path)) |
771 | 1066 { |
1067 errno = EACCES; | |
1068 return -1; | |
1069 } | |
1070 if ((mode & W_OK) != 0 && (attributes & FILE_ATTRIBUTE_READONLY) != 0) | |
428 | 1071 { |
771 | 1072 errno = EACCES; |
1073 return -1; | |
428 | 1074 } |
771 | 1075 if ((mode & D_OK) != 0 && (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) |
1076 { | |
1077 errno = EACCES; | |
1078 return -1; | |
1079 } | |
1080 return 0; | |
428 | 1081 } |
1082 | |
771 | 1083 /* This only works on NTFS volumes, but is useful to have. */ |
1084 /* #### NT 5.0 has a function CreateHardLink to do this directly, | |
1085 and it may do more things. */ | |
428 | 1086 int |
2957 | 1087 mswindows_link (const Ibyte *old, const Ibyte *new_) |
428 | 1088 { |
771 | 1089 HANDLE fileh; |
1090 int result = -1; | |
1204 | 1091 Extbyte *oldext; |
771 | 1092 |
2957 | 1093 if (old == NULL || new_ == NULL) |
771 | 1094 { |
1095 errno = ENOENT; | |
1096 return -1; | |
1097 } | |
1098 | |
2526 | 1099 PATHNAME_CONVERT_OUT (old, oldext); |
1204 | 1100 fileh = qxeCreateFile (oldext, 0, 0, NULL, OPEN_EXISTING, |
771 | 1101 FILE_FLAG_BACKUP_SEMANTICS, NULL); |
1102 if (fileh != INVALID_HANDLE_VALUE) | |
1103 { | |
1104 int wlen; | |
1105 WCHAR *newuni; | |
1106 | |
1107 /* Confusingly, the "alternate" stream name field does not apply | |
1108 when restoring a hard link, and instead contains the actual | |
1109 stream data for the link (ie. the name of the link to create). | |
1110 The WIN32_STREAM_ID structure before the cStreamName field is | |
1111 the stream header, which is then immediately followed by the | |
1112 stream data. */ | |
1113 | |
1114 struct | |
1115 { | |
1116 WIN32_STREAM_ID wid; | |
2421 | 1117 WCHAR wbuffer[_MAX_PATH]; /* extra space for link name */ |
771 | 1118 } data; |
1119 | |
2957 | 1120 TO_EXTERNAL_FORMAT (C_STRING, new_, |
771 | 1121 ALLOCA, (newuni, wlen), Qmswindows_unicode); |
2421 | 1122 if (wlen / sizeof (WCHAR) < _MAX_PATH) |
771 | 1123 { |
1124 LPVOID context = NULL; | |
1125 DWORD wbytes = 0; | |
428 | 1126 |
771 | 1127 wcscpy (data.wid.cStreamName, newuni); |
1128 data.wid.dwStreamId = BACKUP_LINK; | |
1129 data.wid.dwStreamAttributes = 0; | |
1130 data.wid.Size.LowPart = wlen; /* in bytes, not chars! */ | |
1131 data.wid.Size.HighPart = 0; | |
1132 data.wid.dwStreamNameSize = 0; | |
1133 | |
1134 if (BackupWrite (fileh, (LPBYTE)&data, | |
1135 offsetof (WIN32_STREAM_ID, cStreamName) | |
1136 + data.wid.Size.LowPart, | |
1137 &wbytes, FALSE, FALSE, &context) | |
1138 && BackupWrite (fileh, NULL, 0, &wbytes, TRUE, FALSE, &context)) | |
1139 { | |
1140 /* succeeded */ | |
1141 result = 0; | |
1142 } | |
1143 else | |
1144 { | |
1145 /* Should try mapping GetLastError to errno; for now just | |
1146 indicate a general error (eg. links not supported). */ | |
1242 | 1147 errno = EINVAL; /* perhaps EMLINK? */ |
771 | 1148 } |
1149 } | |
1150 | |
1151 CloseHandle (fileh); | |
1152 } | |
1153 else | |
1154 errno = ENOENT; | |
1155 | |
1156 return result; | |
1157 } | |
1158 | |
1159 /* sys_open() merged into sysdep.c sys_open() */ | |
1160 | |
1161 int | |
867 | 1162 mswindows_rename (const Ibyte *oldname, const Ibyte *newname) |
771 | 1163 { |
1164 int result; | |
867 | 1165 Ibyte *temp; |
771 | 1166 |
1167 /* MoveFile on Windows 95 doesn't correctly change the short file name | |
428 | 1168 alias in a number of circumstances (it is not easy to predict when |
1169 just by looking at oldname and newname, unfortunately). In these | |
1170 cases, renaming through a temporary name avoids the problem. | |
1171 | |
771 | 1172 A second problem on Windows 95 is that renaming through a temp name when |
428 | 1173 newname is uppercase fails (the final long name ends up in |
1174 lowercase, although the short alias might be uppercase) UNLESS the | |
1175 long temp name is not 8.3. | |
1176 | |
771 | 1177 So, on Windows 95 we always rename through a temp name, and we make sure |
428 | 1178 the temp name has a long extension to ensure correct renaming. */ |
1179 | |
771 | 1180 /* XEmacs: We sprintf() part of OLDNAME into part of OLDNAME + a number, |
1181 so the following calculation should certainly be enough. */ | |
428 | 1182 |
867 | 1183 temp = qxestrcpy (alloca_ibytes (2 * qxestrlen (oldname) + 100), oldname); |
771 | 1184 |
1185 if (mswindows_windows9x_p) | |
428 | 1186 { |
867 | 1187 Ibyte *o; |
1188 Ibyte *p; | |
771 | 1189 int i = 0; |
428 | 1190 |
771 | 1191 if (o = qxestrrchr (oldname, '\\')) |
1192 o++; | |
1193 else | |
867 | 1194 o = (Ibyte *) oldname; |
771 | 1195 |
1196 if (p = qxestrrchr (temp, '\\')) | |
428 | 1197 p++; |
1198 else | |
1199 p = temp; | |
771 | 1200 |
1201 do | |
1202 { | |
1203 Extbyte *oldext, *tempext; | |
1204 /* Force temp name to require a manufactured 8.3 alias - this | |
1205 seems to make the second rename work properly. */ | |
1206 qxesprintf (p, "_.%s.%u", o, i); | |
1207 i++; | |
2526 | 1208 PATHNAME_CONVERT_OUT (oldname, oldext); |
1209 PATHNAME_CONVERT_OUT (temp, tempext); | |
771 | 1210 result = rename (oldext, tempext); |
1211 } | |
1212 /* This loop must surely terminate! */ | |
1213 while (result < 0 && errno == EEXIST); | |
1214 if (result < 0) | |
428 | 1215 return -1; |
1216 } | |
1217 | |
771 | 1218 /* Emulate Unix behaviour - newname is deleted if it already exists |
428 | 1219 (at least if it is a file; don't do this for directories). |
771 | 1220 |
1221 Since we mustn't do this if we are just changing the case of the | |
1222 file name (we would end up deleting the file we are trying to | |
1223 rename!), we let rename detect if the destination file already | |
1224 exists - that way we avoid the possible pitfalls of trying to | |
1225 determine ourselves whether two names really refer to the same | |
1226 file, which is not always possible in the general case. (Consider | |
1227 all the permutations of shared or subst'd drives, etc.) */ | |
1228 { | |
1229 Extbyte *newext, *tempext; | |
1230 | |
2526 | 1231 PATHNAME_CONVERT_OUT (newname, newext); |
1232 PATHNAME_CONVERT_OUT (temp, tempext); | |
1233 if (XEUNICODE_P) | |
1234 { | |
1235 result = _wrename ((const wchar_t *) tempext, | |
1236 (const wchar_t *) newext); | |
1237 if (result < 0 | |
1238 && (errno == EEXIST || errno == EACCES) | |
1239 && _wchmod ((const wchar_t *) newext, 0666) == 0 | |
1240 && _wunlink ((const wchar_t *) newext) == 0) | |
1241 result = _wrename ((const wchar_t *) tempext, | |
1242 (const wchar_t *) newext); | |
1243 } | |
1244 else | |
1245 { | |
1246 result = rename (tempext, newext); | |
1247 if (result < 0 | |
1248 && (errno == EEXIST || errno == EACCES) | |
1249 && _chmod (newext, 0666) == 0 | |
1250 && _unlink (newext) == 0) | |
1251 result = rename (tempext, newext); | |
1252 } | |
771 | 1253 } |
1254 | |
1255 return result; | |
1256 } | |
428 | 1257 |
771 | 1258 int |
867 | 1259 mswindows_unlink (const Ibyte *path) |
771 | 1260 { |
1261 Extbyte *pathout; | |
1262 | |
2526 | 1263 PATHNAME_CONVERT_OUT (path, pathout); |
771 | 1264 /* On Unix, unlink works without write permission. */ |
2526 | 1265 if (XEUNICODE_P) |
1266 { | |
1267 _wchmod ((const wchar_t *) pathout, 0666); | |
1268 return _wunlink ((const wchar_t *) pathout); | |
1269 } | |
1270 else | |
1271 { | |
1272 _chmod (pathout, 0666); | |
1273 return _unlink (pathout); | |
1274 } | |
428 | 1275 } |
1276 | |
1277 static FILETIME utc_base_ft; | |
592 | 1278 static long double utc_base; |
440 | 1279 static int init = 0; |
771 | 1280 static LARGE_INTEGER utc_base_li; |
440 | 1281 |
771 | 1282 /* XEmacs: We seem to have a new definition of |
1283 mswindows_convert_time(), although I'm not sure why. --ben */ | |
428 | 1284 |
1285 time_t | |
771 | 1286 mswindows_convert_time (FILETIME uft) |
440 | 1287 { |
1288 time_t ret; | |
1289 #ifndef MAXLONGLONG | |
1290 SYSTEMTIME st; | |
1291 struct tm t; | |
1292 FILETIME ft; | |
1293 TIME_ZONE_INFORMATION tzi; | |
1294 DWORD tzid; | |
1295 #else | |
1296 LARGE_INTEGER lft; | |
1297 #endif | |
1298 | |
1299 if (!init) | |
1300 { | |
1301 /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */ | |
1302 SYSTEMTIME st; | |
1303 | |
1304 st.wYear = 1970; | |
1305 st.wMonth = 1; | |
1306 st.wDay = 1; | |
1307 st.wHour = 0; | |
1308 st.wMinute = 0; | |
1309 st.wSecond = 0; | |
1310 st.wMilliseconds = 0; | |
1311 | |
1312 SystemTimeToFileTime (&st, &utc_base_ft); | |
1313 | |
1314 utc_base_li.LowPart = utc_base_ft.dwLowDateTime; | |
1315 utc_base_li.HighPart = utc_base_ft.dwHighDateTime; | |
1316 | |
1317 init = 1; | |
1318 } | |
1319 | |
1320 #ifdef MAXLONGLONG | |
1321 | |
1322 /* On a compiler that supports long integers, do it the easy way */ | |
1323 lft.LowPart = uft.dwLowDateTime; | |
1324 lft.HighPart = uft.dwHighDateTime; | |
1325 ret = (time_t) ((lft.QuadPart - utc_base_li.QuadPart) / 10000000); | |
1326 | |
1327 #else | |
1328 | |
1329 /* Do it the hard way using mktime. */ | |
1330 FileTimeToLocalFileTime(&uft, &ft); | |
1331 FileTimeToSystemTime (&ft, &st); | |
1332 tzid = GetTimeZoneInformation (&tzi); | |
1333 t.tm_year = st.wYear - 1900; | |
1334 t.tm_mon = st.wMonth - 1; | |
1335 t.tm_mday = st.wDay; | |
1336 t.tm_hour = st.wHour; | |
1337 t.tm_min = st.wMinute; | |
1338 t.tm_sec = st.wSecond; | |
1339 t.tm_isdst = (tzid == TIME_ZONE_ID_DAYLIGHT); | |
1340 /* st.wMilliseconds not applicable */ | |
1341 ret = mktime(&t); | |
1342 if (ret == -1) | |
1343 { | |
1344 ret = 0; | |
1345 } | |
1346 | |
1347 #endif | |
1348 | |
1349 return ret; | |
1350 } | |
428 | 1351 |
771 | 1352 static void |
428 | 1353 convert_from_time_t (time_t time, FILETIME * pft) |
1354 { | |
1355 long double tmp; | |
1356 | |
1357 if (!init) | |
1358 { | |
1359 /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */ | |
1360 SYSTEMTIME st; | |
1361 | |
1362 st.wYear = 1970; | |
1363 st.wMonth = 1; | |
1364 st.wDay = 1; | |
1365 st.wHour = 0; | |
1366 st.wMinute = 0; | |
1367 st.wSecond = 0; | |
1368 st.wMilliseconds = 0; | |
1369 | |
1370 SystemTimeToFileTime (&st, &utc_base_ft); | |
1371 utc_base = (long double) utc_base_ft.dwHighDateTime | |
1372 * 4096 * 1024 * 1024 + utc_base_ft.dwLowDateTime; | |
1373 init = 1; | |
1374 } | |
1375 | |
1376 /* time in 100ns units since 1-Jan-1601 */ | |
1377 tmp = (long double) time * 1e7 + utc_base; | |
1378 pft->dwHighDateTime = (DWORD) (tmp / (4096.0 * 1024 * 1024)); | |
771 | 1379 pft->dwLowDateTime = (DWORD) (tmp - (4096.0 * 1024 * 1024) * |
1380 pft->dwHighDateTime); | |
428 | 1381 } |
1382 | |
1383 #if 0 | |
771 | 1384 /* A comment from Emacs 20.6: |
1385 | |
1386 No reason to keep this; faking inode values either by hashing or even | |
428 | 1387 using the file index from GetInformationByHandle, is not perfect and |
1388 so by default Emacs doesn't use the inode values on Windows. | |
1389 Instead, we now determine file-truename correctly (except for | |
1390 possible drive aliasing etc). */ | |
1391 | |
771 | 1392 /* XEmacs: Removed the fake-inodes code here, which was if 0'd out. |
1393 If you want it, look in w32.c in Emacs 20.6. */ | |
428 | 1394 #endif |
1395 | |
442 | 1396 /* #### aichner@ecf.teradyne.com reported that with the library |
1397 provided stat/fstat, (file-exist "d:\\tmp\\") =>> nil, | |
1398 (file-exist "d:\\tmp") =>> t, when d:\tmp exists. Whenever | |
1399 we opt to use non-encapsulated stat(), this should serve as | |
1400 a compatibility test. --kkm */ | |
440 | 1401 |
771 | 1402 /* Provide fstat and utime as well as stat for consistent handling of |
1403 file timestamps. */ | |
442 | 1404 int |
771 | 1405 mswindows_fstat (int desc, struct stat *buf) |
432 | 1406 { |
448 | 1407 HANDLE fh = (HANDLE) _get_osfhandle (desc); |
1408 BY_HANDLE_FILE_INFORMATION info; | |
1409 DWORD fake_inode; | |
1410 int permission; | |
1411 | |
1412 switch (GetFileType (fh) & ~FILE_TYPE_REMOTE) | |
432 | 1413 { |
448 | 1414 case FILE_TYPE_DISK: |
1415 buf->st_mode = _S_IFREG; | |
1416 if (!GetFileInformationByHandle (fh, &info)) | |
1417 { | |
1418 errno = EACCES; | |
1419 return -1; | |
1420 } | |
1421 break; | |
1422 case FILE_TYPE_PIPE: | |
1423 buf->st_mode = _S_IFIFO; | |
1424 goto non_disk; | |
1425 case FILE_TYPE_CHAR: | |
1426 case FILE_TYPE_UNKNOWN: | |
1427 default: | |
1428 buf->st_mode = _S_IFCHR; | |
1429 non_disk: | |
1430 memset (&info, 0, sizeof (info)); | |
1431 info.dwFileAttributes = 0; | |
1432 info.ftCreationTime = utc_base_ft; | |
1433 info.ftLastAccessTime = utc_base_ft; | |
1434 info.ftLastWriteTime = utc_base_ft; | |
1435 } | |
1436 | |
1437 if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) | |
1438 { | |
1439 buf->st_mode = _S_IFDIR; | |
1440 buf->st_nlink = 2; /* doesn't really matter */ | |
1441 fake_inode = 0; /* this doesn't either I think */ | |
432 | 1442 } |
1443 else | |
1444 { | |
462 | 1445 buf->st_nlink = (short) info.nNumberOfLinks; |
448 | 1446 /* Might as well use file index to fake inode values, but this |
1447 is not guaranteed to be unique unless we keep a handle open | |
1448 all the time (even then there are situations where it is | |
1449 not unique). Reputedly, there are at most 48 bits of info | |
1450 (on NTFS, presumably less on FAT). */ | |
1451 fake_inode = info.nFileIndexLow ^ info.nFileIndexHigh; | |
432 | 1452 } |
448 | 1453 |
1454 /* MSVC defines _ino_t to be short; other libc's might not. */ | |
1455 if (sizeof (buf->st_ino) == 2) | |
462 | 1456 buf->st_ino = (unsigned short) (fake_inode ^ (fake_inode >> 16)); |
448 | 1457 else |
462 | 1458 buf->st_ino = (unsigned short) fake_inode; |
448 | 1459 |
1460 /* consider files to belong to current user */ | |
1461 buf->st_uid = 0; | |
1462 buf->st_gid = 0; | |
1463 | |
1464 buf->st_dev = info.dwVolumeSerialNumber; | |
1465 buf->st_rdev = info.dwVolumeSerialNumber; | |
1466 | |
1467 buf->st_size = info.nFileSizeLow; | |
1468 | |
1469 /* Convert timestamps to Unix format. */ | |
771 | 1470 buf->st_mtime = mswindows_convert_time (info.ftLastWriteTime); |
1471 buf->st_atime = mswindows_convert_time (info.ftLastAccessTime); | |
448 | 1472 if (buf->st_atime == 0) buf->st_atime = buf->st_mtime; |
771 | 1473 buf->st_ctime = mswindows_convert_time (info.ftCreationTime); |
448 | 1474 if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime; |
1475 | |
1476 /* determine rwx permissions */ | |
1477 if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) | |
1478 permission = _S_IREAD; | |
1479 else | |
1480 permission = _S_IREAD | _S_IWRITE; | |
1481 | |
1482 if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) | |
1483 permission |= _S_IEXEC; | |
771 | 1484 else |
1485 { | |
1486 #if 0 /* no way of knowing the filename */ | |
1204 | 1487 if (mswindows_is_executable (name)) |
771 | 1488 permission |= _S_IEXEC; |
1489 #endif | |
1490 } | |
448 | 1491 |
1492 buf->st_mode |= permission | (permission >> 3) | (permission >> 6); | |
1493 | |
1494 return 0; | |
432 | 1495 } |
1496 | |
428 | 1497 /* MSVC stat function can't cope with UNC names and has other bugs, so |
1498 replace it with our own. This also allows us to calculate consistent | |
1499 inode values without hacks in the main Emacs code. */ | |
1500 int | |
867 | 1501 mswindows_stat (const Ibyte *path, struct stat *buf) |
428 | 1502 { |
867 | 1503 Ibyte *name, *r; |
771 | 1504 WIN32_FIND_DATAW wfd; |
428 | 1505 HANDLE fh; |
1506 DWORD fake_inode; | |
1507 int permission; | |
771 | 1508 Bytecount len; |
428 | 1509 int rootdir = FALSE; |
771 | 1510 Extbyte *nameext; |
819 | 1511 int errm; |
428 | 1512 |
1513 if (path == NULL || buf == NULL) | |
1514 { | |
1515 errno = EFAULT; | |
1516 return -1; | |
1517 } | |
1518 | |
867 | 1519 name = qxestrcpy (alloca_ibytes (qxestrlen (path) + 10), path); |
819 | 1520 errm = SetErrorMode (SEM_FAILCRITICALERRORS |
1521 | SEM_NOOPENFILEERRORBOX); | |
771 | 1522 |
1523 get_volume_info (name, &path); | |
1524 /* must be valid filename, no wild cards or other invalid characters */ | |
1525 if (qxestrpbrk (name, "*?|<>\"")) | |
428 | 1526 { |
1527 errno = ENOENT; | |
1528 return -1; | |
1529 } | |
1530 | |
771 | 1531 /* If name is "c:/.." or "/.." then stat "c:/" or "/". */ |
1532 r = IS_DEVICE_SEP (name[1]) ? &name[2] : name; | |
1533 if (IS_DIRECTORY_SEP (r[0]) && r[1] == '.' && r[2] == '.' && r[3] == '\0') | |
1534 { | |
1535 r[1] = r[2] = '\0'; | |
1536 } | |
1537 | |
428 | 1538 /* Remove trailing directory separator, unless name is the root |
1539 directory of a drive or UNC volume in which case ensure there | |
1540 is a trailing separator. */ | |
771 | 1541 len = qxestrlen (name); |
428 | 1542 rootdir = (path >= name + len - 1 |
1543 && (IS_DIRECTORY_SEP (*path) || *path == 0)); | |
771 | 1544 |
1545 if (is_unc_volume (name)) | |
1546 { | |
1547 DWORD attrs = unc_volume_file_attributes (name); | |
1548 | |
1549 if (attrs == -1) | |
1550 return -1; | |
428 | 1551 |
771 | 1552 memset (&wfd, 0, sizeof (wfd)); |
1553 wfd.dwFileAttributes = attrs; | |
1554 wfd.ftCreationTime = utc_base_ft; | |
1555 wfd.ftLastAccessTime = utc_base_ft; | |
1556 wfd.ftLastWriteTime = utc_base_ft; | |
1557 /* XEmacs deleted: strcpy (wfd.cFileName, name); | |
1558 Not used later on. */ | |
1559 } | |
1560 else if (rootdir) | |
428 | 1561 { |
1562 if (!IS_DIRECTORY_SEP (name[len-1])) | |
867 | 1563 qxestrcat (name, (Ibyte *) "\\"); |
2526 | 1564 /* File has already been resolved and we don't want to do it again |
1565 in case of lstat() */ | |
771 | 1566 C_STRING_TO_TSTR (name, nameext); |
1567 if (qxeGetDriveType (nameext) < 2) | |
428 | 1568 { |
819 | 1569 SetErrorMode (errm); |
428 | 1570 errno = ENOENT; |
1571 return -1; | |
1572 } | |
1573 memset (&wfd, 0, sizeof (wfd)); | |
1574 wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY; | |
1575 wfd.ftCreationTime = utc_base_ft; | |
1576 wfd.ftLastAccessTime = utc_base_ft; | |
1577 wfd.ftLastWriteTime = utc_base_ft; | |
771 | 1578 /* XEmacs deleted: strcpy (wfd.cFileName, name); |
1579 Not used later on. */ | |
428 | 1580 } |
1581 else | |
1582 { | |
1583 if (IS_DIRECTORY_SEP (name[len-1])) | |
1584 name[len - 1] = 0; | |
1585 | |
1586 /* (This is hacky, but helps when doing file completions on | |
1587 network drives.) Optimize by using information available from | |
1588 active readdir if possible. */ | |
771 | 1589 if (dir_pathname) |
1590 { | |
1591 len = qxestrlen (dir_pathname); | |
1592 if (len && IS_DIRECTORY_SEP (dir_pathname[len-1])) | |
1593 len--; | |
1594 } | |
1595 if (dir_find_handle != INVALID_HANDLE_VALUE | |
1596 && dir_pathname | |
801 | 1597 && qxestrncasecmp_i18n (dir_pathname, name, len) == 0 |
771 | 1598 && IS_DIRECTORY_SEP (name[len]) |
1599 && qxestrcasecmp_i18n (name + len + 1, | |
867 | 1600 (Ibyte *) dir_static.d_name) == 0) |
428 | 1601 { |
1602 /* This was the last entry returned by readdir. */ | |
1603 wfd = dir_find_data; | |
1604 } | |
1605 else | |
1606 { | |
771 | 1607 C_STRING_TO_TSTR (name, nameext); |
1608 fh = qxeFindFirstFile (nameext, &wfd); | |
1609 if (fh == INVALID_HANDLE_VALUE) | |
1610 { | |
819 | 1611 SetErrorMode (errm); |
771 | 1612 errno = ENOENT; |
1613 return -1; | |
1614 } | |
1615 FindClose (fh); | |
1616 /* XEmacs: Don't need to convert wfd.cFileName because | |
1617 not used later on. */ | |
428 | 1618 } |
1619 } | |
1620 | |
1621 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) | |
1622 { | |
1623 buf->st_mode = _S_IFDIR; | |
1624 buf->st_nlink = 2; /* doesn't really matter */ | |
1625 fake_inode = 0; /* this doesn't either I think */ | |
1626 } | |
771 | 1627 else |
428 | 1628 { |
771 | 1629 if (!NILP (Vmswindows_get_true_file_attributes)) |
2526 | 1630 /* File has already been resolved and we don't want to do it again |
1631 in case of lstat() */ | |
771 | 1632 C_STRING_TO_TSTR (name, nameext); |
1633 if (!NILP (Vmswindows_get_true_file_attributes) | |
1634 /* No access rights required to get info. */ | |
1635 && (fh = qxeCreateFile (nameext, 0, 0, NULL, OPEN_EXISTING, 0, NULL)) | |
1636 != INVALID_HANDLE_VALUE) | |
1637 { | |
2526 | 1638 /* This is more accurate in terms of getting the correct number |
771 | 1639 of links, but is quite slow (it is noticable when Emacs is |
1640 making a list of file name completions). */ | |
1641 BY_HANDLE_FILE_INFORMATION info; | |
428 | 1642 |
771 | 1643 if (GetFileInformationByHandle (fh, &info)) |
1644 { | |
1645 buf->st_nlink = (short) info.nNumberOfLinks; | |
1646 /* Might as well use file index to fake inode values, but this | |
1647 is not guaranteed to be unique unless we keep a handle open | |
1648 all the time (even then there are situations where it is | |
1649 not unique). Reputedly, there are at most 48 bits of info | |
1650 (on NTFS, presumably less on FAT). */ | |
1651 fake_inode = info.nFileIndexLow ^ info.nFileIndexHigh; | |
1652 } | |
1653 else | |
1654 { | |
1655 buf->st_nlink = 1; | |
1656 fake_inode = 0; | |
1657 } | |
428 | 1658 |
1659 switch (GetFileType (fh)) | |
1660 { | |
1661 case FILE_TYPE_DISK: | |
1662 buf->st_mode = _S_IFREG; | |
1663 break; | |
1664 case FILE_TYPE_PIPE: | |
1665 buf->st_mode = _S_IFIFO; | |
1666 break; | |
1667 case FILE_TYPE_CHAR: | |
1668 case FILE_TYPE_UNKNOWN: | |
1669 default: | |
1670 buf->st_mode = _S_IFCHR; | |
1671 } | |
1672 CloseHandle (fh); | |
1673 } | |
1674 else | |
1675 { | |
771 | 1676 /* Don't bother to make this information more accurate. */ |
1677 buf->st_mode = _S_IFREG; | |
1678 buf->st_nlink = 1; | |
1679 fake_inode = 0; | |
428 | 1680 } |
2526 | 1681 |
1682 if (mswindows_shortcuts_are_symlinks && | |
1683 buf->st_mode == _S_IFREG) | |
1684 { | |
1685 len = qxestrlen (name); | |
1686 if (len > 4 && !qxestrcasecmp_ascii (name + len - 4, ".LNK")) | |
1687 { | |
1688 /* check if link is valid */ | |
1689 Ibyte *resolved = mswindows_read_link (name); | |
1690 if (resolved) | |
1691 { | |
1692 xfree (resolved, Ibyte *); | |
1693 buf->st_mode = S_IFLNK; | |
1694 } | |
1695 } | |
1696 } | |
428 | 1697 } |
1698 | |
819 | 1699 SetErrorMode (errm); |
1700 | |
428 | 1701 #if 0 |
771 | 1702 /* XEmacs: Removed the fake-inodes code here, which was if 0'd out. |
1703 If you want it, look in w32.c in Emacs 20.6. */ | |
428 | 1704 #endif |
1705 | |
771 | 1706 /* MSVC defines _ino_t to be short; other libc's might not. */ |
1707 if (sizeof (buf->st_ino) == 2) | |
1708 buf->st_ino = (unsigned short) (fake_inode ^ (fake_inode >> 16)); | |
1709 else | |
1710 buf->st_ino = (unsigned short) fake_inode; | |
428 | 1711 |
1712 /* consider files to belong to current user */ | |
771 | 1713 buf->st_uid = the_passwd.pw_uid; |
1714 buf->st_gid = the_passwd.pw_gid; | |
428 | 1715 |
771 | 1716 /* volume_info is set by get_volume_info */ |
428 | 1717 buf->st_dev = volume_info.serialnum; |
1718 buf->st_rdev = volume_info.serialnum; | |
1719 | |
771 | 1720 |
428 | 1721 buf->st_size = wfd.nFileSizeLow; |
1722 | |
1723 /* Convert timestamps to Unix format. */ | |
771 | 1724 buf->st_mtime = mswindows_convert_time (wfd.ftLastWriteTime); |
1725 buf->st_atime = mswindows_convert_time (wfd.ftLastAccessTime); | |
428 | 1726 if (buf->st_atime == 0) buf->st_atime = buf->st_mtime; |
771 | 1727 buf->st_ctime = mswindows_convert_time (wfd.ftCreationTime); |
428 | 1728 if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime; |
1729 | |
1730 /* determine rwx permissions */ | |
1731 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) | |
1732 permission = _S_IREAD; | |
1733 else | |
1734 permission = _S_IREAD | _S_IWRITE; | |
1735 | |
1736 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) | |
1737 permission |= _S_IEXEC; | |
1204 | 1738 else if (mswindows_is_executable (name)) |
771 | 1739 permission |= _S_IEXEC; |
428 | 1740 |
1741 buf->st_mode |= permission | (permission >> 3) | (permission >> 6); | |
1742 | |
1743 return 0; | |
1744 } | |
1745 | |
1746 int | |
771 | 1747 mswindows_utime (Lisp_Object path, struct utimbuf *times) |
428 | 1748 { |
771 | 1749 /* #### Supposedly we're providing this because standard utime() |
1750 might not work; or at the very least to get consistent results | |
1751 since we replace other time-handling routines in stat. But out | |
1752 replacement doesn't seem to work, probably due to some subtle bug | |
1753 in this routine, which should be investigated eventually. So for | |
1754 the moment, we just use utime(), which conceivably might be | |
1755 slightly off in comparison with our own routines? Seems strange, | |
1756 and so far no problems seen. --ben */ | |
428 | 1757 |
771 | 1758 struct utimbuf deftime; |
1759 #if 0 | |
1760 HANDLE fh; | |
1761 #endif | |
1762 static FILETIME mtime; | |
1763 static FILETIME atime; | |
1764 Extbyte *filename; | |
428 | 1765 |
771 | 1766 if (times == NULL) |
428 | 1767 { |
771 | 1768 deftime.modtime = deftime.actime = time (NULL); |
1769 times = &deftime; | |
428 | 1770 } |
1771 | |
2526 | 1772 LISP_PATHNAME_CONVERT_OUT (path, filename); |
771 | 1773 /* APA: SetFileTime fails to set mtime correctly (always 1-Jan-1970) */ |
1774 #if 0 | |
1775 /* Need write access to set times. */ | |
1776 fh = qxeCreateFile (filename, GENERIC_WRITE, | |
1777 FILE_SHARE_READ | FILE_SHARE_WRITE, | |
1778 0, OPEN_EXISTING, 0, NULL); | |
1779 if (fh) | |
1780 { | |
1781 convert_from_time_t (times->actime, &atime); | |
1782 convert_from_time_t (times->modtime, &mtime); | |
1783 if (!SetFileTime (fh, NULL, &atime, &mtime)) | |
1784 { | |
1785 CloseHandle (fh); | |
1786 errno = EACCES; | |
1787 return -1; | |
1788 } | |
1789 CloseHandle (fh); | |
1790 } | |
1791 else | |
1792 { | |
1793 errno = EINVAL; | |
1794 return -1; | |
1795 } | |
1796 return 0; | |
1797 #else | |
1798 { | |
1799 struct _utimbuf newtimes; | |
1800 | |
1801 newtimes.actime = times->actime; | |
1802 newtimes.modtime = times->modtime; | |
1803 | |
1804 if (XEUNICODE_P) | |
1805 return _wutime ((const wchar_t *) filename, &newtimes); | |
1806 else | |
1807 return _utime (filename, &newtimes); | |
1808 } | |
1809 #endif | |
1810 } | |
1811 | |
867 | 1812 Ibyte * |
771 | 1813 mswindows_getdcwd (int drivelet) |
1814 { | |
1815 Extbyte *cwdext; | |
867 | 1816 Ibyte *cwd; |
771 | 1817 |
1818 if (XEUNICODE_P) | |
1819 cwdext = (Extbyte *) _wgetdcwd (drivelet, NULL, 0); | |
1820 else | |
1821 cwdext = _getdcwd (drivelet, NULL, 0); | |
3648 | 1822 if (cwdext == NULL) return NULL; |
771 | 1823 TSTR_TO_C_STRING_MALLOC (cwdext, cwd); |
1726 | 1824 xfree (cwdext, Extbyte *); |
771 | 1825 return cwd; |
428 | 1826 } |
1827 | |
442 | 1828 |
1829 /*--------------------------------------------------------------------*/ | |
1830 /* Memory-mapped files */ | |
1831 /*--------------------------------------------------------------------*/ | |
1832 | |
428 | 1833 int |
867 | 1834 open_input_file (file_data *p_file, const Ibyte *filename) |
428 | 1835 { |
442 | 1836 /* Synched with FSF 20.6. We fixed some warnings. */ |
428 | 1837 HANDLE file; |
1838 HANDLE file_mapping; | |
771 | 1839 void *file_base; |
428 | 1840 DWORD size, upper_size; |
771 | 1841 Extbyte *fileext; |
428 | 1842 |
2526 | 1843 PATHNAME_CONVERT_OUT (filename, fileext); |
771 | 1844 |
1845 file = qxeCreateFile (fileext, GENERIC_READ, FILE_SHARE_READ, NULL, | |
1846 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); | |
428 | 1847 if (file == INVALID_HANDLE_VALUE) |
1848 return FALSE; | |
1849 | |
1850 size = GetFileSize (file, &upper_size); | |
771 | 1851 file_mapping = qxeCreateFileMapping (file, NULL, PAGE_READONLY, |
1852 0, size, NULL); | |
428 | 1853 if (!file_mapping) |
1854 return FALSE; | |
1855 | |
1856 file_base = MapViewOfFile (file_mapping, FILE_MAP_READ, 0, 0, size); | |
1857 if (file_base == 0) | |
1858 return FALSE; | |
1859 | |
771 | 1860 p_file->name = filename; |
442 | 1861 p_file->size = size; |
1862 p_file->file = file; | |
1863 p_file->file_mapping = file_mapping; | |
771 | 1864 p_file->file_base = file_base; |
442 | 1865 |
1866 return TRUE; | |
1867 } | |
1868 | |
1869 int | |
867 | 1870 open_output_file (file_data *p_file, const Ibyte *filename, |
771 | 1871 unsigned long size) |
442 | 1872 { |
1873 /* Synched with FSF 20.6. We fixed some warnings. */ | |
1874 HANDLE file; | |
1875 HANDLE file_mapping; | |
771 | 1876 void *file_base; |
1877 Extbyte *fileext; | |
442 | 1878 |
2526 | 1879 PATHNAME_CONVERT_OUT (filename, fileext); |
771 | 1880 |
1881 file = qxeCreateFile (fileext, GENERIC_READ | GENERIC_WRITE, 0, NULL, | |
1882 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); | |
442 | 1883 if (file == INVALID_HANDLE_VALUE) |
1884 return FALSE; | |
1885 | |
771 | 1886 file_mapping = qxeCreateFileMapping (file, NULL, PAGE_READWRITE, |
1887 0, size, NULL); | |
442 | 1888 if (!file_mapping) |
1889 return FALSE; | |
1890 | |
1891 file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size); | |
1892 if (file_base == NULL) | |
1893 return FALSE; | |
1894 | |
1895 p_file->name = filename; | |
428 | 1896 p_file->size = size; |
1897 p_file->file = file; | |
1898 p_file->file_mapping = file_mapping; | |
771 | 1899 p_file->file_base = file_base; |
428 | 1900 |
1901 return TRUE; | |
1902 } | |
1903 | |
442 | 1904 #if 1 /* !defined(MINGW) */ |
1905 /* Return pointer to section header for section containing the given | |
1906 relative virtual address. */ | |
1907 static IMAGE_SECTION_HEADER * | |
771 | 1908 rva_to_section (DWORD rva, IMAGE_NT_HEADERS *nt_header) |
442 | 1909 { |
1910 /* Synched with FSF 20.6. We added MINGW stuff. */ | |
1911 PIMAGE_SECTION_HEADER section; | |
1912 int i; | |
1913 | |
1914 section = IMAGE_FIRST_SECTION (nt_header); | |
1915 | |
1916 for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++) | |
1917 { | |
1918 /* Some linkers (eg. the NT SDK linker I believe) swapped the | |
1919 meaning of these two values - or rather, they ignored | |
1920 VirtualSize entirely and always set it to zero. This affects | |
1921 some very old exes (eg. gzip dated Dec 1993). Since | |
1922 mswindows_executable_type relies on this function to work reliably, | |
1923 we need to cope with this. */ | |
1924 DWORD real_size = max (section->SizeOfRawData, | |
1925 section->Misc.VirtualSize); | |
1926 if (rva >= section->VirtualAddress | |
1927 && rva < section->VirtualAddress + real_size) | |
1928 return section; | |
1929 section++; | |
1930 } | |
1931 return NULL; | |
1932 } | |
1933 #endif | |
1934 | |
1935 void | |
867 | 1936 mswindows_executable_type (const Ibyte *filename, int *is_dos_app, |
771 | 1937 int *is_cygnus_app) |
442 | 1938 { |
1939 /* Synched with FSF 20.6. We added MINGW stuff and casts. */ | |
1940 file_data executable; | |
867 | 1941 Ibyte *p; |
442 | 1942 |
1943 /* Default values in case we can't tell for sure. */ | |
1944 *is_dos_app = FALSE; | |
1945 *is_cygnus_app = FALSE; | |
1946 | |
1947 if (!open_input_file (&executable, filename)) | |
1948 return; | |
1949 | |
771 | 1950 p = qxestrrchr (filename, '.'); |
442 | 1951 |
1952 /* We can only identify DOS .com programs from the extension. */ | |
2367 | 1953 if (p && qxestrcasecmp_ascii (p, ".com") == 0) |
442 | 1954 *is_dos_app = TRUE; |
2367 | 1955 else if (p && (qxestrcasecmp_ascii (p, ".bat") == 0 || |
1956 qxestrcasecmp_ascii (p, ".cmd") == 0)) | |
442 | 1957 { |
1958 /* A DOS shell script - it appears that CreateProcess is happy to | |
1959 accept this (somewhat surprisingly); presumably it looks at | |
1960 COMSPEC to determine what executable to actually invoke. | |
1961 Therefore, we have to do the same here as well. */ | |
1962 /* Actually, I think it uses the program association for that | |
1963 extension, which is defined in the registry. */ | |
1964 p = egetenv ("COMSPEC"); | |
1965 if (p) | |
1966 mswindows_executable_type (p, is_dos_app, is_cygnus_app); | |
1967 } | |
1968 else | |
1969 { | |
1970 /* Look for DOS .exe signature - if found, we must also check that | |
1971 it isn't really a 16- or 32-bit Windows exe, since both formats | |
1972 start with a DOS program stub. Note that 16-bit Windows | |
1973 executables use the OS/2 1.x format. */ | |
1974 | |
1975 #if 0 /* defined( MINGW ) */ | |
771 | 1976 /* mingw doesn't have enough headers to detect cygwin |
442 | 1977 apps, just do what we can. */ |
771 | 1978 FILHDR *exe_header; |
442 | 1979 |
771 | 1980 exe_header = (FILHDR *) executable.file_base; |
442 | 1981 if (exe_header->e_magic != DOSMAGIC) |
1982 goto unwind; | |
1983 | |
771 | 1984 if ((char *) exe_header->e_lfanew > (char *) executable.size) |
442 | 1985 { |
1986 /* Some dos headers (pkunzip) have bogus e_lfanew fields. */ | |
1987 *is_dos_app = TRUE; | |
1988 } | |
1989 else if (exe_header->nt_signature != NT_SIGNATURE) | |
1990 { | |
1991 *is_dos_app = TRUE; | |
1992 } | |
1993 #else | |
771 | 1994 IMAGE_DOS_HEADER *dos_header; |
1995 IMAGE_NT_HEADERS *nt_header; | |
442 | 1996 |
1997 dos_header = (PIMAGE_DOS_HEADER) executable.file_base; | |
1998 if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) | |
1999 goto unwind; | |
2000 | |
771 | 2001 nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + |
2002 dos_header->e_lfanew); | |
442 | 2003 |
771 | 2004 if ((char *) nt_header > (char *) dos_header + executable.size) |
442 | 2005 { |
2006 /* Some dos headers (pkunzip) have bogus e_lfanew fields. */ | |
2007 *is_dos_app = TRUE; | |
2008 } | |
2009 else if (nt_header->Signature != IMAGE_NT_SIGNATURE && | |
2010 LOWORD (nt_header->Signature) != IMAGE_OS2_SIGNATURE) | |
2011 { | |
2012 *is_dos_app = TRUE; | |
2013 } | |
2014 else if (nt_header->Signature == IMAGE_NT_SIGNATURE) | |
2015 { | |
2016 /* Look for cygwin.dll in DLL import list. */ | |
2017 IMAGE_DATA_DIRECTORY import_dir = | |
771 | 2018 nt_header->OptionalHeader. |
2019 DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; | |
2020 IMAGE_IMPORT_DESCRIPTOR *imports; | |
2021 IMAGE_SECTION_HEADER *section; | |
442 | 2022 |
2023 section = rva_to_section (import_dir.VirtualAddress, nt_header); | |
771 | 2024 imports = |
2025 (IMAGE_IMPORT_DESCRIPTOR *) RVA_TO_PTR (import_dir.VirtualAddress, | |
2026 section, executable); | |
2027 | |
442 | 2028 for ( ; imports->Name; imports++) |
2029 { | |
771 | 2030 Extbyte *dllname_ext = |
2031 (Extbyte *) RVA_TO_PTR (imports->Name, section, executable); | |
867 | 2032 Ibyte *dllname; |
771 | 2033 |
2034 EXTERNAL_TO_C_STRING (dllname_ext, dllname, Qbinary); | |
442 | 2035 |
2036 /* The exact name of the cygwin dll has changed with | |
2037 various releases, but hopefully this will be reasonably | |
2038 future proof. */ | |
867 | 2039 if (qxestrncasecmp (dllname, (Ibyte *) "cygwin", 6) == 0) |
442 | 2040 { |
2041 *is_cygnus_app = TRUE; | |
2042 break; | |
2043 } | |
2044 } | |
2045 } | |
2046 #endif | |
2047 } | |
2048 | |
2049 unwind: | |
2050 close_file_data (&executable); | |
2051 } | |
2052 | |
428 | 2053 /* Close the system structures associated with the given file. */ |
2054 void | |
2055 close_file_data (file_data *p_file) | |
2056 { | |
611 | 2057 UnmapViewOfFile (p_file->file_base); |
2058 CloseHandle (p_file->file_mapping); | |
2059 CloseHandle (p_file->file); | |
428 | 2060 } |
2061 | |
771 | 2062 |
2063 /* Some miscellaneous functions that are Windows specific, but not GUI | |
2064 specific (ie. are applicable in terminal or batch mode as well). */ | |
2065 | |
4487
866b84b7c97e
Remove interactive specs from #'mswindows-{short,long}-file-name,
Aidan Kehoe <kehoea@parhasard.net>
parents:
3648
diff
changeset
|
2066 DEFUN ("mswindows-short-file-name", Fmswindows_short_file_name, 1, 1, 0, /* |
771 | 2067 Return the short file name version (8.3) of the full path of FILENAME. |
2068 If FILENAME does not exist, return nil. | |
2069 All path elements in FILENAME are converted to their short names. | |
2070 */ | |
2071 (filename)) | |
2072 { | |
2421 | 2073 Extbyte shortname[PATH_MAX_EXTERNAL]; |
771 | 2074 Extbyte *fileext; |
867 | 2075 Ibyte *shortint; |
771 | 2076 |
2077 CHECK_STRING (filename); | |
2078 | |
2079 /* first expand it. */ | |
2080 filename = Fexpand_file_name (filename, Qnil); | |
2081 | |
2526 | 2082 LISP_PATHNAME_CONVERT_OUT (filename, fileext); |
771 | 2083 /* luckily, this returns the short version of each element in the path. */ |
2084 if (qxeGetShortPathName (fileext, shortname, | |
2085 sizeof (shortname) / XETCHAR_SIZE) == 0) | |
2086 return Qnil; | |
2087 | |
2088 TSTR_TO_C_STRING (shortname, shortint); | |
2089 MSWINDOWS_NORMALIZE_FILENAME (shortint); | |
2090 | |
1204 | 2091 return build_intstring (shortint); |
771 | 2092 } |
2093 | |
2094 | |
4487
866b84b7c97e
Remove interactive specs from #'mswindows-{short,long}-file-name,
Aidan Kehoe <kehoea@parhasard.net>
parents:
3648
diff
changeset
|
2095 DEFUN ("mswindows-long-file-name", Fmswindows_long_file_name, 1, 1, 0, /* |
771 | 2096 Return the long file name version of the full path of FILENAME. |
2097 If FILENAME does not exist, return nil. | |
2098 All path elements in FILENAME are converted to their long names. | |
2099 */ | |
2100 (filename)) | |
2101 { | |
867 | 2102 Ibyte *longname, *canon; |
771 | 2103 Lisp_Object ret; |
2104 | |
2105 CHECK_STRING (filename); | |
2106 | |
2107 /* first expand it. */ | |
2108 filename = Fexpand_file_name (filename, Qnil); | |
2109 | |
2110 if (!(longname = mswindows_get_long_filename (XSTRING_DATA (filename)))) | |
2111 return Qnil; | |
2112 | |
2113 canon = mswindows_canonicalize_filename (longname); | |
1204 | 2114 ret = build_intstring (canon); |
1726 | 2115 xfree (canon, Ibyte *); |
2116 xfree (longname, Ibyte *); | |
771 | 2117 return ret; |
2118 } | |
2119 | |
814 | 2120 |
2121 void | |
2122 init_nt (void) | |
2123 { | |
2124 /* Initial preparation for subprocess support: replace our standard | |
2125 handles with non-inheritable versions. | |
2126 | |
2127 #### Do we still need this? This is left over from the old process | |
2128 support. */ | |
2129 { | |
2130 HANDLE parent; | |
2131 HANDLE stdin_save = INVALID_HANDLE_VALUE; | |
2132 HANDLE stdout_save = INVALID_HANDLE_VALUE; | |
2133 HANDLE stderr_save = INVALID_HANDLE_VALUE; | |
2134 | |
2135 parent = GetCurrentProcess (); | |
2136 | |
2137 /* ignore errors when duplicating and closing; typically the | |
2138 handles will be invalid when running as a gui program. */ | |
2139 DuplicateHandle (parent, | |
2140 GetStdHandle (STD_INPUT_HANDLE), | |
2141 parent, | |
2142 &stdin_save, | |
2143 0, | |
2144 FALSE, | |
2145 DUPLICATE_SAME_ACCESS); | |
2146 | |
2147 DuplicateHandle (parent, | |
2148 GetStdHandle (STD_OUTPUT_HANDLE), | |
2149 parent, | |
2150 &stdout_save, | |
2151 0, | |
2152 FALSE, | |
2153 DUPLICATE_SAME_ACCESS); | |
2154 | |
2155 DuplicateHandle (parent, | |
2156 GetStdHandle (STD_ERROR_HANDLE), | |
2157 parent, | |
2158 &stderr_save, | |
2159 0, | |
2160 FALSE, | |
2161 DUPLICATE_SAME_ACCESS); | |
2162 | |
2163 retry_fclose (stdin); | |
2164 retry_fclose (stdout); | |
2165 retry_fclose (stderr); | |
2166 | |
2167 if (stdin_save != INVALID_HANDLE_VALUE) | |
2168 _open_osfhandle ((long) stdin_save, O_TEXT); | |
2169 else | |
2170 _open ("nul", O_TEXT | O_NOINHERIT | O_RDONLY); | |
2171 _fdopen (0, "r"); | |
2172 | |
2173 if (stdout_save != INVALID_HANDLE_VALUE) | |
2174 _open_osfhandle ((long) stdout_save, O_TEXT); | |
2175 else | |
2176 _open ("nul", O_TEXT | O_NOINHERIT | O_WRONLY); | |
2177 _fdopen (1, "w"); | |
2178 | |
2179 if (stderr_save != INVALID_HANDLE_VALUE) | |
2180 _open_osfhandle ((long) stderr_save, O_TEXT); | |
2181 else | |
2182 _open ("nul", O_TEXT | O_NOINHERIT | O_WRONLY); | |
2183 _fdopen (2, "w"); | |
2184 } | |
2185 | |
2186 /* determine which drives are fixed, for get_cached_volume_information */ | |
2187 { | |
2188 /* GetDriveType must have trailing backslash. */ | |
867 | 2189 Ibyte drive[] = "A:\\"; |
814 | 2190 |
2191 /* Loop over all possible drive letters */ | |
2192 while (*drive <= 'Z') | |
2193 { | |
2194 Extbyte *driveext; | |
2195 | |
2526 | 2196 PATHNAME_CONVERT_OUT (drive, driveext); |
814 | 2197 |
2198 /* Record if this drive letter refers to a fixed drive. */ | |
2199 fixed_drives[DRIVE_INDEX (*drive)] = | |
2200 (qxeGetDriveType (driveext) == DRIVE_FIXED); | |
2201 | |
2202 (*drive)++; | |
2203 } | |
2204 | |
2205 /* Reset the volume info cache. */ | |
2206 volume_cache = NULL; | |
2207 } | |
2208 } | |
2209 | |
771 | 2210 void |
2211 syms_of_nt (void) | |
2212 { | |
2213 DEFSUBR (Fmswindows_short_file_name); | |
2214 DEFSUBR (Fmswindows_long_file_name); | |
2215 } | |
2216 | |
440 | 2217 void |
2218 vars_of_nt (void) | |
2219 { | |
771 | 2220 DEFVAR_INT ("mswindows-fake-unix-uid", &mswindows_fake_unix_uid /* |
440 | 2221 *Set uid returned by `user-uid' and `user-real-uid'. |
771 | 2222 Under NT and 9x, there are no uids, and even no almighty user called root. |
2223 By setting this variable, you can have any uid of choice. Default is 0. | |
440 | 2224 Changes to this variable take effect immediately. |
2225 */ ); | |
771 | 2226 mswindows_fake_unix_uid = 0; |
2227 | |
2228 DEFVAR_LISP ("mswindows-get-true-file-attributes", &Vmswindows_get_true_file_attributes /* | |
2229 Non-nil means determine accurate link count in file-attributes. | |
2230 This option slows down file-attributes noticeably, so is disabled by | |
2231 default. Note that it is only useful for files on NTFS volumes, | |
2232 where hard links are supported. | |
2233 */ ); | |
2234 Vmswindows_get_true_file_attributes = Qnil; | |
440 | 2235 } |
2236 | |
428 | 2237 /* end of nt.c */ |