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