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