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