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