#lang racket
(require srfi/13)
(provide name-as-string filename-main filename-suffix get-file-or-directory-name make-unique-name move-directory-to/renaming file=?
copy-directories/renaming file-is-visible? parent-directory make-unique-path path-equal? count-lines compose-name
rename-or-copy-file rename-or-copy-directory rename-or-copy-file-or-directory file-equal?)
(define (get-file-or-directory-name file-or-folder)
(path->string (car (reverse (explode-path file-or-folder)))))
(define (split-name name)
(regexp-split #rx"\\." name))
(define (name-as-string name-or-path)
(if (string? name-or-path)
name-or-path
(get-file-or-directory-name name-or-path)))
(define (filename-main name-or-path)
(let* ((name (name-as-string name-or-path))
(suffix (filename-suffix name)))
(if (string=? "" suffix)
name
(string-drop-right name (add1 (string-length suffix))))))
(define (filename-suffix name-or-path)
(let* ((name (name-as-string name-or-path))
(li (split-name name)))
(if (> (length li) 1)
(car (reverse (split-name name)))
"")))
(define (filename-split-number name)
(define parts (regexp-split #rx"-" name))
(if (or (= (length parts) 1) (not (string->number (last parts))))
(values name #f)
(values (string-drop-right name (add1 (string-length (last parts))))
(string->number (last parts)))))
(define (compose-name name n suffix)
(cond
((and (not n) (string=? suffix "")) name)
((not n) (string-append name "." suffix))
((string=? suffix "") (string-append name "-" (number->string n)))
(else (string-append name "-" (number->string n) "." suffix))))
(define (make-unique-name name folder)
(define path (if folder (build-path folder name) name))
(define suffix (filename-suffix name))
(define-values (main-part num) (filename-split-number (filename-main name)))
(if (or (file-exists? path)
(directory-exists? path))
(if num
(make-unique-name
(compose-name main-part (add1 num) suffix)
folder)
(make-unique-name
(compose-name (filename-main name)
1
(filename-suffix name))
folder))
name))
(define (parent-directory path)
(let ((parts (explode-path (simple-form-path path))))
(if (> (length parts) 1)
(apply build-path (reverse (cdr (reverse parts))))
#f)))
(define (make-unique-path suggested-path)
(let ((parent-dir (parent-directory suggested-path)))
(build-path parent-dir
(make-unique-name
(get-file-or-directory-name suggested-path)
parent-dir))))
(define (path-equal? p1 p2)
(equal? (normal-case-path (simple-form-path p1))
(normal-case-path (simple-form-path p1))))
(define (file=? file1 file2)
(if (or (not (file-exists? file1))
(not (file-exists? file2)))
#f
(let ((p1 (open-input-file file1))
(p2 (open-input-file file2)))
(begin0
(= (port-file-identity p1)
(port-file-identity p2))
(close-input-port p1)
(close-input-port p2)))))
(define (copy-directories/renaming source-directory dest-directory pred?)
(for-each
(lambda (folder)
(when (and (directory-exists? folder) (pred? folder))
(copy-directory/files
(build-path source-directory
(get-file-or-directory-name folder))
(build-path dest-directory
(make-unique-name
(get-file-or-directory-name folder)
dest-directory)))))
(directory-list source-directory)))
(define (move-directory-to/renaming source-folder destination-folder)
(define new-path (build-path
destination-folder
(make-unique-name
(get-file-or-directory-name source-folder)
destination-folder)))
(rename-file-or-directory source-folder new-path)
new-path)
(define (file-is-visible? file)
(not (string=? "." (substring (get-file-or-directory-name file) 0 1))))
(define (count-lines (port (current-input-port)))
(let loop ((i 0)
(line (read-line port)))
(if (eof-object? line)
i
(loop (add1 i) (read-line port)))))
(define (rename-or-copy-file source target)
(with-handlers ([exn:fail:filesystem?
(lambda (exn)
(copy-file source target)
(delete-file source))])
(rename-file-or-directory source target)))
(define (rename-or-copy-directory source target)
(with-handlers ([exn:fail:filesystem?
(lambda (exn)
(copy-directory/files source target)
(delete-directory/files source))])
(rename-file-or-directory source target)))
(define (rename-or-copy-file-or-directory source target)
(if (directory-exists? source)
(rename-or-copy-directory source target)
(rename-or-copy-file source target)))
(define (file-equal? file1 file2)
(if (not (= (file-size file1) (file-size file2)))
#f
(if (file=? file1 file2)
#t
(let* ((port1 (open-input-file file1 #:mode 'binary))
(port2 (open-input-file file2 #:mode 'binary))
(result (let loop ((b1 (read-byte port1))
(b2 (read-byte port2)))
(cond
((eof-object? b1) #t)
((not (= b1 b2)) #f)
(else (loop (read-byte port1) (read-byte port2)))))))
(close-input-port port1)
(close-input-port port2)
result))))