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