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