Auto-commit of generated files.
[bpt/emacs.git] / src / w32.c
CommitLineData
b46a6a83 1/* Utility and Unix shadow routines for GNU Emacs on the Microsoft Windows API.
ab422c4d 2 Copyright (C) 1994-1995, 2000-2013 Free Software Foundation, Inc.
95ed0025 3
3b7ad313
EN
4This file is part of GNU Emacs.
5
9ec0b715 6GNU Emacs is free software: you can redistribute it and/or modify
3b7ad313 7it under the terms of the GNU General Public License as published by
9ec0b715
GM
8the Free Software Foundation, either version 3 of the License, or
9(at your option) any later version.
3b7ad313
EN
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
9ec0b715 17along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
95ed0025 18
9ec0b715 19/*
95ed0025
RS
20 Geoff Voelker (voelker@cs.washington.edu) 7-29-94
21*/
76b3903d 22#include <stddef.h> /* for offsetof */
95ed0025
RS
23#include <stdlib.h>
24#include <stdio.h>
ad9e2d54 25#include <float.h> /* for DBL_EPSILON */
95ed0025 26#include <io.h>
480b0c5b 27#include <errno.h>
95ed0025
RS
28#include <fcntl.h>
29#include <ctype.h>
480b0c5b 30#include <signal.h>
b3308d2e 31#include <sys/file.h>
8f5e14c8 32#include <time.h> /* must be before nt/inc/sys/time.h, for MinGW64 */
480b0c5b 33#include <sys/time.h>
16bb7578 34#include <sys/utime.h>
7c80d5ec 35#include <math.h>
480b0c5b
GV
36
37/* must include CRT headers *before* config.h */
4838e624 38
4838e624 39#include <config.h>
6ee4509a 40#include <mbstring.h> /* for _mbspbrk, _mbslwr, _mbsrchr, ... */
4838e624 41
480b0c5b
GV
42#undef access
43#undef chdir
44#undef chmod
45#undef creat
46#undef ctime
47#undef fopen
48#undef link
49#undef mkdir
50#undef mktemp
51#undef open
52#undef rename
53#undef rmdir
54#undef unlink
55
56#undef close
57#undef dup
58#undef dup2
59#undef pipe
60#undef read
61#undef write
95ed0025 62
d8fcc1b9
AI
63#undef strerror
64
97a93095
EZ
65#undef localtime
66
95ed0025 67#include "lisp.h"
95ed0025
RS
68
69#include <pwd.h>
3d19b645 70#include <grp.h>
95ed0025 71
a18d7de6
EZ
72/* MinGW64 (_W64) defines these in its _mingw.h. */
73#if defined(__GNUC__) && !defined(_W64)
971bce75
AI
74#define _ANONYMOUS_UNION
75#define _ANONYMOUS_STRUCT
76#endif
480b0c5b 77#include <windows.h>
b8526f6e
EZ
78/* Some versions of compiler define MEMORYSTATUSEX, some don't, so we
79 use a different name to avoid compilation problems. */
80typedef struct _MEMORY_STATUS_EX {
bedf4aab
JB
81 DWORD dwLength;
82 DWORD dwMemoryLoad;
83 DWORDLONG ullTotalPhys;
84 DWORDLONG ullAvailPhys;
85 DWORDLONG ullTotalPageFile;
86 DWORDLONG ullAvailPageFile;
87 DWORDLONG ullTotalVirtual;
88 DWORDLONG ullAvailVirtual;
89 DWORDLONG ullAvailExtendedVirtual;
b8526f6e 90} MEMORY_STATUS_EX,*LPMEMORY_STATUS_EX;
7c80d5ec 91
634d3003 92#include <lmcons.h>
2d5324c5 93#include <shlobj.h>
00b3b7b3 94
7c80d5ec
EZ
95#include <tlhelp32.h>
96#include <psapi.h>
a6fc3b5c 97#ifndef _MSC_VER
69e847be 98#include <w32api.h>
a6fc3b5c 99#endif
a18d7de6 100#if _WIN32_WINNT < 0x0500
5e617bc2 101#if !defined (__MINGW32__) || __W32API_MAJOR_VERSION < 3 || (__W32API_MAJOR_VERSION == 3 && __W32API_MINOR_VERSION < 15)
7c80d5ec 102/* This either is not in psapi.h or guarded by higher value of
a6d3e72e 103 _WIN32_WINNT than what we use. w32api supplied with MinGW 3.15
69e847be 104 defines it in psapi.h */
7c80d5ec 105typedef struct _PROCESS_MEMORY_COUNTERS_EX {
cb576b5c
FP
106 DWORD cb;
107 DWORD PageFaultCount;
108 SIZE_T PeakWorkingSetSize;
109 SIZE_T WorkingSetSize;
110 SIZE_T QuotaPeakPagedPoolUsage;
111 SIZE_T QuotaPagedPoolUsage;
112 SIZE_T QuotaPeakNonPagedPoolUsage;
113 SIZE_T QuotaNonPagedPoolUsage;
114 SIZE_T PagefileUsage;
115 SIZE_T PeakPagefileUsage;
116 SIZE_T PrivateUsage;
7c80d5ec 117} PROCESS_MEMORY_COUNTERS_EX,*PPROCESS_MEMORY_COUNTERS_EX;
69e847be 118#endif
a18d7de6 119#endif
7c80d5ec 120
6dad7178
EZ
121#include <winioctl.h>
122#include <aclapi.h>
66447e07
EZ
123#include <sddl.h>
124
125#include <sys/acl.h>
126
127/* This is not in MinGW's sddl.h (but they are in MSVC headers), so we
128 define them by hand if not already defined. */
129#ifndef SDDL_REVISION_1
130#define SDDL_REVISION_1 1
131#endif /* SDDL_REVISION_1 */
6dad7178 132
c6e72e17
EZ
133#if defined(_MSC_VER) || defined(_W64)
134/* MSVC and MinGW64 don't provide the definition of
135 REPARSE_DATA_BUFFER and the associated macros, except on ntifs.h,
136 which cannot be included because it triggers conflicts with other
137 Windows API headers. So we define it here by hand. */
6dad7178
EZ
138
139typedef struct _REPARSE_DATA_BUFFER {
140 ULONG ReparseTag;
141 USHORT ReparseDataLength;
142 USHORT Reserved;
143 union {
144 struct {
145 USHORT SubstituteNameOffset;
146 USHORT SubstituteNameLength;
147 USHORT PrintNameOffset;
148 USHORT PrintNameLength;
149 ULONG Flags;
150 WCHAR PathBuffer[1];
151 } SymbolicLinkReparseBuffer;
152 struct {
153 USHORT SubstituteNameOffset;
154 USHORT SubstituteNameLength;
155 USHORT PrintNameOffset;
156 USHORT PrintNameLength;
157 WCHAR PathBuffer[1];
158 } MountPointReparseBuffer;
159 struct {
160 UCHAR DataBuffer[1];
161 } GenericReparseBuffer;
162 } DUMMYUNIONNAME;
163} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
164
c7b36b95 165#ifndef FILE_DEVICE_FILE_SYSTEM
88c4a13c 166#define FILE_DEVICE_FILE_SYSTEM 9
c7b36b95
FP
167#endif
168#ifndef METHOD_BUFFERED
88c4a13c 169#define METHOD_BUFFERED 0
c7b36b95
FP
170#endif
171#ifndef FILE_ANY_ACCESS
88c4a13c 172#define FILE_ANY_ACCESS 0x00000000
c7b36b95
FP
173#endif
174#ifndef CTL_CODE
88c4a13c 175#define CTL_CODE(t,f,m,a) (((t)<<16)|((a)<<14)|((f)<<2)|(m))
c7b36b95 176#endif
88c4a13c
EZ
177#define FSCTL_GET_REPARSE_POINT \
178 CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS)
6dad7178
EZ
179#endif
180
7d701334 181/* TCP connection support. */
480b0c5b
GV
182#include <sys/socket.h>
183#undef socket
184#undef bind
185#undef connect
186#undef htons
187#undef ntohs
188#undef inet_addr
189#undef gethostname
190#undef gethostbyname
191#undef getservbyname
ecd270eb 192#undef getpeername
380961a6 193#undef shutdown
962955c5
JR
194#undef setsockopt
195#undef listen
196#undef getsockname
197#undef accept
198#undef recvfrom
199#undef sendto
00b3b7b3 200
489f9371 201#include "w32.h"
95ef7787 202#include <dirent.h>
501199a3 203#include "w32common.h"
489f9371 204#include "w32heap.h"
a68089e4 205#include "w32select.h"
253574a6 206#include "systime.h"
f481eb31 207#include "dispextern.h" /* for xstrcasecmp */
7c80d5ec 208#include "coding.h" /* for Vlocale_coding_system */
253574a6 209
973f782d
EZ
210#include "careadlinkat.h"
211#include "allocator.h"
212
1eb8fd91 213/* For serial_configure and serial_open. */
d888760c 214#include "process.h"
d888760c 215
2d5324c5
JR
216typedef HRESULT (WINAPI * ShGetFolderPath_fn)
217 (IN HWND, IN int, IN HANDLE, IN DWORD, OUT char *);
218
0898ca10
JB
219Lisp_Object QCloaded_from;
220
b56ceb92 221void globals_of_w32 (void);
8aaaec6b 222static DWORD get_rid (PSID);
6dad7178
EZ
223static int is_symlink (const char *);
224static char * chase_symlinks (const char *);
225static int enable_privilege (LPCTSTR, BOOL, TOKEN_PRIVILEGES *);
226static int restore_privilege (TOKEN_PRIVILEGES *);
227static BOOL WINAPI revert_to_self (void);
9785d95b 228
a68089e4
EZ
229extern int sys_access (const char *, int);
230extern void *e_malloc (size_t);
231extern int sys_select (int, SELECT_TYPE *, SELECT_TYPE *, SELECT_TYPE *,
232 EMACS_TIME *, void *);
233
234
18e070ac 235\f
9d95a291
EZ
236/* Initialization states.
237
238 WARNING: If you add any more such variables for additional APIs,
239 you MUST add initialization for them to globals_of_w32
240 below. This is because these variables might get set
241 to non-NULL values during dumping, but the dumped Emacs
242 cannot reuse those values, because it could be run on a
243 different version of the OS, where API addresses are
244 different. */
9785d95b
BK
245static BOOL g_b_init_is_windows_9x;
246static BOOL g_b_init_open_process_token;
247static BOOL g_b_init_get_token_information;
248static BOOL g_b_init_lookup_account_sid;
c617afce
EZ
249static BOOL g_b_init_get_sid_sub_authority;
250static BOOL g_b_init_get_sid_sub_authority_count;
6dad7178 251static BOOL g_b_init_get_security_info;
8aaaec6b
EZ
252static BOOL g_b_init_get_file_security;
253static BOOL g_b_init_get_security_descriptor_owner;
254static BOOL g_b_init_get_security_descriptor_group;
255static BOOL g_b_init_is_valid_sid;
7c80d5ec
EZ
256static BOOL g_b_init_create_toolhelp32_snapshot;
257static BOOL g_b_init_process32_first;
258static BOOL g_b_init_process32_next;
259static BOOL g_b_init_open_thread_token;
260static BOOL g_b_init_impersonate_self;
261static BOOL g_b_init_revert_to_self;
262static BOOL g_b_init_get_process_memory_info;
263static BOOL g_b_init_get_process_working_set_size;
264static BOOL g_b_init_global_memory_status;
265static BOOL g_b_init_global_memory_status_ex;
f8b35b24
EZ
266static BOOL g_b_init_get_length_sid;
267static BOOL g_b_init_equal_sid;
268static BOOL g_b_init_copy_sid;
ad9e2d54
EZ
269static BOOL g_b_init_get_native_system_info;
270static BOOL g_b_init_get_system_times;
6dad7178 271static BOOL g_b_init_create_symbolic_link;
66447e07
EZ
272static BOOL g_b_init_get_security_descriptor_dacl;
273static BOOL g_b_init_convert_sd_to_sddl;
274static BOOL g_b_init_convert_sddl_to_sd;
275static BOOL g_b_init_is_valid_security_descriptor;
276static BOOL g_b_init_set_file_security;
9785d95b 277
f60ae425
BK
278/*
279 BEGIN: Wrapper functions around OpenProcessToken
280 and other functions in advapi32.dll that are only
281 supported in Windows NT / 2k / XP
282*/
283 /* ** Function pointer typedefs ** */
284typedef BOOL (WINAPI * OpenProcessToken_Proc) (
285 HANDLE ProcessHandle,
286 DWORD DesiredAccess,
287 PHANDLE TokenHandle);
288typedef BOOL (WINAPI * GetTokenInformation_Proc) (
289 HANDLE TokenHandle,
290 TOKEN_INFORMATION_CLASS TokenInformationClass,
291 LPVOID TokenInformation,
292 DWORD TokenInformationLength,
293 PDWORD ReturnLength);
74258518
JR
294typedef BOOL (WINAPI * GetProcessTimes_Proc) (
295 HANDLE process_handle,
296 LPFILETIME creation_time,
297 LPFILETIME exit_time,
298 LPFILETIME kernel_time,
299 LPFILETIME user_time);
300
301GetProcessTimes_Proc get_process_times_fn = NULL;
302
f60ae425
BK
303#ifdef _UNICODE
304const char * const LookupAccountSid_Name = "LookupAccountSidW";
8aaaec6b 305const char * const GetFileSecurity_Name = "GetFileSecurityW";
66447e07 306const char * const SetFileSecurity_Name = "SetFileSecurityW";
f60ae425
BK
307#else
308const char * const LookupAccountSid_Name = "LookupAccountSidA";
8aaaec6b 309const char * const GetFileSecurity_Name = "GetFileSecurityA";
66447e07 310const char * const SetFileSecurity_Name = "SetFileSecurityA";
f60ae425
BK
311#endif
312typedef BOOL (WINAPI * LookupAccountSid_Proc) (
313 LPCTSTR lpSystemName,
314 PSID Sid,
315 LPTSTR Name,
316 LPDWORD cbName,
317 LPTSTR DomainName,
318 LPDWORD cbDomainName,
319 PSID_NAME_USE peUse);
c617afce
EZ
320typedef PDWORD (WINAPI * GetSidSubAuthority_Proc) (
321 PSID pSid,
322 DWORD n);
323typedef PUCHAR (WINAPI * GetSidSubAuthorityCount_Proc) (
324 PSID pSid);
6dad7178
EZ
325typedef DWORD (WINAPI * GetSecurityInfo_Proc) (
326 HANDLE handle,
327 SE_OBJECT_TYPE ObjectType,
328 SECURITY_INFORMATION SecurityInfo,
329 PSID *ppsidOwner,
330 PSID *ppsidGroup,
331 PACL *ppDacl,
332 PACL *ppSacl,
333 PSECURITY_DESCRIPTOR *ppSecurityDescriptor);
8aaaec6b
EZ
334typedef BOOL (WINAPI * GetFileSecurity_Proc) (
335 LPCTSTR lpFileName,
336 SECURITY_INFORMATION RequestedInformation,
337 PSECURITY_DESCRIPTOR pSecurityDescriptor,
338 DWORD nLength,
339 LPDWORD lpnLengthNeeded);
66447e07
EZ
340typedef BOOL (WINAPI *SetFileSecurity_Proc) (
341 LPCTSTR lpFileName,
342 SECURITY_INFORMATION SecurityInformation,
343 PSECURITY_DESCRIPTOR pSecurityDescriptor);
8aaaec6b
EZ
344typedef BOOL (WINAPI * GetSecurityDescriptorOwner_Proc) (
345 PSECURITY_DESCRIPTOR pSecurityDescriptor,
346 PSID *pOwner,
347 LPBOOL lpbOwnerDefaulted);
348typedef BOOL (WINAPI * GetSecurityDescriptorGroup_Proc) (
349 PSECURITY_DESCRIPTOR pSecurityDescriptor,
350 PSID *pGroup,
351 LPBOOL lpbGroupDefaulted);
66447e07
EZ
352typedef BOOL (WINAPI *GetSecurityDescriptorDacl_Proc) (
353 PSECURITY_DESCRIPTOR pSecurityDescriptor,
354 LPBOOL lpbDaclPresent,
355 PACL *pDacl,
356 LPBOOL lpbDaclDefaulted);
8aaaec6b
EZ
357typedef BOOL (WINAPI * IsValidSid_Proc) (
358 PSID sid);
7c80d5ec
EZ
359typedef HANDLE (WINAPI * CreateToolhelp32Snapshot_Proc) (
360 DWORD dwFlags,
361 DWORD th32ProcessID);
362typedef BOOL (WINAPI * Process32First_Proc) (
363 HANDLE hSnapshot,
364 LPPROCESSENTRY32 lppe);
365typedef BOOL (WINAPI * Process32Next_Proc) (
366 HANDLE hSnapshot,
367 LPPROCESSENTRY32 lppe);
368typedef BOOL (WINAPI * OpenThreadToken_Proc) (
369 HANDLE ThreadHandle,
370 DWORD DesiredAccess,
371 BOOL OpenAsSelf,
372 PHANDLE TokenHandle);
373typedef BOOL (WINAPI * ImpersonateSelf_Proc) (
374 SECURITY_IMPERSONATION_LEVEL ImpersonationLevel);
375typedef BOOL (WINAPI * RevertToSelf_Proc) (void);
376typedef BOOL (WINAPI * GetProcessMemoryInfo_Proc) (
377 HANDLE Process,
378 PPROCESS_MEMORY_COUNTERS ppsmemCounters,
379 DWORD cb);
380typedef BOOL (WINAPI * GetProcessWorkingSetSize_Proc) (
381 HANDLE hProcess,
cb576b5c
FP
382 PSIZE_T lpMinimumWorkingSetSize,
383 PSIZE_T lpMaximumWorkingSetSize);
7c80d5ec
EZ
384typedef BOOL (WINAPI * GlobalMemoryStatus_Proc) (
385 LPMEMORYSTATUS lpBuffer);
386typedef BOOL (WINAPI * GlobalMemoryStatusEx_Proc) (
b8526f6e 387 LPMEMORY_STATUS_EX lpBuffer);
f8b35b24
EZ
388typedef BOOL (WINAPI * CopySid_Proc) (
389 DWORD nDestinationSidLength,
390 PSID pDestinationSid,
391 PSID pSourceSid);
392typedef BOOL (WINAPI * EqualSid_Proc) (
393 PSID pSid1,
394 PSID pSid2);
395typedef DWORD (WINAPI * GetLengthSid_Proc) (
396 PSID pSid);
ad9e2d54
EZ
397typedef void (WINAPI * GetNativeSystemInfo_Proc) (
398 LPSYSTEM_INFO lpSystemInfo);
399typedef BOOL (WINAPI * GetSystemTimes_Proc) (
400 LPFILETIME lpIdleTime,
401 LPFILETIME lpKernelTime,
402 LPFILETIME lpUserTime);
6dad7178
EZ
403typedef BOOLEAN (WINAPI *CreateSymbolicLink_Proc) (
404 LPTSTR lpSymlinkFileName,
405 LPTSTR lpTargetFileName,
406 DWORD dwFlags);
66447e07
EZ
407typedef BOOL (WINAPI *ConvertStringSecurityDescriptorToSecurityDescriptor_Proc) (
408 LPCTSTR StringSecurityDescriptor,
409 DWORD StringSDRevision,
410 PSECURITY_DESCRIPTOR *SecurityDescriptor,
411 PULONG SecurityDescriptorSize);
412typedef BOOL (WINAPI *ConvertSecurityDescriptorToStringSecurityDescriptor_Proc) (
413 PSECURITY_DESCRIPTOR SecurityDescriptor,
414 DWORD RequestedStringSDRevision,
415 SECURITY_INFORMATION SecurityInformation,
416 LPTSTR *StringSecurityDescriptor,
417 PULONG StringSecurityDescriptorLen);
418typedef BOOL (WINAPI *IsValidSecurityDescriptor_Proc) (PSECURITY_DESCRIPTOR);
f8b35b24 419
f60ae425 420 /* ** A utility function ** */
9bfb11f9 421static BOOL
b56ceb92 422is_windows_9x (void)
f60ae425 423{
bedf4aab 424 static BOOL s_b_ret = 0;
f60ae425 425 OSVERSIONINFO os_ver;
9785d95b 426 if (g_b_init_is_windows_9x == 0)
f60ae425 427 {
9785d95b 428 g_b_init_is_windows_9x = 1;
ed3751c8
JB
429 ZeroMemory (&os_ver, sizeof (OSVERSIONINFO));
430 os_ver.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
9785d95b
BK
431 if (GetVersionEx (&os_ver))
432 {
433 s_b_ret = (os_ver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
434 }
f60ae425 435 }
9785d95b 436 return s_b_ret;
f60ae425
BK
437}
438
d35af63c
PE
439static Lisp_Object ltime (ULONGLONG);
440
74258518 441/* Get total user and system times for get-internal-run-time.
d35af63c 442 Returns a list of integers if the times are provided by the OS
74258518
JR
443 (NT derivatives), otherwise it returns the result of current-time. */
444Lisp_Object
b56ceb92 445w32_get_internal_run_time (void)
74258518
JR
446{
447 if (get_process_times_fn)
448 {
449 FILETIME create, exit, kernel, user;
ed3751c8 450 HANDLE proc = GetCurrentProcess ();
74258518
JR
451 if ((*get_process_times_fn) (proc, &create, &exit, &kernel, &user))
452 {
453 LARGE_INTEGER user_int, kernel_int, total;
74258518
JR
454 user_int.LowPart = user.dwLowDateTime;
455 user_int.HighPart = user.dwHighDateTime;
456 kernel_int.LowPart = kernel.dwLowDateTime;
457 kernel_int.HighPart = kernel.dwHighDateTime;
458 total.QuadPart = user_int.QuadPart + kernel_int.QuadPart;
d35af63c 459 return ltime (total.QuadPart);
74258518
JR
460 }
461 }
462
463 return Fcurrent_time ();
464}
465
f60ae425
BK
466 /* ** The wrapper functions ** */
467
bedf4aab
JB
468static BOOL WINAPI
469open_process_token (HANDLE ProcessHandle,
470 DWORD DesiredAccess,
471 PHANDLE TokenHandle)
f60ae425 472{
9785d95b 473 static OpenProcessToken_Proc s_pfn_Open_Process_Token = NULL;
f60ae425
BK
474 HMODULE hm_advapi32 = NULL;
475 if (is_windows_9x () == TRUE)
476 {
477 return FALSE;
478 }
9785d95b
BK
479 if (g_b_init_open_process_token == 0)
480 {
481 g_b_init_open_process_token = 1;
482 hm_advapi32 = LoadLibrary ("Advapi32.dll");
483 s_pfn_Open_Process_Token =
484 (OpenProcessToken_Proc) GetProcAddress (hm_advapi32, "OpenProcessToken");
485 }
486 if (s_pfn_Open_Process_Token == NULL)
f60ae425
BK
487 {
488 return FALSE;
489 }
490 return (
9785d95b 491 s_pfn_Open_Process_Token (
f60ae425
BK
492 ProcessHandle,
493 DesiredAccess,
494 TokenHandle)
495 );
496}
497
bedf4aab
JB
498static BOOL WINAPI
499get_token_information (HANDLE TokenHandle,
500 TOKEN_INFORMATION_CLASS TokenInformationClass,
501 LPVOID TokenInformation,
502 DWORD TokenInformationLength,
503 PDWORD ReturnLength)
f60ae425 504{
9785d95b 505 static GetTokenInformation_Proc s_pfn_Get_Token_Information = NULL;
f60ae425
BK
506 HMODULE hm_advapi32 = NULL;
507 if (is_windows_9x () == TRUE)
508 {
509 return FALSE;
510 }
9785d95b
BK
511 if (g_b_init_get_token_information == 0)
512 {
513 g_b_init_get_token_information = 1;
514 hm_advapi32 = LoadLibrary ("Advapi32.dll");
515 s_pfn_Get_Token_Information =
516 (GetTokenInformation_Proc) GetProcAddress (hm_advapi32, "GetTokenInformation");
517 }
518 if (s_pfn_Get_Token_Information == NULL)
f60ae425
BK
519 {
520 return FALSE;
521 }
522 return (
9785d95b 523 s_pfn_Get_Token_Information (
f60ae425
BK
524 TokenHandle,
525 TokenInformationClass,
526 TokenInformation,
527 TokenInformationLength,
528 ReturnLength)
529 );
530}
531
bedf4aab
JB
532static BOOL WINAPI
533lookup_account_sid (LPCTSTR lpSystemName,
534 PSID Sid,
535 LPTSTR Name,
536 LPDWORD cbName,
537 LPTSTR DomainName,
538 LPDWORD cbDomainName,
539 PSID_NAME_USE peUse)
f60ae425 540{
9785d95b 541 static LookupAccountSid_Proc s_pfn_Lookup_Account_Sid = NULL;
f60ae425
BK
542 HMODULE hm_advapi32 = NULL;
543 if (is_windows_9x () == TRUE)
544 {
545 return FALSE;
546 }
9785d95b
BK
547 if (g_b_init_lookup_account_sid == 0)
548 {
549 g_b_init_lookup_account_sid = 1;
550 hm_advapi32 = LoadLibrary ("Advapi32.dll");
551 s_pfn_Lookup_Account_Sid =
552 (LookupAccountSid_Proc) GetProcAddress (hm_advapi32, LookupAccountSid_Name);
553 }
554 if (s_pfn_Lookup_Account_Sid == NULL)
f60ae425
BK
555 {
556 return FALSE;
557 }
558 return (
9785d95b 559 s_pfn_Lookup_Account_Sid (
f60ae425
BK
560 lpSystemName,
561 Sid,
562 Name,
563 cbName,
564 DomainName,
565 cbDomainName,
566 peUse)
567 );
568}
569
bedf4aab
JB
570static PDWORD WINAPI
571get_sid_sub_authority (PSID pSid, DWORD n)
c617afce
EZ
572{
573 static GetSidSubAuthority_Proc s_pfn_Get_Sid_Sub_Authority = NULL;
6811b9f4 574 static DWORD zero = 0U;
c617afce
EZ
575 HMODULE hm_advapi32 = NULL;
576 if (is_windows_9x () == TRUE)
577 {
6811b9f4 578 return &zero;
c617afce
EZ
579 }
580 if (g_b_init_get_sid_sub_authority == 0)
581 {
582 g_b_init_get_sid_sub_authority = 1;
583 hm_advapi32 = LoadLibrary ("Advapi32.dll");
584 s_pfn_Get_Sid_Sub_Authority =
585 (GetSidSubAuthority_Proc) GetProcAddress (
586 hm_advapi32, "GetSidSubAuthority");
587 }
588 if (s_pfn_Get_Sid_Sub_Authority == NULL)
589 {
6811b9f4 590 return &zero;
c617afce
EZ
591 }
592 return (s_pfn_Get_Sid_Sub_Authority (pSid, n));
593}
594
bedf4aab
JB
595static PUCHAR WINAPI
596get_sid_sub_authority_count (PSID pSid)
c617afce
EZ
597{
598 static GetSidSubAuthorityCount_Proc s_pfn_Get_Sid_Sub_Authority_Count = NULL;
6811b9f4 599 static UCHAR zero = 0U;
c617afce
EZ
600 HMODULE hm_advapi32 = NULL;
601 if (is_windows_9x () == TRUE)
602 {
6811b9f4 603 return &zero;
c617afce
EZ
604 }
605 if (g_b_init_get_sid_sub_authority_count == 0)
606 {
607 g_b_init_get_sid_sub_authority_count = 1;
608 hm_advapi32 = LoadLibrary ("Advapi32.dll");
609 s_pfn_Get_Sid_Sub_Authority_Count =
610 (GetSidSubAuthorityCount_Proc) GetProcAddress (
611 hm_advapi32, "GetSidSubAuthorityCount");
612 }
613 if (s_pfn_Get_Sid_Sub_Authority_Count == NULL)
614 {
6811b9f4 615 return &zero;
c617afce
EZ
616 }
617 return (s_pfn_Get_Sid_Sub_Authority_Count (pSid));
618}
619
6dad7178
EZ
620static DWORD WINAPI
621get_security_info (HANDLE handle,
622 SE_OBJECT_TYPE ObjectType,
623 SECURITY_INFORMATION SecurityInfo,
624 PSID *ppsidOwner,
625 PSID *ppsidGroup,
626 PACL *ppDacl,
627 PACL *ppSacl,
628 PSECURITY_DESCRIPTOR *ppSecurityDescriptor)
629{
630 static GetSecurityInfo_Proc s_pfn_Get_Security_Info = NULL;
631 HMODULE hm_advapi32 = NULL;
632 if (is_windows_9x () == TRUE)
633 {
634 return FALSE;
635 }
636 if (g_b_init_get_security_info == 0)
637 {
638 g_b_init_get_security_info = 1;
639 hm_advapi32 = LoadLibrary ("Advapi32.dll");
640 s_pfn_Get_Security_Info =
641 (GetSecurityInfo_Proc) GetProcAddress (
642 hm_advapi32, "GetSecurityInfo");
643 }
644 if (s_pfn_Get_Security_Info == NULL)
645 {
646 return FALSE;
647 }
648 return (s_pfn_Get_Security_Info (handle, ObjectType, SecurityInfo,
649 ppsidOwner, ppsidGroup, ppDacl, ppSacl,
650 ppSecurityDescriptor));
651}
652
bedf4aab
JB
653static BOOL WINAPI
654get_file_security (LPCTSTR lpFileName,
655 SECURITY_INFORMATION RequestedInformation,
656 PSECURITY_DESCRIPTOR pSecurityDescriptor,
657 DWORD nLength,
658 LPDWORD lpnLengthNeeded)
8aaaec6b
EZ
659{
660 static GetFileSecurity_Proc s_pfn_Get_File_Security = NULL;
661 HMODULE hm_advapi32 = NULL;
662 if (is_windows_9x () == TRUE)
663 {
66447e07 664 errno = ENOTSUP;
8aaaec6b
EZ
665 return FALSE;
666 }
667 if (g_b_init_get_file_security == 0)
668 {
669 g_b_init_get_file_security = 1;
670 hm_advapi32 = LoadLibrary ("Advapi32.dll");
671 s_pfn_Get_File_Security =
672 (GetFileSecurity_Proc) GetProcAddress (
673 hm_advapi32, GetFileSecurity_Name);
674 }
675 if (s_pfn_Get_File_Security == NULL)
676 {
66447e07 677 errno = ENOTSUP;
8aaaec6b
EZ
678 return FALSE;
679 }
680 return (s_pfn_Get_File_Security (lpFileName, RequestedInformation,
681 pSecurityDescriptor, nLength,
682 lpnLengthNeeded));
683}
684
66447e07
EZ
685static BOOL WINAPI
686set_file_security (LPCTSTR lpFileName,
687 SECURITY_INFORMATION SecurityInformation,
688 PSECURITY_DESCRIPTOR pSecurityDescriptor)
689{
690 static SetFileSecurity_Proc s_pfn_Set_File_Security = NULL;
691 HMODULE hm_advapi32 = NULL;
692 if (is_windows_9x () == TRUE)
693 {
694 errno = ENOTSUP;
695 return FALSE;
696 }
697 if (g_b_init_set_file_security == 0)
698 {
699 g_b_init_set_file_security = 1;
700 hm_advapi32 = LoadLibrary ("Advapi32.dll");
701 s_pfn_Set_File_Security =
702 (SetFileSecurity_Proc) GetProcAddress (
703 hm_advapi32, SetFileSecurity_Name);
704 }
705 if (s_pfn_Set_File_Security == NULL)
706 {
707 errno = ENOTSUP;
708 return FALSE;
709 }
710 return (s_pfn_Set_File_Security (lpFileName, SecurityInformation,
711 pSecurityDescriptor));
712}
713
bedf4aab
JB
714static BOOL WINAPI
715get_security_descriptor_owner (PSECURITY_DESCRIPTOR pSecurityDescriptor,
716 PSID *pOwner,
717 LPBOOL lpbOwnerDefaulted)
8aaaec6b
EZ
718{
719 static GetSecurityDescriptorOwner_Proc s_pfn_Get_Security_Descriptor_Owner = NULL;
720 HMODULE hm_advapi32 = NULL;
721 if (is_windows_9x () == TRUE)
722 {
66447e07 723 errno = ENOTSUP;
8aaaec6b
EZ
724 return FALSE;
725 }
726 if (g_b_init_get_security_descriptor_owner == 0)
727 {
728 g_b_init_get_security_descriptor_owner = 1;
729 hm_advapi32 = LoadLibrary ("Advapi32.dll");
730 s_pfn_Get_Security_Descriptor_Owner =
731 (GetSecurityDescriptorOwner_Proc) GetProcAddress (
732 hm_advapi32, "GetSecurityDescriptorOwner");
733 }
734 if (s_pfn_Get_Security_Descriptor_Owner == NULL)
735 {
66447e07 736 errno = ENOTSUP;
8aaaec6b
EZ
737 return FALSE;
738 }
739 return (s_pfn_Get_Security_Descriptor_Owner (pSecurityDescriptor, pOwner,
740 lpbOwnerDefaulted));
741}
742
bedf4aab
JB
743static BOOL WINAPI
744get_security_descriptor_group (PSECURITY_DESCRIPTOR pSecurityDescriptor,
745 PSID *pGroup,
746 LPBOOL lpbGroupDefaulted)
8aaaec6b
EZ
747{
748 static GetSecurityDescriptorGroup_Proc s_pfn_Get_Security_Descriptor_Group = NULL;
749 HMODULE hm_advapi32 = NULL;
750 if (is_windows_9x () == TRUE)
751 {
66447e07 752 errno = ENOTSUP;
8aaaec6b
EZ
753 return FALSE;
754 }
755 if (g_b_init_get_security_descriptor_group == 0)
756 {
757 g_b_init_get_security_descriptor_group = 1;
758 hm_advapi32 = LoadLibrary ("Advapi32.dll");
759 s_pfn_Get_Security_Descriptor_Group =
760 (GetSecurityDescriptorGroup_Proc) GetProcAddress (
761 hm_advapi32, "GetSecurityDescriptorGroup");
762 }
763 if (s_pfn_Get_Security_Descriptor_Group == NULL)
764 {
66447e07 765 errno = ENOTSUP;
8aaaec6b
EZ
766 return FALSE;
767 }
768 return (s_pfn_Get_Security_Descriptor_Group (pSecurityDescriptor, pGroup,
769 lpbGroupDefaulted));
770}
771
66447e07
EZ
772static BOOL WINAPI
773get_security_descriptor_dacl (PSECURITY_DESCRIPTOR pSecurityDescriptor,
774 LPBOOL lpbDaclPresent,
775 PACL *pDacl,
776 LPBOOL lpbDaclDefaulted)
777{
778 static GetSecurityDescriptorDacl_Proc s_pfn_Get_Security_Descriptor_Dacl = NULL;
779 HMODULE hm_advapi32 = NULL;
780 if (is_windows_9x () == TRUE)
781 {
782 errno = ENOTSUP;
783 return FALSE;
784 }
785 if (g_b_init_get_security_descriptor_dacl == 0)
786 {
787 g_b_init_get_security_descriptor_dacl = 1;
788 hm_advapi32 = LoadLibrary ("Advapi32.dll");
789 s_pfn_Get_Security_Descriptor_Dacl =
790 (GetSecurityDescriptorDacl_Proc) GetProcAddress (
791 hm_advapi32, "GetSecurityDescriptorDacl");
792 }
793 if (s_pfn_Get_Security_Descriptor_Dacl == NULL)
794 {
795 errno = ENOTSUP;
796 return FALSE;
797 }
798 return (s_pfn_Get_Security_Descriptor_Dacl (pSecurityDescriptor,
799 lpbDaclPresent, pDacl,
800 lpbDaclDefaulted));
801}
802
bedf4aab
JB
803static BOOL WINAPI
804is_valid_sid (PSID sid)
8aaaec6b
EZ
805{
806 static IsValidSid_Proc s_pfn_Is_Valid_Sid = NULL;
807 HMODULE hm_advapi32 = NULL;
808 if (is_windows_9x () == TRUE)
809 {
810 return FALSE;
811 }
812 if (g_b_init_is_valid_sid == 0)
813 {
814 g_b_init_is_valid_sid = 1;
815 hm_advapi32 = LoadLibrary ("Advapi32.dll");
816 s_pfn_Is_Valid_Sid =
817 (IsValidSid_Proc) GetProcAddress (
818 hm_advapi32, "IsValidSid");
819 }
820 if (s_pfn_Is_Valid_Sid == NULL)
821 {
822 return FALSE;
823 }
824 return (s_pfn_Is_Valid_Sid (sid));
825}
826
bedf4aab
JB
827static BOOL WINAPI
828equal_sid (PSID sid1, PSID sid2)
f8b35b24
EZ
829{
830 static EqualSid_Proc s_pfn_Equal_Sid = NULL;
831 HMODULE hm_advapi32 = NULL;
832 if (is_windows_9x () == TRUE)
833 {
834 return FALSE;
835 }
836 if (g_b_init_equal_sid == 0)
837 {
838 g_b_init_equal_sid = 1;
839 hm_advapi32 = LoadLibrary ("Advapi32.dll");
840 s_pfn_Equal_Sid =
841 (EqualSid_Proc) GetProcAddress (
842 hm_advapi32, "EqualSid");
843 }
844 if (s_pfn_Equal_Sid == NULL)
845 {
846 return FALSE;
847 }
848 return (s_pfn_Equal_Sid (sid1, sid2));
849}
850
bedf4aab
JB
851static DWORD WINAPI
852get_length_sid (PSID sid)
f8b35b24
EZ
853{
854 static GetLengthSid_Proc s_pfn_Get_Length_Sid = NULL;
855 HMODULE hm_advapi32 = NULL;
856 if (is_windows_9x () == TRUE)
857 {
858 return 0;
859 }
860 if (g_b_init_get_length_sid == 0)
861 {
862 g_b_init_get_length_sid = 1;
863 hm_advapi32 = LoadLibrary ("Advapi32.dll");
864 s_pfn_Get_Length_Sid =
865 (GetLengthSid_Proc) GetProcAddress (
866 hm_advapi32, "GetLengthSid");
867 }
868 if (s_pfn_Get_Length_Sid == NULL)
869 {
870 return 0;
871 }
872 return (s_pfn_Get_Length_Sid (sid));
873}
874
bedf4aab
JB
875static BOOL WINAPI
876copy_sid (DWORD destlen, PSID dest, PSID src)
f8b35b24
EZ
877{
878 static CopySid_Proc s_pfn_Copy_Sid = NULL;
879 HMODULE hm_advapi32 = NULL;
880 if (is_windows_9x () == TRUE)
881 {
882 return FALSE;
883 }
884 if (g_b_init_copy_sid == 0)
885 {
886 g_b_init_copy_sid = 1;
887 hm_advapi32 = LoadLibrary ("Advapi32.dll");
888 s_pfn_Copy_Sid =
889 (CopySid_Proc) GetProcAddress (
890 hm_advapi32, "CopySid");
891 }
892 if (s_pfn_Copy_Sid == NULL)
893 {
894 return FALSE;
895 }
896 return (s_pfn_Copy_Sid (destlen, dest, src));
897}
898
f60ae425
BK
899/*
900 END: Wrapper functions around OpenProcessToken
901 and other functions in advapi32.dll that are only
902 supported in Windows NT / 2k / XP
903*/
904
bedf4aab
JB
905static void WINAPI
906get_native_system_info (LPSYSTEM_INFO lpSystemInfo)
ad9e2d54
EZ
907{
908 static GetNativeSystemInfo_Proc s_pfn_Get_Native_System_Info = NULL;
909 if (is_windows_9x () != TRUE)
910 {
911 if (g_b_init_get_native_system_info == 0)
912 {
913 g_b_init_get_native_system_info = 1;
914 s_pfn_Get_Native_System_Info =
915 (GetNativeSystemInfo_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
916 "GetNativeSystemInfo");
917 }
918 if (s_pfn_Get_Native_System_Info != NULL)
919 s_pfn_Get_Native_System_Info (lpSystemInfo);
920 }
921 else
922 lpSystemInfo->dwNumberOfProcessors = -1;
923}
924
bedf4aab
JB
925static BOOL WINAPI
926get_system_times (LPFILETIME lpIdleTime,
927 LPFILETIME lpKernelTime,
928 LPFILETIME lpUserTime)
ad9e2d54
EZ
929{
930 static GetSystemTimes_Proc s_pfn_Get_System_times = NULL;
931 if (is_windows_9x () == TRUE)
932 {
933 return FALSE;
934 }
935 if (g_b_init_get_system_times == 0)
936 {
937 g_b_init_get_system_times = 1;
938 s_pfn_Get_System_times =
939 (GetSystemTimes_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
940 "GetSystemTimes");
941 }
942 if (s_pfn_Get_System_times == NULL)
943 return FALSE;
944 return (s_pfn_Get_System_times (lpIdleTime, lpKernelTime, lpUserTime));
945}
6dad7178
EZ
946
947static BOOLEAN WINAPI
948create_symbolic_link (LPTSTR lpSymlinkFilename,
949 LPTSTR lpTargetFileName,
950 DWORD dwFlags)
951{
952 static CreateSymbolicLink_Proc s_pfn_Create_Symbolic_Link = NULL;
953 BOOLEAN retval;
954
955 if (is_windows_9x () == TRUE)
956 {
957 errno = ENOSYS;
958 return 0;
959 }
960 if (g_b_init_create_symbolic_link == 0)
961 {
962 g_b_init_create_symbolic_link = 1;
963#ifdef _UNICODE
964 s_pfn_Create_Symbolic_Link =
965 (CreateSymbolicLink_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
966 "CreateSymbolicLinkW");
967#else
968 s_pfn_Create_Symbolic_Link =
969 (CreateSymbolicLink_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
970 "CreateSymbolicLinkA");
971#endif
972 }
973 if (s_pfn_Create_Symbolic_Link == NULL)
974 {
975 errno = ENOSYS;
976 return 0;
977 }
978
979 retval = s_pfn_Create_Symbolic_Link (lpSymlinkFilename, lpTargetFileName,
980 dwFlags);
981 /* If we were denied creation of the symlink, try again after
982 enabling the SeCreateSymbolicLinkPrivilege for our process. */
983 if (!retval)
984 {
985 TOKEN_PRIVILEGES priv_current;
986
987 if (enable_privilege (SE_CREATE_SYMBOLIC_LINK_NAME, TRUE, &priv_current))
988 {
989 retval = s_pfn_Create_Symbolic_Link (lpSymlinkFilename, lpTargetFileName,
990 dwFlags);
991 restore_privilege (&priv_current);
992 revert_to_self ();
993 }
994 }
995 return retval;
996}
66447e07
EZ
997
998static BOOL WINAPI
999is_valid_security_descriptor (PSECURITY_DESCRIPTOR pSecurityDescriptor)
1000{
1001 static IsValidSecurityDescriptor_Proc s_pfn_Is_Valid_Security_Descriptor_Proc = NULL;
1002
1003 if (is_windows_9x () == TRUE)
1004 {
1005 errno = ENOTSUP;
1006 return FALSE;
1007 }
1008
1009 if (g_b_init_is_valid_security_descriptor == 0)
1010 {
1011 g_b_init_is_valid_security_descriptor = 1;
1012 s_pfn_Is_Valid_Security_Descriptor_Proc =
1013 (IsValidSecurityDescriptor_Proc)GetProcAddress (GetModuleHandle ("Advapi32.dll"),
1014 "IsValidSecurityDescriptor");
1015 }
1016 if (s_pfn_Is_Valid_Security_Descriptor_Proc == NULL)
1017 {
1018 errno = ENOTSUP;
1019 return FALSE;
1020 }
1021
1022 return s_pfn_Is_Valid_Security_Descriptor_Proc (pSecurityDescriptor);
1023}
1024
1025static BOOL WINAPI
1026convert_sd_to_sddl (PSECURITY_DESCRIPTOR SecurityDescriptor,
1027 DWORD RequestedStringSDRevision,
1028 SECURITY_INFORMATION SecurityInformation,
1029 LPTSTR *StringSecurityDescriptor,
1030 PULONG StringSecurityDescriptorLen)
1031{
1032 static ConvertSecurityDescriptorToStringSecurityDescriptor_Proc s_pfn_Convert_SD_To_SDDL = NULL;
1033 BOOL retval;
1034
1035 if (is_windows_9x () == TRUE)
1036 {
1037 errno = ENOTSUP;
1038 return FALSE;
1039 }
1040
1041 if (g_b_init_convert_sd_to_sddl == 0)
1042 {
1043 g_b_init_convert_sd_to_sddl = 1;
1044#ifdef _UNICODE
1045 s_pfn_Convert_SD_To_SDDL =
1046 (ConvertSecurityDescriptorToStringSecurityDescriptor_Proc)GetProcAddress (GetModuleHandle ("Advapi32.dll"),
1047 "ConvertSecurityDescriptorToStringSecurityDescriptorW");
1048#else
1049 s_pfn_Convert_SD_To_SDDL =
1050 (ConvertSecurityDescriptorToStringSecurityDescriptor_Proc)GetProcAddress (GetModuleHandle ("Advapi32.dll"),
1051 "ConvertSecurityDescriptorToStringSecurityDescriptorA");
1052#endif
1053 }
1054 if (s_pfn_Convert_SD_To_SDDL == NULL)
1055 {
1056 errno = ENOTSUP;
1057 return FALSE;
1058 }
1059
1060 retval = s_pfn_Convert_SD_To_SDDL (SecurityDescriptor,
1061 RequestedStringSDRevision,
1062 SecurityInformation,
1063 StringSecurityDescriptor,
1064 StringSecurityDescriptorLen);
1065
1066 return retval;
1067}
1068
1069static BOOL WINAPI
1070convert_sddl_to_sd (LPCTSTR StringSecurityDescriptor,
1071 DWORD StringSDRevision,
1072 PSECURITY_DESCRIPTOR *SecurityDescriptor,
1073 PULONG SecurityDescriptorSize)
1074{
1075 static ConvertStringSecurityDescriptorToSecurityDescriptor_Proc s_pfn_Convert_SDDL_To_SD = NULL;
1076 BOOL retval;
1077
1078 if (is_windows_9x () == TRUE)
1079 {
1080 errno = ENOTSUP;
1081 return FALSE;
1082 }
1083
1084 if (g_b_init_convert_sddl_to_sd == 0)
1085 {
1086 g_b_init_convert_sddl_to_sd = 1;
1087#ifdef _UNICODE
1088 s_pfn_Convert_SDDL_To_SD =
1089 (ConvertStringSecurityDescriptorToSecurityDescriptor_Proc)GetProcAddress (GetModuleHandle ("Advapi32.dll"),
1090 "ConvertStringSecurityDescriptorToSecurityDescriptorW");
1091#else
1092 s_pfn_Convert_SDDL_To_SD =
1093 (ConvertStringSecurityDescriptorToSecurityDescriptor_Proc)GetProcAddress (GetModuleHandle ("Advapi32.dll"),
1094 "ConvertStringSecurityDescriptorToSecurityDescriptorA");
1095#endif
1096 }
1097 if (s_pfn_Convert_SDDL_To_SD == NULL)
1098 {
1099 errno = ENOTSUP;
1100 return FALSE;
1101 }
1102
1103 retval = s_pfn_Convert_SDDL_To_SD (StringSecurityDescriptor,
1104 StringSDRevision,
1105 SecurityDescriptor,
1106 SecurityDescriptorSize);
1107
1108 return retval;
1109}
1110
f60ae425 1111\f
18e070ac 1112
ed91b2ad
EZ
1113/* Return 1 if P is a valid pointer to an object of size SIZE. Return
1114 0 if P is NOT a valid pointer. Return -1 if we cannot validate P.
1115
1116 This is called from alloc.c:valid_pointer_p. */
1117int
1118w32_valid_pointer_p (void *p, int size)
1119{
1120 SIZE_T done;
1121 HANDLE h = OpenProcess (PROCESS_VM_READ, FALSE, GetCurrentProcessId ());
1122
1123 if (h)
1124 {
1125 unsigned char *buf = alloca (size);
1126 int retval = ReadProcessMemory (h, p, buf, size, &done);
1127
1128 CloseHandle (h);
1129 return retval;
1130 }
1131 else
1132 return -1;
1133}
1134
76b3903d 1135static char startup_dir[MAXPATHLEN];
00b3b7b3 1136
95ed0025 1137/* Get the current working directory. */
480b0c5b 1138char *
ec84768f 1139getcwd (char *dir, int dirsize)
95ed0025 1140{
9239d970
PE
1141 if (!dirsize)
1142 {
1143 errno = EINVAL;
1144 return NULL;
1145 }
1146 if (dirsize <= strlen (startup_dir))
1147 {
1148 errno = ERANGE;
1149 return NULL;
1150 }
76b3903d 1151#if 0
480b0c5b
GV
1152 if (GetCurrentDirectory (MAXPATHLEN, dir) > 0)
1153 return dir;
1154 return NULL;
76b3903d 1155#else
8d38f461
EZ
1156 /* Emacs doesn't actually change directory itself, it stays in the
1157 same directory where it was started. */
76b3903d
GV
1158 strcpy (dir, startup_dir);
1159 return dir;
1160#endif
95ed0025
RS
1161}
1162
95ed0025 1163/* Emulate getloadavg. */
ad9e2d54
EZ
1164
1165struct load_sample {
1166 time_t sample_time;
1167 ULONGLONG idle;
1168 ULONGLONG kernel;
1169 ULONGLONG user;
1170};
1171
1172/* Number of processors on this machine. */
1173static unsigned num_of_processors;
1174
1175/* We maintain 1-sec samples for the last 16 minutes in a circular buffer. */
1176static struct load_sample samples[16*60];
1177static int first_idx = -1, last_idx = -1;
1178static int max_idx = sizeof (samples) / sizeof (samples[0]);
1179
1180static int
1181buf_next (int from)
1182{
1183 int next_idx = from + 1;
1184
1185 if (next_idx >= max_idx)
1186 next_idx = 0;
1187
1188 return next_idx;
1189}
1190
1191static int
1192buf_prev (int from)
1193{
1194 int prev_idx = from - 1;
1195
1196 if (prev_idx < 0)
1197 prev_idx = max_idx - 1;
1198
1199 return prev_idx;
1200}
1201
1202static void
1203sample_system_load (ULONGLONG *idle, ULONGLONG *kernel, ULONGLONG *user)
1204{
1205 SYSTEM_INFO sysinfo;
1206 FILETIME ft_idle, ft_user, ft_kernel;
1207
1208 /* Initialize the number of processors on this machine. */
1209 if (num_of_processors <= 0)
1210 {
1211 get_native_system_info (&sysinfo);
1212 num_of_processors = sysinfo.dwNumberOfProcessors;
1213 if (num_of_processors <= 0)
1214 {
1215 GetSystemInfo (&sysinfo);
1216 num_of_processors = sysinfo.dwNumberOfProcessors;
1217 }
1218 if (num_of_processors <= 0)
1219 num_of_processors = 1;
1220 }
1221
1222 /* TODO: Take into account threads that are ready to run, by
1223 sampling the "\System\Processor Queue Length" performance
1224 counter. The code below accounts only for threads that are
1225 actually running. */
1226
1227 if (get_system_times (&ft_idle, &ft_kernel, &ft_user))
1228 {
1229 ULARGE_INTEGER uidle, ukernel, uuser;
1230
1231 memcpy (&uidle, &ft_idle, sizeof (ft_idle));
1232 memcpy (&ukernel, &ft_kernel, sizeof (ft_kernel));
1233 memcpy (&uuser, &ft_user, sizeof (ft_user));
1234 *idle = uidle.QuadPart;
1235 *kernel = ukernel.QuadPart;
1236 *user = uuser.QuadPart;
1237 }
1238 else
1239 {
1240 *idle = 0;
1241 *kernel = 0;
1242 *user = 0;
1243 }
1244}
1245
1246/* Produce the load average for a given time interval, using the
1247 samples in the samples[] array. WHICH can be 0, 1, or 2, meaning
1248 1-minute, 5-minute, or 15-minute average, respectively. */
1249static double
1250getavg (int which)
1251{
1252 double retval = -1.0;
1253 double tdiff;
1254 int idx;
1255 double span = (which == 0 ? 1.0 : (which == 1 ? 5.0 : 15.0)) * 60;
1256 time_t now = samples[last_idx].sample_time;
1257
1258 if (first_idx != last_idx)
1259 {
1260 for (idx = buf_prev (last_idx); ; idx = buf_prev (idx))
1261 {
1262 tdiff = difftime (now, samples[idx].sample_time);
1263 if (tdiff >= span - 2*DBL_EPSILON*now)
1264 {
1265 long double sys =
1266 samples[last_idx].kernel + samples[last_idx].user
1267 - (samples[idx].kernel + samples[idx].user);
1268 long double idl = samples[last_idx].idle - samples[idx].idle;
1269
1270 retval = (1.0 - idl / sys) * num_of_processors;
1271 break;
1272 }
1273 if (idx == first_idx)
1274 break;
1275 }
1276 }
1277
1278 return retval;
1279}
1280
95ed0025
RS
1281int
1282getloadavg (double loadavg[], int nelem)
1283{
ad9e2d54
EZ
1284 int elem;
1285 ULONGLONG idle, kernel, user;
1286 time_t now = time (NULL);
1287
1288 /* Store another sample. We ignore samples that are less than 1 sec
1289 apart. */
1290 if (difftime (now, samples[last_idx].sample_time) >= 1.0 - 2*DBL_EPSILON*now)
1291 {
1292 sample_system_load (&idle, &kernel, &user);
1293 last_idx = buf_next (last_idx);
1294 samples[last_idx].sample_time = now;
1295 samples[last_idx].idle = idle;
1296 samples[last_idx].kernel = kernel;
1297 samples[last_idx].user = user;
1298 /* If the buffer has more that 15 min worth of samples, discard
1299 the old ones. */
1300 if (first_idx == -1)
1301 first_idx = last_idx;
1302 while (first_idx != last_idx
1303 && (difftime (now, samples[first_idx].sample_time)
1304 >= 15.0*60 + 2*DBL_EPSILON*now))
1305 first_idx = buf_next (first_idx);
1306 }
95ed0025 1307
ad9e2d54 1308 for (elem = 0; elem < nelem; elem++)
95ed0025 1309 {
ad9e2d54
EZ
1310 double avg = getavg (elem);
1311
1312 if (avg < 0)
1313 break;
1314 loadavg[elem] = avg;
95ed0025 1315 }
ad9e2d54
EZ
1316
1317 return elem;
95ed0025
RS
1318}
1319
480b0c5b 1320/* Emulate getpwuid, getpwnam and others. */
95ed0025 1321
051fe60d
GV
1322#define PASSWD_FIELD_SIZE 256
1323
07f7980a
EZ
1324static char dflt_passwd_name[PASSWD_FIELD_SIZE];
1325static char dflt_passwd_passwd[PASSWD_FIELD_SIZE];
1326static char dflt_passwd_gecos[PASSWD_FIELD_SIZE];
1327static char dflt_passwd_dir[PASSWD_FIELD_SIZE];
1328static char dflt_passwd_shell[PASSWD_FIELD_SIZE];
95ed0025 1329
07f7980a 1330static struct passwd dflt_passwd =
95ed0025 1331{
07f7980a
EZ
1332 dflt_passwd_name,
1333 dflt_passwd_passwd,
95ed0025
RS
1334 0,
1335 0,
1336 0,
07f7980a
EZ
1337 dflt_passwd_gecos,
1338 dflt_passwd_dir,
1339 dflt_passwd_shell,
95ed0025
RS
1340};
1341
07f7980a
EZ
1342static char dflt_group_name[GNLEN+1];
1343
1344static struct group dflt_group =
3d19b645 1345{
07f7980a
EZ
1346 /* When group information is not available, we return this as the
1347 group for all files. */
1348 dflt_group_name,
1349 0,
3d19b645
LH
1350};
1351
22749e9a 1352unsigned
b56ceb92 1353getuid (void)
177c0ea7 1354{
07f7980a 1355 return dflt_passwd.pw_uid;
480b0c5b
GV
1356}
1357
22749e9a 1358unsigned
b56ceb92 1359geteuid (void)
177c0ea7 1360{
480b0c5b
GV
1361 /* I could imagine arguing for checking to see whether the user is
1362 in the Administrators group and returning a UID of 0 for that
1363 case, but I don't know how wise that would be in the long run. */
177c0ea7 1364 return getuid ();
480b0c5b
GV
1365}
1366
22749e9a 1367unsigned
b56ceb92 1368getgid (void)
177c0ea7 1369{
07f7980a 1370 return dflt_passwd.pw_gid;
480b0c5b
GV
1371}
1372
22749e9a 1373unsigned
b56ceb92 1374getegid (void)
177c0ea7 1375{
480b0c5b
GV
1376 return getgid ();
1377}
1378
95ed0025 1379struct passwd *
22749e9a 1380getpwuid (unsigned uid)
95ed0025 1381{
07f7980a
EZ
1382 if (uid == dflt_passwd.pw_uid)
1383 return &dflt_passwd;
480b0c5b 1384 return NULL;
95ed0025
RS
1385}
1386
3d19b645
LH
1387struct group *
1388getgrgid (gid_t gid)
1389{
07f7980a 1390 return &dflt_group;
3d19b645
LH
1391}
1392
95ed0025
RS
1393struct passwd *
1394getpwnam (char *name)
1395{
1396 struct passwd *pw;
177c0ea7 1397
95ed0025
RS
1398 pw = getpwuid (getuid ());
1399 if (!pw)
1400 return pw;
1401
05131107 1402 if (xstrcasecmp (name, pw->pw_name))
95ed0025
RS
1403 return NULL;
1404
1405 return pw;
1406}
1407
bedf4aab 1408static void
b56ceb92 1409init_user_info (void)
95ed0025 1410{
480b0c5b
GV
1411 /* Find the user's real name by opening the process token and
1412 looking up the name associated with the user-sid in that token.
1413
1414 Use the relative portion of the identifier authority value from
1415 the user-sid as the user id value (same for group id using the
1416 primary group sid from the process token). */
1417
07f7980a 1418 char uname[UNLEN+1], gname[GNLEN+1], domain[1025];
32cef06e 1419 DWORD ulength = sizeof (uname), dlength = sizeof (domain), needed;
07f7980a 1420 DWORD glength = sizeof (gname);
10aabbf9
JB
1421 HANDLE token = NULL;
1422 SID_NAME_USE user_type;
32cef06e
EZ
1423 unsigned char *buf = NULL;
1424 DWORD blen = 0;
634d3003
EZ
1425 TOKEN_USER user_token;
1426 TOKEN_PRIMARY_GROUP group_token;
32cef06e 1427 BOOL result;
10aabbf9 1428
32cef06e
EZ
1429 result = open_process_token (GetCurrentProcess (), TOKEN_QUERY, &token);
1430 if (result)
1431 {
1432 result = get_token_information (token, TokenUser, NULL, 0, &blen);
1433 if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER)
1434 {
1435 buf = xmalloc (blen);
1436 result = get_token_information (token, TokenUser,
1437 (LPVOID)buf, blen, &needed);
1438 if (result)
1439 {
1440 memcpy (&user_token, buf, sizeof (user_token));
1441 result = lookup_account_sid (NULL, user_token.User.Sid,
1442 uname, &ulength,
1443 domain, &dlength, &user_type);
1444 }
1445 }
1446 else
1447 result = FALSE;
1448 }
1449 if (result)
d1c1c3d2 1450 {
07f7980a 1451 strcpy (dflt_passwd.pw_name, uname);
c617afce 1452 /* Determine a reasonable uid value. */
05131107 1453 if (xstrcasecmp ("administrator", uname) == 0)
480b0c5b 1454 {
07f7980a
EZ
1455 dflt_passwd.pw_uid = 500; /* well-known Administrator uid */
1456 dflt_passwd.pw_gid = 513; /* well-known None gid */
480b0c5b
GV
1457 }
1458 else
1459 {
ce0ee994
EZ
1460 /* Use the last sub-authority value of the RID, the relative
1461 portion of the SID, as user/group ID. */
8aaaec6b 1462 dflt_passwd.pw_uid = get_rid (user_token.User.Sid);
480b0c5b 1463
8aaaec6b 1464 /* Get group id and name. */
32cef06e
EZ
1465 result = get_token_information (token, TokenPrimaryGroup,
1466 (LPVOID)buf, blen, &needed);
1467 if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER)
1468 {
1469 buf = xrealloc (buf, blen = needed);
1470 result = get_token_information (token, TokenPrimaryGroup,
1471 (LPVOID)buf, blen, &needed);
1472 }
1473 if (result)
480b0c5b 1474 {
634d3003 1475 memcpy (&group_token, buf, sizeof (group_token));
8aaaec6b 1476 dflt_passwd.pw_gid = get_rid (group_token.PrimaryGroup);
07f7980a 1477 dlength = sizeof (domain);
32cef06e
EZ
1478 /* If we can get at the real Primary Group name, use that.
1479 Otherwise, the default group name was already set to
1480 "None" in globals_of_w32. */
07f7980a
EZ
1481 if (lookup_account_sid (NULL, group_token.PrimaryGroup,
1482 gname, &glength, NULL, &dlength,
1483 &user_type))
1484 strcpy (dflt_group_name, gname);
480b0c5b
GV
1485 }
1486 else
07f7980a 1487 dflt_passwd.pw_gid = dflt_passwd.pw_uid;
480b0c5b
GV
1488 }
1489 }
1490 /* If security calls are not supported (presumably because we
32cef06e 1491 are running under Windows 9X), fallback to this: */
07f7980a 1492 else if (GetUserName (uname, &ulength))
480b0c5b 1493 {
07f7980a 1494 strcpy (dflt_passwd.pw_name, uname);
05131107 1495 if (xstrcasecmp ("administrator", uname) == 0)
07f7980a 1496 dflt_passwd.pw_uid = 0;
480b0c5b 1497 else
07f7980a
EZ
1498 dflt_passwd.pw_uid = 123;
1499 dflt_passwd.pw_gid = dflt_passwd.pw_uid;
480b0c5b
GV
1500 }
1501 else
1502 {
07f7980a
EZ
1503 strcpy (dflt_passwd.pw_name, "unknown");
1504 dflt_passwd.pw_uid = 123;
1505 dflt_passwd.pw_gid = 123;
d1c1c3d2 1506 }
07f7980a 1507 dflt_group.gr_gid = dflt_passwd.pw_gid;
95ed0025 1508
480b0c5b
GV
1509 /* Ensure HOME and SHELL are defined. */
1510 if (getenv ("HOME") == NULL)
1088b922 1511 emacs_abort ();
480b0c5b 1512 if (getenv ("SHELL") == NULL)
1088b922 1513 emacs_abort ();
95ed0025 1514
480b0c5b 1515 /* Set dir and shell from environment variables. */
07f7980a
EZ
1516 strcpy (dflt_passwd.pw_dir, getenv ("HOME"));
1517 strcpy (dflt_passwd.pw_shell, getenv ("SHELL"));
bd4a449f 1518
32cef06e 1519 xfree (buf);
480b0c5b
GV
1520 if (token)
1521 CloseHandle (token);
95ed0025
RS
1522}
1523
95ed0025 1524int
b56ceb92 1525random (void)
95ed0025 1526{
480b0c5b
GV
1527 /* rand () on NT gives us 15 random bits...hack together 30 bits. */
1528 return ((rand () << 15) | rand ());
95ed0025
RS
1529}
1530
95ed0025 1531void
480b0c5b 1532srandom (int seed)
95ed0025 1533{
480b0c5b 1534 srand (seed);
95ed0025
RS
1535}
1536
6d2851de
EZ
1537/* Current codepage for encoding file names. */
1538static int file_name_codepage;
1539
1540/* Return the maximum length in bytes of a multibyte character
1541 sequence encoded in the current ANSI codepage. This is required to
1542 correctly walk the encoded file names one character at a time. */
1543static int
1544max_filename_mbslen (void)
1545{
1546 /* A simple cache to avoid calling GetCPInfo every time we need to
1547 normalize a file name. The file-name encoding is not supposed to
1548 be changed too frequently, if ever. */
1549 static Lisp_Object last_file_name_encoding;
1550 static int last_max_mbslen;
1551 Lisp_Object current_encoding;
1552
1553 current_encoding = Vfile_name_coding_system;
1554 if (NILP (current_encoding))
1555 current_encoding = Vdefault_file_name_coding_system;
1556
1557 if (!EQ (last_file_name_encoding, current_encoding))
1558 {
1559 CPINFO cp_info;
1560
1561 last_file_name_encoding = current_encoding;
1562 /* Default to the current ANSI codepage. */
1563 file_name_codepage = w32_ansi_code_page;
1564 if (!NILP (current_encoding))
1565 {
1566 char *cpname = SDATA (SYMBOL_NAME (current_encoding));
1567 char *cp = NULL, *end;
1568 int cpnum;
1569
1570 if (strncmp (cpname, "cp", 2) == 0)
1571 cp = cpname + 2;
1572 else if (strncmp (cpname, "windows-", 8) == 0)
1573 cp = cpname + 8;
1574
1575 if (cp)
1576 {
1577 end = cp;
1578 cpnum = strtol (cp, &end, 10);
1579 if (cpnum && *end == '\0' && end - cp >= 2)
1580 file_name_codepage = cpnum;
1581 }
1582 }
1583
1584 if (!file_name_codepage)
1585 file_name_codepage = CP_ACP; /* CP_ACP = 0, but let's not assume that */
1586
1587 if (!GetCPInfo (file_name_codepage, &cp_info))
1588 {
1589 file_name_codepage = CP_ACP;
1590 if (!GetCPInfo (file_name_codepage, &cp_info))
1591 emacs_abort ();
1592 }
1593 last_max_mbslen = cp_info.MaxCharSize;
1594 }
1595
1596 return last_max_mbslen;
1597}
76b3903d 1598
cbe39279
RS
1599/* Normalize filename by converting all path separators to
1600 the specified separator. Also conditionally convert upper
1601 case path name components to lower case. */
1602
1603static void
e7ac588e 1604normalize_filename (register char *fp, char path_sep, int multibyte)
cbe39279
RS
1605{
1606 char sep;
6d2851de
EZ
1607 char *elem, *p2;
1608 int dbcs_p = max_filename_mbslen () > 1;
cbe39279 1609
e7ac588e
EZ
1610 /* Multibyte file names are in the Emacs internal representation, so
1611 we can traverse them by bytes with no problems. */
1612 if (multibyte)
1613 dbcs_p = 0;
1614
5162ffce
MB
1615 /* Always lower-case drive letters a-z, even if the filesystem
1616 preserves case in filenames.
1617 This is so filenames can be compared by string comparison
1618 functions that are case-sensitive. Even case-preserving filesystems
1619 do not distinguish case in drive letters. */
6d2851de
EZ
1620 if (dbcs_p)
1621 p2 = CharNextExA (file_name_codepage, fp, 0);
1622 else
1623 p2 = fp + 1;
1624
1625 if (*p2 == ':' && *fp >= 'A' && *fp <= 'Z')
5162ffce
MB
1626 {
1627 *fp += 'a' - 'A';
1628 fp += 2;
1629 }
1630
e7ac588e 1631 if (multibyte || NILP (Vw32_downcase_file_names))
cbe39279
RS
1632 {
1633 while (*fp)
1634 {
1635 if (*fp == '/' || *fp == '\\')
1636 *fp = path_sep;
6d2851de
EZ
1637 if (!dbcs_p)
1638 fp++;
1639 else
1640 fp = CharNextExA (file_name_codepage, fp, 0);
cbe39279
RS
1641 }
1642 return;
1643 }
1644
1645 sep = path_sep; /* convert to this path separator */
1646 elem = fp; /* start of current path element */
1647
1648 do {
1649 if (*fp >= 'a' && *fp <= 'z')
1650 elem = 0; /* don't convert this element */
1651
1652 if (*fp == 0 || *fp == ':')
1653 {
1654 sep = *fp; /* restore current separator (or 0) */
1655 *fp = '/'; /* after conversion of this element */
1656 }
1657
1658 if (*fp == '/' || *fp == '\\')
1659 {
1660 if (elem && elem != fp)
1661 {
1662 *fp = 0; /* temporary end of string */
6d2851de 1663 _mbslwr (elem); /* while we convert to lower case */
cbe39279
RS
1664 }
1665 *fp = sep; /* convert (or restore) path separator */
1666 elem = fp + 1; /* next element starts after separator */
1667 sep = path_sep;
1668 }
6d2851de
EZ
1669 if (*fp)
1670 {
1671 if (!dbcs_p)
1672 fp++;
1673 else
1674 fp = CharNextExA (file_name_codepage, fp, 0);
1675 }
1676 } while (*fp);
cbe39279
RS
1677}
1678
e7ac588e
EZ
1679/* Destructively turn backslashes into slashes. MULTIBYTE non-zero
1680 means the file name is a multibyte string in Emacs's internal
1681 representation. */
95ed0025 1682void
e7ac588e 1683dostounix_filename (register char *p, int multibyte)
95ed0025 1684{
e7ac588e 1685 normalize_filename (p, '/', multibyte);
95ed0025
RS
1686}
1687
480b0c5b 1688/* Destructively turn slashes into backslashes. */
95ed0025 1689void
b56ceb92 1690unixtodos_filename (register char *p)
95ed0025 1691{
e7ac588e 1692 normalize_filename (p, '\\', 0);
95ed0025
RS
1693}
1694
480b0c5b
GV
1695/* Remove all CR's that are followed by a LF.
1696 (From msdos.c...probably should figure out a way to share it,
1697 although this code isn't going to ever change.) */
bedf4aab 1698static int
b56ceb92 1699crlf_to_lf (register int n, register unsigned char *buf)
35f0d482 1700{
480b0c5b
GV
1701 unsigned char *np = buf;
1702 unsigned char *startp = buf;
1703 unsigned char *endp = buf + n;
35f0d482 1704
480b0c5b
GV
1705 if (n == 0)
1706 return n;
1707 while (buf < endp - 1)
95ed0025 1708 {
480b0c5b
GV
1709 if (*buf == 0x0d)
1710 {
1711 if (*(++buf) != 0x0a)
1712 *np++ = 0x0d;
1713 }
1714 else
1715 *np++ = *buf++;
95ed0025 1716 }
480b0c5b
GV
1717 if (buf < endp)
1718 *np++ = *buf++;
1719 return np - startp;
95ed0025
RS
1720}
1721
76b3903d
GV
1722/* Parse the root part of file name, if present. Return length and
1723 optionally store pointer to char after root. */
1724static int
1725parse_root (char * name, char ** pPath)
1726{
1727 char * start = name;
1728
1729 if (name == NULL)
1730 return 0;
1731
1732 /* find the root name of the volume if given */
1733 if (isalpha (name[0]) && name[1] == ':')
1734 {
1735 /* skip past drive specifier */
1736 name += 2;
1737 if (IS_DIRECTORY_SEP (name[0]))
1738 name++;
1739 }
1740 else if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1]))
1741 {
1742 int slashes = 2;
806fed21
EZ
1743 int dbcs_p = max_filename_mbslen () > 1;
1744
76b3903d
GV
1745 name += 2;
1746 do
1747 {
1748 if (IS_DIRECTORY_SEP (*name) && --slashes == 0)
1749 break;
806fed21
EZ
1750 if (dbcs_p)
1751 name = CharNextExA (file_name_codepage, name, 0);
1752 else
1753 name++;
76b3903d
GV
1754 }
1755 while ( *name );
1756 if (IS_DIRECTORY_SEP (name[0]))
1757 name++;
1758 }
1759
1760 if (pPath)
1761 *pPath = name;
1762
1763 return name - start;
1764}
1765
1766/* Get long base name for name; name is assumed to be absolute. */
1767static int
1768get_long_basename (char * name, char * buf, int size)
1769{
1770 WIN32_FIND_DATA find_data;
1771 HANDLE dir_handle;
1772 int len = 0;
1773
9ab8560d 1774 /* must be valid filename, no wild cards or other invalid characters */
22189f79 1775 if (_mbspbrk (name, "*?|<>\""))
bb1584c8
RS
1776 return 0;
1777
76b3903d
GV
1778 dir_handle = FindFirstFile (name, &find_data);
1779 if (dir_handle != INVALID_HANDLE_VALUE)
1780 {
1781 if ((len = strlen (find_data.cFileName)) < size)
1782 memcpy (buf, find_data.cFileName, len + 1);
1783 else
1784 len = 0;
1785 FindClose (dir_handle);
1786 }
1787 return len;
1788}
1789
1790/* Get long name for file, if possible (assumed to be absolute). */
1791BOOL
1792w32_get_long_filename (char * name, char * buf, int size)
1793{
1794 char * o = buf;
1795 char * p;
1796 char * q;
1797 char full[ MAX_PATH ];
1798 int len;
1799
1800 len = strlen (name);
1801 if (len >= MAX_PATH)
1802 return FALSE;
1803
1804 /* Use local copy for destructive modification. */
1805 memcpy (full, name, len+1);
1806 unixtodos_filename (full);
1807
1808 /* Copy root part verbatim. */
1809 len = parse_root (full, &p);
1810 memcpy (o, full, len);
1811 o += len;
4f8ac0b2 1812 *o = '\0';
76b3903d
GV
1813 size -= len;
1814
4f8ac0b2 1815 while (p != NULL && *p)
76b3903d
GV
1816 {
1817 q = p;
6ee4509a 1818 p = _mbschr (q, '\\');
76b3903d
GV
1819 if (p) *p = '\0';
1820 len = get_long_basename (full, o, size);
1821 if (len > 0)
1822 {
1823 o += len;
1824 size -= len;
1825 if (p != NULL)
1826 {
1827 *p++ = '\\';
1828 if (size < 2)
1829 return FALSE;
1830 *o++ = '\\';
1831 size--;
1832 *o = '\0';
1833 }
1834 }
1835 else
1836 return FALSE;
1837 }
76b3903d
GV
1838
1839 return TRUE;
1840}
1841
bedf4aab 1842static int
9d3355d1
GV
1843is_unc_volume (const char *filename)
1844{
1845 const char *ptr = filename;
1846
1847 if (!IS_DIRECTORY_SEP (ptr[0]) || !IS_DIRECTORY_SEP (ptr[1]) || !ptr[2])
1848 return 0;
1849
22189f79 1850 if (_mbspbrk (ptr + 2, "*?|<>\"\\/"))
9d3355d1
GV
1851 return 0;
1852
1853 return 1;
1854}
76b3903d 1855
75ceee05
EZ
1856/* Emulate the Posix unsetenv. */
1857int
1858unsetenv (const char *name)
1859{
1860 char *var;
1861 size_t name_len;
1862 int retval;
1863
1864 if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
1865 {
1866 errno = EINVAL;
1867 return -1;
1868 }
1869 name_len = strlen (name);
1870 /* MS docs says an environment variable cannot be longer than 32K. */
1871 if (name_len > 32767)
1872 {
1873 errno = ENOMEM;
a16e75cd 1874 return 0;
75ceee05
EZ
1875 }
1876 /* It is safe to use 'alloca' with 32K size, since the stack is at
1877 least 2MB, and we set it to 8MB in the link command line. */
1878 var = alloca (name_len + 2);
a7f11948 1879 strncpy (var, name, name_len);
75ceee05
EZ
1880 var[name_len++] = '=';
1881 var[name_len] = '\0';
1882 return _putenv (var);
1883}
1884
1885/* MS _putenv doesn't support removing a variable when the argument
1886 does not include the '=' character, so we fix that here. */
1887int
1888sys_putenv (char *str)
1889{
1890 const char *const name_end = strchr (str, '=');
1891
1892 if (name_end == NULL)
1893 {
1894 /* Remove the variable from the environment. */
1895 return unsetenv (str);
1896 }
1897
1898 return _putenv (str);
1899}
1900
480b0c5b 1901#define REG_ROOT "SOFTWARE\\GNU\\Emacs"
f332b293 1902
177c0ea7 1903LPBYTE
b56ceb92 1904w32_get_resource (char *key, LPDWORD lpdwtype)
f332b293
GV
1905{
1906 LPBYTE lpvalue;
1907 HKEY hrootkey = NULL;
1908 DWORD cbData;
177c0ea7
JB
1909
1910 /* Check both the current user and the local machine to see if
f332b293 1911 we have any resources. */
177c0ea7 1912
f332b293
GV
1913 if (RegOpenKeyEx (HKEY_CURRENT_USER, REG_ROOT, 0, KEY_READ, &hrootkey) == ERROR_SUCCESS)
1914 {
1915 lpvalue = NULL;
1916
177c0ea7 1917 if (RegQueryValueEx (hrootkey, key, NULL, NULL, NULL, &cbData) == ERROR_SUCCESS
23f86fce 1918 && (lpvalue = xmalloc (cbData)) != NULL
f332b293
GV
1919 && RegQueryValueEx (hrootkey, key, NULL, lpdwtype, lpvalue, &cbData) == ERROR_SUCCESS)
1920 {
4da4d9bb 1921 RegCloseKey (hrootkey);
f332b293
GV
1922 return (lpvalue);
1923 }
1924
70fdbb46 1925 xfree (lpvalue);
177c0ea7 1926
f332b293 1927 RegCloseKey (hrootkey);
177c0ea7
JB
1928 }
1929
f332b293
GV
1930 if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, REG_ROOT, 0, KEY_READ, &hrootkey) == ERROR_SUCCESS)
1931 {
1932 lpvalue = NULL;
177c0ea7 1933
76b3903d 1934 if (RegQueryValueEx (hrootkey, key, NULL, NULL, NULL, &cbData) == ERROR_SUCCESS
23f86fce 1935 && (lpvalue = xmalloc (cbData)) != NULL
76b3903d 1936 && RegQueryValueEx (hrootkey, key, NULL, lpdwtype, lpvalue, &cbData) == ERROR_SUCCESS)
f332b293 1937 {
4da4d9bb 1938 RegCloseKey (hrootkey);
f332b293
GV
1939 return (lpvalue);
1940 }
177c0ea7 1941
70fdbb46 1942 xfree (lpvalue);
177c0ea7 1943
f332b293 1944 RegCloseKey (hrootkey);
177c0ea7
JB
1945 }
1946
f332b293
GV
1947 return (NULL);
1948}
1949
75b08edb 1950char *get_emacs_configuration (void);
94eab1c8 1951
f332b293 1952void
aa7b87b0 1953init_environment (char ** argv)
f332b293 1954{
b3308d2e
KH
1955 static const char * const tempdirs[] = {
1956 "$TMPDIR", "$TEMP", "$TMP", "c:/"
1957 };
2d5324c5 1958
b3308d2e 1959 int i;
2d5324c5 1960
b3308d2e
KH
1961 const int imax = sizeof (tempdirs) / sizeof (tempdirs[0]);
1962
1963 /* Make sure they have a usable $TMPDIR. Many Emacs functions use
1964 temporary files and assume "/tmp" if $TMPDIR is unset, which
1965 will break on DOS/Windows. Refuse to work if we cannot find
1966 a directory, not even "c:/", usable for that purpose. */
1967 for (i = 0; i < imax ; i++)
1968 {
1969 const char *tmp = tempdirs[i];
1970
1971 if (*tmp == '$')
1972 tmp = getenv (tmp + 1);
1973 /* Note that `access' can lie to us if the directory resides on a
1974 read-only filesystem, like CD-ROM or a write-protected floppy.
1975 The only way to be really sure is to actually create a file and
1976 see if it succeeds. But I think that's too much to ask. */
6dad7178
EZ
1977
1978 /* MSVCRT's _access crashes with D_OK. */
14f20728 1979 if (tmp && faccessat (AT_FDCWD, tmp, D_OK, AT_EACCESS) == 0)
b3308d2e
KH
1980 {
1981 char * var = alloca (strlen (tmp) + 8);
1982 sprintf (var, "TMPDIR=%s", tmp);
aca583b2 1983 _putenv (strdup (var));
b3308d2e
KH
1984 break;
1985 }
1986 }
1987 if (i >= imax)
1988 cmd_error_internal
1989 (Fcons (Qerror,
1990 Fcons (build_string ("no usable temporary directories found!!"),
1991 Qnil)),
1992 "While setting TMPDIR: ");
1993
ca149beb
AI
1994 /* Check for environment variables and use registry settings if they
1995 don't exist. Fallback on default values where applicable. */
f332b293 1996 {
480b0c5b
GV
1997 int i;
1998 LPBYTE lpval;
1999 DWORD dwType;
69fb0241 2000 char locale_name[32];
2d5324c5 2001 char default_home[MAX_PATH];
fdc5744d 2002 int appdata = 0;
f332b293 2003
e00b99c8 2004 static const struct env_entry
ca149beb
AI
2005 {
2006 char * name;
2007 char * def_value;
e00b99c8 2008 } dflt_envvars[] =
ca149beb 2009 {
76151e2c
EZ
2010 /* If the default value is NULL, we will use the value from the
2011 outside environment or the Registry, but will not push the
2012 variable into the Emacs environment if it is defined neither
2013 in the Registry nor in the outside environment. */
ca149beb
AI
2014 {"HOME", "C:/"},
2015 {"PRELOAD_WINSOCK", NULL},
2016 {"emacs_dir", "C:/emacs"},
76151e2c 2017 {"EMACSLOADPATH", NULL},
ca149beb 2018 {"SHELL", "%emacs_dir%/bin/cmdproxy.exe"},
76151e2c
EZ
2019 {"EMACSDATA", NULL},
2020 {"EMACSPATH", NULL},
2021 {"INFOPATH", NULL},
2022 {"EMACSDOC", NULL},
69fb0241
JR
2023 {"TERM", "cmd"},
2024 {"LANG", NULL},
480b0c5b
GV
2025 };
2026
ed3751c8 2027#define N_ENV_VARS sizeof (dflt_envvars)/sizeof (dflt_envvars[0])
e00b99c8
EZ
2028
2029 /* We need to copy dflt_envvars[] and work on the copy because we
2030 don't want the dumped Emacs to inherit the values of
2031 environment variables we saw during dumping (which could be on
2032 a different system). The defaults above must be left intact. */
2033 struct env_entry env_vars[N_ENV_VARS];
2034
2035 for (i = 0; i < N_ENV_VARS; i++)
2036 env_vars[i] = dflt_envvars[i];
2037
2d5324c5
JR
2038 /* For backwards compatibility, check if a .emacs file exists in C:/
2039 If not, then we can try to default to the appdata directory under the
2040 user's profile, which is more likely to be writable. */
99b1553e 2041 if (!check_existing ("C:/.emacs"))
94eab1c8
JB
2042 {
2043 HRESULT profile_result;
2044 /* Dynamically load ShGetFolderPath, as it won't exist on versions
2045 of Windows 95 and NT4 that have not been updated to include
2046 MSIE 5. */
2047 ShGetFolderPath_fn get_folder_path;
2048 get_folder_path = (ShGetFolderPath_fn)
2049 GetProcAddress (GetModuleHandle ("shell32.dll"), "SHGetFolderPathA");
2050
2051 if (get_folder_path != NULL)
2052 {
2053 profile_result = get_folder_path (NULL, CSIDL_APPDATA, NULL,
2054 0, default_home);
2d5324c5 2055
94eab1c8
JB
2056 /* If we can't get the appdata dir, revert to old behavior. */
2057 if (profile_result == S_OK)
fdc5744d
JB
2058 {
2059 env_vars[0].def_value = default_home;
2060 appdata = 1;
2061 }
94eab1c8
JB
2062 }
2063 }
2d5324c5 2064
69fb0241
JR
2065 /* Get default locale info and use it for LANG. */
2066 if (GetLocaleInfo (LOCALE_USER_DEFAULT,
2067 LOCALE_SABBREVLANGNAME | LOCALE_USE_CP_ACP,
2068 locale_name, sizeof (locale_name)))
2069 {
e00b99c8 2070 for (i = 0; i < N_ENV_VARS; i++)
69fb0241
JR
2071 {
2072 if (strcmp (env_vars[i].name, "LANG") == 0)
2073 {
2074 env_vars[i].def_value = locale_name;
2075 break;
2076 }
2077 }
2078 }
2079
ca149beb
AI
2080#define SET_ENV_BUF_SIZE (4 * MAX_PATH) /* to cover EMACSLOADPATH */
2081
2082 /* Treat emacs_dir specially: set it unconditionally based on our
76151e2c 2083 location. */
ca149beb
AI
2084 {
2085 char *p;
2086 char modname[MAX_PATH];
2087
2088 if (!GetModuleFileName (NULL, modname, MAX_PATH))
1088b922 2089 emacs_abort ();
6ee4509a 2090 if ((p = _mbsrchr (modname, '\\')) == NULL)
1088b922 2091 emacs_abort ();
ca149beb
AI
2092 *p = 0;
2093
6ee4509a 2094 if ((p = _mbsrchr (modname, '\\')) && xstrcasecmp (p, "\\bin") == 0)
ca149beb
AI
2095 {
2096 char buf[SET_ENV_BUF_SIZE];
2097
2098 *p = 0;
6ee4509a 2099 for (p = modname; *p; p = CharNext (p))
ca149beb 2100 if (*p == '\\') *p = '/';
177c0ea7 2101
ed3751c8 2102 _snprintf (buf, sizeof (buf)-1, "emacs_dir=%s", modname);
a302c7ae 2103 _putenv (strdup (buf));
ca149beb 2104 }
950090be
JR
2105 /* Handle running emacs from the build directory: src/oo-spd/i386/ */
2106
2107 /* FIXME: should use substring of get_emacs_configuration ().
2108 But I don't think the Windows build supports alpha, mips etc
2109 anymore, so have taken the easy option for now. */
62aba0d4
FP
2110 else if (p && (xstrcasecmp (p, "\\i386") == 0
2111 || xstrcasecmp (p, "\\AMD64") == 0))
950090be
JR
2112 {
2113 *p = 0;
6ee4509a 2114 p = _mbsrchr (modname, '\\');
950090be
JR
2115 if (p != NULL)
2116 {
2117 *p = 0;
6ee4509a 2118 p = _mbsrchr (modname, '\\');
05131107 2119 if (p && xstrcasecmp (p, "\\src") == 0)
950090be
JR
2120 {
2121 char buf[SET_ENV_BUF_SIZE];
2122
2123 *p = 0;
6ee4509a 2124 for (p = modname; *p; p = CharNext (p))
950090be
JR
2125 if (*p == '\\') *p = '/';
2126
ed3751c8 2127 _snprintf (buf, sizeof (buf)-1, "emacs_dir=%s", modname);
950090be
JR
2128 _putenv (strdup (buf));
2129 }
2130 }
2131 }
ca149beb
AI
2132 }
2133
e00b99c8 2134 for (i = 0; i < N_ENV_VARS; i++)
f332b293 2135 {
ca149beb 2136 if (!getenv (env_vars[i].name))
480b0c5b 2137 {
ca149beb 2138 int dont_free = 0;
480b0c5b 2139
aa5ee2a3
JB
2140 if ((lpval = w32_get_resource (env_vars[i].name, &dwType)) == NULL
2141 /* Also ignore empty environment variables. */
2142 || *lpval == 0)
ca149beb 2143 {
70fdbb46 2144 xfree (lpval);
ca149beb
AI
2145 lpval = env_vars[i].def_value;
2146 dwType = REG_EXPAND_SZ;
2147 dont_free = 1;
fdc5744d 2148 if (!strcmp (env_vars[i].name, "HOME") && !appdata)
3438fe21
EZ
2149 Vdelayed_warnings_list
2150 = Fcons (listn (CONSTYPE_HEAP, 2,
073c88c2 2151 intern ("initialization"),
694b6c97 2152 build_string ("Setting HOME to C:\\ by default is deprecated")),
073c88c2 2153 Vdelayed_warnings_list);
480b0c5b 2154 }
ca149beb
AI
2155
2156 if (lpval)
480b0c5b 2157 {
892eb237 2158 char buf1[SET_ENV_BUF_SIZE], buf2[SET_ENV_BUF_SIZE];
ca149beb 2159
892eb237 2160 if (dwType == REG_EXPAND_SZ)
ed3751c8 2161 ExpandEnvironmentStrings ((LPSTR) lpval, buf1, sizeof (buf1));
ca149beb 2162 else if (dwType == REG_SZ)
892eb237
EZ
2163 strcpy (buf1, lpval);
2164 if (dwType == REG_EXPAND_SZ || dwType == REG_SZ)
ca149beb 2165 {
ed3751c8 2166 _snprintf (buf2, sizeof (buf2)-1, "%s=%s", env_vars[i].name,
892eb237
EZ
2167 buf1);
2168 _putenv (strdup (buf2));
ca149beb 2169 }
f332b293 2170
ca149beb
AI
2171 if (!dont_free)
2172 xfree (lpval);
2173 }
480b0c5b
GV
2174 }
2175 }
2176 }
2177
75b08edb
GV
2178 /* Rebuild system configuration to reflect invoking system. */
2179 Vsystem_configuration = build_string (EMACS_CONFIGURATION);
2180
76b3903d
GV
2181 /* Another special case: on NT, the PATH variable is actually named
2182 "Path" although cmd.exe (perhaps NT itself) arranges for
2183 environment variable lookup and setting to be case insensitive.
2184 However, Emacs assumes a fully case sensitive environment, so we
2185 need to change "Path" to "PATH" to match the expectations of
2186 various elisp packages. We do this by the sneaky method of
2187 modifying the string in the C runtime environ entry.
2188
2189 The same applies to COMSPEC. */
2190 {
2191 char ** envp;
2192
2193 for (envp = environ; *envp; envp++)
2194 if (_strnicmp (*envp, "PATH=", 5) == 0)
2195 memcpy (*envp, "PATH=", 5);
2196 else if (_strnicmp (*envp, "COMSPEC=", 8) == 0)
2197 memcpy (*envp, "COMSPEC=", 8);
2198 }
2199
9239d970 2200 /* Remember the initial working directory for getcwd. */
6dad7178
EZ
2201 /* FIXME: Do we need to resolve possible symlinks in startup_dir?
2202 Does it matter anywhere in Emacs? */
76b3903d 2203 if (!GetCurrentDirectory (MAXPATHLEN, startup_dir))
1088b922 2204 emacs_abort ();
76b3903d
GV
2205
2206 {
aa7b87b0 2207 static char modname[MAX_PATH];
76b3903d
GV
2208
2209 if (!GetModuleFileName (NULL, modname, MAX_PATH))
1088b922 2210 emacs_abort ();
aa7b87b0 2211 argv[0] = modname;
76b3903d
GV
2212 }
2213
20af4831
JR
2214 /* Determine if there is a middle mouse button, to allow parse_button
2215 to decide whether right mouse events should be mouse-2 or
2216 mouse-3. */
e0c181dd 2217 w32_num_mouse_buttons = GetSystemMetrics (SM_CMOUSEBUTTONS);
20af4831 2218
480b0c5b
GV
2219 init_user_info ();
2220}
2221
6dad7178
EZ
2222/* Called from expand-file-name when default-directory is not a string. */
2223
bf794306
EZ
2224char *
2225emacs_root_dir (void)
2226{
2227 static char root_dir[FILENAME_MAX];
2228 const char *p;
2229
2230 p = getenv ("emacs_dir");
2231 if (p == NULL)
1088b922 2232 emacs_abort ();
bf794306
EZ
2233 strcpy (root_dir, p);
2234 root_dir[parse_root (root_dir, NULL)] = '\0';
e7ac588e 2235 dostounix_filename (root_dir, 0);
bf794306
EZ
2236 return root_dir;
2237}
2238
480b0c5b
GV
2239/* We don't have scripts to automatically determine the system configuration
2240 for Emacs before it's compiled, and we don't want to have to make the
2241 user enter it, so we define EMACS_CONFIGURATION to invoke this runtime
2242 routine. */
2243
480b0c5b
GV
2244char *
2245get_emacs_configuration (void)
2246{
2247 char *arch, *oem, *os;
c5247da2 2248 int build_num;
a302c7ae 2249 static char configuration_buffer[32];
480b0c5b
GV
2250
2251 /* Determine the processor type. */
177c0ea7 2252 switch (get_processor_type ())
480b0c5b
GV
2253 {
2254
2255#ifdef PROCESSOR_INTEL_386
2256 case PROCESSOR_INTEL_386:
2257 case PROCESSOR_INTEL_486:
2258 case PROCESSOR_INTEL_PENTIUM:
62aba0d4
FP
2259#ifdef _WIN64
2260 arch = "amd64";
2261#else
480b0c5b 2262 arch = "i386";
62aba0d4
FP
2263#endif
2264 break;
2265#endif
2266#ifdef PROCESSOR_AMD_X8664
2267 case PROCESSOR_AMD_X8664:
2268 arch = "amd64";
480b0c5b
GV
2269 break;
2270#endif
2271
480b0c5b
GV
2272#ifdef PROCESSOR_MIPS_R2000
2273 case PROCESSOR_MIPS_R2000:
2274 case PROCESSOR_MIPS_R3000:
2275 case PROCESSOR_MIPS_R4000:
2276 arch = "mips";
2277 break;
2278#endif
2279
2280#ifdef PROCESSOR_ALPHA_21064
2281 case PROCESSOR_ALPHA_21064:
2282 arch = "alpha";
2283 break;
2284#endif
2285
2286 default:
2287 arch = "unknown";
2288 break;
f332b293 2289 }
480b0c5b 2290
a302c7ae
AI
2291 /* Use the OEM field to reflect the compiler/library combination. */
2292#ifdef _MSC_VER
2293#define COMPILER_NAME "msvc"
2294#else
2295#ifdef __GNUC__
2296#define COMPILER_NAME "mingw"
2297#else
2298#define COMPILER_NAME "unknown"
2299#endif
2300#endif
2301 oem = COMPILER_NAME;
480b0c5b 2302
c5247da2
GV
2303 switch (osinfo_cache.dwPlatformId) {
2304 case VER_PLATFORM_WIN32_NT:
2305 os = "nt";
2306 build_num = osinfo_cache.dwBuildNumber;
2307 break;
2308 case VER_PLATFORM_WIN32_WINDOWS:
2309 if (osinfo_cache.dwMinorVersion == 0) {
2310 os = "windows95";
2311 } else {
2312 os = "windows98";
2313 }
2314 build_num = LOWORD (osinfo_cache.dwBuildNumber);
2315 break;
2316 case VER_PLATFORM_WIN32s:
2317 /* Not supported, should not happen. */
2318 os = "windows32s";
2319 build_num = LOWORD (osinfo_cache.dwBuildNumber);
2320 break;
2321 default:
2322 os = "unknown";
2323 build_num = 0;
2324 break;
2325 }
2326
2327 if (osinfo_cache.dwPlatformId == VER_PLATFORM_WIN32_NT) {
2328 sprintf (configuration_buffer, "%s-%s-%s%d.%d.%d", arch, oem, os,
2329 get_w32_major_version (), get_w32_minor_version (), build_num);
2330 } else {
2331 sprintf (configuration_buffer, "%s-%s-%s.%d", arch, oem, os, build_num);
2332 }
480b0c5b 2333
480b0c5b 2334 return configuration_buffer;
f332b293
GV
2335}
2336
a302c7ae
AI
2337char *
2338get_emacs_configuration_options (void)
2339{
38c54d9d
JB
2340 static char *options_buffer;
2341 char cv[32]; /* Enough for COMPILER_VERSION. */
2342 char *options[] = {
2343 cv, /* To be filled later. */
2344#ifdef EMACSDEBUG
2345 " --no-opt",
d7f29f8e
EZ
2346#endif
2347#ifdef ENABLE_CHECKING
2348 " --enable-checking",
38c54d9d
JB
2349#endif
2350 /* configure.bat already sets USER_CFLAGS and USER_LDFLAGS
2351 with a starting space to save work here. */
2352#ifdef USER_CFLAGS
2353 " --cflags", USER_CFLAGS,
2354#endif
2355#ifdef USER_LDFLAGS
2356 " --ldflags", USER_LDFLAGS,
2357#endif
2358 NULL
2359 };
2360 size_t size = 0;
2361 int i;
a302c7ae
AI
2362
2363/* Work out the effective configure options for this build. */
2364#ifdef _MSC_VER
2365#define COMPILER_VERSION "--with-msvc (%d.%02d)", _MSC_VER / 100, _MSC_VER % 100
2366#else
2367#ifdef __GNUC__
2368#define COMPILER_VERSION "--with-gcc (%d.%d)", __GNUC__, __GNUC_MINOR__
2369#else
2370#define COMPILER_VERSION ""
2371#endif
2372#endif
2373
83e245c4 2374 if (_snprintf (cv, sizeof (cv) - 1, COMPILER_VERSION) < 0)
38c54d9d 2375 return "Error: not enough space for compiler version";
83e245c4 2376 cv[sizeof (cv) - 1] = '\0';
38c54d9d
JB
2377
2378 for (i = 0; options[i]; i++)
2379 size += strlen (options[i]);
2380
2381 options_buffer = xmalloc (size + 1);
fc33e153 2382 options_buffer[0] = '\0';
38c54d9d
JB
2383
2384 for (i = 0; options[i]; i++)
2385 strcat (options_buffer, options[i]);
2386
a302c7ae
AI
2387 return options_buffer;
2388}
2389
2390
35f0d482
KH
2391#include <sys/timeb.h>
2392
2393/* Emulate gettimeofday (Ulrich Leodolter, 1/11/95). */
177c0ea7 2394void
35f0d482
KH
2395gettimeofday (struct timeval *tv, struct timezone *tz)
2396{
6e602566 2397 struct _timeb tb;
35f0d482
KH
2398 _ftime (&tb);
2399
2400 tv->tv_sec = tb.time;
2401 tv->tv_usec = tb.millitm * 1000L;
97a93095
EZ
2402 /* Implementation note: _ftime sometimes doesn't update the dstflag
2403 according to the new timezone when the system timezone is
2404 changed. We could fix that by using GetSystemTime and
2405 GetTimeZoneInformation, but that doesn't seem necessary, since
2406 Emacs always calls gettimeofday with the 2nd argument NULL (see
e9a9ae03 2407 current_emacs_time). */
177c0ea7 2408 if (tz)
35f0d482
KH
2409 {
2410 tz->tz_minuteswest = tb.timezone; /* minutes west of Greenwich */
2411 tz->tz_dsttime = tb.dstflag; /* type of dst correction */
2412 }
2413}
35f0d482 2414
388cdec0
EZ
2415/* Emulate fdutimens. */
2416
2417/* Set the access and modification time stamps of FD (a.k.a. FILE) to be
2418 TIMESPEC[0] and TIMESPEC[1], respectively.
2419 FD must be either negative -- in which case it is ignored --
2420 or a file descriptor that is open on FILE.
2421 If FD is nonnegative, then FILE can be NULL, which means
2422 use just futimes instead of utimes.
2423 If TIMESPEC is null, FAIL.
2424 Return 0 on success, -1 (setting errno) on failure. */
2425
2426int
2427fdutimens (int fd, char const *file, struct timespec const timespec[2])
2428{
2429 struct _utimbuf ut;
2430
2431 if (!timespec)
2432 {
2433 errno = ENOSYS;
2434 return -1;
2435 }
2436 if (fd < 0 && !file)
2437 {
2438 errno = EBADF;
2439 return -1;
2440 }
2441 ut.actime = timespec[0].tv_sec;
2442 ut.modtime = timespec[1].tv_sec;
2443 if (fd >= 0)
2444 return _futime (fd, &ut);
2445 else
2446 return _utime (file, &ut);
2447}
2448
2449
480b0c5b 2450/* ------------------------------------------------------------------------- */
b46a6a83 2451/* IO support and wrapper functions for the Windows API. */
480b0c5b 2452/* ------------------------------------------------------------------------- */
95ed0025 2453
480b0c5b 2454/* Place a wrapper around the MSVC version of ctime. It returns NULL
177c0ea7 2455 on network directories, so we handle that case here.
480b0c5b
GV
2456 (Ulrich Leodolter, 1/11/95). */
2457char *
2458sys_ctime (const time_t *t)
2459{
2460 char *str = (char *) ctime (t);
2461 return (str ? str : "Sun Jan 01 00:00:00 1970");
2462}
2463
2464/* Emulate sleep...we could have done this with a define, but that
2465 would necessitate including windows.h in the files that used it.
2466 This is much easier. */
2467void
2468sys_sleep (int seconds)
2469{
2470 Sleep (seconds * 1000);
2471}
2472
76b3903d 2473/* Internal MSVC functions for low-level descriptor munging */
480b0c5b
GV
2474extern int __cdecl _set_osfhnd (int fd, long h);
2475extern int __cdecl _free_osfhnd (int fd);
2476
2477/* parallel array of private info on file handles */
2478filedesc fd_info [ MAXDESC ];
2479
76b3903d
GV
2480typedef struct volume_info_data {
2481 struct volume_info_data * next;
2482
2483 /* time when info was obtained */
2484 DWORD timestamp;
2485
2486 /* actual volume info */
2487 char * root_dir;
480b0c5b
GV
2488 DWORD serialnum;
2489 DWORD maxcomp;
2490 DWORD flags;
76b3903d
GV
2491 char * name;
2492 char * type;
2493} volume_info_data;
2494
2495/* Global referenced by various functions. */
2496static volume_info_data volume_info;
2497
2498/* Vector to indicate which drives are local and fixed (for which cached
2499 data never expires). */
2500static BOOL fixed_drives[26];
2501
2502/* Consider cached volume information to be stale if older than 10s,
2503 at least for non-local drives. Info for fixed drives is never stale. */
2504#define DRIVE_INDEX( c ) ( (c) <= 'Z' ? (c) - 'A' : (c) - 'a' )
2505#define VOLINFO_STILL_VALID( root_dir, info ) \
2506 ( ( isalpha (root_dir[0]) && \
2507 fixed_drives[ DRIVE_INDEX (root_dir[0]) ] ) \
2508 || GetTickCount () - info->timestamp < 10000 )
2509
2510/* Cache support functions. */
2511
2512/* Simple linked list with linear search is sufficient. */
2513static volume_info_data *volume_cache = NULL;
2514
2515static volume_info_data *
2516lookup_volume_info (char * root_dir)
2517{
2518 volume_info_data * info;
2519
2520 for (info = volume_cache; info; info = info->next)
05131107 2521 if (xstrcasecmp (info->root_dir, root_dir) == 0)
76b3903d
GV
2522 break;
2523 return info;
2524}
2525
2526static void
2527add_volume_info (char * root_dir, volume_info_data * info)
2528{
a302c7ae 2529 info->root_dir = xstrdup (root_dir);
76b3903d
GV
2530 info->next = volume_cache;
2531 volume_cache = info;
2532}
2533
2534
2535/* Wrapper for GetVolumeInformation, which uses caching to avoid
2536 performance penalty (~2ms on 486 for local drives, 7.5ms for local
2537 cdrom drive, ~5-10ms or more for remote drives on LAN). */
bedf4aab 2538static volume_info_data *
76b3903d
GV
2539GetCachedVolumeInformation (char * root_dir)
2540{
2541 volume_info_data * info;
2542 char default_root[ MAX_PATH ];
2543
2544 /* NULL for root_dir means use root from current directory. */
2545 if (root_dir == NULL)
2546 {
2547 if (GetCurrentDirectory (MAX_PATH, default_root) == 0)
2548 return NULL;
2549 parse_root (default_root, &root_dir);
2550 *root_dir = 0;
2551 root_dir = default_root;
2552 }
2553
2554 /* Local fixed drives can be cached permanently. Removable drives
2555 cannot be cached permanently, since the volume name and serial
2556 number (if nothing else) can change. Remote drives should be
2557 treated as if they are removable, since there is no sure way to
2558 tell whether they are or not. Also, the UNC association of drive
2559 letters mapped to remote volumes can be changed at any time (even
2560 by other processes) without notice.
177c0ea7 2561
76b3903d
GV
2562 As a compromise, so we can benefit from caching info for remote
2563 volumes, we use a simple expiry mechanism to invalidate cache
2564 entries that are more than ten seconds old. */
2565
2566#if 0
2567 /* No point doing this, because WNetGetConnection is even slower than
2568 GetVolumeInformation, consistently taking ~50ms on a 486 (FWIW,
2569 GetDriveType is about the only call of this type which does not
2570 involve network access, and so is extremely quick). */
2571
2572 /* Map drive letter to UNC if remote. */
ed3751c8 2573 if (isalpha (root_dir[0]) && !fixed[DRIVE_INDEX (root_dir[0])])
76b3903d
GV
2574 {
2575 char remote_name[ 256 ];
2576 char drive[3] = { root_dir[0], ':' };
2577
2578 if (WNetGetConnection (drive, remote_name, sizeof (remote_name))
2579 == NO_ERROR)
2580 /* do something */ ;
2581 }
2582#endif
2583
2584 info = lookup_volume_info (root_dir);
2585
2586 if (info == NULL || ! VOLINFO_STILL_VALID (root_dir, info))
94eab1c8
JB
2587 {
2588 char name[ 256 ];
2589 DWORD serialnum;
2590 DWORD maxcomp;
2591 DWORD flags;
2592 char type[ 256 ];
2593
2594 /* Info is not cached, or is stale. */
2595 if (!GetVolumeInformation (root_dir,
2596 name, sizeof (name),
2597 &serialnum,
2598 &maxcomp,
2599 &flags,
2600 type, sizeof (type)))
2601 return NULL;
76b3903d 2602
94eab1c8
JB
2603 /* Cache the volume information for future use, overwriting existing
2604 entry if present. */
2605 if (info == NULL)
2606 {
23f86fce 2607 info = xmalloc (sizeof (volume_info_data));
94eab1c8
JB
2608 add_volume_info (root_dir, info);
2609 }
2610 else
2611 {
2612 xfree (info->name);
2613 xfree (info->type);
2614 }
2615
2616 info->name = xstrdup (name);
2617 info->serialnum = serialnum;
2618 info->maxcomp = maxcomp;
2619 info->flags = flags;
2620 info->type = xstrdup (type);
2621 info->timestamp = GetTickCount ();
2622 }
76b3903d
GV
2623
2624 return info;
2625}
480b0c5b 2626
6dad7178
EZ
2627/* Get information on the volume where NAME is held; set path pointer to
2628 start of pathname in NAME (past UNC header\volume header if present),
2629 if pPath is non-NULL.
2630
2631 Note: if NAME includes symlinks, the information is for the volume
2632 of the symlink, not of its target. That's because, even though
2633 GetVolumeInformation returns information about the symlink target
2634 of its argument, we only pass the root directory to
2635 GetVolumeInformation, not the full NAME. */
bedf4aab 2636static int
480b0c5b 2637get_volume_info (const char * name, const char ** pPath)
95ed0025 2638{
480b0c5b
GV
2639 char temp[MAX_PATH];
2640 char *rootname = NULL; /* default to current volume */
76b3903d 2641 volume_info_data * info;
480b0c5b
GV
2642
2643 if (name == NULL)
2644 return FALSE;
2645
6dad7178 2646 /* Find the root name of the volume if given. */
480b0c5b
GV
2647 if (isalpha (name[0]) && name[1] == ':')
2648 {
2649 rootname = temp;
2650 temp[0] = *name++;
2651 temp[1] = *name++;
2652 temp[2] = '\\';
2653 temp[3] = 0;
2654 }
2655 else if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1]))
95ed0025 2656 {
480b0c5b
GV
2657 char *str = temp;
2658 int slashes = 4;
806fed21
EZ
2659 int dbcs_p = max_filename_mbslen () > 1;
2660
480b0c5b
GV
2661 rootname = temp;
2662 do
2663 {
2664 if (IS_DIRECTORY_SEP (*name) && --slashes == 0)
2665 break;
806fed21
EZ
2666 if (!dbcs_p)
2667 *str++ = *name++;
2668 else
2669 {
2670 const char *p = name;
2671
2672 name = CharNextExA (file_name_codepage, name, 0);
2673 memcpy (str, p, name - p);
2674 str += name - p;
2675 }
480b0c5b
GV
2676 }
2677 while ( *name );
2678
480b0c5b
GV
2679 *str++ = '\\';
2680 *str = 0;
95ed0025 2681 }
480b0c5b
GV
2682
2683 if (pPath)
2684 *pPath = name;
177c0ea7 2685
76b3903d
GV
2686 info = GetCachedVolumeInformation (rootname);
2687 if (info != NULL)
95ed0025 2688 {
76b3903d
GV
2689 /* Set global referenced by other functions. */
2690 volume_info = *info;
480b0c5b 2691 return TRUE;
95ed0025 2692 }
480b0c5b
GV
2693 return FALSE;
2694}
2695
2696/* Determine if volume is FAT format (ie. only supports short 8.3
6dad7178
EZ
2697 names); also set path pointer to start of pathname in name, if
2698 pPath is non-NULL. */
bedf4aab 2699static int
480b0c5b
GV
2700is_fat_volume (const char * name, const char ** pPath)
2701{
2702 if (get_volume_info (name, pPath))
2703 return (volume_info.maxcomp == 12);
2704 return FALSE;
2705}
2706
6dad7178
EZ
2707/* Map filename to a valid 8.3 name if necessary.
2708 The result is a pointer to a static buffer, so CAVEAT EMPTOR! */
480b0c5b 2709const char *
fbd6baed 2710map_w32_filename (const char * name, const char ** pPath)
480b0c5b
GV
2711{
2712 static char shortname[MAX_PATH];
2713 char * str = shortname;
2714 char c;
480b0c5b 2715 char * path;
76b3903d 2716 const char * save_name = name;
480b0c5b 2717
ca149beb
AI
2718 if (strlen (name) >= MAX_PATH)
2719 {
2720 /* Return a filename which will cause callers to fail. */
2721 strcpy (shortname, "?");
2722 return shortname;
2723 }
2724
a302c7ae 2725 if (is_fat_volume (name, (const char **)&path)) /* truncate to 8.3 */
95ed0025 2726 {
480b0c5b
GV
2727 register int left = 8; /* maximum number of chars in part */
2728 register int extn = 0; /* extension added? */
2729 register int dots = 2; /* maximum number of dots allowed */
2730
2731 while (name < path)
2732 *str++ = *name++; /* skip past UNC header */
2733
2734 while ((c = *name++))
2735 {
2736 switch ( c )
2737 {
6dad7178 2738 case ':':
480b0c5b
GV
2739 case '\\':
2740 case '/':
6dad7178 2741 *str++ = (c == ':' ? ':' : '\\');
480b0c5b
GV
2742 extn = 0; /* reset extension flags */
2743 dots = 2; /* max 2 dots */
2744 left = 8; /* max length 8 for main part */
2745 break;
2746 case '.':
2747 if ( dots )
2748 {
2749 /* Convert path components of the form .xxx to _xxx,
2750 but leave . and .. as they are. This allows .emacs
2751 to be read as _emacs, for example. */
2752
2753 if (! *name ||
2754 *name == '.' ||
2755 IS_DIRECTORY_SEP (*name))
2756 {
2757 *str++ = '.';
2758 dots--;
2759 }
2760 else
2761 {
2762 *str++ = '_';
2763 left--;
2764 dots = 0;
2765 }
2766 }
2767 else if ( !extn )
2768 {
2769 *str++ = '.';
2770 extn = 1; /* we've got an extension */
2771 left = 3; /* 3 chars in extension */
2772 }
2773 else
2774 {
2775 /* any embedded dots after the first are converted to _ */
2776 *str++ = '_';
2777 }
2778 break;
2779 case '~':
2780 case '#': /* don't lose these, they're important */
2781 if ( ! left )
2782 str[-1] = c; /* replace last character of part */
2783 /* FALLTHRU */
2784 default:
2785 if ( left )
2786 {
2787 *str++ = tolower (c); /* map to lower case (looks nicer) */
2788 left--;
2789 dots = 0; /* started a path component */
2790 }
2791 break;
2792 }
2793 }
2794 *str = '\0';
fc85cb29
RS
2795 }
2796 else
2797 {
2798 strcpy (shortname, name);
2799 unixtodos_filename (shortname);
95ed0025 2800 }
480b0c5b
GV
2801
2802 if (pPath)
76b3903d 2803 *pPath = shortname + (path - save_name);
480b0c5b 2804
fc85cb29 2805 return shortname;
480b0c5b
GV
2806}
2807
b3308d2e
KH
2808static int
2809is_exec (const char * name)
2810{
2811 char * p = strrchr (name, '.');
2812 return
2813 (p != NULL
05131107
JR
2814 && (xstrcasecmp (p, ".exe") == 0 ||
2815 xstrcasecmp (p, ".com") == 0 ||
2816 xstrcasecmp (p, ".bat") == 0 ||
2817 xstrcasecmp (p, ".cmd") == 0));
b3308d2e
KH
2818}
2819
177c0ea7 2820/* Emulate the Unix directory procedures opendir, closedir,
76b3903d
GV
2821 and readdir. We can't use the procedures supplied in sysdep.c,
2822 so we provide them here. */
2823
95ef7787 2824struct dirent dir_static; /* simulated directory contents */
76b3903d
GV
2825static HANDLE dir_find_handle = INVALID_HANDLE_VALUE;
2826static int dir_is_fat;
2827static char dir_pathname[MAXPATHLEN+1];
2828static WIN32_FIND_DATA dir_find_data;
2829
9d3355d1
GV
2830/* Support shares on a network resource as subdirectories of a read-only
2831 root directory. */
2832static HANDLE wnet_enum_handle = INVALID_HANDLE_VALUE;
bedf4aab
JB
2833static HANDLE open_unc_volume (const char *);
2834static char *read_unc_volume (HANDLE, char *, int);
2835static void close_unc_volume (HANDLE);
9d3355d1 2836
76b3903d 2837DIR *
cf01a359 2838opendir (const char *filename)
76b3903d
GV
2839{
2840 DIR *dirp;
2841
2842 /* Opening is done by FindFirstFile. However, a read is inherent to
2843 this operation, so we defer the open until read time. */
2844
76b3903d
GV
2845 if (dir_find_handle != INVALID_HANDLE_VALUE)
2846 return NULL;
9d3355d1
GV
2847 if (wnet_enum_handle != INVALID_HANDLE_VALUE)
2848 return NULL;
2849
6dad7178
EZ
2850 /* Note: We don't support traversal of UNC volumes via symlinks.
2851 Doing so would mean punishing 99.99% of use cases by resolving
2852 all the possible symlinks in FILENAME, recursively. */
9d3355d1
GV
2853 if (is_unc_volume (filename))
2854 {
2855 wnet_enum_handle = open_unc_volume (filename);
2856 if (wnet_enum_handle == INVALID_HANDLE_VALUE)
2857 return NULL;
2858 }
2859
2860 if (!(dirp = (DIR *) malloc (sizeof (DIR))))
2861 return NULL;
76b3903d
GV
2862
2863 dirp->dd_fd = 0;
2864 dirp->dd_loc = 0;
2865 dirp->dd_size = 0;
2866
2867 strncpy (dir_pathname, map_w32_filename (filename, NULL), MAXPATHLEN);
2868 dir_pathname[MAXPATHLEN] = '\0';
6dad7178
EZ
2869 /* Note: We don't support symlinks to file names on FAT volumes.
2870 Doing so would mean punishing 99.99% of use cases by resolving
2871 all the possible symlinks in FILENAME, recursively. */
76b3903d
GV
2872 dir_is_fat = is_fat_volume (filename, NULL);
2873
2874 return dirp;
2875}
2876
2877void
2878closedir (DIR *dirp)
2879{
2880 /* If we have a find-handle open, close it. */
2881 if (dir_find_handle != INVALID_HANDLE_VALUE)
2882 {
2883 FindClose (dir_find_handle);
2884 dir_find_handle = INVALID_HANDLE_VALUE;
2885 }
9d3355d1
GV
2886 else if (wnet_enum_handle != INVALID_HANDLE_VALUE)
2887 {
2888 close_unc_volume (wnet_enum_handle);
2889 wnet_enum_handle = INVALID_HANDLE_VALUE;
2890 }
76b3903d
GV
2891 xfree ((char *) dirp);
2892}
2893
95ef7787 2894struct dirent *
76b3903d
GV
2895readdir (DIR *dirp)
2896{
b07103dc
EZ
2897 int downcase = !NILP (Vw32_downcase_file_names);
2898
9d3355d1
GV
2899 if (wnet_enum_handle != INVALID_HANDLE_VALUE)
2900 {
177c0ea7 2901 if (!read_unc_volume (wnet_enum_handle,
59eb0929
JB
2902 dir_find_data.cFileName,
2903 MAX_PATH))
9d3355d1
GV
2904 return NULL;
2905 }
76b3903d 2906 /* If we aren't dir_finding, do a find-first, otherwise do a find-next. */
9d3355d1 2907 else if (dir_find_handle == INVALID_HANDLE_VALUE)
76b3903d
GV
2908 {
2909 char filename[MAXNAMLEN + 3];
2910 int ln;
806fed21 2911 int dbcs_p = max_filename_mbslen () > 1;
76b3903d
GV
2912
2913 strcpy (filename, dir_pathname);
2914 ln = strlen (filename) - 1;
806fed21
EZ
2915 if (!dbcs_p)
2916 {
2917 if (!IS_DIRECTORY_SEP (filename[ln]))
2918 strcat (filename, "\\");
2919 }
2920 else
2921 {
2922 char *end = filename + ln + 1;
2923 char *last_char = CharPrevExA (file_name_codepage, filename, end, 0);
2924
2925 if (!IS_DIRECTORY_SEP (*last_char))
2926 strcat (filename, "\\");
2927 }
76b3903d
GV
2928 strcat (filename, "*");
2929
6dad7178
EZ
2930 /* Note: No need to resolve symlinks in FILENAME, because
2931 FindFirst opens the directory that is the target of a
2932 symlink. */
76b3903d
GV
2933 dir_find_handle = FindFirstFile (filename, &dir_find_data);
2934
2935 if (dir_find_handle == INVALID_HANDLE_VALUE)
2936 return NULL;
2937 }
2938 else
2939 {
2940 if (!FindNextFile (dir_find_handle, &dir_find_data))
2941 return NULL;
2942 }
177c0ea7 2943
76b3903d
GV
2944 /* Emacs never uses this value, so don't bother making it match
2945 value returned by stat(). */
2946 dir_static.d_ino = 1;
177c0ea7 2947
b07103dc
EZ
2948 strcpy (dir_static.d_name, dir_find_data.cFileName);
2949
2950 /* If the file name in cFileName[] includes `?' characters, it means
2951 the original file name used characters that cannot be represented
2952 by the current ANSI codepage. To avoid total lossage, retrieve
2953 the short 8+3 alias of the long file name. */
2954 if (_mbspbrk (dir_static.d_name, "?"))
2955 {
2956 strcpy (dir_static.d_name, dir_find_data.cAlternateFileName);
2957 downcase = 1; /* 8+3 aliases are returned in all caps */
2958 }
2959 dir_static.d_namlen = strlen (dir_static.d_name);
95ef7787 2960 dir_static.d_reclen = sizeof (struct dirent) - MAXNAMLEN + 3 +
76b3903d 2961 dir_static.d_namlen - dir_static.d_namlen % 4;
177c0ea7 2962
192788d7
EZ
2963 /* If the file name in cFileName[] includes `?' characters, it means
2964 the original file name used characters that cannot be represented
2965 by the current ANSI codepage. To avoid total lossage, retrieve
2966 the short 8+3 alias of the long file name. */
2967 if (_mbspbrk (dir_find_data.cFileName, "?"))
2968 {
2969 strcpy (dir_static.d_name, dir_find_data.cAlternateFileName);
2970 /* 8+3 aliases are returned in all caps, which could break
2971 various alists that look at filenames' extensions. */
2972 downcase = 1;
2973 }
2974 else
2975 strcpy (dir_static.d_name, dir_find_data.cFileName);
2976 dir_static.d_namlen = strlen (dir_static.d_name);
76b3903d 2977 if (dir_is_fat)
6d2851de 2978 _mbslwr (dir_static.d_name);
b07103dc 2979 else if (downcase)
76b3903d
GV
2980 {
2981 register char *p;
6d2851de
EZ
2982 int dbcs_p = max_filename_mbslen () > 1;
2983 for (p = dir_static.d_name; *p; )
2984 {
2985 if (*p >= 'a' && *p <= 'z')
2986 break;
2987 if (dbcs_p)
2988 p = CharNextExA (file_name_codepage, p, 0);
2989 else
2990 p++;
2991 }
76b3903d 2992 if (!*p)
6d2851de 2993 _mbslwr (dir_static.d_name);
76b3903d 2994 }
177c0ea7 2995
76b3903d
GV
2996 return &dir_static;
2997}
2998
bedf4aab 2999static HANDLE
e0c181dd 3000open_unc_volume (const char *path)
9d3355d1 3001{
177c0ea7 3002 NETRESOURCE nr;
9d3355d1
GV
3003 HANDLE henum;
3004 int result;
3005
177c0ea7
JB
3006 nr.dwScope = RESOURCE_GLOBALNET;
3007 nr.dwType = RESOURCETYPE_DISK;
3008 nr.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER;
3009 nr.dwUsage = RESOURCEUSAGE_CONTAINER;
3010 nr.lpLocalName = NULL;
6e602566 3011 nr.lpRemoteName = (LPSTR)map_w32_filename (path, NULL);
177c0ea7
JB
3012 nr.lpComment = NULL;
3013 nr.lpProvider = NULL;
9d3355d1 3014
ed3751c8
JB
3015 result = WNetOpenEnum (RESOURCE_GLOBALNET, RESOURCETYPE_DISK,
3016 RESOURCEUSAGE_CONNECTABLE, &nr, &henum);
9d3355d1
GV
3017
3018 if (result == NO_ERROR)
3019 return henum;
3020 else
3021 return INVALID_HANDLE_VALUE;
3022}
3023
bedf4aab 3024static char *
9d3355d1
GV
3025read_unc_volume (HANDLE henum, char *readbuf, int size)
3026{
a302c7ae 3027 DWORD count;
9d3355d1 3028 int result;
a302c7ae 3029 DWORD bufsize = 512;
9d3355d1
GV
3030 char *buffer;
3031 char *ptr;
806fed21 3032 int dbcs_p = max_filename_mbslen () > 1;
9d3355d1
GV
3033
3034 count = 1;
3035 buffer = alloca (bufsize);
59eb0929 3036 result = WNetEnumResource (henum, &count, buffer, &bufsize);
9d3355d1
GV
3037 if (result != NO_ERROR)
3038 return NULL;
3039
3040 /* WNetEnumResource returns \\resource\share...skip forward to "share". */
3041 ptr = ((LPNETRESOURCE) buffer)->lpRemoteName;
3042 ptr += 2;
806fed21
EZ
3043 if (!dbcs_p)
3044 while (*ptr && !IS_DIRECTORY_SEP (*ptr)) ptr++;
3045 else
3046 {
3047 while (*ptr && !IS_DIRECTORY_SEP (*ptr))
3048 ptr = CharNextExA (file_name_codepage, ptr, 0);
3049 }
9d3355d1
GV
3050 ptr++;
3051
3052 strncpy (readbuf, ptr, size);
3053 return readbuf;
3054}
3055
bedf4aab 3056static void
9d3355d1
GV
3057close_unc_volume (HANDLE henum)
3058{
3059 if (henum != INVALID_HANDLE_VALUE)
3060 WNetCloseEnum (henum);
3061}
3062
bedf4aab 3063static DWORD
e0c181dd 3064unc_volume_file_attributes (const char *path)
9d3355d1
GV
3065{
3066 HANDLE henum;
3067 DWORD attrs;
3068
3069 henum = open_unc_volume (path);
3070 if (henum == INVALID_HANDLE_VALUE)
3071 return -1;
3072
3073 attrs = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_DIRECTORY;
3074
3075 close_unc_volume (henum);
3076
3077 return attrs;
3078}
3079
302d7d54
JR
3080/* Ensure a network connection is authenticated. */
3081static void
3082logon_network_drive (const char *path)
3083{
3084 NETRESOURCE resource;
3085 char share[MAX_PATH];
806fed21 3086 int n_slashes;
40a339c8 3087 char drive[4];
be4c6380 3088 UINT drvtype;
806fed21
EZ
3089 char *p;
3090 int dbcs_p;
40a339c8 3091
be4c6380
EZ
3092 if (IS_DIRECTORY_SEP (path[0]) && IS_DIRECTORY_SEP (path[1]))
3093 drvtype = DRIVE_REMOTE;
3094 else if (path[0] == '\0' || path[1] != ':')
3095 drvtype = GetDriveType (NULL);
3096 else
3097 {
3098 drive[0] = path[0];
3099 drive[1] = ':';
3100 drive[2] = '\\';
3101 drive[3] = '\0';
3102 drvtype = GetDriveType (drive);
3103 }
302d7d54
JR
3104
3105 /* Only logon to networked drives. */
be4c6380 3106 if (drvtype != DRIVE_REMOTE)
302d7d54 3107 return;
40a339c8 3108
302d7d54
JR
3109 n_slashes = 2;
3110 strncpy (share, path, MAX_PATH);
3111 /* Truncate to just server and share name. */
806fed21
EZ
3112 dbcs_p = max_filename_mbslen () > 1;
3113 for (p = share + 2; *p && p < share + MAX_PATH; )
302d7d54 3114 {
806fed21 3115 if (IS_DIRECTORY_SEP (*p) && ++n_slashes > 3)
302d7d54 3116 {
806fed21 3117 *p = '\0';
302d7d54
JR
3118 break;
3119 }
806fed21
EZ
3120 if (dbcs_p)
3121 p = CharNextExA (file_name_codepage, p, 0);
3122 else
3123 p++;
302d7d54
JR
3124 }
3125
3126 resource.dwType = RESOURCETYPE_DISK;
3127 resource.lpLocalName = NULL;
3128 resource.lpRemoteName = share;
3129 resource.lpProvider = NULL;
3130
3131 WNetAddConnection2 (&resource, NULL, NULL, CONNECT_INTERACTIVE);
3132}
480b0c5b 3133
14f20728 3134/* Emulate faccessat(2). */
480b0c5b 3135int
14f20728 3136faccessat (int dirfd, const char * path, int mode, int flags)
480b0c5b 3137{
b3308d2e
KH
3138 DWORD attributes;
3139
14f20728
EZ
3140 if (dirfd != AT_FDCWD
3141 && !(IS_DIRECTORY_SEP (path[0])
3142 || IS_DEVICE_SEP (path[1])))
73dcdb9f
PE
3143 {
3144 errno = EBADF;
3145 return -1;
3146 }
3147
8d38f461
EZ
3148 /* MSVCRT implementation of 'access' doesn't recognize D_OK, and its
3149 newer versions blow up when passed D_OK. */
b3308d2e 3150 path = map_w32_filename (path, NULL);
6dad7178
EZ
3151 /* If the last element of PATH is a symlink, we need to resolve it
3152 to get the attributes of its target file. Note: any symlinks in
3153 PATH elements other than the last one are transparently resolved
3154 by GetFileAttributes below. */
14f20728
EZ
3155 if ((volume_info.flags & FILE_SUPPORTS_REPARSE_POINTS) != 0
3156 && (flags & AT_SYMLINK_NOFOLLOW) == 0)
6dad7178
EZ
3157 path = chase_symlinks (path);
3158
3159 if ((attributes = GetFileAttributes (path)) == -1)
b3308d2e 3160 {
8d38f461
EZ
3161 DWORD w32err = GetLastError ();
3162
3163 switch (w32err)
3164 {
6dad7178
EZ
3165 case ERROR_INVALID_NAME:
3166 case ERROR_BAD_PATHNAME:
3167 if (is_unc_volume (path))
3168 {
3169 attributes = unc_volume_file_attributes (path);
3170 if (attributes == -1)
3171 {
3172 errno = EACCES;
3173 return -1;
3174 }
3175 break;
3176 }
3177 /* FALLTHROUGH */
8d38f461 3178 case ERROR_FILE_NOT_FOUND:
6dad7178 3179 case ERROR_BAD_NETPATH:
8d38f461
EZ
3180 errno = ENOENT;
3181 break;
3182 default:
3183 errno = EACCES;
3184 break;
3185 }
b3308d2e
KH
3186 return -1;
3187 }
6ad30855
EZ
3188 if ((mode & X_OK) != 0
3189 && !(is_exec (path) || (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0))
b3308d2e
KH
3190 {
3191 errno = EACCES;
3192 return -1;
3193 }
3194 if ((mode & W_OK) != 0 && (attributes & FILE_ATTRIBUTE_READONLY) != 0)
3195 {
3196 errno = EACCES;
3197 return -1;
3198 }
3199 if ((mode & D_OK) != 0 && (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
3200 {
3201 errno = EACCES;
3202 return -1;
3203 }
3204 return 0;
480b0c5b
GV
3205}
3206
14f20728
EZ
3207/* Shadow some MSVC runtime functions to map requests for long filenames
3208 to reasonable short names if necessary. This was originally added to
3209 permit running Emacs on NT 3.1 on a FAT partition, which doesn't support
3210 long file names. */
3211
480b0c5b
GV
3212int
3213sys_chdir (const char * path)
3214{
fbd6baed 3215 return _chdir (map_w32_filename (path, NULL));
480b0c5b
GV
3216}
3217
3218int
3219sys_chmod (const char * path, int mode)
3220{
6dad7178
EZ
3221 path = chase_symlinks (map_w32_filename (path, NULL));
3222 return _chmod (path, mode);
480b0c5b
GV
3223}
3224
3225int
3226sys_creat (const char * path, int mode)
3227{
fbd6baed 3228 return _creat (map_w32_filename (path, NULL), mode);
480b0c5b
GV
3229}
3230
3231FILE *
b56ceb92 3232sys_fopen (const char * path, const char * mode)
480b0c5b
GV
3233{
3234 int fd;
3235 int oflag;
3236 const char * mode_save = mode;
3237
3238 /* Force all file handles to be non-inheritable. This is necessary to
3239 ensure child processes don't unwittingly inherit handles that might
3240 prevent future file access. */
3241
3242 if (mode[0] == 'r')
3243 oflag = O_RDONLY;
3244 else if (mode[0] == 'w' || mode[0] == 'a')
3245 oflag = O_WRONLY | O_CREAT | O_TRUNC;
95ed0025 3246 else
480b0c5b
GV
3247 return NULL;
3248
3249 /* Only do simplistic option parsing. */
3250 while (*++mode)
3251 if (mode[0] == '+')
3252 {
3253 oflag &= ~(O_RDONLY | O_WRONLY);
3254 oflag |= O_RDWR;
3255 }
3256 else if (mode[0] == 'b')
3257 {
3258 oflag &= ~O_TEXT;
3259 oflag |= O_BINARY;
3260 }
3261 else if (mode[0] == 't')
3262 {
3263 oflag &= ~O_BINARY;
3264 oflag |= O_TEXT;
3265 }
3266 else break;
3267
fbd6baed 3268 fd = _open (map_w32_filename (path, NULL), oflag | _O_NOINHERIT, 0644);
480b0c5b
GV
3269 if (fd < 0)
3270 return NULL;
3271
76b3903d 3272 return _fdopen (fd, mode_save);
95ed0025 3273}
480b0c5b 3274
76b3903d 3275/* This only works on NTFS volumes, but is useful to have. */
480b0c5b 3276int
76b3903d 3277sys_link (const char * old, const char * new)
480b0c5b 3278{
76b3903d
GV
3279 HANDLE fileh;
3280 int result = -1;
3281 char oldname[MAX_PATH], newname[MAX_PATH];
3282
3283 if (old == NULL || new == NULL)
3284 {
3285 errno = ENOENT;
3286 return -1;
3287 }
3288
3289 strcpy (oldname, map_w32_filename (old, NULL));
3290 strcpy (newname, map_w32_filename (new, NULL));
3291
3292 fileh = CreateFile (oldname, 0, 0, NULL, OPEN_EXISTING,
3293 FILE_FLAG_BACKUP_SEMANTICS, NULL);
3294 if (fileh != INVALID_HANDLE_VALUE)
3295 {
3296 int wlen;
3297
3298 /* Confusingly, the "alternate" stream name field does not apply
3299 when restoring a hard link, and instead contains the actual
3300 stream data for the link (ie. the name of the link to create).
3301 The WIN32_STREAM_ID structure before the cStreamName field is
3302 the stream header, which is then immediately followed by the
3303 stream data. */
3304
3305 struct {
3306 WIN32_STREAM_ID wid;
3307 WCHAR wbuffer[MAX_PATH]; /* extra space for link name */
3308 } data;
3309
3310 wlen = MultiByteToWideChar (CP_ACP, MB_PRECOMPOSED, newname, -1,
3311 data.wid.cStreamName, MAX_PATH);
3312 if (wlen > 0)
3313 {
3314 LPVOID context = NULL;
3315 DWORD wbytes = 0;
3316
3317 data.wid.dwStreamId = BACKUP_LINK;
3318 data.wid.dwStreamAttributes = 0;
ed3751c8 3319 data.wid.Size.LowPart = wlen * sizeof (WCHAR);
76b3903d
GV
3320 data.wid.Size.HighPart = 0;
3321 data.wid.dwStreamNameSize = 0;
3322
3323 if (BackupWrite (fileh, (LPBYTE)&data,
3324 offsetof (WIN32_STREAM_ID, cStreamName)
3325 + data.wid.Size.LowPart,
3326 &wbytes, FALSE, FALSE, &context)
3327 && BackupWrite (fileh, NULL, 0, &wbytes, TRUE, FALSE, &context))
3328 {
3329 /* succeeded */
3330 result = 0;
3331 }
3332 else
3333 {
3334 /* Should try mapping GetLastError to errno; for now just
3335 indicate a general error (eg. links not supported). */
3336 errno = EINVAL; // perhaps EMLINK?
3337 }
3338 }
3339
3340 CloseHandle (fileh);
3341 }
3342 else
3343 errno = ENOENT;
3344
3345 return result;
480b0c5b
GV
3346}
3347
3348int
3349sys_mkdir (const char * path)
3350{
fbd6baed 3351 return _mkdir (map_w32_filename (path, NULL));
480b0c5b
GV
3352}
3353
9d1778b1
RS
3354/* Because of long name mapping issues, we need to implement this
3355 ourselves. Also, MSVC's _mktemp returns NULL when it can't generate
3356 a unique name, instead of setting the input template to an empty
3357 string.
3358
3359 Standard algorithm seems to be use pid or tid with a letter on the
3360 front (in place of the 6 X's) and cycle through the letters to find a
3361 unique name. We extend that to allow any reasonable character as the
3362 first of the 6 X's. */
480b0c5b
GV
3363char *
3364sys_mktemp (char * template)
3365{
9d1778b1
RS
3366 char * p;
3367 int i;
3368 unsigned uid = GetCurrentThreadId ();
3369 static char first_char[] = "abcdefghijklmnopqrstuvwyz0123456789!%-_@#";
3370
3371 if (template == NULL)
3372 return NULL;
3373 p = template + strlen (template);
3374 i = 5;
3375 /* replace up to the last 5 X's with uid in decimal */
3376 while (--p >= template && p[0] == 'X' && --i >= 0)
3377 {
3378 p[0] = '0' + uid % 10;
3379 uid /= 10;
3380 }
3381
3382 if (i < 0 && p[0] == 'X')
3383 {
3384 i = 0;
3385 do
3386 {
3387 int save_errno = errno;
3388 p[0] = first_char[i];
14f20728 3389 if (faccessat (AT_FDCWD, template, F_OK, AT_EACCESS) < 0)
9d1778b1
RS
3390 {
3391 errno = save_errno;
3392 return template;
3393 }
3394 }
3395 while (++i < sizeof (first_char));
3396 }
3397
3398 /* Template is badly formed or else we can't generate a unique name,
3399 so return empty string */
3400 template[0] = 0;
3401 return template;
480b0c5b
GV
3402}
3403
3404int
3405sys_open (const char * path, int oflag, int mode)
3406{
302f0b29 3407 const char* mpath = map_w32_filename (path, NULL);
343a2aef
EZ
3408 int res = -1;
3409
3410 /* If possible, try to open file without _O_CREAT, to be able to
3411 write to existing hidden and system files. Force all file
3412 handles to be non-inheritable. */
3413 if ((oflag & (_O_CREAT | _O_EXCL)) != (_O_CREAT | _O_EXCL))
3414 res = _open (mpath, (oflag & ~_O_CREAT) | _O_NOINHERIT, mode);
224f4ec1
EZ
3415 if (res < 0)
3416 res = _open (mpath, oflag | _O_NOINHERIT, mode);
224f4ec1
EZ
3417
3418 return res;
480b0c5b
GV
3419}
3420
3421int
70743157
PE
3422fchmod (int fd, mode_t mode)
3423{
3424 return 0;
3425}
3426
3427int
3428sys_rename_replace (const char *oldname, const char *newname, BOOL force)
480b0c5b 3429{
cfb5e855 3430 BOOL result;
b3308d2e 3431 char temp[MAX_PATH];
069d2b50
L
3432 int newname_dev;
3433 int oldname_dev;
480b0c5b 3434
e9e23e23 3435 /* MoveFile on Windows 95 doesn't correctly change the short file name
5162ffce
MB
3436 alias in a number of circumstances (it is not easy to predict when
3437 just by looking at oldname and newname, unfortunately). In these
3438 cases, renaming through a temporary name avoids the problem.
3439
e9e23e23 3440 A second problem on Windows 95 is that renaming through a temp name when
5162ffce
MB
3441 newname is uppercase fails (the final long name ends up in
3442 lowercase, although the short alias might be uppercase) UNLESS the
3443 long temp name is not 8.3.
3444
e9e23e23 3445 So, on Windows 95 we always rename through a temp name, and we make sure
5162ffce 3446 the temp name has a long extension to ensure correct renaming. */
480b0c5b 3447
fbd6baed 3448 strcpy (temp, map_w32_filename (oldname, NULL));
480b0c5b 3449
069d2b50
L
3450 /* volume_info is set indirectly by map_w32_filename. */
3451 oldname_dev = volume_info.serialnum;
3452
417a7a0e 3453 if (os_subtype == OS_9X)
480b0c5b 3454 {
b3308d2e 3455 char * o;
480b0c5b 3456 char * p;
b3308d2e
KH
3457 int i = 0;
3458
3459 oldname = map_w32_filename (oldname, NULL);
657d08d3 3460 if ((o = strrchr (oldname, '\\')))
b3308d2e
KH
3461 o++;
3462 else
3463 o = (char *) oldname;
480b0c5b 3464
657d08d3 3465 if ((p = strrchr (temp, '\\')))
480b0c5b
GV
3466 p++;
3467 else
3468 p = temp;
b3308d2e
KH
3469
3470 do
3471 {
3472 /* Force temp name to require a manufactured 8.3 alias - this
3473 seems to make the second rename work properly. */
f313ee82 3474 sprintf (p, "_.%s.%u", o, i);
b3308d2e 3475 i++;
58f0cb7e 3476 result = rename (oldname, temp);
b3308d2e
KH
3477 }
3478 /* This loop must surely terminate! */
cfb5e855 3479 while (result < 0 && errno == EEXIST);
58f0cb7e 3480 if (result < 0)
480b0c5b
GV
3481 return -1;
3482 }
3483
70743157 3484 /* If FORCE, emulate Unix behavior - newname is deleted if it already exists
5162ffce 3485 (at least if it is a file; don't do this for directories).
76b3903d 3486
b3308d2e
KH
3487 Since we mustn't do this if we are just changing the case of the
3488 file name (we would end up deleting the file we are trying to
3489 rename!), we let rename detect if the destination file already
3490 exists - that way we avoid the possible pitfalls of trying to
3491 determine ourselves whether two names really refer to the same
3492 file, which is not always possible in the general case. (Consider
3493 all the permutations of shared or subst'd drives, etc.) */
3494
3495 newname = map_w32_filename (newname, NULL);
069d2b50
L
3496
3497 /* volume_info is set indirectly by map_w32_filename. */
3498 newname_dev = volume_info.serialnum;
3499
eb9ea53f 3500 result = rename (temp, newname);
b3308d2e 3501
70743157 3502 if (result < 0 && force)
069d2b50 3503 {
6dad7178 3504 DWORD w32err = GetLastError ();
069d2b50
L
3505
3506 if (errno == EACCES
3507 && newname_dev != oldname_dev)
3508 {
3509 /* The implementation of `rename' on Windows does not return
3510 errno = EXDEV when you are moving a directory to a
3511 different storage device (ex. logical disk). It returns
3512 EACCES instead. So here we handle such situations and
3513 return EXDEV. */
3514 DWORD attributes;
3515
3516 if ((attributes = GetFileAttributes (temp)) != -1
6dad7178 3517 && (attributes & FILE_ATTRIBUTE_DIRECTORY))
069d2b50
L
3518 errno = EXDEV;
3519 }
3520 else if (errno == EEXIST)
3521 {
3522 if (_chmod (newname, 0666) != 0)
3523 return result;
3524 if (_unlink (newname) != 0)
3525 return result;
3526 result = rename (temp, newname);
3527 }
6dad7178
EZ
3528 else if (w32err == ERROR_PRIVILEGE_NOT_HELD
3529 && is_symlink (temp))
3530 {
3531 /* This is Windows prohibiting the user from creating a
3532 symlink in another place, since that requires
3533 privileges. */
3534 errno = EPERM;
3535 }
069d2b50 3536 }
480b0c5b 3537
eb9ea53f 3538 return result;
480b0c5b
GV
3539}
3540
70743157
PE
3541int
3542sys_rename (char const *old, char const *new)
3543{
3544 return sys_rename_replace (old, new, TRUE);
3545}
3546
480b0c5b
GV
3547int
3548sys_rmdir (const char * path)
3549{
fbd6baed 3550 return _rmdir (map_w32_filename (path, NULL));
480b0c5b
GV
3551}
3552
3553int
3554sys_unlink (const char * path)
3555{
16bb7578
GV
3556 path = map_w32_filename (path, NULL);
3557
3558 /* On Unix, unlink works without write permission. */
3559 _chmod (path, 0666);
3560 return _unlink (path);
480b0c5b
GV
3561}
3562
3563static FILETIME utc_base_ft;
5da9424d 3564static ULONGLONG utc_base; /* In 100ns units */
480b0c5b
GV
3565static int init = 0;
3566
5da9424d
JB
3567#define FILETIME_TO_U64(result, ft) \
3568 do { \
3569 ULARGE_INTEGER uiTemp; \
3570 uiTemp.LowPart = (ft).dwLowDateTime; \
3571 uiTemp.HighPart = (ft).dwHighDateTime; \
3572 result = uiTemp.QuadPart; \
3573 } while (0)
3574
3575static void
b56ceb92 3576initialize_utc_base (void)
7c80d5ec 3577{
5da9424d
JB
3578 /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */
3579 SYSTEMTIME st;
3580
3581 st.wYear = 1970;
3582 st.wMonth = 1;
3583 st.wDay = 1;
3584 st.wHour = 0;
3585 st.wMinute = 0;
3586 st.wSecond = 0;
3587 st.wMilliseconds = 0;
3588
3589 SystemTimeToFileTime (&st, &utc_base_ft);
3590 FILETIME_TO_U64 (utc_base, utc_base_ft);
7c80d5ec
EZ
3591}
3592
480b0c5b
GV
3593static time_t
3594convert_time (FILETIME ft)
3595{
5da9424d 3596 ULONGLONG tmp;
480b0c5b
GV
3597
3598 if (!init)
3599 {
9d4f32e8 3600 initialize_utc_base ();
480b0c5b
GV
3601 init = 1;
3602 }
3603
3604 if (CompareFileTime (&ft, &utc_base_ft) < 0)
3605 return 0;
3606
5da9424d
JB
3607 FILETIME_TO_U64 (tmp, ft);
3608 return (time_t) ((tmp - utc_base) / 10000000L);
480b0c5b
GV
3609}
3610
bedf4aab 3611static void
480b0c5b
GV
3612convert_from_time_t (time_t time, FILETIME * pft)
3613{
5da9424d 3614 ULARGE_INTEGER tmp;
480b0c5b
GV
3615
3616 if (!init)
3617 {
5da9424d 3618 initialize_utc_base ();
480b0c5b
GV
3619 init = 1;
3620 }
3621
3622 /* time in 100ns units since 1-Jan-1601 */
5da9424d
JB
3623 tmp.QuadPart = (ULONGLONG) time * 10000000L + utc_base;
3624 pft->dwHighDateTime = tmp.HighPart;
3625 pft->dwLowDateTime = tmp.LowPart;
480b0c5b 3626}
480b0c5b 3627
76b3903d
GV
3628#if 0
3629/* No reason to keep this; faking inode values either by hashing or even
3630 using the file index from GetInformationByHandle, is not perfect and
3631 so by default Emacs doesn't use the inode values on Windows.
3632 Instead, we now determine file-truename correctly (except for
3633 possible drive aliasing etc). */
3634
3635/* Modified version of "PJW" algorithm (see the "Dragon" compiler book). */
480b0c5b 3636static unsigned
76b3903d 3637hashval (const unsigned char * str)
480b0c5b
GV
3638{
3639 unsigned h = 0;
480b0c5b
GV
3640 while (*str)
3641 {
3642 h = (h << 4) + *str++;
76b3903d 3643 h ^= (h >> 28);
480b0c5b
GV
3644 }
3645 return h;
3646}
3647
3648/* Return the hash value of the canonical pathname, excluding the
3649 drive/UNC header, to get a hopefully unique inode number. */
76b3903d 3650static DWORD
480b0c5b
GV
3651generate_inode_val (const char * name)
3652{
3653 char fullname[ MAX_PATH ];
3654 char * p;
3655 unsigned hash;
3656
76b3903d 3657 /* Get the truly canonical filename, if it exists. (Note: this
e1dbe924 3658 doesn't resolve aliasing due to subst commands, or recognize hard
76b3903d
GV
3659 links. */
3660 if (!w32_get_long_filename ((char *)name, fullname, MAX_PATH))
1088b922 3661 emacs_abort ();
76b3903d
GV
3662
3663 parse_root (fullname, &p);
fbd6baed 3664 /* Normal W32 filesystems are still case insensitive. */
480b0c5b 3665 _strlwr (p);
76b3903d 3666 return hashval (p);
480b0c5b
GV
3667}
3668
76b3903d
GV
3669#endif
3670
8aaaec6b 3671static PSECURITY_DESCRIPTOR
6dad7178
EZ
3672get_file_security_desc_by_handle (HANDLE h)
3673{
3674 PSECURITY_DESCRIPTOR psd = NULL;
3675 DWORD err;
3676 SECURITY_INFORMATION si = OWNER_SECURITY_INFORMATION
3677 | GROUP_SECURITY_INFORMATION /* | DACL_SECURITY_INFORMATION */ ;
3678
3679 err = get_security_info (h, SE_FILE_OBJECT, si,
3680 NULL, NULL, NULL, NULL, &psd);
3681 if (err != ERROR_SUCCESS)
3682 return NULL;
3683
3684 return psd;
3685}
3686
3687static PSECURITY_DESCRIPTOR
3688get_file_security_desc_by_name (const char *fname)
8aaaec6b
EZ
3689{
3690 PSECURITY_DESCRIPTOR psd = NULL;
3691 DWORD sd_len, err;
3692 SECURITY_INFORMATION si = OWNER_SECURITY_INFORMATION
3693 | GROUP_SECURITY_INFORMATION /* | DACL_SECURITY_INFORMATION */ ;
3694
3695 if (!get_file_security (fname, si, psd, 0, &sd_len))
3696 {
3697 err = GetLastError ();
3698 if (err != ERROR_INSUFFICIENT_BUFFER)
3699 return NULL;
3700 }
3701
3702 psd = xmalloc (sd_len);
3703 if (!get_file_security (fname, si, psd, sd_len, &sd_len))
3704 {
3705 xfree (psd);
3706 return NULL;
3707 }
3708
3709 return psd;
3710}
3711
3712static DWORD
3713get_rid (PSID sid)
3714{
3715 unsigned n_subauthorities;
3716
3717 /* Use the last sub-authority value of the RID, the relative
3718 portion of the SID, as user/group ID. */
3719 n_subauthorities = *get_sid_sub_authority_count (sid);
3720 if (n_subauthorities < 1)
3721 return 0; /* the "World" RID */
3722 return *get_sid_sub_authority (sid, n_subauthorities - 1);
3723}
3724
f8b35b24
EZ
3725/* Caching SID and account values for faster lokup. */
3726
3727#ifdef __GNUC__
3728# define FLEXIBLE_ARRAY_MEMBER
3729#else
3730# define FLEXIBLE_ARRAY_MEMBER 1
3731#endif
3732
3733struct w32_id {
22749e9a 3734 unsigned rid;
f8b35b24
EZ
3735 struct w32_id *next;
3736 char name[GNLEN+1];
3737 unsigned char sid[FLEXIBLE_ARRAY_MEMBER];
3738};
3739
3740static struct w32_id *w32_idlist;
3741
3742static int
22749e9a 3743w32_cached_id (PSID sid, unsigned *id, char *name)
f8b35b24
EZ
3744{
3745 struct w32_id *tail, *found;
3746
3747 for (found = NULL, tail = w32_idlist; tail; tail = tail->next)
3748 {
3749 if (equal_sid ((PSID)tail->sid, sid))
3750 {
3751 found = tail;
3752 break;
3753 }
3754 }
3755 if (found)
3756 {
3757 *id = found->rid;
3758 strcpy (name, found->name);
3759 return 1;
3760 }
3761 else
3762 return 0;
3763}
3764
3765static void
22749e9a 3766w32_add_to_cache (PSID sid, unsigned id, char *name)
f8b35b24
EZ
3767{
3768 DWORD sid_len;
3769 struct w32_id *new_entry;
3770
3771 /* We don't want to leave behind stale cache from when Emacs was
3772 dumped. */
3773 if (initialized)
3774 {
3775 sid_len = get_length_sid (sid);
3776 new_entry = xmalloc (offsetof (struct w32_id, sid) + sid_len);
3777 if (new_entry)
3778 {
3779 new_entry->rid = id;
3780 strcpy (new_entry->name, name);
3781 copy_sid (sid_len, (PSID)new_entry->sid, sid);
3782 new_entry->next = w32_idlist;
3783 w32_idlist = new_entry;
3784 }
3785 }
3786}
3787
8aaaec6b
EZ
3788#define UID 1
3789#define GID 2
3790
3791static int
92340ec7 3792get_name_and_id (PSECURITY_DESCRIPTOR psd, unsigned *id, char *nm, int what)
8aaaec6b
EZ
3793{
3794 PSID sid = NULL;
8aaaec6b
EZ
3795 BOOL dflt;
3796 SID_NAME_USE ignore;
3797 char name[UNLEN+1];
3798 DWORD name_len = sizeof (name);
3799 char domain[1024];
ed3751c8 3800 DWORD domain_len = sizeof (domain);
8aaaec6b
EZ
3801 int use_dflt = 0;
3802 int result;
3803
3804 if (what == UID)
3805 result = get_security_descriptor_owner (psd, &sid, &dflt);
3806 else if (what == GID)
3807 result = get_security_descriptor_group (psd, &sid, &dflt);
3808 else
3809 result = 0;
3810
3811 if (!result || !is_valid_sid (sid))
3812 use_dflt = 1;
f8b35b24 3813 else if (!w32_cached_id (sid, id, nm))
8aaaec6b 3814 {
92340ec7 3815 if (!lookup_account_sid (NULL, sid, name, &name_len,
8aaaec6b
EZ
3816 domain, &domain_len, &ignore)
3817 || name_len > UNLEN+1)
3818 use_dflt = 1;
3819 else
3820 {
3821 *id = get_rid (sid);
3822 strcpy (nm, name);
f8b35b24 3823 w32_add_to_cache (sid, *id, name);
8aaaec6b
EZ
3824 }
3825 }
3826 return use_dflt;
3827}
3828
3829static void
92340ec7 3830get_file_owner_and_group (PSECURITY_DESCRIPTOR psd, struct stat *st)
8aaaec6b
EZ
3831{
3832 int dflt_usr = 0, dflt_grp = 0;
3833
3834 if (!psd)
3835 {
3836 dflt_usr = 1;
3837 dflt_grp = 1;
3838 }
3839 else
3840 {
92340ec7 3841 if (get_name_and_id (psd, &st->st_uid, st->st_uname, UID))
8aaaec6b 3842 dflt_usr = 1;
92340ec7 3843 if (get_name_and_id (psd, &st->st_gid, st->st_gname, GID))
8aaaec6b
EZ
3844 dflt_grp = 1;
3845 }
3846 /* Consider files to belong to current user/group, if we cannot get
3847 more accurate information. */
3848 if (dflt_usr)
3849 {
3850 st->st_uid = dflt_passwd.pw_uid;
3851 strcpy (st->st_uname, dflt_passwd.pw_name);
3852 }
3853 if (dflt_grp)
3854 {
3855 st->st_gid = dflt_passwd.pw_gid;
3856 strcpy (st->st_gname, dflt_group.gr_name);
3857 }
3858}
3859
be4c6380
EZ
3860/* Return non-zero if NAME is a potentially slow filesystem. */
3861int
3862is_slow_fs (const char *name)
3863{
3864 char drive_root[4];
3865 UINT devtype;
3866
3867 if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1]))
3868 devtype = DRIVE_REMOTE; /* assume UNC name is remote */
3869 else if (!(strlen (name) >= 2 && IS_DEVICE_SEP (name[1])))
3870 devtype = GetDriveType (NULL); /* use root of current drive */
3871 else
3872 {
3873 /* GetDriveType needs the root directory of the drive. */
3874 strncpy (drive_root, name, 2);
3875 drive_root[2] = '\\';
3876 drive_root[3] = '\0';
3877 devtype = GetDriveType (drive_root);
3878 }
3879 return !(devtype == DRIVE_FIXED || devtype == DRIVE_RAMDISK);
3880}
3881
5c207910
EZ
3882/* If this is non-zero, the caller wants accurate information about
3883 file's owner and group, which could be expensive to get. */
3884int w32_stat_get_owner_group;
3885
480b0c5b
GV
3886/* MSVC stat function can't cope with UNC names and has other bugs, so
3887 replace it with our own. This also allows us to calculate consistent
6dad7178
EZ
3888 inode values and owner/group without hacks in the main Emacs code. */
3889
3890static int
3891stat_worker (const char * path, struct stat * buf, int follow_symlinks)
480b0c5b 3892{
6dad7178 3893 char *name, *save_name, *r;
480b0c5b
GV
3894 WIN32_FIND_DATA wfd;
3895 HANDLE fh;
6dad7178 3896 unsigned __int64 fake_inode = 0;
480b0c5b
GV
3897 int permission;
3898 int len;
3899 int rootdir = FALSE;
8aaaec6b 3900 PSECURITY_DESCRIPTOR psd = NULL;
6dad7178
EZ
3901 int is_a_symlink = 0;
3902 DWORD file_flags = FILE_FLAG_BACKUP_SEMANTICS;
3903 DWORD access_rights = 0;
3904 DWORD fattrs = 0, serialnum = 0, fs_high = 0, fs_low = 0, nlinks = 1;
3905 FILETIME ctime, atime, wtime;
806fed21 3906 int dbcs_p;
480b0c5b
GV
3907
3908 if (path == NULL || buf == NULL)
3909 {
3910 errno = EFAULT;
3911 return -1;
3912 }
3913
6dad7178 3914 save_name = name = (char *) map_w32_filename (path, &path);
22189f79
EZ
3915 /* Must be valid filename, no wild cards or other invalid
3916 characters. We use _mbspbrk to support multibyte strings that
3917 might look to strpbrk as if they included literal *, ?, and other
3918 characters mentioned below that are disallowed by Windows
3919 filesystems. */
3920 if (_mbspbrk (name, "*?|<>\""))
480b0c5b
GV
3921 {
3922 errno = ENOENT;
3923 return -1;
3924 }
3925
3926 /* Remove trailing directory separator, unless name is the root
3927 directory of a drive or UNC volume in which case ensure there
3928 is a trailing separator. */
3929 len = strlen (name);
480b0c5b
GV
3930 name = strcpy (alloca (len + 2), name);
3931
6dad7178
EZ
3932 /* Avoid a somewhat costly call to is_symlink if the filesystem
3933 doesn't support symlinks. */
3934 if ((volume_info.flags & FILE_SUPPORTS_REPARSE_POINTS) != 0)
3935 is_a_symlink = is_symlink (name);
3936
3937 /* Plan A: Open the file and get all the necessary information via
3938 the resulting handle. This solves several issues in one blow:
3939
3940 . retrieves attributes for the target of a symlink, if needed
3941 . gets attributes of root directories and symlinks pointing to
3942 root directories, thus avoiding the need for special-casing
3943 these and detecting them by examining the file-name format
3944 . retrieves more accurate attributes (e.g., non-zero size for
3945 some directories, esp. directories that are junction points)
3946 . correctly resolves "c:/..", "/.." and similar file names
3947 . avoids run-time penalties for 99% of use cases
3948
3949 Plan A is always tried first, unless the user asked not to (but
3950 if the file is a symlink and we need to follow links, we try Plan
3951 A even if the user asked not to).
3952
3953 If Plan A fails, we go to Plan B (below), where various
3954 potentially expensive techniques must be used to handle "special"
3955 files such as UNC volumes etc. */
8aaaec6b 3956 if (!(NILP (Vw32_get_true_file_attributes)
19ced600 3957 || (EQ (Vw32_get_true_file_attributes, Qlocal) && is_slow_fs (name)))
6dad7178
EZ
3958 /* Following symlinks requires getting the info by handle. */
3959 || (is_a_symlink && follow_symlinks))
480b0c5b 3960 {
6dad7178
EZ
3961 BY_HANDLE_FILE_INFORMATION info;
3962
3963 if (is_a_symlink && !follow_symlinks)
3964 file_flags |= FILE_FLAG_OPEN_REPARSE_POINT;
3965 /* READ_CONTROL access rights are required to get security info
3966 by handle. But if the OS doesn't support security in the
3967 first place, we don't need to try. */
3968 if (is_windows_9x () != TRUE)
3969 access_rights |= READ_CONTROL;
3970
3971 fh = CreateFile (name, access_rights, 0, NULL, OPEN_EXISTING,
3972 file_flags, NULL);
3973 /* If CreateFile fails with READ_CONTROL, try again with zero as
3974 access rights. */
3975 if (fh == INVALID_HANDLE_VALUE && access_rights)
3976 fh = CreateFile (name, 0, 0, NULL, OPEN_EXISTING,
3977 file_flags, NULL);
3978 if (fh == INVALID_HANDLE_VALUE)
3979 goto no_true_file_attributes;
3980
3ed8598c 3981 /* This is more accurate in terms of getting the correct number
aa5ee2a3 3982 of links, but is quite slow (it is noticeable when Emacs is
480b0c5b 3983 making a list of file name completions). */
480b0c5b
GV
3984 if (GetFileInformationByHandle (fh, &info))
3985 {
6dad7178 3986 nlinks = info.nNumberOfLinks;
76b3903d
GV
3987 /* Might as well use file index to fake inode values, but this
3988 is not guaranteed to be unique unless we keep a handle open
3989 all the time (even then there are situations where it is
3990 not unique). Reputedly, there are at most 48 bits of info
3991 (on NTFS, presumably less on FAT). */
e3b88685
EZ
3992 fake_inode = info.nFileIndexHigh;
3993 fake_inode <<= 32;
3994 fake_inode += info.nFileIndexLow;
6dad7178
EZ
3995 serialnum = info.dwVolumeSerialNumber;
3996 fs_high = info.nFileSizeHigh;
3997 fs_low = info.nFileSizeLow;
3998 ctime = info.ftCreationTime;
3999 atime = info.ftLastAccessTime;
4000 wtime = info.ftLastWriteTime;
4001 fattrs = info.dwFileAttributes;
480b0c5b
GV
4002 }
4003 else
4004 {
6dad7178
EZ
4005 /* We don't go to Plan B here, because it's not clear that
4006 it's a good idea. The only known use case where
4007 CreateFile succeeds, but GetFileInformationByHandle fails
4008 (with ERROR_INVALID_FUNCTION) is for character devices
4009 such as NUL, PRN, etc. For these, switching to Plan B is
4010 a net loss, because we lose the character device
4011 attribute returned by GetFileType below (FindFirstFile
4012 doesn't set that bit in the attributes), and the other
4013 fields don't make sense for character devices anyway.
4014 Emacs doesn't really care for non-file entities in the
4015 context of l?stat, so neither do we. */
4016
4017 /* w32err is assigned so one could put a breakpoint here and
4018 examine its value, when GetFileInformationByHandle
4019 fails. */
4020 DWORD w32err = GetLastError ();
4021
4022 switch (w32err)
4023 {
4024 case ERROR_FILE_NOT_FOUND: /* can this ever happen? */
4025 errno = ENOENT;
4026 return -1;
4027 }
01f31dfb
AI
4028 }
4029
6dad7178
EZ
4030 /* Test for a symlink before testing for a directory, since
4031 symlinks to directories have the directory bit set, but we
4032 don't want them to appear as directories. */
4033 if (is_a_symlink && !follow_symlinks)
4034 buf->st_mode = S_IFLNK;
4035 else if (fattrs & FILE_ATTRIBUTE_DIRECTORY)
4036 buf->st_mode = S_IFDIR;
93e0f0da
JR
4037 else
4038 {
6dad7178
EZ
4039 DWORD ftype = GetFileType (fh);
4040
4041 switch (ftype)
93e0f0da
JR
4042 {
4043 case FILE_TYPE_DISK:
e3b88685 4044 buf->st_mode = S_IFREG;
93e0f0da
JR
4045 break;
4046 case FILE_TYPE_PIPE:
e3b88685 4047 buf->st_mode = S_IFIFO;
93e0f0da
JR
4048 break;
4049 case FILE_TYPE_CHAR:
4050 case FILE_TYPE_UNKNOWN:
4051 default:
e3b88685 4052 buf->st_mode = S_IFCHR;
93e0f0da 4053 }
480b0c5b 4054 }
6dad7178
EZ
4055 /* We produce the fallback owner and group data, based on the
4056 current user that runs Emacs, in the following cases:
4057
5c207910 4058 . caller didn't request owner and group info
6dad7178
EZ
4059 . this is Windows 9X
4060 . getting security by handle failed, and we need to produce
4061 information for the target of a symlink (this is better
4062 than producing a potentially misleading info about the
4063 symlink itself)
4064
4065 If getting security by handle fails, and we don't need to
4066 resolve symlinks, we try getting security by name. */
5c207910 4067 if (!w32_stat_get_owner_group || is_windows_9x () == TRUE)
92340ec7 4068 get_file_owner_and_group (NULL, buf);
5c207910 4069 else
6dad7178 4070 {
5c207910
EZ
4071 psd = get_file_security_desc_by_handle (fh);
4072 if (psd)
4073 {
a4b0cca1 4074 get_file_owner_and_group (psd, buf);
5c207910
EZ
4075 LocalFree (psd);
4076 }
4077 else if (!(is_a_symlink && follow_symlinks))
4078 {
4079 psd = get_file_security_desc_by_name (name);
a4b0cca1 4080 get_file_owner_and_group (psd, buf);
5c207910
EZ
4081 xfree (psd);
4082 }
4083 else
a4b0cca1 4084 get_file_owner_and_group (NULL, buf);
6dad7178 4085 }
01f31dfb 4086 CloseHandle (fh);
76b3903d
GV
4087 }
4088 else
4089 {
6dad7178
EZ
4090 no_true_file_attributes:
4091 /* Plan B: Either getting a handle on the file failed, or the
4092 caller explicitly asked us to not bother making this
4093 information more accurate.
4094
4095 Implementation note: In Plan B, we never bother to resolve
4096 symlinks, even if we got here because we tried Plan A and
4097 failed. That's because, even if the caller asked for extra
4098 precision by setting Vw32_get_true_file_attributes to t,
4099 resolving symlinks requires acquiring a file handle to the
4100 symlink, which we already know will fail. And if the user
4101 did not ask for extra precision, resolving symlinks will fly
4102 in the face of that request, since the user then wants the
4103 lightweight version of the code. */
806fed21 4104 dbcs_p = max_filename_mbslen () > 1;
6dad7178
EZ
4105 rootdir = (path >= save_name + len - 1
4106 && (IS_DIRECTORY_SEP (*path) || *path == 0));
4107
4108 /* If name is "c:/.." or "/.." then stat "c:/" or "/". */
4109 r = IS_DEVICE_SEP (name[1]) ? &name[2] : name;
4110 if (IS_DIRECTORY_SEP (r[0])
4111 && r[1] == '.' && r[2] == '.' && r[3] == '\0')
4112 r[1] = r[2] = '\0';
4113
4114 /* Note: If NAME is a symlink to the root of a UNC volume
4115 (i.e. "\\SERVER"), we will not detect that here, and we will
4116 return data about the symlink as result of FindFirst below.
4117 This is unfortunate, but that marginal use case does not
4118 justify a call to chase_symlinks which would impose a penalty
4119 on all the other use cases. (We get here for symlinks to
4120 roots of UNC volumes because CreateFile above fails for them,
4121 unlike with symlinks to root directories X:\ of drives.) */
4122 if (is_unc_volume (name))
4123 {
4124 fattrs = unc_volume_file_attributes (name);
4125 if (fattrs == -1)
4126 return -1;
4127
4128 ctime = atime = wtime = utc_base_ft;
4129 }
4130 else if (rootdir)
4131 {
806fed21
EZ
4132 if (!dbcs_p)
4133 {
4134 if (!IS_DIRECTORY_SEP (name[len-1]))
4135 strcat (name, "\\");
4136 }
4137 else
4138 {
4139 char *end = name + len;
4140 char *n = CharPrevExA (file_name_codepage, name, end, 0);
4141
4142 if (!IS_DIRECTORY_SEP (*n))
4143 strcat (name, "\\");
4144 }
6dad7178
EZ
4145 if (GetDriveType (name) < 2)
4146 {
4147 errno = ENOENT;
4148 return -1;
4149 }
4150
4151 fattrs = FILE_ATTRIBUTE_DIRECTORY;
4152 ctime = atime = wtime = utc_base_ft;
4153 }
4154 else
4155 {
806fed21
EZ
4156 if (!dbcs_p)
4157 {
4158 if (IS_DIRECTORY_SEP (name[len-1]))
4159 name[len - 1] = 0;
4160 }
4161 else
4162 {
4163 char *end = name + len;
4164 char *n = CharPrevExA (file_name_codepage, name, end, 0);
4165
4166 if (IS_DIRECTORY_SEP (*n))
4167 *n = 0;
4168 }
6dad7178
EZ
4169
4170 /* (This is hacky, but helps when doing file completions on
4171 network drives.) Optimize by using information available from
4172 active readdir if possible. */
4173 len = strlen (dir_pathname);
806fed21
EZ
4174 if (!dbcs_p)
4175 {
4176 if (IS_DIRECTORY_SEP (dir_pathname[len-1]))
4177 len--;
4178 }
4179 else
4180 {
4181 char *end = dir_pathname + len;
4182 char *n = CharPrevExA (file_name_codepage, dir_pathname, end, 0);
4183
4184 if (IS_DIRECTORY_SEP (*n))
4185 len--;
4186 }
6dad7178
EZ
4187 if (dir_find_handle != INVALID_HANDLE_VALUE
4188 && !(is_a_symlink && follow_symlinks)
4189 && strnicmp (save_name, dir_pathname, len) == 0
4190 && IS_DIRECTORY_SEP (name[len])
4191 && xstrcasecmp (name + len + 1, dir_static.d_name) == 0)
4192 {
4193 /* This was the last entry returned by readdir. */
4194 wfd = dir_find_data;
4195 }
4196 else
4197 {
4198 logon_network_drive (name);
4199
4200 fh = FindFirstFile (name, &wfd);
4201 if (fh == INVALID_HANDLE_VALUE)
4202 {
4203 errno = ENOENT;
4204 return -1;
4205 }
4206 FindClose (fh);
4207 }
4208 /* Note: if NAME is a symlink, the information we get from
4209 FindFirstFile is for the symlink, not its target. */
4210 fattrs = wfd.dwFileAttributes;
4211 ctime = wfd.ftCreationTime;
4212 atime = wfd.ftLastAccessTime;
4213 wtime = wfd.ftLastWriteTime;
4214 fs_high = wfd.nFileSizeHigh;
4215 fs_low = wfd.nFileSizeLow;
4216 fake_inode = 0;
4217 nlinks = 1;
4218 serialnum = volume_info.serialnum;
4219 }
4220 if (is_a_symlink && !follow_symlinks)
4221 buf->st_mode = S_IFLNK;
4222 else if (fattrs & FILE_ATTRIBUTE_DIRECTORY)
4223 buf->st_mode = S_IFDIR;
4224 else
4225 buf->st_mode = S_IFREG;
8aaaec6b 4226
92340ec7 4227 get_file_owner_and_group (NULL, buf);
76b3903d
GV
4228 }
4229
4230#if 0
4231 /* Not sure if there is any point in this. */
4232 if (!NILP (Vw32_generate_fake_inodes))
4233 fake_inode = generate_inode_val (name);
4234 else if (fake_inode == 0)
4235 {
4236 /* For want of something better, try to make everything unique. */
4237 static DWORD gen_num = 0;
4238 fake_inode = ++gen_num;
480b0c5b 4239 }
76b3903d
GV
4240#endif
4241
6dad7178 4242 buf->st_ino = fake_inode;
480b0c5b 4243
6dad7178
EZ
4244 buf->st_dev = serialnum;
4245 buf->st_rdev = serialnum;
480b0c5b 4246
6dad7178 4247 buf->st_size = fs_high;
8aaaec6b 4248 buf->st_size <<= 32;
6dad7178
EZ
4249 buf->st_size += fs_low;
4250 buf->st_nlink = nlinks;
480b0c5b
GV
4251
4252 /* Convert timestamps to Unix format. */
6dad7178
EZ
4253 buf->st_mtime = convert_time (wtime);
4254 buf->st_atime = convert_time (atime);
480b0c5b 4255 if (buf->st_atime == 0) buf->st_atime = buf->st_mtime;
6dad7178 4256 buf->st_ctime = convert_time (ctime);
480b0c5b
GV
4257 if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime;
4258
4259 /* determine rwx permissions */
6dad7178
EZ
4260 if (is_a_symlink && !follow_symlinks)
4261 permission = S_IREAD | S_IWRITE | S_IEXEC; /* Posix expectations */
480b0c5b 4262 else
6dad7178
EZ
4263 {
4264 if (fattrs & FILE_ATTRIBUTE_READONLY)
4265 permission = S_IREAD;
4266 else
4267 permission = S_IREAD | S_IWRITE;
177c0ea7 4268
6dad7178
EZ
4269 if (fattrs & FILE_ATTRIBUTE_DIRECTORY)
4270 permission |= S_IEXEC;
4271 else if (is_exec (name))
4272 permission |= S_IEXEC;
4273 }
480b0c5b
GV
4274
4275 buf->st_mode |= permission | (permission >> 3) | (permission >> 6);
4276
4277 return 0;
4278}
4279
6dad7178
EZ
4280int
4281stat (const char * path, struct stat * buf)
4282{
4283 return stat_worker (path, buf, 1);
4284}
4285
4286int
4287lstat (const char * path, struct stat * buf)
4288{
4289 return stat_worker (path, buf, 0);
4290}
4291
8654f9d7
PE
4292int
4293fstatat (int fd, char const *name, struct stat *st, int flags)
4294{
4295 /* Rely on a hack: an open directory is modeled as file descriptor 0.
4296 This is good enough for the current usage in Emacs, but is fragile.
4297
d9c287e5 4298 FIXME: Add proper support for fdopendir, fstatat, readlinkat.
8654f9d7
PE
4299 Gnulib does this and can serve as a model. */
4300 char fullname[MAX_PATH];
4301
4302 if (fd != AT_FDCWD)
4303 {
4304 if (_snprintf (fullname, sizeof fullname, "%s/%s", dir_pathname, name)
4305 < 0)
4306 {
4307 errno = ENAMETOOLONG;
4308 return -1;
4309 }
4310 name = fullname;
4311 }
4312
4313 return stat_worker (name, st, ! (flags & AT_SYMLINK_NOFOLLOW));
4314}
4315
16bb7578
GV
4316/* Provide fstat and utime as well as stat for consistent handling of
4317 file timestamps. */
4318int
4319fstat (int desc, struct stat * buf)
4320{
4321 HANDLE fh = (HANDLE) _get_osfhandle (desc);
4322 BY_HANDLE_FILE_INFORMATION info;
e3b88685 4323 unsigned __int64 fake_inode;
16bb7578
GV
4324 int permission;
4325
4326 switch (GetFileType (fh) & ~FILE_TYPE_REMOTE)
4327 {
4328 case FILE_TYPE_DISK:
e3b88685 4329 buf->st_mode = S_IFREG;
16bb7578
GV
4330 if (!GetFileInformationByHandle (fh, &info))
4331 {
4332 errno = EACCES;
4333 return -1;
4334 }
4335 break;
4336 case FILE_TYPE_PIPE:
e3b88685 4337 buf->st_mode = S_IFIFO;
16bb7578
GV
4338 goto non_disk;
4339 case FILE_TYPE_CHAR:
4340 case FILE_TYPE_UNKNOWN:
4341 default:
e3b88685 4342 buf->st_mode = S_IFCHR;
16bb7578
GV
4343 non_disk:
4344 memset (&info, 0, sizeof (info));
4345 info.dwFileAttributes = 0;
4346 info.ftCreationTime = utc_base_ft;
4347 info.ftLastAccessTime = utc_base_ft;
4348 info.ftLastWriteTime = utc_base_ft;
4349 }
4350
4351 if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
e3b88685 4352 buf->st_mode = S_IFDIR;
93e0f0da
JR
4353
4354 buf->st_nlink = info.nNumberOfLinks;
4355 /* Might as well use file index to fake inode values, but this
4356 is not guaranteed to be unique unless we keep a handle open
4357 all the time (even then there are situations where it is
4358 not unique). Reputedly, there are at most 48 bits of info
4359 (on NTFS, presumably less on FAT). */
e3b88685
EZ
4360 fake_inode = info.nFileIndexHigh;
4361 fake_inode <<= 32;
4362 fake_inode += info.nFileIndexLow;
16bb7578
GV
4363
4364 /* MSVC defines _ino_t to be short; other libc's might not. */
4365 if (sizeof (buf->st_ino) == 2)
4366 buf->st_ino = fake_inode ^ (fake_inode >> 16);
4367 else
4368 buf->st_ino = fake_inode;
4369
76e9f7b9
EZ
4370 /* If the caller so requested, get the true file owner and group.
4371 Otherwise, consider the file to belong to the current user. */
4372 if (!w32_stat_get_owner_group || is_windows_9x () == TRUE)
4373 get_file_owner_and_group (NULL, buf);
4374 else
4375 {
4376 PSECURITY_DESCRIPTOR psd = NULL;
4377
4378 psd = get_file_security_desc_by_handle (fh);
4379 if (psd)
4380 {
4381 get_file_owner_and_group (psd, buf);
4382 LocalFree (psd);
4383 }
4384 else
4385 get_file_owner_and_group (NULL, buf);
4386 }
16bb7578
GV
4387
4388 buf->st_dev = info.dwVolumeSerialNumber;
4389 buf->st_rdev = info.dwVolumeSerialNumber;
4390
8aaaec6b
EZ
4391 buf->st_size = info.nFileSizeHigh;
4392 buf->st_size <<= 32;
4393 buf->st_size += info.nFileSizeLow;
16bb7578
GV
4394
4395 /* Convert timestamps to Unix format. */
4396 buf->st_mtime = convert_time (info.ftLastWriteTime);
4397 buf->st_atime = convert_time (info.ftLastAccessTime);
4398 if (buf->st_atime == 0) buf->st_atime = buf->st_mtime;
4399 buf->st_ctime = convert_time (info.ftCreationTime);
4400 if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime;
4401
4402 /* determine rwx permissions */
4403 if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
e3b88685 4404 permission = S_IREAD;
16bb7578 4405 else
e3b88685 4406 permission = S_IREAD | S_IWRITE;
177c0ea7 4407
16bb7578 4408 if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
e3b88685 4409 permission |= S_IEXEC;
16bb7578
GV
4410 else
4411 {
4412#if 0 /* no way of knowing the filename */
4413 char * p = strrchr (name, '.');
4414 if (p != NULL &&
05131107
JR
4415 (xstrcasecmp (p, ".exe") == 0 ||
4416 xstrcasecmp (p, ".com") == 0 ||
4417 xstrcasecmp (p, ".bat") == 0 ||
4418 xstrcasecmp (p, ".cmd") == 0))
e3b88685 4419 permission |= S_IEXEC;
16bb7578
GV
4420#endif
4421 }
4422
4423 buf->st_mode |= permission | (permission >> 3) | (permission >> 6);
4424
4425 return 0;
4426}
4427
4428int
4429utime (const char *name, struct utimbuf *times)
4430{
4431 struct utimbuf deftime;
4432 HANDLE fh;
4433 FILETIME mtime;
4434 FILETIME atime;
4435
4436 if (times == NULL)
4437 {
4438 deftime.modtime = deftime.actime = time (NULL);
4439 times = &deftime;
4440 }
4441
4442 /* Need write access to set times. */
0d9f584b 4443 fh = CreateFile (name, FILE_WRITE_ATTRIBUTES,
0ace05d3
EZ
4444 /* If NAME specifies a directory, FILE_SHARE_DELETE
4445 allows other processes to delete files inside it,
4446 while we have the directory open. */
0d9f584b
EZ
4447 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
4448 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
7604f298 4449 if (fh != INVALID_HANDLE_VALUE)
16bb7578
GV
4450 {
4451 convert_from_time_t (times->actime, &atime);
4452 convert_from_time_t (times->modtime, &mtime);
4453 if (!SetFileTime (fh, NULL, &atime, &mtime))
4454 {
4455 CloseHandle (fh);
4456 errno = EACCES;
4457 return -1;
4458 }
4459 CloseHandle (fh);
4460 }
4461 else
4462 {
4463 errno = EINVAL;
4464 return -1;
4465 }
4466 return 0;
4467}
4468
7c80d5ec 4469\f
6dad7178
EZ
4470/* Symlink-related functions. */
4471#ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
4472#define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1
4473#endif
4474
0f7bb05d 4475int
6dad7178 4476symlink (char const *filename, char const *linkname)
0f7bb05d 4477{
6dad7178
EZ
4478 char linkfn[MAX_PATH], *tgtfn;
4479 DWORD flags = 0;
4480 int dir_access, filename_ends_in_slash;
806fed21 4481 int dbcs_p;
6dad7178
EZ
4482
4483 /* Diagnostics follows Posix as much as possible. */
4484 if (filename == NULL || linkname == NULL)
4485 {
4486 errno = EFAULT;
4487 return -1;
4488 }
4489 if (!*filename)
4490 {
4491 errno = ENOENT;
4492 return -1;
4493 }
4494 if (strlen (filename) > MAX_PATH || strlen (linkname) > MAX_PATH)
4495 {
4496 errno = ENAMETOOLONG;
4497 return -1;
4498 }
4499
4500 strcpy (linkfn, map_w32_filename (linkname, NULL));
4501 if ((volume_info.flags & FILE_SUPPORTS_REPARSE_POINTS) == 0)
4502 {
4503 errno = EPERM;
4504 return -1;
4505 }
4506
806fed21
EZ
4507 dbcs_p = max_filename_mbslen () > 1;
4508
6dad7178
EZ
4509 /* Note: since empty FILENAME was already rejected, we can safely
4510 refer to FILENAME[1]. */
4511 if (!(IS_DIRECTORY_SEP (filename[0]) || IS_DEVICE_SEP (filename[1])))
4512 {
4513 /* Non-absolute FILENAME is understood as being relative to
4514 LINKNAME's directory. We need to prepend that directory to
14f20728 4515 FILENAME to get correct results from faccessat below, since
6dad7178
EZ
4516 otherwise it will interpret FILENAME relative to the
4517 directory where the Emacs process runs. Note that
4518 make-symbolic-link always makes sure LINKNAME is a fully
4519 expanded file name. */
4520 char tem[MAX_PATH];
4521 char *p = linkfn + strlen (linkfn);
4522
806fed21
EZ
4523 if (!dbcs_p)
4524 {
4525 while (p > linkfn && !IS_ANY_SEP (p[-1]))
4526 p--;
4527 }
4528 else
4529 {
4530 char *p1 = CharPrevExA (file_name_codepage, linkfn, p, 0);
4531
4532 while (p > linkfn && !IS_ANY_SEP (*p1))
4533 {
4534 p = p1;
4535 p1 = CharPrevExA (file_name_codepage, linkfn, p1, 0);
4536 }
4537 }
6dad7178
EZ
4538 if (p > linkfn)
4539 strncpy (tem, linkfn, p - linkfn);
4540 tem[p - linkfn] = '\0';
4541 strcat (tem, filename);
14f20728 4542 dir_access = faccessat (AT_FDCWD, tem, D_OK, AT_EACCESS);
6dad7178
EZ
4543 }
4544 else
14f20728 4545 dir_access = faccessat (AT_FDCWD, filename, D_OK, AT_EACCESS);
6dad7178
EZ
4546
4547 /* Since Windows distinguishes between symlinks to directories and
8b2e00a3 4548 to files, we provide a kludgy feature: if FILENAME doesn't
6dad7178
EZ
4549 exist, but ends in a slash, we create a symlink to directory. If
4550 FILENAME exists and is a directory, we always create a symlink to
4551 directory. */
806fed21
EZ
4552 if (!dbcs_p)
4553 filename_ends_in_slash = IS_DIRECTORY_SEP (filename[strlen (filename) - 1]);
4554 else
4555 {
4556 const char *end = filename + strlen (filename);
4557 const char *n = CharPrevExA (file_name_codepage, filename, end, 0);
4558
4559 filename_ends_in_slash = IS_DIRECTORY_SEP (*n);
4560 }
6dad7178
EZ
4561 if (dir_access == 0 || filename_ends_in_slash)
4562 flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
4563
4564 tgtfn = (char *)map_w32_filename (filename, NULL);
4565 if (filename_ends_in_slash)
4566 tgtfn[strlen (tgtfn) - 1] = '\0';
4567
4568 errno = 0;
4569 if (!create_symbolic_link (linkfn, tgtfn, flags))
4570 {
4571 /* ENOSYS is set by create_symbolic_link, when it detects that
4572 the OS doesn't support the CreateSymbolicLink API. */
4573 if (errno != ENOSYS)
4574 {
4575 DWORD w32err = GetLastError ();
4576
4577 switch (w32err)
4578 {
4579 /* ERROR_SUCCESS is sometimes returned when LINKFN and
4580 TGTFN point to the same file name, go figure. */
4581 case ERROR_SUCCESS:
4582 case ERROR_FILE_EXISTS:
4583 errno = EEXIST;
4584 break;
4585 case ERROR_ACCESS_DENIED:
4586 errno = EACCES;
4587 break;
4588 case ERROR_FILE_NOT_FOUND:
4589 case ERROR_PATH_NOT_FOUND:
4590 case ERROR_BAD_NETPATH:
4591 case ERROR_INVALID_REPARSE_DATA:
4592 errno = ENOENT;
4593 break;
4594 case ERROR_DIRECTORY:
4595 errno = EISDIR;
4596 break;
4597 case ERROR_PRIVILEGE_NOT_HELD:
4598 case ERROR_NOT_ALL_ASSIGNED:
4599 errno = EPERM;
4600 break;
4601 case ERROR_DISK_FULL:
4602 errno = ENOSPC;
4603 break;
4604 default:
4605 errno = EINVAL;
4606 break;
4607 }
4608 }
4609 return -1;
4610 }
4611 return 0;
0f7bb05d
EZ
4612}
4613
6dad7178
EZ
4614/* A quick inexpensive test of whether FILENAME identifies a file that
4615 is a symlink. Returns non-zero if it is, zero otherwise. FILENAME
4616 must already be in the normalized form returned by
4617 map_w32_filename.
4618
4619 Note: for repeated operations on many files, it is best to test
4620 whether the underlying volume actually supports symlinks, by
4621 testing the FILE_SUPPORTS_REPARSE_POINTS bit in volume's flags, and
4622 avoid the call to this function if it doesn't. That's because the
8b2e00a3 4623 call to GetFileAttributes takes a non-negligible time, especially
6dad7178
EZ
4624 on non-local or removable filesystems. See stat_worker for an
4625 example of how to do that. */
4626static int
4627is_symlink (const char *filename)
4628{
4629 DWORD attrs;
4630 WIN32_FIND_DATA wfd;
4631 HANDLE fh;
4632
4633 attrs = GetFileAttributes (filename);
4634 if (attrs == -1)
4635 {
4636 DWORD w32err = GetLastError ();
4637
4638 switch (w32err)
4639 {
4640 case ERROR_BAD_NETPATH: /* network share, can't be a symlink */
4641 break;
4642 case ERROR_ACCESS_DENIED:
4643 errno = EACCES;
4644 break;
4645 case ERROR_FILE_NOT_FOUND:
4646 case ERROR_PATH_NOT_FOUND:
4647 default:
4648 errno = ENOENT;
4649 break;
4650 }
4651 return 0;
4652 }
4653 if ((attrs & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
4654 return 0;
4655 logon_network_drive (filename);
4656 fh = FindFirstFile (filename, &wfd);
4657 if (fh == INVALID_HANDLE_VALUE)
4658 return 0;
4659 FindClose (fh);
4660 return (wfd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0
4661 && (wfd.dwReserved0 & IO_REPARSE_TAG_SYMLINK) == IO_REPARSE_TAG_SYMLINK;
4662}
4663
4664/* If NAME identifies a symbolic link, copy into BUF the file name of
4665 the symlink's target. Copy at most BUF_SIZE bytes, and do NOT
4666 null-terminate the target name, even if it fits. Return the number
4667 of bytes copied, or -1 if NAME is not a symlink or any error was
4668 encountered while resolving it. The file name copied into BUF is
4669 encoded in the current ANSI codepage. */
0f7bb05d 4670ssize_t
6dad7178 4671readlink (const char *name, char *buf, size_t buf_size)
0f7bb05d 4672{
6dad7178
EZ
4673 const char *path;
4674 TOKEN_PRIVILEGES privs;
4675 int restore_privs = 0;
4676 HANDLE sh;
4677 ssize_t retval;
4678
4679 if (name == NULL)
4680 {
4681 errno = EFAULT;
4682 return -1;
4683 }
4684 if (!*name)
4685 {
4686 errno = ENOENT;
4687 return -1;
4688 }
4689
4690 path = map_w32_filename (name, NULL);
4691
4692 if (strlen (path) > MAX_PATH)
4693 {
4694 errno = ENAMETOOLONG;
4695 return -1;
4696 }
4697
4698 errno = 0;
4699 if (is_windows_9x () == TRUE
4700 || (volume_info.flags & FILE_SUPPORTS_REPARSE_POINTS) == 0
4701 || !is_symlink (path))
4702 {
4703 if (!errno)
4704 errno = EINVAL; /* not a symlink */
4705 return -1;
4706 }
4707
4708 /* Done with simple tests, now we're in for some _real_ work. */
4709 if (enable_privilege (SE_BACKUP_NAME, TRUE, &privs))
4710 restore_privs = 1;
4711 /* Implementation note: From here and onward, don't return early,
4712 since that will fail to restore the original set of privileges of
4713 the calling thread. */
4714
4715 retval = -1; /* not too optimistic, are we? */
4716
4717 /* Note: In the next call to CreateFile, we use zero as the 2nd
4718 argument because, when the symlink is a hidden/system file,
4719 e.g. 'C:\Users\All Users', GENERIC_READ fails with
4720 ERROR_ACCESS_DENIED. Zero seems to work just fine, both for file
4721 and directory symlinks. */
4722 sh = CreateFile (path, 0, 0, NULL, OPEN_EXISTING,
4723 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
4724 NULL);
4725 if (sh != INVALID_HANDLE_VALUE)
4726 {
4727 BYTE reparse_buf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
4728 REPARSE_DATA_BUFFER *reparse_data = (REPARSE_DATA_BUFFER *)&reparse_buf[0];
4729 DWORD retbytes;
4730
4731 if (!DeviceIoControl (sh, FSCTL_GET_REPARSE_POINT, NULL, 0,
4732 reparse_buf, MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
4733 &retbytes, NULL))
4734 errno = EIO;
4735 else if (reparse_data->ReparseTag != IO_REPARSE_TAG_SYMLINK)
4736 errno = EINVAL;
4737 else
4738 {
474d441e 4739 /* Copy the link target name, in wide characters, from
6dad7178
EZ
4740 reparse_data, then convert it to multibyte encoding in
4741 the current locale's codepage. */
4742 WCHAR *lwname;
4743 BYTE lname[MAX_PATH];
4744 USHORT lname_len;
4745 USHORT lwname_len =
4746 reparse_data->SymbolicLinkReparseBuffer.PrintNameLength;
4747 WCHAR *lwname_src =
4748 reparse_data->SymbolicLinkReparseBuffer.PathBuffer
4749 + reparse_data->SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(WCHAR);
26854e9c
EZ
4750 /* This updates file_name_codepage which we need below. */
4751 int dbcs_p = max_filename_mbslen () > 1;
6dad7178
EZ
4752
4753 /* According to MSDN, PrintNameLength does not include the
4754 terminating null character. */
4755 lwname = alloca ((lwname_len + 1) * sizeof(WCHAR));
4756 memcpy (lwname, lwname_src, lwname_len);
4757 lwname[lwname_len/sizeof(WCHAR)] = 0; /* null-terminate */
4758
26854e9c 4759 lname_len = WideCharToMultiByte (file_name_codepage, 0, lwname, -1,
6dad7178
EZ
4760 lname, MAX_PATH, NULL, NULL);
4761 if (!lname_len)
4762 {
4763 /* WideCharToMultiByte failed. */
4764 DWORD w32err1 = GetLastError ();
4765
4766 switch (w32err1)
4767 {
4768 case ERROR_INSUFFICIENT_BUFFER:
4769 errno = ENAMETOOLONG;
4770 break;
4771 case ERROR_INVALID_PARAMETER:
4772 errno = EFAULT;
4773 break;
4774 case ERROR_NO_UNICODE_TRANSLATION:
4775 errno = ENOENT;
4776 break;
4777 default:
4778 errno = EINVAL;
4779 break;
4780 }
4781 }
4782 else
4783 {
4784 size_t size_to_copy = buf_size;
6ee4509a 4785 BYTE *p = lname, *p2;
6dad7178
EZ
4786 BYTE *pend = p + lname_len;
4787
4788 /* Normalize like dostounix_filename does, but we don't
4789 want to assume that lname is null-terminated. */
6ee4509a 4790 if (dbcs_p)
26854e9c 4791 p2 = CharNextExA (file_name_codepage, p, 0);
6ee4509a
EZ
4792 else
4793 p2 = p + 1;
4794 if (*p && *p2 == ':' && *p >= 'A' && *p <= 'Z')
4795 {
4796 *p += 'a' - 'A';
4797 p += 2;
4798 }
6dad7178
EZ
4799 while (p <= pend)
4800 {
4801 if (*p == '\\')
4802 *p = '/';
6ee4509a
EZ
4803 if (dbcs_p)
4804 {
26854e9c 4805 p = CharNextExA (file_name_codepage, p, 0);
6ee4509a
EZ
4806 /* CharNextExA doesn't advance at null character. */
4807 if (!*p)
4808 break;
4809 }
4810 else
4811 ++p;
6dad7178
EZ
4812 }
4813 /* Testing for null-terminated LNAME is paranoia:
4814 WideCharToMultiByte should always return a
4815 null-terminated string when its 4th argument is -1
4816 and its 3rd argument is null-terminated (which they
4817 are, see above). */
4818 if (lname[lname_len - 1] == '\0')
4819 lname_len--;
4820 if (lname_len <= buf_size)
4821 size_to_copy = lname_len;
4822 strncpy (buf, lname, size_to_copy);
4823 /* Success! */
4824 retval = size_to_copy;
4825 }
4826 }
4827 CloseHandle (sh);
4828 }
4829 else
4830 {
4831 /* CreateFile failed. */
4832 DWORD w32err2 = GetLastError ();
4833
4834 switch (w32err2)
4835 {
4836 case ERROR_FILE_NOT_FOUND:
4837 case ERROR_PATH_NOT_FOUND:
4838 errno = ENOENT;
4839 break;
4840 case ERROR_ACCESS_DENIED:
4841 case ERROR_TOO_MANY_OPEN_FILES:
4842 errno = EACCES;
4843 break;
4844 default:
4845 errno = EPERM;
4846 break;
4847 }
4848 }
4849 if (restore_privs)
4850 {
4851 restore_privilege (&privs);
4852 revert_to_self ();
4853 }
4854
4855 return retval;
0f7bb05d
EZ
4856}
4857
8654f9d7
PE
4858ssize_t
4859readlinkat (int fd, char const *name, char *buffer,
4860 size_t buffer_size)
4861{
4862 /* Rely on a hack: an open directory is modeled as file descriptor 0,
4863 as in fstatat. FIXME: Add proper support for readlinkat. */
4864 char fullname[MAX_PATH];
4865
4866 if (fd != AT_FDCWD)
4867 {
4868 if (_snprintf (fullname, sizeof fullname, "%s/%s", dir_pathname, name)
4869 < 0)
4870 {
4871 errno = ENAMETOOLONG;
4872 return -1;
4873 }
4874 name = fullname;
4875 }
4876
4877 return readlink (name, buffer, buffer_size);
4878}
4879
6dad7178
EZ
4880/* If FILE is a symlink, return its target (stored in a static
4881 buffer); otherwise return FILE.
4882
4883 This function repeatedly resolves symlinks in the last component of
4884 a chain of symlink file names, as in foo -> bar -> baz -> ...,
4885 until it arrives at a file whose last component is not a symlink,
4886 or some error occurs. It returns the target of the last
4887 successfully resolved symlink in the chain. If it succeeds to
4888 resolve even a single symlink, the value returned is an absolute
4889 file name with backslashes (result of GetFullPathName). By
4890 contrast, if the original FILE is returned, it is unaltered.
4891
4892 Note: This function can set errno even if it succeeds.
4893
4894 Implementation note: we only resolve the last portion ("basename")
4895 of the argument FILE and of each following file in the chain,
4896 disregarding any possible symlinks in its leading directories.
4897 This is because Windows system calls and library functions
4898 transparently resolve symlinks in leading directories and return
4899 correct information, as long as the basename is not a symlink. */
4900static char *
4901chase_symlinks (const char *file)
4902{
4903 static char target[MAX_PATH];
4904 char link[MAX_PATH];
4905 ssize_t res, link_len;
4906 int loop_count = 0;
806fed21 4907 int dbcs_p;
6dad7178
EZ
4908
4909 if (is_windows_9x () == TRUE || !is_symlink (file))
4910 return (char *)file;
4911
4912 if ((link_len = GetFullPathName (file, MAX_PATH, link, NULL)) == 0)
4913 return (char *)file;
4914
806fed21 4915 dbcs_p = max_filename_mbslen () > 1;
6dad7178
EZ
4916 target[0] = '\0';
4917 do {
4918
4919 /* Remove trailing slashes, as we want to resolve the last
4920 non-trivial part of the link name. */
806fed21
EZ
4921 if (!dbcs_p)
4922 {
4923 while (link_len > 3 && IS_DIRECTORY_SEP (link[link_len-1]))
4924 link[link_len--] = '\0';
4925 }
4926 else if (link_len > 3)
4927 {
4928 char *n = CharPrevExA (file_name_codepage, link, link + link_len, 0);
4929
4930 while (n >= link + 2 && IS_DIRECTORY_SEP (*n))
4931 {
4932 n[1] = '\0';
4933 n = CharPrevExA (file_name_codepage, link, n, 0);
4934 }
4935 }
6dad7178
EZ
4936
4937 res = readlink (link, target, MAX_PATH);
4938 if (res > 0)
4939 {
4940 target[res] = '\0';
4941 if (!(IS_DEVICE_SEP (target[1])
25a20a3a 4942 || (IS_DIRECTORY_SEP (target[0]) && IS_DIRECTORY_SEP (target[1]))))
6dad7178
EZ
4943 {
4944 /* Target is relative. Append it to the directory part of
4945 the symlink, then copy the result back to target. */
4946 char *p = link + link_len;
4947
806fed21
EZ
4948 if (!dbcs_p)
4949 {
4950 while (p > link && !IS_ANY_SEP (p[-1]))
4951 p--;
4952 }
4953 else
4954 {
4955 char *p1 = CharPrevExA (file_name_codepage, link, p, 0);
4956
4957 while (p > link && !IS_ANY_SEP (*p1))
4958 {
4959 p = p1;
4960 p1 = CharPrevExA (file_name_codepage, link, p1, 0);
4961 }
4962 }
6dad7178
EZ
4963 strcpy (p, target);
4964 strcpy (target, link);
4965 }
4966 /* Resolve any "." and ".." to get a fully-qualified file name
4967 in link[] again. */
4968 link_len = GetFullPathName (target, MAX_PATH, link, NULL);
4969 }
4970 } while (res > 0 && link_len > 0 && ++loop_count <= 100);
4971
4972 if (loop_count > 100)
4973 errno = ELOOP;
4974
4975 if (target[0] == '\0') /* not a single call to readlink succeeded */
4976 return (char *)file;
4977 return target;
4978}
4979
66447e07
EZ
4980\f
4981/* Posix ACL emulation. */
4982
4983int
4984acl_valid (acl_t acl)
4985{
4986 return is_valid_security_descriptor ((PSECURITY_DESCRIPTOR)acl) ? 0 : -1;
4987}
4988
4989char *
4990acl_to_text (acl_t acl, ssize_t *size)
4991{
4992 LPTSTR str_acl;
4993 SECURITY_INFORMATION flags =
4994 OWNER_SECURITY_INFORMATION |
4995 GROUP_SECURITY_INFORMATION |
4996 DACL_SECURITY_INFORMATION;
4997 char *retval = NULL;
b764d018 4998 ULONG local_size;
66447e07
EZ
4999 int e = errno;
5000
5001 errno = 0;
5002
5003 if (convert_sd_to_sddl ((PSECURITY_DESCRIPTOR)acl, SDDL_REVISION_1, flags, &str_acl, &local_size))
5004 {
5005 errno = e;
5006 /* We don't want to mix heaps, so we duplicate the string in our
5007 heap and free the one allocated by the API. */
5008 retval = xstrdup (str_acl);
5009 if (size)
5010 *size = local_size;
5011 LocalFree (str_acl);
5012 }
5013 else if (errno != ENOTSUP)
5014 errno = EINVAL;
5015
5016 return retval;
5017}
5018
5019acl_t
5020acl_from_text (const char *acl_str)
5021{
5022 PSECURITY_DESCRIPTOR psd, retval = NULL;
5023 ULONG sd_size;
5024 int e = errno;
5025
5026 errno = 0;
5027
5028 if (convert_sddl_to_sd (acl_str, SDDL_REVISION_1, &psd, &sd_size))
5029 {
5030 errno = e;
5031 retval = xmalloc (sd_size);
5032 memcpy (retval, psd, sd_size);
5033 LocalFree (psd);
5034 }
5035 else if (errno != ENOTSUP)
5036 errno = EINVAL;
5037
5038 return retval;
5039}
5040
5041int
5042acl_free (void *ptr)
5043{
5044 xfree (ptr);
5045 return 0;
5046}
5047
5048acl_t
5049acl_get_file (const char *fname, acl_type_t type)
5050{
5051 PSECURITY_DESCRIPTOR psd = NULL;
474d441e 5052 const char *filename;
66447e07
EZ
5053
5054 if (type == ACL_TYPE_ACCESS)
5055 {
5056 DWORD sd_len, err;
5057 SECURITY_INFORMATION si =
5058 OWNER_SECURITY_INFORMATION |
5059 GROUP_SECURITY_INFORMATION |
5060 DACL_SECURITY_INFORMATION ;
5061 int e = errno;
5062
474d441e
EZ
5063 filename = map_w32_filename (fname, NULL);
5064 if ((volume_info.flags & FILE_SUPPORTS_REPARSE_POINTS) != 0)
5065 fname = chase_symlinks (filename);
5066 else
5067 fname = filename;
5068
66447e07
EZ
5069 errno = 0;
5070 if (!get_file_security (fname, si, psd, 0, &sd_len)
5071 && errno != ENOTSUP)
5072 {
5073 err = GetLastError ();
5074 if (err == ERROR_INSUFFICIENT_BUFFER)
5075 {
5076 psd = xmalloc (sd_len);
5077 if (!get_file_security (fname, si, psd, sd_len, &sd_len))
5078 {
5079 xfree (psd);
5080 errno = EIO;
5081 psd = NULL;
5082 }
5083 }
5084 else if (err == ERROR_FILE_NOT_FOUND
5085 || err == ERROR_PATH_NOT_FOUND)
5086 errno = ENOENT;
5087 else
5088 errno = EIO;
5089 }
5090 else if (!errno)
5091 errno = e;
5092 }
5093 else if (type != ACL_TYPE_DEFAULT)
5094 errno = EINVAL;
5095
5096 return psd;
5097}
5098
5099int
5100acl_set_file (const char *fname, acl_type_t type, acl_t acl)
5101{
5102 TOKEN_PRIVILEGES old1, old2;
5103 DWORD err;
66447e07
EZ
5104 int st = 0, retval = -1;
5105 SECURITY_INFORMATION flags = 0;
5106 PSID psid;
5107 PACL pacl;
5108 BOOL dflt;
5109 BOOL dacl_present;
5110 int e;
474d441e 5111 const char *filename;
66447e07
EZ
5112
5113 if (acl_valid (acl) != 0
5114 || (type != ACL_TYPE_DEFAULT && type != ACL_TYPE_ACCESS))
5115 {
5116 errno = EINVAL;
5117 return -1;
5118 }
5119
5120 if (type == ACL_TYPE_DEFAULT)
5121 {
5122 errno = ENOSYS;
5123 return -1;
5124 }
5125
474d441e
EZ
5126 filename = map_w32_filename (fname, NULL);
5127 if ((volume_info.flags & FILE_SUPPORTS_REPARSE_POINTS) != 0)
5128 fname = chase_symlinks (filename);
5129 else
5130 fname = filename;
5131
66447e07
EZ
5132 if (get_security_descriptor_owner ((PSECURITY_DESCRIPTOR)acl, &psid, &dflt)
5133 && psid)
5134 flags |= OWNER_SECURITY_INFORMATION;
5135 if (get_security_descriptor_group ((PSECURITY_DESCRIPTOR)acl, &psid, &dflt)
5136 && psid)
5137 flags |= GROUP_SECURITY_INFORMATION;
5138 if (get_security_descriptor_dacl ((PSECURITY_DESCRIPTOR)acl, &dacl_present,
5139 &pacl, &dflt)
5140 && dacl_present)
5141 flags |= DACL_SECURITY_INFORMATION;
5142 if (!flags)
5143 return 0;
5144
5145 /* According to KB-245153, setting the owner will succeed if either:
5146 (1) the caller is the user who will be the new owner, and has the
5147 SE_TAKE_OWNERSHIP privilege, or
5148 (2) the caller has the SE_RESTORE privilege, in which case she can
5149 set any valid user or group as the owner
5150
5151 We request below both SE_TAKE_OWNERSHIP and SE_RESTORE
5152 privileges, and disregard any failures in obtaining them. If
5153 these privileges cannot be obtained, and do not already exist in
5154 the calling thread's security token, this function could fail
5155 with EPERM. */
5156 if (enable_privilege (SE_TAKE_OWNERSHIP_NAME, TRUE, &old1))
5157 st++;
5158 if (enable_privilege (SE_RESTORE_NAME, TRUE, &old2))
5159 st++;
5160
5161 e = errno;
5162 errno = 0;
84e5ed96 5163 if (!set_file_security ((char *)fname, flags, (PSECURITY_DESCRIPTOR)acl))
474d441e 5164 {
84e5ed96 5165 err = GetLastError ();
40ff07a5 5166
84e5ed96
EZ
5167 if (errno == ENOTSUP)
5168 ;
5169 else if (err == ERROR_INVALID_OWNER
5170 || err == ERROR_NOT_ALL_ASSIGNED
5171 || err == ERROR_ACCESS_DENIED)
40ff07a5 5172 {
84e5ed96
EZ
5173 /* Maybe the requested ACL and the one the file already has
5174 are identical, in which case we can silently ignore the
5175 failure. (And no, Windows doesn't.) */
5176 acl_t current_acl = acl_get_file (fname, ACL_TYPE_ACCESS);
40ff07a5 5177
84e5ed96
EZ
5178 errno = EPERM;
5179 if (current_acl)
40ff07a5 5180 {
84e5ed96
EZ
5181 char *acl_from = acl_to_text (current_acl, NULL);
5182 char *acl_to = acl_to_text (acl, NULL);
5183
5184 if (acl_from && acl_to && xstrcasecmp (acl_from, acl_to) == 0)
5185 {
5186 retval = 0;
5187 errno = e;
5188 }
5189 if (acl_from)
5190 acl_free (acl_from);
5191 if (acl_to)
5192 acl_free (acl_to);
5193 acl_free (current_acl);
40ff07a5 5194 }
40ff07a5 5195 }
84e5ed96
EZ
5196 else if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND)
5197 errno = ENOENT;
5198 else
5199 errno = EACCES;
5200 }
5201 else
5202 {
5203 retval = 0;
5204 errno = e;
5205 }
5206
5207 if (st)
5208 {
5209 if (st >= 2)
5210 restore_privilege (&old2);
5211 restore_privilege (&old1);
5212 revert_to_self ();
40ff07a5 5213 }
66447e07
EZ
5214
5215 return retval;
5216}
5217
5218\f
6dad7178
EZ
5219/* MS-Windows version of careadlinkat (cf. ../lib/careadlinkat.c). We
5220 have a fixed max size for file names, so we don't need the kind of
5221 alloc/malloc/realloc dance the gnulib version does. We also don't
5222 support FD-relative symlinks. */
973f782d
EZ
5223char *
5224careadlinkat (int fd, char const *filename,
5225 char *buffer, size_t buffer_size,
5226 struct allocator const *alloc,
5227 ssize_t (*preadlinkat) (int, char const *, char *, size_t))
5228{
6dad7178
EZ
5229 char linkname[MAX_PATH];
5230 ssize_t link_size;
5231
6dad7178
EZ
5232 link_size = preadlinkat (fd, filename, linkname, sizeof(linkname));
5233
5234 if (link_size > 0)
5235 {
5236 char *retval = buffer;
5237
5238 linkname[link_size++] = '\0';
5239 if (link_size > buffer_size)
5240 retval = (char *)(alloc ? alloc->allocate : xmalloc) (link_size);
5241 if (retval)
5242 memcpy (retval, linkname, link_size);
5243
5244 return retval;
5245 }
973f782d
EZ
5246 return NULL;
5247}
5248
0f7bb05d 5249\f
7c80d5ec
EZ
5250/* Support for browsing other processes and their attributes. See
5251 process.c for the Lisp bindings. */
5252
5253/* Helper wrapper functions. */
5254
bedf4aab
JB
5255static HANDLE WINAPI
5256create_toolhelp32_snapshot (DWORD Flags, DWORD Ignored)
7c80d5ec
EZ
5257{
5258 static CreateToolhelp32Snapshot_Proc s_pfn_Create_Toolhelp32_Snapshot = NULL;
5259
5260 if (g_b_init_create_toolhelp32_snapshot == 0)
5261 {
5262 g_b_init_create_toolhelp32_snapshot = 1;
5263 s_pfn_Create_Toolhelp32_Snapshot = (CreateToolhelp32Snapshot_Proc)
5264 GetProcAddress (GetModuleHandle ("kernel32.dll"),
5265 "CreateToolhelp32Snapshot");
5266 }
5267 if (s_pfn_Create_Toolhelp32_Snapshot == NULL)
5268 {
5269 return INVALID_HANDLE_VALUE;
5270 }
5271 return (s_pfn_Create_Toolhelp32_Snapshot (Flags, Ignored));
5272}
5273
bedf4aab
JB
5274static BOOL WINAPI
5275process32_first (HANDLE hSnapshot, LPPROCESSENTRY32 lppe)
7c80d5ec
EZ
5276{
5277 static Process32First_Proc s_pfn_Process32_First = NULL;
5278
5279 if (g_b_init_process32_first == 0)
5280 {
5281 g_b_init_process32_first = 1;
5282 s_pfn_Process32_First = (Process32First_Proc)
5283 GetProcAddress (GetModuleHandle ("kernel32.dll"),
5284 "Process32First");
5285 }
5286 if (s_pfn_Process32_First == NULL)
5287 {
5288 return FALSE;
5289 }
5290 return (s_pfn_Process32_First (hSnapshot, lppe));
5291}
5292
bedf4aab
JB
5293static BOOL WINAPI
5294process32_next (HANDLE hSnapshot, LPPROCESSENTRY32 lppe)
7c80d5ec
EZ
5295{
5296 static Process32Next_Proc s_pfn_Process32_Next = NULL;
5297
5298 if (g_b_init_process32_next == 0)
5299 {
5300 g_b_init_process32_next = 1;
5301 s_pfn_Process32_Next = (Process32Next_Proc)
5302 GetProcAddress (GetModuleHandle ("kernel32.dll"),
5303 "Process32Next");
5304 }
5305 if (s_pfn_Process32_Next == NULL)
5306 {
5307 return FALSE;
5308 }
5309 return (s_pfn_Process32_Next (hSnapshot, lppe));
5310}
5311
bedf4aab
JB
5312static BOOL WINAPI
5313open_thread_token (HANDLE ThreadHandle,
5314 DWORD DesiredAccess,
5315 BOOL OpenAsSelf,
5316 PHANDLE TokenHandle)
7c80d5ec
EZ
5317{
5318 static OpenThreadToken_Proc s_pfn_Open_Thread_Token = NULL;
5319 HMODULE hm_advapi32 = NULL;
5320 if (is_windows_9x () == TRUE)
5321 {
5322 SetLastError (ERROR_NOT_SUPPORTED);
5323 return FALSE;
5324 }
5325 if (g_b_init_open_thread_token == 0)
5326 {
5327 g_b_init_open_thread_token = 1;
5328 hm_advapi32 = LoadLibrary ("Advapi32.dll");
5329 s_pfn_Open_Thread_Token =
5330 (OpenThreadToken_Proc) GetProcAddress (hm_advapi32, "OpenThreadToken");
5331 }
5332 if (s_pfn_Open_Thread_Token == NULL)
5333 {
5334 SetLastError (ERROR_NOT_SUPPORTED);
5335 return FALSE;
5336 }
5337 return (
5338 s_pfn_Open_Thread_Token (
5339 ThreadHandle,
5340 DesiredAccess,
5341 OpenAsSelf,
5342 TokenHandle)
5343 );
5344}
5345
bedf4aab
JB
5346static BOOL WINAPI
5347impersonate_self (SECURITY_IMPERSONATION_LEVEL ImpersonationLevel)
7c80d5ec
EZ
5348{
5349 static ImpersonateSelf_Proc s_pfn_Impersonate_Self = NULL;
5350 HMODULE hm_advapi32 = NULL;
5351 if (is_windows_9x () == TRUE)
5352 {
5353 return FALSE;
5354 }
5355 if (g_b_init_impersonate_self == 0)
5356 {
5357 g_b_init_impersonate_self = 1;
5358 hm_advapi32 = LoadLibrary ("Advapi32.dll");
5359 s_pfn_Impersonate_Self =
5360 (ImpersonateSelf_Proc) GetProcAddress (hm_advapi32, "ImpersonateSelf");
5361 }
5362 if (s_pfn_Impersonate_Self == NULL)
5363 {
5364 return FALSE;
5365 }
5366 return s_pfn_Impersonate_Self (ImpersonationLevel);
5367}
5368
bedf4aab
JB
5369static BOOL WINAPI
5370revert_to_self (void)
7c80d5ec
EZ
5371{
5372 static RevertToSelf_Proc s_pfn_Revert_To_Self = NULL;
5373 HMODULE hm_advapi32 = NULL;
5374 if (is_windows_9x () == TRUE)
5375 {
5376 return FALSE;
5377 }
5378 if (g_b_init_revert_to_self == 0)
5379 {
5380 g_b_init_revert_to_self = 1;
5381 hm_advapi32 = LoadLibrary ("Advapi32.dll");
5382 s_pfn_Revert_To_Self =
5383 (RevertToSelf_Proc) GetProcAddress (hm_advapi32, "RevertToSelf");
5384 }
5385 if (s_pfn_Revert_To_Self == NULL)
5386 {
5387 return FALSE;
5388 }
5389 return s_pfn_Revert_To_Self ();
5390}
5391
bedf4aab
JB
5392static BOOL WINAPI
5393get_process_memory_info (HANDLE h_proc,
5394 PPROCESS_MEMORY_COUNTERS mem_counters,
5395 DWORD bufsize)
7c80d5ec
EZ
5396{
5397 static GetProcessMemoryInfo_Proc s_pfn_Get_Process_Memory_Info = NULL;
5398 HMODULE hm_psapi = NULL;
5399 if (is_windows_9x () == TRUE)
5400 {
5401 return FALSE;
5402 }
5403 if (g_b_init_get_process_memory_info == 0)
5404 {
5405 g_b_init_get_process_memory_info = 1;
5406 hm_psapi = LoadLibrary ("Psapi.dll");
5407 if (hm_psapi)
5408 s_pfn_Get_Process_Memory_Info = (GetProcessMemoryInfo_Proc)
5409 GetProcAddress (hm_psapi, "GetProcessMemoryInfo");
5410 }
5411 if (s_pfn_Get_Process_Memory_Info == NULL)
5412 {
5413 return FALSE;
5414 }
5415 return s_pfn_Get_Process_Memory_Info (h_proc, mem_counters, bufsize);
5416}
5417
bedf4aab
JB
5418static BOOL WINAPI
5419get_process_working_set_size (HANDLE h_proc,
cb576b5c
FP
5420 PSIZE_T minrss,
5421 PSIZE_T maxrss)
7c80d5ec
EZ
5422{
5423 static GetProcessWorkingSetSize_Proc
5424 s_pfn_Get_Process_Working_Set_Size = NULL;
5425
5426 if (is_windows_9x () == TRUE)
5427 {
5428 return FALSE;
5429 }
5430 if (g_b_init_get_process_working_set_size == 0)
5431 {
5432 g_b_init_get_process_working_set_size = 1;
5433 s_pfn_Get_Process_Working_Set_Size = (GetProcessWorkingSetSize_Proc)
5434 GetProcAddress (GetModuleHandle ("kernel32.dll"),
5435 "GetProcessWorkingSetSize");
5436 }
5437 if (s_pfn_Get_Process_Working_Set_Size == NULL)
5438 {
5439 return FALSE;
5440 }
5441 return s_pfn_Get_Process_Working_Set_Size (h_proc, minrss, maxrss);
5442}
5443
bedf4aab
JB
5444static BOOL WINAPI
5445global_memory_status (MEMORYSTATUS *buf)
7c80d5ec
EZ
5446{
5447 static GlobalMemoryStatus_Proc s_pfn_Global_Memory_Status = NULL;
5448
5449 if (is_windows_9x () == TRUE)
5450 {
5451 return FALSE;
5452 }
5453 if (g_b_init_global_memory_status == 0)
5454 {
5455 g_b_init_global_memory_status = 1;
5456 s_pfn_Global_Memory_Status = (GlobalMemoryStatus_Proc)
5457 GetProcAddress (GetModuleHandle ("kernel32.dll"),
5458 "GlobalMemoryStatus");
5459 }
5460 if (s_pfn_Global_Memory_Status == NULL)
5461 {
5462 return FALSE;
5463 }
5464 return s_pfn_Global_Memory_Status (buf);
5465}
5466
bedf4aab
JB
5467static BOOL WINAPI
5468global_memory_status_ex (MEMORY_STATUS_EX *buf)
7c80d5ec
EZ
5469{
5470 static GlobalMemoryStatusEx_Proc s_pfn_Global_Memory_Status_Ex = NULL;
5471
5472 if (is_windows_9x () == TRUE)
5473 {
5474 return FALSE;
5475 }
5476 if (g_b_init_global_memory_status_ex == 0)
5477 {
5478 g_b_init_global_memory_status_ex = 1;
5479 s_pfn_Global_Memory_Status_Ex = (GlobalMemoryStatusEx_Proc)
5480 GetProcAddress (GetModuleHandle ("kernel32.dll"),
5481 "GlobalMemoryStatusEx");
5482 }
5483 if (s_pfn_Global_Memory_Status_Ex == NULL)
5484 {
5485 return FALSE;
5486 }
5487 return s_pfn_Global_Memory_Status_Ex (buf);
5488}
5489
5490Lisp_Object
b56ceb92 5491list_system_processes (void)
7c80d5ec
EZ
5492{
5493 struct gcpro gcpro1;
5494 Lisp_Object proclist = Qnil;
5495 HANDLE h_snapshot;
5496
5497 h_snapshot = create_toolhelp32_snapshot (TH32CS_SNAPPROCESS, 0);
5498
5499 if (h_snapshot != INVALID_HANDLE_VALUE)
5500 {
5501 PROCESSENTRY32 proc_entry;
5502 DWORD proc_id;
5503 BOOL res;
5504
5505 GCPRO1 (proclist);
5506
5507 proc_entry.dwSize = sizeof (PROCESSENTRY32);
5508 for (res = process32_first (h_snapshot, &proc_entry); res;
5509 res = process32_next (h_snapshot, &proc_entry))
5510 {
5511 proc_id = proc_entry.th32ProcessID;
5512 proclist = Fcons (make_fixnum_or_float (proc_id), proclist);
5513 }
5514
5515 CloseHandle (h_snapshot);
5516 UNGCPRO;
5517 proclist = Fnreverse (proclist);
5518 }
5519
5520 return proclist;
5521}
5522
5523static int
5524enable_privilege (LPCTSTR priv_name, BOOL enable_p, TOKEN_PRIVILEGES *old_priv)
5525{
5526 TOKEN_PRIVILEGES priv;
5527 DWORD priv_size = sizeof (priv);
5528 DWORD opriv_size = sizeof (*old_priv);
5529 HANDLE h_token = NULL;
5530 HANDLE h_thread = GetCurrentThread ();
5531 int ret_val = 0;
5532 BOOL res;
5533
5534 res = open_thread_token (h_thread,
5535 TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
5536 FALSE, &h_token);
5537 if (!res && GetLastError () == ERROR_NO_TOKEN)
5538 {
5539 if (impersonate_self (SecurityImpersonation))
5540 res = open_thread_token (h_thread,
5541 TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
5542 FALSE, &h_token);
5543 }
5544 if (res)
5545 {
5546 priv.PrivilegeCount = 1;
5547 priv.Privileges[0].Attributes = enable_p ? SE_PRIVILEGE_ENABLED : 0;
5548 LookupPrivilegeValue (NULL, priv_name, &priv.Privileges[0].Luid);
5549 if (AdjustTokenPrivileges (h_token, FALSE, &priv, priv_size,
5550 old_priv, &opriv_size)
5551 && GetLastError () != ERROR_NOT_ALL_ASSIGNED)
5552 ret_val = 1;
5553 }
5554 if (h_token)
5555 CloseHandle (h_token);
5556
5557 return ret_val;
5558}
5559
5560static int
5561restore_privilege (TOKEN_PRIVILEGES *priv)
5562{
5563 DWORD priv_size = sizeof (*priv);
5564 HANDLE h_token = NULL;
5565 int ret_val = 0;
5566
5567 if (open_thread_token (GetCurrentThread (),
5568 TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
5569 FALSE, &h_token))
5570 {
5571 if (AdjustTokenPrivileges (h_token, FALSE, priv, priv_size, NULL, NULL)
5572 && GetLastError () != ERROR_NOT_ALL_ASSIGNED)
5573 ret_val = 1;
5574 }
5575 if (h_token)
5576 CloseHandle (h_token);
5577
5578 return ret_val;
5579}
5580
ca300656 5581static Lisp_Object
d35af63c 5582ltime (ULONGLONG time_100ns)
7c80d5ec 5583{
d35af63c
PE
5584 ULONGLONG time_sec = time_100ns / 10000000;
5585 int subsec = time_100ns % 10000000;
3de717bd
DA
5586 return list4i (time_sec >> 16, time_sec & 0xffff,
5587 subsec / 10, subsec % 10 * 100000);
7c80d5ec
EZ
5588}
5589
d35af63c 5590#define U64_TO_LISP_TIME(time) ltime (time)
5da9424d 5591
7c80d5ec 5592static int
b56ceb92
JB
5593process_times (HANDLE h_proc, Lisp_Object *ctime, Lisp_Object *etime,
5594 Lisp_Object *stime, Lisp_Object *utime, Lisp_Object *ttime,
5595 double *pcpu)
7c80d5ec
EZ
5596{
5597 FILETIME ft_creation, ft_exit, ft_kernel, ft_user, ft_current;
5da9424d 5598 ULONGLONG tem1, tem2, tem3, tem;
7c80d5ec
EZ
5599
5600 if (!h_proc
5601 || !get_process_times_fn
ed3751c8
JB
5602 || !(*get_process_times_fn) (h_proc, &ft_creation, &ft_exit,
5603 &ft_kernel, &ft_user))
7c80d5ec
EZ
5604 return 0;
5605
5606 GetSystemTimeAsFileTime (&ft_current);
5607
5da9424d 5608 FILETIME_TO_U64 (tem1, ft_kernel);
5da9424d
JB
5609 *stime = U64_TO_LISP_TIME (tem1);
5610
5611 FILETIME_TO_U64 (tem2, ft_user);
5da9424d
JB
5612 *utime = U64_TO_LISP_TIME (tem2);
5613
5614 tem3 = tem1 + tem2;
5615 *ttime = U64_TO_LISP_TIME (tem3);
5616
5617 FILETIME_TO_U64 (tem, ft_creation);
3af03101
EZ
5618 /* Process no 4 (System) returns zero creation time. */
5619 if (tem)
d35af63c 5620 tem -= utc_base;
5da9424d
JB
5621 *ctime = U64_TO_LISP_TIME (tem);
5622
3af03101 5623 if (tem)
5da9424d
JB
5624 {
5625 FILETIME_TO_U64 (tem3, ft_current);
d35af63c 5626 tem = (tem3 - utc_base) - tem;
5da9424d
JB
5627 }
5628 *etime = U64_TO_LISP_TIME (tem);
7c80d5ec 5629
3af03101
EZ
5630 if (tem)
5631 {
5632 *pcpu = 100.0 * (tem1 + tem2) / tem;
5633 if (*pcpu > 100)
5634 *pcpu = 100.0;
5635 }
5636 else
5637 *pcpu = 0;
5638
5639 return 1;
7c80d5ec
EZ
5640}
5641
5642Lisp_Object
b56ceb92 5643system_process_attributes (Lisp_Object pid)
7c80d5ec
EZ
5644{
5645 struct gcpro gcpro1, gcpro2, gcpro3;
5646 Lisp_Object attrs = Qnil;
5647 Lisp_Object cmd_str, decoded_cmd, tem;
5648 HANDLE h_snapshot, h_proc;
5649 DWORD proc_id;
754a2d13 5650 int found_proc = 0;
7c80d5ec 5651 char uname[UNLEN+1], gname[GNLEN+1], domain[1025];
32cef06e 5652 DWORD ulength = sizeof (uname), dlength = sizeof (domain), needed;
7c80d5ec
EZ
5653 DWORD glength = sizeof (gname);
5654 HANDLE token = NULL;
5655 SID_NAME_USE user_type;
32cef06e
EZ
5656 unsigned char *buf = NULL;
5657 DWORD blen = 0;
7c80d5ec
EZ
5658 TOKEN_USER user_token;
5659 TOKEN_PRIMARY_GROUP group_token;
22749e9a
EZ
5660 unsigned euid;
5661 unsigned egid;
7c80d5ec
EZ
5662 PROCESS_MEMORY_COUNTERS mem;
5663 PROCESS_MEMORY_COUNTERS_EX mem_ex;
cb576b5c 5664 SIZE_T minrss, maxrss;
7c80d5ec 5665 MEMORYSTATUS memst;
b8526f6e 5666 MEMORY_STATUS_EX memstex;
7c80d5ec 5667 double totphys = 0.0;
031da700 5668 Lisp_Object ctime, stime, utime, etime, ttime;
7c80d5ec 5669 double pcpu;
32cef06e 5670 BOOL result = FALSE;
7c80d5ec
EZ
5671
5672 CHECK_NUMBER_OR_FLOAT (pid);
5673 proc_id = FLOATP (pid) ? XFLOAT_DATA (pid) : XINT (pid);
5674
5675 h_snapshot = create_toolhelp32_snapshot (TH32CS_SNAPPROCESS, 0);
5676
5677 GCPRO3 (attrs, decoded_cmd, tem);
5678
5679 if (h_snapshot != INVALID_HANDLE_VALUE)
5680 {
5681 PROCESSENTRY32 pe;
5682 BOOL res;
5683
5684 pe.dwSize = sizeof (PROCESSENTRY32);
5685 for (res = process32_first (h_snapshot, &pe); res;
5686 res = process32_next (h_snapshot, &pe))
5687 {
5688 if (proc_id == pe.th32ProcessID)
5689 {
5690 if (proc_id == 0)
5691 decoded_cmd = build_string ("Idle");
5692 else
5693 {
5694 /* Decode the command name from locale-specific
5695 encoding. */
5696 cmd_str = make_unibyte_string (pe.szExeFile,
5697 strlen (pe.szExeFile));
5698 decoded_cmd =
5699 code_convert_string_norecord (cmd_str,
5700 Vlocale_coding_system, 0);
5701 }
5702 attrs = Fcons (Fcons (Qcomm, decoded_cmd), attrs);
5703 attrs = Fcons (Fcons (Qppid,
5704 make_fixnum_or_float (pe.th32ParentProcessID)),
5705 attrs);
5706 attrs = Fcons (Fcons (Qpri, make_number (pe.pcPriClassBase)),
5707 attrs);
5708 attrs = Fcons (Fcons (Qthcount,
5709 make_fixnum_or_float (pe.cntThreads)),
5710 attrs);
754a2d13 5711 found_proc = 1;
7c80d5ec
EZ
5712 break;
5713 }
5714 }
5715
5716 CloseHandle (h_snapshot);
5717 }
5718
754a2d13
EZ
5719 if (!found_proc)
5720 {
5721 UNGCPRO;
5722 return Qnil;
5723 }
5724
7c80d5ec
EZ
5725 h_proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
5726 FALSE, proc_id);
5727 /* If we were denied a handle to the process, try again after
5728 enabling the SeDebugPrivilege in our process. */
5729 if (!h_proc)
5730 {
5731 TOKEN_PRIVILEGES priv_current;
5732
5733 if (enable_privilege (SE_DEBUG_NAME, TRUE, &priv_current))
5734 {
5735 h_proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
5736 FALSE, proc_id);
5737 restore_privilege (&priv_current);
5738 revert_to_self ();
5739 }
5740 }
32cef06e 5741 if (h_proc)
7c80d5ec 5742 {
32cef06e
EZ
5743 result = open_process_token (h_proc, TOKEN_QUERY, &token);
5744 if (result)
f8b35b24 5745 {
32cef06e
EZ
5746 result = get_token_information (token, TokenUser, NULL, 0, &blen);
5747 if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER)
5748 {
5749 buf = xmalloc (blen);
5750 result = get_token_information (token, TokenUser,
5751 (LPVOID)buf, blen, &needed);
5752 if (result)
5753 {
5754 memcpy (&user_token, buf, sizeof (user_token));
5755 if (!w32_cached_id (user_token.User.Sid, &euid, uname))
5756 {
5757 euid = get_rid (user_token.User.Sid);
5758 result = lookup_account_sid (NULL, user_token.User.Sid,
5759 uname, &ulength,
5760 domain, &dlength,
5761 &user_type);
5762 if (result)
5763 w32_add_to_cache (user_token.User.Sid, euid, uname);
5764 else
5765 {
5766 strcpy (uname, "unknown");
5767 result = TRUE;
5768 }
5769 }
5770 ulength = strlen (uname);
5771 }
5772 }
7c80d5ec 5773 }
32cef06e 5774 if (result)
7c80d5ec 5775 {
32cef06e
EZ
5776 /* Determine a reasonable euid and gid values. */
5777 if (xstrcasecmp ("administrator", uname) == 0)
7c80d5ec 5778 {
32cef06e
EZ
5779 euid = 500; /* well-known Administrator uid */
5780 egid = 513; /* well-known None gid */
5781 }
5782 else
5783 {
5784 /* Get group id and name. */
5785 result = get_token_information (token, TokenPrimaryGroup,
5786 (LPVOID)buf, blen, &needed);
5787 if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER)
f8b35b24 5788 {
32cef06e
EZ
5789 buf = xrealloc (buf, blen = needed);
5790 result = get_token_information (token, TokenPrimaryGroup,
5791 (LPVOID)buf, blen, &needed);
5792 }
5793 if (result)
5794 {
5795 memcpy (&group_token, buf, sizeof (group_token));
5796 if (!w32_cached_id (group_token.PrimaryGroup, &egid, gname))
5797 {
5798 egid = get_rid (group_token.PrimaryGroup);
5799 dlength = sizeof (domain);
5800 result =
5801 lookup_account_sid (NULL, group_token.PrimaryGroup,
5802 gname, &glength, NULL, &dlength,
5803 &user_type);
5804 if (result)
5805 w32_add_to_cache (group_token.PrimaryGroup,
5806 egid, gname);
5807 else
5808 {
5809 strcpy (gname, "None");
5810 result = TRUE;
5811 }
5812 }
5813 glength = strlen (gname);
f8b35b24 5814 }
7c80d5ec 5815 }
7c80d5ec 5816 }
5f445726 5817 xfree (buf);
7c80d5ec 5818 }
32cef06e 5819 if (!result)
7c80d5ec 5820 {
32cef06e
EZ
5821 if (!is_windows_9x ())
5822 {
5823 /* We couldn't open the process token, presumably because of
5824 insufficient access rights. Assume this process is run
5825 by the system. */
5826 strcpy (uname, "SYSTEM");
5827 strcpy (gname, "None");
5828 euid = 18; /* SYSTEM */
5829 egid = 513; /* None */
5830 glength = strlen (gname);
5831 ulength = strlen (uname);
5832 }
5833 /* If we are running under Windows 9X, where security calls are
5834 not supported, we assume all processes are run by the current
5835 user. */
5836 else if (GetUserName (uname, &ulength))
5837 {
5838 if (xstrcasecmp ("administrator", uname) == 0)
5839 euid = 0;
5840 else
5841 euid = 123;
5842 egid = euid;
5843 strcpy (gname, "None");
5844 glength = strlen (gname);
5845 ulength = strlen (uname);
5846 }
7c80d5ec 5847 else
32cef06e
EZ
5848 {
5849 euid = 123;
5850 egid = 123;
5851 strcpy (uname, "administrator");
5852 ulength = strlen (uname);
5853 strcpy (gname, "None");
5854 glength = strlen (gname);
5855 }
5856 if (token)
5857 CloseHandle (token);
7c80d5ec 5858 }
7c80d5ec
EZ
5859
5860 attrs = Fcons (Fcons (Qeuid, make_fixnum_or_float (euid)), attrs);
5861 tem = make_unibyte_string (uname, ulength);
5862 attrs = Fcons (Fcons (Quser,
5863 code_convert_string_norecord (tem, Vlocale_coding_system, 0)),
5864 attrs);
5865 attrs = Fcons (Fcons (Qegid, make_fixnum_or_float (egid)), attrs);
5866 tem = make_unibyte_string (gname, glength);
5867 attrs = Fcons (Fcons (Qgroup,
5868 code_convert_string_norecord (tem, Vlocale_coding_system, 0)),
5869 attrs);
5870
5871 if (global_memory_status_ex (&memstex))
235661f6 5872#if __GNUC__ || (defined (_MSC_VER) && _MSC_VER >= 1300)
7c80d5ec 5873 totphys = memstex.ullTotalPhys / 1024.0;
235661f6
EZ
5874#else
5875 /* Visual Studio 6 cannot convert an unsigned __int64 type to
5876 double, so we need to do this for it... */
5877 {
5878 DWORD tot_hi = memstex.ullTotalPhys >> 32;
5879 DWORD tot_md = (memstex.ullTotalPhys & 0x00000000ffffffff) >> 10;
5880 DWORD tot_lo = memstex.ullTotalPhys % 1024;
5881
5882 totphys = tot_hi * 4194304.0 + tot_md + tot_lo / 1024.0;
5883 }
5884#endif /* __GNUC__ || _MSC_VER >= 1300 */
7c80d5ec
EZ
5885 else if (global_memory_status (&memst))
5886 totphys = memst.dwTotalPhys / 1024.0;
5887
5888 if (h_proc
5889 && get_process_memory_info (h_proc, (PROCESS_MEMORY_COUNTERS *)&mem_ex,
5890 sizeof (mem_ex)))
5891 {
cb576b5c 5892 SIZE_T rss = mem_ex.WorkingSetSize / 1024;
7c80d5ec
EZ
5893
5894 attrs = Fcons (Fcons (Qmajflt,
5895 make_fixnum_or_float (mem_ex.PageFaultCount)),
5896 attrs);
5897 attrs = Fcons (Fcons (Qvsize,
5898 make_fixnum_or_float (mem_ex.PrivateUsage / 1024)),
5899 attrs);
5900 attrs = Fcons (Fcons (Qrss, make_fixnum_or_float (rss)), attrs);
5901 if (totphys)
5902 attrs = Fcons (Fcons (Qpmem, make_float (100. * rss / totphys)), attrs);
5903 }
5904 else if (h_proc
5905 && get_process_memory_info (h_proc, &mem, sizeof (mem)))
5906 {
cb576b5c 5907 SIZE_T rss = mem_ex.WorkingSetSize / 1024;
7c80d5ec
EZ
5908
5909 attrs = Fcons (Fcons (Qmajflt,
5910 make_fixnum_or_float (mem.PageFaultCount)),
5911 attrs);
5912 attrs = Fcons (Fcons (Qrss, make_fixnum_or_float (rss)), attrs);
5913 if (totphys)
5914 attrs = Fcons (Fcons (Qpmem, make_float (100. * rss / totphys)), attrs);
5915 }
5916 else if (h_proc
5917 && get_process_working_set_size (h_proc, &minrss, &maxrss))
5918 {
5919 DWORD rss = maxrss / 1024;
5920
5921 attrs = Fcons (Fcons (Qrss, make_fixnum_or_float (maxrss / 1024)), attrs);
5922 if (totphys)
5923 attrs = Fcons (Fcons (Qpmem, make_float (100. * rss / totphys)), attrs);
5924 }
5925
031da700 5926 if (process_times (h_proc, &ctime, &etime, &stime, &utime, &ttime, &pcpu))
7c80d5ec
EZ
5927 {
5928 attrs = Fcons (Fcons (Qutime, utime), attrs);
5929 attrs = Fcons (Fcons (Qstime, stime), attrs);
031da700 5930 attrs = Fcons (Fcons (Qtime, ttime), attrs);
7c80d5ec
EZ
5931 attrs = Fcons (Fcons (Qstart, ctime), attrs);
5932 attrs = Fcons (Fcons (Qetime, etime), attrs);
5933 attrs = Fcons (Fcons (Qpcpu, make_float (pcpu)), attrs);
5934 }
5935
5936 /* FIXME: Retrieve command line by walking the PEB of the process. */
5937
5938 if (h_proc)
5939 CloseHandle (h_proc);
5940 UNGCPRO;
5941 return attrs;
5942}
5943
5944\f
480b0c5b
GV
5945/* Wrappers for winsock functions to map between our file descriptors
5946 and winsock's handles; also set h_errno for convenience.
5947
5948 To allow Emacs to run on systems which don't have winsock support
5949 installed, we dynamically link to winsock on startup if present, and
5950 otherwise provide the minimum necessary functionality
5951 (eg. gethostname). */
5952
5953/* function pointers for relevant socket functions */
5954int (PASCAL *pfn_WSAStartup) (WORD wVersionRequired, LPWSADATA lpWSAData);
5955void (PASCAL *pfn_WSASetLastError) (int iError);
5956int (PASCAL *pfn_WSAGetLastError) (void);
26fb7bc4 5957int (PASCAL *pfn_WSAEventSelect) (SOCKET s, HANDLE hEventObject, long lNetworkEvents);
64570b36
KS
5958HANDLE (PASCAL *pfn_WSACreateEvent) (void);
5959int (PASCAL *pfn_WSACloseEvent) (HANDLE hEvent);
480b0c5b
GV
5960int (PASCAL *pfn_socket) (int af, int type, int protocol);
5961int (PASCAL *pfn_bind) (SOCKET s, const struct sockaddr *addr, int namelen);
5962int (PASCAL *pfn_connect) (SOCKET s, const struct sockaddr *addr, int namelen);
5963int (PASCAL *pfn_ioctlsocket) (SOCKET s, long cmd, u_long *argp);
5964int (PASCAL *pfn_recv) (SOCKET s, char * buf, int len, int flags);
5965int (PASCAL *pfn_send) (SOCKET s, const char * buf, int len, int flags);
5966int (PASCAL *pfn_closesocket) (SOCKET s);
5967int (PASCAL *pfn_shutdown) (SOCKET s, int how);
5968int (PASCAL *pfn_WSACleanup) (void);
5969
5970u_short (PASCAL *pfn_htons) (u_short hostshort);
5971u_short (PASCAL *pfn_ntohs) (u_short netshort);
5972unsigned long (PASCAL *pfn_inet_addr) (const char * cp);
5973int (PASCAL *pfn_gethostname) (char * name, int namelen);
5974struct hostent * (PASCAL *pfn_gethostbyname) (const char * name);
5975struct servent * (PASCAL *pfn_getservbyname) (const char * name, const char * proto);
ecd270eb 5976int (PASCAL *pfn_getpeername) (SOCKET s, struct sockaddr *addr, int * namelen);
962955c5
JR
5977int (PASCAL *pfn_setsockopt) (SOCKET s, int level, int optname,
5978 const char * optval, int optlen);
5979int (PASCAL *pfn_listen) (SOCKET s, int backlog);
5980int (PASCAL *pfn_getsockname) (SOCKET s, struct sockaddr * name,
5981 int * namelen);
5982SOCKET (PASCAL *pfn_accept) (SOCKET s, struct sockaddr * addr, int * addrlen);
5983int (PASCAL *pfn_recvfrom) (SOCKET s, char * buf, int len, int flags,
5984 struct sockaddr * from, int * fromlen);
5985int (PASCAL *pfn_sendto) (SOCKET s, const char * buf, int len, int flags,
5986 const struct sockaddr * to, int tolen);
5987
f1614061
RS
5988/* SetHandleInformation is only needed to make sockets non-inheritable. */
5989BOOL (WINAPI *pfn_SetHandleInformation) (HANDLE object, DWORD mask, DWORD flags);
5990#ifndef HANDLE_FLAG_INHERIT
5991#define HANDLE_FLAG_INHERIT 1
5992#endif
480b0c5b 5993
f249a012
RS
5994HANDLE winsock_lib;
5995static int winsock_inuse;
480b0c5b 5996
f249a012 5997BOOL
480b0c5b
GV
5998term_winsock (void)
5999{
f249a012 6000 if (winsock_lib != NULL && winsock_inuse == 0)
480b0c5b 6001 {
f249a012
RS
6002 /* Not sure what would cause WSAENETDOWN, or even if it can happen
6003 after WSAStartup returns successfully, but it seems reasonable
6004 to allow unloading winsock anyway in that case. */
6005 if (pfn_WSACleanup () == 0 ||
6006 pfn_WSAGetLastError () == WSAENETDOWN)
6007 {
6008 if (FreeLibrary (winsock_lib))
6009 winsock_lib = NULL;
6010 return TRUE;
6011 }
480b0c5b 6012 }
f249a012 6013 return FALSE;
480b0c5b
GV
6014}
6015
f249a012
RS
6016BOOL
6017init_winsock (int load_now)
480b0c5b
GV
6018{
6019 WSADATA winsockData;
6020
f249a012
RS
6021 if (winsock_lib != NULL)
6022 return TRUE;
f1614061 6023
f1614061
RS
6024 pfn_SetHandleInformation
6025 = (void *) GetProcAddress (GetModuleHandle ("kernel32.dll"),
6026 "SetHandleInformation");
6027
64570b36 6028 winsock_lib = LoadLibrary ("Ws2_32.dll");
480b0c5b
GV
6029
6030 if (winsock_lib != NULL)
6031 {
6032 /* dynamically link to socket functions */
6033
6034#define LOAD_PROC(fn) \
6035 if ((pfn_##fn = (void *) GetProcAddress (winsock_lib, #fn)) == NULL) \
6036 goto fail;
6037
ed3751c8
JB
6038 LOAD_PROC (WSAStartup);
6039 LOAD_PROC (WSASetLastError);
6040 LOAD_PROC (WSAGetLastError);
6041 LOAD_PROC (WSAEventSelect);
6042 LOAD_PROC (WSACreateEvent);
6043 LOAD_PROC (WSACloseEvent);
6044 LOAD_PROC (socket);
6045 LOAD_PROC (bind);
6046 LOAD_PROC (connect);
6047 LOAD_PROC (ioctlsocket);
6048 LOAD_PROC (recv);
6049 LOAD_PROC (send);
6050 LOAD_PROC (closesocket);
6051 LOAD_PROC (shutdown);
6052 LOAD_PROC (htons);
6053 LOAD_PROC (ntohs);
6054 LOAD_PROC (inet_addr);
6055 LOAD_PROC (gethostname);
6056 LOAD_PROC (gethostbyname);
6057 LOAD_PROC (getservbyname);
6058 LOAD_PROC (getpeername);
6059 LOAD_PROC (WSACleanup);
6060 LOAD_PROC (setsockopt);
6061 LOAD_PROC (listen);
6062 LOAD_PROC (getsockname);
6063 LOAD_PROC (accept);
6064 LOAD_PROC (recvfrom);
6065 LOAD_PROC (sendto);
f249a012
RS
6066#undef LOAD_PROC
6067
480b0c5b
GV
6068 /* specify version 1.1 of winsock */
6069 if (pfn_WSAStartup (0x101, &winsockData) == 0)
6070 {
f249a012
RS
6071 if (winsockData.wVersion != 0x101)
6072 goto fail;
6073
6074 if (!load_now)
6075 {
6076 /* Report that winsock exists and is usable, but leave
6077 socket functions disabled. I am assuming that calling
6078 WSAStartup does not require any network interaction,
6079 and in particular does not cause or require a dial-up
6080 connection to be established. */
6081
6082 pfn_WSACleanup ();
6083 FreeLibrary (winsock_lib);
6084 winsock_lib = NULL;
6085 }
6086 winsock_inuse = 0;
6087 return TRUE;
480b0c5b
GV
6088 }
6089
6090 fail:
6091 FreeLibrary (winsock_lib);
f249a012 6092 winsock_lib = NULL;
480b0c5b 6093 }
f249a012
RS
6094
6095 return FALSE;
480b0c5b
GV
6096}
6097
6098
6099int h_errno = 0;
6100
f277993b
EZ
6101/* Function to map winsock error codes to errno codes for those errno
6102 code defined in errno.h (errno values not defined by errno.h are
6103 already in nt/inc/sys/socket.h). */
9bfb11f9 6104static void
b56ceb92 6105set_errno (void)
480b0c5b 6106{
f277993b
EZ
6107 int wsa_err;
6108
6109 h_errno = 0;
f249a012 6110 if (winsock_lib == NULL)
f277993b 6111 wsa_err = EINVAL;
480b0c5b 6112 else
f277993b 6113 wsa_err = pfn_WSAGetLastError ();
480b0c5b 6114
f277993b 6115 switch (wsa_err)
480b0c5b 6116 {
f277993b
EZ
6117 case WSAEACCES: errno = EACCES; break;
6118 case WSAEBADF: errno = EBADF; break;
6119 case WSAEFAULT: errno = EFAULT; break;
6120 case WSAEINTR: errno = EINTR; break;
6121 case WSAEINVAL: errno = EINVAL; break;
6122 case WSAEMFILE: errno = EMFILE; break;
6123 case WSAENAMETOOLONG: errno = ENAMETOOLONG; break;
6124 case WSAENOTEMPTY: errno = ENOTEMPTY; break;
6125 default: errno = wsa_err; break;
480b0c5b 6126 }
480b0c5b
GV
6127}
6128
9bfb11f9 6129static void
b56ceb92 6130check_errno (void)
480b0c5b 6131{
f277993b
EZ
6132 h_errno = 0;
6133 if (winsock_lib != NULL)
480b0c5b
GV
6134 pfn_WSASetLastError (0);
6135}
6136
d8fcc1b9
AI
6137/* Extend strerror to handle the winsock-specific error codes. */
6138struct {
6139 int errnum;
6140 char * msg;
6141} _wsa_errlist[] = {
1db5b1ad
JB
6142 {WSAEINTR , "Interrupted function call"},
6143 {WSAEBADF , "Bad file descriptor"},
6144 {WSAEACCES , "Permission denied"},
6145 {WSAEFAULT , "Bad address"},
6146 {WSAEINVAL , "Invalid argument"},
6147 {WSAEMFILE , "Too many open files"},
6148
6149 {WSAEWOULDBLOCK , "Resource temporarily unavailable"},
6150 {WSAEINPROGRESS , "Operation now in progress"},
6151 {WSAEALREADY , "Operation already in progress"},
6152 {WSAENOTSOCK , "Socket operation on non-socket"},
6153 {WSAEDESTADDRREQ , "Destination address required"},
6154 {WSAEMSGSIZE , "Message too long"},
6155 {WSAEPROTOTYPE , "Protocol wrong type for socket"},
6156 {WSAENOPROTOOPT , "Bad protocol option"},
6157 {WSAEPROTONOSUPPORT , "Protocol not supported"},
6158 {WSAESOCKTNOSUPPORT , "Socket type not supported"},
6159 {WSAEOPNOTSUPP , "Operation not supported"},
6160 {WSAEPFNOSUPPORT , "Protocol family not supported"},
6161 {WSAEAFNOSUPPORT , "Address family not supported by protocol family"},
6162 {WSAEADDRINUSE , "Address already in use"},
6163 {WSAEADDRNOTAVAIL , "Cannot assign requested address"},
6164 {WSAENETDOWN , "Network is down"},
6165 {WSAENETUNREACH , "Network is unreachable"},
6166 {WSAENETRESET , "Network dropped connection on reset"},
6167 {WSAECONNABORTED , "Software caused connection abort"},
6168 {WSAECONNRESET , "Connection reset by peer"},
6169 {WSAENOBUFS , "No buffer space available"},
6170 {WSAEISCONN , "Socket is already connected"},
6171 {WSAENOTCONN , "Socket is not connected"},
6172 {WSAESHUTDOWN , "Cannot send after socket shutdown"},
6173 {WSAETOOMANYREFS , "Too many references"}, /* not sure */
6174 {WSAETIMEDOUT , "Connection timed out"},
6175 {WSAECONNREFUSED , "Connection refused"},
6176 {WSAELOOP , "Network loop"}, /* not sure */
6177 {WSAENAMETOOLONG , "Name is too long"},
6178 {WSAEHOSTDOWN , "Host is down"},
6179 {WSAEHOSTUNREACH , "No route to host"},
6180 {WSAENOTEMPTY , "Buffer not empty"}, /* not sure */
6181 {WSAEPROCLIM , "Too many processes"},
6182 {WSAEUSERS , "Too many users"}, /* not sure */
6183 {WSAEDQUOT , "Double quote in host name"}, /* really not sure */
6184 {WSAESTALE , "Data is stale"}, /* not sure */
6185 {WSAEREMOTE , "Remote error"}, /* not sure */
6186
6187 {WSASYSNOTREADY , "Network subsystem is unavailable"},
6188 {WSAVERNOTSUPPORTED , "WINSOCK.DLL version out of range"},
6189 {WSANOTINITIALISED , "Winsock not initialized successfully"},
6190 {WSAEDISCON , "Graceful shutdown in progress"},
d8fcc1b9 6191#ifdef WSAENOMORE
1db5b1ad
JB
6192 {WSAENOMORE , "No more operations allowed"}, /* not sure */
6193 {WSAECANCELLED , "Operation cancelled"}, /* not sure */
6194 {WSAEINVALIDPROCTABLE , "Invalid procedure table from service provider"},
6195 {WSAEINVALIDPROVIDER , "Invalid service provider version number"},
6196 {WSAEPROVIDERFAILEDINIT , "Unable to initialize a service provider"},
6197 {WSASYSCALLFAILURE , "System call failure"},
6198 {WSASERVICE_NOT_FOUND , "Service not found"}, /* not sure */
6199 {WSATYPE_NOT_FOUND , "Class type not found"},
6200 {WSA_E_NO_MORE , "No more resources available"}, /* really not sure */
6201 {WSA_E_CANCELLED , "Operation already cancelled"}, /* really not sure */
6202 {WSAEREFUSED , "Operation refused"}, /* not sure */
d8fcc1b9 6203#endif
177c0ea7 6204
1db5b1ad
JB
6205 {WSAHOST_NOT_FOUND , "Host not found"},
6206 {WSATRY_AGAIN , "Authoritative host not found during name lookup"},
6207 {WSANO_RECOVERY , "Non-recoverable error during name lookup"},
6208 {WSANO_DATA , "Valid name, no data record of requested type"},
d8fcc1b9 6209
1db5b1ad 6210 {-1, NULL}
d8fcc1b9
AI
6211};
6212
6213char *
ed3751c8 6214sys_strerror (int error_no)
d8fcc1b9
AI
6215{
6216 int i;
6217 static char unknown_msg[40];
6218
a302c7ae
AI
6219 if (error_no >= 0 && error_no < sys_nerr)
6220 return sys_errlist[error_no];
d8fcc1b9
AI
6221
6222 for (i = 0; _wsa_errlist[i].errnum >= 0; i++)
6223 if (_wsa_errlist[i].errnum == error_no)
6224 return _wsa_errlist[i].msg;
6225
ed3751c8 6226 sprintf (unknown_msg, "Unidentified error: %d", error_no);
d8fcc1b9
AI
6227 return unknown_msg;
6228}
6229
480b0c5b
GV
6230/* [andrewi 3-May-96] I've had conflicting results using both methods,
6231 but I believe the method of keeping the socket handle separate (and
6232 insuring it is not inheritable) is the correct one. */
6233
480b0c5b 6234#define SOCK_HANDLE(fd) ((SOCKET) fd_info[fd].hnd)
480b0c5b 6235
bedf4aab 6236static int socket_to_fd (SOCKET s);
962955c5 6237
480b0c5b 6238int
ed3751c8 6239sys_socket (int af, int type, int protocol)
480b0c5b 6240{
962955c5 6241 SOCKET s;
480b0c5b 6242
f249a012 6243 if (winsock_lib == NULL)
480b0c5b 6244 {
f277993b 6245 errno = ENETDOWN;
480b0c5b
GV
6246 return INVALID_SOCKET;
6247 }
6248
6249 check_errno ();
6250
6251 /* call the real socket function */
962955c5 6252 s = pfn_socket (af, type, protocol);
177c0ea7 6253
480b0c5b 6254 if (s != INVALID_SOCKET)
f277993b 6255 return socket_to_fd (s);
480b0c5b 6256
962955c5
JR
6257 set_errno ();
6258 return -1;
6259}
6260
6261/* Convert a SOCKET to a file descriptor. */
bedf4aab 6262static int
962955c5
JR
6263socket_to_fd (SOCKET s)
6264{
6265 int fd;
6266 child_process * cp;
6267
6268 /* Although under NT 3.5 _open_osfhandle will accept a socket
6269 handle, if opened with SO_OPENTYPE == SO_SYNCHRONOUS_NONALERT,
6270 that does not work under NT 3.1. However, we can get the same
6271 effect by using a backdoor function to replace an existing
6272 descriptor handle with the one we want. */
6273
6274 /* allocate a file descriptor (with appropriate flags) */
6275 fd = _open ("NUL:", _O_RDWR);
6276 if (fd >= 0)
6277 {
962955c5
JR
6278 /* Make a non-inheritable copy of the socket handle. Note
6279 that it is possible that sockets aren't actually kernel
6280 handles, which appears to be the case on Windows 9x when
6281 the MS Proxy winsock client is installed. */
6282 {
6283 /* Apparently there is a bug in NT 3.51 with some service
6284 packs, which prevents using DuplicateHandle to make a
6285 socket handle non-inheritable (causes WSACleanup to
6286 hang). The work-around is to use SetHandleInformation
6287 instead if it is available and implemented. */
6288 if (pfn_SetHandleInformation)
480b0c5b 6289 {
962955c5
JR
6290 pfn_SetHandleInformation ((HANDLE) s, HANDLE_FLAG_INHERIT, 0);
6291 }
6292 else
6293 {
6294 HANDLE parent = GetCurrentProcess ();
6295 HANDLE new_s = INVALID_HANDLE_VALUE;
6296
6297 if (DuplicateHandle (parent,
6298 (HANDLE) s,
6299 parent,
6300 &new_s,
6301 0,
6302 FALSE,
6303 DUPLICATE_SAME_ACCESS))
f1614061 6304 {
962955c5
JR
6305 /* It is possible that DuplicateHandle succeeds even
6306 though the socket wasn't really a kernel handle,
6307 because a real handle has the same value. So
6308 test whether the new handle really is a socket. */
6309 long nonblocking = 0;
6310 if (pfn_ioctlsocket ((SOCKET) new_s, FIONBIO, &nonblocking) == 0)
ca149beb 6311 {
962955c5
JR
6312 pfn_closesocket (s);
6313 s = (SOCKET) new_s;
6314 }
6315 else
6316 {
6317 CloseHandle (new_s);
6318 }
177c0ea7 6319 }
480b0c5b 6320 }
962955c5 6321 }
bcf7fe2a 6322 eassert (fd < MAXDESC);
962955c5 6323 fd_info[fd].hnd = (HANDLE) s;
480b0c5b 6324
962955c5
JR
6325 /* set our own internal flags */
6326 fd_info[fd].flags = FILE_SOCKET | FILE_BINARY | FILE_READ | FILE_WRITE;
480b0c5b 6327
962955c5
JR
6328 cp = new_child ();
6329 if (cp)
6330 {
6331 cp->fd = fd;
6332 cp->status = STATUS_READ_ACKNOWLEDGED;
480b0c5b 6333
962955c5
JR
6334 /* attach child_process to fd_info */
6335 if (fd_info[ fd ].cp != NULL)
6336 {
6337 DebPrint (("sys_socket: fd_info[%d] apparently in use!\n", fd));
1088b922 6338 emacs_abort ();
480b0c5b
GV
6339 }
6340
962955c5
JR
6341 fd_info[ fd ].cp = cp;
6342
6343 /* success! */
6344 winsock_inuse++; /* count open sockets */
6345 return fd;
480b0c5b 6346 }
480b0c5b 6347
962955c5
JR
6348 /* clean up */
6349 _close (fd);
6350 }
f277993b 6351 else
962955c5 6352 pfn_closesocket (s);
f277993b 6353 errno = EMFILE;
480b0c5b
GV
6354 return -1;
6355}
6356
480b0c5b
GV
6357int
6358sys_bind (int s, const struct sockaddr * addr, int namelen)
6359{
f249a012 6360 if (winsock_lib == NULL)
480b0c5b 6361 {
f277993b 6362 errno = ENOTSOCK;
480b0c5b
GV
6363 return SOCKET_ERROR;
6364 }
6365
6366 check_errno ();
6367 if (fd_info[s].flags & FILE_SOCKET)
6368 {
6369 int rc = pfn_bind (SOCK_HANDLE (s), addr, namelen);
6370 if (rc == SOCKET_ERROR)
6371 set_errno ();
6372 return rc;
6373 }
f277993b 6374 errno = ENOTSOCK;
480b0c5b
GV
6375 return SOCKET_ERROR;
6376}
6377
480b0c5b
GV
6378int
6379sys_connect (int s, const struct sockaddr * name, int namelen)
6380{
f249a012 6381 if (winsock_lib == NULL)
480b0c5b 6382 {
f277993b 6383 errno = ENOTSOCK;
480b0c5b
GV
6384 return SOCKET_ERROR;
6385 }
6386
6387 check_errno ();
6388 if (fd_info[s].flags & FILE_SOCKET)
6389 {
6390 int rc = pfn_connect (SOCK_HANDLE (s), name, namelen);
6391 if (rc == SOCKET_ERROR)
6392 set_errno ();
6393 return rc;
6394 }
f277993b 6395 errno = ENOTSOCK;
480b0c5b
GV
6396 return SOCKET_ERROR;
6397}
6398
6399u_short
6400sys_htons (u_short hostshort)
6401{
f249a012 6402 return (winsock_lib != NULL) ?
480b0c5b
GV
6403 pfn_htons (hostshort) : hostshort;
6404}
6405
6406u_short
6407sys_ntohs (u_short netshort)
6408{
f249a012 6409 return (winsock_lib != NULL) ?
480b0c5b
GV
6410 pfn_ntohs (netshort) : netshort;
6411}
6412
6413unsigned long
6414sys_inet_addr (const char * cp)
6415{
f249a012 6416 return (winsock_lib != NULL) ?
480b0c5b
GV
6417 pfn_inet_addr (cp) : INADDR_NONE;
6418}
6419
6420int
6421sys_gethostname (char * name, int namelen)
6422{
f249a012 6423 if (winsock_lib != NULL)
f277993b
EZ
6424 {
6425 int retval;
6426
6427 check_errno ();
6428 retval = pfn_gethostname (name, namelen);
6429 if (retval == SOCKET_ERROR)
6430 set_errno ();
6431 return retval;
6432 }
480b0c5b
GV
6433
6434 if (namelen > MAX_COMPUTERNAME_LENGTH)
a302c7ae 6435 return !GetComputerName (name, (DWORD *)&namelen);
480b0c5b 6436
f277993b 6437 errno = EFAULT;
480b0c5b
GV
6438 return SOCKET_ERROR;
6439}
6440
6441struct hostent *
ed3751c8 6442sys_gethostbyname (const char * name)
480b0c5b
GV
6443{
6444 struct hostent * host;
f277993b 6445 int h_err = h_errno;
480b0c5b 6446
f249a012 6447 if (winsock_lib == NULL)
480b0c5b 6448 {
f277993b
EZ
6449 h_errno = NO_RECOVERY;
6450 errno = ENETDOWN;
480b0c5b
GV
6451 return NULL;
6452 }
6453
6454 check_errno ();
6455 host = pfn_gethostbyname (name);
6456 if (!host)
f277993b
EZ
6457 {
6458 set_errno ();
6459 h_errno = errno;
6460 }
6461 else
6462 h_errno = h_err;
480b0c5b
GV
6463 return host;
6464}
6465
6466struct servent *
ed3751c8 6467sys_getservbyname (const char * name, const char * proto)
480b0c5b
GV
6468{
6469 struct servent * serv;
6470
f249a012 6471 if (winsock_lib == NULL)
480b0c5b 6472 {
f277993b 6473 errno = ENETDOWN;
480b0c5b
GV
6474 return NULL;
6475 }
6476
6477 check_errno ();
6478 serv = pfn_getservbyname (name, proto);
6479 if (!serv)
6480 set_errno ();
6481 return serv;
6482}
6483
ecd270eb
JR
6484int
6485sys_getpeername (int s, struct sockaddr *addr, int * namelen)
6486{
6487 if (winsock_lib == NULL)
6488 {
f277993b 6489 errno = ENETDOWN;
ecd270eb
JR
6490 return SOCKET_ERROR;
6491 }
6492
6493 check_errno ();
6494 if (fd_info[s].flags & FILE_SOCKET)
6495 {
6496 int rc = pfn_getpeername (SOCK_HANDLE (s), addr, namelen);
6497 if (rc == SOCKET_ERROR)
6498 set_errno ();
6499 return rc;
6500 }
f277993b 6501 errno = ENOTSOCK;
ecd270eb
JR
6502 return SOCKET_ERROR;
6503}
6504
380961a6
GV
6505int
6506sys_shutdown (int s, int how)
6507{
380961a6
GV
6508 if (winsock_lib == NULL)
6509 {
f277993b 6510 errno = ENETDOWN;
380961a6
GV
6511 return SOCKET_ERROR;
6512 }
6513
6514 check_errno ();
6515 if (fd_info[s].flags & FILE_SOCKET)
6516 {
6517 int rc = pfn_shutdown (SOCK_HANDLE (s), how);
962955c5
JR
6518 if (rc == SOCKET_ERROR)
6519 set_errno ();
6520 return rc;
6521 }
f277993b 6522 errno = ENOTSOCK;
962955c5
JR
6523 return SOCKET_ERROR;
6524}
6525
6526int
a5a389bb 6527sys_setsockopt (int s, int level, int optname, const void * optval, int optlen)
962955c5
JR
6528{
6529 if (winsock_lib == NULL)
6530 {
f277993b 6531 errno = ENETDOWN;
962955c5
JR
6532 return SOCKET_ERROR;
6533 }
6534
6535 check_errno ();
6536 if (fd_info[s].flags & FILE_SOCKET)
6537 {
6538 int rc = pfn_setsockopt (SOCK_HANDLE (s), level, optname,
a5a389bb 6539 (const char *)optval, optlen);
962955c5
JR
6540 if (rc == SOCKET_ERROR)
6541 set_errno ();
6542 return rc;
6543 }
f277993b 6544 errno = ENOTSOCK;
177c0ea7 6545 return SOCKET_ERROR;
962955c5
JR
6546}
6547
6548int
6549sys_listen (int s, int backlog)
6550{
6551 if (winsock_lib == NULL)
6552 {
f277993b 6553 errno = ENETDOWN;
962955c5
JR
6554 return SOCKET_ERROR;
6555 }
6556
6557 check_errno ();
6558 if (fd_info[s].flags & FILE_SOCKET)
6559 {
6560 int rc = pfn_listen (SOCK_HANDLE (s), backlog);
6561 if (rc == SOCKET_ERROR)
6562 set_errno ();
26fb7bc4 6563 else
64570b36 6564 fd_info[s].flags |= FILE_LISTEN;
962955c5
JR
6565 return rc;
6566 }
f277993b 6567 errno = ENOTSOCK;
177c0ea7 6568 return SOCKET_ERROR;
962955c5
JR
6569}
6570
6571int
6572sys_getsockname (int s, struct sockaddr * name, int * namelen)
6573{
6574 if (winsock_lib == NULL)
6575 {
f277993b 6576 errno = ENETDOWN;
962955c5
JR
6577 return SOCKET_ERROR;
6578 }
6579
6580 check_errno ();
6581 if (fd_info[s].flags & FILE_SOCKET)
6582 {
6583 int rc = pfn_getsockname (SOCK_HANDLE (s), name, namelen);
6584 if (rc == SOCKET_ERROR)
6585 set_errno ();
6586 return rc;
6587 }
f277993b 6588 errno = ENOTSOCK;
177c0ea7 6589 return SOCKET_ERROR;
962955c5
JR
6590}
6591
6592int
6593sys_accept (int s, struct sockaddr * addr, int * addrlen)
6594{
6595 if (winsock_lib == NULL)
6596 {
f277993b 6597 errno = ENETDOWN;
962955c5
JR
6598 return -1;
6599 }
6600
6601 check_errno ();
26fb7bc4 6602 if (fd_info[s].flags & FILE_LISTEN)
962955c5 6603 {
a0ad1860 6604 SOCKET t = pfn_accept (SOCK_HANDLE (s), addr, addrlen);
64570b36
KS
6605 int fd = -1;
6606 if (t == INVALID_SOCKET)
6607 set_errno ();
6608 else
f277993b 6609 fd = socket_to_fd (t);
962955c5 6610
bcf7fe2a
EZ
6611 if (fd >= 0)
6612 {
6613 fd_info[s].cp->status = STATUS_READ_ACKNOWLEDGED;
6614 ResetEvent (fd_info[s].cp->char_avail);
6615 }
64570b36 6616 return fd;
962955c5 6617 }
f277993b 6618 errno = ENOTSOCK;
962955c5
JR
6619 return -1;
6620}
6621
6622int
6623sys_recvfrom (int s, char * buf, int len, int flags,
b56ceb92 6624 struct sockaddr * from, int * fromlen)
962955c5
JR
6625{
6626 if (winsock_lib == NULL)
6627 {
f277993b 6628 errno = ENETDOWN;
962955c5
JR
6629 return SOCKET_ERROR;
6630 }
6631
6632 check_errno ();
6633 if (fd_info[s].flags & FILE_SOCKET)
6634 {
6635 int rc = pfn_recvfrom (SOCK_HANDLE (s), buf, len, flags, from, fromlen);
6636 if (rc == SOCKET_ERROR)
6637 set_errno ();
6638 return rc;
6639 }
f277993b 6640 errno = ENOTSOCK;
962955c5
JR
6641 return SOCKET_ERROR;
6642}
6643
6644int
6645sys_sendto (int s, const char * buf, int len, int flags,
6646 const struct sockaddr * to, int tolen)
6647{
6648 if (winsock_lib == NULL)
6649 {
f277993b 6650 errno = ENETDOWN;
962955c5
JR
6651 return SOCKET_ERROR;
6652 }
6653
6654 check_errno ();
6655 if (fd_info[s].flags & FILE_SOCKET)
6656 {
6657 int rc = pfn_sendto (SOCK_HANDLE (s), buf, len, flags, to, tolen);
380961a6
GV
6658 if (rc == SOCKET_ERROR)
6659 set_errno ();
6660 return rc;
6661 }
f277993b 6662 errno = ENOTSOCK;
380961a6
GV
6663 return SOCKET_ERROR;
6664}
6665
ecd270eb
JR
6666/* Windows does not have an fcntl function. Provide an implementation
6667 solely for making sockets non-blocking. */
6668int
6669fcntl (int s, int cmd, int options)
6670{
6671 if (winsock_lib == NULL)
6672 {
f277993b 6673 errno = ENETDOWN;
ecd270eb
JR
6674 return -1;
6675 }
6676
6677 check_errno ();
6678 if (fd_info[s].flags & FILE_SOCKET)
6679 {
49cdacda 6680 if (cmd == F_SETFL && options == O_NONBLOCK)
ecd270eb
JR
6681 {
6682 unsigned long nblock = 1;
6683 int rc = pfn_ioctlsocket (SOCK_HANDLE (s), FIONBIO, &nblock);
6684 if (rc == SOCKET_ERROR)
9d4f32e8 6685 set_errno ();
ecd270eb
JR
6686 /* Keep track of the fact that we set this to non-blocking. */
6687 fd_info[s].flags |= FILE_NDELAY;
6688 return rc;
6689 }
6690 else
6691 {
f277993b 6692 errno = EINVAL;
ecd270eb
JR
6693 return SOCKET_ERROR;
6694 }
6695 }
f277993b 6696 errno = ENOTSOCK;
ecd270eb
JR
6697 return SOCKET_ERROR;
6698}
6699
480b0c5b
GV
6700
6701/* Shadow main io functions: we need to handle pipes and sockets more
6702 intelligently, and implement non-blocking mode as well. */
6703
6704int
6705sys_close (int fd)
6706{
6707 int rc;
6708
7559f399 6709 if (fd < 0)
480b0c5b
GV
6710 {
6711 errno = EBADF;
6712 return -1;
6713 }
6714
7559f399 6715 if (fd < MAXDESC && fd_info[fd].cp)
480b0c5b
GV
6716 {
6717 child_process * cp = fd_info[fd].cp;
6718
6719 fd_info[fd].cp = NULL;
6720
6721 if (CHILD_ACTIVE (cp))
6722 {
6723 /* if last descriptor to active child_process then cleanup */
6724 int i;
6725 for (i = 0; i < MAXDESC; i++)
6726 {
6727 if (i == fd)
6728 continue;
6729 if (fd_info[i].cp == cp)
6730 break;
6731 }
6732 if (i == MAXDESC)
6733 {
480b0c5b
GV
6734 if (fd_info[fd].flags & FILE_SOCKET)
6735 {
1088b922 6736 if (winsock_lib == NULL) emacs_abort ();
480b0c5b
GV
6737
6738 pfn_shutdown (SOCK_HANDLE (fd), 2);
6739 rc = pfn_closesocket (SOCK_HANDLE (fd));
7d701334 6740
f249a012 6741 winsock_inuse--; /* count open sockets */
480b0c5b 6742 }
299614f3
EZ
6743 /* If the process handle is NULL, it's either a socket
6744 or serial connection, or a subprocess that was
6745 already reaped by reap_subprocess, but whose
6746 resources were not yet freed, because its output was
6747 not fully read yet by the time it was reaped. (This
6748 usually happens with async subprocesses whose output
6749 is being read by Emacs.) Otherwise, this process was
6750 not reaped yet, so we set its FD to a negative value
6751 to make sure sys_select will eventually get to
6752 calling the SIGCHLD handler for it, which will then
6753 invoke waitpid and reap_subprocess. */
6754 if (cp->procinfo.hProcess == NULL)
6755 delete_child (cp);
6756 else
6757 cp->fd = -1;
480b0c5b
GV
6758 }
6759 }
6760 }
6761
224f4ec1
EZ
6762 if (fd >= 0 && fd < MAXDESC)
6763 fd_info[fd].flags = 0;
6764
480b0c5b 6765 /* Note that sockets do not need special treatment here (at least on
e9e23e23 6766 NT and Windows 95 using the standard tcp/ip stacks) - it appears that
480b0c5b
GV
6767 closesocket is equivalent to CloseHandle, which is to be expected
6768 because socket handles are fully fledged kernel handles. */
6769 rc = _close (fd);
6770
480b0c5b
GV
6771 return rc;
6772}
6773
6774int
6775sys_dup (int fd)
6776{
6777 int new_fd;
6778
6779 new_fd = _dup (fd);
7559f399 6780 if (new_fd >= 0 && new_fd < MAXDESC)
480b0c5b
GV
6781 {
6782 /* duplicate our internal info as well */
6783 fd_info[new_fd] = fd_info[fd];
6784 }
6785 return new_fd;
6786}
6787
480b0c5b
GV
6788int
6789sys_dup2 (int src, int dst)
6790{
6791 int rc;
6792
6793 if (dst < 0 || dst >= MAXDESC)
6794 {
6795 errno = EBADF;
6796 return -1;
6797 }
6798
6799 /* make sure we close the destination first if it's a pipe or socket */
6800 if (src != dst && fd_info[dst].flags != 0)
6801 sys_close (dst);
177c0ea7 6802
480b0c5b
GV
6803 rc = _dup2 (src, dst);
6804 if (rc == 0)
6805 {
6806 /* duplicate our internal info as well */
6807 fd_info[dst] = fd_info[src];
6808 }
6809 return rc;
6810}
6811
480b0c5b
GV
6812/* Unix pipe() has only one arg */
6813int
6814sys_pipe (int * phandles)
6815{
6816 int rc;
6817 unsigned flags;
480b0c5b 6818
76b3903d
GV
6819 /* make pipe handles non-inheritable; when we spawn a child, we
6820 replace the relevant handle with an inheritable one. Also put
6821 pipes into binary mode; we will do text mode translation ourselves
6822 if required. */
6823 rc = _pipe (phandles, 0, _O_NOINHERIT | _O_BINARY);
480b0c5b
GV
6824
6825 if (rc == 0)
6826 {
cb72110d
JR
6827 /* Protect against overflow, since Windows can open more handles than
6828 our fd_info array has room for. */
6829 if (phandles[0] >= MAXDESC || phandles[1] >= MAXDESC)
6830 {
6831 _close (phandles[0]);
6832 _close (phandles[1]);
6e432f0c 6833 errno = EMFILE;
cb72110d
JR
6834 rc = -1;
6835 }
6836 else
6837 {
6838 flags = FILE_PIPE | FILE_READ | FILE_BINARY;
6839 fd_info[phandles[0]].flags = flags;
480b0c5b 6840
cb72110d
JR
6841 flags = FILE_PIPE | FILE_WRITE | FILE_BINARY;
6842 fd_info[phandles[1]].flags = flags;
6843 }
480b0c5b
GV
6844 }
6845
6846 return rc;
6847}
6848
6849/* Function to do blocking read of one byte, needed to implement
d3d14b40
EZ
6850 select. It is only allowed on communication ports, sockets, or
6851 pipes. */
480b0c5b
GV
6852int
6853_sys_read_ahead (int fd)
6854{
6855 child_process * cp;
6856 int rc;
6857
6858 if (fd < 0 || fd >= MAXDESC)
6859 return STATUS_READ_ERROR;
6860
6861 cp = fd_info[fd].cp;
6862
6863 if (cp == NULL || cp->fd != fd || cp->status != STATUS_READ_READY)
6864 return STATUS_READ_ERROR;
6865
d888760c 6866 if ((fd_info[fd].flags & (FILE_PIPE | FILE_SERIAL | FILE_SOCKET)) == 0
480b0c5b
GV
6867 || (fd_info[fd].flags & FILE_READ) == 0)
6868 {
d888760c 6869 DebPrint (("_sys_read_ahead: internal error: fd %d is not a pipe, serial port, or socket!\n", fd));
1088b922 6870 emacs_abort ();
480b0c5b 6871 }
177c0ea7 6872
480b0c5b 6873 cp->status = STATUS_READ_IN_PROGRESS;
177c0ea7 6874
480b0c5b 6875 if (fd_info[fd].flags & FILE_PIPE)
f7554349 6876 {
f7554349
KH
6877 rc = _read (fd, &cp->chr, sizeof (char));
6878
6879 /* Give subprocess time to buffer some more output for us before
e9e23e23 6880 reporting that input is available; we need this because Windows 95
f7554349
KH
6881 connects DOS programs to pipes by making the pipe appear to be
6882 the normal console stdout - as a result most DOS programs will
d888760c 6883 write to stdout without buffering, ie. one character at a
fbd6baed 6884 time. Even some W32 programs do this - "dir" in a command
f7554349
KH
6885 shell on NT is very slow if we don't do this. */
6886 if (rc > 0)
6887 {
78806724 6888 int wait = w32_pipe_read_delay;
f7554349
KH
6889
6890 if (wait > 0)
6891 Sleep (wait);
6892 else if (wait < 0)
6893 while (++wait <= 0)
6894 /* Yield remainder of our time slice, effectively giving a
6895 temporary priority boost to the child process. */
6896 Sleep (0);
6897 }
6898 }
d888760c
GM
6899 else if (fd_info[fd].flags & FILE_SERIAL)
6900 {
6901 HANDLE hnd = fd_info[fd].hnd;
6902 OVERLAPPED *ovl = &fd_info[fd].cp->ovl_read;
6903 COMMTIMEOUTS ct;
6904
6905 /* Configure timeouts for blocking read. */
6906 if (!GetCommTimeouts (hnd, &ct))
6e432f0c
EZ
6907 {
6908 cp->status = STATUS_READ_ERROR;
6909 return STATUS_READ_ERROR;
6910 }
d888760c
GM
6911 ct.ReadIntervalTimeout = 0;
6912 ct.ReadTotalTimeoutMultiplier = 0;
6913 ct.ReadTotalTimeoutConstant = 0;
6914 if (!SetCommTimeouts (hnd, &ct))
6e432f0c
EZ
6915 {
6916 cp->status = STATUS_READ_ERROR;
6917 return STATUS_READ_ERROR;
6918 }
d888760c
GM
6919
6920 if (!ReadFile (hnd, &cp->chr, sizeof (char), (DWORD*) &rc, ovl))
6921 {
6922 if (GetLastError () != ERROR_IO_PENDING)
6e432f0c
EZ
6923 {
6924 cp->status = STATUS_READ_ERROR;
6925 return STATUS_READ_ERROR;
6926 }
d888760c 6927 if (!GetOverlappedResult (hnd, ovl, (DWORD*) &rc, TRUE))
6e432f0c
EZ
6928 {
6929 cp->status = STATUS_READ_ERROR;
6930 return STATUS_READ_ERROR;
6931 }
d888760c
GM
6932 }
6933 }
480b0c5b 6934 else if (fd_info[fd].flags & FILE_SOCKET)
ecd270eb
JR
6935 {
6936 unsigned long nblock = 0;
6937 /* We always want this to block, so temporarily disable NDELAY. */
6938 if (fd_info[fd].flags & FILE_NDELAY)
6939 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
6940
6941 rc = pfn_recv (SOCK_HANDLE (fd), &cp->chr, sizeof (char), 0);
6942
6943 if (fd_info[fd].flags & FILE_NDELAY)
6944 {
6945 nblock = 1;
6946 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
6947 }
6948 }
177c0ea7 6949
480b0c5b
GV
6950 if (rc == sizeof (char))
6951 cp->status = STATUS_READ_SUCCEEDED;
6952 else
6953 cp->status = STATUS_READ_FAILED;
6954
6955 return cp->status;
6956}
6957
9bfb11f9
KS
6958int
6959_sys_wait_accept (int fd)
64570b36
KS
6960{
6961 HANDLE hEv;
6962 child_process * cp;
6963 int rc;
6964
6965 if (fd < 0 || fd >= MAXDESC)
6966 return STATUS_READ_ERROR;
6967
6968 cp = fd_info[fd].cp;
6969
6970 if (cp == NULL || cp->fd != fd || cp->status != STATUS_READ_READY)
6971 return STATUS_READ_ERROR;
6972
6973 cp->status = STATUS_READ_FAILED;
6974
6975 hEv = pfn_WSACreateEvent ();
6976 rc = pfn_WSAEventSelect (SOCK_HANDLE (fd), hEv, FD_ACCEPT);
6977 if (rc != SOCKET_ERROR)
6978 {
6979 rc = WaitForSingleObject (hEv, INFINITE);
6980 pfn_WSAEventSelect (SOCK_HANDLE (fd), NULL, 0);
64570b36
KS
6981 if (rc == WAIT_OBJECT_0)
6982 cp->status = STATUS_READ_SUCCEEDED;
6983 }
7046f191 6984 pfn_WSACloseEvent (hEv);
64570b36
KS
6985
6986 return cp->status;
6987}
6988
480b0c5b
GV
6989int
6990sys_read (int fd, char * buffer, unsigned int count)
6991{
6992 int nchars;
480b0c5b
GV
6993 int to_read;
6994 DWORD waiting;
76b3903d 6995 char * orig_buffer = buffer;
480b0c5b 6996
7559f399 6997 if (fd < 0)
480b0c5b
GV
6998 {
6999 errno = EBADF;
7000 return -1;
7001 }
7002
d888760c 7003 if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET | FILE_SERIAL))
480b0c5b
GV
7004 {
7005 child_process *cp = fd_info[fd].cp;
7006
7007 if ((fd_info[fd].flags & FILE_READ) == 0)
7008 {
7009 errno = EBADF;
7010 return -1;
7011 }
7012
76b3903d
GV
7013 nchars = 0;
7014
7015 /* re-read CR carried over from last read */
7016 if (fd_info[fd].flags & FILE_LAST_CR)
7017 {
1088b922 7018 if (fd_info[fd].flags & FILE_BINARY) emacs_abort ();
76b3903d
GV
7019 *buffer++ = 0x0d;
7020 count--;
7021 nchars++;
f52eb3ef 7022 fd_info[fd].flags &= ~FILE_LAST_CR;
76b3903d
GV
7023 }
7024
480b0c5b
GV
7025 /* presence of a child_process structure means we are operating in
7026 non-blocking mode - otherwise we just call _read directly.
7027 Note that the child_process structure might be missing because
7028 reap_subprocess has been called; in this case the pipe is
7029 already broken, so calling _read on it is okay. */
7030 if (cp)
7031 {
7032 int current_status = cp->status;
7033
7034 switch (current_status)
7035 {
7036 case STATUS_READ_FAILED:
7037 case STATUS_READ_ERROR:
f52eb3ef
GV
7038 /* report normal EOF if nothing in buffer */
7039 if (nchars <= 0)
7040 fd_info[fd].flags |= FILE_AT_EOF;
7041 return nchars;
480b0c5b
GV
7042
7043 case STATUS_READ_READY:
7044 case STATUS_READ_IN_PROGRESS:
7045 DebPrint (("sys_read called when read is in progress\n"));
7046 errno = EWOULDBLOCK;
7047 return -1;
7048
7049 case STATUS_READ_SUCCEEDED:
7050 /* consume read-ahead char */
7051 *buffer++ = cp->chr;
7052 count--;
76b3903d 7053 nchars++;
480b0c5b
GV
7054 cp->status = STATUS_READ_ACKNOWLEDGED;
7055 ResetEvent (cp->char_avail);
7056
7057 case STATUS_READ_ACKNOWLEDGED:
7058 break;
7059
7060 default:
7061 DebPrint (("sys_read: bad status %d\n", current_status));
7062 errno = EBADF;
7063 return -1;
7064 }
7065
7066 if (fd_info[fd].flags & FILE_PIPE)
7067 {
7068 PeekNamedPipe ((HANDLE) _get_osfhandle (fd), NULL, 0, NULL, &waiting, NULL);
7069 to_read = min (waiting, (DWORD) count);
f52eb3ef
GV
7070
7071 if (to_read > 0)
7072 nchars += _read (fd, buffer, to_read);
480b0c5b 7073 }
d888760c
GM
7074 else if (fd_info[fd].flags & FILE_SERIAL)
7075 {
7076 HANDLE hnd = fd_info[fd].hnd;
7077 OVERLAPPED *ovl = &fd_info[fd].cp->ovl_read;
d888760c
GM
7078 int rc = 0;
7079 COMMTIMEOUTS ct;
7080
7081 if (count > 0)
7082 {
7083 /* Configure timeouts for non-blocking read. */
7084 if (!GetCommTimeouts (hnd, &ct))
7085 {
7086 errno = EIO;
7087 return -1;
7088 }
7089 ct.ReadIntervalTimeout = MAXDWORD;
7090 ct.ReadTotalTimeoutMultiplier = 0;
7091 ct.ReadTotalTimeoutConstant = 0;
7092 if (!SetCommTimeouts (hnd, &ct))
7093 {
7094 errno = EIO;
7095 return -1;
7096 }
7097
7098 if (!ResetEvent (ovl->hEvent))
7099 {
7100 errno = EIO;
7101 return -1;
7102 }
7103 if (!ReadFile (hnd, buffer, count, (DWORD*) &rc, ovl))
7104 {
7105 if (GetLastError () != ERROR_IO_PENDING)
7106 {
7107 errno = EIO;
7108 return -1;
7109 }
7110 if (!GetOverlappedResult (hnd, ovl, (DWORD*) &rc, TRUE))
7111 {
7112 errno = EIO;
7113 return -1;
7114 }
7115 }
7116 nchars += rc;
7117 }
7118 }
480b0c5b
GV
7119 else /* FILE_SOCKET */
7120 {
1088b922 7121 if (winsock_lib == NULL) emacs_abort ();
480b0c5b
GV
7122
7123 /* do the equivalent of a non-blocking read */
7124 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONREAD, &waiting);
76b3903d 7125 if (waiting == 0 && nchars == 0)
480b0c5b 7126 {
f277993b 7127 errno = EWOULDBLOCK;
480b0c5b
GV
7128 return -1;
7129 }
7130
480b0c5b
GV
7131 if (waiting)
7132 {
7133 /* always use binary mode for sockets */
76b3903d
GV
7134 int res = pfn_recv (SOCK_HANDLE (fd), buffer, count, 0);
7135 if (res == SOCKET_ERROR)
480b0c5b 7136 {
ed3751c8
JB
7137 DebPrint (("sys_read.recv failed with error %d on socket %ld\n",
7138 pfn_WSAGetLastError (), SOCK_HANDLE (fd)));
76b3903d
GV
7139 set_errno ();
7140 return -1;
480b0c5b 7141 }
76b3903d 7142 nchars += res;
480b0c5b
GV
7143 }
7144 }
480b0c5b
GV
7145 }
7146 else
f52eb3ef
GV
7147 {
7148 int nread = _read (fd, buffer, count);
7149 if (nread >= 0)
7150 nchars += nread;
7151 else if (nchars == 0)
7152 nchars = nread;
7153 }
76b3903d 7154
f52eb3ef
GV
7155 if (nchars <= 0)
7156 fd_info[fd].flags |= FILE_AT_EOF;
76b3903d 7157 /* Perform text mode translation if required. */
f52eb3ef 7158 else if ((fd_info[fd].flags & FILE_BINARY) == 0)
76b3903d
GV
7159 {
7160 nchars = crlf_to_lf (nchars, orig_buffer);
7161 /* If buffer contains only CR, return that. To be absolutely
7162 sure we should attempt to read the next char, but in
7163 practice a CR to be followed by LF would not appear by
7164 itself in the buffer. */
7165 if (nchars > 1 && orig_buffer[nchars - 1] == 0x0d)
7166 {
7167 fd_info[fd].flags |= FILE_LAST_CR;
7168 nchars--;
7169 }
76b3903d 7170 }
480b0c5b
GV
7171 }
7172 else
7173 nchars = _read (fd, buffer, count);
7174
76b3903d 7175 return nchars;
480b0c5b
GV
7176}
7177
d888760c
GM
7178/* From w32xfns.c */
7179extern HANDLE interrupt_handle;
7180
480b0c5b
GV
7181/* For now, don't bother with a non-blocking mode */
7182int
7183sys_write (int fd, const void * buffer, unsigned int count)
7184{
7185 int nchars;
7186
7559f399 7187 if (fd < 0)
480b0c5b
GV
7188 {
7189 errno = EBADF;
7190 return -1;
7191 }
7192
d888760c 7193 if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET | FILE_SERIAL))
76b3903d
GV
7194 {
7195 if ((fd_info[fd].flags & FILE_WRITE) == 0)
7196 {
7197 errno = EBADF;
7198 return -1;
7199 }
7200
7201 /* Perform text mode translation if required. */
7202 if ((fd_info[fd].flags & FILE_BINARY) == 0)
7203 {
7204 char * tmpbuf = alloca (count * 2);
7205 unsigned char * src = (void *)buffer;
7206 unsigned char * dst = tmpbuf;
7207 int nbytes = count;
7208
7209 while (1)
7210 {
7211 unsigned char *next;
7212 /* copy next line or remaining bytes */
7213 next = _memccpy (dst, src, '\n', nbytes);
7214 if (next)
7215 {
7216 /* copied one line ending with '\n' */
7217 int copied = next - dst;
7218 nbytes -= copied;
7219 src += copied;
7220 /* insert '\r' before '\n' */
7221 next[-1] = '\r';
7222 next[0] = '\n';
7223 dst = next + 1;
7224 count++;
177c0ea7 7225 }
76b3903d
GV
7226 else
7227 /* copied remaining partial line -> now finished */
7228 break;
7229 }
7230 buffer = tmpbuf;
7231 }
7232 }
7233
d888760c
GM
7234 if (fd < MAXDESC && fd_info[fd].flags & FILE_SERIAL)
7235 {
7236 HANDLE hnd = (HANDLE) _get_osfhandle (fd);
7237 OVERLAPPED *ovl = &fd_info[fd].cp->ovl_write;
7238 HANDLE wait_hnd[2] = { interrupt_handle, ovl->hEvent };
7239 DWORD active = 0;
7240
7241 if (!WriteFile (hnd, buffer, count, (DWORD*) &nchars, ovl))
7242 {
7243 if (GetLastError () != ERROR_IO_PENDING)
7244 {
7245 errno = EIO;
7246 return -1;
7247 }
7248 if (detect_input_pending ())
7249 active = MsgWaitForMultipleObjects (2, wait_hnd, FALSE, INFINITE,
7250 QS_ALLINPUT);
7251 else
7252 active = WaitForMultipleObjects (2, wait_hnd, FALSE, INFINITE);
7253 if (active == WAIT_OBJECT_0)
7254 { /* User pressed C-g, cancel write, then leave. Don't bother
7255 cleaning up as we may only get stuck in buggy drivers. */
7256 PurgeComm (hnd, PURGE_TXABORT | PURGE_TXCLEAR);
7257 CancelIo (hnd);
7258 errno = EIO;
7259 return -1;
7260 }
7261 if (active == WAIT_OBJECT_0 + 1
7262 && !GetOverlappedResult (hnd, ovl, (DWORD*) &nchars, TRUE))
7263 {
7264 errno = EIO;
7265 return -1;
7266 }
7267 }
7268 }
7d701334 7269 else if (fd < MAXDESC && fd_info[fd].flags & FILE_SOCKET)
480b0c5b 7270 {
30a32e0e 7271 unsigned long nblock = 0;
1088b922 7272 if (winsock_lib == NULL) emacs_abort ();
30a32e0e
JR
7273
7274 /* TODO: implement select() properly so non-blocking I/O works. */
7275 /* For now, make sure the write blocks. */
7276 if (fd_info[fd].flags & FILE_NDELAY)
7277 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
7278
480b0c5b 7279 nchars = pfn_send (SOCK_HANDLE (fd), buffer, count, 0);
30a32e0e
JR
7280
7281 /* Set the socket back to non-blocking if it was before,
7282 for other operations that support it. */
7283 if (fd_info[fd].flags & FILE_NDELAY)
7284 {
7285 nblock = 1;
7286 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
7287 }
7288
480b0c5b
GV
7289 if (nchars == SOCKET_ERROR)
7290 {
ed3751c8
JB
7291 DebPrint (("sys_write.send failed with error %d on socket %ld\n",
7292 pfn_WSAGetLastError (), SOCK_HANDLE (fd)));
480b0c5b
GV
7293 set_errno ();
7294 }
7295 }
7296 else
6e83d800
EZ
7297 {
7298 /* Some networked filesystems don't like too large writes, so
7299 break them into smaller chunks. See the Comments section of
7300 the MSDN documentation of WriteFile for details behind the
7301 choice of the value of CHUNK below. See also the thread
7302 http://thread.gmane.org/gmane.comp.version-control.git/145294
7303 in the git mailing list. */
7304 const unsigned char *p = buffer;
7305 const unsigned chunk = 30 * 1024 * 1024;
7306
7307 nchars = 0;
7308 while (count > 0)
7309 {
7310 unsigned this_chunk = count < chunk ? count : chunk;
7311 int n = _write (fd, p, this_chunk);
7312
7313 nchars += n;
7314 if (n < 0)
7315 {
7316 nchars = n;
7317 break;
7318 }
7319 else if (n < this_chunk)
7320 break;
7321 count -= n;
7322 p += n;
7323 }
7324 }
480b0c5b
GV
7325
7326 return nchars;
7327}
7328
97a93095
EZ
7329/* The Windows CRT functions are "optimized for speed", so they don't
7330 check for timezone and DST changes if they were last called less
7331 than 1 minute ago (see http://support.microsoft.com/kb/821231). So
7332 all Emacs features that repeatedly call time functions (e.g.,
7333 display-time) are in real danger of missing timezone and DST
7334 changes. Calling tzset before each localtime call fixes that. */
7335struct tm *
7336sys_localtime (const time_t *t)
7337{
7338 tzset ();
7339 return localtime (t);
7340}
7341
0898ca10
JB
7342
7343\f
d07ff9db
CY
7344/* Try loading LIBRARY_ID from the file(s) specified in
7345 Vdynamic_library_alist. If the library is loaded successfully,
7346 return the handle of the DLL, and record the filename in the
7347 property :loaded-from of LIBRARY_ID. If the library could not be
7348 found, or when it was already loaded (because the handle is not
7349 recorded anywhere, and so is lost after use), return NULL.
7350
7351 We could also save the handle in :loaded-from, but currently
7352 there's no use case for it. */
0898ca10 7353HMODULE
d07ff9db 7354w32_delayed_load (Lisp_Object library_id)
0898ca10
JB
7355{
7356 HMODULE library_dll = NULL;
7357
7358 CHECK_SYMBOL (library_id);
7359
d07ff9db
CY
7360 if (CONSP (Vdynamic_library_alist)
7361 && NILP (Fassq (library_id, Vlibrary_cache)))
0898ca10
JB
7362 {
7363 Lisp_Object found = Qnil;
d07ff9db 7364 Lisp_Object dlls = Fassq (library_id, Vdynamic_library_alist);
0898ca10
JB
7365
7366 if (CONSP (dlls))
7367 for (dlls = XCDR (dlls); CONSP (dlls); dlls = XCDR (dlls))
7368 {
7369 CHECK_STRING_CAR (dlls);
657d08d3 7370 if ((library_dll = LoadLibrary (SDATA (XCAR (dlls)))))
0898ca10 7371 {
2a8ce227
JB
7372 char name[MAX_PATH];
7373 DWORD len;
7374
7375 len = GetModuleFileNameA (library_dll, name, sizeof (name));
7376 found = Fcons (XCAR (dlls),
7377 (len > 0)
7378 /* Possibly truncated */
7379 ? make_specified_string (name, -1, len, 1)
7380 : Qnil);
0898ca10
JB
7381 break;
7382 }
7383 }
7384
7385 Fput (library_id, QCloaded_from, found);
7386 }
7387
7388 return library_dll;
7389}
7390
7391\f
76151e2c 7392void
b56ceb92 7393check_windows_init_file (void)
f52eb3ef 7394{
f52eb3ef
GV
7395 /* A common indication that Emacs is not installed properly is when
7396 it cannot find the Windows installation file. If this file does
7397 not exist in the expected place, tell the user. */
7398
c7aa8333
EZ
7399 if (!noninteractive && !inhibit_window_system
7400 /* Vload_path is not yet initialized when we are loading
7401 loadup.el. */
7402 && NILP (Vpurify_flag))
d54abccd 7403 {
d54abccd
GV
7404 Lisp_Object init_file;
7405 int fd;
f52eb3ef 7406
d54abccd 7407 init_file = build_string ("term/w32-win");
76151e2c 7408 fd = openp (Vload_path, init_file, Fget_load_suffixes (), NULL, Qnil);
177c0ea7 7409 if (fd < 0)
d54abccd 7410 {
76151e2c 7411 Lisp_Object load_path_print = Fprin1_to_string (Vload_path, Qnil);
d5db4077
KR
7412 char *init_file_name = SDATA (init_file);
7413 char *load_path = SDATA (load_path_print);
acc23b87
KS
7414 char *buffer = alloca (1024
7415 + strlen (init_file_name)
7416 + strlen (load_path));
d54abccd 7417
177c0ea7 7418 sprintf (buffer,
d54abccd
GV
7419 "The Emacs Windows initialization file \"%s.el\" "
7420 "could not be found in your Emacs installation. "
7421 "Emacs checked the following directories for this file:\n"
7422 "\n%s\n\n"
7423 "When Emacs cannot find this file, it usually means that it "
7424 "was not installed properly, or its distribution file was "
7425 "not unpacked properly.\nSee the README.W32 file in the "
7426 "top-level Emacs directory for more information.",
7427 init_file_name, load_path);
7428 MessageBox (NULL,
7429 buffer,
7430 "Emacs Abort Dialog",
7431 MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
1088b922 7432 /* Use the low-level system abort. */
d54abccd
GV
7433 abort ();
7434 }
7435 else
7436 {
a302c7ae 7437 _close (fd);
d54abccd 7438 }
f52eb3ef 7439 }
f52eb3ef 7440}
480b0c5b
GV
7441
7442void
16b22fef 7443term_ntproc (int ignored)
480b0c5b 7444{
16b22fef 7445 (void)ignored;
c06c382a
EZ
7446
7447 term_timers ();
7448
480b0c5b
GV
7449 /* shutdown the socket interface if necessary */
7450 term_winsock ();
52c7f9ee
JR
7451
7452 term_w32select ();
480b0c5b
GV
7453}
7454
7455void
16b22fef 7456init_ntproc (int dumping)
480b0c5b 7457{
c06c382a
EZ
7458 sigset_t initial_mask = 0;
7459
e1dbe924 7460 /* Initialize the socket interface now if available and requested by
f249a012 7461 the user by defining PRELOAD_WINSOCK; otherwise loading will be
fbd6baed 7462 delayed until open-network-stream is called (w32-has-winsock can
f249a012
RS
7463 also be used to dynamically load or reload winsock).
7464
7465 Conveniently, init_environment is called before us, so
7466 PRELOAD_WINSOCK can be set in the registry. */
7467
7468 /* Always initialize this correctly. */
7469 winsock_lib = NULL;
7470
7471 if (getenv ("PRELOAD_WINSOCK") != NULL)
7472 init_winsock (TRUE);
480b0c5b
GV
7473
7474 /* Initial preparation for subprocess support: replace our standard
7475 handles with non-inheritable versions. */
7476 {
7477 HANDLE parent;
7478 HANDLE stdin_save = INVALID_HANDLE_VALUE;
7479 HANDLE stdout_save = INVALID_HANDLE_VALUE;
7480 HANDLE stderr_save = INVALID_HANDLE_VALUE;
7481
7482 parent = GetCurrentProcess ();
7483
7484 /* ignore errors when duplicating and closing; typically the
7485 handles will be invalid when running as a gui program. */
177c0ea7
JB
7486 DuplicateHandle (parent,
7487 GetStdHandle (STD_INPUT_HANDLE),
480b0c5b 7488 parent,
177c0ea7
JB
7489 &stdin_save,
7490 0,
7491 FALSE,
480b0c5b 7492 DUPLICATE_SAME_ACCESS);
177c0ea7 7493
480b0c5b
GV
7494 DuplicateHandle (parent,
7495 GetStdHandle (STD_OUTPUT_HANDLE),
7496 parent,
7497 &stdout_save,
7498 0,
7499 FALSE,
7500 DUPLICATE_SAME_ACCESS);
177c0ea7 7501
480b0c5b
GV
7502 DuplicateHandle (parent,
7503 GetStdHandle (STD_ERROR_HANDLE),
7504 parent,
7505 &stderr_save,
7506 0,
7507 FALSE,
7508 DUPLICATE_SAME_ACCESS);
177c0ea7 7509
480b0c5b
GV
7510 fclose (stdin);
7511 fclose (stdout);
7512 fclose (stderr);
7513
7514 if (stdin_save != INVALID_HANDLE_VALUE)
62aba0d4 7515 _open_osfhandle ((intptr_t) stdin_save, O_TEXT);
480b0c5b 7516 else
76b3903d
GV
7517 _open ("nul", O_TEXT | O_NOINHERIT | O_RDONLY);
7518 _fdopen (0, "r");
480b0c5b
GV
7519
7520 if (stdout_save != INVALID_HANDLE_VALUE)
62aba0d4 7521 _open_osfhandle ((intptr_t) stdout_save, O_TEXT);
480b0c5b 7522 else
76b3903d
GV
7523 _open ("nul", O_TEXT | O_NOINHERIT | O_WRONLY);
7524 _fdopen (1, "w");
480b0c5b
GV
7525
7526 if (stderr_save != INVALID_HANDLE_VALUE)
62aba0d4 7527 _open_osfhandle ((intptr_t) stderr_save, O_TEXT);
480b0c5b 7528 else
76b3903d
GV
7529 _open ("nul", O_TEXT | O_NOINHERIT | O_WRONLY);
7530 _fdopen (2, "w");
480b0c5b
GV
7531 }
7532
7533 /* unfortunately, atexit depends on implementation of malloc */
7534 /* atexit (term_ntproc); */
16b22fef 7535 if (!dumping)
c06c382a
EZ
7536 {
7537 /* Make sure we start with all signals unblocked. */
7538 sigprocmask (SIG_SETMASK, &initial_mask, NULL);
7539 signal (SIGABRT, term_ntproc);
7540 }
7541 init_timers ();
76b3903d
GV
7542
7543 /* determine which drives are fixed, for GetCachedVolumeInformation */
7544 {
7545 /* GetDriveType must have trailing backslash. */
7546 char drive[] = "A:\\";
7547
7548 /* Loop over all possible drive letters */
7549 while (*drive <= 'Z')
7550 {
7551 /* Record if this drive letter refers to a fixed drive. */
177c0ea7 7552 fixed_drives[DRIVE_INDEX (*drive)] =
76b3903d
GV
7553 (GetDriveType (drive) == DRIVE_FIXED);
7554
7555 (*drive)++;
7556 }
a302c7ae
AI
7557
7558 /* Reset the volume info cache. */
7559 volume_cache = NULL;
76b3903d 7560 }
480b0c5b
GV
7561}
7562
a8c3a596
JR
7563/*
7564 shutdown_handler ensures that buffers' autosave files are
7565 up to date when the user logs off, or the system shuts down.
7566*/
bedf4aab 7567static BOOL WINAPI
ed3751c8 7568shutdown_handler (DWORD type)
a8c3a596
JR
7569{
7570 /* Ctrl-C and Ctrl-Break are already suppressed, so don't handle them. */
7571 if (type == CTRL_CLOSE_EVENT /* User closes console window. */
7572 || type == CTRL_LOGOFF_EVENT /* User logs off. */
7573 || type == CTRL_SHUTDOWN_EVENT) /* User shutsdown. */
7574 {
7575 /* Shut down cleanly, making sure autosave files are up to date. */
1882aa38 7576 shut_down_emacs (0, Qnil);
a8c3a596
JR
7577 }
7578
7046f191 7579 /* Allow other handlers to handle this signal. */
a8c3a596
JR
7580 return FALSE;
7581}
7582
9785d95b
BK
7583/*
7584 globals_of_w32 is used to initialize those global variables that
7585 must always be initialized on startup even when the global variable
7586 initialized is non zero (see the function main in emacs.c).
7587*/
9bfb11f9 7588void
b56ceb92 7589globals_of_w32 (void)
9785d95b 7590{
74258518
JR
7591 HMODULE kernel32 = GetModuleHandle ("kernel32.dll");
7592
7593 get_process_times_fn = (GetProcessTimes_Proc)
7594 GetProcAddress (kernel32, "GetProcessTimes");
7595
cd3520a4 7596 DEFSYM (QCloaded_from, ":loaded-from");
0898ca10 7597
9785d95b
BK
7598 g_b_init_is_windows_9x = 0;
7599 g_b_init_open_process_token = 0;
7600 g_b_init_get_token_information = 0;
7601 g_b_init_lookup_account_sid = 0;
9d95a291
EZ
7602 g_b_init_get_sid_sub_authority = 0;
7603 g_b_init_get_sid_sub_authority_count = 0;
6dad7178 7604 g_b_init_get_security_info = 0;
8aaaec6b
EZ
7605 g_b_init_get_file_security = 0;
7606 g_b_init_get_security_descriptor_owner = 0;
7607 g_b_init_get_security_descriptor_group = 0;
7608 g_b_init_is_valid_sid = 0;
7c80d5ec
EZ
7609 g_b_init_create_toolhelp32_snapshot = 0;
7610 g_b_init_process32_first = 0;
7611 g_b_init_process32_next = 0;
7612 g_b_init_open_thread_token = 0;
7613 g_b_init_impersonate_self = 0;
7614 g_b_init_revert_to_self = 0;
7615 g_b_init_get_process_memory_info = 0;
7616 g_b_init_get_process_working_set_size = 0;
7617 g_b_init_global_memory_status = 0;
7618 g_b_init_global_memory_status_ex = 0;
f8b35b24
EZ
7619 g_b_init_equal_sid = 0;
7620 g_b_init_copy_sid = 0;
7621 g_b_init_get_length_sid = 0;
ad9e2d54
EZ
7622 g_b_init_get_native_system_info = 0;
7623 g_b_init_get_system_times = 0;
6dad7178 7624 g_b_init_create_symbolic_link = 0;
66447e07
EZ
7625 g_b_init_get_security_descriptor_dacl = 0;
7626 g_b_init_convert_sd_to_sddl = 0;
7627 g_b_init_convert_sddl_to_sd = 0;
7628 g_b_init_is_valid_security_descriptor = 0;
7629 g_b_init_set_file_security = 0;
ad9e2d54 7630 num_of_processors = 0;
a8c3a596
JR
7631 /* The following sets a handler for shutdown notifications for
7632 console apps. This actually applies to Emacs in both console and
7633 GUI modes, since we had to fool windows into thinking emacs is a
7634 console application to get console mode to work. */
ed3751c8 7635 SetConsoleCtrlHandler (shutdown_handler, TRUE);
8aaaec6b
EZ
7636
7637 /* "None" is the default group name on standalone workstations. */
7638 strcpy (dflt_group_name, "None");
5c207910
EZ
7639
7640 /* Reset, in case it has some value inherited from dump time. */
7641 w32_stat_get_owner_group = 0;
9785d95b
BK
7642}
7643
d888760c 7644/* For make-serial-process */
b56ceb92
JB
7645int
7646serial_open (char *port)
d888760c
GM
7647{
7648 HANDLE hnd;
7649 child_process *cp;
7650 int fd = -1;
7651
7652 hnd = CreateFile (port, GENERIC_READ | GENERIC_WRITE, 0, 0,
7653 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
7654 if (hnd == INVALID_HANDLE_VALUE)
7655 error ("Could not open %s", port);
62aba0d4 7656 fd = (int) _open_osfhandle ((intptr_t) hnd, 0);
d888760c
GM
7657 if (fd == -1)
7658 error ("Could not open %s", port);
7659
7660 cp = new_child ();
7661 if (!cp)
7662 error ("Could not create child process");
7663 cp->fd = fd;
7664 cp->status = STATUS_READ_ACKNOWLEDGED;
7665 fd_info[ fd ].hnd = hnd;
7666 fd_info[ fd ].flags |=
7667 FILE_READ | FILE_WRITE | FILE_BINARY | FILE_SERIAL;
7668 if (fd_info[ fd ].cp != NULL)
7669 {
7670 error ("fd_info[fd = %d] is already in use", fd);
7671 }
7672 fd_info[ fd ].cp = cp;
7673 cp->ovl_read.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
7674 if (cp->ovl_read.hEvent == NULL)
7675 error ("Could not create read event");
7676 cp->ovl_write.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
7677 if (cp->ovl_write.hEvent == NULL)
7678 error ("Could not create write event");
7679
7680 return fd;
7681}
7682
7683/* For serial-process-configure */
7684void
9d4f32e8 7685serial_configure (struct Lisp_Process *p, Lisp_Object contact)
d888760c
GM
7686{
7687 Lisp_Object childp2 = Qnil;
7688 Lisp_Object tem = Qnil;
7689 HANDLE hnd;
7690 DCB dcb;
7691 COMMTIMEOUTS ct;
7692 char summary[4] = "???"; /* This usually becomes "8N1". */
7693
7694 if ((fd_info[ p->outfd ].flags & FILE_SERIAL) == 0)
7695 error ("Not a serial process");
7696 hnd = fd_info[ p->outfd ].hnd;
7697
4d2b044c 7698 childp2 = Fcopy_sequence (p->childp);
d888760c
GM
7699
7700 /* Initialize timeouts for blocking read and blocking write. */
7701 if (!GetCommTimeouts (hnd, &ct))
7702 error ("GetCommTimeouts() failed");
7703 ct.ReadIntervalTimeout = 0;
7704 ct.ReadTotalTimeoutMultiplier = 0;
7705 ct.ReadTotalTimeoutConstant = 0;
7706 ct.WriteTotalTimeoutMultiplier = 0;
7707 ct.WriteTotalTimeoutConstant = 0;
7708 if (!SetCommTimeouts (hnd, &ct))
7709 error ("SetCommTimeouts() failed");
7710 /* Read port attributes and prepare default configuration. */
7711 memset (&dcb, 0, sizeof (dcb));
7712 dcb.DCBlength = sizeof (DCB);
7713 if (!GetCommState (hnd, &dcb))
7714 error ("GetCommState() failed");
7715 dcb.fBinary = TRUE;
7716 dcb.fNull = FALSE;
7717 dcb.fAbortOnError = FALSE;
7718 /* dcb.XonLim and dcb.XoffLim are set by GetCommState() */
7719 dcb.ErrorChar = 0;
7720 dcb.EofChar = 0;
7721 dcb.EvtChar = 0;
7722
7723 /* Configure speed. */
7724 if (!NILP (Fplist_member (contact, QCspeed)))
7725 tem = Fplist_get (contact, QCspeed);
7726 else
4d2b044c 7727 tem = Fplist_get (p->childp, QCspeed);
d888760c
GM
7728 CHECK_NUMBER (tem);
7729 dcb.BaudRate = XINT (tem);
7730 childp2 = Fplist_put (childp2, QCspeed, tem);
7731
7732 /* Configure bytesize. */
7733 if (!NILP (Fplist_member (contact, QCbytesize)))
7734 tem = Fplist_get (contact, QCbytesize);
7735 else
4d2b044c 7736 tem = Fplist_get (p->childp, QCbytesize);
d888760c
GM
7737 if (NILP (tem))
7738 tem = make_number (8);
7739 CHECK_NUMBER (tem);
7740 if (XINT (tem) != 7 && XINT (tem) != 8)
7741 error (":bytesize must be nil (8), 7, or 8");
7742 dcb.ByteSize = XINT (tem);
7743 summary[0] = XINT (tem) + '0';
7744 childp2 = Fplist_put (childp2, QCbytesize, tem);
7745
7746 /* Configure parity. */
7747 if (!NILP (Fplist_member (contact, QCparity)))
7748 tem = Fplist_get (contact, QCparity);
7749 else
4d2b044c 7750 tem = Fplist_get (p->childp, QCparity);
d888760c
GM
7751 if (!NILP (tem) && !EQ (tem, Qeven) && !EQ (tem, Qodd))
7752 error (":parity must be nil (no parity), `even', or `odd'");
7753 dcb.fParity = FALSE;
7754 dcb.Parity = NOPARITY;
7755 dcb.fErrorChar = FALSE;
7756 if (NILP (tem))
7757 {
7758 summary[1] = 'N';
7759 }
7760 else if (EQ (tem, Qeven))
7761 {
7762 summary[1] = 'E';
7763 dcb.fParity = TRUE;
7764 dcb.Parity = EVENPARITY;
7765 dcb.fErrorChar = TRUE;
7766 }
7767 else if (EQ (tem, Qodd))
7768 {
7769 summary[1] = 'O';
7770 dcb.fParity = TRUE;
7771 dcb.Parity = ODDPARITY;
7772 dcb.fErrorChar = TRUE;
7773 }
7774 childp2 = Fplist_put (childp2, QCparity, tem);
7775
7776 /* Configure stopbits. */
7777 if (!NILP (Fplist_member (contact, QCstopbits)))
7778 tem = Fplist_get (contact, QCstopbits);
7779 else
4d2b044c 7780 tem = Fplist_get (p->childp, QCstopbits);
d888760c
GM
7781 if (NILP (tem))
7782 tem = make_number (1);
7783 CHECK_NUMBER (tem);
7784 if (XINT (tem) != 1 && XINT (tem) != 2)
7785 error (":stopbits must be nil (1 stopbit), 1, or 2");
7786 summary[2] = XINT (tem) + '0';
7787 if (XINT (tem) == 1)
7788 dcb.StopBits = ONESTOPBIT;
7789 else if (XINT (tem) == 2)
7790 dcb.StopBits = TWOSTOPBITS;
7791 childp2 = Fplist_put (childp2, QCstopbits, tem);
7792
7793 /* Configure flowcontrol. */
7794 if (!NILP (Fplist_member (contact, QCflowcontrol)))
7795 tem = Fplist_get (contact, QCflowcontrol);
7796 else
4d2b044c 7797 tem = Fplist_get (p->childp, QCflowcontrol);
d888760c
GM
7798 if (!NILP (tem) && !EQ (tem, Qhw) && !EQ (tem, Qsw))
7799 error (":flowcontrol must be nil (no flowcontrol), `hw', or `sw'");
7800 dcb.fOutxCtsFlow = FALSE;
7801 dcb.fOutxDsrFlow = FALSE;
7802 dcb.fDtrControl = DTR_CONTROL_DISABLE;
7803 dcb.fDsrSensitivity = FALSE;
7804 dcb.fTXContinueOnXoff = FALSE;
7805 dcb.fOutX = FALSE;
7806 dcb.fInX = FALSE;
7807 dcb.fRtsControl = RTS_CONTROL_DISABLE;
7808 dcb.XonChar = 17; /* Control-Q */
7809 dcb.XoffChar = 19; /* Control-S */
7810 if (NILP (tem))
7811 {
7812 /* Already configured. */
7813 }
7814 else if (EQ (tem, Qhw))
7815 {
7816 dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
7817 dcb.fOutxCtsFlow = TRUE;
7818 }
7819 else if (EQ (tem, Qsw))
7820 {
7821 dcb.fOutX = TRUE;
7822 dcb.fInX = TRUE;
7823 }
7824 childp2 = Fplist_put (childp2, QCflowcontrol, tem);
7825
7826 /* Activate configuration. */
7827 if (!SetCommState (hnd, &dcb))
7828 error ("SetCommState() failed");
7829
7830 childp2 = Fplist_put (childp2, QCsummary, build_string (summary));
6a09a33b 7831 pset_childp (p, childp2);
d888760c
GM
7832}
7833
e061a11b
TZ
7834#ifdef HAVE_GNUTLS
7835
7836ssize_t
7837emacs_gnutls_pull (gnutls_transport_ptr_t p, void* buf, size_t sz)
7838{
d78cf5ed 7839 int n, err;
e061a11b 7840 SELECT_TYPE fdset;
a68089e4 7841 EMACS_TIME timeout;
e061a11b
TZ
7842 struct Lisp_Process *process = (struct Lisp_Process *)p;
7843 int fd = process->infd;
7844
d78cf5ed 7845 n = sys_read (fd, (char*)buf, sz);
e061a11b 7846
d78cf5ed
CB
7847 if (n >= 0)
7848 return n;
e061a11b 7849
d78cf5ed 7850 err = errno;
e061a11b 7851
d78cf5ed
CB
7852 /* Translate the WSAEWOULDBLOCK alias EWOULDBLOCK to EAGAIN. */
7853 if (err == EWOULDBLOCK)
7854 err = EAGAIN;
e061a11b 7855
d78cf5ed 7856 emacs_gnutls_transport_set_errno (process->gnutls_state, err);
e061a11b 7857
d78cf5ed 7858 return -1;
e061a11b 7859}
ab5796a9 7860
e061a11b
TZ
7861ssize_t
7862emacs_gnutls_push (gnutls_transport_ptr_t p, const void* buf, size_t sz)
7863{
7864 struct Lisp_Process *process = (struct Lisp_Process *)p;
42ce4c63 7865 int fd = process->outfd;
5e617bc2 7866 ssize_t n = sys_write (fd, buf, sz);
e061a11b
TZ
7867
7868 /* 0 or more bytes written means everything went fine. */
7869 if (n >= 0)
7870 return n;
7871
7872 /* Negative bytes written means we got an error in errno.
7873 Translate the WSAEWOULDBLOCK alias EWOULDBLOCK to EAGAIN. */
0898ca10
JB
7874 emacs_gnutls_transport_set_errno (process->gnutls_state,
7875 errno == EWOULDBLOCK ? EAGAIN : errno);
e061a11b
TZ
7876
7877 return -1;
7878}
7879#endif /* HAVE_GNUTLS */
7880
7881/* end of w32.c */