#lang scheme/base (require mzlib/list mzlib/plt-match mzlib/contract mzlib/serialize mzlib/pretty (lib "xml.ss" "xml") net/url) (define path-element? (or/c string? path? (symbols 'up 'same))) (define port-number? (between/c 1 65535)) (provide/contract [path-element? contract?] [port-number? contract?] [pretty-print-invalid-xexpr (exn:invalid-xexpr? any/c . -> . void)] [url-replace-path (((listof path/param?) . -> . (listof path/param?)) url? . -> . url?)] [explode-path* (path? . -> . (listof path-element?))] [path-without-base (path? path? . -> . (listof path-element?))] [list-prefix? (list? list? . -> . boolean?)] [strip-prefix-ups ((listof path-element?) . -> . (listof path-element?))] [url-path->string ((listof path/param?) . -> . string?)] [network-error ((symbol? string?) (listof any/c) . ->* . (void))] [directory-part (path? . -> . path?)] ; XXX Eliminate use of this [lowercase-symbol! ((or/c string? bytes?) . -> . symbol?)] [exn->string ((or/c exn? any/c) . -> . string?)] [build-path-unless-absolute (path-string? path-string? . -> . path?)] [read/string (string? . -> . serializable?)] [write/string (serializable? . -> . string?)]) (define (pretty-print-invalid-xexpr exn xexpr) (define code (exn:invalid-xexpr-code exn)) (parameterize ([pretty-print-size-hook (lambda (v display? out) (and (equal? v code) (string-length (format (if display? "~a" "~v") v))))] [pretty-print-print-hook (lambda (v display? out) (fprintf out (string-append "" (if display? "~a" "~v") "") v))]) (pretty-print xexpr))) (define (read/string str) (read (open-input-string str))) (define (write/string v) (define str (open-output-string)) (write v str) (get-output-string str)) ; explode-path* : path? -> (listof path?) (define (explode-path* p) (let loop ([p p] [r empty]) (cond [(eq? 'relative p) r] [(not p) r] [else (let-values ([(base name dir?) (split-path p)]) (loop base (list* name r)))]))) ; strip-prefix-ups : (listof path-element?) -> (listof path-element?) (define (strip-prefix-ups l) (define prefix? (box #t)) (filter (lambda (p) (if (unbox prefix?) (if (eq? 'up p) #f (begin #t (set-box! prefix? #f))) #t)) l)) ; list-prefix : list? list? -> (or/c list? false/c) ; Is l a prefix or r?, and what is that prefix? (define (list-prefix? ls rs) (match ls [(list) #t] [(list-rest l0 ls) (match rs [(list) #f] [(list-rest r0 rs) (if (equal? l0 r0) (list-prefix? ls rs) #f)])])) ; path-without-base : path? path? -> (listof path-element?) (define (path-without-base base path) (define b (explode-path* base)) (define p (explode-path* path)) (if (list-prefix? b p) (list-tail p (length b)) (error 'path-without-base "~a is not a prefix of ~a" base path))) ;; replace-path: (url-path -> url-path) url -> url ;; make a new url by replacing the path part of a url with a function ;; of the url's old path ;; also remove the query (define (url-replace-path proc in-url) (let ([new-path (proc (url-path in-url))]) (make-url (url-scheme in-url) (url-user in-url) (url-host in-url) (url-port in-url) (url-path-absolute? in-url) new-path empty (url-fragment in-url)))) ;; ripped this off from url-unit.ss (define (url-path->string strs) (apply string-append (apply append (map (lambda (s) (list "/" (maybe-join-params s))) strs)))) ;; needs to unquote things! (define (maybe-join-params s) (if (string? s) s (let ([s (path/param-path s)]) (if (string? s) s (case s [(same) "."] [(up) ".."] [else (error 'maybe-join-params "bad value from path/param-path: ~e" s)]))))) ;; network-error: symbol string . values -> void ;; throws a formatted exn:fail:network (define (network-error src fmt . args) (raise (make-exn:fail:network (format "~a: ~a" src (apply format fmt args)) (current-continuation-marks)))) ;; build-path-unless-absolute : path-string? path-string? -> path? (define (build-path-unless-absolute base path) (if (absolute-path? path) (build-path path) (build-path base path))) ;; exn->string : (or/c exn any) -> string (define (exn->string exn) (if (exn? exn) (parameterize ([current-error-port (open-output-string)]) ((error-display-handler) (exn-message exn) exn) (get-output-string (current-error-port))) (format "~s\n" exn))) ; lowercase-symbol! : (or/c string bytes) -> symbol (define (lowercase-symbol! s) (string->symbol (string-downcase (if (bytes? s) (bytes->string/utf-8 s) s)))) (define (directory-part path) (let-values ([(base name must-be-dir) (split-path path)]) (cond [(eq? 'relative base) (current-directory)] [(not base) (error 'directory-part "~a is a top-level directory" path)] [(path? base) base])))