#lang scheme/base
(provide make-mfile
open-output-mfile
open-input-mfile
with-output-to-mfile
mfile-size
mfile->bytes
bytes->mfile
mfile->string/utf-8
string->mfile/utf-8
make-mdir
mdir-refs
with-mdir
mdir-sorted
)
(require "file.ss"
scheme/foreign
scheme/file)
(define *id* 0)
(define *id-semaphore* (make-semaphore 1))
(define (make-id)
(semaphore-wait *id-semaphore*)
(let ((id *id*))
(set! *id* (add1 *id*))
(semaphore-post *id-semaphore*)
id))
(define (make-temp-path)
(build-path (temp-dir) (format "~s" (make-id))))
(define *temp-dir*
(let ((TMPDIR (getenv "TMPDIR")))
(if TMPDIR
(let ((dir (string->path TMPDIR)))
(printf "mfile directory: ~a\n" TMPDIR)
dir)
(make-temporary-file "mz~a" 'directory))))
(define (temp-dir) *temp-dir*)
(define-struct mfile-meta (path))
(define (delete path)
(define (warning _)
(printf "WARNING: can't delete ~s\n" path))
(with-handlers ((void warning))
(delete-file path)))
(define (make-registered-mfile path)
(define (cleanup tf) (delete (mfile-meta-path tf)))
(define tf (make-mfile-meta path))
(register-finalizer tf cleanup)
tf)
(define (make-mfile [external-path #f])
(let* ((path (make-temp-path))
(tf (make-registered-mfile path)))
(when external-path
(unless (file-exists? external-path)
(error 'mfile-file-not-found "~s" external-path))
(make-file-or-directory-link
(path->complete-path external-path) path))
tf))
(define (make-mfile-rename [original #f])
(let ((path (make-temp-path)))
(when original
(rename-file-or-directory original path))
(make-registered-mfile path)))
(define (open-input-mfile tf
#:mode (m 'binary))
(open-input-file (mfile-meta-path tf) #:mode m))
(define (open-output-mfile tf
#:mode (m 'binary)
#:exists (e 'error))
(let ((path (mfile-meta-path tf)))
(open-output-file path #:mode m #:exists e)))
(define (with-mdir bindings thunk)
(define tempdir (make-temp-path))
(define output (make-hash))
(define (file filename) (build-path tempdir filename))
(unless (hash? bindings)
(set! bindings (make-immutable-hash bindings)))
(dynamic-wind
(lambda ()
(make-directory tempdir)
(for (((name tf) bindings))
(make-file-or-directory-link
(mfile-meta-path tf) (file name))))
(lambda ()
(parameterize ((current-directory tempdir))
(thunk) output))
(lambda ()
(for (((filename _) bindings))
(delete (file filename)))
(for ((name (directory-list tempdir)))
(hash-set! output name
(make-mfile-rename
(file name))))
(delete-directory tempdir))))
(define (mdir-refs/list mdir . refs)
(for/list ((r refs)) (hash-ref mdir (build-path r) (lambda _ #f))))
(define (mdir-refs . args)
(apply values (apply mdir-refs/list args)))
(define make-mdir make-hash)
(define with-output-to-mfile
(with-file open-output-mfile
current-output-port
close-output-port
make-mfile))
(define with-input-from-mfile
(with-file open-input-mfile
current-input-port
close-input-port
make-mfile))
(define (mfile-size mfile)
(file-size (mfile-meta-path mfile)))
(define (bytes->mfile bytes)
(let ((mfile (make-mfile)))
(with-output-to-mfile mfile (lambda () (write-bytes bytes)))
mfile))
(define (mfile->bytes mfile)
(with-input-from-mfile
mfile
(lambda () (read-bytes (mfile-size mfile)))))
(define (mfile->string/utf-8 mfile)
(bytes->string/utf-8 (mfile->bytes mfile)))
(define (string->mfile/utf-8 str)
(bytes->mfile (string->bytes/utf-8 str)))
(define (mdir-sorted mdir)
(map cdr
(sort
(for/list (((name mf) mdir))
(cons (path->string name) mf))
string-ci<? #:key car)))