View source with formatted comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        J.Wielemaker@vu.nl
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (c)  2018, VU University Amsterdam
    7    All rights reserved.
    8
    9    Redistribution and use in source and binary forms, with or without
   10    modification, are permitted provided that the following conditions
   11    are met:
   12
   13    1. Redistributions of source code must retain the above copyright
   14       notice, this list of conditions and the following disclaimer.
   15
   16    2. Redistributions in binary form must reproduce the above copyright
   17       notice, this list of conditions and the following disclaimer in
   18       the documentation and/or other materials provided with the
   19       distribution.
   20
   21    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   22    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   23    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   24    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   25    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   26    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   27    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   28    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   29    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   30    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   31    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   32    POSSIBILITY OF SUCH DAMAGE.
   33*/
   34
   35:- module(zip,
   36          [ zip_open/4,                    % +File, +Mode, -Zipper, +Options
   37            zip_close/1,                   % +Zipper
   38            zip_close/2,                   % +Zipper, +Comment
   39                                           % Entry predicates
   40            with_zipper/2,                 % +Zipper, :Goal
   41            zipper_open_new_file_in_zip/4, % +Zipper, +File, -Stream, +Options
   42            zipper_goto/2,                 % +Zipper, +Where
   43            zipper_open_current/3,         % +Zipper, -Stream, +Options
   44            zipper_members/2,              % +Zipper, -Entries
   45            zipper_file_info/3             % +Zipper, -Name, -Attrs
   46          ]).   47:- use_module(library(error)).   48:- use_module(library(option)).   49
   50:- meta_predicate
   51    with_zipper(+, 0).   52
   53/** <module> Access resource ZIP archives
   54
   55This library provides access to ZIP files.   ZIP files are used to store
   56SWI-Prolog _resources_. Ths library provides more  high level access and
   57documentation in addition to the low level   access provided as built in
   58as it is needed to bootstrap SWI-Prolog.
   59
   60Access to a zip file is provided by  means of a _zipper_ object. This is
   61a _blob_ that is subject to atom garbage collection. Collecting a zipper
   62closes the underlying OS access.
   63
   64A zipper is a  stateful  object.   We  recognise  the  following states:
   65_idle_, _scan_, _read_entry_, _write_entry_ and   _close_. The interface
   66raise  a  _permission_error_  when  trying  to  make  an  illegal  state
   67transition.
   68
   69Being stateful, a zipper cannot  be   used  simultaneously from multiple
   70threads. The zipper becomes _owned_ by a   thread  when moving to _scan_
   71using zipper_goto/2. It is released after zipper_open_current/3 followed
   72by closing the stream.
   73*/
   74
   75%!  zip_open(+File, +Mode, -Zipper, +Options) is det.
   76%
   77%   Create a Zipper, providing access to File.  Mode is one of `read` or
   78%   `write`. The Options list is currently ignored.
   79
   80zip_open(File, Mode, Zipper, _Options) :-
   81    must_be(oneof([read,write]), Mode),
   82    open(File, Mode, Stream, [type(binary)]),
   83    zip_open_stream(Stream, Zipper, [close_parent(true)]).
   84
   85%!  zip_close(+Zipper) is det.
   86%!  zip_close(+Zipper, +Options) is det.
   87%
   88%   Close a zipper. Options processed:
   89%
   90%     - comment(+Comment)
   91%     If the zipper is open for writing, set the global comment
   92%     for the zip file.
   93
   94zip_close(Zipper) :-
   95    zip_close_(Zipper, _).
   96zip_close(Zipper, Options) :-
   97    option(comment(Comment), Options, _),
   98    zip_close_(Zipper, Comment).
   99
  100%!  zipper_goto(+Zipper, +Where) is semidet.
  101%
  102%   Seek Zipper to a specified entry.  Where is one of
  103%
  104%     - first
  105%     Go to the first entry.  Fails if the zip is empty.
  106%     - next
  107%     Go to the next entry.  Fails if there is no next entry.
  108%     - file(Name)
  109%     Go to the entry with the specified name.
  110%
  111
  112%!  zipper_open_current(+Zipper, -Stream, +Options) is det.
  113%
  114%   Open the current entry as an  input   stream.  Before  this call the
  115%   caller must use zipper_goto/2 to position to archive.  Options:
  116%
  117%     - type(+Type)
  118%     - encoding(+Encoding)
  119%     - bom(+Boolean)
  120%     Determine type and encoding of the stream.  The semantics
  121%     is the same as for open/4.
  122%     - release(+Boolean)
  123%     If `true` (default), release te archive for access by other
  124%     threads after the entry is closed.
  125%
  126%   It is allowed to call zip_close/1   immediately  after this call, in
  127%   which case the archive is closed when the entry is closed.
  128
  129%!  with_zipper(+Zipper, :Goal)
  130%
  131%   Run Goal while holding ownership over Zipper.
  132
  133with_zipper(Zipper, Goal) :-
  134    setup_call_cleanup(
  135        zip_lock(Zipper),
  136        Goal,
  137        zip_unlock(Zipper)).
  138
  139%!  zip_members(+Zipper, -Members:list(atom)) is det.
  140%
  141%   True when Members is the list of file names in the Zipper.
  142
  143zipper_members(Zipper, Members) :-
  144    with_zipper(Zipper,
  145                ( zipper_goto(Zipper, first),
  146                  zip_members_(Zipper, Members)
  147                )).
  148
  149zip_members_(Zipper, [Name|T]) :-
  150    zip_file_info_(Zipper, Name, _Attrs),
  151    (   zipper_goto(Zipper, next)
  152    ->  zip_members_(Zipper, T)
  153    ;   T = []
  154    ).
  155
  156%!  zipper_file_info(+Zipper, -Name, -Attrs) is det.
  157%
  158%   Obtain information about the current  zip   entry.  Name  is an atom
  159%   representing the name of the entry. Attrs is a dict holding:
  160%
  161%     - compressed_size:Bytes
  162%     Size in the archive
  163%     - uncompressed_size:Bytes
  164%     Bytes after decompression
  165%     - time:Stamp
  166%     Numeric time stamp in Prolog native format (float
  167%     expressing seconds since Jan 1, 1970).  Note that
  168%     the resolution of time in zip archives is one
  169%     second.
  170%     - extra:Extra
  171%     - comment:Extra
  172%     Optional additional fields.
  173%     - offset:Offset
  174%     Direct pointer to this entry.  May be used with zip_goto/2.
  175
  176zipper_file_info(Zipper, Name, Attrs) :-
  177    zip_file_info_(Zipper, Name,
  178                   info(CompressedSize, UnCompressedSize,
  179                        Extra, Comment,
  180                        Time, Offset)),
  181    Attrs0 = zip{compressed_size:CompressedSize,
  182                 uncompressed_size:UnCompressedSize,
  183                 offset:Offset
  184                },
  185    zip_attr(Extra,   extra,   Attrs0, Attrs1),
  186    zip_attr(Comment, comment, Attrs1, Attrs2),
  187    zip_attr(Time,    time,    Attrs2, Attrs).
  188
  189zip_attr("", _, Attrs, Attrs) :- !.
  190zip_attr('', _, Attrs, Attrs) :- !.
  191zip_attr(Value, Name, Attrs0, Attrs) :-
  192    put_dict(Name, Attrs0, Value, Attrs)