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