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