* doc/lispref/variables.texi (Scope): Mention the availability of lexbind.
[emacs.git] / src / w32.c
blobd4f68544c1dd4b528c1a52cba14f8aea9de2629e
1 /* Utility and Unix shadow routines for GNU Emacs on the Microsoft W32 API.
2 Copyright (C) 1994-1995, 2000-2011 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
20 Geoff Voelker (voelker@cs.washington.edu) 7-29-94
22 #include <stddef.h> /* for offsetof */
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <float.h> /* for DBL_EPSILON */
26 #include <io.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <ctype.h>
30 #include <signal.h>
31 #include <sys/file.h>
32 #include <sys/time.h>
33 #include <sys/utime.h>
34 #include <mbstring.h> /* for _mbspbrk */
35 #include <math.h>
36 #include <setjmp.h>
38 /* must include CRT headers *before* config.h */
40 #include <config.h>
42 #undef access
43 #undef chdir
44 #undef chmod
45 #undef creat
46 #undef ctime
47 #undef fopen
48 #undef link
49 #undef mkdir
50 #undef mktemp
51 #undef open
52 #undef rename
53 #undef rmdir
54 #undef unlink
56 #undef close
57 #undef dup
58 #undef dup2
59 #undef pipe
60 #undef read
61 #undef write
63 #undef strerror
65 #include "lisp.h"
67 #include <pwd.h>
68 #include <grp.h>
70 #ifdef __GNUC__
71 #define _ANONYMOUS_UNION
72 #define _ANONYMOUS_STRUCT
73 #endif
74 #include <windows.h>
75 /* Some versions of compiler define MEMORYSTATUSEX, some don't, so we
76 use a different name to avoid compilation problems. */
77 typedef struct _MEMORY_STATUS_EX {
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 } MEMORY_STATUS_EX,*LPMEMORY_STATUS_EX;
89 #include <lmcons.h>
90 #include <shlobj.h>
92 #include <tlhelp32.h>
93 #include <psapi.h>
94 #include <w32api.h>
95 #if !defined(__MINGW32__) || __W32API_MAJOR_VERSION < 3 || (__W32API_MAJOR_VERSION == 3 && __W32API_MINOR_VERSION < 15)
96 /* This either is not in psapi.h or guarded by higher value of
97 _WIN32_WINNT than what we use. w32api supplied with MinGW 3.15
98 defines it in psapi.h */
99 typedef struct _PROCESS_MEMORY_COUNTERS_EX {
100 DWORD cb;
101 DWORD PageFaultCount;
102 DWORD PeakWorkingSetSize;
103 DWORD WorkingSetSize;
104 DWORD QuotaPeakPagedPoolUsage;
105 DWORD QuotaPagedPoolUsage;
106 DWORD QuotaPeakNonPagedPoolUsage;
107 DWORD QuotaNonPagedPoolUsage;
108 DWORD PagefileUsage;
109 DWORD PeakPagefileUsage;
110 DWORD PrivateUsage;
111 } PROCESS_MEMORY_COUNTERS_EX,*PPROCESS_MEMORY_COUNTERS_EX;
112 #endif
114 /* TCP connection support. */
115 #include <sys/socket.h>
116 #undef socket
117 #undef bind
118 #undef connect
119 #undef htons
120 #undef ntohs
121 #undef inet_addr
122 #undef gethostname
123 #undef gethostbyname
124 #undef getservbyname
125 #undef getpeername
126 #undef shutdown
127 #undef setsockopt
128 #undef listen
129 #undef getsockname
130 #undef accept
131 #undef recvfrom
132 #undef sendto
134 #include "w32.h"
135 #include "ndir.h"
136 #include "w32heap.h"
137 #include "systime.h"
138 #include "dispextern.h" /* for xstrcasecmp */
139 #include "coding.h" /* for Vlocale_coding_system */
141 /* For serial_configure and serial_open. */
142 #include "process.h"
144 typedef HRESULT (WINAPI * ShGetFolderPath_fn)
145 (IN HWND, IN int, IN HANDLE, IN DWORD, OUT char *);
147 void globals_of_w32 (void);
148 static DWORD get_rid (PSID);
150 /* Defined in process.c for its own purpose. */
151 extern Lisp_Object Qlocal;
154 /* Initialization states.
156 WARNING: If you add any more such variables for additional APIs,
157 you MUST add initialization for them to globals_of_w32
158 below. This is because these variables might get set
159 to non-NULL values during dumping, but the dumped Emacs
160 cannot reuse those values, because it could be run on a
161 different version of the OS, where API addresses are
162 different. */
163 static BOOL g_b_init_is_windows_9x;
164 static BOOL g_b_init_open_process_token;
165 static BOOL g_b_init_get_token_information;
166 static BOOL g_b_init_lookup_account_sid;
167 static BOOL g_b_init_get_sid_sub_authority;
168 static BOOL g_b_init_get_sid_sub_authority_count;
169 static BOOL g_b_init_get_file_security;
170 static BOOL g_b_init_get_security_descriptor_owner;
171 static BOOL g_b_init_get_security_descriptor_group;
172 static BOOL g_b_init_is_valid_sid;
173 static BOOL g_b_init_create_toolhelp32_snapshot;
174 static BOOL g_b_init_process32_first;
175 static BOOL g_b_init_process32_next;
176 static BOOL g_b_init_open_thread_token;
177 static BOOL g_b_init_impersonate_self;
178 static BOOL g_b_init_revert_to_self;
179 static BOOL g_b_init_get_process_memory_info;
180 static BOOL g_b_init_get_process_working_set_size;
181 static BOOL g_b_init_global_memory_status;
182 static BOOL g_b_init_global_memory_status_ex;
183 static BOOL g_b_init_get_length_sid;
184 static BOOL g_b_init_equal_sid;
185 static BOOL g_b_init_copy_sid;
186 static BOOL g_b_init_get_native_system_info;
187 static BOOL g_b_init_get_system_times;
190 BEGIN: Wrapper functions around OpenProcessToken
191 and other functions in advapi32.dll that are only
192 supported in Windows NT / 2k / XP
194 /* ** Function pointer typedefs ** */
195 typedef BOOL (WINAPI * OpenProcessToken_Proc) (
196 HANDLE ProcessHandle,
197 DWORD DesiredAccess,
198 PHANDLE TokenHandle);
199 typedef BOOL (WINAPI * GetTokenInformation_Proc) (
200 HANDLE TokenHandle,
201 TOKEN_INFORMATION_CLASS TokenInformationClass,
202 LPVOID TokenInformation,
203 DWORD TokenInformationLength,
204 PDWORD ReturnLength);
205 typedef BOOL (WINAPI * GetProcessTimes_Proc) (
206 HANDLE process_handle,
207 LPFILETIME creation_time,
208 LPFILETIME exit_time,
209 LPFILETIME kernel_time,
210 LPFILETIME user_time);
212 GetProcessTimes_Proc get_process_times_fn = NULL;
214 #ifdef _UNICODE
215 const char * const LookupAccountSid_Name = "LookupAccountSidW";
216 const char * const GetFileSecurity_Name = "GetFileSecurityW";
217 #else
218 const char * const LookupAccountSid_Name = "LookupAccountSidA";
219 const char * const GetFileSecurity_Name = "GetFileSecurityA";
220 #endif
221 typedef BOOL (WINAPI * LookupAccountSid_Proc) (
222 LPCTSTR lpSystemName,
223 PSID Sid,
224 LPTSTR Name,
225 LPDWORD cbName,
226 LPTSTR DomainName,
227 LPDWORD cbDomainName,
228 PSID_NAME_USE peUse);
229 typedef PDWORD (WINAPI * GetSidSubAuthority_Proc) (
230 PSID pSid,
231 DWORD n);
232 typedef PUCHAR (WINAPI * GetSidSubAuthorityCount_Proc) (
233 PSID pSid);
234 typedef BOOL (WINAPI * GetFileSecurity_Proc) (
235 LPCTSTR lpFileName,
236 SECURITY_INFORMATION RequestedInformation,
237 PSECURITY_DESCRIPTOR pSecurityDescriptor,
238 DWORD nLength,
239 LPDWORD lpnLengthNeeded);
240 typedef BOOL (WINAPI * GetSecurityDescriptorOwner_Proc) (
241 PSECURITY_DESCRIPTOR pSecurityDescriptor,
242 PSID *pOwner,
243 LPBOOL lpbOwnerDefaulted);
244 typedef BOOL (WINAPI * GetSecurityDescriptorGroup_Proc) (
245 PSECURITY_DESCRIPTOR pSecurityDescriptor,
246 PSID *pGroup,
247 LPBOOL lpbGroupDefaulted);
248 typedef BOOL (WINAPI * IsValidSid_Proc) (
249 PSID sid);
250 typedef HANDLE (WINAPI * CreateToolhelp32Snapshot_Proc) (
251 DWORD dwFlags,
252 DWORD th32ProcessID);
253 typedef BOOL (WINAPI * Process32First_Proc) (
254 HANDLE hSnapshot,
255 LPPROCESSENTRY32 lppe);
256 typedef BOOL (WINAPI * Process32Next_Proc) (
257 HANDLE hSnapshot,
258 LPPROCESSENTRY32 lppe);
259 typedef BOOL (WINAPI * OpenThreadToken_Proc) (
260 HANDLE ThreadHandle,
261 DWORD DesiredAccess,
262 BOOL OpenAsSelf,
263 PHANDLE TokenHandle);
264 typedef BOOL (WINAPI * ImpersonateSelf_Proc) (
265 SECURITY_IMPERSONATION_LEVEL ImpersonationLevel);
266 typedef BOOL (WINAPI * RevertToSelf_Proc) (void);
267 typedef BOOL (WINAPI * GetProcessMemoryInfo_Proc) (
268 HANDLE Process,
269 PPROCESS_MEMORY_COUNTERS ppsmemCounters,
270 DWORD cb);
271 typedef BOOL (WINAPI * GetProcessWorkingSetSize_Proc) (
272 HANDLE hProcess,
273 DWORD * lpMinimumWorkingSetSize,
274 DWORD * lpMaximumWorkingSetSize);
275 typedef BOOL (WINAPI * GlobalMemoryStatus_Proc) (
276 LPMEMORYSTATUS lpBuffer);
277 typedef BOOL (WINAPI * GlobalMemoryStatusEx_Proc) (
278 LPMEMORY_STATUS_EX lpBuffer);
279 typedef BOOL (WINAPI * CopySid_Proc) (
280 DWORD nDestinationSidLength,
281 PSID pDestinationSid,
282 PSID pSourceSid);
283 typedef BOOL (WINAPI * EqualSid_Proc) (
284 PSID pSid1,
285 PSID pSid2);
286 typedef DWORD (WINAPI * GetLengthSid_Proc) (
287 PSID pSid);
288 typedef void (WINAPI * GetNativeSystemInfo_Proc) (
289 LPSYSTEM_INFO lpSystemInfo);
290 typedef BOOL (WINAPI * GetSystemTimes_Proc) (
291 LPFILETIME lpIdleTime,
292 LPFILETIME lpKernelTime,
293 LPFILETIME lpUserTime);
295 /* ** A utility function ** */
296 static BOOL
297 is_windows_9x (void)
299 static BOOL s_b_ret = 0;
300 OSVERSIONINFO os_ver;
301 if (g_b_init_is_windows_9x == 0)
303 g_b_init_is_windows_9x = 1;
304 ZeroMemory (&os_ver, sizeof (OSVERSIONINFO));
305 os_ver.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
306 if (GetVersionEx (&os_ver))
308 s_b_ret = (os_ver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
311 return s_b_ret;
314 /* Get total user and system times for get-internal-run-time.
315 Returns a list of three integers if the times are provided by the OS
316 (NT derivatives), otherwise it returns the result of current-time. */
317 Lisp_Object
318 w32_get_internal_run_time (void)
320 if (get_process_times_fn)
322 FILETIME create, exit, kernel, user;
323 HANDLE proc = GetCurrentProcess ();
324 if ((*get_process_times_fn) (proc, &create, &exit, &kernel, &user))
326 LARGE_INTEGER user_int, kernel_int, total;
327 int microseconds;
328 user_int.LowPart = user.dwLowDateTime;
329 user_int.HighPart = user.dwHighDateTime;
330 kernel_int.LowPart = kernel.dwLowDateTime;
331 kernel_int.HighPart = kernel.dwHighDateTime;
332 total.QuadPart = user_int.QuadPart + kernel_int.QuadPart;
333 /* FILETIME is 100 nanosecond increments, Emacs only wants
334 microsecond resolution. */
335 total.QuadPart /= 10;
336 microseconds = total.QuadPart % 1000000;
337 total.QuadPart /= 1000000;
339 /* Sanity check to make sure we can represent the result. */
340 if (total.HighPart == 0)
342 int secs = total.LowPart;
344 return list3 (make_number ((secs >> 16) & 0xffff),
345 make_number (secs & 0xffff),
346 make_number (microseconds));
351 return Fcurrent_time ();
354 /* ** The wrapper functions ** */
356 static BOOL WINAPI
357 open_process_token (HANDLE ProcessHandle,
358 DWORD DesiredAccess,
359 PHANDLE TokenHandle)
361 static OpenProcessToken_Proc s_pfn_Open_Process_Token = NULL;
362 HMODULE hm_advapi32 = NULL;
363 if (is_windows_9x () == TRUE)
365 return FALSE;
367 if (g_b_init_open_process_token == 0)
369 g_b_init_open_process_token = 1;
370 hm_advapi32 = LoadLibrary ("Advapi32.dll");
371 s_pfn_Open_Process_Token =
372 (OpenProcessToken_Proc) GetProcAddress (hm_advapi32, "OpenProcessToken");
374 if (s_pfn_Open_Process_Token == NULL)
376 return FALSE;
378 return (
379 s_pfn_Open_Process_Token (
380 ProcessHandle,
381 DesiredAccess,
382 TokenHandle)
386 static BOOL WINAPI
387 get_token_information (HANDLE TokenHandle,
388 TOKEN_INFORMATION_CLASS TokenInformationClass,
389 LPVOID TokenInformation,
390 DWORD TokenInformationLength,
391 PDWORD ReturnLength)
393 static GetTokenInformation_Proc s_pfn_Get_Token_Information = NULL;
394 HMODULE hm_advapi32 = NULL;
395 if (is_windows_9x () == TRUE)
397 return FALSE;
399 if (g_b_init_get_token_information == 0)
401 g_b_init_get_token_information = 1;
402 hm_advapi32 = LoadLibrary ("Advapi32.dll");
403 s_pfn_Get_Token_Information =
404 (GetTokenInformation_Proc) GetProcAddress (hm_advapi32, "GetTokenInformation");
406 if (s_pfn_Get_Token_Information == NULL)
408 return FALSE;
410 return (
411 s_pfn_Get_Token_Information (
412 TokenHandle,
413 TokenInformationClass,
414 TokenInformation,
415 TokenInformationLength,
416 ReturnLength)
420 static BOOL WINAPI
421 lookup_account_sid (LPCTSTR lpSystemName,
422 PSID Sid,
423 LPTSTR Name,
424 LPDWORD cbName,
425 LPTSTR DomainName,
426 LPDWORD cbDomainName,
427 PSID_NAME_USE peUse)
429 static LookupAccountSid_Proc s_pfn_Lookup_Account_Sid = NULL;
430 HMODULE hm_advapi32 = NULL;
431 if (is_windows_9x () == TRUE)
433 return FALSE;
435 if (g_b_init_lookup_account_sid == 0)
437 g_b_init_lookup_account_sid = 1;
438 hm_advapi32 = LoadLibrary ("Advapi32.dll");
439 s_pfn_Lookup_Account_Sid =
440 (LookupAccountSid_Proc) GetProcAddress (hm_advapi32, LookupAccountSid_Name);
442 if (s_pfn_Lookup_Account_Sid == NULL)
444 return FALSE;
446 return (
447 s_pfn_Lookup_Account_Sid (
448 lpSystemName,
449 Sid,
450 Name,
451 cbName,
452 DomainName,
453 cbDomainName,
454 peUse)
458 static PDWORD WINAPI
459 get_sid_sub_authority (PSID pSid, DWORD n)
461 static GetSidSubAuthority_Proc s_pfn_Get_Sid_Sub_Authority = NULL;
462 static DWORD zero = 0U;
463 HMODULE hm_advapi32 = NULL;
464 if (is_windows_9x () == TRUE)
466 return &zero;
468 if (g_b_init_get_sid_sub_authority == 0)
470 g_b_init_get_sid_sub_authority = 1;
471 hm_advapi32 = LoadLibrary ("Advapi32.dll");
472 s_pfn_Get_Sid_Sub_Authority =
473 (GetSidSubAuthority_Proc) GetProcAddress (
474 hm_advapi32, "GetSidSubAuthority");
476 if (s_pfn_Get_Sid_Sub_Authority == NULL)
478 return &zero;
480 return (s_pfn_Get_Sid_Sub_Authority (pSid, n));
483 static PUCHAR WINAPI
484 get_sid_sub_authority_count (PSID pSid)
486 static GetSidSubAuthorityCount_Proc s_pfn_Get_Sid_Sub_Authority_Count = NULL;
487 static UCHAR zero = 0U;
488 HMODULE hm_advapi32 = NULL;
489 if (is_windows_9x () == TRUE)
491 return &zero;
493 if (g_b_init_get_sid_sub_authority_count == 0)
495 g_b_init_get_sid_sub_authority_count = 1;
496 hm_advapi32 = LoadLibrary ("Advapi32.dll");
497 s_pfn_Get_Sid_Sub_Authority_Count =
498 (GetSidSubAuthorityCount_Proc) GetProcAddress (
499 hm_advapi32, "GetSidSubAuthorityCount");
501 if (s_pfn_Get_Sid_Sub_Authority_Count == NULL)
503 return &zero;
505 return (s_pfn_Get_Sid_Sub_Authority_Count (pSid));
508 static BOOL WINAPI
509 get_file_security (LPCTSTR lpFileName,
510 SECURITY_INFORMATION RequestedInformation,
511 PSECURITY_DESCRIPTOR pSecurityDescriptor,
512 DWORD nLength,
513 LPDWORD lpnLengthNeeded)
515 static GetFileSecurity_Proc s_pfn_Get_File_Security = NULL;
516 HMODULE hm_advapi32 = NULL;
517 if (is_windows_9x () == TRUE)
519 return FALSE;
521 if (g_b_init_get_file_security == 0)
523 g_b_init_get_file_security = 1;
524 hm_advapi32 = LoadLibrary ("Advapi32.dll");
525 s_pfn_Get_File_Security =
526 (GetFileSecurity_Proc) GetProcAddress (
527 hm_advapi32, GetFileSecurity_Name);
529 if (s_pfn_Get_File_Security == NULL)
531 return FALSE;
533 return (s_pfn_Get_File_Security (lpFileName, RequestedInformation,
534 pSecurityDescriptor, nLength,
535 lpnLengthNeeded));
538 static BOOL WINAPI
539 get_security_descriptor_owner (PSECURITY_DESCRIPTOR pSecurityDescriptor,
540 PSID *pOwner,
541 LPBOOL lpbOwnerDefaulted)
543 static GetSecurityDescriptorOwner_Proc s_pfn_Get_Security_Descriptor_Owner = NULL;
544 HMODULE hm_advapi32 = NULL;
545 if (is_windows_9x () == TRUE)
547 return FALSE;
549 if (g_b_init_get_security_descriptor_owner == 0)
551 g_b_init_get_security_descriptor_owner = 1;
552 hm_advapi32 = LoadLibrary ("Advapi32.dll");
553 s_pfn_Get_Security_Descriptor_Owner =
554 (GetSecurityDescriptorOwner_Proc) GetProcAddress (
555 hm_advapi32, "GetSecurityDescriptorOwner");
557 if (s_pfn_Get_Security_Descriptor_Owner == NULL)
559 return FALSE;
561 return (s_pfn_Get_Security_Descriptor_Owner (pSecurityDescriptor, pOwner,
562 lpbOwnerDefaulted));
565 static BOOL WINAPI
566 get_security_descriptor_group (PSECURITY_DESCRIPTOR pSecurityDescriptor,
567 PSID *pGroup,
568 LPBOOL lpbGroupDefaulted)
570 static GetSecurityDescriptorGroup_Proc s_pfn_Get_Security_Descriptor_Group = NULL;
571 HMODULE hm_advapi32 = NULL;
572 if (is_windows_9x () == TRUE)
574 return FALSE;
576 if (g_b_init_get_security_descriptor_group == 0)
578 g_b_init_get_security_descriptor_group = 1;
579 hm_advapi32 = LoadLibrary ("Advapi32.dll");
580 s_pfn_Get_Security_Descriptor_Group =
581 (GetSecurityDescriptorGroup_Proc) GetProcAddress (
582 hm_advapi32, "GetSecurityDescriptorGroup");
584 if (s_pfn_Get_Security_Descriptor_Group == NULL)
586 return FALSE;
588 return (s_pfn_Get_Security_Descriptor_Group (pSecurityDescriptor, pGroup,
589 lpbGroupDefaulted));
592 static BOOL WINAPI
593 is_valid_sid (PSID sid)
595 static IsValidSid_Proc s_pfn_Is_Valid_Sid = NULL;
596 HMODULE hm_advapi32 = NULL;
597 if (is_windows_9x () == TRUE)
599 return FALSE;
601 if (g_b_init_is_valid_sid == 0)
603 g_b_init_is_valid_sid = 1;
604 hm_advapi32 = LoadLibrary ("Advapi32.dll");
605 s_pfn_Is_Valid_Sid =
606 (IsValidSid_Proc) GetProcAddress (
607 hm_advapi32, "IsValidSid");
609 if (s_pfn_Is_Valid_Sid == NULL)
611 return FALSE;
613 return (s_pfn_Is_Valid_Sid (sid));
616 static BOOL WINAPI
617 equal_sid (PSID sid1, PSID sid2)
619 static EqualSid_Proc s_pfn_Equal_Sid = NULL;
620 HMODULE hm_advapi32 = NULL;
621 if (is_windows_9x () == TRUE)
623 return FALSE;
625 if (g_b_init_equal_sid == 0)
627 g_b_init_equal_sid = 1;
628 hm_advapi32 = LoadLibrary ("Advapi32.dll");
629 s_pfn_Equal_Sid =
630 (EqualSid_Proc) GetProcAddress (
631 hm_advapi32, "EqualSid");
633 if (s_pfn_Equal_Sid == NULL)
635 return FALSE;
637 return (s_pfn_Equal_Sid (sid1, sid2));
640 static DWORD WINAPI
641 get_length_sid (PSID sid)
643 static GetLengthSid_Proc s_pfn_Get_Length_Sid = NULL;
644 HMODULE hm_advapi32 = NULL;
645 if (is_windows_9x () == TRUE)
647 return 0;
649 if (g_b_init_get_length_sid == 0)
651 g_b_init_get_length_sid = 1;
652 hm_advapi32 = LoadLibrary ("Advapi32.dll");
653 s_pfn_Get_Length_Sid =
654 (GetLengthSid_Proc) GetProcAddress (
655 hm_advapi32, "GetLengthSid");
657 if (s_pfn_Get_Length_Sid == NULL)
659 return 0;
661 return (s_pfn_Get_Length_Sid (sid));
664 static BOOL WINAPI
665 copy_sid (DWORD destlen, PSID dest, PSID src)
667 static CopySid_Proc s_pfn_Copy_Sid = NULL;
668 HMODULE hm_advapi32 = NULL;
669 if (is_windows_9x () == TRUE)
671 return FALSE;
673 if (g_b_init_copy_sid == 0)
675 g_b_init_copy_sid = 1;
676 hm_advapi32 = LoadLibrary ("Advapi32.dll");
677 s_pfn_Copy_Sid =
678 (CopySid_Proc) GetProcAddress (
679 hm_advapi32, "CopySid");
681 if (s_pfn_Copy_Sid == NULL)
683 return FALSE;
685 return (s_pfn_Copy_Sid (destlen, dest, src));
689 END: Wrapper functions around OpenProcessToken
690 and other functions in advapi32.dll that are only
691 supported in Windows NT / 2k / XP
694 static void WINAPI
695 get_native_system_info (LPSYSTEM_INFO lpSystemInfo)
697 static GetNativeSystemInfo_Proc s_pfn_Get_Native_System_Info = NULL;
698 if (is_windows_9x () != TRUE)
700 if (g_b_init_get_native_system_info == 0)
702 g_b_init_get_native_system_info = 1;
703 s_pfn_Get_Native_System_Info =
704 (GetNativeSystemInfo_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
705 "GetNativeSystemInfo");
707 if (s_pfn_Get_Native_System_Info != NULL)
708 s_pfn_Get_Native_System_Info (lpSystemInfo);
710 else
711 lpSystemInfo->dwNumberOfProcessors = -1;
714 static BOOL WINAPI
715 get_system_times (LPFILETIME lpIdleTime,
716 LPFILETIME lpKernelTime,
717 LPFILETIME lpUserTime)
719 static GetSystemTimes_Proc s_pfn_Get_System_times = NULL;
720 if (is_windows_9x () == TRUE)
722 return FALSE;
724 if (g_b_init_get_system_times == 0)
726 g_b_init_get_system_times = 1;
727 s_pfn_Get_System_times =
728 (GetSystemTimes_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
729 "GetSystemTimes");
731 if (s_pfn_Get_System_times == NULL)
732 return FALSE;
733 return (s_pfn_Get_System_times (lpIdleTime, lpKernelTime, lpUserTime));
736 /* Equivalent of strerror for W32 error codes. */
737 char *
738 w32_strerror (int error_no)
740 static char buf[500];
742 if (error_no == 0)
743 error_no = GetLastError ();
745 buf[0] = '\0';
746 if (!FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL,
747 error_no,
748 0, /* choose most suitable language */
749 buf, sizeof (buf), NULL))
750 sprintf (buf, "w32 error %u", error_no);
751 return buf;
754 /* Return 1 if P is a valid pointer to an object of size SIZE. Return
755 0 if P is NOT a valid pointer. Return -1 if we cannot validate P.
757 This is called from alloc.c:valid_pointer_p. */
759 w32_valid_pointer_p (void *p, int size)
761 SIZE_T done;
762 HANDLE h = OpenProcess (PROCESS_VM_READ, FALSE, GetCurrentProcessId ());
764 if (h)
766 unsigned char *buf = alloca (size);
767 int retval = ReadProcessMemory (h, p, buf, size, &done);
769 CloseHandle (h);
770 return retval;
772 else
773 return -1;
776 static char startup_dir[MAXPATHLEN];
778 /* Get the current working directory. */
779 char *
780 getwd (char *dir)
782 #if 0
783 if (GetCurrentDirectory (MAXPATHLEN, dir) > 0)
784 return dir;
785 return NULL;
786 #else
787 /* Emacs doesn't actually change directory itself, and we want to
788 force our real wd to be where emacs.exe is to avoid unnecessary
789 conflicts when trying to rename or delete directories. */
790 strcpy (dir, startup_dir);
791 return dir;
792 #endif
795 /* Emulate getloadavg. */
797 struct load_sample {
798 time_t sample_time;
799 ULONGLONG idle;
800 ULONGLONG kernel;
801 ULONGLONG user;
804 /* Number of processors on this machine. */
805 static unsigned num_of_processors;
807 /* We maintain 1-sec samples for the last 16 minutes in a circular buffer. */
808 static struct load_sample samples[16*60];
809 static int first_idx = -1, last_idx = -1;
810 static int max_idx = sizeof (samples) / sizeof (samples[0]);
812 static int
813 buf_next (int from)
815 int next_idx = from + 1;
817 if (next_idx >= max_idx)
818 next_idx = 0;
820 return next_idx;
823 static int
824 buf_prev (int from)
826 int prev_idx = from - 1;
828 if (prev_idx < 0)
829 prev_idx = max_idx - 1;
831 return prev_idx;
834 static void
835 sample_system_load (ULONGLONG *idle, ULONGLONG *kernel, ULONGLONG *user)
837 SYSTEM_INFO sysinfo;
838 FILETIME ft_idle, ft_user, ft_kernel;
840 /* Initialize the number of processors on this machine. */
841 if (num_of_processors <= 0)
843 get_native_system_info (&sysinfo);
844 num_of_processors = sysinfo.dwNumberOfProcessors;
845 if (num_of_processors <= 0)
847 GetSystemInfo (&sysinfo);
848 num_of_processors = sysinfo.dwNumberOfProcessors;
850 if (num_of_processors <= 0)
851 num_of_processors = 1;
854 /* TODO: Take into account threads that are ready to run, by
855 sampling the "\System\Processor Queue Length" performance
856 counter. The code below accounts only for threads that are
857 actually running. */
859 if (get_system_times (&ft_idle, &ft_kernel, &ft_user))
861 ULARGE_INTEGER uidle, ukernel, uuser;
863 memcpy (&uidle, &ft_idle, sizeof (ft_idle));
864 memcpy (&ukernel, &ft_kernel, sizeof (ft_kernel));
865 memcpy (&uuser, &ft_user, sizeof (ft_user));
866 *idle = uidle.QuadPart;
867 *kernel = ukernel.QuadPart;
868 *user = uuser.QuadPart;
870 else
872 *idle = 0;
873 *kernel = 0;
874 *user = 0;
878 /* Produce the load average for a given time interval, using the
879 samples in the samples[] array. WHICH can be 0, 1, or 2, meaning
880 1-minute, 5-minute, or 15-minute average, respectively. */
881 static double
882 getavg (int which)
884 double retval = -1.0;
885 double tdiff;
886 int idx;
887 double span = (which == 0 ? 1.0 : (which == 1 ? 5.0 : 15.0)) * 60;
888 time_t now = samples[last_idx].sample_time;
890 if (first_idx != last_idx)
892 for (idx = buf_prev (last_idx); ; idx = buf_prev (idx))
894 tdiff = difftime (now, samples[idx].sample_time);
895 if (tdiff >= span - 2*DBL_EPSILON*now)
897 long double sys =
898 samples[last_idx].kernel + samples[last_idx].user
899 - (samples[idx].kernel + samples[idx].user);
900 long double idl = samples[last_idx].idle - samples[idx].idle;
902 retval = (1.0 - idl / sys) * num_of_processors;
903 break;
905 if (idx == first_idx)
906 break;
910 return retval;
914 getloadavg (double loadavg[], int nelem)
916 int elem;
917 ULONGLONG idle, kernel, user;
918 time_t now = time (NULL);
920 /* Store another sample. We ignore samples that are less than 1 sec
921 apart. */
922 if (difftime (now, samples[last_idx].sample_time) >= 1.0 - 2*DBL_EPSILON*now)
924 sample_system_load (&idle, &kernel, &user);
925 last_idx = buf_next (last_idx);
926 samples[last_idx].sample_time = now;
927 samples[last_idx].idle = idle;
928 samples[last_idx].kernel = kernel;
929 samples[last_idx].user = user;
930 /* If the buffer has more that 15 min worth of samples, discard
931 the old ones. */
932 if (first_idx == -1)
933 first_idx = last_idx;
934 while (first_idx != last_idx
935 && (difftime (now, samples[first_idx].sample_time)
936 >= 15.0*60 + 2*DBL_EPSILON*now))
937 first_idx = buf_next (first_idx);
940 for (elem = 0; elem < nelem; elem++)
942 double avg = getavg (elem);
944 if (avg < 0)
945 break;
946 loadavg[elem] = avg;
949 return elem;
952 /* Emulate getpwuid, getpwnam and others. */
954 #define PASSWD_FIELD_SIZE 256
956 static char dflt_passwd_name[PASSWD_FIELD_SIZE];
957 static char dflt_passwd_passwd[PASSWD_FIELD_SIZE];
958 static char dflt_passwd_gecos[PASSWD_FIELD_SIZE];
959 static char dflt_passwd_dir[PASSWD_FIELD_SIZE];
960 static char dflt_passwd_shell[PASSWD_FIELD_SIZE];
962 static struct passwd dflt_passwd =
964 dflt_passwd_name,
965 dflt_passwd_passwd,
969 dflt_passwd_gecos,
970 dflt_passwd_dir,
971 dflt_passwd_shell,
974 static char dflt_group_name[GNLEN+1];
976 static struct group dflt_group =
978 /* When group information is not available, we return this as the
979 group for all files. */
980 dflt_group_name,
984 unsigned
985 getuid (void)
987 return dflt_passwd.pw_uid;
990 unsigned
991 geteuid (void)
993 /* I could imagine arguing for checking to see whether the user is
994 in the Administrators group and returning a UID of 0 for that
995 case, but I don't know how wise that would be in the long run. */
996 return getuid ();
999 unsigned
1000 getgid (void)
1002 return dflt_passwd.pw_gid;
1005 unsigned
1006 getegid (void)
1008 return getgid ();
1011 struct passwd *
1012 getpwuid (unsigned uid)
1014 if (uid == dflt_passwd.pw_uid)
1015 return &dflt_passwd;
1016 return NULL;
1019 struct group *
1020 getgrgid (gid_t gid)
1022 return &dflt_group;
1025 struct passwd *
1026 getpwnam (char *name)
1028 struct passwd *pw;
1030 pw = getpwuid (getuid ());
1031 if (!pw)
1032 return pw;
1034 if (xstrcasecmp (name, pw->pw_name))
1035 return NULL;
1037 return pw;
1040 static void
1041 init_user_info (void)
1043 /* Find the user's real name by opening the process token and
1044 looking up the name associated with the user-sid in that token.
1046 Use the relative portion of the identifier authority value from
1047 the user-sid as the user id value (same for group id using the
1048 primary group sid from the process token). */
1050 char uname[UNLEN+1], gname[GNLEN+1], domain[1025];
1051 DWORD ulength = sizeof (uname), dlength = sizeof (domain), needed;
1052 DWORD glength = sizeof (gname);
1053 HANDLE token = NULL;
1054 SID_NAME_USE user_type;
1055 unsigned char *buf = NULL;
1056 DWORD blen = 0;
1057 TOKEN_USER user_token;
1058 TOKEN_PRIMARY_GROUP group_token;
1059 BOOL result;
1061 result = open_process_token (GetCurrentProcess (), TOKEN_QUERY, &token);
1062 if (result)
1064 result = get_token_information (token, TokenUser, NULL, 0, &blen);
1065 if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER)
1067 buf = xmalloc (blen);
1068 result = get_token_information (token, TokenUser,
1069 (LPVOID)buf, blen, &needed);
1070 if (result)
1072 memcpy (&user_token, buf, sizeof (user_token));
1073 result = lookup_account_sid (NULL, user_token.User.Sid,
1074 uname, &ulength,
1075 domain, &dlength, &user_type);
1078 else
1079 result = FALSE;
1081 if (result)
1083 strcpy (dflt_passwd.pw_name, uname);
1084 /* Determine a reasonable uid value. */
1085 if (xstrcasecmp ("administrator", uname) == 0)
1087 dflt_passwd.pw_uid = 500; /* well-known Administrator uid */
1088 dflt_passwd.pw_gid = 513; /* well-known None gid */
1090 else
1092 /* Use the last sub-authority value of the RID, the relative
1093 portion of the SID, as user/group ID. */
1094 dflt_passwd.pw_uid = get_rid (user_token.User.Sid);
1096 /* Get group id and name. */
1097 result = get_token_information (token, TokenPrimaryGroup,
1098 (LPVOID)buf, blen, &needed);
1099 if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER)
1101 buf = xrealloc (buf, blen = needed);
1102 result = get_token_information (token, TokenPrimaryGroup,
1103 (LPVOID)buf, blen, &needed);
1105 if (result)
1107 memcpy (&group_token, buf, sizeof (group_token));
1108 dflt_passwd.pw_gid = get_rid (group_token.PrimaryGroup);
1109 dlength = sizeof (domain);
1110 /* If we can get at the real Primary Group name, use that.
1111 Otherwise, the default group name was already set to
1112 "None" in globals_of_w32. */
1113 if (lookup_account_sid (NULL, group_token.PrimaryGroup,
1114 gname, &glength, NULL, &dlength,
1115 &user_type))
1116 strcpy (dflt_group_name, gname);
1118 else
1119 dflt_passwd.pw_gid = dflt_passwd.pw_uid;
1122 /* If security calls are not supported (presumably because we
1123 are running under Windows 9X), fallback to this: */
1124 else if (GetUserName (uname, &ulength))
1126 strcpy (dflt_passwd.pw_name, uname);
1127 if (xstrcasecmp ("administrator", uname) == 0)
1128 dflt_passwd.pw_uid = 0;
1129 else
1130 dflt_passwd.pw_uid = 123;
1131 dflt_passwd.pw_gid = dflt_passwd.pw_uid;
1133 else
1135 strcpy (dflt_passwd.pw_name, "unknown");
1136 dflt_passwd.pw_uid = 123;
1137 dflt_passwd.pw_gid = 123;
1139 dflt_group.gr_gid = dflt_passwd.pw_gid;
1141 /* Ensure HOME and SHELL are defined. */
1142 if (getenv ("HOME") == NULL)
1143 abort ();
1144 if (getenv ("SHELL") == NULL)
1145 abort ();
1147 /* Set dir and shell from environment variables. */
1148 strcpy (dflt_passwd.pw_dir, getenv ("HOME"));
1149 strcpy (dflt_passwd.pw_shell, getenv ("SHELL"));
1151 xfree (buf);
1152 if (token)
1153 CloseHandle (token);
1157 random (void)
1159 /* rand () on NT gives us 15 random bits...hack together 30 bits. */
1160 return ((rand () << 15) | rand ());
1163 void
1164 srandom (int seed)
1166 srand (seed);
1170 /* Normalize filename by converting all path separators to
1171 the specified separator. Also conditionally convert upper
1172 case path name components to lower case. */
1174 static void
1175 normalize_filename (register char *fp, char path_sep)
1177 char sep;
1178 char *elem;
1180 /* Always lower-case drive letters a-z, even if the filesystem
1181 preserves case in filenames.
1182 This is so filenames can be compared by string comparison
1183 functions that are case-sensitive. Even case-preserving filesystems
1184 do not distinguish case in drive letters. */
1185 if (fp[1] == ':' && *fp >= 'A' && *fp <= 'Z')
1187 *fp += 'a' - 'A';
1188 fp += 2;
1191 if (NILP (Vw32_downcase_file_names))
1193 while (*fp)
1195 if (*fp == '/' || *fp == '\\')
1196 *fp = path_sep;
1197 fp++;
1199 return;
1202 sep = path_sep; /* convert to this path separator */
1203 elem = fp; /* start of current path element */
1205 do {
1206 if (*fp >= 'a' && *fp <= 'z')
1207 elem = 0; /* don't convert this element */
1209 if (*fp == 0 || *fp == ':')
1211 sep = *fp; /* restore current separator (or 0) */
1212 *fp = '/'; /* after conversion of this element */
1215 if (*fp == '/' || *fp == '\\')
1217 if (elem && elem != fp)
1219 *fp = 0; /* temporary end of string */
1220 _strlwr (elem); /* while we convert to lower case */
1222 *fp = sep; /* convert (or restore) path separator */
1223 elem = fp + 1; /* next element starts after separator */
1224 sep = path_sep;
1226 } while (*fp++);
1229 /* Destructively turn backslashes into slashes. */
1230 void
1231 dostounix_filename (register char *p)
1233 normalize_filename (p, '/');
1236 /* Destructively turn slashes into backslashes. */
1237 void
1238 unixtodos_filename (register char *p)
1240 normalize_filename (p, '\\');
1243 /* Remove all CR's that are followed by a LF.
1244 (From msdos.c...probably should figure out a way to share it,
1245 although this code isn't going to ever change.) */
1246 static int
1247 crlf_to_lf (register int n, register unsigned char *buf)
1249 unsigned char *np = buf;
1250 unsigned char *startp = buf;
1251 unsigned char *endp = buf + n;
1253 if (n == 0)
1254 return n;
1255 while (buf < endp - 1)
1257 if (*buf == 0x0d)
1259 if (*(++buf) != 0x0a)
1260 *np++ = 0x0d;
1262 else
1263 *np++ = *buf++;
1265 if (buf < endp)
1266 *np++ = *buf++;
1267 return np - startp;
1270 /* Parse the root part of file name, if present. Return length and
1271 optionally store pointer to char after root. */
1272 static int
1273 parse_root (char * name, char ** pPath)
1275 char * start = name;
1277 if (name == NULL)
1278 return 0;
1280 /* find the root name of the volume if given */
1281 if (isalpha (name[0]) && name[1] == ':')
1283 /* skip past drive specifier */
1284 name += 2;
1285 if (IS_DIRECTORY_SEP (name[0]))
1286 name++;
1288 else if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1]))
1290 int slashes = 2;
1291 name += 2;
1294 if (IS_DIRECTORY_SEP (*name) && --slashes == 0)
1295 break;
1296 name++;
1298 while ( *name );
1299 if (IS_DIRECTORY_SEP (name[0]))
1300 name++;
1303 if (pPath)
1304 *pPath = name;
1306 return name - start;
1309 /* Get long base name for name; name is assumed to be absolute. */
1310 static int
1311 get_long_basename (char * name, char * buf, int size)
1313 WIN32_FIND_DATA find_data;
1314 HANDLE dir_handle;
1315 int len = 0;
1317 /* must be valid filename, no wild cards or other invalid characters */
1318 if (_mbspbrk (name, "*?|<>\""))
1319 return 0;
1321 dir_handle = FindFirstFile (name, &find_data);
1322 if (dir_handle != INVALID_HANDLE_VALUE)
1324 if ((len = strlen (find_data.cFileName)) < size)
1325 memcpy (buf, find_data.cFileName, len + 1);
1326 else
1327 len = 0;
1328 FindClose (dir_handle);
1330 return len;
1333 /* Get long name for file, if possible (assumed to be absolute). */
1334 BOOL
1335 w32_get_long_filename (char * name, char * buf, int size)
1337 char * o = buf;
1338 char * p;
1339 char * q;
1340 char full[ MAX_PATH ];
1341 int len;
1343 len = strlen (name);
1344 if (len >= MAX_PATH)
1345 return FALSE;
1347 /* Use local copy for destructive modification. */
1348 memcpy (full, name, len+1);
1349 unixtodos_filename (full);
1351 /* Copy root part verbatim. */
1352 len = parse_root (full, &p);
1353 memcpy (o, full, len);
1354 o += len;
1355 *o = '\0';
1356 size -= len;
1358 while (p != NULL && *p)
1360 q = p;
1361 p = strchr (q, '\\');
1362 if (p) *p = '\0';
1363 len = get_long_basename (full, o, size);
1364 if (len > 0)
1366 o += len;
1367 size -= len;
1368 if (p != NULL)
1370 *p++ = '\\';
1371 if (size < 2)
1372 return FALSE;
1373 *o++ = '\\';
1374 size--;
1375 *o = '\0';
1378 else
1379 return FALSE;
1382 return TRUE;
1385 static int
1386 is_unc_volume (const char *filename)
1388 const char *ptr = filename;
1390 if (!IS_DIRECTORY_SEP (ptr[0]) || !IS_DIRECTORY_SEP (ptr[1]) || !ptr[2])
1391 return 0;
1393 if (_mbspbrk (ptr + 2, "*?|<>\"\\/"))
1394 return 0;
1396 return 1;
1399 /* Routines that are no-ops on NT but are defined to get Emacs to compile. */
1402 sigsetmask (int signal_mask)
1404 return 0;
1408 sigmask (int sig)
1410 return 0;
1414 sigblock (int sig)
1416 return 0;
1420 sigunblock (int sig)
1422 return 0;
1426 sigemptyset (sigset_t *set)
1428 return 0;
1432 sigaddset (sigset_t *set, int signo)
1434 return 0;
1438 sigfillset (sigset_t *set)
1440 return 0;
1444 sigprocmask (int how, const sigset_t *set, sigset_t *oset)
1446 return 0;
1450 setpgrp (int pid, int gid)
1452 return 0;
1456 alarm (int seconds)
1458 return 0;
1461 #define REG_ROOT "SOFTWARE\\GNU\\Emacs"
1463 LPBYTE
1464 w32_get_resource (char *key, LPDWORD lpdwtype)
1466 LPBYTE lpvalue;
1467 HKEY hrootkey = NULL;
1468 DWORD cbData;
1470 /* Check both the current user and the local machine to see if
1471 we have any resources. */
1473 if (RegOpenKeyEx (HKEY_CURRENT_USER, REG_ROOT, 0, KEY_READ, &hrootkey) == ERROR_SUCCESS)
1475 lpvalue = NULL;
1477 if (RegQueryValueEx (hrootkey, key, NULL, NULL, NULL, &cbData) == ERROR_SUCCESS
1478 && (lpvalue = (LPBYTE) xmalloc (cbData)) != NULL
1479 && RegQueryValueEx (hrootkey, key, NULL, lpdwtype, lpvalue, &cbData) == ERROR_SUCCESS)
1481 RegCloseKey (hrootkey);
1482 return (lpvalue);
1485 xfree (lpvalue);
1487 RegCloseKey (hrootkey);
1490 if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, REG_ROOT, 0, KEY_READ, &hrootkey) == ERROR_SUCCESS)
1492 lpvalue = NULL;
1494 if (RegQueryValueEx (hrootkey, key, NULL, NULL, NULL, &cbData) == ERROR_SUCCESS
1495 && (lpvalue = (LPBYTE) xmalloc (cbData)) != NULL
1496 && RegQueryValueEx (hrootkey, key, NULL, lpdwtype, lpvalue, &cbData) == ERROR_SUCCESS)
1498 RegCloseKey (hrootkey);
1499 return (lpvalue);
1502 xfree (lpvalue);
1504 RegCloseKey (hrootkey);
1507 return (NULL);
1510 char *get_emacs_configuration (void);
1511 void
1512 init_environment (char ** argv)
1514 static const char * const tempdirs[] = {
1515 "$TMPDIR", "$TEMP", "$TMP", "c:/"
1518 int i;
1520 const int imax = sizeof (tempdirs) / sizeof (tempdirs[0]);
1522 /* Make sure they have a usable $TMPDIR. Many Emacs functions use
1523 temporary files and assume "/tmp" if $TMPDIR is unset, which
1524 will break on DOS/Windows. Refuse to work if we cannot find
1525 a directory, not even "c:/", usable for that purpose. */
1526 for (i = 0; i < imax ; i++)
1528 const char *tmp = tempdirs[i];
1530 if (*tmp == '$')
1531 tmp = getenv (tmp + 1);
1532 /* Note that `access' can lie to us if the directory resides on a
1533 read-only filesystem, like CD-ROM or a write-protected floppy.
1534 The only way to be really sure is to actually create a file and
1535 see if it succeeds. But I think that's too much to ask. */
1536 if (tmp && _access (tmp, D_OK) == 0)
1538 char * var = alloca (strlen (tmp) + 8);
1539 sprintf (var, "TMPDIR=%s", tmp);
1540 _putenv (strdup (var));
1541 break;
1544 if (i >= imax)
1545 cmd_error_internal
1546 (Fcons (Qerror,
1547 Fcons (build_string ("no usable temporary directories found!!"),
1548 Qnil)),
1549 "While setting TMPDIR: ");
1551 /* Check for environment variables and use registry settings if they
1552 don't exist. Fallback on default values where applicable. */
1554 int i;
1555 LPBYTE lpval;
1556 DWORD dwType;
1557 char locale_name[32];
1558 struct stat ignored;
1559 char default_home[MAX_PATH];
1561 static const struct env_entry
1563 char * name;
1564 char * def_value;
1565 } dflt_envvars[] =
1567 {"HOME", "C:/"},
1568 {"PRELOAD_WINSOCK", NULL},
1569 {"emacs_dir", "C:/emacs"},
1570 {"EMACSLOADPATH", "%emacs_dir%/site-lisp;%emacs_dir%/../site-lisp;%emacs_dir%/lisp;%emacs_dir%/leim"},
1571 {"SHELL", "%emacs_dir%/bin/cmdproxy.exe"},
1572 {"EMACSDATA", "%emacs_dir%/etc"},
1573 {"EMACSPATH", "%emacs_dir%/bin"},
1574 /* We no longer set INFOPATH because Info-default-directory-list
1575 is then ignored. */
1576 /* {"INFOPATH", "%emacs_dir%/info"}, */
1577 {"EMACSDOC", "%emacs_dir%/etc"},
1578 {"TERM", "cmd"},
1579 {"LANG", NULL},
1582 #define N_ENV_VARS sizeof (dflt_envvars)/sizeof (dflt_envvars[0])
1584 /* We need to copy dflt_envvars[] and work on the copy because we
1585 don't want the dumped Emacs to inherit the values of
1586 environment variables we saw during dumping (which could be on
1587 a different system). The defaults above must be left intact. */
1588 struct env_entry env_vars[N_ENV_VARS];
1590 for (i = 0; i < N_ENV_VARS; i++)
1591 env_vars[i] = dflt_envvars[i];
1593 /* For backwards compatibility, check if a .emacs file exists in C:/
1594 If not, then we can try to default to the appdata directory under the
1595 user's profile, which is more likely to be writable. */
1596 if (stat ("C:/.emacs", &ignored) < 0)
1598 HRESULT profile_result;
1599 /* Dynamically load ShGetFolderPath, as it won't exist on versions
1600 of Windows 95 and NT4 that have not been updated to include
1601 MSIE 5. */
1602 ShGetFolderPath_fn get_folder_path;
1603 get_folder_path = (ShGetFolderPath_fn)
1604 GetProcAddress (GetModuleHandle ("shell32.dll"), "SHGetFolderPathA");
1606 if (get_folder_path != NULL)
1608 profile_result = get_folder_path (NULL, CSIDL_APPDATA, NULL,
1609 0, default_home);
1611 /* If we can't get the appdata dir, revert to old behavior. */
1612 if (profile_result == S_OK)
1613 env_vars[0].def_value = default_home;
1617 /* Get default locale info and use it for LANG. */
1618 if (GetLocaleInfo (LOCALE_USER_DEFAULT,
1619 LOCALE_SABBREVLANGNAME | LOCALE_USE_CP_ACP,
1620 locale_name, sizeof (locale_name)))
1622 for (i = 0; i < N_ENV_VARS; i++)
1624 if (strcmp (env_vars[i].name, "LANG") == 0)
1626 env_vars[i].def_value = locale_name;
1627 break;
1632 #define SET_ENV_BUF_SIZE (4 * MAX_PATH) /* to cover EMACSLOADPATH */
1634 /* Treat emacs_dir specially: set it unconditionally based on our
1635 location, if it appears that we are running from the bin subdir
1636 of a standard installation. */
1638 char *p;
1639 char modname[MAX_PATH];
1641 if (!GetModuleFileName (NULL, modname, MAX_PATH))
1642 abort ();
1643 if ((p = strrchr (modname, '\\')) == NULL)
1644 abort ();
1645 *p = 0;
1647 if ((p = strrchr (modname, '\\')) && xstrcasecmp (p, "\\bin") == 0)
1649 char buf[SET_ENV_BUF_SIZE];
1651 *p = 0;
1652 for (p = modname; *p; p++)
1653 if (*p == '\\') *p = '/';
1655 _snprintf (buf, sizeof (buf)-1, "emacs_dir=%s", modname);
1656 _putenv (strdup (buf));
1658 /* Handle running emacs from the build directory: src/oo-spd/i386/ */
1660 /* FIXME: should use substring of get_emacs_configuration ().
1661 But I don't think the Windows build supports alpha, mips etc
1662 anymore, so have taken the easy option for now. */
1663 else if (p && xstrcasecmp (p, "\\i386") == 0)
1665 *p = 0;
1666 p = strrchr (modname, '\\');
1667 if (p != NULL)
1669 *p = 0;
1670 p = strrchr (modname, '\\');
1671 if (p && xstrcasecmp (p, "\\src") == 0)
1673 char buf[SET_ENV_BUF_SIZE];
1675 *p = 0;
1676 for (p = modname; *p; p++)
1677 if (*p == '\\') *p = '/';
1679 _snprintf (buf, sizeof (buf)-1, "emacs_dir=%s", modname);
1680 _putenv (strdup (buf));
1686 for (i = 0; i < N_ENV_VARS; i++)
1688 if (!getenv (env_vars[i].name))
1690 int dont_free = 0;
1692 if ((lpval = w32_get_resource (env_vars[i].name, &dwType)) == NULL
1693 /* Also ignore empty environment variables. */
1694 || *lpval == 0)
1696 xfree (lpval);
1697 lpval = env_vars[i].def_value;
1698 dwType = REG_EXPAND_SZ;
1699 dont_free = 1;
1702 if (lpval)
1704 char buf1[SET_ENV_BUF_SIZE], buf2[SET_ENV_BUF_SIZE];
1706 if (dwType == REG_EXPAND_SZ)
1707 ExpandEnvironmentStrings ((LPSTR) lpval, buf1, sizeof (buf1));
1708 else if (dwType == REG_SZ)
1709 strcpy (buf1, lpval);
1710 if (dwType == REG_EXPAND_SZ || dwType == REG_SZ)
1712 _snprintf (buf2, sizeof (buf2)-1, "%s=%s", env_vars[i].name,
1713 buf1);
1714 _putenv (strdup (buf2));
1717 if (!dont_free)
1718 xfree (lpval);
1724 /* Rebuild system configuration to reflect invoking system. */
1725 Vsystem_configuration = build_string (EMACS_CONFIGURATION);
1727 /* Another special case: on NT, the PATH variable is actually named
1728 "Path" although cmd.exe (perhaps NT itself) arranges for
1729 environment variable lookup and setting to be case insensitive.
1730 However, Emacs assumes a fully case sensitive environment, so we
1731 need to change "Path" to "PATH" to match the expectations of
1732 various elisp packages. We do this by the sneaky method of
1733 modifying the string in the C runtime environ entry.
1735 The same applies to COMSPEC. */
1737 char ** envp;
1739 for (envp = environ; *envp; envp++)
1740 if (_strnicmp (*envp, "PATH=", 5) == 0)
1741 memcpy (*envp, "PATH=", 5);
1742 else if (_strnicmp (*envp, "COMSPEC=", 8) == 0)
1743 memcpy (*envp, "COMSPEC=", 8);
1746 /* Remember the initial working directory for getwd, then make the
1747 real wd be the location of emacs.exe to avoid conflicts when
1748 renaming or deleting directories. (We also don't call chdir when
1749 running subprocesses for the same reason.) */
1750 if (!GetCurrentDirectory (MAXPATHLEN, startup_dir))
1751 abort ();
1754 char *p;
1755 static char modname[MAX_PATH];
1757 if (!GetModuleFileName (NULL, modname, MAX_PATH))
1758 abort ();
1759 if ((p = strrchr (modname, '\\')) == NULL)
1760 abort ();
1761 *p = 0;
1763 SetCurrentDirectory (modname);
1765 /* Ensure argv[0] has the full path to Emacs. */
1766 *p = '\\';
1767 argv[0] = modname;
1770 /* Determine if there is a middle mouse button, to allow parse_button
1771 to decide whether right mouse events should be mouse-2 or
1772 mouse-3. */
1773 w32_num_mouse_buttons = GetSystemMetrics (SM_CMOUSEBUTTONS);
1775 init_user_info ();
1778 char *
1779 emacs_root_dir (void)
1781 static char root_dir[FILENAME_MAX];
1782 const char *p;
1784 p = getenv ("emacs_dir");
1785 if (p == NULL)
1786 abort ();
1787 strcpy (root_dir, p);
1788 root_dir[parse_root (root_dir, NULL)] = '\0';
1789 dostounix_filename (root_dir);
1790 return root_dir;
1793 /* We don't have scripts to automatically determine the system configuration
1794 for Emacs before it's compiled, and we don't want to have to make the
1795 user enter it, so we define EMACS_CONFIGURATION to invoke this runtime
1796 routine. */
1798 char *
1799 get_emacs_configuration (void)
1801 char *arch, *oem, *os;
1802 int build_num;
1803 static char configuration_buffer[32];
1805 /* Determine the processor type. */
1806 switch (get_processor_type ())
1809 #ifdef PROCESSOR_INTEL_386
1810 case PROCESSOR_INTEL_386:
1811 case PROCESSOR_INTEL_486:
1812 case PROCESSOR_INTEL_PENTIUM:
1813 arch = "i386";
1814 break;
1815 #endif
1817 #ifdef PROCESSOR_MIPS_R2000
1818 case PROCESSOR_MIPS_R2000:
1819 case PROCESSOR_MIPS_R3000:
1820 case PROCESSOR_MIPS_R4000:
1821 arch = "mips";
1822 break;
1823 #endif
1825 #ifdef PROCESSOR_ALPHA_21064
1826 case PROCESSOR_ALPHA_21064:
1827 arch = "alpha";
1828 break;
1829 #endif
1831 default:
1832 arch = "unknown";
1833 break;
1836 /* Use the OEM field to reflect the compiler/library combination. */
1837 #ifdef _MSC_VER
1838 #define COMPILER_NAME "msvc"
1839 #else
1840 #ifdef __GNUC__
1841 #define COMPILER_NAME "mingw"
1842 #else
1843 #define COMPILER_NAME "unknown"
1844 #endif
1845 #endif
1846 oem = COMPILER_NAME;
1848 switch (osinfo_cache.dwPlatformId) {
1849 case VER_PLATFORM_WIN32_NT:
1850 os = "nt";
1851 build_num = osinfo_cache.dwBuildNumber;
1852 break;
1853 case VER_PLATFORM_WIN32_WINDOWS:
1854 if (osinfo_cache.dwMinorVersion == 0) {
1855 os = "windows95";
1856 } else {
1857 os = "windows98";
1859 build_num = LOWORD (osinfo_cache.dwBuildNumber);
1860 break;
1861 case VER_PLATFORM_WIN32s:
1862 /* Not supported, should not happen. */
1863 os = "windows32s";
1864 build_num = LOWORD (osinfo_cache.dwBuildNumber);
1865 break;
1866 default:
1867 os = "unknown";
1868 build_num = 0;
1869 break;
1872 if (osinfo_cache.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1873 sprintf (configuration_buffer, "%s-%s-%s%d.%d.%d", arch, oem, os,
1874 get_w32_major_version (), get_w32_minor_version (), build_num);
1875 } else {
1876 sprintf (configuration_buffer, "%s-%s-%s.%d", arch, oem, os, build_num);
1879 return configuration_buffer;
1882 char *
1883 get_emacs_configuration_options (void)
1885 static char *options_buffer;
1886 char cv[32]; /* Enough for COMPILER_VERSION. */
1887 char *options[] = {
1888 cv, /* To be filled later. */
1889 #ifdef EMACSDEBUG
1890 " --no-opt",
1891 #endif
1892 /* configure.bat already sets USER_CFLAGS and USER_LDFLAGS
1893 with a starting space to save work here. */
1894 #ifdef USER_CFLAGS
1895 " --cflags", USER_CFLAGS,
1896 #endif
1897 #ifdef USER_LDFLAGS
1898 " --ldflags", USER_LDFLAGS,
1899 #endif
1900 NULL
1902 size_t size = 0;
1903 int i;
1905 /* Work out the effective configure options for this build. */
1906 #ifdef _MSC_VER
1907 #define COMPILER_VERSION "--with-msvc (%d.%02d)", _MSC_VER / 100, _MSC_VER % 100
1908 #else
1909 #ifdef __GNUC__
1910 #define COMPILER_VERSION "--with-gcc (%d.%d)", __GNUC__, __GNUC_MINOR__
1911 #else
1912 #define COMPILER_VERSION ""
1913 #endif
1914 #endif
1916 if (_snprintf (cv, sizeof (cv) - 1, COMPILER_VERSION) < 0)
1917 return "Error: not enough space for compiler version";
1918 cv[sizeof (cv) - 1] = '\0';
1920 for (i = 0; options[i]; i++)
1921 size += strlen (options[i]);
1923 options_buffer = xmalloc (size + 1);
1924 options_buffer[0] = '\0';
1926 for (i = 0; options[i]; i++)
1927 strcat (options_buffer, options[i]);
1929 return options_buffer;
1933 #include <sys/timeb.h>
1935 /* Emulate gettimeofday (Ulrich Leodolter, 1/11/95). */
1936 void
1937 gettimeofday (struct timeval *tv, struct timezone *tz)
1939 struct _timeb tb;
1940 _ftime (&tb);
1942 tv->tv_sec = tb.time;
1943 tv->tv_usec = tb.millitm * 1000L;
1944 if (tz)
1946 tz->tz_minuteswest = tb.timezone; /* minutes west of Greenwich */
1947 tz->tz_dsttime = tb.dstflag; /* type of dst correction */
1951 /* ------------------------------------------------------------------------- */
1952 /* IO support and wrapper functions for W32 API. */
1953 /* ------------------------------------------------------------------------- */
1955 /* Place a wrapper around the MSVC version of ctime. It returns NULL
1956 on network directories, so we handle that case here.
1957 (Ulrich Leodolter, 1/11/95). */
1958 char *
1959 sys_ctime (const time_t *t)
1961 char *str = (char *) ctime (t);
1962 return (str ? str : "Sun Jan 01 00:00:00 1970");
1965 /* Emulate sleep...we could have done this with a define, but that
1966 would necessitate including windows.h in the files that used it.
1967 This is much easier. */
1968 void
1969 sys_sleep (int seconds)
1971 Sleep (seconds * 1000);
1974 /* Internal MSVC functions for low-level descriptor munging */
1975 extern int __cdecl _set_osfhnd (int fd, long h);
1976 extern int __cdecl _free_osfhnd (int fd);
1978 /* parallel array of private info on file handles */
1979 filedesc fd_info [ MAXDESC ];
1981 typedef struct volume_info_data {
1982 struct volume_info_data * next;
1984 /* time when info was obtained */
1985 DWORD timestamp;
1987 /* actual volume info */
1988 char * root_dir;
1989 DWORD serialnum;
1990 DWORD maxcomp;
1991 DWORD flags;
1992 char * name;
1993 char * type;
1994 } volume_info_data;
1996 /* Global referenced by various functions. */
1997 static volume_info_data volume_info;
1999 /* Vector to indicate which drives are local and fixed (for which cached
2000 data never expires). */
2001 static BOOL fixed_drives[26];
2003 /* Consider cached volume information to be stale if older than 10s,
2004 at least for non-local drives. Info for fixed drives is never stale. */
2005 #define DRIVE_INDEX( c ) ( (c) <= 'Z' ? (c) - 'A' : (c) - 'a' )
2006 #define VOLINFO_STILL_VALID( root_dir, info ) \
2007 ( ( isalpha (root_dir[0]) && \
2008 fixed_drives[ DRIVE_INDEX (root_dir[0]) ] ) \
2009 || GetTickCount () - info->timestamp < 10000 )
2011 /* Cache support functions. */
2013 /* Simple linked list with linear search is sufficient. */
2014 static volume_info_data *volume_cache = NULL;
2016 static volume_info_data *
2017 lookup_volume_info (char * root_dir)
2019 volume_info_data * info;
2021 for (info = volume_cache; info; info = info->next)
2022 if (xstrcasecmp (info->root_dir, root_dir) == 0)
2023 break;
2024 return info;
2027 static void
2028 add_volume_info (char * root_dir, volume_info_data * info)
2030 info->root_dir = xstrdup (root_dir);
2031 info->next = volume_cache;
2032 volume_cache = info;
2036 /* Wrapper for GetVolumeInformation, which uses caching to avoid
2037 performance penalty (~2ms on 486 for local drives, 7.5ms for local
2038 cdrom drive, ~5-10ms or more for remote drives on LAN). */
2039 static volume_info_data *
2040 GetCachedVolumeInformation (char * root_dir)
2042 volume_info_data * info;
2043 char default_root[ MAX_PATH ];
2045 /* NULL for root_dir means use root from current directory. */
2046 if (root_dir == NULL)
2048 if (GetCurrentDirectory (MAX_PATH, default_root) == 0)
2049 return NULL;
2050 parse_root (default_root, &root_dir);
2051 *root_dir = 0;
2052 root_dir = default_root;
2055 /* Local fixed drives can be cached permanently. Removable drives
2056 cannot be cached permanently, since the volume name and serial
2057 number (if nothing else) can change. Remote drives should be
2058 treated as if they are removable, since there is no sure way to
2059 tell whether they are or not. Also, the UNC association of drive
2060 letters mapped to remote volumes can be changed at any time (even
2061 by other processes) without notice.
2063 As a compromise, so we can benefit from caching info for remote
2064 volumes, we use a simple expiry mechanism to invalidate cache
2065 entries that are more than ten seconds old. */
2067 #if 0
2068 /* No point doing this, because WNetGetConnection is even slower than
2069 GetVolumeInformation, consistently taking ~50ms on a 486 (FWIW,
2070 GetDriveType is about the only call of this type which does not
2071 involve network access, and so is extremely quick). */
2073 /* Map drive letter to UNC if remote. */
2074 if (isalpha (root_dir[0]) && !fixed[DRIVE_INDEX (root_dir[0])])
2076 char remote_name[ 256 ];
2077 char drive[3] = { root_dir[0], ':' };
2079 if (WNetGetConnection (drive, remote_name, sizeof (remote_name))
2080 == NO_ERROR)
2081 /* do something */ ;
2083 #endif
2085 info = lookup_volume_info (root_dir);
2087 if (info == NULL || ! VOLINFO_STILL_VALID (root_dir, info))
2089 char name[ 256 ];
2090 DWORD serialnum;
2091 DWORD maxcomp;
2092 DWORD flags;
2093 char type[ 256 ];
2095 /* Info is not cached, or is stale. */
2096 if (!GetVolumeInformation (root_dir,
2097 name, sizeof (name),
2098 &serialnum,
2099 &maxcomp,
2100 &flags,
2101 type, sizeof (type)))
2102 return NULL;
2104 /* Cache the volume information for future use, overwriting existing
2105 entry if present. */
2106 if (info == NULL)
2108 info = (volume_info_data *) xmalloc (sizeof (volume_info_data));
2109 add_volume_info (root_dir, info);
2111 else
2113 xfree (info->name);
2114 xfree (info->type);
2117 info->name = xstrdup (name);
2118 info->serialnum = serialnum;
2119 info->maxcomp = maxcomp;
2120 info->flags = flags;
2121 info->type = xstrdup (type);
2122 info->timestamp = GetTickCount ();
2125 return info;
2128 /* Get information on the volume where name is held; set path pointer to
2129 start of pathname in name (past UNC header\volume header if present). */
2130 static int
2131 get_volume_info (const char * name, const char ** pPath)
2133 char temp[MAX_PATH];
2134 char *rootname = NULL; /* default to current volume */
2135 volume_info_data * info;
2137 if (name == NULL)
2138 return FALSE;
2140 /* find the root name of the volume if given */
2141 if (isalpha (name[0]) && name[1] == ':')
2143 rootname = temp;
2144 temp[0] = *name++;
2145 temp[1] = *name++;
2146 temp[2] = '\\';
2147 temp[3] = 0;
2149 else if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1]))
2151 char *str = temp;
2152 int slashes = 4;
2153 rootname = temp;
2156 if (IS_DIRECTORY_SEP (*name) && --slashes == 0)
2157 break;
2158 *str++ = *name++;
2160 while ( *name );
2162 *str++ = '\\';
2163 *str = 0;
2166 if (pPath)
2167 *pPath = name;
2169 info = GetCachedVolumeInformation (rootname);
2170 if (info != NULL)
2172 /* Set global referenced by other functions. */
2173 volume_info = *info;
2174 return TRUE;
2176 return FALSE;
2179 /* Determine if volume is FAT format (ie. only supports short 8.3
2180 names); also set path pointer to start of pathname in name. */
2181 static int
2182 is_fat_volume (const char * name, const char ** pPath)
2184 if (get_volume_info (name, pPath))
2185 return (volume_info.maxcomp == 12);
2186 return FALSE;
2189 /* Map filename to a valid 8.3 name if necessary. */
2190 const char *
2191 map_w32_filename (const char * name, const char ** pPath)
2193 static char shortname[MAX_PATH];
2194 char * str = shortname;
2195 char c;
2196 char * path;
2197 const char * save_name = name;
2199 if (strlen (name) >= MAX_PATH)
2201 /* Return a filename which will cause callers to fail. */
2202 strcpy (shortname, "?");
2203 return shortname;
2206 if (is_fat_volume (name, (const char **)&path)) /* truncate to 8.3 */
2208 register int left = 8; /* maximum number of chars in part */
2209 register int extn = 0; /* extension added? */
2210 register int dots = 2; /* maximum number of dots allowed */
2212 while (name < path)
2213 *str++ = *name++; /* skip past UNC header */
2215 while ((c = *name++))
2217 switch ( c )
2219 case '\\':
2220 case '/':
2221 *str++ = '\\';
2222 extn = 0; /* reset extension flags */
2223 dots = 2; /* max 2 dots */
2224 left = 8; /* max length 8 for main part */
2225 break;
2226 case ':':
2227 *str++ = ':';
2228 extn = 0; /* reset extension flags */
2229 dots = 2; /* max 2 dots */
2230 left = 8; /* max length 8 for main part */
2231 break;
2232 case '.':
2233 if ( dots )
2235 /* Convert path components of the form .xxx to _xxx,
2236 but leave . and .. as they are. This allows .emacs
2237 to be read as _emacs, for example. */
2239 if (! *name ||
2240 *name == '.' ||
2241 IS_DIRECTORY_SEP (*name))
2243 *str++ = '.';
2244 dots--;
2246 else
2248 *str++ = '_';
2249 left--;
2250 dots = 0;
2253 else if ( !extn )
2255 *str++ = '.';
2256 extn = 1; /* we've got an extension */
2257 left = 3; /* 3 chars in extension */
2259 else
2261 /* any embedded dots after the first are converted to _ */
2262 *str++ = '_';
2264 break;
2265 case '~':
2266 case '#': /* don't lose these, they're important */
2267 if ( ! left )
2268 str[-1] = c; /* replace last character of part */
2269 /* FALLTHRU */
2270 default:
2271 if ( left )
2273 *str++ = tolower (c); /* map to lower case (looks nicer) */
2274 left--;
2275 dots = 0; /* started a path component */
2277 break;
2280 *str = '\0';
2282 else
2284 strcpy (shortname, name);
2285 unixtodos_filename (shortname);
2288 if (pPath)
2289 *pPath = shortname + (path - save_name);
2291 return shortname;
2294 static int
2295 is_exec (const char * name)
2297 char * p = strrchr (name, '.');
2298 return
2299 (p != NULL
2300 && (xstrcasecmp (p, ".exe") == 0 ||
2301 xstrcasecmp (p, ".com") == 0 ||
2302 xstrcasecmp (p, ".bat") == 0 ||
2303 xstrcasecmp (p, ".cmd") == 0));
2306 /* Emulate the Unix directory procedures opendir, closedir,
2307 and readdir. We can't use the procedures supplied in sysdep.c,
2308 so we provide them here. */
2310 struct direct dir_static; /* simulated directory contents */
2311 static HANDLE dir_find_handle = INVALID_HANDLE_VALUE;
2312 static int dir_is_fat;
2313 static char dir_pathname[MAXPATHLEN+1];
2314 static WIN32_FIND_DATA dir_find_data;
2316 /* Support shares on a network resource as subdirectories of a read-only
2317 root directory. */
2318 static HANDLE wnet_enum_handle = INVALID_HANDLE_VALUE;
2319 static HANDLE open_unc_volume (const char *);
2320 static char *read_unc_volume (HANDLE, char *, int);
2321 static void close_unc_volume (HANDLE);
2323 DIR *
2324 opendir (char *filename)
2326 DIR *dirp;
2328 /* Opening is done by FindFirstFile. However, a read is inherent to
2329 this operation, so we defer the open until read time. */
2331 if (dir_find_handle != INVALID_HANDLE_VALUE)
2332 return NULL;
2333 if (wnet_enum_handle != INVALID_HANDLE_VALUE)
2334 return NULL;
2336 if (is_unc_volume (filename))
2338 wnet_enum_handle = open_unc_volume (filename);
2339 if (wnet_enum_handle == INVALID_HANDLE_VALUE)
2340 return NULL;
2343 if (!(dirp = (DIR *) malloc (sizeof (DIR))))
2344 return NULL;
2346 dirp->dd_fd = 0;
2347 dirp->dd_loc = 0;
2348 dirp->dd_size = 0;
2350 strncpy (dir_pathname, map_w32_filename (filename, NULL), MAXPATHLEN);
2351 dir_pathname[MAXPATHLEN] = '\0';
2352 dir_is_fat = is_fat_volume (filename, NULL);
2354 return dirp;
2357 void
2358 closedir (DIR *dirp)
2360 /* If we have a find-handle open, close it. */
2361 if (dir_find_handle != INVALID_HANDLE_VALUE)
2363 FindClose (dir_find_handle);
2364 dir_find_handle = INVALID_HANDLE_VALUE;
2366 else if (wnet_enum_handle != INVALID_HANDLE_VALUE)
2368 close_unc_volume (wnet_enum_handle);
2369 wnet_enum_handle = INVALID_HANDLE_VALUE;
2371 xfree ((char *) dirp);
2374 struct direct *
2375 readdir (DIR *dirp)
2377 int downcase = !NILP (Vw32_downcase_file_names);
2379 if (wnet_enum_handle != INVALID_HANDLE_VALUE)
2381 if (!read_unc_volume (wnet_enum_handle,
2382 dir_find_data.cFileName,
2383 MAX_PATH))
2384 return NULL;
2386 /* If we aren't dir_finding, do a find-first, otherwise do a find-next. */
2387 else if (dir_find_handle == INVALID_HANDLE_VALUE)
2389 char filename[MAXNAMLEN + 3];
2390 int ln;
2392 strcpy (filename, dir_pathname);
2393 ln = strlen (filename) - 1;
2394 if (!IS_DIRECTORY_SEP (filename[ln]))
2395 strcat (filename, "\\");
2396 strcat (filename, "*");
2398 dir_find_handle = FindFirstFile (filename, &dir_find_data);
2400 if (dir_find_handle == INVALID_HANDLE_VALUE)
2401 return NULL;
2403 else
2405 if (!FindNextFile (dir_find_handle, &dir_find_data))
2406 return NULL;
2409 /* Emacs never uses this value, so don't bother making it match
2410 value returned by stat(). */
2411 dir_static.d_ino = 1;
2413 strcpy (dir_static.d_name, dir_find_data.cFileName);
2415 /* If the file name in cFileName[] includes `?' characters, it means
2416 the original file name used characters that cannot be represented
2417 by the current ANSI codepage. To avoid total lossage, retrieve
2418 the short 8+3 alias of the long file name. */
2419 if (_mbspbrk (dir_static.d_name, "?"))
2421 strcpy (dir_static.d_name, dir_find_data.cAlternateFileName);
2422 downcase = 1; /* 8+3 aliases are returned in all caps */
2424 dir_static.d_namlen = strlen (dir_static.d_name);
2425 dir_static.d_reclen = sizeof (struct direct) - MAXNAMLEN + 3 +
2426 dir_static.d_namlen - dir_static.d_namlen % 4;
2428 /* If the file name in cFileName[] includes `?' characters, it means
2429 the original file name used characters that cannot be represented
2430 by the current ANSI codepage. To avoid total lossage, retrieve
2431 the short 8+3 alias of the long file name. */
2432 if (_mbspbrk (dir_find_data.cFileName, "?"))
2434 strcpy (dir_static.d_name, dir_find_data.cAlternateFileName);
2435 /* 8+3 aliases are returned in all caps, which could break
2436 various alists that look at filenames' extensions. */
2437 downcase = 1;
2439 else
2440 strcpy (dir_static.d_name, dir_find_data.cFileName);
2441 dir_static.d_namlen = strlen (dir_static.d_name);
2442 if (dir_is_fat)
2443 _strlwr (dir_static.d_name);
2444 else if (downcase)
2446 register char *p;
2447 for (p = dir_static.d_name; *p; p++)
2448 if (*p >= 'a' && *p <= 'z')
2449 break;
2450 if (!*p)
2451 _strlwr (dir_static.d_name);
2454 return &dir_static;
2457 static HANDLE
2458 open_unc_volume (const char *path)
2460 NETRESOURCE nr;
2461 HANDLE henum;
2462 int result;
2464 nr.dwScope = RESOURCE_GLOBALNET;
2465 nr.dwType = RESOURCETYPE_DISK;
2466 nr.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER;
2467 nr.dwUsage = RESOURCEUSAGE_CONTAINER;
2468 nr.lpLocalName = NULL;
2469 nr.lpRemoteName = (LPSTR)map_w32_filename (path, NULL);
2470 nr.lpComment = NULL;
2471 nr.lpProvider = NULL;
2473 result = WNetOpenEnum (RESOURCE_GLOBALNET, RESOURCETYPE_DISK,
2474 RESOURCEUSAGE_CONNECTABLE, &nr, &henum);
2476 if (result == NO_ERROR)
2477 return henum;
2478 else
2479 return INVALID_HANDLE_VALUE;
2482 static char *
2483 read_unc_volume (HANDLE henum, char *readbuf, int size)
2485 DWORD count;
2486 int result;
2487 DWORD bufsize = 512;
2488 char *buffer;
2489 char *ptr;
2491 count = 1;
2492 buffer = alloca (bufsize);
2493 result = WNetEnumResource (wnet_enum_handle, &count, buffer, &bufsize);
2494 if (result != NO_ERROR)
2495 return NULL;
2497 /* WNetEnumResource returns \\resource\share...skip forward to "share". */
2498 ptr = ((LPNETRESOURCE) buffer)->lpRemoteName;
2499 ptr += 2;
2500 while (*ptr && !IS_DIRECTORY_SEP (*ptr)) ptr++;
2501 ptr++;
2503 strncpy (readbuf, ptr, size);
2504 return readbuf;
2507 static void
2508 close_unc_volume (HANDLE henum)
2510 if (henum != INVALID_HANDLE_VALUE)
2511 WNetCloseEnum (henum);
2514 static DWORD
2515 unc_volume_file_attributes (const char *path)
2517 HANDLE henum;
2518 DWORD attrs;
2520 henum = open_unc_volume (path);
2521 if (henum == INVALID_HANDLE_VALUE)
2522 return -1;
2524 attrs = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_DIRECTORY;
2526 close_unc_volume (henum);
2528 return attrs;
2531 /* Ensure a network connection is authenticated. */
2532 static void
2533 logon_network_drive (const char *path)
2535 NETRESOURCE resource;
2536 char share[MAX_PATH];
2537 int i, n_slashes;
2538 char drive[4];
2539 UINT drvtype;
2541 if (IS_DIRECTORY_SEP (path[0]) && IS_DIRECTORY_SEP (path[1]))
2542 drvtype = DRIVE_REMOTE;
2543 else if (path[0] == '\0' || path[1] != ':')
2544 drvtype = GetDriveType (NULL);
2545 else
2547 drive[0] = path[0];
2548 drive[1] = ':';
2549 drive[2] = '\\';
2550 drive[3] = '\0';
2551 drvtype = GetDriveType (drive);
2554 /* Only logon to networked drives. */
2555 if (drvtype != DRIVE_REMOTE)
2556 return;
2558 n_slashes = 2;
2559 strncpy (share, path, MAX_PATH);
2560 /* Truncate to just server and share name. */
2561 for (i = 2; i < MAX_PATH; i++)
2563 if (IS_DIRECTORY_SEP (share[i]) && ++n_slashes > 3)
2565 share[i] = '\0';
2566 break;
2570 resource.dwType = RESOURCETYPE_DISK;
2571 resource.lpLocalName = NULL;
2572 resource.lpRemoteName = share;
2573 resource.lpProvider = NULL;
2575 WNetAddConnection2 (&resource, NULL, NULL, CONNECT_INTERACTIVE);
2578 /* Shadow some MSVC runtime functions to map requests for long filenames
2579 to reasonable short names if necessary. This was originally added to
2580 permit running Emacs on NT 3.1 on a FAT partition, which doesn't support
2581 long file names. */
2584 sys_access (const char * path, int mode)
2586 DWORD attributes;
2588 /* MSVC implementation doesn't recognize D_OK. */
2589 path = map_w32_filename (path, NULL);
2590 if (is_unc_volume (path))
2592 attributes = unc_volume_file_attributes (path);
2593 if (attributes == -1) {
2594 errno = EACCES;
2595 return -1;
2598 else if ((attributes = GetFileAttributes (path)) == -1)
2600 /* Should try mapping GetLastError to errno; for now just indicate
2601 that path doesn't exist. */
2602 errno = EACCES;
2603 return -1;
2605 if ((mode & X_OK) != 0 && !is_exec (path))
2607 errno = EACCES;
2608 return -1;
2610 if ((mode & W_OK) != 0 && (attributes & FILE_ATTRIBUTE_READONLY) != 0)
2612 errno = EACCES;
2613 return -1;
2615 if ((mode & D_OK) != 0 && (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
2617 errno = EACCES;
2618 return -1;
2620 return 0;
2624 sys_chdir (const char * path)
2626 return _chdir (map_w32_filename (path, NULL));
2630 sys_chmod (const char * path, int mode)
2632 return _chmod (map_w32_filename (path, NULL), mode);
2636 sys_chown (const char *path, uid_t owner, gid_t group)
2638 if (sys_chmod (path, S_IREAD) == -1) /* check if file exists */
2639 return -1;
2640 return 0;
2644 sys_creat (const char * path, int mode)
2646 return _creat (map_w32_filename (path, NULL), mode);
2649 FILE *
2650 sys_fopen (const char * path, const char * mode)
2652 int fd;
2653 int oflag;
2654 const char * mode_save = mode;
2656 /* Force all file handles to be non-inheritable. This is necessary to
2657 ensure child processes don't unwittingly inherit handles that might
2658 prevent future file access. */
2660 if (mode[0] == 'r')
2661 oflag = O_RDONLY;
2662 else if (mode[0] == 'w' || mode[0] == 'a')
2663 oflag = O_WRONLY | O_CREAT | O_TRUNC;
2664 else
2665 return NULL;
2667 /* Only do simplistic option parsing. */
2668 while (*++mode)
2669 if (mode[0] == '+')
2671 oflag &= ~(O_RDONLY | O_WRONLY);
2672 oflag |= O_RDWR;
2674 else if (mode[0] == 'b')
2676 oflag &= ~O_TEXT;
2677 oflag |= O_BINARY;
2679 else if (mode[0] == 't')
2681 oflag &= ~O_BINARY;
2682 oflag |= O_TEXT;
2684 else break;
2686 fd = _open (map_w32_filename (path, NULL), oflag | _O_NOINHERIT, 0644);
2687 if (fd < 0)
2688 return NULL;
2690 return _fdopen (fd, mode_save);
2693 /* This only works on NTFS volumes, but is useful to have. */
2695 sys_link (const char * old, const char * new)
2697 HANDLE fileh;
2698 int result = -1;
2699 char oldname[MAX_PATH], newname[MAX_PATH];
2701 if (old == NULL || new == NULL)
2703 errno = ENOENT;
2704 return -1;
2707 strcpy (oldname, map_w32_filename (old, NULL));
2708 strcpy (newname, map_w32_filename (new, NULL));
2710 fileh = CreateFile (oldname, 0, 0, NULL, OPEN_EXISTING,
2711 FILE_FLAG_BACKUP_SEMANTICS, NULL);
2712 if (fileh != INVALID_HANDLE_VALUE)
2714 int wlen;
2716 /* Confusingly, the "alternate" stream name field does not apply
2717 when restoring a hard link, and instead contains the actual
2718 stream data for the link (ie. the name of the link to create).
2719 The WIN32_STREAM_ID structure before the cStreamName field is
2720 the stream header, which is then immediately followed by the
2721 stream data. */
2723 struct {
2724 WIN32_STREAM_ID wid;
2725 WCHAR wbuffer[MAX_PATH]; /* extra space for link name */
2726 } data;
2728 wlen = MultiByteToWideChar (CP_ACP, MB_PRECOMPOSED, newname, -1,
2729 data.wid.cStreamName, MAX_PATH);
2730 if (wlen > 0)
2732 LPVOID context = NULL;
2733 DWORD wbytes = 0;
2735 data.wid.dwStreamId = BACKUP_LINK;
2736 data.wid.dwStreamAttributes = 0;
2737 data.wid.Size.LowPart = wlen * sizeof (WCHAR);
2738 data.wid.Size.HighPart = 0;
2739 data.wid.dwStreamNameSize = 0;
2741 if (BackupWrite (fileh, (LPBYTE)&data,
2742 offsetof (WIN32_STREAM_ID, cStreamName)
2743 + data.wid.Size.LowPart,
2744 &wbytes, FALSE, FALSE, &context)
2745 && BackupWrite (fileh, NULL, 0, &wbytes, TRUE, FALSE, &context))
2747 /* succeeded */
2748 result = 0;
2750 else
2752 /* Should try mapping GetLastError to errno; for now just
2753 indicate a general error (eg. links not supported). */
2754 errno = EINVAL; // perhaps EMLINK?
2758 CloseHandle (fileh);
2760 else
2761 errno = ENOENT;
2763 return result;
2767 sys_mkdir (const char * path)
2769 return _mkdir (map_w32_filename (path, NULL));
2772 /* Because of long name mapping issues, we need to implement this
2773 ourselves. Also, MSVC's _mktemp returns NULL when it can't generate
2774 a unique name, instead of setting the input template to an empty
2775 string.
2777 Standard algorithm seems to be use pid or tid with a letter on the
2778 front (in place of the 6 X's) and cycle through the letters to find a
2779 unique name. We extend that to allow any reasonable character as the
2780 first of the 6 X's. */
2781 char *
2782 sys_mktemp (char * template)
2784 char * p;
2785 int i;
2786 unsigned uid = GetCurrentThreadId ();
2787 static char first_char[] = "abcdefghijklmnopqrstuvwyz0123456789!%-_@#";
2789 if (template == NULL)
2790 return NULL;
2791 p = template + strlen (template);
2792 i = 5;
2793 /* replace up to the last 5 X's with uid in decimal */
2794 while (--p >= template && p[0] == 'X' && --i >= 0)
2796 p[0] = '0' + uid % 10;
2797 uid /= 10;
2800 if (i < 0 && p[0] == 'X')
2802 i = 0;
2805 int save_errno = errno;
2806 p[0] = first_char[i];
2807 if (sys_access (template, 0) < 0)
2809 errno = save_errno;
2810 return template;
2813 while (++i < sizeof (first_char));
2816 /* Template is badly formed or else we can't generate a unique name,
2817 so return empty string */
2818 template[0] = 0;
2819 return template;
2823 sys_open (const char * path, int oflag, int mode)
2825 const char* mpath = map_w32_filename (path, NULL);
2826 /* Try to open file without _O_CREAT, to be able to write to hidden
2827 and system files. Force all file handles to be
2828 non-inheritable. */
2829 int res = _open (mpath, (oflag & ~_O_CREAT) | _O_NOINHERIT, mode);
2830 if (res >= 0)
2831 return res;
2832 return _open (mpath, oflag | _O_NOINHERIT, mode);
2836 sys_rename (const char * oldname, const char * newname)
2838 BOOL result;
2839 char temp[MAX_PATH];
2841 /* MoveFile on Windows 95 doesn't correctly change the short file name
2842 alias in a number of circumstances (it is not easy to predict when
2843 just by looking at oldname and newname, unfortunately). In these
2844 cases, renaming through a temporary name avoids the problem.
2846 A second problem on Windows 95 is that renaming through a temp name when
2847 newname is uppercase fails (the final long name ends up in
2848 lowercase, although the short alias might be uppercase) UNLESS the
2849 long temp name is not 8.3.
2851 So, on Windows 95 we always rename through a temp name, and we make sure
2852 the temp name has a long extension to ensure correct renaming. */
2854 strcpy (temp, map_w32_filename (oldname, NULL));
2856 if (os_subtype == OS_WIN95)
2858 char * o;
2859 char * p;
2860 int i = 0;
2862 oldname = map_w32_filename (oldname, NULL);
2863 if (o = strrchr (oldname, '\\'))
2864 o++;
2865 else
2866 o = (char *) oldname;
2868 if (p = strrchr (temp, '\\'))
2869 p++;
2870 else
2871 p = temp;
2875 /* Force temp name to require a manufactured 8.3 alias - this
2876 seems to make the second rename work properly. */
2877 sprintf (p, "_.%s.%u", o, i);
2878 i++;
2879 result = rename (oldname, temp);
2881 /* This loop must surely terminate! */
2882 while (result < 0 && errno == EEXIST);
2883 if (result < 0)
2884 return -1;
2887 /* Emulate Unix behavior - newname is deleted if it already exists
2888 (at least if it is a file; don't do this for directories).
2890 Since we mustn't do this if we are just changing the case of the
2891 file name (we would end up deleting the file we are trying to
2892 rename!), we let rename detect if the destination file already
2893 exists - that way we avoid the possible pitfalls of trying to
2894 determine ourselves whether two names really refer to the same
2895 file, which is not always possible in the general case. (Consider
2896 all the permutations of shared or subst'd drives, etc.) */
2898 newname = map_w32_filename (newname, NULL);
2899 result = rename (temp, newname);
2901 if (result < 0
2902 && errno == EEXIST
2903 && _chmod (newname, 0666) == 0
2904 && _unlink (newname) == 0)
2905 result = rename (temp, newname);
2907 return result;
2911 sys_rmdir (const char * path)
2913 return _rmdir (map_w32_filename (path, NULL));
2917 sys_unlink (const char * path)
2919 path = map_w32_filename (path, NULL);
2921 /* On Unix, unlink works without write permission. */
2922 _chmod (path, 0666);
2923 return _unlink (path);
2926 static FILETIME utc_base_ft;
2927 static ULONGLONG utc_base; /* In 100ns units */
2928 static int init = 0;
2930 #define FILETIME_TO_U64(result, ft) \
2931 do { \
2932 ULARGE_INTEGER uiTemp; \
2933 uiTemp.LowPart = (ft).dwLowDateTime; \
2934 uiTemp.HighPart = (ft).dwHighDateTime; \
2935 result = uiTemp.QuadPart; \
2936 } while (0)
2938 static void
2939 initialize_utc_base (void)
2941 /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */
2942 SYSTEMTIME st;
2944 st.wYear = 1970;
2945 st.wMonth = 1;
2946 st.wDay = 1;
2947 st.wHour = 0;
2948 st.wMinute = 0;
2949 st.wSecond = 0;
2950 st.wMilliseconds = 0;
2952 SystemTimeToFileTime (&st, &utc_base_ft);
2953 FILETIME_TO_U64 (utc_base, utc_base_ft);
2956 static time_t
2957 convert_time (FILETIME ft)
2959 ULONGLONG tmp;
2961 if (!init)
2963 initialize_utc_base ();
2964 init = 1;
2967 if (CompareFileTime (&ft, &utc_base_ft) < 0)
2968 return 0;
2970 FILETIME_TO_U64 (tmp, ft);
2971 return (time_t) ((tmp - utc_base) / 10000000L);
2974 static void
2975 convert_from_time_t (time_t time, FILETIME * pft)
2977 ULARGE_INTEGER tmp;
2979 if (!init)
2981 initialize_utc_base ();
2982 init = 1;
2985 /* time in 100ns units since 1-Jan-1601 */
2986 tmp.QuadPart = (ULONGLONG) time * 10000000L + utc_base;
2987 pft->dwHighDateTime = tmp.HighPart;
2988 pft->dwLowDateTime = tmp.LowPart;
2991 #if 0
2992 /* No reason to keep this; faking inode values either by hashing or even
2993 using the file index from GetInformationByHandle, is not perfect and
2994 so by default Emacs doesn't use the inode values on Windows.
2995 Instead, we now determine file-truename correctly (except for
2996 possible drive aliasing etc). */
2998 /* Modified version of "PJW" algorithm (see the "Dragon" compiler book). */
2999 static unsigned
3000 hashval (const unsigned char * str)
3002 unsigned h = 0;
3003 while (*str)
3005 h = (h << 4) + *str++;
3006 h ^= (h >> 28);
3008 return h;
3011 /* Return the hash value of the canonical pathname, excluding the
3012 drive/UNC header, to get a hopefully unique inode number. */
3013 static DWORD
3014 generate_inode_val (const char * name)
3016 char fullname[ MAX_PATH ];
3017 char * p;
3018 unsigned hash;
3020 /* Get the truly canonical filename, if it exists. (Note: this
3021 doesn't resolve aliasing due to subst commands, or recognise hard
3022 links. */
3023 if (!w32_get_long_filename ((char *)name, fullname, MAX_PATH))
3024 abort ();
3026 parse_root (fullname, &p);
3027 /* Normal W32 filesystems are still case insensitive. */
3028 _strlwr (p);
3029 return hashval (p);
3032 #endif
3034 static PSECURITY_DESCRIPTOR
3035 get_file_security_desc (const char *fname)
3037 PSECURITY_DESCRIPTOR psd = NULL;
3038 DWORD sd_len, err;
3039 SECURITY_INFORMATION si = OWNER_SECURITY_INFORMATION
3040 | GROUP_SECURITY_INFORMATION /* | DACL_SECURITY_INFORMATION */ ;
3042 if (!get_file_security (fname, si, psd, 0, &sd_len))
3044 err = GetLastError ();
3045 if (err != ERROR_INSUFFICIENT_BUFFER)
3046 return NULL;
3049 psd = xmalloc (sd_len);
3050 if (!get_file_security (fname, si, psd, sd_len, &sd_len))
3052 xfree (psd);
3053 return NULL;
3056 return psd;
3059 static DWORD
3060 get_rid (PSID sid)
3062 unsigned n_subauthorities;
3064 /* Use the last sub-authority value of the RID, the relative
3065 portion of the SID, as user/group ID. */
3066 n_subauthorities = *get_sid_sub_authority_count (sid);
3067 if (n_subauthorities < 1)
3068 return 0; /* the "World" RID */
3069 return *get_sid_sub_authority (sid, n_subauthorities - 1);
3072 /* Caching SID and account values for faster lokup. */
3074 #ifdef __GNUC__
3075 # define FLEXIBLE_ARRAY_MEMBER
3076 #else
3077 # define FLEXIBLE_ARRAY_MEMBER 1
3078 #endif
3080 struct w32_id {
3081 unsigned rid;
3082 struct w32_id *next;
3083 char name[GNLEN+1];
3084 unsigned char sid[FLEXIBLE_ARRAY_MEMBER];
3087 static struct w32_id *w32_idlist;
3089 static int
3090 w32_cached_id (PSID sid, unsigned *id, char *name)
3092 struct w32_id *tail, *found;
3094 for (found = NULL, tail = w32_idlist; tail; tail = tail->next)
3096 if (equal_sid ((PSID)tail->sid, sid))
3098 found = tail;
3099 break;
3102 if (found)
3104 *id = found->rid;
3105 strcpy (name, found->name);
3106 return 1;
3108 else
3109 return 0;
3112 static void
3113 w32_add_to_cache (PSID sid, unsigned id, char *name)
3115 DWORD sid_len;
3116 struct w32_id *new_entry;
3118 /* We don't want to leave behind stale cache from when Emacs was
3119 dumped. */
3120 if (initialized)
3122 sid_len = get_length_sid (sid);
3123 new_entry = xmalloc (offsetof (struct w32_id, sid) + sid_len);
3124 if (new_entry)
3126 new_entry->rid = id;
3127 strcpy (new_entry->name, name);
3128 copy_sid (sid_len, (PSID)new_entry->sid, sid);
3129 new_entry->next = w32_idlist;
3130 w32_idlist = new_entry;
3135 #define UID 1
3136 #define GID 2
3138 static int
3139 get_name_and_id (PSECURITY_DESCRIPTOR psd, const char *fname,
3140 unsigned *id, char *nm, int what)
3142 PSID sid = NULL;
3143 char machine[MAX_COMPUTERNAME_LENGTH+1];
3144 BOOL dflt;
3145 SID_NAME_USE ignore;
3146 char name[UNLEN+1];
3147 DWORD name_len = sizeof (name);
3148 char domain[1024];
3149 DWORD domain_len = sizeof (domain);
3150 char *mp = NULL;
3151 int use_dflt = 0;
3152 int result;
3154 if (what == UID)
3155 result = get_security_descriptor_owner (psd, &sid, &dflt);
3156 else if (what == GID)
3157 result = get_security_descriptor_group (psd, &sid, &dflt);
3158 else
3159 result = 0;
3161 if (!result || !is_valid_sid (sid))
3162 use_dflt = 1;
3163 else if (!w32_cached_id (sid, id, nm))
3165 /* If FNAME is a UNC, we need to lookup account on the
3166 specified machine. */
3167 if (IS_DIRECTORY_SEP (fname[0]) && IS_DIRECTORY_SEP (fname[1])
3168 && fname[2] != '\0')
3170 const char *s;
3171 char *p;
3173 for (s = fname + 2, p = machine;
3174 *s && !IS_DIRECTORY_SEP (*s); s++, p++)
3175 *p = *s;
3176 *p = '\0';
3177 mp = machine;
3180 if (!lookup_account_sid (mp, sid, name, &name_len,
3181 domain, &domain_len, &ignore)
3182 || name_len > UNLEN+1)
3183 use_dflt = 1;
3184 else
3186 *id = get_rid (sid);
3187 strcpy (nm, name);
3188 w32_add_to_cache (sid, *id, name);
3191 return use_dflt;
3194 static void
3195 get_file_owner_and_group (PSECURITY_DESCRIPTOR psd,
3196 const char *fname,
3197 struct stat *st)
3199 int dflt_usr = 0, dflt_grp = 0;
3201 if (!psd)
3203 dflt_usr = 1;
3204 dflt_grp = 1;
3206 else
3208 if (get_name_and_id (psd, fname, &st->st_uid, st->st_uname, UID))
3209 dflt_usr = 1;
3210 if (get_name_and_id (psd, fname, &st->st_gid, st->st_gname, GID))
3211 dflt_grp = 1;
3213 /* Consider files to belong to current user/group, if we cannot get
3214 more accurate information. */
3215 if (dflt_usr)
3217 st->st_uid = dflt_passwd.pw_uid;
3218 strcpy (st->st_uname, dflt_passwd.pw_name);
3220 if (dflt_grp)
3222 st->st_gid = dflt_passwd.pw_gid;
3223 strcpy (st->st_gname, dflt_group.gr_name);
3227 /* Return non-zero if NAME is a potentially slow filesystem. */
3229 is_slow_fs (const char *name)
3231 char drive_root[4];
3232 UINT devtype;
3234 if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1]))
3235 devtype = DRIVE_REMOTE; /* assume UNC name is remote */
3236 else if (!(strlen (name) >= 2 && IS_DEVICE_SEP (name[1])))
3237 devtype = GetDriveType (NULL); /* use root of current drive */
3238 else
3240 /* GetDriveType needs the root directory of the drive. */
3241 strncpy (drive_root, name, 2);
3242 drive_root[2] = '\\';
3243 drive_root[3] = '\0';
3244 devtype = GetDriveType (drive_root);
3246 return !(devtype == DRIVE_FIXED || devtype == DRIVE_RAMDISK);
3249 /* MSVC stat function can't cope with UNC names and has other bugs, so
3250 replace it with our own. This also allows us to calculate consistent
3251 inode values without hacks in the main Emacs code. */
3253 stat (const char * path, struct stat * buf)
3255 char *name, *r;
3256 char drive_root[4];
3257 UINT devtype;
3258 WIN32_FIND_DATA wfd;
3259 HANDLE fh;
3260 unsigned __int64 fake_inode;
3261 int permission;
3262 int len;
3263 int rootdir = FALSE;
3264 PSECURITY_DESCRIPTOR psd = NULL;
3266 if (path == NULL || buf == NULL)
3268 errno = EFAULT;
3269 return -1;
3272 name = (char *) map_w32_filename (path, &path);
3273 /* Must be valid filename, no wild cards or other invalid
3274 characters. We use _mbspbrk to support multibyte strings that
3275 might look to strpbrk as if they included literal *, ?, and other
3276 characters mentioned below that are disallowed by Windows
3277 filesystems. */
3278 if (_mbspbrk (name, "*?|<>\""))
3280 errno = ENOENT;
3281 return -1;
3284 /* If name is "c:/.." or "/.." then stat "c:/" or "/". */
3285 r = IS_DEVICE_SEP (name[1]) ? &name[2] : name;
3286 if (IS_DIRECTORY_SEP (r[0]) && r[1] == '.' && r[2] == '.' && r[3] == '\0')
3288 r[1] = r[2] = '\0';
3291 /* Remove trailing directory separator, unless name is the root
3292 directory of a drive or UNC volume in which case ensure there
3293 is a trailing separator. */
3294 len = strlen (name);
3295 rootdir = (path >= name + len - 1
3296 && (IS_DIRECTORY_SEP (*path) || *path == 0));
3297 name = strcpy (alloca (len + 2), name);
3299 if (is_unc_volume (name))
3301 DWORD attrs = unc_volume_file_attributes (name);
3303 if (attrs == -1)
3304 return -1;
3306 memset (&wfd, 0, sizeof (wfd));
3307 wfd.dwFileAttributes = attrs;
3308 wfd.ftCreationTime = utc_base_ft;
3309 wfd.ftLastAccessTime = utc_base_ft;
3310 wfd.ftLastWriteTime = utc_base_ft;
3311 strcpy (wfd.cFileName, name);
3313 else if (rootdir)
3315 if (!IS_DIRECTORY_SEP (name[len-1]))
3316 strcat (name, "\\");
3317 if (GetDriveType (name) < 2)
3319 errno = ENOENT;
3320 return -1;
3322 memset (&wfd, 0, sizeof (wfd));
3323 wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
3324 wfd.ftCreationTime = utc_base_ft;
3325 wfd.ftLastAccessTime = utc_base_ft;
3326 wfd.ftLastWriteTime = utc_base_ft;
3327 strcpy (wfd.cFileName, name);
3329 else
3331 if (IS_DIRECTORY_SEP (name[len-1]))
3332 name[len - 1] = 0;
3334 /* (This is hacky, but helps when doing file completions on
3335 network drives.) Optimize by using information available from
3336 active readdir if possible. */
3337 len = strlen (dir_pathname);
3338 if (IS_DIRECTORY_SEP (dir_pathname[len-1]))
3339 len--;
3340 if (dir_find_handle != INVALID_HANDLE_VALUE
3341 && strnicmp (name, dir_pathname, len) == 0
3342 && IS_DIRECTORY_SEP (name[len])
3343 && xstrcasecmp (name + len + 1, dir_static.d_name) == 0)
3345 /* This was the last entry returned by readdir. */
3346 wfd = dir_find_data;
3348 else
3350 logon_network_drive (name);
3352 fh = FindFirstFile (name, &wfd);
3353 if (fh == INVALID_HANDLE_VALUE)
3355 errno = ENOENT;
3356 return -1;
3358 FindClose (fh);
3362 if (!(NILP (Vw32_get_true_file_attributes)
3363 || (EQ (Vw32_get_true_file_attributes, Qlocal) && is_slow_fs (name)))
3364 /* No access rights required to get info. */
3365 && (fh = CreateFile (name, 0, 0, NULL, OPEN_EXISTING,
3366 FILE_FLAG_BACKUP_SEMANTICS, NULL))
3367 != INVALID_HANDLE_VALUE)
3369 /* This is more accurate in terms of gettting the correct number
3370 of links, but is quite slow (it is noticeable when Emacs is
3371 making a list of file name completions). */
3372 BY_HANDLE_FILE_INFORMATION info;
3374 if (GetFileInformationByHandle (fh, &info))
3376 buf->st_nlink = info.nNumberOfLinks;
3377 /* Might as well use file index to fake inode values, but this
3378 is not guaranteed to be unique unless we keep a handle open
3379 all the time (even then there are situations where it is
3380 not unique). Reputedly, there are at most 48 bits of info
3381 (on NTFS, presumably less on FAT). */
3382 fake_inode = info.nFileIndexHigh;
3383 fake_inode <<= 32;
3384 fake_inode += info.nFileIndexLow;
3386 else
3388 buf->st_nlink = 1;
3389 fake_inode = 0;
3392 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
3394 buf->st_mode = S_IFDIR;
3396 else
3398 switch (GetFileType (fh))
3400 case FILE_TYPE_DISK:
3401 buf->st_mode = S_IFREG;
3402 break;
3403 case FILE_TYPE_PIPE:
3404 buf->st_mode = S_IFIFO;
3405 break;
3406 case FILE_TYPE_CHAR:
3407 case FILE_TYPE_UNKNOWN:
3408 default:
3409 buf->st_mode = S_IFCHR;
3412 CloseHandle (fh);
3413 psd = get_file_security_desc (name);
3414 get_file_owner_and_group (psd, name, buf);
3416 else
3418 /* Don't bother to make this information more accurate. */
3419 buf->st_mode = (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ?
3420 S_IFDIR : S_IFREG;
3421 buf->st_nlink = 1;
3422 fake_inode = 0;
3424 get_file_owner_and_group (NULL, name, buf);
3426 xfree (psd);
3428 #if 0
3429 /* Not sure if there is any point in this. */
3430 if (!NILP (Vw32_generate_fake_inodes))
3431 fake_inode = generate_inode_val (name);
3432 else if (fake_inode == 0)
3434 /* For want of something better, try to make everything unique. */
3435 static DWORD gen_num = 0;
3436 fake_inode = ++gen_num;
3438 #endif
3440 /* MSVC defines _ino_t to be short; other libc's might not. */
3441 if (sizeof (buf->st_ino) == 2)
3442 buf->st_ino = fake_inode ^ (fake_inode >> 16);
3443 else
3444 buf->st_ino = fake_inode;
3446 /* volume_info is set indirectly by map_w32_filename */
3447 buf->st_dev = volume_info.serialnum;
3448 buf->st_rdev = volume_info.serialnum;
3450 buf->st_size = wfd.nFileSizeHigh;
3451 buf->st_size <<= 32;
3452 buf->st_size += wfd.nFileSizeLow;
3454 /* Convert timestamps to Unix format. */
3455 buf->st_mtime = convert_time (wfd.ftLastWriteTime);
3456 buf->st_atime = convert_time (wfd.ftLastAccessTime);
3457 if (buf->st_atime == 0) buf->st_atime = buf->st_mtime;
3458 buf->st_ctime = convert_time (wfd.ftCreationTime);
3459 if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime;
3461 /* determine rwx permissions */
3462 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
3463 permission = S_IREAD;
3464 else
3465 permission = S_IREAD | S_IWRITE;
3467 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
3468 permission |= S_IEXEC;
3469 else if (is_exec (name))
3470 permission |= S_IEXEC;
3472 buf->st_mode |= permission | (permission >> 3) | (permission >> 6);
3474 return 0;
3477 /* Provide fstat and utime as well as stat for consistent handling of
3478 file timestamps. */
3480 fstat (int desc, struct stat * buf)
3482 HANDLE fh = (HANDLE) _get_osfhandle (desc);
3483 BY_HANDLE_FILE_INFORMATION info;
3484 unsigned __int64 fake_inode;
3485 int permission;
3487 switch (GetFileType (fh) & ~FILE_TYPE_REMOTE)
3489 case FILE_TYPE_DISK:
3490 buf->st_mode = S_IFREG;
3491 if (!GetFileInformationByHandle (fh, &info))
3493 errno = EACCES;
3494 return -1;
3496 break;
3497 case FILE_TYPE_PIPE:
3498 buf->st_mode = S_IFIFO;
3499 goto non_disk;
3500 case FILE_TYPE_CHAR:
3501 case FILE_TYPE_UNKNOWN:
3502 default:
3503 buf->st_mode = S_IFCHR;
3504 non_disk:
3505 memset (&info, 0, sizeof (info));
3506 info.dwFileAttributes = 0;
3507 info.ftCreationTime = utc_base_ft;
3508 info.ftLastAccessTime = utc_base_ft;
3509 info.ftLastWriteTime = utc_base_ft;
3512 if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
3513 buf->st_mode = S_IFDIR;
3515 buf->st_nlink = info.nNumberOfLinks;
3516 /* Might as well use file index to fake inode values, but this
3517 is not guaranteed to be unique unless we keep a handle open
3518 all the time (even then there are situations where it is
3519 not unique). Reputedly, there are at most 48 bits of info
3520 (on NTFS, presumably less on FAT). */
3521 fake_inode = info.nFileIndexHigh;
3522 fake_inode <<= 32;
3523 fake_inode += info.nFileIndexLow;
3525 /* MSVC defines _ino_t to be short; other libc's might not. */
3526 if (sizeof (buf->st_ino) == 2)
3527 buf->st_ino = fake_inode ^ (fake_inode >> 16);
3528 else
3529 buf->st_ino = fake_inode;
3531 /* Consider files to belong to current user.
3532 FIXME: this should use GetSecurityInfo API, but it is only
3533 available for _WIN32_WINNT >= 0x501. */
3534 buf->st_uid = dflt_passwd.pw_uid;
3535 buf->st_gid = dflt_passwd.pw_gid;
3536 strcpy (buf->st_uname, dflt_passwd.pw_name);
3537 strcpy (buf->st_gname, dflt_group.gr_name);
3539 buf->st_dev = info.dwVolumeSerialNumber;
3540 buf->st_rdev = info.dwVolumeSerialNumber;
3542 buf->st_size = info.nFileSizeHigh;
3543 buf->st_size <<= 32;
3544 buf->st_size += info.nFileSizeLow;
3546 /* Convert timestamps to Unix format. */
3547 buf->st_mtime = convert_time (info.ftLastWriteTime);
3548 buf->st_atime = convert_time (info.ftLastAccessTime);
3549 if (buf->st_atime == 0) buf->st_atime = buf->st_mtime;
3550 buf->st_ctime = convert_time (info.ftCreationTime);
3551 if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime;
3553 /* determine rwx permissions */
3554 if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
3555 permission = S_IREAD;
3556 else
3557 permission = S_IREAD | S_IWRITE;
3559 if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
3560 permission |= S_IEXEC;
3561 else
3563 #if 0 /* no way of knowing the filename */
3564 char * p = strrchr (name, '.');
3565 if (p != NULL &&
3566 (xstrcasecmp (p, ".exe") == 0 ||
3567 xstrcasecmp (p, ".com") == 0 ||
3568 xstrcasecmp (p, ".bat") == 0 ||
3569 xstrcasecmp (p, ".cmd") == 0))
3570 permission |= S_IEXEC;
3571 #endif
3574 buf->st_mode |= permission | (permission >> 3) | (permission >> 6);
3576 return 0;
3580 utime (const char *name, struct utimbuf *times)
3582 struct utimbuf deftime;
3583 HANDLE fh;
3584 FILETIME mtime;
3585 FILETIME atime;
3587 if (times == NULL)
3589 deftime.modtime = deftime.actime = time (NULL);
3590 times = &deftime;
3593 /* Need write access to set times. */
3594 fh = CreateFile (name, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
3595 0, OPEN_EXISTING, 0, NULL);
3596 if (fh)
3598 convert_from_time_t (times->actime, &atime);
3599 convert_from_time_t (times->modtime, &mtime);
3600 if (!SetFileTime (fh, NULL, &atime, &mtime))
3602 CloseHandle (fh);
3603 errno = EACCES;
3604 return -1;
3606 CloseHandle (fh);
3608 else
3610 errno = EINVAL;
3611 return -1;
3613 return 0;
3617 /* Support for browsing other processes and their attributes. See
3618 process.c for the Lisp bindings. */
3620 /* Helper wrapper functions. */
3622 static HANDLE WINAPI
3623 create_toolhelp32_snapshot (DWORD Flags, DWORD Ignored)
3625 static CreateToolhelp32Snapshot_Proc s_pfn_Create_Toolhelp32_Snapshot = NULL;
3627 if (g_b_init_create_toolhelp32_snapshot == 0)
3629 g_b_init_create_toolhelp32_snapshot = 1;
3630 s_pfn_Create_Toolhelp32_Snapshot = (CreateToolhelp32Snapshot_Proc)
3631 GetProcAddress (GetModuleHandle ("kernel32.dll"),
3632 "CreateToolhelp32Snapshot");
3634 if (s_pfn_Create_Toolhelp32_Snapshot == NULL)
3636 return INVALID_HANDLE_VALUE;
3638 return (s_pfn_Create_Toolhelp32_Snapshot (Flags, Ignored));
3641 static BOOL WINAPI
3642 process32_first (HANDLE hSnapshot, LPPROCESSENTRY32 lppe)
3644 static Process32First_Proc s_pfn_Process32_First = NULL;
3646 if (g_b_init_process32_first == 0)
3648 g_b_init_process32_first = 1;
3649 s_pfn_Process32_First = (Process32First_Proc)
3650 GetProcAddress (GetModuleHandle ("kernel32.dll"),
3651 "Process32First");
3653 if (s_pfn_Process32_First == NULL)
3655 return FALSE;
3657 return (s_pfn_Process32_First (hSnapshot, lppe));
3660 static BOOL WINAPI
3661 process32_next (HANDLE hSnapshot, LPPROCESSENTRY32 lppe)
3663 static Process32Next_Proc s_pfn_Process32_Next = NULL;
3665 if (g_b_init_process32_next == 0)
3667 g_b_init_process32_next = 1;
3668 s_pfn_Process32_Next = (Process32Next_Proc)
3669 GetProcAddress (GetModuleHandle ("kernel32.dll"),
3670 "Process32Next");
3672 if (s_pfn_Process32_Next == NULL)
3674 return FALSE;
3676 return (s_pfn_Process32_Next (hSnapshot, lppe));
3679 static BOOL WINAPI
3680 open_thread_token (HANDLE ThreadHandle,
3681 DWORD DesiredAccess,
3682 BOOL OpenAsSelf,
3683 PHANDLE TokenHandle)
3685 static OpenThreadToken_Proc s_pfn_Open_Thread_Token = NULL;
3686 HMODULE hm_advapi32 = NULL;
3687 if (is_windows_9x () == TRUE)
3689 SetLastError (ERROR_NOT_SUPPORTED);
3690 return FALSE;
3692 if (g_b_init_open_thread_token == 0)
3694 g_b_init_open_thread_token = 1;
3695 hm_advapi32 = LoadLibrary ("Advapi32.dll");
3696 s_pfn_Open_Thread_Token =
3697 (OpenThreadToken_Proc) GetProcAddress (hm_advapi32, "OpenThreadToken");
3699 if (s_pfn_Open_Thread_Token == NULL)
3701 SetLastError (ERROR_NOT_SUPPORTED);
3702 return FALSE;
3704 return (
3705 s_pfn_Open_Thread_Token (
3706 ThreadHandle,
3707 DesiredAccess,
3708 OpenAsSelf,
3709 TokenHandle)
3713 static BOOL WINAPI
3714 impersonate_self (SECURITY_IMPERSONATION_LEVEL ImpersonationLevel)
3716 static ImpersonateSelf_Proc s_pfn_Impersonate_Self = NULL;
3717 HMODULE hm_advapi32 = NULL;
3718 if (is_windows_9x () == TRUE)
3720 return FALSE;
3722 if (g_b_init_impersonate_self == 0)
3724 g_b_init_impersonate_self = 1;
3725 hm_advapi32 = LoadLibrary ("Advapi32.dll");
3726 s_pfn_Impersonate_Self =
3727 (ImpersonateSelf_Proc) GetProcAddress (hm_advapi32, "ImpersonateSelf");
3729 if (s_pfn_Impersonate_Self == NULL)
3731 return FALSE;
3733 return s_pfn_Impersonate_Self (ImpersonationLevel);
3736 static BOOL WINAPI
3737 revert_to_self (void)
3739 static RevertToSelf_Proc s_pfn_Revert_To_Self = NULL;
3740 HMODULE hm_advapi32 = NULL;
3741 if (is_windows_9x () == TRUE)
3743 return FALSE;
3745 if (g_b_init_revert_to_self == 0)
3747 g_b_init_revert_to_self = 1;
3748 hm_advapi32 = LoadLibrary ("Advapi32.dll");
3749 s_pfn_Revert_To_Self =
3750 (RevertToSelf_Proc) GetProcAddress (hm_advapi32, "RevertToSelf");
3752 if (s_pfn_Revert_To_Self == NULL)
3754 return FALSE;
3756 return s_pfn_Revert_To_Self ();
3759 static BOOL WINAPI
3760 get_process_memory_info (HANDLE h_proc,
3761 PPROCESS_MEMORY_COUNTERS mem_counters,
3762 DWORD bufsize)
3764 static GetProcessMemoryInfo_Proc s_pfn_Get_Process_Memory_Info = NULL;
3765 HMODULE hm_psapi = NULL;
3766 if (is_windows_9x () == TRUE)
3768 return FALSE;
3770 if (g_b_init_get_process_memory_info == 0)
3772 g_b_init_get_process_memory_info = 1;
3773 hm_psapi = LoadLibrary ("Psapi.dll");
3774 if (hm_psapi)
3775 s_pfn_Get_Process_Memory_Info = (GetProcessMemoryInfo_Proc)
3776 GetProcAddress (hm_psapi, "GetProcessMemoryInfo");
3778 if (s_pfn_Get_Process_Memory_Info == NULL)
3780 return FALSE;
3782 return s_pfn_Get_Process_Memory_Info (h_proc, mem_counters, bufsize);
3785 static BOOL WINAPI
3786 get_process_working_set_size (HANDLE h_proc,
3787 DWORD *minrss,
3788 DWORD *maxrss)
3790 static GetProcessWorkingSetSize_Proc
3791 s_pfn_Get_Process_Working_Set_Size = NULL;
3793 if (is_windows_9x () == TRUE)
3795 return FALSE;
3797 if (g_b_init_get_process_working_set_size == 0)
3799 g_b_init_get_process_working_set_size = 1;
3800 s_pfn_Get_Process_Working_Set_Size = (GetProcessWorkingSetSize_Proc)
3801 GetProcAddress (GetModuleHandle ("kernel32.dll"),
3802 "GetProcessWorkingSetSize");
3804 if (s_pfn_Get_Process_Working_Set_Size == NULL)
3806 return FALSE;
3808 return s_pfn_Get_Process_Working_Set_Size (h_proc, minrss, maxrss);
3811 static BOOL WINAPI
3812 global_memory_status (MEMORYSTATUS *buf)
3814 static GlobalMemoryStatus_Proc s_pfn_Global_Memory_Status = NULL;
3816 if (is_windows_9x () == TRUE)
3818 return FALSE;
3820 if (g_b_init_global_memory_status == 0)
3822 g_b_init_global_memory_status = 1;
3823 s_pfn_Global_Memory_Status = (GlobalMemoryStatus_Proc)
3824 GetProcAddress (GetModuleHandle ("kernel32.dll"),
3825 "GlobalMemoryStatus");
3827 if (s_pfn_Global_Memory_Status == NULL)
3829 return FALSE;
3831 return s_pfn_Global_Memory_Status (buf);
3834 static BOOL WINAPI
3835 global_memory_status_ex (MEMORY_STATUS_EX *buf)
3837 static GlobalMemoryStatusEx_Proc s_pfn_Global_Memory_Status_Ex = NULL;
3839 if (is_windows_9x () == TRUE)
3841 return FALSE;
3843 if (g_b_init_global_memory_status_ex == 0)
3845 g_b_init_global_memory_status_ex = 1;
3846 s_pfn_Global_Memory_Status_Ex = (GlobalMemoryStatusEx_Proc)
3847 GetProcAddress (GetModuleHandle ("kernel32.dll"),
3848 "GlobalMemoryStatusEx");
3850 if (s_pfn_Global_Memory_Status_Ex == NULL)
3852 return FALSE;
3854 return s_pfn_Global_Memory_Status_Ex (buf);
3857 Lisp_Object
3858 list_system_processes (void)
3860 struct gcpro gcpro1;
3861 Lisp_Object proclist = Qnil;
3862 HANDLE h_snapshot;
3864 h_snapshot = create_toolhelp32_snapshot (TH32CS_SNAPPROCESS, 0);
3866 if (h_snapshot != INVALID_HANDLE_VALUE)
3868 PROCESSENTRY32 proc_entry;
3869 DWORD proc_id;
3870 BOOL res;
3872 GCPRO1 (proclist);
3874 proc_entry.dwSize = sizeof (PROCESSENTRY32);
3875 for (res = process32_first (h_snapshot, &proc_entry); res;
3876 res = process32_next (h_snapshot, &proc_entry))
3878 proc_id = proc_entry.th32ProcessID;
3879 proclist = Fcons (make_fixnum_or_float (proc_id), proclist);
3882 CloseHandle (h_snapshot);
3883 UNGCPRO;
3884 proclist = Fnreverse (proclist);
3887 return proclist;
3890 static int
3891 enable_privilege (LPCTSTR priv_name, BOOL enable_p, TOKEN_PRIVILEGES *old_priv)
3893 TOKEN_PRIVILEGES priv;
3894 DWORD priv_size = sizeof (priv);
3895 DWORD opriv_size = sizeof (*old_priv);
3896 HANDLE h_token = NULL;
3897 HANDLE h_thread = GetCurrentThread ();
3898 int ret_val = 0;
3899 BOOL res;
3901 res = open_thread_token (h_thread,
3902 TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
3903 FALSE, &h_token);
3904 if (!res && GetLastError () == ERROR_NO_TOKEN)
3906 if (impersonate_self (SecurityImpersonation))
3907 res = open_thread_token (h_thread,
3908 TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
3909 FALSE, &h_token);
3911 if (res)
3913 priv.PrivilegeCount = 1;
3914 priv.Privileges[0].Attributes = enable_p ? SE_PRIVILEGE_ENABLED : 0;
3915 LookupPrivilegeValue (NULL, priv_name, &priv.Privileges[0].Luid);
3916 if (AdjustTokenPrivileges (h_token, FALSE, &priv, priv_size,
3917 old_priv, &opriv_size)
3918 && GetLastError () != ERROR_NOT_ALL_ASSIGNED)
3919 ret_val = 1;
3921 if (h_token)
3922 CloseHandle (h_token);
3924 return ret_val;
3927 static int
3928 restore_privilege (TOKEN_PRIVILEGES *priv)
3930 DWORD priv_size = sizeof (*priv);
3931 HANDLE h_token = NULL;
3932 int ret_val = 0;
3934 if (open_thread_token (GetCurrentThread (),
3935 TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
3936 FALSE, &h_token))
3938 if (AdjustTokenPrivileges (h_token, FALSE, priv, priv_size, NULL, NULL)
3939 && GetLastError () != ERROR_NOT_ALL_ASSIGNED)
3940 ret_val = 1;
3942 if (h_token)
3943 CloseHandle (h_token);
3945 return ret_val;
3948 static Lisp_Object
3949 ltime (long time_sec, long time_usec)
3951 return list3 (make_number ((time_sec >> 16) & 0xffff),
3952 make_number (time_sec & 0xffff),
3953 make_number (time_usec));
3956 #define U64_TO_LISP_TIME(time) ltime ((time) / 1000000L, (time) % 1000000L)
3958 static int
3959 process_times (HANDLE h_proc, Lisp_Object *ctime, Lisp_Object *etime,
3960 Lisp_Object *stime, Lisp_Object *utime, Lisp_Object *ttime,
3961 double *pcpu)
3963 FILETIME ft_creation, ft_exit, ft_kernel, ft_user, ft_current;
3964 ULONGLONG tem1, tem2, tem3, tem;
3966 if (!h_proc
3967 || !get_process_times_fn
3968 || !(*get_process_times_fn) (h_proc, &ft_creation, &ft_exit,
3969 &ft_kernel, &ft_user))
3970 return 0;
3972 GetSystemTimeAsFileTime (&ft_current);
3974 FILETIME_TO_U64 (tem1, ft_kernel);
3975 tem1 /= 10L;
3976 *stime = U64_TO_LISP_TIME (tem1);
3978 FILETIME_TO_U64 (tem2, ft_user);
3979 tem2 /= 10L;
3980 *utime = U64_TO_LISP_TIME (tem2);
3982 tem3 = tem1 + tem2;
3983 *ttime = U64_TO_LISP_TIME (tem3);
3985 FILETIME_TO_U64 (tem, ft_creation);
3986 /* Process no 4 (System) returns zero creation time. */
3987 if (tem)
3988 tem = (tem - utc_base) / 10L;
3989 *ctime = U64_TO_LISP_TIME (tem);
3991 if (tem)
3993 FILETIME_TO_U64 (tem3, ft_current);
3994 tem = (tem3 - utc_base) / 10L - tem;
3996 *etime = U64_TO_LISP_TIME (tem);
3998 if (tem)
4000 *pcpu = 100.0 * (tem1 + tem2) / tem;
4001 if (*pcpu > 100)
4002 *pcpu = 100.0;
4004 else
4005 *pcpu = 0;
4007 return 1;
4010 Lisp_Object
4011 system_process_attributes (Lisp_Object pid)
4013 struct gcpro gcpro1, gcpro2, gcpro3;
4014 Lisp_Object attrs = Qnil;
4015 Lisp_Object cmd_str, decoded_cmd, tem;
4016 HANDLE h_snapshot, h_proc;
4017 DWORD proc_id;
4018 int found_proc = 0;
4019 char uname[UNLEN+1], gname[GNLEN+1], domain[1025];
4020 DWORD ulength = sizeof (uname), dlength = sizeof (domain), needed;
4021 DWORD glength = sizeof (gname);
4022 HANDLE token = NULL;
4023 SID_NAME_USE user_type;
4024 unsigned char *buf = NULL;
4025 DWORD blen = 0;
4026 TOKEN_USER user_token;
4027 TOKEN_PRIMARY_GROUP group_token;
4028 unsigned euid;
4029 unsigned egid;
4030 DWORD sess;
4031 PROCESS_MEMORY_COUNTERS mem;
4032 PROCESS_MEMORY_COUNTERS_EX mem_ex;
4033 DWORD minrss, maxrss;
4034 MEMORYSTATUS memst;
4035 MEMORY_STATUS_EX memstex;
4036 double totphys = 0.0;
4037 Lisp_Object ctime, stime, utime, etime, ttime;
4038 double pcpu;
4039 BOOL result = FALSE;
4041 CHECK_NUMBER_OR_FLOAT (pid);
4042 proc_id = FLOATP (pid) ? XFLOAT_DATA (pid) : XINT (pid);
4044 h_snapshot = create_toolhelp32_snapshot (TH32CS_SNAPPROCESS, 0);
4046 GCPRO3 (attrs, decoded_cmd, tem);
4048 if (h_snapshot != INVALID_HANDLE_VALUE)
4050 PROCESSENTRY32 pe;
4051 BOOL res;
4053 pe.dwSize = sizeof (PROCESSENTRY32);
4054 for (res = process32_first (h_snapshot, &pe); res;
4055 res = process32_next (h_snapshot, &pe))
4057 if (proc_id == pe.th32ProcessID)
4059 if (proc_id == 0)
4060 decoded_cmd = build_string ("Idle");
4061 else
4063 /* Decode the command name from locale-specific
4064 encoding. */
4065 cmd_str = make_unibyte_string (pe.szExeFile,
4066 strlen (pe.szExeFile));
4067 decoded_cmd =
4068 code_convert_string_norecord (cmd_str,
4069 Vlocale_coding_system, 0);
4071 attrs = Fcons (Fcons (Qcomm, decoded_cmd), attrs);
4072 attrs = Fcons (Fcons (Qppid,
4073 make_fixnum_or_float (pe.th32ParentProcessID)),
4074 attrs);
4075 attrs = Fcons (Fcons (Qpri, make_number (pe.pcPriClassBase)),
4076 attrs);
4077 attrs = Fcons (Fcons (Qthcount,
4078 make_fixnum_or_float (pe.cntThreads)),
4079 attrs);
4080 found_proc = 1;
4081 break;
4085 CloseHandle (h_snapshot);
4088 if (!found_proc)
4090 UNGCPRO;
4091 return Qnil;
4094 h_proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
4095 FALSE, proc_id);
4096 /* If we were denied a handle to the process, try again after
4097 enabling the SeDebugPrivilege in our process. */
4098 if (!h_proc)
4100 TOKEN_PRIVILEGES priv_current;
4102 if (enable_privilege (SE_DEBUG_NAME, TRUE, &priv_current))
4104 h_proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
4105 FALSE, proc_id);
4106 restore_privilege (&priv_current);
4107 revert_to_self ();
4110 if (h_proc)
4112 result = open_process_token (h_proc, TOKEN_QUERY, &token);
4113 if (result)
4115 result = get_token_information (token, TokenUser, NULL, 0, &blen);
4116 if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER)
4118 buf = xmalloc (blen);
4119 result = get_token_information (token, TokenUser,
4120 (LPVOID)buf, blen, &needed);
4121 if (result)
4123 memcpy (&user_token, buf, sizeof (user_token));
4124 if (!w32_cached_id (user_token.User.Sid, &euid, uname))
4126 euid = get_rid (user_token.User.Sid);
4127 result = lookup_account_sid (NULL, user_token.User.Sid,
4128 uname, &ulength,
4129 domain, &dlength,
4130 &user_type);
4131 if (result)
4132 w32_add_to_cache (user_token.User.Sid, euid, uname);
4133 else
4135 strcpy (uname, "unknown");
4136 result = TRUE;
4139 ulength = strlen (uname);
4143 if (result)
4145 /* Determine a reasonable euid and gid values. */
4146 if (xstrcasecmp ("administrator", uname) == 0)
4148 euid = 500; /* well-known Administrator uid */
4149 egid = 513; /* well-known None gid */
4151 else
4153 /* Get group id and name. */
4154 result = get_token_information (token, TokenPrimaryGroup,
4155 (LPVOID)buf, blen, &needed);
4156 if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER)
4158 buf = xrealloc (buf, blen = needed);
4159 result = get_token_information (token, TokenPrimaryGroup,
4160 (LPVOID)buf, blen, &needed);
4162 if (result)
4164 memcpy (&group_token, buf, sizeof (group_token));
4165 if (!w32_cached_id (group_token.PrimaryGroup, &egid, gname))
4167 egid = get_rid (group_token.PrimaryGroup);
4168 dlength = sizeof (domain);
4169 result =
4170 lookup_account_sid (NULL, group_token.PrimaryGroup,
4171 gname, &glength, NULL, &dlength,
4172 &user_type);
4173 if (result)
4174 w32_add_to_cache (group_token.PrimaryGroup,
4175 egid, gname);
4176 else
4178 strcpy (gname, "None");
4179 result = TRUE;
4182 glength = strlen (gname);
4186 xfree (buf);
4188 if (!result)
4190 if (!is_windows_9x ())
4192 /* We couldn't open the process token, presumably because of
4193 insufficient access rights. Assume this process is run
4194 by the system. */
4195 strcpy (uname, "SYSTEM");
4196 strcpy (gname, "None");
4197 euid = 18; /* SYSTEM */
4198 egid = 513; /* None */
4199 glength = strlen (gname);
4200 ulength = strlen (uname);
4202 /* If we are running under Windows 9X, where security calls are
4203 not supported, we assume all processes are run by the current
4204 user. */
4205 else if (GetUserName (uname, &ulength))
4207 if (xstrcasecmp ("administrator", uname) == 0)
4208 euid = 0;
4209 else
4210 euid = 123;
4211 egid = euid;
4212 strcpy (gname, "None");
4213 glength = strlen (gname);
4214 ulength = strlen (uname);
4216 else
4218 euid = 123;
4219 egid = 123;
4220 strcpy (uname, "administrator");
4221 ulength = strlen (uname);
4222 strcpy (gname, "None");
4223 glength = strlen (gname);
4225 if (token)
4226 CloseHandle (token);
4229 attrs = Fcons (Fcons (Qeuid, make_fixnum_or_float (euid)), attrs);
4230 tem = make_unibyte_string (uname, ulength);
4231 attrs = Fcons (Fcons (Quser,
4232 code_convert_string_norecord (tem, Vlocale_coding_system, 0)),
4233 attrs);
4234 attrs = Fcons (Fcons (Qegid, make_fixnum_or_float (egid)), attrs);
4235 tem = make_unibyte_string (gname, glength);
4236 attrs = Fcons (Fcons (Qgroup,
4237 code_convert_string_norecord (tem, Vlocale_coding_system, 0)),
4238 attrs);
4240 if (global_memory_status_ex (&memstex))
4241 #if __GNUC__ || (defined (_MSC_VER) && _MSC_VER >= 1300)
4242 totphys = memstex.ullTotalPhys / 1024.0;
4243 #else
4244 /* Visual Studio 6 cannot convert an unsigned __int64 type to
4245 double, so we need to do this for it... */
4247 DWORD tot_hi = memstex.ullTotalPhys >> 32;
4248 DWORD tot_md = (memstex.ullTotalPhys & 0x00000000ffffffff) >> 10;
4249 DWORD tot_lo = memstex.ullTotalPhys % 1024;
4251 totphys = tot_hi * 4194304.0 + tot_md + tot_lo / 1024.0;
4253 #endif /* __GNUC__ || _MSC_VER >= 1300 */
4254 else if (global_memory_status (&memst))
4255 totphys = memst.dwTotalPhys / 1024.0;
4257 if (h_proc
4258 && get_process_memory_info (h_proc, (PROCESS_MEMORY_COUNTERS *)&mem_ex,
4259 sizeof (mem_ex)))
4261 DWORD rss = mem_ex.WorkingSetSize / 1024;
4263 attrs = Fcons (Fcons (Qmajflt,
4264 make_fixnum_or_float (mem_ex.PageFaultCount)),
4265 attrs);
4266 attrs = Fcons (Fcons (Qvsize,
4267 make_fixnum_or_float (mem_ex.PrivateUsage / 1024)),
4268 attrs);
4269 attrs = Fcons (Fcons (Qrss, make_fixnum_or_float (rss)), attrs);
4270 if (totphys)
4271 attrs = Fcons (Fcons (Qpmem, make_float (100. * rss / totphys)), attrs);
4273 else if (h_proc
4274 && get_process_memory_info (h_proc, &mem, sizeof (mem)))
4276 DWORD rss = mem_ex.WorkingSetSize / 1024;
4278 attrs = Fcons (Fcons (Qmajflt,
4279 make_fixnum_or_float (mem.PageFaultCount)),
4280 attrs);
4281 attrs = Fcons (Fcons (Qrss, make_fixnum_or_float (rss)), attrs);
4282 if (totphys)
4283 attrs = Fcons (Fcons (Qpmem, make_float (100. * rss / totphys)), attrs);
4285 else if (h_proc
4286 && get_process_working_set_size (h_proc, &minrss, &maxrss))
4288 DWORD rss = maxrss / 1024;
4290 attrs = Fcons (Fcons (Qrss, make_fixnum_or_float (maxrss / 1024)), attrs);
4291 if (totphys)
4292 attrs = Fcons (Fcons (Qpmem, make_float (100. * rss / totphys)), attrs);
4295 if (process_times (h_proc, &ctime, &etime, &stime, &utime, &ttime, &pcpu))
4297 attrs = Fcons (Fcons (Qutime, utime), attrs);
4298 attrs = Fcons (Fcons (Qstime, stime), attrs);
4299 attrs = Fcons (Fcons (Qtime, ttime), attrs);
4300 attrs = Fcons (Fcons (Qstart, ctime), attrs);
4301 attrs = Fcons (Fcons (Qetime, etime), attrs);
4302 attrs = Fcons (Fcons (Qpcpu, make_float (pcpu)), attrs);
4305 /* FIXME: Retrieve command line by walking the PEB of the process. */
4307 if (h_proc)
4308 CloseHandle (h_proc);
4309 UNGCPRO;
4310 return attrs;
4314 /* Wrappers for winsock functions to map between our file descriptors
4315 and winsock's handles; also set h_errno for convenience.
4317 To allow Emacs to run on systems which don't have winsock support
4318 installed, we dynamically link to winsock on startup if present, and
4319 otherwise provide the minimum necessary functionality
4320 (eg. gethostname). */
4322 /* function pointers for relevant socket functions */
4323 int (PASCAL *pfn_WSAStartup) (WORD wVersionRequired, LPWSADATA lpWSAData);
4324 void (PASCAL *pfn_WSASetLastError) (int iError);
4325 int (PASCAL *pfn_WSAGetLastError) (void);
4326 int (PASCAL *pfn_WSAEventSelect) (SOCKET s, HANDLE hEventObject, long lNetworkEvents);
4327 HANDLE (PASCAL *pfn_WSACreateEvent) (void);
4328 int (PASCAL *pfn_WSACloseEvent) (HANDLE hEvent);
4329 int (PASCAL *pfn_socket) (int af, int type, int protocol);
4330 int (PASCAL *pfn_bind) (SOCKET s, const struct sockaddr *addr, int namelen);
4331 int (PASCAL *pfn_connect) (SOCKET s, const struct sockaddr *addr, int namelen);
4332 int (PASCAL *pfn_ioctlsocket) (SOCKET s, long cmd, u_long *argp);
4333 int (PASCAL *pfn_recv) (SOCKET s, char * buf, int len, int flags);
4334 int (PASCAL *pfn_send) (SOCKET s, const char * buf, int len, int flags);
4335 int (PASCAL *pfn_closesocket) (SOCKET s);
4336 int (PASCAL *pfn_shutdown) (SOCKET s, int how);
4337 int (PASCAL *pfn_WSACleanup) (void);
4339 u_short (PASCAL *pfn_htons) (u_short hostshort);
4340 u_short (PASCAL *pfn_ntohs) (u_short netshort);
4341 unsigned long (PASCAL *pfn_inet_addr) (const char * cp);
4342 int (PASCAL *pfn_gethostname) (char * name, int namelen);
4343 struct hostent * (PASCAL *pfn_gethostbyname) (const char * name);
4344 struct servent * (PASCAL *pfn_getservbyname) (const char * name, const char * proto);
4345 int (PASCAL *pfn_getpeername) (SOCKET s, struct sockaddr *addr, int * namelen);
4346 int (PASCAL *pfn_setsockopt) (SOCKET s, int level, int optname,
4347 const char * optval, int optlen);
4348 int (PASCAL *pfn_listen) (SOCKET s, int backlog);
4349 int (PASCAL *pfn_getsockname) (SOCKET s, struct sockaddr * name,
4350 int * namelen);
4351 SOCKET (PASCAL *pfn_accept) (SOCKET s, struct sockaddr * addr, int * addrlen);
4352 int (PASCAL *pfn_recvfrom) (SOCKET s, char * buf, int len, int flags,
4353 struct sockaddr * from, int * fromlen);
4354 int (PASCAL *pfn_sendto) (SOCKET s, const char * buf, int len, int flags,
4355 const struct sockaddr * to, int tolen);
4357 /* SetHandleInformation is only needed to make sockets non-inheritable. */
4358 BOOL (WINAPI *pfn_SetHandleInformation) (HANDLE object, DWORD mask, DWORD flags);
4359 #ifndef HANDLE_FLAG_INHERIT
4360 #define HANDLE_FLAG_INHERIT 1
4361 #endif
4363 HANDLE winsock_lib;
4364 static int winsock_inuse;
4366 BOOL
4367 term_winsock (void)
4369 if (winsock_lib != NULL && winsock_inuse == 0)
4371 /* Not sure what would cause WSAENETDOWN, or even if it can happen
4372 after WSAStartup returns successfully, but it seems reasonable
4373 to allow unloading winsock anyway in that case. */
4374 if (pfn_WSACleanup () == 0 ||
4375 pfn_WSAGetLastError () == WSAENETDOWN)
4377 if (FreeLibrary (winsock_lib))
4378 winsock_lib = NULL;
4379 return TRUE;
4382 return FALSE;
4385 BOOL
4386 init_winsock (int load_now)
4388 WSADATA winsockData;
4390 if (winsock_lib != NULL)
4391 return TRUE;
4393 pfn_SetHandleInformation
4394 = (void *) GetProcAddress (GetModuleHandle ("kernel32.dll"),
4395 "SetHandleInformation");
4397 winsock_lib = LoadLibrary ("Ws2_32.dll");
4399 if (winsock_lib != NULL)
4401 /* dynamically link to socket functions */
4403 #define LOAD_PROC(fn) \
4404 if ((pfn_##fn = (void *) GetProcAddress (winsock_lib, #fn)) == NULL) \
4405 goto fail;
4407 LOAD_PROC (WSAStartup);
4408 LOAD_PROC (WSASetLastError);
4409 LOAD_PROC (WSAGetLastError);
4410 LOAD_PROC (WSAEventSelect);
4411 LOAD_PROC (WSACreateEvent);
4412 LOAD_PROC (WSACloseEvent);
4413 LOAD_PROC (socket);
4414 LOAD_PROC (bind);
4415 LOAD_PROC (connect);
4416 LOAD_PROC (ioctlsocket);
4417 LOAD_PROC (recv);
4418 LOAD_PROC (send);
4419 LOAD_PROC (closesocket);
4420 LOAD_PROC (shutdown);
4421 LOAD_PROC (htons);
4422 LOAD_PROC (ntohs);
4423 LOAD_PROC (inet_addr);
4424 LOAD_PROC (gethostname);
4425 LOAD_PROC (gethostbyname);
4426 LOAD_PROC (getservbyname);
4427 LOAD_PROC (getpeername);
4428 LOAD_PROC (WSACleanup);
4429 LOAD_PROC (setsockopt);
4430 LOAD_PROC (listen);
4431 LOAD_PROC (getsockname);
4432 LOAD_PROC (accept);
4433 LOAD_PROC (recvfrom);
4434 LOAD_PROC (sendto);
4435 #undef LOAD_PROC
4437 /* specify version 1.1 of winsock */
4438 if (pfn_WSAStartup (0x101, &winsockData) == 0)
4440 if (winsockData.wVersion != 0x101)
4441 goto fail;
4443 if (!load_now)
4445 /* Report that winsock exists and is usable, but leave
4446 socket functions disabled. I am assuming that calling
4447 WSAStartup does not require any network interaction,
4448 and in particular does not cause or require a dial-up
4449 connection to be established. */
4451 pfn_WSACleanup ();
4452 FreeLibrary (winsock_lib);
4453 winsock_lib = NULL;
4455 winsock_inuse = 0;
4456 return TRUE;
4459 fail:
4460 FreeLibrary (winsock_lib);
4461 winsock_lib = NULL;
4464 return FALSE;
4468 int h_errno = 0;
4470 /* function to set h_errno for compatibility; map winsock error codes to
4471 normal system codes where they overlap (non-overlapping definitions
4472 are already in <sys/socket.h> */
4473 static void
4474 set_errno (void)
4476 if (winsock_lib == NULL)
4477 h_errno = EINVAL;
4478 else
4479 h_errno = pfn_WSAGetLastError ();
4481 switch (h_errno)
4483 case WSAEACCES: h_errno = EACCES; break;
4484 case WSAEBADF: h_errno = EBADF; break;
4485 case WSAEFAULT: h_errno = EFAULT; break;
4486 case WSAEINTR: h_errno = EINTR; break;
4487 case WSAEINVAL: h_errno = EINVAL; break;
4488 case WSAEMFILE: h_errno = EMFILE; break;
4489 case WSAENAMETOOLONG: h_errno = ENAMETOOLONG; break;
4490 case WSAENOTEMPTY: h_errno = ENOTEMPTY; break;
4492 errno = h_errno;
4495 static void
4496 check_errno (void)
4498 if (h_errno == 0 && winsock_lib != NULL)
4499 pfn_WSASetLastError (0);
4502 /* Extend strerror to handle the winsock-specific error codes. */
4503 struct {
4504 int errnum;
4505 char * msg;
4506 } _wsa_errlist[] = {
4507 WSAEINTR , "Interrupted function call",
4508 WSAEBADF , "Bad file descriptor",
4509 WSAEACCES , "Permission denied",
4510 WSAEFAULT , "Bad address",
4511 WSAEINVAL , "Invalid argument",
4512 WSAEMFILE , "Too many open files",
4514 WSAEWOULDBLOCK , "Resource temporarily unavailable",
4515 WSAEINPROGRESS , "Operation now in progress",
4516 WSAEALREADY , "Operation already in progress",
4517 WSAENOTSOCK , "Socket operation on non-socket",
4518 WSAEDESTADDRREQ , "Destination address required",
4519 WSAEMSGSIZE , "Message too long",
4520 WSAEPROTOTYPE , "Protocol wrong type for socket",
4521 WSAENOPROTOOPT , "Bad protocol option",
4522 WSAEPROTONOSUPPORT , "Protocol not supported",
4523 WSAESOCKTNOSUPPORT , "Socket type not supported",
4524 WSAEOPNOTSUPP , "Operation not supported",
4525 WSAEPFNOSUPPORT , "Protocol family not supported",
4526 WSAEAFNOSUPPORT , "Address family not supported by protocol family",
4527 WSAEADDRINUSE , "Address already in use",
4528 WSAEADDRNOTAVAIL , "Cannot assign requested address",
4529 WSAENETDOWN , "Network is down",
4530 WSAENETUNREACH , "Network is unreachable",
4531 WSAENETRESET , "Network dropped connection on reset",
4532 WSAECONNABORTED , "Software caused connection abort",
4533 WSAECONNRESET , "Connection reset by peer",
4534 WSAENOBUFS , "No buffer space available",
4535 WSAEISCONN , "Socket is already connected",
4536 WSAENOTCONN , "Socket is not connected",
4537 WSAESHUTDOWN , "Cannot send after socket shutdown",
4538 WSAETOOMANYREFS , "Too many references", /* not sure */
4539 WSAETIMEDOUT , "Connection timed out",
4540 WSAECONNREFUSED , "Connection refused",
4541 WSAELOOP , "Network loop", /* not sure */
4542 WSAENAMETOOLONG , "Name is too long",
4543 WSAEHOSTDOWN , "Host is down",
4544 WSAEHOSTUNREACH , "No route to host",
4545 WSAENOTEMPTY , "Buffer not empty", /* not sure */
4546 WSAEPROCLIM , "Too many processes",
4547 WSAEUSERS , "Too many users", /* not sure */
4548 WSAEDQUOT , "Double quote in host name", /* really not sure */
4549 WSAESTALE , "Data is stale", /* not sure */
4550 WSAEREMOTE , "Remote error", /* not sure */
4552 WSASYSNOTREADY , "Network subsystem is unavailable",
4553 WSAVERNOTSUPPORTED , "WINSOCK.DLL version out of range",
4554 WSANOTINITIALISED , "Winsock not initialized successfully",
4555 WSAEDISCON , "Graceful shutdown in progress",
4556 #ifdef WSAENOMORE
4557 WSAENOMORE , "No more operations allowed", /* not sure */
4558 WSAECANCELLED , "Operation cancelled", /* not sure */
4559 WSAEINVALIDPROCTABLE , "Invalid procedure table from service provider",
4560 WSAEINVALIDPROVIDER , "Invalid service provider version number",
4561 WSAEPROVIDERFAILEDINIT , "Unable to initialize a service provider",
4562 WSASYSCALLFAILURE , "System call failure",
4563 WSASERVICE_NOT_FOUND , "Service not found", /* not sure */
4564 WSATYPE_NOT_FOUND , "Class type not found",
4565 WSA_E_NO_MORE , "No more resources available", /* really not sure */
4566 WSA_E_CANCELLED , "Operation already cancelled", /* really not sure */
4567 WSAEREFUSED , "Operation refused", /* not sure */
4568 #endif
4570 WSAHOST_NOT_FOUND , "Host not found",
4571 WSATRY_AGAIN , "Authoritative host not found during name lookup",
4572 WSANO_RECOVERY , "Non-recoverable error during name lookup",
4573 WSANO_DATA , "Valid name, no data record of requested type",
4575 -1, NULL
4578 char *
4579 sys_strerror (int error_no)
4581 int i;
4582 static char unknown_msg[40];
4584 if (error_no >= 0 && error_no < sys_nerr)
4585 return sys_errlist[error_no];
4587 for (i = 0; _wsa_errlist[i].errnum >= 0; i++)
4588 if (_wsa_errlist[i].errnum == error_no)
4589 return _wsa_errlist[i].msg;
4591 sprintf (unknown_msg, "Unidentified error: %d", error_no);
4592 return unknown_msg;
4595 /* [andrewi 3-May-96] I've had conflicting results using both methods,
4596 but I believe the method of keeping the socket handle separate (and
4597 insuring it is not inheritable) is the correct one. */
4599 #define SOCK_HANDLE(fd) ((SOCKET) fd_info[fd].hnd)
4601 static int socket_to_fd (SOCKET s);
4604 sys_socket (int af, int type, int protocol)
4606 SOCKET s;
4608 if (winsock_lib == NULL)
4610 h_errno = ENETDOWN;
4611 return INVALID_SOCKET;
4614 check_errno ();
4616 /* call the real socket function */
4617 s = pfn_socket (af, type, protocol);
4619 if (s != INVALID_SOCKET)
4620 return socket_to_fd (s);
4622 set_errno ();
4623 return -1;
4626 /* Convert a SOCKET to a file descriptor. */
4627 static int
4628 socket_to_fd (SOCKET s)
4630 int fd;
4631 child_process * cp;
4633 /* Although under NT 3.5 _open_osfhandle will accept a socket
4634 handle, if opened with SO_OPENTYPE == SO_SYNCHRONOUS_NONALERT,
4635 that does not work under NT 3.1. However, we can get the same
4636 effect by using a backdoor function to replace an existing
4637 descriptor handle with the one we want. */
4639 /* allocate a file descriptor (with appropriate flags) */
4640 fd = _open ("NUL:", _O_RDWR);
4641 if (fd >= 0)
4643 /* Make a non-inheritable copy of the socket handle. Note
4644 that it is possible that sockets aren't actually kernel
4645 handles, which appears to be the case on Windows 9x when
4646 the MS Proxy winsock client is installed. */
4648 /* Apparently there is a bug in NT 3.51 with some service
4649 packs, which prevents using DuplicateHandle to make a
4650 socket handle non-inheritable (causes WSACleanup to
4651 hang). The work-around is to use SetHandleInformation
4652 instead if it is available and implemented. */
4653 if (pfn_SetHandleInformation)
4655 pfn_SetHandleInformation ((HANDLE) s, HANDLE_FLAG_INHERIT, 0);
4657 else
4659 HANDLE parent = GetCurrentProcess ();
4660 HANDLE new_s = INVALID_HANDLE_VALUE;
4662 if (DuplicateHandle (parent,
4663 (HANDLE) s,
4664 parent,
4665 &new_s,
4667 FALSE,
4668 DUPLICATE_SAME_ACCESS))
4670 /* It is possible that DuplicateHandle succeeds even
4671 though the socket wasn't really a kernel handle,
4672 because a real handle has the same value. So
4673 test whether the new handle really is a socket. */
4674 long nonblocking = 0;
4675 if (pfn_ioctlsocket ((SOCKET) new_s, FIONBIO, &nonblocking) == 0)
4677 pfn_closesocket (s);
4678 s = (SOCKET) new_s;
4680 else
4682 CloseHandle (new_s);
4687 fd_info[fd].hnd = (HANDLE) s;
4689 /* set our own internal flags */
4690 fd_info[fd].flags = FILE_SOCKET | FILE_BINARY | FILE_READ | FILE_WRITE;
4692 cp = new_child ();
4693 if (cp)
4695 cp->fd = fd;
4696 cp->status = STATUS_READ_ACKNOWLEDGED;
4698 /* attach child_process to fd_info */
4699 if (fd_info[ fd ].cp != NULL)
4701 DebPrint (("sys_socket: fd_info[%d] apparently in use!\n", fd));
4702 abort ();
4705 fd_info[ fd ].cp = cp;
4707 /* success! */
4708 winsock_inuse++; /* count open sockets */
4709 return fd;
4712 /* clean up */
4713 _close (fd);
4715 pfn_closesocket (s);
4716 h_errno = EMFILE;
4717 return -1;
4721 sys_bind (int s, const struct sockaddr * addr, int namelen)
4723 if (winsock_lib == NULL)
4725 h_errno = ENOTSOCK;
4726 return SOCKET_ERROR;
4729 check_errno ();
4730 if (fd_info[s].flags & FILE_SOCKET)
4732 int rc = pfn_bind (SOCK_HANDLE (s), addr, namelen);
4733 if (rc == SOCKET_ERROR)
4734 set_errno ();
4735 return rc;
4737 h_errno = ENOTSOCK;
4738 return SOCKET_ERROR;
4742 sys_connect (int s, const struct sockaddr * name, int namelen)
4744 if (winsock_lib == NULL)
4746 h_errno = ENOTSOCK;
4747 return SOCKET_ERROR;
4750 check_errno ();
4751 if (fd_info[s].flags & FILE_SOCKET)
4753 int rc = pfn_connect (SOCK_HANDLE (s), name, namelen);
4754 if (rc == SOCKET_ERROR)
4755 set_errno ();
4756 return rc;
4758 h_errno = ENOTSOCK;
4759 return SOCKET_ERROR;
4762 u_short
4763 sys_htons (u_short hostshort)
4765 return (winsock_lib != NULL) ?
4766 pfn_htons (hostshort) : hostshort;
4769 u_short
4770 sys_ntohs (u_short netshort)
4772 return (winsock_lib != NULL) ?
4773 pfn_ntohs (netshort) : netshort;
4776 unsigned long
4777 sys_inet_addr (const char * cp)
4779 return (winsock_lib != NULL) ?
4780 pfn_inet_addr (cp) : INADDR_NONE;
4784 sys_gethostname (char * name, int namelen)
4786 if (winsock_lib != NULL)
4787 return pfn_gethostname (name, namelen);
4789 if (namelen > MAX_COMPUTERNAME_LENGTH)
4790 return !GetComputerName (name, (DWORD *)&namelen);
4792 h_errno = EFAULT;
4793 return SOCKET_ERROR;
4796 struct hostent *
4797 sys_gethostbyname (const char * name)
4799 struct hostent * host;
4801 if (winsock_lib == NULL)
4803 h_errno = ENETDOWN;
4804 return NULL;
4807 check_errno ();
4808 host = pfn_gethostbyname (name);
4809 if (!host)
4810 set_errno ();
4811 return host;
4814 struct servent *
4815 sys_getservbyname (const char * name, const char * proto)
4817 struct servent * serv;
4819 if (winsock_lib == NULL)
4821 h_errno = ENETDOWN;
4822 return NULL;
4825 check_errno ();
4826 serv = pfn_getservbyname (name, proto);
4827 if (!serv)
4828 set_errno ();
4829 return serv;
4833 sys_getpeername (int s, struct sockaddr *addr, int * namelen)
4835 if (winsock_lib == NULL)
4837 h_errno = ENETDOWN;
4838 return SOCKET_ERROR;
4841 check_errno ();
4842 if (fd_info[s].flags & FILE_SOCKET)
4844 int rc = pfn_getpeername (SOCK_HANDLE (s), addr, namelen);
4845 if (rc == SOCKET_ERROR)
4846 set_errno ();
4847 return rc;
4849 h_errno = ENOTSOCK;
4850 return SOCKET_ERROR;
4854 sys_shutdown (int s, int how)
4856 if (winsock_lib == NULL)
4858 h_errno = ENETDOWN;
4859 return SOCKET_ERROR;
4862 check_errno ();
4863 if (fd_info[s].flags & FILE_SOCKET)
4865 int rc = pfn_shutdown (SOCK_HANDLE (s), how);
4866 if (rc == SOCKET_ERROR)
4867 set_errno ();
4868 return rc;
4870 h_errno = ENOTSOCK;
4871 return SOCKET_ERROR;
4875 sys_setsockopt (int s, int level, int optname, const void * optval, int optlen)
4877 if (winsock_lib == NULL)
4879 h_errno = ENETDOWN;
4880 return SOCKET_ERROR;
4883 check_errno ();
4884 if (fd_info[s].flags & FILE_SOCKET)
4886 int rc = pfn_setsockopt (SOCK_HANDLE (s), level, optname,
4887 (const char *)optval, optlen);
4888 if (rc == SOCKET_ERROR)
4889 set_errno ();
4890 return rc;
4892 h_errno = ENOTSOCK;
4893 return SOCKET_ERROR;
4897 sys_listen (int s, int backlog)
4899 if (winsock_lib == NULL)
4901 h_errno = ENETDOWN;
4902 return SOCKET_ERROR;
4905 check_errno ();
4906 if (fd_info[s].flags & FILE_SOCKET)
4908 int rc = pfn_listen (SOCK_HANDLE (s), backlog);
4909 if (rc == SOCKET_ERROR)
4910 set_errno ();
4911 else
4912 fd_info[s].flags |= FILE_LISTEN;
4913 return rc;
4915 h_errno = ENOTSOCK;
4916 return SOCKET_ERROR;
4920 sys_getsockname (int s, struct sockaddr * name, int * namelen)
4922 if (winsock_lib == NULL)
4924 h_errno = ENETDOWN;
4925 return SOCKET_ERROR;
4928 check_errno ();
4929 if (fd_info[s].flags & FILE_SOCKET)
4931 int rc = pfn_getsockname (SOCK_HANDLE (s), name, namelen);
4932 if (rc == SOCKET_ERROR)
4933 set_errno ();
4934 return rc;
4936 h_errno = ENOTSOCK;
4937 return SOCKET_ERROR;
4941 sys_accept (int s, struct sockaddr * addr, int * addrlen)
4943 if (winsock_lib == NULL)
4945 h_errno = ENETDOWN;
4946 return -1;
4949 check_errno ();
4950 if (fd_info[s].flags & FILE_LISTEN)
4952 SOCKET t = pfn_accept (SOCK_HANDLE (s), addr, addrlen);
4953 int fd = -1;
4954 if (t == INVALID_SOCKET)
4955 set_errno ();
4956 else
4957 fd = socket_to_fd (t);
4959 fd_info[s].cp->status = STATUS_READ_ACKNOWLEDGED;
4960 ResetEvent (fd_info[s].cp->char_avail);
4961 return fd;
4963 h_errno = ENOTSOCK;
4964 return -1;
4968 sys_recvfrom (int s, char * buf, int len, int flags,
4969 struct sockaddr * from, int * fromlen)
4971 if (winsock_lib == NULL)
4973 h_errno = ENETDOWN;
4974 return SOCKET_ERROR;
4977 check_errno ();
4978 if (fd_info[s].flags & FILE_SOCKET)
4980 int rc = pfn_recvfrom (SOCK_HANDLE (s), buf, len, flags, from, fromlen);
4981 if (rc == SOCKET_ERROR)
4982 set_errno ();
4983 return rc;
4985 h_errno = ENOTSOCK;
4986 return SOCKET_ERROR;
4990 sys_sendto (int s, const char * buf, int len, int flags,
4991 const struct sockaddr * to, int tolen)
4993 if (winsock_lib == NULL)
4995 h_errno = ENETDOWN;
4996 return SOCKET_ERROR;
4999 check_errno ();
5000 if (fd_info[s].flags & FILE_SOCKET)
5002 int rc = pfn_sendto (SOCK_HANDLE (s), buf, len, flags, to, tolen);
5003 if (rc == SOCKET_ERROR)
5004 set_errno ();
5005 return rc;
5007 h_errno = ENOTSOCK;
5008 return SOCKET_ERROR;
5011 /* Windows does not have an fcntl function. Provide an implementation
5012 solely for making sockets non-blocking. */
5014 fcntl (int s, int cmd, int options)
5016 if (winsock_lib == NULL)
5018 h_errno = ENETDOWN;
5019 return -1;
5022 check_errno ();
5023 if (fd_info[s].flags & FILE_SOCKET)
5025 if (cmd == F_SETFL && options == O_NDELAY)
5027 unsigned long nblock = 1;
5028 int rc = pfn_ioctlsocket (SOCK_HANDLE (s), FIONBIO, &nblock);
5029 if (rc == SOCKET_ERROR)
5030 set_errno ();
5031 /* Keep track of the fact that we set this to non-blocking. */
5032 fd_info[s].flags |= FILE_NDELAY;
5033 return rc;
5035 else
5037 h_errno = EINVAL;
5038 return SOCKET_ERROR;
5041 h_errno = ENOTSOCK;
5042 return SOCKET_ERROR;
5046 /* Shadow main io functions: we need to handle pipes and sockets more
5047 intelligently, and implement non-blocking mode as well. */
5050 sys_close (int fd)
5052 int rc;
5054 if (fd < 0)
5056 errno = EBADF;
5057 return -1;
5060 if (fd < MAXDESC && fd_info[fd].cp)
5062 child_process * cp = fd_info[fd].cp;
5064 fd_info[fd].cp = NULL;
5066 if (CHILD_ACTIVE (cp))
5068 /* if last descriptor to active child_process then cleanup */
5069 int i;
5070 for (i = 0; i < MAXDESC; i++)
5072 if (i == fd)
5073 continue;
5074 if (fd_info[i].cp == cp)
5075 break;
5077 if (i == MAXDESC)
5079 if (fd_info[fd].flags & FILE_SOCKET)
5081 if (winsock_lib == NULL) abort ();
5083 pfn_shutdown (SOCK_HANDLE (fd), 2);
5084 rc = pfn_closesocket (SOCK_HANDLE (fd));
5086 winsock_inuse--; /* count open sockets */
5088 delete_child (cp);
5093 /* Note that sockets do not need special treatment here (at least on
5094 NT and Windows 95 using the standard tcp/ip stacks) - it appears that
5095 closesocket is equivalent to CloseHandle, which is to be expected
5096 because socket handles are fully fledged kernel handles. */
5097 rc = _close (fd);
5099 if (rc == 0 && fd < MAXDESC)
5100 fd_info[fd].flags = 0;
5102 return rc;
5106 sys_dup (int fd)
5108 int new_fd;
5110 new_fd = _dup (fd);
5111 if (new_fd >= 0 && new_fd < MAXDESC)
5113 /* duplicate our internal info as well */
5114 fd_info[new_fd] = fd_info[fd];
5116 return new_fd;
5120 sys_dup2 (int src, int dst)
5122 int rc;
5124 if (dst < 0 || dst >= MAXDESC)
5126 errno = EBADF;
5127 return -1;
5130 /* make sure we close the destination first if it's a pipe or socket */
5131 if (src != dst && fd_info[dst].flags != 0)
5132 sys_close (dst);
5134 rc = _dup2 (src, dst);
5135 if (rc == 0)
5137 /* duplicate our internal info as well */
5138 fd_info[dst] = fd_info[src];
5140 return rc;
5143 /* Unix pipe() has only one arg */
5145 sys_pipe (int * phandles)
5147 int rc;
5148 unsigned flags;
5150 /* make pipe handles non-inheritable; when we spawn a child, we
5151 replace the relevant handle with an inheritable one. Also put
5152 pipes into binary mode; we will do text mode translation ourselves
5153 if required. */
5154 rc = _pipe (phandles, 0, _O_NOINHERIT | _O_BINARY);
5156 if (rc == 0)
5158 /* Protect against overflow, since Windows can open more handles than
5159 our fd_info array has room for. */
5160 if (phandles[0] >= MAXDESC || phandles[1] >= MAXDESC)
5162 _close (phandles[0]);
5163 _close (phandles[1]);
5164 rc = -1;
5166 else
5168 flags = FILE_PIPE | FILE_READ | FILE_BINARY;
5169 fd_info[phandles[0]].flags = flags;
5171 flags = FILE_PIPE | FILE_WRITE | FILE_BINARY;
5172 fd_info[phandles[1]].flags = flags;
5176 return rc;
5179 /* Function to do blocking read of one byte, needed to implement
5180 select. It is only allowed on sockets and pipes. */
5182 _sys_read_ahead (int fd)
5184 child_process * cp;
5185 int rc;
5187 if (fd < 0 || fd >= MAXDESC)
5188 return STATUS_READ_ERROR;
5190 cp = fd_info[fd].cp;
5192 if (cp == NULL || cp->fd != fd || cp->status != STATUS_READ_READY)
5193 return STATUS_READ_ERROR;
5195 if ((fd_info[fd].flags & (FILE_PIPE | FILE_SERIAL | FILE_SOCKET)) == 0
5196 || (fd_info[fd].flags & FILE_READ) == 0)
5198 DebPrint (("_sys_read_ahead: internal error: fd %d is not a pipe, serial port, or socket!\n", fd));
5199 abort ();
5202 cp->status = STATUS_READ_IN_PROGRESS;
5204 if (fd_info[fd].flags & FILE_PIPE)
5206 rc = _read (fd, &cp->chr, sizeof (char));
5208 /* Give subprocess time to buffer some more output for us before
5209 reporting that input is available; we need this because Windows 95
5210 connects DOS programs to pipes by making the pipe appear to be
5211 the normal console stdout - as a result most DOS programs will
5212 write to stdout without buffering, ie. one character at a
5213 time. Even some W32 programs do this - "dir" in a command
5214 shell on NT is very slow if we don't do this. */
5215 if (rc > 0)
5217 int wait = w32_pipe_read_delay;
5219 if (wait > 0)
5220 Sleep (wait);
5221 else if (wait < 0)
5222 while (++wait <= 0)
5223 /* Yield remainder of our time slice, effectively giving a
5224 temporary priority boost to the child process. */
5225 Sleep (0);
5228 else if (fd_info[fd].flags & FILE_SERIAL)
5230 HANDLE hnd = fd_info[fd].hnd;
5231 OVERLAPPED *ovl = &fd_info[fd].cp->ovl_read;
5232 COMMTIMEOUTS ct;
5234 /* Configure timeouts for blocking read. */
5235 if (!GetCommTimeouts (hnd, &ct))
5236 return STATUS_READ_ERROR;
5237 ct.ReadIntervalTimeout = 0;
5238 ct.ReadTotalTimeoutMultiplier = 0;
5239 ct.ReadTotalTimeoutConstant = 0;
5240 if (!SetCommTimeouts (hnd, &ct))
5241 return STATUS_READ_ERROR;
5243 if (!ReadFile (hnd, &cp->chr, sizeof (char), (DWORD*) &rc, ovl))
5245 if (GetLastError () != ERROR_IO_PENDING)
5246 return STATUS_READ_ERROR;
5247 if (!GetOverlappedResult (hnd, ovl, (DWORD*) &rc, TRUE))
5248 return STATUS_READ_ERROR;
5251 else if (fd_info[fd].flags & FILE_SOCKET)
5253 unsigned long nblock = 0;
5254 /* We always want this to block, so temporarily disable NDELAY. */
5255 if (fd_info[fd].flags & FILE_NDELAY)
5256 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
5258 rc = pfn_recv (SOCK_HANDLE (fd), &cp->chr, sizeof (char), 0);
5260 if (fd_info[fd].flags & FILE_NDELAY)
5262 nblock = 1;
5263 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
5267 if (rc == sizeof (char))
5268 cp->status = STATUS_READ_SUCCEEDED;
5269 else
5270 cp->status = STATUS_READ_FAILED;
5272 return cp->status;
5276 _sys_wait_accept (int fd)
5278 HANDLE hEv;
5279 child_process * cp;
5280 int rc;
5282 if (fd < 0 || fd >= MAXDESC)
5283 return STATUS_READ_ERROR;
5285 cp = fd_info[fd].cp;
5287 if (cp == NULL || cp->fd != fd || cp->status != STATUS_READ_READY)
5288 return STATUS_READ_ERROR;
5290 cp->status = STATUS_READ_FAILED;
5292 hEv = pfn_WSACreateEvent ();
5293 rc = pfn_WSAEventSelect (SOCK_HANDLE (fd), hEv, FD_ACCEPT);
5294 if (rc != SOCKET_ERROR)
5296 rc = WaitForSingleObject (hEv, INFINITE);
5297 pfn_WSAEventSelect (SOCK_HANDLE (fd), NULL, 0);
5298 if (rc == WAIT_OBJECT_0)
5299 cp->status = STATUS_READ_SUCCEEDED;
5301 pfn_WSACloseEvent (hEv);
5303 return cp->status;
5307 sys_read (int fd, char * buffer, unsigned int count)
5309 int nchars;
5310 int to_read;
5311 DWORD waiting;
5312 char * orig_buffer = buffer;
5314 if (fd < 0)
5316 errno = EBADF;
5317 return -1;
5320 if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET | FILE_SERIAL))
5322 child_process *cp = fd_info[fd].cp;
5324 if ((fd_info[fd].flags & FILE_READ) == 0)
5326 errno = EBADF;
5327 return -1;
5330 nchars = 0;
5332 /* re-read CR carried over from last read */
5333 if (fd_info[fd].flags & FILE_LAST_CR)
5335 if (fd_info[fd].flags & FILE_BINARY) abort ();
5336 *buffer++ = 0x0d;
5337 count--;
5338 nchars++;
5339 fd_info[fd].flags &= ~FILE_LAST_CR;
5342 /* presence of a child_process structure means we are operating in
5343 non-blocking mode - otherwise we just call _read directly.
5344 Note that the child_process structure might be missing because
5345 reap_subprocess has been called; in this case the pipe is
5346 already broken, so calling _read on it is okay. */
5347 if (cp)
5349 int current_status = cp->status;
5351 switch (current_status)
5353 case STATUS_READ_FAILED:
5354 case STATUS_READ_ERROR:
5355 /* report normal EOF if nothing in buffer */
5356 if (nchars <= 0)
5357 fd_info[fd].flags |= FILE_AT_EOF;
5358 return nchars;
5360 case STATUS_READ_READY:
5361 case STATUS_READ_IN_PROGRESS:
5362 DebPrint (("sys_read called when read is in progress\n"));
5363 errno = EWOULDBLOCK;
5364 return -1;
5366 case STATUS_READ_SUCCEEDED:
5367 /* consume read-ahead char */
5368 *buffer++ = cp->chr;
5369 count--;
5370 nchars++;
5371 cp->status = STATUS_READ_ACKNOWLEDGED;
5372 ResetEvent (cp->char_avail);
5374 case STATUS_READ_ACKNOWLEDGED:
5375 break;
5377 default:
5378 DebPrint (("sys_read: bad status %d\n", current_status));
5379 errno = EBADF;
5380 return -1;
5383 if (fd_info[fd].flags & FILE_PIPE)
5385 PeekNamedPipe ((HANDLE) _get_osfhandle (fd), NULL, 0, NULL, &waiting, NULL);
5386 to_read = min (waiting, (DWORD) count);
5388 if (to_read > 0)
5389 nchars += _read (fd, buffer, to_read);
5391 else if (fd_info[fd].flags & FILE_SERIAL)
5393 HANDLE hnd = fd_info[fd].hnd;
5394 OVERLAPPED *ovl = &fd_info[fd].cp->ovl_read;
5395 DWORD err = 0;
5396 int rc = 0;
5397 COMMTIMEOUTS ct;
5399 if (count > 0)
5401 /* Configure timeouts for non-blocking read. */
5402 if (!GetCommTimeouts (hnd, &ct))
5404 errno = EIO;
5405 return -1;
5407 ct.ReadIntervalTimeout = MAXDWORD;
5408 ct.ReadTotalTimeoutMultiplier = 0;
5409 ct.ReadTotalTimeoutConstant = 0;
5410 if (!SetCommTimeouts (hnd, &ct))
5412 errno = EIO;
5413 return -1;
5416 if (!ResetEvent (ovl->hEvent))
5418 errno = EIO;
5419 return -1;
5421 if (!ReadFile (hnd, buffer, count, (DWORD*) &rc, ovl))
5423 if (GetLastError () != ERROR_IO_PENDING)
5425 errno = EIO;
5426 return -1;
5428 if (!GetOverlappedResult (hnd, ovl, (DWORD*) &rc, TRUE))
5430 errno = EIO;
5431 return -1;
5434 nchars += rc;
5437 else /* FILE_SOCKET */
5439 if (winsock_lib == NULL) abort ();
5441 /* do the equivalent of a non-blocking read */
5442 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONREAD, &waiting);
5443 if (waiting == 0 && nchars == 0)
5445 h_errno = errno = EWOULDBLOCK;
5446 return -1;
5449 if (waiting)
5451 /* always use binary mode for sockets */
5452 int res = pfn_recv (SOCK_HANDLE (fd), buffer, count, 0);
5453 if (res == SOCKET_ERROR)
5455 DebPrint (("sys_read.recv failed with error %d on socket %ld\n",
5456 pfn_WSAGetLastError (), SOCK_HANDLE (fd)));
5457 set_errno ();
5458 return -1;
5460 nchars += res;
5464 else
5466 int nread = _read (fd, buffer, count);
5467 if (nread >= 0)
5468 nchars += nread;
5469 else if (nchars == 0)
5470 nchars = nread;
5473 if (nchars <= 0)
5474 fd_info[fd].flags |= FILE_AT_EOF;
5475 /* Perform text mode translation if required. */
5476 else if ((fd_info[fd].flags & FILE_BINARY) == 0)
5478 nchars = crlf_to_lf (nchars, orig_buffer);
5479 /* If buffer contains only CR, return that. To be absolutely
5480 sure we should attempt to read the next char, but in
5481 practice a CR to be followed by LF would not appear by
5482 itself in the buffer. */
5483 if (nchars > 1 && orig_buffer[nchars - 1] == 0x0d)
5485 fd_info[fd].flags |= FILE_LAST_CR;
5486 nchars--;
5490 else
5491 nchars = _read (fd, buffer, count);
5493 return nchars;
5496 /* From w32xfns.c */
5497 extern HANDLE interrupt_handle;
5499 /* For now, don't bother with a non-blocking mode */
5501 sys_write (int fd, const void * buffer, unsigned int count)
5503 int nchars;
5505 if (fd < 0)
5507 errno = EBADF;
5508 return -1;
5511 if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET | FILE_SERIAL))
5513 if ((fd_info[fd].flags & FILE_WRITE) == 0)
5515 errno = EBADF;
5516 return -1;
5519 /* Perform text mode translation if required. */
5520 if ((fd_info[fd].flags & FILE_BINARY) == 0)
5522 char * tmpbuf = alloca (count * 2);
5523 unsigned char * src = (void *)buffer;
5524 unsigned char * dst = tmpbuf;
5525 int nbytes = count;
5527 while (1)
5529 unsigned char *next;
5530 /* copy next line or remaining bytes */
5531 next = _memccpy (dst, src, '\n', nbytes);
5532 if (next)
5534 /* copied one line ending with '\n' */
5535 int copied = next - dst;
5536 nbytes -= copied;
5537 src += copied;
5538 /* insert '\r' before '\n' */
5539 next[-1] = '\r';
5540 next[0] = '\n';
5541 dst = next + 1;
5542 count++;
5544 else
5545 /* copied remaining partial line -> now finished */
5546 break;
5548 buffer = tmpbuf;
5552 if (fd < MAXDESC && fd_info[fd].flags & FILE_SERIAL)
5554 HANDLE hnd = (HANDLE) _get_osfhandle (fd);
5555 OVERLAPPED *ovl = &fd_info[fd].cp->ovl_write;
5556 HANDLE wait_hnd[2] = { interrupt_handle, ovl->hEvent };
5557 DWORD active = 0;
5559 if (!WriteFile (hnd, buffer, count, (DWORD*) &nchars, ovl))
5561 if (GetLastError () != ERROR_IO_PENDING)
5563 errno = EIO;
5564 return -1;
5566 if (detect_input_pending ())
5567 active = MsgWaitForMultipleObjects (2, wait_hnd, FALSE, INFINITE,
5568 QS_ALLINPUT);
5569 else
5570 active = WaitForMultipleObjects (2, wait_hnd, FALSE, INFINITE);
5571 if (active == WAIT_OBJECT_0)
5572 { /* User pressed C-g, cancel write, then leave. Don't bother
5573 cleaning up as we may only get stuck in buggy drivers. */
5574 PurgeComm (hnd, PURGE_TXABORT | PURGE_TXCLEAR);
5575 CancelIo (hnd);
5576 errno = EIO;
5577 return -1;
5579 if (active == WAIT_OBJECT_0 + 1
5580 && !GetOverlappedResult (hnd, ovl, (DWORD*) &nchars, TRUE))
5582 errno = EIO;
5583 return -1;
5587 else if (fd < MAXDESC && fd_info[fd].flags & FILE_SOCKET)
5589 unsigned long nblock = 0;
5590 if (winsock_lib == NULL) abort ();
5592 /* TODO: implement select() properly so non-blocking I/O works. */
5593 /* For now, make sure the write blocks. */
5594 if (fd_info[fd].flags & FILE_NDELAY)
5595 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
5597 nchars = pfn_send (SOCK_HANDLE (fd), buffer, count, 0);
5599 /* Set the socket back to non-blocking if it was before,
5600 for other operations that support it. */
5601 if (fd_info[fd].flags & FILE_NDELAY)
5603 nblock = 1;
5604 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
5607 if (nchars == SOCKET_ERROR)
5609 DebPrint (("sys_write.send failed with error %d on socket %ld\n",
5610 pfn_WSAGetLastError (), SOCK_HANDLE (fd)));
5611 set_errno ();
5614 else
5616 /* Some networked filesystems don't like too large writes, so
5617 break them into smaller chunks. See the Comments section of
5618 the MSDN documentation of WriteFile for details behind the
5619 choice of the value of CHUNK below. See also the thread
5620 http://thread.gmane.org/gmane.comp.version-control.git/145294
5621 in the git mailing list. */
5622 const unsigned char *p = buffer;
5623 const unsigned chunk = 30 * 1024 * 1024;
5625 nchars = 0;
5626 while (count > 0)
5628 unsigned this_chunk = count < chunk ? count : chunk;
5629 int n = _write (fd, p, this_chunk);
5631 nchars += n;
5632 if (n < 0)
5634 nchars = n;
5635 break;
5637 else if (n < this_chunk)
5638 break;
5639 count -= n;
5640 p += n;
5644 return nchars;
5647 static void
5648 check_windows_init_file (void)
5650 extern int noninteractive, inhibit_window_system;
5652 /* A common indication that Emacs is not installed properly is when
5653 it cannot find the Windows installation file. If this file does
5654 not exist in the expected place, tell the user. */
5656 if (!noninteractive && !inhibit_window_system)
5658 Lisp_Object objs[2];
5659 Lisp_Object full_load_path;
5660 Lisp_Object init_file;
5661 int fd;
5663 objs[0] = Vload_path;
5664 objs[1] = decode_env_path (0, (getenv ("EMACSLOADPATH")));
5665 full_load_path = Fappend (2, objs);
5666 init_file = build_string ("term/w32-win");
5667 fd = openp (full_load_path, init_file, Fget_load_suffixes (), NULL, Qnil);
5668 if (fd < 0)
5670 Lisp_Object load_path_print = Fprin1_to_string (full_load_path, Qnil);
5671 char *init_file_name = SDATA (init_file);
5672 char *load_path = SDATA (load_path_print);
5673 char *buffer = alloca (1024
5674 + strlen (init_file_name)
5675 + strlen (load_path));
5677 sprintf (buffer,
5678 "The Emacs Windows initialization file \"%s.el\" "
5679 "could not be found in your Emacs installation. "
5680 "Emacs checked the following directories for this file:\n"
5681 "\n%s\n\n"
5682 "When Emacs cannot find this file, it usually means that it "
5683 "was not installed properly, or its distribution file was "
5684 "not unpacked properly.\nSee the README.W32 file in the "
5685 "top-level Emacs directory for more information.",
5686 init_file_name, load_path);
5687 MessageBox (NULL,
5688 buffer,
5689 "Emacs Abort Dialog",
5690 MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
5691 /* Use the low-level Emacs abort. */
5692 #undef abort
5693 abort ();
5695 else
5697 _close (fd);
5702 void
5703 term_ntproc (void)
5705 /* shutdown the socket interface if necessary */
5706 term_winsock ();
5708 term_w32select ();
5711 void
5712 init_ntproc (void)
5714 /* Initialise the socket interface now if available and requested by
5715 the user by defining PRELOAD_WINSOCK; otherwise loading will be
5716 delayed until open-network-stream is called (w32-has-winsock can
5717 also be used to dynamically load or reload winsock).
5719 Conveniently, init_environment is called before us, so
5720 PRELOAD_WINSOCK can be set in the registry. */
5722 /* Always initialize this correctly. */
5723 winsock_lib = NULL;
5725 if (getenv ("PRELOAD_WINSOCK") != NULL)
5726 init_winsock (TRUE);
5728 /* Initial preparation for subprocess support: replace our standard
5729 handles with non-inheritable versions. */
5731 HANDLE parent;
5732 HANDLE stdin_save = INVALID_HANDLE_VALUE;
5733 HANDLE stdout_save = INVALID_HANDLE_VALUE;
5734 HANDLE stderr_save = INVALID_HANDLE_VALUE;
5736 parent = GetCurrentProcess ();
5738 /* ignore errors when duplicating and closing; typically the
5739 handles will be invalid when running as a gui program. */
5740 DuplicateHandle (parent,
5741 GetStdHandle (STD_INPUT_HANDLE),
5742 parent,
5743 &stdin_save,
5745 FALSE,
5746 DUPLICATE_SAME_ACCESS);
5748 DuplicateHandle (parent,
5749 GetStdHandle (STD_OUTPUT_HANDLE),
5750 parent,
5751 &stdout_save,
5753 FALSE,
5754 DUPLICATE_SAME_ACCESS);
5756 DuplicateHandle (parent,
5757 GetStdHandle (STD_ERROR_HANDLE),
5758 parent,
5759 &stderr_save,
5761 FALSE,
5762 DUPLICATE_SAME_ACCESS);
5764 fclose (stdin);
5765 fclose (stdout);
5766 fclose (stderr);
5768 if (stdin_save != INVALID_HANDLE_VALUE)
5769 _open_osfhandle ((long) stdin_save, O_TEXT);
5770 else
5771 _open ("nul", O_TEXT | O_NOINHERIT | O_RDONLY);
5772 _fdopen (0, "r");
5774 if (stdout_save != INVALID_HANDLE_VALUE)
5775 _open_osfhandle ((long) stdout_save, O_TEXT);
5776 else
5777 _open ("nul", O_TEXT | O_NOINHERIT | O_WRONLY);
5778 _fdopen (1, "w");
5780 if (stderr_save != INVALID_HANDLE_VALUE)
5781 _open_osfhandle ((long) stderr_save, O_TEXT);
5782 else
5783 _open ("nul", O_TEXT | O_NOINHERIT | O_WRONLY);
5784 _fdopen (2, "w");
5787 /* unfortunately, atexit depends on implementation of malloc */
5788 /* atexit (term_ntproc); */
5789 signal (SIGABRT, term_ntproc);
5791 /* determine which drives are fixed, for GetCachedVolumeInformation */
5793 /* GetDriveType must have trailing backslash. */
5794 char drive[] = "A:\\";
5796 /* Loop over all possible drive letters */
5797 while (*drive <= 'Z')
5799 /* Record if this drive letter refers to a fixed drive. */
5800 fixed_drives[DRIVE_INDEX (*drive)] =
5801 (GetDriveType (drive) == DRIVE_FIXED);
5803 (*drive)++;
5806 /* Reset the volume info cache. */
5807 volume_cache = NULL;
5810 /* Check to see if Emacs has been installed correctly. */
5811 check_windows_init_file ();
5815 shutdown_handler ensures that buffers' autosave files are
5816 up to date when the user logs off, or the system shuts down.
5818 static BOOL WINAPI
5819 shutdown_handler (DWORD type)
5821 /* Ctrl-C and Ctrl-Break are already suppressed, so don't handle them. */
5822 if (type == CTRL_CLOSE_EVENT /* User closes console window. */
5823 || type == CTRL_LOGOFF_EVENT /* User logs off. */
5824 || type == CTRL_SHUTDOWN_EVENT) /* User shutsdown. */
5826 /* Shut down cleanly, making sure autosave files are up to date. */
5827 shut_down_emacs (0, 0, Qnil);
5830 /* Allow other handlers to handle this signal. */
5831 return FALSE;
5835 globals_of_w32 is used to initialize those global variables that
5836 must always be initialized on startup even when the global variable
5837 initialized is non zero (see the function main in emacs.c).
5839 void
5840 globals_of_w32 (void)
5842 HMODULE kernel32 = GetModuleHandle ("kernel32.dll");
5844 get_process_times_fn = (GetProcessTimes_Proc)
5845 GetProcAddress (kernel32, "GetProcessTimes");
5847 g_b_init_is_windows_9x = 0;
5848 g_b_init_open_process_token = 0;
5849 g_b_init_get_token_information = 0;
5850 g_b_init_lookup_account_sid = 0;
5851 g_b_init_get_sid_sub_authority = 0;
5852 g_b_init_get_sid_sub_authority_count = 0;
5853 g_b_init_get_file_security = 0;
5854 g_b_init_get_security_descriptor_owner = 0;
5855 g_b_init_get_security_descriptor_group = 0;
5856 g_b_init_is_valid_sid = 0;
5857 g_b_init_create_toolhelp32_snapshot = 0;
5858 g_b_init_process32_first = 0;
5859 g_b_init_process32_next = 0;
5860 g_b_init_open_thread_token = 0;
5861 g_b_init_impersonate_self = 0;
5862 g_b_init_revert_to_self = 0;
5863 g_b_init_get_process_memory_info = 0;
5864 g_b_init_get_process_working_set_size = 0;
5865 g_b_init_global_memory_status = 0;
5866 g_b_init_global_memory_status_ex = 0;
5867 g_b_init_equal_sid = 0;
5868 g_b_init_copy_sid = 0;
5869 g_b_init_get_length_sid = 0;
5870 g_b_init_get_native_system_info = 0;
5871 g_b_init_get_system_times = 0;
5872 num_of_processors = 0;
5873 /* The following sets a handler for shutdown notifications for
5874 console apps. This actually applies to Emacs in both console and
5875 GUI modes, since we had to fool windows into thinking emacs is a
5876 console application to get console mode to work. */
5877 SetConsoleCtrlHandler (shutdown_handler, TRUE);
5879 /* "None" is the default group name on standalone workstations. */
5880 strcpy (dflt_group_name, "None");
5883 /* For make-serial-process */
5885 serial_open (char *port)
5887 HANDLE hnd;
5888 child_process *cp;
5889 int fd = -1;
5891 hnd = CreateFile (port, GENERIC_READ | GENERIC_WRITE, 0, 0,
5892 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
5893 if (hnd == INVALID_HANDLE_VALUE)
5894 error ("Could not open %s", port);
5895 fd = (int) _open_osfhandle ((int) hnd, 0);
5896 if (fd == -1)
5897 error ("Could not open %s", port);
5899 cp = new_child ();
5900 if (!cp)
5901 error ("Could not create child process");
5902 cp->fd = fd;
5903 cp->status = STATUS_READ_ACKNOWLEDGED;
5904 fd_info[ fd ].hnd = hnd;
5905 fd_info[ fd ].flags |=
5906 FILE_READ | FILE_WRITE | FILE_BINARY | FILE_SERIAL;
5907 if (fd_info[ fd ].cp != NULL)
5909 error ("fd_info[fd = %d] is already in use", fd);
5911 fd_info[ fd ].cp = cp;
5912 cp->ovl_read.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
5913 if (cp->ovl_read.hEvent == NULL)
5914 error ("Could not create read event");
5915 cp->ovl_write.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
5916 if (cp->ovl_write.hEvent == NULL)
5917 error ("Could not create write event");
5919 return fd;
5922 /* For serial-process-configure */
5923 void
5924 serial_configure (struct Lisp_Process *p, Lisp_Object contact)
5926 Lisp_Object childp2 = Qnil;
5927 Lisp_Object tem = Qnil;
5928 HANDLE hnd;
5929 DCB dcb;
5930 COMMTIMEOUTS ct;
5931 char summary[4] = "???"; /* This usually becomes "8N1". */
5933 if ((fd_info[ p->outfd ].flags & FILE_SERIAL) == 0)
5934 error ("Not a serial process");
5935 hnd = fd_info[ p->outfd ].hnd;
5937 childp2 = Fcopy_sequence (p->childp);
5939 /* Initialize timeouts for blocking read and blocking write. */
5940 if (!GetCommTimeouts (hnd, &ct))
5941 error ("GetCommTimeouts() failed");
5942 ct.ReadIntervalTimeout = 0;
5943 ct.ReadTotalTimeoutMultiplier = 0;
5944 ct.ReadTotalTimeoutConstant = 0;
5945 ct.WriteTotalTimeoutMultiplier = 0;
5946 ct.WriteTotalTimeoutConstant = 0;
5947 if (!SetCommTimeouts (hnd, &ct))
5948 error ("SetCommTimeouts() failed");
5949 /* Read port attributes and prepare default configuration. */
5950 memset (&dcb, 0, sizeof (dcb));
5951 dcb.DCBlength = sizeof (DCB);
5952 if (!GetCommState (hnd, &dcb))
5953 error ("GetCommState() failed");
5954 dcb.fBinary = TRUE;
5955 dcb.fNull = FALSE;
5956 dcb.fAbortOnError = FALSE;
5957 /* dcb.XonLim and dcb.XoffLim are set by GetCommState() */
5958 dcb.ErrorChar = 0;
5959 dcb.EofChar = 0;
5960 dcb.EvtChar = 0;
5962 /* Configure speed. */
5963 if (!NILP (Fplist_member (contact, QCspeed)))
5964 tem = Fplist_get (contact, QCspeed);
5965 else
5966 tem = Fplist_get (p->childp, QCspeed);
5967 CHECK_NUMBER (tem);
5968 dcb.BaudRate = XINT (tem);
5969 childp2 = Fplist_put (childp2, QCspeed, tem);
5971 /* Configure bytesize. */
5972 if (!NILP (Fplist_member (contact, QCbytesize)))
5973 tem = Fplist_get (contact, QCbytesize);
5974 else
5975 tem = Fplist_get (p->childp, QCbytesize);
5976 if (NILP (tem))
5977 tem = make_number (8);
5978 CHECK_NUMBER (tem);
5979 if (XINT (tem) != 7 && XINT (tem) != 8)
5980 error (":bytesize must be nil (8), 7, or 8");
5981 dcb.ByteSize = XINT (tem);
5982 summary[0] = XINT (tem) + '0';
5983 childp2 = Fplist_put (childp2, QCbytesize, tem);
5985 /* Configure parity. */
5986 if (!NILP (Fplist_member (contact, QCparity)))
5987 tem = Fplist_get (contact, QCparity);
5988 else
5989 tem = Fplist_get (p->childp, QCparity);
5990 if (!NILP (tem) && !EQ (tem, Qeven) && !EQ (tem, Qodd))
5991 error (":parity must be nil (no parity), `even', or `odd'");
5992 dcb.fParity = FALSE;
5993 dcb.Parity = NOPARITY;
5994 dcb.fErrorChar = FALSE;
5995 if (NILP (tem))
5997 summary[1] = 'N';
5999 else if (EQ (tem, Qeven))
6001 summary[1] = 'E';
6002 dcb.fParity = TRUE;
6003 dcb.Parity = EVENPARITY;
6004 dcb.fErrorChar = TRUE;
6006 else if (EQ (tem, Qodd))
6008 summary[1] = 'O';
6009 dcb.fParity = TRUE;
6010 dcb.Parity = ODDPARITY;
6011 dcb.fErrorChar = TRUE;
6013 childp2 = Fplist_put (childp2, QCparity, tem);
6015 /* Configure stopbits. */
6016 if (!NILP (Fplist_member (contact, QCstopbits)))
6017 tem = Fplist_get (contact, QCstopbits);
6018 else
6019 tem = Fplist_get (p->childp, QCstopbits);
6020 if (NILP (tem))
6021 tem = make_number (1);
6022 CHECK_NUMBER (tem);
6023 if (XINT (tem) != 1 && XINT (tem) != 2)
6024 error (":stopbits must be nil (1 stopbit), 1, or 2");
6025 summary[2] = XINT (tem) + '0';
6026 if (XINT (tem) == 1)
6027 dcb.StopBits = ONESTOPBIT;
6028 else if (XINT (tem) == 2)
6029 dcb.StopBits = TWOSTOPBITS;
6030 childp2 = Fplist_put (childp2, QCstopbits, tem);
6032 /* Configure flowcontrol. */
6033 if (!NILP (Fplist_member (contact, QCflowcontrol)))
6034 tem = Fplist_get (contact, QCflowcontrol);
6035 else
6036 tem = Fplist_get (p->childp, QCflowcontrol);
6037 if (!NILP (tem) && !EQ (tem, Qhw) && !EQ (tem, Qsw))
6038 error (":flowcontrol must be nil (no flowcontrol), `hw', or `sw'");
6039 dcb.fOutxCtsFlow = FALSE;
6040 dcb.fOutxDsrFlow = FALSE;
6041 dcb.fDtrControl = DTR_CONTROL_DISABLE;
6042 dcb.fDsrSensitivity = FALSE;
6043 dcb.fTXContinueOnXoff = FALSE;
6044 dcb.fOutX = FALSE;
6045 dcb.fInX = FALSE;
6046 dcb.fRtsControl = RTS_CONTROL_DISABLE;
6047 dcb.XonChar = 17; /* Control-Q */
6048 dcb.XoffChar = 19; /* Control-S */
6049 if (NILP (tem))
6051 /* Already configured. */
6053 else if (EQ (tem, Qhw))
6055 dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
6056 dcb.fOutxCtsFlow = TRUE;
6058 else if (EQ (tem, Qsw))
6060 dcb.fOutX = TRUE;
6061 dcb.fInX = TRUE;
6063 childp2 = Fplist_put (childp2, QCflowcontrol, tem);
6065 /* Activate configuration. */
6066 if (!SetCommState (hnd, &dcb))
6067 error ("SetCommState() failed");
6069 childp2 = Fplist_put (childp2, QCsummary, build_string (summary));
6070 p->childp = childp2;
6073 /* end of w32.c */