~ chicken-core (chicken-5) /posix-common.scm
Trap1;;;; posix-common.scm - common code for UNIX and Windows versions of the posix unit
2;
3; Copyright (c) 2010-2022, The CHICKEN Team
4; All rights reserved.
5;
6; Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
7; conditions are met:
8;
9; Redistributions of source code must retain the above copyright notice, this list of conditions and the following
10; disclaimer.
11; Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
12; disclaimer in the documentation and/or other materials provided with the distribution.
13; Neither the name of the author nor the names of its contributors may be used to endorse or promote
14; products derived from this software without specific prior written permission.
15;
16; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
17; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18; AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24; POSSIBILITY OF SUCH DAMAGE.
25
26
27(declare
28 (foreign-declare #<<EOF
29
30#include <signal.h>
31
32static int C_not_implemented(void);
33int C_not_implemented() { return -1; }
34
35static C_TLS struct stat C_statbuf;
36
37#define C_stat_type (C_statbuf.st_mode & S_IFMT)
38#define C_stat_perm (C_statbuf.st_mode & ~S_IFMT)
39
40#define C_u_i_stat(fn) C_fix(C_stat(C_c_string(fn), &C_statbuf))
41#define C_u_i_fstat(fd) C_fix(fstat(C_unfix(fd), &C_statbuf))
42
43#ifndef S_IFSOCK
44# define S_IFSOCK 0140000
45#endif
46
47#ifndef S_IRUSR
48# define S_IRUSR S_IREAD
49#endif
50#ifndef S_IWUSR
51# define S_IWUSR S_IWRITE
52#endif
53#ifndef S_IXUSR
54# define S_IXUSR S_IEXEC
55#endif
56
57#ifndef S_IRGRP
58# define S_IRGRP S_IREAD
59#endif
60#ifndef S_IWGRP
61# define S_IWGRP S_IWRITE
62#endif
63#ifndef S_IXGRP
64# define S_IXGRP S_IEXEC
65#endif
66
67#ifndef S_IROTH
68# define S_IROTH S_IREAD
69#endif
70#ifndef S_IWOTH
71# define S_IWOTH S_IWRITE
72#endif
73#ifndef S_IXOTH
74# define S_IXOTH S_IEXEC
75#endif
76
77#define cpy_tmvec_to_tmstc08(ptm, v) \
78 ((ptm)->tm_sec = C_unfix(C_block_item((v), 0)), \
79 (ptm)->tm_min = C_unfix(C_block_item((v), 1)), \
80 (ptm)->tm_hour = C_unfix(C_block_item((v), 2)), \
81 (ptm)->tm_mday = C_unfix(C_block_item((v), 3)), \
82 (ptm)->tm_mon = C_unfix(C_block_item((v), 4)), \
83 (ptm)->tm_year = C_unfix(C_block_item((v), 5)), \
84 (ptm)->tm_wday = C_unfix(C_block_item((v), 6)), \
85 (ptm)->tm_yday = C_unfix(C_block_item((v), 7)), \
86 (ptm)->tm_isdst = (C_block_item((v), 8) != C_SCHEME_FALSE))
87
88#define cpy_tmvec_to_tmstc9(ptm, v) \
89 (((struct tm *)ptm)->tm_gmtoff = -C_unfix(C_block_item((v), 9)))
90
91#define C_tm_set_08(v, tm) cpy_tmvec_to_tmstc08( (tm), (v) )
92#define C_tm_set_9(v, tm) cpy_tmvec_to_tmstc9( (tm), (v) )
93
94static struct tm *
95C_tm_set( C_word v, void *tm )
96{
97 C_tm_set_08( v, (struct tm *)tm );
98#if defined(C_GNU_ENV) && !defined(__CYGWIN__) && !defined(__uClinux__)
99 C_tm_set_9( v, (struct tm *)tm );
100#endif
101 return tm;
102}
103
104#define TIME_STRING_MAXLENGTH 255
105static char C_time_string [TIME_STRING_MAXLENGTH + 1];
106#undef TIME_STRING_MAXLENGTH
107
108#define C_strftime(v, f, tm) \
109 (strftime(C_time_string, sizeof(C_time_string), C_c_string(f), C_tm_set((v), (tm))) ? C_time_string : NULL)
110#define C_a_mktime(ptr, c, v, tm) C_int64_to_num(ptr, mktime(C_tm_set((v), C_data_pointer(tm))))
111#define C_asctime(v, tm) (asctime(C_tm_set((v), (tm))))
112
113#define C_fdopen(a, n, fd, m) C_mpointer(a, fdopen(C_unfix(fd), C_c_string(m)))
114#define C_dup(x) C_fix(dup(C_unfix(x)))
115#define C_dup2(x, y) C_fix(dup2(C_unfix(x), C_unfix(y)))
116
117#define C_set_file_ptr(port, ptr) (C_set_block_item(port, 0, (C_block_item(ptr, 0))), C_SCHEME_UNDEFINED)
118
119/* It is assumed that 'int' is-a 'long' */
120#define C_ftell(a, n, p) C_int64_to_num(a, ftell(C_port_file(p)))
121#define C_fseek(p, n, w) C_mk_nbool(fseek(C_port_file(p), C_num_to_int64(n), C_unfix(w)))
122#define C_lseek(fd, o, w) C_fix(lseek(C_unfix(fd), C_num_to_int64(o), C_unfix(w)))
123
124EOF
125))
126
127(include "common-declarations.scm")
128
129(define-syntax define-unimplemented
130 (syntax-rules ()
131 ((_ ?name)
132 (define (?name . _)
133 (error '?name (##core#immutable '"this function is not available on this platform")) ) ) ) )
134
135(define-syntax set!-unimplemented
136 (syntax-rules ()
137 ((_ ?name)
138 (set! ?name
139 (lambda _
140 (error '?name (##core#immutable '"this function is not available on this platform"))) ) ) ) )
141
142
143;;; Error codes:
144
145(define-foreign-variable _errno int "errno")
146
147(define-foreign-variable _eperm int "EPERM")
148(define-foreign-variable _enoent int "ENOENT")
149(define-foreign-variable _esrch int "ESRCH")
150(define-foreign-variable _eintr int "EINTR")
151(define-foreign-variable _eio int "EIO")
152(define-foreign-variable _enoexec int "ENOEXEC")
153(define-foreign-variable _ebadf int "EBADF")
154(define-foreign-variable _echild int "ECHILD")
155(define-foreign-variable _enomem int "ENOMEM")
156(define-foreign-variable _eacces int "EACCES")
157(define-foreign-variable _efault int "EFAULT")
158(define-foreign-variable _ebusy int "EBUSY")
159(define-foreign-variable _eexist int "EEXIST")
160(define-foreign-variable _enotdir int "ENOTDIR")
161(define-foreign-variable _eisdir int "EISDIR")
162(define-foreign-variable _einval int "EINVAL")
163(define-foreign-variable _emfile int "EMFILE")
164(define-foreign-variable _enospc int "ENOSPC")
165(define-foreign-variable _espipe int "ESPIPE")
166(define-foreign-variable _epipe int "EPIPE")
167(define-foreign-variable _eagain int "EAGAIN")
168(define-foreign-variable _erofs int "EROFS")
169(define-foreign-variable _enxio int "ENXIO")
170(define-foreign-variable _e2big int "E2BIG")
171(define-foreign-variable _exdev int "EXDEV")
172(define-foreign-variable _enodev int "ENODEV")
173(define-foreign-variable _enfile int "ENFILE")
174(define-foreign-variable _enotty int "ENOTTY")
175(define-foreign-variable _efbig int "EFBIG")
176(define-foreign-variable _emlink int "EMLINK")
177(define-foreign-variable _edom int "EDOM")
178(define-foreign-variable _erange int "ERANGE")
179(define-foreign-variable _edeadlk int "EDEADLK")
180(define-foreign-variable _enametoolong int "ENAMETOOLONG")
181(define-foreign-variable _enolck int "ENOLCK")
182(define-foreign-variable _enosys int "ENOSYS")
183(define-foreign-variable _enotempty int "ENOTEMPTY")
184(define-foreign-variable _eilseq int "EILSEQ")
185(define-foreign-variable _ewouldblock int "EWOULDBLOCK")
186
187(define posix-error
188 (let ([strerror (foreign-lambda c-string "strerror" int)]
189 [string-append string-append] )
190 (lambda (type loc msg . args)
191 (let ([rn (##sys#update-errno)])
192 (apply ##sys#signal-hook/errno
193 type rn loc (string-append msg " - " (strerror rn)) args)))))
194
195(define ##sys#posix-error posix-error)
196
197
198;;; File properties
199
200(define-foreign-variable _stat_st_ino unsigned-int "C_statbuf.st_ino")
201(define-foreign-variable _stat_st_nlink unsigned-int "C_statbuf.st_nlink")
202(define-foreign-variable _stat_st_gid unsigned-int "C_statbuf.st_gid")
203(define-foreign-variable _stat_st_size integer64 "C_statbuf.st_size")
204(define-foreign-variable _stat_st_mtime integer64 "C_statbuf.st_mtime")
205(define-foreign-variable _stat_st_atime integer64 "C_statbuf.st_atime")
206(define-foreign-variable _stat_st_ctime integer64 "C_statbuf.st_ctime")
207(define-foreign-variable _stat_st_uid unsigned-int "C_statbuf.st_uid")
208(define-foreign-variable _stat_st_mode unsigned-int "C_statbuf.st_mode")
209(define-foreign-variable _stat_st_dev unsigned-int "C_statbuf.st_dev")
210(define-foreign-variable _stat_st_rdev unsigned-int "C_statbuf.st_rdev")
211
212(define-syntax stat-mode
213 (er-macro-transformer
214 (lambda (x r c)
215 ;; no need to rename here
216 (let* ((mode (cadr x))
217 (name (symbol->string mode)))
218 `(##core#begin
219 (declare
220 (foreign-declare
221 ,(string-append "#ifndef " name "\n"
222 "#define " name " S_IFREG\n"
223 "#endif\n")))
224 (define-foreign-variable ,mode unsigned-int))))))
225
226(stat-mode S_IFLNK)
227(stat-mode S_IFREG)
228(stat-mode S_IFDIR)
229(stat-mode S_IFCHR)
230(stat-mode S_IFBLK)
231(stat-mode S_IFSOCK)
232(stat-mode S_IFIFO)
233
234(define (stat file link err loc)
235 (let ((r (cond ((fixnum? file) (##core#inline "C_u_i_fstat" file))
236 ((port? file) (##core#inline "C_u_i_fstat" (chicken.file.posix#port->fileno file)))
237 ((string? file)
238 (let ((path (##sys#make-c-string file loc)))
239 (if link
240 (##core#inline "C_u_i_lstat" path)
241 (##core#inline "C_u_i_stat" path))))
242 (else
243 (##sys#signal-hook
244 #:type-error loc "bad argument type - not a fixnum, port or string" file)) ) ) )
245 (if (fx< r 0)
246 (if err
247 (posix-error #:file-error loc "cannot access file" file)
248 #f)
249 #t)))
250
251(set! chicken.file.posix#file-stat
252 (lambda (f #!optional link)
253 (stat f link #t 'file-stat)
254 (vector _stat_st_ino _stat_st_mode _stat_st_nlink
255 _stat_st_uid _stat_st_gid _stat_st_size
256 _stat_st_atime _stat_st_ctime _stat_st_mtime
257 _stat_st_dev _stat_st_rdev
258 _stat_st_blksize _stat_st_blocks) ) )
259
260(set! chicken.file.posix#set-file-permissions!
261 (lambda (f p)
262 (##sys#check-fixnum p 'set-file-permissions!)
263 (let ((r (cond ((fixnum? f) (##core#inline "C_fchmod" f p))
264 ((port? f) (##core#inline "C_fchmod" (chicken.file.posix#port->fileno f) p))
265 ((string? f)
266 (##core#inline "C_chmod"
267 (##sys#make-c-string f 'set-file-permissions!) p))
268 (else
269 (##sys#signal-hook
270 #:type-error 'file-permissions
271 "bad argument type - not a fixnum, port or string" f)) ) ) )
272 (when (fx< r 0)
273 (posix-error #:file-error 'set-file-permissions! "cannot change file permissions" f p) ) )))
274
275(set! chicken.file.posix#file-modification-time
276 (lambda (f)
277 (stat f #f #t 'file-modification-time)
278 _stat_st_mtime))
279(set! chicken.file.posix#file-access-time
280 (lambda (f)
281 (stat f #f #t 'file-access-time)
282 _stat_st_atime))
283(set! chicken.file.posix#file-change-time
284 (lambda (f)
285 (stat f #f #t 'file-change-time)
286 _stat_st_ctime))
287
288(set! chicken.file.posix#set-file-times!
289 (lambda (f . rest)
290 (let-optionals* rest ((atime (current-seconds)) (mtime atime))
291 (when atime (##sys#check-exact-integer atime 'set-file-times!))
292 (when mtime (##sys#check-exact-integer mtime 'set-file-times!))
293 (let ((r ((foreign-lambda int "set_file_mtime"
294 c-string scheme-object scheme-object)
295 f atime mtime)))
296 (when (fx< r 0)
297 (apply posix-error
298 #:file-error
299 'set-file-times! "cannot set file times" f rest))))))
300
301(set! chicken.file.posix#file-size
302 (lambda (f) (stat f #f #t 'file-size) _stat_st_size))
303
304(set! chicken.file.posix#set-file-owner!
305 (lambda (f uid)
306 (chown 'set-file-owner! f uid -1)))
307
308(set! chicken.file.posix#set-file-group!
309 (lambda (f gid)
310 (chown 'set-file-group! f -1 gid)))
311
312(set! chicken.file.posix#file-owner
313 (getter-with-setter
314 (lambda (f) (stat f #f #t 'file-owner) _stat_st_uid)
315 chicken.file.posix#set-file-owner!
316 "(chicken.file.posix#file-owner f)") )
317
318(set! chicken.file.posix#file-group
319 (getter-with-setter
320 (lambda (f) (stat f #f #t 'file-group) _stat_st_gid)
321 chicken.file.posix#set-file-group!
322 "(chicken.file.posix#file-group f)") )
323
324(set! chicken.file.posix#file-permissions
325 (getter-with-setter
326 (lambda (f)
327 (stat f #f #t 'file-permissions)
328 (foreign-value "C_stat_perm" unsigned-int))
329 chicken.file.posix#set-file-permissions!
330 "(chicken.file.posix#file-permissions f)"))
331
332(set! chicken.file.posix#file-type
333 (lambda (file #!optional link (err #t))
334 (and (stat file link err 'file-type)
335 (let ((res (foreign-value "C_stat_type" unsigned-int)))
336 (cond
337 ((fx= res S_IFREG) 'regular-file)
338 ((fx= res S_IFLNK) 'symbolic-link)
339 ((fx= res S_IFDIR) 'directory)
340 ((fx= res S_IFCHR) 'character-device)
341 ((fx= res S_IFBLK) 'block-device)
342 ((fx= res S_IFIFO) 'fifo)
343 ((fx= res S_IFSOCK) 'socket)
344 (else 'regular-file))))))
345
346(set! chicken.file.posix#regular-file?
347 (lambda (file)
348 (eq? 'regular-file (chicken.file.posix#file-type file #f #f))))
349
350(set! chicken.file.posix#symbolic-link?
351 (lambda (file)
352 (eq? 'symbolic-link (chicken.file.posix#file-type file #t #f))))
353
354(set! chicken.file.posix#block-device?
355 (lambda (file)
356 (eq? 'block-device (chicken.file.posix#file-type file #f #f))))
357
358(set! chicken.file.posix#character-device?
359 (lambda (file)
360 (eq? 'character-device (chicken.file.posix#file-type file #f #f))))
361
362(set! chicken.file.posix#fifo?
363 (lambda (file)
364 (eq? 'fifo (chicken.file.posix#file-type file #f #f))))
365
366(set! chicken.file.posix#socket?
367 (lambda (file)
368 (eq? 'socket (chicken.file.posix#file-type file #f #f))))
369
370(set! chicken.file.posix#directory?
371 (lambda (file)
372 (eq? 'directory (chicken.file.posix#file-type file #f #f))))
373
374
375;;; File position access:
376
377(define-foreign-variable _seek_set int "SEEK_SET")
378(define-foreign-variable _seek_cur int "SEEK_CUR")
379(define-foreign-variable _seek_end int "SEEK_END")
380
381(set! chicken.file.posix#seek/set _seek_set)
382(set! chicken.file.posix#seek/end _seek_end)
383(set! chicken.file.posix#seek/cur _seek_cur)
384
385(set! chicken.file.posix#set-file-position!
386 (lambda (port pos . whence)
387 (let ((whence (if (pair? whence) (car whence) _seek_set)))
388 (##sys#check-fixnum pos 'set-file-position!)
389 (##sys#check-fixnum whence 'set-file-position!)
390 (unless (cond ((port? port)
391 (and-let* ((stream (eq? (##sys#slot port 7) 'stream))
392 (res (##core#inline "C_fseek" port pos whence)))
393 (##sys#setislot port 6 #f) ;; Reset EOF status
394 res))
395 ((fixnum? port)
396 (##core#inline "C_lseek" port pos whence))
397 (else
398 (##sys#signal-hook #:type-error 'set-file-position! "invalid file" port)) )
399 (posix-error #:file-error 'set-file-position! "cannot set file position" port pos) ) ) ) )
400
401(set! chicken.file.posix#file-position
402 (getter-with-setter
403 (lambda (port)
404 (let ((pos (cond ((port? port)
405 (if (eq? (##sys#slot port 7) 'stream)
406 (##core#inline_allocate ("C_ftell" 7) port)
407 -1) )
408 ((fixnum? port)
409 (##core#inline "C_lseek" port 0 _seek_cur) )
410 (else
411 (##sys#signal-hook #:type-error 'file-position "invalid file" port)) ) ) )
412 (when (< pos 0)
413 (posix-error #:file-error 'file-position "cannot retrieve file position of port" port) )
414 pos) )
415 chicken.file.posix#set-file-position! ; doesn't accept WHENCE
416 "(chicken.file.posix#file-position port)"))
417
418
419;;; Using file-descriptors:
420
421(define-foreign-variable _stdin_fileno int "STDIN_FILENO")
422(define-foreign-variable _stdout_fileno int "STDOUT_FILENO")
423(define-foreign-variable _stderr_fileno int "STDERR_FILENO")
424
425(set! chicken.file.posix#fileno/stdin _stdin_fileno)
426(set! chicken.file.posix#fileno/stdout _stdout_fileno)
427(set! chicken.file.posix#fileno/stderr _stderr_fileno)
428
429(define-foreign-variable _o_rdonly int "O_RDONLY")
430(define-foreign-variable _o_wronly int "O_WRONLY")
431(define-foreign-variable _o_rdwr int "O_RDWR")
432(define-foreign-variable _o_creat int "O_CREAT")
433(define-foreign-variable _o_append int "O_APPEND")
434(define-foreign-variable _o_excl int "O_EXCL")
435(define-foreign-variable _o_trunc int "O_TRUNC")
436(define-foreign-variable _o_binary int "O_BINARY")
437(define-foreign-variable _o_text int "O_TEXT")
438
439(set! chicken.file.posix#open/rdonly _o_rdonly)
440(set! chicken.file.posix#open/wronly _o_wronly)
441(set! chicken.file.posix#open/rdwr _o_rdwr)
442(set! chicken.file.posix#open/read _o_rdonly)
443(set! chicken.file.posix#open/write _o_wronly)
444(set! chicken.file.posix#open/creat _o_creat)
445(set! chicken.file.posix#open/append _o_append)
446(set! chicken.file.posix#open/excl _o_excl)
447(set! chicken.file.posix#open/trunc _o_trunc)
448(set! chicken.file.posix#open/binary _o_binary)
449(set! chicken.file.posix#open/text _o_text)
450
451;; open/noinherit is platform-specific
452
453(define-foreign-variable _s_irusr int "S_IRUSR")
454(define-foreign-variable _s_iwusr int "S_IWUSR")
455(define-foreign-variable _s_ixusr int "S_IXUSR")
456(define-foreign-variable _s_irgrp int "S_IRGRP")
457(define-foreign-variable _s_iwgrp int "S_IWGRP")
458(define-foreign-variable _s_ixgrp int "S_IXGRP")
459(define-foreign-variable _s_iroth int "S_IROTH")
460(define-foreign-variable _s_iwoth int "S_IWOTH")
461(define-foreign-variable _s_ixoth int "S_IXOTH")
462(define-foreign-variable _s_irwxu int "S_IRUSR | S_IWUSR | S_IXUSR")
463(define-foreign-variable _s_irwxg int "S_IRGRP | S_IWGRP | S_IXGRP")
464(define-foreign-variable _s_irwxo int "S_IROTH | S_IWOTH | S_IXOTH")
465
466(set! chicken.file.posix#perm/irusr _s_irusr)
467(set! chicken.file.posix#perm/iwusr _s_iwusr)
468(set! chicken.file.posix#perm/ixusr _s_ixusr)
469(set! chicken.file.posix#perm/irgrp _s_irgrp)
470(set! chicken.file.posix#perm/iwgrp _s_iwgrp)
471(set! chicken.file.posix#perm/ixgrp _s_ixgrp)
472(set! chicken.file.posix#perm/iroth _s_iroth)
473(set! chicken.file.posix#perm/iwoth _s_iwoth)
474(set! chicken.file.posix#perm/ixoth _s_ixoth)
475(set! chicken.file.posix#perm/irwxu _s_irwxu)
476(set! chicken.file.posix#perm/irwxg _s_irwxg)
477(set! chicken.file.posix#perm/irwxo _s_irwxo)
478
479;; perm/isvtx, perm/isuid and perm/isgid are platform-specific
480
481(let ()
482 (define (mode inp m loc)
483 (##sys#make-c-string
484 (cond ((pair? m)
485 (let ([m (car m)])
486 (case m
487 ((#:append) (if (not inp) "a" (##sys#error "invalid mode for input file" m)))
488 (else (##sys#error "invalid mode argument" m)) ) ) )
489 [inp "r"]
490 [else "w"] )
491 loc) )
492 (define (check loc fd inp r)
493 (if (##sys#null-pointer? r)
494 (posix-error #:file-error loc "cannot open file" fd)
495 (let ((port (##sys#make-port (if inp 1 2) ##sys#stream-port-class "(fdport)" 'stream)))
496 (##core#inline "C_set_file_ptr" port r)
497 port) ) )
498 (set! chicken.file.posix#open-input-file*
499 (lambda (fd . m)
500 (##sys#check-fixnum fd 'open-input-file*)
501 (check 'open-input-file* fd #t (##core#inline_allocate ("C_fdopen" 2) fd (mode #t m 'open-input-file*))) ) )
502 (set! chicken.file.posix#open-output-file*
503 (lambda (fd . m)
504 (##sys#check-fixnum fd 'open-output-file*)
505 (check 'open-output-file* fd #f (##core#inline_allocate ("C_fdopen" 2) fd (mode #f m 'open-output-file*)) ) ) ) )
506
507(set! chicken.file.posix#port->fileno
508 (lambda (port)
509 (##sys#check-open-port port 'port->fileno)
510 (cond ((eq? 'socket (##sys#slot port 7))
511 ;; Extract socket-FD from the port's "data" object - this is identical
512 ;; to "##sys#tcp-port->fileno" in the tcp unit (tcp.scm). We code it in
513 ;; this low-level manner to avoid depend on code defined there.
514 ;; Peter agrees with that. I think. Have a nice day.
515 (##sys#slot (##sys#port-data port) 0) )
516 ((not (zero? (##sys#peek-unsigned-integer port 0)))
517 (let ([fd (##core#inline "C_port_fileno" port)])
518 (when (fx< fd 0)
519 (posix-error #:file-error 'port->fileno "cannot access file-descriptor of port" port) )
520 fd) )
521 (else (posix-error #:type-error 'port->fileno "port has no attached file" port)) ) ) )
522
523(set! chicken.file.posix#duplicate-fileno
524 (lambda (old . new)
525 (##sys#check-fixnum old 'duplicate-fileno)
526 (let ([fd (if (null? new)
527 (##core#inline "C_dup" old)
528 (let ([n (car new)])
529 (##sys#check-fixnum n 'duplicate-fileno)
530 (##core#inline "C_dup2" old n) ) ) ] )
531 (when (fx< fd 0)
532 (posix-error #:file-error 'duplicate-fileno "cannot duplicate file-descriptor" old) )
533 fd) ) )
534
535
536;;; Access process ID:
537
538(set! chicken.process-context.posix#current-process-id
539 (foreign-lambda int "C_getpid"))
540
541
542;;; Set or get current directory by file descriptor:
543
544(set! chicken.process-context.posix#change-directory*
545 (lambda (fd)
546 (##sys#check-fixnum fd 'change-directory*)
547 (unless (fx= 0 (##core#inline "C_fchdir" fd))
548 (posix-error #:file-error 'change-directory* "cannot change current directory" fd))
549 fd))
550
551(set! ##sys#change-directory-hook
552 (let ((cd ##sys#change-directory-hook))
553 (lambda (dir)
554 ((if (fixnum? dir)
555 chicken.process-context.posix#change-directory*
556 cd) dir))))
557
558;;; umask
559
560(set! chicken.file.posix#file-creation-mode
561 (getter-with-setter
562 (lambda (#!optional um)
563 (when um (##sys#check-fixnum um 'file-creation-mode))
564 (let ((um2 (##core#inline "C_umask" (or um 0))))
565 (unless um (##core#inline "C_umask" um2)) ; restore
566 um2))
567 (lambda (um)
568 (##sys#check-fixnum um 'file-creation-mode)
569 (##core#inline "C_umask" um))
570 "(chicken.file.posix#file-creation-mode mode)"))
571
572
573;;; Time related things:
574
575(define decode-seconds (##core#primitive "C_decode_seconds"))
576
577(define (check-time-vector loc tm)
578 (##sys#check-vector tm loc)
579 (when (fx< (##sys#size tm) 10)
580 (##sys#error loc "time vector too short" tm) ) )
581
582(set! chicken.time.posix#seconds->local-time
583 (lambda (#!optional (secs (current-seconds)))
584 (##sys#check-exact-integer secs 'seconds->local-time)
585 (decode-seconds secs #f) ))
586
587(set! chicken.time.posix#seconds->utc-time
588 (lambda (#!optional (secs (current-seconds)))
589 (##sys#check-exact-integer secs 'seconds->utc-time)
590 (decode-seconds secs #t) ) )
591
592(set! chicken.time.posix#seconds->string
593 (let ([ctime (foreign-lambda c-string "C_ctime" integer)])
594 (lambda (#!optional (secs (current-seconds)))
595 (##sys#check-exact-integer secs 'seconds->string)
596 (let ([str (ctime secs)])
597 (if str
598 (##sys#substring str 0 (fx- (##sys#size str) 1))
599 (##sys#error 'seconds->string "cannot convert seconds to string" secs) ) ) ) ) )
600
601(set! chicken.time.posix#local-time->seconds
602 (let ((tm-size (foreign-value "sizeof(struct tm)" int)))
603 (lambda (tm)
604 (check-time-vector 'local-time->seconds tm)
605 (let ((t (##core#inline_allocate ("C_a_mktime" 7) tm (##sys#make-string tm-size #\nul))))
606 (if (= -1 t)
607 (##sys#error 'local-time->seconds "cannot convert time vector to seconds" tm)
608 t)))))
609
610(set! chicken.time.posix#time->string
611 (let ((asctime (foreign-lambda c-string "C_asctime" scheme-object scheme-pointer))
612 (strftime (foreign-lambda c-string "C_strftime" scheme-object scheme-object scheme-pointer))
613 (tm-size (foreign-value "sizeof(struct tm)" int)))
614 (lambda (tm #!optional fmt)
615 (check-time-vector 'time->string tm)
616 (if fmt
617 (begin
618 (##sys#check-string fmt 'time->string)
619 (or (strftime tm (##sys#make-c-string fmt 'time->string) (##sys#make-string tm-size #\nul))
620 (##sys#error 'time->string "time formatting overflows buffer" tm)) )
621 (let ([str (asctime tm (##sys#make-string tm-size #\nul))])
622 (if str
623 (##sys#substring str 0 (fx- (##sys#size str) 1))
624 (##sys#error 'time->string "cannot convert time vector to string" tm) ) ) ) ) ) )
625
626
627;;; Signals
628
629(set! chicken.process.signal#set-signal-handler! ; DEPRECATED
630 (lambda (sig proc)
631 (##sys#check-fixnum sig 'set-signal-handler!)
632 (##core#inline "C_establish_signal_handler" sig (and proc sig))
633 (vector-set! ##sys#signal-vector sig proc) ) )
634
635(set! chicken.process.signal#signal-handler ; DEPRECATED
636 (getter-with-setter
637 (lambda (sig)
638 (##sys#check-fixnum sig 'signal-handler)
639 (##sys#slot ##sys#signal-vector sig) )
640 chicken.process.signal#set-signal-handler!
641 "(chicken.process.signal#signal-handler sig)"))
642
643(set! chicken.process.signal#make-signal-handler
644 (lambda sigs
645 (let ((q (##sys#make-event-queue)))
646 (for-each
647 (lambda (sig)
648 (##sys#check-fixnum sig 'make-signal-handler)
649 (##core#inline "C_establish_signal_handler" sig sig)
650 (vector-set! ##sys#signal-vector sig
651 (lambda (sig) (##sys#add-event-to-queue! q sig))))
652 sigs)
653 (lambda (#!optional wait)
654 (if wait
655 (##sys#wait-for-next-event q)
656 (##sys#get-next-event q))))))
657
658(set! chicken.process.signal#signal-ignore
659 (lambda (sig)
660 (##sys#check-fixnum sig 'signal-ignore)
661 (##core#inline "C_establish_signal_handler" sig #f)
662 (vector-set! ##sys#signal-vector sig #f)))
663
664(set! chicken.process.signal#signal-default
665 (lambda (sig)
666 (##sys#check-fixnum sig 'signal-default)
667 (##core#inline "C_establish_signal_handler" sig #t)
668 (vector-set! ##sys#signal-vector sig #f)))
669
670
671;;; Processes
672
673(set! chicken.process#process-sleep
674 (lambda (n)
675 (##sys#check-fixnum n 'process-sleep)
676 (##core#inline "C_i_process_sleep" n)))
677
678(set! chicken.process#process-wait
679 (lambda args
680 (let-optionals* args ((pid #f) (nohang #f))
681 (let ((pid (or pid -1)))
682 (##sys#check-fixnum pid 'process-wait)
683 (receive (epid enorm ecode) (process-wait-impl pid nohang)
684 (if (fx= epid -1)
685 (posix-error #:process-error 'process-wait "waiting for child process failed" pid)
686 (values epid enorm ecode) ) ) ) ) ) )
687
688;; This can construct argv or envp for process-execute or process-run
689(define list->c-string-buffer
690 (let ((c-string->allocated-pointer
691 (foreign-lambda* c-pointer ((scheme-object o))
692 "char *ptr = C_malloc(C_header_size(o)); \n"
693 "if (ptr != NULL) {\n"
694 " C_memcpy(ptr, C_data_pointer(o), C_header_size(o)); \n"
695 "}\n"
696 "C_return(ptr);")))
697 (lambda (string-list convert loc)
698 (##sys#check-list string-list loc)
699
700 (let* ((string-count (##sys#length string-list))
701 ;; NUL-terminated, so we must add one
702 (buffer (make-pointer-vector (add1 string-count) #f)))
703
704 (handle-exceptions exn
705 ;; Free to avoid memory leak, then reraise
706 (begin (free-c-string-buffer buffer) (signal exn))
707
708 (do ((sl string-list (cdr sl))
709 (i 0 (fx+ i 1)))
710 ((or (null? sl) (fx= i string-count))) ; Should coincide
711
712 (##sys#check-string (car sl) loc)
713 ;; This avoids embedded NULs and appends a NUL, so "cs" is
714 ;; safe to copy and use as-is in the pointer-vector.
715 (let* ((cs (##sys#make-c-string (convert (car sl)) loc))
716 (csp (c-string->allocated-pointer cs)))
717 (unless csp (error loc "Out of memory"))
718 (pointer-vector-set! buffer i csp)))
719
720 buffer)))))
721
722(define (free-c-string-buffer buffer-array)
723 (let ((size (pointer-vector-length buffer-array)))
724 (do ((i 0 (fx+ i 1)))
725 ((fx= i size))
726 (and-let* ((s (pointer-vector-ref buffer-array i)))
727 (free s)))))
728
729;; Environments are represented as string->string association lists
730(define (check-environment-list lst loc)
731 (##sys#check-list lst loc)
732 (for-each
733 (lambda (p)
734 (##sys#check-pair p loc)
735 (##sys#check-string (car p) loc)
736 (##sys#check-string (cdr p) loc))
737 lst))
738
739(define call-with-exec-args
740 (let ((nop (lambda (x) x)))
741 (lambda (loc filename argconv arglist envlist proc)
742 (let* ((args (cons filename arglist)) ; Add argv[0]
743 (argbuf (list->c-string-buffer args argconv loc))
744 (envbuf #f))
745
746 (handle-exceptions exn
747 ;; Free to avoid memory leak, then reraise
748 (begin (free-c-string-buffer argbuf)
749 (when envbuf (free-c-string-buffer envbuf))
750 (signal exn))
751
752 ;; Envlist is never converted, so we always use nop here
753 (when envlist
754 (check-environment-list envlist loc)
755 (set! envbuf
756 (list->c-string-buffer
757 (map (lambda (p) (string-append (car p) "=" (cdr p))) envlist)
758 nop loc)))
759
760 (proc (##sys#make-c-string filename loc) argbuf envbuf))))))
761
762;; Pipes:
763
764(define-foreign-variable _pipe_buf int "PIPE_BUF")
765(set! chicken.process#pipe/buf _pipe_buf)
766
767(let ()
768 (define (mode arg) (if (pair? arg) (##sys#slot arg 0) #:text))
769 (define (badmode m) (##sys#error "illegal input/output mode specifier" m))
770 (define (check loc cmd inp r)
771 (if (##sys#null-pointer? r)
772 (posix-error #:file-error loc "cannot open pipe" cmd)
773 (let ((port (##sys#make-port (if inp 1 2) ##sys#stream-port-class "(pipe)" 'stream)))
774 (##core#inline "C_set_file_ptr" port r)
775 port) ) )
776 (set! chicken.process#open-input-pipe
777 (lambda (cmd . m)
778 (##sys#check-string cmd 'open-input-pipe)
779 (let ([m (mode m)])
780 (check
781 'open-input-pipe
782 cmd #t
783 (case m
784 ((#:text) (##core#inline_allocate ("open_text_input_pipe" 2) (##sys#make-c-string cmd 'open-input-pipe)))
785 ((#:binary) (##core#inline_allocate ("open_binary_input_pipe" 2) (##sys#make-c-string cmd 'open-input-pipe)))
786 (else (badmode m)) ) ) ) ) )
787 (set! chicken.process#open-output-pipe
788 (lambda (cmd . m)
789 (##sys#check-string cmd 'open-output-pipe)
790 (let ((m (mode m)))
791 (check
792 'open-output-pipe
793 cmd #f
794 (case m
795 ((#:text) (##core#inline_allocate ("open_text_output_pipe" 2) (##sys#make-c-string cmd 'open-output-pipe)))
796 ((#:binary) (##core#inline_allocate ("open_binary_output_pipe" 2) (##sys#make-c-string cmd 'open-output-pipe)))
797 (else (badmode m)) ) ) ) ) )
798 (set! chicken.process#close-input-pipe
799 (lambda (port)
800 (##sys#check-input-port port #t 'close-input-pipe)
801 (let ((r (##core#inline "close_pipe" port)))
802 (when (eq? -1 r)
803 (posix-error #:file-error 'close-input-pipe "error while closing pipe" port))
804 r) ) )
805 (set! chicken.process#close-output-pipe
806 (lambda (port)
807 (##sys#check-output-port port #t 'close-output-pipe)
808 (let ((r (##core#inline "close_pipe" port)))
809 (when (eq? -1 r)
810 (posix-error #:file-error 'close-output-pipe "error while closing pipe" port))
811 r) ) ))
812
813(set! chicken.process#with-input-from-pipe
814 (lambda (cmd thunk . mode)
815 (let ((p (apply chicken.process#open-input-pipe cmd mode)))
816 (fluid-let ((##sys#standard-input p))
817 (call-with-values thunk
818 (lambda results
819 (chicken.process#close-input-pipe p)
820 (apply values results) ) ) ) ) ) )
821
822(set! chicken.process#call-with-output-pipe
823 (lambda (cmd proc . mode)
824 (let ((p (apply chicken.process#open-output-pipe cmd mode)))
825 (call-with-values
826 (lambda () (proc p))
827 (lambda results
828 (chicken.process#close-output-pipe p)
829 (apply values results) ) ) ) ) )
830
831(set! chicken.process#call-with-input-pipe
832 (lambda (cmd proc . mode)
833 (let ([p (apply chicken.process#open-input-pipe cmd mode)])
834 (call-with-values
835 (lambda () (proc p))
836 (lambda results
837 (chicken.process#close-input-pipe p)
838 (apply values results) ) ) ) ) )
839
840(set! chicken.process#with-output-to-pipe
841 (lambda (cmd thunk . mode)
842 (let ((p (apply chicken.process#open-output-pipe cmd mode)))
843 (fluid-let ((##sys#standard-output p))
844 (call-with-values thunk
845 (lambda results
846 (chicken.process#close-output-pipe p)
847 (apply values results) ) ) ) ) ) )