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