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