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