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