comparison src/nt.c @ 771:943eaba38521

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