declare smobs in alloc.c
[bpt/emacs.git] / lib-src / ntlib.c
CommitLineData
95ed0025 1/* Utility and Unix shadow routines for GNU Emacs support programs on NT.
59a428eb 2
ba318903 3Copyright (C) 1994, 2001-2014 Free Software Foundation, Inc.
59a428eb
GM
4
5Author: Geoff Voelker (voelker@cs.washington.edu)
6Created: 10-8-94
95ed0025 7
3b7ad313
EN
8This file is part of GNU Emacs.
9
294981c7 10GNU Emacs is free software: you can redistribute it and/or modify
3b7ad313 11it under the terms of the GNU General Public License as published by
294981c7
GM
12the Free Software Foundation, either version 3 of the License, or
13(at your option) any later version.
3b7ad313
EN
14
15GNU Emacs is distributed in the hope that it will be useful,
16but WITHOUT ANY WARRANTY; without even the implied warranty of
17MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18GNU General Public License for more details.
19
20You should have received a copy of the GNU General Public License
59a428eb 21along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
95ed0025
RS
22
23#include <windows.h>
24#include <stdlib.h>
25#include <stdio.h>
14f29224
GV
26#include <time.h>
27#include <direct.h>
10fea9c4
EZ
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <errno.h>
9c88f339 31#include <ctype.h>
62aba0d4 32#include <sys/timeb.h>
27067208 33#include <mbstring.h>
14f29224
GV
34
35#include "ntlib.h"
95ed0025 36
b88b62de
EZ
37/* MinGW64 defines _TIMEZONE_DEFINED and defines 'struct timespec' in
38 its system headers. */
39#ifndef _TIMEZONE_DEFINED
62aba0d4
FP
40struct timezone
41{
42 int tz_minuteswest; /* minutes west of Greenwich */
43 int tz_dsttime; /* type of dst correction */
44};
b88b62de 45#endif
62aba0d4 46
95ed0025
RS
47#define MAXPATHLEN _MAX_PATH
48
49/* Emulate sleep...we could have done this with a define, but that
50 would necessitate including windows.h in the files that used it.
51 This is much easier. */
7c4026b6
EZ
52unsigned
53sleep (unsigned seconds)
95ed0025
RS
54{
55 Sleep (seconds * 1000);
7c4026b6 56 return 0;
95ed0025
RS
57}
58
59/* Get the current working directory. */
133319ab 60char *
95ed0025
RS
61getwd (char *dir)
62{
14f29224
GV
63 if (GetCurrentDirectory (MAXPATHLEN, dir) > 0)
64 return dir;
65 return NULL;
95ed0025
RS
66}
67
68static HANDLE getppid_parent;
69static int getppid_ppid;
70
71int
7c3320d8 72getppid (void)
95ed0025
RS
73{
74 char *ppid;
75 DWORD result;
76
97e3c91b 77 ppid = getenv ("EM_PARENT_PROCESS_ID");
177c0ea7 78 if (!ppid)
95ed0025 79 {
7c3320d8 80 printf ("no pid.\n");
95ed0025 81 return 0;
177c0ea7
JB
82 }
83 else
95ed0025
RS
84 {
85 getppid_ppid = atoi (ppid);
86 }
87
177c0ea7 88 if (!getppid_parent)
95ed0025 89 {
7c3320d8 90 getppid_parent = OpenProcess (SYNCHRONIZE, FALSE, atoi (ppid));
177c0ea7 91 if (!getppid_parent)
95ed0025
RS
92 {
93 printf ("Failed to open handle to parent process: %d\n",
7c3320d8 94 GetLastError ());
95ed0025
RS
95 exit (1);
96 }
97 }
98
99 result = WaitForSingleObject (getppid_parent, 0);
177c0ea7 100 switch (result)
95ed0025
RS
101 {
102 case WAIT_TIMEOUT:
103 /* The parent is still alive. */
104 return getppid_ppid;
105 case WAIT_OBJECT_0:
106 /* The parent is gone. Return the pid of Unix init (1). */
107 return 1;
108 case WAIT_FAILED:
109 default:
7c3320d8 110 printf ("Checking parent status failed: %d\n", GetLastError ());
95ed0025
RS
111 exit (1);
112 }
113}
14f29224
GV
114
115char *
7c3320d8 116getlogin (void)
14f29224
GV
117{
118 static char user_name[256];
119 DWORD length = sizeof (user_name);
120
121 if (GetUserName (user_name, &length))
122 return user_name;
123 return NULL;
124}
125
126char *
127cuserid (char * s)
128{
129 char * name = getlogin ();
130 if (s)
131 return strcpy (s, name ? name : "");
132 return name;
133}
134
22749e9a 135unsigned
7c3320d8 136getuid (void)
14f29224
GV
137{
138 return 0;
139}
140
7c4026b6
EZ
141unsigned
142geteuid (void)
143{
144 return getuid ();
145}
146
b372fceb 147unsigned
7c3320d8 148getgid (void)
b372fceb
JB
149{
150 return 0;
151}
152
153unsigned
7c3320d8 154getegid (void)
b372fceb
JB
155{
156 return 0;
157}
158
14f29224 159int
22749e9a 160setuid (unsigned uid)
14f29224
GV
161{
162 return 0;
163}
164
b372fceb 165int
f915f0f7 166setregid (unsigned rgid, unsigned gid)
b372fceb
JB
167{
168 return 0;
169}
170
14f29224 171struct passwd *
22749e9a 172getpwuid (unsigned uid)
14f29224
GV
173{
174 return NULL;
175}
176
177char *
178getpass (const char * prompt)
179{
180 static char input[256];
181 HANDLE in;
182 HANDLE err;
183 DWORD count;
184
185 in = GetStdHandle (STD_INPUT_HANDLE);
186 err = GetStdHandle (STD_ERROR_HANDLE);
187
188 if (in == INVALID_HANDLE_VALUE || err == INVALID_HANDLE_VALUE)
189 return NULL;
190
191 if (WriteFile (err, prompt, strlen (prompt), &count, NULL))
192 {
193 int istty = (GetFileType (in) == FILE_TYPE_CHAR);
194 DWORD old_flags;
195 int rc;
196
197 if (istty)
198 {
199 if (GetConsoleMode (in, &old_flags))
200 SetConsoleMode (in, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
201 else
202 istty = 0;
203 }
204 rc = ReadFile (in, input, sizeof (input), &count, NULL);
205 if (count >= 2 && input[count - 2] == '\r')
206 input[count - 2] = '\0';
207 else
208 {
209 char buf[256];
210 while (ReadFile (in, buf, sizeof (buf), &count, NULL) > 0)
211 if (count >= 2 && buf[count - 2] == '\r')
212 break;
213 }
214 WriteFile (err, "\r\n", 2, &count, NULL);
215 if (istty)
216 SetConsoleMode (in, old_flags);
217 if (rc)
218 return input;
219 }
220
221 return NULL;
222}
223
62aba0d4
FP
224/* This is needed because lib/gettime.c calls gettimeofday, which MSVC
225 doesn't have. Copied from w32.c. */
226void
227gettimeofday (struct timeval *tv, struct timezone *tz)
228{
229 struct _timeb tb;
230 _ftime (&tb);
231
232 tv->tv_sec = tb.time;
233 tv->tv_usec = tb.millitm * 1000L;
234 /* Implementation note: _ftime sometimes doesn't update the dstflag
235 according to the new timezone when the system timezone is
236 changed. We could fix that by using GetSystemTime and
237 GetTimeZoneInformation, but that doesn't seem necessary, since
238 Emacs always calls gettimeofday with the 2nd argument NULL (see
239 current_emacs_time). */
240 if (tz)
241 {
242 tz->tz_minuteswest = tb.timezone; /* minutes west of Greenwich */
243 tz->tz_dsttime = tb.dstflag; /* type of dst correction */
244 }
245}
246
14f29224 247int
22749e9a 248fchown (int fd, unsigned uid, unsigned gid)
14f29224
GV
249{
250 return 0;
251}
252
253/* Place a wrapper around the MSVC version of ctime. It returns NULL
177c0ea7 254 on network directories, so we handle that case here.
14f29224
GV
255 (Ulrich Leodolter, 1/11/95). */
256char *
257sys_ctime (const time_t *t)
258{
259 char *str = (char *) ctime (t);
260 return (str ? str : "Sun Jan 01 00:00:00 1970");
261}
262
263FILE *
7c3320d8 264sys_fopen (const char * path, const char * mode)
14f29224
GV
265{
266 return fopen (path, mode);
267}
268
269int
270sys_chdir (const char * path)
271{
272 return _chdir (path);
273}
ab5796a9 274
10fea9c4
EZ
275static FILETIME utc_base_ft;
276static long double utc_base;
277static int init = 0;
278
279static time_t
280convert_time (FILETIME ft)
281{
282 long double ret;
283
284 if (CompareFileTime (&ft, &utc_base_ft) < 0)
285 return 0;
286
287 ret = (long double) ft.dwHighDateTime
288 * 4096.0L * 1024.0L * 1024.0L + ft.dwLowDateTime;
289 ret -= utc_base;
290 return (time_t) (ret * 1e-7L);
291}
292
293static int
294is_exec (const char * name)
295{
296 char * p = strrchr (name, '.');
297 return
298 (p != NULL
299 && (stricmp (p, ".exe") == 0 ||
300 stricmp (p, ".com") == 0 ||
301 stricmp (p, ".bat") == 0 ||
302 stricmp (p, ".cmd") == 0));
303}
304
59a428eb 305/* FIXME? This is in config.nt now - is this still needed? */
10fea9c4
EZ
306#define IS_DIRECTORY_SEP(x) ((x) == '/' || (x) == '\\')
307
308/* We need this because nt/inc/sys/stat.h defines struct stat that is
309 incompatible with the MS run-time libraries. */
310int
311stat (const char * path, struct stat * buf)
312{
313 WIN32_FIND_DATA wfd;
314 HANDLE fh;
315 int permission;
316 int len;
317 int rootdir = FALSE;
318 char *name = alloca (FILENAME_MAX);
319
320 if (!init)
321 {
322 /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */
323 SYSTEMTIME st;
324
325 st.wYear = 1970;
326 st.wMonth = 1;
327 st.wDay = 1;
328 st.wHour = 0;
329 st.wMinute = 0;
330 st.wSecond = 0;
331 st.wMilliseconds = 0;
332
333 SystemTimeToFileTime (&st, &utc_base_ft);
334 utc_base = (long double) utc_base_ft.dwHighDateTime
335 * 4096.0L * 1024.0L * 1024.0L + utc_base_ft.dwLowDateTime;
336 init = 1;
337 }
338
339 if (path == NULL || buf == NULL || *path == '\0')
340 {
341 errno = EFAULT;
342 return -1;
343 }
344 if (_mbspbrk (path, "*?|<>\""))
345 {
346 errno = ENOENT;
347 return -1;
348 }
349
350 strcpy (name, path);
351 /* Remove trailing directory separator, unless name is the root
352 directory of a drive in which case ensure there is a trailing
353 separator. */
354 len = strlen (name);
355 rootdir = IS_DIRECTORY_SEP (name[0])
356 || (len == 3 && name[1] == ':' && IS_DIRECTORY_SEP (name[2]));
357 if (rootdir)
358 {
359 if (GetDriveType (name) < 2)
360 {
361 errno = ENOENT;
362 return -1;
363 }
364 memset (&wfd, 0, sizeof (wfd));
365 wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
366 wfd.ftCreationTime = utc_base_ft;
367 wfd.ftLastAccessTime = utc_base_ft;
368 wfd.ftLastWriteTime = utc_base_ft;
369 strcpy (wfd.cFileName, name);
370 }
371 else
372 {
373 if (IS_DIRECTORY_SEP (name[len-1]))
374 name[len - 1] = 0;
375
376 fh = FindFirstFile (name, &wfd);
377 if (fh == INVALID_HANDLE_VALUE)
378 {
379 errno = ENOENT;
380 return -1;
381 }
382 FindClose (fh);
383 }
384 buf->st_mode = (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ?
385 S_IFDIR : S_IFREG;
386 buf->st_nlink = 1;
387 buf->st_ino = 0;
388
389 if (name[0] && name[1] == ':')
390 buf->st_dev = tolower (name[0]) - 'a' + 1;
391 else
392 buf->st_dev = _getdrive ();
393 buf->st_rdev = buf->st_dev;
394
395 buf->st_size = wfd.nFileSizeLow;
396
397 /* Convert timestamps to Unix format. */
398 buf->st_mtime = convert_time (wfd.ftLastWriteTime);
399 buf->st_atime = convert_time (wfd.ftLastAccessTime);
400 if (buf->st_atime == 0) buf->st_atime = buf->st_mtime;
401 buf->st_ctime = convert_time (wfd.ftCreationTime);
402 if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime;
403
404 /* determine rwx permissions */
405 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
406 permission = S_IREAD;
407 else
408 permission = S_IREAD | S_IWRITE;
409
410 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
411 permission |= S_IEXEC;
412 else if (is_exec (name))
413 permission |= S_IEXEC;
414
415 buf->st_mode |= permission | (permission >> 3) | (permission >> 6);
416
417 return 0;
418}
419
6dad7178
EZ
420int
421lstat (const char * path, struct stat * buf)
422{
423 return stat (path, buf);
424}
e443729d
EZ
425
426/* Implementation of mkostemp for MS-Windows, to avoid race conditions
98a428c1 427 when using mktemp. Copied from w32.c.
e443729d 428
98a428c1
EZ
429 This is used only in update-game-score.c. It is overkill for that
430 use case, since update-game-score renames the temporary file into
431 the game score file, which isn't atomic on MS-Windows anyway, when
432 the game score already existed before running the program, which it
433 almost always does. But using a simpler implementation just to
434 make a point is uneconomical... */
e443729d
EZ
435
436int
437mkostemp (char * template, int flags)
438{
439 char * p;
440 int i, fd = -1;
441 unsigned uid = GetCurrentThreadId ();
442 int save_errno = errno;
443 static char first_char[] = "abcdefghijklmnopqrstuvwyz0123456789!%-_@#";
444
445 errno = EINVAL;
446 if (template == NULL)
447 return -1;
448
449 p = template + strlen (template);
450 i = 5;
451 /* replace up to the last 5 X's with uid in decimal */
452 while (--p >= template && p[0] == 'X' && --i >= 0)
453 {
454 p[0] = '0' + uid % 10;
455 uid /= 10;
456 }
457
458 if (i < 0 && p[0] == 'X')
459 {
460 i = 0;
461 do
462 {
463 p[0] = first_char[i];
464 if ((fd = open (template,
465 flags | _O_CREAT | _O_EXCL | _O_RDWR,
466 S_IRUSR | S_IWUSR)) >= 0
467 || errno != EEXIST)
468 {
469 if (fd >= 0)
470 errno = save_errno;
471 return fd;
472 }
473 }
474 while (++i < sizeof (first_char));
475 }
476
477 /* Template is badly formed or else we can't generate a unique name. */
478 return -1;
479}
98a428c1
EZ
480
481/* On Windows, you cannot rename into an existing file. */
482int
483sys_rename (const char *from, const char *to)
484{
485 int retval = rename (from, to);
486
487 if (retval < 0 && errno == EEXIST)
488 {
489 if (unlink (to) == 0)
490 retval = rename (from, to);
491 }
492 return retval;
493}