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