(module io-utils mzscheme (require (lib "pretty.ss") (lib "match.ss") (lib "process.ss") ;; system "lazy-connect.ss" ) (provide in out read-byte-timeout with-output-to-file/safe with-io-device write-tree file-in-path) ;; GENERIC PORT IO ;; TODO: abstract this in a 'lazy-open' library. ;; False if timeout. Input ports are synchronizable events in plt ;; scheme. This predicate guarantees read-byte will not block. (define (port-ready? timeout port) (sync/timeout timeout port)) ;; Read a byte from port with timeout in seconds. (define (read-byte-timeout port timeout) (let again () (if (port-ready? timeout port) (read-byte port) (begin (error 'time-out "~a" timeout) )))) ;; Darcs-friendly saving of data file. Using the pretty printer ;; seems to be friendly enough. (define (write-tree . args) (apply pretty-print args)) ;; Lookup a file in a search path. (define (file-in-path path filename) (let next ((p path)) (if (null? p) (error 'file-not-in-path "~a ~a" filename path) (let ((full (format "~a/~a" (car p) filename))) (if (file-exists? full) full (next (cdr p))))))) ;; Remove a file if it exists. (define (delete-if-exists file) (if (file-exists? file) (delete-file file))) ;; Save to a file, but do it safely. (define (with-output-to-file/safe file thunk) (let ((file.bak (string-append file "~")) (file.tmp (string-append file ".bak"))) ;; In case thunk fails, first write to a temp file. (delete-if-exists file.tmp) (let ((value (with-output-to-file file.tmp thunk))) ;; Cycle backups. (delete-if-exists file.bak) (if (file-exists? file) (rename-file-or-directory file file.bak)) (rename-file-or-directory file.tmp file) value))) ;; TARGET I/O PORT UTILITIES ;; - target I/O port accessed through a parameter ;; - opened lazily ;; - close protected through dynamic wind ;; 'open' is a thunk that provides an opened port. If it's false, ;; the port is opened. ;; Construction (define-struct io-port (in out)) (define (lazy-io-port portspec) ;; (printf "making lazy port ~a\n" portspec) (lambda () (when (null? portspec) (error 'io-not-connected)) (apply open-io-port portspec))) (define (open-io-port name baud) (let-values (((i o) (open-input-output-file name 'append))) (file-stream-buffer-mode o 'none) (stty name baud) (make-io-port i o))) (define (stty name baud) (system (format "bin/serial ~a ~a" name baud))) ;; Destruction (define (close-io-port io) (close-input-port (io-port-in io)) (close-output-port (io-port-out io))) ;; Port access valid in 'with-lazy-connect' context. (define (i-port) (io-port-in (lazy-connection))) (define (o-port) (io-port-out (lazy-connection))) (define (with-io-device portspec thunk) ;; (printf "with-io-device ~a\n" portspec) (with-lazy-connect (lazy-io-port portspec) thunk close-io-port)) ;; Shortcut access. (define (in) (read-byte-timeout (i-port) 3)) ;; FIXME: make this configurable (define (out byte) (write-byte byte (o-port))) )