~ chicken-core (master) /posixwin.scm
Trap1;;;; posixwin.scm - Miscellaneous file- and process-handling routines, available on Windows
2;
3; Copyright (c) 2008-2022, The CHICKEN Team
4; Copyright (c) 2000-2007, Felix L. Winkelmann
5; All rights reserved.
6;
7; Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8; conditions are met:
9;
10; Redistributions of source code must retain the above copyright notice, this list of conditions and the following
11; disclaimer.
12; Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
13; disclaimer in the documentation and/or other materials provided with the distribution.
14; Neither the name of the author nor the names of its contributors may be used to endorse or promote
15; products derived from this software without specific prior written permission.
16;
17; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
18; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
19; AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
20; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25; POSSIBILITY OF SUCH DAMAGE.
26
27
28; Not implemented:
29;
30; open/noctty open/nonblock open/fsync open/sync
31; perm/isvtx perm/isuid perm/isgid
32; file-select
33; set-signal-mask! signal-mask signal-masked? signal-mask! signal-unmask!
34; user-information
35; change-file-owner
36; current-user-id current-group-id current-effective-user-id current-effective-group-id
37; current-effective-user-name
38; set-user-id! set-group-id!
39; create-session
40; process-group-id set-process-group-id!
41; create-symbolic-link read-symbolic-link
42; file-truncate
43; file-lock file-lock/blocking file-unlock file-test-lock
44; create-fifo
45; prot/...
46; map/...
47; set-alarm!
48; process-fork process-wait
49; parent-process-id
50; process-signal
51
52
53; Issues
54;
55; - Use of a UTF8 encoded string will not work properly. Windows uses a
56; 16-bit UNICODE character string encoding and specialized system calls
57; and/or structure settings for the use of such strings.
58
59
60(declare
61 (uses data-structures))
62
63(define-foreign-variable _stat_st_blksize scheme-object "C_SCHEME_UNDEFINED")
64(define-foreign-variable _stat_st_blocks scheme-object "C_SCHEME_UNDEFINED")
65
66(include "posix-common.scm")
67
68#>
69
70#ifndef WIN32_LEAN_AND_MEAN
71# define WIN32_LEAN_AND_MEAN
72#endif
73
74#include <direct.h>
75#include <errno.h>
76#include <fcntl.h>
77#include <io.h>
78#include <process.h>
79#include <signal.h>
80#include <stdio.h>
81#include <utime.h>
82#include <windows.h>
83#include <winsock2.h>
84
85#define PIPE_BUF 512
86
87#ifndef EWOULDBLOCK
88# define EWOULDBLOCK 0
89#endif
90
91static int C_pipefds[ 2 ];
92static time_t C_secs;
93
94/* pipe handles */
95static HANDLE C_rd0, C_wr0, C_wr0_, C_rd1, C_wr1, C_rd1_;
96static HANDLE C_save0, C_save1; /* saved I/O handles */
97static char C_rdbuf; /* one-char buffer for read */
98static int C_exstatus;
99
100/* platform information; initialized for cached testing */
101static char C_shlcmd[255 + 1] = "";
102
103/* Current user name */
104static C_char C_username[255 + 1] = "";
105
106#define open_binary_input_pipe(a, n, name) C_mpointer(a, _wpopen(C_OS_FILENAME(name, 0), L"r"))
107#define open_text_input_pipe(a, n, name) open_binary_input_pipe(a, n, name)
108#define open_binary_output_pipe(a, n, name) C_mpointer(a, _wpopen(C_OS_FILENAME(name, 0), L"w"))
109#define open_text_output_pipe(a, n, name) open_binary_output_pipe(a, n, name)
110#define close_pipe(p) C_fix(_pclose(C_port_file(p)))
111
112#define C_chmod(fn, m) C_fix(_wchmod(C_OS_FILENAME(fn, 0), C_unfix(m)))
113#define C_pipe(d, m) C_fix(_pipe(C_pipefds, PIPE_BUF, C_unfix(m)))
114#define C_close(fd) C_fix(close(C_unfix(fd)))
115
116#define C_u_i_lstat(fn) C_u_i_stat(fn)
117
118#define C_open(fn, fl, m) C_fix(_wopen(C_OS_FILENAME(fn, 0), C_unfix(fl), C_unfix(m)))
119#define C_read(fd, b, n) C_fix(read(C_unfix(fd), C_data_pointer(b), C_unfix(n)))
120#define C_write(fd, b, n) C_fix(write(C_unfix(fd), C_data_pointer(b), C_unfix(n)))
121
122#define C_flushall() C_fix(_flushall())
123
124#define C_umask(m) C_fix(_umask(C_unfix(m)))
125
126#define C_ctime(n) (C_secs = (n), ctime(&C_secs))
127
128#define TIME_STRING_MAXLENGTH 255
129static char C_time_string [TIME_STRING_MAXLENGTH + 1];
130#undef TIME_STRING_MAXLENGTH
131
132/*
133 mapping from Win32 error codes to errno
134*/
135
136typedef struct
137{
138 DWORD win32;
139 int libc;
140} errmap_t;
141
142static errmap_t errmap[] =
143{
144 {ERROR_INVALID_FUNCTION, EINVAL},
145 {ERROR_FILE_NOT_FOUND, ENOENT},
146 {ERROR_PATH_NOT_FOUND, ENOENT},
147 {ERROR_TOO_MANY_OPEN_FILES, EMFILE},
148 {ERROR_ACCESS_DENIED, EACCES},
149 {ERROR_INVALID_HANDLE, EBADF},
150 {ERROR_ARENA_TRASHED, ENOMEM},
151 {ERROR_NOT_ENOUGH_MEMORY, ENOMEM},
152 {ERROR_INVALID_BLOCK, ENOMEM},
153 {ERROR_BAD_ENVIRONMENT, E2BIG},
154 {ERROR_BAD_FORMAT, ENOEXEC},
155 {ERROR_INVALID_ACCESS, EINVAL},
156 {ERROR_INVALID_DATA, EINVAL},
157 {ERROR_INVALID_DRIVE, ENOENT},
158 {ERROR_CURRENT_DIRECTORY, EACCES},
159 {ERROR_NOT_SAME_DEVICE, EXDEV},
160 {ERROR_NO_MORE_FILES, ENOENT},
161 {ERROR_LOCK_VIOLATION, EACCES},
162 {ERROR_BAD_NETPATH, ENOENT},
163 {ERROR_NETWORK_ACCESS_DENIED, EACCES},
164 {ERROR_BAD_NET_NAME, ENOENT},
165 {ERROR_FILE_EXISTS, EEXIST},
166 {ERROR_CANNOT_MAKE, EACCES},
167 {ERROR_FAIL_I24, EACCES},
168 {ERROR_INVALID_PARAMETER, EINVAL},
169 {ERROR_NO_PROC_SLOTS, EAGAIN},
170 {ERROR_DRIVE_LOCKED, EACCES},
171 {ERROR_BROKEN_PIPE, EPIPE},
172 {ERROR_DISK_FULL, ENOSPC},
173 {ERROR_INVALID_TARGET_HANDLE, EBADF},
174 {ERROR_INVALID_HANDLE, EINVAL},
175 {ERROR_WAIT_NO_CHILDREN, ECHILD},
176 {ERROR_CHILD_NOT_COMPLETE, ECHILD},
177 {ERROR_DIRECT_ACCESS_HANDLE, EBADF},
178 {ERROR_NEGATIVE_SEEK, EINVAL},
179 {ERROR_SEEK_ON_DEVICE, EACCES},
180 {ERROR_DIR_NOT_EMPTY, ENOTEMPTY},
181 {ERROR_NOT_LOCKED, EACCES},
182 {ERROR_BAD_PATHNAME, ENOENT},
183 {ERROR_MAX_THRDS_REACHED, EAGAIN},
184 {ERROR_LOCK_FAILED, EACCES},
185 {ERROR_ALREADY_EXISTS, EEXIST},
186 {ERROR_FILENAME_EXCED_RANGE, ENOENT},
187 {ERROR_NESTING_NOT_ALLOWED, EAGAIN},
188 {ERROR_NOT_ENOUGH_QUOTA, ENOMEM},
189 {0, 0}
190};
191
192static void
193set_errno(DWORD w32err)
194{
195 errmap_t *map;
196 for (map = errmap; map->win32; ++map)
197 {
198 if (map->win32 == w32err)
199 {
200 errno = map->libc;
201 return;
202 }
203 }
204 errno = ENOSYS; /* For lack of anything better */
205}
206
207static int
208set_last_errno()
209{
210 set_errno(GetLastError());
211 return 0;
212}
213
214static int fd_to_path(C_word fd, C_WCHAR path[])
215{
216 DWORD result;
217 HANDLE fh = (HANDLE)_get_osfhandle(C_unfix(fd));
218
219 if (fh == INVALID_HANDLE_VALUE) {
220 set_last_errno();
221 return -1;
222 }
223
224 /* XXX wchar_t */
225 result = GetFinalPathNameByHandleW(fh, path, MAX_PATH, VOLUME_NAME_DOS);
226 if (result == 0) {
227 set_last_errno();
228 return -1;
229 } else if (result >= MAX_PATH) { /* Shouldn't happen */
230 errno = ENOMEM; /* For lack of anything better */
231 return -1;
232 } else {
233 return 0;
234 }
235}
236
237static C_word C_fchmod(C_word fd, C_word m)
238{
239 C_WCHAR path[MAX_PATH];
240 if (fd_to_path(fd, path) == -1) return C_fix(-1);
241 else return C_fix(_wchmod(path, C_unfix(m)));
242}
243
244static C_word C_fchdir(C_word fd)
245{
246 C_WCHAR path[MAX_PATH];
247 if (fd_to_path(fd, path) == -1) return C_fix(-1);
248 else return C_fix(_wchdir(path));
249}
250
251static int
252process_wait(C_word h, C_word t)
253{
254 if (WaitForSingleObject((HANDLE)h, (t ? 0 : INFINITE)) == WAIT_OBJECT_0)
255 {
256 DWORD ret;
257 if (GetExitCodeProcess((HANDLE)h, &ret))
258 {
259 CloseHandle((HANDLE)h);
260 C_exstatus = ret;
261 return 1;
262 }
263 }
264 return set_last_errno();
265}
266
267#define C_process_wait(p, t) (process_wait(C_unfix(p), C_truep(t)) ? C_SCHEME_TRUE : C_SCHEME_FALSE)
268
269
270static int C_isNT = 0;
271
272
273static int
274C_windows_nt()
275{
276 static int has_info = 0;
277
278 if(!has_info) {
279 OSVERSIONINFO ovf;
280 ZeroMemory(&ovf, sizeof(ovf));
281 ovf.dwOSVersionInfoSize = sizeof(ovf);
282 has_info = 1;
283
284 if(GetVersionEx(&ovf)) {
285 SYSTEM_INFO si;
286
287 switch (ovf.dwPlatformId) {
288 case VER_PLATFORM_WIN32_NT:
289 return C_isNT = 1;
290 }
291 }
292 }
293
294 return C_isNT;
295}
296
297
298static int
299get_shlcmd()
300{
301 static wchar_t buf[ 255 ];
302 /* Do we need to build the shell command pathname? */
303 if (!strlen(C_shlcmd))
304 {
305 char *cmdnam = C_windows_nt() ? "\\cmd.exe" : "\\command.com";
306 UINT len = GetSystemDirectoryW(buf, sizeof(buf));
307 if (len)
308 C_strlcpy(C_shlcmd + len, C_utf8(buf), sizeof(C_shlcmd));
309 else
310 return set_last_errno();
311 }
312
313 return 1;
314}
315
316#define C_sysinfo() (sysinfo() ? C_SCHEME_TRUE : C_SCHEME_FALSE)
317#define C_get_shlcmd() (get_shlcmd() ? C_SCHEME_TRUE : C_SCHEME_FALSE)
318
319/* GetUserName */
320
321static int
322get_user_name()
323{
324 static wchar_t buf[ 255 ];
325 if (!C_strlen(C_username))
326 {
327 DWORD bufCharCount = sizeof(buf) / sizeof(buf[0]);
328 if (!GetUserNameW(buf, &bufCharCount))
329 return set_last_errno();
330 C_strlcpy(C_username, C_utf8(buf), sizeof(C_username));
331 }
332 return 1;
333}
334
335#define C_get_user_name() (get_user_name() ? C_SCHEME_TRUE : C_SCHEME_FALSE)
336
337/*
338 Spawn a process directly.
339 Params:
340 app Command to execute.
341 cmdlin Command line (arguments).
342 env Environment for the new process (may be NULL).
343 handle, stdin, stdout, stderr
344 Spawned process info are returned in integers.
345 When spawned process shares standard io stream with the parent
346 process the respective value in handle, stdin, stdout, stderr
347 is -1.
348 params A bitmask controling operation.
349 Bit 1: Child & parent share standard input if this bit is set.
350 Bit 2: Share standard output if bit is set.
351 Bit 3: Share standard error if bit is set.
352
353 Returns: pid, zero return value indicates failure.
354*/
355static DWORD
356C_process(const char *app, const char *cmdlin, const char **env,
357 int *phandle, int *pstdin_fd, int *pstdout_fd, int *pstderr_fd,
358 int params)
359{
360 int i;
361 int success = TRUE;
362 DWORD pid;
363 const int f_share_io[3] = { params & 1, params & 2, params & 4};
364 int io_fds[3] = { -1, -1, -1 };
365 HANDLE
366 child_io_handles[3] = { NULL, NULL, NULL },
367 standard_io_handles[3] = {
368 GetStdHandle(STD_INPUT_HANDLE),
369 GetStdHandle(STD_OUTPUT_HANDLE),
370 GetStdHandle(STD_ERROR_HANDLE)};
371 const char modes[3] = "rww";
372 HANDLE cur_process = GetCurrentProcess(), child_process = NULL;
373 void* envblk = NULL;
374
375 /****** create io handles & fds ***/
376
377 for (i=0; i<3 && success; ++i)
378 {
379 if (f_share_io[i])
380 {
381 success = DuplicateHandle(
382 cur_process, standard_io_handles[i],
383 cur_process, &child_io_handles[i],
384 0, FALSE, DUPLICATE_SAME_ACCESS);
385 }
386 else
387 {
388 HANDLE a, b;
389 success = CreatePipe(&a,&b,NULL,0);
390 if(success)
391 {
392 HANDLE parent_end;
393 if (modes[i]=='r') { child_io_handles[i]=a; parent_end=b; }
394 else { parent_end=a; child_io_handles[i]=b; }
395 success = (io_fds[i] = _open_osfhandle((C_word)parent_end,0)) >= 0;
396 /* Make new handle inheritable */
397 if (success)
398 success = SetHandleInformation(child_io_handles[i], HANDLE_FLAG_INHERIT, -1);
399 }
400 }
401 }
402
403#if 0 /* Requires a sorted list by key! */
404 /****** create environment block if necessary ****/
405
406 if (env && success)
407 {
408 char** p;
409 int len = 0;
410
411 for (p = env; *p; ++p) len += strlen(*p) + 1;
412
413 if (envblk = C_malloc((len + 1) * sizeof(wchar_t));
414 {
415 wchar_t* pb = (wchar_t*)envblk;
416 for (p = env; *p; ++p)
417 {
418 wchar_t *u = C_utf16(*p, 0);
419 int n = wcslen(*u);
420 C_memcpy(pb, *u, n + 1);
421 pb += n + 1;
422 }
423 *pb = '\0';
424 /* This _should_ already have been checked for embedded NUL bytes */
425 }
426 else
427 success = FALSE;
428 }
429#endif
430
431 /****** finally spawn process ****/
432
433 if (success)
434 {
435 PROCESS_INFORMATION pi;
436 STARTUPINFO si;
437
438 ZeroMemory(&pi,sizeof pi);
439 ZeroMemory(&si,sizeof si);
440 si.cb = sizeof si;
441 si.dwFlags = STARTF_USESTDHANDLES;
442 si.hStdInput = child_io_handles[0];
443 si.hStdOutput = child_io_handles[1];
444 si.hStdError = child_io_handles[2];
445
446 /* FIXME passing 'app' param causes failure & possible stack corruption */
447 success = CreateProcessW(
448 NULL, C_utf16(cmdlin, 0), NULL, NULL, TRUE, 0, envblk, NULL, &si, &pi);
449
450 if (success)
451 {
452 child_process=pi.hProcess;
453 CloseHandle(pi.hThread);
454 pid = pi.dwProcessId;
455 }
456 else
457 set_last_errno();
458 }
459 else
460 set_last_errno();
461
462 /****** cleanup & return *********/
463
464 /* parent must close child end */
465 for (i=0; i<3; ++i) {
466 if (child_io_handles[i] != NULL)
467 CloseHandle(child_io_handles[i]);
468 }
469
470 if (success)
471 {
472 *phandle = (C_word)child_process;
473 *pstdin_fd = io_fds[0];
474 *pstdout_fd = io_fds[1];
475 *pstderr_fd = io_fds[2];
476 }
477 else
478 {
479 for (i=0; i<3; ++i) {
480 if (io_fds[i] != -1)
481 _close(io_fds[i]);
482 }
483 }
484
485 return success;
486}
487
488static int set_file_mtime(C_word filename, C_word atime, C_word mtime)
489{
490 struct _stat64i32 sb;
491 struct _utimbuf tb;
492 C_word bv = C_block_item(filename, 0);
493 C_WCHAR *fn = C_OS_FILENAME(bv, 0);
494
495 /* Only stat if needed */
496 if (atime == C_SCHEME_FALSE || mtime == C_SCHEME_FALSE) {
497 if (C_stat(fn, &sb) == -1) return -1;
498 }
499
500 if (atime == C_SCHEME_FALSE) {
501 tb.actime = sb.st_atime;
502 } else {
503 tb.actime = C_num_to_int64(atime);
504 }
505 if (mtime == C_SCHEME_FALSE) {
506 tb.modtime = sb.st_mtime;
507 } else {
508 tb.modtime = C_num_to_int64(mtime);
509 }
510 return _wutime(fn, &tb);
511}
512
513#define C_u_i_execvp(f, a) C_fix(_wexecvp(C_c_string(f), (void *)C_c_pointer_vector_or_null(a)))
514#define C_u_i_execve(f,a,e) C_fix(_wexecve(C_c_string(f), (void *)C_c_pointer_vector_or_null(a), (void *)C_c_pointer_vector_or_null(e)))
515
516/* MS replacement for the fork-exec pair */
517#define C_u_i_spawnvp(m,f,a) C_fix(_wspawnvp(C_unfix(m), C_c_string(f), (void *)C_c_pointer_vector_or_null(a)))
518#define C_u_i_spawnvpe(m,f,a,e) C_fix(_wspawnvpe(C_unfix(m), C_c_string(f), (void *)C_c_pointer_vector_or_null(a), (void *)C_c_pointer_vector_or_null(e)))
519
520<#
521
522(import (only chicken.string string-intersperse))
523
524;;; Lo-level I/O:
525
526(define-foreign-variable _o_noinherit int "O_NOINHERIT")
527(set! chicken.file.posix#open/noinherit _o_noinherit)
528
529(set! chicken.file.posix#file-open
530 (let ((defmode (bitwise-ior _s_irusr _s_iwusr _s_irgrp _s_iwgrp _s_iroth _s_iwoth)))
531 (lambda (filename flags . mode)
532 (let ([mode (if (pair? mode) (car mode) defmode)])
533 (##sys#check-string filename 'file-open)
534 (##sys#check-fixnum flags 'file-open)
535 (##sys#check-fixnum mode 'file-open)
536 (let ([fd (##core#inline "C_open" (##sys#make-c-string filename 'file-open) flags mode)])
537 (when (eq? -1 fd)
538 (##sys#signal-hook/errno
539 #:file-error (##sys#update-errno) 'file-open "cannot open file" filename flags mode))
540 fd) ) ) ) )
541
542(set! chicken.file.posix#file-close
543 (lambda (fd)
544 (##sys#check-fixnum fd 'file-close)
545 (let loop ()
546 (when (fx< (##core#inline "C_close" fd) 0)
547 (cond
548 ((fx= _errno _eintr) (##sys#dispatch-interrupt loop))
549 (else
550 (posix-error #:file-error 'file-close "cannot close file" fd)))))))
551
552(set! chicken.file.posix#file-read
553 (lambda (fd size . buffer)
554 (##sys#check-fixnum fd 'file-read)
555 (##sys#check-fixnum size 'file-read)
556 (let ([buf (if (pair? buffer) (car buffer) (make-string size))])
557 (unless (and (##core#inline "C_blockp" buf) (##core#inline "C_byteblockp" buf))
558 (##sys#signal-hook #:type-error 'file-read "bad argument type - not a string or bytevector" buf) )
559 (let ([n (##core#inline "C_read" fd buf size)])
560 (when (eq? -1 n)
561 (##sys#signal-hook/errno
562 #:file-error (##sys#update-errno) 'file-read "cannot read from file" fd size))
563 (list buf n) ) ) ) )
564
565(set! chicken.file.posix#file-write
566 (lambda (fd buffer #!optional size)
567 (##sys#check-fixnum fd 'file-write)
568 (when (string? buffer)
569 (set! buffer (##sys#slot buffer 0))
570 (unless size (set! size (fx- (##sys#size buffer) 1))))
571 (unless (##core#inline "C_byteblockp" buffer)
572 (##sys#signal-hook #:type-error 'file-write "bad argument type - not a string or bytevector" buffer) )
573 (let ((size (or size (##sys#size buffer))))
574 (##sys#check-fixnum size 'file-write)
575 (let ([n (##core#inline "C_write" fd buffer size)])
576 (when (eq? -1 n)
577 (##sys#signal-hook/errno
578 #:file-error (##sys#update-errno) 'file-write "cannot write to file" fd size))
579 n) ) ) )
580
581(set! chicken.file.posix#file-mkstemp
582 (lambda (template)
583 (##sys#check-string template 'file-mkstemp)
584 (let* ((diz "0123456789abcdefghijklmnopqrstuvwxyz")
585 (diz-len (string-length diz))
586 (max-attempts (* diz-len diz-len diz-len))
587 (tmpl (string-copy template)) ; We'll overwrite this later
588 (tmpl-len (string-length tmpl))
589 (first-x (let loop ((i (fx- tmpl-len 1)))
590 (if (and (fx>= i 0)
591 (eq? (string-ref tmpl i) #\X))
592 (loop (fx- i 1))
593 (fx+ i 1)))))
594 (cond ((not (##sys#file-exists? (or (pathname-directory template) ".") #f #t 'file-mkstemp))
595 ;; Quit early instead of looping needlessly with C_open
596 ;; failing every time. This is a race condition, but not
597 ;; a security-critical one.
598 (##sys#signal-hook #:file-error 'file-mkstemp "non-existent directory" template))
599 ((fx= first-x tmpl-len)
600 (##sys#signal-hook #:file-error 'file-mkstemp "invalid template" template)))
601 (let loop ((count 1))
602 (let suffix-loop ((index (fx- tmpl-len 1)))
603 (when (fx>= index first-x)
604 (string-set! tmpl index
605 (string-ref diz (##core#inline "C_rand" diz-len)))
606 (suffix-loop (fx- index 1))))
607 (let ((fd (##core#inline "C_open"
608 (##sys#make-c-string tmpl 'file-open)
609 (bitwise-ior chicken.file.posix#open/rdwr
610 chicken.file.posix#open/creat
611 chicken.file.posix#open/excl)
612 (fxior _s_irusr _s_iwusr))))
613 (if (eq? -1 fd)
614 (if (fx< count max-attempts)
615 (loop (fx+ count 1))
616 (posix-error #:file-error 'file-mkstemp "cannot create temporary file" template))
617 (values fd tmpl)))))))
618
619;;; Pipe primitive:
620
621(define-foreign-variable _pipefd0 int "C_pipefds[ 0 ]")
622(define-foreign-variable _pipefd1 int "C_pipefds[ 1 ]")
623
624(set! chicken.process#create-pipe
625 (lambda (#!optional (mode (fxior chicken.file.posix#open/binary
626 chicken.file.posix#open/noinherit)))
627 (when (fx< (##core#inline "C_pipe" #f mode) 0)
628 (##sys#signal-hook/errno
629 #:file-error (##sys#update-errno) 'create-pipe "cannot create pipe"))
630 (values _pipefd0 _pipefd1) ) )
631
632;;; Signal processing:
633
634(define-foreign-variable _nsig int "NSIG")
635(define-foreign-variable _sigterm int "SIGTERM")
636(define-foreign-variable _sigint int "SIGINT")
637(define-foreign-variable _sigfpe int "SIGFPE")
638(define-foreign-variable _sigill int "SIGILL")
639(define-foreign-variable _sigsegv int "SIGSEGV")
640(define-foreign-variable _sigabrt int "SIGABRT")
641(define-foreign-variable _sigbreak int "SIGBREAK")
642
643(set! chicken.process.signal#signal/term _sigterm)
644(set! chicken.process.signal#signal/int _sigint)
645(set! chicken.process.signal#signal/fpe _sigfpe)
646(set! chicken.process.signal#signal/ill _sigill)
647(set! chicken.process.signal#signal/segv _sigsegv)
648(set! chicken.process.signal#signal/abrt _sigabrt)
649(set! chicken.process.signal#signal/break _sigbreak)
650(set! chicken.process.signal#signal/alrm 0)
651(set! chicken.process.signal#signal/bus 0)
652(set! chicken.process.signal#signal/chld 0)
653(set! chicken.process.signal#signal/cont 0)
654(set! chicken.process.signal#signal/hup 0)
655(set! chicken.process.signal#signal/io 0)
656(set! chicken.process.signal#signal/kill 0)
657(set! chicken.process.signal#signal/pipe 0)
658(set! chicken.process.signal#signal/prof 0)
659(set! chicken.process.signal#signal/quit 0)
660(set! chicken.process.signal#signal/stop 0)
661(set! chicken.process.signal#signal/trap 0)
662(set! chicken.process.signal#signal/tstp 0)
663(set! chicken.process.signal#signal/urg 0)
664(set! chicken.process.signal#signal/usr1 0)
665(set! chicken.process.signal#signal/usr2 0)
666(set! chicken.process.signal#signal/vtalrm 0)
667(set! chicken.process.signal#signal/winch 0)
668(set! chicken.process.signal#signal/xcpu 0)
669(set! chicken.process.signal#signal/xfsz 0)
670
671(set! chicken.process.signal#signals-list
672 (list
673 chicken.process.signal#signal/term
674 chicken.process.signal#signal/int
675 chicken.process.signal#signal/fpe
676 chicken.process.signal#signal/ill
677 chicken.process.signal#signal/segv
678 chicken.process.signal#signal/abrt
679 chicken.process.signal#signal/break))
680
681;;; Using file-descriptors:
682
683(define duplicate-fileno
684 (lambda (old . new)
685 (##sys#check-fixnum old duplicate-fileno)
686 (let ([fd (if (null? new)
687 (##core#inline "C_dup" old)
688 (let ([n (car new)])
689 (##sys#check-fixnum n 'duplicate-fileno)
690 (##core#inline "C_dup2" old n) ) ) ] )
691 (when (fx< fd 0)
692 (##sys#signal-hook/errno
693 #:file-error (##sys#update-errno) 'duplicate-fileno "cannot duplicate file descriptor" old))
694 fd) ) )
695
696
697;;; Time related things:
698
699(set! chicken.time.posix#local-timezone-abbreviation
700 (foreign-lambda* c-string ()
701 "char *z = (_daylight ? _tzname[1] : _tzname[0]);\n"
702 "C_return(z);") )
703
704
705;;; Process handling:
706
707(define-foreign-variable _p_overlay int "P_OVERLAY")
708(define-foreign-variable _p_wait int "P_WAIT")
709(define-foreign-variable _p_nowait int "P_NOWAIT")
710(define-foreign-variable _p_nowaito int "P_NOWAITO")
711(define-foreign-variable _p_detach int "P_DETACH")
712
713(set! chicken.process#spawn/overlay _p_overlay)
714(set! chicken.process#spawn/wait _p_wait)
715(set! chicken.process#spawn/nowait _p_nowait)
716(set! chicken.process#spawn/nowaito _p_nowaito)
717(set! chicken.process#spawn/detach _p_detach)
718
719; Windows uses a commandline style for process arguments. Thus any
720; arguments with embedded whitespace will parse incorrectly. Must
721; string-quote such arguments.
722(define quote-arg-string
723 (let ((needs-quoting?
724 ;; This is essentially (string-any char-whitespace? s) but we
725 ;; don't want a SRFI-13 dependency. (Do we?)
726 (lambda (s)
727 (let ((len (string-length s)))
728 (let loop ((i 0))
729 (cond
730 ((fx= i len) #f)
731 ((char-whitespace? (string-ref s i)) #t)
732 (else (loop (fx+ i 1)))))))))
733 (lambda (str)
734 (if (needs-quoting? str) (string-append "\"" str "\"") str))))
735
736(define c-string->allocated-pointer
737 (foreign-lambda* c-pointer ((scheme-object o))
738 "char *ptr = C_malloc(C_header_size(o) * sizeof(wchar_t)); \n"
739 "if (ptr != NULL) {\n"
740 " wchar_t *u = C_utf16(C_data_pointer(o), 0); \n"
741 " C_memcpy(ptr, u, wcslen(u) + 1); \n"
742 "}\n"
743 "C_return(ptr);"))
744
745(set! chicken.process#process-execute
746 (lambda (filename #!optional (arglist '()) envlist exactf)
747 (let ((argconv (if exactf (lambda (x) x) quote-arg-string)))
748 (call-with-exec-args
749 'process-execute filename argconv arglist envlist
750 (lambda (prg argbuf envbuf)
751 (##core#inline "C_flushall")
752 (let ((r (if envbuf
753 (##core#inline "C_u_i_execve" prg argbuf envbuf)
754 (##core#inline "C_u_i_execvp" prg argbuf))))
755 (when (fx= r -1)
756 (posix-error #:process-error 'process-execute "cannot execute process" filename))))))))
757
758(set! chicken.process#process-spawn
759 (lambda (mode filename #!optional (arglist '()) envlist exactf)
760 (let ((argconv (if exactf (lambda (x) x) quote-arg-string)))
761 (##sys#check-fixnum mode 'process-spawn)
762 (call-with-exec-args
763 'process-spawn filename argconv arglist envlist
764 (lambda (prg argbuf envbuf)
765 (##core#inline "C_flushall")
766 (let ((r (if envbuf
767 (##core#inline "C_u_i_spawnvpe" mode prg argbuf envbuf)
768 (##core#inline "C_u_i_spawnvp" mode prg argbuf))))
769 (if (fx= r -1)
770 (posix-error #:process-error 'process-spawn
771 "cannot spawn process" filename)
772 (register-pid r))))))))
773
774(define-foreign-variable _shlcmd c-string "C_shlcmd")
775
776(define (shell-command loc)
777 (or (get-environment-variable "COMSPEC")
778 (if (##core#inline "C_get_shlcmd")
779 _shlcmd
780 (##sys#error/errno
781 (##sys#update-errno) loc "cannot retrieve system directory"))))
782
783(define (shell-command-arguments cmdlin)
784 (list "/c" cmdlin) )
785
786(set! chicken.process#process-run
787 (lambda (f . args)
788 (let ((args (if (pair? args) (car args) #f)))
789 (if args
790 (chicken.process#process-spawn
791 chicken.process#spawn/nowait f args)
792 (chicken.process#process-spawn
793 chicken.process#spawn/nowait
794 (shell-command 'process-run)
795 (shell-command-arguments f)) ) ) ) )
796
797;;; Run subprocess connected with pipes:
798(define-foreign-variable _rdbuf char "C_rdbuf")
799(define-foreign-variable _wr0 int "C_wr0_")
800(define-foreign-variable _rd1 int "C_rd1_")
801
802; from original by Mejedi
803;; process-impl
804; loc caller procedure symbol
805; cmd pathname or commandline
806; args string-list or '()
807; env string-list or #f (currently ignored)
808; stdoutf #f then share, or #t then create
809; stdinf #f then share, or #t then create
810; stderrf #f then share, or #t then create
811;
812; (values stdin-input-port? stdout-output-port? pid stderr-input-port?)
813; where stdin-input-port?, etc. is a port or #f, indicating no port created.
814
815(define process-impl
816 ;; XXX TODO: When environment is implemented, check for embedded NUL bytes!
817 (let ([c-process
818 (foreign-lambda bool "C_process" c-string c-string c-pointer
819 (c-pointer int) (c-pointer int) (c-pointer int) (c-pointer int) int)])
820 ; The environment list must be sorted & include current directory
821 ; information for the system drives. i.e !C:=...
822 ; For now any environment is ignored.
823 (lambda (loc cmd args env stdoutf stdinf stderrf exactf enc)
824 (let* ((arglist (cons cmd args))
825 (cmdlin (string-intersperse
826 (if exactf
827 arglist
828 (map quote-arg-string arglist)))))
829 (let-location ([handle int -1]
830 [stdin_fd int -1] [stdout_fd int -1] [stderr_fd int -1])
831 (let ([res
832 (c-process cmd cmdlin #f
833 (location handle)
834 (location stdin_fd) (location stdout_fd) (location stderr_fd)
835 (+ (if stdinf 0 1) (if stdoutf 0 2) (if stderrf 0 4)))])
836 (if (integer? res)
837 (make-process
838 res #f
839 (and stdoutf (chicken.file.posix#open-input-file*
840 stdout_fd)) ;Parent stdin
841 (and stdinf (chicken.file.posix#open-output-file*
842 stdin_fd)) ;Parent stdout
843 handle
844 (and stderrf (chicken.file.posix#open-input-file*
845 stderr_fd)
846 #f))
847 (##sys#signal-hook/errno
848 #:process-error (##sys#update-errno) loc "cannot execute process" cmdlin))))))))
849
850;; TODO: See if this can be moved to posix-common
851(let ((%process
852 (lambda (loc cmd args env exactf enc)
853 (let ((chkstrlst
854 (lambda (lst)
855 (##sys#check-list lst loc)
856 (for-each (cut ##sys#check-string <> loc) lst) )))
857 (##sys#check-string cmd loc)
858 (if args
859 (chkstrlst args)
860 (begin
861 (set! exactf #t)
862 (set! args (shell-command-arguments cmd))
863 (set! cmd (shell-command loc)) ) )
864 (when env (check-environment-list env loc))
865 (process-impl loc cmd args env #t #t err? exactf enc)))))
866 (set! chicken.process#process
867 (lambda (cmd #!optional args env (enc 'utf-8) exactf)
868 (%process 'process cmd args env exactf enc) ))
869 (set! chicken.process#process*
870 (lambda (cmd #!optional args env (enc 'utf-8) exactf)
871 (%process 'process* cmd args env exactf enc) )) )
872
873(define-foreign-variable _exstatus int "C_exstatus")
874
875(define (process-wait-impl pid nohang)
876 (cond ((##core#inline "C_process_wait" pid nohang)
877 (values pid #t _exstatus))
878 (else (values -1 #f #f) ) ))
879
880
881;;; Getting group- and user-information:
882
883(define-foreign-variable _username c-string "C_username")
884
885(set! chicken.process-context.posix#current-user-name
886 (lambda ()
887 (if (##core#inline "C_get_user_name")
888 _username
889 (##sys#error/errno
890 (##sys#update-errno) 'current-user-name "cannot retrieve current user-name"))))
891
892
893;;; unimplemented stuff:
894
895(define-unimplemented chown) ; covers set-file-group! and set-file-owner!
896(set!-unimplemented chicken.file.posix#create-fifo)
897(set!-unimplemented chicken.process-context.posix#create-session)
898(set!-unimplemented chicken.file.posix#create-symbolic-link)
899(set!-unimplemented chicken.process-context.posix#current-effective-group-id)
900(set!-unimplemented chicken.process-context.posix#current-effective-user-id)
901(set!-unimplemented chicken.process-context.posix#current-effective-user-name)
902(set!-unimplemented chicken.process-context.posix#current-group-id)
903(set!-unimplemented chicken.process-context.posix#current-user-id)
904(set!-unimplemented chicken.process-context.posix#user-information)
905(set!-unimplemented chicken.file.posix#file-control)
906(set!-unimplemented chicken.file.posix#file-link)
907(set!-unimplemented chicken.file.posix#file-lock)
908(set!-unimplemented chicken.file.posix#file-lock/blocking)
909(set!-unimplemented chicken.file.posix#file-select)
910(set!-unimplemented chicken.file.posix#file-test-lock)
911(set!-unimplemented chicken.file.posix#file-truncate)
912(set!-unimplemented chicken.file.posix#file-unlock)
913(set!-unimplemented chicken.process-context.posix#parent-process-id)
914(set!-unimplemented chicken.process#process-fork)
915(set!-unimplemented chicken.process-context.posix#process-group-id)
916(set!-unimplemented chicken.process#process-signal)
917(set!-unimplemented chicken.file.posix#read-symbolic-link)
918(set!-unimplemented chicken.process.signal#set-alarm!)
919(set!-unimplemented chicken.process-context.posix#set-root-directory!)
920(set!-unimplemented chicken.process.signal#set-signal-mask!)
921(set!-unimplemented chicken.process.signal#signal-mask)
922(set!-unimplemented chicken.process.signal#signal-mask!)
923(set!-unimplemented chicken.process.signal#signal-masked?)
924(set!-unimplemented chicken.process.signal#signal-unmask!)
925(set!-unimplemented chicken.process-context.posix#user-information)
926(set!-unimplemented chicken.time.posix#utc-time->seconds)
927(set!-unimplemented chicken.time.posix#string->time)
928
929;; Unix-only definitions
930(set! chicken.file.posix#fcntl/dupfd 0)
931(set! chicken.file.posix#fcntl/getfd 0)
932(set! chicken.file.posix#fcntl/setfd 0)
933(set! chicken.file.posix#fcntl/getfl 0)
934(set! chicken.file.posix#fcntl/setfl 0)
935(set! chicken.file.posix#open/noctty 0)
936(set! chicken.file.posix#open/nonblock 0)
937(set! chicken.file.posix#open/fsync 0)
938(set! chicken.file.posix#open/sync 0)
939(set! chicken.file.posix#perm/isgid 0)
940(set! chicken.file.posix#perm/isuid 0)
941(set! chicken.file.posix#perm/isvtx 0)