diff --git a/pkgs/racket-doc/file/scribblings/unzip.scrbl b/pkgs/racket-doc/file/scribblings/unzip.scrbl index aa1a13f26f..546780d491 100644 --- a/pkgs/racket-doc/file/scribblings/unzip.scrbl +++ b/pkgs/racket-doc/file/scribblings/unzip.scrbl @@ -13,11 +13,15 @@ a function to extract items from a @exec{zip} archive.} . -> . any) (bytes? boolean? input-port? . -> . any)) (make-filesystem-entry-reader)] + [#:must-unzip? must-unzip? any/c #f] [#:preserve-timestamps? preserve-timestamps? any/c #f] [#:utc-timestamps? utc-timestamps? any/c #f]) void?]{ -Unzips an entire @exec{zip} archive from @racket[in]. +Unzips an entire @exec{zip} archive from @racket[in]. If @racket[in] +does not start with @exec{zip}-archive magic bytes, an error is +reported only if @racket[must-unzip?] is true, otherwise the result is +@racket[(void)] with no bytes consumed from @racket[in]. For each entry in the archive, the @racket[entry-reader] procedure is called with three or four arguments: the byte string representing the entry @@ -33,18 +37,24 @@ but if @racket[utc-timestamps?] is true, then the time in the archive is interpreted as UTC. @history[#:changed "6.0.0.3" @elem{Added the @racket[#:preserve-timestamps?] argument.} - #:changed "6.0.1.12" @elem{Added the @racket[#:utc-timestamps?] argument.}]} + #:changed "6.0.1.12" @elem{Added the @racket[#:utc-timestamps?] argument.} + #:changed "8.0.0.10" @elem{Added the @racket[#:must-unzip?] argument.}]} @defproc[(call-with-unzip [in (or/c path-string? input-port?)] - [proc (-> path-string? any)]) + [proc (-> path-string? any)] + [#:must-unzip? must-unzip? any/c #f]) any]{ Unpacks @racket[in] to a temporary directory, calls @racket[proc] on the temporary directory's path, and then deletes the temporary directory while returning the result of @racket[proc]. -@history[#:added "6.0.1.6"]} +Like @racket[unzip], no error is reported in the case @racket[in] is +not a @exec{zip} archive, unless @racket[must-unzip?] is true. + +@history[#:added "6.0.1.6" + #:changed "8.0.0.10" @elem{Added the @racket[#:must-unzip?] argument.}]} @defproc[(make-filesystem-entry-reader diff --git a/pkgs/racket-test/tests/file/unzip.rkt b/pkgs/racket-test/tests/file/unzip.rkt index 1b0bf749d2..cdde7e0ad4 100644 --- a/pkgs/racket-test/tests/file/unzip.rkt +++ b/pkgs/racket-test/tests/file/unzip.rkt @@ -62,7 +62,14 @@ (break-thread t) (sync t) 'done)) - => 'done)) + => 'done) + + (test (call-with-unzip (open-input-bytes #"not a zip stream") void) + => (void)) + (test (call-with-unzip (open-input-bytes #"not a zip stream") + void + #:must-unzip? #t) + =error> "input does not appear to be an archive")) (provide tests) diff --git a/racket/collects/file/unzip.rkt b/racket/collects/file/unzip.rkt index f271889b8f..a090b2a76e 100644 --- a/racket/collects/file/unzip.rkt +++ b/racket/collects/file/unzip.rkt @@ -18,7 +18,8 @@ (or/c (bytes? boolean? input-port? (or/c #f exact-integer?) . -> . any) (bytes? boolean? input-port? . -> . any)) #:preserve-timestamps? any/c - #:utc-timestamps? any/c) + #:utc-timestamps? any/c + #:must-unzip? any/c) . ->* . any)] [make-filesystem-entry-reader (() (#:dest @@ -48,9 +49,10 @@ . ->* . any)] - [call-with-unzip (-> (or/c path-string? input-port?) - (-> path-string? any) - any)] + [call-with-unzip (((or/c path-string? input-port?) + (-> path-string? any)) + (#:must-unzip? any/c) + . ->* . any)] [call-with-unzip-entry (-> (or/c path-string? input-port?) path-string? (-> path-string? any) @@ -309,17 +311,22 @@ ;; unzip : [(or/c path-string? input-port) (bytes boolean input-port -> any)] -> any (define unzip - (lambda (in [read-entry (make-filesystem-entry-reader)] - #:preserve-timestamps? [preserve-timestamps? #f] - #:utc-timestamps? [utc? #f]) + (lambda (orig-in [read-entry (make-filesystem-entry-reader)] + #:must-unzip? [must-unzip? #f] + #:preserve-timestamps? [preserve-timestamps? #f] + #:utc-timestamps? [utc? #f]) (call-with-input - in + orig-in (lambda (in) - (when (= (peek-integer 4 #f in #f) *local-file-header*) - (unzip-one-entry in read-entry preserve-timestamps? utc?) - (unzip in read-entry - #:preserve-timestamps? preserve-timestamps? - #:utc-timestamps? utc?)))))) + (cond + [(= (peek-integer 4 #f in #f) *local-file-header*) + (unzip-one-entry in read-entry preserve-timestamps? utc?) + (unzip in read-entry + #:preserve-timestamps? preserve-timestamps? + #:utc-timestamps? utc?)] + [must-unzip? + (error 'unzip "input does not appear to be an archive\n input: ~e" orig-in)] + [else (void)]))))) (define (input-size in) (file-position in eof) @@ -403,13 +410,17 @@ (lambda () (delete-directory/files temp-dir))))) -(define (call-with-unzip zip-file user-proc) +(define (call-with-unzip zip-file user-proc + #:must-unzip? [must-unzip? #f]) (let ([temp-dir #f]) (dynamic-wind (lambda () (set! temp-dir (make-temporary-file "ziptmp~a" 'directory))) (lambda () - (unzip zip-file (make-filesystem-entry-reader #:dest temp-dir #:exists 'replace)) + (unzip zip-file (make-filesystem-entry-reader + #:dest temp-dir + #:exists 'replace) + #:must-unzip? must-unzip?) (user-proc temp-dir)) (lambda () (delete-directory/files temp-dir)))))