(w32_system_process_attributes) [_MSC_VER < 1300]: Alternative calculation
[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))
235661f6 3995#if __GNUC__ || (defined (_MSC_VER) && _MSC_VER >= 1300)
7c80d5ec 3996 totphys = memstex.ullTotalPhys / 1024.0;
235661f6
EZ
3997#else
3998 /* Visual Studio 6 cannot convert an unsigned __int64 type to
3999 double, so we need to do this for it... */
4000 {
4001 DWORD tot_hi = memstex.ullTotalPhys >> 32;
4002 DWORD tot_md = (memstex.ullTotalPhys & 0x00000000ffffffff) >> 10;
4003 DWORD tot_lo = memstex.ullTotalPhys % 1024;
4004
4005 totphys = tot_hi * 4194304.0 + tot_md + tot_lo / 1024.0;
4006 }
4007#endif /* __GNUC__ || _MSC_VER >= 1300 */
7c80d5ec
EZ
4008 else if (global_memory_status (&memst))
4009 totphys = memst.dwTotalPhys / 1024.0;
4010
4011 if (h_proc
4012 && get_process_memory_info (h_proc, (PROCESS_MEMORY_COUNTERS *)&mem_ex,
4013 sizeof (mem_ex)))
4014 {
4015 DWORD rss = mem_ex.WorkingSetSize / 1024;
4016
4017 attrs = Fcons (Fcons (Qmajflt,
4018 make_fixnum_or_float (mem_ex.PageFaultCount)),
4019 attrs);
4020 attrs = Fcons (Fcons (Qvsize,
4021 make_fixnum_or_float (mem_ex.PrivateUsage / 1024)),
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_memory_info (h_proc, &mem, sizeof (mem)))
4029 {
4030 DWORD rss = mem_ex.WorkingSetSize / 1024;
4031
4032 attrs = Fcons (Fcons (Qmajflt,
4033 make_fixnum_or_float (mem.PageFaultCount)),
4034 attrs);
4035 attrs = Fcons (Fcons (Qrss, make_fixnum_or_float (rss)), attrs);
4036 if (totphys)
4037 attrs = Fcons (Fcons (Qpmem, make_float (100. * rss / totphys)), attrs);
4038 }
4039 else if (h_proc
4040 && get_process_working_set_size (h_proc, &minrss, &maxrss))
4041 {
4042 DWORD rss = maxrss / 1024;
4043
4044 attrs = Fcons (Fcons (Qrss, make_fixnum_or_float (maxrss / 1024)), attrs);
4045 if (totphys)
4046 attrs = Fcons (Fcons (Qpmem, make_float (100. * rss / totphys)), attrs);
4047 }
4048
4049 if (process_times (h_proc, &ctime, &etime, &stime, &utime, &pcpu))
4050 {
4051 attrs = Fcons (Fcons (Qutime, utime), attrs);
4052 attrs = Fcons (Fcons (Qstime, stime), attrs);
4053 attrs = Fcons (Fcons (Qstart, ctime), attrs);
4054 attrs = Fcons (Fcons (Qetime, etime), attrs);
4055 attrs = Fcons (Fcons (Qpcpu, make_float (pcpu)), attrs);
4056 }
4057
4058 /* FIXME: Retrieve command line by walking the PEB of the process. */
4059
4060 if (h_proc)
4061 CloseHandle (h_proc);
4062 UNGCPRO;
4063 return attrs;
4064}
4065
4066\f
480b0c5b
GV
4067#ifdef HAVE_SOCKETS
4068
4069/* Wrappers for winsock functions to map between our file descriptors
4070 and winsock's handles; also set h_errno for convenience.
4071
4072 To allow Emacs to run on systems which don't have winsock support
4073 installed, we dynamically link to winsock on startup if present, and
4074 otherwise provide the minimum necessary functionality
4075 (eg. gethostname). */
4076
4077/* function pointers for relevant socket functions */
4078int (PASCAL *pfn_WSAStartup) (WORD wVersionRequired, LPWSADATA lpWSAData);
4079void (PASCAL *pfn_WSASetLastError) (int iError);
4080int (PASCAL *pfn_WSAGetLastError) (void);
26fb7bc4 4081int (PASCAL *pfn_WSAEventSelect) (SOCKET s, HANDLE hEventObject, long lNetworkEvents);
64570b36
KS
4082HANDLE (PASCAL *pfn_WSACreateEvent) (void);
4083int (PASCAL *pfn_WSACloseEvent) (HANDLE hEvent);
480b0c5b
GV
4084int (PASCAL *pfn_socket) (int af, int type, int protocol);
4085int (PASCAL *pfn_bind) (SOCKET s, const struct sockaddr *addr, int namelen);
4086int (PASCAL *pfn_connect) (SOCKET s, const struct sockaddr *addr, int namelen);
4087int (PASCAL *pfn_ioctlsocket) (SOCKET s, long cmd, u_long *argp);
4088int (PASCAL *pfn_recv) (SOCKET s, char * buf, int len, int flags);
4089int (PASCAL *pfn_send) (SOCKET s, const char * buf, int len, int flags);
4090int (PASCAL *pfn_closesocket) (SOCKET s);
4091int (PASCAL *pfn_shutdown) (SOCKET s, int how);
4092int (PASCAL *pfn_WSACleanup) (void);
4093
4094u_short (PASCAL *pfn_htons) (u_short hostshort);
4095u_short (PASCAL *pfn_ntohs) (u_short netshort);
4096unsigned long (PASCAL *pfn_inet_addr) (const char * cp);
4097int (PASCAL *pfn_gethostname) (char * name, int namelen);
4098struct hostent * (PASCAL *pfn_gethostbyname) (const char * name);
4099struct servent * (PASCAL *pfn_getservbyname) (const char * name, const char * proto);
ecd270eb 4100int (PASCAL *pfn_getpeername) (SOCKET s, struct sockaddr *addr, int * namelen);
962955c5
JR
4101int (PASCAL *pfn_setsockopt) (SOCKET s, int level, int optname,
4102 const char * optval, int optlen);
4103int (PASCAL *pfn_listen) (SOCKET s, int backlog);
4104int (PASCAL *pfn_getsockname) (SOCKET s, struct sockaddr * name,
4105 int * namelen);
4106SOCKET (PASCAL *pfn_accept) (SOCKET s, struct sockaddr * addr, int * addrlen);
4107int (PASCAL *pfn_recvfrom) (SOCKET s, char * buf, int len, int flags,
4108 struct sockaddr * from, int * fromlen);
4109int (PASCAL *pfn_sendto) (SOCKET s, const char * buf, int len, int flags,
4110 const struct sockaddr * to, int tolen);
4111
f1614061
RS
4112/* SetHandleInformation is only needed to make sockets non-inheritable. */
4113BOOL (WINAPI *pfn_SetHandleInformation) (HANDLE object, DWORD mask, DWORD flags);
4114#ifndef HANDLE_FLAG_INHERIT
4115#define HANDLE_FLAG_INHERIT 1
4116#endif
480b0c5b 4117
f249a012
RS
4118HANDLE winsock_lib;
4119static int winsock_inuse;
480b0c5b 4120
f249a012 4121BOOL
480b0c5b
GV
4122term_winsock (void)
4123{
f249a012 4124 if (winsock_lib != NULL && winsock_inuse == 0)
480b0c5b 4125 {
f249a012
RS
4126 /* Not sure what would cause WSAENETDOWN, or even if it can happen
4127 after WSAStartup returns successfully, but it seems reasonable
4128 to allow unloading winsock anyway in that case. */
4129 if (pfn_WSACleanup () == 0 ||
4130 pfn_WSAGetLastError () == WSAENETDOWN)
4131 {
4132 if (FreeLibrary (winsock_lib))
4133 winsock_lib = NULL;
4134 return TRUE;
4135 }
480b0c5b 4136 }
f249a012 4137 return FALSE;
480b0c5b
GV
4138}
4139
f249a012
RS
4140BOOL
4141init_winsock (int load_now)
480b0c5b
GV
4142{
4143 WSADATA winsockData;
4144
f249a012
RS
4145 if (winsock_lib != NULL)
4146 return TRUE;
f1614061
RS
4147
4148 pfn_SetHandleInformation = NULL;
4149 pfn_SetHandleInformation
4150 = (void *) GetProcAddress (GetModuleHandle ("kernel32.dll"),
4151 "SetHandleInformation");
4152
64570b36 4153 winsock_lib = LoadLibrary ("Ws2_32.dll");
480b0c5b
GV
4154
4155 if (winsock_lib != NULL)
4156 {
4157 /* dynamically link to socket functions */
4158
4159#define LOAD_PROC(fn) \
4160 if ((pfn_##fn = (void *) GetProcAddress (winsock_lib, #fn)) == NULL) \
4161 goto fail;
4162
4163 LOAD_PROC( WSAStartup );
4164 LOAD_PROC( WSASetLastError );
4165 LOAD_PROC( WSAGetLastError );
26fb7bc4 4166 LOAD_PROC( WSAEventSelect );
64570b36
KS
4167 LOAD_PROC( WSACreateEvent );
4168 LOAD_PROC( WSACloseEvent );
480b0c5b
GV
4169 LOAD_PROC( socket );
4170 LOAD_PROC( bind );
4171 LOAD_PROC( connect );
4172 LOAD_PROC( ioctlsocket );
4173 LOAD_PROC( recv );
4174 LOAD_PROC( send );
4175 LOAD_PROC( closesocket );
4176 LOAD_PROC( shutdown );
4177 LOAD_PROC( htons );
4178 LOAD_PROC( ntohs );
4179 LOAD_PROC( inet_addr );
4180 LOAD_PROC( gethostname );
4181 LOAD_PROC( gethostbyname );
4182 LOAD_PROC( getservbyname );
ecd270eb 4183 LOAD_PROC( getpeername );
480b0c5b 4184 LOAD_PROC( WSACleanup );
962955c5
JR
4185 LOAD_PROC( setsockopt );
4186 LOAD_PROC( listen );
4187 LOAD_PROC( getsockname );
4188 LOAD_PROC( accept );
4189 LOAD_PROC( recvfrom );
4190 LOAD_PROC( sendto );
f249a012
RS
4191#undef LOAD_PROC
4192
480b0c5b
GV
4193 /* specify version 1.1 of winsock */
4194 if (pfn_WSAStartup (0x101, &winsockData) == 0)
4195 {
f249a012
RS
4196 if (winsockData.wVersion != 0x101)
4197 goto fail;
4198
4199 if (!load_now)
4200 {
4201 /* Report that winsock exists and is usable, but leave
4202 socket functions disabled. I am assuming that calling
4203 WSAStartup does not require any network interaction,
4204 and in particular does not cause or require a dial-up
4205 connection to be established. */
4206
4207 pfn_WSACleanup ();
4208 FreeLibrary (winsock_lib);
4209 winsock_lib = NULL;
4210 }
4211 winsock_inuse = 0;
4212 return TRUE;
480b0c5b
GV
4213 }
4214
4215 fail:
4216 FreeLibrary (winsock_lib);
f249a012 4217 winsock_lib = NULL;
480b0c5b 4218 }
f249a012
RS
4219
4220 return FALSE;
480b0c5b
GV
4221}
4222
4223
4224int h_errno = 0;
4225
f8381794 4226/* function to set h_errno for compatibility; map winsock error codes to
480b0c5b
GV
4227 normal system codes where they overlap (non-overlapping definitions
4228 are already in <sys/socket.h> */
9bfb11f9
KS
4229static void
4230set_errno ()
480b0c5b 4231{
f249a012 4232 if (winsock_lib == NULL)
480b0c5b
GV
4233 h_errno = EINVAL;
4234 else
4235 h_errno = pfn_WSAGetLastError ();
4236
4237 switch (h_errno)
4238 {
4239 case WSAEACCES: h_errno = EACCES; break;
4240 case WSAEBADF: h_errno = EBADF; break;
4241 case WSAEFAULT: h_errno = EFAULT; break;
4242 case WSAEINTR: h_errno = EINTR; break;
4243 case WSAEINVAL: h_errno = EINVAL; break;
4244 case WSAEMFILE: h_errno = EMFILE; break;
4245 case WSAENAMETOOLONG: h_errno = ENAMETOOLONG; break;
4246 case WSAENOTEMPTY: h_errno = ENOTEMPTY; break;
4247 }
4248 errno = h_errno;
4249}
4250
9bfb11f9
KS
4251static void
4252check_errno ()
480b0c5b 4253{
f249a012 4254 if (h_errno == 0 && winsock_lib != NULL)
480b0c5b
GV
4255 pfn_WSASetLastError (0);
4256}
4257
d8fcc1b9
AI
4258/* Extend strerror to handle the winsock-specific error codes. */
4259struct {
4260 int errnum;
4261 char * msg;
4262} _wsa_errlist[] = {
4263 WSAEINTR , "Interrupted function call",
4264 WSAEBADF , "Bad file descriptor",
4265 WSAEACCES , "Permission denied",
4266 WSAEFAULT , "Bad address",
4267 WSAEINVAL , "Invalid argument",
4268 WSAEMFILE , "Too many open files",
177c0ea7 4269
d8fcc1b9
AI
4270 WSAEWOULDBLOCK , "Resource temporarily unavailable",
4271 WSAEINPROGRESS , "Operation now in progress",
4272 WSAEALREADY , "Operation already in progress",
4273 WSAENOTSOCK , "Socket operation on non-socket",
4274 WSAEDESTADDRREQ , "Destination address required",
4275 WSAEMSGSIZE , "Message too long",
4276 WSAEPROTOTYPE , "Protocol wrong type for socket",
4277 WSAENOPROTOOPT , "Bad protocol option",
4278 WSAEPROTONOSUPPORT , "Protocol not supported",
4279 WSAESOCKTNOSUPPORT , "Socket type not supported",
4280 WSAEOPNOTSUPP , "Operation not supported",
4281 WSAEPFNOSUPPORT , "Protocol family not supported",
4282 WSAEAFNOSUPPORT , "Address family not supported by protocol family",
4283 WSAEADDRINUSE , "Address already in use",
4284 WSAEADDRNOTAVAIL , "Cannot assign requested address",
4285 WSAENETDOWN , "Network is down",
4286 WSAENETUNREACH , "Network is unreachable",
4287 WSAENETRESET , "Network dropped connection on reset",
4288 WSAECONNABORTED , "Software caused connection abort",
4289 WSAECONNRESET , "Connection reset by peer",
4290 WSAENOBUFS , "No buffer space available",
4291 WSAEISCONN , "Socket is already connected",
4292 WSAENOTCONN , "Socket is not connected",
4293 WSAESHUTDOWN , "Cannot send after socket shutdown",
4294 WSAETOOMANYREFS , "Too many references", /* not sure */
4295 WSAETIMEDOUT , "Connection timed out",
4296 WSAECONNREFUSED , "Connection refused",
4297 WSAELOOP , "Network loop", /* not sure */
4298 WSAENAMETOOLONG , "Name is too long",
4299 WSAEHOSTDOWN , "Host is down",
4300 WSAEHOSTUNREACH , "No route to host",
4301 WSAENOTEMPTY , "Buffer not empty", /* not sure */
4302 WSAEPROCLIM , "Too many processes",
4303 WSAEUSERS , "Too many users", /* not sure */
4304 WSAEDQUOT , "Double quote in host name", /* really not sure */
4305 WSAESTALE , "Data is stale", /* not sure */
4306 WSAEREMOTE , "Remote error", /* not sure */
177c0ea7 4307
d8fcc1b9
AI
4308 WSASYSNOTREADY , "Network subsystem is unavailable",
4309 WSAVERNOTSUPPORTED , "WINSOCK.DLL version out of range",
4310 WSANOTINITIALISED , "Winsock not initialized successfully",
4311 WSAEDISCON , "Graceful shutdown in progress",
4312#ifdef WSAENOMORE
4313 WSAENOMORE , "No more operations allowed", /* not sure */
4314 WSAECANCELLED , "Operation cancelled", /* not sure */
4315 WSAEINVALIDPROCTABLE , "Invalid procedure table from service provider",
4316 WSAEINVALIDPROVIDER , "Invalid service provider version number",
4317 WSAEPROVIDERFAILEDINIT , "Unable to initialize a service provider",
aa5ee2a3 4318 WSASYSCALLFAILURE , "System call failure",
d8fcc1b9
AI
4319 WSASERVICE_NOT_FOUND , "Service not found", /* not sure */
4320 WSATYPE_NOT_FOUND , "Class type not found",
4321 WSA_E_NO_MORE , "No more resources available", /* really not sure */
4322 WSA_E_CANCELLED , "Operation already cancelled", /* really not sure */
4323 WSAEREFUSED , "Operation refused", /* not sure */
4324#endif
177c0ea7 4325
d8fcc1b9
AI
4326 WSAHOST_NOT_FOUND , "Host not found",
4327 WSATRY_AGAIN , "Authoritative host not found during name lookup",
4328 WSANO_RECOVERY , "Non-recoverable error during name lookup",
4329 WSANO_DATA , "Valid name, no data record of requested type",
4330
4331 -1, NULL
4332};
4333
4334char *
4335sys_strerror(int error_no)
4336{
4337 int i;
4338 static char unknown_msg[40];
4339
a302c7ae
AI
4340 if (error_no >= 0 && error_no < sys_nerr)
4341 return sys_errlist[error_no];
d8fcc1b9
AI
4342
4343 for (i = 0; _wsa_errlist[i].errnum >= 0; i++)
4344 if (_wsa_errlist[i].errnum == error_no)
4345 return _wsa_errlist[i].msg;
4346
4347 sprintf(unknown_msg, "Unidentified error: %d", error_no);
4348 return unknown_msg;
4349}
4350
480b0c5b
GV
4351/* [andrewi 3-May-96] I've had conflicting results using both methods,
4352 but I believe the method of keeping the socket handle separate (and
4353 insuring it is not inheritable) is the correct one. */
4354
4355//#define SOCK_REPLACE_HANDLE
4356
4357#ifdef SOCK_REPLACE_HANDLE
4358#define SOCK_HANDLE(fd) ((SOCKET) _get_osfhandle (fd))
4359#else
4360#define SOCK_HANDLE(fd) ((SOCKET) fd_info[fd].hnd)
4361#endif
4362
962955c5
JR
4363int socket_to_fd (SOCKET s);
4364
480b0c5b
GV
4365int
4366sys_socket(int af, int type, int protocol)
4367{
962955c5 4368 SOCKET s;
480b0c5b 4369
f249a012 4370 if (winsock_lib == NULL)
480b0c5b
GV
4371 {
4372 h_errno = ENETDOWN;
4373 return INVALID_SOCKET;
4374 }
4375
4376 check_errno ();
4377
4378 /* call the real socket function */
962955c5 4379 s = pfn_socket (af, type, protocol);
177c0ea7 4380
480b0c5b 4381 if (s != INVALID_SOCKET)
962955c5 4382 return socket_to_fd (s);
480b0c5b 4383
962955c5
JR
4384 set_errno ();
4385 return -1;
4386}
4387
4388/* Convert a SOCKET to a file descriptor. */
4389int
4390socket_to_fd (SOCKET s)
4391{
4392 int fd;
4393 child_process * cp;
4394
4395 /* Although under NT 3.5 _open_osfhandle will accept a socket
4396 handle, if opened with SO_OPENTYPE == SO_SYNCHRONOUS_NONALERT,
4397 that does not work under NT 3.1. However, we can get the same
4398 effect by using a backdoor function to replace an existing
4399 descriptor handle with the one we want. */
4400
4401 /* allocate a file descriptor (with appropriate flags) */
4402 fd = _open ("NUL:", _O_RDWR);
4403 if (fd >= 0)
4404 {
480b0c5b 4405#ifdef SOCK_REPLACE_HANDLE
962955c5
JR
4406 /* now replace handle to NUL with our socket handle */
4407 CloseHandle ((HANDLE) _get_osfhandle (fd));
4408 _free_osfhnd (fd);
4409 _set_osfhnd (fd, s);
4410 /* setmode (fd, _O_BINARY); */
480b0c5b 4411#else
962955c5
JR
4412 /* Make a non-inheritable copy of the socket handle. Note
4413 that it is possible that sockets aren't actually kernel
4414 handles, which appears to be the case on Windows 9x when
4415 the MS Proxy winsock client is installed. */
4416 {
4417 /* Apparently there is a bug in NT 3.51 with some service
4418 packs, which prevents using DuplicateHandle to make a
4419 socket handle non-inheritable (causes WSACleanup to
4420 hang). The work-around is to use SetHandleInformation
4421 instead if it is available and implemented. */
4422 if (pfn_SetHandleInformation)
480b0c5b 4423 {
962955c5
JR
4424 pfn_SetHandleInformation ((HANDLE) s, HANDLE_FLAG_INHERIT, 0);
4425 }
4426 else
4427 {
4428 HANDLE parent = GetCurrentProcess ();
4429 HANDLE new_s = INVALID_HANDLE_VALUE;
4430
4431 if (DuplicateHandle (parent,
4432 (HANDLE) s,
4433 parent,
4434 &new_s,
4435 0,
4436 FALSE,
4437 DUPLICATE_SAME_ACCESS))
f1614061 4438 {
962955c5
JR
4439 /* It is possible that DuplicateHandle succeeds even
4440 though the socket wasn't really a kernel handle,
4441 because a real handle has the same value. So
4442 test whether the new handle really is a socket. */
4443 long nonblocking = 0;
4444 if (pfn_ioctlsocket ((SOCKET) new_s, FIONBIO, &nonblocking) == 0)
ca149beb 4445 {
962955c5
JR
4446 pfn_closesocket (s);
4447 s = (SOCKET) new_s;
4448 }
4449 else
4450 {
4451 CloseHandle (new_s);
4452 }
177c0ea7 4453 }
480b0c5b 4454 }
962955c5
JR
4455 }
4456 fd_info[fd].hnd = (HANDLE) s;
480b0c5b
GV
4457#endif
4458
962955c5
JR
4459 /* set our own internal flags */
4460 fd_info[fd].flags = FILE_SOCKET | FILE_BINARY | FILE_READ | FILE_WRITE;
480b0c5b 4461
962955c5
JR
4462 cp = new_child ();
4463 if (cp)
4464 {
4465 cp->fd = fd;
4466 cp->status = STATUS_READ_ACKNOWLEDGED;
480b0c5b 4467
962955c5
JR
4468 /* attach child_process to fd_info */
4469 if (fd_info[ fd ].cp != NULL)
4470 {
4471 DebPrint (("sys_socket: fd_info[%d] apparently in use!\n", fd));
4472 abort ();
480b0c5b
GV
4473 }
4474
962955c5
JR
4475 fd_info[ fd ].cp = cp;
4476
4477 /* success! */
4478 winsock_inuse++; /* count open sockets */
4479 return fd;
480b0c5b 4480 }
480b0c5b 4481
962955c5
JR
4482 /* clean up */
4483 _close (fd);
4484 }
4485 pfn_closesocket (s);
4486 h_errno = EMFILE;
480b0c5b
GV
4487 return -1;
4488}
4489
4490
4491int
4492sys_bind (int s, const struct sockaddr * addr, int namelen)
4493{
f249a012 4494 if (winsock_lib == NULL)
480b0c5b
GV
4495 {
4496 h_errno = ENOTSOCK;
4497 return SOCKET_ERROR;
4498 }
4499
4500 check_errno ();
4501 if (fd_info[s].flags & FILE_SOCKET)
4502 {
4503 int rc = pfn_bind (SOCK_HANDLE (s), addr, namelen);
4504 if (rc == SOCKET_ERROR)
4505 set_errno ();
4506 return rc;
4507 }
4508 h_errno = ENOTSOCK;
4509 return SOCKET_ERROR;
4510}
4511
4512
4513int
4514sys_connect (int s, const struct sockaddr * name, int namelen)
4515{
f249a012 4516 if (winsock_lib == NULL)
480b0c5b
GV
4517 {
4518 h_errno = ENOTSOCK;
4519 return SOCKET_ERROR;
4520 }
4521
4522 check_errno ();
4523 if (fd_info[s].flags & FILE_SOCKET)
4524 {
4525 int rc = pfn_connect (SOCK_HANDLE (s), name, namelen);
4526 if (rc == SOCKET_ERROR)
4527 set_errno ();
4528 return rc;
4529 }
4530 h_errno = ENOTSOCK;
4531 return SOCKET_ERROR;
4532}
4533
4534u_short
4535sys_htons (u_short hostshort)
4536{
f249a012 4537 return (winsock_lib != NULL) ?
480b0c5b
GV
4538 pfn_htons (hostshort) : hostshort;
4539}
4540
4541u_short
4542sys_ntohs (u_short netshort)
4543{
f249a012 4544 return (winsock_lib != NULL) ?
480b0c5b
GV
4545 pfn_ntohs (netshort) : netshort;
4546}
4547
4548unsigned long
4549sys_inet_addr (const char * cp)
4550{
f249a012 4551 return (winsock_lib != NULL) ?
480b0c5b
GV
4552 pfn_inet_addr (cp) : INADDR_NONE;
4553}
4554
4555int
4556sys_gethostname (char * name, int namelen)
4557{
f249a012 4558 if (winsock_lib != NULL)
480b0c5b
GV
4559 return pfn_gethostname (name, namelen);
4560
4561 if (namelen > MAX_COMPUTERNAME_LENGTH)
a302c7ae 4562 return !GetComputerName (name, (DWORD *)&namelen);
480b0c5b
GV
4563
4564 h_errno = EFAULT;
4565 return SOCKET_ERROR;
4566}
4567
4568struct hostent *
4569sys_gethostbyname(const char * name)
4570{
4571 struct hostent * host;
4572
f249a012 4573 if (winsock_lib == NULL)
480b0c5b
GV
4574 {
4575 h_errno = ENETDOWN;
4576 return NULL;
4577 }
4578
4579 check_errno ();
4580 host = pfn_gethostbyname (name);
4581 if (!host)
4582 set_errno ();
4583 return host;
4584}
4585
4586struct servent *
4587sys_getservbyname(const char * name, const char * proto)
4588{
4589 struct servent * serv;
4590
f249a012 4591 if (winsock_lib == NULL)
480b0c5b
GV
4592 {
4593 h_errno = ENETDOWN;
4594 return NULL;
4595 }
4596
4597 check_errno ();
4598 serv = pfn_getservbyname (name, proto);
4599 if (!serv)
4600 set_errno ();
4601 return serv;
4602}
4603
ecd270eb
JR
4604int
4605sys_getpeername (int s, struct sockaddr *addr, int * namelen)
4606{
4607 if (winsock_lib == NULL)
4608 {
4609 h_errno = ENETDOWN;
4610 return SOCKET_ERROR;
4611 }
4612
4613 check_errno ();
4614 if (fd_info[s].flags & FILE_SOCKET)
4615 {
4616 int rc = pfn_getpeername (SOCK_HANDLE (s), addr, namelen);
4617 if (rc == SOCKET_ERROR)
4618 set_errno ();
4619 return rc;
4620 }
4621 h_errno = ENOTSOCK;
4622 return SOCKET_ERROR;
4623}
4624
4625
380961a6
GV
4626int
4627sys_shutdown (int s, int how)
4628{
380961a6
GV
4629 if (winsock_lib == NULL)
4630 {
4631 h_errno = ENETDOWN;
4632 return SOCKET_ERROR;
4633 }
4634
4635 check_errno ();
4636 if (fd_info[s].flags & FILE_SOCKET)
4637 {
4638 int rc = pfn_shutdown (SOCK_HANDLE (s), how);
962955c5
JR
4639 if (rc == SOCKET_ERROR)
4640 set_errno ();
4641 return rc;
4642 }
4643 h_errno = ENOTSOCK;
4644 return SOCKET_ERROR;
4645}
4646
4647int
a5a389bb 4648sys_setsockopt (int s, int level, int optname, const void * optval, int optlen)
962955c5
JR
4649{
4650 if (winsock_lib == NULL)
4651 {
4652 h_errno = ENETDOWN;
4653 return SOCKET_ERROR;
4654 }
4655
4656 check_errno ();
4657 if (fd_info[s].flags & FILE_SOCKET)
4658 {
4659 int rc = pfn_setsockopt (SOCK_HANDLE (s), level, optname,
a5a389bb 4660 (const char *)optval, optlen);
962955c5
JR
4661 if (rc == SOCKET_ERROR)
4662 set_errno ();
4663 return rc;
4664 }
4665 h_errno = ENOTSOCK;
177c0ea7 4666 return SOCKET_ERROR;
962955c5
JR
4667}
4668
4669int
4670sys_listen (int s, int backlog)
4671{
4672 if (winsock_lib == NULL)
4673 {
4674 h_errno = ENETDOWN;
4675 return SOCKET_ERROR;
4676 }
4677
4678 check_errno ();
4679 if (fd_info[s].flags & FILE_SOCKET)
4680 {
4681 int rc = pfn_listen (SOCK_HANDLE (s), backlog);
4682 if (rc == SOCKET_ERROR)
4683 set_errno ();
26fb7bc4 4684 else
64570b36 4685 fd_info[s].flags |= FILE_LISTEN;
962955c5
JR
4686 return rc;
4687 }
4688 h_errno = ENOTSOCK;
177c0ea7 4689 return SOCKET_ERROR;
962955c5
JR
4690}
4691
4692int
4693sys_getsockname (int s, struct sockaddr * name, int * namelen)
4694{
4695 if (winsock_lib == NULL)
4696 {
4697 h_errno = ENETDOWN;
4698 return SOCKET_ERROR;
4699 }
4700
4701 check_errno ();
4702 if (fd_info[s].flags & FILE_SOCKET)
4703 {
4704 int rc = pfn_getsockname (SOCK_HANDLE (s), name, namelen);
4705 if (rc == SOCKET_ERROR)
4706 set_errno ();
4707 return rc;
4708 }
4709 h_errno = ENOTSOCK;
177c0ea7 4710 return SOCKET_ERROR;
962955c5
JR
4711}
4712
4713int
4714sys_accept (int s, struct sockaddr * addr, int * addrlen)
4715{
4716 if (winsock_lib == NULL)
4717 {
4718 h_errno = ENETDOWN;
4719 return -1;
4720 }
4721
4722 check_errno ();
26fb7bc4 4723 if (fd_info[s].flags & FILE_LISTEN)
962955c5 4724 {
a0ad1860 4725 SOCKET t = pfn_accept (SOCK_HANDLE (s), addr, addrlen);
64570b36
KS
4726 int fd = -1;
4727 if (t == INVALID_SOCKET)
4728 set_errno ();
4729 else
4730 fd = socket_to_fd (t);
962955c5 4731
64570b36
KS
4732 fd_info[s].cp->status = STATUS_READ_ACKNOWLEDGED;
4733 ResetEvent (fd_info[s].cp->char_avail);
4734 return fd;
962955c5
JR
4735 }
4736 h_errno = ENOTSOCK;
4737 return -1;
4738}
4739
4740int
4741sys_recvfrom (int s, char * buf, int len, int flags,
4742 struct sockaddr * from, int * fromlen)
4743{
4744 if (winsock_lib == NULL)
4745 {
4746 h_errno = ENETDOWN;
4747 return SOCKET_ERROR;
4748 }
4749
4750 check_errno ();
4751 if (fd_info[s].flags & FILE_SOCKET)
4752 {
4753 int rc = pfn_recvfrom (SOCK_HANDLE (s), buf, len, flags, from, fromlen);
4754 if (rc == SOCKET_ERROR)
4755 set_errno ();
4756 return rc;
4757 }
4758 h_errno = ENOTSOCK;
4759 return SOCKET_ERROR;
4760}
4761
4762int
4763sys_sendto (int s, const char * buf, int len, int flags,
4764 const struct sockaddr * to, int tolen)
4765{
4766 if (winsock_lib == NULL)
4767 {
4768 h_errno = ENETDOWN;
4769 return SOCKET_ERROR;
4770 }
4771
4772 check_errno ();
4773 if (fd_info[s].flags & FILE_SOCKET)
4774 {
4775 int rc = pfn_sendto (SOCK_HANDLE (s), buf, len, flags, to, tolen);
380961a6
GV
4776 if (rc == SOCKET_ERROR)
4777 set_errno ();
4778 return rc;
4779 }
4780 h_errno = ENOTSOCK;
4781 return SOCKET_ERROR;
4782}
4783
ecd270eb
JR
4784/* Windows does not have an fcntl function. Provide an implementation
4785 solely for making sockets non-blocking. */
4786int
4787fcntl (int s, int cmd, int options)
4788{
4789 if (winsock_lib == NULL)
4790 {
4791 h_errno = ENETDOWN;
4792 return -1;
4793 }
4794
4795 check_errno ();
4796 if (fd_info[s].flags & FILE_SOCKET)
4797 {
4798 if (cmd == F_SETFL && options == O_NDELAY)
4799 {
4800 unsigned long nblock = 1;
4801 int rc = pfn_ioctlsocket (SOCK_HANDLE (s), FIONBIO, &nblock);
4802 if (rc == SOCKET_ERROR)
4803 set_errno();
4804 /* Keep track of the fact that we set this to non-blocking. */
4805 fd_info[s].flags |= FILE_NDELAY;
4806 return rc;
4807 }
4808 else
4809 {
4810 h_errno = EINVAL;
4811 return SOCKET_ERROR;
4812 }
4813 }
4814 h_errno = ENOTSOCK;
4815 return SOCKET_ERROR;
4816}
4817
480b0c5b
GV
4818#endif /* HAVE_SOCKETS */
4819
4820
4821/* Shadow main io functions: we need to handle pipes and sockets more
4822 intelligently, and implement non-blocking mode as well. */
4823
4824int
4825sys_close (int fd)
4826{
4827 int rc;
4828
7559f399 4829 if (fd < 0)
480b0c5b
GV
4830 {
4831 errno = EBADF;
4832 return -1;
4833 }
4834
7559f399 4835 if (fd < MAXDESC && fd_info[fd].cp)
480b0c5b
GV
4836 {
4837 child_process * cp = fd_info[fd].cp;
4838
4839 fd_info[fd].cp = NULL;
4840
4841 if (CHILD_ACTIVE (cp))
4842 {
4843 /* if last descriptor to active child_process then cleanup */
4844 int i;
4845 for (i = 0; i < MAXDESC; i++)
4846 {
4847 if (i == fd)
4848 continue;
4849 if (fd_info[i].cp == cp)
4850 break;
4851 }
4852 if (i == MAXDESC)
4853 {
f249a012 4854#ifdef HAVE_SOCKETS
480b0c5b
GV
4855 if (fd_info[fd].flags & FILE_SOCKET)
4856 {
f249a012
RS
4857#ifndef SOCK_REPLACE_HANDLE
4858 if (winsock_lib == NULL) abort ();
480b0c5b
GV
4859
4860 pfn_shutdown (SOCK_HANDLE (fd), 2);
4861 rc = pfn_closesocket (SOCK_HANDLE (fd));
f249a012
RS
4862#endif
4863 winsock_inuse--; /* count open sockets */
480b0c5b
GV
4864 }
4865#endif
4866 delete_child (cp);
4867 }
4868 }
4869 }
4870
4871 /* Note that sockets do not need special treatment here (at least on
e9e23e23 4872 NT and Windows 95 using the standard tcp/ip stacks) - it appears that
480b0c5b
GV
4873 closesocket is equivalent to CloseHandle, which is to be expected
4874 because socket handles are fully fledged kernel handles. */
4875 rc = _close (fd);
4876
7559f399 4877 if (rc == 0 && fd < MAXDESC)
480b0c5b
GV
4878 fd_info[fd].flags = 0;
4879
4880 return rc;
4881}
4882
4883int
4884sys_dup (int fd)
4885{
4886 int new_fd;
4887
4888 new_fd = _dup (fd);
7559f399 4889 if (new_fd >= 0 && new_fd < MAXDESC)
480b0c5b
GV
4890 {
4891 /* duplicate our internal info as well */
4892 fd_info[new_fd] = fd_info[fd];
4893 }
4894 return new_fd;
4895}
4896
4897
4898int
4899sys_dup2 (int src, int dst)
4900{
4901 int rc;
4902
4903 if (dst < 0 || dst >= MAXDESC)
4904 {
4905 errno = EBADF;
4906 return -1;
4907 }
4908
4909 /* make sure we close the destination first if it's a pipe or socket */
4910 if (src != dst && fd_info[dst].flags != 0)
4911 sys_close (dst);
177c0ea7 4912
480b0c5b
GV
4913 rc = _dup2 (src, dst);
4914 if (rc == 0)
4915 {
4916 /* duplicate our internal info as well */
4917 fd_info[dst] = fd_info[src];
4918 }
4919 return rc;
4920}
4921
480b0c5b
GV
4922/* Unix pipe() has only one arg */
4923int
4924sys_pipe (int * phandles)
4925{
4926 int rc;
4927 unsigned flags;
480b0c5b 4928
76b3903d
GV
4929 /* make pipe handles non-inheritable; when we spawn a child, we
4930 replace the relevant handle with an inheritable one. Also put
4931 pipes into binary mode; we will do text mode translation ourselves
4932 if required. */
4933 rc = _pipe (phandles, 0, _O_NOINHERIT | _O_BINARY);
480b0c5b
GV
4934
4935 if (rc == 0)
4936 {
cb72110d
JR
4937 /* Protect against overflow, since Windows can open more handles than
4938 our fd_info array has room for. */
4939 if (phandles[0] >= MAXDESC || phandles[1] >= MAXDESC)
4940 {
4941 _close (phandles[0]);
4942 _close (phandles[1]);
4943 rc = -1;
4944 }
4945 else
4946 {
4947 flags = FILE_PIPE | FILE_READ | FILE_BINARY;
4948 fd_info[phandles[0]].flags = flags;
480b0c5b 4949
cb72110d
JR
4950 flags = FILE_PIPE | FILE_WRITE | FILE_BINARY;
4951 fd_info[phandles[1]].flags = flags;
4952 }
480b0c5b
GV
4953 }
4954
4955 return rc;
4956}
4957
f7554349 4958/* From ntproc.c */
78806724 4959extern int w32_pipe_read_delay;
f7554349 4960
480b0c5b
GV
4961/* Function to do blocking read of one byte, needed to implement
4962 select. It is only allowed on sockets and pipes. */
4963int
4964_sys_read_ahead (int fd)
4965{
4966 child_process * cp;
4967 int rc;
4968
4969 if (fd < 0 || fd >= MAXDESC)
4970 return STATUS_READ_ERROR;
4971
4972 cp = fd_info[fd].cp;
4973
4974 if (cp == NULL || cp->fd != fd || cp->status != STATUS_READ_READY)
4975 return STATUS_READ_ERROR;
4976
d888760c 4977 if ((fd_info[fd].flags & (FILE_PIPE | FILE_SERIAL | FILE_SOCKET)) == 0
480b0c5b
GV
4978 || (fd_info[fd].flags & FILE_READ) == 0)
4979 {
d888760c 4980 DebPrint (("_sys_read_ahead: internal error: fd %d is not a pipe, serial port, or socket!\n", fd));
480b0c5b
GV
4981 abort ();
4982 }
177c0ea7 4983
480b0c5b 4984 cp->status = STATUS_READ_IN_PROGRESS;
177c0ea7 4985
480b0c5b 4986 if (fd_info[fd].flags & FILE_PIPE)
f7554349 4987 {
f7554349
KH
4988 rc = _read (fd, &cp->chr, sizeof (char));
4989
4990 /* Give subprocess time to buffer some more output for us before
e9e23e23 4991 reporting that input is available; we need this because Windows 95
f7554349
KH
4992 connects DOS programs to pipes by making the pipe appear to be
4993 the normal console stdout - as a result most DOS programs will
d888760c 4994 write to stdout without buffering, ie. one character at a
fbd6baed 4995 time. Even some W32 programs do this - "dir" in a command
f7554349
KH
4996 shell on NT is very slow if we don't do this. */
4997 if (rc > 0)
4998 {
78806724 4999 int wait = w32_pipe_read_delay;
f7554349
KH
5000
5001 if (wait > 0)
5002 Sleep (wait);
5003 else if (wait < 0)
5004 while (++wait <= 0)
5005 /* Yield remainder of our time slice, effectively giving a
5006 temporary priority boost to the child process. */
5007 Sleep (0);
5008 }
5009 }
d888760c
GM
5010 else if (fd_info[fd].flags & FILE_SERIAL)
5011 {
5012 HANDLE hnd = fd_info[fd].hnd;
5013 OVERLAPPED *ovl = &fd_info[fd].cp->ovl_read;
5014 COMMTIMEOUTS ct;
5015
5016 /* Configure timeouts for blocking read. */
5017 if (!GetCommTimeouts (hnd, &ct))
5018 return STATUS_READ_ERROR;
5019 ct.ReadIntervalTimeout = 0;
5020 ct.ReadTotalTimeoutMultiplier = 0;
5021 ct.ReadTotalTimeoutConstant = 0;
5022 if (!SetCommTimeouts (hnd, &ct))
5023 return STATUS_READ_ERROR;
5024
5025 if (!ReadFile (hnd, &cp->chr, sizeof (char), (DWORD*) &rc, ovl))
5026 {
5027 if (GetLastError () != ERROR_IO_PENDING)
5028 return STATUS_READ_ERROR;
5029 if (!GetOverlappedResult (hnd, ovl, (DWORD*) &rc, TRUE))
5030 return STATUS_READ_ERROR;
5031 }
5032 }
480b0c5b
GV
5033#ifdef HAVE_SOCKETS
5034 else if (fd_info[fd].flags & FILE_SOCKET)
ecd270eb
JR
5035 {
5036 unsigned long nblock = 0;
5037 /* We always want this to block, so temporarily disable NDELAY. */
5038 if (fd_info[fd].flags & FILE_NDELAY)
5039 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
5040
5041 rc = pfn_recv (SOCK_HANDLE (fd), &cp->chr, sizeof (char), 0);
5042
5043 if (fd_info[fd].flags & FILE_NDELAY)
5044 {
5045 nblock = 1;
5046 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
5047 }
5048 }
480b0c5b 5049#endif
177c0ea7 5050
480b0c5b
GV
5051 if (rc == sizeof (char))
5052 cp->status = STATUS_READ_SUCCEEDED;
5053 else
5054 cp->status = STATUS_READ_FAILED;
5055
5056 return cp->status;
5057}
5058
9bfb11f9
KS
5059int
5060_sys_wait_accept (int fd)
64570b36
KS
5061{
5062 HANDLE hEv;
5063 child_process * cp;
5064 int rc;
5065
5066 if (fd < 0 || fd >= MAXDESC)
5067 return STATUS_READ_ERROR;
5068
5069 cp = fd_info[fd].cp;
5070
5071 if (cp == NULL || cp->fd != fd || cp->status != STATUS_READ_READY)
5072 return STATUS_READ_ERROR;
5073
5074 cp->status = STATUS_READ_FAILED;
5075
5076 hEv = pfn_WSACreateEvent ();
5077 rc = pfn_WSAEventSelect (SOCK_HANDLE (fd), hEv, FD_ACCEPT);
5078 if (rc != SOCKET_ERROR)
5079 {
5080 rc = WaitForSingleObject (hEv, INFINITE);
5081 pfn_WSAEventSelect (SOCK_HANDLE (fd), NULL, 0);
64570b36
KS
5082 if (rc == WAIT_OBJECT_0)
5083 cp->status = STATUS_READ_SUCCEEDED;
5084 }
7046f191 5085 pfn_WSACloseEvent (hEv);
64570b36
KS
5086
5087 return cp->status;
5088}
5089
480b0c5b
GV
5090int
5091sys_read (int fd, char * buffer, unsigned int count)
5092{
5093 int nchars;
480b0c5b
GV
5094 int to_read;
5095 DWORD waiting;
76b3903d 5096 char * orig_buffer = buffer;
480b0c5b 5097
7559f399 5098 if (fd < 0)
480b0c5b
GV
5099 {
5100 errno = EBADF;
5101 return -1;
5102 }
5103
d888760c 5104 if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET | FILE_SERIAL))
480b0c5b
GV
5105 {
5106 child_process *cp = fd_info[fd].cp;
5107
5108 if ((fd_info[fd].flags & FILE_READ) == 0)
5109 {
5110 errno = EBADF;
5111 return -1;
5112 }
5113
76b3903d
GV
5114 nchars = 0;
5115
5116 /* re-read CR carried over from last read */
5117 if (fd_info[fd].flags & FILE_LAST_CR)
5118 {
5119 if (fd_info[fd].flags & FILE_BINARY) abort ();
5120 *buffer++ = 0x0d;
5121 count--;
5122 nchars++;
f52eb3ef 5123 fd_info[fd].flags &= ~FILE_LAST_CR;
76b3903d
GV
5124 }
5125
480b0c5b
GV
5126 /* presence of a child_process structure means we are operating in
5127 non-blocking mode - otherwise we just call _read directly.
5128 Note that the child_process structure might be missing because
5129 reap_subprocess has been called; in this case the pipe is
5130 already broken, so calling _read on it is okay. */
5131 if (cp)
5132 {
5133 int current_status = cp->status;
5134
5135 switch (current_status)
5136 {
5137 case STATUS_READ_FAILED:
5138 case STATUS_READ_ERROR:
f52eb3ef
GV
5139 /* report normal EOF if nothing in buffer */
5140 if (nchars <= 0)
5141 fd_info[fd].flags |= FILE_AT_EOF;
5142 return nchars;
480b0c5b
GV
5143
5144 case STATUS_READ_READY:
5145 case STATUS_READ_IN_PROGRESS:
5146 DebPrint (("sys_read called when read is in progress\n"));
5147 errno = EWOULDBLOCK;
5148 return -1;
5149
5150 case STATUS_READ_SUCCEEDED:
5151 /* consume read-ahead char */
5152 *buffer++ = cp->chr;
5153 count--;
76b3903d 5154 nchars++;
480b0c5b
GV
5155 cp->status = STATUS_READ_ACKNOWLEDGED;
5156 ResetEvent (cp->char_avail);
5157
5158 case STATUS_READ_ACKNOWLEDGED:
5159 break;
5160
5161 default:
5162 DebPrint (("sys_read: bad status %d\n", current_status));
5163 errno = EBADF;
5164 return -1;
5165 }
5166
5167 if (fd_info[fd].flags & FILE_PIPE)
5168 {
5169 PeekNamedPipe ((HANDLE) _get_osfhandle (fd), NULL, 0, NULL, &waiting, NULL);
5170 to_read = min (waiting, (DWORD) count);
f52eb3ef
GV
5171
5172 if (to_read > 0)
5173 nchars += _read (fd, buffer, to_read);
480b0c5b 5174 }
d888760c
GM
5175 else if (fd_info[fd].flags & FILE_SERIAL)
5176 {
5177 HANDLE hnd = fd_info[fd].hnd;
5178 OVERLAPPED *ovl = &fd_info[fd].cp->ovl_read;
5179 DWORD err = 0;
5180 int rc = 0;
5181 COMMTIMEOUTS ct;
5182
5183 if (count > 0)
5184 {
5185 /* Configure timeouts for non-blocking read. */
5186 if (!GetCommTimeouts (hnd, &ct))
5187 {
5188 errno = EIO;
5189 return -1;
5190 }
5191 ct.ReadIntervalTimeout = MAXDWORD;
5192 ct.ReadTotalTimeoutMultiplier = 0;
5193 ct.ReadTotalTimeoutConstant = 0;
5194 if (!SetCommTimeouts (hnd, &ct))
5195 {
5196 errno = EIO;
5197 return -1;
5198 }
5199
5200 if (!ResetEvent (ovl->hEvent))
5201 {
5202 errno = EIO;
5203 return -1;
5204 }
5205 if (!ReadFile (hnd, buffer, count, (DWORD*) &rc, ovl))
5206 {
5207 if (GetLastError () != ERROR_IO_PENDING)
5208 {
5209 errno = EIO;
5210 return -1;
5211 }
5212 if (!GetOverlappedResult (hnd, ovl, (DWORD*) &rc, TRUE))
5213 {
5214 errno = EIO;
5215 return -1;
5216 }
5217 }
5218 nchars += rc;
5219 }
5220 }
480b0c5b
GV
5221#ifdef HAVE_SOCKETS
5222 else /* FILE_SOCKET */
5223 {
f249a012 5224 if (winsock_lib == NULL) abort ();
480b0c5b
GV
5225
5226 /* do the equivalent of a non-blocking read */
5227 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONREAD, &waiting);
76b3903d 5228 if (waiting == 0 && nchars == 0)
480b0c5b
GV
5229 {
5230 h_errno = errno = EWOULDBLOCK;
5231 return -1;
5232 }
5233
480b0c5b
GV
5234 if (waiting)
5235 {
5236 /* always use binary mode for sockets */
76b3903d
GV
5237 int res = pfn_recv (SOCK_HANDLE (fd), buffer, count, 0);
5238 if (res == SOCKET_ERROR)
480b0c5b
GV
5239 {
5240 DebPrint(("sys_read.recv failed with error %d on socket %ld\n",
5241 pfn_WSAGetLastError (), SOCK_HANDLE (fd)));
76b3903d
GV
5242 set_errno ();
5243 return -1;
480b0c5b 5244 }
76b3903d 5245 nchars += res;
480b0c5b
GV
5246 }
5247 }
5248#endif
5249 }
5250 else
f52eb3ef
GV
5251 {
5252 int nread = _read (fd, buffer, count);
5253 if (nread >= 0)
5254 nchars += nread;
5255 else if (nchars == 0)
5256 nchars = nread;
5257 }
76b3903d 5258
f52eb3ef
GV
5259 if (nchars <= 0)
5260 fd_info[fd].flags |= FILE_AT_EOF;
76b3903d 5261 /* Perform text mode translation if required. */
f52eb3ef 5262 else if ((fd_info[fd].flags & FILE_BINARY) == 0)
76b3903d
GV
5263 {
5264 nchars = crlf_to_lf (nchars, orig_buffer);
5265 /* If buffer contains only CR, return that. To be absolutely
5266 sure we should attempt to read the next char, but in
5267 practice a CR to be followed by LF would not appear by
5268 itself in the buffer. */
5269 if (nchars > 1 && orig_buffer[nchars - 1] == 0x0d)
5270 {
5271 fd_info[fd].flags |= FILE_LAST_CR;
5272 nchars--;
5273 }
76b3903d 5274 }
480b0c5b
GV
5275 }
5276 else
5277 nchars = _read (fd, buffer, count);
5278
76b3903d 5279 return nchars;
480b0c5b
GV
5280}
5281
d888760c
GM
5282/* From w32xfns.c */
5283extern HANDLE interrupt_handle;
5284
480b0c5b
GV
5285/* For now, don't bother with a non-blocking mode */
5286int
5287sys_write (int fd, const void * buffer, unsigned int count)
5288{
5289 int nchars;
5290
7559f399 5291 if (fd < 0)
480b0c5b
GV
5292 {
5293 errno = EBADF;
5294 return -1;
5295 }
5296
d888760c 5297 if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET | FILE_SERIAL))
76b3903d
GV
5298 {
5299 if ((fd_info[fd].flags & FILE_WRITE) == 0)
5300 {
5301 errno = EBADF;
5302 return -1;
5303 }
5304
5305 /* Perform text mode translation if required. */
5306 if ((fd_info[fd].flags & FILE_BINARY) == 0)
5307 {
5308 char * tmpbuf = alloca (count * 2);
5309 unsigned char * src = (void *)buffer;
5310 unsigned char * dst = tmpbuf;
5311 int nbytes = count;
5312
5313 while (1)
5314 {
5315 unsigned char *next;
5316 /* copy next line or remaining bytes */
5317 next = _memccpy (dst, src, '\n', nbytes);
5318 if (next)
5319 {
5320 /* copied one line ending with '\n' */
5321 int copied = next - dst;
5322 nbytes -= copied;
5323 src += copied;
5324 /* insert '\r' before '\n' */
5325 next[-1] = '\r';
5326 next[0] = '\n';
5327 dst = next + 1;
5328 count++;
177c0ea7 5329 }
76b3903d
GV
5330 else
5331 /* copied remaining partial line -> now finished */
5332 break;
5333 }
5334 buffer = tmpbuf;
5335 }
5336 }
5337
d888760c
GM
5338 if (fd < MAXDESC && fd_info[fd].flags & FILE_SERIAL)
5339 {
5340 HANDLE hnd = (HANDLE) _get_osfhandle (fd);
5341 OVERLAPPED *ovl = &fd_info[fd].cp->ovl_write;
5342 HANDLE wait_hnd[2] = { interrupt_handle, ovl->hEvent };
5343 DWORD active = 0;
5344
5345 if (!WriteFile (hnd, buffer, count, (DWORD*) &nchars, ovl))
5346 {
5347 if (GetLastError () != ERROR_IO_PENDING)
5348 {
5349 errno = EIO;
5350 return -1;
5351 }
5352 if (detect_input_pending ())
5353 active = MsgWaitForMultipleObjects (2, wait_hnd, FALSE, INFINITE,
5354 QS_ALLINPUT);
5355 else
5356 active = WaitForMultipleObjects (2, wait_hnd, FALSE, INFINITE);
5357 if (active == WAIT_OBJECT_0)
5358 { /* User pressed C-g, cancel write, then leave. Don't bother
5359 cleaning up as we may only get stuck in buggy drivers. */
5360 PurgeComm (hnd, PURGE_TXABORT | PURGE_TXCLEAR);
5361 CancelIo (hnd);
5362 errno = EIO;
5363 return -1;
5364 }
5365 if (active == WAIT_OBJECT_0 + 1
5366 && !GetOverlappedResult (hnd, ovl, (DWORD*) &nchars, TRUE))
5367 {
5368 errno = EIO;
5369 return -1;
5370 }
5371 }
5372 }
5373 else
480b0c5b 5374#ifdef HAVE_SOCKETS
7559f399 5375 if (fd < MAXDESC && fd_info[fd].flags & FILE_SOCKET)
480b0c5b 5376 {
30a32e0e 5377 unsigned long nblock = 0;
f249a012 5378 if (winsock_lib == NULL) abort ();
30a32e0e
JR
5379
5380 /* TODO: implement select() properly so non-blocking I/O works. */
5381 /* For now, make sure the write blocks. */
5382 if (fd_info[fd].flags & FILE_NDELAY)
5383 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
5384
480b0c5b 5385 nchars = pfn_send (SOCK_HANDLE (fd), buffer, count, 0);
30a32e0e
JR
5386
5387 /* Set the socket back to non-blocking if it was before,
5388 for other operations that support it. */
5389 if (fd_info[fd].flags & FILE_NDELAY)
5390 {
5391 nblock = 1;
5392 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
5393 }
5394
480b0c5b
GV
5395 if (nchars == SOCKET_ERROR)
5396 {
670773af 5397 DebPrint(("sys_write.send failed with error %d on socket %ld\n",
480b0c5b
GV
5398 pfn_WSAGetLastError (), SOCK_HANDLE (fd)));
5399 set_errno ();
5400 }
5401 }
5402 else
5403#endif
5404 nchars = _write (fd, buffer, count);
5405
5406 return nchars;
5407}
5408
f52eb3ef
GV
5409static void
5410check_windows_init_file ()
5411{
5412 extern int noninteractive, inhibit_window_system;
5413
5414 /* A common indication that Emacs is not installed properly is when
5415 it cannot find the Windows installation file. If this file does
5416 not exist in the expected place, tell the user. */
5417
177c0ea7 5418 if (!noninteractive && !inhibit_window_system)
d54abccd
GV
5419 {
5420 extern Lisp_Object Vwindow_system, Vload_path, Qfile_exists_p;
a0b9c838 5421 Lisp_Object objs[2];
96ef7d42 5422 Lisp_Object full_load_path;
d54abccd
GV
5423 Lisp_Object init_file;
5424 int fd;
f52eb3ef 5425
a0b9c838
GV
5426 objs[0] = Vload_path;
5427 objs[1] = decode_env_path (0, (getenv ("EMACSLOADPATH")));
5428 full_load_path = Fappend (2, objs);
d54abccd 5429 init_file = build_string ("term/w32-win");
c50a2aa6 5430 fd = openp (full_load_path, init_file, Fget_load_suffixes (), NULL, Qnil);
177c0ea7 5431 if (fd < 0)
d54abccd 5432 {
96ef7d42 5433 Lisp_Object load_path_print = Fprin1_to_string (full_load_path, Qnil);
d5db4077
KR
5434 char *init_file_name = SDATA (init_file);
5435 char *load_path = SDATA (load_path_print);
acc23b87
KS
5436 char *buffer = alloca (1024
5437 + strlen (init_file_name)
5438 + strlen (load_path));
d54abccd 5439
177c0ea7 5440 sprintf (buffer,
d54abccd
GV
5441 "The Emacs Windows initialization file \"%s.el\" "
5442 "could not be found in your Emacs installation. "
5443 "Emacs checked the following directories for this file:\n"
5444 "\n%s\n\n"
5445 "When Emacs cannot find this file, it usually means that it "
5446 "was not installed properly, or its distribution file was "
5447 "not unpacked properly.\nSee the README.W32 file in the "
5448 "top-level Emacs directory for more information.",
5449 init_file_name, load_path);
5450 MessageBox (NULL,
5451 buffer,
5452 "Emacs Abort Dialog",
5453 MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
f52eb3ef
GV
5454 /* Use the low-level Emacs abort. */
5455#undef abort
d54abccd
GV
5456 abort ();
5457 }
5458 else
5459 {
a302c7ae 5460 _close (fd);
d54abccd 5461 }
f52eb3ef 5462 }
f52eb3ef 5463}
480b0c5b
GV
5464
5465void
5466term_ntproc ()
5467{
5468#ifdef HAVE_SOCKETS
5469 /* shutdown the socket interface if necessary */
5470 term_winsock ();
5471#endif
52c7f9ee
JR
5472
5473 term_w32select ();
480b0c5b
GV
5474}
5475
5476void
5477init_ntproc ()
5478{
5479#ifdef HAVE_SOCKETS
f249a012
RS
5480 /* Initialise the socket interface now if available and requested by
5481 the user by defining PRELOAD_WINSOCK; otherwise loading will be
fbd6baed 5482 delayed until open-network-stream is called (w32-has-winsock can
f249a012
RS
5483 also be used to dynamically load or reload winsock).
5484
5485 Conveniently, init_environment is called before us, so
5486 PRELOAD_WINSOCK can be set in the registry. */
5487
5488 /* Always initialize this correctly. */
5489 winsock_lib = NULL;
5490
5491 if (getenv ("PRELOAD_WINSOCK") != NULL)
5492 init_winsock (TRUE);
480b0c5b
GV
5493#endif
5494
5495 /* Initial preparation for subprocess support: replace our standard
5496 handles with non-inheritable versions. */
5497 {
5498 HANDLE parent;
5499 HANDLE stdin_save = INVALID_HANDLE_VALUE;
5500 HANDLE stdout_save = INVALID_HANDLE_VALUE;
5501 HANDLE stderr_save = INVALID_HANDLE_VALUE;
5502
5503 parent = GetCurrentProcess ();
5504
5505 /* ignore errors when duplicating and closing; typically the
5506 handles will be invalid when running as a gui program. */
177c0ea7
JB
5507 DuplicateHandle (parent,
5508 GetStdHandle (STD_INPUT_HANDLE),
480b0c5b 5509 parent,
177c0ea7
JB
5510 &stdin_save,
5511 0,
5512 FALSE,
480b0c5b 5513 DUPLICATE_SAME_ACCESS);
177c0ea7 5514
480b0c5b
GV
5515 DuplicateHandle (parent,
5516 GetStdHandle (STD_OUTPUT_HANDLE),
5517 parent,
5518 &stdout_save,
5519 0,
5520 FALSE,
5521 DUPLICATE_SAME_ACCESS);
177c0ea7 5522
480b0c5b
GV
5523 DuplicateHandle (parent,
5524 GetStdHandle (STD_ERROR_HANDLE),
5525 parent,
5526 &stderr_save,
5527 0,
5528 FALSE,
5529 DUPLICATE_SAME_ACCESS);
177c0ea7 5530
480b0c5b
GV
5531 fclose (stdin);
5532 fclose (stdout);
5533 fclose (stderr);
5534
5535 if (stdin_save != INVALID_HANDLE_VALUE)
5536 _open_osfhandle ((long) stdin_save, O_TEXT);
5537 else
76b3903d
GV
5538 _open ("nul", O_TEXT | O_NOINHERIT | O_RDONLY);
5539 _fdopen (0, "r");
480b0c5b
GV
5540
5541 if (stdout_save != INVALID_HANDLE_VALUE)
5542 _open_osfhandle ((long) stdout_save, O_TEXT);
5543 else
76b3903d
GV
5544 _open ("nul", O_TEXT | O_NOINHERIT | O_WRONLY);
5545 _fdopen (1, "w");
480b0c5b
GV
5546
5547 if (stderr_save != INVALID_HANDLE_VALUE)
5548 _open_osfhandle ((long) stderr_save, O_TEXT);
5549 else
76b3903d
GV
5550 _open ("nul", O_TEXT | O_NOINHERIT | O_WRONLY);
5551 _fdopen (2, "w");
480b0c5b
GV
5552 }
5553
5554 /* unfortunately, atexit depends on implementation of malloc */
5555 /* atexit (term_ntproc); */
5556 signal (SIGABRT, term_ntproc);
76b3903d
GV
5557
5558 /* determine which drives are fixed, for GetCachedVolumeInformation */
5559 {
5560 /* GetDriveType must have trailing backslash. */
5561 char drive[] = "A:\\";
5562
5563 /* Loop over all possible drive letters */
5564 while (*drive <= 'Z')
5565 {
5566 /* Record if this drive letter refers to a fixed drive. */
177c0ea7 5567 fixed_drives[DRIVE_INDEX (*drive)] =
76b3903d
GV
5568 (GetDriveType (drive) == DRIVE_FIXED);
5569
5570 (*drive)++;
5571 }
a302c7ae
AI
5572
5573 /* Reset the volume info cache. */
5574 volume_cache = NULL;
76b3903d 5575 }
177c0ea7 5576
d54abccd
GV
5577 /* Check to see if Emacs has been installed correctly. */
5578 check_windows_init_file ();
480b0c5b
GV
5579}
5580
a8c3a596
JR
5581/*
5582 shutdown_handler ensures that buffers' autosave files are
5583 up to date when the user logs off, or the system shuts down.
5584*/
5585BOOL WINAPI shutdown_handler(DWORD type)
5586{
5587 /* Ctrl-C and Ctrl-Break are already suppressed, so don't handle them. */
5588 if (type == CTRL_CLOSE_EVENT /* User closes console window. */
5589 || type == CTRL_LOGOFF_EVENT /* User logs off. */
5590 || type == CTRL_SHUTDOWN_EVENT) /* User shutsdown. */
5591 {
5592 /* Shut down cleanly, making sure autosave files are up to date. */
5593 shut_down_emacs (0, 0, Qnil);
5594 }
5595
7046f191 5596 /* Allow other handlers to handle this signal. */
a8c3a596
JR
5597 return FALSE;
5598}
5599
9785d95b
BK
5600/*
5601 globals_of_w32 is used to initialize those global variables that
5602 must always be initialized on startup even when the global variable
5603 initialized is non zero (see the function main in emacs.c).
5604*/
9bfb11f9
KS
5605void
5606globals_of_w32 ()
9785d95b 5607{
74258518
JR
5608 HMODULE kernel32 = GetModuleHandle ("kernel32.dll");
5609
5610 get_process_times_fn = (GetProcessTimes_Proc)
5611 GetProcAddress (kernel32, "GetProcessTimes");
5612
9785d95b
BK
5613 g_b_init_is_windows_9x = 0;
5614 g_b_init_open_process_token = 0;
5615 g_b_init_get_token_information = 0;
5616 g_b_init_lookup_account_sid = 0;
5617 g_b_init_get_sid_identifier_authority = 0;
9d95a291
EZ
5618 g_b_init_get_sid_sub_authority = 0;
5619 g_b_init_get_sid_sub_authority_count = 0;
8aaaec6b
EZ
5620 g_b_init_get_file_security = 0;
5621 g_b_init_get_security_descriptor_owner = 0;
5622 g_b_init_get_security_descriptor_group = 0;
5623 g_b_init_is_valid_sid = 0;
7c80d5ec
EZ
5624 g_b_init_create_toolhelp32_snapshot = 0;
5625 g_b_init_process32_first = 0;
5626 g_b_init_process32_next = 0;
5627 g_b_init_open_thread_token = 0;
5628 g_b_init_impersonate_self = 0;
5629 g_b_init_revert_to_self = 0;
5630 g_b_init_get_process_memory_info = 0;
5631 g_b_init_get_process_working_set_size = 0;
5632 g_b_init_global_memory_status = 0;
5633 g_b_init_global_memory_status_ex = 0;
f8b35b24
EZ
5634 g_b_init_equal_sid = 0;
5635 g_b_init_copy_sid = 0;
5636 g_b_init_get_length_sid = 0;
a8c3a596
JR
5637 /* The following sets a handler for shutdown notifications for
5638 console apps. This actually applies to Emacs in both console and
5639 GUI modes, since we had to fool windows into thinking emacs is a
5640 console application to get console mode to work. */
5641 SetConsoleCtrlHandler(shutdown_handler, TRUE);
8aaaec6b
EZ
5642
5643 /* "None" is the default group name on standalone workstations. */
5644 strcpy (dflt_group_name, "None");
9785d95b
BK
5645}
5646
d888760c
GM
5647/* For make-serial-process */
5648int serial_open (char *port)
5649{
5650 HANDLE hnd;
5651 child_process *cp;
5652 int fd = -1;
5653
5654 hnd = CreateFile (port, GENERIC_READ | GENERIC_WRITE, 0, 0,
5655 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
5656 if (hnd == INVALID_HANDLE_VALUE)
5657 error ("Could not open %s", port);
5658 fd = (int) _open_osfhandle ((int) hnd, 0);
5659 if (fd == -1)
5660 error ("Could not open %s", port);
5661
5662 cp = new_child ();
5663 if (!cp)
5664 error ("Could not create child process");
5665 cp->fd = fd;
5666 cp->status = STATUS_READ_ACKNOWLEDGED;
5667 fd_info[ fd ].hnd = hnd;
5668 fd_info[ fd ].flags |=
5669 FILE_READ | FILE_WRITE | FILE_BINARY | FILE_SERIAL;
5670 if (fd_info[ fd ].cp != NULL)
5671 {
5672 error ("fd_info[fd = %d] is already in use", fd);
5673 }
5674 fd_info[ fd ].cp = cp;
5675 cp->ovl_read.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
5676 if (cp->ovl_read.hEvent == NULL)
5677 error ("Could not create read event");
5678 cp->ovl_write.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
5679 if (cp->ovl_write.hEvent == NULL)
5680 error ("Could not create write event");
5681
5682 return fd;
5683}
5684
5685/* For serial-process-configure */
5686void
5687serial_configure (struct Lisp_Process *p,
5688 Lisp_Object contact)
5689{
5690 Lisp_Object childp2 = Qnil;
5691 Lisp_Object tem = Qnil;
5692 HANDLE hnd;
5693 DCB dcb;
5694 COMMTIMEOUTS ct;
5695 char summary[4] = "???"; /* This usually becomes "8N1". */
5696
5697 if ((fd_info[ p->outfd ].flags & FILE_SERIAL) == 0)
5698 error ("Not a serial process");
5699 hnd = fd_info[ p->outfd ].hnd;
5700
5701 childp2 = Fcopy_sequence (p->childp);
5702
5703 /* Initialize timeouts for blocking read and blocking write. */
5704 if (!GetCommTimeouts (hnd, &ct))
5705 error ("GetCommTimeouts() failed");
5706 ct.ReadIntervalTimeout = 0;
5707 ct.ReadTotalTimeoutMultiplier = 0;
5708 ct.ReadTotalTimeoutConstant = 0;
5709 ct.WriteTotalTimeoutMultiplier = 0;
5710 ct.WriteTotalTimeoutConstant = 0;
5711 if (!SetCommTimeouts (hnd, &ct))
5712 error ("SetCommTimeouts() failed");
5713 /* Read port attributes and prepare default configuration. */
5714 memset (&dcb, 0, sizeof (dcb));
5715 dcb.DCBlength = sizeof (DCB);
5716 if (!GetCommState (hnd, &dcb))
5717 error ("GetCommState() failed");
5718 dcb.fBinary = TRUE;
5719 dcb.fNull = FALSE;
5720 dcb.fAbortOnError = FALSE;
5721 /* dcb.XonLim and dcb.XoffLim are set by GetCommState() */
5722 dcb.ErrorChar = 0;
5723 dcb.EofChar = 0;
5724 dcb.EvtChar = 0;
5725
5726 /* Configure speed. */
5727 if (!NILP (Fplist_member (contact, QCspeed)))
5728 tem = Fplist_get (contact, QCspeed);
5729 else
5730 tem = Fplist_get (p->childp, QCspeed);
5731 CHECK_NUMBER (tem);
5732 dcb.BaudRate = XINT (tem);
5733 childp2 = Fplist_put (childp2, QCspeed, tem);
5734
5735 /* Configure bytesize. */
5736 if (!NILP (Fplist_member (contact, QCbytesize)))
5737 tem = Fplist_get (contact, QCbytesize);
5738 else
5739 tem = Fplist_get (p->childp, QCbytesize);
5740 if (NILP (tem))
5741 tem = make_number (8);
5742 CHECK_NUMBER (tem);
5743 if (XINT (tem) != 7 && XINT (tem) != 8)
5744 error (":bytesize must be nil (8), 7, or 8");
5745 dcb.ByteSize = XINT (tem);
5746 summary[0] = XINT (tem) + '0';
5747 childp2 = Fplist_put (childp2, QCbytesize, tem);
5748
5749 /* Configure parity. */
5750 if (!NILP (Fplist_member (contact, QCparity)))
5751 tem = Fplist_get (contact, QCparity);
5752 else
5753 tem = Fplist_get (p->childp, QCparity);
5754 if (!NILP (tem) && !EQ (tem, Qeven) && !EQ (tem, Qodd))
5755 error (":parity must be nil (no parity), `even', or `odd'");
5756 dcb.fParity = FALSE;
5757 dcb.Parity = NOPARITY;
5758 dcb.fErrorChar = FALSE;
5759 if (NILP (tem))
5760 {
5761 summary[1] = 'N';
5762 }
5763 else if (EQ (tem, Qeven))
5764 {
5765 summary[1] = 'E';
5766 dcb.fParity = TRUE;
5767 dcb.Parity = EVENPARITY;
5768 dcb.fErrorChar = TRUE;
5769 }
5770 else if (EQ (tem, Qodd))
5771 {
5772 summary[1] = 'O';
5773 dcb.fParity = TRUE;
5774 dcb.Parity = ODDPARITY;
5775 dcb.fErrorChar = TRUE;
5776 }
5777 childp2 = Fplist_put (childp2, QCparity, tem);
5778
5779 /* Configure stopbits. */
5780 if (!NILP (Fplist_member (contact, QCstopbits)))
5781 tem = Fplist_get (contact, QCstopbits);
5782 else
5783 tem = Fplist_get (p->childp, QCstopbits);
5784 if (NILP (tem))
5785 tem = make_number (1);
5786 CHECK_NUMBER (tem);
5787 if (XINT (tem) != 1 && XINT (tem) != 2)
5788 error (":stopbits must be nil (1 stopbit), 1, or 2");
5789 summary[2] = XINT (tem) + '0';
5790 if (XINT (tem) == 1)
5791 dcb.StopBits = ONESTOPBIT;
5792 else if (XINT (tem) == 2)
5793 dcb.StopBits = TWOSTOPBITS;
5794 childp2 = Fplist_put (childp2, QCstopbits, tem);
5795
5796 /* Configure flowcontrol. */
5797 if (!NILP (Fplist_member (contact, QCflowcontrol)))
5798 tem = Fplist_get (contact, QCflowcontrol);
5799 else
5800 tem = Fplist_get (p->childp, QCflowcontrol);
5801 if (!NILP (tem) && !EQ (tem, Qhw) && !EQ (tem, Qsw))
5802 error (":flowcontrol must be nil (no flowcontrol), `hw', or `sw'");
5803 dcb.fOutxCtsFlow = FALSE;
5804 dcb.fOutxDsrFlow = FALSE;
5805 dcb.fDtrControl = DTR_CONTROL_DISABLE;
5806 dcb.fDsrSensitivity = FALSE;
5807 dcb.fTXContinueOnXoff = FALSE;
5808 dcb.fOutX = FALSE;
5809 dcb.fInX = FALSE;
5810 dcb.fRtsControl = RTS_CONTROL_DISABLE;
5811 dcb.XonChar = 17; /* Control-Q */
5812 dcb.XoffChar = 19; /* Control-S */
5813 if (NILP (tem))
5814 {
5815 /* Already configured. */
5816 }
5817 else if (EQ (tem, Qhw))
5818 {
5819 dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
5820 dcb.fOutxCtsFlow = TRUE;
5821 }
5822 else if (EQ (tem, Qsw))
5823 {
5824 dcb.fOutX = TRUE;
5825 dcb.fInX = TRUE;
5826 }
5827 childp2 = Fplist_put (childp2, QCflowcontrol, tem);
5828
5829 /* Activate configuration. */
5830 if (!SetCommState (hnd, &dcb))
5831 error ("SetCommState() failed");
5832
5833 childp2 = Fplist_put (childp2, QCsummary, build_string (summary));
5834 p->childp = childp2;
5835}
5836
aa5ee2a3 5837/* end of w32.c */
ab5796a9
MB
5838
5839/* arch-tag: 90442dd3-37be-482b-b272-ac752e3049f1
5840 (do not change this comment) */