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