pax_global_header00006660000000000000000000000064146425411270014520gustar00rootroot0000000000000052 comment=72080af43fbb6787971d5893682e2682397b6ab9 xavierleroy-camlzip-72080af/000077500000000000000000000000001464254112700160575ustar00rootroot00000000000000xavierleroy-camlzip-72080af/.depend000066400000000000000000000002611464254112700173160ustar00rootroot00000000000000zlibstubs.o: zlibstubs.c gzip.cmo: zlib.cmi gzip.cmi gzip.cmx: zlib.cmx gzip.cmi zip.cmo: zlib.cmi zip.cmi zip.cmx: zlib.cmx zip.cmi zlib.cmo: zlib.cmi zlib.cmx: zlib.cmi xavierleroy-camlzip-72080af/.gitignore000066400000000000000000000001511464254112700200440ustar00rootroot00000000000000*.a *.cma *.cmi *.cmo *.cmx *.cmxs *.cmxa *.o *.so *.cmt *.cmti test/minigzip test/minizip test/testzlib xavierleroy-camlzip-72080af/Changes000066400000000000000000000063751464254112700173650ustar00rootroot00000000000000Release 1.12: - #35, #43: add full support for ZIP64 archives [Jules Villard and Xavier Leroy] - #41, #42: fix memory leak when a `Zlib.stream` is finalized and `Zlib.deflate_end` / `Zlib.inflate_end` was not called before Release 1.11: - `Zip.add_entry_generator ~level:0` was missing a CRC update - #28: fix build on platforms with no shared libs - #33: update META files to use plugin convention - Use `Stdlib.xxx` instead of `Pervasives.xxx`. OCaml >= 4.07 is required. - `make all` automatically builds in native-code if supported - Added local .opam file - Updated tests and added automatic test harness - Generate and install .mli, .cmt, and .cmti files Release 1.10: - #25: Fix Gzip.flush_continue [James Owen] - Improve compatibility with OCaml 4.09 Release 1.09: - #22: Add a Gzip.flush_continue function that allows the user to flush the contents of the zip buffer all the way to disk but then continue writing to the zipped channel [James Owen] Release 1.08: - ocamlfind is now mandatory as a consequence of #4 - #4: use ocamlfind and $(EXT_...) configuration variables for better cross-platform support in Makefile [user 'whitequark'] - Improve Mingw support in Makefile (install .dll files) [Bertrand Jeannet] - #5: make sure 'zip' and 'camlzip' packages can be used both in the same executable [Rudi Grinberg] - #6: Z_BUF_ERROR is not a fatal error, just a transient condition [Tuukka Korhonen] - #13: fix memory leak in Zlib.compress_direct [Alain Frisch] - #14: add Zlib.deflate_string and Zlib.inflate_string, using immutable strings as input buffers instead of mutable bytes [Vincent Balat] - #16: assertion failure when reading ZIP files with 2^16 entries or more [Einar Lielmanis] - #18: in Zip.open_in, properly close channels in case of error [Daniel Weil] Release 1.07: - Allocate Zlib data structures outside the OCaml heap for compatibility with recent versions of Zlib (Github issue #1, pull request #2, report and fix by Einar Lielmanis). - Don't pass -L and -I options to the C compiler unless necessary. - Compile and install the shared library zip.cmxs. (Contributed by E. Millon.) - ocamlfind: install under 'zip' and 'camlzip' package names. (This is what the OPAM package does.) Release 1.06: - Switch to "safe string" mode. Some API functions that use to take strings now take byte sequences instead. OCaml 4.02 or up is required. - Update for OCaml 4.03. - Avoid Zlib error when calling Gzip.output with length = 0. - Improve support for ZIP files / ZIP file members greater than 2 Gbytes. Release 1.05: - Added support for findlib (Contributed by E. Friendly) Release 1.04: - Added function Zip.add_entry_generator. (Contributed by A. Frisch.) - The "level" optional argument was sometimes not honored; fixed. - Relicensed under LGPL 2.1 or above, with Caml's special exception for static linking. Release 1.03: - Fixed bug in Zlib.uncompress that could cause it to loop infinitely. - Documentation comments in .mli files converted to ocamldoc format. Release 1.02: - 64-bit incompatibility fixed. - Better support for large ZIP files (> 2 Gb). - Added Caml's special exception for static linking to the license. Release 1.01: - Use ocamlmklib to create library and possibly DLL (for OCaml 3.04 and up). Release 1.00: - First public release xavierleroy-camlzip-72080af/LICENSE000066400000000000000000000656401464254112700170770ustar00rootroot00000000000000This Library is distributed under the terms of the GNU Lesser General Public License (LGPL) version 2.1 or above (included below). As a special exception to the GNU Lesser General Public License, you may link, statically or dynamically, a "work that uses the Library" with a publicly distributed version of the Library to produce an executable file containing portions of the Library, and distribute that executable file under terms of your choice, without any of the additional requirements listed in clause 6 of the GNU Lesser General Public License. By "a publicly distributed version of the Library", we mean either the unmodified Library as distributed by INRIA, or a modified version of the Library that is distributed under the conditions defined in clause 3 of the GNU Lesser General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU Lesser General Public License. ---------------------------------------------------------------------- GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! xavierleroy-camlzip-72080af/META-camlzip000066400000000000000000000000161464254112700201620ustar00rootroot00000000000000requires="zip"xavierleroy-camlzip-72080af/META-zip000066400000000000000000000002451464254112700173310ustar00rootroot00000000000000version="1.12" requires="unix" archive(byte)="zip.cma" archive(native)="zip.cmxa" archive(native,plugin)="zip.cmxs" plugin(byte)="zip.cma" plugin(native)="zip.cmxs" xavierleroy-camlzip-72080af/Makefile000066400000000000000000000043241464254112700175220ustar00rootroot00000000000000### Configuration section # The name of the Zlib library. Usually -lz ZLIB_LIB=-lz # The directory containing the Zlib library (libz.a or libz.so) # Leave empty if libz is in a standard linker directory ZLIB_LIBDIR= # ZLIB_LIBDIR=/usr/local/lib # The directory containing the Zlib header file (zlib.h) # Leave empty if zlib.h is in a standard compiler directory ZLIB_INCLUDE= # ZLIB_INCLUDE=/usr/local/include ### End of configuration section OCAMLC=ocamlfind ocamlc -g -safe-string -bin-annot -package unix OCAMLOPT=ocamlfind ocamlopt -safe-string -package unix OCAMLDEP=ocamlfind ocamldep OCAMLMKLIB=ocamlfind ocamlmklib OBJS=zlib.cmo zip.cmo gzip.cmo C_OBJS=zlibstubs.o include $(shell ocamlfind ocamlc -where)/Makefile.config ifeq "${NATDYNLINK}" "true" ZIP_CMXS = zip.cmxs endif ZLIB_L_OPT=$(if $(ZLIB_LIBDIR),-L$(ZLIB_LIBDIR)) ZLIB_I_OPT=$(if $(ZLIB_INCLUDE),-ccopt -I$(ZLIB_INCLUDE)) all:: allbyt ifneq "${ARCH}" "none" ifneq "${NATIVE_COMPILER}" "false" all:: allopt endif endif allbyt: libcamlzip$(EXT_LIB) zip.cma allopt: libcamlzip$(EXT_LIB) zip.cmxa $(ZIP_CMXS) zip.cma: $(OBJS) $(OCAMLMKLIB) -o zip -oc camlzip $(OBJS) $(ZLIB_L_OPT) $(ZLIB_LIB) zip.cmxa: $(OBJS:.cmo=.cmx) $(OCAMLMKLIB) -o zip -oc camlzip $(OBJS:.cmo=.cmx) $(ZLIB_L_OPT) $(ZLIB_LIB) zip.cmxs: zip.cmxa libcamlzip$(EXT_LIB) $(OCAMLOPT) -shared -linkall -I ./ -o $@ $^ libcamlzip$(EXT_LIB): $(C_OBJS) $(OCAMLMKLIB) -oc camlzip $(C_OBJS) $(ZLIB_L_OPT) $(ZLIB_LIB) .SUFFIXES: .mli .ml .cmo .cmi .cmx .mli.cmi: $(OCAMLC) -c $< .ml.cmo: $(OCAMLC) -c $< .ml.cmx: $(OCAMLOPT) -c $< .c.o: $(OCAMLC) -c -ccopt -g $(ZLIB_I_OPT) $< clean: rm -f *.cm* rm -f *.o *.a *.so rm -rf doc/ TOINSTALL=\ *.cma *$(EXT_LIB) \ *.mli *.cmi *.cmti *.cmt \ $(wildcard *.cmx) $(wildcard *.cmxa) \ $(wildcard *.cmxs) $(wildcard *$(EXT_DLL)) install-findlib: install install: cp META-zip META && \ ocamlfind install zip META $(TOINSTALL) && \ rm META cp META-camlzip META && \ ocamlfind install camlzip META && \ rm META uninstall: ocamlfind remove zip ocamlfind remove camlzip depend: gcc -MM $(ZLIB_I_OPT) *.c > .depend $(OCAMLDEP) *.mli *.ml >> .depend include .depend doc: *.mli mkdir -p doc ocamldoc -d doc/ -html *.mli xavierleroy-camlzip-72080af/README.md000066400000000000000000000035621464254112700173440ustar00rootroot00000000000000# The CamlZip library ## DESCRIPTION This Objective Caml library provides easy access to compressed files in ZIP and GZIP format, as well as to Java JAR files. It provides functions for reading from and writing to compressed files in these formats. ## REQUIREMENTS * Objective Caml 4.07 or up. * The Findlib / ocamlfind library manager. * The Zlib C library, version 1.1.3 or up. You need both the library and its development headers. For Debian and Ubuntu, install the package `zlib1-dev`. For Fedora and RHEL, install the package `zlib-devel`. The Zlib source distribution is at https://zlib.net/ . ## INSTALLATION * Do `make all`. * If it complains that `zlib.h` or `-lz` cannot be found, it is probably because Zlib is installed in non-standard directories. Edit the top of the Makefile to set the appropriate values for `ZLIB_LIBDIR` and `ZLIB_INCLUDE`, or pass these values to `make`, for example: ``` make ZLIB_LIBDIR=/opt/lib ZLIB_INCLUDE=/opt/include all ``` * Become super-user if necessary and do `make install`. This installs the library through ocamlfind. ## DOCUMENTATION AND USAGE See the comments in files zip.mli and gzip.mli. Alternatively, do `make doc` and open the file `./doc/index.html`. Compilation: `ocamlfind ocamlopt -package zip ...` Linking: `ocamlfind ocamlopt -package zip -linkpgk ...` The directory test/ contains examples of using this library. ## LICENSING This library is copyright 2001, 2002, 2006, 2007, 2008, 2016, 2017, 2020 Institut National de Recherche en Informatique et en Automatique (INRIA), and distributed under the terms of the GNU Lesser General Public License (LGPL) version 2.1 or above, with a special exception concerning static linking. See the file LICENSE for the exact licensing terms. ## BUG REPORTS AND USER FEEDBACK Please use the [issue tracker](https://github.com/xavierleroy/camlzip/issues) xavierleroy-camlzip-72080af/camlzip.opam000066400000000000000000000014131464254112700203730ustar00rootroot00000000000000opam-version: "2.0" version: "1.12" synopsis: "Accessing compressed files in ZIP, GZIP and JAR format." description: "The Camlzip library provides easy access to compressed files in ZIP and GZIP format, as well as to Java JAR files. It provides functions for reading from and writing to compressed files in these formats." maintainer: ["Xavier Leroy "] authors: ["Xavier Leroy"] homepage: "https://github.com/xavierleroy/camlzip" bug-reports: "https://github.com/xavierleroy/camlzip/issues" dev-repo: "git+https://github.com/xavierleroy/camlzip.git" license: "LGPL-2.1-or-later with OCaml-LGPL-linking-exception" depends: [ "ocaml" {>= "4.07.0"} "ocamlfind" {build} "conf-zlib" ] build: [ [make "all"] ] install: [make "install"] xavierleroy-camlzip-72080af/gzip.ml000066400000000000000000000220441464254112700173640ustar00rootroot00000000000000(***********************************************************************) (* *) (* The CamlZip library *) (* *) (* Xavier Leroy, projet Cristal, INRIA Rocquencourt *) (* *) (* Copyright 2001 Institut National de Recherche en Informatique et *) (* en Automatique. All rights reserved. This file is distributed *) (* under the terms of the GNU Library General Public License, with *) (* the special exception on linking described in file LICENSE. *) (* *) (***********************************************************************) (* $Id$ *) (* Module [Gzip]: reading and writing to/from [gzip] compressed files *) exception Error of string let buffer_size = 1024 type in_channel = { in_chan: Stdlib.in_channel; in_buffer: bytes; mutable in_pos: int; mutable in_avail: int; mutable in_eof: bool; in_stream: Zlib.stream; mutable in_size: int32; mutable in_crc: int32 } let open_in_chan ic = (* Superficial parsing of header *) begin try let id1 = input_byte ic in let id2 = input_byte ic in if id1 <> 0x1F || id2 <> 0x8B then raise(Error("bad magic number, not a gzip file")); let cm = input_byte ic in if cm <> 8 then raise(Error("unknown compression method")); let flags = input_byte ic in if flags land 0xE0 <> 0 then raise(Error("bad flags, not a gzip file")); for i = 1 to 6 do ignore(input_byte ic) done; if flags land 0x04 <> 0 then begin (* Skip extra data *) let len1 = input_byte ic in let len2 = input_byte ic in for i = 1 to len1 + len2 lsl 8 do ignore(input_byte ic) done end; if flags land 0x08 <> 0 then begin (* Skip original file name *) while input_byte ic <> 0 do () done end; if flags land 0x10 <> 0 then begin (* Skip comment *) while input_byte ic <> 0 do () done end; if flags land 0x02 <> 0 then begin (* Skip header CRC *) ignore(input_byte ic); ignore(input_byte ic) end with End_of_file -> raise(Error("premature end of file, not a gzip file")) end; { in_chan = ic; in_buffer = Bytes.create buffer_size; in_pos = 0; in_avail = 0; in_eof = false; in_stream = Zlib.inflate_init false; in_size = Int32.zero; in_crc = Int32.zero } let open_in filename = let ic = Stdlib.open_in_bin filename in try open_in_chan ic with exn -> Stdlib.close_in ic; raise exn let read_byte iz = if iz.in_avail = 0 then begin let n = Stdlib.input iz.in_chan iz.in_buffer 0 (Bytes.length iz.in_buffer) in if n = 0 then raise End_of_file; iz.in_pos <- 0; iz.in_avail <- n end; let c = Bytes.get iz.in_buffer iz.in_pos in iz.in_pos <- iz.in_pos + 1; iz.in_avail <- iz.in_avail - 1; Char.code c let read_int32 iz = let b1 = read_byte iz in let b2 = read_byte iz in let b3 = read_byte iz in let b4 = read_byte iz in Int32.logor (Int32.of_int b1) (Int32.logor (Int32.shift_left (Int32.of_int b2) 8) (Int32.logor (Int32.shift_left (Int32.of_int b3) 16) (Int32.shift_left (Int32.of_int b4) 24))) let rec input iz buf pos len = if pos < 0 || len < 0 || pos + len > Bytes.length buf then invalid_arg "Gzip.input"; if iz.in_eof then 0 else begin if iz.in_avail = 0 then begin let n = Stdlib.input iz.in_chan iz.in_buffer 0 (Bytes.length iz.in_buffer) in if n = 0 then raise(Error("truncated file")); iz.in_pos <- 0; iz.in_avail <- n end; let (finished, used_in, used_out) = try Zlib.inflate iz.in_stream iz.in_buffer iz.in_pos iz.in_avail buf pos len Zlib.Z_SYNC_FLUSH with Zlib.Error(_, _) -> raise(Error("error during decompression")) in iz.in_pos <- iz.in_pos + used_in; iz.in_avail <- iz.in_avail - used_in; iz.in_crc <- Zlib.update_crc iz.in_crc buf pos used_out; iz.in_size <- Int32.add iz.in_size (Int32.of_int used_out); if finished then begin try let crc = read_int32 iz in let size = read_int32 iz in if iz.in_crc <> crc then raise(Error("CRC mismatch, data corrupted")); if iz.in_size <> size then raise(Error("size mismatch, data corrupted")); iz.in_eof <- true; used_out with End_of_file -> raise(Error("truncated file")) end else if used_out = 0 then input iz buf pos len else used_out end let rec really_input iz buf pos len = if len <= 0 then () else begin let n = input iz buf pos len in if n = 0 then raise End_of_file; really_input iz buf (pos + n) (len - n) end let char_buffer = Bytes.create 1 let input_char iz = if input iz char_buffer 0 1 = 0 then raise End_of_file else Bytes.get char_buffer 0 let input_byte iz = Char.code (input_char iz) let dispose iz = iz.in_eof <- true; Zlib.inflate_end iz.in_stream let close_in iz = dispose iz; Stdlib.close_in iz.in_chan type out_channel = { out_chan: Stdlib.out_channel; out_buffer: bytes; mutable out_pos: int; mutable out_avail: int; out_stream: Zlib.stream; mutable out_size: int32; mutable out_crc: int32 } let open_out_chan ?(level = 6) oc = if level < 1 || level > 9 then invalid_arg "Gzip.open_out: bad level"; (* Write minimal header *) output_byte oc 0x1F; (* ID1 *) output_byte oc 0x8B; (* ID2 *) output_byte oc 8; (* compression method *) output_byte oc 0; (* flags *) for i = 1 to 4 do output_byte oc 0 done; (* mtime *) output_byte oc 0; (* xflags *) output_byte oc 0xFF; (* OS (unknown) *) { out_chan = oc; out_buffer = Bytes.create buffer_size; out_pos = 0; out_avail = buffer_size; out_stream = Zlib.deflate_init level false; out_size = Int32.zero; out_crc = Int32.zero } let open_out ?(level = 6) filename = open_out_chan ~level (Stdlib.open_out_bin filename) let flush_and_reset_out_buffer oz = Stdlib.output oz.out_chan oz.out_buffer 0 oz.out_pos; oz.out_pos <- 0; oz.out_avail <- Bytes.length oz.out_buffer let rec output oz buf pos len = if pos < 0 || len < 0 || pos + len > Bytes.length buf then invalid_arg "Gzip.output"; (* If output buffer is full, flush it *) if oz.out_avail = 0 then flush_and_reset_out_buffer oz; (* Patch request #1428: Zlib disallows zero-length writes *) if len > 0 then begin let (_, used_in, used_out) = try Zlib.deflate oz.out_stream buf pos len oz.out_buffer oz.out_pos oz.out_avail Zlib.Z_NO_FLUSH with Zlib.Error(_, _) -> raise (Error("error during compression")) in oz.out_pos <- oz.out_pos + used_out; oz.out_avail <- oz.out_avail - used_out; oz.out_size <- Int32.add oz.out_size (Int32.of_int used_in); oz.out_crc <- Zlib.update_crc oz.out_crc buf pos used_in; if used_in < len then output oz buf (pos + used_in) (len - used_in) end let output_substring oz buf pos len = output oz (Bytes.unsafe_of_string buf) pos len let output_char oz c = Bytes.set char_buffer 0 c; output oz char_buffer 0 1 let output_byte oz b = output_char oz (Char.unsafe_chr b) let write_int32 oc n = let r = ref n in for i = 1 to 4 do Stdlib.output_byte oc (Int32.to_int !r); r := Int32.shift_right_logical !r 8 done let flush_to_out_chan ~flush_command oz = let rec do_flush () = (* If output buffer is full, flush it *) if oz.out_avail = 0 then flush_and_reset_out_buffer oz; let (finished, _, used_out) = Zlib.deflate oz.out_stream oz.out_buffer 0 0 oz.out_buffer oz.out_pos oz.out_avail flush_command in oz.out_pos <- oz.out_pos + used_out; oz.out_avail <- oz.out_avail - used_out; (* When we use the Z_FINISH command, we must retry if finished is false. For all other * flush commands, we should retry if we have filled the output buffer *) let continue = (flush_command = Zlib.Z_FINISH && not finished) || oz.out_avail = 0 in if continue then do_flush() in do_flush(); (* Final data flush *) if oz.out_pos > 0 then flush_and_reset_out_buffer oz let flush_continue oz = (* Flush everything to the underlying file channel, then flush the channel. *) flush_to_out_chan ~flush_command:Zlib.Z_SYNC_FLUSH oz; Stdlib.flush oz.out_chan let flush oz = (* Flush everything to the output channel. *) flush_to_out_chan ~flush_command:Zlib.Z_FINISH oz; (* Write CRC and size *) write_int32 oz.out_chan oz.out_crc; write_int32 oz.out_chan oz.out_size; (* Dispose of stream *) Zlib.deflate_end oz.out_stream let close_out oz = flush oz; Stdlib.close_out oz.out_chan xavierleroy-camlzip-72080af/gzip.mli000066400000000000000000000160211464254112700175330ustar00rootroot00000000000000(***********************************************************************) (* *) (* The CamlZip library *) (* *) (* Xavier Leroy, projet Cristal, INRIA Rocquencourt *) (* *) (* Copyright 2001 Institut National de Recherche en Informatique et *) (* en Automatique. All rights reserved. This file is distributed *) (* under the terms of the GNU Library General Public License, with *) (* the special exception on linking described in file LICENSE. *) (* *) (***********************************************************************) (* $Id$ *) (** Reading and writing to/from [gzip] compressed files This module provides functions to read and write compressed data to/from files in [gzip] format. *) (** {1 Reading from compressed files} *) type in_channel (** Abstract type representing a channel opened for reading from a compressed file. *) val open_in: string -> in_channel (** Open a compressed file for reading. The argument is the file name. *) val open_in_chan: Stdlib.in_channel -> in_channel (** Open a compressed file for reading. The argument is a regular file channel already opened on the compressed file. *) val input_char: in_channel -> char (** Uncompress one character from the given channel, and return it. Raise [End_of_file] if no more compressed data is available. *) val input_byte: in_channel -> int (** Same as [Gzip.input_char], but return the 8-bit integer representing the character. Raise [End_of_file] if no more compressed data is available. *) val input: in_channel -> bytes -> int -> int -> int (** [input ic buf pos len] uncompresses up to [len] characters from the given channel [ic], storing them in string [buf], starting at character number [pos]. It returns the actual number of characters read, between 0 and [len] (inclusive). A return value of 0 means that the end of file was reached. A return value between 0 and [len] exclusive means that not all requested [len] characters were read, either because no more characters were available at that time, or because the implementation found it convenient to do a partial read; [input] must be called again to read the remaining characters, if desired. (See also [Gzip.really_input] for reading exactly [len] characters.) Exception [Invalid_argument "Gzip.input"] is raised if [pos] and [len] do not designate a valid substring of [buf]. *) val really_input: in_channel -> bytes -> int -> int -> unit (** [really_input ic buf pos len] uncompresses [len] characters from the given channel, storing them in string [buf], starting at character number [pos]. Raise [End_of_file] if fewer than [len] characters can be read. Raise [Invalid_argument "Gzip.input"] if [pos] and [len] do not designate a valid substring of [buf]. *) val close_in: in_channel -> unit (** Close the given input channel. If the channel was created with [Gzip.open_in_chan], the underlying regular file channel (of type [Stdlib.in_channel]) is also closed. Do not apply any of the functions above to a closed channel. *) val dispose: in_channel -> unit (** Same as [Gzip.close_in], but does not close the underlying regular file channel (of type [Stdlib.in_channel]); just dispose of the resources associated with the decompression channel. This can be useful if e.g. the underlying file channel is a network socket on which more (uncompressed) data is expected. *) (** {1 Writing to compressed files} *) type out_channel (** Abstract type representing a channel opened for writing to a compressed file. *) val open_out: ?level:int -> string -> out_channel (** Open a compressed file for writing. The argument is the file name. The file is created if it does not exist, or truncated to zero length if it exists. The optional [level] argument (an integer between 1 and 9) indicates the compression level, with 1 being the weakest (but fastest) compression and 9 being the strongest (but slowest) compression. The default level is 6 (medium compression). *) val open_out_chan: ?level:int -> Stdlib.out_channel -> out_channel (** Open a compressed file for writing. The argument is a regular file channel already opened on the compressed file. The optional [level] argument sets the compression level as documented for [Gzip.open_out]. *) val output_char: out_channel -> char -> unit (** Output one character to the given compressed channel. *) val output_byte: out_channel -> int -> unit (** Same as [Gzip.output_char], but the output character is given by its code. The given integer is taken modulo 256. *) val output: out_channel -> bytes -> int -> int -> unit (** [output oc buf pos len] compresses and writes [len] characters from string [buf], starting at offset [pos], and writes the compressed data to the channel [oc]. Raise [Invalid_argument "Gzip.output"] if [pos] and [len] do not designate a valid substring of [buf]. *) val output_substring: out_channel -> string -> int -> int -> unit (** Same as [output], but takes a string as argument instead of a byte sequence. @since 1.06 *) val close_out: out_channel -> unit (** Close the given output channel. If the channel was created with [Gzip.open_out_chan], the underlying regular file channel (of type [Stdlib.out_channel]) is also closed. Do not apply any of the functions above to a closed channel. *) val flush: out_channel -> unit (** Same as [Gzip.close_out], but do not close the underlying regular file channel (of type [Stdlib.out_channel]); just flush all pending compressed data and dispose of the resources associated with the compression channel. This can be useful if e.g. the underlying file channel is a network socket on which more data is to be sent. *) val flush_continue: out_channel -> unit (** Flush all pending compressed data through both the compression channel and the underlying regular file channel, but keep both channels open to accept further data. *) (** {1 Error reporting} *) exception Error of string (** Exception raised by the functions above to signal errors during compression or decompression, or ill-formed input files. *) xavierleroy-camlzip-72080af/test/000077500000000000000000000000001464254112700170365ustar00rootroot00000000000000xavierleroy-camlzip-72080af/test/Makefile000066400000000000000000000011631464254112700204770ustar00rootroot00000000000000all: test-testzlib test-minizip test-minigzip OCAMLOPT=ocamlfind ocamlopt -safe-string -package unix -linkpkg minigzip: ../zip.cmxa minigzip.ml $(OCAMLOPT) -ccopt -g -g -I .. -o minigzip ../zip.cmxa minigzip.ml test-minigzip: minigzip minigzip.run sh ./minigzip.run minizip: ../zip.cmxa minizip.ml $(OCAMLOPT) -ccopt -g -g -I .. -o minizip ../zip.cmxa minizip.ml test-minizip: minizip minizip.run sh ./minizip.run testzlib: ../zip.cmxa testzlib.ml $(OCAMLOPT) -g -I .. -o testzlib ../zip.cmxa testzlib.ml test-testzlib: testzlib testzlib.run sh ./testzlib.run clean: rm -f *.cm* rm -f minigzip minizip testzlib xavierleroy-camlzip-72080af/test/bigfiles.run000066400000000000000000000014201464254112700213450ustar00rootroot00000000000000#!/bin/sh here=`pwd` tempdir="$1" if [ -z "$tempdir" ]; then echo "Usage: bigfiles.run " 1>&2 echo " must have 10 Gb of free space" 1>&2 exit 2 fi cd $tempdir trap "rm -f zeroes" 0 EXIT INT echo "Creating big file..." dd if=/dev/zero of=zeroes bs=1000000 count=5120 echo "Running the tests..." runtest() { rm -rf result minizip.zip mkdir result if ($2 ./minizip.zip zeroes > /dev/null && \ (cd result && $3 ../minizip.zip > /dev/null) && \ cmp result/zeroes zeroes) then rm -rf result minizip.zip; echo "$1: passed" else rm -rf result minizip.zip; echo "$1: FAILED"; exit 2 fi } runtest "Big file 1" "$here/minizip c" "$here/minizip x" runtest "Big file 2" "zip -r" "$here/minizip x" runtest "Big file 3" "$here/minizip c" "unzip" xavierleroy-camlzip-72080af/test/manyfiles.run000066400000000000000000000017011464254112700215520ustar00rootroot00000000000000#!/bin/sh here=`pwd` tempdir="$1" if [ -z "$tempdir" ]; then echo "Usage: manyfiles.run " 1>&2 echo " must have 10 Gb of free space" 1>&2 exit 2 fi cd $tempdir trap "rm -rf hier" 0 EXIT INT echo "Creating file hierarchy..." rm -rf hier mkdir hier hier/1 j=1 while [ $j -le 300 ]; do dd if=/dev/zero of=hier/1/$j bs=50000 count=1 status=none j=`expr $j + 1` done i=2 while [ $i -le 300 ]; do ln -s 1 hier/$i i=`expr $i + 1` done echo "Running the tests..." runtest() { rm -rf result minizip.zip mkdir result if ($2 ./minizip.zip hier > /dev/null && \ (cd result && $3 ../minizip.zip > /dev/null) && \ diff -q -r result/hier hier) then rm -rf result minizip.zip; echo "$1: passed" else rm -rf result minizip.zip; echo "$1: FAILED"; exit 2 fi } runtest "Many files 1" "$here/minizip c" "$here/minizip x" runtest "Many files 2" "zip -r" "$here/minizip x" runtest "Many files 3" "$here/minizip c" "unzip" xavierleroy-camlzip-72080af/test/minigzip.ml000066400000000000000000000030471464254112700212220ustar00rootroot00000000000000(***********************************************************************) (* *) (* The CamlZip library *) (* *) (* Xavier Leroy, projet Cristal, INRIA Rocquencourt *) (* *) (* Copyright 2001 Institut National de Recherche en Informatique et *) (* en Automatique. All rights reserved. This file is distributed *) (* under the terms of the GNU Library General Public License, with *) (* the special exception on linking described in file LICENSE. *) (* *) (***********************************************************************) (* $Id$ *) let buffer = Bytes.create 4096 let _ = if Array.length Sys.argv >= 2 && Sys.argv.(1) = "-d" then begin (* decompress *) let ic = Gzip.open_in_chan stdin in let rec decompress () = let n = Gzip.input ic buffer 0 (Bytes.length buffer) in if n = 0 then () else begin output stdout buffer 0 n; decompress() end in decompress(); Gzip.dispose ic end else begin (* compress *) let oc = Gzip.open_out_chan stdout in let rec compress () = let n = input stdin buffer 0 (Bytes.length buffer) in if n = 0 then () else begin Gzip.output oc buffer 0 n; Gzip.flush_continue oc; compress() end in compress(); Gzip.flush oc end xavierleroy-camlzip-72080af/test/minigzip.run000066400000000000000000000003641464254112700214150ustar00rootroot00000000000000#!/bin/sh runtest() { if $2 < minigzip.ml | $3 -d | cmp - minigzip.ml then echo "$1: passed" else echo "$1: FAILED"; exit 2 fi } runtest "Gzip 1" ./minigzip ./minigzip runtest "Gzip 2" ./minigzip gzip runtest "Gzip 3" gzip ./minigzip xavierleroy-camlzip-72080af/test/minizip.ml000066400000000000000000000063171464254112700210560ustar00rootroot00000000000000(***********************************************************************) (* *) (* The CamlZip library *) (* *) (* Xavier Leroy, projet Cristal, INRIA Rocquencourt *) (* *) (* Copyright 2001 Institut National de Recherche en Informatique et *) (* en Automatique. All rights reserved. This file is distributed *) (* under the terms of the GNU Library General Public License, with *) (* the special exception on linking described in file LICENSE. *) (* *) (***********************************************************************) (* $Id$ *) open Printf let list_entry e = let t = Unix.localtime e.Zip.mtime in printf "%6d %6d %c %04d-%02d-%02d %02d:%02d %c %s\n" e.Zip.uncompressed_size e.Zip.compressed_size (match e.Zip.methd with Zip.Stored -> 's' | Zip.Deflated -> 'd') (t.Unix.tm_year + 1900) (t.Unix.tm_mon + 1) t.Unix.tm_mday t.Unix.tm_hour t.Unix.tm_min (if e.Zip.is_directory then 'd' else ' ') e.Zip.filename; if e.Zip.comment <> "" then printf " %s\n" e.Zip.comment let list zipfile = let ic = Zip.open_in zipfile in if Zip.comment ic <> "" then printf "%s\n" (Zip.comment ic); List.iter list_entry (Zip.entries ic); Zip.close_in ic let extract_entry ifile e = print_string e.Zip.filename; print_newline(); if e.Zip.is_directory then begin try Unix.mkdir e.Zip.filename 0o777 with Unix.Unix_error(Unix.EEXIST, _, _) -> () end else begin Zip.copy_entry_to_file ifile e e.Zip.filename end let extract zipfile = let ic = Zip.open_in zipfile in List.iter (extract_entry ic) (Zip.entries ic); Zip.close_in ic let rec add_entry oc file = let s = Unix.stat file in match s.Unix.st_kind with Unix.S_REG -> printf "Adding file %s\n" file; flush stdout; Zip.copy_file_to_entry file oc ~mtime:s.Unix.st_mtime file | Unix.S_DIR -> printf "Adding directory %s\n" file; flush stdout; Zip.add_entry "" oc ~mtime:s.Unix.st_mtime (if Filename.check_suffix file "/" then file else file ^ "/"); let d = Unix.opendir file in begin try while true do let e = Unix.readdir d in if e <> "." && e <> ".." then add_entry oc (Filename.concat file e) done with End_of_file -> () end; Unix.closedir d | _ -> () let create zipfile files = let oc = Zip.open_out zipfile in Array.iter (add_entry oc) files; Zip.close_out oc let usage() = prerr_string "Usage: minizip t show contents of minizip x extract files from minizip c .. create a with the given files\n"; exit 2 let _ = if Array.length Sys.argv < 3 then usage(); match Sys.argv.(1) with "t" -> list Sys.argv.(2) | "x" -> extract Sys.argv.(2) | "c" -> create Sys.argv.(2) (Array.sub Sys.argv 3 (Array.length Sys.argv - 3)) | _ -> usage() xavierleroy-camlzip-72080af/test/minizip.run000066400000000000000000000007431464254112700212470ustar00rootroot00000000000000#!/bin/sh here=`pwd` runtest() { d=/tmp/minizip$$ zf=/tmp/minizip$$.zip rm -rf $d $zf mkdir $d if $2 $zf * > /dev/null && \ (cd $d && $3 $zf > /dev/null) && \ cmp minizip.ml $d/minizip.ml && \ cmp minigzip.ml $d/minigzip.ml then rm -rf $d $zf; echo "$1: passed" else rm -rf $d $zf; echo "$1: FAILED"; exit 2 fi } runtest "Zip 1" "$here/minizip c" "$here/minizip x" runtest "Zip 2" "zip -r" "$here/minizip x" runtest "Zip 3" "$here/minizip c" "unzip" xavierleroy-camlzip-72080af/test/testzlib.ml000066400000000000000000000012521464254112700212300ustar00rootroot00000000000000let compress infile outfile = let ic = open_in_bin infile and oc = open_out_bin outfile in Zlib.compress (fun buf -> input ic buf 0 (Bytes.length buf)) (fun buf len -> output oc buf 0 len); close_in ic; close_out oc let uncompress infile outfile = let ic = open_in_bin infile and oc = open_out_bin outfile in Zlib.uncompress (fun buf -> input ic buf 0 (Bytes.length buf)) (fun buf len -> output oc buf 0 len); close_in ic; close_out oc let _ = if Array.length Sys.argv >= 4 && Sys.argv.(1) = "-d" then uncompress Sys.argv.(2) Sys.argv.(3) else if Array.length Sys.argv >= 3 then compress Sys.argv.(1) Sys.argv.(2) xavierleroy-camlzip-72080af/test/testzlib.run000066400000000000000000000003141464254112700214220ustar00rootroot00000000000000#!/bin/sh t1=`mktemp` t2=`mktemp` if ./testzlib testzlib.ml $t1 && ./testzlib -d $t1 $t2 && cmp $t2 testzlib.ml then rm -f $t1 $t2; echo "Zlib: passed" else rm -f $t1 $t2; echo "Zlib: FAILED"; exit 2 fi xavierleroy-camlzip-72080af/zip.ml000066400000000000000000000704661464254112700172300ustar00rootroot00000000000000(***********************************************************************) (* *) (* The CamlZip library *) (* *) (* Xavier Leroy, projet Cristal, INRIA Rocquencourt *) (* *) (* Copyright 2001 Institut National de Recherche en Informatique et *) (* en Automatique. All rights reserved. This file is distributed *) (* under the terms of the GNU Lesser General Public License, with *) (* the special exception on linking described in file LICENSE. *) (* *) (***********************************************************************) (* $Id$ *) (* Module [Zip]: reading and writing ZIP archives *) exception Error of string * string * string let int64_of_uint32 n = Int64.(logand (of_int32 n) 0xFFFF_FFFFL) let read1 = input_byte let read2 ic = let lb = read1 ic in let hb = read1 ic in lb lor (hb lsl 8) let read4 ic = let lw = read2 ic in let hw = read2 ic in Int32.logor (Int32.of_int lw) (Int32.shift_left (Int32.of_int hw) 16) let read8 ic = let ll = read4 ic in let hl = read4 ic in Int64.logor (int64_of_uint32 ll) (Int64.shift_left (int64_of_uint32 hl) 32) let readstring ic n = let s = Bytes.create n in really_input ic s 0 n; Bytes.unsafe_to_string s let write1 = output_byte let write2 oc n = write1 oc n; write1 oc (n lsr 8) let write4 oc n = write2 oc (Int32.to_int n); write2 oc (Int32.to_int (Int32.shift_right_logical n 16)) let write8 oc n = write4 oc (Int64.to_int32 n); write4 oc (Int64.to_int32 (Int64.shift_right_logical n 32)) let writestring oc s = output_string oc s type compression_method = Stored | Deflated type entry = { filename: string; comment: string; methd: compression_method; mtime: float; crc: int32; uncompressed_size: int; compressed_size: int; is_directory: bool; file_offset: int64 } type in_file = { if_filename: string; if_channel: Stdlib.in_channel; if_entries: entry list; if_directory: (string, entry) Hashtbl.t; if_comment: string } let entries ifile = ifile.if_entries let comment ifile = ifile.if_comment type out_file = { of_filename: string; of_channel: Stdlib.out_channel; mutable of_entries: entry list; of_comment: string } (* Return the position of the last occurrence of [pattern] in [buf], or -1 if not found. *) let strrstr (pattern: string) (buf: bytes) ofs len = let rec search i j = if i < ofs then -1 else if j >= String.length pattern then i else if String.get pattern j = Bytes.get buf (i + j) then search i (j+1) else search (i-1) 0 in search (ofs + len - String.length pattern) 0 (* Determine if a file name is a directory (ends with /) *) let filename_is_directory name = String.length name > 0 && name.[String.length name - 1] = '/' (* Convert between Unix dates and DOS dates *) let unixtime_of_dostime time date = fst(Unix.mktime { Unix.tm_sec = (time lsl 1) land 0x3e; Unix.tm_min = (time lsr 5) land 0x3f; Unix.tm_hour = (time lsr 11) land 0x1f; Unix.tm_mday = date land 0x1f; Unix.tm_mon = ((date lsr 5) land 0xf) - 1; Unix.tm_year = ((date lsr 9) land 0x7f) + 80; Unix.tm_wday = 0; Unix.tm_yday = 0; Unix.tm_isdst = false }) let dostime_of_unixtime t = let tm = Unix.localtime t in (tm.Unix.tm_sec lsr 1 + (tm.Unix.tm_min lsl 5) + (tm.Unix.tm_hour lsl 11), tm.Unix.tm_mday + (tm.Unix.tm_mon + 1) lsl 5 + (tm.Unix.tm_year - 80) lsl 9) (* Parse the extra fields attached to some other structures *) let parse_extra_field ef = let rec parse accu pos = if pos + 4 > String.length ef then List.rev accu else begin let id = String.get_uint16_le ef pos in let sz = String.get_uint16_le ef (pos + 2) in let sz = min sz (String.length ef - (pos + 4)) in let data = String.sub ef (pos + 4) sz in parse ((id, data) :: accu) (pos + 4 + sz) end in parse [] 0 (* Locate the end of central directory record *) let locate_ecd filename ic = let buf = Bytes.create 256 in let filelen = LargeFile.in_channel_length ic in let rec find_ecd pos len = (* On input, bytes 0 ... len - 1 of buf reflect what is at pos in ic *) if pos <= 0L || Int64.sub filelen pos >= 0x10000L then raise (Error(filename, "", "end of central directory not found, not a ZIP file")); let toread = if pos >= 128L then 128 else Int64.to_int pos in (* Make room for "toread" extra bytes, and read them *) Bytes.blit buf 0 buf toread (256 - toread); let newpos = Int64.(sub pos (of_int toread)) in LargeFile.seek_in ic newpos; really_input ic buf 0 toread; let newlen = min (toread + len) 256 in (* Search for magic number *) let ofs = strrstr "PK\005\006" buf 0 newlen in if ofs >= 0 && newlen >= 22 && (let comment_len = Bytes.get_uint16_le buf (ofs + 20) in Int64.(add newpos (of_int (ofs + 22 + comment_len)))= filelen) then Int64.(add newpos (of_int ofs)) else find_ecd newpos newlen in find_ecd filelen 0 (* Read ZIP64 end of central directory record locator *) let read_ecd64_locator filename ic ecd_pos = if ecd_pos < 20L then raise(Error(filename, "", "ZIP64 ECD record locator missing")); let ecd64_locator_pos = Int64.(sub ecd_pos (of_int 20)) in LargeFile.seek_in ic ecd64_locator_pos ; let magic = read4 ic in if magic <> 0x07064b50l then raise(Error(filename, "", "ZIP64 ECD record locator missing")); let disk_no = read4 ic in let ecd64_offset = read8 ic in let n_disks = read4 ic in if disk_no <> 0l || n_disks <> 0l then raise (Error(filename, "", "multi-disk ZIP files not supported")); ecd64_offset (* Read ZIP64 end of central directory record *) type cd_info = { cd_offset: int64; (* file position of start of CD *) cd_size: int64; (* size of CD in bytes *) cd_count: int64; (* number of CD entries *) ecd_comment: string } let read_ecd64 filename ic ecd_pos comment = let ecd64_pos = read_ecd64_locator filename ic ecd_pos in LargeFile.seek_in ic ecd64_pos ; let magic = read4 ic in if magic <> 0x06064b50l then raise(Error(filename, "", "ZIP64 ECD record missing")); let _size = read8 ic in let _version_made_by = read2 ic in let version_needed = read2 ic in let n_disks = read4 ic in let cd_disk_no = read4 ic in let _disk_n_entries = read8 ic in let cd_count = read8 ic in let cd_size = read8 ic in let cd_offset = read8 ic in if version_needed > 45 then raise(Error(filename, filename, "unsupported ZIP version")); if cd_disk_no <> 0l || n_disks <> 0l then raise (Error(filename, "", "multi-disk ZIP files not supported")); { cd_offset; cd_size; cd_count; ecd_comment = comment } (* Read end of central directory record *) let read_ecd filename ic = let ecd_pos = locate_ecd filename ic in LargeFile.seek_in ic ecd_pos; let magic = read4 ic in let disk_no = read2 ic in let cd_disk_no = read2 ic in let _disk_entries = read2 ic in let cd_entries = read2 ic in let cd_size = read4 ic in let cd_offset = read4 ic in let comment_len = read2 ic in let comment = readstring ic comment_len in assert (magic = Int32.of_int 0x06054b50); if disk_no <> 0 || cd_disk_no <> 0 then raise (Error(filename, "", "multi-disk ZIP files not supported")); if cd_offset = 0xffff_ffffl || cd_size = 0xffff_ffffl then read_ecd64 filename ic ecd_pos comment else { cd_offset = int64_of_uint32 cd_offset; cd_size = int64_of_uint32 cd_size; cd_count = Int64.of_int cd_entries; ecd_comment = comment } (* Fixup sizes from a ZIP64 extended information extra field *) let fixup_sizes extra uncompressed_size compressed_size offset = let pos = ref 0 in let process orig = if orig <> 0xFFFF_FFFFl then int64_of_uint32 orig else begin let newval = String.get_int64_le extra !pos in pos := !pos + 8; newval end in let uncompressed_size = process uncompressed_size in let compressed_size = process compressed_size in let offset = process offset in (uncompressed_size, compressed_size, offset) (* Read central directory entry *) let read_directory_entry filename ic = let magic = read4 ic in if magic <> 0x02014b50l then raise (Error(filename, "", "wrong file header in central directory")); let _version_made_by = read2 ic in let version_needed = read2 ic in let flags = read2 ic in let methd = read2 ic in let lastmod_time = read2 ic in let lastmod_date = read2 ic in let crc = read4 ic in let compr_size = read4 ic in let uncompr_size = read4 ic in let name_len = read2 ic in let extra_len = read2 ic in let comment_len = read2 ic in let _disk_number = read2 ic in let _internal_attr = read2 ic in let _external_attr = read4 ic in let header_offset = read4 ic in let name = readstring ic name_len in let extra = readstring ic extra_len in let comment = readstring ic comment_len in if version_needed > 45 then raise(Error(filename, name, "unsupported ZIP version")); if flags land 1 <> 0 then raise (Error(filename, name, "encrypted entries not supported")); let (uncompressed_size, compressed_size, file_offset) = if compr_size <> 0xffff_ffffl && uncompr_size <> 0xffff_ffffl && header_offset <> 0xffff_ffffl then (int64_of_uint32 uncompr_size, int64_of_uint32 compr_size, int64_of_uint32 header_offset) else begin match List.assoc_opt 1 (parse_extra_field extra) with | None -> raise(Error(filename, name, "ZIP64 extensible data record missing")) | Some e -> fixup_sizes e uncompr_size compr_size header_offset end in let int_of_uint64 n = if n >= 0L && n <= Int64.of_int max_int then Int64.to_int n else raise(Error(filename, name, "size too large to be represented")) in { filename = name; comment = comment; methd = (match methd with | 0 -> Stored | 8 -> Deflated | _ -> raise (Error(filename, name, "unknown compression method"))); mtime = unixtime_of_dostime lastmod_time lastmod_date; crc = crc; uncompressed_size = int_of_uint64 uncompressed_size; compressed_size = int_of_uint64 compressed_size; is_directory = filename_is_directory name; file_offset } (* Read central directory *) let read_cd filename ic cdinfo = try LargeFile.seek_in ic cdinfo.cd_offset; let entries = ref [] in let entrycnt = ref Int64.zero in let cd_bound = Int64.add cdinfo.cd_offset cdinfo.cd_size in while LargeFile.pos_in ic < cd_bound do entrycnt := Int64.(add !entrycnt one) ; let e = read_directory_entry filename ic in entries := e :: !entries done; if cd_bound <> LargeFile.pos_in ic || (cdinfo.cd_count <> !entrycnt && cdinfo.cd_count <> 0xFFFFL) then raise(Error(filename, "", "wrong number of entries in central directory")); List.rev !entries with End_of_file -> raise (Error(filename, "", "end-of-file while reading central directory")) (* Open a ZIP file for reading *) let open_in filename = let ic = Stdlib.open_in_bin filename in try let cdinfo = read_ecd filename ic in let entries = read_cd filename ic cdinfo in let table_size = match Int64.(div cdinfo.cd_count 3L |> unsigned_to_int) with Some sz -> sz | None -> 65535 in let dir = Hashtbl.create table_size in List.iter (fun e -> Hashtbl.add dir e.filename e) entries; { if_filename = filename; if_channel = ic; if_entries = entries; if_directory = dir; if_comment = cdinfo.ecd_comment } with exn -> Stdlib.close_in ic; raise exn (* Close a ZIP file opened for reading *) let close_in ifile = Stdlib.close_in ifile.if_channel (* Return the info associated with an entry *) let find_entry ifile name = Hashtbl.find ifile.if_directory name (* Position on an entry *) let goto_entry ifile e = try let ic = ifile.if_channel in LargeFile.seek_in ic e.file_offset; let magic = read4 ic in if magic <> 0x04034b50l then raise (Error(ifile.if_filename, e.filename, "wrong local file header")); let _version_needed = read2 ic in let _flags = read2 ic in let _methd = read2 ic in let _lastmod_time = read2 ic in let _lastmod_date = read2 ic in let _crc = read4 ic in let _compr_size = read4 ic in let _uncompr_size = read4 ic in let filename_len = read2 ic in let extra_len = read2 ic in (* Could validate information read against directory entry, but what the heck *) LargeFile.seek_in ifile.if_channel (Int64.add e.file_offset (Int64.of_int (30 + filename_len + extra_len))) with End_of_file -> raise (Error(ifile.if_filename, e.filename, "truncated local file header")) (* Read the contents of an entry as a string *) let read_entry ifile e = try goto_entry ifile e; let res = Bytes.create e.uncompressed_size in match e.methd with Stored -> if e.compressed_size <> e.uncompressed_size then raise (Error(ifile.if_filename, e.filename, "wrong size for stored entry")); really_input ifile.if_channel res 0 e.uncompressed_size; Bytes.unsafe_to_string res | Deflated -> let in_avail = ref e.compressed_size in let out_pos = ref 0 in begin try Zlib.uncompress ~header:false (fun buf -> let read = input ifile.if_channel buf 0 (min !in_avail (Bytes.length buf)) in in_avail := !in_avail - read; read) (fun buf len -> if !out_pos + len > Bytes.length res then raise (Error(ifile.if_filename, e.filename, "wrong size for deflated entry (too much data)")); Bytes.blit buf 0 res !out_pos len; out_pos := !out_pos + len) with Zlib.Error(_, _) -> raise (Error(ifile.if_filename, e.filename, "decompression error")) end; if !out_pos <> Bytes.length res then raise (Error(ifile.if_filename, e.filename, "wrong size for deflated entry (not enough data)")); let crc = Zlib.update_crc Int32.zero res 0 (Bytes.length res) in if crc <> e.crc then raise (Error(ifile.if_filename, e.filename, "CRC mismatch")); Bytes.unsafe_to_string res with End_of_file -> raise (Error(ifile.if_filename, e.filename, "truncated data")) (* Write the contents of an entry into an out channel *) let copy_entry_to_channel ifile e oc = try goto_entry ifile e; match e.methd with Stored -> if e.compressed_size <> e.uncompressed_size then raise (Error(ifile.if_filename, e.filename, "wrong size for stored entry")); let buf = Bytes.create 4096 in let rec copy n = if n > 0 then begin let r = input ifile.if_channel buf 0 (min n (Bytes.length buf)) in output oc buf 0 r; copy (n - r) end in copy e.uncompressed_size | Deflated -> let in_avail = ref e.compressed_size in let crc = ref Int32.zero in begin try Zlib.uncompress ~header:false (fun buf -> let read = input ifile.if_channel buf 0 (min !in_avail (Bytes.length buf)) in in_avail := !in_avail - read; read) (fun buf len -> output oc buf 0 len; crc := Zlib.update_crc !crc buf 0 len) with Zlib.Error(_, _) -> raise (Error(ifile.if_filename, e.filename, "decompression error")) end; if !crc <> e.crc then raise (Error(ifile.if_filename, e.filename, "CRC mismatch")) with End_of_file -> raise (Error(ifile.if_filename, e.filename, "truncated data")) (* Write the contents of an entry to a file *) let copy_entry_to_file ifile e outfilename = let oc = open_out_bin outfilename in try copy_entry_to_channel ifile e oc; close_out oc; begin try Unix.utimes outfilename e.mtime e.mtime with Unix.Unix_error(_, _, _) | Invalid_argument _ -> () end with x -> close_out oc; Sys.remove outfilename; raise x (* Open a ZIP file for writing *) let open_out ?(comment = "") filename = if String.length comment >= 0x10000 then raise(Error(filename, "", "comment too long")); { of_filename = filename; of_channel = Stdlib.open_out_bin filename; of_entries = []; of_comment = comment } (* Close a ZIP file for writing. Add central directory and ECD. *) let write4_cautious oc ov n = write4 oc (if ov then 0xFFFF_FFFFl else Int64.to_int32 n) let write_directory_entry oc e = let overflow = e.file_offset > 0xFFFF_FFFFL || Int64.of_int e.compressed_size > 0xFFFF_FFFFL || Int64.of_int e.uncompressed_size > 0xFFFF_FFFFL in write4 oc 0x02014b50l; (* signature *) let version = match e.methd with Stored -> 10 | Deflated -> 20 in write2 oc version; (* version made by *) write2 oc version; (* version needed to extract *) write2 oc 8; (* flags *) write2 oc (match e.methd with Stored -> 0 | Deflated -> 8); (* method *) let (time, date) = dostime_of_unixtime e.mtime in write2 oc time; (* last mod time *) write2 oc date; (* last mod date *) write4 oc e.crc; (* CRC32 *) write4_cautious oc overflow (Int64.of_int e.compressed_size); (* compressed size *) write4_cautious oc overflow (Int64.of_int e.uncompressed_size); (* uncompressed size *) write2 oc (String.length e.filename); (* filename length *) write2 oc (if overflow then 28 else 0); (* extra length *) write2 oc (String.length e.comment); (* comment length *) write2 oc 0; (* disk number start *) write2 oc 0; (* internal attributes *) write4 oc 0l; (* external attributes *) write4_cautious oc overflow e.file_offset; (* offset of local header *) writestring oc e.filename; (* filename *) if overflow then begin (* extra data *) write2 oc 0x0001; (* header ID *) write2 oc 24; (* payload size *) write8 oc (Int64.of_int e.uncompressed_size); write8 oc (Int64.of_int e.compressed_size); write8 oc e.file_offset end; writestring oc e.comment (* file comment *) let close_out ofile = let oc = ofile.of_channel in let start_cd = LargeFile.pos_out oc in List.iter (write_directory_entry oc) (List.rev ofile.of_entries); let start_ecd = LargeFile.pos_out oc in let cd_size = Int64.sub start_ecd start_cd in let num_entries = List.length ofile.of_entries in let overflow = num_entries > 0xFFFF || start_cd > 0xFFFF_FFFFL || cd_size > 0xFFFF_FFFFL in if overflow then begin (* Write ZIP64 end of central directory record *) write4 oc 0x06064b50l; (* signature *) write8 oc 44L; (* size ECD record *) write2 oc 45; (* version made *) write2 oc 45; (* version needed *) write4 oc 0l; (* disk number *) write4 oc 0l; (* CD disk number *) let ne = Int64.of_int num_entries in write8 oc ne; (* num disk entries *) write8 oc ne; (* num entries *) write8 oc cd_size; (* size of the CD *) write8 oc start_cd; (* start offset for CD *) (* Write ZIP64 end of central directory locator *) write4 oc 0x07064b50l; (* signature *) write4 oc 0l; (* CD disk number *) write8 oc start_ecd; (* Position of ECD record *) write4 oc 0l (* number of disks *) end; (* Write ZIP end of central directory record *) write4 oc 0x06054b50l; (* signature *) write2 oc 0; (* disk number *) write2 oc 0; (* number of disk with central dir *) let ne = if overflow then 0xFFFF else num_entries in write2 oc ne; (* # entries in this disk *) write2 oc ne; (* # entries in central dir *) write4_cautious oc overflow cd_size; (* size of central dir *) write4_cautious oc overflow start_cd; (* offset of central dir *) write2 oc (String.length ofile.of_comment); (* length of comment *) writestring oc ofile.of_comment; (* comment *) Stdlib.close_out oc (* Write a local file header and return the corresponding entry *) let add_entry_header ofile comment level mtime filename = if level < 0 || level > 9 then raise(Error(ofile.of_filename, filename, "wrong compression level")); if String.length filename >= 0x10000 then raise(Error(ofile.of_filename, filename, "filename too long")); if String.length comment >= 0x10000 then raise(Error(ofile.of_filename, filename, "comment too long")); let oc = ofile.of_channel in let pos = LargeFile.pos_out oc in write4 oc 0x04034b50l; (* signature *) let version = if level = 0 then 10 else 20 in write2 oc version; (* version needed to extract *) write2 oc 0; (* flags *) write2 oc (if level = 0 then 0 else 8); (* method *) let (time, date) = dostime_of_unixtime mtime in write2 oc time; (* last mod time *) write2 oc date; (* last mod date *) write4 oc 0l; (* CRC32 - to be filled later *) write4 oc 0l; (* compressed size - later *) write4 oc 0l; (* uncompressed size - later *) write2 oc (String.length filename); (* filename length *) write2 oc 20; (* extra length *) writestring oc filename; (* filename *) write2 oc 0x0001; (* extra data - header ID *) write2 oc 16; (* payload size *) write8 oc 0L; (* compressed size - later *) write8 oc 0L; (* uncompressed size - later *) { filename = filename; comment = comment; methd = (if level = 0 then Stored else Deflated); mtime = mtime; crc = Int32.zero; uncompressed_size = 0; compressed_size = 0; is_directory = filename_is_directory filename; file_offset = pos } (* Write the correct sizes and CRC in the local file header and update the entry *) let update_entry ofile crc compr_size uncompr_size entry = let csz = Int64.of_int compr_size and usz = Int64.of_int uncompr_size in let overflow = csz > 0xFFFF_FFFFL || usz > 0xFFFF_FFFFL in let oc = ofile.of_channel in let cur = LargeFile.pos_out oc in LargeFile.seek_out oc (Int64.add entry.file_offset 14L); write4 oc crc; (* CRC *) write4_cautious oc overflow csz; (* compressed size *) write4_cautious oc overflow usz; (* uncompressed size *) if overflow then begin LargeFile.seek_out oc Int64.(add entry.file_offset (of_int (30 + String.length entry.filename + 4))); write8 oc csz; (* compressed size *) write8 oc usz (* uncompressed size *) end; LargeFile.seek_out oc cur; { entry with crc = crc; uncompressed_size = uncompr_size; compressed_size = compr_size } (* Add an entry with the contents of a string *) let add_entry data ofile ?(comment = "") ?(level = 6) ?(mtime = Unix.time()) name = let e = add_entry_header ofile comment level mtime name in let crc = Zlib.update_crc_string Int32.zero data 0 (String.length data) in let compr_size = match level with 0 -> output_substring ofile.of_channel data 0 (String.length data); String.length data | _ -> let in_pos = ref 0 in let out_pos = ref 0 in try Zlib.compress ~level ~header:false (fun buf -> let n = min (String.length data - !in_pos) (Bytes.length buf) in String.blit data !in_pos buf 0 n; in_pos := !in_pos + n; n) (fun buf n -> output ofile.of_channel buf 0 n; out_pos := !out_pos + n); !out_pos with Zlib.Error(_, _) -> raise (Error(ofile.of_filename, name, "compression error")) in let e' = update_entry ofile crc compr_size (String.length data) e in ofile.of_entries <- e' :: ofile.of_entries (* Add an entry with the contents of an in channel *) let copy_channel_to_entry ic ofile ?(comment = "") ?(level = 6) ?(mtime = Unix.time()) name = let e = add_entry_header ofile comment level mtime name in let crc = ref Int32.zero in let (compr_size, uncompr_size) = match level with 0 -> let buf = Bytes.create 4096 in let rec copy sz = let r = input ic buf 0 (Bytes.length buf) in if r = 0 then sz else begin crc := Zlib.update_crc !crc buf 0 r; output ofile.of_channel buf 0 r; copy (sz + r) end in let size = copy 0 in (size, size) | _ -> let in_pos = ref 0 in let out_pos = ref 0 in try Zlib.compress ~level ~header:false (fun buf -> let r = input ic buf 0 (Bytes.length buf) in crc := Zlib.update_crc !crc buf 0 r; in_pos := !in_pos + r; r) (fun buf n -> output ofile.of_channel buf 0 n; out_pos := !out_pos + n); (!out_pos, !in_pos) with Zlib.Error(_, _) -> raise (Error(ofile.of_filename, name, "compression error")) in let e' = update_entry ofile !crc compr_size uncompr_size e in ofile.of_entries <- e' :: ofile.of_entries (* Add an entry with the contents of a file *) let copy_file_to_entry infilename ofile ?(comment = "") ?(level = 6) ?mtime name = let ic = open_in_bin infilename in let mtime' = match mtime with Some t -> mtime | None -> try Some((Unix.stat infilename).Unix.st_mtime) with Unix.Unix_error(_,_,_) -> None in try copy_channel_to_entry ic ofile ~comment ~level ?mtime:mtime' name; Stdlib.close_in ic with x -> Stdlib.close_in ic; raise x (* Add an entry whose content will be produced by the caller *) let add_entry_generator ofile ?(comment = "") ?(level = 6) ?(mtime = Unix.time()) name = let e = add_entry_header ofile comment level mtime name in let crc = ref Int32.zero in let compr_size = ref 0 in let uncompr_size = ref 0 in let finished = ref false in let check () = if !finished then raise (Error(ofile.of_filename, name, "entry already finished")) in let finish () = finished := true; let e' = update_entry ofile !crc !compr_size !uncompr_size e in ofile.of_entries <- e' :: ofile.of_entries in match level with | 0 -> (fun buf pos len -> check (); output ofile.of_channel buf pos len; compr_size := !compr_size + len; uncompr_size := !uncompr_size + len; crc := Zlib.update_crc !crc buf pos len ), (fun () -> check (); finish () ) | _ -> let (send, flush) = Zlib.compress_direct ~level ~header:false (fun buf n -> output ofile.of_channel buf 0 n; compr_size := !compr_size + n) in (fun buf pos len -> check (); try send buf pos len; uncompr_size := !uncompr_size + len; crc := Zlib.update_crc !crc buf pos len with Zlib.Error(_, _) -> raise (Error(ofile.of_filename, name, "compression error")) ), (fun () -> check (); try flush (); finish () with Zlib.Error(_, _) -> raise (Error(ofile.of_filename, name, "compression error")) ) xavierleroy-camlzip-72080af/zip.mli000066400000000000000000000217671464254112700174010ustar00rootroot00000000000000(***********************************************************************) (* *) (* The CamlZip library *) (* *) (* Xavier Leroy, projet Cristal, INRIA Rocquencourt *) (* *) (* Copyright 2001 Institut National de Recherche en Informatique et *) (* en Automatique. All rights reserved. This file is distributed *) (* under the terms of the GNU Lesser General Public License, with *) (* the special exception on linking described in file LICENSE. *) (* *) (***********************************************************************) (* $Id$ *) (** Reading and writing ZIP archives This module provides functions for reading and writing ZIP archive files. ZIP archives package one or more compressed files into a single ``ZIP file'' along with information about the files, including file name, date and time of last modification, user-provided comments, and a checksum to verify the integrity of each entry. The entries of a ZIP file are not necessarily actual files, and can actually consist of arbitrary data. The ZIP file format used in this module is identical to that implemented by the popular [pkzip] archiver under Windows, and by the Info-ZIP [zip] and [unzip] commands under Unix and Windows. This format is also identical to the JAR file format used by Java. *) (** {1 Information on ZIP entries} *) type compression_method = | Stored (** data is stored without compression *) | Deflated (** data is compressed with the ``deflate'' algorithm *) (** Indicate whether the data in the entry is compressed or not. *) type entry = { filename: string; (** file name for entry *) comment: string; (** comment attached to entry *) methd: compression_method; (** compression method *) mtime: float; (** last modification time (seconds since epoch) *) crc: int32; (** cyclic redundancy check for data *) uncompressed_size: int; (** size of original data in bytes *) compressed_size: int; (** size of compressed data *) is_directory: bool; (** whether this entry represents a directory *) file_offset: int64 (** for internal use *) } (** Description of an entry in a ZIP file. *) (** {1 Reading from ZIP files} *) type in_file (** Abstract type representing a handle opened for reading from a ZIP file. *) val open_in: string -> in_file (** Open the ZIP file with the given filename. Return a handle opened for reading from this file. *) val entries: in_file -> entry list (** Return a list of all entries in the given ZIP file. *) val comment: in_file -> string (** Return the comment attached to the given ZIP file, or the empty string if none. *) val find_entry: in_file -> string -> entry (** [Zip.find_entry zf filename] returns the description of the entry having name [filename] in the ZIP file [zf]. Raises [Not_found] if no such entry exists. The file name must match exactly; in particular, case is significant. File names must use [/] (slash) as the directory separator. The name of a directory must end with a trailing [/] (slash). *) val read_entry: in_file -> entry -> string (** [Zip.read_entry zf e] reads and uncompresses the data (file contents) associated with entry [e] of ZIP file [zf]. The data is returned as a character string. *) val copy_entry_to_channel: in_file -> entry -> out_channel -> unit (** [Zip.copy_entry_to_channel zf e oc] reads and uncompresses the data associated with entry [e] of ZIP file [zf]. It then writes this data to the output channel [oc]. *) val copy_entry_to_file: in_file -> entry -> string -> unit (** [Zip.copy_entry_to_file zf e destfile] reads and uncompresses the data associated with entry [e] of ZIP file [zf]. It then writes this data to the file named [destfile]. The file [destfile] is created if it does not exist, and overwritten otherwise. The last modification date of the file is set to that indicated in the ZIP entry [e], if possible. *) val close_in: in_file -> unit (** Close the given ZIP file handle. If the ZIP file handle was created by [open_in_channel], the underlying input channel is closed. *) (** {1 Writing to ZIP files} *) type out_file (** Abstract type representing a handle opened for writing to a ZIP file. *) val open_out: ?comment: string -> string -> out_file (** Create (or truncate to zero length) the ZIP file with the given filename. Return a handle opened for writing to this file. The optional argument [comment] is a comment string that is attached to the ZIP file as a whole (as opposed to the comments that can be attached to individual ZIP entries). *) val add_entry: string -> out_file -> ?comment: string -> ?level: int -> ?mtime: float -> string -> unit (** [Zip.add_entry data zf name] adds a new entry to the ZIP file [zf]. The data (file contents) associated with the entry is taken from the string [data]. It is compressed and written to the ZIP file [zf]. [name] is the file name stored along with this entry. Several optional arguments can be provided to control the format and attached information of the entry: @param comment attached to the entry (a string). Default: empty. @param level compression level for the entry. This is an integer between 0 and 9, with 0 meaning no compression (store as is), 1 lowest compression, 9 highest compression. Higher levels result in smaller compressed data, but longer compression times. Default: 6 (moderate compression). @param mtime last modification time (in seconds since the epoch). Default: the current time. *) val copy_channel_to_entry: in_channel -> out_file -> ?comment: string -> ?level: int -> ?mtime: float -> string -> unit (** Same as [Zip.add_entry], but the data associated with the entry is read from the input channel given as first argument. The channel is read up to end of file. *) val copy_file_to_entry: string -> out_file -> ?comment: string -> ?level: int -> ?mtime: float -> string -> unit (** Same as [Zip.add_entry], but the data associated with the entry is read from the file whose name is given as first argument. Also, the default value for the [mtime] optional parameter is the time of last modification of the file. *) val add_entry_generator: out_file -> ?comment: string -> ?level: int -> ?mtime: float -> string -> (bytes -> int -> int -> unit) * (unit -> unit) (** [Zip.add_entry_generator zf name] returns a pair of functions [(add, finish)]. It adds a new entry to the ZIP file [zf]. The file name stored along with this entry is [name]. Initially, no data is stored in this entry. To store data in this entry, the program must repeatedly call the [add] function returned by [Zip.add_entry_generator]. An invocation [add s ofs len] stores [len] characters of byte sequence [s] starting at offset [ofs] in the ZIP entry. When all the data forming the entry has been sent, the program must call the [finish] function returned by [Zip.add_entry_generator]. [finish] must be called exactly once. The optional arguments to [Zip.add_entry_generator] are as described in {!Zip.add_entry}. *) val close_out: out_file -> unit (** Finish writing the ZIP archive by adding the table of contents, and close it. *) (** {1 Error reporting} *) exception Error of string * string * string (** Exception raised when an ill-formed ZIP archive is encountered, or illegal parameters are given to the functions in this module. The exception is of the form [Error(ZIP_name, entry_name, message)] where [ZIP_name] is the name of the ZIP file, [entry_name] the name of the offending entry, and [message] an explanation of the error. *) xavierleroy-camlzip-72080af/zlib.ml000066400000000000000000000113641464254112700173560ustar00rootroot00000000000000(***********************************************************************) (* *) (* The CamlZip library *) (* *) (* Xavier Leroy, projet Cristal, INRIA Rocquencourt *) (* *) (* Copyright 2001 Institut National de Recherche en Informatique et *) (* en Automatique. All rights reserved. This file is distributed *) (* under the terms of the GNU Lesser General Public License, with *) (* the special exception on linking described in file LICENSE. *) (* *) (***********************************************************************) (* $Id$ *) exception Error of string * string let _ = Callback.register_exception "Zlib.Error" (Error("","")) type stream type flush_command = Z_NO_FLUSH | Z_SYNC_FLUSH | Z_FULL_FLUSH | Z_FINISH external deflate_init: int -> bool -> stream = "camlzip_deflateInit" external deflate: stream -> bytes -> int -> int -> bytes -> int -> int -> flush_command -> bool * int * int = "camlzip_deflate_bytecode" "camlzip_deflate" external deflate_string: stream -> string -> int -> int -> bytes -> int -> int -> flush_command -> bool * int * int = "camlzip_deflate_bytecode" "camlzip_deflate" external deflate_end: stream -> unit = "camlzip_deflateEnd" external inflate_init: bool -> stream = "camlzip_inflateInit" external inflate: stream -> bytes -> int -> int -> bytes -> int -> int -> flush_command -> bool * int * int = "camlzip_inflate_bytecode" "camlzip_inflate" external inflate_string: stream -> string -> int -> int -> bytes -> int -> int -> flush_command -> bool * int * int = "camlzip_inflate_bytecode" "camlzip_inflate" external inflate_end: stream -> unit = "camlzip_inflateEnd" external update_crc: int32 -> bytes -> int -> int -> int32 = "camlzip_update_crc32" external update_crc_string: int32 -> string -> int -> int -> int32 = "camlzip_update_crc32" let buffer_size = 1024 let compress ?(level = 6) ?(header = true) refill flush = let inbuf = Bytes.create buffer_size and outbuf = Bytes.create buffer_size in let zs = deflate_init level header in let rec compr inpos inavail = if inavail = 0 then begin let incount = refill inbuf in if incount = 0 then compr_finish() else compr 0 incount end else begin let (_, used_in, used_out) = deflate zs inbuf inpos inavail outbuf 0 buffer_size Z_NO_FLUSH in flush outbuf used_out; compr (inpos + used_in) (inavail - used_in) end and compr_finish () = let (finished, _, used_out) = deflate zs inbuf 0 0 outbuf 0 buffer_size Z_FINISH in flush outbuf used_out; if not finished then compr_finish() in compr 0 0; deflate_end zs let compress_direct ?(level = 6) ?(header = true) flush = let outbuf = Bytes.create buffer_size in let zs = deflate_init level header in let rec compr inbuf inpos inavail = if inavail = 0 then () else begin let (_, used_in, used_out) = deflate zs inbuf inpos inavail outbuf 0 buffer_size Z_NO_FLUSH in flush outbuf used_out; compr inbuf (inpos + used_in) (inavail - used_in) end and compr_finish () = let (finished, _, used_out) = deflate zs (Bytes.unsafe_of_string "") 0 0 outbuf 0 buffer_size Z_FINISH in flush outbuf used_out; if not finished then compr_finish() else deflate_end zs in compr, compr_finish let uncompress ?(header = true) refill flush = let inbuf = Bytes.create buffer_size and outbuf = Bytes.create buffer_size in let zs = inflate_init header in let rec uncompr inpos inavail = if inavail = 0 then begin let incount = refill inbuf in if incount = 0 then uncompr_finish true else uncompr 0 incount end else begin let (finished, used_in, used_out) = inflate zs inbuf inpos inavail outbuf 0 buffer_size Z_SYNC_FLUSH in flush outbuf used_out; if not finished then uncompr (inpos + used_in) (inavail - used_in) end and uncompr_finish first_finish = (* Gotcha: if there is no header, inflate requires an extra "dummy" byte after the compressed stream in order to complete decompression and return finished = true. *) let dummy_byte = if first_finish && not header then 1 else 0 in let (finished, _, used_out) = inflate zs inbuf 0 dummy_byte outbuf 0 buffer_size Z_SYNC_FLUSH in flush outbuf used_out; if not finished then uncompr_finish false in uncompr 0 0; inflate_end zs xavierleroy-camlzip-72080af/zlib.mli000066400000000000000000000047121464254112700175260ustar00rootroot00000000000000(***********************************************************************) (* *) (* The CamlZip library *) (* *) (* Xavier Leroy, projet Cristal, INRIA Rocquencourt *) (* *) (* Copyright 2001 Institut National de Recherche en Informatique et *) (* en Automatique. All rights reserved. This file is distributed *) (* under the terms of the GNU Lesser General Public License, with *) (* the special exception on linking described in file LICENSE. *) (* *) (***********************************************************************) (* $Id$ *) exception Error of string * string val compress: ?level: int -> ?header: bool -> (bytes -> int) -> (bytes -> int -> unit) -> unit val compress_direct: ?level: int -> ?header: bool -> (bytes -> int -> unit) -> (bytes -> int -> int -> unit) * (unit -> unit) val uncompress: ?header: bool -> (bytes -> int) -> (bytes -> int -> unit) -> unit type stream type flush_command = Z_NO_FLUSH | Z_SYNC_FLUSH | Z_FULL_FLUSH | Z_FINISH external deflate_init: int -> bool -> stream = "camlzip_deflateInit" external deflate: stream -> bytes -> int -> int -> bytes -> int -> int -> flush_command -> bool * int * int = "camlzip_deflate_bytecode" "camlzip_deflate" external deflate_string: stream -> string -> int -> int -> bytes -> int -> int -> flush_command -> bool * int * int = "camlzip_deflate_bytecode" "camlzip_deflate" external deflate_end: stream -> unit = "camlzip_deflateEnd" external inflate_init: bool -> stream = "camlzip_inflateInit" external inflate: stream -> bytes -> int -> int -> bytes -> int -> int -> flush_command -> bool * int * int = "camlzip_inflate_bytecode" "camlzip_inflate" external inflate_string: stream -> string -> int -> int -> bytes -> int -> int -> flush_command -> bool * int * int = "camlzip_inflate_bytecode" "camlzip_inflate" external inflate_end: stream -> unit = "camlzip_inflateEnd" external update_crc: int32 -> bytes -> int -> int -> int32 = "camlzip_update_crc32" external update_crc_string: int32 -> string -> int -> int -> int32 = "camlzip_update_crc32" xavierleroy-camlzip-72080af/zlibstubs.c000066400000000000000000000152641464254112700202540ustar00rootroot00000000000000/***********************************************************************/ /* */ /* The CamlZip library */ /* */ /* Xavier Leroy, projet Cristal, INRIA Rocquencourt */ /* */ /* Copyright 2001 Institut National de Recherche en Informatique et */ /* en Automatique. All rights reserved. This file is distributed */ /* under the terms of the GNU Lesser General Public License, with */ /* the special exception on linking described in file LICENSE. */ /* */ /***********************************************************************/ /* $Id$ */ /* Stub code to interface with Zlib */ #include #include #include #include #include #include #include #include #define ZStream_val(v) (*((z_streamp *) Data_custom_val(v))) static const value * camlzip_error_exn = NULL; static void camlzip_error(char * fn, value vzs) { char * msg; value s1 = Val_unit, s2 = Val_unit, bucket = Val_unit; msg = ZStream_val(vzs)->msg; if (msg == NULL) msg = ""; if (camlzip_error_exn == NULL) { camlzip_error_exn = caml_named_value("Zlib.Error"); if (camlzip_error_exn == NULL) caml_invalid_argument("Exception Zlib.Error not initialized"); } Begin_roots3(s1, s2, bucket); s1 = caml_copy_string(fn); s2 = caml_copy_string(msg); bucket = caml_alloc_small(3, 0); Field(bucket, 0) = *camlzip_error_exn; Field(bucket, 1) = s1; Field(bucket, 2) = s2; End_roots(); caml_raise(bucket); } static void camlzip_free_dstream(value vzs) { deflateEnd(ZStream_val(vzs)); caml_stat_free(ZStream_val(vzs)); ZStream_val(vzs) = NULL; } static struct custom_operations camlzip_dstream_ops = { "camlzip_dstream_ops", &camlzip_free_dstream, NULL, NULL, NULL, NULL }; value camlzip_deflateInit(value vlevel, value expect_header) { value vzs = caml_alloc_custom_mem(&camlzip_dstream_ops, sizeof(z_streamp), sizeof(z_stream)); ZStream_val(vzs) = caml_stat_alloc(sizeof(z_stream)); /* Zlib API: the fields zalloc, zfree and opaque must be initialized */ ZStream_val(vzs)->zalloc = NULL; ZStream_val(vzs)->zfree = NULL; ZStream_val(vzs)->opaque = NULL; if (deflateInit2(ZStream_val(vzs), Int_val(vlevel), Z_DEFLATED, Bool_val(expect_header) ? MAX_WBITS : -MAX_WBITS, 8, Z_DEFAULT_STRATEGY) != Z_OK) camlzip_error("Zlib.deflateInit", vzs); return vzs; } static int camlzip_flush_table[] = { Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FULL_FLUSH, Z_FINISH }; value camlzip_deflate(value vzs, value srcbuf, value srcpos, value srclen, value dstbuf, value dstpos, value dstlen, value vflush) { z_stream * zs = ZStream_val(vzs); int retcode; long used_in, used_out; value res; zs->next_in = &Byte_u(srcbuf, Long_val(srcpos)); zs->avail_in = Long_val(srclen); zs->next_out = &Byte_u(dstbuf, Long_val(dstpos)); zs->avail_out = Long_val(dstlen); retcode = deflate(zs, camlzip_flush_table[Int_val(vflush)]); if (retcode < 0 && retcode != Z_BUF_ERROR) camlzip_error("Zlib.deflate", vzs); used_in = Long_val(srclen) - zs->avail_in; used_out = Long_val(dstlen) - zs->avail_out; zs->next_in = NULL; /* not required, but cleaner */ zs->next_out = NULL; /* (avoid dangling pointers into Caml heap) */ res = caml_alloc_small(3, 0); Field(res, 0) = Val_bool(retcode == Z_STREAM_END); Field(res, 1) = Val_int(used_in); Field(res, 2) = Val_int(used_out); return res; } value camlzip_deflate_bytecode(value * arg, int nargs) { return camlzip_deflate(arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], arg[6], arg[7]); } value camlzip_deflateEnd(value vzs) { if (deflateEnd(ZStream_val(vzs)) != Z_OK) camlzip_error("Zlib.deflateEnd", vzs); return Val_unit; } static void camlzip_free_istream(value vzs) { inflateEnd(ZStream_val(vzs)); caml_stat_free(ZStream_val(vzs)); ZStream_val(vzs) = NULL; } static struct custom_operations camlzip_istream_ops = { "camlzip_dstream_ops", &camlzip_free_istream, NULL, NULL, NULL, NULL }; value camlzip_inflateInit(value expect_header) { value vzs = caml_alloc_custom_mem(&camlzip_istream_ops, sizeof(z_streamp), sizeof(z_stream)); /* Zlib API: The fields next_in, avail_in, zalloc, zfree and opaque must be initialized */ ZStream_val(vzs) = caml_stat_alloc(sizeof(z_stream)); ZStream_val(vzs)->zalloc = NULL; ZStream_val(vzs)->zfree = NULL; ZStream_val(vzs)->opaque = NULL; ZStream_val(vzs)->next_in = NULL; ZStream_val(vzs)->avail_in = 0; if (inflateInit2(ZStream_val(vzs), Bool_val(expect_header) ? MAX_WBITS : -MAX_WBITS) != Z_OK) camlzip_error("Zlib.inflateInit", vzs); return vzs; } value camlzip_inflate(value vzs, value srcbuf, value srcpos, value srclen, value dstbuf, value dstpos, value dstlen, value vflush) { z_stream * zs = ZStream_val(vzs); int retcode; long used_in, used_out; value res; zs->next_in = &Byte_u(srcbuf, Long_val(srcpos)); zs->avail_in = Long_val(srclen); zs->next_out = &Byte_u(dstbuf, Long_val(dstpos)); zs->avail_out = Long_val(dstlen); retcode = inflate(zs, camlzip_flush_table[Int_val(vflush)]); if ((retcode < 0 && retcode != Z_BUF_ERROR) || retcode == Z_NEED_DICT) camlzip_error("Zlib.inflate", vzs); used_in = Long_val(srclen) - zs->avail_in; used_out = Long_val(dstlen) - zs->avail_out; zs->next_in = NULL; /* not required, but cleaner */ zs->next_out = NULL; /* (avoid dangling pointers into Caml heap) */ res = caml_alloc_small(3, 0); Field(res, 0) = Val_bool(retcode == Z_STREAM_END); Field(res, 1) = Val_int(used_in); Field(res, 2) = Val_int(used_out); return res; } value camlzip_inflate_bytecode(value * arg, int nargs) { return camlzip_inflate(arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], arg[6], arg[7]); } value camlzip_inflateEnd(value vzs) { if (inflateEnd(ZStream_val(vzs)) != Z_OK) camlzip_error("Zlib.inflateEnd", vzs); return Val_unit; } value camlzip_update_crc32(value crc, value buf, value pos, value len) { return caml_copy_int32(crc32((uint32_t) Int32_val(crc), &Byte_u(buf, Long_val(pos)), Long_val(len))); }