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