src/w32.c: Remove unused code and make static many functions.
[emacs.git] / src / w32.c
blob4ccda449b47487fb33a564089f091156be0ec96d
1 /* Utility and Unix shadow routines for GNU Emacs on the Microsoft W32 API.
2 Copyright (C) 1994, 1995, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
3 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
5 This file is part of GNU Emacs.
7 GNU Emacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
21 Geoff Voelker (voelker@cs.washington.edu) 7-29-94
23 #include <stddef.h> /* for offsetof */
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <float.h> /* for DBL_EPSILON */
27 #include <io.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <ctype.h>
31 #include <signal.h>
32 #include <sys/file.h>
33 #include <sys/time.h>
34 #include <sys/utime.h>
35 #include <mbstring.h> /* for _mbspbrk */
36 #include <math.h>
37 #include <setjmp.h>
39 /* must include CRT headers *before* config.h */
41 #ifdef HAVE_CONFIG_H
42 #include <config.h>
43 #endif
45 #undef access
46 #undef chdir
47 #undef chmod
48 #undef creat
49 #undef ctime
50 #undef fopen
51 #undef link
52 #undef mkdir
53 #undef mktemp
54 #undef open
55 #undef rename
56 #undef rmdir
57 #undef unlink
59 #undef close
60 #undef dup
61 #undef dup2
62 #undef pipe
63 #undef read
64 #undef write
66 #undef strerror
68 #include "lisp.h"
70 #include <pwd.h>
71 #include <grp.h>
73 #ifdef __GNUC__
74 #define _ANONYMOUS_UNION
75 #define _ANONYMOUS_STRUCT
76 #endif
77 #include <windows.h>
78 /* Some versions of compiler define MEMORYSTATUSEX, some don't, so we
79 use a different name to avoid compilation problems. */
80 typedef struct _MEMORY_STATUS_EX {
81 DWORD dwLength;
82 DWORD dwMemoryLoad;
83 DWORDLONG ullTotalPhys;
84 DWORDLONG ullAvailPhys;
85 DWORDLONG ullTotalPageFile;
86 DWORDLONG ullAvailPageFile;
87 DWORDLONG ullTotalVirtual;
88 DWORDLONG ullAvailVirtual;
89 DWORDLONG ullAvailExtendedVirtual;
90 } MEMORY_STATUS_EX,*LPMEMORY_STATUS_EX;
92 #include <lmcons.h>
93 #include <shlobj.h>
95 #include <tlhelp32.h>
96 #include <psapi.h>
97 #include <w32api.h>
98 #if !defined(__MINGW32__) || __W32API_MAJOR_VERSION < 3 || (__W32API_MAJOR_VERSION == 3 && __W32API_MINOR_VERSION < 15)
99 /* This either is not in psapi.h or guarded by higher value of
100 _WIN32_WINNT than what we use. w32api suplied with MinGW 3.15
101 defines it in psapi.h */
102 typedef struct _PROCESS_MEMORY_COUNTERS_EX {
103 DWORD cb;
104 DWORD PageFaultCount;
105 DWORD PeakWorkingSetSize;
106 DWORD WorkingSetSize;
107 DWORD QuotaPeakPagedPoolUsage;
108 DWORD QuotaPagedPoolUsage;
109 DWORD QuotaPeakNonPagedPoolUsage;
110 DWORD QuotaNonPagedPoolUsage;
111 DWORD PagefileUsage;
112 DWORD PeakPagefileUsage;
113 DWORD PrivateUsage;
114 } PROCESS_MEMORY_COUNTERS_EX,*PPROCESS_MEMORY_COUNTERS_EX;
115 #endif
117 /* TCP connection support. */
118 #include <sys/socket.h>
119 #undef socket
120 #undef bind
121 #undef connect
122 #undef htons
123 #undef ntohs
124 #undef inet_addr
125 #undef gethostname
126 #undef gethostbyname
127 #undef getservbyname
128 #undef getpeername
129 #undef shutdown
130 #undef setsockopt
131 #undef listen
132 #undef getsockname
133 #undef accept
134 #undef recvfrom
135 #undef sendto
137 #include "w32.h"
138 #include "ndir.h"
139 #include "w32heap.h"
140 #include "systime.h"
141 #include "dispextern.h" /* for xstrcasecmp */
142 #include "coding.h" /* for Vlocale_coding_system */
144 /* For serial_configure and serial_open. */
145 #include "process.h"
147 typedef HRESULT (WINAPI * ShGetFolderPath_fn)
148 (IN HWND, IN int, IN HANDLE, IN DWORD, OUT char *);
150 void globals_of_w32 (void);
151 static DWORD get_rid (PSID);
153 extern Lisp_Object Vw32_downcase_file_names;
154 extern Lisp_Object Vw32_generate_fake_inodes;
155 extern Lisp_Object Vw32_get_true_file_attributes;
156 /* Defined in process.c for its own purpose. */
157 extern Lisp_Object Qlocal;
159 extern int w32_num_mouse_buttons;
162 /* Initialization states.
164 WARNING: If you add any more such variables for additional APIs,
165 you MUST add initialization for them to globals_of_w32
166 below. This is because these variables might get set
167 to non-NULL values during dumping, but the dumped Emacs
168 cannot reuse those values, because it could be run on a
169 different version of the OS, where API addresses are
170 different. */
171 static BOOL g_b_init_is_windows_9x;
172 static BOOL g_b_init_open_process_token;
173 static BOOL g_b_init_get_token_information;
174 static BOOL g_b_init_lookup_account_sid;
175 static BOOL g_b_init_get_sid_sub_authority;
176 static BOOL g_b_init_get_sid_sub_authority_count;
177 static BOOL g_b_init_get_file_security;
178 static BOOL g_b_init_get_security_descriptor_owner;
179 static BOOL g_b_init_get_security_descriptor_group;
180 static BOOL g_b_init_is_valid_sid;
181 static BOOL g_b_init_create_toolhelp32_snapshot;
182 static BOOL g_b_init_process32_first;
183 static BOOL g_b_init_process32_next;
184 static BOOL g_b_init_open_thread_token;
185 static BOOL g_b_init_impersonate_self;
186 static BOOL g_b_init_revert_to_self;
187 static BOOL g_b_init_get_process_memory_info;
188 static BOOL g_b_init_get_process_working_set_size;
189 static BOOL g_b_init_global_memory_status;
190 static BOOL g_b_init_global_memory_status_ex;
191 static BOOL g_b_init_get_length_sid;
192 static BOOL g_b_init_equal_sid;
193 static BOOL g_b_init_copy_sid;
194 static BOOL g_b_init_get_native_system_info;
195 static BOOL g_b_init_get_system_times;
198 BEGIN: Wrapper functions around OpenProcessToken
199 and other functions in advapi32.dll that are only
200 supported in Windows NT / 2k / XP
202 /* ** Function pointer typedefs ** */
203 typedef BOOL (WINAPI * OpenProcessToken_Proc) (
204 HANDLE ProcessHandle,
205 DWORD DesiredAccess,
206 PHANDLE TokenHandle);
207 typedef BOOL (WINAPI * GetTokenInformation_Proc) (
208 HANDLE TokenHandle,
209 TOKEN_INFORMATION_CLASS TokenInformationClass,
210 LPVOID TokenInformation,
211 DWORD TokenInformationLength,
212 PDWORD ReturnLength);
213 typedef BOOL (WINAPI * GetProcessTimes_Proc) (
214 HANDLE process_handle,
215 LPFILETIME creation_time,
216 LPFILETIME exit_time,
217 LPFILETIME kernel_time,
218 LPFILETIME user_time);
220 GetProcessTimes_Proc get_process_times_fn = NULL;
222 #ifdef _UNICODE
223 const char * const LookupAccountSid_Name = "LookupAccountSidW";
224 const char * const GetFileSecurity_Name = "GetFileSecurityW";
225 #else
226 const char * const LookupAccountSid_Name = "LookupAccountSidA";
227 const char * const GetFileSecurity_Name = "GetFileSecurityA";
228 #endif
229 typedef BOOL (WINAPI * LookupAccountSid_Proc) (
230 LPCTSTR lpSystemName,
231 PSID Sid,
232 LPTSTR Name,
233 LPDWORD cbName,
234 LPTSTR DomainName,
235 LPDWORD cbDomainName,
236 PSID_NAME_USE peUse);
237 typedef PDWORD (WINAPI * GetSidSubAuthority_Proc) (
238 PSID pSid,
239 DWORD n);
240 typedef PUCHAR (WINAPI * GetSidSubAuthorityCount_Proc) (
241 PSID pSid);
242 typedef BOOL (WINAPI * GetFileSecurity_Proc) (
243 LPCTSTR lpFileName,
244 SECURITY_INFORMATION RequestedInformation,
245 PSECURITY_DESCRIPTOR pSecurityDescriptor,
246 DWORD nLength,
247 LPDWORD lpnLengthNeeded);
248 typedef BOOL (WINAPI * GetSecurityDescriptorOwner_Proc) (
249 PSECURITY_DESCRIPTOR pSecurityDescriptor,
250 PSID *pOwner,
251 LPBOOL lpbOwnerDefaulted);
252 typedef BOOL (WINAPI * GetSecurityDescriptorGroup_Proc) (
253 PSECURITY_DESCRIPTOR pSecurityDescriptor,
254 PSID *pGroup,
255 LPBOOL lpbGroupDefaulted);
256 typedef BOOL (WINAPI * IsValidSid_Proc) (
257 PSID sid);
258 typedef HANDLE (WINAPI * CreateToolhelp32Snapshot_Proc) (
259 DWORD dwFlags,
260 DWORD th32ProcessID);
261 typedef BOOL (WINAPI * Process32First_Proc) (
262 HANDLE hSnapshot,
263 LPPROCESSENTRY32 lppe);
264 typedef BOOL (WINAPI * Process32Next_Proc) (
265 HANDLE hSnapshot,
266 LPPROCESSENTRY32 lppe);
267 typedef BOOL (WINAPI * OpenThreadToken_Proc) (
268 HANDLE ThreadHandle,
269 DWORD DesiredAccess,
270 BOOL OpenAsSelf,
271 PHANDLE TokenHandle);
272 typedef BOOL (WINAPI * ImpersonateSelf_Proc) (
273 SECURITY_IMPERSONATION_LEVEL ImpersonationLevel);
274 typedef BOOL (WINAPI * RevertToSelf_Proc) (void);
275 typedef BOOL (WINAPI * GetProcessMemoryInfo_Proc) (
276 HANDLE Process,
277 PPROCESS_MEMORY_COUNTERS ppsmemCounters,
278 DWORD cb);
279 typedef BOOL (WINAPI * GetProcessWorkingSetSize_Proc) (
280 HANDLE hProcess,
281 DWORD * lpMinimumWorkingSetSize,
282 DWORD * lpMaximumWorkingSetSize);
283 typedef BOOL (WINAPI * GlobalMemoryStatus_Proc) (
284 LPMEMORYSTATUS lpBuffer);
285 typedef BOOL (WINAPI * GlobalMemoryStatusEx_Proc) (
286 LPMEMORY_STATUS_EX lpBuffer);
287 typedef BOOL (WINAPI * CopySid_Proc) (
288 DWORD nDestinationSidLength,
289 PSID pDestinationSid,
290 PSID pSourceSid);
291 typedef BOOL (WINAPI * EqualSid_Proc) (
292 PSID pSid1,
293 PSID pSid2);
294 typedef DWORD (WINAPI * GetLengthSid_Proc) (
295 PSID pSid);
296 typedef void (WINAPI * GetNativeSystemInfo_Proc) (
297 LPSYSTEM_INFO lpSystemInfo);
298 typedef BOOL (WINAPI * GetSystemTimes_Proc) (
299 LPFILETIME lpIdleTime,
300 LPFILETIME lpKernelTime,
301 LPFILETIME lpUserTime);
303 /* ** A utility function ** */
304 static BOOL
305 is_windows_9x (void)
307 static BOOL s_b_ret = 0;
308 OSVERSIONINFO os_ver;
309 if (g_b_init_is_windows_9x == 0)
311 g_b_init_is_windows_9x = 1;
312 ZeroMemory (&os_ver, sizeof (OSVERSIONINFO));
313 os_ver.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
314 if (GetVersionEx (&os_ver))
316 s_b_ret = (os_ver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
319 return s_b_ret;
322 /* Get total user and system times for get-internal-run-time.
323 Returns a list of three integers if the times are provided by the OS
324 (NT derivatives), otherwise it returns the result of current-time. */
325 Lisp_Object
326 w32_get_internal_run_time (void)
328 if (get_process_times_fn)
330 FILETIME create, exit, kernel, user;
331 HANDLE proc = GetCurrentProcess ();
332 if ((*get_process_times_fn) (proc, &create, &exit, &kernel, &user))
334 LARGE_INTEGER user_int, kernel_int, total;
335 int microseconds;
336 user_int.LowPart = user.dwLowDateTime;
337 user_int.HighPart = user.dwHighDateTime;
338 kernel_int.LowPart = kernel.dwLowDateTime;
339 kernel_int.HighPart = kernel.dwHighDateTime;
340 total.QuadPart = user_int.QuadPart + kernel_int.QuadPart;
341 /* FILETIME is 100 nanosecond increments, Emacs only wants
342 microsecond resolution. */
343 total.QuadPart /= 10;
344 microseconds = total.QuadPart % 1000000;
345 total.QuadPart /= 1000000;
347 /* Sanity check to make sure we can represent the result. */
348 if (total.HighPart == 0)
350 int secs = total.LowPart;
352 return list3 (make_number ((secs >> 16) & 0xffff),
353 make_number (secs & 0xffff),
354 make_number (microseconds));
359 return Fcurrent_time ();
362 /* ** The wrapper functions ** */
364 static BOOL WINAPI
365 open_process_token (HANDLE ProcessHandle,
366 DWORD DesiredAccess,
367 PHANDLE TokenHandle)
369 static OpenProcessToken_Proc s_pfn_Open_Process_Token = NULL;
370 HMODULE hm_advapi32 = NULL;
371 if (is_windows_9x () == TRUE)
373 return FALSE;
375 if (g_b_init_open_process_token == 0)
377 g_b_init_open_process_token = 1;
378 hm_advapi32 = LoadLibrary ("Advapi32.dll");
379 s_pfn_Open_Process_Token =
380 (OpenProcessToken_Proc) GetProcAddress (hm_advapi32, "OpenProcessToken");
382 if (s_pfn_Open_Process_Token == NULL)
384 return FALSE;
386 return (
387 s_pfn_Open_Process_Token (
388 ProcessHandle,
389 DesiredAccess,
390 TokenHandle)
394 static BOOL WINAPI
395 get_token_information (HANDLE TokenHandle,
396 TOKEN_INFORMATION_CLASS TokenInformationClass,
397 LPVOID TokenInformation,
398 DWORD TokenInformationLength,
399 PDWORD ReturnLength)
401 static GetTokenInformation_Proc s_pfn_Get_Token_Information = NULL;
402 HMODULE hm_advapi32 = NULL;
403 if (is_windows_9x () == TRUE)
405 return FALSE;
407 if (g_b_init_get_token_information == 0)
409 g_b_init_get_token_information = 1;
410 hm_advapi32 = LoadLibrary ("Advapi32.dll");
411 s_pfn_Get_Token_Information =
412 (GetTokenInformation_Proc) GetProcAddress (hm_advapi32, "GetTokenInformation");
414 if (s_pfn_Get_Token_Information == NULL)
416 return FALSE;
418 return (
419 s_pfn_Get_Token_Information (
420 TokenHandle,
421 TokenInformationClass,
422 TokenInformation,
423 TokenInformationLength,
424 ReturnLength)
428 static BOOL WINAPI
429 lookup_account_sid (LPCTSTR lpSystemName,
430 PSID Sid,
431 LPTSTR Name,
432 LPDWORD cbName,
433 LPTSTR DomainName,
434 LPDWORD cbDomainName,
435 PSID_NAME_USE peUse)
437 static LookupAccountSid_Proc s_pfn_Lookup_Account_Sid = NULL;
438 HMODULE hm_advapi32 = NULL;
439 if (is_windows_9x () == TRUE)
441 return FALSE;
443 if (g_b_init_lookup_account_sid == 0)
445 g_b_init_lookup_account_sid = 1;
446 hm_advapi32 = LoadLibrary ("Advapi32.dll");
447 s_pfn_Lookup_Account_Sid =
448 (LookupAccountSid_Proc) GetProcAddress (hm_advapi32, LookupAccountSid_Name);
450 if (s_pfn_Lookup_Account_Sid == NULL)
452 return FALSE;
454 return (
455 s_pfn_Lookup_Account_Sid (
456 lpSystemName,
457 Sid,
458 Name,
459 cbName,
460 DomainName,
461 cbDomainName,
462 peUse)
466 static PDWORD WINAPI
467 get_sid_sub_authority (PSID pSid, DWORD n)
469 static GetSidSubAuthority_Proc s_pfn_Get_Sid_Sub_Authority = NULL;
470 static DWORD zero = 0U;
471 HMODULE hm_advapi32 = NULL;
472 if (is_windows_9x () == TRUE)
474 return &zero;
476 if (g_b_init_get_sid_sub_authority == 0)
478 g_b_init_get_sid_sub_authority = 1;
479 hm_advapi32 = LoadLibrary ("Advapi32.dll");
480 s_pfn_Get_Sid_Sub_Authority =
481 (GetSidSubAuthority_Proc) GetProcAddress (
482 hm_advapi32, "GetSidSubAuthority");
484 if (s_pfn_Get_Sid_Sub_Authority == NULL)
486 return &zero;
488 return (s_pfn_Get_Sid_Sub_Authority (pSid, n));
491 static PUCHAR WINAPI
492 get_sid_sub_authority_count (PSID pSid)
494 static GetSidSubAuthorityCount_Proc s_pfn_Get_Sid_Sub_Authority_Count = NULL;
495 static UCHAR zero = 0U;
496 HMODULE hm_advapi32 = NULL;
497 if (is_windows_9x () == TRUE)
499 return &zero;
501 if (g_b_init_get_sid_sub_authority_count == 0)
503 g_b_init_get_sid_sub_authority_count = 1;
504 hm_advapi32 = LoadLibrary ("Advapi32.dll");
505 s_pfn_Get_Sid_Sub_Authority_Count =
506 (GetSidSubAuthorityCount_Proc) GetProcAddress (
507 hm_advapi32, "GetSidSubAuthorityCount");
509 if (s_pfn_Get_Sid_Sub_Authority_Count == NULL)
511 return &zero;
513 return (s_pfn_Get_Sid_Sub_Authority_Count (pSid));
516 static BOOL WINAPI
517 get_file_security (LPCTSTR lpFileName,
518 SECURITY_INFORMATION RequestedInformation,
519 PSECURITY_DESCRIPTOR pSecurityDescriptor,
520 DWORD nLength,
521 LPDWORD lpnLengthNeeded)
523 static GetFileSecurity_Proc s_pfn_Get_File_Security = NULL;
524 HMODULE hm_advapi32 = NULL;
525 if (is_windows_9x () == TRUE)
527 return FALSE;
529 if (g_b_init_get_file_security == 0)
531 g_b_init_get_file_security = 1;
532 hm_advapi32 = LoadLibrary ("Advapi32.dll");
533 s_pfn_Get_File_Security =
534 (GetFileSecurity_Proc) GetProcAddress (
535 hm_advapi32, GetFileSecurity_Name);
537 if (s_pfn_Get_File_Security == NULL)
539 return FALSE;
541 return (s_pfn_Get_File_Security (lpFileName, RequestedInformation,
542 pSecurityDescriptor, nLength,
543 lpnLengthNeeded));
546 static BOOL WINAPI
547 get_security_descriptor_owner (PSECURITY_DESCRIPTOR pSecurityDescriptor,
548 PSID *pOwner,
549 LPBOOL lpbOwnerDefaulted)
551 static GetSecurityDescriptorOwner_Proc s_pfn_Get_Security_Descriptor_Owner = NULL;
552 HMODULE hm_advapi32 = NULL;
553 if (is_windows_9x () == TRUE)
555 return FALSE;
557 if (g_b_init_get_security_descriptor_owner == 0)
559 g_b_init_get_security_descriptor_owner = 1;
560 hm_advapi32 = LoadLibrary ("Advapi32.dll");
561 s_pfn_Get_Security_Descriptor_Owner =
562 (GetSecurityDescriptorOwner_Proc) GetProcAddress (
563 hm_advapi32, "GetSecurityDescriptorOwner");
565 if (s_pfn_Get_Security_Descriptor_Owner == NULL)
567 return FALSE;
569 return (s_pfn_Get_Security_Descriptor_Owner (pSecurityDescriptor, pOwner,
570 lpbOwnerDefaulted));
573 static BOOL WINAPI
574 get_security_descriptor_group (PSECURITY_DESCRIPTOR pSecurityDescriptor,
575 PSID *pGroup,
576 LPBOOL lpbGroupDefaulted)
578 static GetSecurityDescriptorGroup_Proc s_pfn_Get_Security_Descriptor_Group = NULL;
579 HMODULE hm_advapi32 = NULL;
580 if (is_windows_9x () == TRUE)
582 return FALSE;
584 if (g_b_init_get_security_descriptor_group == 0)
586 g_b_init_get_security_descriptor_group = 1;
587 hm_advapi32 = LoadLibrary ("Advapi32.dll");
588 s_pfn_Get_Security_Descriptor_Group =
589 (GetSecurityDescriptorGroup_Proc) GetProcAddress (
590 hm_advapi32, "GetSecurityDescriptorGroup");
592 if (s_pfn_Get_Security_Descriptor_Group == NULL)
594 return FALSE;
596 return (s_pfn_Get_Security_Descriptor_Group (pSecurityDescriptor, pGroup,
597 lpbGroupDefaulted));
600 static BOOL WINAPI
601 is_valid_sid (PSID sid)
603 static IsValidSid_Proc s_pfn_Is_Valid_Sid = NULL;
604 HMODULE hm_advapi32 = NULL;
605 if (is_windows_9x () == TRUE)
607 return FALSE;
609 if (g_b_init_is_valid_sid == 0)
611 g_b_init_is_valid_sid = 1;
612 hm_advapi32 = LoadLibrary ("Advapi32.dll");
613 s_pfn_Is_Valid_Sid =
614 (IsValidSid_Proc) GetProcAddress (
615 hm_advapi32, "IsValidSid");
617 if (s_pfn_Is_Valid_Sid == NULL)
619 return FALSE;
621 return (s_pfn_Is_Valid_Sid (sid));
624 static BOOL WINAPI
625 equal_sid (PSID sid1, PSID sid2)
627 static EqualSid_Proc s_pfn_Equal_Sid = NULL;
628 HMODULE hm_advapi32 = NULL;
629 if (is_windows_9x () == TRUE)
631 return FALSE;
633 if (g_b_init_equal_sid == 0)
635 g_b_init_equal_sid = 1;
636 hm_advapi32 = LoadLibrary ("Advapi32.dll");
637 s_pfn_Equal_Sid =
638 (EqualSid_Proc) GetProcAddress (
639 hm_advapi32, "EqualSid");
641 if (s_pfn_Equal_Sid == NULL)
643 return FALSE;
645 return (s_pfn_Equal_Sid (sid1, sid2));
648 static DWORD WINAPI
649 get_length_sid (PSID sid)
651 static GetLengthSid_Proc s_pfn_Get_Length_Sid = NULL;
652 HMODULE hm_advapi32 = NULL;
653 if (is_windows_9x () == TRUE)
655 return 0;
657 if (g_b_init_get_length_sid == 0)
659 g_b_init_get_length_sid = 1;
660 hm_advapi32 = LoadLibrary ("Advapi32.dll");
661 s_pfn_Get_Length_Sid =
662 (GetLengthSid_Proc) GetProcAddress (
663 hm_advapi32, "GetLengthSid");
665 if (s_pfn_Get_Length_Sid == NULL)
667 return 0;
669 return (s_pfn_Get_Length_Sid (sid));
672 static BOOL WINAPI
673 copy_sid (DWORD destlen, PSID dest, PSID src)
675 static CopySid_Proc s_pfn_Copy_Sid = NULL;
676 HMODULE hm_advapi32 = NULL;
677 if (is_windows_9x () == TRUE)
679 return FALSE;
681 if (g_b_init_copy_sid == 0)
683 g_b_init_copy_sid = 1;
684 hm_advapi32 = LoadLibrary ("Advapi32.dll");
685 s_pfn_Copy_Sid =
686 (CopySid_Proc) GetProcAddress (
687 hm_advapi32, "CopySid");
689 if (s_pfn_Copy_Sid == NULL)
691 return FALSE;
693 return (s_pfn_Copy_Sid (destlen, dest, src));
697 END: Wrapper functions around OpenProcessToken
698 and other functions in advapi32.dll that are only
699 supported in Windows NT / 2k / XP
702 static void WINAPI
703 get_native_system_info (LPSYSTEM_INFO lpSystemInfo)
705 static GetNativeSystemInfo_Proc s_pfn_Get_Native_System_Info = NULL;
706 if (is_windows_9x () != TRUE)
708 if (g_b_init_get_native_system_info == 0)
710 g_b_init_get_native_system_info = 1;
711 s_pfn_Get_Native_System_Info =
712 (GetNativeSystemInfo_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
713 "GetNativeSystemInfo");
715 if (s_pfn_Get_Native_System_Info != NULL)
716 s_pfn_Get_Native_System_Info (lpSystemInfo);
718 else
719 lpSystemInfo->dwNumberOfProcessors = -1;
722 static BOOL WINAPI
723 get_system_times (LPFILETIME lpIdleTime,
724 LPFILETIME lpKernelTime,
725 LPFILETIME lpUserTime)
727 static GetSystemTimes_Proc s_pfn_Get_System_times = NULL;
728 if (is_windows_9x () == TRUE)
730 return FALSE;
732 if (g_b_init_get_system_times == 0)
734 g_b_init_get_system_times = 1;
735 s_pfn_Get_System_times =
736 (GetSystemTimes_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
737 "GetSystemTimes");
739 if (s_pfn_Get_System_times == NULL)
740 return FALSE;
741 return (s_pfn_Get_System_times (lpIdleTime, lpKernelTime, lpUserTime));
744 /* Equivalent of strerror for W32 error codes. */
745 char *
746 w32_strerror (int error_no)
748 static char buf[500];
750 if (error_no == 0)
751 error_no = GetLastError ();
753 buf[0] = '\0';
754 if (!FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL,
755 error_no,
756 0, /* choose most suitable language */
757 buf, sizeof (buf), NULL))
758 sprintf (buf, "w32 error %u", error_no);
759 return buf;
762 /* Return 1 if P is a valid pointer to an object of size SIZE. Return
763 0 if P is NOT a valid pointer. Return -1 if we cannot validate P.
765 This is called from alloc.c:valid_pointer_p. */
767 w32_valid_pointer_p (void *p, int size)
769 SIZE_T done;
770 HANDLE h = OpenProcess (PROCESS_VM_READ, FALSE, GetCurrentProcessId ());
772 if (h)
774 unsigned char *buf = alloca (size);
775 int retval = ReadProcessMemory (h, p, buf, size, &done);
777 CloseHandle (h);
778 return retval;
780 else
781 return -1;
784 static char startup_dir[MAXPATHLEN];
786 /* Get the current working directory. */
787 char *
788 getwd (char *dir)
790 #if 0
791 if (GetCurrentDirectory (MAXPATHLEN, dir) > 0)
792 return dir;
793 return NULL;
794 #else
795 /* Emacs doesn't actually change directory itself, and we want to
796 force our real wd to be where emacs.exe is to avoid unnecessary
797 conflicts when trying to rename or delete directories. */
798 strcpy (dir, startup_dir);
799 return dir;
800 #endif
803 /* Emulate getloadavg. */
805 struct load_sample {
806 time_t sample_time;
807 ULONGLONG idle;
808 ULONGLONG kernel;
809 ULONGLONG user;
812 /* Number of processors on this machine. */
813 static unsigned num_of_processors;
815 /* We maintain 1-sec samples for the last 16 minutes in a circular buffer. */
816 static struct load_sample samples[16*60];
817 static int first_idx = -1, last_idx = -1;
818 static int max_idx = sizeof (samples) / sizeof (samples[0]);
820 static int
821 buf_next (int from)
823 int next_idx = from + 1;
825 if (next_idx >= max_idx)
826 next_idx = 0;
828 return next_idx;
831 static int
832 buf_prev (int from)
834 int prev_idx = from - 1;
836 if (prev_idx < 0)
837 prev_idx = max_idx - 1;
839 return prev_idx;
842 static void
843 sample_system_load (ULONGLONG *idle, ULONGLONG *kernel, ULONGLONG *user)
845 SYSTEM_INFO sysinfo;
846 FILETIME ft_idle, ft_user, ft_kernel;
848 /* Initialize the number of processors on this machine. */
849 if (num_of_processors <= 0)
851 get_native_system_info (&sysinfo);
852 num_of_processors = sysinfo.dwNumberOfProcessors;
853 if (num_of_processors <= 0)
855 GetSystemInfo (&sysinfo);
856 num_of_processors = sysinfo.dwNumberOfProcessors;
858 if (num_of_processors <= 0)
859 num_of_processors = 1;
862 /* TODO: Take into account threads that are ready to run, by
863 sampling the "\System\Processor Queue Length" performance
864 counter. The code below accounts only for threads that are
865 actually running. */
867 if (get_system_times (&ft_idle, &ft_kernel, &ft_user))
869 ULARGE_INTEGER uidle, ukernel, uuser;
871 memcpy (&uidle, &ft_idle, sizeof (ft_idle));
872 memcpy (&ukernel, &ft_kernel, sizeof (ft_kernel));
873 memcpy (&uuser, &ft_user, sizeof (ft_user));
874 *idle = uidle.QuadPart;
875 *kernel = ukernel.QuadPart;
876 *user = uuser.QuadPart;
878 else
880 *idle = 0;
881 *kernel = 0;
882 *user = 0;
886 /* Produce the load average for a given time interval, using the
887 samples in the samples[] array. WHICH can be 0, 1, or 2, meaning
888 1-minute, 5-minute, or 15-minute average, respectively. */
889 static double
890 getavg (int which)
892 double retval = -1.0;
893 double tdiff;
894 int idx;
895 double span = (which == 0 ? 1.0 : (which == 1 ? 5.0 : 15.0)) * 60;
896 time_t now = samples[last_idx].sample_time;
898 if (first_idx != last_idx)
900 for (idx = buf_prev (last_idx); ; idx = buf_prev (idx))
902 tdiff = difftime (now, samples[idx].sample_time);
903 if (tdiff >= span - 2*DBL_EPSILON*now)
905 long double sys =
906 samples[last_idx].kernel + samples[last_idx].user
907 - (samples[idx].kernel + samples[idx].user);
908 long double idl = samples[last_idx].idle - samples[idx].idle;
910 retval = (1.0 - idl / sys) * num_of_processors;
911 break;
913 if (idx == first_idx)
914 break;
918 return retval;
922 getloadavg (double loadavg[], int nelem)
924 int elem;
925 ULONGLONG idle, kernel, user;
926 time_t now = time (NULL);
928 /* Store another sample. We ignore samples that are less than 1 sec
929 apart. */
930 if (difftime (now, samples[last_idx].sample_time) >= 1.0 - 2*DBL_EPSILON*now)
932 sample_system_load (&idle, &kernel, &user);
933 last_idx = buf_next (last_idx);
934 samples[last_idx].sample_time = now;
935 samples[last_idx].idle = idle;
936 samples[last_idx].kernel = kernel;
937 samples[last_idx].user = user;
938 /* If the buffer has more that 15 min worth of samples, discard
939 the old ones. */
940 if (first_idx == -1)
941 first_idx = last_idx;
942 while (first_idx != last_idx
943 && (difftime (now, samples[first_idx].sample_time)
944 >= 15.0*60 + 2*DBL_EPSILON*now))
945 first_idx = buf_next (first_idx);
948 for (elem = 0; elem < nelem; elem++)
950 double avg = getavg (elem);
952 if (avg < 0)
953 break;
954 loadavg[elem] = avg;
957 return elem;
960 /* Emulate getpwuid, getpwnam and others. */
962 #define PASSWD_FIELD_SIZE 256
964 static char dflt_passwd_name[PASSWD_FIELD_SIZE];
965 static char dflt_passwd_passwd[PASSWD_FIELD_SIZE];
966 static char dflt_passwd_gecos[PASSWD_FIELD_SIZE];
967 static char dflt_passwd_dir[PASSWD_FIELD_SIZE];
968 static char dflt_passwd_shell[PASSWD_FIELD_SIZE];
970 static struct passwd dflt_passwd =
972 dflt_passwd_name,
973 dflt_passwd_passwd,
977 dflt_passwd_gecos,
978 dflt_passwd_dir,
979 dflt_passwd_shell,
982 static char dflt_group_name[GNLEN+1];
984 static struct group dflt_group =
986 /* When group information is not available, we return this as the
987 group for all files. */
988 dflt_group_name,
992 unsigned
993 getuid (void)
995 return dflt_passwd.pw_uid;
998 unsigned
999 geteuid (void)
1001 /* I could imagine arguing for checking to see whether the user is
1002 in the Administrators group and returning a UID of 0 for that
1003 case, but I don't know how wise that would be in the long run. */
1004 return getuid ();
1007 unsigned
1008 getgid (void)
1010 return dflt_passwd.pw_gid;
1013 unsigned
1014 getegid (void)
1016 return getgid ();
1019 struct passwd *
1020 getpwuid (unsigned uid)
1022 if (uid == dflt_passwd.pw_uid)
1023 return &dflt_passwd;
1024 return NULL;
1027 struct group *
1028 getgrgid (gid_t gid)
1030 return &dflt_group;
1033 struct passwd *
1034 getpwnam (char *name)
1036 struct passwd *pw;
1038 pw = getpwuid (getuid ());
1039 if (!pw)
1040 return pw;
1042 if (xstrcasecmp (name, pw->pw_name))
1043 return NULL;
1045 return pw;
1048 static void
1049 init_user_info (void)
1051 /* Find the user's real name by opening the process token and
1052 looking up the name associated with the user-sid in that token.
1054 Use the relative portion of the identifier authority value from
1055 the user-sid as the user id value (same for group id using the
1056 primary group sid from the process token). */
1058 char uname[UNLEN+1], gname[GNLEN+1], domain[1025];
1059 DWORD ulength = sizeof (uname), dlength = sizeof (domain), needed;
1060 DWORD glength = sizeof (gname);
1061 HANDLE token = NULL;
1062 SID_NAME_USE user_type;
1063 unsigned char *buf = NULL;
1064 DWORD blen = 0;
1065 TOKEN_USER user_token;
1066 TOKEN_PRIMARY_GROUP group_token;
1067 BOOL result;
1069 result = open_process_token (GetCurrentProcess (), TOKEN_QUERY, &token);
1070 if (result)
1072 result = get_token_information (token, TokenUser, NULL, 0, &blen);
1073 if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER)
1075 buf = xmalloc (blen);
1076 result = get_token_information (token, TokenUser,
1077 (LPVOID)buf, blen, &needed);
1078 if (result)
1080 memcpy (&user_token, buf, sizeof (user_token));
1081 result = lookup_account_sid (NULL, user_token.User.Sid,
1082 uname, &ulength,
1083 domain, &dlength, &user_type);
1086 else
1087 result = FALSE;
1089 if (result)
1091 strcpy (dflt_passwd.pw_name, uname);
1092 /* Determine a reasonable uid value. */
1093 if (xstrcasecmp ("administrator", uname) == 0)
1095 dflt_passwd.pw_uid = 500; /* well-known Administrator uid */
1096 dflt_passwd.pw_gid = 513; /* well-known None gid */
1098 else
1100 /* Use the last sub-authority value of the RID, the relative
1101 portion of the SID, as user/group ID. */
1102 dflt_passwd.pw_uid = get_rid (user_token.User.Sid);
1104 /* Get group id and name. */
1105 result = get_token_information (token, TokenPrimaryGroup,
1106 (LPVOID)buf, blen, &needed);
1107 if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER)
1109 buf = xrealloc (buf, blen = needed);
1110 result = get_token_information (token, TokenPrimaryGroup,
1111 (LPVOID)buf, blen, &needed);
1113 if (result)
1115 memcpy (&group_token, buf, sizeof (group_token));
1116 dflt_passwd.pw_gid = get_rid (group_token.PrimaryGroup);
1117 dlength = sizeof (domain);
1118 /* If we can get at the real Primary Group name, use that.
1119 Otherwise, the default group name was already set to
1120 "None" in globals_of_w32. */
1121 if (lookup_account_sid (NULL, group_token.PrimaryGroup,
1122 gname, &glength, NULL, &dlength,
1123 &user_type))
1124 strcpy (dflt_group_name, gname);
1126 else
1127 dflt_passwd.pw_gid = dflt_passwd.pw_uid;
1130 /* If security calls are not supported (presumably because we
1131 are running under Windows 9X), fallback to this: */
1132 else if (GetUserName (uname, &ulength))
1134 strcpy (dflt_passwd.pw_name, uname);
1135 if (xstrcasecmp ("administrator", uname) == 0)
1136 dflt_passwd.pw_uid = 0;
1137 else
1138 dflt_passwd.pw_uid = 123;
1139 dflt_passwd.pw_gid = dflt_passwd.pw_uid;
1141 else
1143 strcpy (dflt_passwd.pw_name, "unknown");
1144 dflt_passwd.pw_uid = 123;
1145 dflt_passwd.pw_gid = 123;
1147 dflt_group.gr_gid = dflt_passwd.pw_gid;
1149 /* Ensure HOME and SHELL are defined. */
1150 if (getenv ("HOME") == NULL)
1151 abort ();
1152 if (getenv ("SHELL") == NULL)
1153 abort ();
1155 /* Set dir and shell from environment variables. */
1156 strcpy (dflt_passwd.pw_dir, getenv ("HOME"));
1157 strcpy (dflt_passwd.pw_shell, getenv ("SHELL"));
1159 xfree (buf);
1160 if (token)
1161 CloseHandle (token);
1165 random (void)
1167 /* rand () on NT gives us 15 random bits...hack together 30 bits. */
1168 return ((rand () << 15) | rand ());
1171 void
1172 srandom (int seed)
1174 srand (seed);
1178 /* Normalize filename by converting all path separators to
1179 the specified separator. Also conditionally convert upper
1180 case path name components to lower case. */
1182 static void
1183 normalize_filename (register char *fp, char path_sep)
1185 char sep;
1186 char *elem;
1188 /* Always lower-case drive letters a-z, even if the filesystem
1189 preserves case in filenames.
1190 This is so filenames can be compared by string comparison
1191 functions that are case-sensitive. Even case-preserving filesystems
1192 do not distinguish case in drive letters. */
1193 if (fp[1] == ':' && *fp >= 'A' && *fp <= 'Z')
1195 *fp += 'a' - 'A';
1196 fp += 2;
1199 if (NILP (Vw32_downcase_file_names))
1201 while (*fp)
1203 if (*fp == '/' || *fp == '\\')
1204 *fp = path_sep;
1205 fp++;
1207 return;
1210 sep = path_sep; /* convert to this path separator */
1211 elem = fp; /* start of current path element */
1213 do {
1214 if (*fp >= 'a' && *fp <= 'z')
1215 elem = 0; /* don't convert this element */
1217 if (*fp == 0 || *fp == ':')
1219 sep = *fp; /* restore current separator (or 0) */
1220 *fp = '/'; /* after conversion of this element */
1223 if (*fp == '/' || *fp == '\\')
1225 if (elem && elem != fp)
1227 *fp = 0; /* temporary end of string */
1228 _strlwr (elem); /* while we convert to lower case */
1230 *fp = sep; /* convert (or restore) path separator */
1231 elem = fp + 1; /* next element starts after separator */
1232 sep = path_sep;
1234 } while (*fp++);
1237 /* Destructively turn backslashes into slashes. */
1238 void
1239 dostounix_filename (register char *p)
1241 normalize_filename (p, '/');
1244 /* Destructively turn slashes into backslashes. */
1245 void
1246 unixtodos_filename (register char *p)
1248 normalize_filename (p, '\\');
1251 /* Remove all CR's that are followed by a LF.
1252 (From msdos.c...probably should figure out a way to share it,
1253 although this code isn't going to ever change.) */
1254 static int
1255 crlf_to_lf (register int n, register unsigned char *buf)
1257 unsigned char *np = buf;
1258 unsigned char *startp = buf;
1259 unsigned char *endp = buf + n;
1261 if (n == 0)
1262 return n;
1263 while (buf < endp - 1)
1265 if (*buf == 0x0d)
1267 if (*(++buf) != 0x0a)
1268 *np++ = 0x0d;
1270 else
1271 *np++ = *buf++;
1273 if (buf < endp)
1274 *np++ = *buf++;
1275 return np - startp;
1278 /* Parse the root part of file name, if present. Return length and
1279 optionally store pointer to char after root. */
1280 static int
1281 parse_root (char * name, char ** pPath)
1283 char * start = name;
1285 if (name == NULL)
1286 return 0;
1288 /* find the root name of the volume if given */
1289 if (isalpha (name[0]) && name[1] == ':')
1291 /* skip past drive specifier */
1292 name += 2;
1293 if (IS_DIRECTORY_SEP (name[0]))
1294 name++;
1296 else if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1]))
1298 int slashes = 2;
1299 name += 2;
1302 if (IS_DIRECTORY_SEP (*name) && --slashes == 0)
1303 break;
1304 name++;
1306 while ( *name );
1307 if (IS_DIRECTORY_SEP (name[0]))
1308 name++;
1311 if (pPath)
1312 *pPath = name;
1314 return name - start;
1317 /* Get long base name for name; name is assumed to be absolute. */
1318 static int
1319 get_long_basename (char * name, char * buf, int size)
1321 WIN32_FIND_DATA find_data;
1322 HANDLE dir_handle;
1323 int len = 0;
1325 /* must be valid filename, no wild cards or other invalid characters */
1326 if (_mbspbrk (name, "*?|<>\""))
1327 return 0;
1329 dir_handle = FindFirstFile (name, &find_data);
1330 if (dir_handle != INVALID_HANDLE_VALUE)
1332 if ((len = strlen (find_data.cFileName)) < size)
1333 memcpy (buf, find_data.cFileName, len + 1);
1334 else
1335 len = 0;
1336 FindClose (dir_handle);
1338 return len;
1341 /* Get long name for file, if possible (assumed to be absolute). */
1342 BOOL
1343 w32_get_long_filename (char * name, char * buf, int size)
1345 char * o = buf;
1346 char * p;
1347 char * q;
1348 char full[ MAX_PATH ];
1349 int len;
1351 len = strlen (name);
1352 if (len >= MAX_PATH)
1353 return FALSE;
1355 /* Use local copy for destructive modification. */
1356 memcpy (full, name, len+1);
1357 unixtodos_filename (full);
1359 /* Copy root part verbatim. */
1360 len = parse_root (full, &p);
1361 memcpy (o, full, len);
1362 o += len;
1363 *o = '\0';
1364 size -= len;
1366 while (p != NULL && *p)
1368 q = p;
1369 p = strchr (q, '\\');
1370 if (p) *p = '\0';
1371 len = get_long_basename (full, o, size);
1372 if (len > 0)
1374 o += len;
1375 size -= len;
1376 if (p != NULL)
1378 *p++ = '\\';
1379 if (size < 2)
1380 return FALSE;
1381 *o++ = '\\';
1382 size--;
1383 *o = '\0';
1386 else
1387 return FALSE;
1390 return TRUE;
1393 static int
1394 is_unc_volume (const char *filename)
1396 const char *ptr = filename;
1398 if (!IS_DIRECTORY_SEP (ptr[0]) || !IS_DIRECTORY_SEP (ptr[1]) || !ptr[2])
1399 return 0;
1401 if (_mbspbrk (ptr + 2, "*?|<>\"\\/"))
1402 return 0;
1404 return 1;
1407 /* Routines that are no-ops on NT but are defined to get Emacs to compile. */
1410 sigsetmask (int signal_mask)
1412 return 0;
1416 sigmask (int sig)
1418 return 0;
1422 sigblock (int sig)
1424 return 0;
1428 sigunblock (int sig)
1430 return 0;
1434 sigemptyset (sigset_t *set)
1436 return 0;
1440 sigaddset (sigset_t *set, int signo)
1442 return 0;
1446 sigfillset (sigset_t *set)
1448 return 0;
1452 sigprocmask (int how, const sigset_t *set, sigset_t *oset)
1454 return 0;
1458 setpgrp (int pid, int gid)
1460 return 0;
1464 alarm (int seconds)
1466 return 0;
1469 #define REG_ROOT "SOFTWARE\\GNU\\Emacs"
1471 LPBYTE
1472 w32_get_resource (char *key, LPDWORD lpdwtype)
1474 LPBYTE lpvalue;
1475 HKEY hrootkey = NULL;
1476 DWORD cbData;
1478 /* Check both the current user and the local machine to see if
1479 we have any resources. */
1481 if (RegOpenKeyEx (HKEY_CURRENT_USER, REG_ROOT, 0, KEY_READ, &hrootkey) == ERROR_SUCCESS)
1483 lpvalue = NULL;
1485 if (RegQueryValueEx (hrootkey, key, NULL, NULL, NULL, &cbData) == ERROR_SUCCESS
1486 && (lpvalue = (LPBYTE) xmalloc (cbData)) != NULL
1487 && RegQueryValueEx (hrootkey, key, NULL, lpdwtype, lpvalue, &cbData) == ERROR_SUCCESS)
1489 RegCloseKey (hrootkey);
1490 return (lpvalue);
1493 xfree (lpvalue);
1495 RegCloseKey (hrootkey);
1498 if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, REG_ROOT, 0, KEY_READ, &hrootkey) == ERROR_SUCCESS)
1500 lpvalue = NULL;
1502 if (RegQueryValueEx (hrootkey, key, NULL, NULL, NULL, &cbData) == ERROR_SUCCESS
1503 && (lpvalue = (LPBYTE) xmalloc (cbData)) != NULL
1504 && RegQueryValueEx (hrootkey, key, NULL, lpdwtype, lpvalue, &cbData) == ERROR_SUCCESS)
1506 RegCloseKey (hrootkey);
1507 return (lpvalue);
1510 xfree (lpvalue);
1512 RegCloseKey (hrootkey);
1515 return (NULL);
1518 char *get_emacs_configuration (void);
1519 extern Lisp_Object Vsystem_configuration;
1521 void
1522 init_environment (char ** argv)
1524 static const char * const tempdirs[] = {
1525 "$TMPDIR", "$TEMP", "$TMP", "c:/"
1528 int i;
1530 const int imax = sizeof (tempdirs) / sizeof (tempdirs[0]);
1532 /* Make sure they have a usable $TMPDIR. Many Emacs functions use
1533 temporary files and assume "/tmp" if $TMPDIR is unset, which
1534 will break on DOS/Windows. Refuse to work if we cannot find
1535 a directory, not even "c:/", usable for that purpose. */
1536 for (i = 0; i < imax ; i++)
1538 const char *tmp = tempdirs[i];
1540 if (*tmp == '$')
1541 tmp = getenv (tmp + 1);
1542 /* Note that `access' can lie to us if the directory resides on a
1543 read-only filesystem, like CD-ROM or a write-protected floppy.
1544 The only way to be really sure is to actually create a file and
1545 see if it succeeds. But I think that's too much to ask. */
1546 if (tmp && _access (tmp, D_OK) == 0)
1548 char * var = alloca (strlen (tmp) + 8);
1549 sprintf (var, "TMPDIR=%s", tmp);
1550 _putenv (strdup (var));
1551 break;
1554 if (i >= imax)
1555 cmd_error_internal
1556 (Fcons (Qerror,
1557 Fcons (build_string ("no usable temporary directories found!!"),
1558 Qnil)),
1559 "While setting TMPDIR: ");
1561 /* Check for environment variables and use registry settings if they
1562 don't exist. Fallback on default values where applicable. */
1564 int i;
1565 LPBYTE lpval;
1566 DWORD dwType;
1567 char locale_name[32];
1568 struct stat ignored;
1569 char default_home[MAX_PATH];
1571 static const struct env_entry
1573 char * name;
1574 char * def_value;
1575 } dflt_envvars[] =
1577 {"HOME", "C:/"},
1578 {"PRELOAD_WINSOCK", NULL},
1579 {"emacs_dir", "C:/emacs"},
1580 {"EMACSLOADPATH", "%emacs_dir%/site-lisp;%emacs_dir%/../site-lisp;%emacs_dir%/lisp;%emacs_dir%/leim"},
1581 {"SHELL", "%emacs_dir%/bin/cmdproxy.exe"},
1582 {"EMACSDATA", "%emacs_dir%/etc"},
1583 {"EMACSPATH", "%emacs_dir%/bin"},
1584 /* We no longer set INFOPATH because Info-default-directory-list
1585 is then ignored. */
1586 /* {"INFOPATH", "%emacs_dir%/info"}, */
1587 {"EMACSDOC", "%emacs_dir%/etc"},
1588 {"TERM", "cmd"},
1589 {"LANG", NULL},
1592 #define N_ENV_VARS sizeof (dflt_envvars)/sizeof (dflt_envvars[0])
1594 /* We need to copy dflt_envvars[] and work on the copy because we
1595 don't want the dumped Emacs to inherit the values of
1596 environment variables we saw during dumping (which could be on
1597 a different system). The defaults above must be left intact. */
1598 struct env_entry env_vars[N_ENV_VARS];
1600 for (i = 0; i < N_ENV_VARS; i++)
1601 env_vars[i] = dflt_envvars[i];
1603 /* For backwards compatibility, check if a .emacs file exists in C:/
1604 If not, then we can try to default to the appdata directory under the
1605 user's profile, which is more likely to be writable. */
1606 if (stat ("C:/.emacs", &ignored) < 0)
1608 HRESULT profile_result;
1609 /* Dynamically load ShGetFolderPath, as it won't exist on versions
1610 of Windows 95 and NT4 that have not been updated to include
1611 MSIE 5. */
1612 ShGetFolderPath_fn get_folder_path;
1613 get_folder_path = (ShGetFolderPath_fn)
1614 GetProcAddress (GetModuleHandle ("shell32.dll"), "SHGetFolderPathA");
1616 if (get_folder_path != NULL)
1618 profile_result = get_folder_path (NULL, CSIDL_APPDATA, NULL,
1619 0, default_home);
1621 /* If we can't get the appdata dir, revert to old behavior. */
1622 if (profile_result == S_OK)
1623 env_vars[0].def_value = default_home;
1627 /* Get default locale info and use it for LANG. */
1628 if (GetLocaleInfo (LOCALE_USER_DEFAULT,
1629 LOCALE_SABBREVLANGNAME | LOCALE_USE_CP_ACP,
1630 locale_name, sizeof (locale_name)))
1632 for (i = 0; i < N_ENV_VARS; i++)
1634 if (strcmp (env_vars[i].name, "LANG") == 0)
1636 env_vars[i].def_value = locale_name;
1637 break;
1642 #define SET_ENV_BUF_SIZE (4 * MAX_PATH) /* to cover EMACSLOADPATH */
1644 /* Treat emacs_dir specially: set it unconditionally based on our
1645 location, if it appears that we are running from the bin subdir
1646 of a standard installation. */
1648 char *p;
1649 char modname[MAX_PATH];
1651 if (!GetModuleFileName (NULL, modname, MAX_PATH))
1652 abort ();
1653 if ((p = strrchr (modname, '\\')) == NULL)
1654 abort ();
1655 *p = 0;
1657 if ((p = strrchr (modname, '\\')) && xstrcasecmp (p, "\\bin") == 0)
1659 char buf[SET_ENV_BUF_SIZE];
1661 *p = 0;
1662 for (p = modname; *p; p++)
1663 if (*p == '\\') *p = '/';
1665 _snprintf (buf, sizeof (buf)-1, "emacs_dir=%s", modname);
1666 _putenv (strdup (buf));
1668 /* Handle running emacs from the build directory: src/oo-spd/i386/ */
1670 /* FIXME: should use substring of get_emacs_configuration ().
1671 But I don't think the Windows build supports alpha, mips etc
1672 anymore, so have taken the easy option for now. */
1673 else if (p && xstrcasecmp (p, "\\i386") == 0)
1675 *p = 0;
1676 p = strrchr (modname, '\\');
1677 if (p != NULL)
1679 *p = 0;
1680 p = strrchr (modname, '\\');
1681 if (p && xstrcasecmp (p, "\\src") == 0)
1683 char buf[SET_ENV_BUF_SIZE];
1685 *p = 0;
1686 for (p = modname; *p; p++)
1687 if (*p == '\\') *p = '/';
1689 _snprintf (buf, sizeof (buf)-1, "emacs_dir=%s", modname);
1690 _putenv (strdup (buf));
1696 for (i = 0; i < N_ENV_VARS; i++)
1698 if (!getenv (env_vars[i].name))
1700 int dont_free = 0;
1702 if ((lpval = w32_get_resource (env_vars[i].name, &dwType)) == NULL
1703 /* Also ignore empty environment variables. */
1704 || *lpval == 0)
1706 xfree (lpval);
1707 lpval = env_vars[i].def_value;
1708 dwType = REG_EXPAND_SZ;
1709 dont_free = 1;
1712 if (lpval)
1714 char buf1[SET_ENV_BUF_SIZE], buf2[SET_ENV_BUF_SIZE];
1716 if (dwType == REG_EXPAND_SZ)
1717 ExpandEnvironmentStrings ((LPSTR) lpval, buf1, sizeof (buf1));
1718 else if (dwType == REG_SZ)
1719 strcpy (buf1, lpval);
1720 if (dwType == REG_EXPAND_SZ || dwType == REG_SZ)
1722 _snprintf (buf2, sizeof (buf2)-1, "%s=%s", env_vars[i].name,
1723 buf1);
1724 _putenv (strdup (buf2));
1727 if (!dont_free)
1728 xfree (lpval);
1734 /* Rebuild system configuration to reflect invoking system. */
1735 Vsystem_configuration = build_string (EMACS_CONFIGURATION);
1737 /* Another special case: on NT, the PATH variable is actually named
1738 "Path" although cmd.exe (perhaps NT itself) arranges for
1739 environment variable lookup and setting to be case insensitive.
1740 However, Emacs assumes a fully case sensitive environment, so we
1741 need to change "Path" to "PATH" to match the expectations of
1742 various elisp packages. We do this by the sneaky method of
1743 modifying the string in the C runtime environ entry.
1745 The same applies to COMSPEC. */
1747 char ** envp;
1749 for (envp = environ; *envp; envp++)
1750 if (_strnicmp (*envp, "PATH=", 5) == 0)
1751 memcpy (*envp, "PATH=", 5);
1752 else if (_strnicmp (*envp, "COMSPEC=", 8) == 0)
1753 memcpy (*envp, "COMSPEC=", 8);
1756 /* Remember the initial working directory for getwd, then make the
1757 real wd be the location of emacs.exe to avoid conflicts when
1758 renaming or deleting directories. (We also don't call chdir when
1759 running subprocesses for the same reason.) */
1760 if (!GetCurrentDirectory (MAXPATHLEN, startup_dir))
1761 abort ();
1764 char *p;
1765 static char modname[MAX_PATH];
1767 if (!GetModuleFileName (NULL, modname, MAX_PATH))
1768 abort ();
1769 if ((p = strrchr (modname, '\\')) == NULL)
1770 abort ();
1771 *p = 0;
1773 SetCurrentDirectory (modname);
1775 /* Ensure argv[0] has the full path to Emacs. */
1776 *p = '\\';
1777 argv[0] = modname;
1780 /* Determine if there is a middle mouse button, to allow parse_button
1781 to decide whether right mouse events should be mouse-2 or
1782 mouse-3. */
1783 w32_num_mouse_buttons = GetSystemMetrics (SM_CMOUSEBUTTONS);
1785 init_user_info ();
1788 char *
1789 emacs_root_dir (void)
1791 static char root_dir[FILENAME_MAX];
1792 const char *p;
1794 p = getenv ("emacs_dir");
1795 if (p == NULL)
1796 abort ();
1797 strcpy (root_dir, p);
1798 root_dir[parse_root (root_dir, NULL)] = '\0';
1799 dostounix_filename (root_dir);
1800 return root_dir;
1803 /* We don't have scripts to automatically determine the system configuration
1804 for Emacs before it's compiled, and we don't want to have to make the
1805 user enter it, so we define EMACS_CONFIGURATION to invoke this runtime
1806 routine. */
1808 char *
1809 get_emacs_configuration (void)
1811 char *arch, *oem, *os;
1812 int build_num;
1813 static char configuration_buffer[32];
1815 /* Determine the processor type. */
1816 switch (get_processor_type ())
1819 #ifdef PROCESSOR_INTEL_386
1820 case PROCESSOR_INTEL_386:
1821 case PROCESSOR_INTEL_486:
1822 case PROCESSOR_INTEL_PENTIUM:
1823 arch = "i386";
1824 break;
1825 #endif
1827 #ifdef PROCESSOR_MIPS_R2000
1828 case PROCESSOR_MIPS_R2000:
1829 case PROCESSOR_MIPS_R3000:
1830 case PROCESSOR_MIPS_R4000:
1831 arch = "mips";
1832 break;
1833 #endif
1835 #ifdef PROCESSOR_ALPHA_21064
1836 case PROCESSOR_ALPHA_21064:
1837 arch = "alpha";
1838 break;
1839 #endif
1841 default:
1842 arch = "unknown";
1843 break;
1846 /* Use the OEM field to reflect the compiler/library combination. */
1847 #ifdef _MSC_VER
1848 #define COMPILER_NAME "msvc"
1849 #else
1850 #ifdef __GNUC__
1851 #define COMPILER_NAME "mingw"
1852 #else
1853 #define COMPILER_NAME "unknown"
1854 #endif
1855 #endif
1856 oem = COMPILER_NAME;
1858 switch (osinfo_cache.dwPlatformId) {
1859 case VER_PLATFORM_WIN32_NT:
1860 os = "nt";
1861 build_num = osinfo_cache.dwBuildNumber;
1862 break;
1863 case VER_PLATFORM_WIN32_WINDOWS:
1864 if (osinfo_cache.dwMinorVersion == 0) {
1865 os = "windows95";
1866 } else {
1867 os = "windows98";
1869 build_num = LOWORD (osinfo_cache.dwBuildNumber);
1870 break;
1871 case VER_PLATFORM_WIN32s:
1872 /* Not supported, should not happen. */
1873 os = "windows32s";
1874 build_num = LOWORD (osinfo_cache.dwBuildNumber);
1875 break;
1876 default:
1877 os = "unknown";
1878 build_num = 0;
1879 break;
1882 if (osinfo_cache.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1883 sprintf (configuration_buffer, "%s-%s-%s%d.%d.%d", arch, oem, os,
1884 get_w32_major_version (), get_w32_minor_version (), build_num);
1885 } else {
1886 sprintf (configuration_buffer, "%s-%s-%s.%d", arch, oem, os, build_num);
1889 return configuration_buffer;
1892 char *
1893 get_emacs_configuration_options (void)
1895 static char *options_buffer;
1896 char cv[32]; /* Enough for COMPILER_VERSION. */
1897 char *options[] = {
1898 cv, /* To be filled later. */
1899 #ifdef EMACSDEBUG
1900 " --no-opt",
1901 #endif
1902 /* configure.bat already sets USER_CFLAGS and USER_LDFLAGS
1903 with a starting space to save work here. */
1904 #ifdef USER_CFLAGS
1905 " --cflags", USER_CFLAGS,
1906 #endif
1907 #ifdef USER_LDFLAGS
1908 " --ldflags", USER_LDFLAGS,
1909 #endif
1910 NULL
1912 size_t size = 0;
1913 int i;
1915 /* Work out the effective configure options for this build. */
1916 #ifdef _MSC_VER
1917 #define COMPILER_VERSION "--with-msvc (%d.%02d)", _MSC_VER / 100, _MSC_VER % 100
1918 #else
1919 #ifdef __GNUC__
1920 #define COMPILER_VERSION "--with-gcc (%d.%d)", __GNUC__, __GNUC_MINOR__
1921 #else
1922 #define COMPILER_VERSION ""
1923 #endif
1924 #endif
1926 if (_snprintf (cv, sizeof (cv) - 1, COMPILER_VERSION) < 0)
1927 return "Error: not enough space for compiler version";
1928 cv[sizeof (cv) - 1] = '\0';
1930 for (i = 0; options[i]; i++)
1931 size += strlen (options[i]);
1933 options_buffer = xmalloc (size + 1);
1934 options_buffer[0] = '\0';
1936 for (i = 0; options[i]; i++)
1937 strcat (options_buffer, options[i]);
1939 return options_buffer;
1943 #include <sys/timeb.h>
1945 /* Emulate gettimeofday (Ulrich Leodolter, 1/11/95). */
1946 void
1947 gettimeofday (struct timeval *tv, struct timezone *tz)
1949 struct _timeb tb;
1950 _ftime (&tb);
1952 tv->tv_sec = tb.time;
1953 tv->tv_usec = tb.millitm * 1000L;
1954 if (tz)
1956 tz->tz_minuteswest = tb.timezone; /* minutes west of Greenwich */
1957 tz->tz_dsttime = tb.dstflag; /* type of dst correction */
1961 /* ------------------------------------------------------------------------- */
1962 /* IO support and wrapper functions for W32 API. */
1963 /* ------------------------------------------------------------------------- */
1965 /* Place a wrapper around the MSVC version of ctime. It returns NULL
1966 on network directories, so we handle that case here.
1967 (Ulrich Leodolter, 1/11/95). */
1968 char *
1969 sys_ctime (const time_t *t)
1971 char *str = (char *) ctime (t);
1972 return (str ? str : "Sun Jan 01 00:00:00 1970");
1975 /* Emulate sleep...we could have done this with a define, but that
1976 would necessitate including windows.h in the files that used it.
1977 This is much easier. */
1978 void
1979 sys_sleep (int seconds)
1981 Sleep (seconds * 1000);
1984 /* Internal MSVC functions for low-level descriptor munging */
1985 extern int __cdecl _set_osfhnd (int fd, long h);
1986 extern int __cdecl _free_osfhnd (int fd);
1988 /* parallel array of private info on file handles */
1989 filedesc fd_info [ MAXDESC ];
1991 typedef struct volume_info_data {
1992 struct volume_info_data * next;
1994 /* time when info was obtained */
1995 DWORD timestamp;
1997 /* actual volume info */
1998 char * root_dir;
1999 DWORD serialnum;
2000 DWORD maxcomp;
2001 DWORD flags;
2002 char * name;
2003 char * type;
2004 } volume_info_data;
2006 /* Global referenced by various functions. */
2007 static volume_info_data volume_info;
2009 /* Vector to indicate which drives are local and fixed (for which cached
2010 data never expires). */
2011 static BOOL fixed_drives[26];
2013 /* Consider cached volume information to be stale if older than 10s,
2014 at least for non-local drives. Info for fixed drives is never stale. */
2015 #define DRIVE_INDEX( c ) ( (c) <= 'Z' ? (c) - 'A' : (c) - 'a' )
2016 #define VOLINFO_STILL_VALID( root_dir, info ) \
2017 ( ( isalpha (root_dir[0]) && \
2018 fixed_drives[ DRIVE_INDEX (root_dir[0]) ] ) \
2019 || GetTickCount () - info->timestamp < 10000 )
2021 /* Cache support functions. */
2023 /* Simple linked list with linear search is sufficient. */
2024 static volume_info_data *volume_cache = NULL;
2026 static volume_info_data *
2027 lookup_volume_info (char * root_dir)
2029 volume_info_data * info;
2031 for (info = volume_cache; info; info = info->next)
2032 if (xstrcasecmp (info->root_dir, root_dir) == 0)
2033 break;
2034 return info;
2037 static void
2038 add_volume_info (char * root_dir, volume_info_data * info)
2040 info->root_dir = xstrdup (root_dir);
2041 info->next = volume_cache;
2042 volume_cache = info;
2046 /* Wrapper for GetVolumeInformation, which uses caching to avoid
2047 performance penalty (~2ms on 486 for local drives, 7.5ms for local
2048 cdrom drive, ~5-10ms or more for remote drives on LAN). */
2049 static volume_info_data *
2050 GetCachedVolumeInformation (char * root_dir)
2052 volume_info_data * info;
2053 char default_root[ MAX_PATH ];
2055 /* NULL for root_dir means use root from current directory. */
2056 if (root_dir == NULL)
2058 if (GetCurrentDirectory (MAX_PATH, default_root) == 0)
2059 return NULL;
2060 parse_root (default_root, &root_dir);
2061 *root_dir = 0;
2062 root_dir = default_root;
2065 /* Local fixed drives can be cached permanently. Removable drives
2066 cannot be cached permanently, since the volume name and serial
2067 number (if nothing else) can change. Remote drives should be
2068 treated as if they are removable, since there is no sure way to
2069 tell whether they are or not. Also, the UNC association of drive
2070 letters mapped to remote volumes can be changed at any time (even
2071 by other processes) without notice.
2073 As a compromise, so we can benefit from caching info for remote
2074 volumes, we use a simple expiry mechanism to invalidate cache
2075 entries that are more than ten seconds old. */
2077 #if 0
2078 /* No point doing this, because WNetGetConnection is even slower than
2079 GetVolumeInformation, consistently taking ~50ms on a 486 (FWIW,
2080 GetDriveType is about the only call of this type which does not
2081 involve network access, and so is extremely quick). */
2083 /* Map drive letter to UNC if remote. */
2084 if (isalpha (root_dir[0]) && !fixed[DRIVE_INDEX (root_dir[0])])
2086 char remote_name[ 256 ];
2087 char drive[3] = { root_dir[0], ':' };
2089 if (WNetGetConnection (drive, remote_name, sizeof (remote_name))
2090 == NO_ERROR)
2091 /* do something */ ;
2093 #endif
2095 info = lookup_volume_info (root_dir);
2097 if (info == NULL || ! VOLINFO_STILL_VALID (root_dir, info))
2099 char name[ 256 ];
2100 DWORD serialnum;
2101 DWORD maxcomp;
2102 DWORD flags;
2103 char type[ 256 ];
2105 /* Info is not cached, or is stale. */
2106 if (!GetVolumeInformation (root_dir,
2107 name, sizeof (name),
2108 &serialnum,
2109 &maxcomp,
2110 &flags,
2111 type, sizeof (type)))
2112 return NULL;
2114 /* Cache the volume information for future use, overwriting existing
2115 entry if present. */
2116 if (info == NULL)
2118 info = (volume_info_data *) xmalloc (sizeof (volume_info_data));
2119 add_volume_info (root_dir, info);
2121 else
2123 xfree (info->name);
2124 xfree (info->type);
2127 info->name = xstrdup (name);
2128 info->serialnum = serialnum;
2129 info->maxcomp = maxcomp;
2130 info->flags = flags;
2131 info->type = xstrdup (type);
2132 info->timestamp = GetTickCount ();
2135 return info;
2138 /* Get information on the volume where name is held; set path pointer to
2139 start of pathname in name (past UNC header\volume header if present). */
2140 static int
2141 get_volume_info (const char * name, const char ** pPath)
2143 char temp[MAX_PATH];
2144 char *rootname = NULL; /* default to current volume */
2145 volume_info_data * info;
2147 if (name == NULL)
2148 return FALSE;
2150 /* find the root name of the volume if given */
2151 if (isalpha (name[0]) && name[1] == ':')
2153 rootname = temp;
2154 temp[0] = *name++;
2155 temp[1] = *name++;
2156 temp[2] = '\\';
2157 temp[3] = 0;
2159 else if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1]))
2161 char *str = temp;
2162 int slashes = 4;
2163 rootname = temp;
2166 if (IS_DIRECTORY_SEP (*name) && --slashes == 0)
2167 break;
2168 *str++ = *name++;
2170 while ( *name );
2172 *str++ = '\\';
2173 *str = 0;
2176 if (pPath)
2177 *pPath = name;
2179 info = GetCachedVolumeInformation (rootname);
2180 if (info != NULL)
2182 /* Set global referenced by other functions. */
2183 volume_info = *info;
2184 return TRUE;
2186 return FALSE;
2189 /* Determine if volume is FAT format (ie. only supports short 8.3
2190 names); also set path pointer to start of pathname in name. */
2191 static int
2192 is_fat_volume (const char * name, const char ** pPath)
2194 if (get_volume_info (name, pPath))
2195 return (volume_info.maxcomp == 12);
2196 return FALSE;
2199 /* Map filename to a valid 8.3 name if necessary. */
2200 const char *
2201 map_w32_filename (const char * name, const char ** pPath)
2203 static char shortname[MAX_PATH];
2204 char * str = shortname;
2205 char c;
2206 char * path;
2207 const char * save_name = name;
2209 if (strlen (name) >= MAX_PATH)
2211 /* Return a filename which will cause callers to fail. */
2212 strcpy (shortname, "?");
2213 return shortname;
2216 if (is_fat_volume (name, (const char **)&path)) /* truncate to 8.3 */
2218 register int left = 8; /* maximum number of chars in part */
2219 register int extn = 0; /* extension added? */
2220 register int dots = 2; /* maximum number of dots allowed */
2222 while (name < path)
2223 *str++ = *name++; /* skip past UNC header */
2225 while ((c = *name++))
2227 switch ( c )
2229 case '\\':
2230 case '/':
2231 *str++ = '\\';
2232 extn = 0; /* reset extension flags */
2233 dots = 2; /* max 2 dots */
2234 left = 8; /* max length 8 for main part */
2235 break;
2236 case ':':
2237 *str++ = ':';
2238 extn = 0; /* reset extension flags */
2239 dots = 2; /* max 2 dots */
2240 left = 8; /* max length 8 for main part */
2241 break;
2242 case '.':
2243 if ( dots )
2245 /* Convert path components of the form .xxx to _xxx,
2246 but leave . and .. as they are. This allows .emacs
2247 to be read as _emacs, for example. */
2249 if (! *name ||
2250 *name == '.' ||
2251 IS_DIRECTORY_SEP (*name))
2253 *str++ = '.';
2254 dots--;
2256 else
2258 *str++ = '_';
2259 left--;
2260 dots = 0;
2263 else if ( !extn )
2265 *str++ = '.';
2266 extn = 1; /* we've got an extension */
2267 left = 3; /* 3 chars in extension */
2269 else
2271 /* any embedded dots after the first are converted to _ */
2272 *str++ = '_';
2274 break;
2275 case '~':
2276 case '#': /* don't lose these, they're important */
2277 if ( ! left )
2278 str[-1] = c; /* replace last character of part */
2279 /* FALLTHRU */
2280 default:
2281 if ( left )
2283 *str++ = tolower (c); /* map to lower case (looks nicer) */
2284 left--;
2285 dots = 0; /* started a path component */
2287 break;
2290 *str = '\0';
2292 else
2294 strcpy (shortname, name);
2295 unixtodos_filename (shortname);
2298 if (pPath)
2299 *pPath = shortname + (path - save_name);
2301 return shortname;
2304 static int
2305 is_exec (const char * name)
2307 char * p = strrchr (name, '.');
2308 return
2309 (p != NULL
2310 && (xstrcasecmp (p, ".exe") == 0 ||
2311 xstrcasecmp (p, ".com") == 0 ||
2312 xstrcasecmp (p, ".bat") == 0 ||
2313 xstrcasecmp (p, ".cmd") == 0));
2316 /* Emulate the Unix directory procedures opendir, closedir,
2317 and readdir. We can't use the procedures supplied in sysdep.c,
2318 so we provide them here. */
2320 struct direct dir_static; /* simulated directory contents */
2321 static HANDLE dir_find_handle = INVALID_HANDLE_VALUE;
2322 static int dir_is_fat;
2323 static char dir_pathname[MAXPATHLEN+1];
2324 static WIN32_FIND_DATA dir_find_data;
2326 /* Support shares on a network resource as subdirectories of a read-only
2327 root directory. */
2328 static HANDLE wnet_enum_handle = INVALID_HANDLE_VALUE;
2329 static HANDLE open_unc_volume (const char *);
2330 static char *read_unc_volume (HANDLE, char *, int);
2331 static void close_unc_volume (HANDLE);
2333 DIR *
2334 opendir (char *filename)
2336 DIR *dirp;
2338 /* Opening is done by FindFirstFile. However, a read is inherent to
2339 this operation, so we defer the open until read time. */
2341 if (dir_find_handle != INVALID_HANDLE_VALUE)
2342 return NULL;
2343 if (wnet_enum_handle != INVALID_HANDLE_VALUE)
2344 return NULL;
2346 if (is_unc_volume (filename))
2348 wnet_enum_handle = open_unc_volume (filename);
2349 if (wnet_enum_handle == INVALID_HANDLE_VALUE)
2350 return NULL;
2353 if (!(dirp = (DIR *) malloc (sizeof (DIR))))
2354 return NULL;
2356 dirp->dd_fd = 0;
2357 dirp->dd_loc = 0;
2358 dirp->dd_size = 0;
2360 strncpy (dir_pathname, map_w32_filename (filename, NULL), MAXPATHLEN);
2361 dir_pathname[MAXPATHLEN] = '\0';
2362 dir_is_fat = is_fat_volume (filename, NULL);
2364 return dirp;
2367 void
2368 closedir (DIR *dirp)
2370 /* If we have a find-handle open, close it. */
2371 if (dir_find_handle != INVALID_HANDLE_VALUE)
2373 FindClose (dir_find_handle);
2374 dir_find_handle = INVALID_HANDLE_VALUE;
2376 else if (wnet_enum_handle != INVALID_HANDLE_VALUE)
2378 close_unc_volume (wnet_enum_handle);
2379 wnet_enum_handle = INVALID_HANDLE_VALUE;
2381 xfree ((char *) dirp);
2384 struct direct *
2385 readdir (DIR *dirp)
2387 int downcase = !NILP (Vw32_downcase_file_names);
2389 if (wnet_enum_handle != INVALID_HANDLE_VALUE)
2391 if (!read_unc_volume (wnet_enum_handle,
2392 dir_find_data.cFileName,
2393 MAX_PATH))
2394 return NULL;
2396 /* If we aren't dir_finding, do a find-first, otherwise do a find-next. */
2397 else if (dir_find_handle == INVALID_HANDLE_VALUE)
2399 char filename[MAXNAMLEN + 3];
2400 int ln;
2402 strcpy (filename, dir_pathname);
2403 ln = strlen (filename) - 1;
2404 if (!IS_DIRECTORY_SEP (filename[ln]))
2405 strcat (filename, "\\");
2406 strcat (filename, "*");
2408 dir_find_handle = FindFirstFile (filename, &dir_find_data);
2410 if (dir_find_handle == INVALID_HANDLE_VALUE)
2411 return NULL;
2413 else
2415 if (!FindNextFile (dir_find_handle, &dir_find_data))
2416 return NULL;
2419 /* Emacs never uses this value, so don't bother making it match
2420 value returned by stat(). */
2421 dir_static.d_ino = 1;
2423 strcpy (dir_static.d_name, dir_find_data.cFileName);
2425 /* If the file name in cFileName[] includes `?' characters, it means
2426 the original file name used characters that cannot be represented
2427 by the current ANSI codepage. To avoid total lossage, retrieve
2428 the short 8+3 alias of the long file name. */
2429 if (_mbspbrk (dir_static.d_name, "?"))
2431 strcpy (dir_static.d_name, dir_find_data.cAlternateFileName);
2432 downcase = 1; /* 8+3 aliases are returned in all caps */
2434 dir_static.d_namlen = strlen (dir_static.d_name);
2435 dir_static.d_reclen = sizeof (struct direct) - MAXNAMLEN + 3 +
2436 dir_static.d_namlen - dir_static.d_namlen % 4;
2438 /* If the file name in cFileName[] includes `?' characters, it means
2439 the original file name used characters that cannot be represented
2440 by the current ANSI codepage. To avoid total lossage, retrieve
2441 the short 8+3 alias of the long file name. */
2442 if (_mbspbrk (dir_find_data.cFileName, "?"))
2444 strcpy (dir_static.d_name, dir_find_data.cAlternateFileName);
2445 /* 8+3 aliases are returned in all caps, which could break
2446 various alists that look at filenames' extensions. */
2447 downcase = 1;
2449 else
2450 strcpy (dir_static.d_name, dir_find_data.cFileName);
2451 dir_static.d_namlen = strlen (dir_static.d_name);
2452 if (dir_is_fat)
2453 _strlwr (dir_static.d_name);
2454 else if (downcase)
2456 register char *p;
2457 for (p = dir_static.d_name; *p; p++)
2458 if (*p >= 'a' && *p <= 'z')
2459 break;
2460 if (!*p)
2461 _strlwr (dir_static.d_name);
2464 return &dir_static;
2467 static HANDLE
2468 open_unc_volume (const char *path)
2470 NETRESOURCE nr;
2471 HANDLE henum;
2472 int result;
2474 nr.dwScope = RESOURCE_GLOBALNET;
2475 nr.dwType = RESOURCETYPE_DISK;
2476 nr.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER;
2477 nr.dwUsage = RESOURCEUSAGE_CONTAINER;
2478 nr.lpLocalName = NULL;
2479 nr.lpRemoteName = (LPSTR)map_w32_filename (path, NULL);
2480 nr.lpComment = NULL;
2481 nr.lpProvider = NULL;
2483 result = WNetOpenEnum (RESOURCE_GLOBALNET, RESOURCETYPE_DISK,
2484 RESOURCEUSAGE_CONNECTABLE, &nr, &henum);
2486 if (result == NO_ERROR)
2487 return henum;
2488 else
2489 return INVALID_HANDLE_VALUE;
2492 static char *
2493 read_unc_volume (HANDLE henum, char *readbuf, int size)
2495 DWORD count;
2496 int result;
2497 DWORD bufsize = 512;
2498 char *buffer;
2499 char *ptr;
2501 count = 1;
2502 buffer = alloca (bufsize);
2503 result = WNetEnumResource (wnet_enum_handle, &count, buffer, &bufsize);
2504 if (result != NO_ERROR)
2505 return NULL;
2507 /* WNetEnumResource returns \\resource\share...skip forward to "share". */
2508 ptr = ((LPNETRESOURCE) buffer)->lpRemoteName;
2509 ptr += 2;
2510 while (*ptr && !IS_DIRECTORY_SEP (*ptr)) ptr++;
2511 ptr++;
2513 strncpy (readbuf, ptr, size);
2514 return readbuf;
2517 static void
2518 close_unc_volume (HANDLE henum)
2520 if (henum != INVALID_HANDLE_VALUE)
2521 WNetCloseEnum (henum);
2524 static DWORD
2525 unc_volume_file_attributes (const char *path)
2527 HANDLE henum;
2528 DWORD attrs;
2530 henum = open_unc_volume (path);
2531 if (henum == INVALID_HANDLE_VALUE)
2532 return -1;
2534 attrs = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_DIRECTORY;
2536 close_unc_volume (henum);
2538 return attrs;
2541 /* Ensure a network connection is authenticated. */
2542 static void
2543 logon_network_drive (const char *path)
2545 NETRESOURCE resource;
2546 char share[MAX_PATH];
2547 int i, n_slashes;
2548 char drive[4];
2549 UINT drvtype;
2551 if (IS_DIRECTORY_SEP (path[0]) && IS_DIRECTORY_SEP (path[1]))
2552 drvtype = DRIVE_REMOTE;
2553 else if (path[0] == '\0' || path[1] != ':')
2554 drvtype = GetDriveType (NULL);
2555 else
2557 drive[0] = path[0];
2558 drive[1] = ':';
2559 drive[2] = '\\';
2560 drive[3] = '\0';
2561 drvtype = GetDriveType (drive);
2564 /* Only logon to networked drives. */
2565 if (drvtype != DRIVE_REMOTE)
2566 return;
2568 n_slashes = 2;
2569 strncpy (share, path, MAX_PATH);
2570 /* Truncate to just server and share name. */
2571 for (i = 2; i < MAX_PATH; i++)
2573 if (IS_DIRECTORY_SEP (share[i]) && ++n_slashes > 3)
2575 share[i] = '\0';
2576 break;
2580 resource.dwType = RESOURCETYPE_DISK;
2581 resource.lpLocalName = NULL;
2582 resource.lpRemoteName = share;
2583 resource.lpProvider = NULL;
2585 WNetAddConnection2 (&resource, NULL, NULL, CONNECT_INTERACTIVE);
2588 /* Shadow some MSVC runtime functions to map requests for long filenames
2589 to reasonable short names if necessary. This was originally added to
2590 permit running Emacs on NT 3.1 on a FAT partition, which doesn't support
2591 long file names. */
2594 sys_access (const char * path, int mode)
2596 DWORD attributes;
2598 /* MSVC implementation doesn't recognize D_OK. */
2599 path = map_w32_filename (path, NULL);
2600 if (is_unc_volume (path))
2602 attributes = unc_volume_file_attributes (path);
2603 if (attributes == -1) {
2604 errno = EACCES;
2605 return -1;
2608 else if ((attributes = GetFileAttributes (path)) == -1)
2610 /* Should try mapping GetLastError to errno; for now just indicate
2611 that path doesn't exist. */
2612 errno = EACCES;
2613 return -1;
2615 if ((mode & X_OK) != 0 && !is_exec (path))
2617 errno = EACCES;
2618 return -1;
2620 if ((mode & W_OK) != 0 && (attributes & FILE_ATTRIBUTE_READONLY) != 0)
2622 errno = EACCES;
2623 return -1;
2625 if ((mode & D_OK) != 0 && (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
2627 errno = EACCES;
2628 return -1;
2630 return 0;
2634 sys_chdir (const char * path)
2636 return _chdir (map_w32_filename (path, NULL));
2640 sys_chmod (const char * path, int mode)
2642 return _chmod (map_w32_filename (path, NULL), mode);
2646 sys_chown (const char *path, uid_t owner, gid_t group)
2648 if (sys_chmod (path, S_IREAD) == -1) /* check if file exists */
2649 return -1;
2650 return 0;
2654 sys_creat (const char * path, int mode)
2656 return _creat (map_w32_filename (path, NULL), mode);
2659 FILE *
2660 sys_fopen (const char * path, const char * mode)
2662 int fd;
2663 int oflag;
2664 const char * mode_save = mode;
2666 /* Force all file handles to be non-inheritable. This is necessary to
2667 ensure child processes don't unwittingly inherit handles that might
2668 prevent future file access. */
2670 if (mode[0] == 'r')
2671 oflag = O_RDONLY;
2672 else if (mode[0] == 'w' || mode[0] == 'a')
2673 oflag = O_WRONLY | O_CREAT | O_TRUNC;
2674 else
2675 return NULL;
2677 /* Only do simplistic option parsing. */
2678 while (*++mode)
2679 if (mode[0] == '+')
2681 oflag &= ~(O_RDONLY | O_WRONLY);
2682 oflag |= O_RDWR;
2684 else if (mode[0] == 'b')
2686 oflag &= ~O_TEXT;
2687 oflag |= O_BINARY;
2689 else if (mode[0] == 't')
2691 oflag &= ~O_BINARY;
2692 oflag |= O_TEXT;
2694 else break;
2696 fd = _open (map_w32_filename (path, NULL), oflag | _O_NOINHERIT, 0644);
2697 if (fd < 0)
2698 return NULL;
2700 return _fdopen (fd, mode_save);
2703 /* This only works on NTFS volumes, but is useful to have. */
2705 sys_link (const char * old, const char * new)
2707 HANDLE fileh;
2708 int result = -1;
2709 char oldname[MAX_PATH], newname[MAX_PATH];
2711 if (old == NULL || new == NULL)
2713 errno = ENOENT;
2714 return -1;
2717 strcpy (oldname, map_w32_filename (old, NULL));
2718 strcpy (newname, map_w32_filename (new, NULL));
2720 fileh = CreateFile (oldname, 0, 0, NULL, OPEN_EXISTING,
2721 FILE_FLAG_BACKUP_SEMANTICS, NULL);
2722 if (fileh != INVALID_HANDLE_VALUE)
2724 int wlen;
2726 /* Confusingly, the "alternate" stream name field does not apply
2727 when restoring a hard link, and instead contains the actual
2728 stream data for the link (ie. the name of the link to create).
2729 The WIN32_STREAM_ID structure before the cStreamName field is
2730 the stream header, which is then immediately followed by the
2731 stream data. */
2733 struct {
2734 WIN32_STREAM_ID wid;
2735 WCHAR wbuffer[MAX_PATH]; /* extra space for link name */
2736 } data;
2738 wlen = MultiByteToWideChar (CP_ACP, MB_PRECOMPOSED, newname, -1,
2739 data.wid.cStreamName, MAX_PATH);
2740 if (wlen > 0)
2742 LPVOID context = NULL;
2743 DWORD wbytes = 0;
2745 data.wid.dwStreamId = BACKUP_LINK;
2746 data.wid.dwStreamAttributes = 0;
2747 data.wid.Size.LowPart = wlen * sizeof (WCHAR);
2748 data.wid.Size.HighPart = 0;
2749 data.wid.dwStreamNameSize = 0;
2751 if (BackupWrite (fileh, (LPBYTE)&data,
2752 offsetof (WIN32_STREAM_ID, cStreamName)
2753 + data.wid.Size.LowPart,
2754 &wbytes, FALSE, FALSE, &context)
2755 && BackupWrite (fileh, NULL, 0, &wbytes, TRUE, FALSE, &context))
2757 /* succeeded */
2758 result = 0;
2760 else
2762 /* Should try mapping GetLastError to errno; for now just
2763 indicate a general error (eg. links not supported). */
2764 errno = EINVAL; // perhaps EMLINK?
2768 CloseHandle (fileh);
2770 else
2771 errno = ENOENT;
2773 return result;
2777 sys_mkdir (const char * path)
2779 return _mkdir (map_w32_filename (path, NULL));
2782 /* Because of long name mapping issues, we need to implement this
2783 ourselves. Also, MSVC's _mktemp returns NULL when it can't generate
2784 a unique name, instead of setting the input template to an empty
2785 string.
2787 Standard algorithm seems to be use pid or tid with a letter on the
2788 front (in place of the 6 X's) and cycle through the letters to find a
2789 unique name. We extend that to allow any reasonable character as the
2790 first of the 6 X's. */
2791 char *
2792 sys_mktemp (char * template)
2794 char * p;
2795 int i;
2796 unsigned uid = GetCurrentThreadId ();
2797 static char first_char[] = "abcdefghijklmnopqrstuvwyz0123456789!%-_@#";
2799 if (template == NULL)
2800 return NULL;
2801 p = template + strlen (template);
2802 i = 5;
2803 /* replace up to the last 5 X's with uid in decimal */
2804 while (--p >= template && p[0] == 'X' && --i >= 0)
2806 p[0] = '0' + uid % 10;
2807 uid /= 10;
2810 if (i < 0 && p[0] == 'X')
2812 i = 0;
2815 int save_errno = errno;
2816 p[0] = first_char[i];
2817 if (sys_access (template, 0) < 0)
2819 errno = save_errno;
2820 return template;
2823 while (++i < sizeof (first_char));
2826 /* Template is badly formed or else we can't generate a unique name,
2827 so return empty string */
2828 template[0] = 0;
2829 return template;
2833 sys_open (const char * path, int oflag, int mode)
2835 const char* mpath = map_w32_filename (path, NULL);
2836 /* Try to open file without _O_CREAT, to be able to write to hidden
2837 and system files. Force all file handles to be
2838 non-inheritable. */
2839 int res = _open (mpath, (oflag & ~_O_CREAT) | _O_NOINHERIT, mode);
2840 if (res >= 0)
2841 return res;
2842 return _open (mpath, oflag | _O_NOINHERIT, mode);
2846 sys_rename (const char * oldname, const char * newname)
2848 BOOL result;
2849 char temp[MAX_PATH];
2851 /* MoveFile on Windows 95 doesn't correctly change the short file name
2852 alias in a number of circumstances (it is not easy to predict when
2853 just by looking at oldname and newname, unfortunately). In these
2854 cases, renaming through a temporary name avoids the problem.
2856 A second problem on Windows 95 is that renaming through a temp name when
2857 newname is uppercase fails (the final long name ends up in
2858 lowercase, although the short alias might be uppercase) UNLESS the
2859 long temp name is not 8.3.
2861 So, on Windows 95 we always rename through a temp name, and we make sure
2862 the temp name has a long extension to ensure correct renaming. */
2864 strcpy (temp, map_w32_filename (oldname, NULL));
2866 if (os_subtype == OS_WIN95)
2868 char * o;
2869 char * p;
2870 int i = 0;
2872 oldname = map_w32_filename (oldname, NULL);
2873 if (o = strrchr (oldname, '\\'))
2874 o++;
2875 else
2876 o = (char *) oldname;
2878 if (p = strrchr (temp, '\\'))
2879 p++;
2880 else
2881 p = temp;
2885 /* Force temp name to require a manufactured 8.3 alias - this
2886 seems to make the second rename work properly. */
2887 sprintf (p, "_.%s.%u", o, i);
2888 i++;
2889 result = rename (oldname, temp);
2891 /* This loop must surely terminate! */
2892 while (result < 0 && errno == EEXIST);
2893 if (result < 0)
2894 return -1;
2897 /* Emulate Unix behavior - newname is deleted if it already exists
2898 (at least if it is a file; don't do this for directories).
2900 Since we mustn't do this if we are just changing the case of the
2901 file name (we would end up deleting the file we are trying to
2902 rename!), we let rename detect if the destination file already
2903 exists - that way we avoid the possible pitfalls of trying to
2904 determine ourselves whether two names really refer to the same
2905 file, which is not always possible in the general case. (Consider
2906 all the permutations of shared or subst'd drives, etc.) */
2908 newname = map_w32_filename (newname, NULL);
2909 result = rename (temp, newname);
2911 if (result < 0
2912 && errno == EEXIST
2913 && _chmod (newname, 0666) == 0
2914 && _unlink (newname) == 0)
2915 result = rename (temp, newname);
2917 return result;
2921 sys_rmdir (const char * path)
2923 return _rmdir (map_w32_filename (path, NULL));
2927 sys_unlink (const char * path)
2929 path = map_w32_filename (path, NULL);
2931 /* On Unix, unlink works without write permission. */
2932 _chmod (path, 0666);
2933 return _unlink (path);
2936 static FILETIME utc_base_ft;
2937 static ULONGLONG utc_base; /* In 100ns units */
2938 static int init = 0;
2940 #define FILETIME_TO_U64(result, ft) \
2941 do { \
2942 ULARGE_INTEGER uiTemp; \
2943 uiTemp.LowPart = (ft).dwLowDateTime; \
2944 uiTemp.HighPart = (ft).dwHighDateTime; \
2945 result = uiTemp.QuadPart; \
2946 } while (0)
2948 static void
2949 initialize_utc_base (void)
2951 /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */
2952 SYSTEMTIME st;
2954 st.wYear = 1970;
2955 st.wMonth = 1;
2956 st.wDay = 1;
2957 st.wHour = 0;
2958 st.wMinute = 0;
2959 st.wSecond = 0;
2960 st.wMilliseconds = 0;
2962 SystemTimeToFileTime (&st, &utc_base_ft);
2963 FILETIME_TO_U64 (utc_base, utc_base_ft);
2966 static time_t
2967 convert_time (FILETIME ft)
2969 ULONGLONG tmp;
2971 if (!init)
2973 initialize_utc_base();
2974 init = 1;
2977 if (CompareFileTime (&ft, &utc_base_ft) < 0)
2978 return 0;
2980 FILETIME_TO_U64 (tmp, ft);
2981 return (time_t) ((tmp - utc_base) / 10000000L);
2984 static void
2985 convert_from_time_t (time_t time, FILETIME * pft)
2987 ULARGE_INTEGER tmp;
2989 if (!init)
2991 initialize_utc_base ();
2992 init = 1;
2995 /* time in 100ns units since 1-Jan-1601 */
2996 tmp.QuadPart = (ULONGLONG) time * 10000000L + utc_base;
2997 pft->dwHighDateTime = tmp.HighPart;
2998 pft->dwLowDateTime = tmp.LowPart;
3001 #if 0
3002 /* No reason to keep this; faking inode values either by hashing or even
3003 using the file index from GetInformationByHandle, is not perfect and
3004 so by default Emacs doesn't use the inode values on Windows.
3005 Instead, we now determine file-truename correctly (except for
3006 possible drive aliasing etc). */
3008 /* Modified version of "PJW" algorithm (see the "Dragon" compiler book). */
3009 static unsigned
3010 hashval (const unsigned char * str)
3012 unsigned h = 0;
3013 while (*str)
3015 h = (h << 4) + *str++;
3016 h ^= (h >> 28);
3018 return h;
3021 /* Return the hash value of the canonical pathname, excluding the
3022 drive/UNC header, to get a hopefully unique inode number. */
3023 static DWORD
3024 generate_inode_val (const char * name)
3026 char fullname[ MAX_PATH ];
3027 char * p;
3028 unsigned hash;
3030 /* Get the truly canonical filename, if it exists. (Note: this
3031 doesn't resolve aliasing due to subst commands, or recognise hard
3032 links. */
3033 if (!w32_get_long_filename ((char *)name, fullname, MAX_PATH))
3034 abort ();
3036 parse_root (fullname, &p);
3037 /* Normal W32 filesystems are still case insensitive. */
3038 _strlwr (p);
3039 return hashval (p);
3042 #endif
3044 static PSECURITY_DESCRIPTOR
3045 get_file_security_desc (const char *fname)
3047 PSECURITY_DESCRIPTOR psd = NULL;
3048 DWORD sd_len, err;
3049 SECURITY_INFORMATION si = OWNER_SECURITY_INFORMATION
3050 | GROUP_SECURITY_INFORMATION /* | DACL_SECURITY_INFORMATION */ ;
3052 if (!get_file_security (fname, si, psd, 0, &sd_len))
3054 err = GetLastError ();
3055 if (err != ERROR_INSUFFICIENT_BUFFER)
3056 return NULL;
3059 psd = xmalloc (sd_len);
3060 if (!get_file_security (fname, si, psd, sd_len, &sd_len))
3062 xfree (psd);
3063 return NULL;
3066 return psd;
3069 static DWORD
3070 get_rid (PSID sid)
3072 unsigned n_subauthorities;
3074 /* Use the last sub-authority value of the RID, the relative
3075 portion of the SID, as user/group ID. */
3076 n_subauthorities = *get_sid_sub_authority_count (sid);
3077 if (n_subauthorities < 1)
3078 return 0; /* the "World" RID */
3079 return *get_sid_sub_authority (sid, n_subauthorities - 1);
3082 /* Caching SID and account values for faster lokup. */
3084 #ifdef __GNUC__
3085 # define FLEXIBLE_ARRAY_MEMBER
3086 #else
3087 # define FLEXIBLE_ARRAY_MEMBER 1
3088 #endif
3090 struct w32_id {
3091 unsigned rid;
3092 struct w32_id *next;
3093 char name[GNLEN+1];
3094 unsigned char sid[FLEXIBLE_ARRAY_MEMBER];
3097 static struct w32_id *w32_idlist;
3099 static int
3100 w32_cached_id (PSID sid, unsigned *id, char *name)
3102 struct w32_id *tail, *found;
3104 for (found = NULL, tail = w32_idlist; tail; tail = tail->next)
3106 if (equal_sid ((PSID)tail->sid, sid))
3108 found = tail;
3109 break;
3112 if (found)
3114 *id = found->rid;
3115 strcpy (name, found->name);
3116 return 1;
3118 else
3119 return 0;
3122 static void
3123 w32_add_to_cache (PSID sid, unsigned id, char *name)
3125 DWORD sid_len;
3126 struct w32_id *new_entry;
3128 /* We don't want to leave behind stale cache from when Emacs was
3129 dumped. */
3130 if (initialized)
3132 sid_len = get_length_sid (sid);
3133 new_entry = xmalloc (offsetof (struct w32_id, sid) + sid_len);
3134 if (new_entry)
3136 new_entry->rid = id;
3137 strcpy (new_entry->name, name);
3138 copy_sid (sid_len, (PSID)new_entry->sid, sid);
3139 new_entry->next = w32_idlist;
3140 w32_idlist = new_entry;
3145 #define UID 1
3146 #define GID 2
3148 static int
3149 get_name_and_id (PSECURITY_DESCRIPTOR psd, const char *fname,
3150 unsigned *id, char *nm, int what)
3152 PSID sid = NULL;
3153 char machine[MAX_COMPUTERNAME_LENGTH+1];
3154 BOOL dflt;
3155 SID_NAME_USE ignore;
3156 char name[UNLEN+1];
3157 DWORD name_len = sizeof (name);
3158 char domain[1024];
3159 DWORD domain_len = sizeof (domain);
3160 char *mp = NULL;
3161 int use_dflt = 0;
3162 int result;
3164 if (what == UID)
3165 result = get_security_descriptor_owner (psd, &sid, &dflt);
3166 else if (what == GID)
3167 result = get_security_descriptor_group (psd, &sid, &dflt);
3168 else
3169 result = 0;
3171 if (!result || !is_valid_sid (sid))
3172 use_dflt = 1;
3173 else if (!w32_cached_id (sid, id, nm))
3175 /* If FNAME is a UNC, we need to lookup account on the
3176 specified machine. */
3177 if (IS_DIRECTORY_SEP (fname[0]) && IS_DIRECTORY_SEP (fname[1])
3178 && fname[2] != '\0')
3180 const char *s;
3181 char *p;
3183 for (s = fname + 2, p = machine;
3184 *s && !IS_DIRECTORY_SEP (*s); s++, p++)
3185 *p = *s;
3186 *p = '\0';
3187 mp = machine;
3190 if (!lookup_account_sid (mp, sid, name, &name_len,
3191 domain, &domain_len, &ignore)
3192 || name_len > UNLEN+1)
3193 use_dflt = 1;
3194 else
3196 *id = get_rid (sid);
3197 strcpy (nm, name);
3198 w32_add_to_cache (sid, *id, name);
3201 return use_dflt;
3204 static void
3205 get_file_owner_and_group (PSECURITY_DESCRIPTOR psd,
3206 const char *fname,
3207 struct stat *st)
3209 int dflt_usr = 0, dflt_grp = 0;
3211 if (!psd)
3213 dflt_usr = 1;
3214 dflt_grp = 1;
3216 else
3218 if (get_name_and_id (psd, fname, &st->st_uid, st->st_uname, UID))
3219 dflt_usr = 1;
3220 if (get_name_and_id (psd, fname, &st->st_gid, st->st_gname, GID))
3221 dflt_grp = 1;
3223 /* Consider files to belong to current user/group, if we cannot get
3224 more accurate information. */
3225 if (dflt_usr)
3227 st->st_uid = dflt_passwd.pw_uid;
3228 strcpy (st->st_uname, dflt_passwd.pw_name);
3230 if (dflt_grp)
3232 st->st_gid = dflt_passwd.pw_gid;
3233 strcpy (st->st_gname, dflt_group.gr_name);
3237 /* Return non-zero if NAME is a potentially slow filesystem. */
3239 is_slow_fs (const char *name)
3241 char drive_root[4];
3242 UINT devtype;
3244 if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1]))
3245 devtype = DRIVE_REMOTE; /* assume UNC name is remote */
3246 else if (!(strlen (name) >= 2 && IS_DEVICE_SEP (name[1])))
3247 devtype = GetDriveType (NULL); /* use root of current drive */
3248 else
3250 /* GetDriveType needs the root directory of the drive. */
3251 strncpy (drive_root, name, 2);
3252 drive_root[2] = '\\';
3253 drive_root[3] = '\0';
3254 devtype = GetDriveType (drive_root);
3256 return !(devtype == DRIVE_FIXED || devtype == DRIVE_RAMDISK);
3259 /* MSVC stat function can't cope with UNC names and has other bugs, so
3260 replace it with our own. This also allows us to calculate consistent
3261 inode values without hacks in the main Emacs code. */
3263 stat (const char * path, struct stat * buf)
3265 char *name, *r;
3266 char drive_root[4];
3267 UINT devtype;
3268 WIN32_FIND_DATA wfd;
3269 HANDLE fh;
3270 unsigned __int64 fake_inode;
3271 int permission;
3272 int len;
3273 int rootdir = FALSE;
3274 PSECURITY_DESCRIPTOR psd = NULL;
3276 if (path == NULL || buf == NULL)
3278 errno = EFAULT;
3279 return -1;
3282 name = (char *) map_w32_filename (path, &path);
3283 /* Must be valid filename, no wild cards or other invalid
3284 characters. We use _mbspbrk to support multibyte strings that
3285 might look to strpbrk as if they included literal *, ?, and other
3286 characters mentioned below that are disallowed by Windows
3287 filesystems. */
3288 if (_mbspbrk (name, "*?|<>\""))
3290 errno = ENOENT;
3291 return -1;
3294 /* If name is "c:/.." or "/.." then stat "c:/" or "/". */
3295 r = IS_DEVICE_SEP (name[1]) ? &name[2] : name;
3296 if (IS_DIRECTORY_SEP (r[0]) && r[1] == '.' && r[2] == '.' && r[3] == '\0')
3298 r[1] = r[2] = '\0';
3301 /* Remove trailing directory separator, unless name is the root
3302 directory of a drive or UNC volume in which case ensure there
3303 is a trailing separator. */
3304 len = strlen (name);
3305 rootdir = (path >= name + len - 1
3306 && (IS_DIRECTORY_SEP (*path) || *path == 0));
3307 name = strcpy (alloca (len + 2), name);
3309 if (is_unc_volume (name))
3311 DWORD attrs = unc_volume_file_attributes (name);
3313 if (attrs == -1)
3314 return -1;
3316 memset (&wfd, 0, sizeof (wfd));
3317 wfd.dwFileAttributes = attrs;
3318 wfd.ftCreationTime = utc_base_ft;
3319 wfd.ftLastAccessTime = utc_base_ft;
3320 wfd.ftLastWriteTime = utc_base_ft;
3321 strcpy (wfd.cFileName, name);
3323 else if (rootdir)
3325 if (!IS_DIRECTORY_SEP (name[len-1]))
3326 strcat (name, "\\");
3327 if (GetDriveType (name) < 2)
3329 errno = ENOENT;
3330 return -1;
3332 memset (&wfd, 0, sizeof (wfd));
3333 wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
3334 wfd.ftCreationTime = utc_base_ft;
3335 wfd.ftLastAccessTime = utc_base_ft;
3336 wfd.ftLastWriteTime = utc_base_ft;
3337 strcpy (wfd.cFileName, name);
3339 else
3341 if (IS_DIRECTORY_SEP (name[len-1]))
3342 name[len - 1] = 0;
3344 /* (This is hacky, but helps when doing file completions on
3345 network drives.) Optimize by using information available from
3346 active readdir if possible. */
3347 len = strlen (dir_pathname);
3348 if (IS_DIRECTORY_SEP (dir_pathname[len-1]))
3349 len--;
3350 if (dir_find_handle != INVALID_HANDLE_VALUE
3351 && strnicmp (name, dir_pathname, len) == 0
3352 && IS_DIRECTORY_SEP (name[len])
3353 && xstrcasecmp (name + len + 1, dir_static.d_name) == 0)
3355 /* This was the last entry returned by readdir. */
3356 wfd = dir_find_data;
3358 else
3360 logon_network_drive (name);
3362 fh = FindFirstFile (name, &wfd);
3363 if (fh == INVALID_HANDLE_VALUE)
3365 errno = ENOENT;
3366 return -1;
3368 FindClose (fh);
3372 if (!(NILP (Vw32_get_true_file_attributes)
3373 || (EQ (Vw32_get_true_file_attributes, Qlocal) && is_slow_fs (name)))
3374 /* No access rights required to get info. */
3375 && (fh = CreateFile (name, 0, 0, NULL, OPEN_EXISTING,
3376 FILE_FLAG_BACKUP_SEMANTICS, NULL))
3377 != INVALID_HANDLE_VALUE)
3379 /* This is more accurate in terms of gettting the correct number
3380 of links, but is quite slow (it is noticeable when Emacs is
3381 making a list of file name completions). */
3382 BY_HANDLE_FILE_INFORMATION info;
3384 if (GetFileInformationByHandle (fh, &info))
3386 buf->st_nlink = info.nNumberOfLinks;
3387 /* Might as well use file index to fake inode values, but this
3388 is not guaranteed to be unique unless we keep a handle open
3389 all the time (even then there are situations where it is
3390 not unique). Reputedly, there are at most 48 bits of info
3391 (on NTFS, presumably less on FAT). */
3392 fake_inode = info.nFileIndexHigh;
3393 fake_inode <<= 32;
3394 fake_inode += info.nFileIndexLow;
3396 else
3398 buf->st_nlink = 1;
3399 fake_inode = 0;
3402 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
3404 buf->st_mode = S_IFDIR;
3406 else
3408 switch (GetFileType (fh))
3410 case FILE_TYPE_DISK:
3411 buf->st_mode = S_IFREG;
3412 break;
3413 case FILE_TYPE_PIPE:
3414 buf->st_mode = S_IFIFO;
3415 break;
3416 case FILE_TYPE_CHAR:
3417 case FILE_TYPE_UNKNOWN:
3418 default:
3419 buf->st_mode = S_IFCHR;
3422 CloseHandle (fh);
3423 psd = get_file_security_desc (name);
3424 get_file_owner_and_group (psd, name, buf);
3426 else
3428 /* Don't bother to make this information more accurate. */
3429 buf->st_mode = (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ?
3430 S_IFDIR : S_IFREG;
3431 buf->st_nlink = 1;
3432 fake_inode = 0;
3434 get_file_owner_and_group (NULL, name, buf);
3436 xfree (psd);
3438 #if 0
3439 /* Not sure if there is any point in this. */
3440 if (!NILP (Vw32_generate_fake_inodes))
3441 fake_inode = generate_inode_val (name);
3442 else if (fake_inode == 0)
3444 /* For want of something better, try to make everything unique. */
3445 static DWORD gen_num = 0;
3446 fake_inode = ++gen_num;
3448 #endif
3450 /* MSVC defines _ino_t to be short; other libc's might not. */
3451 if (sizeof (buf->st_ino) == 2)
3452 buf->st_ino = fake_inode ^ (fake_inode >> 16);
3453 else
3454 buf->st_ino = fake_inode;
3456 /* volume_info is set indirectly by map_w32_filename */
3457 buf->st_dev = volume_info.serialnum;
3458 buf->st_rdev = volume_info.serialnum;
3460 buf->st_size = wfd.nFileSizeHigh;
3461 buf->st_size <<= 32;
3462 buf->st_size += wfd.nFileSizeLow;
3464 /* Convert timestamps to Unix format. */
3465 buf->st_mtime = convert_time (wfd.ftLastWriteTime);
3466 buf->st_atime = convert_time (wfd.ftLastAccessTime);
3467 if (buf->st_atime == 0) buf->st_atime = buf->st_mtime;
3468 buf->st_ctime = convert_time (wfd.ftCreationTime);
3469 if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime;
3471 /* determine rwx permissions */
3472 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
3473 permission = S_IREAD;
3474 else
3475 permission = S_IREAD | S_IWRITE;
3477 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
3478 permission |= S_IEXEC;
3479 else if (is_exec (name))
3480 permission |= S_IEXEC;
3482 buf->st_mode |= permission | (permission >> 3) | (permission >> 6);
3484 return 0;
3487 /* Provide fstat and utime as well as stat for consistent handling of
3488 file timestamps. */
3490 fstat (int desc, struct stat * buf)
3492 HANDLE fh = (HANDLE) _get_osfhandle (desc);
3493 BY_HANDLE_FILE_INFORMATION info;
3494 unsigned __int64 fake_inode;
3495 int permission;
3497 switch (GetFileType (fh) & ~FILE_TYPE_REMOTE)
3499 case FILE_TYPE_DISK:
3500 buf->st_mode = S_IFREG;
3501 if (!GetFileInformationByHandle (fh, &info))
3503 errno = EACCES;
3504 return -1;
3506 break;
3507 case FILE_TYPE_PIPE:
3508 buf->st_mode = S_IFIFO;
3509 goto non_disk;
3510 case FILE_TYPE_CHAR:
3511 case FILE_TYPE_UNKNOWN:
3512 default:
3513 buf->st_mode = S_IFCHR;
3514 non_disk:
3515 memset (&info, 0, sizeof (info));
3516 info.dwFileAttributes = 0;
3517 info.ftCreationTime = utc_base_ft;
3518 info.ftLastAccessTime = utc_base_ft;
3519 info.ftLastWriteTime = utc_base_ft;
3522 if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
3523 buf->st_mode = S_IFDIR;
3525 buf->st_nlink = info.nNumberOfLinks;
3526 /* Might as well use file index to fake inode values, but this
3527 is not guaranteed to be unique unless we keep a handle open
3528 all the time (even then there are situations where it is
3529 not unique). Reputedly, there are at most 48 bits of info
3530 (on NTFS, presumably less on FAT). */
3531 fake_inode = info.nFileIndexHigh;
3532 fake_inode <<= 32;
3533 fake_inode += info.nFileIndexLow;
3535 /* MSVC defines _ino_t to be short; other libc's might not. */
3536 if (sizeof (buf->st_ino) == 2)
3537 buf->st_ino = fake_inode ^ (fake_inode >> 16);
3538 else
3539 buf->st_ino = fake_inode;
3541 /* Consider files to belong to current user.
3542 FIXME: this should use GetSecurityInfo API, but it is only
3543 available for _WIN32_WINNT >= 0x501. */
3544 buf->st_uid = dflt_passwd.pw_uid;
3545 buf->st_gid = dflt_passwd.pw_gid;
3546 strcpy (buf->st_uname, dflt_passwd.pw_name);
3547 strcpy (buf->st_gname, dflt_group.gr_name);
3549 buf->st_dev = info.dwVolumeSerialNumber;
3550 buf->st_rdev = info.dwVolumeSerialNumber;
3552 buf->st_size = info.nFileSizeHigh;
3553 buf->st_size <<= 32;
3554 buf->st_size += info.nFileSizeLow;
3556 /* Convert timestamps to Unix format. */
3557 buf->st_mtime = convert_time (info.ftLastWriteTime);
3558 buf->st_atime = convert_time (info.ftLastAccessTime);
3559 if (buf->st_atime == 0) buf->st_atime = buf->st_mtime;
3560 buf->st_ctime = convert_time (info.ftCreationTime);
3561 if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime;
3563 /* determine rwx permissions */
3564 if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
3565 permission = S_IREAD;
3566 else
3567 permission = S_IREAD | S_IWRITE;
3569 if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
3570 permission |= S_IEXEC;
3571 else
3573 #if 0 /* no way of knowing the filename */
3574 char * p = strrchr (name, '.');
3575 if (p != NULL &&
3576 (xstrcasecmp (p, ".exe") == 0 ||
3577 xstrcasecmp (p, ".com") == 0 ||
3578 xstrcasecmp (p, ".bat") == 0 ||
3579 xstrcasecmp (p, ".cmd") == 0))
3580 permission |= S_IEXEC;
3581 #endif
3584 buf->st_mode |= permission | (permission >> 3) | (permission >> 6);
3586 return 0;
3590 utime (const char *name, struct utimbuf *times)
3592 struct utimbuf deftime;
3593 HANDLE fh;
3594 FILETIME mtime;
3595 FILETIME atime;
3597 if (times == NULL)
3599 deftime.modtime = deftime.actime = time (NULL);
3600 times = &deftime;
3603 /* Need write access to set times. */
3604 fh = CreateFile (name, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
3605 0, OPEN_EXISTING, 0, NULL);
3606 if (fh)
3608 convert_from_time_t (times->actime, &atime);
3609 convert_from_time_t (times->modtime, &mtime);
3610 if (!SetFileTime (fh, NULL, &atime, &mtime))
3612 CloseHandle (fh);
3613 errno = EACCES;
3614 return -1;
3616 CloseHandle (fh);
3618 else
3620 errno = EINVAL;
3621 return -1;
3623 return 0;
3627 /* Support for browsing other processes and their attributes. See
3628 process.c for the Lisp bindings. */
3630 /* Helper wrapper functions. */
3632 static HANDLE WINAPI
3633 create_toolhelp32_snapshot (DWORD Flags, DWORD Ignored)
3635 static CreateToolhelp32Snapshot_Proc s_pfn_Create_Toolhelp32_Snapshot = NULL;
3637 if (g_b_init_create_toolhelp32_snapshot == 0)
3639 g_b_init_create_toolhelp32_snapshot = 1;
3640 s_pfn_Create_Toolhelp32_Snapshot = (CreateToolhelp32Snapshot_Proc)
3641 GetProcAddress (GetModuleHandle ("kernel32.dll"),
3642 "CreateToolhelp32Snapshot");
3644 if (s_pfn_Create_Toolhelp32_Snapshot == NULL)
3646 return INVALID_HANDLE_VALUE;
3648 return (s_pfn_Create_Toolhelp32_Snapshot (Flags, Ignored));
3651 static BOOL WINAPI
3652 process32_first (HANDLE hSnapshot, LPPROCESSENTRY32 lppe)
3654 static Process32First_Proc s_pfn_Process32_First = NULL;
3656 if (g_b_init_process32_first == 0)
3658 g_b_init_process32_first = 1;
3659 s_pfn_Process32_First = (Process32First_Proc)
3660 GetProcAddress (GetModuleHandle ("kernel32.dll"),
3661 "Process32First");
3663 if (s_pfn_Process32_First == NULL)
3665 return FALSE;
3667 return (s_pfn_Process32_First (hSnapshot, lppe));
3670 static BOOL WINAPI
3671 process32_next (HANDLE hSnapshot, LPPROCESSENTRY32 lppe)
3673 static Process32Next_Proc s_pfn_Process32_Next = NULL;
3675 if (g_b_init_process32_next == 0)
3677 g_b_init_process32_next = 1;
3678 s_pfn_Process32_Next = (Process32Next_Proc)
3679 GetProcAddress (GetModuleHandle ("kernel32.dll"),
3680 "Process32Next");
3682 if (s_pfn_Process32_Next == NULL)
3684 return FALSE;
3686 return (s_pfn_Process32_Next (hSnapshot, lppe));
3689 static BOOL WINAPI
3690 open_thread_token (HANDLE ThreadHandle,
3691 DWORD DesiredAccess,
3692 BOOL OpenAsSelf,
3693 PHANDLE TokenHandle)
3695 static OpenThreadToken_Proc s_pfn_Open_Thread_Token = NULL;
3696 HMODULE hm_advapi32 = NULL;
3697 if (is_windows_9x () == TRUE)
3699 SetLastError (ERROR_NOT_SUPPORTED);
3700 return FALSE;
3702 if (g_b_init_open_thread_token == 0)
3704 g_b_init_open_thread_token = 1;
3705 hm_advapi32 = LoadLibrary ("Advapi32.dll");
3706 s_pfn_Open_Thread_Token =
3707 (OpenThreadToken_Proc) GetProcAddress (hm_advapi32, "OpenThreadToken");
3709 if (s_pfn_Open_Thread_Token == NULL)
3711 SetLastError (ERROR_NOT_SUPPORTED);
3712 return FALSE;
3714 return (
3715 s_pfn_Open_Thread_Token (
3716 ThreadHandle,
3717 DesiredAccess,
3718 OpenAsSelf,
3719 TokenHandle)
3723 static BOOL WINAPI
3724 impersonate_self (SECURITY_IMPERSONATION_LEVEL ImpersonationLevel)
3726 static ImpersonateSelf_Proc s_pfn_Impersonate_Self = NULL;
3727 HMODULE hm_advapi32 = NULL;
3728 if (is_windows_9x () == TRUE)
3730 return FALSE;
3732 if (g_b_init_impersonate_self == 0)
3734 g_b_init_impersonate_self = 1;
3735 hm_advapi32 = LoadLibrary ("Advapi32.dll");
3736 s_pfn_Impersonate_Self =
3737 (ImpersonateSelf_Proc) GetProcAddress (hm_advapi32, "ImpersonateSelf");
3739 if (s_pfn_Impersonate_Self == NULL)
3741 return FALSE;
3743 return s_pfn_Impersonate_Self (ImpersonationLevel);
3746 static BOOL WINAPI
3747 revert_to_self (void)
3749 static RevertToSelf_Proc s_pfn_Revert_To_Self = NULL;
3750 HMODULE hm_advapi32 = NULL;
3751 if (is_windows_9x () == TRUE)
3753 return FALSE;
3755 if (g_b_init_revert_to_self == 0)
3757 g_b_init_revert_to_self = 1;
3758 hm_advapi32 = LoadLibrary ("Advapi32.dll");
3759 s_pfn_Revert_To_Self =
3760 (RevertToSelf_Proc) GetProcAddress (hm_advapi32, "RevertToSelf");
3762 if (s_pfn_Revert_To_Self == NULL)
3764 return FALSE;
3766 return s_pfn_Revert_To_Self ();
3769 static BOOL WINAPI
3770 get_process_memory_info (HANDLE h_proc,
3771 PPROCESS_MEMORY_COUNTERS mem_counters,
3772 DWORD bufsize)
3774 static GetProcessMemoryInfo_Proc s_pfn_Get_Process_Memory_Info = NULL;
3775 HMODULE hm_psapi = NULL;
3776 if (is_windows_9x () == TRUE)
3778 return FALSE;
3780 if (g_b_init_get_process_memory_info == 0)
3782 g_b_init_get_process_memory_info = 1;
3783 hm_psapi = LoadLibrary ("Psapi.dll");
3784 if (hm_psapi)
3785 s_pfn_Get_Process_Memory_Info = (GetProcessMemoryInfo_Proc)
3786 GetProcAddress (hm_psapi, "GetProcessMemoryInfo");
3788 if (s_pfn_Get_Process_Memory_Info == NULL)
3790 return FALSE;
3792 return s_pfn_Get_Process_Memory_Info (h_proc, mem_counters, bufsize);
3795 static BOOL WINAPI
3796 get_process_working_set_size (HANDLE h_proc,
3797 DWORD *minrss,
3798 DWORD *maxrss)
3800 static GetProcessWorkingSetSize_Proc
3801 s_pfn_Get_Process_Working_Set_Size = NULL;
3803 if (is_windows_9x () == TRUE)
3805 return FALSE;
3807 if (g_b_init_get_process_working_set_size == 0)
3809 g_b_init_get_process_working_set_size = 1;
3810 s_pfn_Get_Process_Working_Set_Size = (GetProcessWorkingSetSize_Proc)
3811 GetProcAddress (GetModuleHandle ("kernel32.dll"),
3812 "GetProcessWorkingSetSize");
3814 if (s_pfn_Get_Process_Working_Set_Size == NULL)
3816 return FALSE;
3818 return s_pfn_Get_Process_Working_Set_Size (h_proc, minrss, maxrss);
3821 static BOOL WINAPI
3822 global_memory_status (MEMORYSTATUS *buf)
3824 static GlobalMemoryStatus_Proc s_pfn_Global_Memory_Status = NULL;
3826 if (is_windows_9x () == TRUE)
3828 return FALSE;
3830 if (g_b_init_global_memory_status == 0)
3832 g_b_init_global_memory_status = 1;
3833 s_pfn_Global_Memory_Status = (GlobalMemoryStatus_Proc)
3834 GetProcAddress (GetModuleHandle ("kernel32.dll"),
3835 "GlobalMemoryStatus");
3837 if (s_pfn_Global_Memory_Status == NULL)
3839 return FALSE;
3841 return s_pfn_Global_Memory_Status (buf);
3844 static BOOL WINAPI
3845 global_memory_status_ex (MEMORY_STATUS_EX *buf)
3847 static GlobalMemoryStatusEx_Proc s_pfn_Global_Memory_Status_Ex = NULL;
3849 if (is_windows_9x () == TRUE)
3851 return FALSE;
3853 if (g_b_init_global_memory_status_ex == 0)
3855 g_b_init_global_memory_status_ex = 1;
3856 s_pfn_Global_Memory_Status_Ex = (GlobalMemoryStatusEx_Proc)
3857 GetProcAddress (GetModuleHandle ("kernel32.dll"),
3858 "GlobalMemoryStatusEx");
3860 if (s_pfn_Global_Memory_Status_Ex == NULL)
3862 return FALSE;
3864 return s_pfn_Global_Memory_Status_Ex (buf);
3867 Lisp_Object
3868 list_system_processes (void)
3870 struct gcpro gcpro1;
3871 Lisp_Object proclist = Qnil;
3872 HANDLE h_snapshot;
3874 h_snapshot = create_toolhelp32_snapshot (TH32CS_SNAPPROCESS, 0);
3876 if (h_snapshot != INVALID_HANDLE_VALUE)
3878 PROCESSENTRY32 proc_entry;
3879 DWORD proc_id;
3880 BOOL res;
3882 GCPRO1 (proclist);
3884 proc_entry.dwSize = sizeof (PROCESSENTRY32);
3885 for (res = process32_first (h_snapshot, &proc_entry); res;
3886 res = process32_next (h_snapshot, &proc_entry))
3888 proc_id = proc_entry.th32ProcessID;
3889 proclist = Fcons (make_fixnum_or_float (proc_id), proclist);
3892 CloseHandle (h_snapshot);
3893 UNGCPRO;
3894 proclist = Fnreverse (proclist);
3897 return proclist;
3900 static int
3901 enable_privilege (LPCTSTR priv_name, BOOL enable_p, TOKEN_PRIVILEGES *old_priv)
3903 TOKEN_PRIVILEGES priv;
3904 DWORD priv_size = sizeof (priv);
3905 DWORD opriv_size = sizeof (*old_priv);
3906 HANDLE h_token = NULL;
3907 HANDLE h_thread = GetCurrentThread ();
3908 int ret_val = 0;
3909 BOOL res;
3911 res = open_thread_token (h_thread,
3912 TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
3913 FALSE, &h_token);
3914 if (!res && GetLastError () == ERROR_NO_TOKEN)
3916 if (impersonate_self (SecurityImpersonation))
3917 res = open_thread_token (h_thread,
3918 TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
3919 FALSE, &h_token);
3921 if (res)
3923 priv.PrivilegeCount = 1;
3924 priv.Privileges[0].Attributes = enable_p ? SE_PRIVILEGE_ENABLED : 0;
3925 LookupPrivilegeValue (NULL, priv_name, &priv.Privileges[0].Luid);
3926 if (AdjustTokenPrivileges (h_token, FALSE, &priv, priv_size,
3927 old_priv, &opriv_size)
3928 && GetLastError () != ERROR_NOT_ALL_ASSIGNED)
3929 ret_val = 1;
3931 if (h_token)
3932 CloseHandle (h_token);
3934 return ret_val;
3937 static int
3938 restore_privilege (TOKEN_PRIVILEGES *priv)
3940 DWORD priv_size = sizeof (*priv);
3941 HANDLE h_token = NULL;
3942 int ret_val = 0;
3944 if (open_thread_token (GetCurrentThread (),
3945 TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
3946 FALSE, &h_token))
3948 if (AdjustTokenPrivileges (h_token, FALSE, priv, priv_size, NULL, NULL)
3949 && GetLastError () != ERROR_NOT_ALL_ASSIGNED)
3950 ret_val = 1;
3952 if (h_token)
3953 CloseHandle (h_token);
3955 return ret_val;
3958 static Lisp_Object
3959 ltime (long time_sec, long time_usec)
3961 return list3 (make_number ((time_sec >> 16) & 0xffff),
3962 make_number (time_sec & 0xffff),
3963 make_number (time_usec));
3966 #define U64_TO_LISP_TIME(time) ltime ((time) / 1000000L, (time) % 1000000L)
3968 static int
3969 process_times (HANDLE h_proc, Lisp_Object *ctime, Lisp_Object *etime,
3970 Lisp_Object *stime, Lisp_Object *utime, Lisp_Object *ttime,
3971 double *pcpu)
3973 FILETIME ft_creation, ft_exit, ft_kernel, ft_user, ft_current;
3974 ULONGLONG tem1, tem2, tem3, tem;
3976 if (!h_proc
3977 || !get_process_times_fn
3978 || !(*get_process_times_fn) (h_proc, &ft_creation, &ft_exit,
3979 &ft_kernel, &ft_user))
3980 return 0;
3982 GetSystemTimeAsFileTime (&ft_current);
3984 FILETIME_TO_U64 (tem1, ft_kernel);
3985 tem1 /= 10L;
3986 *stime = U64_TO_LISP_TIME (tem1);
3988 FILETIME_TO_U64 (tem2, ft_user);
3989 tem2 /= 10L;
3990 *utime = U64_TO_LISP_TIME (tem2);
3992 tem3 = tem1 + tem2;
3993 *ttime = U64_TO_LISP_TIME (tem3);
3995 FILETIME_TO_U64 (tem, ft_creation);
3996 /* Process no 4 (System) returns zero creation time. */
3997 if (tem)
3998 tem = (tem - utc_base) / 10L;
3999 *ctime = U64_TO_LISP_TIME (tem);
4001 if (tem)
4003 FILETIME_TO_U64 (tem3, ft_current);
4004 tem = (tem3 - utc_base) / 10L - tem;
4006 *etime = U64_TO_LISP_TIME (tem);
4008 if (tem)
4010 *pcpu = 100.0 * (tem1 + tem2) / tem;
4011 if (*pcpu > 100)
4012 *pcpu = 100.0;
4014 else
4015 *pcpu = 0;
4017 return 1;
4020 Lisp_Object
4021 system_process_attributes (Lisp_Object pid)
4023 struct gcpro gcpro1, gcpro2, gcpro3;
4024 Lisp_Object attrs = Qnil;
4025 Lisp_Object cmd_str, decoded_cmd, tem;
4026 HANDLE h_snapshot, h_proc;
4027 DWORD proc_id;
4028 int found_proc = 0;
4029 char uname[UNLEN+1], gname[GNLEN+1], domain[1025];
4030 DWORD ulength = sizeof (uname), dlength = sizeof (domain), needed;
4031 DWORD glength = sizeof (gname);
4032 HANDLE token = NULL;
4033 SID_NAME_USE user_type;
4034 unsigned char *buf = NULL;
4035 DWORD blen = 0;
4036 TOKEN_USER user_token;
4037 TOKEN_PRIMARY_GROUP group_token;
4038 unsigned euid;
4039 unsigned egid;
4040 DWORD sess;
4041 PROCESS_MEMORY_COUNTERS mem;
4042 PROCESS_MEMORY_COUNTERS_EX mem_ex;
4043 DWORD minrss, maxrss;
4044 MEMORYSTATUS memst;
4045 MEMORY_STATUS_EX memstex;
4046 double totphys = 0.0;
4047 Lisp_Object ctime, stime, utime, etime, ttime;
4048 double pcpu;
4049 BOOL result = FALSE;
4051 CHECK_NUMBER_OR_FLOAT (pid);
4052 proc_id = FLOATP (pid) ? XFLOAT_DATA (pid) : XINT (pid);
4054 h_snapshot = create_toolhelp32_snapshot (TH32CS_SNAPPROCESS, 0);
4056 GCPRO3 (attrs, decoded_cmd, tem);
4058 if (h_snapshot != INVALID_HANDLE_VALUE)
4060 PROCESSENTRY32 pe;
4061 BOOL res;
4063 pe.dwSize = sizeof (PROCESSENTRY32);
4064 for (res = process32_first (h_snapshot, &pe); res;
4065 res = process32_next (h_snapshot, &pe))
4067 if (proc_id == pe.th32ProcessID)
4069 if (proc_id == 0)
4070 decoded_cmd = build_string ("Idle");
4071 else
4073 /* Decode the command name from locale-specific
4074 encoding. */
4075 cmd_str = make_unibyte_string (pe.szExeFile,
4076 strlen (pe.szExeFile));
4077 decoded_cmd =
4078 code_convert_string_norecord (cmd_str,
4079 Vlocale_coding_system, 0);
4081 attrs = Fcons (Fcons (Qcomm, decoded_cmd), attrs);
4082 attrs = Fcons (Fcons (Qppid,
4083 make_fixnum_or_float (pe.th32ParentProcessID)),
4084 attrs);
4085 attrs = Fcons (Fcons (Qpri, make_number (pe.pcPriClassBase)),
4086 attrs);
4087 attrs = Fcons (Fcons (Qthcount,
4088 make_fixnum_or_float (pe.cntThreads)),
4089 attrs);
4090 found_proc = 1;
4091 break;
4095 CloseHandle (h_snapshot);
4098 if (!found_proc)
4100 UNGCPRO;
4101 return Qnil;
4104 h_proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
4105 FALSE, proc_id);
4106 /* If we were denied a handle to the process, try again after
4107 enabling the SeDebugPrivilege in our process. */
4108 if (!h_proc)
4110 TOKEN_PRIVILEGES priv_current;
4112 if (enable_privilege (SE_DEBUG_NAME, TRUE, &priv_current))
4114 h_proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
4115 FALSE, proc_id);
4116 restore_privilege (&priv_current);
4117 revert_to_self ();
4120 if (h_proc)
4122 result = open_process_token (h_proc, TOKEN_QUERY, &token);
4123 if (result)
4125 result = get_token_information (token, TokenUser, NULL, 0, &blen);
4126 if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER)
4128 buf = xmalloc (blen);
4129 result = get_token_information (token, TokenUser,
4130 (LPVOID)buf, blen, &needed);
4131 if (result)
4133 memcpy (&user_token, buf, sizeof (user_token));
4134 if (!w32_cached_id (user_token.User.Sid, &euid, uname))
4136 euid = get_rid (user_token.User.Sid);
4137 result = lookup_account_sid (NULL, user_token.User.Sid,
4138 uname, &ulength,
4139 domain, &dlength,
4140 &user_type);
4141 if (result)
4142 w32_add_to_cache (user_token.User.Sid, euid, uname);
4143 else
4145 strcpy (uname, "unknown");
4146 result = TRUE;
4149 ulength = strlen (uname);
4153 if (result)
4155 /* Determine a reasonable euid and gid values. */
4156 if (xstrcasecmp ("administrator", uname) == 0)
4158 euid = 500; /* well-known Administrator uid */
4159 egid = 513; /* well-known None gid */
4161 else
4163 /* Get group id and name. */
4164 result = get_token_information (token, TokenPrimaryGroup,
4165 (LPVOID)buf, blen, &needed);
4166 if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER)
4168 buf = xrealloc (buf, blen = needed);
4169 result = get_token_information (token, TokenPrimaryGroup,
4170 (LPVOID)buf, blen, &needed);
4172 if (result)
4174 memcpy (&group_token, buf, sizeof (group_token));
4175 if (!w32_cached_id (group_token.PrimaryGroup, &egid, gname))
4177 egid = get_rid (group_token.PrimaryGroup);
4178 dlength = sizeof (domain);
4179 result =
4180 lookup_account_sid (NULL, group_token.PrimaryGroup,
4181 gname, &glength, NULL, &dlength,
4182 &user_type);
4183 if (result)
4184 w32_add_to_cache (group_token.PrimaryGroup,
4185 egid, gname);
4186 else
4188 strcpy (gname, "None");
4189 result = TRUE;
4192 glength = strlen (gname);
4196 xfree (buf);
4198 if (!result)
4200 if (!is_windows_9x ())
4202 /* We couldn't open the process token, presumably because of
4203 insufficient access rights. Assume this process is run
4204 by the system. */
4205 strcpy (uname, "SYSTEM");
4206 strcpy (gname, "None");
4207 euid = 18; /* SYSTEM */
4208 egid = 513; /* None */
4209 glength = strlen (gname);
4210 ulength = strlen (uname);
4212 /* If we are running under Windows 9X, where security calls are
4213 not supported, we assume all processes are run by the current
4214 user. */
4215 else if (GetUserName (uname, &ulength))
4217 if (xstrcasecmp ("administrator", uname) == 0)
4218 euid = 0;
4219 else
4220 euid = 123;
4221 egid = euid;
4222 strcpy (gname, "None");
4223 glength = strlen (gname);
4224 ulength = strlen (uname);
4226 else
4228 euid = 123;
4229 egid = 123;
4230 strcpy (uname, "administrator");
4231 ulength = strlen (uname);
4232 strcpy (gname, "None");
4233 glength = strlen (gname);
4235 if (token)
4236 CloseHandle (token);
4239 attrs = Fcons (Fcons (Qeuid, make_fixnum_or_float (euid)), attrs);
4240 tem = make_unibyte_string (uname, ulength);
4241 attrs = Fcons (Fcons (Quser,
4242 code_convert_string_norecord (tem, Vlocale_coding_system, 0)),
4243 attrs);
4244 attrs = Fcons (Fcons (Qegid, make_fixnum_or_float (egid)), attrs);
4245 tem = make_unibyte_string (gname, glength);
4246 attrs = Fcons (Fcons (Qgroup,
4247 code_convert_string_norecord (tem, Vlocale_coding_system, 0)),
4248 attrs);
4250 if (global_memory_status_ex (&memstex))
4251 #if __GNUC__ || (defined (_MSC_VER) && _MSC_VER >= 1300)
4252 totphys = memstex.ullTotalPhys / 1024.0;
4253 #else
4254 /* Visual Studio 6 cannot convert an unsigned __int64 type to
4255 double, so we need to do this for it... */
4257 DWORD tot_hi = memstex.ullTotalPhys >> 32;
4258 DWORD tot_md = (memstex.ullTotalPhys & 0x00000000ffffffff) >> 10;
4259 DWORD tot_lo = memstex.ullTotalPhys % 1024;
4261 totphys = tot_hi * 4194304.0 + tot_md + tot_lo / 1024.0;
4263 #endif /* __GNUC__ || _MSC_VER >= 1300 */
4264 else if (global_memory_status (&memst))
4265 totphys = memst.dwTotalPhys / 1024.0;
4267 if (h_proc
4268 && get_process_memory_info (h_proc, (PROCESS_MEMORY_COUNTERS *)&mem_ex,
4269 sizeof (mem_ex)))
4271 DWORD rss = mem_ex.WorkingSetSize / 1024;
4273 attrs = Fcons (Fcons (Qmajflt,
4274 make_fixnum_or_float (mem_ex.PageFaultCount)),
4275 attrs);
4276 attrs = Fcons (Fcons (Qvsize,
4277 make_fixnum_or_float (mem_ex.PrivateUsage / 1024)),
4278 attrs);
4279 attrs = Fcons (Fcons (Qrss, make_fixnum_or_float (rss)), attrs);
4280 if (totphys)
4281 attrs = Fcons (Fcons (Qpmem, make_float (100. * rss / totphys)), attrs);
4283 else if (h_proc
4284 && get_process_memory_info (h_proc, &mem, sizeof (mem)))
4286 DWORD rss = mem_ex.WorkingSetSize / 1024;
4288 attrs = Fcons (Fcons (Qmajflt,
4289 make_fixnum_or_float (mem.PageFaultCount)),
4290 attrs);
4291 attrs = Fcons (Fcons (Qrss, make_fixnum_or_float (rss)), attrs);
4292 if (totphys)
4293 attrs = Fcons (Fcons (Qpmem, make_float (100. * rss / totphys)), attrs);
4295 else if (h_proc
4296 && get_process_working_set_size (h_proc, &minrss, &maxrss))
4298 DWORD rss = maxrss / 1024;
4300 attrs = Fcons (Fcons (Qrss, make_fixnum_or_float (maxrss / 1024)), attrs);
4301 if (totphys)
4302 attrs = Fcons (Fcons (Qpmem, make_float (100. * rss / totphys)), attrs);
4305 if (process_times (h_proc, &ctime, &etime, &stime, &utime, &ttime, &pcpu))
4307 attrs = Fcons (Fcons (Qutime, utime), attrs);
4308 attrs = Fcons (Fcons (Qstime, stime), attrs);
4309 attrs = Fcons (Fcons (Qtime, ttime), attrs);
4310 attrs = Fcons (Fcons (Qstart, ctime), attrs);
4311 attrs = Fcons (Fcons (Qetime, etime), attrs);
4312 attrs = Fcons (Fcons (Qpcpu, make_float (pcpu)), attrs);
4315 /* FIXME: Retrieve command line by walking the PEB of the process. */
4317 if (h_proc)
4318 CloseHandle (h_proc);
4319 UNGCPRO;
4320 return attrs;
4324 /* Wrappers for winsock functions to map between our file descriptors
4325 and winsock's handles; also set h_errno for convenience.
4327 To allow Emacs to run on systems which don't have winsock support
4328 installed, we dynamically link to winsock on startup if present, and
4329 otherwise provide the minimum necessary functionality
4330 (eg. gethostname). */
4332 /* function pointers for relevant socket functions */
4333 int (PASCAL *pfn_WSAStartup) (WORD wVersionRequired, LPWSADATA lpWSAData);
4334 void (PASCAL *pfn_WSASetLastError) (int iError);
4335 int (PASCAL *pfn_WSAGetLastError) (void);
4336 int (PASCAL *pfn_WSAEventSelect) (SOCKET s, HANDLE hEventObject, long lNetworkEvents);
4337 HANDLE (PASCAL *pfn_WSACreateEvent) (void);
4338 int (PASCAL *pfn_WSACloseEvent) (HANDLE hEvent);
4339 int (PASCAL *pfn_socket) (int af, int type, int protocol);
4340 int (PASCAL *pfn_bind) (SOCKET s, const struct sockaddr *addr, int namelen);
4341 int (PASCAL *pfn_connect) (SOCKET s, const struct sockaddr *addr, int namelen);
4342 int (PASCAL *pfn_ioctlsocket) (SOCKET s, long cmd, u_long *argp);
4343 int (PASCAL *pfn_recv) (SOCKET s, char * buf, int len, int flags);
4344 int (PASCAL *pfn_send) (SOCKET s, const char * buf, int len, int flags);
4345 int (PASCAL *pfn_closesocket) (SOCKET s);
4346 int (PASCAL *pfn_shutdown) (SOCKET s, int how);
4347 int (PASCAL *pfn_WSACleanup) (void);
4349 u_short (PASCAL *pfn_htons) (u_short hostshort);
4350 u_short (PASCAL *pfn_ntohs) (u_short netshort);
4351 unsigned long (PASCAL *pfn_inet_addr) (const char * cp);
4352 int (PASCAL *pfn_gethostname) (char * name, int namelen);
4353 struct hostent * (PASCAL *pfn_gethostbyname) (const char * name);
4354 struct servent * (PASCAL *pfn_getservbyname) (const char * name, const char * proto);
4355 int (PASCAL *pfn_getpeername) (SOCKET s, struct sockaddr *addr, int * namelen);
4356 int (PASCAL *pfn_setsockopt) (SOCKET s, int level, int optname,
4357 const char * optval, int optlen);
4358 int (PASCAL *pfn_listen) (SOCKET s, int backlog);
4359 int (PASCAL *pfn_getsockname) (SOCKET s, struct sockaddr * name,
4360 int * namelen);
4361 SOCKET (PASCAL *pfn_accept) (SOCKET s, struct sockaddr * addr, int * addrlen);
4362 int (PASCAL *pfn_recvfrom) (SOCKET s, char * buf, int len, int flags,
4363 struct sockaddr * from, int * fromlen);
4364 int (PASCAL *pfn_sendto) (SOCKET s, const char * buf, int len, int flags,
4365 const struct sockaddr * to, int tolen);
4367 /* SetHandleInformation is only needed to make sockets non-inheritable. */
4368 BOOL (WINAPI *pfn_SetHandleInformation) (HANDLE object, DWORD mask, DWORD flags);
4369 #ifndef HANDLE_FLAG_INHERIT
4370 #define HANDLE_FLAG_INHERIT 1
4371 #endif
4373 HANDLE winsock_lib;
4374 static int winsock_inuse;
4376 BOOL
4377 term_winsock (void)
4379 if (winsock_lib != NULL && winsock_inuse == 0)
4381 /* Not sure what would cause WSAENETDOWN, or even if it can happen
4382 after WSAStartup returns successfully, but it seems reasonable
4383 to allow unloading winsock anyway in that case. */
4384 if (pfn_WSACleanup () == 0 ||
4385 pfn_WSAGetLastError () == WSAENETDOWN)
4387 if (FreeLibrary (winsock_lib))
4388 winsock_lib = NULL;
4389 return TRUE;
4392 return FALSE;
4395 BOOL
4396 init_winsock (int load_now)
4398 WSADATA winsockData;
4400 if (winsock_lib != NULL)
4401 return TRUE;
4403 pfn_SetHandleInformation
4404 = (void *) GetProcAddress (GetModuleHandle ("kernel32.dll"),
4405 "SetHandleInformation");
4407 winsock_lib = LoadLibrary ("Ws2_32.dll");
4409 if (winsock_lib != NULL)
4411 /* dynamically link to socket functions */
4413 #define LOAD_PROC(fn) \
4414 if ((pfn_##fn = (void *) GetProcAddress (winsock_lib, #fn)) == NULL) \
4415 goto fail;
4417 LOAD_PROC (WSAStartup);
4418 LOAD_PROC (WSASetLastError);
4419 LOAD_PROC (WSAGetLastError);
4420 LOAD_PROC (WSAEventSelect);
4421 LOAD_PROC (WSACreateEvent);
4422 LOAD_PROC (WSACloseEvent);
4423 LOAD_PROC (socket);
4424 LOAD_PROC (bind);
4425 LOAD_PROC (connect);
4426 LOAD_PROC (ioctlsocket);
4427 LOAD_PROC (recv);
4428 LOAD_PROC (send);
4429 LOAD_PROC (closesocket);
4430 LOAD_PROC (shutdown);
4431 LOAD_PROC (htons);
4432 LOAD_PROC (ntohs);
4433 LOAD_PROC (inet_addr);
4434 LOAD_PROC (gethostname);
4435 LOAD_PROC (gethostbyname);
4436 LOAD_PROC (getservbyname);
4437 LOAD_PROC (getpeername);
4438 LOAD_PROC (WSACleanup);
4439 LOAD_PROC (setsockopt);
4440 LOAD_PROC (listen);
4441 LOAD_PROC (getsockname);
4442 LOAD_PROC (accept);
4443 LOAD_PROC (recvfrom);
4444 LOAD_PROC (sendto);
4445 #undef LOAD_PROC
4447 /* specify version 1.1 of winsock */
4448 if (pfn_WSAStartup (0x101, &winsockData) == 0)
4450 if (winsockData.wVersion != 0x101)
4451 goto fail;
4453 if (!load_now)
4455 /* Report that winsock exists and is usable, but leave
4456 socket functions disabled. I am assuming that calling
4457 WSAStartup does not require any network interaction,
4458 and in particular does not cause or require a dial-up
4459 connection to be established. */
4461 pfn_WSACleanup ();
4462 FreeLibrary (winsock_lib);
4463 winsock_lib = NULL;
4465 winsock_inuse = 0;
4466 return TRUE;
4469 fail:
4470 FreeLibrary (winsock_lib);
4471 winsock_lib = NULL;
4474 return FALSE;
4478 int h_errno = 0;
4480 /* function to set h_errno for compatibility; map winsock error codes to
4481 normal system codes where they overlap (non-overlapping definitions
4482 are already in <sys/socket.h> */
4483 static void
4484 set_errno (void)
4486 if (winsock_lib == NULL)
4487 h_errno = EINVAL;
4488 else
4489 h_errno = pfn_WSAGetLastError ();
4491 switch (h_errno)
4493 case WSAEACCES: h_errno = EACCES; break;
4494 case WSAEBADF: h_errno = EBADF; break;
4495 case WSAEFAULT: h_errno = EFAULT; break;
4496 case WSAEINTR: h_errno = EINTR; break;
4497 case WSAEINVAL: h_errno = EINVAL; break;
4498 case WSAEMFILE: h_errno = EMFILE; break;
4499 case WSAENAMETOOLONG: h_errno = ENAMETOOLONG; break;
4500 case WSAENOTEMPTY: h_errno = ENOTEMPTY; break;
4502 errno = h_errno;
4505 static void
4506 check_errno (void)
4508 if (h_errno == 0 && winsock_lib != NULL)
4509 pfn_WSASetLastError (0);
4512 /* Extend strerror to handle the winsock-specific error codes. */
4513 struct {
4514 int errnum;
4515 char * msg;
4516 } _wsa_errlist[] = {
4517 WSAEINTR , "Interrupted function call",
4518 WSAEBADF , "Bad file descriptor",
4519 WSAEACCES , "Permission denied",
4520 WSAEFAULT , "Bad address",
4521 WSAEINVAL , "Invalid argument",
4522 WSAEMFILE , "Too many open files",
4524 WSAEWOULDBLOCK , "Resource temporarily unavailable",
4525 WSAEINPROGRESS , "Operation now in progress",
4526 WSAEALREADY , "Operation already in progress",
4527 WSAENOTSOCK , "Socket operation on non-socket",
4528 WSAEDESTADDRREQ , "Destination address required",
4529 WSAEMSGSIZE , "Message too long",
4530 WSAEPROTOTYPE , "Protocol wrong type for socket",
4531 WSAENOPROTOOPT , "Bad protocol option",
4532 WSAEPROTONOSUPPORT , "Protocol not supported",
4533 WSAESOCKTNOSUPPORT , "Socket type not supported",
4534 WSAEOPNOTSUPP , "Operation not supported",
4535 WSAEPFNOSUPPORT , "Protocol family not supported",
4536 WSAEAFNOSUPPORT , "Address family not supported by protocol family",
4537 WSAEADDRINUSE , "Address already in use",
4538 WSAEADDRNOTAVAIL , "Cannot assign requested address",
4539 WSAENETDOWN , "Network is down",
4540 WSAENETUNREACH , "Network is unreachable",
4541 WSAENETRESET , "Network dropped connection on reset",
4542 WSAECONNABORTED , "Software caused connection abort",
4543 WSAECONNRESET , "Connection reset by peer",
4544 WSAENOBUFS , "No buffer space available",
4545 WSAEISCONN , "Socket is already connected",
4546 WSAENOTCONN , "Socket is not connected",
4547 WSAESHUTDOWN , "Cannot send after socket shutdown",
4548 WSAETOOMANYREFS , "Too many references", /* not sure */
4549 WSAETIMEDOUT , "Connection timed out",
4550 WSAECONNREFUSED , "Connection refused",
4551 WSAELOOP , "Network loop", /* not sure */
4552 WSAENAMETOOLONG , "Name is too long",
4553 WSAEHOSTDOWN , "Host is down",
4554 WSAEHOSTUNREACH , "No route to host",
4555 WSAENOTEMPTY , "Buffer not empty", /* not sure */
4556 WSAEPROCLIM , "Too many processes",
4557 WSAEUSERS , "Too many users", /* not sure */
4558 WSAEDQUOT , "Double quote in host name", /* really not sure */
4559 WSAESTALE , "Data is stale", /* not sure */
4560 WSAEREMOTE , "Remote error", /* not sure */
4562 WSASYSNOTREADY , "Network subsystem is unavailable",
4563 WSAVERNOTSUPPORTED , "WINSOCK.DLL version out of range",
4564 WSANOTINITIALISED , "Winsock not initialized successfully",
4565 WSAEDISCON , "Graceful shutdown in progress",
4566 #ifdef WSAENOMORE
4567 WSAENOMORE , "No more operations allowed", /* not sure */
4568 WSAECANCELLED , "Operation cancelled", /* not sure */
4569 WSAEINVALIDPROCTABLE , "Invalid procedure table from service provider",
4570 WSAEINVALIDPROVIDER , "Invalid service provider version number",
4571 WSAEPROVIDERFAILEDINIT , "Unable to initialize a service provider",
4572 WSASYSCALLFAILURE , "System call failure",
4573 WSASERVICE_NOT_FOUND , "Service not found", /* not sure */
4574 WSATYPE_NOT_FOUND , "Class type not found",
4575 WSA_E_NO_MORE , "No more resources available", /* really not sure */
4576 WSA_E_CANCELLED , "Operation already cancelled", /* really not sure */
4577 WSAEREFUSED , "Operation refused", /* not sure */
4578 #endif
4580 WSAHOST_NOT_FOUND , "Host not found",
4581 WSATRY_AGAIN , "Authoritative host not found during name lookup",
4582 WSANO_RECOVERY , "Non-recoverable error during name lookup",
4583 WSANO_DATA , "Valid name, no data record of requested type",
4585 -1, NULL
4588 char *
4589 sys_strerror (int error_no)
4591 int i;
4592 static char unknown_msg[40];
4594 if (error_no >= 0 && error_no < sys_nerr)
4595 return sys_errlist[error_no];
4597 for (i = 0; _wsa_errlist[i].errnum >= 0; i++)
4598 if (_wsa_errlist[i].errnum == error_no)
4599 return _wsa_errlist[i].msg;
4601 sprintf (unknown_msg, "Unidentified error: %d", error_no);
4602 return unknown_msg;
4605 /* [andrewi 3-May-96] I've had conflicting results using both methods,
4606 but I believe the method of keeping the socket handle separate (and
4607 insuring it is not inheritable) is the correct one. */
4609 #define SOCK_HANDLE(fd) ((SOCKET) fd_info[fd].hnd)
4611 static int socket_to_fd (SOCKET s);
4614 sys_socket (int af, int type, int protocol)
4616 SOCKET s;
4618 if (winsock_lib == NULL)
4620 h_errno = ENETDOWN;
4621 return INVALID_SOCKET;
4624 check_errno ();
4626 /* call the real socket function */
4627 s = pfn_socket (af, type, protocol);
4629 if (s != INVALID_SOCKET)
4630 return socket_to_fd (s);
4632 set_errno ();
4633 return -1;
4636 /* Convert a SOCKET to a file descriptor. */
4637 static int
4638 socket_to_fd (SOCKET s)
4640 int fd;
4641 child_process * cp;
4643 /* Although under NT 3.5 _open_osfhandle will accept a socket
4644 handle, if opened with SO_OPENTYPE == SO_SYNCHRONOUS_NONALERT,
4645 that does not work under NT 3.1. However, we can get the same
4646 effect by using a backdoor function to replace an existing
4647 descriptor handle with the one we want. */
4649 /* allocate a file descriptor (with appropriate flags) */
4650 fd = _open ("NUL:", _O_RDWR);
4651 if (fd >= 0)
4653 /* Make a non-inheritable copy of the socket handle. Note
4654 that it is possible that sockets aren't actually kernel
4655 handles, which appears to be the case on Windows 9x when
4656 the MS Proxy winsock client is installed. */
4658 /* Apparently there is a bug in NT 3.51 with some service
4659 packs, which prevents using DuplicateHandle to make a
4660 socket handle non-inheritable (causes WSACleanup to
4661 hang). The work-around is to use SetHandleInformation
4662 instead if it is available and implemented. */
4663 if (pfn_SetHandleInformation)
4665 pfn_SetHandleInformation ((HANDLE) s, HANDLE_FLAG_INHERIT, 0);
4667 else
4669 HANDLE parent = GetCurrentProcess ();
4670 HANDLE new_s = INVALID_HANDLE_VALUE;
4672 if (DuplicateHandle (parent,
4673 (HANDLE) s,
4674 parent,
4675 &new_s,
4677 FALSE,
4678 DUPLICATE_SAME_ACCESS))
4680 /* It is possible that DuplicateHandle succeeds even
4681 though the socket wasn't really a kernel handle,
4682 because a real handle has the same value. So
4683 test whether the new handle really is a socket. */
4684 long nonblocking = 0;
4685 if (pfn_ioctlsocket ((SOCKET) new_s, FIONBIO, &nonblocking) == 0)
4687 pfn_closesocket (s);
4688 s = (SOCKET) new_s;
4690 else
4692 CloseHandle (new_s);
4697 fd_info[fd].hnd = (HANDLE) s;
4699 /* set our own internal flags */
4700 fd_info[fd].flags = FILE_SOCKET | FILE_BINARY | FILE_READ | FILE_WRITE;
4702 cp = new_child ();
4703 if (cp)
4705 cp->fd = fd;
4706 cp->status = STATUS_READ_ACKNOWLEDGED;
4708 /* attach child_process to fd_info */
4709 if (fd_info[ fd ].cp != NULL)
4711 DebPrint (("sys_socket: fd_info[%d] apparently in use!\n", fd));
4712 abort ();
4715 fd_info[ fd ].cp = cp;
4717 /* success! */
4718 winsock_inuse++; /* count open sockets */
4719 return fd;
4722 /* clean up */
4723 _close (fd);
4725 pfn_closesocket (s);
4726 h_errno = EMFILE;
4727 return -1;
4731 sys_bind (int s, const struct sockaddr * addr, int namelen)
4733 if (winsock_lib == NULL)
4735 h_errno = ENOTSOCK;
4736 return SOCKET_ERROR;
4739 check_errno ();
4740 if (fd_info[s].flags & FILE_SOCKET)
4742 int rc = pfn_bind (SOCK_HANDLE (s), addr, namelen);
4743 if (rc == SOCKET_ERROR)
4744 set_errno ();
4745 return rc;
4747 h_errno = ENOTSOCK;
4748 return SOCKET_ERROR;
4752 sys_connect (int s, const struct sockaddr * name, int namelen)
4754 if (winsock_lib == NULL)
4756 h_errno = ENOTSOCK;
4757 return SOCKET_ERROR;
4760 check_errno ();
4761 if (fd_info[s].flags & FILE_SOCKET)
4763 int rc = pfn_connect (SOCK_HANDLE (s), name, namelen);
4764 if (rc == SOCKET_ERROR)
4765 set_errno ();
4766 return rc;
4768 h_errno = ENOTSOCK;
4769 return SOCKET_ERROR;
4772 u_short
4773 sys_htons (u_short hostshort)
4775 return (winsock_lib != NULL) ?
4776 pfn_htons (hostshort) : hostshort;
4779 u_short
4780 sys_ntohs (u_short netshort)
4782 return (winsock_lib != NULL) ?
4783 pfn_ntohs (netshort) : netshort;
4786 unsigned long
4787 sys_inet_addr (const char * cp)
4789 return (winsock_lib != NULL) ?
4790 pfn_inet_addr (cp) : INADDR_NONE;
4794 sys_gethostname (char * name, int namelen)
4796 if (winsock_lib != NULL)
4797 return pfn_gethostname (name, namelen);
4799 if (namelen > MAX_COMPUTERNAME_LENGTH)
4800 return !GetComputerName (name, (DWORD *)&namelen);
4802 h_errno = EFAULT;
4803 return SOCKET_ERROR;
4806 struct hostent *
4807 sys_gethostbyname (const char * name)
4809 struct hostent * host;
4811 if (winsock_lib == NULL)
4813 h_errno = ENETDOWN;
4814 return NULL;
4817 check_errno ();
4818 host = pfn_gethostbyname (name);
4819 if (!host)
4820 set_errno ();
4821 return host;
4824 struct servent *
4825 sys_getservbyname (const char * name, const char * proto)
4827 struct servent * serv;
4829 if (winsock_lib == NULL)
4831 h_errno = ENETDOWN;
4832 return NULL;
4835 check_errno ();
4836 serv = pfn_getservbyname (name, proto);
4837 if (!serv)
4838 set_errno ();
4839 return serv;
4843 sys_getpeername (int s, struct sockaddr *addr, int * namelen)
4845 if (winsock_lib == NULL)
4847 h_errno = ENETDOWN;
4848 return SOCKET_ERROR;
4851 check_errno ();
4852 if (fd_info[s].flags & FILE_SOCKET)
4854 int rc = pfn_getpeername (SOCK_HANDLE (s), addr, namelen);
4855 if (rc == SOCKET_ERROR)
4856 set_errno ();
4857 return rc;
4859 h_errno = ENOTSOCK;
4860 return SOCKET_ERROR;
4864 sys_shutdown (int s, int how)
4866 if (winsock_lib == NULL)
4868 h_errno = ENETDOWN;
4869 return SOCKET_ERROR;
4872 check_errno ();
4873 if (fd_info[s].flags & FILE_SOCKET)
4875 int rc = pfn_shutdown (SOCK_HANDLE (s), how);
4876 if (rc == SOCKET_ERROR)
4877 set_errno ();
4878 return rc;
4880 h_errno = ENOTSOCK;
4881 return SOCKET_ERROR;
4885 sys_setsockopt (int s, int level, int optname, const void * optval, int optlen)
4887 if (winsock_lib == NULL)
4889 h_errno = ENETDOWN;
4890 return SOCKET_ERROR;
4893 check_errno ();
4894 if (fd_info[s].flags & FILE_SOCKET)
4896 int rc = pfn_setsockopt (SOCK_HANDLE (s), level, optname,
4897 (const char *)optval, optlen);
4898 if (rc == SOCKET_ERROR)
4899 set_errno ();
4900 return rc;
4902 h_errno = ENOTSOCK;
4903 return SOCKET_ERROR;
4907 sys_listen (int s, int backlog)
4909 if (winsock_lib == NULL)
4911 h_errno = ENETDOWN;
4912 return SOCKET_ERROR;
4915 check_errno ();
4916 if (fd_info[s].flags & FILE_SOCKET)
4918 int rc = pfn_listen (SOCK_HANDLE (s), backlog);
4919 if (rc == SOCKET_ERROR)
4920 set_errno ();
4921 else
4922 fd_info[s].flags |= FILE_LISTEN;
4923 return rc;
4925 h_errno = ENOTSOCK;
4926 return SOCKET_ERROR;
4930 sys_getsockname (int s, struct sockaddr * name, int * namelen)
4932 if (winsock_lib == NULL)
4934 h_errno = ENETDOWN;
4935 return SOCKET_ERROR;
4938 check_errno ();
4939 if (fd_info[s].flags & FILE_SOCKET)
4941 int rc = pfn_getsockname (SOCK_HANDLE (s), name, namelen);
4942 if (rc == SOCKET_ERROR)
4943 set_errno ();
4944 return rc;
4946 h_errno = ENOTSOCK;
4947 return SOCKET_ERROR;
4951 sys_accept (int s, struct sockaddr * addr, int * addrlen)
4953 if (winsock_lib == NULL)
4955 h_errno = ENETDOWN;
4956 return -1;
4959 check_errno ();
4960 if (fd_info[s].flags & FILE_LISTEN)
4962 SOCKET t = pfn_accept (SOCK_HANDLE (s), addr, addrlen);
4963 int fd = -1;
4964 if (t == INVALID_SOCKET)
4965 set_errno ();
4966 else
4967 fd = socket_to_fd (t);
4969 fd_info[s].cp->status = STATUS_READ_ACKNOWLEDGED;
4970 ResetEvent (fd_info[s].cp->char_avail);
4971 return fd;
4973 h_errno = ENOTSOCK;
4974 return -1;
4978 sys_recvfrom (int s, char * buf, int len, int flags,
4979 struct sockaddr * from, int * fromlen)
4981 if (winsock_lib == NULL)
4983 h_errno = ENETDOWN;
4984 return SOCKET_ERROR;
4987 check_errno ();
4988 if (fd_info[s].flags & FILE_SOCKET)
4990 int rc = pfn_recvfrom (SOCK_HANDLE (s), buf, len, flags, from, fromlen);
4991 if (rc == SOCKET_ERROR)
4992 set_errno ();
4993 return rc;
4995 h_errno = ENOTSOCK;
4996 return SOCKET_ERROR;
5000 sys_sendto (int s, const char * buf, int len, int flags,
5001 const struct sockaddr * to, int tolen)
5003 if (winsock_lib == NULL)
5005 h_errno = ENETDOWN;
5006 return SOCKET_ERROR;
5009 check_errno ();
5010 if (fd_info[s].flags & FILE_SOCKET)
5012 int rc = pfn_sendto (SOCK_HANDLE (s), buf, len, flags, to, tolen);
5013 if (rc == SOCKET_ERROR)
5014 set_errno ();
5015 return rc;
5017 h_errno = ENOTSOCK;
5018 return SOCKET_ERROR;
5021 /* Windows does not have an fcntl function. Provide an implementation
5022 solely for making sockets non-blocking. */
5024 fcntl (int s, int cmd, int options)
5026 if (winsock_lib == NULL)
5028 h_errno = ENETDOWN;
5029 return -1;
5032 check_errno ();
5033 if (fd_info[s].flags & FILE_SOCKET)
5035 if (cmd == F_SETFL && options == O_NDELAY)
5037 unsigned long nblock = 1;
5038 int rc = pfn_ioctlsocket (SOCK_HANDLE (s), FIONBIO, &nblock);
5039 if (rc == SOCKET_ERROR)
5040 set_errno();
5041 /* Keep track of the fact that we set this to non-blocking. */
5042 fd_info[s].flags |= FILE_NDELAY;
5043 return rc;
5045 else
5047 h_errno = EINVAL;
5048 return SOCKET_ERROR;
5051 h_errno = ENOTSOCK;
5052 return SOCKET_ERROR;
5056 /* Shadow main io functions: we need to handle pipes and sockets more
5057 intelligently, and implement non-blocking mode as well. */
5060 sys_close (int fd)
5062 int rc;
5064 if (fd < 0)
5066 errno = EBADF;
5067 return -1;
5070 if (fd < MAXDESC && fd_info[fd].cp)
5072 child_process * cp = fd_info[fd].cp;
5074 fd_info[fd].cp = NULL;
5076 if (CHILD_ACTIVE (cp))
5078 /* if last descriptor to active child_process then cleanup */
5079 int i;
5080 for (i = 0; i < MAXDESC; i++)
5082 if (i == fd)
5083 continue;
5084 if (fd_info[i].cp == cp)
5085 break;
5087 if (i == MAXDESC)
5089 if (fd_info[fd].flags & FILE_SOCKET)
5091 if (winsock_lib == NULL) abort ();
5093 pfn_shutdown (SOCK_HANDLE (fd), 2);
5094 rc = pfn_closesocket (SOCK_HANDLE (fd));
5096 winsock_inuse--; /* count open sockets */
5098 delete_child (cp);
5103 /* Note that sockets do not need special treatment here (at least on
5104 NT and Windows 95 using the standard tcp/ip stacks) - it appears that
5105 closesocket is equivalent to CloseHandle, which is to be expected
5106 because socket handles are fully fledged kernel handles. */
5107 rc = _close (fd);
5109 if (rc == 0 && fd < MAXDESC)
5110 fd_info[fd].flags = 0;
5112 return rc;
5116 sys_dup (int fd)
5118 int new_fd;
5120 new_fd = _dup (fd);
5121 if (new_fd >= 0 && new_fd < MAXDESC)
5123 /* duplicate our internal info as well */
5124 fd_info[new_fd] = fd_info[fd];
5126 return new_fd;
5130 sys_dup2 (int src, int dst)
5132 int rc;
5134 if (dst < 0 || dst >= MAXDESC)
5136 errno = EBADF;
5137 return -1;
5140 /* make sure we close the destination first if it's a pipe or socket */
5141 if (src != dst && fd_info[dst].flags != 0)
5142 sys_close (dst);
5144 rc = _dup2 (src, dst);
5145 if (rc == 0)
5147 /* duplicate our internal info as well */
5148 fd_info[dst] = fd_info[src];
5150 return rc;
5153 /* Unix pipe() has only one arg */
5155 sys_pipe (int * phandles)
5157 int rc;
5158 unsigned flags;
5160 /* make pipe handles non-inheritable; when we spawn a child, we
5161 replace the relevant handle with an inheritable one. Also put
5162 pipes into binary mode; we will do text mode translation ourselves
5163 if required. */
5164 rc = _pipe (phandles, 0, _O_NOINHERIT | _O_BINARY);
5166 if (rc == 0)
5168 /* Protect against overflow, since Windows can open more handles than
5169 our fd_info array has room for. */
5170 if (phandles[0] >= MAXDESC || phandles[1] >= MAXDESC)
5172 _close (phandles[0]);
5173 _close (phandles[1]);
5174 rc = -1;
5176 else
5178 flags = FILE_PIPE | FILE_READ | FILE_BINARY;
5179 fd_info[phandles[0]].flags = flags;
5181 flags = FILE_PIPE | FILE_WRITE | FILE_BINARY;
5182 fd_info[phandles[1]].flags = flags;
5186 return rc;
5189 /* From ntproc.c */
5190 extern int w32_pipe_read_delay;
5192 /* Function to do blocking read of one byte, needed to implement
5193 select. It is only allowed on sockets and pipes. */
5195 _sys_read_ahead (int fd)
5197 child_process * cp;
5198 int rc;
5200 if (fd < 0 || fd >= MAXDESC)
5201 return STATUS_READ_ERROR;
5203 cp = fd_info[fd].cp;
5205 if (cp == NULL || cp->fd != fd || cp->status != STATUS_READ_READY)
5206 return STATUS_READ_ERROR;
5208 if ((fd_info[fd].flags & (FILE_PIPE | FILE_SERIAL | FILE_SOCKET)) == 0
5209 || (fd_info[fd].flags & FILE_READ) == 0)
5211 DebPrint (("_sys_read_ahead: internal error: fd %d is not a pipe, serial port, or socket!\n", fd));
5212 abort ();
5215 cp->status = STATUS_READ_IN_PROGRESS;
5217 if (fd_info[fd].flags & FILE_PIPE)
5219 rc = _read (fd, &cp->chr, sizeof (char));
5221 /* Give subprocess time to buffer some more output for us before
5222 reporting that input is available; we need this because Windows 95
5223 connects DOS programs to pipes by making the pipe appear to be
5224 the normal console stdout - as a result most DOS programs will
5225 write to stdout without buffering, ie. one character at a
5226 time. Even some W32 programs do this - "dir" in a command
5227 shell on NT is very slow if we don't do this. */
5228 if (rc > 0)
5230 int wait = w32_pipe_read_delay;
5232 if (wait > 0)
5233 Sleep (wait);
5234 else if (wait < 0)
5235 while (++wait <= 0)
5236 /* Yield remainder of our time slice, effectively giving a
5237 temporary priority boost to the child process. */
5238 Sleep (0);
5241 else if (fd_info[fd].flags & FILE_SERIAL)
5243 HANDLE hnd = fd_info[fd].hnd;
5244 OVERLAPPED *ovl = &fd_info[fd].cp->ovl_read;
5245 COMMTIMEOUTS ct;
5247 /* Configure timeouts for blocking read. */
5248 if (!GetCommTimeouts (hnd, &ct))
5249 return STATUS_READ_ERROR;
5250 ct.ReadIntervalTimeout = 0;
5251 ct.ReadTotalTimeoutMultiplier = 0;
5252 ct.ReadTotalTimeoutConstant = 0;
5253 if (!SetCommTimeouts (hnd, &ct))
5254 return STATUS_READ_ERROR;
5256 if (!ReadFile (hnd, &cp->chr, sizeof (char), (DWORD*) &rc, ovl))
5258 if (GetLastError () != ERROR_IO_PENDING)
5259 return STATUS_READ_ERROR;
5260 if (!GetOverlappedResult (hnd, ovl, (DWORD*) &rc, TRUE))
5261 return STATUS_READ_ERROR;
5264 else if (fd_info[fd].flags & FILE_SOCKET)
5266 unsigned long nblock = 0;
5267 /* We always want this to block, so temporarily disable NDELAY. */
5268 if (fd_info[fd].flags & FILE_NDELAY)
5269 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
5271 rc = pfn_recv (SOCK_HANDLE (fd), &cp->chr, sizeof (char), 0);
5273 if (fd_info[fd].flags & FILE_NDELAY)
5275 nblock = 1;
5276 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
5280 if (rc == sizeof (char))
5281 cp->status = STATUS_READ_SUCCEEDED;
5282 else
5283 cp->status = STATUS_READ_FAILED;
5285 return cp->status;
5289 _sys_wait_accept (int fd)
5291 HANDLE hEv;
5292 child_process * cp;
5293 int rc;
5295 if (fd < 0 || fd >= MAXDESC)
5296 return STATUS_READ_ERROR;
5298 cp = fd_info[fd].cp;
5300 if (cp == NULL || cp->fd != fd || cp->status != STATUS_READ_READY)
5301 return STATUS_READ_ERROR;
5303 cp->status = STATUS_READ_FAILED;
5305 hEv = pfn_WSACreateEvent ();
5306 rc = pfn_WSAEventSelect (SOCK_HANDLE (fd), hEv, FD_ACCEPT);
5307 if (rc != SOCKET_ERROR)
5309 rc = WaitForSingleObject (hEv, INFINITE);
5310 pfn_WSAEventSelect (SOCK_HANDLE (fd), NULL, 0);
5311 if (rc == WAIT_OBJECT_0)
5312 cp->status = STATUS_READ_SUCCEEDED;
5314 pfn_WSACloseEvent (hEv);
5316 return cp->status;
5320 sys_read (int fd, char * buffer, unsigned int count)
5322 int nchars;
5323 int to_read;
5324 DWORD waiting;
5325 char * orig_buffer = buffer;
5327 if (fd < 0)
5329 errno = EBADF;
5330 return -1;
5333 if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET | FILE_SERIAL))
5335 child_process *cp = fd_info[fd].cp;
5337 if ((fd_info[fd].flags & FILE_READ) == 0)
5339 errno = EBADF;
5340 return -1;
5343 nchars = 0;
5345 /* re-read CR carried over from last read */
5346 if (fd_info[fd].flags & FILE_LAST_CR)
5348 if (fd_info[fd].flags & FILE_BINARY) abort ();
5349 *buffer++ = 0x0d;
5350 count--;
5351 nchars++;
5352 fd_info[fd].flags &= ~FILE_LAST_CR;
5355 /* presence of a child_process structure means we are operating in
5356 non-blocking mode - otherwise we just call _read directly.
5357 Note that the child_process structure might be missing because
5358 reap_subprocess has been called; in this case the pipe is
5359 already broken, so calling _read on it is okay. */
5360 if (cp)
5362 int current_status = cp->status;
5364 switch (current_status)
5366 case STATUS_READ_FAILED:
5367 case STATUS_READ_ERROR:
5368 /* report normal EOF if nothing in buffer */
5369 if (nchars <= 0)
5370 fd_info[fd].flags |= FILE_AT_EOF;
5371 return nchars;
5373 case STATUS_READ_READY:
5374 case STATUS_READ_IN_PROGRESS:
5375 DebPrint (("sys_read called when read is in progress\n"));
5376 errno = EWOULDBLOCK;
5377 return -1;
5379 case STATUS_READ_SUCCEEDED:
5380 /* consume read-ahead char */
5381 *buffer++ = cp->chr;
5382 count--;
5383 nchars++;
5384 cp->status = STATUS_READ_ACKNOWLEDGED;
5385 ResetEvent (cp->char_avail);
5387 case STATUS_READ_ACKNOWLEDGED:
5388 break;
5390 default:
5391 DebPrint (("sys_read: bad status %d\n", current_status));
5392 errno = EBADF;
5393 return -1;
5396 if (fd_info[fd].flags & FILE_PIPE)
5398 PeekNamedPipe ((HANDLE) _get_osfhandle (fd), NULL, 0, NULL, &waiting, NULL);
5399 to_read = min (waiting, (DWORD) count);
5401 if (to_read > 0)
5402 nchars += _read (fd, buffer, to_read);
5404 else if (fd_info[fd].flags & FILE_SERIAL)
5406 HANDLE hnd = fd_info[fd].hnd;
5407 OVERLAPPED *ovl = &fd_info[fd].cp->ovl_read;
5408 DWORD err = 0;
5409 int rc = 0;
5410 COMMTIMEOUTS ct;
5412 if (count > 0)
5414 /* Configure timeouts for non-blocking read. */
5415 if (!GetCommTimeouts (hnd, &ct))
5417 errno = EIO;
5418 return -1;
5420 ct.ReadIntervalTimeout = MAXDWORD;
5421 ct.ReadTotalTimeoutMultiplier = 0;
5422 ct.ReadTotalTimeoutConstant = 0;
5423 if (!SetCommTimeouts (hnd, &ct))
5425 errno = EIO;
5426 return -1;
5429 if (!ResetEvent (ovl->hEvent))
5431 errno = EIO;
5432 return -1;
5434 if (!ReadFile (hnd, buffer, count, (DWORD*) &rc, ovl))
5436 if (GetLastError () != ERROR_IO_PENDING)
5438 errno = EIO;
5439 return -1;
5441 if (!GetOverlappedResult (hnd, ovl, (DWORD*) &rc, TRUE))
5443 errno = EIO;
5444 return -1;
5447 nchars += rc;
5450 else /* FILE_SOCKET */
5452 if (winsock_lib == NULL) abort ();
5454 /* do the equivalent of a non-blocking read */
5455 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONREAD, &waiting);
5456 if (waiting == 0 && nchars == 0)
5458 h_errno = errno = EWOULDBLOCK;
5459 return -1;
5462 if (waiting)
5464 /* always use binary mode for sockets */
5465 int res = pfn_recv (SOCK_HANDLE (fd), buffer, count, 0);
5466 if (res == SOCKET_ERROR)
5468 DebPrint (("sys_read.recv failed with error %d on socket %ld\n",
5469 pfn_WSAGetLastError (), SOCK_HANDLE (fd)));
5470 set_errno ();
5471 return -1;
5473 nchars += res;
5477 else
5479 int nread = _read (fd, buffer, count);
5480 if (nread >= 0)
5481 nchars += nread;
5482 else if (nchars == 0)
5483 nchars = nread;
5486 if (nchars <= 0)
5487 fd_info[fd].flags |= FILE_AT_EOF;
5488 /* Perform text mode translation if required. */
5489 else if ((fd_info[fd].flags & FILE_BINARY) == 0)
5491 nchars = crlf_to_lf (nchars, orig_buffer);
5492 /* If buffer contains only CR, return that. To be absolutely
5493 sure we should attempt to read the next char, but in
5494 practice a CR to be followed by LF would not appear by
5495 itself in the buffer. */
5496 if (nchars > 1 && orig_buffer[nchars - 1] == 0x0d)
5498 fd_info[fd].flags |= FILE_LAST_CR;
5499 nchars--;
5503 else
5504 nchars = _read (fd, buffer, count);
5506 return nchars;
5509 /* From w32xfns.c */
5510 extern HANDLE interrupt_handle;
5512 /* For now, don't bother with a non-blocking mode */
5514 sys_write (int fd, const void * buffer, unsigned int count)
5516 int nchars;
5518 if (fd < 0)
5520 errno = EBADF;
5521 return -1;
5524 if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET | FILE_SERIAL))
5526 if ((fd_info[fd].flags & FILE_WRITE) == 0)
5528 errno = EBADF;
5529 return -1;
5532 /* Perform text mode translation if required. */
5533 if ((fd_info[fd].flags & FILE_BINARY) == 0)
5535 char * tmpbuf = alloca (count * 2);
5536 unsigned char * src = (void *)buffer;
5537 unsigned char * dst = tmpbuf;
5538 int nbytes = count;
5540 while (1)
5542 unsigned char *next;
5543 /* copy next line or remaining bytes */
5544 next = _memccpy (dst, src, '\n', nbytes);
5545 if (next)
5547 /* copied one line ending with '\n' */
5548 int copied = next - dst;
5549 nbytes -= copied;
5550 src += copied;
5551 /* insert '\r' before '\n' */
5552 next[-1] = '\r';
5553 next[0] = '\n';
5554 dst = next + 1;
5555 count++;
5557 else
5558 /* copied remaining partial line -> now finished */
5559 break;
5561 buffer = tmpbuf;
5565 if (fd < MAXDESC && fd_info[fd].flags & FILE_SERIAL)
5567 HANDLE hnd = (HANDLE) _get_osfhandle (fd);
5568 OVERLAPPED *ovl = &fd_info[fd].cp->ovl_write;
5569 HANDLE wait_hnd[2] = { interrupt_handle, ovl->hEvent };
5570 DWORD active = 0;
5572 if (!WriteFile (hnd, buffer, count, (DWORD*) &nchars, ovl))
5574 if (GetLastError () != ERROR_IO_PENDING)
5576 errno = EIO;
5577 return -1;
5579 if (detect_input_pending ())
5580 active = MsgWaitForMultipleObjects (2, wait_hnd, FALSE, INFINITE,
5581 QS_ALLINPUT);
5582 else
5583 active = WaitForMultipleObjects (2, wait_hnd, FALSE, INFINITE);
5584 if (active == WAIT_OBJECT_0)
5585 { /* User pressed C-g, cancel write, then leave. Don't bother
5586 cleaning up as we may only get stuck in buggy drivers. */
5587 PurgeComm (hnd, PURGE_TXABORT | PURGE_TXCLEAR);
5588 CancelIo (hnd);
5589 errno = EIO;
5590 return -1;
5592 if (active == WAIT_OBJECT_0 + 1
5593 && !GetOverlappedResult (hnd, ovl, (DWORD*) &nchars, TRUE))
5595 errno = EIO;
5596 return -1;
5600 else if (fd < MAXDESC && fd_info[fd].flags & FILE_SOCKET)
5602 unsigned long nblock = 0;
5603 if (winsock_lib == NULL) abort ();
5605 /* TODO: implement select() properly so non-blocking I/O works. */
5606 /* For now, make sure the write blocks. */
5607 if (fd_info[fd].flags & FILE_NDELAY)
5608 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
5610 nchars = pfn_send (SOCK_HANDLE (fd), buffer, count, 0);
5612 /* Set the socket back to non-blocking if it was before,
5613 for other operations that support it. */
5614 if (fd_info[fd].flags & FILE_NDELAY)
5616 nblock = 1;
5617 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
5620 if (nchars == SOCKET_ERROR)
5622 DebPrint (("sys_write.send failed with error %d on socket %ld\n",
5623 pfn_WSAGetLastError (), SOCK_HANDLE (fd)));
5624 set_errno ();
5627 else
5629 /* Some networked filesystems don't like too large writes, so
5630 break them into smaller chunks. See the Comments section of
5631 the MSDN documentation of WriteFile for details behind the
5632 choice of the value of CHUNK below. See also the thread
5633 http://thread.gmane.org/gmane.comp.version-control.git/145294
5634 in the git mailing list. */
5635 const unsigned char *p = buffer;
5636 const unsigned chunk = 30 * 1024 * 1024;
5638 nchars = 0;
5639 while (count > 0)
5641 unsigned this_chunk = count < chunk ? count : chunk;
5642 int n = _write (fd, p, this_chunk);
5644 nchars += n;
5645 if (n < 0)
5647 nchars = n;
5648 break;
5650 else if (n < this_chunk)
5651 break;
5652 count -= n;
5653 p += n;
5657 return nchars;
5660 static void
5661 check_windows_init_file (void)
5663 extern int noninteractive, inhibit_window_system;
5665 /* A common indication that Emacs is not installed properly is when
5666 it cannot find the Windows installation file. If this file does
5667 not exist in the expected place, tell the user. */
5669 if (!noninteractive && !inhibit_window_system)
5671 extern Lisp_Object Vwindow_system, Vload_path, Qfile_exists_p;
5672 Lisp_Object objs[2];
5673 Lisp_Object full_load_path;
5674 Lisp_Object init_file;
5675 int fd;
5677 objs[0] = Vload_path;
5678 objs[1] = decode_env_path (0, (getenv ("EMACSLOADPATH")));
5679 full_load_path = Fappend (2, objs);
5680 init_file = build_string ("term/w32-win");
5681 fd = openp (full_load_path, init_file, Fget_load_suffixes (), NULL, Qnil);
5682 if (fd < 0)
5684 Lisp_Object load_path_print = Fprin1_to_string (full_load_path, Qnil);
5685 char *init_file_name = SDATA (init_file);
5686 char *load_path = SDATA (load_path_print);
5687 char *buffer = alloca (1024
5688 + strlen (init_file_name)
5689 + strlen (load_path));
5691 sprintf (buffer,
5692 "The Emacs Windows initialization file \"%s.el\" "
5693 "could not be found in your Emacs installation. "
5694 "Emacs checked the following directories for this file:\n"
5695 "\n%s\n\n"
5696 "When Emacs cannot find this file, it usually means that it "
5697 "was not installed properly, or its distribution file was "
5698 "not unpacked properly.\nSee the README.W32 file in the "
5699 "top-level Emacs directory for more information.",
5700 init_file_name, load_path);
5701 MessageBox (NULL,
5702 buffer,
5703 "Emacs Abort Dialog",
5704 MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
5705 /* Use the low-level Emacs abort. */
5706 #undef abort
5707 abort ();
5709 else
5711 _close (fd);
5716 void
5717 term_ntproc (void)
5719 /* shutdown the socket interface if necessary */
5720 term_winsock ();
5722 term_w32select ();
5725 void
5726 init_ntproc (void)
5728 /* Initialise the socket interface now if available and requested by
5729 the user by defining PRELOAD_WINSOCK; otherwise loading will be
5730 delayed until open-network-stream is called (w32-has-winsock can
5731 also be used to dynamically load or reload winsock).
5733 Conveniently, init_environment is called before us, so
5734 PRELOAD_WINSOCK can be set in the registry. */
5736 /* Always initialize this correctly. */
5737 winsock_lib = NULL;
5739 if (getenv ("PRELOAD_WINSOCK") != NULL)
5740 init_winsock (TRUE);
5742 /* Initial preparation for subprocess support: replace our standard
5743 handles with non-inheritable versions. */
5745 HANDLE parent;
5746 HANDLE stdin_save = INVALID_HANDLE_VALUE;
5747 HANDLE stdout_save = INVALID_HANDLE_VALUE;
5748 HANDLE stderr_save = INVALID_HANDLE_VALUE;
5750 parent = GetCurrentProcess ();
5752 /* ignore errors when duplicating and closing; typically the
5753 handles will be invalid when running as a gui program. */
5754 DuplicateHandle (parent,
5755 GetStdHandle (STD_INPUT_HANDLE),
5756 parent,
5757 &stdin_save,
5759 FALSE,
5760 DUPLICATE_SAME_ACCESS);
5762 DuplicateHandle (parent,
5763 GetStdHandle (STD_OUTPUT_HANDLE),
5764 parent,
5765 &stdout_save,
5767 FALSE,
5768 DUPLICATE_SAME_ACCESS);
5770 DuplicateHandle (parent,
5771 GetStdHandle (STD_ERROR_HANDLE),
5772 parent,
5773 &stderr_save,
5775 FALSE,
5776 DUPLICATE_SAME_ACCESS);
5778 fclose (stdin);
5779 fclose (stdout);
5780 fclose (stderr);
5782 if (stdin_save != INVALID_HANDLE_VALUE)
5783 _open_osfhandle ((long) stdin_save, O_TEXT);
5784 else
5785 _open ("nul", O_TEXT | O_NOINHERIT | O_RDONLY);
5786 _fdopen (0, "r");
5788 if (stdout_save != INVALID_HANDLE_VALUE)
5789 _open_osfhandle ((long) stdout_save, O_TEXT);
5790 else
5791 _open ("nul", O_TEXT | O_NOINHERIT | O_WRONLY);
5792 _fdopen (1, "w");
5794 if (stderr_save != INVALID_HANDLE_VALUE)
5795 _open_osfhandle ((long) stderr_save, O_TEXT);
5796 else
5797 _open ("nul", O_TEXT | O_NOINHERIT | O_WRONLY);
5798 _fdopen (2, "w");
5801 /* unfortunately, atexit depends on implementation of malloc */
5802 /* atexit (term_ntproc); */
5803 signal (SIGABRT, term_ntproc);
5805 /* determine which drives are fixed, for GetCachedVolumeInformation */
5807 /* GetDriveType must have trailing backslash. */
5808 char drive[] = "A:\\";
5810 /* Loop over all possible drive letters */
5811 while (*drive <= 'Z')
5813 /* Record if this drive letter refers to a fixed drive. */
5814 fixed_drives[DRIVE_INDEX (*drive)] =
5815 (GetDriveType (drive) == DRIVE_FIXED);
5817 (*drive)++;
5820 /* Reset the volume info cache. */
5821 volume_cache = NULL;
5824 /* Check to see if Emacs has been installed correctly. */
5825 check_windows_init_file ();
5829 shutdown_handler ensures that buffers' autosave files are
5830 up to date when the user logs off, or the system shuts down.
5832 static BOOL WINAPI
5833 shutdown_handler (DWORD type)
5835 /* Ctrl-C and Ctrl-Break are already suppressed, so don't handle them. */
5836 if (type == CTRL_CLOSE_EVENT /* User closes console window. */
5837 || type == CTRL_LOGOFF_EVENT /* User logs off. */
5838 || type == CTRL_SHUTDOWN_EVENT) /* User shutsdown. */
5840 /* Shut down cleanly, making sure autosave files are up to date. */
5841 shut_down_emacs (0, 0, Qnil);
5844 /* Allow other handlers to handle this signal. */
5845 return FALSE;
5849 globals_of_w32 is used to initialize those global variables that
5850 must always be initialized on startup even when the global variable
5851 initialized is non zero (see the function main in emacs.c).
5853 void
5854 globals_of_w32 (void)
5856 HMODULE kernel32 = GetModuleHandle ("kernel32.dll");
5858 get_process_times_fn = (GetProcessTimes_Proc)
5859 GetProcAddress (kernel32, "GetProcessTimes");
5861 g_b_init_is_windows_9x = 0;
5862 g_b_init_open_process_token = 0;
5863 g_b_init_get_token_information = 0;
5864 g_b_init_lookup_account_sid = 0;
5865 g_b_init_get_sid_sub_authority = 0;
5866 g_b_init_get_sid_sub_authority_count = 0;
5867 g_b_init_get_file_security = 0;
5868 g_b_init_get_security_descriptor_owner = 0;
5869 g_b_init_get_security_descriptor_group = 0;
5870 g_b_init_is_valid_sid = 0;
5871 g_b_init_create_toolhelp32_snapshot = 0;
5872 g_b_init_process32_first = 0;
5873 g_b_init_process32_next = 0;
5874 g_b_init_open_thread_token = 0;
5875 g_b_init_impersonate_self = 0;
5876 g_b_init_revert_to_self = 0;
5877 g_b_init_get_process_memory_info = 0;
5878 g_b_init_get_process_working_set_size = 0;
5879 g_b_init_global_memory_status = 0;
5880 g_b_init_global_memory_status_ex = 0;
5881 g_b_init_equal_sid = 0;
5882 g_b_init_copy_sid = 0;
5883 g_b_init_get_length_sid = 0;
5884 g_b_init_get_native_system_info = 0;
5885 g_b_init_get_system_times = 0;
5886 num_of_processors = 0;
5887 /* The following sets a handler for shutdown notifications for
5888 console apps. This actually applies to Emacs in both console and
5889 GUI modes, since we had to fool windows into thinking emacs is a
5890 console application to get console mode to work. */
5891 SetConsoleCtrlHandler (shutdown_handler, TRUE);
5893 /* "None" is the default group name on standalone workstations. */
5894 strcpy (dflt_group_name, "None");
5897 /* For make-serial-process */
5899 serial_open (char *port)
5901 HANDLE hnd;
5902 child_process *cp;
5903 int fd = -1;
5905 hnd = CreateFile (port, GENERIC_READ | GENERIC_WRITE, 0, 0,
5906 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
5907 if (hnd == INVALID_HANDLE_VALUE)
5908 error ("Could not open %s", port);
5909 fd = (int) _open_osfhandle ((int) hnd, 0);
5910 if (fd == -1)
5911 error ("Could not open %s", port);
5913 cp = new_child ();
5914 if (!cp)
5915 error ("Could not create child process");
5916 cp->fd = fd;
5917 cp->status = STATUS_READ_ACKNOWLEDGED;
5918 fd_info[ fd ].hnd = hnd;
5919 fd_info[ fd ].flags |=
5920 FILE_READ | FILE_WRITE | FILE_BINARY | FILE_SERIAL;
5921 if (fd_info[ fd ].cp != NULL)
5923 error ("fd_info[fd = %d] is already in use", fd);
5925 fd_info[ fd ].cp = cp;
5926 cp->ovl_read.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
5927 if (cp->ovl_read.hEvent == NULL)
5928 error ("Could not create read event");
5929 cp->ovl_write.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
5930 if (cp->ovl_write.hEvent == NULL)
5931 error ("Could not create write event");
5933 return fd;
5936 /* For serial-process-configure */
5937 void
5938 serial_configure (struct Lisp_Process *p,
5939 Lisp_Object contact)
5941 Lisp_Object childp2 = Qnil;
5942 Lisp_Object tem = Qnil;
5943 HANDLE hnd;
5944 DCB dcb;
5945 COMMTIMEOUTS ct;
5946 char summary[4] = "???"; /* This usually becomes "8N1". */
5948 if ((fd_info[ p->outfd ].flags & FILE_SERIAL) == 0)
5949 error ("Not a serial process");
5950 hnd = fd_info[ p->outfd ].hnd;
5952 childp2 = Fcopy_sequence (p->childp);
5954 /* Initialize timeouts for blocking read and blocking write. */
5955 if (!GetCommTimeouts (hnd, &ct))
5956 error ("GetCommTimeouts() failed");
5957 ct.ReadIntervalTimeout = 0;
5958 ct.ReadTotalTimeoutMultiplier = 0;
5959 ct.ReadTotalTimeoutConstant = 0;
5960 ct.WriteTotalTimeoutMultiplier = 0;
5961 ct.WriteTotalTimeoutConstant = 0;
5962 if (!SetCommTimeouts (hnd, &ct))
5963 error ("SetCommTimeouts() failed");
5964 /* Read port attributes and prepare default configuration. */
5965 memset (&dcb, 0, sizeof (dcb));
5966 dcb.DCBlength = sizeof (DCB);
5967 if (!GetCommState (hnd, &dcb))
5968 error ("GetCommState() failed");
5969 dcb.fBinary = TRUE;
5970 dcb.fNull = FALSE;
5971 dcb.fAbortOnError = FALSE;
5972 /* dcb.XonLim and dcb.XoffLim are set by GetCommState() */
5973 dcb.ErrorChar = 0;
5974 dcb.EofChar = 0;
5975 dcb.EvtChar = 0;
5977 /* Configure speed. */
5978 if (!NILP (Fplist_member (contact, QCspeed)))
5979 tem = Fplist_get (contact, QCspeed);
5980 else
5981 tem = Fplist_get (p->childp, QCspeed);
5982 CHECK_NUMBER (tem);
5983 dcb.BaudRate = XINT (tem);
5984 childp2 = Fplist_put (childp2, QCspeed, tem);
5986 /* Configure bytesize. */
5987 if (!NILP (Fplist_member (contact, QCbytesize)))
5988 tem = Fplist_get (contact, QCbytesize);
5989 else
5990 tem = Fplist_get (p->childp, QCbytesize);
5991 if (NILP (tem))
5992 tem = make_number (8);
5993 CHECK_NUMBER (tem);
5994 if (XINT (tem) != 7 && XINT (tem) != 8)
5995 error (":bytesize must be nil (8), 7, or 8");
5996 dcb.ByteSize = XINT (tem);
5997 summary[0] = XINT (tem) + '0';
5998 childp2 = Fplist_put (childp2, QCbytesize, tem);
6000 /* Configure parity. */
6001 if (!NILP (Fplist_member (contact, QCparity)))
6002 tem = Fplist_get (contact, QCparity);
6003 else
6004 tem = Fplist_get (p->childp, QCparity);
6005 if (!NILP (tem) && !EQ (tem, Qeven) && !EQ (tem, Qodd))
6006 error (":parity must be nil (no parity), `even', or `odd'");
6007 dcb.fParity = FALSE;
6008 dcb.Parity = NOPARITY;
6009 dcb.fErrorChar = FALSE;
6010 if (NILP (tem))
6012 summary[1] = 'N';
6014 else if (EQ (tem, Qeven))
6016 summary[1] = 'E';
6017 dcb.fParity = TRUE;
6018 dcb.Parity = EVENPARITY;
6019 dcb.fErrorChar = TRUE;
6021 else if (EQ (tem, Qodd))
6023 summary[1] = 'O';
6024 dcb.fParity = TRUE;
6025 dcb.Parity = ODDPARITY;
6026 dcb.fErrorChar = TRUE;
6028 childp2 = Fplist_put (childp2, QCparity, tem);
6030 /* Configure stopbits. */
6031 if (!NILP (Fplist_member (contact, QCstopbits)))
6032 tem = Fplist_get (contact, QCstopbits);
6033 else
6034 tem = Fplist_get (p->childp, QCstopbits);
6035 if (NILP (tem))
6036 tem = make_number (1);
6037 CHECK_NUMBER (tem);
6038 if (XINT (tem) != 1 && XINT (tem) != 2)
6039 error (":stopbits must be nil (1 stopbit), 1, or 2");
6040 summary[2] = XINT (tem) + '0';
6041 if (XINT (tem) == 1)
6042 dcb.StopBits = ONESTOPBIT;
6043 else if (XINT (tem) == 2)
6044 dcb.StopBits = TWOSTOPBITS;
6045 childp2 = Fplist_put (childp2, QCstopbits, tem);
6047 /* Configure flowcontrol. */
6048 if (!NILP (Fplist_member (contact, QCflowcontrol)))
6049 tem = Fplist_get (contact, QCflowcontrol);
6050 else
6051 tem = Fplist_get (p->childp, QCflowcontrol);
6052 if (!NILP (tem) && !EQ (tem, Qhw) && !EQ (tem, Qsw))
6053 error (":flowcontrol must be nil (no flowcontrol), `hw', or `sw'");
6054 dcb.fOutxCtsFlow = FALSE;
6055 dcb.fOutxDsrFlow = FALSE;
6056 dcb.fDtrControl = DTR_CONTROL_DISABLE;
6057 dcb.fDsrSensitivity = FALSE;
6058 dcb.fTXContinueOnXoff = FALSE;
6059 dcb.fOutX = FALSE;
6060 dcb.fInX = FALSE;
6061 dcb.fRtsControl = RTS_CONTROL_DISABLE;
6062 dcb.XonChar = 17; /* Control-Q */
6063 dcb.XoffChar = 19; /* Control-S */
6064 if (NILP (tem))
6066 /* Already configured. */
6068 else if (EQ (tem, Qhw))
6070 dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
6071 dcb.fOutxCtsFlow = TRUE;
6073 else if (EQ (tem, Qsw))
6075 dcb.fOutX = TRUE;
6076 dcb.fInX = TRUE;
6078 childp2 = Fplist_put (childp2, QCflowcontrol, tem);
6080 /* Activate configuration. */
6081 if (!SetCommState (hnd, &dcb))
6082 error ("SetCommState() failed");
6084 childp2 = Fplist_put (childp2, QCsummary, build_string (summary));
6085 p->childp = childp2;
6088 /* end of w32.c */
6090 /* arch-tag: 90442dd3-37be-482b-b272-ac752e3049f1
6091 (do not change this comment) */