(module file mzscheme (require (lib "contract.ss")) (require (lib "etc.ss")) (require (lib "file.ss")) (require (lib "list.ss" "srfi" "1")) ;; A relative-path is a path for which relative-path? is #t. ;; relative-path/c : contract (define relative-path/c (and/c (union path? string?) relative-path?)) ;; explode-relative-path : relative-path -> (listof path) ;; computes the list of directories in a relative path (define (explode-relative-path path) (let loop ([path path] [rest '()]) (let-values ([(base name dir?) (split-path path)]) (cond [(eq? base 'relative) (cons name rest)] [(or (not (path? name)) (not path)) (raise-type-error 'explode-relative-path "relative-path in normal form" path)] [else (loop base (cons name rest))])))) ;; TODO: make this work with complete paths, too? ;; TODO: is the directory-exists? test appropriate here? I'm thinking no... ;; collect-subdirectories : relative-path -> (listof relative-path) (define (collect-subdirectories path) (fold (lambda (dir dirs) (let ([fst (if (null? dirs) dir (build-path (car dirs) dir))]) (if (directory-exists? fst) (cons fst dirs) dirs))) null (explode-relative-path path))) ;; path=? : path path ... -> boolean ;; determines whether two paths refer to the same normalized path (define (path=? path1 path2 . paths) (apply bytes=? (map (compose path->bytes normalize-path) (cons path1 (cons path2 paths))))) ;; TODO: can we make these contracts a little more precise? -- namely, the ;; relative-paths must be in normal form (no ../ or ./, for example) (provide/contract [relative-path/c contract?] [explode-relative-path (relative-path/c . -> . (listof path?))] [collect-subdirectories (relative-path/c . -> . (listof relative-path/c))] [path=? ((path? path?) (listof path?) . ->* . (boolean?))]) )