declare smobs in alloc.c
[bpt/emacs.git] / lib-src / ntlib.c
1 /* Utility and Unix shadow routines for GNU Emacs support programs on NT.
2
3 Copyright (C) 1994, 2001-2014 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
13 (at 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 <http://www.gnu.org/licenses/>. */
22
23 #include <windows.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <time.h>
27 #include <direct.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <errno.h>
31 #include <ctype.h>
32 #include <sys/timeb.h>
33 #include <mbstring.h>
34
35 #include "ntlib.h"
36
37 /* MinGW64 defines _TIMEZONE_DEFINED and defines 'struct timespec' in
38 its system headers. */
39 #ifndef _TIMEZONE_DEFINED
40 struct timezone
41 {
42 int tz_minuteswest; /* minutes west of Greenwich */
43 int tz_dsttime; /* type of dst correction */
44 };
45 #endif
46
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. */
52 unsigned
53 sleep (unsigned seconds)
54 {
55 Sleep (seconds * 1000);
56 return 0;
57 }
58
59 /* Get the current working directory. */
60 char *
61 getwd (char *dir)
62 {
63 if (GetCurrentDirectory (MAXPATHLEN, dir) > 0)
64 return dir;
65 return NULL;
66 }
67
68 static HANDLE getppid_parent;
69 static int getppid_ppid;
70
71 int
72 getppid (void)
73 {
74 char *ppid;
75 DWORD result;
76
77 ppid = getenv ("EM_PARENT_PROCESS_ID");
78 if (!ppid)
79 {
80 printf ("no pid.\n");
81 return 0;
82 }
83 else
84 {
85 getppid_ppid = atoi (ppid);
86 }
87
88 if (!getppid_parent)
89 {
90 getppid_parent = OpenProcess (SYNCHRONIZE, FALSE, atoi (ppid));
91 if (!getppid_parent)
92 {
93 printf ("Failed to open handle to parent process: %d\n",
94 GetLastError ());
95 exit (1);
96 }
97 }
98
99 result = WaitForSingleObject (getppid_parent, 0);
100 switch (result)
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:
110 printf ("Checking parent status failed: %d\n", GetLastError ());
111 exit (1);
112 }
113 }
114
115 char *
116 getlogin (void)
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
126 char *
127 cuserid (char * s)
128 {
129 char * name = getlogin ();
130 if (s)
131 return strcpy (s, name ? name : "");
132 return name;
133 }
134
135 unsigned
136 getuid (void)
137 {
138 return 0;
139 }
140
141 unsigned
142 geteuid (void)
143 {
144 return getuid ();
145 }
146
147 unsigned
148 getgid (void)
149 {
150 return 0;
151 }
152
153 unsigned
154 getegid (void)
155 {
156 return 0;
157 }
158
159 int
160 setuid (unsigned uid)
161 {
162 return 0;
163 }
164
165 int
166 setregid (unsigned rgid, unsigned gid)
167 {
168 return 0;
169 }
170
171 struct passwd *
172 getpwuid (unsigned uid)
173 {
174 return NULL;
175 }
176
177 char *
178 getpass (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
224 /* This is needed because lib/gettime.c calls gettimeofday, which MSVC
225 doesn't have. Copied from w32.c. */
226 void
227 gettimeofday (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
247 int
248 fchown (int fd, unsigned uid, unsigned gid)
249 {
250 return 0;
251 }
252
253 /* Place a wrapper around the MSVC version of ctime. It returns NULL
254 on network directories, so we handle that case here.
255 (Ulrich Leodolter, 1/11/95). */
256 char *
257 sys_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
263 FILE *
264 sys_fopen (const char * path, const char * mode)
265 {
266 return fopen (path, mode);
267 }
268
269 int
270 sys_chdir (const char * path)
271 {
272 return _chdir (path);
273 }
274
275 static FILETIME utc_base_ft;
276 static long double utc_base;
277 static int init = 0;
278
279 static time_t
280 convert_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
293 static int
294 is_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
305 /* FIXME? This is in config.nt now - is this still needed? */
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. */
310 int
311 stat (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
420 int
421 lstat (const char * path, struct stat * buf)
422 {
423 return stat (path, buf);
424 }
425
426 /* Implementation of mkostemp for MS-Windows, to avoid race conditions
427 when using mktemp. Copied from w32.c.
428
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... */
435
436 int
437 mkostemp (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 }
480
481 /* On Windows, you cannot rename into an existing file. */
482 int
483 sys_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 }