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