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