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