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