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