;;; 6.ss ;;; Copyright 1984-2017 Cisco Systems, Inc. ;;; ;;; Licensed under the Apache License, Version 2.0 (the "License"); ;;; you may not use this file except in compliance with the License. ;;; You may obtain a copy of the License at ;;; ;;; http://www.apache.org/licenses/LICENSE-2.0 ;;; ;;; Unless required by applicable law or agreed to in writing, software ;;; distributed under the License is distributed on an "AS IS" BASIS, ;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ;;; See the License for the specific language governing permissions and ;;; limitations under the License. (begin (define with-output-to-string (lambda (th) (unless (procedure? th) ($oops 'with-output-to-string "~s is not a procedure" th)) (parameterize ([current-output-port (open-output-string)]) (th) (get-output-string (current-output-port))))) (define with-input-from-string (lambda (s th) (unless (string? s) ($oops 'with-input-from-string "~s is not a string" s)) (unless (procedure? th) ($oops 'with-input-from-string "~s is not a procedure" th)) (let ([p (open-input-string s)]) (call-with-values (lambda () (parameterize ([current-input-port p]) (th))) (lambda v (apply values v)))))) (let () (define getwd (if (foreign-entry? "(cs)s_getwd") (foreign-procedure "(cs)s_getwd" () string) (lambda () (let ([p (process "exec /bin/pwd")]) (let ([ip (car p)] [op (open-output-string)]) (let f () (let ([c (read-char ip)]) (if (or (eof-object? c) (char=? c #\newline)) (begin (close-output-port (cadr p)) (close-input-port ip) (get-output-string op)) (begin (write-char c op) (f)))))))))) (define chdir (foreign-procedure "(cs)s_chdir" (string) integer-32)) (define $cd (case-lambda [(who) (or (getwd) ($oops who "cannot determine current directory"))] [(dir who) (unless (string? dir) ($oops who "~s is not a string" dir)) (unless (= (chdir dir) 0) ($oops who "cannot set current directory to ~s" dir))])) (set-who! current-directory (case-lambda [() ($cd who)] [(dir) ($cd dir who)])) (set-who! cd (case-lambda [() ($cd who)] [(dir) ($cd dir who)]))) (let () (define who 'mkdir) (define fp (foreign-procedure "(cs)mkdir" (string uptr) ptr)) (define (do-mkdir path mode) (unless (string? path) ($oops who "~s is not a string" path)) (unless (fixnum? mode) ($oops who "~s is not a fixnum" mode)) (let ([x (fp path mode)]) (cond [(eqv? x #t) (void)] [(string? x) ($oops/c who (make-i/o-filename-error path) "cannot create ~s: ~(~a~)" path x)] [else ($oops/c who (make-i/o-filename-error path) "cannot create ~s" path)]))) (set! mkdir (case-lambda [(path) (do-mkdir path #o777)] [(path mode) (do-mkdir path mode)]))) (define-who chmod (let ([fp (foreign-procedure "(cs)chmod" (string fixnum) ptr)]) (lambda (path mode) (unless (string? path) ($oops who "~s is not a string" path)) (unless (fixnum? mode) ($oops who "~s is not a fixnum" mode)) (let ([x (fp path mode)]) (cond [(eqv? x #t) (void)] [(string? x) ($oops/c who (make-i/o-filename-error path) "cannot modify ~s: ~(~a~)" path x)] [else ($oops/c who (make-i/o-filename-error path) "cannot modify ~s" path)]))))) (define-who get-mode (let ([fp (foreign-procedure "(cs)getmod" (string boolean) ptr)]) (rec get-mode (case-lambda [(path) (get-mode path #t)] [(path follow?) (define (err x) (if (string? x) ($oops/c who (make-i/o-filename-error path) "failed for ~s: ~(~a~)" path x) ($oops/c who (make-i/o-filename-error path) "failed for ~s" path))) (unless (string? path) ($oops who "~s is not a string" path)) (let ([x (fp path follow?)]) (if (fixnum? x) x (err x)))])))) (let () (define file-x-time (lambda (who path-fp fd-fp file follow?) (define (path-err path x) (if (string? x) ($oops/c who (make-i/o-filename-error path) "failed for ~s: ~(~a~)" path x) ($oops/c who (make-i/o-filename-error path) "failed for ~s" path))) (unless (or (string? file) (and (port? file) (file-port? file))) ($oops who "~s is not a string or file port" file)) (if (string? file) (let ([x (path-fp file follow?)]) (if (pair? x) (make-time 'time-utc (cdr x) (car x)) (path-err file x))) (let ([x (fd-fp (port-file-descriptor file))]) (cond [(pair? x) (make-time 'time-utc (cdr x) (car x))] [(string? x) ($oops who "failed for ~s: ~(~a~)" file x)] [else ($oops who "failed for ~s" file)]))))) (define-syntax define-file-x-time (syntax-rules () [(_ name path-name fd-name) (set-who! name (let ([path-fp (foreign-procedure path-name (string boolean) ptr)] [fd-fp (foreign-procedure fd-name (fixnum) ptr)]) (case-lambda [(file) (file-x-time who path-fp fd-fp file #t)] [(file follow?) (file-x-time who path-fp fd-fp file follow?)])))])) (define-file-x-time file-access-time "(cs)path_atime" "(cs)fd_atime") (define-file-x-time file-change-time "(cs)path_ctime" "(cs)fd_atime") (define-file-x-time file-modification-time "(cs)path_mtime" "(cs)fd_mtime")) (define directory-separator (lambda () (#2%directory-separator))) (define directory-separator? (lambda (c) (unless (char? c) ($oops 'directory-separator? "~s is not a character" c)) (#3%directory-separator? c))) (define-who directory-list (let ([dl (if-feature windows (let ([wl (foreign-procedure "(cs)find_files" (string) scheme-object)]) (lambda (path) (let ([n (string-length path)]) (unless (and (fx> n 0) (let nostars? ([i 0]) (or (fx= i n) (and (not (char=? (string-ref path i) #\*)) (nostars? (fx+ i 1)))))) ($oops who "invalid directory name ~s" path)) (wl (if (memv (string-ref path (fx- n 1)) '(#\\ #\/ #\:)) (string-append path "*") (string-append path "\\*")))))) (foreign-procedure "(cs)directory_list" (string) scheme-object))]) (lambda (path) (unless (string? path) ($oops who "~s is not a string" path)) (let ([bv* (dl path)]) (if (string? bv*) ($oops/c who (make-i/o-filename-error path) "failed for ~a: ~(~a~)" path bv*) (remp (lambda (s) (let ([n (string-length s)]) (or (and (fx= n 1) (char=? (string-ref s 0) #\.)) (and (fx= n 2) (char=? (string-ref s 0) #\.) (char=? (string-ref s 1) #\.))))) (map (if-feature windows (lambda (bv) (utf16->string bv 'little #t)) utf8->string) bv*))))))) (define-who file-exists? (let ([fp (foreign-procedure "(cs)file_existsp" (string boolean) boolean)]) (rec file-exists? (case-lambda [(path) (file-exists? path #t)] [(path follow?) (unless (string? path) ($oops who "~s is not a string" path)) (fp path follow?)])))) (define-who #(r6rs: file-exists?) (lambda (path) (#2%file-exists? path #t))) (define-who file-regular? (let ([fp (foreign-procedure "(cs)file_regularp" (string boolean) boolean)]) (rec file-regular? (case-lambda [(path) (file-regular? path #t)] [(path follow?) (unless (string? path) ($oops who "~s is not a string" path)) (fp path follow?)])))) (define-who file-directory? (let ([fp (foreign-procedure "(cs)file_directoryp" (string boolean) boolean)]) (rec file-directory? (case-lambda [(path) (file-directory? path #t)] [(path follow?) (unless (string? path) ($oops who "~s is not a string" path)) (fp path follow?)])))) (define-who file-symbolic-link? (let ([fp (foreign-procedure "(cs)file_symbolic_linkp" (string) boolean)]) (lambda (path) (unless (string? path) ($oops who "~s is not a string" path)) (fp path)))) (let () (define fp-delete-file (foreign-procedure "(cs)delete_file" (string) scheme-object)) (define fp-delete-directory (foreign-procedure "(cs)delete_directory" (string) scheme-object)) (define (do-delete who fp path error?) (unless (string? path) ($oops who "~s is not a string" path)) (let ([x (fp path)]) (if error? (cond [(eqv? x #t) (void)] [(string? x) ($oops/c who (make-i/o-filename-error path) "failed for ~a: ~(~a~)" path x)] [else ($oops/c who (make-i/o-filename-error path) "failed for ~a" path)]) (eq? x #t)))) (set-who! delete-file (case-lambda [(path) (do-delete who fp-delete-file path #f)] [(path error?) (do-delete who fp-delete-file path error?)])) (set-who! #(r6rs: delete-file) ; implicit rec (lambda (path) (do-delete who fp-delete-file path #t))) (set-who! delete-directory (case-lambda [(path) (do-delete who fp-delete-directory path #f)] [(path error?) (do-delete who fp-delete-directory path error?)]))) (let () (define fp (foreign-procedure "(cs)rename_file" (string string) ptr)) (set-who! rename-file (lambda (path1 path2) (unless (string? path1) ($oops who "~s is not a string" path1)) (unless (string? path2) ($oops who "~s is not a string" path2)) (let ([x (fp path1 path2)]) (cond [(eqv? x #t) (void)] [(string? x) ($oops/c who (condition (make-i/o-filename-error path1) (make-i/o-filename-error path2)) "cannot rename ~s to ~s: ~(~a~)" path1 path2 x)] [else ($oops/c who (condition (make-i/o-filename-error path1) (make-i/o-filename-error path2)) "cannot rename ~s to ~s" path1 path2)]))))) ;;; path procedures (let () (define windows? (if-feature windows #t #f)) (define directory-separator-predicate (lambda (s) (if (and windows? (string? s) (let ([n (string-length s)]) (and (fx>= n 4) (char=? (string-ref s 0) #\\) (char=? (string-ref s 1) #\\) (char=? (string-ref s 2) #\?) (char=? (string-ref s 3) #\\)))) (lambda (c) (char=? c #\\)) directory-separator?))) (define path-base (lambda (s n) (cond [(and windows? (fx>= n 2) (char=? (string-ref s 1) #\:) (let ([c (string-ref s 0)]) (or (char<=? #\a c #\z) (char<=? #\A c #\Z)))) (if (and (fx>= n 3) (directory-separator? (string-ref s 2))) 3 2)] [(and windows? (fx>= n 4) (char=? (string-ref s 0) #\\) (char=? (string-ref s 1) #\\) (char=? (string-ref s 2) #\?) (char=? (string-ref s 3) #\\)) (cond [(and (fx>= n 6) (char=? (string-ref s 5) #\:) (let ([c (string-ref s 4)]) (or (char<=? #\a c #\z) (char<=? #\A c #\Z)))) (if (and (fx>= n 7) (char=? (string-ref s 6) #\\)) 7 6)] [(and windows? (fx>= n 8) (char-ci=? (string-ref s 4) #\U) (char-ci=? (string-ref s 5) #\N) (char-ci=? (string-ref s 6) #\C) (char=? (string-ref s 7) #\\)) (let loop ([i (if (and (fx>= n 9) (char=? (string-ref s 8) #\\)) 9 8)]) (if (or (fx= i n) (char=? (string-ref s i) #\\)) i (loop (fx+ i 1))))] [else 4])] [(and windows? (fx>= n 2) (directory-separator? (string-ref s 0)) (directory-separator? (string-ref s 1))) (let loop ([i 2]) (if (or (fx= i n) (directory-separator? (string-ref s i))) i (loop (fx+ i 1))))] [(and (fx>= n 1) (directory-separator? (string-ref s 0))) 1] [(and (fx>= n 1) (char=? (string-ref s 0) #\.)) (if (or (fx= n 1) (directory-separator? (string-ref s 1))) 1 (if (and (char=? (string-ref s 1) #\.) (or (fx= n 2) (directory-separator? (string-ref s 2)))) 2 0))] [(and (fx>= n 1) (char=? (string-ref s 0) #\~)) (if (or (fx= n 1) (directory-separator? (string-ref s 1))) 1 (let loop ([i 2]) (if (or (fx= i n) (directory-separator? (string-ref s i))) i (loop (fx+ i 1)))))] [else 0]))) (set-who! path-absolute? (lambda (s) (unless (string? s) ($oops who "~s is not a string" s)) (let ([n (string-length s)]) (or (and (fx>= n 1) (directory-separator? (string-ref s 0))) (and (fx>= n 1) (char=? (string-ref s 0) #\~)) (and windows? (fx>= n 3) (char=? (string-ref s 1) #\:) (let ([c (string-ref s 0)]) (or (char<=? #\a c #\z) (char<=? #\A c #\Z))) (directory-separator? (string-ref s 2))))))) (set-who! path-extension (lambda (s) (define directory-separator? (directory-separator-predicate s)) (unless (string? s) ($oops who "~s is not a string" s)) (let* ([n (string-length s)] [base (path-base s n)]) (let loop ([i n]) (let ([i (fx- i 1)]) (if (or (fx< i base) (directory-separator? (string-ref s i))) "" (if (char=? (string-ref s i) #\.) (if (and (fx= i (fx- n 1)) (or (fx= i base) (directory-separator? (string-ref s (fx- i 1))) (and (char=? (string-ref s (fx- i 1)) #\.) (or (fx= (fx- i 1) base) (directory-separator? (string-ref s (fx- i 2))))))) "" (substring s (fx+ i 1) n)) (loop i)))))))) (set-who! path-root (lambda (s) (define directory-separator? (directory-separator-predicate s)) (unless (string? s) ($oops who "~s is not a string" s)) (let* ([n (string-length s)] [base (path-base s n)]) (let loop ([i n]) (let ([i (fx- i 1)]) (if (or (fx< i base) (directory-separator? (string-ref s i))) s (if (char=? (string-ref s i) #\.) (if (and (fx= i (fx- n 1)) (or (fx= i base) (directory-separator? (string-ref s (fx- i 1))) (and (char=? (string-ref s (fx- i 1)) #\.) (or (fx= (fx- i 1) base) (directory-separator? (string-ref s (fx- i 2))))))) s (substring s 0 i)) (loop i)))))))) (set-who! path-last (lambda (s) (define directory-separator? (directory-separator-predicate s)) (unless (string? s) ($oops who "~s is not a string" s)) (let* ([n (string-length s)] [base (path-base s n)]) (let loop ([i n]) (cond [(fx= i base) (if (fx= base 0) s (substring s base n))] [(directory-separator? (string-ref s (fx- i 1))) (substring s i n)] [else (loop (fx- i 1))]))))) (set-who! path-parent (lambda (s) (define directory-separator? (directory-separator-predicate s)) (define (skip-sep-backward s i base) (let ([i (fx- i 1)]) (if (or (fx= i base) (not (directory-separator? (string-ref s (fx- i 1))))) i (skip-sep-backward s i base)))) (unless (string? s) ($oops who "~s is not a string" s)) (let* ([n (string-length s)] [base (path-base s n)]) (let loop ([i n]) (cond [(fx= i base) (substring s 0 base)] [(directory-separator? (string-ref s (fx- i 1))) (substring s 0 (skip-sep-backward s i base))] [else (loop (fx- i 1))]))))) (set-who! path-first (lambda (s) (define directory-separator? (directory-separator-predicate s)) (unless (string? s) ($oops who "~s is not a string" s)) (let* ([n (string-length s)] [base (path-base s n)]) (if (fx= base 0) (let loop ([i 0]) (cond [(fx= i n) ""] [(directory-separator? (string-ref s i)) (substring s 0 i)] [else (loop (fx+ i 1))])) (if (fx= base n) s (substring s 0 base)))))) (set-who! path-rest (lambda (s) (define directory-separator? (directory-separator-predicate s)) (define (skip-sep s i n) (if (or (fx= i n) (not (directory-separator? (string-ref s i)))) i (skip-sep s (fx+ i 1) n))) (unless (string? s) ($oops who "~s is not a string" s)) (let* ([n (string-length s)] [base (path-base s n)]) (if (fx= base 0) (let loop ([i 0]) (cond [(fx= i n) s] [(directory-separator? (string-ref s i)) (substring s (skip-sep s (fx+ i 1) n) n)] [else (loop (fx+ i 1))])) (substring s (skip-sep s base n) n))))) ) )