📄 ftw.scm
字号:
;; FILENAME parameter to `nftw'.;;;; chdir';; If this flag is given the current working directory is;; changed to the directory containing the reported object;; before the callback procedure is called.;;;; depth';; If this option is given the procedure visits first all files;; and subdirectories before the callback procedure is called;; for the directory itself (depth-first processing). This;; also means the type flag given to the callback procedure is;; `directory-processed' and not `directory'.;;;; The return value is computed in the same way as for `ftw'.;; `nftw' returns #t if no failure occurred in `nftw' and all;; callback procedure call return values are also #t. For internal;; errors such as memory problems the error `ftw-error' is thrown.;; If the return value of a callback invocation is not #t this;; very same value is returned.;;; Code:(define-module (ice-9 ftw) :export (ftw nftw))(define (directory-files dir) (let ((dir-stream (opendir dir))) (let loop ((new (readdir dir-stream)) (acc '())) (if (eof-object? new) acc (loop (readdir dir-stream) (if (or (string=? "." new) ;;; ignore (string=? ".." new)) ;;; ignore acc (cons new acc)))))))(define (pathify . nodes) (let loop ((nodes nodes) (result "")) (if (null? nodes) (or (and (string=? "" result) "") (substring result 1 (string-length result))) (loop (cdr nodes) (string-append result "/" (car nodes))))))(define (abs? filename) (char=? #\/ (string-ref filename 0)))(define (visited?-proc size) (let ((visited (make-hash-table size))) (lambda (s) (and s (let ((ino (stat:ino s))) (or (hash-ref visited ino) (begin (hash-set! visited ino #t) #f)))))))(define (stat-dir-readable?-proc uid gid) (let ((uid (getuid)) (gid (getgid))) (lambda (s) (let* ((perms (stat:perms s)) (perms-bit-set? (lambda (mask) (not (= 0 (logand mask perms)))))) (or (and (= uid (stat:uid s)) (perms-bit-set? #o400)) (and (= gid (stat:gid s)) (perms-bit-set? #o040)) (perms-bit-set? #o004))))))(define (stat&flag-proc dir-readable? . control-flags) (let* ((directory-flag (if (memq 'depth control-flags) 'directory-processed 'directory)) (stale-symlink-flag (if (memq 'nftw-style control-flags) 'stale-symlink 'symlink)) (physical? (memq 'physical control-flags)) (easy-flag (lambda (s) (let ((type (stat:type s))) (if (eq? 'directory type) (if (dir-readable? s) directory-flag 'directory-not-readable) 'regular))))) (lambda (name) (let ((s (false-if-exception (lstat name)))) (cond ((not s) (values s 'invalid-stat)) ((eq? 'symlink (stat:type s)) (let ((s-follow (false-if-exception (stat name)))) (cond ((not s-follow) (values s stale-symlink-flag)) ((and s-follow physical?) (values s 'symlink)) ((and s-follow (not physical?)) (values s-follow (easy-flag s-follow)))))) (else (values s (easy-flag s))))))))(define (clean name) (let ((last-char-index (1- (string-length name)))) (if (char=? #\/ (string-ref name last-char-index)) (substring name 0 last-char-index) name)))(define (ftw filename proc . options) (let* ((visited? (visited?-proc (cond ((memq 'hash-size options) => cadr) (else 211)))) (stat&flag (stat&flag-proc (stat-dir-readable?-proc (getuid) (getgid))))) (letrec ((go (lambda (fullname) (call-with-values (lambda () (stat&flag fullname)) (lambda (s flag) (or (visited? s) (let ((ret (proc fullname s flag))) ; callback (or (eq? #t ret) (throw 'ftw-early-exit ret)) (and (eq? 'directory flag) (for-each (lambda (child) (go (pathify fullname child))) (directory-files fullname))) #t))))))) (catch 'ftw-early-exit (lambda () (go (clean filename))) (lambda (key val) val)))))(define (nftw filename proc . control-flags) (let* ((od (getcwd)) ; orig dir (odev (let ((s (false-if-exception (lstat filename)))) (if s (stat:dev s) -1))) (same-dev? (if (memq 'mount control-flags) (lambda (s) (= (stat:dev s) odev)) (lambda (s) #t))) (base-sub (lambda (name base) (substring name 0 base))) (maybe-cd (if (memq 'chdir control-flags) (if (abs? filename) (lambda (fullname base) (or (= 0 base) (chdir (base-sub fullname base)))) (lambda (fullname base) (chdir (pathify od (base-sub fullname base))))) (lambda (fullname base) #t))) (maybe-cd-back (if (memq 'chdir control-flags) (lambda () (chdir od)) (lambda () #t))) (depth-first? (memq 'depth control-flags)) (visited? (visited?-proc (cond ((memq 'hash-size control-flags) => cadr) (else 211)))) (has-kids? (if depth-first? (lambda (flag) (eq? flag 'directory-processed)) (lambda (flag) (eq? flag 'directory)))) (stat&flag (apply stat&flag-proc (stat-dir-readable?-proc (getuid) (getgid)) (cons 'nftw-style control-flags)))) (letrec ((go (lambda (fullname base level) (call-with-values (lambda () (stat&flag fullname)) (lambda (s flag) (letrec ((self (lambda () (maybe-cd fullname base) ;; the callback (let ((ret (proc fullname s flag base level))) (maybe-cd-back) (or (eq? #t ret) (throw 'nftw-early-exit ret))))) (kids (lambda () (and (has-kids? flag) (for-each (lambda (child) (go (pathify fullname child) (1+ (string-length fullname)) (1+ level))) (directory-files fullname)))))) (or (visited? s) (not (same-dev? s)) (if depth-first? (begin (kids) (self)) (begin (self) (kids))))))) #t))) (let ((ret (catch 'nftw-early-exit (lambda () (go (clean filename) 0 0)) (lambda (key val) val)))) (chdir od) ret))));;; ftw.scm ends here
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -