ikarus/scheme/ikarus.posix.ss

286 lines
9.2 KiB
Scheme

;;; Ikarus Scheme -- A compiler for R6RS Scheme.
;;; Copyright (C) 2006,2007,2008 Abdulaziz Ghuloum
;;;
;;; This program is free software: you can redistribute it and/or modify
;;; it under the terms of the GNU General Public License version 3 as
;;; published by the Free Software Foundation.
;;;
;;; This program is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;;; General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with this program. If not, see <http://www.gnu.org/licenses/>.
(library (ikarus.posix)
(export posix-fork fork waitpid system file-exists? delete-file
nanosleep getenv env environ file-ctime current-directory
file-regular? file-directory? file-symbolic-link? make-symbolic-link
directory-list make-directory delete-directory change-mode
strerror)
(import
(rnrs bytevectors)
(except (ikarus)
nanosleep
posix-fork fork waitpid system file-exists? delete-file
getenv env environ file-ctime current-directory
file-regular? file-directory? file-symbolic-link? make-symbolic-link
directory-list make-directory delete-directory change-mode
strerror))
(define posix-fork
(lambda ()
(foreign-call "ikrt_fork")))
(define fork
(lambda (parent-proc child-proc)
(let ([pid (posix-fork)])
(cond
[(fx= pid 0) (child-proc)]
[(fx< pid 0) (raise/strerror 'fork pid)]
[else (parent-proc pid)]))))
(define waitpid
(lambda (pid)
(unless (fixnum? pid)
(die 'waitpid "not a fixnum" pid))
(let ([r (foreign-call "ikrt_waitpid" pid)])
(if (fx< r 0)
(raise/strerror 'waitpid r)
r))))
(define system
(lambda (x)
(unless (string? x)
(die 'system "not a string" x))
(let ([rv (foreign-call "ik_system"
(string->utf8 x))])
(if (fx< rv 0)
(raise/strerror 'system rv)
rv))))
(define stat
(lambda (path follow who)
(unless (string? path)
(die who "not a string" path))
(let ([r (foreign-call "ikrt_stat" (string->utf8 path) follow)])
(case r
[(0) 'unknown]
[(1) 'regular]
[(2) 'directory]
[(3) 'symlink]
[(-45) #f] ;; from ikarus-errno.c: ENOENT -- path does not exist
[else (raise/strerror who r path)]))))
(define file-exists?
(case-lambda
[(path) (file-exists? path #t)]
[(path follow)
(and (stat path follow 'file-exists?) #t)]))
(define file-regular?
(case-lambda
[(path) (file-regular? path #t)]
[(path follow)
(eq? 'regular (stat path follow 'file-regular?))]))
(define file-directory?
(case-lambda
[(path) (file-directory? path #t)]
[(path follow)
(eq? 'directory (stat path follow 'file-directory?))]))
(define file-symbolic-link?
(lambda (path)
(eq? 'symlink (stat path #f 'file-symbolic-link?))))
(define delete-file
(lambda (x)
(define who 'delete-file)
(unless (string? x)
(die who "filename is not a string" x))
(let ([v (foreign-call "ikrt_delete_file"
(string->utf8 x))])
(unless (eq? v #t)
(raise/strerror who v x)))))
(define directory-list
(lambda (path)
(define who 'directory-list)
(unless (string? path)
(die who "not a string" path))
(let ([r (foreign-call "ikrt_directory_list" (string->utf8 path))])
(if (fixnum? r)
(raise/strerror who r path)
(map utf8->string (reverse r))))))
(define make-directory
(case-lambda
[(path) (make-directory path #o755)]
[(path mode)
(define who 'make-directory)
(unless (string? path)
(die who "not a string" path))
(unless (fixnum? mode)
(die who "not a fixnum" mode))
(let ([r (foreign-call "ikrt_mkdir" (string->utf8 path) mode)])
(unless (eq? r #t)
(raise/strerror who r path)))]))
(define delete-directory
(case-lambda
[(path) (delete-directory path #f)]
[(path want-error?)
(define who 'delete-directory)
(unless (string? path)
(die who "not a string" path))
(let ([r (foreign-call "ikrt_rmdir" (string->utf8 path))])
(if want-error?
(unless (eq? r #t) (raise/strerror who r path))
(eq? r #t)))]))
(define change-mode
(lambda (path mode)
(define who 'change-mode)
(unless (string? path)
(die who "not a string" path))
(unless (fixnum? mode)
(die who "not a fixnum" mode))
(let ([r (foreign-call "ikrt_chmod" (string->utf8 path) mode)])
(unless (eq? r #t)
(raise/strerror who r path)))))
(define make-symbolic-link
(lambda (to path)
(define who 'make-symbolic-link)
(unless (and (string? to) (string? path))
(die who "not a string" (if (string? to) path to)))
(let ([r (foreign-call "ikrt_symlink"
(string->utf8 to) (string->utf8 path))])
(unless (eq? r #t)
(raise/strerror who r path)))))
(define (file-ctime x)
(define who 'file-ctime)
(unless (string? x)
(die who "not a string" x))
(let ([p (cons #f #f)])
(let ([v (foreign-call "ikrt_file_ctime" (string->utf8 x) p)])
(case v
[(0) (+ (* (car p) #e1e9) (cdr p))]
[else (raise/strerror who v x)]))))
(define ($getenv-bv key)
(foreign-call "ikrt_getenv" key))
(define ($getenv-str key)
(utf8->string ($getenv-bv (string->utf8 key))))
(define (getenv key)
(if (string? key)
($getenv-str key)
(die 'getenv "the key is not a string" key)))
(define env
(let ()
(define env
(case-lambda
[(key)
(if (string? key)
(foreign-call "ikrt_getenv" key)
(die 'env "the key is not a string" key))]
[(key val) (env key val #t)]
[(key val overwrite?)
(if (string? key)
(if (string? val)
(unless (foreign-call "ikrt_setenv" key val overwrite?)
(die 'env "failed" key val))
(die 'env "the value is not a string" val))
(die 'env "the key is not a string" key))]))
(define busted (lambda args (die 'env "BUG: busted!")))
busted))
(define environ (lambda args (die 'environ "busted!")))
(define environ^
(lambda ()
(map
(lambda (s)
(define (loc= s i n)
(cond
[(fx= i n) i]
[(char=? (string-ref s i) #\=) i]
[else (loc= s (fx+ i 1) n)]))
(let ([n (string-length s)])
(let ([i (loc= s 0 n)])
(cons (substring s 0 i)
(if (fx< (fxadd1 i) n)
(substring s (fxadd1 i) n)
"")))))
(foreign-call "ikrt_environ"))))
(define (nanosleep secs nsecs)
(import (ikarus system $fx))
(unless (cond
[(fixnum? secs) ($fx>= secs 0)]
[(bignum? secs) (<= 0 secs (- (expt 2 32) 1))]
[else (die 'nanosleep "not an exact integer" secs)])
(die 'nanosleep "seconds must be a nonnegative integer <=" secs))
(unless (cond
[(fixnum? nsecs) ($fx>= nsecs 0)]
[(bignum? nsecs) (<= 0 nsecs 999999999)]
[else (die 'nanosleep "not an exact integer" nsecs)])
(die 'nanosleep "nanoseconds must be an integer \
in the range 0..999999999" nsecs))
(let ([rv (foreign-call "ikrt_nanosleep" secs nsecs)])
(unless (eq? rv 0)
(error 'nanosleep "failed"))))
(define current-directory
(case-lambda
[()
(let ([v (foreign-call "ikrt_getcwd")])
(if (bytevector? v)
(utf8->string v)
(raise/strerror 'current-directory v)))]
[(x)
(if (string? x)
(let ([rv (foreign-call "ikrt_chdir" (string->utf8 x))])
(unless (eq? rv #t)
(raise/strerror 'current-directory rv x)))
(die 'current-directory "not a string" x))]))
(define raise/strerror
(case-lambda
[(who errno-code)
(raise/strerror who errno-code #f)]
[(who errno-code filename)
(raise
(condition
(make-who-condition who)
(make-message-condition (strerror errno-code))
(if filename
(make-i/o-filename-error filename)
(condition))))]))
(define strerror
(lambda (errno-code)
(define who 'strerror)
(unless (fixnum? errno-code)
(die who "not a fixnum" errno-code))
(let ([emsg (foreign-call "ikrt_strerror" errno-code)])
(if emsg
(let ([errno-name
(foreign-call "ikrt_errno_code_to_name" errno-code)])
(assert errno-name)
(format "~a: ~a"
(utf8->string errno-name)
(utf8->string emsg)))
(format "Ikarus's ~a: don't know Ikarus errno code ~s"
who errno-code)))))
)