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