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