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