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