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