Implement scrolling of TTY menus when the screen is too short.
[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 *,
43aac990 250 struct timespec *, 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 {
3f940c5a 6095 release_listen_threads ();
f249a012
RS
6096 /* Not sure what would cause WSAENETDOWN, or even if it can happen
6097 after WSAStartup returns successfully, but it seems reasonable
6098 to allow unloading winsock anyway in that case. */
6099 if (pfn_WSACleanup () == 0 ||
6100 pfn_WSAGetLastError () == WSAENETDOWN)
6101 {
6102 if (FreeLibrary (winsock_lib))
6103 winsock_lib = NULL;
6104 return TRUE;
6105 }
480b0c5b 6106 }
f249a012 6107 return FALSE;
480b0c5b
GV
6108}
6109
f249a012
RS
6110BOOL
6111init_winsock (int load_now)
480b0c5b
GV
6112{
6113 WSADATA winsockData;
6114
f249a012
RS
6115 if (winsock_lib != NULL)
6116 return TRUE;
f1614061 6117
f1614061
RS
6118 pfn_SetHandleInformation
6119 = (void *) GetProcAddress (GetModuleHandle ("kernel32.dll"),
6120 "SetHandleInformation");
6121
64570b36 6122 winsock_lib = LoadLibrary ("Ws2_32.dll");
480b0c5b
GV
6123
6124 if (winsock_lib != NULL)
6125 {
6126 /* dynamically link to socket functions */
6127
6128#define LOAD_PROC(fn) \
6129 if ((pfn_##fn = (void *) GetProcAddress (winsock_lib, #fn)) == NULL) \
6130 goto fail;
6131
ed3751c8
JB
6132 LOAD_PROC (WSAStartup);
6133 LOAD_PROC (WSASetLastError);
6134 LOAD_PROC (WSAGetLastError);
6135 LOAD_PROC (WSAEventSelect);
6136 LOAD_PROC (WSACreateEvent);
6137 LOAD_PROC (WSACloseEvent);
6138 LOAD_PROC (socket);
6139 LOAD_PROC (bind);
6140 LOAD_PROC (connect);
6141 LOAD_PROC (ioctlsocket);
6142 LOAD_PROC (recv);
6143 LOAD_PROC (send);
6144 LOAD_PROC (closesocket);
6145 LOAD_PROC (shutdown);
6146 LOAD_PROC (htons);
6147 LOAD_PROC (ntohs);
6148 LOAD_PROC (inet_addr);
6149 LOAD_PROC (gethostname);
6150 LOAD_PROC (gethostbyname);
6151 LOAD_PROC (getservbyname);
6152 LOAD_PROC (getpeername);
6153 LOAD_PROC (WSACleanup);
6154 LOAD_PROC (setsockopt);
6155 LOAD_PROC (listen);
6156 LOAD_PROC (getsockname);
6157 LOAD_PROC (accept);
6158 LOAD_PROC (recvfrom);
6159 LOAD_PROC (sendto);
f249a012
RS
6160#undef LOAD_PROC
6161
480b0c5b
GV
6162 /* specify version 1.1 of winsock */
6163 if (pfn_WSAStartup (0x101, &winsockData) == 0)
6164 {
f249a012
RS
6165 if (winsockData.wVersion != 0x101)
6166 goto fail;
6167
6168 if (!load_now)
6169 {
6170 /* Report that winsock exists and is usable, but leave
6171 socket functions disabled. I am assuming that calling
6172 WSAStartup does not require any network interaction,
6173 and in particular does not cause or require a dial-up
6174 connection to be established. */
6175
6176 pfn_WSACleanup ();
6177 FreeLibrary (winsock_lib);
6178 winsock_lib = NULL;
6179 }
6180 winsock_inuse = 0;
6181 return TRUE;
480b0c5b
GV
6182 }
6183
6184 fail:
6185 FreeLibrary (winsock_lib);
f249a012 6186 winsock_lib = NULL;
480b0c5b 6187 }
f249a012
RS
6188
6189 return FALSE;
480b0c5b
GV
6190}
6191
6192
6193int h_errno = 0;
6194
f277993b
EZ
6195/* Function to map winsock error codes to errno codes for those errno
6196 code defined in errno.h (errno values not defined by errno.h are
6197 already in nt/inc/sys/socket.h). */
9bfb11f9 6198static void
b56ceb92 6199set_errno (void)
480b0c5b 6200{
f277993b
EZ
6201 int wsa_err;
6202
6203 h_errno = 0;
f249a012 6204 if (winsock_lib == NULL)
f277993b 6205 wsa_err = EINVAL;
480b0c5b 6206 else
f277993b 6207 wsa_err = pfn_WSAGetLastError ();
480b0c5b 6208
f277993b 6209 switch (wsa_err)
480b0c5b 6210 {
f277993b
EZ
6211 case WSAEACCES: errno = EACCES; break;
6212 case WSAEBADF: errno = EBADF; break;
6213 case WSAEFAULT: errno = EFAULT; break;
6214 case WSAEINTR: errno = EINTR; break;
6215 case WSAEINVAL: errno = EINVAL; break;
6216 case WSAEMFILE: errno = EMFILE; break;
6217 case WSAENAMETOOLONG: errno = ENAMETOOLONG; break;
6218 case WSAENOTEMPTY: errno = ENOTEMPTY; break;
6219 default: errno = wsa_err; break;
480b0c5b 6220 }
480b0c5b
GV
6221}
6222
9bfb11f9 6223static void
b56ceb92 6224check_errno (void)
480b0c5b 6225{
f277993b
EZ
6226 h_errno = 0;
6227 if (winsock_lib != NULL)
480b0c5b
GV
6228 pfn_WSASetLastError (0);
6229}
6230
d8fcc1b9
AI
6231/* Extend strerror to handle the winsock-specific error codes. */
6232struct {
6233 int errnum;
6234 char * msg;
6235} _wsa_errlist[] = {
1db5b1ad
JB
6236 {WSAEINTR , "Interrupted function call"},
6237 {WSAEBADF , "Bad file descriptor"},
6238 {WSAEACCES , "Permission denied"},
6239 {WSAEFAULT , "Bad address"},
6240 {WSAEINVAL , "Invalid argument"},
6241 {WSAEMFILE , "Too many open files"},
6242
6243 {WSAEWOULDBLOCK , "Resource temporarily unavailable"},
6244 {WSAEINPROGRESS , "Operation now in progress"},
6245 {WSAEALREADY , "Operation already in progress"},
6246 {WSAENOTSOCK , "Socket operation on non-socket"},
6247 {WSAEDESTADDRREQ , "Destination address required"},
6248 {WSAEMSGSIZE , "Message too long"},
6249 {WSAEPROTOTYPE , "Protocol wrong type for socket"},
6250 {WSAENOPROTOOPT , "Bad protocol option"},
6251 {WSAEPROTONOSUPPORT , "Protocol not supported"},
6252 {WSAESOCKTNOSUPPORT , "Socket type not supported"},
6253 {WSAEOPNOTSUPP , "Operation not supported"},
6254 {WSAEPFNOSUPPORT , "Protocol family not supported"},
6255 {WSAEAFNOSUPPORT , "Address family not supported by protocol family"},
6256 {WSAEADDRINUSE , "Address already in use"},
6257 {WSAEADDRNOTAVAIL , "Cannot assign requested address"},
6258 {WSAENETDOWN , "Network is down"},
6259 {WSAENETUNREACH , "Network is unreachable"},
6260 {WSAENETRESET , "Network dropped connection on reset"},
6261 {WSAECONNABORTED , "Software caused connection abort"},
6262 {WSAECONNRESET , "Connection reset by peer"},
6263 {WSAENOBUFS , "No buffer space available"},
6264 {WSAEISCONN , "Socket is already connected"},
6265 {WSAENOTCONN , "Socket is not connected"},
6266 {WSAESHUTDOWN , "Cannot send after socket shutdown"},
6267 {WSAETOOMANYREFS , "Too many references"}, /* not sure */
6268 {WSAETIMEDOUT , "Connection timed out"},
6269 {WSAECONNREFUSED , "Connection refused"},
6270 {WSAELOOP , "Network loop"}, /* not sure */
6271 {WSAENAMETOOLONG , "Name is too long"},
6272 {WSAEHOSTDOWN , "Host is down"},
6273 {WSAEHOSTUNREACH , "No route to host"},
6274 {WSAENOTEMPTY , "Buffer not empty"}, /* not sure */
6275 {WSAEPROCLIM , "Too many processes"},
6276 {WSAEUSERS , "Too many users"}, /* not sure */
6277 {WSAEDQUOT , "Double quote in host name"}, /* really not sure */
6278 {WSAESTALE , "Data is stale"}, /* not sure */
6279 {WSAEREMOTE , "Remote error"}, /* not sure */
6280
6281 {WSASYSNOTREADY , "Network subsystem is unavailable"},
6282 {WSAVERNOTSUPPORTED , "WINSOCK.DLL version out of range"},
6283 {WSANOTINITIALISED , "Winsock not initialized successfully"},
6284 {WSAEDISCON , "Graceful shutdown in progress"},
d8fcc1b9 6285#ifdef WSAENOMORE
1db5b1ad
JB
6286 {WSAENOMORE , "No more operations allowed"}, /* not sure */
6287 {WSAECANCELLED , "Operation cancelled"}, /* not sure */
6288 {WSAEINVALIDPROCTABLE , "Invalid procedure table from service provider"},
6289 {WSAEINVALIDPROVIDER , "Invalid service provider version number"},
6290 {WSAEPROVIDERFAILEDINIT , "Unable to initialize a service provider"},
6291 {WSASYSCALLFAILURE , "System call failure"},
6292 {WSASERVICE_NOT_FOUND , "Service not found"}, /* not sure */
6293 {WSATYPE_NOT_FOUND , "Class type not found"},
6294 {WSA_E_NO_MORE , "No more resources available"}, /* really not sure */
6295 {WSA_E_CANCELLED , "Operation already cancelled"}, /* really not sure */
6296 {WSAEREFUSED , "Operation refused"}, /* not sure */
d8fcc1b9 6297#endif
177c0ea7 6298
1db5b1ad
JB
6299 {WSAHOST_NOT_FOUND , "Host not found"},
6300 {WSATRY_AGAIN , "Authoritative host not found during name lookup"},
6301 {WSANO_RECOVERY , "Non-recoverable error during name lookup"},
6302 {WSANO_DATA , "Valid name, no data record of requested type"},
d8fcc1b9 6303
1db5b1ad 6304 {-1, NULL}
d8fcc1b9
AI
6305};
6306
6307char *
ed3751c8 6308sys_strerror (int error_no)
d8fcc1b9
AI
6309{
6310 int i;
6311 static char unknown_msg[40];
6312
a302c7ae
AI
6313 if (error_no >= 0 && error_no < sys_nerr)
6314 return sys_errlist[error_no];
d8fcc1b9
AI
6315
6316 for (i = 0; _wsa_errlist[i].errnum >= 0; i++)
6317 if (_wsa_errlist[i].errnum == error_no)
6318 return _wsa_errlist[i].msg;
6319
ed3751c8 6320 sprintf (unknown_msg, "Unidentified error: %d", error_no);
d8fcc1b9
AI
6321 return unknown_msg;
6322}
6323
480b0c5b
GV
6324/* [andrewi 3-May-96] I've had conflicting results using both methods,
6325 but I believe the method of keeping the socket handle separate (and
6326 insuring it is not inheritable) is the correct one. */
6327
480b0c5b 6328#define SOCK_HANDLE(fd) ((SOCKET) fd_info[fd].hnd)
480b0c5b 6329
bedf4aab 6330static int socket_to_fd (SOCKET s);
962955c5 6331
480b0c5b 6332int
ed3751c8 6333sys_socket (int af, int type, int protocol)
480b0c5b 6334{
962955c5 6335 SOCKET s;
480b0c5b 6336
f249a012 6337 if (winsock_lib == NULL)
480b0c5b 6338 {
f277993b 6339 errno = ENETDOWN;
480b0c5b
GV
6340 return INVALID_SOCKET;
6341 }
6342
6343 check_errno ();
6344
6345 /* call the real socket function */
962955c5 6346 s = pfn_socket (af, type, protocol);
177c0ea7 6347
480b0c5b 6348 if (s != INVALID_SOCKET)
f277993b 6349 return socket_to_fd (s);
480b0c5b 6350
962955c5
JR
6351 set_errno ();
6352 return -1;
6353}
6354
6355/* Convert a SOCKET to a file descriptor. */
bedf4aab 6356static int
962955c5
JR
6357socket_to_fd (SOCKET s)
6358{
6359 int fd;
6360 child_process * cp;
6361
6362 /* Although under NT 3.5 _open_osfhandle will accept a socket
6363 handle, if opened with SO_OPENTYPE == SO_SYNCHRONOUS_NONALERT,
6364 that does not work under NT 3.1. However, we can get the same
6365 effect by using a backdoor function to replace an existing
6366 descriptor handle with the one we want. */
6367
6368 /* allocate a file descriptor (with appropriate flags) */
6369 fd = _open ("NUL:", _O_RDWR);
6370 if (fd >= 0)
6371 {
962955c5
JR
6372 /* Make a non-inheritable copy of the socket handle. Note
6373 that it is possible that sockets aren't actually kernel
6374 handles, which appears to be the case on Windows 9x when
6375 the MS Proxy winsock client is installed. */
6376 {
6377 /* Apparently there is a bug in NT 3.51 with some service
6378 packs, which prevents using DuplicateHandle to make a
6379 socket handle non-inheritable (causes WSACleanup to
6380 hang). The work-around is to use SetHandleInformation
6381 instead if it is available and implemented. */
6382 if (pfn_SetHandleInformation)
480b0c5b 6383 {
962955c5
JR
6384 pfn_SetHandleInformation ((HANDLE) s, HANDLE_FLAG_INHERIT, 0);
6385 }
6386 else
6387 {
6388 HANDLE parent = GetCurrentProcess ();
6389 HANDLE new_s = INVALID_HANDLE_VALUE;
6390
6391 if (DuplicateHandle (parent,
6392 (HANDLE) s,
6393 parent,
6394 &new_s,
6395 0,
6396 FALSE,
6397 DUPLICATE_SAME_ACCESS))
f1614061 6398 {
962955c5
JR
6399 /* It is possible that DuplicateHandle succeeds even
6400 though the socket wasn't really a kernel handle,
6401 because a real handle has the same value. So
6402 test whether the new handle really is a socket. */
6403 long nonblocking = 0;
6404 if (pfn_ioctlsocket ((SOCKET) new_s, FIONBIO, &nonblocking) == 0)
ca149beb 6405 {
962955c5
JR
6406 pfn_closesocket (s);
6407 s = (SOCKET) new_s;
6408 }
6409 else
6410 {
6411 CloseHandle (new_s);
6412 }
177c0ea7 6413 }
480b0c5b 6414 }
962955c5 6415 }
bcf7fe2a 6416 eassert (fd < MAXDESC);
962955c5 6417 fd_info[fd].hnd = (HANDLE) s;
480b0c5b 6418
962955c5
JR
6419 /* set our own internal flags */
6420 fd_info[fd].flags = FILE_SOCKET | FILE_BINARY | FILE_READ | FILE_WRITE;
480b0c5b 6421
962955c5
JR
6422 cp = new_child ();
6423 if (cp)
6424 {
6425 cp->fd = fd;
6426 cp->status = STATUS_READ_ACKNOWLEDGED;
480b0c5b 6427
962955c5
JR
6428 /* attach child_process to fd_info */
6429 if (fd_info[ fd ].cp != NULL)
6430 {
6431 DebPrint (("sys_socket: fd_info[%d] apparently in use!\n", fd));
1088b922 6432 emacs_abort ();
480b0c5b
GV
6433 }
6434
962955c5
JR
6435 fd_info[ fd ].cp = cp;
6436
6437 /* success! */
6438 winsock_inuse++; /* count open sockets */
6439 return fd;
480b0c5b 6440 }
480b0c5b 6441
962955c5
JR
6442 /* clean up */
6443 _close (fd);
6444 }
f277993b 6445 else
962955c5 6446 pfn_closesocket (s);
f277993b 6447 errno = EMFILE;
480b0c5b
GV
6448 return -1;
6449}
6450
480b0c5b
GV
6451int
6452sys_bind (int s, const struct sockaddr * addr, int namelen)
6453{
f249a012 6454 if (winsock_lib == NULL)
480b0c5b 6455 {
f277993b 6456 errno = ENOTSOCK;
480b0c5b
GV
6457 return SOCKET_ERROR;
6458 }
6459
6460 check_errno ();
6461 if (fd_info[s].flags & FILE_SOCKET)
6462 {
6463 int rc = pfn_bind (SOCK_HANDLE (s), addr, namelen);
6464 if (rc == SOCKET_ERROR)
6465 set_errno ();
6466 return rc;
6467 }
f277993b 6468 errno = ENOTSOCK;
480b0c5b
GV
6469 return SOCKET_ERROR;
6470}
6471
480b0c5b
GV
6472int
6473sys_connect (int s, const struct sockaddr * name, int namelen)
6474{
f249a012 6475 if (winsock_lib == NULL)
480b0c5b 6476 {
f277993b 6477 errno = ENOTSOCK;
480b0c5b
GV
6478 return SOCKET_ERROR;
6479 }
6480
6481 check_errno ();
6482 if (fd_info[s].flags & FILE_SOCKET)
6483 {
6484 int rc = pfn_connect (SOCK_HANDLE (s), name, namelen);
6485 if (rc == SOCKET_ERROR)
6486 set_errno ();
6487 return rc;
6488 }
f277993b 6489 errno = ENOTSOCK;
480b0c5b
GV
6490 return SOCKET_ERROR;
6491}
6492
6493u_short
6494sys_htons (u_short hostshort)
6495{
f249a012 6496 return (winsock_lib != NULL) ?
480b0c5b
GV
6497 pfn_htons (hostshort) : hostshort;
6498}
6499
6500u_short
6501sys_ntohs (u_short netshort)
6502{
f249a012 6503 return (winsock_lib != NULL) ?
480b0c5b
GV
6504 pfn_ntohs (netshort) : netshort;
6505}
6506
6507unsigned long
6508sys_inet_addr (const char * cp)
6509{
f249a012 6510 return (winsock_lib != NULL) ?
480b0c5b
GV
6511 pfn_inet_addr (cp) : INADDR_NONE;
6512}
6513
6514int
6515sys_gethostname (char * name, int namelen)
6516{
f249a012 6517 if (winsock_lib != NULL)
f277993b
EZ
6518 {
6519 int retval;
6520
6521 check_errno ();
6522 retval = pfn_gethostname (name, namelen);
6523 if (retval == SOCKET_ERROR)
6524 set_errno ();
6525 return retval;
6526 }
480b0c5b
GV
6527
6528 if (namelen > MAX_COMPUTERNAME_LENGTH)
a302c7ae 6529 return !GetComputerName (name, (DWORD *)&namelen);
480b0c5b 6530
f277993b 6531 errno = EFAULT;
480b0c5b
GV
6532 return SOCKET_ERROR;
6533}
6534
6535struct hostent *
ed3751c8 6536sys_gethostbyname (const char * name)
480b0c5b
GV
6537{
6538 struct hostent * host;
f277993b 6539 int h_err = h_errno;
480b0c5b 6540
f249a012 6541 if (winsock_lib == NULL)
480b0c5b 6542 {
f277993b
EZ
6543 h_errno = NO_RECOVERY;
6544 errno = ENETDOWN;
480b0c5b
GV
6545 return NULL;
6546 }
6547
6548 check_errno ();
6549 host = pfn_gethostbyname (name);
6550 if (!host)
f277993b
EZ
6551 {
6552 set_errno ();
6553 h_errno = errno;
6554 }
6555 else
6556 h_errno = h_err;
480b0c5b
GV
6557 return host;
6558}
6559
6560struct servent *
ed3751c8 6561sys_getservbyname (const char * name, const char * proto)
480b0c5b
GV
6562{
6563 struct servent * serv;
6564
f249a012 6565 if (winsock_lib == NULL)
480b0c5b 6566 {
f277993b 6567 errno = ENETDOWN;
480b0c5b
GV
6568 return NULL;
6569 }
6570
6571 check_errno ();
6572 serv = pfn_getservbyname (name, proto);
6573 if (!serv)
6574 set_errno ();
6575 return serv;
6576}
6577
ecd270eb
JR
6578int
6579sys_getpeername (int s, struct sockaddr *addr, int * namelen)
6580{
6581 if (winsock_lib == NULL)
6582 {
f277993b 6583 errno = ENETDOWN;
ecd270eb
JR
6584 return SOCKET_ERROR;
6585 }
6586
6587 check_errno ();
6588 if (fd_info[s].flags & FILE_SOCKET)
6589 {
6590 int rc = pfn_getpeername (SOCK_HANDLE (s), addr, namelen);
6591 if (rc == SOCKET_ERROR)
6592 set_errno ();
6593 return rc;
6594 }
f277993b 6595 errno = ENOTSOCK;
ecd270eb
JR
6596 return SOCKET_ERROR;
6597}
6598
380961a6
GV
6599int
6600sys_shutdown (int s, int how)
6601{
380961a6
GV
6602 if (winsock_lib == NULL)
6603 {
f277993b 6604 errno = ENETDOWN;
380961a6
GV
6605 return SOCKET_ERROR;
6606 }
6607
6608 check_errno ();
6609 if (fd_info[s].flags & FILE_SOCKET)
6610 {
6611 int rc = pfn_shutdown (SOCK_HANDLE (s), how);
962955c5
JR
6612 if (rc == SOCKET_ERROR)
6613 set_errno ();
6614 return rc;
6615 }
f277993b 6616 errno = ENOTSOCK;
962955c5
JR
6617 return SOCKET_ERROR;
6618}
6619
6620int
a5a389bb 6621sys_setsockopt (int s, int level, int optname, const void * optval, int optlen)
962955c5
JR
6622{
6623 if (winsock_lib == NULL)
6624 {
f277993b 6625 errno = ENETDOWN;
962955c5
JR
6626 return SOCKET_ERROR;
6627 }
6628
6629 check_errno ();
6630 if (fd_info[s].flags & FILE_SOCKET)
6631 {
6632 int rc = pfn_setsockopt (SOCK_HANDLE (s), level, optname,
a5a389bb 6633 (const char *)optval, optlen);
962955c5
JR
6634 if (rc == SOCKET_ERROR)
6635 set_errno ();
6636 return rc;
6637 }
f277993b 6638 errno = ENOTSOCK;
177c0ea7 6639 return SOCKET_ERROR;
962955c5
JR
6640}
6641
6642int
6643sys_listen (int s, int backlog)
6644{
6645 if (winsock_lib == NULL)
6646 {
f277993b 6647 errno = ENETDOWN;
962955c5
JR
6648 return SOCKET_ERROR;
6649 }
6650
6651 check_errno ();
6652 if (fd_info[s].flags & FILE_SOCKET)
6653 {
6654 int rc = pfn_listen (SOCK_HANDLE (s), backlog);
6655 if (rc == SOCKET_ERROR)
6656 set_errno ();
26fb7bc4 6657 else
64570b36 6658 fd_info[s].flags |= FILE_LISTEN;
962955c5
JR
6659 return rc;
6660 }
f277993b 6661 errno = ENOTSOCK;
177c0ea7 6662 return SOCKET_ERROR;
962955c5
JR
6663}
6664
6665int
6666sys_getsockname (int s, struct sockaddr * name, int * namelen)
6667{
6668 if (winsock_lib == NULL)
6669 {
f277993b 6670 errno = ENETDOWN;
962955c5
JR
6671 return SOCKET_ERROR;
6672 }
6673
6674 check_errno ();
6675 if (fd_info[s].flags & FILE_SOCKET)
6676 {
6677 int rc = pfn_getsockname (SOCK_HANDLE (s), name, namelen);
6678 if (rc == SOCKET_ERROR)
6679 set_errno ();
6680 return rc;
6681 }
f277993b 6682 errno = ENOTSOCK;
177c0ea7 6683 return SOCKET_ERROR;
962955c5
JR
6684}
6685
6686int
6687sys_accept (int s, struct sockaddr * addr, int * addrlen)
6688{
6689 if (winsock_lib == NULL)
6690 {
f277993b 6691 errno = ENETDOWN;
962955c5
JR
6692 return -1;
6693 }
6694
6695 check_errno ();
26fb7bc4 6696 if (fd_info[s].flags & FILE_LISTEN)
962955c5 6697 {
a0ad1860 6698 SOCKET t = pfn_accept (SOCK_HANDLE (s), addr, addrlen);
64570b36
KS
6699 int fd = -1;
6700 if (t == INVALID_SOCKET)
6701 set_errno ();
6702 else
f277993b 6703 fd = socket_to_fd (t);
962955c5 6704
bcf7fe2a
EZ
6705 if (fd >= 0)
6706 {
6707 fd_info[s].cp->status = STATUS_READ_ACKNOWLEDGED;
6708 ResetEvent (fd_info[s].cp->char_avail);
6709 }
64570b36 6710 return fd;
962955c5 6711 }
f277993b 6712 errno = ENOTSOCK;
962955c5
JR
6713 return -1;
6714}
6715
6716int
6717sys_recvfrom (int s, char * buf, int len, int flags,
b56ceb92 6718 struct sockaddr * from, int * fromlen)
962955c5
JR
6719{
6720 if (winsock_lib == NULL)
6721 {
f277993b 6722 errno = ENETDOWN;
962955c5
JR
6723 return SOCKET_ERROR;
6724 }
6725
6726 check_errno ();
6727 if (fd_info[s].flags & FILE_SOCKET)
6728 {
6729 int rc = pfn_recvfrom (SOCK_HANDLE (s), buf, len, flags, from, fromlen);
6730 if (rc == SOCKET_ERROR)
6731 set_errno ();
6732 return rc;
6733 }
f277993b 6734 errno = ENOTSOCK;
962955c5
JR
6735 return SOCKET_ERROR;
6736}
6737
6738int
6739sys_sendto (int s, const char * buf, int len, int flags,
6740 const struct sockaddr * to, int tolen)
6741{
6742 if (winsock_lib == NULL)
6743 {
f277993b 6744 errno = ENETDOWN;
962955c5
JR
6745 return SOCKET_ERROR;
6746 }
6747
6748 check_errno ();
6749 if (fd_info[s].flags & FILE_SOCKET)
6750 {
6751 int rc = pfn_sendto (SOCK_HANDLE (s), buf, len, flags, to, tolen);
380961a6
GV
6752 if (rc == SOCKET_ERROR)
6753 set_errno ();
6754 return rc;
6755 }
f277993b 6756 errno = ENOTSOCK;
380961a6
GV
6757 return SOCKET_ERROR;
6758}
6759
ecd270eb 6760/* Windows does not have an fcntl function. Provide an implementation
067428c1 6761 good enough for Emacs. */
ecd270eb
JR
6762int
6763fcntl (int s, int cmd, int options)
6764{
067428c1
PE
6765 /* In the w32 Emacs port, fcntl (fd, F_DUPFD_CLOEXEC, fd1) is always
6766 invoked in a context where fd1 is closed and all descriptors less
6767 than fd1 are open, so sys_dup is an adequate implementation. */
6768 if (cmd == F_DUPFD_CLOEXEC)
6769 return sys_dup (s);
6770
ecd270eb
JR
6771 if (winsock_lib == NULL)
6772 {
f277993b 6773 errno = ENETDOWN;
ecd270eb
JR
6774 return -1;
6775 }
6776
6777 check_errno ();
6778 if (fd_info[s].flags & FILE_SOCKET)
6779 {
49cdacda 6780 if (cmd == F_SETFL && options == O_NONBLOCK)
ecd270eb
JR
6781 {
6782 unsigned long nblock = 1;
6783 int rc = pfn_ioctlsocket (SOCK_HANDLE (s), FIONBIO, &nblock);
6784 if (rc == SOCKET_ERROR)
9d4f32e8 6785 set_errno ();
ecd270eb
JR
6786 /* Keep track of the fact that we set this to non-blocking. */
6787 fd_info[s].flags |= FILE_NDELAY;
6788 return rc;
6789 }
6790 else
6791 {
f277993b 6792 errno = EINVAL;
ecd270eb
JR
6793 return SOCKET_ERROR;
6794 }
6795 }
f277993b 6796 errno = ENOTSOCK;
ecd270eb
JR
6797 return SOCKET_ERROR;
6798}
6799
480b0c5b
GV
6800
6801/* Shadow main io functions: we need to handle pipes and sockets more
6802 intelligently, and implement non-blocking mode as well. */
6803
6804int
6805sys_close (int fd)
6806{
6807 int rc;
6808
7559f399 6809 if (fd < 0)
480b0c5b
GV
6810 {
6811 errno = EBADF;
6812 return -1;
6813 }
6814
7559f399 6815 if (fd < MAXDESC && fd_info[fd].cp)
480b0c5b
GV
6816 {
6817 child_process * cp = fd_info[fd].cp;
6818
6819 fd_info[fd].cp = NULL;
6820
6821 if (CHILD_ACTIVE (cp))
6822 {
6823 /* if last descriptor to active child_process then cleanup */
6824 int i;
6825 for (i = 0; i < MAXDESC; i++)
6826 {
6827 if (i == fd)
6828 continue;
6829 if (fd_info[i].cp == cp)
6830 break;
6831 }
6832 if (i == MAXDESC)
6833 {
480b0c5b
GV
6834 if (fd_info[fd].flags & FILE_SOCKET)
6835 {
1088b922 6836 if (winsock_lib == NULL) emacs_abort ();
480b0c5b
GV
6837
6838 pfn_shutdown (SOCK_HANDLE (fd), 2);
6839 rc = pfn_closesocket (SOCK_HANDLE (fd));
7d701334 6840
f249a012 6841 winsock_inuse--; /* count open sockets */
480b0c5b 6842 }
299614f3
EZ
6843 /* If the process handle is NULL, it's either a socket
6844 or serial connection, or a subprocess that was
6845 already reaped by reap_subprocess, but whose
6846 resources were not yet freed, because its output was
6847 not fully read yet by the time it was reaped. (This
6848 usually happens with async subprocesses whose output
6849 is being read by Emacs.) Otherwise, this process was
6850 not reaped yet, so we set its FD to a negative value
6851 to make sure sys_select will eventually get to
6852 calling the SIGCHLD handler for it, which will then
6853 invoke waitpid and reap_subprocess. */
6854 if (cp->procinfo.hProcess == NULL)
6855 delete_child (cp);
6856 else
6857 cp->fd = -1;
480b0c5b
GV
6858 }
6859 }
6860 }
6861
224f4ec1
EZ
6862 if (fd >= 0 && fd < MAXDESC)
6863 fd_info[fd].flags = 0;
6864
480b0c5b 6865 /* Note that sockets do not need special treatment here (at least on
e9e23e23 6866 NT and Windows 95 using the standard tcp/ip stacks) - it appears that
480b0c5b
GV
6867 closesocket is equivalent to CloseHandle, which is to be expected
6868 because socket handles are fully fledged kernel handles. */
6869 rc = _close (fd);
6870
480b0c5b
GV
6871 return rc;
6872}
6873
6874int
6875sys_dup (int fd)
6876{
6877 int new_fd;
6878
6879 new_fd = _dup (fd);
7559f399 6880 if (new_fd >= 0 && new_fd < MAXDESC)
480b0c5b
GV
6881 {
6882 /* duplicate our internal info as well */
6883 fd_info[new_fd] = fd_info[fd];
6884 }
6885 return new_fd;
6886}
6887
480b0c5b
GV
6888int
6889sys_dup2 (int src, int dst)
6890{
6891 int rc;
6892
6893 if (dst < 0 || dst >= MAXDESC)
6894 {
6895 errno = EBADF;
6896 return -1;
6897 }
6898
6899 /* make sure we close the destination first if it's a pipe or socket */
6900 if (src != dst && fd_info[dst].flags != 0)
6901 sys_close (dst);
177c0ea7 6902
480b0c5b
GV
6903 rc = _dup2 (src, dst);
6904 if (rc == 0)
6905 {
6906 /* duplicate our internal info as well */
6907 fd_info[dst] = fd_info[src];
6908 }
6909 return rc;
6910}
6911
480b0c5b 6912int
067428c1 6913pipe2 (int * phandles, int pipe2_flags)
480b0c5b
GV
6914{
6915 int rc;
6916 unsigned flags;
480b0c5b 6917
067428c1
PE
6918 eassert (pipe2_flags == O_CLOEXEC);
6919
76b3903d
GV
6920 /* make pipe handles non-inheritable; when we spawn a child, we
6921 replace the relevant handle with an inheritable one. Also put
6922 pipes into binary mode; we will do text mode translation ourselves
6923 if required. */
6924 rc = _pipe (phandles, 0, _O_NOINHERIT | _O_BINARY);
480b0c5b
GV
6925
6926 if (rc == 0)
6927 {
cb72110d
JR
6928 /* Protect against overflow, since Windows can open more handles than
6929 our fd_info array has room for. */
6930 if (phandles[0] >= MAXDESC || phandles[1] >= MAXDESC)
6931 {
6932 _close (phandles[0]);
6933 _close (phandles[1]);
6e432f0c 6934 errno = EMFILE;
cb72110d
JR
6935 rc = -1;
6936 }
6937 else
6938 {
6939 flags = FILE_PIPE | FILE_READ | FILE_BINARY;
6940 fd_info[phandles[0]].flags = flags;
480b0c5b 6941
cb72110d
JR
6942 flags = FILE_PIPE | FILE_WRITE | FILE_BINARY;
6943 fd_info[phandles[1]].flags = flags;
6944 }
480b0c5b
GV
6945 }
6946
6947 return rc;
6948}
6949
6950/* Function to do blocking read of one byte, needed to implement
d3d14b40
EZ
6951 select. It is only allowed on communication ports, sockets, or
6952 pipes. */
480b0c5b
GV
6953int
6954_sys_read_ahead (int fd)
6955{
6956 child_process * cp;
6957 int rc;
6958
6959 if (fd < 0 || fd >= MAXDESC)
6960 return STATUS_READ_ERROR;
6961
6962 cp = fd_info[fd].cp;
6963
6964 if (cp == NULL || cp->fd != fd || cp->status != STATUS_READ_READY)
6965 return STATUS_READ_ERROR;
6966
d888760c 6967 if ((fd_info[fd].flags & (FILE_PIPE | FILE_SERIAL | FILE_SOCKET)) == 0
480b0c5b
GV
6968 || (fd_info[fd].flags & FILE_READ) == 0)
6969 {
d888760c 6970 DebPrint (("_sys_read_ahead: internal error: fd %d is not a pipe, serial port, or socket!\n", fd));
1088b922 6971 emacs_abort ();
480b0c5b 6972 }
177c0ea7 6973
480b0c5b 6974 cp->status = STATUS_READ_IN_PROGRESS;
177c0ea7 6975
480b0c5b 6976 if (fd_info[fd].flags & FILE_PIPE)
f7554349 6977 {
f7554349
KH
6978 rc = _read (fd, &cp->chr, sizeof (char));
6979
6980 /* Give subprocess time to buffer some more output for us before
e9e23e23 6981 reporting that input is available; we need this because Windows 95
f7554349
KH
6982 connects DOS programs to pipes by making the pipe appear to be
6983 the normal console stdout - as a result most DOS programs will
d888760c 6984 write to stdout without buffering, ie. one character at a
fbd6baed 6985 time. Even some W32 programs do this - "dir" in a command
f7554349
KH
6986 shell on NT is very slow if we don't do this. */
6987 if (rc > 0)
6988 {
78806724 6989 int wait = w32_pipe_read_delay;
f7554349
KH
6990
6991 if (wait > 0)
6992 Sleep (wait);
6993 else if (wait < 0)
6994 while (++wait <= 0)
6995 /* Yield remainder of our time slice, effectively giving a
6996 temporary priority boost to the child process. */
6997 Sleep (0);
6998 }
6999 }
d888760c
GM
7000 else if (fd_info[fd].flags & FILE_SERIAL)
7001 {
7002 HANDLE hnd = fd_info[fd].hnd;
7003 OVERLAPPED *ovl = &fd_info[fd].cp->ovl_read;
7004 COMMTIMEOUTS ct;
7005
7006 /* Configure timeouts for blocking read. */
7007 if (!GetCommTimeouts (hnd, &ct))
6e432f0c
EZ
7008 {
7009 cp->status = STATUS_READ_ERROR;
7010 return STATUS_READ_ERROR;
7011 }
d888760c
GM
7012 ct.ReadIntervalTimeout = 0;
7013 ct.ReadTotalTimeoutMultiplier = 0;
7014 ct.ReadTotalTimeoutConstant = 0;
7015 if (!SetCommTimeouts (hnd, &ct))
6e432f0c
EZ
7016 {
7017 cp->status = STATUS_READ_ERROR;
7018 return STATUS_READ_ERROR;
7019 }
d888760c
GM
7020
7021 if (!ReadFile (hnd, &cp->chr, sizeof (char), (DWORD*) &rc, ovl))
7022 {
7023 if (GetLastError () != ERROR_IO_PENDING)
6e432f0c
EZ
7024 {
7025 cp->status = STATUS_READ_ERROR;
7026 return STATUS_READ_ERROR;
7027 }
d888760c 7028 if (!GetOverlappedResult (hnd, ovl, (DWORD*) &rc, TRUE))
6e432f0c
EZ
7029 {
7030 cp->status = STATUS_READ_ERROR;
7031 return STATUS_READ_ERROR;
7032 }
d888760c
GM
7033 }
7034 }
480b0c5b 7035 else if (fd_info[fd].flags & FILE_SOCKET)
ecd270eb
JR
7036 {
7037 unsigned long nblock = 0;
7038 /* We always want this to block, so temporarily disable NDELAY. */
7039 if (fd_info[fd].flags & FILE_NDELAY)
7040 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
7041
7042 rc = pfn_recv (SOCK_HANDLE (fd), &cp->chr, sizeof (char), 0);
7043
7044 if (fd_info[fd].flags & FILE_NDELAY)
7045 {
7046 nblock = 1;
7047 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
7048 }
7049 }
177c0ea7 7050
480b0c5b
GV
7051 if (rc == sizeof (char))
7052 cp->status = STATUS_READ_SUCCEEDED;
7053 else
7054 cp->status = STATUS_READ_FAILED;
7055
7056 return cp->status;
7057}
7058
9bfb11f9
KS
7059int
7060_sys_wait_accept (int fd)
64570b36
KS
7061{
7062 HANDLE hEv;
7063 child_process * cp;
7064 int rc;
7065
7066 if (fd < 0 || fd >= MAXDESC)
7067 return STATUS_READ_ERROR;
7068
7069 cp = fd_info[fd].cp;
7070
7071 if (cp == NULL || cp->fd != fd || cp->status != STATUS_READ_READY)
7072 return STATUS_READ_ERROR;
7073
7074 cp->status = STATUS_READ_FAILED;
7075
7076 hEv = pfn_WSACreateEvent ();
7077 rc = pfn_WSAEventSelect (SOCK_HANDLE (fd), hEv, FD_ACCEPT);
7078 if (rc != SOCKET_ERROR)
7079 {
3f940c5a
EZ
7080 do {
7081 rc = WaitForSingleObject (hEv, 500);
7082 Sleep (5);
7083 } while (rc == WAIT_TIMEOUT
7084 && cp->status != STATUS_READ_ERROR
7085 && cp->char_avail);
64570b36 7086 pfn_WSAEventSelect (SOCK_HANDLE (fd), NULL, 0);
64570b36
KS
7087 if (rc == WAIT_OBJECT_0)
7088 cp->status = STATUS_READ_SUCCEEDED;
7089 }
7046f191 7090 pfn_WSACloseEvent (hEv);
64570b36
KS
7091
7092 return cp->status;
7093}
7094
480b0c5b
GV
7095int
7096sys_read (int fd, char * buffer, unsigned int count)
7097{
7098 int nchars;
480b0c5b
GV
7099 int to_read;
7100 DWORD waiting;
76b3903d 7101 char * orig_buffer = buffer;
480b0c5b 7102
7559f399 7103 if (fd < 0)
480b0c5b
GV
7104 {
7105 errno = EBADF;
7106 return -1;
7107 }
7108
d888760c 7109 if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET | FILE_SERIAL))
480b0c5b
GV
7110 {
7111 child_process *cp = fd_info[fd].cp;
7112
7113 if ((fd_info[fd].flags & FILE_READ) == 0)
7114 {
7115 errno = EBADF;
7116 return -1;
7117 }
7118
76b3903d
GV
7119 nchars = 0;
7120
7121 /* re-read CR carried over from last read */
7122 if (fd_info[fd].flags & FILE_LAST_CR)
7123 {
1088b922 7124 if (fd_info[fd].flags & FILE_BINARY) emacs_abort ();
76b3903d
GV
7125 *buffer++ = 0x0d;
7126 count--;
7127 nchars++;
f52eb3ef 7128 fd_info[fd].flags &= ~FILE_LAST_CR;
76b3903d
GV
7129 }
7130
480b0c5b
GV
7131 /* presence of a child_process structure means we are operating in
7132 non-blocking mode - otherwise we just call _read directly.
7133 Note that the child_process structure might be missing because
7134 reap_subprocess has been called; in this case the pipe is
7135 already broken, so calling _read on it is okay. */
7136 if (cp)
7137 {
7138 int current_status = cp->status;
7139
7140 switch (current_status)
7141 {
7142 case STATUS_READ_FAILED:
7143 case STATUS_READ_ERROR:
f52eb3ef
GV
7144 /* report normal EOF if nothing in buffer */
7145 if (nchars <= 0)
7146 fd_info[fd].flags |= FILE_AT_EOF;
7147 return nchars;
480b0c5b
GV
7148
7149 case STATUS_READ_READY:
7150 case STATUS_READ_IN_PROGRESS:
7151 DebPrint (("sys_read called when read is in progress\n"));
7152 errno = EWOULDBLOCK;
7153 return -1;
7154
7155 case STATUS_READ_SUCCEEDED:
7156 /* consume read-ahead char */
7157 *buffer++ = cp->chr;
7158 count--;
76b3903d 7159 nchars++;
480b0c5b
GV
7160 cp->status = STATUS_READ_ACKNOWLEDGED;
7161 ResetEvent (cp->char_avail);
7162
7163 case STATUS_READ_ACKNOWLEDGED:
7164 break;
7165
7166 default:
7167 DebPrint (("sys_read: bad status %d\n", current_status));
7168 errno = EBADF;
7169 return -1;
7170 }
7171
7172 if (fd_info[fd].flags & FILE_PIPE)
7173 {
7174 PeekNamedPipe ((HANDLE) _get_osfhandle (fd), NULL, 0, NULL, &waiting, NULL);
7175 to_read = min (waiting, (DWORD) count);
f52eb3ef
GV
7176
7177 if (to_read > 0)
7178 nchars += _read (fd, buffer, to_read);
480b0c5b 7179 }
d888760c
GM
7180 else if (fd_info[fd].flags & FILE_SERIAL)
7181 {
7182 HANDLE hnd = fd_info[fd].hnd;
7183 OVERLAPPED *ovl = &fd_info[fd].cp->ovl_read;
d888760c
GM
7184 int rc = 0;
7185 COMMTIMEOUTS ct;
7186
7187 if (count > 0)
7188 {
7189 /* Configure timeouts for non-blocking read. */
7190 if (!GetCommTimeouts (hnd, &ct))
7191 {
7192 errno = EIO;
7193 return -1;
7194 }
7195 ct.ReadIntervalTimeout = MAXDWORD;
7196 ct.ReadTotalTimeoutMultiplier = 0;
7197 ct.ReadTotalTimeoutConstant = 0;
7198 if (!SetCommTimeouts (hnd, &ct))
7199 {
7200 errno = EIO;
7201 return -1;
7202 }
7203
7204 if (!ResetEvent (ovl->hEvent))
7205 {
7206 errno = EIO;
7207 return -1;
7208 }
7209 if (!ReadFile (hnd, buffer, count, (DWORD*) &rc, ovl))
7210 {
7211 if (GetLastError () != ERROR_IO_PENDING)
7212 {
7213 errno = EIO;
7214 return -1;
7215 }
7216 if (!GetOverlappedResult (hnd, ovl, (DWORD*) &rc, TRUE))
7217 {
7218 errno = EIO;
7219 return -1;
7220 }
7221 }
7222 nchars += rc;
7223 }
7224 }
480b0c5b
GV
7225 else /* FILE_SOCKET */
7226 {
1088b922 7227 if (winsock_lib == NULL) emacs_abort ();
480b0c5b
GV
7228
7229 /* do the equivalent of a non-blocking read */
7230 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONREAD, &waiting);
76b3903d 7231 if (waiting == 0 && nchars == 0)
480b0c5b 7232 {
f277993b 7233 errno = EWOULDBLOCK;
480b0c5b
GV
7234 return -1;
7235 }
7236
480b0c5b
GV
7237 if (waiting)
7238 {
7239 /* always use binary mode for sockets */
76b3903d
GV
7240 int res = pfn_recv (SOCK_HANDLE (fd), buffer, count, 0);
7241 if (res == SOCKET_ERROR)
480b0c5b 7242 {
ed3751c8
JB
7243 DebPrint (("sys_read.recv failed with error %d on socket %ld\n",
7244 pfn_WSAGetLastError (), SOCK_HANDLE (fd)));
76b3903d
GV
7245 set_errno ();
7246 return -1;
480b0c5b 7247 }
76b3903d 7248 nchars += res;
480b0c5b
GV
7249 }
7250 }
480b0c5b
GV
7251 }
7252 else
f52eb3ef
GV
7253 {
7254 int nread = _read (fd, buffer, count);
7255 if (nread >= 0)
7256 nchars += nread;
7257 else if (nchars == 0)
7258 nchars = nread;
7259 }
76b3903d 7260
f52eb3ef
GV
7261 if (nchars <= 0)
7262 fd_info[fd].flags |= FILE_AT_EOF;
76b3903d 7263 /* Perform text mode translation if required. */
f52eb3ef 7264 else if ((fd_info[fd].flags & FILE_BINARY) == 0)
76b3903d
GV
7265 {
7266 nchars = crlf_to_lf (nchars, orig_buffer);
7267 /* If buffer contains only CR, return that. To be absolutely
7268 sure we should attempt to read the next char, but in
7269 practice a CR to be followed by LF would not appear by
7270 itself in the buffer. */
7271 if (nchars > 1 && orig_buffer[nchars - 1] == 0x0d)
7272 {
7273 fd_info[fd].flags |= FILE_LAST_CR;
7274 nchars--;
7275 }
76b3903d 7276 }
480b0c5b
GV
7277 }
7278 else
7279 nchars = _read (fd, buffer, count);
7280
76b3903d 7281 return nchars;
480b0c5b
GV
7282}
7283
d888760c
GM
7284/* From w32xfns.c */
7285extern HANDLE interrupt_handle;
7286
480b0c5b
GV
7287/* For now, don't bother with a non-blocking mode */
7288int
7289sys_write (int fd, const void * buffer, unsigned int count)
7290{
7291 int nchars;
7292
7559f399 7293 if (fd < 0)
480b0c5b
GV
7294 {
7295 errno = EBADF;
7296 return -1;
7297 }
7298
d888760c 7299 if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET | FILE_SERIAL))
76b3903d
GV
7300 {
7301 if ((fd_info[fd].flags & FILE_WRITE) == 0)
7302 {
7303 errno = EBADF;
7304 return -1;
7305 }
7306
7307 /* Perform text mode translation if required. */
7308 if ((fd_info[fd].flags & FILE_BINARY) == 0)
7309 {
7310 char * tmpbuf = alloca (count * 2);
7311 unsigned char * src = (void *)buffer;
7312 unsigned char * dst = tmpbuf;
7313 int nbytes = count;
7314
7315 while (1)
7316 {
7317 unsigned char *next;
7318 /* copy next line or remaining bytes */
7319 next = _memccpy (dst, src, '\n', nbytes);
7320 if (next)
7321 {
7322 /* copied one line ending with '\n' */
7323 int copied = next - dst;
7324 nbytes -= copied;
7325 src += copied;
7326 /* insert '\r' before '\n' */
7327 next[-1] = '\r';
7328 next[0] = '\n';
7329 dst = next + 1;
7330 count++;
177c0ea7 7331 }
76b3903d
GV
7332 else
7333 /* copied remaining partial line -> now finished */
7334 break;
7335 }
7336 buffer = tmpbuf;
7337 }
7338 }
7339
d888760c
GM
7340 if (fd < MAXDESC && fd_info[fd].flags & FILE_SERIAL)
7341 {
7342 HANDLE hnd = (HANDLE) _get_osfhandle (fd);
7343 OVERLAPPED *ovl = &fd_info[fd].cp->ovl_write;
7344 HANDLE wait_hnd[2] = { interrupt_handle, ovl->hEvent };
7345 DWORD active = 0;
7346
7347 if (!WriteFile (hnd, buffer, count, (DWORD*) &nchars, ovl))
7348 {
7349 if (GetLastError () != ERROR_IO_PENDING)
7350 {
7351 errno = EIO;
7352 return -1;
7353 }
7354 if (detect_input_pending ())
7355 active = MsgWaitForMultipleObjects (2, wait_hnd, FALSE, INFINITE,
7356 QS_ALLINPUT);
7357 else
7358 active = WaitForMultipleObjects (2, wait_hnd, FALSE, INFINITE);
7359 if (active == WAIT_OBJECT_0)
7360 { /* User pressed C-g, cancel write, then leave. Don't bother
7361 cleaning up as we may only get stuck in buggy drivers. */
7362 PurgeComm (hnd, PURGE_TXABORT | PURGE_TXCLEAR);
7363 CancelIo (hnd);
7364 errno = EIO;
7365 return -1;
7366 }
7367 if (active == WAIT_OBJECT_0 + 1
7368 && !GetOverlappedResult (hnd, ovl, (DWORD*) &nchars, TRUE))
7369 {
7370 errno = EIO;
7371 return -1;
7372 }
7373 }
7374 }
7d701334 7375 else if (fd < MAXDESC && fd_info[fd].flags & FILE_SOCKET)
480b0c5b 7376 {
30a32e0e 7377 unsigned long nblock = 0;
1088b922 7378 if (winsock_lib == NULL) emacs_abort ();
30a32e0e
JR
7379
7380 /* TODO: implement select() properly so non-blocking I/O works. */
7381 /* For now, make sure the write blocks. */
7382 if (fd_info[fd].flags & FILE_NDELAY)
7383 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
7384
480b0c5b 7385 nchars = pfn_send (SOCK_HANDLE (fd), buffer, count, 0);
30a32e0e
JR
7386
7387 /* Set the socket back to non-blocking if it was before,
7388 for other operations that support it. */
7389 if (fd_info[fd].flags & FILE_NDELAY)
7390 {
7391 nblock = 1;
7392 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
7393 }
7394
480b0c5b
GV
7395 if (nchars == SOCKET_ERROR)
7396 {
ed3751c8
JB
7397 DebPrint (("sys_write.send failed with error %d on socket %ld\n",
7398 pfn_WSAGetLastError (), SOCK_HANDLE (fd)));
480b0c5b
GV
7399 set_errno ();
7400 }
7401 }
7402 else
6e83d800
EZ
7403 {
7404 /* Some networked filesystems don't like too large writes, so
7405 break them into smaller chunks. See the Comments section of
7406 the MSDN documentation of WriteFile for details behind the
7407 choice of the value of CHUNK below. See also the thread
7408 http://thread.gmane.org/gmane.comp.version-control.git/145294
7409 in the git mailing list. */
7410 const unsigned char *p = buffer;
7411 const unsigned chunk = 30 * 1024 * 1024;
7412
7413 nchars = 0;
7414 while (count > 0)
7415 {
7416 unsigned this_chunk = count < chunk ? count : chunk;
7417 int n = _write (fd, p, this_chunk);
7418
7419 nchars += n;
7420 if (n < 0)
7421 {
7422 nchars = n;
7423 break;
7424 }
7425 else if (n < this_chunk)
7426 break;
7427 count -= n;
7428 p += n;
7429 }
7430 }
480b0c5b
GV
7431
7432 return nchars;
7433}
7434
97a93095
EZ
7435/* The Windows CRT functions are "optimized for speed", so they don't
7436 check for timezone and DST changes if they were last called less
7437 than 1 minute ago (see http://support.microsoft.com/kb/821231). So
7438 all Emacs features that repeatedly call time functions (e.g.,
7439 display-time) are in real danger of missing timezone and DST
7440 changes. Calling tzset before each localtime call fixes that. */
7441struct tm *
7442sys_localtime (const time_t *t)
7443{
7444 tzset ();
7445 return localtime (t);
7446}
7447
0898ca10
JB
7448
7449\f
d07ff9db
CY
7450/* Try loading LIBRARY_ID from the file(s) specified in
7451 Vdynamic_library_alist. If the library is loaded successfully,
7452 return the handle of the DLL, and record the filename in the
7453 property :loaded-from of LIBRARY_ID. If the library could not be
7454 found, or when it was already loaded (because the handle is not
7455 recorded anywhere, and so is lost after use), return NULL.
7456
7457 We could also save the handle in :loaded-from, but currently
7458 there's no use case for it. */
0898ca10 7459HMODULE
d07ff9db 7460w32_delayed_load (Lisp_Object library_id)
0898ca10
JB
7461{
7462 HMODULE library_dll = NULL;
7463
7464 CHECK_SYMBOL (library_id);
7465
d07ff9db
CY
7466 if (CONSP (Vdynamic_library_alist)
7467 && NILP (Fassq (library_id, Vlibrary_cache)))
0898ca10
JB
7468 {
7469 Lisp_Object found = Qnil;
d07ff9db 7470 Lisp_Object dlls = Fassq (library_id, Vdynamic_library_alist);
0898ca10
JB
7471
7472 if (CONSP (dlls))
7473 for (dlls = XCDR (dlls); CONSP (dlls); dlls = XCDR (dlls))
7474 {
7475 CHECK_STRING_CAR (dlls);
657d08d3 7476 if ((library_dll = LoadLibrary (SDATA (XCAR (dlls)))))
0898ca10 7477 {
2a8ce227
JB
7478 char name[MAX_PATH];
7479 DWORD len;
7480
7481 len = GetModuleFileNameA (library_dll, name, sizeof (name));
7482 found = Fcons (XCAR (dlls),
7483 (len > 0)
7484 /* Possibly truncated */
7485 ? make_specified_string (name, -1, len, 1)
7486 : Qnil);
0898ca10
JB
7487 break;
7488 }
7489 }
7490
7491 Fput (library_id, QCloaded_from, found);
7492 }
7493
7494 return library_dll;
7495}
7496
7497\f
76151e2c 7498void
b56ceb92 7499check_windows_init_file (void)
f52eb3ef 7500{
f52eb3ef
GV
7501 /* A common indication that Emacs is not installed properly is when
7502 it cannot find the Windows installation file. If this file does
7503 not exist in the expected place, tell the user. */
7504
c7aa8333
EZ
7505 if (!noninteractive && !inhibit_window_system
7506 /* Vload_path is not yet initialized when we are loading
7507 loadup.el. */
7508 && NILP (Vpurify_flag))
d54abccd 7509 {
d54abccd
GV
7510 Lisp_Object init_file;
7511 int fd;
f52eb3ef 7512
d54abccd 7513 init_file = build_string ("term/w32-win");
76151e2c 7514 fd = openp (Vload_path, init_file, Fget_load_suffixes (), NULL, Qnil);
177c0ea7 7515 if (fd < 0)
d54abccd 7516 {
76151e2c 7517 Lisp_Object load_path_print = Fprin1_to_string (Vload_path, Qnil);
d5db4077
KR
7518 char *init_file_name = SDATA (init_file);
7519 char *load_path = SDATA (load_path_print);
acc23b87
KS
7520 char *buffer = alloca (1024
7521 + strlen (init_file_name)
7522 + strlen (load_path));
d54abccd 7523
177c0ea7 7524 sprintf (buffer,
d54abccd
GV
7525 "The Emacs Windows initialization file \"%s.el\" "
7526 "could not be found in your Emacs installation. "
7527 "Emacs checked the following directories for this file:\n"
7528 "\n%s\n\n"
7529 "When Emacs cannot find this file, it usually means that it "
7530 "was not installed properly, or its distribution file was "
7531 "not unpacked properly.\nSee the README.W32 file in the "
7532 "top-level Emacs directory for more information.",
7533 init_file_name, load_path);
7534 MessageBox (NULL,
7535 buffer,
7536 "Emacs Abort Dialog",
7537 MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
1088b922 7538 /* Use the low-level system abort. */
d54abccd
GV
7539 abort ();
7540 }
7541 else
7542 {
a302c7ae 7543 _close (fd);
d54abccd 7544 }
f52eb3ef 7545 }
f52eb3ef 7546}
480b0c5b
GV
7547
7548void
16b22fef 7549term_ntproc (int ignored)
480b0c5b 7550{
16b22fef 7551 (void)ignored;
c06c382a
EZ
7552
7553 term_timers ();
7554
480b0c5b
GV
7555 /* shutdown the socket interface if necessary */
7556 term_winsock ();
52c7f9ee
JR
7557
7558 term_w32select ();
480b0c5b
GV
7559}
7560
7561void
16b22fef 7562init_ntproc (int dumping)
480b0c5b 7563{
c06c382a
EZ
7564 sigset_t initial_mask = 0;
7565
e1dbe924 7566 /* Initialize the socket interface now if available and requested by
f249a012 7567 the user by defining PRELOAD_WINSOCK; otherwise loading will be
fbd6baed 7568 delayed until open-network-stream is called (w32-has-winsock can
f249a012
RS
7569 also be used to dynamically load or reload winsock).
7570
7571 Conveniently, init_environment is called before us, so
7572 PRELOAD_WINSOCK can be set in the registry. */
7573
7574 /* Always initialize this correctly. */
7575 winsock_lib = NULL;
7576
7577 if (getenv ("PRELOAD_WINSOCK") != NULL)
7578 init_winsock (TRUE);
480b0c5b
GV
7579
7580 /* Initial preparation for subprocess support: replace our standard
7581 handles with non-inheritable versions. */
7582 {
7583 HANDLE parent;
7584 HANDLE stdin_save = INVALID_HANDLE_VALUE;
7585 HANDLE stdout_save = INVALID_HANDLE_VALUE;
7586 HANDLE stderr_save = INVALID_HANDLE_VALUE;
7587
7588 parent = GetCurrentProcess ();
7589
7590 /* ignore errors when duplicating and closing; typically the
7591 handles will be invalid when running as a gui program. */
177c0ea7
JB
7592 DuplicateHandle (parent,
7593 GetStdHandle (STD_INPUT_HANDLE),
480b0c5b 7594 parent,
177c0ea7
JB
7595 &stdin_save,
7596 0,
7597 FALSE,
480b0c5b 7598 DUPLICATE_SAME_ACCESS);
177c0ea7 7599
480b0c5b
GV
7600 DuplicateHandle (parent,
7601 GetStdHandle (STD_OUTPUT_HANDLE),
7602 parent,
7603 &stdout_save,
7604 0,
7605 FALSE,
7606 DUPLICATE_SAME_ACCESS);
177c0ea7 7607
480b0c5b
GV
7608 DuplicateHandle (parent,
7609 GetStdHandle (STD_ERROR_HANDLE),
7610 parent,
7611 &stderr_save,
7612 0,
7613 FALSE,
7614 DUPLICATE_SAME_ACCESS);
177c0ea7 7615
480b0c5b
GV
7616 fclose (stdin);
7617 fclose (stdout);
7618 fclose (stderr);
7619
7620 if (stdin_save != INVALID_HANDLE_VALUE)
62aba0d4 7621 _open_osfhandle ((intptr_t) stdin_save, O_TEXT);
480b0c5b 7622 else
76b3903d
GV
7623 _open ("nul", O_TEXT | O_NOINHERIT | O_RDONLY);
7624 _fdopen (0, "r");
480b0c5b
GV
7625
7626 if (stdout_save != INVALID_HANDLE_VALUE)
62aba0d4 7627 _open_osfhandle ((intptr_t) stdout_save, O_TEXT);
480b0c5b 7628 else
76b3903d
GV
7629 _open ("nul", O_TEXT | O_NOINHERIT | O_WRONLY);
7630 _fdopen (1, "w");
480b0c5b
GV
7631
7632 if (stderr_save != INVALID_HANDLE_VALUE)
62aba0d4 7633 _open_osfhandle ((intptr_t) stderr_save, O_TEXT);
480b0c5b 7634 else
76b3903d
GV
7635 _open ("nul", O_TEXT | O_NOINHERIT | O_WRONLY);
7636 _fdopen (2, "w");
480b0c5b
GV
7637 }
7638
7639 /* unfortunately, atexit depends on implementation of malloc */
7640 /* atexit (term_ntproc); */
16b22fef 7641 if (!dumping)
c06c382a
EZ
7642 {
7643 /* Make sure we start with all signals unblocked. */
7644 sigprocmask (SIG_SETMASK, &initial_mask, NULL);
7645 signal (SIGABRT, term_ntproc);
7646 }
7647 init_timers ();
76b3903d
GV
7648
7649 /* determine which drives are fixed, for GetCachedVolumeInformation */
7650 {
7651 /* GetDriveType must have trailing backslash. */
7652 char drive[] = "A:\\";
7653
7654 /* Loop over all possible drive letters */
7655 while (*drive <= 'Z')
7656 {
7657 /* Record if this drive letter refers to a fixed drive. */
177c0ea7 7658 fixed_drives[DRIVE_INDEX (*drive)] =
76b3903d
GV
7659 (GetDriveType (drive) == DRIVE_FIXED);
7660
7661 (*drive)++;
7662 }
a302c7ae
AI
7663
7664 /* Reset the volume info cache. */
7665 volume_cache = NULL;
76b3903d 7666 }
480b0c5b
GV
7667}
7668
a8c3a596
JR
7669/*
7670 shutdown_handler ensures that buffers' autosave files are
7671 up to date when the user logs off, or the system shuts down.
7672*/
bedf4aab 7673static BOOL WINAPI
ed3751c8 7674shutdown_handler (DWORD type)
a8c3a596
JR
7675{
7676 /* Ctrl-C and Ctrl-Break are already suppressed, so don't handle them. */
7677 if (type == CTRL_CLOSE_EVENT /* User closes console window. */
7678 || type == CTRL_LOGOFF_EVENT /* User logs off. */
7679 || type == CTRL_SHUTDOWN_EVENT) /* User shutsdown. */
7680 {
7681 /* Shut down cleanly, making sure autosave files are up to date. */
1882aa38 7682 shut_down_emacs (0, Qnil);
a8c3a596
JR
7683 }
7684
7046f191 7685 /* Allow other handlers to handle this signal. */
a8c3a596
JR
7686 return FALSE;
7687}
7688
9785d95b
BK
7689/*
7690 globals_of_w32 is used to initialize those global variables that
7691 must always be initialized on startup even when the global variable
7692 initialized is non zero (see the function main in emacs.c).
7693*/
9bfb11f9 7694void
b56ceb92 7695globals_of_w32 (void)
9785d95b 7696{
74258518
JR
7697 HMODULE kernel32 = GetModuleHandle ("kernel32.dll");
7698
7699 get_process_times_fn = (GetProcessTimes_Proc)
7700 GetProcAddress (kernel32, "GetProcessTimes");
7701
cd3520a4 7702 DEFSYM (QCloaded_from, ":loaded-from");
0898ca10 7703
9785d95b
BK
7704 g_b_init_is_windows_9x = 0;
7705 g_b_init_open_process_token = 0;
7706 g_b_init_get_token_information = 0;
7707 g_b_init_lookup_account_sid = 0;
9d95a291
EZ
7708 g_b_init_get_sid_sub_authority = 0;
7709 g_b_init_get_sid_sub_authority_count = 0;
6dad7178 7710 g_b_init_get_security_info = 0;
8aaaec6b
EZ
7711 g_b_init_get_file_security = 0;
7712 g_b_init_get_security_descriptor_owner = 0;
7713 g_b_init_get_security_descriptor_group = 0;
7714 g_b_init_is_valid_sid = 0;
7c80d5ec
EZ
7715 g_b_init_create_toolhelp32_snapshot = 0;
7716 g_b_init_process32_first = 0;
7717 g_b_init_process32_next = 0;
7718 g_b_init_open_thread_token = 0;
7719 g_b_init_impersonate_self = 0;
7720 g_b_init_revert_to_self = 0;
7721 g_b_init_get_process_memory_info = 0;
7722 g_b_init_get_process_working_set_size = 0;
7723 g_b_init_global_memory_status = 0;
7724 g_b_init_global_memory_status_ex = 0;
f8b35b24
EZ
7725 g_b_init_equal_sid = 0;
7726 g_b_init_copy_sid = 0;
7727 g_b_init_get_length_sid = 0;
ad9e2d54
EZ
7728 g_b_init_get_native_system_info = 0;
7729 g_b_init_get_system_times = 0;
6dad7178 7730 g_b_init_create_symbolic_link = 0;
66447e07
EZ
7731 g_b_init_get_security_descriptor_dacl = 0;
7732 g_b_init_convert_sd_to_sddl = 0;
7733 g_b_init_convert_sddl_to_sd = 0;
7734 g_b_init_is_valid_security_descriptor = 0;
7735 g_b_init_set_file_security = 0;
ad9e2d54 7736 num_of_processors = 0;
a8c3a596
JR
7737 /* The following sets a handler for shutdown notifications for
7738 console apps. This actually applies to Emacs in both console and
7739 GUI modes, since we had to fool windows into thinking emacs is a
7740 console application to get console mode to work. */
ed3751c8 7741 SetConsoleCtrlHandler (shutdown_handler, TRUE);
8aaaec6b
EZ
7742
7743 /* "None" is the default group name on standalone workstations. */
7744 strcpy (dflt_group_name, "None");
5c207910
EZ
7745
7746 /* Reset, in case it has some value inherited from dump time. */
7747 w32_stat_get_owner_group = 0;
9785d95b
BK
7748}
7749
d888760c 7750/* For make-serial-process */
b56ceb92 7751int
4e604a5d 7752serial_open (Lisp_Object port_obj)
d888760c 7753{
4e604a5d 7754 char *port = SSDATA (port_obj);
d888760c
GM
7755 HANDLE hnd;
7756 child_process *cp;
7757 int fd = -1;
7758
7759 hnd = CreateFile (port, GENERIC_READ | GENERIC_WRITE, 0, 0,
7760 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
7761 if (hnd == INVALID_HANDLE_VALUE)
7762 error ("Could not open %s", port);
62aba0d4 7763 fd = (int) _open_osfhandle ((intptr_t) hnd, 0);
d888760c
GM
7764 if (fd == -1)
7765 error ("Could not open %s", port);
7766
7767 cp = new_child ();
7768 if (!cp)
7769 error ("Could not create child process");
7770 cp->fd = fd;
7771 cp->status = STATUS_READ_ACKNOWLEDGED;
7772 fd_info[ fd ].hnd = hnd;
7773 fd_info[ fd ].flags |=
7774 FILE_READ | FILE_WRITE | FILE_BINARY | FILE_SERIAL;
7775 if (fd_info[ fd ].cp != NULL)
7776 {
7777 error ("fd_info[fd = %d] is already in use", fd);
7778 }
7779 fd_info[ fd ].cp = cp;
7780 cp->ovl_read.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
7781 if (cp->ovl_read.hEvent == NULL)
7782 error ("Could not create read event");
7783 cp->ovl_write.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
7784 if (cp->ovl_write.hEvent == NULL)
7785 error ("Could not create write event");
7786
7787 return fd;
7788}
7789
7790/* For serial-process-configure */
7791void
9d4f32e8 7792serial_configure (struct Lisp_Process *p, Lisp_Object contact)
d888760c
GM
7793{
7794 Lisp_Object childp2 = Qnil;
7795 Lisp_Object tem = Qnil;
7796 HANDLE hnd;
7797 DCB dcb;
7798 COMMTIMEOUTS ct;
7799 char summary[4] = "???"; /* This usually becomes "8N1". */
7800
7801 if ((fd_info[ p->outfd ].flags & FILE_SERIAL) == 0)
7802 error ("Not a serial process");
7803 hnd = fd_info[ p->outfd ].hnd;
7804
4d2b044c 7805 childp2 = Fcopy_sequence (p->childp);
d888760c
GM
7806
7807 /* Initialize timeouts for blocking read and blocking write. */
7808 if (!GetCommTimeouts (hnd, &ct))
7809 error ("GetCommTimeouts() failed");
7810 ct.ReadIntervalTimeout = 0;
7811 ct.ReadTotalTimeoutMultiplier = 0;
7812 ct.ReadTotalTimeoutConstant = 0;
7813 ct.WriteTotalTimeoutMultiplier = 0;
7814 ct.WriteTotalTimeoutConstant = 0;
7815 if (!SetCommTimeouts (hnd, &ct))
7816 error ("SetCommTimeouts() failed");
7817 /* Read port attributes and prepare default configuration. */
7818 memset (&dcb, 0, sizeof (dcb));
7819 dcb.DCBlength = sizeof (DCB);
7820 if (!GetCommState (hnd, &dcb))
7821 error ("GetCommState() failed");
7822 dcb.fBinary = TRUE;
7823 dcb.fNull = FALSE;
7824 dcb.fAbortOnError = FALSE;
7825 /* dcb.XonLim and dcb.XoffLim are set by GetCommState() */
7826 dcb.ErrorChar = 0;
7827 dcb.EofChar = 0;
7828 dcb.EvtChar = 0;
7829
7830 /* Configure speed. */
7831 if (!NILP (Fplist_member (contact, QCspeed)))
7832 tem = Fplist_get (contact, QCspeed);
7833 else
4d2b044c 7834 tem = Fplist_get (p->childp, QCspeed);
d888760c
GM
7835 CHECK_NUMBER (tem);
7836 dcb.BaudRate = XINT (tem);
7837 childp2 = Fplist_put (childp2, QCspeed, tem);
7838
7839 /* Configure bytesize. */
7840 if (!NILP (Fplist_member (contact, QCbytesize)))
7841 tem = Fplist_get (contact, QCbytesize);
7842 else
4d2b044c 7843 tem = Fplist_get (p->childp, QCbytesize);
d888760c
GM
7844 if (NILP (tem))
7845 tem = make_number (8);
7846 CHECK_NUMBER (tem);
7847 if (XINT (tem) != 7 && XINT (tem) != 8)
7848 error (":bytesize must be nil (8), 7, or 8");
7849 dcb.ByteSize = XINT (tem);
7850 summary[0] = XINT (tem) + '0';
7851 childp2 = Fplist_put (childp2, QCbytesize, tem);
7852
7853 /* Configure parity. */
7854 if (!NILP (Fplist_member (contact, QCparity)))
7855 tem = Fplist_get (contact, QCparity);
7856 else
4d2b044c 7857 tem = Fplist_get (p->childp, QCparity);
d888760c
GM
7858 if (!NILP (tem) && !EQ (tem, Qeven) && !EQ (tem, Qodd))
7859 error (":parity must be nil (no parity), `even', or `odd'");
7860 dcb.fParity = FALSE;
7861 dcb.Parity = NOPARITY;
7862 dcb.fErrorChar = FALSE;
7863 if (NILP (tem))
7864 {
7865 summary[1] = 'N';
7866 }
7867 else if (EQ (tem, Qeven))
7868 {
7869 summary[1] = 'E';
7870 dcb.fParity = TRUE;
7871 dcb.Parity = EVENPARITY;
7872 dcb.fErrorChar = TRUE;
7873 }
7874 else if (EQ (tem, Qodd))
7875 {
7876 summary[1] = 'O';
7877 dcb.fParity = TRUE;
7878 dcb.Parity = ODDPARITY;
7879 dcb.fErrorChar = TRUE;
7880 }
7881 childp2 = Fplist_put (childp2, QCparity, tem);
7882
7883 /* Configure stopbits. */
7884 if (!NILP (Fplist_member (contact, QCstopbits)))
7885 tem = Fplist_get (contact, QCstopbits);
7886 else
4d2b044c 7887 tem = Fplist_get (p->childp, QCstopbits);
d888760c
GM
7888 if (NILP (tem))
7889 tem = make_number (1);
7890 CHECK_NUMBER (tem);
7891 if (XINT (tem) != 1 && XINT (tem) != 2)
7892 error (":stopbits must be nil (1 stopbit), 1, or 2");
7893 summary[2] = XINT (tem) + '0';
7894 if (XINT (tem) == 1)
7895 dcb.StopBits = ONESTOPBIT;
7896 else if (XINT (tem) == 2)
7897 dcb.StopBits = TWOSTOPBITS;
7898 childp2 = Fplist_put (childp2, QCstopbits, tem);
7899
7900 /* Configure flowcontrol. */
7901 if (!NILP (Fplist_member (contact, QCflowcontrol)))
7902 tem = Fplist_get (contact, QCflowcontrol);
7903 else
4d2b044c 7904 tem = Fplist_get (p->childp, QCflowcontrol);
d888760c
GM
7905 if (!NILP (tem) && !EQ (tem, Qhw) && !EQ (tem, Qsw))
7906 error (":flowcontrol must be nil (no flowcontrol), `hw', or `sw'");
7907 dcb.fOutxCtsFlow = FALSE;
7908 dcb.fOutxDsrFlow = FALSE;
7909 dcb.fDtrControl = DTR_CONTROL_DISABLE;
7910 dcb.fDsrSensitivity = FALSE;
7911 dcb.fTXContinueOnXoff = FALSE;
7912 dcb.fOutX = FALSE;
7913 dcb.fInX = FALSE;
7914 dcb.fRtsControl = RTS_CONTROL_DISABLE;
7915 dcb.XonChar = 17; /* Control-Q */
7916 dcb.XoffChar = 19; /* Control-S */
7917 if (NILP (tem))
7918 {
7919 /* Already configured. */
7920 }
7921 else if (EQ (tem, Qhw))
7922 {
7923 dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
7924 dcb.fOutxCtsFlow = TRUE;
7925 }
7926 else if (EQ (tem, Qsw))
7927 {
7928 dcb.fOutX = TRUE;
7929 dcb.fInX = TRUE;
7930 }
7931 childp2 = Fplist_put (childp2, QCflowcontrol, tem);
7932
7933 /* Activate configuration. */
7934 if (!SetCommState (hnd, &dcb))
7935 error ("SetCommState() failed");
7936
7937 childp2 = Fplist_put (childp2, QCsummary, build_string (summary));
6a09a33b 7938 pset_childp (p, childp2);
d888760c
GM
7939}
7940
e061a11b
TZ
7941#ifdef HAVE_GNUTLS
7942
7943ssize_t
7944emacs_gnutls_pull (gnutls_transport_ptr_t p, void* buf, size_t sz)
7945{
d78cf5ed 7946 int n, err;
e061a11b 7947 SELECT_TYPE fdset;
43aac990 7948 struct timespec timeout;
e061a11b
TZ
7949 struct Lisp_Process *process = (struct Lisp_Process *)p;
7950 int fd = process->infd;
7951
d78cf5ed 7952 n = sys_read (fd, (char*)buf, sz);
e061a11b 7953
d78cf5ed
CB
7954 if (n >= 0)
7955 return n;
e061a11b 7956
d78cf5ed 7957 err = errno;
e061a11b 7958
d78cf5ed
CB
7959 /* Translate the WSAEWOULDBLOCK alias EWOULDBLOCK to EAGAIN. */
7960 if (err == EWOULDBLOCK)
7961 err = EAGAIN;
e061a11b 7962
d78cf5ed 7963 emacs_gnutls_transport_set_errno (process->gnutls_state, err);
e061a11b 7964
d78cf5ed 7965 return -1;
e061a11b 7966}
ab5796a9 7967
e061a11b
TZ
7968ssize_t
7969emacs_gnutls_push (gnutls_transport_ptr_t p, const void* buf, size_t sz)
7970{
7971 struct Lisp_Process *process = (struct Lisp_Process *)p;
42ce4c63 7972 int fd = process->outfd;
5e617bc2 7973 ssize_t n = sys_write (fd, buf, sz);
e061a11b
TZ
7974
7975 /* 0 or more bytes written means everything went fine. */
7976 if (n >= 0)
7977 return n;
7978
7979 /* Negative bytes written means we got an error in errno.
7980 Translate the WSAEWOULDBLOCK alias EWOULDBLOCK to EAGAIN. */
0898ca10
JB
7981 emacs_gnutls_transport_set_errno (process->gnutls_state,
7982 errno == EWOULDBLOCK ? EAGAIN : errno);
e061a11b
TZ
7983
7984 return -1;
7985}
7986#endif /* HAVE_GNUTLS */
7987
7988/* end of w32.c */