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