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