{"size": 8059, "ext": "scrbl", "max_stars_repo_path": "pkgs/racket-doc/file/scribblings/unzip.scrbl", "max_stars_repo_name": "lkh01/racket", "max_stars_repo_stars_event_min_datetime": "2018-07-05T21:18:24.000Z", "max_stars_repo_stars_event_max_datetime": "2021-06-04T14:48:37.000Z", "max_issues_repo_path": "pkgs/racket-doc/file/scribblings/unzip.scrbl", "max_issues_repo_name": "lkh01/racket", "max_issues_repo_issues_event_min_datetime": "2017-05-10T07:33:20.000Z", "max_issues_repo_issues_event_max_datetime": "2018-07-19T15:49:46.000Z", "max_forks_repo_path": "pkgs/racket-doc/file/scribblings/unzip.scrbl", "max_forks_repo_name": "lkh01/racket", "max_forks_repo_forks_event_min_datetime": "2016-04-25T11:47:57.000Z", "max_forks_repo_forks_event_max_datetime": "2017-05-09T14:52:48.000Z"}
{"max_stars_count": 4, "max_issues_count": 9, "max_forks_count": 3, "avg_line_length": 41.7564766839, "max_line_length": 93, "alphanum_fraction": 0.6639781611}
#lang scribble/doc
@(require "common.rkt" (for-label file/unzip))

@title[#:tag "unzip"]{@exec{zip} File Extraction}
@author{David Herman}

@defmodule[file/unzip]{The @racketmodname[file/unzip] library provides
a function to extract items from a @exec{zip} archive.}

@defproc[(unzip [in (or/c path-string? input-port?)]
                [entry-reader (if preserve-timestamps?
                                  (bytes? boolean? input-port? (or/c #f exact-integer?)
                                   . -> . any)
                                  (bytes? boolean? input-port? . -> . any))
                              (make-filesystem-entry-reader)]
                [#:preserve-timestamps? preserve-timestamps? any/c #f]
                [#:utc-timestamps? utc-timestamps? any/c #f])
         void?]{

Unzips an entire @exec{zip} archive 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
name, a boolean flag indicating whether the entry represents a
directory, an input port containing the inflated contents of the
entry, and (if @racket[preserve-timestamps?]) @racket[#f] or a timestamp
for a file. The default @racket[entry-reader] unpacks entries to the
filesystem; call @racket[make-filesystem-entry-reader] to configure
aspects of the unpacking, such as  the destination directory.

Normally, @exec{zip} archives record modification dates in local time,
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.}]}


@defproc[(call-with-unzip [in (or/c path-string? input-port?)]
                          [proc (-> path-string? any)])
         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"]}


@defproc[(make-filesystem-entry-reader
          [#:dest dest-path (or/c path-string? #f) #f]
          [#:strip-count strip-count exact-nonnegative-integer? 0]
          [#:permissive? permissive? any/c #f]
          [#:exists exists (or/c 'skip 'error 'replace 'truncate 
                                 'truncate/replace 'append 'update
                                 'can-update 'must-truncate)
                           'error])
         ((bytes? boolean? input-port?) ((or/c #f exact-integer?))
          . ->* . any)]{

Creates a @exec{zip} entry reader that can be used with either
@racket[unzip] or @racket[unzip-entry] and whose behavior is to save
entries to the local filesystem. Intermediate directories are always
created if necessary before creating files. Directory entries are
created as directories in the filesystem, and their entry contents are
ignored.

If @racket[dest-path] is not @racket[#f], every path in the archive is
prefixed to determine the destination path of the extracted entry.

If @racket[strip-count] is positive, then @racket[strip-count] path
elements are removed from the entry path from the archive (before
prefixing the path with @racket[dest-path]); if the item's path
contains @racket[strip-count] elements, then it is not extracted.

Unless @racket[permissive?] is true, then entries with paths containing
an up-directory indicator are disallowed, and a link entry whose target
is an absolute path or contains an up-directory indicator is also
disallowed. Absolute paths are always disallowed. A disallowed
path triggers an exception.

If @racket[exists] is @racket['skip] and the file for an entry already
exists, then the entry is skipped. Otherwise, @racket[exists] is
passed on to @racket[open-output-file] for writing the entry's
inflated content.

@history[#:changed "6.0.0.3"
         @elem{Added support for the optional timestamp argument in the result function.}
         #:changed "6.3"
         @elem{Added the @racket[#:permissive?] argument.}]}


@defproc[(read-zip-directory [in (or/c path-string? input-port?)]) zip-directory?]{

Reads the central directory of a @exec{zip} file and generates a
@deftech{zip directory} representing the zip file's contents.  If
@racket[in] is an input port, it must support position setting via
@racket[file-position].

This procedure performs limited I/O: it reads the list of entries from
the @exec{zip} file, but it does not inflate any of their
contents.}

@defproc[(zip-directory? [v any/c]) boolean?]{

Returns @racket[#t] if @racket[v] is a @tech{zip directory},
@racket[#f] otherwise.}


@defproc[(zip-directory-entries [zipdir zip-directory?]) (listof bytes?)]{

Extracts the list of entries for a @exec{zip} archive.}


@defproc[(zip-directory-contains? [zipdir zip-directory?]
                                  [name (or/c bytes? path-string?)])
         boolean?]{

Determines whether the given entry name occurs in the given @tech{zip
directory}. If @racket[name] is not a byte string, it is converted
using @racket[path->zip-path].

Directory entries match with or without trailing slashes.}


@defproc[(zip-directory-includes-directory? [zipdir zip-directory?]
                                            [name (or/c bytes? path-string?)])
         boolean?]{

Determines whether the given name is included anywhere in the given
@tech{zip directory} as a filesystem directory, either as an entry
itself or as the containing directory of other entries. If
@racket[name] is not a byte string, it is converted using
@racket[path->zip-path].}


@defproc[(unzip-entry [in (or/c path-string? input-port?)]
                      [zipdir zip-directory?]
                      [entry (or/c bytes? path-string?)]
                      [entry-reader (if preserve-timestamps?
                                        (bytes? boolean? input-port? (or/c #f exact-integer?)
                                         . -> . any)
                                        (bytes? boolean? input-port? . -> . any))
                                    (make-filesystem-entry-reader)]
                      [#:preserve-timestamps? preserve-timestamps? any/c #f]
                      [#:utc-timestamps? utc-timestamps? any/c #f])
         void?]{

Unzips a single entry from a @exec{zip} archive based on a previously
read @tech{zip directory}, @racket[zipdir], from
@racket[read-zip-directory].  If @racket[in] is an input port, it must
support position setting via @racket[file-position].

The @racket[entry] parameter is a byte string whose name must be found
in the zip file's central directory. If @racket[entry] is not a byte
string, it is converted using @racket[path->zip-path].

The @racket[read-entry] argument is used to read the contents of the zip entry
in the same way as for @racket[unzip].

If @racket[entry] is not in @racket[zipdir], an
@racket[exn:fail:unzip:no-such-entry] exception is raised.

@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.}]}


@defproc[(call-with-unzip-entry [in (or/c path-string? input-port?)]
                                [entry path-string?]
                                [proc (-> path-string? any)])
         any]{

Unpacks @racket[entry] within @racket[in] to a temporary directory,
calls @racket[proc] on the unpacked file's path, and then
deletes the temporary directory while returning the result of
@racket[proc].

@history[#:added "6.0.1.6"]}


@defproc[(path->zip-path [path path-string?]) bytes?]{

Converts a file name potentially containing path separators in the current
platform's format to use path separators recognized by the zip file
format: @litchar{/}.}


@defstruct[(exn:fail:unzip:no-such-entry exn:fail) ([entry bytes?])]{

Raised when a requested entry cannot be found in a @exec{zip}
archive. The @racket[entry] field is a byte string representing the
requested entry name.}
