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