1 /*
2  * system.c - system interface
3  *
4  *   Copyright (c) 2000-2020  Shiro Kawai  <shiro@acm.org>
5  *
6  *   Redistribution and use in source and binary forms, with or without
7  *   modification, are permitted provided that the following conditions
8  *   are met:
9  *
10  *   1. Redistributions of source code must retain the above copyright
11  *      notice, this list of conditions and the following disclaimer.
12  *
13  *   2. Redistributions in binary form must reproduce the above copyright
14  *      notice, this list of conditions and the following disclaimer in the
15  *      documentation and/or other materials provided with the distribution.
16  *
17  *   3. Neither the name of the authors nor the names of its contributors
18  *      may be used to endorse or promote products derived from this
19  *      software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27  *   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28  *   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29  *   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30  *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31  *   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #define LIBGAUCHE_BODY
35 #include "gauche.h"
36 #include "gauche/class.h"
37 #include "gauche/bignum.h"
38 #include "gauche/priv/builtin-syms.h"
39 
40 #include <locale.h>
41 #include <errno.h>
42 #include <string.h>
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <ctype.h>
46 #include <fcntl.h>
47 #include <math.h>
48 #include <dirent.h>
49 
50 #if !defined(GAUCHE_WINDOWS)
51 #include <grp.h>
52 #include <pwd.h>
53 #include <sys/times.h>
54 #include <sys/wait.h>
55 
56 # if !defined(HAVE_CRT_EXTERNS_H)
57 /* POSIX defines environ, and ISO C defines __environ.
58    Modern C seems to have the latter declared in unistd.h */
59 extern char **environ;
60 # else  /* HAVE_CRT_EXTERNS_H */
61 /* On newer OSX, we can't directly access global 'environ' variable.
62    We need to use _NSGetEnviron(), and this header defines it. */
63 #include <crt_externs.h>
64 # endif /* HAVE_CRT_EXTERNS_H */
65 #else   /* GAUCHE_WINDOWS */
66 #include <lm.h>
67 #include <tlhelp32.h>
68 /* For windows redirection; win_prepare_handles creats and returns
69    win_redirects[3].  Each entry contains an inheritable handle for
70    the child process' stdin, stdout and stderr, respectively, and the flag
71    duped indicates whether the parent process must close the handle. */
72 typedef struct win_redirects_rec {
73     HANDLE *h;
74     int duped;
75 } win_redirects;
76 static win_redirects *win_prepare_handles(int *fds);
77 static int win_wait_for_handles(HANDLE *handles, int nhandles, int options,
78                                 int *status /*out*/);
79 #endif  /* GAUCHE_WINDOWS */
80 
81 #ifdef HAVE_GLOB_H
82 #include <glob.h>
83 #endif
84 #ifdef HAVE_SCHED_H
85 #include <sched.h>
86 #endif
87 
88 /*
89  * Auxiliary system interface functions.   See syslib.stub for
90  * Scheme binding.
91  */
92 
93 /*===============================================================
94  * Conversion between off_t and Scheme integer.
95  * off_t can be either 32bit or 64bit.
96  */
Scm_IntegerToOffset(ScmObj i)97 off_t Scm_IntegerToOffset(ScmObj i)
98 {
99     if (SCM_INTP(i)) {
100         return (off_t)SCM_INT_VALUE(i);
101     } else if (SCM_BIGNUMP(i)) {
102 #if SIZEOF_OFF_T == SIZEOF_LONG
103         return (off_t)Scm_GetIntegerClamp(i, SCM_CLAMP_ERROR, NULL);
104 #elif SIZEOF_OFF_T == 8
105         return (off_t)Scm_GetInteger64Clamp(i, SCM_CLAMP_ERROR, NULL);
106 #else
107         /* I don't think there's such an architecture. */
108 # error "off_t size on this platform is not suported."
109 #endif
110     }
111     Scm_Error("bad value as offset: %S", i);
112     return (off_t)-1;       /* dummy */
113 }
114 
Scm_OffsetToInteger(off_t off)115 ScmObj Scm_OffsetToInteger(off_t off)
116 {
117 #if SIZEOF_OFF_T == SIZEOF_LONG
118     return Scm_MakeInteger(off);
119 #elif SIZEOF_OFF_T == 8
120     return Scm_MakeInteger64((int64_t)off);
121 #else
122 # error "off_t size on this platform is not suported."
123 #endif
124 }
125 
126 /*===============================================================
127  * Windows specific - conversion between mbs and wcs.
128  */
129 #if defined(GAUCHE_WINDOWS) && defined(UNICODE)
130 #include "win-compat.c"
131 
Scm_MBS2WCS(const char * s)132 WCHAR *Scm_MBS2WCS(const char *s)
133 {
134     return mbs2wcs(s, TRUE, Scm_Error);
135 }
136 
Scm_WCS2MBS(const WCHAR * s)137 const char *Scm_WCS2MBS(const WCHAR *s)
138 {
139     return wcs2mbs(s, TRUE, Scm_Error);
140 }
141 #endif /* defined(GAUCHE_WINDOWS) && defined(UNICODE) */
142 
143 /*===============================================================
144  * OBSOLETED: Wrapper to the system call to handle signals.
145  * Use SCM_SYSCALL_{I|P} macro instead.
146  */
Scm_SysCall(int r)147 int Scm_SysCall(int r)
148 {
149     Scm_Warn("Obsoleted API Scm_SysCall is called.");
150     if (r < 0 && errno == EINTR) {
151         ScmVM *vm = Scm_VM();
152         errno = 0;
153         SCM_SIGCHECK(vm);
154     }
155     return r;
156 }
157 
Scm_PtrSysCall(void * r)158 void *Scm_PtrSysCall(void *r)
159 {
160     Scm_Warn("Obsoleted API Scm_PtrSysCall is called.");
161     if (r == NULL && errno == EINTR) {
162         ScmVM *vm = Scm_VM();
163         errno = 0;
164         SCM_SIGCHECK(vm);
165     }
166     return r;
167 }
168 
169 /*
170  * A utility function for the procedures that accepts either port or
171  * integer file descriptor.  Returns the file descriptor.  If port_or_fd
172  * is a port that is not associated with the system file, and needfd is
173  * true, signals error.  Otherwise it returns -1.
174  */
Scm_GetPortFd(ScmObj port_or_fd,int needfd)175 int Scm_GetPortFd(ScmObj port_or_fd, int needfd)
176 {
177     int fd = -1;
178     if (SCM_INTP(port_or_fd)) {
179         fd = SCM_INT_VALUE(port_or_fd);
180     } else if (SCM_PORTP(port_or_fd)) {
181         fd = Scm_PortFileNo(SCM_PORT(port_or_fd));
182         if (fd < 0 && needfd) {
183             Scm_Error("the port is not associated with a system file descriptor: %S",
184                       port_or_fd);
185         }
186     } else {
187         Scm_Error("port or small integer required, but got %S", port_or_fd);
188     }
189     return fd;
190 }
191 
192 /*===============================================================
193  * Directory primitives (dirent.h)
194  *   We don't provide the iterator primitives, but a function which
195  *   reads entire directory.
196  */
197 
198 /* Returns a list of directory entries.  If pathname is not a directory,
199    or can't be opened by some reason, an error is signalled. */
Scm_ReadDirectory(ScmString * pathname)200 ScmObj Scm_ReadDirectory(ScmString *pathname)
201 {
202     ScmObj head = SCM_NIL, tail = SCM_NIL;
203 #if !defined(GAUCHE_WINDOWS)
204     ScmVM *vm = Scm_VM();
205     struct dirent *dire;
206     DIR *dirp = opendir(Scm_GetStringConst(pathname));
207 
208     if (dirp == NULL) {
209         SCM_SIGCHECK(vm);
210         Scm_SysError("couldn't open directory %S", pathname);
211     }
212     while ((dire = readdir(dirp)) != NULL) {
213         ScmObj ent = SCM_MAKE_STR_COPYING(dire->d_name);
214         SCM_APPEND1(head, tail, ent);
215     }
216     SCM_SIGCHECK(vm);
217     closedir(dirp);
218     return head;
219 #else  /* GAUCHE_WINDOWS */
220     WIN32_FIND_DATA fdata;
221     DWORD winerrno;
222     ScmObj pattern;
223 
224     int pathlen = SCM_STRING_LENGTH(pathname);
225     if (pathlen == 0) {
226         Scm_Error("Couldn't open directory \"\"");
227     }
228     ScmChar lastchar = Scm_StringRef(pathname, pathlen-1, FALSE);
229     if (lastchar == SCM_CHAR('/') || lastchar == SCM_CHAR('\\')) {
230         pattern = Scm_StringAppendC(pathname, "*", 1, 1);
231     } else {
232         pattern = Scm_StringAppendC(pathname, "\\*", 2, 2);
233     }
234     const char *path = Scm_GetStringConst(SCM_STRING(pattern));
235 
236     HANDLE dirp = FindFirstFile(SCM_MBS2WCS(path), &fdata);
237     if (dirp == INVALID_HANDLE_VALUE) {
238         if ((winerrno = GetLastError()) != ERROR_FILE_NOT_FOUND) goto err;
239         return head;
240     }
241     const char *tpath = SCM_WCS2MBS(fdata.cFileName);
242     SCM_APPEND1(head, tail, SCM_MAKE_STR_COPYING(tpath));
243     while (FindNextFile(dirp, &fdata) != 0) {
244         tpath = SCM_WCS2MBS(fdata.cFileName);
245         SCM_APPEND1(head, tail, SCM_MAKE_STR_COPYING(tpath));
246     }
247     winerrno = GetLastError();
248     FindClose(dirp);
249     if (winerrno != ERROR_NO_MORE_FILES) goto err;
250     return head;
251  err:
252     Scm_Error("Searching directory failed by windows error %d",
253               winerrno);
254     return SCM_UNDEFINED;       /* dummy */
255 #endif
256 }
257 
258 /* getcwd compatibility layer.
259    Some implementations of getcwd accepts NULL as buffer to allocate
260    enough buffer memory in it, but that's not standardized and we avoid
261    relying on it.
262  */
Scm_GetCwd(void)263 ScmObj Scm_GetCwd(void)
264 {
265 #if defined(GAUCHE_WINDOWS)&&defined(UNICODE)
266 #  define CHAR_T wchar_t
267 #  define GETCWD _wgetcwd
268 #else  /*!(defined(GAUCHE_WINDOWS)&&defined(UNICODE))*/
269 #  define CHAR_T char
270 #  define GETCWD getcwd
271 #endif /*!(defined(GAUCHE_WINDOWS)&&defined(UNICODE))*/
272 
273 #define GETCWD_INITIAL_BUFFER_SIZE 1024
274     int bufsiz = GETCWD_INITIAL_BUFFER_SIZE;
275     CHAR_T sbuf[GETCWD_INITIAL_BUFFER_SIZE];
276     CHAR_T *buf = sbuf;
277     CHAR_T *r;
278 
279     for (;;) {
280         SCM_SYSCALL3(r, GETCWD(buf, bufsiz), r == NULL);
281         if (r != NULL) break;
282         if (errno == ERANGE) {
283             bufsiz *= 2;
284             buf = SCM_NEW_ATOMIC_ARRAY(CHAR_T, bufsiz);
285         } else {
286             Scm_SysError("getcwd failed");
287         }
288     }
289 #if defined(GAUCHE_WINDOWS) && defined(UNICODE)
290     return Scm_MakeString(Scm_WCS2MBS(buf), -1, -1, 0);
291 #else  /*!(defined(GAUCHE_WINDOWS) && defined(UNICODE))*/
292     return Scm_MakeString(buf, -1, -1, SCM_STRING_COPYING);
293 #endif /*!(defined(GAUCHE_WINDOWS) && defined(UNICODE))*/
294 #undef CHAR_T
295 }
296 
297 /*===============================================================
298  * Pathname manipulation
299  *
300  *  It gets complicated since the byte '/' and '\\' can appear in
301  *  the trailing octets of a multibyte character.
302  *  Assuming these operations won't be a bottleneck, we use simple and
303  *  straightforward code rather than tricky and fast one.
304  */
305 
306 /* Returns the system's native pathname delimiter. */
Scm_PathDelimiter(void)307 const char *Scm_PathDelimiter(void)
308 {
309 #if !defined(GAUCHE_WINDOWS)
310     return "/";
311 #else  /* GAUCHE_WINDOWS */
312     return "\\";
313 #endif /* GAUCHE_WINDOWS */
314 }
315 
316 /* On Windows, '/' is *allowed* to be an alternative separator. */
317 #if defined(GAUCHE_WINDOWS)
318 #define SEPARATOR '\\'
319 #define ROOTDIR   "\\"
320 #define SEPARATOR_P(c)  ((c) == SEPARATOR || (c) == '/')
321 #else
322 #define SEPARATOR '/'
323 #define ROOTDIR   "/"
324 #define SEPARATOR_P(c)  ((c) == SEPARATOR)
325 #endif
326 
327 /* Returns the pointer to the first path separator character,
328    or NULL if no separator is found. */
get_first_separator(const char * path,const char * end)329 static const char *get_first_separator(const char *path, const char *end)
330 {
331     const char *p = path;
332     while (p < end) {
333         if (SEPARATOR_P(*p)) return p;
334         p += SCM_CHAR_NFOLLOWS(*p)+1;
335     }
336     return NULL;
337 }
338 
339 /* Returns the pointer to the last path separator character,
340    or NULL if no separator is found. */
get_last_separator(const char * path,const char * end)341 static const char *get_last_separator(const char *path, const char *end)
342 {
343     const char *p = path, *last = NULL;
344     while (p < end) {
345         if (SEPARATOR_P(*p)) last = p;
346         p += SCM_CHAR_NFOLLOWS(*p)+1;
347     }
348     return last;
349 }
350 
skip_separators(const char * p,const char * end)351 static const char *skip_separators(const char *p, const char *end)
352 {
353     while (p < end) {
354         if (!SEPARATOR_P(*p)) break;
355         p += SCM_CHAR_NFOLLOWS(*p)+1;
356     }
357     return p;
358 }
359 
360 /* Returns the end pointer sans trailing separators. */
truncate_trailing_separators(const char * path,const char * end)361 static const char *truncate_trailing_separators(const char *path,
362                                                 const char *end)
363 {
364     const char *p = get_first_separator(path, end);
365     if (p == NULL) return end;
366     for (;;) {
367         const char *q = skip_separators(p, end);
368         if (q == end) return p;
369         p = get_first_separator(q, end);
370         if (p == NULL) return end;
371     }
372 }
373 
374 /* for keyword arguments */
375 static ScmObj key_absolute = SCM_FALSE;
376 static ScmObj key_expand = SCM_FALSE;
377 static ScmObj key_canonicalize = SCM_FALSE;
378 
Scm_NormalizePathname(ScmString * pathname,int flags)379 ScmObj Scm_NormalizePathname(ScmString *pathname, int flags)
380 {
381     static ScmObj proc = SCM_UNDEFINED;
382     SCM_BIND_PROC(proc, "sys-normalize-pathname", Scm_GaucheModule());
383 
384     ScmObj h = SCM_NIL, t = SCM_NIL;
385     SCM_APPEND1(h, t, SCM_OBJ(pathname));
386     if (flags & SCM_PATH_ABSOLUTE) {
387         SCM_APPEND1(h, t, key_absolute);
388         SCM_APPEND1(h, t, SCM_TRUE);
389     }
390     if (flags & SCM_PATH_CANONICALIZE) {
391         SCM_APPEND1(h, t, key_canonicalize);
392         SCM_APPEND1(h, t, SCM_TRUE);
393     }
394     if (flags & SCM_PATH_EXPAND) {
395         SCM_APPEND1(h, t, key_expand);
396         SCM_APPEND1(h, t, SCM_TRUE);
397     }
398     return Scm_ApplyRec(proc, h);
399 }
400 
401 /* Returns system's temporary directory. */
Scm_TmpDir(void)402 ScmObj Scm_TmpDir(void)
403 {
404 #if defined(GAUCHE_WINDOWS)
405 # define TMP_PATH_MAX 1024
406     TCHAR buf[TMP_PATH_MAX+1], *tbuf = buf;
407     /* According to the windows document, this API checks environment
408        variables TMP, TEMP, and USERPROFILE.  Fallback is the Windows
409        directory. */
410     DWORD r = GetTempPath(TMP_PATH_MAX, buf);
411     if (r == 0) Scm_SysError("GetTempPath failed");
412     if (r > TMP_PATH_MAX) {
413         tbuf = SCM_NEW_ATOMIC_ARRAY(TCHAR, r+1);
414         DWORD r2 = GetTempPath(r, tbuf);
415         if (r2 != r) Scm_SysError("GetTempPath failed");
416     }
417     return SCM_MAKE_STR_COPYING(SCM_WCS2MBS(tbuf));
418 #else  /*!GAUCHE_WINDOWS*/
419     const char *s;
420     if ((s = Scm_GetEnv("TMPDIR")) != NULL) return SCM_MAKE_STR_COPYING(s);
421     if ((s = Scm_GetEnv("TMP")) != NULL) return SCM_MAKE_STR_COPYING(s);
422     else return SCM_MAKE_STR("/tmp"); /* fallback */
423 #endif /*!GAUCHE_WINDOWS*/
424 }
425 
426 /* Basename and dirname.
427    On Win32, we need to treat drive names specially, e.g.:
428    (sys-dirname "C:/a") == (sys-dirname "C:/") == (sys-dirname "C:") == "C:\\"
429    (sys-basename "C:/") == (sys-basename "C:) == ""
430 */
431 
Scm_BaseName(ScmString * filename)432 ScmObj Scm_BaseName(ScmString *filename)
433 {
434     ScmSmallInt size;
435     const char *path = Scm_GetStringContent(filename, &size, NULL, NULL);
436 
437 #if defined(GAUCHE_WINDOWS)
438     /* Ignore drive letter, for it can never be a part of basename. */
439     if (size >= 2 && path[1] == ':' && isalpha(path[0])) {
440         path += 2;
441         size -= 2;
442     }
443 #endif /* GAUCHE_WINDOWS) */
444 
445     if (size == 0) return SCM_MAKE_STR("");
446     const char *endp = truncate_trailing_separators(path, path+size);
447     const char *last = get_last_separator(path, endp);
448     if (last == NULL) {
449         return Scm_MakeString(path, (int)(endp-path), -1, 0);
450     } else {
451         return Scm_MakeString(last+1, (int)(endp-last-1), -1, 0);
452     }
453 }
454 
Scm_DirName(ScmString * filename)455 ScmObj Scm_DirName(ScmString *filename)
456 {
457     ScmSmallInt size;
458     const char *path = Scm_GetStringContent(filename, &size, NULL, NULL);
459 #if defined(GAUCHE_WINDOWS)
460     int drive_letter = -1;
461     if (size >= 2 && path[1] == ':' && isalpha(path[0])) {
462         drive_letter = path[0];
463         path += 2;
464         size -= 2;
465     }
466 #endif /* GAUCHE_WINDOWS */
467 
468     if (size == 0) { path = NULL; goto finale; }
469     const char *endp = truncate_trailing_separators(path, path+size);
470     if (endp == path) { path = ROOTDIR, size = 1; goto finale; }
471     const char *last = get_last_separator(path, endp);
472     if (last == NULL) { path = ".", size = 1; goto finale; }
473 
474     /* we have "something/", and 'last' points to the last separator. */
475     last = truncate_trailing_separators(path, last);
476     if (last == path) {
477         path = ROOTDIR, size = 1;
478     } else {
479         size = (int)(last - path);
480     }
481  finale:
482 #if defined(GAUCHE_WINDOWS)
483     if (drive_letter > 0) {
484         ScmObj z;
485         char p[3] = "x:";
486         p[0] = (char)drive_letter;
487         z = Scm_MakeString(p, 2, 2, SCM_MAKSTR_COPYING);
488         if (path) {
489             return Scm_StringAppendC(SCM_STRING(z), path, size, -1);
490         } else {
491             return Scm_StringAppendC(SCM_STRING(z), ROOTDIR, 1, -1);
492         }
493     }
494 #endif /* GAUCHE_WINDOWS */
495     if (path) return Scm_MakeString(path, size, -1, 0);
496     else      return Scm_MakeString(".", 1, 1, 0);
497 }
498 
499 #undef ROOTDIR
500 #undef SEPARATOR
501 
502 
503 #if !defined(HAVE_MKSTEMP) || !defined(HAVE_MKDTEMP)
504 /*
505  * Helper function to emulate mkstemp or mkdtemp.  FUNC returns 0 on
506  * success and non-zero otherwize.  NAME is a name of operation
507  * performed by FUNC.  ARG is caller supplied data passed to FUNC.
508  */
emulate_mkxtemp(char * name,char * templat,int (* func)(char *,void *),void * arg)509 static void emulate_mkxtemp(char *name, char *templat,
510                             int (*func)(char *, void *), void *arg)
511 {
512     /* Emulate mkxtemp. */
513     int siz = (int)strlen(templat);
514     if (siz < 6) {
515         Scm_Error("%s - invalid template: %s", name, templat);
516     }
517 #define MKXTEMP_MAX_TRIALS 65535   /* avoid infinite loop */
518     {
519         u_long seed = (u_long)time(NULL);
520         int numtry, rv;
521         char suffix[7];
522         for (numtry=0; numtry<MKXTEMP_MAX_TRIALS; numtry++) {
523             snprintf(suffix, 7, "%06lx", (seed>>8)&0xffffff);
524             memcpy(templat+siz-6, suffix, 7);
525             rv = (*func)(templat, arg);
526             if (rv == 0) break;
527             seed *= 2654435761UL;
528         }
529         if (numtry == MKXTEMP_MAX_TRIALS) {
530             Scm_Error("%s failed", name);
531         }
532     }
533 }
534 #endif /* !defined(HAVE_MKSTEMP) || !defined(HAVE_MKDTEMP) */
535 
536 #define MKXTEMP_PATH_MAX 1025  /* Geez, remove me */
build_template(ScmString * templat,char * name)537 static void build_template(ScmString *templat, char *name)
538 {
539     ScmSmallInt siz;
540     const char *t = Scm_GetStringContent(templat, &siz, NULL, NULL);
541     if (siz >= MKXTEMP_PATH_MAX-6) {
542         Scm_Error("pathname too long: %S", templat);
543     }
544     memcpy(name, t, siz);
545     memcpy(name + siz, "XXXXXX", 6);
546     name[siz+6] = '\0';
547 }
548 
549 #if !defined(HAVE_MKSTEMP)
create_tmpfile(char * templat,void * arg)550 static int create_tmpfile(char *templat, void *arg)
551 {
552     int *fdp = (int *)arg;
553     int flags;
554 
555 #if defined(GAUCHE_WINDOWS)
556     flags = O_CREAT|O_EXCL|O_WRONLY|O_BINARY;
557 #else  /* !GAUCHE_WINDOWS */
558     flags = O_CREAT|O_EXCL|O_WRONLY;
559 #endif /* !GAUCHE_WINDOWS */
560     SCM_SYSCALL(*fdp, open(templat, flags, 0600));
561     return *fdp < 0;
562 }
563 #endif
564 
565 /* Make mkstemp() work even if the system doesn't have one. */
Scm_Mkstemp(char * templat)566 int Scm_Mkstemp(char *templat)
567 {
568     int fd = -1;
569 #if defined(HAVE_MKSTEMP)
570     SCM_SYSCALL(fd, mkstemp(templat));
571     if (fd < 0) Scm_SysError("mkstemp failed");
572     return fd;
573 #else   /*!defined(HAVE_MKSTEMP)*/
574     emulate_mkxtemp("mkstemp", templat, create_tmpfile, &fd);
575     return fd;
576 #endif /*!defined(HAVE_MKSTEMP)*/
577 }
578 
579 
Scm_SysMkstemp(ScmString * templat)580 ScmObj Scm_SysMkstemp(ScmString *templat)
581 {
582     char name[MKXTEMP_PATH_MAX];
583     build_template(templat, name);
584     int fd = Scm_Mkstemp(name);
585     ScmObj sname = SCM_MAKE_STR_COPYING(name);
586     SCM_RETURN(Scm_Values2(Scm_MakePortWithFd(sname, SCM_PORT_OUTPUT, fd,
587                                               SCM_PORT_BUFFER_FULL, TRUE),
588                            sname));
589 }
590 
591 #if !defined(HAVE_MKDTEMP)
create_tmpdir(char * templat,void * arg SCM_UNUSED)592 static int create_tmpdir(char *templat, void *arg SCM_UNUSED)
593 {
594     int r;
595 
596 #if defined(GAUCHE_WINDOWS)
597     SCM_SYSCALL(r, mkdir(templat));
598 #else  /* !GAUCHE_WINDOWS */
599     SCM_SYSCALL(r, mkdir(templat, 0700));
600 #endif /* !GAUCHE_WINDOWS */
601     return r < 0;
602 }
603 #endif
604 
Scm_SysMkdtemp(ScmString * templat)605 ScmObj Scm_SysMkdtemp(ScmString *templat)
606 {
607     char name[MKXTEMP_PATH_MAX];
608     build_template(templat, name);
609 
610 #if defined(HAVE_MKDTEMP)
611     {
612       char *p = NULL;
613       SCM_SYSCALL3(p, mkdtemp(name), (p == NULL));
614       if (p == NULL) Scm_SysError("mkdtemp failed");
615     }
616 #else   /*!defined(HAVE_MKDTEMP)*/
617     emulate_mkxtemp("mkdtemp", name, create_tmpdir, NULL);
618 #endif /*!defined(HAVE_MKDTEMP)*/
619 
620     return SCM_MAKE_STR_COPYING(name);
621 }
622 
623 /*===============================================================
624  * Stat (sys/stat.h)
625  */
626 
stat_allocate(ScmClass * klass,ScmObj initargs SCM_UNUSED)627 static ScmObj stat_allocate(ScmClass *klass, ScmObj initargs SCM_UNUSED)
628 {
629     return SCM_OBJ(SCM_NEW_INSTANCE(ScmSysStat, klass));
630 }
631 
stat_hash(ScmObj obj,ScmSmallInt salt,u_long flags)632 static ScmSmallInt stat_hash(ScmObj obj, ScmSmallInt salt, u_long flags)
633 {
634     ScmStat *s = SCM_SYS_STAT_STAT(obj);
635     ScmSmallInt h = salt;
636 #define STAT_HASH_UI(name)                                              \
637     h = Scm_CombineHashValue(Scm_SmallIntHash((ScmSmallInt)s->SCM_CPP_CAT(st_, name), \
638                                               salt, flags), h)
639 #define STAT_HASH_TIME(name)                                            \
640     h = Scm_CombineHashValue(Scm_Int64Hash((int64_t)s->SCM_CPP_CAT(st_, name), \
641                                            salt, flags), h)
642 #define STAT_HASH_TIMESPEC(name) \
643     h = Scm_CombineHashValue(Scm_Int64Hash((int64_t)s->SCM_CPP_CAT(st_, name).tv_sec, \
644                                            salt, flags), \
645         Scm_CombineHashValue(Scm_Int64Hash((int64_t)s->SCM_CPP_CAT(st_, name).tv_nsec, \
646                                            salt, flags),h))
647 
648 
649     STAT_HASH_UI(mode);
650     STAT_HASH_UI(ino);
651     STAT_HASH_UI(dev);
652     STAT_HASH_UI(rdev);
653     STAT_HASH_UI(nlink);
654     STAT_HASH_UI(uid);
655     STAT_HASH_UI(gid);
656 #if HAVE_STRUCT_STAT_ST_ATIM
657     STAT_HASH_TIMESPEC(atim);
658 #else
659     STAT_HASH_TIME(atime);
660 #endif
661 #if HAVE_STRUCT_STAT_ST_MTIM
662     STAT_HASH_TIMESPEC(mtim);
663 #else
664     STAT_HASH_TIME(mtime);
665 #endif
666 #if HAVE_STRUCT_STAT_ST_CTIM
667     STAT_HASH_TIMESPEC(ctim);
668 #else
669     STAT_HASH_TIME(ctime);
670 #endif
671     return h;
672 }
673 
674 SCM_DEFINE_BUILTIN_CLASS(Scm_SysStatClass,
675                          NULL, NULL, stat_hash,
676                          stat_allocate,
677                          SCM_CLASS_DEFAULT_CPL);
678 
Scm_MakeSysStat(void)679 ScmObj Scm_MakeSysStat(void)
680 {
681     return stat_allocate(&Scm_SysStatClass, SCM_NIL);
682 }
683 
stat_type_get(ScmSysStat * stat)684 static ScmObj stat_type_get(ScmSysStat *stat)
685 {
686     if (S_ISDIR(SCM_SYS_STAT_STAT(stat)->st_mode)) return (SCM_SYM_DIRECTORY);
687     if (S_ISREG(SCM_SYS_STAT_STAT(stat)->st_mode)) return (SCM_SYM_REGULAR);
688     if (S_ISCHR(SCM_SYS_STAT_STAT(stat)->st_mode)) return (SCM_SYM_CHARACTER);
689     if (S_ISBLK(SCM_SYS_STAT_STAT(stat)->st_mode)) return (SCM_SYM_BLOCK);
690     if (S_ISFIFO(SCM_SYS_STAT_STAT(stat)->st_mode)) return (SCM_SYM_FIFO);
691 #ifdef S_ISLNK
692     if (S_ISLNK(SCM_SYS_STAT_STAT(stat)->st_mode)) return (SCM_SYM_SYMLINK);
693 #endif
694 #ifdef S_ISSOCK
695     if (S_ISSOCK(SCM_SYS_STAT_STAT(stat)->st_mode)) return (SCM_SYM_SOCKET);
696 #endif
697     return (SCM_FALSE);
698 }
699 
stat_perm_get(ScmSysStat * stat)700 static ScmObj stat_perm_get(ScmSysStat *stat)
701 {
702     return Scm_MakeIntegerFromUI(SCM_SYS_STAT_STAT(stat)->st_mode & 0777);
703 }
704 
stat_size_get(ScmSysStat * stat)705 static ScmObj stat_size_get(ScmSysStat *stat)
706 {
707     return Scm_OffsetToInteger(SCM_SYS_STAT_STAT(stat)->st_size);
708 }
709 
710 
711 #define STAT_GETTER_UI(name) \
712   static ScmObj SCM_CPP_CAT3(stat_, name, _get)(ScmSysStat *s) \
713   { return Scm_MakeIntegerFromUI((u_long)(SCM_SYS_STAT_STAT(s)->SCM_CPP_CAT(st_, name))); }
714 #define STAT_GETTER_TIME(name) \
715   static ScmObj SCM_CPP_CAT3(stat_, name, _get)(ScmSysStat *s) \
716   { return Scm_MakeSysTime(SCM_SYS_STAT_STAT(s)->SCM_CPP_CAT(st_, name)); }
717 
718 STAT_GETTER_UI(mode)
STAT_GETTER_UI(ino)719 STAT_GETTER_UI(ino)
720 STAT_GETTER_UI(dev)
721 STAT_GETTER_UI(rdev)
722 STAT_GETTER_UI(nlink)
723 STAT_GETTER_UI(uid)
724 STAT_GETTER_UI(gid)
725 STAT_GETTER_TIME(atime)
726 STAT_GETTER_TIME(mtime)
727 STAT_GETTER_TIME(ctime)
728 
729 static ScmObj stat_atim_get(ScmSysStat *s)
730 {
731 #if HAVE_STRUCT_STAT_ST_ATIM
732     return Scm_MakeTime64(SCM_SYM_TIME_UTC,
733                           (int64_t)s->statrec.st_atim.tv_sec,
734                           s->statrec.st_atim.tv_nsec);
735 #else
736     return Scm_MakeTime64(SCM_SYM_TIME_UTC,
737                           (int64_t)s->statrec.st_atime,
738                           0);
739 #endif
740 }
741 
stat_mtim_get(ScmSysStat * s)742 static ScmObj stat_mtim_get(ScmSysStat *s)
743 {
744 #if HAVE_STRUCT_STAT_ST_MTIM
745     return Scm_MakeTime64(SCM_SYM_TIME_UTC,
746                           (int64_t)s->statrec.st_mtim.tv_sec,
747                           s->statrec.st_mtim.tv_nsec);
748 #else
749     return Scm_MakeTime64(SCM_SYM_TIME_UTC,
750                           (int64_t)s->statrec.st_mtime,
751                           0);
752 #endif
753 }
754 
stat_ctim_get(ScmSysStat * s)755 static ScmObj stat_ctim_get(ScmSysStat *s)
756 {
757 #if HAVE_STRUCT_STAT_ST_CTIM
758     return Scm_MakeTime64(SCM_SYM_TIME_UTC,
759                           (int64_t)s->statrec.st_ctim.tv_sec,
760                           s->statrec.st_ctim.tv_nsec);
761 #else
762     return Scm_MakeTime64(SCM_SYM_TIME_UTC,
763                           (int64_t)s->statrec.st_ctime,
764                           0);
765 #endif
766 }
767 
768 static ScmClassStaticSlotSpec stat_slots[] = {
769     SCM_CLASS_SLOT_SPEC("type",  stat_type_get,  NULL),
770     SCM_CLASS_SLOT_SPEC("perm",  stat_perm_get,  NULL),
771     SCM_CLASS_SLOT_SPEC("mode",  stat_mode_get,  NULL),
772     SCM_CLASS_SLOT_SPEC("ino",   stat_ino_get,   NULL),
773     SCM_CLASS_SLOT_SPEC("dev",   stat_dev_get,   NULL),
774     SCM_CLASS_SLOT_SPEC("rdev",  stat_rdev_get,  NULL),
775     SCM_CLASS_SLOT_SPEC("nlink", stat_nlink_get, NULL),
776     SCM_CLASS_SLOT_SPEC("uid",   stat_uid_get,   NULL),
777     SCM_CLASS_SLOT_SPEC("gid",   stat_gid_get,   NULL),
778     SCM_CLASS_SLOT_SPEC("size",  stat_size_get,  NULL),
779     SCM_CLASS_SLOT_SPEC("atime", stat_atime_get, NULL),
780     SCM_CLASS_SLOT_SPEC("mtime", stat_mtime_get, NULL),
781     SCM_CLASS_SLOT_SPEC("ctime", stat_ctime_get, NULL),
782     SCM_CLASS_SLOT_SPEC("atim",  stat_atim_get, NULL),
783     SCM_CLASS_SLOT_SPEC("mtim",  stat_mtim_get, NULL),
784     SCM_CLASS_SLOT_SPEC("ctim",  stat_ctim_get, NULL),
785     SCM_CLASS_SLOT_SPEC_END()
786 };
787 
788 /*===============================================================
789  * Time (sys/time.h and time.h)
790  */
791 
792 /* Gauche has two notion of time.  A simple number is used by the low-level
793  * system interface (sys-time, sys-gettimeofday).  An object of <time> class
794  * is used for higher-level interface, including threads.
795  */
796 
797 /* <time> object */
798 
time_allocate(ScmClass * klass,ScmObj initargs SCM_UNUSED)799 static ScmObj time_allocate(ScmClass *klass, ScmObj initargs SCM_UNUSED)
800 {
801     ScmTime *t = SCM_NEW_INSTANCE(ScmTime, klass);
802     t->type = SCM_SYM_TIME_UTC;
803     t->sec = 0;
804     t->nsec = 0;
805     return SCM_OBJ(t);
806 }
807 
time_print(ScmObj obj,ScmPort * port,ScmWriteContext * ctx SCM_UNUSED)808 static void time_print(ScmObj obj, ScmPort *port,
809                        ScmWriteContext *ctx SCM_UNUSED)
810 {
811     ScmTime *t = SCM_TIME(obj);
812     ScmObj sec = Scm_MakeInteger64(t->sec);
813     long nsec = t->nsec;
814     /* t->sec can be negative for time-difference. */
815     if (Scm_Sign(sec) < 0 && t->nsec > 0) {
816         sec = Scm_Abs(Scm_Add(sec, SCM_MAKE_INT(1)));
817         nsec = 1000000000L - nsec;
818         Scm_Printf(port, "#<%S -%S.%09lu>", t->type, sec, nsec);
819     } else {
820         Scm_Printf(port, "#<%S %S.%09lu>", t->type, sec, nsec);
821     }
822 }
823 
time_compare(ScmObj x,ScmObj y,int equalp)824 static int time_compare(ScmObj x, ScmObj y, int equalp)
825 {
826     ScmTime *tx = SCM_TIME(x);
827     ScmTime *ty = SCM_TIME(y);
828 
829     if (equalp) {
830         if (SCM_EQ(tx->type, ty->type)
831             && tx->sec == ty->sec
832             && tx->nsec == ty->nsec) {
833             return 0;
834         } else {
835             return 1;
836         }
837     } else {
838         if (!SCM_EQ(tx->type, ty->type)) {
839             Scm_Error("cannot compare different types of time objects: %S vs %S", x, y);
840         }
841         if (tx->sec < ty->sec) return -1;
842         if (tx->sec == ty->sec) {
843             if (tx->nsec < ty->nsec) return -1;
844             if (tx->nsec == ty->nsec) return 0;
845             else return 1;
846         }
847         else return 1;
848     }
849 }
850 
time_hash(ScmObj x,ScmSmallInt salt,u_long flags)851 static ScmSmallInt time_hash(ScmObj x, ScmSmallInt salt, u_long flags)
852 {
853     ScmTime *t = SCM_TIME(x);
854     ScmSmallInt h = salt;
855     h = Scm_CombineHashValue(Scm_RecursiveHash(t->type, salt, flags), h);
856     h = Scm_CombineHashValue(Scm_Int64Hash(t->sec, salt, flags), h);
857     h = Scm_CombineHashValue(Scm_SmallIntHash(t->nsec, salt, flags), h);
858     return h;
859 }
860 
861 SCM_DEFINE_BUILTIN_CLASS(Scm_TimeClass,
862                          time_print, time_compare, time_hash,
863                          time_allocate, SCM_CLASS_DEFAULT_CPL);
864 
make_time_int(ScmObj type)865 static ScmTime *make_time_int(ScmObj type)
866 {
867     ScmTime *t = SCM_TIME(time_allocate(SCM_CLASS_TIME, SCM_NIL));
868     t->type = SCM_FALSEP(type)? SCM_SYM_TIME_UTC : type;
869     return t;
870 }
871 
872 
Scm_MakeTime(ScmObj type,long sec,long nsec)873 ScmObj Scm_MakeTime(ScmObj type, long sec, long nsec)
874 {
875     ScmTime *t = make_time_int(type);
876     t->sec = (int64_t)sec;
877     t->nsec = nsec;
878     return SCM_OBJ(t);
879 }
880 
Scm_MakeTime64(ScmObj type,int64_t sec,long nsec)881 ScmObj Scm_MakeTime64(ScmObj type, int64_t sec, long nsec)
882 {
883     ScmTime *t = make_time_int(type);
884     t->sec = sec;
885     t->nsec = nsec;
886     return SCM_OBJ(t);
887 }
888 
889 /* Abstract gettimeofday() */
Scm_GetTimeOfDay(u_long * sec,u_long * usec)890 void Scm_GetTimeOfDay(u_long *sec, u_long *usec)
891 {
892 #if defined(HAVE_GETTIMEOFDAY)
893     struct timeval tv;
894     int r;
895     SCM_SYSCALL(r, gettimeofday(&tv, NULL));
896     if (r < 0) Scm_SysError("gettimeofday failed");
897     *sec = (u_long)tv.tv_sec;
898     *usec = (u_long)tv.tv_usec;
899 #elif defined(GAUCHE_WINDOWS)
900     FILETIME ft;
901     GetSystemTimeAsFileTime(&ft);
902     SCM_FILETIME_TO_UNIXTIME(ft, *sec, *usec);
903 #else  /* !HAVE_GETTIMEOFDAY && !GAUCHE_WINDOWS */
904     /* Last resort */
905     *sec = (u_long)time(NULL);
906     *usec = 0;
907 #endif /* !HAVE_GETTIMEOFDAY && !GAUCHE_WINDOWS */
908 }
909 
910 /* Abstract clock_gettime and clock_getres.
911    If the system doesn't have these, those API returns FALSE; the caller
912    should make up fallback means.
913 
914    NB: XCode8 breaks clock_getres on OSX 10.11---it's only provided in
915    OSX 10.12, but the SDK pretends it's available on all platforms.
916    For the workaround, we call OSX specific functions.
917    Cf. http://developer.apple.com/library/mac/#qa/qa1398/_index.html
918  */
919 #if defined(__APPLE__) && defined(__MACH__)
920 #include <mach/mach.h>
921 #include <mach/mach_time.h>
922 static mach_timebase_info_data_t tinfo;
923 #endif /* __APPLE__ && __MACH__ */
924 
Scm_ClockGetTimeMonotonic(u_long * sec,u_long * nsec)925 int Scm_ClockGetTimeMonotonic(u_long *sec, u_long *nsec)
926 {
927 #if defined(__APPLE__) && defined(__MACH__)
928     if (tinfo.denom == 0) {
929 	(void)mach_timebase_info(&tinfo);
930     }
931     uint64_t t = mach_absolute_time();
932     uint64_t ns = t * tinfo.numer / tinfo.denom;
933     *sec = ns / 1000000000;
934     *nsec = ns % 1000000000;
935     return TRUE;
936 #elif defined(GAUCHE_WINDOWS)
937     /* On MinGW, clock_gettime is in libwinpthread-1.dll; we avoid depending
938        on it. */
939     LARGE_INTEGER qpf;
940     LARGE_INTEGER qpc;
941     if (!QueryPerformanceFrequency(&qpf)) {
942         Scm_SysError("QueryPerformanceFrequency failed");
943     }
944     if (!QueryPerformanceCounter(&qpc)) {
945         Scm_SysError("QueryPerformanceCounter failed");
946     }
947     *sec = (u_long)(qpc.QuadPart / qpf.QuadPart);
948     *nsec = (u_long)((qpc.QuadPart % qpf.QuadPart) * 1000000000 / qpf.QuadPart);
949     return TRUE;
950 #elif defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
951     ScmTimeSpec ts;
952     int r;
953     SCM_SYSCALL(r, clock_gettime(CLOCK_MONOTONIC, &ts));
954     if (r < 0) Scm_SysError("clock_gettime failed");
955     *sec = (u_long)ts.tv_sec;
956     *nsec = (u_long)ts.tv_nsec;
957     return TRUE;
958 #else  /*!HAVE_CLOCK_GETTIME*/
959     *sec = *nsec = 0;
960     return FALSE;
961 #endif /*!HAVE_CLOCK_GETTIME*/
962 }
963 
Scm_ClockGetResMonotonic(u_long * sec,u_long * nsec)964 int Scm_ClockGetResMonotonic(u_long *sec, u_long *nsec)
965 {
966 #if defined(__APPLE__) && defined(__MACH__)
967     if (tinfo.denom == 0) {
968 	(void)mach_timebase_info(&tinfo);
969     }
970     if (tinfo.numer <= tinfo.denom) {
971 	/* The precision is finer than nano seconds, but we can only
972 	   represent nanosecond resolution. */
973 	*sec = 0;
974 	*nsec = 1;
975     } else {
976 	*sec = 0;
977 	*nsec = tinfo.numer / tinfo.denom;
978     }
979     return TRUE;
980 #elif defined(GAUCHE_WINDOWS)
981     /* On MinGW, clock_getres is in libwinpthread-1.dll; we avoid depending
982        on it. */
983     LARGE_INTEGER qpf;
984     if (!QueryPerformanceFrequency(&qpf)) {
985         Scm_SysError("QueryPerformanceFrequency failed");
986     }
987     *sec = 0;
988     *nsec = (u_long)(1000000000 / qpf.QuadPart);
989     if (*nsec == 0) *nsec = 1;
990     return TRUE;
991 #elif defined(HAVE_CLOCK_GETRES) && defined(CLOCK_MONOTONIC)
992     ScmTimeSpec ts;
993     int r;
994     SCM_SYSCALL(r, clock_getres(CLOCK_MONOTONIC, &ts));
995     if (r < 0) Scm_SysError("clock_getres failed");
996     *sec = (u_long)ts.tv_sec;
997     *nsec = (u_long)ts.tv_nsec;
998     return TRUE;
999 #else  /*!HAVE_CLOCK_GETRES*/
1000     *sec = *nsec = 0;
1001     return FALSE;
1002 #endif /*!HAVE_CLOCK_GETRES*/
1003 }
1004 
1005 
1006 /* Experimental.  This returns the microsecond-resolution time, wrapped
1007    around the fixnum resolution.  In 32-bit architecture it's a bit more
1008    than 1000seconds.  Good for micro-profiling, since this guarantees
1009    no allocation.  Returned value can be negative. */
Scm_CurrentMicroseconds()1010 long Scm_CurrentMicroseconds()
1011 {
1012     u_long sec, usec;
1013     Scm_GetTimeOfDay(&sec, &usec);
1014     /* we ignore overflow */
1015     usec += sec * 1000000;
1016     usec &= (1UL<<(SCM_SMALL_INT_SIZE+1)) - 1;
1017     if (usec > SCM_SMALL_INT_MAX) usec -= (1UL<<(SCM_SMALL_INT_SIZE+1));
1018     return (long)usec;
1019 }
1020 
Scm_CurrentTime(void)1021 ScmObj Scm_CurrentTime(void)
1022 {
1023     u_long sec, usec;
1024     Scm_GetTimeOfDay(&sec, &usec);
1025     return Scm_MakeTime(SCM_SYM_TIME_UTC, sec, usec*1000);
1026 }
1027 
Scm_IntSecondsToTime(long sec)1028 ScmObj Scm_IntSecondsToTime(long sec)
1029 {
1030     return Scm_MakeTime(SCM_SYM_TIME_UTC, sec, 0);
1031 }
1032 
Scm_Int64SecondsToTime(int64_t sec)1033 ScmObj Scm_Int64SecondsToTime(int64_t sec)
1034 {
1035     return Scm_MakeTime64(SCM_SYM_TIME_UTC, sec, 0);
1036 }
1037 
Scm_RealSecondsToTime(double sec)1038 ScmObj Scm_RealSecondsToTime(double sec)
1039 {
1040     double s;
1041     double frac = modf(sec, &s);
1042     int64_t secs = (int64_t)s;
1043     return Scm_MakeTime64(SCM_SYM_TIME_UTC, secs, (long)(frac * 1.0e9));
1044 }
1045 
time_type_get(ScmTime * t)1046 static ScmObj time_type_get(ScmTime *t)
1047 {
1048     return t->type;
1049 }
1050 
time_type_set(ScmTime * t,ScmObj val)1051 static void time_type_set(ScmTime *t, ScmObj val)
1052 {
1053     if (!SCM_SYMBOLP(val)) {
1054         Scm_Error("time type must be a symbol, but got %S", val);
1055     }
1056     t->type = val;
1057 }
1058 
time_sec_get(ScmTime * t)1059 static ScmObj time_sec_get(ScmTime *t)
1060 {
1061     return Scm_MakeInteger64(t->sec);
1062 }
1063 
time_sec_set(ScmTime * t,ScmObj val)1064 static void time_sec_set(ScmTime *t, ScmObj val)
1065 {
1066     if (!SCM_REALP(val)) {
1067         Scm_Error("real number required, but got %S", val);
1068     }
1069     t->sec = Scm_GetInteger64(val);
1070 }
1071 
time_nsec_get(ScmTime * t)1072 static ScmObj time_nsec_get(ScmTime *t)
1073 {
1074     return Scm_MakeInteger(t->nsec);
1075 }
1076 
time_nsec_set(ScmTime * t,ScmObj val)1077 static void time_nsec_set(ScmTime *t, ScmObj val)
1078 {
1079     if (!SCM_REALP(val)) {
1080         Scm_Error("real number required, but got %S", val);
1081     }
1082     long l = Scm_GetInteger(val);
1083     if (l >= 1000000000) {
1084         Scm_Error("nanoseconds out of range: %ld", l);
1085     }
1086     t->nsec = l;
1087 }
1088 
1089 static ScmClassStaticSlotSpec time_slots[] = {
1090     SCM_CLASS_SLOT_SPEC("type",       time_type_get, time_type_set),
1091     SCM_CLASS_SLOT_SPEC("second",     time_sec_get, time_sec_set),
1092     SCM_CLASS_SLOT_SPEC("nanosecond", time_nsec_get, time_nsec_set),
1093     SCM_CLASS_SLOT_SPEC_END()
1094 };
1095 
1096 /* time_t and conversion routines */
1097 /* NB: I assume time_t is typedefed to either an integral type or
1098  * a floating point type.  As far as I know it is true on most
1099  * current architectures.  POSIX doesn't specify so, however; it
1100  * may be some weird structure.  If you find such an architecture,
1101  * tweak configure.in and modify the following two functions.
1102  */
Scm_MakeSysTime(time_t t)1103 ScmObj Scm_MakeSysTime(time_t t)
1104 {
1105 #ifdef INTEGRAL_TIME_T
1106     return Scm_MakeIntegerFromUI((unsigned long)t);
1107 #else
1108     double val = (double)t;
1109     return Scm_MakeFlonum(val);
1110 #endif
1111 }
1112 
Scm_GetSysTime(ScmObj val)1113 time_t Scm_GetSysTime(ScmObj val)
1114 {
1115     if (SCM_TIMEP(val)) {
1116 #ifdef INTEGRAL_TIME_T
1117         return (time_t)SCM_TIME(val)->sec;
1118 #else
1119         return (time_t)(Scm_Int64ToDouble(SCM_TIME(val)->sec) +
1120                         (double)SCM_TIME(val)->nsec/1.0e9);
1121 #endif
1122     } else if (SCM_NUMBERP(val)) {
1123 #ifdef INTEGRAL_TIME_T
1124         return (time_t)Scm_GetUInteger(val);
1125 #else
1126         return (time_t)Scm_GetDouble(val);
1127 #endif
1128     } else {
1129         Scm_Error("bad time value: either a <time> object or a real number is required, but got %S", val);
1130         return (time_t)0;       /* dummy */
1131     }
1132 }
1133 
Scm_TimeToSeconds(ScmTime * t)1134 ScmObj Scm_TimeToSeconds(ScmTime *t)
1135 {
1136     if (t->nsec) {
1137         return Scm_MakeFlonum((double)(t->sec) + (double)t->nsec/1.0e9);
1138     } else {
1139         return Scm_MakeInteger64(t->sec);
1140     }
1141 }
1142 
1143 /* Scheme time -> timespec conversion */
Scm_GetTimeSpec(ScmObj t,ScmTimeSpec * spec)1144 ScmTimeSpec *Scm_GetTimeSpec(ScmObj t, ScmTimeSpec *spec)
1145 {
1146     if (SCM_FALSEP(t)) return NULL;
1147     if (SCM_TIMEP(t)) {
1148         spec->tv_sec = SCM_TIME(t)->sec;
1149         spec->tv_nsec = SCM_TIME(t)->nsec;
1150     } else if (!SCM_REALP(t)) {
1151         Scm_Error("bad timeout spec: <time> object or real number is required, but got %S", t);
1152     } else {
1153         ScmTime *ct = SCM_TIME(Scm_CurrentTime());
1154         spec->tv_sec = ct->sec;
1155         spec->tv_nsec = ct->nsec;
1156         if (SCM_INTP(t)) {
1157             spec->tv_sec += Scm_GetUInteger(t);
1158         } else if (!SCM_REALP(t)) {
1159             Scm_Panic("implementation error: Scm_GetTimeSpec: something wrong");
1160         } else {
1161             double s;
1162             spec->tv_nsec += (unsigned long)(modf(Scm_GetDouble(t), &s)*1.0e9);
1163             spec->tv_sec += (unsigned long)s;
1164             while (spec->tv_nsec >= 1000000000) {
1165                 spec->tv_nsec -= 1000000000;
1166                 spec->tv_sec += 1;
1167             }
1168         }
1169     }
1170     return spec;
1171 }
1172 
1173 /* <sys-tm> object */
1174 
tm_allocate(ScmClass * klass,ScmObj initargs SCM_UNUSED)1175 static ScmObj tm_allocate(ScmClass *klass, ScmObj initargs SCM_UNUSED)
1176 {
1177     return SCM_OBJ(SCM_NEW_INSTANCE(ScmSysTm, klass));
1178 }
1179 
tm_print(ScmObj obj,ScmPort * port,ScmWriteContext * ctx SCM_UNUSED)1180 static void tm_print(ScmObj obj, ScmPort *port, ScmWriteContext *ctx SCM_UNUSED)
1181 {
1182 #define TM_BUFSIZ 50
1183     char buf[TM_BUFSIZ];
1184     ScmSysTm *st = SCM_SYS_TM(obj);
1185 #if !defined(GAUCHE_WINDOWS)
1186     strftime(buf, TM_BUFSIZ, "%a %b %e %T %Y", &st->tm);
1187 #else  /* GAUCHE_WINDOWS */
1188     strftime(buf, TM_BUFSIZ, "%a %b %d %H:%M:%S %Y", &st->tm);
1189 #endif /* GAUCHE_WINDOWS */
1190     Scm_Printf(port, "#<sys-tm \"%s\">", buf);
1191 #undef TM_BUFSIZ
1192 }
1193 
1194 SCM_DEFINE_BUILTIN_CLASS(Scm_SysTmClass,
1195                          tm_print, NULL, NULL,
1196                          tm_allocate, SCM_CLASS_DEFAULT_CPL);
1197 
Scm_MakeSysTm(struct tm * tm)1198 ScmObj Scm_MakeSysTm(struct tm *tm)
1199 {
1200     ScmSysTm *st = SCM_NEW(ScmSysTm);
1201     SCM_SET_CLASS(st, SCM_CLASS_SYS_TM);
1202     st->tm = *tm;               /* copy */
1203     return SCM_OBJ(st);
1204 }
1205 
1206 #define TM_ACCESSOR(name)                                               \
1207   static ScmObj SCM_CPP_CAT(name, _get)(ScmSysTm *tm) {                 \
1208     return Scm_MakeInteger(tm->tm.name);                                \
1209   }                                                                     \
1210   static void SCM_CPP_CAT(name, _set)(ScmSysTm *tm, ScmObj val) {       \
1211     if (!SCM_EXACTP(val))                                               \
1212       Scm_Error("exact integer required, but got %S", val);             \
1213     tm->tm.name = Scm_GetInteger(val);                                  \
1214   }
1215 
1216 TM_ACCESSOR(tm_sec)
1217 TM_ACCESSOR(tm_min)
1218 TM_ACCESSOR(tm_hour)
1219 TM_ACCESSOR(tm_mday)
1220 TM_ACCESSOR(tm_mon)
1221 TM_ACCESSOR(tm_year)
1222 TM_ACCESSOR(tm_wday)
1223 TM_ACCESSOR(tm_yday)
1224 TM_ACCESSOR(tm_isdst)
1225 
1226 static ScmClassStaticSlotSpec tm_slots[] = {
1227     SCM_CLASS_SLOT_SPEC("sec", tm_sec_get, tm_sec_set),
1228     SCM_CLASS_SLOT_SPEC("min", tm_min_get, tm_min_set),
1229     SCM_CLASS_SLOT_SPEC("hour", tm_hour_get, tm_hour_set),
1230     SCM_CLASS_SLOT_SPEC("mday", tm_mday_get, tm_mday_set),
1231     SCM_CLASS_SLOT_SPEC("mon", tm_mon_get, tm_mon_set),
1232     SCM_CLASS_SLOT_SPEC("year", tm_year_get, tm_year_set),
1233     SCM_CLASS_SLOT_SPEC("wday", tm_wday_get, tm_wday_set),
1234     SCM_CLASS_SLOT_SPEC("yday", tm_yday_get, tm_yday_set),
1235     SCM_CLASS_SLOT_SPEC("isdst", tm_isdst_get, tm_isdst_set),
1236     SCM_CLASS_SLOT_SPEC_END()
1237 };
1238 
1239 /*
1240  * nanosleep() compatibility layer
1241  */
Scm_NanoSleep(const ScmTimeSpec * req,ScmTimeSpec * rem)1242 int Scm_NanoSleep(const ScmTimeSpec *req, ScmTimeSpec *rem)
1243 {
1244 #if defined(GAUCHE_WINDOWS)
1245     /* Recent mingw32 includes nanosleep but it seems broken, so we keep
1246        using this compatibility code for the time being. */
1247     DWORD msecs = 0;
1248     time_t sec;
1249     u_long overflow = 0, c;
1250     const DWORD MSEC_OVERFLOW = 4294967; /* 4294967*1000 = 0xfffffed8 */
1251 
1252     /* It's very unlikely that we overflow msecs, but just in case... */
1253     if (req->tv_sec > 0 || (req->tv_sec == 0 && req->tv_nsec > 0)) {
1254         if ((unsigned)req->tv_sec >= MSEC_OVERFLOW) {
1255             overflow = req->tv_sec / MSEC_OVERFLOW;
1256             sec = req->tv_sec % MSEC_OVERFLOW;
1257         } else {
1258             sec = req->tv_sec;
1259         }
1260         msecs = (sec * 1000 + (req->tv_nsec + 999999)/1000000);
1261     }
1262     Sleep (msecs);
1263     for (c = 0; c < overflow; c++) {
1264         Sleep(MSEC_OVERFLOW * 1000);
1265     }
1266     if (rem) {
1267         rem->tv_sec = rem->tv_nsec = 0;
1268     }
1269     return 0;
1270 #elif defined(HAVE_NANOSLEEP)
1271     return nanosleep(req, rem);
1272 #else   /* !defined(HAVE_NANOSLEEP) && !defined(GAUCHE_WINDOWS) */
1273     /* This case should be excluded at the caller site */
1274     errno = EINVAL;
1275     return -1;
1276 #endif
1277 }
1278 
1279 /*===============================================================
1280  * Yielding CPU (sched.h, if available)
1281  */
1282 
1283 /* If sched_yield is not available, we make the calling thread sleep
1284    small amount of time, hoping there are other threads that can run
1285    in place. */
1286 void
Scm_YieldCPU(void)1287 Scm_YieldCPU(void)
1288 {
1289 #if defined(GAUCHE_WINDOWS)
1290     /* Windows have select(), but it doesn't allow all fds are NULL. */
1291     Sleep(0);
1292 #elif defined(HAVE_SCHED_YIELD)
1293     sched_yield();
1294 #elif defined(HAVE_NANOSLEEP)
1295     /* We can use struct timespec instead of ScmTimeSpec here, for mingw
1296        won't use this path. */
1297     struct timespec spec;
1298     spec.tv_sec = 0;
1299     spec.tv_nsec = 1;
1300     nanosleep(&spec, NULL);
1301 #elif defined(HAVE_SELECT)
1302     struct timeval tv;
1303     tv.tv_sec = 0;
1304     tv.tv_usec = 1;
1305     select(0, NULL, NULL, NULL, &tv);
1306 #else /* the last resort */
1307     sleep(1);
1308 #endif
1309 }
1310 
1311 /*===============================================================
1312  * Groups (grp.h)
1313  */
1314 
grp_print(ScmObj obj,ScmPort * port,ScmWriteContext * ctx SCM_UNUSED)1315 static void grp_print(ScmObj obj, ScmPort *port, ScmWriteContext *ctx SCM_UNUSED)
1316 {
1317     Scm_Printf(port, "#<sys-group %S>",
1318                SCM_SYS_GROUP(obj)->name);
1319 }
1320 
grp_compare(ScmObj x,ScmObj y,int equalp)1321 static int grp_compare(ScmObj x, ScmObj y, int equalp)
1322 {
1323     ScmSysGroup *gx = SCM_SYS_GROUP(x);
1324     ScmSysGroup *gy = SCM_SYS_GROUP(y);
1325 
1326     if (equalp) {
1327         return (Scm_EqualP(gx->name, gy->name)
1328                 && Scm_EqualP(gx->gid, gy->gid)
1329                 && Scm_EqualP(gx->passwd, gy->passwd)
1330                 && Scm_EqualP(gx->mem, gy->mem));
1331     } else {
1332         /* This is arbitrary, but having some order allows the object
1333            to be used as a key in treemap. */
1334         int r = Scm_Compare(gx->gid, gy->gid);
1335         if (r != 0) return r;
1336         r = Scm_Compare(gx->name, gy->name);
1337         if (r != 0) return r;
1338         r = Scm_Compare(gx->passwd, gy->passwd);
1339         if (r != 0) return r;
1340         return Scm_Compare(gx->mem, gy->mem);
1341     }
1342 }
1343 
grp_hash(ScmObj obj,ScmSmallInt salt,u_long flags)1344 static ScmSmallInt grp_hash(ScmObj obj, ScmSmallInt salt, u_long flags)
1345 {
1346     ScmSysGroup *g = SCM_SYS_GROUP(obj);
1347     ScmSmallInt h = salt;
1348     h = Scm_CombineHashValue(Scm_RecursiveHash(g->name, salt, flags), h);
1349     h = Scm_CombineHashValue(Scm_RecursiveHash(g->gid, salt, flags), h);
1350     h = Scm_CombineHashValue(Scm_RecursiveHash(g->passwd, salt, flags), h);
1351     h = Scm_CombineHashValue(Scm_RecursiveHash(g->mem, salt, flags), h);
1352     return h;
1353 }
1354 
1355 SCM_DEFINE_BUILTIN_CLASS(Scm_SysGroupClass,
1356                          grp_print, grp_compare, grp_hash,
1357                          NULL, SCM_CLASS_DEFAULT_CPL);
1358 
make_group(struct group * g)1359 static ScmObj make_group(struct group *g)
1360 {
1361     ScmSysGroup *sg = SCM_NEW(ScmSysGroup);
1362     SCM_SET_CLASS(sg, SCM_CLASS_SYS_GROUP);
1363 
1364     sg->name = SCM_MAKE_STR_COPYING(g->gr_name);
1365 #ifdef HAVE_STRUCT_GROUP_GR_PASSWD
1366     sg->passwd = SCM_MAKE_STR_COPYING(g->gr_passwd);
1367 #else
1368     sg->passwd = SCM_FALSE;
1369 #endif
1370     sg->gid = Scm_MakeInteger(g->gr_gid);
1371     sg->mem = Scm_CStringArrayToList((const char**)g->gr_mem, -1,
1372                                      SCM_MAKSTR_COPYING);
1373     return SCM_OBJ(sg);
1374 }
1375 
Scm_GetGroupById(gid_t gid)1376 ScmObj Scm_GetGroupById(gid_t gid)
1377 {
1378     struct group *gdata = getgrgid(gid);
1379     if (gdata == NULL) {
1380         Scm_SigCheck(Scm_VM());
1381         return SCM_FALSE;
1382     } else {
1383         return make_group(gdata);
1384     }
1385 }
1386 
Scm_GetGroupByName(ScmString * name)1387 ScmObj Scm_GetGroupByName(ScmString *name)
1388 {
1389     struct group *gdata = getgrnam(Scm_GetStringConst(name));
1390     if (gdata == NULL) {
1391         Scm_SigCheck(Scm_VM());
1392         return SCM_FALSE;
1393     } else {
1394         return make_group(gdata);
1395     }
1396 }
1397 
1398 #define GRP_GETTER(name) \
1399   static ScmObj SCM_CPP_CAT3(grp_, name, _get)(ScmSysGroup *s) \
1400   { return s->name; }
1401 
1402 GRP_GETTER(name)
1403 GRP_GETTER(gid)
1404 GRP_GETTER(passwd)
1405 GRP_GETTER(mem)
1406 
1407 static ScmClassStaticSlotSpec grp_slots[] = {
1408     SCM_CLASS_SLOT_SPEC("name",   grp_name_get, NULL),
1409     SCM_CLASS_SLOT_SPEC("gid",    grp_gid_get, NULL),
1410     SCM_CLASS_SLOT_SPEC("passwd", grp_passwd_get, NULL),
1411     SCM_CLASS_SLOT_SPEC("mem",    grp_mem_get, NULL),
1412     SCM_CLASS_SLOT_SPEC_END()
1413 };
1414 
1415 /*===============================================================
1416  * Passwords (pwd.h)
1417  *   Patch provided by Yuuki Takahashi (t.yuuki@mbc.nifty.com)
1418  */
1419 
pwd_print(ScmObj obj,ScmPort * port,ScmWriteContext * ctx SCM_UNUSED)1420 static void pwd_print(ScmObj obj, ScmPort *port,
1421                       ScmWriteContext *ctx SCM_UNUSED)
1422 {
1423     Scm_Printf(port, "#<sys-passwd %S>",
1424                SCM_SYS_PASSWD(obj)->name);
1425 }
1426 
1427 SCM_DEFINE_BUILTIN_CLASS_SIMPLE(Scm_SysPasswdClass, pwd_print);
1428 
make_passwd(struct passwd * pw)1429 static ScmObj make_passwd(struct passwd *pw)
1430 {
1431     ScmSysPasswd *sp = SCM_NEW(ScmSysPasswd);
1432     SCM_SET_CLASS(sp, SCM_CLASS_SYS_PASSWD);
1433 
1434     sp->name = SCM_MAKE_STR_COPYING(pw->pw_name);
1435     sp->uid = Scm_MakeInteger(pw->pw_uid);
1436     sp->gid = Scm_MakeInteger(pw->pw_gid);
1437 #ifdef HAVE_STRUCT_PASSWD_PW_PASSWD
1438     sp->passwd = SCM_MAKE_STR_COPYING(pw->pw_passwd);
1439 #else
1440     sp->passwd = SCM_FALSE;
1441 #endif
1442 #ifdef HAVE_STRUCT_PASSWD_PW_GECOS
1443     sp->gecos = SCM_MAKE_STR_COPYING(pw->pw_gecos);
1444 #else
1445     sp->gecos = SCM_FALSE;
1446 #endif
1447 #ifdef HAVE_STRUCT_PASSWD_PW_CLASS
1448     sp->pwclass = SCM_MAKE_STR_COPYING(pw->pw_class);
1449 #else
1450     sp->pwclass = SCM_FALSE;
1451 #endif
1452     sp->dir = SCM_MAKE_STR_COPYING(pw->pw_dir);
1453     sp->shell = SCM_MAKE_STR_COPYING(pw->pw_shell);
1454     return SCM_OBJ(sp);
1455 }
1456 
Scm_GetPasswdById(uid_t uid)1457 ScmObj Scm_GetPasswdById(uid_t uid)
1458 {
1459     struct passwd *pdata = getpwuid(uid);
1460     if (pdata == NULL) {
1461         Scm_SigCheck(Scm_VM());
1462         return SCM_FALSE;
1463     } else {
1464         return make_passwd(pdata);
1465     }
1466 }
1467 
Scm_GetPasswdByName(ScmString * name)1468 ScmObj Scm_GetPasswdByName(ScmString *name)
1469 {
1470     struct passwd *pdata = getpwnam(Scm_GetStringConst(name));
1471     if (pdata == NULL) {
1472         Scm_SigCheck(Scm_VM());
1473         return SCM_FALSE;
1474     } else {
1475         return make_passwd(pdata);
1476     }
1477 }
1478 
1479 #define PWD_GETTER(name) \
1480   static ScmObj SCM_CPP_CAT3(pwd_, name, _get)(ScmSysPasswd *p) \
1481   { return p->name; }
1482 
1483 PWD_GETTER(name)
1484 PWD_GETTER(uid)
1485 PWD_GETTER(gid)
1486 PWD_GETTER(passwd)
1487 PWD_GETTER(gecos)
1488 PWD_GETTER(dir)
1489 PWD_GETTER(shell)
1490 PWD_GETTER(pwclass)
1491 
1492 static ScmClassStaticSlotSpec pwd_slots[] = {
1493     SCM_CLASS_SLOT_SPEC("name",   pwd_name_get, NULL),
1494     SCM_CLASS_SLOT_SPEC("uid",    pwd_uid_get, NULL),
1495     SCM_CLASS_SLOT_SPEC("gid",    pwd_gid_get, NULL),
1496     SCM_CLASS_SLOT_SPEC("passwd", pwd_passwd_get, NULL),
1497     SCM_CLASS_SLOT_SPEC("gecos",  pwd_gecos_get, NULL),
1498     SCM_CLASS_SLOT_SPEC("dir",    pwd_dir_get, NULL),
1499     SCM_CLASS_SLOT_SPEC("shell",  pwd_shell_get, NULL),
1500     SCM_CLASS_SLOT_SPEC("class",  pwd_pwclass_get, NULL),
1501     SCM_CLASS_SLOT_SPEC_END()
1502 };
1503 
1504 /*
1505  * Check if we're suid/sgid-ed.
1506  */
1507 
1508 /* We "remember" the initial state, in case issetugid() isn't available.
1509    This isn't perfect, for the process may change euid/egid before calling
1510    Scm_Init().  */
1511 static int initial_ugid_differ = FALSE;
1512 
Scm_IsSugid(void)1513 int Scm_IsSugid(void)
1514 {
1515 #if HAVE_ISSETUGID
1516     return issetugid();
1517 #else
1518     return initial_ugid_differ;
1519 #endif /* GAUCHE_WINDOWS */
1520 }
1521 
1522 /*===============================================================
1523  * Process management
1524  */
1525 
1526 /* Child process management (windows only)
1527  *   On windows, parent-child relationship is very weak.  The system
1528  *   records parent's pid (and we can query it in a very twisted way), but
1529  *   the child's process record is discarded upon child's termination
1530  *   unless the parent keeps its process handle.   To emulate exec-wait
1531  *   semantics, we keep the list of child process handles whose status is
1532  *   unclaimed.
1533  *   One issue is that we cannot wait() for child processes that
1534  *   are created by Gauche extension code and not using Scm_SysExec API.
1535  */
1536 #if defined(GAUCHE_WINDOWS)
1537 static struct process_mgr_rec {
1538     ScmObj children;
1539     ScmInternalMutex mutex;
1540 } process_mgr = { SCM_NIL, SCM_INTERNAL_MUTEX_INITIALIZER };
1541 
win_process_register(ScmObj process)1542 ScmObj win_process_register(ScmObj process)
1543 {
1544     SCM_ASSERT(Scm_WinProcessP(process));
1545     ScmObj pair = Scm_Cons(process, SCM_NIL);
1546     SCM_INTERNAL_MUTEX_LOCK(process_mgr.mutex);
1547     SCM_SET_CDR_UNCHECKED(pair, process_mgr.children);
1548     process_mgr.children = pair;
1549     SCM_INTERNAL_MUTEX_UNLOCK(process_mgr.mutex);
1550     return process;
1551 }
1552 
win_process_unregister(ScmObj process)1553 ScmObj win_process_unregister(ScmObj process)
1554 {
1555     SCM_INTERNAL_MUTEX_LOCK(process_mgr.mutex);
1556     process_mgr.children = Scm_DeleteX(process, process_mgr.children,
1557                                        SCM_CMP_EQ);
1558     SCM_INTERNAL_MUTEX_UNLOCK(process_mgr.mutex);
1559     return process;
1560 }
1561 
win_process_active_child_p(ScmObj process)1562 int win_process_active_child_p(ScmObj process)
1563 {
1564     SCM_INTERNAL_MUTEX_LOCK(process_mgr.mutex);
1565     ScmObj r = Scm_Member(process, process_mgr.children, SCM_CMP_EQ);
1566     SCM_INTERNAL_MUTEX_UNLOCK(process_mgr.mutex);
1567     return !SCM_FALSEP(r);
1568 }
1569 
win_process_get_array(int * size)1570 ScmObj *win_process_get_array(int *size /*out*/)
1571 {
1572     SCM_INTERNAL_MUTEX_LOCK(process_mgr.mutex);
1573     ScmSize array_size;
1574     ScmObj *r = Scm_ListToArray(process_mgr.children, &array_size, NULL, TRUE);
1575     *size = (int)array_size;
1576     SCM_INTERNAL_MUTEX_UNLOCK(process_mgr.mutex);
1577     return r;
1578 }
1579 
win_process_cleanup(void * data SCM_UNUSED)1580 void win_process_cleanup(void *data SCM_UNUSED)
1581 {
1582     SCM_INTERNAL_MUTEX_LOCK(process_mgr.mutex);
1583     ScmObj cp;
1584     SCM_FOR_EACH(cp, process_mgr.children) {
1585         CloseHandle(Scm_WinHandle(SCM_CAR(cp), SCM_FALSE));
1586     }
1587     process_mgr.children = SCM_NIL;
1588     SCM_INTERNAL_MUTEX_UNLOCK(process_mgr.mutex);
1589 }
1590 #endif /*GAUCHE_WINDOWS*/
1591 
1592 /* Command line construction (Windows only)
1593  *   In order to use CreateProcess we have to concatenate all arguments
1594  *   into one command line string.  Proper escaping should be considered
1595  *   when the arguments include whitespaces or double-quotes.
1596  *   It's pretty silly that we have to do this, since the child process
1597  *   crt will re-parse the command line again.  Besides, since the parsing
1598  *   of the command line is up to each application, THERE IS NO WAY TO
1599  *   GUARANTEE TO QUOTE THE ARGUMENTS PROPERLY.   This is intolerably
1600  *   broken specification.
1601  */
1602 #if defined(GAUCHE_WINDOWS)
win_create_command_line(ScmObj args)1603 char *win_create_command_line(ScmObj args)
1604 {
1605     ScmObj ostr = Scm_MakeOutputStringPort(TRUE);
1606     static ScmObj proc = SCM_UNDEFINED;
1607     SCM_BIND_PROC(proc, "%sys-escape-windows-command-line", Scm_GaucheModule());
1608     ScmObj ap;
1609     SCM_FOR_EACH(ap, args) {
1610       ScmObj escaped = Scm_ApplyRec1(proc, SCM_CAR(ap));
1611       Scm_Printf(SCM_PORT(ostr), "%A ", escaped);
1612     }
1613     ScmObj out = Scm_GetOutputStringUnsafe(SCM_PORT(ostr), 0);
1614     return Scm_GetString(SCM_STRING(out));
1615 }
1616 #endif /*GAUCHE_WINDOWS*/
1617 
1618 /* Scm_SysExec
1619  *   execvp(), with optionally setting stdios correctly.
1620  *
1621  *   iomap argument, when provided, specifies how the open file descriptors
1622  *   are treated.  If it is not a pair, nothing will be changed for open
1623  *   file descriptors.  If it is a pair, it must be a list of
1624  *   (<to> . <from>), where <tofd> is an integer file descriptor that
1625  *   executed process will get, and <from> is either an integer file descriptor
1626  *   or a port.   If a list is passed to iomap, any file descriptors other
1627  *   than specified in the list will be closed before exec().
1628  *
1629  *   If forkp arg is TRUE, this function forks before swapping file
1630  *   descriptors.  It is more reliable way to fork&exec in multi-threaded
1631  *   program.  In such a case, this function returns Scheme integer to
1632  *   show the children's pid.   If fork arg is FALSE, this procedure
1633  *   of course never returns.
1634  *
1635  *   On Windows port, this returns a process handle obejct instead of
1636  *   pid of the child process in fork mode.  We need to keep handle, or
1637  *   the process exit status will be lost when the child process terminates.
1638  */
Scm_SysExec(ScmString * file,ScmObj args,ScmObj iomap,ScmSysSigset * mask,ScmString * dir,int flags)1639 ScmObj Scm_SysExec(ScmString *file, ScmObj args, ScmObj iomap,
1640                    ScmSysSigset *mask, ScmString *dir, int flags)
1641 {
1642     int argc = Scm_Length(args);
1643     pid_t pid = 0;
1644     int forkp = flags & SCM_EXEC_WITH_FORK;
1645     int detachp = flags & SCM_EXEC_DETACHED;
1646 
1647     if (argc < 1) {
1648         Scm_Error("argument list must have at least one element: %S", args);
1649     }
1650 
1651     /* make a C array of C strings */
1652     char **argv = Scm_ListToCStringArray(args, TRUE, NULL);
1653     const char *program = Scm_GetStringConst(file);
1654 
1655     /* setting up iomap table */
1656     int *fds = Scm_SysPrepareFdMap(iomap);
1657 
1658     /*
1659      * From now on, we have totally different code for Unix and Windows.
1660      */
1661 #if !defined(GAUCHE_WINDOWS)
1662     /*
1663      * Unix path
1664      */
1665     const char *cdir = NULL;
1666     if (dir != NULL) cdir = Scm_GetStringConst(dir);
1667 
1668     /* When requested, call fork() here. */
1669     if (forkp) {
1670         SCM_SYSCALL(pid, fork());
1671         if (pid < 0) Scm_SysError("fork failed");
1672     }
1673 
1674     if (!forkp || pid == 0) {   /* possibly the child process */
1675 
1676         /* If we're running the daemon, we fork again to detach the parent,
1677            and also reset the session id. */
1678         if (detachp) {
1679             SCM_SYSCALL(pid, fork());
1680             if (pid < 0) Scm_SysError("fork failed");
1681             if (pid > 0) exit(0);   /* not Scm_Exit(), for we don't want to
1682                                        run the cleanup stuff. */
1683             setsid();
1684         }
1685 
1686         if (cdir != NULL) {
1687             if (chdir(cdir) < 0) {
1688                 Scm_Panic("chdir to %s failed before executing %s: %s",
1689                           cdir, program, strerror(errno));
1690             }
1691         }
1692 
1693         Scm_SysSwapFds(fds);
1694         if (mask) {
1695             Scm_ResetSignalHandlers(&mask->set);
1696             Scm_SysSigmask(SIG_SETMASK, mask);
1697         }
1698 
1699         execvp(program, (char *const*)argv);
1700         /* here, we failed */
1701         Scm_Panic("exec failed: %s: %s", program, strerror(errno));
1702     }
1703 
1704     /* We come here only when fork is requested. */
1705     return Scm_MakeInteger(pid);
1706 #else  /* GAUCHE_WINDOWS */
1707     /*
1708      * Windows path
1709      */
1710     const char *cdir = NULL;
1711     (void)mask; /* suppress unused var warning */
1712     (void)pid;  /* suppress unused var warning */
1713     if (dir != NULL) {
1714         /* we need full path for CreateProcess. */
1715         dir = SCM_STRING(Scm_NormalizePathname(dir, SCM_PATH_ABSOLUTE|SCM_PATH_CANONICALIZE));
1716         cdir = Scm_GetStringConst(dir);
1717 
1718         /* If the program is given in relative pathname,
1719            it must be adjusted relative to the specified directory. */
1720         if (program[0] != '/' && program[0] != '\\'
1721             && !(program[0] && program[1] == ':')) {
1722             ScmDString ds;
1723             int c = cdir[strlen(cdir)-1];
1724             Scm_DStringInit(&ds);
1725             Scm_DStringPutz(&ds, cdir, -1);
1726             if (c != '/' && c != '\\') Scm_DStringPutc(&ds, SCM_CHAR('/'));
1727             Scm_DStringPutz(&ds, program, -1);
1728             program = Scm_DStringGetz(&ds);
1729         }
1730     }
1731 
1732     if (forkp) {
1733         TCHAR  program_path[MAX_PATH+1], *filepart;
1734         win_redirects *hs = win_prepare_handles(fds);
1735         PROCESS_INFORMATION pi;
1736         DWORD creation_flags = 0;
1737 
1738         BOOL pathlen = SearchPath(NULL, SCM_MBS2WCS(program),
1739                                   _T(".exe"), MAX_PATH, program_path,
1740                                   &filepart);
1741         if (pathlen == 0) Scm_SysError("cannot find program '%s'", program);
1742         program_path[pathlen] = 0;
1743 
1744         STARTUPINFO si;
1745         GetStartupInfo(&si);
1746         if (hs != NULL) {
1747             si.dwFlags |= STARTF_USESTDHANDLES;
1748             si.hStdInput  = hs[0].h;
1749             si.hStdOutput = hs[1].h;
1750             si.hStdError  = hs[2].h;
1751         }
1752 
1753         LPCTSTR curdir = NULL;
1754         if (cdir != NULL) curdir = SCM_MBS2WCS(cdir);
1755 
1756         if (detachp) {
1757             creation_flags |= CREATE_NEW_PROCESS_GROUP;
1758         }
1759 
1760         BOOL r = CreateProcess(program_path,
1761                                SCM_MBS2WCS(win_create_command_line(args)),
1762                                NULL, /* process attr */
1763                                NULL, /* thread addr */
1764                                TRUE, /* inherit handles */
1765                                creation_flags, /* creation flags */
1766                                NULL, /* nenvironment */
1767                                curdir, /* current dir */
1768                                &si,  /* startup info */
1769                                &pi); /* process info */
1770         if (hs != NULL) {
1771             for (int i=0; i<3; i++) {
1772                 /* hs[i].h may be a handle duped in win_prepare_handles().
1773                    We have to close it in parent process or they would be
1774                    inherited to subsequent child process.  (The higher-level
1775                    Scheme routine closes the open end of the pipe, but that
1776                    won't affect the duped one. */
1777                 if (hs[i].duped) CloseHandle(hs[i].h);
1778             }
1779         }
1780         if (r == 0) Scm_SysError("spawning %s failed", program);
1781         CloseHandle(pi.hThread); /* we don't need it. */
1782         return win_process_register(Scm_MakeWinProcess(pi.hProcess));
1783     } else {
1784         Scm_SysSwapFds(fds);
1785         if (cdir != NULL) {
1786             if (_chdir(cdir) < 0) {
1787                 Scm_SysError("Couldn't chdir to %s", cdir);
1788             }
1789         }
1790         /* TODO: We should probably use Windows API to handle various
1791            options consistently with fork-and-exec case above. */
1792 #if defined(__MINGW64_VERSION_MAJOR)
1793         execvp(program, (char *const*)argv);
1794 #else  /* !defined(__MINGW64_VERSION_MAJOR) */
1795         execvp(program, (const char *const*)argv);
1796 #endif /* !defined(__MINGW64_VERSION_MAJOR) */
1797         Scm_Panic("exec failed: %s: %s", program, strerror(errno));
1798     }
1799     return SCM_FALSE; /* dummy */
1800 #endif /* GAUCHE_WINDOWS */
1801 }
1802 
1803 /* Two auxiliary functions to support iomap feature.  They are exposed
1804    so that the library can implement iomap feature as the same way as
1805    sys-exec.
1806 
1807    The first function, Scm_SysPrepareFdMap, walks iomap structure and
1808    prepare a table of file descriptors to modify.  The second function,
1809    Scm_SysSwapFds, takes the table and modifies process's file descriptors.
1810 
1811    We need to split this feature to two function, since it is unsafe
1812    to raise an error after fork() in multi-threaded environment.
1813    Scm_SysPrepareFdMap may throw an error if passed iomap contains
1814    invalid entries.  On the other hand, Scm_SysSwapFds just aborts if
1815    things goes wrong---not only because of the MT-safety issue, but also
1816    it is generally impossible to handle errors reasonably since we don't
1817    even sure we have stdios.   And the client code is supposed to call
1818    fork() between these functions.
1819 
1820    The client code should treat the returned pointer of Scm_SysPrepareFdMap
1821    opaque, and pass it to Scm_SysSwapFds as is.
1822 */
Scm_SysPrepareFdMap(ScmObj iomap)1823 int *Scm_SysPrepareFdMap(ScmObj iomap)
1824 {
1825     int *fds = NULL;
1826     if (SCM_PAIRP(iomap)) {
1827         int iollen = Scm_Length(iomap);
1828 
1829         /* check argument vailidity before duping file descriptors, so that
1830            we can still use Scm_Error */
1831         if (iollen < 0) {
1832             Scm_Error("proper list required for iolist, but got %S", iomap);
1833         }
1834         fds    = SCM_NEW_ATOMIC2(int *, 2 * iollen * sizeof(int) + 1);
1835         fds[0] = iollen;
1836         int *tofd   = fds + 1;
1837         int *fromfd = fds + 1 + iollen;
1838 
1839         ScmObj iop;
1840         int i = 0;
1841         SCM_FOR_EACH(iop, iomap) {
1842             ScmObj port, elt = SCM_CAR(iop);
1843             if (!SCM_PAIRP(elt) || !SCM_INTP(SCM_CAR(elt))
1844                 || (!SCM_PORTP(SCM_CDR(elt)) && !SCM_INTP(SCM_CDR(elt)))) {
1845                 Scm_Error("bad iomap specification: needs (int . int-or-port): %S", elt);
1846             }
1847             tofd[i] = SCM_INT_VALUE(SCM_CAR(elt));
1848             if (SCM_INTP(SCM_CDR(elt))) {
1849                 fromfd[i] = SCM_INT_VALUE(SCM_CDR(elt));
1850             } else {
1851                 port = SCM_CDAR(iop);
1852                 fromfd[i] = Scm_PortFileNo(SCM_PORT(port));
1853                 if (fromfd[i] < 0) {
1854                     Scm_Error("iolist requires a port that has associated file descriptor, but got %S",
1855                               SCM_CDAR(iop));
1856                 }
1857                 if (tofd[i] == 0 && !SCM_IPORTP(port))
1858                     Scm_Error("input port required to make it stdin: %S",
1859                               port);
1860                 if (tofd[i] == 1 && !SCM_OPORTP(port))
1861                     Scm_Error("output port required to make it stdout: %S",
1862                               port);
1863                 if (tofd[i] == 2 && !SCM_OPORTP(port))
1864                     Scm_Error("output port required to make it stderr: %S",
1865                               port);
1866             }
1867             i++;
1868         }
1869     }
1870     return fds;
1871 }
1872 
Scm_SysSwapFds(int * fds)1873 void Scm_SysSwapFds(int *fds)
1874 {
1875     if (fds == NULL) return;
1876 
1877     int maxfd;
1878     int nfds = fds[0];
1879     int *tofd   = fds + 1;
1880     int *fromfd = fds + 1 + nfds;
1881 
1882     /* TODO: use getdtablehi if available */
1883 #if !defined(GAUCHE_WINDOWS)
1884     if ((maxfd = sysconf(_SC_OPEN_MAX)) < 0) {
1885         Scm_Panic("failed to get OPEN_MAX value from sysconf");
1886     }
1887 #else  /*GAUCHE_WINDOWS*/
1888     maxfd = 256;        /* guess it and cross your finger */
1889 #endif /*GAUCHE_WINDOWS*/
1890 
1891     /* Dup fromfd to the corresponding tofd.  We need to be careful
1892        not to override the destination fd if it will be used. */
1893     for (int i=0; i<nfds; i++) {
1894         if (tofd[i] == fromfd[i]) continue;
1895         for (int j=i+1; j<nfds; j++) {
1896             if (tofd[i] == fromfd[j]) {
1897                 int tmp = dup(tofd[i]);
1898                 if (tmp < 0) Scm_Panic("dup failed: %s", strerror(errno));
1899                 fromfd[j] = tmp;
1900             }
1901         }
1902         if (dup2(fromfd[i], tofd[i]) < 0)
1903             Scm_Panic("dup2 failed: %s", strerror(errno));
1904     }
1905 
1906     /* Close unused fds */
1907     for (int fd=0; fd<maxfd; fd++) {
1908         int j;
1909         for (j=0; j<nfds; j++) if (fd == tofd[j]) break;
1910         if (j == nfds) close(fd);
1911     }
1912 }
1913 
1914 #if defined(GAUCHE_WINDOWS)
1915 /* Fds is Scm_SysPrepareFdMap returns. */
win_prepare_handles(int * fds)1916 static win_redirects *win_prepare_handles(int *fds)
1917 {
1918     if (fds == NULL) return NULL;
1919 
1920     /* For the time being, we only consider stdin, stdout, and stderr. */
1921     win_redirects *hs = SCM_NEW_ATOMIC_ARRAY(win_redirects, 3);
1922     int count = fds[0];
1923 
1924     for (int i=0; i<count; i++) {
1925         int to = fds[i+1], from = fds[i+1+count];
1926         if (to >= 0 && to < 3) {
1927             if (from >= 3) {
1928                 /* FROM may be a pipe.  in that case, it will be closed
1929                    in the higher-level, so we shouldn't give
1930                    DUPLICATE_CLOSE_SOURCE here. */
1931                 HANDLE zh;
1932                 if (!DuplicateHandle(GetCurrentProcess(),
1933                                      (HANDLE)_get_osfhandle(from),
1934                                      GetCurrentProcess(),
1935                                      &zh,
1936                                      0, TRUE,
1937                                      DUPLICATE_SAME_ACCESS)) {
1938                     Scm_SysError("DuplicateHandle failed");
1939                 }
1940                 hs[to].h = zh;
1941                 hs[to].duped = TRUE;
1942             } else {
1943                 hs[to].h = (HANDLE)_get_osfhandle(from);
1944                 hs[to].duped = FALSE;
1945             }
1946         }
1947     }
1948     for (int i=0; i<3; i++) {
1949         if (hs[i].h == NULL) {
1950             hs[i].h = (HANDLE)_get_osfhandle(i);
1951             hs[i].duped = FALSE;
1952         }
1953     }
1954     return hs;
1955 }
1956 #endif /*GAUCHE_WINDOWS*/
1957 
1958 /*===============================================================
1959  * Kill
1960  *
1961  *  It is simple on Unix, but on windows it is a lot more involved,
1962  *  mainly due to the lack of signals as the means of IPC.
1963  */
Scm_SysKill(ScmObj process,int signal)1964 void Scm_SysKill(ScmObj process, int signal)
1965 {
1966 #if !defined(GAUCHE_WINDOWS)
1967     pid_t pid;
1968     int r;
1969     if (!SCM_INTEGERP(process)) SCM_TYPE_ERROR(process, "integer process id");
1970     pid = Scm_GetInteger(process);
1971     SCM_SYSCALL(r, kill(pid, signal));
1972     if (r < 0) Scm_SysError("kill failed");
1973 #else  /*GAUCHE_WINDOWS*/
1974     /* You cannot really "send" signals to other processes on Windows.
1975        We try to emulate SIGKILL and SIGINT by Windows API.
1976        To send a signal to the current process we can use raise(). */
1977     HANDLE p;
1978     BOOL r;
1979     DWORD errcode;
1980     int pid_given = FALSE;
1981     pid_t pid = 0;
1982 
1983     if (SCM_INTEGERP(process)) {
1984         pid_given = TRUE; pid = Scm_GetInteger(process);
1985     } else if (Scm_WinProcessP(process)) {
1986         pid = Scm_WinProcessPID(process);
1987     } else {
1988         SCM_TYPE_ERROR(process, "process handle or integer process id");
1989     }
1990 
1991     if (signal == SIGKILL) {
1992         if (pid_given) {
1993             p = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
1994             if (p == NULL) Scm_SysError("OpenProcess failed for pid %d", pid);
1995         } else {
1996             p = Scm_WinProcess(process);
1997         }
1998         /* We send 0xff00 + KILL, so that the receiving process (if it is
1999            Gauche) can yield an exit status that indicates it is kill. */
2000         r = TerminateProcess(p, SIGKILL+0xff00);
2001         errcode = GetLastError();
2002         if (pid_given) CloseHandle(p);
2003         SetLastError(errcode);
2004         if (r == 0) Scm_SysError("TerminateProcess failed");
2005         return;
2006     }
2007     /* another idea; we may map SIGTERM to WM_CLOSE message. */
2008 
2009     if (signal == 0) {
2010         /* We're supposed to do the error check without actually sending
2011            the signal.   For now we just pretend nothing's wrong. */
2012         return;
2013     }
2014     if (pid == getpid()) {
2015         /* we're sending signal to the current process. */
2016         int r = raise(signal); /* r==0 is success */
2017         if (r < 0) Scm_SysError("raise failed");
2018         return;
2019     }
2020     if (signal == SIGINT || signal == SIGABRT) {
2021         /* we can emulate these signals by console event, although the
2022            semantics of process group differ from unix significantly.
2023            Process group id is the same as the pid of the process
2024            that started the group.  So you cannot send SIGABRT only
2025            to the process group leader.  OTOH, for SIGINT, the windows
2026            manual says it always directed to the specified process,
2027            not the process group, unless pid == 0 */
2028         if (pid < 0) pid = -pid;
2029         r = GenerateConsoleCtrlEvent(pid,
2030                                      (signal == SIGINT)?
2031                                      CTRL_C_EVENT : CTRL_BREAK_EVENT);
2032         if (r == 0) {
2033             Scm_SysError("GenerateConsoleCtrlEvent failed for process %d", pid);
2034         }
2035         return;
2036     }
2037     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2038 #endif /*GAUCHE_WINDOWS*/
2039 }
2040 
2041 /*===============================================================
2042  * Wait
2043  *
2044  *  A wrapper of waitpid.  Returns two values---the process object or pid that
2045  *  whose status has been taken, and the exit status.
2046  *  Again, it is simple on Unix, but on windows it is a lot more involved.
2047  */
2048 
Scm_SysWait(ScmObj process,int options)2049 ScmObj Scm_SysWait(ScmObj process, int options)
2050 {
2051 #if !defined(GAUCHE_WINDOWS)
2052     pid_t r;
2053     int status = 0;
2054     if (!SCM_INTEGERP(process)) SCM_TYPE_ERROR(process, "integer process id");
2055     SCM_SYSCALL(r, waitpid(Scm_GetInteger(process), &status, options));
2056     if (r < 0) Scm_SysError("waitpid() failed");
2057     return Scm_Values2(Scm_MakeInteger(r), Scm_MakeInteger(status));
2058 #else  /* GAUCHE_WINDOWS */
2059     /* We have four cases
2060        process is integer and < -1   -> not supported.
2061        process is -1 or 0 -> wait for all children (we ignore process group)
2062        process is integer and > 0  -> wait for specific pid
2063        process is #<win:process-handle> -> wait for specified process
2064        The common op is factored out in win_wait_for_handles. */
2065     int r, status = 0;
2066 
2067     if (SCM_INTEGERP(process)) {
2068         pid_t pid = Scm_GetInteger(process);
2069         if (pid < -1) {
2070             /* Windows doesn't have the concept of "process group id" */
2071             SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2072             Scm_SysError("waitpid cannot wait for process group on Windows.");
2073         }
2074         if (pid > 0) {
2075             /* wait for specific pid */
2076             HANDLE handle = OpenProcess(SYNCHRONIZE|PROCESS_QUERY_INFORMATION,
2077                                         FALSE, pid);
2078             DWORD errcode;
2079             if (handle == NULL) {
2080                 Scm_SysError("OpenProcess failed for pid %d", pid);
2081             }
2082             r = win_wait_for_handles(&handle, 1, options, &status);
2083             errcode = GetLastError();
2084             CloseHandle(handle);
2085             SetLastError(errcode);
2086             if (r == -2) goto timeout;
2087             if (r == -1) goto error;
2088             return Scm_Values2(Scm_MakeInteger(pid), Scm_MakeInteger(status));
2089         }
2090         else {
2091             /* wait for any children. */
2092             ScmObj *children;
2093             int num_children, i;
2094             HANDLE *handles;
2095             children = win_process_get_array(&num_children);
2096             if (num_children == 0) {
2097                 SetLastError(ERROR_WAIT_NO_CHILDREN);
2098                 Scm_SysError("waitpid failed");
2099             }
2100             handles = SCM_NEW_ATOMIC_ARRAY(HANDLE, num_children);
2101             for (i=0; i<num_children; i++) {
2102                 handles[i] = Scm_WinProcess(children[i]);
2103             }
2104             r = win_wait_for_handles(handles, num_children, options, &status);
2105             if (r == -2) goto timeout;
2106             if (r == -1) goto error;
2107             win_process_unregister(children[r]);
2108             return Scm_Values2(children[r], Scm_MakeInteger(status));
2109         }
2110     } else if (Scm_WinProcessP(process)) {
2111         /* wait for the specified process */
2112         HANDLE handle;
2113         if (!win_process_active_child_p(process)) {
2114             SetLastError(ERROR_WAIT_NO_CHILDREN);
2115             Scm_SysError("waitpid failed");
2116         }
2117         handle = Scm_WinProcess(process);
2118         r = win_wait_for_handles(&handle, 1, options, &status);
2119         if (r == -2) goto timeout;
2120         if (r == -1) goto error;
2121         win_process_unregister(process);
2122         return Scm_Values2(process, Scm_MakeInteger(status));
2123     }
2124   timeout:
2125     return Scm_Values2(SCM_MAKE_INT(0), SCM_MAKE_INT(0));
2126   error:
2127     Scm_SysError("waitpid failed");
2128     return SCM_UNDEFINED;  /* dummy */
2129 #endif /* GAUCHE_WINDOWS */
2130 }
2131 
2132 #if defined(GAUCHE_WINDOWS)
2133 /* aux fn. */
win_wait_for_handles(HANDLE * handles,int nhandles,int options,int * status)2134 static int win_wait_for_handles(HANDLE *handles, int nhandles, int options,
2135                                 int *status /*out*/)
2136 {
2137     DWORD r = MsgWaitForMultipleObjects(nhandles,
2138                                         handles,
2139                                         FALSE,
2140                                         (options&WNOHANG)? 0 : INFINITE,
2141                                         0);
2142     if (r == WAIT_FAILED) return -1;
2143     if (r == WAIT_TIMEOUT) return -2;
2144     if ((int)r >= (int)WAIT_OBJECT_0 && (int)r < (int)WAIT_OBJECT_0 + nhandles) {
2145         DWORD exitcode;
2146         int index = r - WAIT_OBJECT_0;
2147         r = GetExitCodeProcess(handles[index], &exitcode);
2148         if (r == 0) return -1;
2149         *status = exitcode;
2150         return index;
2151     }
2152     return -1;
2153 }
2154 #endif /*GAUCHE_WINDOWS*/
2155 
2156 /*===============================================================
2157  * select
2158  */
2159 
2160 #ifdef HAVE_SELECT
fdset_allocate(ScmClass * klass,ScmObj initargs SCM_UNUSED)2161 static ScmObj fdset_allocate(ScmClass *klass, ScmObj initargs SCM_UNUSED)
2162 {
2163     ScmSysFdset *set = SCM_NEW_INSTANCE(ScmSysFdset, klass);
2164     set->maxfd = -1;
2165     FD_ZERO(&set->fdset);
2166     return SCM_OBJ(set);
2167 }
2168 
fdset_copy(ScmSysFdset * fdset)2169 static ScmSysFdset *fdset_copy(ScmSysFdset *fdset)
2170 {
2171     ScmSysFdset *set = SCM_NEW(ScmSysFdset);
2172     SCM_SET_CLASS(set, SCM_CLASS_SYS_FDSET);
2173     set->maxfd = fdset->maxfd;
2174     set->fdset = fdset->fdset;
2175     return set;
2176 }
2177 
2178 SCM_DEFINE_BUILTIN_CLASS(Scm_SysFdsetClass, NULL, NULL, NULL,
2179                          fdset_allocate, SCM_CLASS_DEFAULT_CPL);
2180 
select_checkfd(ScmObj fds)2181 static ScmSysFdset *select_checkfd(ScmObj fds)
2182 {
2183     if (SCM_FALSEP(fds)) return NULL;
2184     if (!SCM_SYS_FDSET_P(fds))
2185         Scm_Error("sys-fdset object or #f is required, but got %S", fds);
2186     return SCM_SYS_FDSET(fds);
2187 }
2188 
select_timeval(ScmObj timeout,struct timeval * tm)2189 static struct timeval *select_timeval(ScmObj timeout, struct timeval *tm)
2190 {
2191     if (SCM_FALSEP(timeout)) return NULL;
2192     if (SCM_INTP(timeout)) {
2193         int val = SCM_INT_VALUE(timeout);
2194         if (val < 0) goto badtv;
2195         tm->tv_sec = val / 1000000;
2196         tm->tv_usec = val % 1000000;
2197         return tm;
2198     } else if (SCM_BIGNUMP(timeout)) {
2199         long usec;
2200         ScmObj sec;
2201         if (Scm_Sign(timeout) < 0) goto badtv;
2202         sec = Scm_BignumDivSI(SCM_BIGNUM(timeout), 1000000, &usec);
2203         tm->tv_sec = Scm_GetInteger(sec);
2204         tm->tv_usec = usec;
2205         return tm;
2206     } else if (SCM_FLONUMP(timeout)) {
2207         long val = Scm_GetInteger(timeout);
2208         if (val < 0) goto badtv;
2209         tm->tv_sec = val / 1000000;
2210         tm->tv_usec = val % 1000000;
2211         return tm;
2212     } else if (SCM_PAIRP(timeout) && SCM_PAIRP(SCM_CDR(timeout))) {
2213         ScmObj sec = SCM_CAR(timeout);
2214         ScmObj usec = SCM_CADR(timeout);
2215         long isec, iusec;
2216         if (!Scm_IntegerP(sec) || !Scm_IntegerP(usec)) goto badtv;
2217         isec = Scm_GetInteger(sec);
2218         iusec = Scm_GetInteger(usec);
2219         if (isec < 0 || iusec < 0) goto badtv;
2220         tm->tv_sec = isec;
2221         tm->tv_usec = iusec;
2222         return tm;
2223     }
2224   badtv:
2225     Scm_Error("timeval needs to be a real number (in microseconds) or a list of two integers (seconds and microseconds), but got %S", timeout);
2226     return NULL;                /* dummy */
2227 }
2228 
select_int(ScmSysFdset * rfds,ScmSysFdset * wfds,ScmSysFdset * efds,ScmObj timeout)2229 static ScmObj select_int(ScmSysFdset *rfds, ScmSysFdset *wfds,
2230                          ScmSysFdset *efds, ScmObj timeout)
2231 {
2232     int numfds, maxfds = 0;
2233     struct timeval tm;
2234     if (rfds) maxfds = rfds->maxfd;
2235     if (wfds && wfds->maxfd > maxfds) maxfds = wfds->maxfd;
2236     if (efds && efds->maxfd > maxfds) maxfds = efds->maxfd;
2237 
2238     SCM_SYSCALL(numfds,
2239                 select(maxfds+1,
2240                        (rfds? &rfds->fdset : NULL),
2241                        (wfds? &wfds->fdset : NULL),
2242                        (efds? &efds->fdset : NULL),
2243                        select_timeval(timeout, &tm)));
2244     if (numfds < 0) Scm_SysError("select failed");
2245     return Scm_Values4(Scm_MakeInteger(numfds),
2246                        (rfds? SCM_OBJ(rfds) : SCM_FALSE),
2247                        (wfds? SCM_OBJ(wfds) : SCM_FALSE),
2248                        (efds? SCM_OBJ(efds) : SCM_FALSE));
2249 }
2250 
Scm_SysSelect(ScmObj rfds,ScmObj wfds,ScmObj efds,ScmObj timeout)2251 ScmObj Scm_SysSelect(ScmObj rfds, ScmObj wfds, ScmObj efds, ScmObj timeout)
2252 {
2253     ScmSysFdset *r = select_checkfd(rfds);
2254     ScmSysFdset *w = select_checkfd(wfds);
2255     ScmSysFdset *e = select_checkfd(efds);
2256     return select_int((r? fdset_copy(r) : NULL),
2257                       (w? fdset_copy(w) : NULL),
2258                       (e? fdset_copy(e) : NULL),
2259                       timeout);
2260 }
2261 
Scm_SysSelectX(ScmObj rfds,ScmObj wfds,ScmObj efds,ScmObj timeout)2262 ScmObj Scm_SysSelectX(ScmObj rfds, ScmObj wfds, ScmObj efds, ScmObj timeout)
2263 {
2264     ScmSysFdset *r = select_checkfd(rfds);
2265     ScmSysFdset *w = select_checkfd(wfds);
2266     ScmSysFdset *e = select_checkfd(efds);
2267     return select_int(r, w, e, timeout);
2268 }
2269 
2270 #endif /* HAVE_SELECT */
2271 
2272 /*===============================================================
2273  * Environment
2274  */
2275 
2276 /* We provide a compatibility layer for getenv/setenv stuff, whose semantics
2277    slightly differ among platforms.
2278 
2279    POSIX putenv() has a flaw that passed string can't be freed reliably;
2280    the system may retain the pointer, so the caller can't free it afterwards,
2281    while putenv() itself can't know if the passed pointer is malloc-ed or
2282    static.  Some Unixes appears to change the semantics, guaranteeing
2283    the system copies the passed string so that the caller can free it;
2284    however, it's not easy to check which semantics the given platform uses.
2285 
2286    What POSIX suggests is setenv() when you want to pass malloc-ed
2287    strings.  Unfortunately it is a newer addition and not all platforms
2288    supports it.  Windows doesn't, either, but it offers _[w]putenv_s
2289    as an alternative.  Unfortunately again, current MinGW doesn't include
2290    _[w]putenv_s in its headers and import libraries.
2291 
2292    So, for those platforms, we use putenv/_wputenv.  We track allocated
2293    memory in env_string table, keyed by names of envvars, and free them
2294    whenever we put a new definition of envvars we've inserted before.
2295 
2296    Another merit of this compatibility layer is to guarantee MT-safeness;
2297    Putenv/setenv aren't usually MT-safe, neither is getenv when environment
2298    is being modified.
2299 */
2300 
2301 static ScmInternalMutex env_mutex;
2302 static ScmHashCore env_strings; /* name -> malloc-ed mem.
2303                                    used with putenv()/_wputenv() to prevent
2304                                    leak. */
2305 
Scm_GetEnv(const char * name)2306 const char *Scm_GetEnv(const char *name)
2307 {
2308 #if defined(GAUCHE_WINDOWS) && defined(UNICODE)
2309     const wchar_t *wname = Scm_MBS2WCS(name);
2310     const char *value = NULL;
2311     (void)SCM_INTERNAL_MUTEX_LOCK(env_mutex);
2312     const wchar_t *wvalue = _wgetenv(wname);
2313     if (wvalue != NULL) {
2314         value = Scm_WCS2MBS(wvalue);
2315     }
2316     (void)SCM_INTERNAL_MUTEX_UNLOCK(env_mutex);
2317     return value;
2318 #else  /*!(defined(GAUCHE_WINDOWS) && defined(UNICODE))*/
2319     (void)SCM_INTERNAL_MUTEX_LOCK(env_mutex);
2320     const char *value = SCM_STRDUP(getenv(name));
2321     (void)SCM_INTERNAL_MUTEX_UNLOCK(env_mutex);
2322     return value;
2323 #endif /*!(defined(GAUCHE_WINDOWS) && defined(UNICODE))*/
2324 }
2325 
Scm_SetEnv(const char * name,const char * value,int overwrite)2326 void Scm_SetEnv(const char *name, const char *value, int overwrite)
2327 {
2328 #if defined(GAUCHE_WINDOWS) && defined(UNICODE)
2329     /* We need to use _wputenv for wide-character support.  Since we pass
2330        the converted strings to OS, we have to allocate them by malloc.
2331        To prevent leak, we register the allocated memory to the global
2332        hash table, and free it when Scm_SetEnv is called with the same NAME
2333        again. */
2334     wchar_t *wname = Scm_MBS2WCS(name);
2335     wchar_t *wvalue = Scm_MBS2WCS(value);
2336     int nlen = wcslen(wname);
2337     int vlen = wcslen(wvalue);
2338     wchar_t *wnameval = (wchar_t*)malloc((nlen+vlen+2)*sizeof(wchar_t));
2339     if (wnameval == NULL) {
2340         Scm_Error("sys-setenv: out of memory");
2341     }
2342     wcscpy(wnameval, wname);
2343     wcscpy(wnameval+nlen, L"=");
2344     wcscpy(wnameval+nlen+1, wvalue);
2345 
2346     ScmObj sname = Scm_MakeString(name, -1, -1, SCM_STRING_COPYING);
2347 
2348     int result = 0;
2349     wchar_t *prev_mem = NULL;
2350 
2351     (void)SCM_INTERNAL_MUTEX_LOCK(env_mutex);
2352     if (overwrite || _wgetenv(wname) == NULL) {
2353         result = _wputenv(wnameval);
2354         if (result >= 0) {
2355             ScmDictEntry *e = Scm_HashCoreSearch(&env_strings,
2356                                                  (intptr_t)sname,
2357                                                  SCM_DICT_CREATE);
2358             /* SCM_DICT_VALUE is only for ScmObj, so we directly access value
2359                field here. */
2360             prev_mem = (wchar_t*)e->value;
2361             e->value = (intptr_t)wnameval;
2362         }
2363     }
2364     (void)SCM_INTERNAL_MUTEX_UNLOCK(env_mutex);
2365 
2366     if (result < 0) {
2367         free(wnameval);
2368         Scm_SysError("setenv failed on '%s=%s'", name, value);
2369     }
2370     if (prev_mem != NULL) {
2371         free(prev_mem);
2372     }
2373 #elif defined(HAVE_SETENV)
2374     (void)SCM_INTERNAL_MUTEX_LOCK(env_mutex);
2375     int r = setenv(name, value, overwrite);
2376     (void)SCM_INTERNAL_MUTEX_UNLOCK(env_mutex);
2377     if (r < 0) Scm_SysError("setenv failed on '%s=%s'", name, value);
2378 #elif defined(HAVE_PUTENV)
2379     int nlen = (int)strlen(name);
2380     int vlen = (int)strlen(value);
2381     char *nameval = (char*)malloc(nlen+vlen+2);
2382     if (nameval == NULL) {
2383         Scm_Error("sys-setenv: out of memory");
2384     }
2385     strcpy(nameval, name);
2386     strcpy(nameval+nlen, "=");
2387     strcpy(nameval+nlen+1, value);
2388 
2389     ScmObj sname = Scm_MakeString(name, -1, -1, SCM_STRING_COPYING);
2390 
2391     int result = 0;
2392     char *prev_mem = NULL;
2393 
2394     (void)SCM_INTERNAL_MUTEX_LOCK(env_mutex);
2395     if (overwrite || getenv(name) == NULL) {
2396         result = putenv(nameval);
2397         if (result >= 0) {
2398             ScmDictEntry *e = Scm_HashCoreSearch(&env_strings,
2399                                                  (intptr_t)sname,
2400                                                  SCM_DICT_CREATE);
2401             /* SCM_DICT_VALUE is only for ScmObj, so we directly access value
2402                field here. */
2403             prev_mem = (char*)e->value;
2404             e->value = (intptr_t)nameval;
2405         }
2406     }
2407     (void)SCM_INTERNAL_MUTEX_UNLOCK(env_mutex);
2408     if (result < 0) {
2409         free (nameval);
2410         Scm_SysError("putenv failed on '%s=%s'", name, value);
2411     }
2412     if (prev_mem != NULL) {
2413         free(prev_mem);
2414     }
2415 #else /* !HAVE_SETENV && !HAVE_PUTENV */
2416     /* We can't do much.  we may replace environ by ourselves, but
2417        it is unlikely that the system have extern environ and not putenv.
2418     */
2419     Scm_Error("neither setenv nor putenv is supported on this platform.");
2420 #endif
2421 }
2422 
2423 /* Returns the system's environment table as a list of strings.
2424    Each string is in the format of "key=value". */
Scm_Environ(void)2425 ScmObj Scm_Environ(void)
2426 {
2427 #if defined(GAUCHE_WINDOWS)
2428 #define ENV_BUFSIZ 64
2429     LPVOID ss = GetEnvironmentStrings();
2430     ScmObj h = SCM_NIL, t = SCM_NIL;
2431     TCHAR *cp = (TCHAR*)ss, *pp;
2432     TCHAR sbuf[ENV_BUFSIZ], *buf=sbuf;
2433     int bsize = ENV_BUFSIZ, size;
2434 
2435     do {
2436         for (pp=cp; *pp; pp++) /*proceed ptr*/;
2437         size = (int)(pp - cp) + 1;
2438         if (size >= bsize) {
2439             buf = SCM_NEW_ATOMIC_ARRAY(TCHAR, size);
2440             bsize = size;
2441         }
2442         memcpy(buf, cp, size*sizeof(TCHAR));
2443         SCM_APPEND1(h, t, SCM_MAKE_STR_COPYING(SCM_WCS2MBS(buf)));
2444         cp = pp+1;
2445     } while (pp[1] != 0);
2446     FreeEnvironmentStrings(ss);
2447     return h;
2448 #else
2449     (void)SCM_INTERNAL_MUTEX_LOCK(env_mutex);
2450 #  if defined(HAVE_CRT_EXTERNS_H)
2451     char **environ = *_NSGetEnviron();  /* OSX Hack*/
2452 #  endif
2453     ScmObj r = (environ == NULL
2454                 ? SCM_NIL
2455                 : Scm_CStringArrayToList((const char**)environ, -1,
2456                                          SCM_STRING_COPYING));
2457     (void)SCM_INTERNAL_MUTEX_UNLOCK(env_mutex);
2458     return r;
2459 #endif /*!GAUCHE_WINDOWS*/
2460 }
2461 
Scm_UnsetEnv(const char * name)2462 void Scm_UnsetEnv(const char *name)
2463 {
2464 #if defined(HAVE_UNSETENV)
2465     /* NB: If we HAVE_SETENV, we don't have any entries in env_strings,
2466        so the lookup of snv_strings is a waste; but the result is always
2467        NULL and it won't harm the operation, and we expect sys-unsetenv
2468        is rarely used, so we just let it waste cpu cycles. */
2469     char *prev_mem = NULL;
2470     ScmObj sname = Scm_MakeString(name, -1, -1, SCM_STRING_COPYING);
2471     (void)SCM_INTERNAL_MUTEX_LOCK(env_mutex);
2472     int r = unsetenv(name);
2473     ScmDictEntry *e = Scm_HashCoreSearch(&env_strings,
2474                                          (intptr_t)sname,
2475                                          SCM_DICT_DELETE);
2476     if (e != NULL) { prev_mem = (char*)e->value; e->value = (intptr_t)NULL; }
2477     (void)SCM_INTERNAL_MUTEX_UNLOCK(env_mutex);
2478     if (r < 0) Scm_SysError("unsetenv failed on %s", name);
2479     if (prev_mem != NULL) free(prev_mem);
2480 #else  /*!HAVE_UNSETENV*/
2481     (void)name; /* suppress unused var warning */
2482     Scm_Error("sys-unsetenv is not supported on this platform.");
2483 #endif /*!HAVE_UNSETENV*/
2484 }
2485 
Scm_ClearEnv()2486 void Scm_ClearEnv()
2487 {
2488 #if defined(HAVE_CLEARENV)
2489     /* As in Scm_UnsetEnv, we don't need env_strings business if
2490        we HAVE_SETENV, but it does no harm either. */
2491     (void)SCM_INTERNAL_MUTEX_LOCK(env_mutex);
2492     int r = clearenv();
2493     ScmHashIter iter;
2494     Scm_HashIterInit(&iter, &env_strings);
2495     ScmDictEntry *e;
2496     while ((e = Scm_HashIterNext(&iter)) != NULL) {
2497         free((void*)e->value);
2498         e->value = (intptr_t)NULL;
2499     }
2500     Scm_HashCoreClear(&env_strings);
2501     (void)SCM_INTERNAL_MUTEX_UNLOCK(env_mutex);
2502     if (r < 0) Scm_SysError("clearenv failed");
2503 #else  /*!HAVE_UNSETENV*/
2504     Scm_Error("sys-clearenv is not supported on this platform.");
2505 #endif /*!HAVE_UNSETENV*/
2506 }
2507 
2508 /*===============================================================
2509  * Closer-to-metal thingy
2510  */
2511 
2512 /* Try to find # of available processors.  If we don't know how to
2513    find that info on the platform, we fall back to 1.
2514    If GAUCHE_AVAILABLE_PROCESSORS environment variable is defined and
2515    has the value interpreted as a positive integer, we use that value
2516    instead.
2517 */
Scm_AvailableProcessors()2518 int Scm_AvailableProcessors()
2519 {
2520     const char *env = Scm_GetEnv("GAUCHE_AVAILABLE_PROCESSORS");
2521     if (env && env[0] != '\0') {
2522         char *ep;
2523         long v = strtol(env, &ep, 10);
2524         if (v > 0 && *ep == '\0') return (int)v;
2525     }
2526 #if !defined(GAUCHE_WINDOWS)
2527 #if   defined(_SC_NPROCESSORS_ONLN)
2528     return (int)sysconf(_SC_NPROCESSORS_ONLN);
2529 #else  /*!defined(_SC_NPROCESSORS_ONLN)*/
2530     return 1;                   /* fallback */
2531 #endif /*!defined(_SC_NPROCESSORS_ONLN)*/
2532 #else  /*defined(GAUCHE_WINDOWS)*/
2533     SYSTEM_INFO sysinfo;
2534     GetSystemInfo( &sysinfo );
2535     return (int)sysinfo.dwNumberOfProcessors;
2536 #endif /*defined(GAUCHE_WINDOWS)*/
2537 }
2538 
2539 /*===============================================================
2540  * Emulation layer for Windows
2541  */
2542 #if defined(GAUCHE_WINDOWS)
2543 
2544 /* Dynamically obtain an entry point that may not be available on
2545    all Windows versions.  If throw_error is TRUE, throws an error
2546    if DLL mapping failed, or entry cannot be found.  Otherwise,
2547    returns NULL on error. */
get_api_entry(const TCHAR * module,const char * proc,int throw_error)2548 static void *get_api_entry(const TCHAR *module, const char *proc,
2549                            int throw_error)
2550 {
2551     void *entry;
2552     HMODULE m = LoadLibrary(module);
2553     if (m == NULL) {
2554         if (throw_error)
2555             Scm_SysError("LoadLibrary(%s) failed", SCM_WCS2MBS(module));
2556         else
2557             return NULL;
2558     }
2559     entry = (void*)GetProcAddress(m, proc);
2560     if (entry == NULL) {
2561         DWORD errcode = GetLastError();
2562         FreeLibrary(m);
2563         SetLastError(errcode);
2564         if (throw_error)
2565             Scm_SysError("GetProcAddress(%s) failed", proc);
2566         else
2567             return NULL;
2568     }
2569     return entry;
2570 }
2571 
2572 /* Scan the processes to find out either the parent process, or the
2573    child processes of the current process.  I cannot imagine why we
2574    need such a hassle to perform this kind of simple task, but this
2575    is the way the MS document suggests.
2576    Returns a single Scheme integer of the parent process id if childrenp
2577    is FALSE; returns a list of Scheme integers of child process ids if
2578    childrenp is TRUE. */
get_relative_processes(int childrenp)2579 static ScmObj get_relative_processes(int childrenp)
2580 {
2581     HANDLE snapshot;
2582     PROCESSENTRY32 entry;
2583     DWORD myid = GetCurrentProcessId(), parentid = 0;
2584     int found = FALSE;
2585     ScmObj h = SCM_NIL, t = SCM_NIL; /* children pids */
2586 
2587     snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
2588     if (snapshot == INVALID_HANDLE_VALUE) {
2589         Scm_Error("couldn't take process snapshot in getppid()");
2590     }
2591     entry.dwSize = sizeof(PROCESSENTRY32);
2592     if (!Process32First(snapshot, &entry)) {
2593         CloseHandle(snapshot);
2594         Scm_Error("Process32First failed in getppid()");
2595     }
2596     do {
2597         if (childrenp) {
2598             if (entry.th32ParentProcessID == myid) {
2599                 SCM_APPEND1(h, t, Scm_MakeInteger(entry.th32ProcessID));
2600             }
2601         } else {
2602             if (entry.th32ProcessID == myid) {
2603                 parentid = entry.th32ParentProcessID;
2604                 found = TRUE;
2605                 break;
2606             }
2607         }
2608     } while (Process32Next(snapshot, &entry));
2609     CloseHandle(snapshot);
2610 
2611     if (childrenp) {
2612         return h;
2613     } else {
2614         if (!found) {
2615             Scm_Error("couldn't find the current process entry in getppid()");
2616         }
2617         return Scm_MakeInteger(parentid);
2618     }
2619 }
2620 
2621 /* Retrieve PID from windows process handle wrapper.  */
2622 
Scm_WinProcessPID(ScmObj handle)2623 pid_t Scm_WinProcessPID(ScmObj handle)
2624 {
2625     /* GetProcessId seems very primitive procedure, but somehow Windows
2626        only provides it in XP SP1 or after.  Before that it seems you
2627        can only map pid -> handle by OpenProcess but you can't do the
2628        reverse (except you enumerate all process ids, calling OpenProcess
2629        on each and look for one whose handle matches the given handle.
2630        Sounds expensive. */
2631     static DWORD (WINAPI *pGetProcessId)(HANDLE) = NULL;
2632     static int queried = FALSE;
2633 
2634     if (!Scm_WinProcessP(handle)) {
2635         SCM_TYPE_ERROR(handle, "<win:handle process>");
2636     }
2637 
2638     if (pGetProcessId == NULL) {
2639         if (queried) return (pid_t)-1;
2640         pGetProcessId = get_api_entry(_T("kernel32.dll"), "GetProcessId",
2641                                       FALSE);
2642         if (pGetProcessId == NULL) {
2643             queried = TRUE;
2644             return (pid_t)-1;
2645         }
2646     }
2647     return pGetProcessId(Scm_WinProcess(handle));
2648 }
2649 
2650 /*
2651  * Users and groups
2652  * Kinda Kluge, since we don't have "user id" associated with each
2653  * user.  (If a domain server is active, Windows security manager seems
2654  * to assign an unique user id for every user; but it doesn't seem available
2655  * for stand-alone machine.)
2656  */
2657 
convert_user(const USER_INFO_2 * wuser,struct passwd * res)2658 static void convert_user(const USER_INFO_2 *wuser, struct passwd *res)
2659 {
2660     res->pw_name    = (const char*)SCM_WCS2MBS(wuser->usri2_name);
2661     res->pw_passwd  = "*";
2662     res->pw_uid     = 0;
2663     res->pw_gid     = 0;
2664     res->pw_comment = (const char*)SCM_WCS2MBS(wuser->usri2_comment);
2665     res->pw_gecos   = (const char*)SCM_WCS2MBS(wuser->usri2_full_name);
2666     res->pw_dir     = (const char*)SCM_WCS2MBS(wuser->usri2_home_dir);
2667     res->pw_shell   = "";
2668 }
2669 
2670 /* Arrgh! thread unsafe!  just for the time being...*/
2671 static struct passwd pwbuf = { "dummy", "", 0, 0, "", "", "", "" };
2672 
getpwnam(const char * name)2673 struct passwd *getpwnam(const char *name)
2674 {
2675     USER_INFO_2 *res;
2676     if (NetUserGetInfo(NULL, (LPCWSTR)SCM_MBS2WCS(name), 2, (LPBYTE*)&res)
2677         != NERR_Success) {
2678         return NULL;
2679     }
2680     convert_user(res, &pwbuf);
2681     NetApiBufferFree(res);
2682     return &pwbuf;
2683 }
2684 
getpwuid(uid_t uid SCM_UNUSED)2685 struct passwd *getpwuid(uid_t uid SCM_UNUSED)
2686 {
2687     /* for the time being, we just ignore uid and returns the current
2688        user info. */
2689 #define NAMELENGTH 256
2690     TCHAR buf[NAMELENGTH];
2691     DWORD len = NAMELENGTH;
2692     if (GetUserName(buf, &len) == 0) {
2693         return NULL;
2694     }
2695     return getpwnam(SCM_WCS2MBS(buf));
2696 }
2697 
2698 static struct group dummy_group = {
2699     "dummy",
2700     "",
2701     100,
2702     NULL
2703 };
2704 
getgrgid(gid_t gid SCM_UNUSED)2705 struct group *getgrgid(gid_t gid SCM_UNUSED)
2706 {
2707     return &dummy_group;
2708 }
2709 
getgrnam(const char * name SCM_UNUSED)2710 struct group *getgrnam(const char *name SCM_UNUSED)
2711 {
2712     return &dummy_group;
2713 }
2714 
2715 /* Kluge kluge kluge */
getuid(void)2716 uid_t getuid(void)
2717 {
2718     return 0;
2719 }
2720 
geteuid(void)2721 uid_t geteuid(void)
2722 {
2723     return 0;
2724 }
2725 
getgid(void)2726 gid_t getgid(void)
2727 {
2728     return 0;
2729 }
2730 
getegid(void)2731 gid_t getegid(void)
2732 {
2733     return 0;
2734 }
2735 
getppid(void)2736 pid_t getppid(void)
2737 {
2738     ScmObj ppid = get_relative_processes(FALSE);
2739     return Scm_GetInteger(ppid);
2740 }
2741 
getlogin(void)2742 const char *getlogin(void)
2743 {
2744     static TCHAR buf[256]; /* this isn't thread-safe, but getlogin() is
2745                               inherently thread-unsafe call anyway */
2746     DWORD size = sizeof(buf)/sizeof(TCHAR);
2747     BOOL r;
2748     r = GetUserName(buf, &size);
2749     if (r) {
2750         return SCM_WCS2MBS(buf);
2751     } else {
2752         return NULL;
2753     }
2754 }
2755 
times(struct tms * info)2756 clock_t times(struct tms *info)
2757 {
2758     HANDLE process = GetCurrentProcess();
2759     FILETIME ctime, xtime, utime, stime;
2760     int64_t val;
2761     const int factor = 10000000/CLK_TCK;
2762     const int bias   = factor/2;
2763 
2764     if (!GetProcessTimes(process, &ctime, &xtime, &stime, &utime)) {
2765         Scm_SysError("GetProcessTimes failed");
2766     }
2767     val = ((int64_t)stime.dwHighDateTime << 32) + stime.dwLowDateTime;
2768     info->tms_stime = (u_int)((val+bias) / factor);
2769     val = ((int64_t)utime.dwHighDateTime << 32) + utime.dwLowDateTime;
2770     info->tms_utime = (u_int)((val+bias) / factor);
2771 
2772     info->tms_cstime = 0;
2773     info->tms_cutime = 0;
2774     return 0;
2775 }
2776 
2777 
2778 
2779 /*
2780  * Other obscure stuff
2781  */
2782 
fork(void)2783 int fork(void)
2784 {
2785     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2786     return -1;
2787 }
2788 
pipe(int fd[])2789 int pipe(int fd[])
2790 {
2791 #define PIPE_BUFFER_SIZE 512
2792     /* NB: We create pipe with NOINHERIT to avoid complication when spawning
2793        child process.  Scm_SysExec will dups the handle with inheritable flag
2794        for the children.  */
2795     int r = _pipe(fd, PIPE_BUFFER_SIZE, O_BINARY|O_NOINHERIT);
2796     return r;
2797 }
2798 
2799 /* If the given handle points to a pipe, returns its name.
2800    As of Oct 2016, mingw headers does not include
2801    GetFileInformationByHandleEx API, so we provide alternative. */
2802 
2803 typedef struct {
2804     DWORD FileNameLength;
2805     WCHAR FileName[1];
2806 } X_FILE_NAME_INFO;
2807 
2808 typedef enum {
2809     X_FileNameInfo = 2
2810 } X_FILE_INFO_BY_HANDLE_CLASS;
2811 
Scm_WinGetPipeName(HANDLE h)2812 ScmObj Scm_WinGetPipeName(HANDLE h)
2813 {
2814     if (GetFileType(h) != FILE_TYPE_PIPE) return SCM_FALSE;
2815     static BOOL (WINAPI *pGetFileInformationByHandleEx)(HANDLE,
2816 							X_FILE_INFO_BY_HANDLE_CLASS,
2817 							LPVOID, DWORD) = NULL;
2818 
2819     if (pGetFileInformationByHandleEx == NULL) {
2820 	pGetFileInformationByHandleEx =
2821 	    get_api_entry(_T("kernel32.dll"),
2822 			  "GetFileInformationByHandleEx",
2823 			  FALSE);
2824     }
2825     if (pGetFileInformationByHandleEx == NULL) return SCM_FALSE;
2826 
2827     DWORD size = sizeof(X_FILE_NAME_INFO) + sizeof(WCHAR)*MAX_PATH;
2828     X_FILE_NAME_INFO *info = SCM_MALLOC_ATOMIC(size);
2829     BOOL r = pGetFileInformationByHandleEx(h, X_FileNameInfo, info, size);
2830     if (!r) return SCM_FALSE;
2831 
2832     info->FileName[info->FileNameLength / sizeof(WCHAR)] = 0;
2833     return SCM_MAKE_STR_COPYING(SCM_WCS2MBS(info->FileName));
2834 }
2835 
ttyname(int desc SCM_UNUSED)2836 char *ttyname(int desc SCM_UNUSED)
2837 {
2838     return NULL;
2839 }
2840 
2841 #if !HAVE_UTIMENSAT
2842 /* Emulate utimensat() by utime().  For MinGW. */
utimensat(int dirfd SCM_UNUSED,const char * path,const ScmTimeSpec times[2],int flags SCM_UNUSED)2843 int utimensat(int dirfd SCM_UNUSED,
2844               const char *path,
2845               const ScmTimeSpec times[2],
2846               int flags SCM_UNUSED)
2847 {
2848     struct utimbuf buf;
2849     buf.actime = times[0].tv_sec;
2850     buf.modtime = times[1].tv_sec;
2851 
2852     if (times[0].tv_nsec == UTIME_NOW) {
2853         buf.actime = time(NULL);
2854     }
2855     if (times[1].tv_nsec == UTIME_NOW) {
2856         buf.modtime = time(NULL);
2857     }
2858     /* TODO: UTIME_OMIT case */
2859 
2860     return utime(path, &buf);
2861 }
2862 #endif /*!HAVE_UTIMENSAT*/
2863 
2864 #ifndef __MINGW64_VERSION_MAJOR /* MinGW64 has truncate and ftruncate */
2865 
win_truncate(HANDLE file,off_t len)2866 static int win_truncate(HANDLE file, off_t len)
2867 {
2868     typedef BOOL (WINAPI *pSetEndOfFile_t)(HANDLE);
2869     typedef BOOL (WINAPI *pSetFilePointer_t)(HANDLE, LONG, PLONG, DWORD);
2870 
2871     static pSetEndOfFile_t pSetEndOfFile = NULL;
2872     static pSetFilePointer_t pSetFilePointer = NULL;
2873 
2874     DWORD r1;
2875     BOOL  r2;
2876 
2877     if (pSetEndOfFile == NULL) {
2878         pSetEndOfFile = (pSetEndOfFile_t)get_api_entry(_T("kernel32.dll"),
2879                                                        "SetEndOfFile",
2880                                                        FALSE);
2881         if (pSetEndOfFile == NULL) return -1;
2882     }
2883     if (pSetFilePointer == NULL) {
2884         pSetFilePointer = (pSetFilePointer_t)get_api_entry(_T("kernel32.dll"),
2885                                                            "SetFilePointer",
2886                                                            FALSE);
2887         if (pSetFilePointer == NULL) return -1;
2888     }
2889 
2890     /* TODO: 64bit size support! */
2891     r1 = pSetFilePointer(file, (LONG)len, NULL, FILE_BEGIN);
2892     if (r1 == INVALID_SET_FILE_POINTER) return -1;
2893     r2 = pSetEndOfFile(file);
2894     if (r2 == 0) return -1;
2895     return 0;
2896 }
2897 
truncate(const char * path,off_t len)2898 int truncate(const char *path, off_t len)
2899 {
2900     HANDLE file;
2901     int r;
2902 
2903     file = CreateFile(SCM_MBS2WCS(path), GENERIC_WRITE,
2904                       FILE_SHARE_READ|FILE_SHARE_WRITE,
2905                       NULL, OPEN_EXISTING, 0, NULL);
2906     if (file == INVALID_HANDLE_VALUE) return -1;
2907     r = win_truncate(file, len);
2908     if (r < 0) {
2909         DWORD errcode = GetLastError();
2910         CloseHandle(file);
2911         SetLastError(errcode);
2912         return -1;
2913     }
2914     CloseHandle(file);
2915     return 0;
2916 }
2917 
ftruncate(int fd,off_t len)2918 int ftruncate(int fd, off_t len)
2919 {
2920     HANDLE h = (HANDLE)_get_osfhandle(fd);
2921     int r;
2922     if (h == INVALID_HANDLE_VALUE) return -1;
2923     r = win_truncate(h, len);
2924     if (r < 0) return -1;
2925     return 0;
2926 }
2927 
2928 #endif /* __MINGW64_VERSION_MAJOR */
2929 
alarm(unsigned int seconds SCM_UNUSED)2930 unsigned int alarm(unsigned int seconds SCM_UNUSED)
2931 {
2932     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2933     Scm_SysError("alarm");
2934     return 0;
2935 }
2936 
2937 /* file links */
link(const char * existing,const char * newpath)2938 int link(const char *existing, const char *newpath)
2939 {
2940     /* CreateHardLink only exists in WinNT or later.  Officially we don't
2941        support anything before, but let's try to be kind for the legacy
2942        system ...*/
2943     typedef BOOL (WINAPI *pCreateHardLink_t)(LPTSTR, LPTSTR,
2944                                              LPSECURITY_ATTRIBUTES);
2945     static pCreateHardLink_t pCreateHardLink = NULL;
2946     BOOL r;
2947 #if defined(UNICODE)
2948 #define CREATEHARDLINK  "CreateHardLinkW"
2949 #else
2950 #define CREATEHARDLINK  "CreateHardLinkA"
2951 #endif
2952 
2953     if (pCreateHardLink == NULL) {
2954         pCreateHardLink = (pCreateHardLink_t)get_api_entry(_T("kernel32.dll"),
2955                                                            CREATEHARDLINK,
2956                                                            TRUE);
2957     }
2958     r = pCreateHardLink((LPTSTR)SCM_MBS2WCS(newpath),
2959                         (LPTSTR)SCM_MBS2WCS(existing), NULL);
2960     return r? 0 : -1;
2961 }
2962 
2963 /* Winsock requires some obscure initialization.
2964    We perform initialization here, since winsock module is used
2965    in both gauche.net and gauche.auxsys. */
2966 static WSADATA wsaData;
2967 
init_winsock(void)2968 static void init_winsock(void)
2969 {
2970     int opt;
2971     int r = WSAStartup(MAKEWORD(2,2), &wsaData);
2972     if (r != 0) {
2973         SetLastError(r);
2974         Scm_SysError("WSAStartup failed");
2975     }
2976     /* windows voodoo to make _open_osfhandle magic work */
2977     opt = SO_SYNCHRONOUS_NONALERT;
2978     r = setsockopt(INVALID_SOCKET, SOL_SOCKET,
2979                    SO_OPENTYPE, (char*)&opt, sizeof(opt));
2980     if (r == SOCKET_ERROR) {
2981         Scm_SysError("winsock initialization failed");
2982     }
2983 }
2984 
fini_winsock(void * data SCM_UNUSED)2985 static void fini_winsock(void *data SCM_UNUSED)
2986 {
2987     (void)WSACleanup();
2988 }
2989 
2990 /* Win32 thread support.  See also gauche/wthread.h */
2991 
2992 #if defined(GAUCHE_USE_WTHREADS)
2993 
Scm__WinCreateMutex()2994 HANDLE Scm__WinCreateMutex()
2995 {
2996     HANDLE m = CreateMutex(NULL, FALSE, NULL);
2997     if (m == NULL) Scm_SysError("couldn't create a mutex");
2998     return m;
2999 }
3000 
Scm__WinMutexLock(HANDLE mutex)3001 int Scm__WinMutexLock(HANDLE mutex)
3002 {
3003     DWORD r = WaitForSingleObject(mutex, INFINITE);
3004     if (r == WAIT_OBJECT_0) return 0;
3005     else return 1;              /* TODO: proper error handling */
3006 }
3007 
3008 /* Win32 conditional variable emulation.
3009    Native condition variable support is only available on Windows Vista
3010    and later.  We don't want to drop XP support (yet), so we avoid using
3011    it.  Instead we emulate posix condition variable semantics.
3012    We enhanced the implementation described as the SignalObjectAndWait
3013    solution shown in
3014    <http://www1.cse.wustl.edu/~schmidt/win32-cv-1.html>
3015  */
Scm__InternalCondInit(ScmInternalCond * cond)3016 void Scm__InternalCondInit(ScmInternalCond *cond)
3017 {
3018     cond->numWaiters = 0;
3019     cond->broadcast = FALSE;
3020     cond->mutex = NULL;         /* set by the first CondWait */
3021     cond->sem = CreateSemaphore(NULL,          /* no security */
3022                                 0, 0x7fffffff, /* initial and max val */
3023                                 NULL);         /* name */
3024     if (cond->sem == NULL) {
3025         Scm_SysError("couldn't create a semaphore for a condition variable");
3026     }
3027     cond->done = CreateEvent(NULL,  /* no security */
3028                              FALSE, /* auto-reset */
3029                              FALSE, /* initially non-signalled */
3030                              NULL); /* name */
3031     if (cond->done == NULL) {
3032         DWORD err = GetLastError();
3033         CloseHandle(cond->sem);
3034         SetLastError(err);
3035         Scm_SysError("couldn't create event for a condition variable");
3036     }
3037     InitializeCriticalSection(&cond->numWaitersLock);
3038 }
3039 
Scm__InternalCondWait(ScmInternalCond * cond,ScmInternalMutex * mutex,ScmTimeSpec * pts)3040 int Scm__InternalCondWait(ScmInternalCond *cond, ScmInternalMutex *mutex,
3041                           ScmTimeSpec *pts)
3042 {
3043     DWORD r0, r1;
3044     DWORD timeout_msec;
3045     int badMutex = FALSE, lastWaiter;
3046 
3047     if (pts) {
3048         u_long now_sec, now_usec;
3049         u_long target_sec, target_usec;
3050         Scm_GetTimeOfDay(&now_sec, &now_usec);
3051         target_sec = pts->tv_sec;
3052         target_usec = pts->tv_nsec / 1000;
3053         if (target_sec < now_sec
3054             || (target_sec == now_sec && target_usec <= now_usec)) {
3055             timeout_msec = 0;
3056         } else if (target_usec >= now_usec) {
3057             timeout_msec = ceil((target_sec - now_sec) * 1000
3058                                 + (target_usec - now_usec)/1000.0);
3059         } else {
3060             timeout_msec = ceil((target_sec - now_sec - 1) * 1000
3061                                 + (1.0e6 + target_usec - now_usec)/1000.0);
3062         }
3063     } else {
3064         timeout_msec = INFINITE;
3065     }
3066 
3067     EnterCriticalSection(&cond->numWaitersLock);
3068     /* If we're the first one to wait on this cond var, set cond->mutex.
3069        We don't allow to use multiple mutexes together with single cond var.
3070      */
3071     if (cond->mutex != NULL && cond->mutex != mutex) {
3072         badMutex = TRUE;
3073     } else {
3074         cond->numWaiters++;
3075         if (cond->mutex == NULL) cond->mutex = mutex;
3076     }
3077     LeaveCriticalSection(&cond->numWaitersLock);
3078 
3079     if (badMutex) {
3080         Scm_Error("Attempt to wait on condition variable %p with different"
3081                   " mutex %p\n", cond, mutex);
3082     }
3083 
3084     /* Signals mutex and atomically waits on the semaphore */
3085     r0 = SignalObjectAndWait(*mutex, cond->sem, timeout_msec, FALSE);
3086 
3087     /* We're signaled, or timed out.   There can be a case that cond is
3088        broadcasted between the timeout of SignalObjectAndWait and the
3089        following EnterCriticalSection.  So we should check lastWaiter
3090        anyway. */
3091     EnterCriticalSection(&cond->numWaitersLock);
3092     cond->numWaiters--;
3093     lastWaiter = cond->broadcast && cond->numWaiters == 0;
3094     LeaveCriticalSection(&cond->numWaitersLock);
3095 
3096     if (lastWaiter) {
3097         /* tell the broadcaster that all the waiters have gained
3098            control, and wait to acquire mutex. */
3099         r1 = SignalObjectAndWait(cond->done, *mutex, INFINITE, FALSE);
3100     } else {
3101         /* Acquire mutex */
3102         r1 = WaitForSingleObject(*mutex, INFINITE);
3103     }
3104     if (r0 == WAIT_TIMEOUT) return SCM_INTERNAL_COND_TIMEDOUT;
3105     if (r0 != WAIT_OBJECT_0 || r1 != WAIT_OBJECT_0) return -1;
3106     return 0;
3107 }
3108 
Scm__InternalCondSignal(ScmInternalCond * cond)3109 int Scm__InternalCondSignal(ScmInternalCond *cond)
3110 {
3111     int haveWaiters;
3112     BOOL r = TRUE;
3113 
3114     if (!cond->mutex) return 0; /* nobody ever waited on this cond var. */
3115 
3116     SCM_INTERNAL_MUTEX_SAFE_LOCK_BEGIN(cond->mutex);
3117 
3118     EnterCriticalSection(&cond->numWaitersLock);
3119     haveWaiters = (cond->numWaiters > 0);
3120     LeaveCriticalSection(&cond->numWaitersLock);
3121 
3122     if (haveWaiters) {
3123         r = ReleaseSemaphore(cond->sem, 1, 0);
3124     }
3125 
3126     SCM_INTERNAL_MUTEX_SAFE_LOCK_END();
3127     if (!r) return -1;
3128     return 0;
3129 }
3130 
Scm__InternalCondBroadcast(ScmInternalCond * cond)3131 int Scm__InternalCondBroadcast(ScmInternalCond *cond)
3132 {
3133     int haveWaiters;
3134     DWORD err = 0;
3135     BOOL r0 = TRUE;
3136     DWORD r1 = WAIT_OBJECT_0;
3137 
3138     if (!cond->mutex) return 0; /* nobody ever waited on this cond var. */
3139 
3140     SCM_INTERNAL_MUTEX_SAFE_LOCK_BEGIN(cond->mutex);
3141 
3142     EnterCriticalSection(&cond->numWaitersLock);
3143     cond->broadcast = haveWaiters = (cond->numWaiters > 0);
3144 
3145     if (haveWaiters) {
3146         r0 = ReleaseSemaphore(cond->sem, cond->numWaiters, 0);
3147         if (!r0) err = GetLastError();
3148         LeaveCriticalSection(&cond->numWaitersLock);
3149 
3150         if (r0) {
3151             /* Each waiter aquires mutex in turn, until the last waiter
3152                who will signal on 'done'. */
3153             r1 = WaitForSingleObject(cond->done, INFINITE);
3154             cond->broadcast = FALSE; /* safe; nobody will check this */
3155         }
3156     } else {
3157         /* nobody's waiting */
3158         LeaveCriticalSection(&cond->numWaitersLock);
3159     }
3160 
3161     SCM_INTERNAL_MUTEX_SAFE_LOCK_END();
3162 
3163     if (!r0) { SetLastError(err); return -1; }
3164     if (r1 != WAIT_OBJECT_0) return -1;
3165     return 0;
3166 }
3167 
Scm__InternalCondDestroy(ScmInternalCond * cond)3168 void Scm__InternalCondDestroy(ScmInternalCond *cond)
3169 {
3170     CloseHandle(cond->sem);
3171     cond->sem = NULL;
3172     CloseHandle(cond->done);
3173     cond->done = NULL;
3174 }
3175 
Scm__WinThreadExit()3176 void Scm__WinThreadExit()
3177 {
3178     ScmVM *vm = Scm_VM();
3179     ScmWinCleanup *cup = vm->winCleanup;
3180     while (cup) {
3181         cup->cleanup(cup->data);
3182         cup = cup->prev;
3183     }
3184     GC_ExitThread(0);
3185 }
3186 
3187 #endif /* GAUCHE_USE_WTHREADS */
3188 
3189 #endif /* GAUCHE_WINDOWS */
3190 
3191 /*===============================================================
3192  * Initialization
3193  */
Scm__InitSystem(void)3194 void Scm__InitSystem(void)
3195 {
3196     ScmModule *mod = Scm_GaucheModule();
3197     Scm_InitStaticClass(&Scm_SysStatClass, "<sys-stat>", mod, stat_slots, 0);
3198     Scm_InitStaticClass(&Scm_TimeClass, "<time>", mod, time_slots, 0);
3199     Scm_InitStaticClass(&Scm_SysTmClass, "<sys-tm>", mod, tm_slots, 0);
3200     Scm_InitStaticClass(&Scm_SysGroupClass, "<sys-group>", mod, grp_slots, 0);
3201     Scm_InitStaticClass(&Scm_SysPasswdClass, "<sys-passwd>", mod, pwd_slots, 0);
3202 #ifdef HAVE_SELECT
3203     Scm_InitStaticClass(&Scm_SysFdsetClass, "<sys-fdset>", mod, NULL, 0);
3204 #endif
3205     SCM_INTERNAL_MUTEX_INIT(env_mutex);
3206     Scm_HashCoreInitSimple(&env_strings, SCM_HASH_STRING, 0, NULL);
3207 
3208     key_absolute = SCM_MAKE_KEYWORD("absolute");
3209     key_expand = SCM_MAKE_KEYWORD("expand");
3210     key_canonicalize = SCM_MAKE_KEYWORD("canonicalize");
3211 
3212     initial_ugid_differ = (geteuid() != getuid() || getegid() != getgid());
3213 
3214 #ifdef GAUCHE_WINDOWS
3215     init_winsock();
3216     SCM_INTERNAL_MUTEX_INIT(process_mgr.mutex);
3217     Scm_AddCleanupHandler(fini_winsock, NULL);
3218     Scm_AddCleanupHandler(win_process_cleanup, NULL);
3219 #endif
3220 }
3221