~ chicken-core (master) /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)56(import (only (scheme base) open-output-string get-output-string))5758(include "common-declarations.scm")5960;;; Pathname operations:6162;; Platform specific absolute pathname operations:63;; absolute-pathname-root => #f or (<match> [<origin>] <root>)64;;65;; Not for general consumption6667(define absolute-pathname-root)68(define root-origin)69(define root-directory)7071(if ##sys#windows-platform72 (let ((rx (irregex "([A-Za-z]:)?([\\/\\\\]).*")))73 (set! absolute-pathname-root (lambda (pn) (irregex-match rx pn)))74 (set! root-origin (lambda (rt) (and rt (irregex-match-substring rt 1))))75 (set! root-directory (lambda (rt) (and rt (irregex-match-substring rt 2)))))76 (let ((rx (irregex "(/).*")))77 (set! absolute-pathname-root (lambda (pn) (irregex-match rx pn)))78 (set! root-origin (lambda (rt) #f))79 (set! root-directory (lambda (rt) (and rt (irregex-match-substring rt 1))))))8081(define (absolute-pathname? pn)82 (##sys#check-string pn 'absolute-pathname?)83 (irregex-match-data? (absolute-pathname-root pn)))8485(define-inline (*char-pds? ch)86 (eq? #\/ ch))8788(define (chop-pds str)89 (and str90 (let lp ((len (string-length str)))91 (cond ((and (fx>= len 1)92 (*char-pds? (string-ref str (fx- len 1))))93 (lp (fx- len 1)))94 ((fx< len (string-length str))95 (##sys#substring str 0 len))96 (else str)))))9798(define make-pathname)99(define make-absolute-pathname)100101(let ()102103 (define (conc-dirs dirs)104 (##sys#check-list dirs 'make-pathname)105 (let loop ((strs dirs))106 (if (null? strs)107 ""108 (let ((s1 (car strs)))109 (if (zero? (string-length s1))110 (loop (cdr strs))111 (string-append112 (chop-pds (car strs))113 "/"114 (loop (cdr strs))))))))115116 (define (canonicalize-dirs dirs)117 (cond ((or (not dirs) (null? dirs)) "")118 ((string? dirs) (conc-dirs (list dirs)))119 (else (conc-dirs dirs))))120121 (define (_make-pathname loc dir file ext)122 (let ((ext (or ext ""))123 (file (or file "")))124 (##sys#check-string dir loc)125 (##sys#check-string file loc)126 (##sys#check-string ext loc)127 (string-append128 dir129 (if (and (fx>= (string-length dir) 1)130 (fx>= (string-length file) 1)131 (*char-pds? (string-ref file 0)))132 (##sys#substring file 1 (string-length file))133 file)134 (if (and (fx> (string-length ext) 0)135 (not (char=? (string-ref ext 0) #\.)))136 "."137 "")138 ext)))139140 (set! make-pathname141 (lambda (dirs file #!optional ext)142 (_make-pathname 'make-pathname (canonicalize-dirs dirs) file ext)))143144 (set! make-absolute-pathname145 (lambda (dirs file #!optional ext)146 (_make-pathname147 'make-absolute-pathname148 (let ((dir (canonicalize-dirs dirs)))149 (if (absolute-pathname? dir)150 dir151 (##sys#string-append "/" dir)))152 file ext))))153154(define decompose-pathname155 (let* ((patt1 (if ##sys#windows-platform156 "^(.*[\\/\\\\])?([^\\/\\\\]+)(\\.([^\\/\\\\.]+))$"157 "^(.*/)?([^/]+)(\\.([^/.]+))$"))158 (patt2 (if ##sys#windows-platform159 "^(.*[\\/\\\\])?((\\.)?[^\\/\\\\]+)$"160 "^(.*/)?((\\.)?[^/]+)$"))161 (rx1 (irregex patt1))162 (rx2 (irregex patt2))163 (strip-pds164 (lambda (dir)165 (and dir166 (let ((chopped (chop-pds dir)))167 (if (fx> (string-length chopped) 0)168 chopped169 (##sys#substring dir 0 1)))))))170 (lambda (pn)171 (##sys#check-string pn 'decompose-pathname)172 (if (fx= 0 (string-length pn))173 (values #f #f #f)174 (let ((ms (irregex-search rx1 pn)))175 (if ms176 (values177 (strip-pds (irregex-match-substring ms 1))178 (irregex-match-substring ms 2)179 (irregex-match-substring ms 4))180 (let ((ms (irregex-search rx2 pn)))181 (if ms182 (values183 (strip-pds (irregex-match-substring ms 1))184 (irregex-match-substring ms 2)185 #f)186 (values (strip-pds pn) #f #f)))))))))187188(define pathname-directory189 (lambda (pn)190 (let-values (((dir file ext) (decompose-pathname pn)))191 dir)))192193(define pathname-file194 (lambda (pn)195 (let-values (((dir file ext) (decompose-pathname pn)))196 file)))197198(define pathname-extension199 (lambda (pn)200 (let-values (((dir file ext) (decompose-pathname pn)))201 ext)))202203(define pathname-strip-directory204 (lambda (pn)205 (let-values (((dir file ext) (decompose-pathname pn)))206 (make-pathname #f file ext))))207208(define pathname-strip-extension209 (lambda (pn)210 (let-values (((dir file ext) (decompose-pathname pn)))211 (make-pathname dir file))))212213(define pathname-replace-directory214 (lambda (pn dir)215 (let-values (((_ file ext) (decompose-pathname pn)))216 (make-pathname dir file ext))))217218(define pathname-replace-file219 (lambda (pn file)220 (let-values (((dir _ ext) (decompose-pathname pn)))221 (make-pathname dir file ext))))222223(define pathname-replace-extension224 (lambda (pn ext)225 (let-values (((dir file _) (decompose-pathname pn)))226 (make-pathname dir file ext))))227228;;; normalize pathname for a particular platform229230(define normalize-pathname231 (let ((bldplt (if (eq? (software-version) 'mingw) 'windows 'unix)))232 (define (addpart part parts)233 (cond ((string=? "." part) parts)234 ((string=? ".." part)235 (if (or (null? parts)236 (string=? ".." (car parts)))237 (cons part parts)238 (cdr parts)))239 (else (cons part parts))))240 (lambda (path #!optional (platform bldplt))241 (let ((sep #\/))242 (define (pds? c) (eq? c #\/))243 (##sys#check-string path 'normalize-pathname)244 (let ((len (string-length path))245 (type #f)246 (drive #f))247 (let loop ((i 0) (prev 0) (parts '()))248 (cond ((fx>= i len)249 (when (fx> i prev)250 (set! parts (addpart (##sys#substring path prev i) parts)))251 (if (null? parts)252 (let ((r (if (eq? type 'abs) (string sep) ".")))253 (if drive (##sys#string-append drive r) r))254 (let ((out (open-output-string))255 (parts (##sys#fast-reverse parts)))256 (display (car parts) out)257 (for-each258 (lambda (p)259 (##sys#write-char-0 sep out)260 (display p out))261 (cdr parts))262 (when (fx= i prev) (##sys#write-char-0 sep out))263 (let ((r (get-output-string out)))264 (when (eq? type 'abs)265 (set! r (##sys#string-append (string sep) r)))266 (when drive267 (set! r (##sys#string-append drive r)))268 r))))269 ((pds? (string-ref path i))270 (when (not type)271 (set! type (if (fx= i prev) 'abs 'rel)))272 (if (fx= i prev)273 (loop (fx+ i 1) (fx+ i 1) parts)274 (loop (fx+ i 1)275 (fx+ i 1)276 (addpart (##sys#substring path prev i) parts))))277 ((and (null? parts)278 (char=? (string-ref path i) #\:)279 (eq? platform 'windows))280 (set! drive (##sys#substring path 0 (fx+ i 1)))281 (loop (fx+ i 1) (fx+ i 1) '()))282 (else (loop (fx+ i 1) prev parts)))))))))283284;; directory pathname => list of strings285;; does arg check286287(define split-directory288 (lambda (loc dir keep?)289 (##sys#check-string dir loc)290 (string-split dir (if ##sys#windows-platform "/\\" "/") keep?)))291292;; Directory string or list only contains path-separators293;; and/or current-directory (".") names.294295(define (directory-null? dir)296 (let loop ((ls (if (list? dir) dir (split-directory 'directory-null? dir #t))))297 (or (null? ls)298 (and (member (car ls) '("" "."))299 (loop (cdr ls))))))300301;; Directory string => {<origin> <root> <directory-list>}302;; where any maybe #f when missing303304(define (decompose-directory dir)305 (define (strip-origin-prefix org decomp)306 #;(assert (or (not org) decomp)) ;cannot have an "origin" but no "decomp"307 (if (not org)308 decomp309 (let ((1st (car decomp)))310 (let ((olen (string-length org)))311 (if (not (##core#inline "C_u_i_substring_equal_p" org 1st 0 0 olen))312 ; then origin is not a prefix (really shouldn't happen)313 decomp314 ; else is a prefix315 (let ((rst (cdr decomp))316 (elen (string-length 1st)))317 (if (fx= olen elen)318 ; then origin is a list prefix319 rst320 ; else origin is a string prefix321 (cons (##sys#substring 1st olen elen) rst))))))))322 (let* ((ls (split-directory 'decompose-directory dir #f))323 (rt (absolute-pathname-root dir))324 (org (root-origin rt)))325 (values org (root-directory rt) (strip-origin-prefix org (and (not (null? ls)) ls))))))