(library (temp-file) (export working-directory with-dir-thunk with-dir with-temp-file-thunk with-temp-file-containing-thunk with-temp-file-sized-thunk with-temp-file with-temp-file-containing with-temp-file-sized disable-unlink) (import (chezscheme) (disk-units) (fmt fmt) (srfi s8 receive) (only (srfi s1 lists) span)) ;; FIXME: global var! Not thread safe. (define working-dir ".") (define (working-directory) working-dir) (define (mkdir-p) (system (string-append "mkdir -p " working-dir))) (define (with-dir-thunk path thunk) (fluid-let ((working-dir (string-append working-dir "/" path))) (mkdir-p) (thunk))) (define-syntax with-dir (syntax-rules () ((_ path b1 b2 ...) (with-dir-thunk path (lambda () b1 b2 ...))))) (define (with-temp-dir-thunk path thunk) (with-dir-thunk path (lambda () (auto-unlink-file path (thunk))))) (define temp-filename (lambda (filename) (let ((counter 0)) (let loop () (let ((path (fmt #f (cat (dsp working-dir) (dsp "/") (pad-char #\0 (pad/left 4 (num counter))) (dsp "-") (dsp filename))))) (set! counter (+ counter 1)) (if (file-exists? path) (loop) path)))))) ;; fn takes the path (define (with-temp-file-thunk filename fn) (let ((path (temp-filename filename))) (auto-unlink-file path (lambda () (fn path))))) (define-syntax with-temp-file (syntax-rules () ((_ ((v f)) b1 b2 ...) (with-temp-file-thunk f (lambda (v) b1 b2 ...))) ((_ ((v1 f1) v2 ...) b1 b2 ...) (with-temp-file-thunk f1 (lambda (v1) (with-temp-file (v2 ...) b1 b2 ...)))))) ;; Creates a temporary file with the specified contents. (define (with-temp-file-containing-thunk filename contents fn) (with-temp-file-thunk filename (lambda (path) (with-output-to-file path (lambda () (put-string (current-output-port) contents))) (fn path)))) (define-syntax with-temp-file-containing (syntax-rules () ((_ ((v f txt)) b1 b2 ...) (with-temp-file-containing-thunk f txt (lambda (v) b1 b2 ...))) ((_ ((v f txt) rest ...) b1 b2 ...) (with-temp-file-containing-thunk f txt (lambda (v) (with-temp-file-containing (rest ...) b1 b2 ...)))))) (define (safe-to-bytes maybe-size) (if (disk-size? maybe-size) (to-bytes maybe-size) maybe-size)) (define (suitable-block-size size) (let loop ((bs (* 1024 1024 4))) (if (> (mod size bs) 0) (loop (/ bs 2)) bs))) ;; It's much faster if we write large blocks (define (dd-zero-file path size) (let* ((bytes (safe-to-bytes size)) (bs (suitable-block-size bytes)) (count (floor (/ bytes bs)))) (system (fmt #f "dd if=/dev/zero of=" path " bs=" (wrt bs) " count=" (wrt count) " 2> /dev/null > /dev/null")))) (define (with-temp-file-sized-thunk filename size fn) (with-temp-file-thunk filename (lambda (path) (dd-zero-file path size) (fn path)))) (define-syntax with-temp-file-sized (syntax-rules () ((_ ((v f size)) b1 b2 ...) (with-temp-file-sized-thunk f size (lambda (v) b1 b2 ...))) ((_ ((v f size) rest ...) b1 b2 ...) (with-temp-file-sized-thunk f size (lambda (v) (with-temp-file-sized (rest ...) b1 b2 ...)))))) ;;------------------------- (define should-unlink #t) (define (disable-unlink-thunk fn) (fluid-let ((should-unlink #f)) (fn))) (define-syntax disable-unlink (syntax-rules () ((_ b1 b2 ...) (disable-unlink-thunk (lambda () b1 b2 ...))))) ;; FIXME: use 'run' so we get logging (define (unlink-file path) (when should-unlink (system (string-append "rm -f " path)))) (define (unlink-dir path) (when should-unlink (system (string-append "rmdir -f " path)))) (define (auto-unlink-file path thunk) (dynamic-wind (lambda () #t) thunk (lambda () (unlink-file path)))) (define (auto-unlink-dir path thunk) (dynamic-wind (lambda () #t) thunk (lambda () (unlink-dir path)))) )