~ chicken-core (chicken-5) /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 C_TLS int C_pipefds[ 2 ];
92static C_TLS time_t C_secs;
93
94/* pipe handles */
95static C_TLS HANDLE C_rd0, C_wr0, C_wr0_, C_rd1, C_wr1, C_rd1_;
96static C_TLS HANDLE C_save0, C_save1; /* saved I/O handles */
97static C_TLS char C_rdbuf; /* one-char buffer for read */
98static C_TLS int C_exstatus;
99
100/* platform information; initialized for cached testing */
101static C_TLS char C_shlcmd[256] = "";
102
103/* Current user name */
104static C_TLS TCHAR C_username[255 + 1] = "";
105
106#define open_binary_input_pipe(a, n, name) C_mpointer(a, _popen(C_c_string(name), "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, _popen(C_c_string(name), "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(chmod(C_c_string(fn), 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_u_i_execvp(f,a) C_fix(execvp(C_c_string(f), (const char *const *)C_c_pointer_vector_or_null(a)))
119#define C_u_i_execve(f,a,e) C_fix(execve(C_c_string(f), (const char *const *)C_c_pointer_vector_or_null(a), (const char *const *)C_c_pointer_vector_or_null(e)))
120
121/* MS replacement for the fork-exec pair */
122#define C_u_i_spawnvp(m,f,a) C_fix(spawnvp(C_unfix(m), C_c_string(f), (const char *const *)C_c_pointer_vector_or_null(a)))
123#define C_u_i_spawnvpe(m,f,a,e) C_fix(spawnvpe(C_unfix(m), C_c_string(f), (const char *const *)C_c_pointer_vector_or_null(a), (const char *const *)C_c_pointer_vector_or_null(e)))
124
125#define C_open(fn, fl, m) C_fix(open(C_c_string(fn), C_unfix(fl), C_unfix(m)))
126#define C_read(fd, b, n) C_fix(read(C_unfix(fd), C_data_pointer(b), C_unfix(n)))
127#define C_write(fd, b, n) C_fix(write(C_unfix(fd), C_data_pointer(b), C_unfix(n)))
128
129#define C_flushall() C_fix(_flushall())
130
131#define C_umask(m) C_fix(_umask(C_unfix(m)))
132
133#define C_ctime(n) (C_secs = (n), ctime(&C_secs))
134
135#define TIME_STRING_MAXLENGTH 255
136static char C_time_string [TIME_STRING_MAXLENGTH + 1];
137#undef TIME_STRING_MAXLENGTH
138
139/*
140 mapping from Win32 error codes to errno
141*/
142
143typedef struct
144{
145 DWORD win32;
146 int libc;
147} errmap_t;
148
149static errmap_t errmap[] =
150{
151 {ERROR_INVALID_FUNCTION, EINVAL},
152 {ERROR_FILE_NOT_FOUND, ENOENT},
153 {ERROR_PATH_NOT_FOUND, ENOENT},
154 {ERROR_TOO_MANY_OPEN_FILES, EMFILE},
155 {ERROR_ACCESS_DENIED, EACCES},
156 {ERROR_INVALID_HANDLE, EBADF},
157 {ERROR_ARENA_TRASHED, ENOMEM},
158 {ERROR_NOT_ENOUGH_MEMORY, ENOMEM},
159 {ERROR_INVALID_BLOCK, ENOMEM},
160 {ERROR_BAD_ENVIRONMENT, E2BIG},
161 {ERROR_BAD_FORMAT, ENOEXEC},
162 {ERROR_INVALID_ACCESS, EINVAL},
163 {ERROR_INVALID_DATA, EINVAL},
164 {ERROR_INVALID_DRIVE, ENOENT},
165 {ERROR_CURRENT_DIRECTORY, EACCES},
166 {ERROR_NOT_SAME_DEVICE, EXDEV},
167 {ERROR_NO_MORE_FILES, ENOENT},
168 {ERROR_LOCK_VIOLATION, EACCES},
169 {ERROR_BAD_NETPATH, ENOENT},
170 {ERROR_NETWORK_ACCESS_DENIED, EACCES},
171 {ERROR_BAD_NET_NAME, ENOENT},
172 {ERROR_FILE_EXISTS, EEXIST},
173 {ERROR_CANNOT_MAKE, EACCES},
174 {ERROR_FAIL_I24, EACCES},
175 {ERROR_INVALID_PARAMETER, EINVAL},
176 {ERROR_NO_PROC_SLOTS, EAGAIN},
177 {ERROR_DRIVE_LOCKED, EACCES},
178 {ERROR_BROKEN_PIPE, EPIPE},
179 {ERROR_DISK_FULL, ENOSPC},
180 {ERROR_INVALID_TARGET_HANDLE, EBADF},
181 {ERROR_INVALID_HANDLE, EINVAL},
182 {ERROR_WAIT_NO_CHILDREN, ECHILD},
183 {ERROR_CHILD_NOT_COMPLETE, ECHILD},
184 {ERROR_DIRECT_ACCESS_HANDLE, EBADF},
185 {ERROR_NEGATIVE_SEEK, EINVAL},
186 {ERROR_SEEK_ON_DEVICE, EACCES},
187 {ERROR_DIR_NOT_EMPTY, ENOTEMPTY},
188 {ERROR_NOT_LOCKED, EACCES},
189 {ERROR_BAD_PATHNAME, ENOENT},
190 {ERROR_MAX_THRDS_REACHED, EAGAIN},
191 {ERROR_LOCK_FAILED, EACCES},
192 {ERROR_ALREADY_EXISTS, EEXIST},
193 {ERROR_FILENAME_EXCED_RANGE, ENOENT},
194 {ERROR_NESTING_NOT_ALLOWED, EAGAIN},
195 {ERROR_NOT_ENOUGH_QUOTA, ENOMEM},
196 {0, 0}
197};
198
199static void C_fcall
200set_errno(DWORD w32err)
201{
202 errmap_t *map;
203 for (map = errmap; map->win32; ++map)
204 {
205 if (map->win32 == w32err)
206 {
207 errno = map->libc;
208 return;
209 }
210 }
211 errno = ENOSYS; /* For lack of anything better */
212}
213
214static int C_fcall
215set_last_errno()
216{
217 set_errno(GetLastError());
218 return 0;
219}
220
221static int fd_to_path(C_word fd, TCHAR path[])
222{
223 DWORD result;
224 HANDLE fh = (HANDLE)_get_osfhandle(C_unfix(fd));
225
226 if (fh == INVALID_HANDLE_VALUE) {
227 set_last_errno();
228 return -1;
229 }
230
231 result = GetFinalPathNameByHandle(fh, path, MAX_PATH, VOLUME_NAME_DOS);
232 if (result == 0) {
233 set_last_errno();
234 return -1;
235 } else if (result >= MAX_PATH) { /* Shouldn't happen */
236 errno = ENOMEM; /* For lack of anything better */
237 return -1;
238 } else {
239 return 0;
240 }
241}
242
243static C_word C_fchmod(C_word fd, C_word m)
244{
245 TCHAR path[MAX_PATH];
246 if (fd_to_path(fd, path) == -1) return C_fix(-1);
247 else return C_fix(chmod(path, C_unfix(m)));
248}
249
250static C_word C_fchdir(C_word fd)
251{
252 TCHAR path[MAX_PATH];
253 if (fd_to_path(fd, path) == -1) return C_fix(-1);
254 else return C_fix(chdir(path));
255}
256
257static int C_fcall
258process_wait(C_word h, C_word t)
259{
260 if (WaitForSingleObject((HANDLE)h, (t ? 0 : INFINITE)) == WAIT_OBJECT_0)
261 {
262 DWORD ret;
263 if (GetExitCodeProcess((HANDLE)h, &ret))
264 {
265 CloseHandle((HANDLE)h);
266 C_exstatus = ret;
267 return 1;
268 }
269 }
270 return set_last_errno();
271}
272
273#define C_process_wait(p, t) (process_wait(C_unfix(p), C_truep(t)) ? C_SCHEME_TRUE : C_SCHEME_FALSE)
274
275
276static C_TLS int C_isNT = 0;
277
278
279static int C_fcall
280C_windows_nt()
281{
282 static int has_info = 0;
283
284 if(!has_info) {
285 OSVERSIONINFO ovf;
286 ZeroMemory(&ovf, sizeof(ovf));
287 ovf.dwOSVersionInfoSize = sizeof(ovf);
288 has_info = 1;
289
290 if(GetVersionEx(&ovf)) {
291 SYSTEM_INFO si;
292
293 switch (ovf.dwPlatformId) {
294 case VER_PLATFORM_WIN32_NT:
295 return C_isNT = 1;
296 }
297 }
298 }
299
300 return C_isNT;
301}
302
303
304static int C_fcall
305get_shlcmd()
306{
307 /* Do we need to build the shell command pathname? */
308 if (!strlen(C_shlcmd))
309 {
310 char *cmdnam = C_windows_nt() ? "\\cmd.exe" : "\\command.com";
311 UINT len = GetSystemDirectory(C_shlcmd, sizeof(C_shlcmd) - strlen(cmdnam));
312 if (len)
313 C_strlcpy(C_shlcmd + len, cmdnam, sizeof(C_shlcmd));
314 else
315 return set_last_errno();
316 }
317
318 return 1;
319}
320
321#define C_sysinfo() (sysinfo() ? C_SCHEME_TRUE : C_SCHEME_FALSE)
322#define C_get_shlcmd() (get_shlcmd() ? C_SCHEME_TRUE : C_SCHEME_FALSE)
323
324/* GetUserName */
325
326static int C_fcall
327get_user_name()
328{
329 if (!strlen(C_username))
330 {
331 DWORD bufCharCount = sizeof(C_username) / sizeof(C_username[0]);
332 if (!GetUserName(C_username, &bufCharCount))
333 return set_last_errno();
334 }
335 return 1;
336}
337
338#define C_get_user_name() (get_user_name() ? C_SCHEME_TRUE : C_SCHEME_FALSE)
339
340/*
341 Spawn a process directly.
342 Params:
343 app Command to execute.
344 cmdlin Command line (arguments).
345 env Environment for the new process (may be NULL).
346 handle, stdin, stdout, stderr
347 Spawned process info are returned in integers.
348 When spawned process shares standard io stream with the parent
349 process the respective value in handle, stdin, stdout, stderr
350 is -1.
351 params A bitmask controling operation.
352 Bit 1: Child & parent share standard input if this bit is set.
353 Bit 2: Share standard output if bit is set.
354 Bit 3: Share standard error if bit is set.
355
356 Returns: zero return value indicates failure.
357*/
358static int C_fcall
359C_process(const char *app, const char *cmdlin, const char **env,
360 int *phandle, int *pstdin_fd, int *pstdout_fd, int *pstderr_fd,
361 int params)
362{
363 int i;
364 int success = TRUE;
365 const int f_share_io[3] = { params & 1, params & 2, params & 4};
366 int io_fds[3] = { -1, -1, -1 };
367 HANDLE
368 child_io_handles[3] = { NULL, NULL, NULL },
369 standard_io_handles[3] = {
370 GetStdHandle(STD_INPUT_HANDLE),
371 GetStdHandle(STD_OUTPUT_HANDLE),
372 GetStdHandle(STD_ERROR_HANDLE)};
373 const char modes[3] = "rww";
374 HANDLE cur_process = GetCurrentProcess(), child_process = NULL;
375 void* envblk = NULL;
376
377 /****** create io handles & fds ***/
378
379 for (i=0; i<3 && success; ++i)
380 {
381 if (f_share_io[i])
382 {
383 success = DuplicateHandle(
384 cur_process, standard_io_handles[i],
385 cur_process, &child_io_handles[i],
386 0, FALSE, DUPLICATE_SAME_ACCESS);
387 }
388 else
389 {
390 HANDLE a, b;
391 success = CreatePipe(&a,&b,NULL,0);
392 if(success)
393 {
394 HANDLE parent_end;
395 if (modes[i]=='r') { child_io_handles[i]=a; parent_end=b; }
396 else { parent_end=a; child_io_handles[i]=b; }
397 success = (io_fds[i] = _open_osfhandle((C_word)parent_end,0)) >= 0;
398 /* Make new handle inheritable */
399 if (success)
400 success = SetHandleInformation(child_io_handles[i], HANDLE_FLAG_INHERIT, -1);
401 }
402 }
403 }
404
405#if 0 /* Requires a sorted list by key! */
406 /****** create environment block if necessary ****/
407
408 if (env && success)
409 {
410 char** p;
411 int len = 0;
412
413 for (p = env; *p; ++p) len += strlen(*p) + 1;
414
415 if (envblk = C_malloc(len + 1))
416 {
417 char* pb = (char*)envblk;
418 for (p = env; *p; ++p)
419 {
420 C_strlcpy(pb, *p, len+1);
421 pb += strlen(*p) + 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 = CreateProcess(
448 NULL, (char*)cmdlin, NULL, NULL, TRUE, 0, envblk, NULL, &si, &pi);
449
450 if (success)
451 {
452 child_process=pi.hProcess;
453 CloseHandle(pi.hThread);
454 }
455 else
456 set_last_errno();
457 }
458 else
459 set_last_errno();
460
461 /****** cleanup & return *********/
462
463 /* parent must close child end */
464 for (i=0; i<3; ++i) {
465 if (child_io_handles[i] != NULL)
466 CloseHandle(child_io_handles[i]);
467 }
468
469 if (success)
470 {
471 *phandle = (C_word)child_process;
472 *pstdin_fd = io_fds[0];
473 *pstdout_fd = io_fds[1];
474 *pstderr_fd = io_fds[2];
475 }
476 else
477 {
478 for (i=0; i<3; ++i) {
479 if (io_fds[i] != -1)
480 _close(io_fds[i]);
481 }
482 }
483
484 return success;
485}
486
487static int set_file_mtime(char *filename, C_word atime, C_word mtime)
488{
489 struct stat sb;
490 struct _utimbuf tb;
491
492 /* Only stat if needed */
493 if (atime == C_SCHEME_FALSE || mtime == C_SCHEME_FALSE) {
494 if (C_stat(filename, &sb) == -1) return -1;
495 }
496
497 if (atime == C_SCHEME_FALSE) {
498 tb.actime = sb.st_atime;
499 } else {
500 tb.actime = C_num_to_int64(atime);
501 }
502 if (mtime == C_SCHEME_FALSE) {
503 tb.modtime = sb.st_mtime;
504 } else {
505 tb.modtime = C_num_to_int64(mtime);
506 }
507 return _utime(filename, &tb);
508}
509
510<#
511
512(import (only chicken.string string-intersperse))
513
514;;; Lo-level I/O:
515
516(define-foreign-variable _o_noinherit int "O_NOINHERIT")
517(set! chicken.file.posix#open/noinherit _o_noinherit)
518
519(set! chicken.file.posix#file-open
520 (let ((defmode (bitwise-ior _s_irusr _s_iwusr _s_irgrp _s_iwgrp _s_iroth _s_iwoth)))
521 (lambda (filename flags . mode)
522 (let ([mode (if (pair? mode) (car mode) defmode)])
523 (##sys#check-string filename 'file-open)
524 (##sys#check-fixnum flags 'file-open)
525 (##sys#check-fixnum mode 'file-open)
526 (let ([fd (##core#inline "C_open" (##sys#make-c-string filename 'file-open) flags mode)])
527 (when (eq? -1 fd)
528 (##sys#signal-hook/errno
529 #:file-error (##sys#update-errno) 'file-open "cannot open file" filename flags mode))
530 fd) ) ) ) )
531
532(set! chicken.file.posix#file-close
533 (lambda (fd)
534 (##sys#check-fixnum fd 'file-close)
535 (let loop ()
536 (when (fx< (##core#inline "C_close" fd) 0)
537 (cond
538 ((fx= _errno _eintr) (##sys#dispatch-interrupt loop))
539 (else
540 (posix-error #:file-error 'file-close "cannot close file" fd)))))))
541
542(set! chicken.file.posix#file-read
543 (lambda (fd size . buffer)
544 (##sys#check-fixnum fd 'file-read)
545 (##sys#check-fixnum size 'file-read)
546 (let ([buf (if (pair? buffer) (car buffer) (make-string size))])
547 (unless (and (##core#inline "C_blockp" buf) (##core#inline "C_byteblockp" buf))
548 (##sys#signal-hook #:type-error 'file-read "bad argument type - not a string or blob" buf) )
549 (let ([n (##core#inline "C_read" fd buf size)])
550 (when (eq? -1 n)
551 (##sys#signal-hook/errno
552 #:file-error (##sys#update-errno) 'file-read "cannot read from file" fd size))
553 (list buf n) ) ) ) )
554
555(set! chicken.file.posix#file-write
556 (lambda (fd buffer . size)
557 (##sys#check-fixnum fd 'file-write)
558 (unless (and (##core#inline "C_blockp" buffer) (##core#inline "C_byteblockp" buffer))
559 (##sys#signal-hook #:type-error 'file-write "bad argument type - not a string or blob" buffer) )
560 (let ([size (if (pair? size) (car size) (##sys#size buffer))])
561 (##sys#check-fixnum size 'file-write)
562 (let ([n (##core#inline "C_write" fd buffer size)])
563 (when (eq? -1 n)
564 (##sys#signal-hook/errno
565 #:file-error (##sys#update-errno) 'file-write "cannot write to file" fd size))
566 n) ) ) )
567
568(set! chicken.file.posix#file-mkstemp
569 (lambda (template)
570 (##sys#check-string template 'file-mkstemp)
571 (let* ((diz "0123456789abcdefghijklmnopqrstuvwxyz")
572 (diz-len (string-length diz))
573 (max-attempts (* diz-len diz-len diz-len))
574 (tmpl (string-copy template)) ; We'll overwrite this later
575 (tmpl-len (string-length tmpl))
576 (first-x (let loop ((i (fx- tmpl-len 1)))
577 (if (and (fx>= i 0)
578 (eq? (string-ref tmpl i) #\X))
579 (loop (fx- i 1))
580 (fx+ i 1)))))
581 (cond ((not (##sys#file-exists? (or (pathname-directory template) ".") #f #t 'file-mkstemp))
582 ;; Quit early instead of looping needlessly with C_open
583 ;; failing every time. This is a race condition, but not
584 ;; a security-critical one.
585 (##sys#signal-hook #:file-error 'file-mkstemp "non-existent directory" template))
586 ((fx= first-x tmpl-len)
587 (##sys#signal-hook #:file-error 'file-mkstemp "invalid template" template)))
588 (let loop ((count 1))
589 (let suffix-loop ((index (fx- tmpl-len 1)))
590 (when (fx>= index first-x)
591 (string-set! tmpl index
592 (string-ref diz (##core#inline "C_rand" diz-len)))
593 (suffix-loop (fx- index 1))))
594 (let ((fd (##core#inline "C_open"
595 (##sys#make-c-string tmpl 'file-open)
596 (bitwise-ior chicken.file.posix#open/rdwr
597 chicken.file.posix#open/creat
598 chicken.file.posix#open/excl)
599 (fxior _s_irusr _s_iwusr))))
600 (if (eq? -1 fd)
601 (if (fx< count max-attempts)
602 (loop (fx+ count 1))
603 (posix-error #:file-error 'file-mkstemp "cannot create temporary file" template))
604 (values fd tmpl)))))))
605
606;;; Pipe primitive:
607
608(define-foreign-variable _pipefd0 int "C_pipefds[ 0 ]")
609(define-foreign-variable _pipefd1 int "C_pipefds[ 1 ]")
610
611(set! chicken.process#create-pipe
612 (lambda (#!optional (mode (fxior chicken.file.posix#open/binary
613 chicken.file.posix#open/noinherit)))
614 (when (fx< (##core#inline "C_pipe" #f mode) 0)
615 (##sys#signal-hook/errno
616 #:file-error (##sys#update-errno) 'create-pipe "cannot create pipe"))
617 (values _pipefd0 _pipefd1) ) )
618
619;;; Signal processing:
620
621(define-foreign-variable _nsig int "NSIG")
622(define-foreign-variable _sigterm int "SIGTERM")
623(define-foreign-variable _sigint int "SIGINT")
624(define-foreign-variable _sigfpe int "SIGFPE")
625(define-foreign-variable _sigill int "SIGILL")
626(define-foreign-variable _sigsegv int "SIGSEGV")
627(define-foreign-variable _sigabrt int "SIGABRT")
628(define-foreign-variable _sigbreak int "SIGBREAK")
629
630(set! chicken.process.signal#signal/term _sigterm)
631(set! chicken.process.signal#signal/int _sigint)
632(set! chicken.process.signal#signal/fpe _sigfpe)
633(set! chicken.process.signal#signal/ill _sigill)
634(set! chicken.process.signal#signal/segv _sigsegv)
635(set! chicken.process.signal#signal/abrt _sigabrt)
636(set! chicken.process.signal#signal/break _sigbreak)
637(set! chicken.process.signal#signal/alrm 0)
638(set! chicken.process.signal#signal/bus 0)
639(set! chicken.process.signal#signal/chld 0)
640(set! chicken.process.signal#signal/cont 0)
641(set! chicken.process.signal#signal/hup 0)
642(set! chicken.process.signal#signal/io 0)
643(set! chicken.process.signal#signal/kill 0)
644(set! chicken.process.signal#signal/pipe 0)
645(set! chicken.process.signal#signal/prof 0)
646(set! chicken.process.signal#signal/quit 0)
647(set! chicken.process.signal#signal/stop 0)
648(set! chicken.process.signal#signal/trap 0)
649(set! chicken.process.signal#signal/tstp 0)
650(set! chicken.process.signal#signal/urg 0)
651(set! chicken.process.signal#signal/usr1 0)
652(set! chicken.process.signal#signal/usr2 0)
653(set! chicken.process.signal#signal/vtalrm 0)
654(set! chicken.process.signal#signal/winch 0)
655(set! chicken.process.signal#signal/xcpu 0)
656(set! chicken.process.signal#signal/xfsz 0)
657
658(set! chicken.process.signal#signals-list
659 (list
660 chicken.process.signal#signal/term
661 chicken.process.signal#signal/int
662 chicken.process.signal#signal/fpe
663 chicken.process.signal#signal/ill
664 chicken.process.signal#signal/segv
665 chicken.process.signal#signal/abrt
666 chicken.process.signal#signal/break))
667
668;;; Using file-descriptors:
669
670(define duplicate-fileno
671 (lambda (old . new)
672 (##sys#check-fixnum old duplicate-fileno)
673 (let ([fd (if (null? new)
674 (##core#inline "C_dup" old)
675 (let ([n (car new)])
676 (##sys#check-fixnum n 'duplicate-fileno)
677 (##core#inline "C_dup2" old n) ) ) ] )
678 (when (fx< fd 0)
679 (##sys#signal-hook/errno
680 #:file-error (##sys#update-errno) 'duplicate-fileno "cannot duplicate file descriptor" old))
681 fd) ) )
682
683
684;;; Time related things:
685
686(set! chicken.time.posix#local-timezone-abbreviation
687 (foreign-lambda* c-string ()
688 "char *z = (_daylight ? _tzname[1] : _tzname[0]);\n"
689 "C_return(z);") )
690
691
692;;; Process handling:
693
694(define-foreign-variable _p_overlay int "P_OVERLAY")
695(define-foreign-variable _p_wait int "P_WAIT")
696(define-foreign-variable _p_nowait int "P_NOWAIT")
697(define-foreign-variable _p_nowaito int "P_NOWAITO")
698(define-foreign-variable _p_detach int "P_DETACH")
699
700(set! chicken.process#spawn/overlay _p_overlay)
701(set! chicken.process#spawn/wait _p_wait)
702(set! chicken.process#spawn/nowait _p_nowait)
703(set! chicken.process#spawn/nowaito _p_nowaito)
704(set! chicken.process#spawn/detach _p_detach)
705
706; Windows uses a commandline style for process arguments. Thus any
707; arguments with embedded whitespace will parse incorrectly. Must
708; string-quote such arguments.
709(define quote-arg-string
710 (let ((needs-quoting?
711 ;; This is essentially (string-any char-whitespace? s) but we
712 ;; don't want a SRFI-13 dependency. (Do we?)
713 (lambda (s)
714 (let ((len (string-length s)))
715 (let loop ((i 0))
716 (cond
717 ((fx= i len) #f)
718 ((char-whitespace? (string-ref s i)) #t)
719 (else (loop (fx+ i 1)))))))))
720 (lambda (str)
721 (if (needs-quoting? str) (string-append "\"" str "\"") str))))
722
723(set! chicken.process#process-execute
724 (lambda (filename #!optional (arglist '()) envlist exactf)
725 (let ((argconv (if exactf (lambda (x) x) quote-arg-string)))
726 (call-with-exec-args
727 'process-execute filename argconv arglist envlist
728 (lambda (prg argbuf envbuf)
729 (##core#inline "C_flushall")
730 (let ((r (if envbuf
731 (##core#inline "C_u_i_execve" prg argbuf envbuf)
732 (##core#inline "C_u_i_execvp" prg argbuf))))
733 (when (fx= r -1)
734 (posix-error #:process-error 'process-execute "cannot execute process" filename))))))))
735
736(set! chicken.process#process-spawn
737 (lambda (mode filename #!optional (arglist '()) envlist exactf)
738 (let ((argconv (if exactf (lambda (x) x) quote-arg-string)))
739 (##sys#check-fixnum mode 'process-spawn)
740 (call-with-exec-args
741 'process-spawn filename argconv arglist envlist
742 (lambda (prg argbuf envbuf)
743 (##core#inline "C_flushall")
744 (let ((r (if envbuf
745 (##core#inline "C_u_i_spawnvpe" mode prg argbuf envbuf)
746 (##core#inline "C_u_i_spawnvp" mode prg argbuf))))
747 (when (fx= r -1)
748 (posix-error #:process-error 'process-spawn "cannot spawn process" filename))
749 r))))))
750
751(define-foreign-variable _shlcmd c-string "C_shlcmd")
752
753(define (shell-command loc)
754 (or (get-environment-variable "COMSPEC")
755 (if (##core#inline "C_get_shlcmd")
756 _shlcmd
757 (##sys#error/errno
758 (##sys#update-errno) loc "cannot retrieve system directory"))))
759
760(define (shell-command-arguments cmdlin)
761 (list "/c" cmdlin) )
762
763(set! chicken.process#process-run
764 (lambda (f . args)
765 (let ((args (if (pair? args) (car args) #f)))
766 (if args
767 (chicken.process#process-spawn
768 chicken.process#spawn/nowait f args)
769 (chicken.process#process-spawn
770 chicken.process#spawn/nowait
771 (shell-command 'process-run)
772 (shell-command-arguments f)) ) ) ) )
773
774;;; Run subprocess connected with pipes:
775(define-foreign-variable _rdbuf char "C_rdbuf")
776(define-foreign-variable _wr0 int "C_wr0_")
777(define-foreign-variable _rd1 int "C_rd1_")
778
779; from original by Mejedi
780;; process-impl
781; loc caller procedure symbol
782; cmd pathname or commandline
783; args string-list or '()
784; env string-list or #f (currently ignored)
785; stdoutf #f then share, or #t then create
786; stdinf #f then share, or #t then create
787; stderrf #f then share, or #t then create
788;
789; (values stdin-input-port? stdout-output-port? pid stderr-input-port?)
790; where stdin-input-port?, etc. is a port or #f, indicating no port created.
791
792(define process-impl
793 ;; XXX TODO: When environment is implemented, check for embedded NUL bytes!
794 (let ([c-process
795 (foreign-lambda bool "C_process" c-string c-string c-pointer
796 (c-pointer int) (c-pointer int) (c-pointer int) (c-pointer int) int)])
797 ; The environment list must be sorted & include current directory
798 ; information for the system drives. i.e !C:=...
799 ; For now any environment is ignored.
800 (lambda (loc cmd args env stdoutf stdinf stderrf #!optional exactf)
801 (let* ((arglist (cons cmd args))
802 (cmdlin (string-intersperse
803 (if exactf
804 arglist
805 (map quote-arg-string arglist)))))
806 (let-location ([handle int -1]
807 [stdin_fd int -1] [stdout_fd int -1] [stderr_fd int -1])
808 (let ([res
809 (c-process cmd cmdlin #f
810 (location handle)
811 (location stdin_fd) (location stdout_fd) (location stderr_fd)
812 (+ (if stdinf 0 1) (if stdoutf 0 2) (if stderrf 0 4)))])
813 (if res
814 (values
815 (and stdoutf (chicken.file.posix#open-input-file*
816 stdout_fd)) ;Parent stdin
817 (and stdinf (chicken.file.posix#open-output-file*
818 stdin_fd)) ;Parent stdout
819 handle
820 (and stderrf (chicken.file.posix#open-input-file*
821 stderr_fd)))
822 (##sys#signal-hook/errno
823 #:process-error (##sys#update-errno) loc "cannot execute process" cmdlin))))))))
824
825;; TODO: See if this can be moved to posix-common
826(let ((%process
827 (lambda (loc err? cmd args env exactf)
828 (let ((chkstrlst
829 (lambda (lst)
830 (##sys#check-list lst loc)
831 (for-each (cut ##sys#check-string <> loc) lst) )))
832 (##sys#check-string cmd loc)
833 (if args
834 (chkstrlst args)
835 (begin
836 (set! exactf #t)
837 (set! args (shell-command-arguments cmd))
838 (set! cmd (shell-command loc)) ) )
839 (when env (check-environment-list env loc))
840 (receive (in out pid err)
841 (process-impl loc cmd args env #t #t err? exactf)
842 (if err?
843 (values in out pid err)
844 (values in out pid) ) ) ) )) )
845 (set! chicken.process#process
846 (lambda (cmd #!optional args env exactf)
847 (%process 'process #f cmd args env exactf) ))
848 (set! chicken.process#process*
849 (lambda (cmd #!optional args env exactf)
850 (%process 'process* #t cmd args env exactf) )) )
851
852(define-foreign-variable _exstatus int "C_exstatus")
853
854(define (process-wait-impl pid nohang)
855 (if (##core#inline "C_process_wait" pid nohang)
856 (values pid #t _exstatus)
857 (values -1 #f #f) ) )
858
859
860;;; Getting group- and user-information:
861
862(define-foreign-variable _username c-string "C_username")
863
864(set! chicken.process-context.posix#current-user-name
865 (lambda ()
866 (if (##core#inline "C_get_user_name")
867 _username
868 (##sys#error/errno
869 (##sys#update-errno) 'current-user-name "cannot retrieve current user-name"))))
870
871
872;;; unimplemented stuff:
873
874(define-unimplemented chown) ; covers set-file-group! and set-file-owner!
875(set!-unimplemented chicken.file.posix#create-fifo)
876(set!-unimplemented chicken.process-context.posix#create-session)
877(set!-unimplemented chicken.file.posix#create-symbolic-link)
878(set!-unimplemented chicken.process-context.posix#current-effective-group-id)
879(set!-unimplemented chicken.process-context.posix#current-effective-user-id)
880(set!-unimplemented chicken.process-context.posix#current-effective-user-name)
881(set!-unimplemented chicken.process-context.posix#current-group-id)
882(set!-unimplemented chicken.process-context.posix#current-user-id)
883(set!-unimplemented chicken.process-context.posix#user-information)
884(set!-unimplemented chicken.file.posix#file-control)
885(set!-unimplemented chicken.file.posix#file-link)
886(set!-unimplemented chicken.file.posix#file-lock)
887(set!-unimplemented chicken.file.posix#file-lock/blocking)
888(set!-unimplemented chicken.file.posix#file-select)
889(set!-unimplemented chicken.file.posix#file-test-lock)
890(set!-unimplemented chicken.file.posix#file-truncate)
891(set!-unimplemented chicken.file.posix#file-unlock)
892(set!-unimplemented chicken.process-context.posix#parent-process-id)
893(set!-unimplemented chicken.process#process-fork)
894(set!-unimplemented chicken.process-context.posix#process-group-id)
895(set!-unimplemented chicken.process#process-signal)
896(set!-unimplemented chicken.file.posix#read-symbolic-link)
897(set!-unimplemented chicken.process.signal#set-alarm!)
898(set!-unimplemented chicken.process-context.posix#set-root-directory!)
899(set!-unimplemented chicken.process.signal#set-signal-mask!)
900(set!-unimplemented chicken.process.signal#signal-mask)
901(set!-unimplemented chicken.process.signal#signal-mask!)
902(set!-unimplemented chicken.process.signal#signal-masked?)
903(set!-unimplemented chicken.process.signal#signal-unmask!)
904(set!-unimplemented chicken.process-context.posix#user-information)
905(set!-unimplemented chicken.time.posix#utc-time->seconds)
906(set!-unimplemented chicken.time.posix#string->time)
907
908;; Unix-only definitions
909(set! chicken.file.posix#fcntl/dupfd 0)
910(set! chicken.file.posix#fcntl/getfd 0)
911(set! chicken.file.posix#fcntl/setfd 0)
912(set! chicken.file.posix#fcntl/getfl 0)
913(set! chicken.file.posix#fcntl/setfl 0)
914(set! chicken.file.posix#open/noctty 0)
915(set! chicken.file.posix#open/nonblock 0)
916(set! chicken.file.posix#open/fsync 0)
917(set! chicken.file.posix#open/sync 0)
918(set! chicken.file.posix#perm/isgid 0)
919(set! chicken.file.posix#perm/isuid 0)
920(set! chicken.file.posix#perm/isvtx 0)