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