~ chicken-core (chicken-5) /pathname.scm
Trap1;;;; pathname.scm - Pathname operations2;3; Copyright (c) 2008-2022, The CHICKEN Team4; Copyright (c) 2000-2007, Felix L. Winkelmann5; All rights reserved.6;7; Redistribution and use in source and binary forms, with or without8; modification, are permitted provided that the following conditions9; are met:10;11; Redistributions of source code must retain the above copyright12; notice, this list of conditions and the following disclaimer.13;14; Redistributions in binary form must reproduce the above copyright15; notice, this list of conditions and the following disclaimer in16; the documentation and/or other materials provided with the17; distribution.18;19; Neither the name of the author nor the names of its contributors20; may be used to endorse or promote products derived from this21; software without specific prior written permission.22;23; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS24; "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT25; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS26; FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE27; COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,28; INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES29; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR30; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)31; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,32; STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)33; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED34; OF THE POSSIBILITY OF SUCH DAMAGE.3536(declare37 (unit pathname)38 (uses data-structures irregex)39 (fixnum)40 (disable-interrupts))4142(module chicken.pathname43 (absolute-pathname? decompose-directory decompose-pathname44 directory-null? make-absolute-pathname make-pathname45 normalize-pathname pathname-directory pathname-extension46 pathname-file pathname-replace-directory pathname-replace-extension47 pathname-replace-file pathname-strip-directory48 pathname-strip-extension)4950(import scheme51 chicken.base52 chicken.fixnum53 chicken.irregex54 chicken.platform55 chicken.string)5657(include "common-declarations.scm")5859;;; Pathname operations:6061;; Platform specific absolute pathname operations:62;; absolute-pathname-root => #f or (<match> [<origin>] <root>)63;;64;; Not for general consumption6566(define absolute-pathname-root)67(define root-origin)68(define root-directory)6970(if ##sys#windows-platform71 (let ((rx (irregex "([A-Za-z]:)?([\\/\\\\]).*")))72 (set! absolute-pathname-root (lambda (pn) (irregex-match rx pn)))73 (set! root-origin (lambda (rt) (and rt (irregex-match-substring rt 1))))74 (set! root-directory (lambda (rt) (and rt (irregex-match-substring rt 2)))))75 (let ((rx (irregex "(/).*")))76 (set! absolute-pathname-root (lambda (pn) (irregex-match rx pn)))77 (set! root-origin (lambda (rt) #f))78 (set! root-directory (lambda (rt) (and rt (irregex-match-substring rt 1))))))7980(define (absolute-pathname? pn)81 (##sys#check-string pn 'absolute-pathname?)82 (irregex-match-data? (absolute-pathname-root pn)))8384(define-inline (*char-pds? ch)85 (if ##sys#windows-platform86 (memq ch '(#\\ #\/))87 (eq? #\/ ch)))8889(define (chop-pds str)90 (and str91 (let lp ((len (##sys#size str)))92 (cond ((and (fx>= len 1)93 (*char-pds? (##core#inline "C_subchar" str (fx- len 1))))94 (lp (fx- len 1)))95 ((fx< len (##sys#size str))96 (##sys#substring str 0 len))97 (else str)))))9899(define make-pathname)100(define make-absolute-pathname)101102(let ((pds (if ##sys#windows-platform "\\" "/")))103104 (define (conc-dirs dirs)105 (##sys#check-list dirs 'make-pathname)106 (let loop ((strs dirs))107 (if (null? strs)108 ""109 (let ((s1 (car strs)))110 (if (zero? (string-length s1))111 (loop (cdr strs))112 (string-append113 (chop-pds (car strs))114 pds115 (loop (cdr strs))))))))116117 (define (canonicalize-dirs dirs)118 (cond ((or (not dirs) (null? dirs)) "")119 ((string? dirs) (conc-dirs (list dirs)))120 (else (conc-dirs dirs))))121122 (define (_make-pathname loc dir file ext)123 (let ((ext (or ext ""))124 (file (or file "")))125 (##sys#check-string dir loc)126 (##sys#check-string file loc)127 (##sys#check-string ext loc)128 (string-append129 dir130 (if (and (fx>= (##sys#size dir) 1)131 (fx>= (##sys#size file) 1)132 (*char-pds? (##core#inline "C_subchar" file 0)))133 (##sys#substring file 1 (##sys#size file))134 file)135 (if (and (fx> (##sys#size ext) 0)136 (not (char=? (##core#inline "C_subchar" ext 0) #\.)))137 "."138 "")139 ext)))140141 (set! make-pathname142 (lambda (dirs file #!optional ext)143 (_make-pathname 'make-pathname (canonicalize-dirs dirs) file ext)))144145 (set! make-absolute-pathname146 (lambda (dirs file #!optional ext)147 (_make-pathname148 'make-absolute-pathname149 (let ((dir (canonicalize-dirs dirs)))150 (if (absolute-pathname? dir)151 dir152 (##sys#string-append pds dir)))153 file ext))))154155(define decompose-pathname156 (let* ((patt1 (if ##sys#windows-platform157 "^(.*[\\/\\\\])?([^\\/\\\\]+)(\\.([^\\/\\\\.]+))$"158 "^(.*/)?([^/]+)(\\.([^/.]+))$"))159 (patt2 (if ##sys#windows-platform160 "^(.*[\\/\\\\])?((\\.)?[^\\/\\\\]+)$"161 "^(.*/)?((\\.)?[^/]+)$"))162 (rx1 (irregex patt1))163 (rx2 (irregex patt2))164 (strip-pds165 (lambda (dir)166 (and dir167 (let ((chopped (chop-pds dir)))168 (if (fx> (##sys#size chopped) 0)169 chopped170 (##sys#substring dir 0 1)))))))171 (lambda (pn)172 (##sys#check-string pn 'decompose-pathname)173 (if (fx= 0 (##sys#size pn))174 (values #f #f #f)175 (let ((ms (irregex-search rx1 pn)))176 (if ms177 (values178 (strip-pds (irregex-match-substring ms 1))179 (irregex-match-substring ms 2)180 (irregex-match-substring ms 4))181 (let ((ms (irregex-search rx2 pn)))182 (if ms183 (values184 (strip-pds (irregex-match-substring ms 1))185 (irregex-match-substring ms 2)186 #f)187 (values (strip-pds pn) #f #f)))))))))188189(define pathname-directory190 (lambda (pn)191 (let-values (((dir file ext) (decompose-pathname pn)))192 dir)))193194(define pathname-file195 (lambda (pn)196 (let-values (((dir file ext) (decompose-pathname pn)))197 file)))198199(define pathname-extension200 (lambda (pn)201 (let-values (((dir file ext) (decompose-pathname pn)))202 ext)))203204(define pathname-strip-directory205 (lambda (pn)206 (let-values (((dir file ext) (decompose-pathname pn)))207 (make-pathname #f file ext))))208209(define pathname-strip-extension210 (lambda (pn)211 (let-values (((dir file ext) (decompose-pathname pn)))212 (make-pathname dir file))))213214(define pathname-replace-directory215 (lambda (pn dir)216 (let-values (((_ file ext) (decompose-pathname pn)))217 (make-pathname dir file ext))))218219(define pathname-replace-file220 (lambda (pn file)221 (let-values (((dir _ ext) (decompose-pathname pn)))222 (make-pathname dir file ext))))223224(define pathname-replace-extension225 (lambda (pn ext)226 (let-values (((dir file _) (decompose-pathname pn)))227 (make-pathname dir file ext))))228229;;; normalize pathname for a particular platform230231(define normalize-pathname232 (let ((bldplt (if (eq? (software-version) 'mingw32) 'windows 'unix)))233 (define (addpart part parts)234 (cond ((string=? "." part) parts)235 ((string=? ".." part)236 (if (or (null? parts)237 (string=? ".." (car parts)))238 (cons part parts)239 (cdr parts)))240 (else (cons part parts))))241 (lambda (path #!optional (platform bldplt))242 (let ((sep (if (eq? platform 'windows) #\\ #\/)))243 (define (pds? c)244 (if (eq? platform 'windows)245 (memq c '(#\/ #\\))246 (eq? c #\/)))247 (##sys#check-string path 'normalize-pathname)248 (let ((len (##sys#size path))249 (type #f)250 (drive #f))251 (let loop ((i 0) (prev 0) (parts '()))252 (cond ((fx>= i len)253 (when (fx> i prev)254 (set! parts (addpart (##sys#substring path prev i) parts)))255 (if (null? parts)256 (let ((r (if (eq? type 'abs) (string sep) ".")))257 (if drive (##sys#string-append drive r) r))258 (let ((out (open-output-string))259 (parts (##sys#fast-reverse parts)))260 (display (car parts) out)261 (for-each262 (lambda (p)263 (##sys#write-char-0 sep out)264 (display p out))265 (cdr parts))266 (when (fx= i prev) (##sys#write-char-0 sep out))267 (let ((r (get-output-string out)))268 (when (eq? type 'abs)269 (set! r (##sys#string-append (string sep) r)))270 (when drive271 (set! r (##sys#string-append drive r)))272 r))))273 ((pds? (string-ref path i))274 (when (not type)275 (set! type (if (fx= i prev) 'abs 'rel)))276 (if (fx= i prev)277 (loop (fx+ i 1) (fx+ i 1) parts)278 (loop (fx+ i 1)279 (fx+ i 1)280 (addpart (##sys#substring path prev i) parts))))281 ((and (null? parts)282 (char=? (string-ref path i) #\:)283 (eq? platform 'windows))284 (set! drive (##sys#substring path 0 (fx+ i 1)))285 (loop (fx+ i 1) (fx+ i 1) '()))286 (else (loop (fx+ i 1) prev parts)))))))))287288;; directory pathname => list of strings289;; does arg check290291(define split-directory292 (lambda (loc dir keep?)293 (##sys#check-string dir loc)294 (string-split dir (if ##sys#windows-platform "/\\" "/") keep?)))295296;; Directory string or list only contains path-separators297;; and/or current-directory (".") names.298299(define (directory-null? dir)300 (let loop ((ls (if (list? dir) dir (split-directory 'directory-null? dir #t))))301 (or (null? ls)302 (and (member (car ls) '("" "."))303 (loop (cdr ls))))))304305;; Directory string => {<origin> <root> <directory-list>}306;; where any maybe #f when missing307308(define (decompose-directory dir)309 (define (strip-origin-prefix org decomp)310 #;(assert (or (not org) decomp)) ;cannot have an "origin" but no "decomp"311 (if (not org)312 decomp313 (let ((1st (car decomp)))314 (let ((olen (##sys#size org)))315 (if (not (##core#inline "C_substring_compare" org 1st 0 0 olen))316 ; then origin is not a prefix (really shouldn't happen)317 decomp318 ; else is a prefix319 (let ((rst (cdr decomp))320 (elen (##sys#size 1st)))321 (if (fx= olen elen)322 ; then origin is a list prefix323 rst324 ; else origin is a string prefix325 (cons (##sys#substring 1st olen elen) rst))))))))326 (let* ((ls (split-directory 'decompose-directory dir #f))327 (rt (absolute-pathname-root dir))328 (org (root-origin rt)))329 (values org (root-directory rt) (strip-origin-prefix org (and (not (null? ls)) ls))))))