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