1 /* Utility and Unix shadow routines for GNU Emacs support programs on NT.
2
3 Copyright (C) 1994, 2001-2021 Free Software Foundation, Inc.
4
5 Author: Geoff Voelker (voelker@cs.washington.edu)
6 Created: 10-8-94
7
8 This file is part of GNU Emacs.
9
10 GNU Emacs is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or (at
13 your option) any later version.
14
15 GNU Emacs is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
22
23 #define DEFER_MS_W32_H
24 #include <config.h>
25
26 #include <windows.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <time.h>
30 #include <direct.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <errno.h>
34 #include <ctype.h>
35 #include <sys/timeb.h>
36 #include <mbstring.h>
37 #include <locale.h>
38
39 #include <nl_types.h>
40 #include <langinfo.h>
41
42 #include "ntlib.h"
43
44 char *sys_ctime (const time_t *);
45 FILE *sys_fopen (const char *, const char *);
46 int sys_mkdir (const char *, mode_t);
47 int sys_chdir (const char *);
48 int mkostemp (char *, int);
49 int sys_rename (const char *, const char *);
50 int sys_open (const char *, int, int);
51
52 /* MinGW64 defines _TIMEZONE_DEFINED and defines 'struct timespec' in
53 its system headers. */
54 #ifndef _TIMEZONE_DEFINED
55 struct timezone
56 {
57 int tz_minuteswest; /* minutes west of Greenwich */
58 int tz_dsttime; /* type of dst correction */
59 };
60 #endif
61
62 #define MAXPATHLEN _MAX_PATH
63
64 /* Emulate sleep...we could have done this with a define, but that
65 would necessitate including windows.h in the files that used it.
66 This is much easier. */
67 unsigned
sleep(unsigned seconds)68 sleep (unsigned seconds)
69 {
70 Sleep (seconds * 1000);
71 return 0;
72 }
73
74 /* Get the current working directory. */
75 char *
getwd(char * dir)76 getwd (char *dir)
77 {
78 if (GetCurrentDirectory (MAXPATHLEN, dir) > 0)
79 return dir;
80 return NULL;
81 }
82
83 static HANDLE getppid_parent;
84 static int getppid_ppid;
85
86 int
getppid(void)87 getppid (void)
88 {
89 char *ppid;
90 DWORD result;
91
92 ppid = getenv ("EM_PARENT_PROCESS_ID");
93 if (!ppid)
94 {
95 printf ("no pid.\n");
96 return 0;
97 }
98 else
99 {
100 getppid_ppid = atoi (ppid);
101 }
102
103 if (!getppid_parent)
104 {
105 getppid_parent = OpenProcess (SYNCHRONIZE, FALSE, atoi (ppid));
106 if (!getppid_parent)
107 {
108 printf ("Failed to open handle to parent process: %lu\n",
109 GetLastError ());
110 exit (1);
111 }
112 }
113
114 result = WaitForSingleObject (getppid_parent, 0);
115 switch (result)
116 {
117 case WAIT_TIMEOUT:
118 /* The parent is still alive. */
119 return getppid_ppid;
120 case WAIT_OBJECT_0:
121 /* The parent is gone. Return the pid of Unix init (1). */
122 return 1;
123 case WAIT_FAILED:
124 default:
125 printf ("Checking parent status failed: %lu\n", GetLastError ());
126 exit (1);
127 }
128 }
129
130 char *
getlogin(void)131 getlogin (void)
132 {
133 static char user_name[256];
134 DWORD length = sizeof (user_name);
135
136 if (GetUserName (user_name, &length))
137 return user_name;
138 return NULL;
139 }
140
141 char *
cuserid(char * s)142 cuserid (char * s)
143 {
144 char * name = getlogin ();
145 if (s)
146 return strcpy (s, name ? name : "");
147 return name;
148 }
149
150 unsigned
getuid(void)151 getuid (void)
152 {
153 return 0;
154 }
155
156 unsigned
geteuid(void)157 geteuid (void)
158 {
159 return getuid ();
160 }
161
162 unsigned
getgid(void)163 getgid (void)
164 {
165 return 0;
166 }
167
168 unsigned
getegid(void)169 getegid (void)
170 {
171 return 0;
172 }
173
174 int
setuid(unsigned uid)175 setuid (unsigned uid)
176 {
177 return 0;
178 }
179
180 int
setregid(unsigned rgid,unsigned gid)181 setregid (unsigned rgid, unsigned gid)
182 {
183 return 0;
184 }
185
186 struct passwd *
getpwuid(unsigned uid)187 getpwuid (unsigned uid)
188 {
189 return NULL;
190 }
191
192 char *
getpass(const char * prompt)193 getpass (const char * prompt)
194 {
195 static char input[256];
196 HANDLE in;
197 HANDLE err;
198 DWORD count;
199
200 in = GetStdHandle (STD_INPUT_HANDLE);
201 err = GetStdHandle (STD_ERROR_HANDLE);
202
203 if (in == INVALID_HANDLE_VALUE || err == INVALID_HANDLE_VALUE)
204 return NULL;
205
206 if (WriteFile (err, prompt, strlen (prompt), &count, NULL))
207 {
208 int istty = (GetFileType (in) == FILE_TYPE_CHAR);
209 DWORD old_flags;
210 int rc;
211
212 if (istty)
213 {
214 if (GetConsoleMode (in, &old_flags))
215 SetConsoleMode (in, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
216 else
217 istty = 0;
218 }
219 rc = ReadFile (in, input, sizeof (input), &count, NULL);
220 if (count >= 2 && input[count - 2] == '\r')
221 input[count - 2] = '\0';
222 else
223 {
224 char buf[256];
225 while (ReadFile (in, buf, sizeof (buf), &count, NULL) > 0)
226 if (count >= 2 && buf[count - 2] == '\r')
227 break;
228 }
229 WriteFile (err, "\r\n", 2, &count, NULL);
230 if (istty)
231 SetConsoleMode (in, old_flags);
232 if (rc)
233 return input;
234 }
235
236 return NULL;
237 }
238
239 int
fchown(int fd,unsigned uid,unsigned gid)240 fchown (int fd, unsigned uid, unsigned gid)
241 {
242 return 0;
243 }
244
245 FILE *
sys_fopen(const char * path,const char * mode)246 sys_fopen (const char * path, const char * mode)
247 {
248 return fopen (path, mode);
249 }
250
251 int
sys_chdir(const char * path)252 sys_chdir (const char * path)
253 {
254 return _chdir (path);
255 }
256
257 int
sys_mkdir(const char * path,mode_t mode)258 sys_mkdir (const char * path, mode_t mode)
259 {
260 return _mkdir (path);
261 }
262
263 static FILETIME utc_base_ft;
264 static long double utc_base;
265 static int init = 0;
266
267 static time_t
convert_time(FILETIME ft)268 convert_time (FILETIME ft)
269 {
270 long double ret;
271
272 if (CompareFileTime (&ft, &utc_base_ft) < 0)
273 return 0;
274
275 ret = (long double) ft.dwHighDateTime
276 * 4096.0L * 1024.0L * 1024.0L + ft.dwLowDateTime;
277 ret -= utc_base;
278 return (time_t) (ret * 1e-7L);
279 }
280
281 static int
is_exec(const char * name)282 is_exec (const char * name)
283 {
284 char * p = strrchr (name, '.');
285 return
286 (p != NULL
287 && (stricmp (p, ".exe") == 0 ||
288 stricmp (p, ".com") == 0 ||
289 stricmp (p, ".bat") == 0 ||
290 stricmp (p, ".cmd") == 0));
291 }
292
293 /* We need stat/fsfat below because nt/inc/sys/stat.h defines struct
294 stat that is incompatible with the MS run-time libraries. */
295 int
stat(const char * path,struct stat * buf)296 stat (const char * path, struct stat * buf)
297 {
298 WIN32_FIND_DATA wfd;
299 HANDLE fh;
300 int permission;
301 int len;
302 int rootdir = FALSE;
303 char *name = alloca (FILENAME_MAX);
304
305 if (!init)
306 {
307 /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */
308 SYSTEMTIME st;
309
310 st.wYear = 1970;
311 st.wMonth = 1;
312 st.wDay = 1;
313 st.wHour = 0;
314 st.wMinute = 0;
315 st.wSecond = 0;
316 st.wMilliseconds = 0;
317
318 SystemTimeToFileTime (&st, &utc_base_ft);
319 utc_base = (long double) utc_base_ft.dwHighDateTime
320 * 4096.0L * 1024.0L * 1024.0L + utc_base_ft.dwLowDateTime;
321 init = 1;
322 }
323
324 if (path == NULL || buf == NULL || *path == '\0')
325 {
326 errno = EFAULT;
327 return -1;
328 }
329 if (_mbspbrk (path, "*?|<>\""))
330 {
331 errno = ENOENT;
332 return -1;
333 }
334
335 strcpy (name, path);
336 /* Remove trailing directory separator, unless name is the root
337 directory of a drive in which case ensure there is a trailing
338 separator. */
339 len = strlen (name);
340 rootdir = IS_DIRECTORY_SEP (name[0])
341 || (len == 3 && name[1] == ':' && IS_DIRECTORY_SEP (name[2]));
342 if (rootdir)
343 {
344 if (GetDriveType (name) < 2)
345 {
346 errno = ENOENT;
347 return -1;
348 }
349 memset (&wfd, 0, sizeof (wfd));
350 wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
351 wfd.ftCreationTime = utc_base_ft;
352 wfd.ftLastAccessTime = utc_base_ft;
353 wfd.ftLastWriteTime = utc_base_ft;
354 strcpy (wfd.cFileName, name);
355 }
356 else
357 {
358 if (IS_DIRECTORY_SEP (name[len-1]))
359 name[len - 1] = 0;
360
361 fh = FindFirstFile (name, &wfd);
362 if (fh == INVALID_HANDLE_VALUE)
363 {
364 errno = ENOENT;
365 return -1;
366 }
367 FindClose (fh);
368 }
369 buf->st_mode = (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ?
370 S_IFDIR : S_IFREG;
371 buf->st_nlink = 1;
372 buf->st_ino = 0;
373
374 if (name[0] && name[1] == ':')
375 buf->st_dev = tolower (name[0]) - 'a' + 1;
376 else
377 buf->st_dev = _getdrive ();
378 buf->st_rdev = buf->st_dev;
379
380 buf->st_size = wfd.nFileSizeHigh;
381 buf->st_size <<= 32;
382 buf->st_size += wfd.nFileSizeLow;
383
384 /* Convert timestamps to Unix format. */
385 buf->st_mtime = convert_time (wfd.ftLastWriteTime);
386 buf->st_atime = convert_time (wfd.ftLastAccessTime);
387 if (buf->st_atime == 0) buf->st_atime = buf->st_mtime;
388 buf->st_ctime = convert_time (wfd.ftCreationTime);
389 if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime;
390
391 /* determine rwx permissions */
392 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
393 permission = S_IREAD;
394 else
395 permission = S_IREAD | S_IWRITE;
396
397 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
398 permission |= S_IEXEC;
399 else if (is_exec (name))
400 permission |= S_IEXEC;
401
402 buf->st_mode |= permission | (permission >> 3) | (permission >> 6);
403
404 return 0;
405 }
406
407 int
lstat(const char * path,struct stat * buf)408 lstat (const char * path, struct stat * buf)
409 {
410 return stat (path, buf);
411 }
412
413 int
fstat(int desc,struct stat * buf)414 fstat (int desc, struct stat * buf)
415 {
416 HANDLE fh = (HANDLE) _get_osfhandle (desc);
417 BY_HANDLE_FILE_INFORMATION info;
418 unsigned __int64 fake_inode;
419 int permission;
420
421 if (!init)
422 {
423 /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */
424 SYSTEMTIME st;
425
426 st.wYear = 1970;
427 st.wMonth = 1;
428 st.wDay = 1;
429 st.wHour = 0;
430 st.wMinute = 0;
431 st.wSecond = 0;
432 st.wMilliseconds = 0;
433
434 SystemTimeToFileTime (&st, &utc_base_ft);
435 utc_base = (long double) utc_base_ft.dwHighDateTime
436 * 4096.0L * 1024.0L * 1024.0L + utc_base_ft.dwLowDateTime;
437 init = 1;
438 }
439
440 switch (GetFileType (fh) & ~FILE_TYPE_REMOTE)
441 {
442 case FILE_TYPE_DISK:
443 buf->st_mode = S_IFREG;
444 if (!GetFileInformationByHandle (fh, &info))
445 {
446 errno = EACCES;
447 return -1;
448 }
449 break;
450 case FILE_TYPE_PIPE:
451 buf->st_mode = S_IFIFO;
452 goto non_disk;
453 case FILE_TYPE_CHAR:
454 case FILE_TYPE_UNKNOWN:
455 default:
456 buf->st_mode = S_IFCHR;
457 non_disk:
458 memset (&info, 0, sizeof (info));
459 info.dwFileAttributes = 0;
460 info.ftCreationTime = utc_base_ft;
461 info.ftLastAccessTime = utc_base_ft;
462 info.ftLastWriteTime = utc_base_ft;
463 }
464
465 if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
466 buf->st_mode = S_IFDIR;
467
468 buf->st_nlink = info.nNumberOfLinks;
469 /* Might as well use file index to fake inode values, but this
470 is not guaranteed to be unique unless we keep a handle open
471 all the time. */
472 fake_inode = info.nFileIndexHigh;
473 fake_inode <<= 32;
474 fake_inode += info.nFileIndexLow;
475 buf->st_ino = fake_inode;
476
477 buf->st_dev = info.dwVolumeSerialNumber;
478 buf->st_rdev = info.dwVolumeSerialNumber;
479
480 buf->st_size = info.nFileSizeHigh;
481 buf->st_size <<= 32;
482 buf->st_size += info.nFileSizeLow;
483
484 /* Convert timestamps to Unix format. */
485 buf->st_mtime = convert_time (info.ftLastWriteTime);
486 buf->st_atime = convert_time (info.ftLastAccessTime);
487 if (buf->st_atime == 0) buf->st_atime = buf->st_mtime;
488 buf->st_ctime = convert_time (info.ftCreationTime);
489 if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime;
490
491 /* determine rwx permissions */
492 if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
493 permission = S_IREAD;
494 else
495 permission = S_IREAD | S_IWRITE;
496
497 if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
498 permission |= S_IEXEC;
499
500 buf->st_mode |= permission | (permission >> 3) | (permission >> 6);
501
502 return 0;
503 }
504
505 /* On Windows, you cannot rename into an existing file. */
506 int
sys_rename(const char * from,const char * to)507 sys_rename (const char *from, const char *to)
508 {
509 int retval = rename (from, to);
510
511 if (retval < 0 && errno == EEXIST)
512 {
513 if (unlink (to) == 0)
514 retval = rename (from, to);
515 }
516 return retval;
517 }
518
519 int
sys_open(const char * path,int oflag,int mode)520 sys_open (const char * path, int oflag, int mode)
521 {
522 return _open (path, oflag, mode);
523 }
524
525 /* Emulation of nl_langinfo that supports only CODESET.
526 Used in Gnulib regex.c. */
527 char *
nl_langinfo(nl_item item)528 nl_langinfo (nl_item item)
529 {
530 switch (item)
531 {
532 case CODESET:
533 {
534 /* Shamelessly stolen from Gnulib's nl_langinfo.c, modulo
535 CPP directives. */
536 static char buf[2 + 10 + 1];
537 char const *locale = setlocale (LC_CTYPE, NULL);
538 char *codeset = buf;
539 size_t codesetlen;
540 codeset[0] = '\0';
541
542 if (locale && locale[0])
543 {
544 /* If the locale name contains an encoding after the
545 dot, return it. */
546 char *dot = strchr (locale, '.');
547
548 if (dot)
549 {
550 /* Look for the possible @... trailer and remove it,
551 if any. */
552 char *codeset_start = dot + 1;
553 char const *modifier = strchr (codeset_start, '@');
554
555 if (! modifier)
556 codeset = codeset_start;
557 else
558 {
559 codesetlen = modifier - codeset_start;
560 if (codesetlen < sizeof buf)
561 {
562 codeset = memcpy (buf, codeset_start, codesetlen);
563 codeset[codesetlen] = '\0';
564 }
565 }
566 }
567 }
568 /* If setlocale is successful, it returns the number of the
569 codepage, as a string. Otherwise, fall back on Windows
570 API GetACP, which returns the locale's codepage as a
571 number (although this doesn't change according to what
572 the 'setlocale' call specified). Either way, prepend
573 "CP" to make it a valid codeset name. */
574 codesetlen = strlen (codeset);
575 if (0 < codesetlen && codesetlen < sizeof buf - 2)
576 memmove (buf + 2, codeset, codesetlen + 1);
577 else
578 sprintf (buf + 2, "%u", GetACP ());
579 codeset = memcpy (buf, "CP", 2);
580
581 return codeset;
582 }
583 default:
584 return (char *) "";
585 }
586 }
587