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