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( , ). 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)