Compare commits

...

116 Commits

Author SHA1 Message Date
Kas
96825016ed
Fix clang build error on Windows (#280)
Co-authored-by: kas <exactly-one-kas@users.noreply.github.com>
2022-10-19 13:37:42 +02:00
Thomas Perl
7a57414261
Allow opening read-only ZIP files (#278) 2022-09-16 21:45:45 +02:00
Kuba Podgórski
114d9d46fd
strncpy filename_size (#273)
Some checks failed
buildbot/IOSCMakeBuilder Build finished.
2022-08-05 12:31:20 +02:00
Kuba Podgórski
42151612e8 Update zip.c
Fixes https://github.com/kuba--/zip/issues/270
2022-07-07 21:56:32 +02:00
Nikola Zivkovic
e14fef2108
Enable building tests only if the project is built as a standalone one (#269) 2022-06-25 22:27:31 +02:00
HellsCoder
f8c58ebf75
fix delete entries fail (#267)
* fix delete entries fail

* Do not close file on zip_file_move error

Co-authored-by: Kuba Podgórski <kuba--@users.noreply.github.com>
2022-06-25 22:17:39 +02:00
Kuba Podgórski
822a973260
Update README.md 2022-06-06 10:38:44 +02:00
Kuba Podgórski
a78a6bb1dc
Update miniz.h (#262) 2022-06-06 02:00:53 +02:00
Dialga
55b5d810b5
fix ftruncate for tcc on Windows (#260)
* fix ftruncate for tcc on Windows

* Update zip.c
2022-06-03 22:29:37 +02:00
Dialga
4f23190324
fix mkdir for tcc on Windows (#261) 2022-06-03 22:29:12 +02:00
Kuba Podgórski
203ef139b0 Address infer's issues 2022-05-08 22:46:21 +02:00
Kuba Podgórski
341ed58e9e
Expose entry index as (s)size_t (#249) 2022-04-24 02:10:55 +02:00
Kuba Podgórski
e112112218
Update zip.c (#245) 2022-03-12 22:54:59 +01:00
Kuba Podgórski
c52f10699b check pState->m_pFile 2022-03-12 14:17:34 +01:00
Kuba Podgórski
4244a1e8af
replace S_IWRITE by S_IWUSR (#244) 2022-03-12 14:03:27 +01:00
Pavel Rojtberg
7789dcbfd5
forward changes we use in ogre (#242)
* allow access to the file compressed size

* option for case-sensitive operation

* refactor naming

Co-authored-by: Kuba Podgórski <kuba--@users.noreply.github.com>
2022-03-12 00:33:30 +01:00
Kuba Podgórski
3229b037a1
Add ZIP_RAW_ENTRYNAME (#240) 2022-02-16 02:05:19 +01:00
Markus Pristovsek
440570fcbe
do not jump over initialisation (#238) 2022-02-13 20:34:15 +01:00
Kuba Podgórski
babc060fab
Update miniz.h 2022-02-10 01:23:24 +01:00
Dialga
88e4788137
Add switch case for MZ_ZIP_TOTAL_ERRORS (#237) 2022-02-09 01:24:03 +01:00
Kuba Podgórski
a597bdf814 Update cmake 2022-02-06 16:52:50 +01:00
Dialga
d7109a6368
Fix mz_zip_time_t_to_dos_time() call (#236) 2022-02-06 16:44:04 +01:00
Kuba Podgórski
dce6952692
Fix compilation on MS-DOS/DJGPP (#235) 2022-02-06 02:23:16 +01:00
Kuba Podgórski
e7b792d7c1
Update CMakeLists.txt 2022-02-05 01:23:53 +01:00
Kuba Podgórski
ac7f600cce
Update CMakeLists.txt 2022-02-05 01:21:41 +01:00
Kuba Podgórski
2652bd2bce
Update zip.c (#230) 2022-01-13 02:43:17 +01:00
Kuba Podgórski
11cc5c6d30
permissions xattr. logic from zip info (#227) 2022-01-10 23:19:36 +01:00
Kuba Podgórski
9a52cfc458
No freopen for append (#223) 2021-12-25 19:18:33 +01:00
Kuba Podgórski
3e9952a21f
MAX_PATH=1024 (#222)
* MAX_PATH=1024

* MZ_ZIP_MAX_IO_BUF_SIZE = 8K
2021-12-25 19:15:52 +01:00
appledragon
c30cb27c74
for support shareable file open on windows (#220)
Support shareable file open on windows
Co-authored-by: visla-dragon <visla-dragon@visla.us>
2021-12-23 16:46:47 +01:00
Kuba Podgórski
a14f978c50 clang-format 2021-12-10 19:39:59 +01:00
Javier Serrano
a18b5b36fe
Port of features from miniz (creation of ZIP64 files and empty folders) (#215) 2021-12-10 19:35:03 +01:00
Kuba Podgórski
ee8e1e0f02
switch to ninja build (#216) 2021-12-10 13:21:19 +01:00
Kuba Podgórski
72cb23c311
pass MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 to mz_zip_writer_add_to_central_dir (#214) 2021-12-10 12:14:38 +01:00
Jiang Sir
594dbd5cc4
Add sanitizers for zip (#213) 2021-12-02 21:44:40 +01:00
Kuba Podgórski
cf4022802e
Create .gitattributes 2021-11-12 00:45:00 +01:00
Samuel Marks
58bde05a12
[CMakeLists.txt] Set C89; [src/zip.c] C89-compatibility (resolves: "GCC does not allow variable declarations in for loop initializers before C99") (#212) 2021-11-12 00:11:55 +01:00
Kuba Podgórski
326d8c2e33
Update README.md 2021-11-09 11:16:09 +01:00
Kuba Podgórski
d85a6e5ec1
Fix defines (#208) 2021-09-01 21:18:10 +02:00
Kuba Podgórski
692cbcf8ca
Fix - unsafe _wfopen and _wfreopen in Visual Studio (#206) 2021-09-01 13:15:39 +02:00
Kuba Podgórski
ffd2d395f8
Update README.md 2021-07-29 11:40:17 +02:00
Kuba Podgórski
5b3f3877ff
Upgrade miniz dependency to version 2.2.0 (#203) 2021-07-29 10:56:20 +02:00
Kuba Podgórski
b7193d815a Upgrade version to 0.1.32 2021-07-22 12:31:47 +02:00
Kuba Podgórski
e6f08d4a71
Accept wchars for MKDIR (#200)
* mz_mkdir
2021-07-22 12:26:35 +02:00
Kuba Podgórski
eacec85ddc
Fix compiler warnings on win (#199)
Closes https://github.com/kuba--/zip/issues/198
2021-07-17 11:55:12 +02:00
Kuba Podgórski
1c30e54204 Update zip.c 2021-07-09 18:24:56 +02:00
Kuba Podgórski
e5eaa5e45c
use size_t if it's possible (#196) 2021-07-09 11:44:21 +02:00
Kuba Podgórski
d1dd4f799c
Custom mz_stat for MSC (#194)
* Custom mz_stat for MSC
2021-06-28 21:56:09 +02:00
Kuba Podgórski
de71d577e0
API to set custom global CRC-32 function (#191)
* API to set custom global CRC-32 function
2021-06-16 13:22:26 +02:00
Kuba Podgórski
f02a47dee0
Update README.md 2021-05-19 00:59:16 +02:00
Kuba Podgórski
06f96867a8
Update README.md 2021-05-19 00:58:15 +02:00
Kuba Podgórski
d922667772
use size_t for buffer size (#187) 2021-05-11 23:20:52 +02:00
Yurii Nakonechnyi
c172655e5e
Updated README: fixed example: size_t -> ssize_t (#186)
* Updated README: fixed example: size_t -> ssize_t

* Updated README - fixed example - zip_entry_read() bufsize is size_t (not ssize_t)
2021-05-11 17:31:49 +02:00
Kuba Podgórski
4076d86dbb Update build.yml 2021-05-02 00:45:00 +02:00
Владислав
9c79c4eef3
Fix zip_errlist missing commas (#184) 2021-05-02 00:02:16 +02:00
BeastLe9enD
d9b45e6e23
Fix MSVC C2440 pointer cast error (#183) 2021-04-27 19:29:25 +02:00
Kuba Podgórski
c3ddb3f960
Update README.md 2021-04-27 14:02:28 +02:00
SpaceIm
2f6857e267
add zip::zip alias target (#180)
it is consistent with zip::zip imported target in generated config file
2021-04-13 01:00:48 +02:00
SpaceIm
bc03c01e57
add option to build documentation (#179) 2021-04-13 01:00:07 +02:00
SpaceIm
e43ba3b98f
Exports symbols of API and hides others symbols (#178)
* exports symbols of API and hides others symbols

* fix sanitizers in tests

* add export macro in public header
2021-04-13 00:42:59 +02:00
SpaceIm
3c0319673c
allow static lib without PIC (#177) 2021-04-12 15:33:14 +02:00
Kuba Podgórski
f72dce22bd
Replace _wfopen_s by _wfopen, _wfreopen_s by _wfreopen (#176) 2021-03-15 11:40:41 +01:00
Kuba Podgórski
54af749c22
Merge pull request #175 from kuba--/err-code
Return code on error
2021-03-13 18:27:23 +01:00
Kuba Podgórski
252d6c18cf Add error lookup function 2021-03-13 18:09:47 +01:00
Kuba Podgórski
f0e5c7b150 Return code on error 2021-03-12 20:12:41 +01:00
Kuba Podgórski
d01ec45d94
Minor API changes (#174)
For public API s/zip_total_entries/zip_entries_total/
2021-03-11 22:43:34 +01:00
Kuba Podgórski
9cf2857e82
Use minunit for testing (#173) 2021-03-11 12:57:36 +01:00
Kuba Podgórski
b38024b5dc
Update README.md 2021-03-08 21:47:43 +01:00
Kuba Podgórski
ad87c52a9a
Add freebsd build (#172) 2021-03-08 21:11:24 +01:00
Kuba Podgórski
72d28abe8a
Test stream API also on WIN (#171)
* Test stream API also on WIN
* Fix error C2057: expected constant expression
2021-03-07 22:12:01 +01:00
jinfeihan57
878cdc06fe
fix issue 169 (#170)
Co-authored-by: jinfeihan57 <hanjinfei57@gmail.com>
2021-03-07 15:36:13 +01:00
Kuba Podgórski
0cc4e16aca
Update README.md 2021-03-06 18:26:01 +01:00
Kuba Podgórski
a631ce45f7 clang-format 2021-03-06 18:13:11 +01:00
jinfeihan57
bfcfd1aa4e
Issue 153 NEW PR (#167)
* issue153

* fix issue 153

* malloc->calloc

* add zip_open_stream() to readme

Co-authored-by: hanjinfei57 <hanjinfei57@gmail.com>
2021-03-06 11:56:21 +01:00
Kuba Podgórski
05f412b3e6
Update README.md 2021-01-06 22:34:44 +01:00
Kuba Podgórski
82fbb50f53
Fix return code for extract (#166) 2021-01-01 20:05:24 +01:00
Kuba Podgórski
992106448d
Fix warnings for MSC (#162)
* Include <io.h> for MSC

* Address #160 comments
2020-12-22 13:25:28 +01:00
Kuba Podgórski
903beb7d1c
Fix normalize entry path (#159) 2020-12-18 12:46:01 +01:00
Kuba Podgórski
91a2e2cc6c Remove release tag. 2020-12-17 17:53:59 +01:00
Kuba Podgórski
b2fc08e6b7 clang-format 2020-12-17 17:52:46 +01:00
Tim Gates
5b679e68a0
docs: fix simple typo, purpsosely -> purposely (#158)
There is a small typo in src/miniz.h.

Should read `purposely` rather than `purpsosely`.
2020-12-17 13:01:59 +01:00
Mykhailo Pylyp
6fc01609d6
fix compilation error for msvs latest (#155) 2020-12-17 13:01:11 +01:00
Kuba Podgórski
ea597c5f1c
No symlink signature (#152) 2020-12-01 14:39:15 +01:00
Vadim Grigoruk
d7df626f3a
fixed compilation with MINIZ_NO_TIME (#151) 2020-11-26 14:46:10 +01:00
Vadim Grigoruk
a67b0b7074
Open files with wide char names on Windows (#150) 2020-11-25 16:44:43 +01:00
Kuba Podgórski
cc1f3d52bf
No extra check if SSIZE_T is defined (#148) 2020-11-18 13:14:43 +01:00
Kuba Podgórski
ffe690c402
define __have_typedef_ssize_t (#147) 2020-11-13 12:38:53 +01:00
Kuba Podgórski
ddb8e54174
Update build.yml (#146) 2020-11-13 11:21:10 +01:00
jinfeihan57
42a6b39526
Add delete entry function (#142) 2020-11-10 11:53:19 +01:00
Kuba Podgórski
7faf19034e Update README.md 2020-11-05 13:18:46 +01:00
Kuba Podgórski
26a5708c67 Move Contribution to CONTRIBUTING.md
Add links to cool projects.
2020-11-05 13:17:04 +01:00
Kuba Podgórski
3d8c7ce458
Create CONTRIBUTING.md 2020-11-05 10:49:58 +01:00
jinfeihan57
e2246cb5c6
Add zip_open_stream() func. (#140) 2020-09-17 09:32:59 +02:00
Kuba Podgórski
e54c766ffb Try to fix build on unix 2020-09-10 15:51:49 +02:00
Kuba Podgórski
6b038da83f Revert "Remove -pedantic for tests in release mode."
This reverts commit c970a571babd19fc14b2aa783962de5a41c00ead.
2020-09-10 15:49:41 +02:00
Kuba Podgórski
c970a571ba Remove -pedantic for tests in release mode. 2020-09-10 15:37:29 +02:00
Ali Hardan
1d0fc2c043
Update README.md (#137) 2020-08-29 00:23:24 +02:00
jinfeihan57
0d24296d51
fix issue #123 (#136)
* normalize entry names on extract
2020-08-28 14:46:06 +02:00
jinfeihan57
b76004b3b7
using clang-format & test not support windows (#134)
* add zip_extract_stream
2020-08-24 16:43:51 +02:00
Kuba Podgórski
59b7942cd8 Add example how to compress a folder. 2020-08-19 21:50:27 +02:00
Kuba Podgórski
529eef88ad
set(CMAKE_VERBOSE_MAKEFILE ON) (#133)
* set(CMAKE_VERBOSE_MAKEFILE ON)

* -D CMAKE_AR=/usr/bin/ar
2020-08-18 13:21:05 +02:00
Kuba Podgórski
6e7dce3daa trigger build on pr event 2020-08-18 12:50:45 +02:00
Kuba Podgórski
853f41516f Revert "zip_extract_stream() (#131)"
This reverts commit c555a9636f72c03e25e5a525a7725cb7e6a67680.
2020-08-18 12:29:22 +02:00
Kuba Podgórski
ce1a404443 Revert "clang-format"
This reverts commit 046a374bf0f03ec9e00b7717409864cd610f7764.
2020-08-18 12:29:18 +02:00
Kuba Podgórski
046a374bf0 clang-format 2020-08-18 12:20:46 +02:00
jinfeihan57
c555a9636f
zip_extract_stream() (#131)
Add zip_extract_stream
2020-08-18 11:00:01 +02:00
Kuba Podgórski
a462384060 Fix tinfl_decompress 2020-07-25 23:40:13 +02:00
Kuba Podgórski
8ceef944ca Fix test for clang scan-build 2020-07-25 23:15:29 +02:00
Kuba Podgórski
5efb939fb8
Update README.md 2020-07-25 23:02:48 +02:00
Kuba Podgórski
a87edc4d8f
Update README.md 2020-07-25 23:01:46 +02:00
Kuba Podgórski
96924c94da
promote to 0.1.19 2020-04-20 23:17:44 +02:00
Kuba Podgórski
e6c9807d8d
CMake3.4 -DCMAKE_WINDOWS_EXPORT_ALL_SYMBOLS=TRUE (#122)
Co-authored-by: kuba-- <>
2020-04-20 23:15:55 +02:00
Kuba Podgórski
f053be982f v0.1.18 2020-02-04 13:45:44 +01:00
Kuba Podgórski
42c8ba8f70
Fix compilation on FreeBSD 12.1 32Bit with GCC 9.2.0 (#119) 2020-02-04 13:31:40 +01:00
Kuba Podgórski
b41b3b3b0b
remove codecov (#118) 2020-01-30 23:25:47 +01:00
Kuba Podgórski
d50a1239d0
Update README.md 2020-01-24 21:52:12 +01:00
18 changed files with 9737 additions and 4383 deletions

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
*.h linguist-language=C

View File

@ -1,47 +1,79 @@
name: build
on: [push]
on: [push, pull_request]
jobs:
ubuntu:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Tools
run: sudo apt-get -y install tree ninja-build
- name: Configure
run: cmake -DENABLE_COVERAGE=ON -DSANITIZE_ADDRESS=On .
run: |
cmake -DSANITIZE_ADDRESS=On -S . -B build -GNinja
- name: Build
run: cmake --build .
run: |
cmake --build build
tree -sha build
- name: Test
run: ASAN_OPTIONS=detect_leaks=0 LSAN_OPTIONS=verbosity=1:log_threads=1 ctest -VV
- name: CodeCov
run: bash <(curl -s https://codecov.io/bash)
run: ASAN_OPTIONS=detect_leaks=0 LSAN_OPTIONS=verbosity=1:log_threads=1 cmake --build build --target test
macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- name: Tools
run: brew install tree ninja
- name: Configure
run: |
pip3 install scan-build
scan-build cmake -DSANITIZE_ADDRESS=On .
cmake -DSANITIZE_ADDRESS=On -S . -B build -GNinja
- name: Build
run: scan-build -v --exclude test -enable-checker security.FloatLoopCounter -enable-checker security.insecureAPI.UncheckedReturn --status-bugs cmake --build .
run: |
cmake --build build
tree -sha build
- name: Test
run: ctest -VV
run: cmake --build build --target test
# freebsd:
# runs-on: macos-latest
# steps:
# - uses: actions/checkout@v2
# - name: Build and Test
# uses: vmactions/freebsd-vm@v0.1.2
# with:
# usesh: true
# prepare: pkg install -y cmake tree lang/gcc
# run: |
# cmake .
# cmake --build .
# tree -sha .
# ctest -VV
windows:
windows-msvc:
runs-on: "windows-latest"
steps:
- uses: actions/checkout@v2
- name: Configure
run: cmake .
run: cmake -S . -B build
- name: Build
run: cmake --build . --config "Debug"
run: |
cmake --build build --config "Debug"
tree /a /f build
- name: Test
run: ctest -VV -C "Debug"
run: |
cd build
ctest -VV -C "Debug"
windows-mingw:
runs-on: "windows-latest"
steps:
- uses: actions/checkout@v2
- name: Configure
run: cmake -S . -B build -G "MinGW Makefiles"
- name: Build
run: |
cmake --build build
tree /a /f build
- name: Test
run: cmake --build build --target test

2
.gitignore vendored
View File

@ -1,7 +1,9 @@
/build/
/test/build/
/xcodeproj/
/infer-out/
.vscode/
Testing/
# Object files
*.o

View File

@ -1,30 +1,49 @@
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.14)
project(zip
LANGUAGES C
VERSION "0.1.17")
VERSION "0.2.3")
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
set(CMAKE_VERBOSE_MAKEFILE ON)
option(CMAKE_DISABLE_TESTING "Disable test creation" OFF)
# Enable building tests only if the project is being built as a standalone one
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
option(CMAKE_DISABLE_TESTING "Disable test creation" OFF)
else ()
option(CMAKE_DISABLE_TESTING "Disable test creation" ON)
endif ()
if (MSVC)
# Use secure functions by default and suppress warnings about "deprecated" functions
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_NONSTDC_NO_WARNINGS=1 /D _CRT_SECURE_NO_WARNINGS=1")
elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR
"${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR
"${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall -Wextra -Werror -pedantic")
if(ENABLE_COVERAGE)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
endif()
endif (MSVC)
option(CMAKE_ENABLE_SANITIZERS "Enable zip sanitizers" OFF)
option(ZIP_STATIC_PIC "Build static zip with PIC" ON)
option(ZIP_BUILD_DOCS "Generate API documentation with Doxygen" OFF)
if(ZIP_ENABLE_SHARABLE_FILE_OPEN)
add_definitions(-DZIP_ENABLE_SHARABLE_FILE_OPEN)
endif()
if(CMAKE_SIZEOF_VOID_P EQUAL 4)
# large file support
add_definitions(-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64)
endif()
# zip
set(SRC src/miniz.h src/zip.h src/zip.c)
add_library(${PROJECT_NAME} ${SRC})
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
if(ZIP_STATIC_PIC)
set_property(TARGET ${PROJECT_NAME} PROPERTY POSITION_INDEPENDENT_CODE 1)
endif()
set_property(TARGET ${PROJECT_NAME} PROPERTY C_VISIBILITY_PRESET hidden)
if(BUILD_SHARED_LIBS)
target_compile_definitions(${PROJECT_NAME}
PUBLIC ZIP_SHARED
PRIVATE ZIP_BUILD_SHARED
)
endif()
target_include_directories(${PROJECT_NAME} PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
$<INSTALL_INTERFACE:include>
@ -34,13 +53,26 @@ target_include_directories(${PROJECT_NAME} PUBLIC
if (NOT CMAKE_DISABLE_TESTING)
enable_testing()
add_subdirectory(test)
endif()
if (CMAKE_ENABLE_SANITIZERS)
find_package(Sanitizers)
add_sanitizers(${PROJECT_NAME} ${test_out})
add_sanitizers(${PROJECT_NAME})
endif()
####
# Installation (https://github.com/forexample/package-example) {
set(CMAKE_C_STANDARD 90)
if (MSVC)
# Use secure functions by default and suppress warnings about "deprecated" functions
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_NONSTDC_NO_WARNINGS=1 /D _CRT_SECURE_NO_WARNINGS=1")
elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR
"${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR
"${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror -pedantic")
endif (MSVC)
####
set(CONFIG_INSTALL_DIR "lib/cmake/${PROJECT_NAME}")
set(INCLUDE_INSTALL_DIR "include")
@ -80,8 +112,6 @@ install(
DESTINATION "${CONFIG_INSTALL_DIR}"
)
# }
install(TARGETS ${PROJECT_NAME}
EXPORT ${TARGETS_EXPORT_NAME}
RUNTIME DESTINATION bin
@ -102,8 +132,8 @@ if(NOT TARGET uninstall)
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake)
endif()
find_package(Doxygen)
if(DOXYGEN_FOUND)
if(ZIP_BUILD_DOCS)
find_package(Doxygen REQUIRED)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY)
add_custom_target(doc
${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile

9
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,9 @@
# Contribution Rules/Coding Standards
No need to throw away your coding style, just do your best to follow default clang-format style.
Apply `clang-format` to the source files before commit:
```sh
for file in $(git ls-files | \grep -E '\.(c|h)$' | \grep -v -- '#')
do
clang-format -i $file
done
```

202
README.md
View File

@ -1,9 +1,7 @@
### A portable (OSX/Linux/Windows), simple zip library written in C
This is done by hacking awesome [miniz](https://code.google.com/p/miniz) library and layering functions on top of the miniz v1.15 API.
### A portable (OSX/Linux/Windows/Android/iOS), simple zip library written in C
This is done by hacking awesome [miniz](https://github.com/richgel999/miniz) library and layering functions on top of the miniz v2.2.0 API.
[![Build](https://github.com/kuba--/zip/workflows/build/badge.svg)](https://github.com/kuba--/zip/actions?query=workflow%3Abuild)
[![Version](https://badge.fury.io/gh/kuba--%2Fzip.svg)](https://github.com/kuba--/zip/releases)
[![Codecov](https://codecov.io/gh/kuba--/zip/branch/master/graph/badge.svg)](https://codecov.io/gh/kuba--/zip)
# The Idea
@ -156,10 +154,52 @@ struct zip_t *zip = zip_open("foo.zip", 0, 'r');
zip_close(zip);
```
* Create a new zip archive in memory (stream API).
```c
char *outbuf = NULL;
size_t outbufsize = 0;
const char *inbuf = "Append some data here...\0";
struct zip_t *zip = zip_stream_open(NULL, 0, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
{
zip_entry_open(zip, "foo-1.txt");
{
zip_entry_write(zip, inbuf, strlen(inbuf));
}
zip_entry_close(zip);
/* copy compressed stream into outbuf */
zip_stream_copy(zip, (void **)&outbuf, &outbufsize);
}
zip_stream_close(zip);
free(outbuf);
```
* Extract a zip entry into a memory (stream API).
```c
char *buf = NULL;
size_t bufsize = 0;
struct zip_t *zip = zip_stream_open(zipstream, zipstreamsize, 0, 'r');
{
zip_entry_open(zip, "foo-1.txt");
{
zip_entry_read(zip, (void **)&buf, &bufsize);
}
zip_entry_close(zip);
}
zip_stream_close(zip);
free(buf);
```
* List of all zip entries
```c
struct zip_t *zip = zip_open("foo.zip", 0, 'r');
int i, n = zip_total_entries(zip);
int i, n = zip_entries_total(zip);
for (i = 0; i < n; ++i) {
zip_entry_openbyindex(zip, i);
{
@ -173,16 +213,60 @@ for (i = 0; i < n; ++i) {
zip_close(zip);
```
* Compress folder (recursively)
```c
void zip_walk(struct zip_t *zip, const char *path) {
DIR *dir;
struct dirent *entry;
char fullpath[MAX_PATH];
struct stat s;
memset(fullpath, 0, MAX_PATH);
dir = opendir(path);
assert(dir);
while ((entry = readdir(dir))) {
// skip "." and ".."
if (!strcmp(entry->d_name, ".\0") || !strcmp(entry->d_name, "..\0"))
continue;
snprintf(fullpath, sizeof(fullpath), "%s/%s", path, entry->d_name);
stat(fullpath, &s);
if (S_ISDIR(s.st_mode))
zip_walk(zip, fullpath);
else {
zip_entry_open(zip, fullpath);
zip_entry_fwrite(zip, fullpath);
zip_entry_close(zip);
}
}
closedir(dir);
}
```
* Delete zip archive entries.
```c
char *entries[] = {"unused.txt", "remove.ini", "delete.me"};
struct zip_t *zip = zip_open("foo.zip", 0, 'd');
{
zip_entries_delete(zip, entries, 3);
}
zip_close(zip);
```
# Bindings
Compile zip library as a dynamic library.
```shell
$ mkdir build
$ cd build
$ cmake -DBUILD_SHARED_LIBS=true ..
$ make
$ cmake --build .
```
### Go (cgo)
### [Go](https://golang.org) (cgo)
```go
package main
@ -212,7 +296,54 @@ func main() {
}
```
### Ruby (ffi)
### [Rust](https://www.rust-lang.org) (ffi)
```rust
extern crate libc;
use std::ffi::CString;
#[repr(C)]
pub struct Zip {
_private: [u8; 0],
}
#[link(name = "zip")]
extern "C" {
fn zip_open(path: *const libc::c_char, level: libc::c_int, mode: libc::c_char) -> *mut Zip;
fn zip_close(zip: *mut Zip) -> libc::c_void;
fn zip_entry_open(zip: *mut Zip, entryname: *const libc::c_char) -> libc::c_int;
fn zip_entry_close(zip: *mut Zip) -> libc::c_int;
fn zip_entry_write(
zip: *mut Zip,
buf: *const libc::c_void,
bufsize: libc::size_t,
) -> libc::c_int;
}
fn main() {
let path = CString::new("/tmp/rust.zip").unwrap();
let mode: libc::c_char = 'w' as libc::c_char;
let entryname = CString::new("test.txt").unwrap();
let content = "test content\0";
unsafe {
let zip: *mut Zip = zip_open(path.as_ptr(), 5, mode);
{
zip_entry_open(zip, entryname.as_ptr());
{
let buf = content.as_ptr() as *const libc::c_void;
let bufsize = content.len() as libc::size_t;
zip_entry_write(zip, buf, bufsize);
}
zip_entry_close(zip);
}
zip_close(zip);
}
}
```
### [Ruby](http://www.ruby-lang.org) (ffi)
Install _ffi_ gem.
```shell
$ gem install ffi
@ -245,7 +376,7 @@ Zip.zip_entry_close(ptr)
Zip.zip_close(ptr)
```
### Python (cffi)
### [Python](https://www.python.org) (cffi)
Install _cffi_ package
```shell
$ pip install cffi
@ -279,7 +410,36 @@ Zip.zip_entry_close(ptr)
Zip.zip_close(ptr)
```
### Ring
### [Never](https://never-lang.readthedocs.io/) (ffi)
```never
extern "libzip.so" func zip_open(zipname: string, level: int, mode: char) -> c_ptr
extern "libzip.so" func zip_close(zip: c_ptr) -> void
extern "libzip.so" func zip_entry_open(zip: c_ptr, entryname: string) -> int
extern "libzip.so" func zip_entry_close(zip: c_ptr) -> int
extern "libzip.so" func zip_entry_write(zip: c_ptr, buf: string, bufsize: int) -> int
extern "libzip.so" func zip_entry_fwrite(zip: c_ptr, filename: string) -> int
func main() -> int
{
let content = "Test content"
let zip = zip_open("/tmp/never.zip", 6, 'w');
zip_entry_open(zip, "test.file");
zip_entry_fwrite(zip, "/tmp/test.txt");
zip_entry_close(zip);
zip_entry_open(zip, "test.content");
zip_entry_write(zip, content, length(content));
zip_entry_close(zip);
zip_close(zip);
0
}
```
### [Ring](http://ring-lang.net)
The language comes with RingZip based on this library
```ring
load "ziplib.ring"
@ -296,13 +456,17 @@ new Zip {
}
```
# Contribution Rules/Coding Standards
No need to throw away your coding style, just do your best to follow default clang-format style.
Apply `clang-format` to the source files before commit:
```sh
for file in $(git ls-files | \grep -E '\.(c|h)$' | \grep -v -- '#')
do
clang-format -i $file
done
```
# Check out more cool projects which use this library:
- [Filament](https://github.com/google/filament): Filament is a real-time physically based rendering engine for Android, iOS, Linux, macOS, Windows, and WebGL. It is designed to be as small as possible and as efficient as possible on Android.
- [Hermes JS Engine](https://github.com/facebook/hermes): Hermes is a JavaScript engine optimized for fast start-up of React Native apps on Android. It features ahead-of-time static optimization and compact bytecode.
- [Monster Mash](https://github.com/google/monster-mash): New Sketch-Based Modeling and Animation Tool.
- [Object-Oriented Graphics Rendering Engine](https://github.com/OGRECave/ogre): OGRE is a scene-oriented, flexible 3D engine written in C++ designed to make it easier and more intuitive for developers to produce games and demos utilising 3D hardware.
- [Open Asset Import Library](https://github.com/assimp/assimp): A library to import and export various 3d-model-formats including scene-post-processing to generate missing render data.
- [PowerToys](https://github.com/microsoft/PowerToys): Set of utilities for power users to tune and streamline their Windows 10 experience for greater productivity.
- [The Ring Programming Language](https://ring-lang.github.io): Innovative and practical general-purpose multi-paradigm language.
- [The V Programming Language](https://github.com/vlang/v): Simple, fast, safe, compiled. For developing maintainable software.
- [TIC-80](https://github.com/nesbox/TIC-80): TIC-80 is a FREE and OPEN SOURCE fantasy computer for making, playing and sharing tiny games.
- [Urho3D](https://github.com/urho3d/Urho3D): Urho3D is a free lightweight, cross-platform 2D and 3D game engine implemented in C++ and released under the MIT license. Greatly inspired by OGRE and Horde3D.
- [Vcpkg](https://github.com/microsoft/vcpkg): Vcpkg helps you manage C and C++ libraries on Windows, Linux and MacOS.
- [and more...](https://grep.app/search?q=kuba--/zip)

10242
src/miniz.h

File diff suppressed because it is too large Load Diff

1416
src/zip.c

File diff suppressed because it is too large Load Diff

237
src/zip.h
View File

@ -12,17 +12,29 @@
#ifndef ZIP_H
#define ZIP_H
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#ifndef ZIP_SHARED
#define ZIP_EXPORT
#else
#ifdef _WIN32
#ifdef ZIP_BUILD_SHARED
#define ZIP_EXPORT __declspec(dllexport)
#else
#define ZIP_EXPORT __declspec(dllimport)
#endif
#else
#define ZIP_EXPORT __attribute__((visibility("default")))
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if !defined(_SSIZE_T_DEFINED) && !defined(_SSIZE_T_DEFINED_) && \
!defined(__DEFINED_ssize_t) && !defined(__ssize_t_defined) && \
!defined(_SSIZE_T) && !defined(_SSIZE_T_)
#if !defined(_POSIX_C_SOURCE) && defined(_MSC_VER)
// 64-bit Windows is the only mainstream platform
// where sizeof(long) != sizeof(void*)
#ifdef _WIN64
@ -30,18 +42,10 @@ typedef long long ssize_t; /* byte count or error */
#else
typedef long ssize_t; /* byte count or error */
#endif
#define _SSIZE_T_DEFINED
#define _SSIZE_T_DEFINED_
#define __DEFINED_ssize_t
#define __ssize_t_defined
#define _SSIZE_T
#define _SSIZE_T_
#endif
#ifndef MAX_PATH
#define MAX_PATH 32767 /* # chars in a path name including NULL */
#define MAX_PATH 1024 /* # chars in a path name including NULL */
#endif
/**
@ -58,9 +62,49 @@ typedef long ssize_t; /* byte count or error */
/**
* Default zip compression level.
*/
#define ZIP_DEFAULT_COMPRESSION_LEVEL 6
/**
* Error codes
*/
#define ZIP_ENOINIT -1 // not initialized
#define ZIP_EINVENTNAME -2 // invalid entry name
#define ZIP_ENOENT -3 // entry not found
#define ZIP_EINVMODE -4 // invalid zip mode
#define ZIP_EINVLVL -5 // invalid compression level
#define ZIP_ENOSUP64 -6 // no zip 64 support
#define ZIP_EMEMSET -7 // memset error
#define ZIP_EWRTENT -8 // cannot write data to entry
#define ZIP_ETDEFLINIT -9 // cannot initialize tdefl compressor
#define ZIP_EINVIDX -10 // invalid index
#define ZIP_ENOHDR -11 // header not found
#define ZIP_ETDEFLBUF -12 // cannot flush tdefl buffer
#define ZIP_ECRTHDR -13 // cannot create entry header
#define ZIP_EWRTHDR -14 // cannot write entry header
#define ZIP_EWRTDIR -15 // cannot write to central dir
#define ZIP_EOPNFILE -16 // cannot open file
#define ZIP_EINVENTTYPE -17 // invalid entry type
#define ZIP_EMEMNOALLOC -18 // extracting data using no memory allocation
#define ZIP_ENOFILE -19 // file not found
#define ZIP_ENOPERM -20 // no permission
#define ZIP_EOOMEM -21 // out of memory
#define ZIP_EINVZIPNAME -22 // invalid zip archive name
#define ZIP_EMKDIR -23 // make dir error
#define ZIP_ESYMLINK -24 // symlink error
#define ZIP_ECLSZIP -25 // close archive error
#define ZIP_ECAPSIZE -26 // capacity size too small
#define ZIP_EFSEEK -27 // fseek error
#define ZIP_EFREAD -28 // fread error
#define ZIP_EFWRITE -29 // fwrite error
/**
* Looks up the error message string coresponding to an error number.
* @param errnum error number
* @return error message string coresponding to errnum or NULL if error is not
* found.
*/
extern ZIP_EXPORT const char *zip_strerror(int errnum);
/**
* @struct zip_t
*
@ -81,14 +125,15 @@ struct zip_t;
*
* @return the zip archive handler or NULL on error
*/
extern struct zip_t *zip_open(const char *zipname, int level, char mode);
extern ZIP_EXPORT struct zip_t *zip_open(const char *zipname, int level,
char mode);
/**
* Closes the zip archive, releases resources - always finalize.
*
* @param zip zip archive handler.
*/
extern void zip_close(struct zip_t *zip);
extern ZIP_EXPORT void zip_close(struct zip_t *zip);
/**
* Determines if the archive has a zip64 end of central directory headers.
@ -98,7 +143,7 @@ extern void zip_close(struct zip_t *zip);
* @return the return code - 1 (true), 0 (false), negative number (< 0) on
* error.
*/
extern int zip_is64(struct zip_t *zip);
extern ZIP_EXPORT int zip_is64(struct zip_t *zip);
/**
* Opens an entry by name in the zip archive.
@ -112,7 +157,22 @@ extern int zip_is64(struct zip_t *zip);
*
* @return the return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_entry_open(struct zip_t *zip, const char *entryname);
extern ZIP_EXPORT int zip_entry_open(struct zip_t *zip, const char *entryname);
/**
* Opens an entry by name in the zip archive.
*
* For zip archive opened in 'w' or 'a' mode the function will append
* a new entry. In readonly mode the function tries to locate the entry
* in global dictionary (case sensitive).
*
* @param zip zip archive handler.
* @param entryname an entry name in local dictionary (case sensitive).
*
* @return the return code - 0 on success, negative number (< 0) on error.
*/
extern ZIP_EXPORT int zip_entry_opencasesensitive(struct zip_t *zip,
const char *entryname);
/**
* Opens a new entry by index in the zip archive.
@ -124,7 +184,7 @@ extern int zip_entry_open(struct zip_t *zip, const char *entryname);
*
* @return the return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_entry_openbyindex(struct zip_t *zip, int index);
extern ZIP_EXPORT int zip_entry_openbyindex(struct zip_t *zip, size_t index);
/**
* Closes a zip entry, flushes buffer and releases resources.
@ -133,7 +193,7 @@ extern int zip_entry_openbyindex(struct zip_t *zip, int index);
*
* @return the return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_entry_close(struct zip_t *zip);
extern ZIP_EXPORT int zip_entry_close(struct zip_t *zip);
/**
* Returns a local name of the current zip entry.
@ -149,7 +209,7 @@ extern int zip_entry_close(struct zip_t *zip);
*
* @return the pointer to the current zip entry name, or NULL on error.
*/
extern const char *zip_entry_name(struct zip_t *zip);
extern ZIP_EXPORT const char *zip_entry_name(struct zip_t *zip);
/**
* Returns an index of the current zip entry.
@ -158,7 +218,7 @@ extern const char *zip_entry_name(struct zip_t *zip);
*
* @return the index on success, negative number (< 0) on error.
*/
extern int zip_entry_index(struct zip_t *zip);
extern ZIP_EXPORT ssize_t zip_entry_index(struct zip_t *zip);
/**
* Determines if the current zip entry is a directory entry.
@ -168,16 +228,35 @@ extern int zip_entry_index(struct zip_t *zip);
* @return the return code - 1 (true), 0 (false), negative number (< 0) on
* error.
*/
extern int zip_entry_isdir(struct zip_t *zip);
extern ZIP_EXPORT int zip_entry_isdir(struct zip_t *zip);
/**
* Returns an uncompressed size of the current zip entry.
* Returns the uncompressed size of the current zip entry.
* Alias for zip_entry_uncomp_size (for backward compatibility).
*
* @param zip zip archive handler.
*
* @return the uncompressed size in bytes.
*/
extern unsigned long long zip_entry_size(struct zip_t *zip);
extern ZIP_EXPORT unsigned long long zip_entry_size(struct zip_t *zip);
/**
* Returns the uncompressed size of the current zip entry.
*
* @param zip zip archive handler.
*
* @return the uncompressed size in bytes.
*/
extern ZIP_EXPORT unsigned long long zip_entry_uncomp_size(struct zip_t *zip);
/**
* Returns the compressed size of the current zip entry.
*
* @param zip zip archive handler.
*
* @return the compressed size in bytes.
*/
extern ZIP_EXPORT unsigned long long zip_entry_comp_size(struct zip_t *zip);
/**
* Returns CRC-32 checksum of the current zip entry.
@ -186,7 +265,7 @@ extern unsigned long long zip_entry_size(struct zip_t *zip);
*
* @return the CRC-32 checksum.
*/
extern unsigned int zip_entry_crc32(struct zip_t *zip);
extern ZIP_EXPORT unsigned int zip_entry_crc32(struct zip_t *zip);
/**
* Compresses an input buffer for the current zip entry.
@ -197,7 +276,8 @@ extern unsigned int zip_entry_crc32(struct zip_t *zip);
*
* @return the return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize);
extern ZIP_EXPORT int zip_entry_write(struct zip_t *zip, const void *buf,
size_t bufsize);
/**
* Compresses a file for the current zip entry.
@ -207,7 +287,7 @@ extern int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize);
*
* @return the return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_entry_fwrite(struct zip_t *zip, const char *filename);
extern ZIP_EXPORT int zip_entry_fwrite(struct zip_t *zip, const char *filename);
/**
* Extracts the current zip entry into output buffer.
@ -222,9 +302,10 @@ extern int zip_entry_fwrite(struct zip_t *zip, const char *filename);
* for large entries, please take a look at zip_entry_extract function.
*
* @return the return code - the number of bytes actually read on success.
* Otherwise a -1 on error.
* Otherwise a negative number (< 0) on error.
*/
extern ssize_t zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize);
extern ZIP_EXPORT ssize_t zip_entry_read(struct zip_t *zip, void **buf,
size_t *bufsize);
/**
* Extracts the current zip entry into a memory buffer using no memory
@ -236,14 +317,15 @@ extern ssize_t zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize);
*
* @note ensure supplied output buffer is large enough.
* zip_entry_size function (returns uncompressed size for the current
* entry) can be handy to estimate how big buffer is needed. for large
* entries, please take a look at zip_entry_extract function.
* entry) can be handy to estimate how big buffer is needed.
* For large entries, please take a look at zip_entry_extract function.
*
* @return the return code - the number of bytes actually read on success.
* Otherwise a -1 on error (e.g. bufsize is not large enough).
* Otherwise a negative number (< 0) on error (e.g. bufsize is not large
* enough).
*/
extern ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf,
size_t bufsize);
extern ZIP_EXPORT ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf,
size_t bufsize);
/**
* Extracts the current zip entry into output file.
@ -253,7 +335,7 @@ extern ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf,
*
* @return the return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_entry_fread(struct zip_t *zip, const char *filename);
extern ZIP_EXPORT int zip_entry_fread(struct zip_t *zip, const char *filename);
/**
* Extracts the current zip entry using a callback function (on_extract).
@ -265,9 +347,9 @@ extern int zip_entry_fread(struct zip_t *zip, const char *filename);
*
* @return the return code - 0 on success, negative number (< 0) on error.
*/
extern int
extern ZIP_EXPORT int
zip_entry_extract(struct zip_t *zip,
size_t (*on_extract)(void *arg, unsigned long long offset,
size_t (*on_extract)(void *arg, uint64_t offset,
const void *data, size_t size),
void *arg);
@ -279,7 +361,72 @@ zip_entry_extract(struct zip_t *zip,
* @return the return code - the number of entries on success, negative number
* (< 0) on error.
*/
extern int zip_total_entries(struct zip_t *zip);
extern ZIP_EXPORT ssize_t zip_entries_total(struct zip_t *zip);
/**
* Deletes zip archive entries.
*
* @param zip zip archive handler.
* @param entries array of zip archive entries to be deleted.
* @param len the number of entries to be deleted.
* @return the number of deleted entries, or negative number (< 0) on error.
*/
extern ZIP_EXPORT ssize_t zip_entries_delete(struct zip_t *zip,
char *const entries[], size_t len);
/**
* Extracts a zip archive stream into directory.
*
* If on_extract is not NULL, the callback will be called after
* successfully extracted each zip entry.
* Returning a negative value from the callback will cause abort and return an
* error. The last argument (void *arg) is optional, which you can use to pass
* data to the on_extract callback.
*
* @param stream zip archive stream.
* @param size stream size.
* @param dir output directory.
* @param on_extract on extract callback.
* @param arg opaque pointer.
*
* @return the return code - 0 on success, negative number (< 0) on error.
*/
extern ZIP_EXPORT int
zip_stream_extract(const char *stream, size_t size, const char *dir,
int (*on_extract)(const char *filename, void *arg),
void *arg);
/**
* Opens zip archive stream into memory.
*
* @param stream zip archive stream.
* @param size stream size.
*
* @return the zip archive handler or NULL on error
*/
extern ZIP_EXPORT struct zip_t *zip_stream_open(const char *stream, size_t size,
int level, char mode);
/**
* Copy zip archive stream output buffer.
*
* @param zip zip archive handler.
* @param buf output buffer. User should free buf.
* @param bufsize output buffer size (in bytes).
*
* @return copy size
*/
extern ZIP_EXPORT ssize_t zip_stream_copy(struct zip_t *zip, void **buf,
size_t *bufsize);
/**
* Close zip archive releases resources.
*
* @param zip zip archive handler.
*
* @return
*/
extern ZIP_EXPORT void zip_stream_close(struct zip_t *zip);
/**
* Creates a new archive and puts files into a single zip archive.
@ -290,7 +437,8 @@ extern int zip_total_entries(struct zip_t *zip);
*
* @return the return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_create(const char *zipname, const char *filenames[], size_t len);
extern ZIP_EXPORT int zip_create(const char *zipname, const char *filenames[],
size_t len);
/**
* Extracts a zip archive file into directory.
@ -308,12 +456,11 @@ extern int zip_create(const char *zipname, const char *filenames[], size_t len);
*
* @return the return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_extract(const char *zipname, const char *dir,
int (*on_extract_entry)(const char *filename, void *arg),
void *arg);
extern ZIP_EXPORT int zip_extract(const char *zipname, const char *dir,
int (*on_extract_entry)(const char *filename,
void *arg),
void *arg);
/** @} */
#ifdef __cplusplus
}
#endif

View File

@ -1,11 +1,40 @@
cmake_minimum_required(VERSION 2.8)
cmake_minimum_required(VERSION 3.14)
# test
set(test_out test.out)
find_package(Sanitizers)
add_executable(${test_out} test.c)
target_link_libraries(${test_out} zip)
# tests
set(test_write_out test_write.out)
add_executable(${test_write_out} test_write.c)
target_link_libraries(${test_write_out} zip)
add_test(NAME ${test_write_out} COMMAND ${test_write_out})
add_sanitizers(${test_write_out})
add_test(NAME ${test_out} COMMAND ${test_out})
set(test_append_out test_append.out)
add_executable(${test_append_out} test_append.c)
target_link_libraries(${test_append_out} zip)
add_test(NAME ${test_append_out} COMMAND ${test_append_out})
add_sanitizers(${test_append_out})
set(test_out ${test_out} PARENT_SCOPE)
set(test_read_out test_read.out)
add_executable(${test_read_out} test_read.c)
target_link_libraries(${test_read_out} zip)
add_test(NAME ${test_read_out} COMMAND ${test_read_out})
add_sanitizers(${test_read_out})
set(test_extract_out test_extract.out)
add_executable(${test_extract_out} test_extract.c)
target_link_libraries(${test_extract_out} zip)
add_test(NAME ${test_extract_out} COMMAND ${test_extract_out})
add_sanitizers(${test_extract_out})
set(test_entry_out test_entry.out)
add_executable(${test_entry_out} test_entry.c)
target_link_libraries(${test_entry_out} zip)
add_test(NAME ${test_entry_out} COMMAND ${test_entry_out})
add_sanitizers(${test_entry_out})
set(test_permissions_out test_permissions.out)
add_executable(${test_permissions_out} test_permissions.c)
target_link_libraries(${test_permissions_out} zip)
add_test(NAME ${test_permissions_out} COMMAND ${test_permissions_out})
add_sanitizers(${test_permissions_out})

370
test/minunit.h Normal file
View File

@ -0,0 +1,370 @@
/*
* Copyright (c) 2012 David Siñuela Pastor, siu.4coders@gmail.com
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef MINUNIT_MINUNIT_H
#define MINUNIT_MINUNIT_H
#ifdef __cplusplus
extern "C" {
#endif
#if defined(_WIN32)
#include <Windows.h>
#if defined(_MSC_VER) && _MSC_VER < 1900
#define snprintf _snprintf
#define __func__ __FUNCTION__
#endif
#elif defined(__unix__) || defined(__unix) || defined(unix) || \
(defined(__APPLE__) && defined(__MACH__))
/* Change POSIX C SOURCE version for pure c99 compilers */
#if !defined(_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 200112L
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200112L
#endif
#include <string.h>
#include <sys/resource.h>
#include <sys/time.h> /* gethrtime(), gettimeofday() */
#include <sys/times.h>
#include <time.h> /* clock_gettime(), time() */
#include <unistd.h> /* POSIX flags */
#if defined(__MACH__) && defined(__APPLE__)
#include <mach/mach.h>
#include <mach/mach_time.h>
#endif
#if __GNUC__ >= 5 && !defined(__STDC_VERSION__)
#define __func__ __extension__ __FUNCTION__
#endif
#else
#error "Unable to define timers for an unknown OS."
#endif
#include <math.h>
#include <stdio.h>
/* Maximum length of last message */
#define MINUNIT_MESSAGE_LEN 1024
/* Accuracy with which floats are compared */
#define MINUNIT_EPSILON 1E-12
/* Misc. counters */
static int minunit_run = 0;
static int minunit_assert = 0;
static int minunit_fail = 0;
static int minunit_status = 0;
/* Timers */
static double minunit_real_timer = 0;
static double minunit_proc_timer = 0;
/* Last message */
static char minunit_last_message[MINUNIT_MESSAGE_LEN];
/* Test setup and teardown function pointers */
static void (*minunit_setup)(void) = NULL;
static void (*minunit_teardown)(void) = NULL;
/* Definitions */
#define MU_TEST(method_name) static void method_name(void)
#define MU_TEST_SUITE(suite_name) static void suite_name(void)
#define MU__SAFE_BLOCK(block) \
do { \
block \
} while (0)
/* Run test suite and unset setup and teardown functions */
#define MU_RUN_SUITE(suite_name) \
MU__SAFE_BLOCK(suite_name(); minunit_setup = NULL; minunit_teardown = NULL;)
/* Configure setup and teardown functions */
#define MU_SUITE_CONFIGURE(setup_fun, teardown_fun) \
MU__SAFE_BLOCK(minunit_setup = setup_fun; minunit_teardown = teardown_fun;)
/* Test runner */
#define MU_RUN_TEST(test) \
MU__SAFE_BLOCK( \
if (minunit_real_timer == 0 && minunit_proc_timer == 0) { \
minunit_real_timer = mu_timer_real(); \
minunit_proc_timer = mu_timer_cpu(); \
} if (minunit_setup) (*minunit_setup)(); \
minunit_status = 0; test(); minunit_run++; if (minunit_status) { \
minunit_fail++; \
printf("F"); \
printf("\n%s\n", minunit_last_message); \
} fflush(stdout); \
if (minunit_teardown)(*minunit_teardown)();)
/* Report */
#define MU_REPORT() \
MU__SAFE_BLOCK( \
double minunit_end_real_timer; double minunit_end_proc_timer; \
printf("\n\n%d tests, %d assertions, %d failures\n", minunit_run, \
minunit_assert, minunit_fail); \
minunit_end_real_timer = mu_timer_real(); \
minunit_end_proc_timer = mu_timer_cpu(); \
printf("\nFinished in %.8f seconds (real) %.8f seconds (proc)\n\n", \
minunit_end_real_timer - minunit_real_timer, \
minunit_end_proc_timer - minunit_proc_timer);)
#define MU_EXIT_CODE minunit_fail
/* Assertions */
#define mu_check(test) \
MU__SAFE_BLOCK( \
minunit_assert++; if (!(test)) { \
snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, \
"%s failed:\n\t%s:%d: %s", __func__, __FILE__, __LINE__, \
#test); \
minunit_status = 1; \
return; \
} else { printf("."); })
#define mu_fail(message) \
MU__SAFE_BLOCK(minunit_assert++; \
snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, \
"%s failed:\n\t%s:%d: %s", __func__, __FILE__, \
__LINE__, message); \
minunit_status = 1; return;)
#define mu_assert(test, message) \
MU__SAFE_BLOCK( \
minunit_assert++; if (!(test)) { \
snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, \
"%s failed:\n\t%s:%d: %s", __func__, __FILE__, __LINE__, \
message); \
minunit_status = 1; \
return; \
} else { printf("."); })
#define mu_assert_int_eq(expected, result) \
MU__SAFE_BLOCK( \
int minunit_tmp_e; int minunit_tmp_r; minunit_assert++; \
minunit_tmp_e = (expected); minunit_tmp_r = (result); \
if (minunit_tmp_e != minunit_tmp_r) { \
snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, \
"%s failed:\n\t%s:%d: %d expected but was %d", __func__, \
__FILE__, __LINE__, minunit_tmp_e, minunit_tmp_r); \
minunit_status = 1; \
return; \
} else { printf("."); })
#define mu_assert_double_eq(expected, result) \
MU__SAFE_BLOCK( \
double minunit_tmp_e; double minunit_tmp_r; minunit_assert++; \
minunit_tmp_e = (expected); minunit_tmp_r = (result); \
if (fabs(minunit_tmp_e - minunit_tmp_r) > MINUNIT_EPSILON) { \
int minunit_significant_figures = 1 - log10(MINUNIT_EPSILON); \
snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, \
"%s failed:\n\t%s:%d: %.*g expected but was %.*g", __func__, \
__FILE__, __LINE__, minunit_significant_figures, \
minunit_tmp_e, minunit_significant_figures, minunit_tmp_r); \
minunit_status = 1; \
return; \
} else { printf("."); })
#define mu_assert_string_eq(expected, result) \
MU__SAFE_BLOCK( \
const char *minunit_tmp_e = expected; \
const char *minunit_tmp_r = result; minunit_assert++; \
if (!minunit_tmp_e) { \
minunit_tmp_e = "<null pointer>"; \
} if (!minunit_tmp_r) { \
minunit_tmp_r = "<null pointer>"; \
} if (strcmp(minunit_tmp_e, minunit_tmp_r)) { \
snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, \
"%s failed:\n\t%s:%d: '%s' expected but was '%s'", __func__, \
__FILE__, __LINE__, minunit_tmp_e, minunit_tmp_r); \
minunit_status = 1; \
return; \
} else { printf("."); })
/*
* The following two functions were written by David Robert Nadeau
* from http://NadeauSoftware.com/ and distributed under the
* Creative Commons Attribution 3.0 Unported License
*/
/**
* Returns the real time, in seconds, or -1.0 if an error occurred.
*
* Time is measured since an arbitrary and OS-dependent start time.
* The returned real time is only useful for computing an elapsed time
* between two calls to this function.
*/
static double mu_timer_real(void) {
#if defined(_WIN32)
/* Windows 2000 and later. ---------------------------------- */
LARGE_INTEGER Time;
LARGE_INTEGER Frequency;
QueryPerformanceFrequency(&Frequency);
QueryPerformanceCounter(&Time);
Time.QuadPart *= 1000000;
Time.QuadPart /= Frequency.QuadPart;
return (double)Time.QuadPart / 1000000.0;
#elif (defined(__hpux) || defined(hpux)) || \
((defined(__sun__) || defined(__sun) || defined(sun)) && \
(defined(__SVR4) || defined(__svr4__)))
/* HP-UX, Solaris. ------------------------------------------ */
return (double)gethrtime() / 1000000000.0;
#elif defined(__MACH__) && defined(__APPLE__)
/* OSX. ----------------------------------------------------- */
static double timeConvert = 0.0;
if (timeConvert == 0.0) {
mach_timebase_info_data_t timeBase;
(void)mach_timebase_info(&timeBase);
timeConvert =
(double)timeBase.numer / (double)timeBase.denom / 1000000000.0;
}
return (double)mach_absolute_time() * timeConvert;
#elif defined(_POSIX_VERSION)
/* POSIX. --------------------------------------------------- */
struct timeval tm;
#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
{
struct timespec ts;
#if defined(CLOCK_MONOTONIC_PRECISE)
/* BSD. --------------------------------------------- */
const clockid_t id = CLOCK_MONOTONIC_PRECISE;
#elif defined(CLOCK_MONOTONIC_RAW)
/* Linux. ------------------------------------------- */
const clockid_t id = CLOCK_MONOTONIC_RAW;
#elif defined(CLOCK_HIGHRES)
/* Solaris. ----------------------------------------- */
const clockid_t id = CLOCK_HIGHRES;
#elif defined(CLOCK_MONOTONIC)
/* AIX, BSD, Linux, POSIX, Solaris. ----------------- */
const clockid_t id = CLOCK_MONOTONIC;
#elif defined(CLOCK_REALTIME)
/* AIX, BSD, HP-UX, Linux, POSIX. ------------------- */
const clockid_t id = CLOCK_REALTIME;
#else
const clockid_t id = (clockid_t)-1; /* Unknown. */
#endif /* CLOCK_* */
if (id != (clockid_t)-1 && clock_gettime(id, &ts) != -1)
return (double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0;
/* Fall thru. */
}
#endif /* _POSIX_TIMERS */
/* AIX, BSD, Cygwin, HP-UX, Linux, OSX, POSIX, Solaris. ----- */
gettimeofday(&tm, NULL);
return (double)tm.tv_sec + (double)tm.tv_usec / 1000000.0;
#else
return -1.0; /* Failed. */
#endif
}
/**
* Returns the amount of CPU time used by the current process,
* in seconds, or -1.0 if an error occurred.
*/
static double mu_timer_cpu(void) {
#if defined(_WIN32)
/* Windows -------------------------------------------------- */
FILETIME createTime;
FILETIME exitTime;
FILETIME kernelTime;
FILETIME userTime;
/* This approach has a resolution of 1/64 second. Unfortunately, Windows' API
* does not offer better */
if (GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime,
&userTime) != 0) {
ULARGE_INTEGER userSystemTime;
memcpy(&userSystemTime, &userTime, sizeof(ULARGE_INTEGER));
return (double)userSystemTime.QuadPart / 10000000.0;
}
#elif defined(__unix__) || defined(__unix) || defined(unix) || \
(defined(__APPLE__) && defined(__MACH__))
/* AIX, BSD, Cygwin, HP-UX, Linux, OSX, and Solaris --------- */
#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
/* Prefer high-res POSIX timers, when available. */
{
clockid_t id;
struct timespec ts;
#if _POSIX_CPUTIME > 0
/* Clock ids vary by OS. Query the id, if possible. */
if (clock_getcpuclockid(0, &id) == -1)
#endif
#if defined(CLOCK_PROCESS_CPUTIME_ID)
/* Use known clock id for AIX, Linux, or Solaris. */
id = CLOCK_PROCESS_CPUTIME_ID;
#elif defined(CLOCK_VIRTUAL)
/* Use known clock id for BSD or HP-UX. */
id = CLOCK_VIRTUAL;
#else
id = (clockid_t)-1;
#endif
if (id != (clockid_t)-1 && clock_gettime(id, &ts) != -1)
return (double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0;
}
#endif
#if defined(RUSAGE_SELF)
{
struct rusage rusage;
if (getrusage(RUSAGE_SELF, &rusage) != -1)
return (double)rusage.ru_utime.tv_sec +
(double)rusage.ru_utime.tv_usec / 1000000.0;
}
#endif
#if defined(_SC_CLK_TCK)
{
const double ticks = (double)sysconf(_SC_CLK_TCK);
struct tms tms;
if (times(&tms) != (clock_t)-1)
return (double)tms.tms_utime / ticks;
}
#endif
#if defined(CLOCKS_PER_SEC)
{
clock_t cl = clock();
if (cl != (clock_t)-1)
return (double)cl / (double)CLOCKS_PER_SEC;
}
#endif
#endif
return -1; /* Failed. */
}
#ifdef __cplusplus
}
#endif
#endif /* MINUNIT_MINUNIT_H */

View File

@ -1,495 +0,0 @@
#include <zip.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#if defined(_MSC_VER) || defined(__MINGW64__) || defined(__MINGW32__)
#define MZ_FILE_STAT_STRUCT _stat
#define MZ_FILE_STAT _stat
#else
#define MZ_FILE_STAT_STRUCT stat
#define MZ_FILE_STAT stat
#endif
#define ZIPNAME "test.zip\0"
#define TESTDATA1 "Some test data 1...\0"
#define CRC32DATA1 2220805626
#define TESTDATA2 "Some test data 2...\0"
#define CRC32DATA2 2532008468
#define RFILE "4.txt\0"
#define RMODE 0100444
#define WFILE "6.txt\0"
#define WMODE 0100666
#define XFILE "7.txt\0"
#define XMODE 0100777
#define UNIXMODE 0100644
#define UNUSED(x) (void)x
static int total_entries = 0;
static void test_write(void) {
struct zip_t *zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
assert(zip != NULL);
assert(0 == zip_entry_open(zip, "test/test-1.txt"));
assert(0 == zip_entry_write(zip, TESTDATA1, strlen(TESTDATA1)));
assert(0 == strcmp(zip_entry_name(zip), "test/test-1.txt"));
assert(total_entries == zip_entry_index(zip));
assert(strlen(TESTDATA1) == zip_entry_size(zip));
assert(CRC32DATA1 == zip_entry_crc32(zip));
++total_entries;
assert(0 == zip_entry_close(zip));
assert(0 == zip_is64(zip));
zip_close(zip);
}
static void test_append(void) {
struct zip_t *zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'a');
assert(zip != NULL);
assert(0 == zip_entry_open(zip, "test\\test-2.txt"));
assert(0 == strcmp(zip_entry_name(zip), "test/test-2.txt"));
assert(total_entries == zip_entry_index(zip));
assert(0 == zip_entry_write(zip, TESTDATA2, strlen(TESTDATA2)));
assert(strlen(TESTDATA2) == zip_entry_size(zip));
assert(CRC32DATA2 == zip_entry_crc32(zip));
++total_entries;
assert(0 == zip_entry_close(zip));
assert(0 == zip_entry_open(zip, "test\\empty/"));
assert(0 == strcmp(zip_entry_name(zip), "test/empty/"));
assert(0 == zip_entry_size(zip));
assert(0 == zip_entry_crc32(zip));
assert(total_entries == zip_entry_index(zip));
++total_entries;
assert(0 == zip_entry_close(zip));
assert(0 == zip_entry_open(zip, "empty/"));
assert(0 == strcmp(zip_entry_name(zip), "empty/"));
assert(0 == zip_entry_size(zip));
assert(0 == zip_entry_crc32(zip));
assert(total_entries == zip_entry_index(zip));
++total_entries;
assert(0 == zip_entry_close(zip));
zip_close(zip);
}
static void test_read(void) {
char *buf = NULL;
ssize_t bufsize;
size_t buftmp;
struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
assert(zip != NULL);
assert(0 == zip_is64(zip));
assert(0 == zip_entry_open(zip, "test\\test-1.txt"));
assert(strlen(TESTDATA1) == zip_entry_size(zip));
assert(CRC32DATA1 == zip_entry_crc32(zip));
bufsize = zip_entry_read(zip, (void **)&buf, &buftmp);
assert(bufsize == strlen(TESTDATA1));
assert((size_t)bufsize == buftmp);
assert(0 == strncmp(buf, TESTDATA1, bufsize));
assert(0 == zip_entry_close(zip));
free(buf);
buf = NULL;
bufsize = 0;
assert(0 == zip_entry_open(zip, "test/test-2.txt"));
assert(strlen(TESTDATA2) == zip_entry_size(zip));
assert(CRC32DATA2 == zip_entry_crc32(zip));
bufsize = zip_entry_read(zip, (void **)&buf, NULL);
assert((size_t)bufsize == strlen(TESTDATA2));
assert(0 == strncmp(buf, TESTDATA2, (size_t)bufsize));
assert(0 == zip_entry_close(zip));
free(buf);
buf = NULL;
bufsize = 0;
assert(0 == zip_entry_open(zip, "test\\empty/"));
assert(0 == strcmp(zip_entry_name(zip), "test/empty/"));
assert(0 == zip_entry_size(zip));
assert(0 == zip_entry_crc32(zip));
assert(0 == zip_entry_close(zip));
buftmp = strlen(TESTDATA2);
buf = calloc(buftmp, sizeof(char));
assert(0 == zip_entry_open(zip, "test/test-2.txt"));
bufsize = zip_entry_noallocread(zip, (void *)buf, buftmp);
assert(buftmp == (size_t)bufsize);
assert(0 == strncmp(buf, TESTDATA2, buftmp));
assert(0 == zip_entry_close(zip));
free(buf);
buf = NULL;
bufsize = 0;
buftmp = strlen(TESTDATA1);
buf = calloc(buftmp, sizeof(char));
assert(0 == zip_entry_open(zip, "test/test-1.txt"));
bufsize = zip_entry_noallocread(zip, (void *)buf, buftmp);
assert(buftmp == (size_t)bufsize);
assert(0 == strncmp(buf, TESTDATA1, buftmp));
assert(0 == zip_entry_close(zip));
free(buf);
buf = NULL;
bufsize = 0;
zip_close(zip);
}
struct buffer_t {
char *data;
size_t size;
};
static size_t on_extract(void *arg, unsigned long long offset, const void *data,
size_t size) {
UNUSED(offset);
struct buffer_t *buf = (struct buffer_t *)arg;
buf->data = realloc(buf->data, buf->size + size + 1);
assert(NULL != buf->data);
memcpy(&(buf->data[buf->size]), data, size);
buf->size += size;
buf->data[buf->size] = 0;
return size;
}
static void test_extract(void) {
struct buffer_t buf;
struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
assert(zip != NULL);
memset((void *)&buf, 0, sizeof(struct buffer_t));
assert(0 == zip_entry_open(zip, "test/test-1.txt"));
assert(0 == zip_entry_extract(zip, on_extract, &buf));
assert(buf.size == strlen(TESTDATA1));
assert(0 == strncmp(buf.data, TESTDATA1, buf.size));
assert(0 == zip_entry_close(zip));
free(buf.data);
buf.data = NULL;
buf.size = 0;
zip_close(zip);
}
static void test_total_entries(void) {
struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
assert(zip != NULL);
int n = zip_total_entries(zip);
zip_close(zip);
assert(n == total_entries);
}
static void test_entry_name(void) {
struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
assert(zip != NULL);
assert(zip_entry_name(zip) == NULL);
assert(0 == zip_entry_open(zip, "test\\test-1.txt"));
assert(NULL != zip_entry_name(zip));
assert(0 == strcmp(zip_entry_name(zip), "test/test-1.txt"));
assert(strlen(TESTDATA1) == zip_entry_size(zip));
assert(CRC32DATA1 == zip_entry_crc32(zip));
assert(0 == zip_entry_index(zip));
assert(0 == zip_entry_close(zip));
assert(0 == zip_entry_open(zip, "test/test-2.txt"));
assert(NULL != zip_entry_name(zip));
assert(0 == strcmp(zip_entry_name(zip), "test/test-2.txt"));
assert(strlen(TESTDATA2) == zip_entry_size(zip));
assert(CRC32DATA2 == zip_entry_crc32(zip));
assert(1 == zip_entry_index(zip));
assert(0 == zip_entry_close(zip));
zip_close(zip);
}
static void test_entry_index(void) {
struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
assert(zip != NULL);
assert(0 == zip_entry_open(zip, "test\\test-1.txt"));
assert(0 == zip_entry_index(zip));
assert(0 == strcmp(zip_entry_name(zip), "test/test-1.txt"));
assert(strlen(TESTDATA1) == zip_entry_size(zip));
assert(CRC32DATA1 == zip_entry_crc32(zip));
assert(0 == zip_entry_close(zip));
assert(0 == zip_entry_open(zip, "test/test-2.txt"));
assert(1 == zip_entry_index(zip));
assert(0 == strcmp(zip_entry_name(zip), "test/test-2.txt"));
assert(strlen(TESTDATA2) == zip_entry_size(zip));
assert(CRC32DATA2 == zip_entry_crc32(zip));
assert(0 == zip_entry_close(zip));
zip_close(zip);
}
static void test_entry_openbyindex(void) {
struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
assert(zip != NULL);
assert(0 == zip_entry_openbyindex(zip, 1));
assert(1 == zip_entry_index(zip));
assert(strlen(TESTDATA2) == zip_entry_size(zip));
assert(CRC32DATA2 == zip_entry_crc32(zip));
assert(0 == strcmp(zip_entry_name(zip), "test/test-2.txt"));
assert(0 == zip_entry_close(zip));
assert(0 == zip_entry_openbyindex(zip, 0));
assert(0 == zip_entry_index(zip));
assert(strlen(TESTDATA1) == zip_entry_size(zip));
assert(CRC32DATA1 == zip_entry_crc32(zip));
assert(0 == strcmp(zip_entry_name(zip), "test/test-1.txt"));
assert(0 == zip_entry_close(zip));
zip_close(zip);
}
static void test_list_entries(void) {
struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
assert(zip != NULL);
int i = 0, n = zip_total_entries(zip);
for (; i < n; ++i) {
assert(0 == zip_entry_openbyindex(zip, i));
fprintf(stdout, "[%d]: %s", i, zip_entry_name(zip));
if (zip_entry_isdir(zip)) {
fprintf(stdout, " (DIR)");
}
fprintf(stdout, "\n");
assert(0 == zip_entry_close(zip));
}
zip_close(zip);
}
static void test_fwrite(void) {
const char *filename = WFILE;
FILE *stream = NULL;
struct zip_t *zip = NULL;
#if defined(_MSC_VER)
if (0 != fopen_s(&stream, filename, "w+"))
#else
if (!(stream = fopen(filename, "w+")))
#endif
{
// Cannot open filename
fprintf(stdout, "Cannot open filename\n");
assert(0 == -1);
}
fwrite(TESTDATA1, sizeof(char), strlen(TESTDATA1), stream);
assert(0 == fclose(stream));
zip = zip_open(ZIPNAME, 9, 'w');
assert(zip != NULL);
assert(0 == zip_entry_open(zip, WFILE));
assert(0 == zip_entry_fwrite(zip, WFILE));
assert(0 == zip_entry_close(zip));
assert(0 == zip_is64(zip));
zip_close(zip);
remove(WFILE);
remove(ZIPNAME);
}
static void test_exe_permissions(void) {
#if defined(_WIN32) || defined(__WIN32__)
#else
struct MZ_FILE_STAT_STRUCT file_stats;
const char *filenames[] = {XFILE};
FILE *f = fopen(XFILE, "w");
fclose(f);
chmod(XFILE, XMODE);
remove(ZIPNAME);
assert(0 == zip_create(ZIPNAME, filenames, 1));
remove(XFILE);
assert(0 == zip_extract(ZIPNAME, ".", NULL, NULL));
assert(0 == MZ_FILE_STAT(XFILE, &file_stats));
assert(XMODE == file_stats.st_mode);
remove(XFILE);
remove(ZIPNAME);
#endif
}
static void test_read_permissions(void) {
#if defined(_MSC_VER)
#else
struct MZ_FILE_STAT_STRUCT file_stats;
const char *filenames[] = {RFILE};
FILE *f = fopen(RFILE, "w");
fclose(f);
chmod(RFILE, RMODE);
remove(ZIPNAME);
assert(0 == zip_create(ZIPNAME, filenames, 1));
// chmod from 444 to 666 to be able delete the file on windows
chmod(RFILE, WMODE);
remove(RFILE);
assert(0 == zip_extract(ZIPNAME, ".", NULL, NULL));
assert(0 == MZ_FILE_STAT(RFILE, &file_stats));
assert(RMODE == file_stats.st_mode);
chmod(RFILE, WMODE);
remove(RFILE);
remove(ZIPNAME);
#endif
}
static void test_write_permissions(void) {
#if defined(_MSC_VER)
#else
struct MZ_FILE_STAT_STRUCT file_stats;
const char *filenames[] = {WFILE};
FILE *f = fopen(WFILE, "w");
fclose(f);
chmod(WFILE, WMODE);
remove(ZIPNAME);
assert(0 == zip_create(ZIPNAME, filenames, 1));
remove(WFILE);
assert(0 == zip_extract(ZIPNAME, ".", NULL, NULL));
assert(0 == MZ_FILE_STAT(WFILE, &file_stats));
assert(WMODE == file_stats.st_mode);
remove(WFILE);
remove(ZIPNAME);
#endif
}
static void test_mtime(void) {
struct MZ_FILE_STAT_STRUCT file_stat1, file_stat2;
const char *filename = WFILE;
FILE *stream = NULL;
struct zip_t *zip = NULL;
#if defined(_MSC_VER)
if (0 != fopen_s(&stream, filename, "w+"))
#else
if (!(stream = fopen(filename, "w+")))
#endif
{
// Cannot open filename
fprintf(stdout, "Cannot open filename\n");
assert(0 == -1);
}
fwrite(TESTDATA1, sizeof(char), strlen(TESTDATA1), stream);
assert(0 == fclose(stream));
memset(&file_stat1, 0, sizeof(file_stat1));
memset(&file_stat2, 0, sizeof(file_stat2));
zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
assert(zip != NULL);
assert(0 == zip_entry_open(zip, filename));
assert(0 == zip_entry_fwrite(zip, filename));
assert(0 == zip_entry_close(zip));
zip_close(zip);
assert(0 == MZ_FILE_STAT(filename, &file_stat1));
remove(filename);
assert(0 == zip_extract(ZIPNAME, ".", NULL, NULL));
assert(0 == MZ_FILE_STAT(filename, &file_stat2));
fprintf(stdout, "file_stat1.st_mtime: %lu\n", file_stat1.st_mtime);
fprintf(stdout, "file_stat2.st_mtime: %lu\n", file_stat2.st_mtime);
assert(labs(file_stat1.st_mtime - file_stat2.st_mtime) <= 1);
remove(filename);
remove(ZIPNAME);
}
static void test_unix_permissions(void) {
#if defined(_WIN64) || defined(_WIN32) || defined(__WIN32__)
#else
// UNIX or APPLE
struct MZ_FILE_STAT_STRUCT file_stats;
remove(ZIPNAME);
struct zip_t *zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
assert(zip != NULL);
assert(0 == zip_entry_open(zip, RFILE));
assert(0 == zip_entry_write(zip, TESTDATA1, strlen(TESTDATA1)));
assert(0 == zip_entry_close(zip));
zip_close(zip);
remove(RFILE);
assert(0 == zip_extract(ZIPNAME, ".", NULL, NULL));
assert(0 == MZ_FILE_STAT(RFILE, &file_stats));
assert(UNIXMODE == file_stats.st_mode);
remove(RFILE);
remove(ZIPNAME);
#endif
}
int main(int argc, char *argv[]) {
UNUSED(argc);
UNUSED(argv);
remove(ZIPNAME);
test_write();
test_append();
test_read();
test_extract();
test_total_entries();
test_entry_name();
test_entry_index();
test_entry_openbyindex();
test_list_entries();
test_fwrite();
test_read_permissions();
test_write_permissions();
test_exe_permissions();
test_mtime();
test_unix_permissions();
remove(ZIPNAME);
return 0;
}

96
test/test_append.c Normal file
View File

@ -0,0 +1,96 @@
#include <stdio.h>
#include <stdlib.h>
#include <zip.h>
#include "minunit.h"
static char ZIPNAME[L_tmpnam + 1] = {0};
static int total_entries = 0;
#define TESTDATA1 "Some test data 1...\0"
void test_setup(void) {
strncpy(ZIPNAME, "z-XXXXXX\0", L_tmpnam);
mktemp(ZIPNAME);
struct zip_t *zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
zip_entry_open(zip, "test/test-1.txt");
zip_entry_write(zip, TESTDATA1, strlen(TESTDATA1));
zip_entry_close(zip);
++total_entries;
zip_close(zip);
}
void test_teardown(void) { remove(ZIPNAME); }
#define TESTDATA2 "Some test data 2...\0"
#define CRC32DATA2 2532008468
MU_TEST(test_append) {
struct zip_t *zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'a');
mu_check(zip != NULL);
mu_assert_int_eq(0, zip_entry_open(zip, "test\\test-2.txt"));
mu_assert_int_eq(0, strcmp(zip_entry_name(zip), "test/test-2.txt"));
mu_assert_int_eq(total_entries, zip_entry_index(zip));
mu_assert_int_eq(0, zip_entry_write(zip, TESTDATA2, strlen(TESTDATA2)));
mu_assert_int_eq(strlen(TESTDATA2), zip_entry_size(zip));
mu_check(CRC32DATA2 == zip_entry_crc32(zip));
mu_assert_int_eq(0, zip_entry_close(zip));
++total_entries;
zip_close(zip);
zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'a');
mu_assert_int_eq(0, zip_entry_open(zip, "test\\empty/"));
mu_assert_int_eq(0, strcmp(zip_entry_name(zip), "test/empty/"));
mu_assert_int_eq(0, zip_entry_size(zip));
mu_assert_int_eq(0, zip_entry_crc32(zip));
mu_assert_int_eq(total_entries, zip_entry_index(zip));
mu_assert_int_eq(0, zip_entry_close(zip));
++total_entries;
zip_close(zip);
zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'a');
mu_assert_int_eq(0, zip_entry_open(zip, "empty/"));
mu_assert_int_eq(0, strcmp(zip_entry_name(zip), "empty/"));
mu_assert_int_eq(0, zip_entry_size(zip));
mu_assert_int_eq(0, zip_entry_crc32(zip));
mu_assert_int_eq(total_entries, zip_entry_index(zip));
mu_assert_int_eq(0, zip_entry_close(zip));
++total_entries;
mu_assert_int_eq(0, zip_entry_open(zip, "dotfiles/.test"));
mu_assert_int_eq(0, strcmp(zip_entry_name(zip), "dotfiles/.test"));
mu_assert_int_eq(0, zip_entry_size(zip));
mu_assert_int_eq(0, zip_entry_crc32(zip));
mu_assert_int_eq(0, zip_entry_write(zip, TESTDATA2, strlen(TESTDATA2)));
mu_assert_int_eq(strlen(TESTDATA2), zip_entry_size(zip));
mu_check(CRC32DATA2 == zip_entry_crc32(zip));
mu_assert_int_eq(total_entries, zip_entry_index(zip));
mu_assert_int_eq(0, zip_entry_close(zip));
++total_entries;
mu_assert_int_eq(total_entries, zip_entries_total(zip));
zip_close(zip);
}
MU_TEST_SUITE(test_append_suite) {
MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
MU_RUN_TEST(test_append);
}
#define UNUSED(x) (void)x
int main(int argc, char *argv[]) {
UNUSED(argc);
UNUSED(argv);
MU_RUN_SUITE(test_append_suite);
MU_REPORT();
return MU_EXIT_CODE;
}

297
test/test_entry.c Normal file
View File

@ -0,0 +1,297 @@
#include <stdio.h>
#include <stdlib.h>
#include <zip.h>
#include "minunit.h"
static char ZIPNAME[L_tmpnam + 1] = {0};
#define CRC32DATA1 2220805626
#define TESTDATA1 "Some test data 1...\0"
#define TESTDATA2 "Some test data 2...\0"
#define CRC32DATA2 2532008468
static int total_entries = 0;
void test_setup(void) {
strncpy(ZIPNAME, "z-XXXXXX\0", L_tmpnam);
mktemp(ZIPNAME);
struct zip_t *zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
zip_entry_open(zip, "test/test-1.txt");
zip_entry_write(zip, TESTDATA1, strlen(TESTDATA1));
zip_entry_close(zip);
++total_entries;
zip_entry_open(zip, "test\\test-2.txt");
zip_entry_write(zip, TESTDATA2, strlen(TESTDATA2));
zip_entry_close(zip);
++total_entries;
zip_entry_open(zip, "test\\empty/");
zip_entry_close(zip);
++total_entries;
zip_entry_open(zip, "empty/");
zip_entry_close(zip);
++total_entries;
zip_entry_open(zip, "dotfiles/.test");
zip_entry_write(zip, TESTDATA2, strlen(TESTDATA2));
zip_entry_close(zip);
++total_entries;
zip_entry_open(zip, "delete.me");
zip_entry_write(zip, TESTDATA1, strlen(TESTDATA1));
zip_entry_close(zip);
++total_entries;
zip_entry_open(zip, "_");
zip_entry_write(zip, TESTDATA2, strlen(TESTDATA2));
zip_entry_close(zip);
++total_entries;
zip_entry_open(zip, "delete/file.1");
zip_entry_write(zip, TESTDATA1, strlen(TESTDATA1));
zip_entry_close(zip);
++total_entries;
zip_entry_open(zip, "delete/file.2");
zip_entry_write(zip, TESTDATA2, strlen(TESTDATA2));
zip_entry_close(zip);
++total_entries;
zip_entry_open(zip, "deleteme/file.3");
zip_entry_write(zip, TESTDATA1, strlen(TESTDATA1));
zip_entry_close(zip);
++total_entries;
zip_entry_open(zip, "delete/file.4");
zip_entry_write(zip, TESTDATA2, strlen(TESTDATA2));
zip_entry_close(zip);
++total_entries;
zip_close(zip);
}
void test_teardown(void) {
total_entries = 0;
remove(ZIPNAME);
}
MU_TEST(test_entry_name) {
struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
mu_check(zip != NULL);
mu_check(zip_entry_name(zip) == NULL);
mu_assert_int_eq(0, zip_entry_open(zip, "test\\test-1.txt"));
mu_check(NULL != zip_entry_name(zip));
mu_assert_int_eq(0, strcmp(zip_entry_name(zip), "test/test-1.txt"));
mu_assert_int_eq(strlen(TESTDATA1), zip_entry_size(zip));
mu_check(CRC32DATA1 == zip_entry_crc32(zip));
mu_assert_int_eq(0, zip_entry_index(zip));
mu_assert_int_eq(0, zip_entry_close(zip));
mu_assert_int_eq(0, zip_entry_open(zip, "test/test-2.txt"));
mu_check(NULL != zip_entry_name(zip));
mu_assert_int_eq(0, strcmp(zip_entry_name(zip), "test/test-2.txt"));
mu_assert_int_eq(strlen(TESTDATA2), zip_entry_size(zip));
mu_check(CRC32DATA2 == zip_entry_crc32(zip));
mu_assert_int_eq(1, zip_entry_index(zip));
mu_assert_int_eq(0, zip_entry_close(zip));
zip_close(zip);
}
MU_TEST(test_entry_opencasesensitive) {
struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
mu_check(zip != NULL);
mu_check(zip_entry_name(zip) == NULL);
mu_assert_int_eq(0, zip_entry_open(zip, "test/TEST-1.TXT"));
mu_check(NULL != zip_entry_name(zip));
mu_assert_int_eq(0, zip_entry_close(zip));
mu_assert_int_eq(ZIP_ENOENT,
zip_entry_opencasesensitive(zip, "test/TEST-1.TXT"));
zip_close(zip);
}
MU_TEST(test_entry_index) {
struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
mu_check(zip != NULL);
mu_assert_int_eq(0, zip_entry_open(zip, "test\\test-1.txt"));
mu_assert_int_eq(0, zip_entry_index(zip));
mu_assert_int_eq(0, strcmp(zip_entry_name(zip), "test/test-1.txt"));
mu_assert_int_eq(strlen(TESTDATA1), zip_entry_size(zip));
mu_check(CRC32DATA1 == zip_entry_crc32(zip));
mu_assert_int_eq(0, zip_entry_close(zip));
mu_assert_int_eq(0, zip_entry_open(zip, "test/test-2.txt"));
mu_assert_int_eq(1, zip_entry_index(zip));
mu_assert_int_eq(0, strcmp(zip_entry_name(zip), "test/test-2.txt"));
mu_assert_int_eq(strlen(TESTDATA2), zip_entry_size(zip));
mu_check(CRC32DATA2 == zip_entry_crc32(zip));
mu_assert_int_eq(0, zip_entry_close(zip));
zip_close(zip);
}
MU_TEST(test_entry_openbyindex) {
struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
mu_check(zip != NULL);
mu_assert_int_eq(0, zip_entry_openbyindex(zip, 1));
mu_assert_int_eq(1, zip_entry_index(zip));
mu_assert_int_eq(strlen(TESTDATA2), zip_entry_size(zip));
mu_check(CRC32DATA2 == zip_entry_crc32(zip));
mu_assert_int_eq(0, strcmp(zip_entry_name(zip), "test/test-2.txt"));
mu_assert_int_eq(0, zip_entry_close(zip));
mu_assert_int_eq(0, zip_entry_openbyindex(zip, 0));
mu_assert_int_eq(0, zip_entry_index(zip));
mu_assert_int_eq(strlen(TESTDATA1), zip_entry_size(zip));
mu_check(CRC32DATA1 == zip_entry_crc32(zip));
mu_assert_int_eq(0, strcmp(zip_entry_name(zip), "test/test-1.txt"));
mu_assert_int_eq(0, zip_entry_close(zip));
zip_close(zip);
}
MU_TEST(test_entry_read) {
char *bufencode1 = NULL;
char *bufencode2 = NULL;
char *buf = NULL;
size_t bufsize;
struct zip_t *zip =
zip_stream_open(NULL, 0, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
mu_check(zip != NULL);
mu_assert_int_eq(0, zip_entry_open(zip, "test/test-1.txt"));
mu_assert_int_eq(0, zip_entry_write(zip, TESTDATA1, strlen(TESTDATA1)));
mu_assert_int_eq(0, zip_entry_close(zip));
ssize_t n = zip_stream_copy(zip, (void **)&bufencode1, NULL);
zip_stream_copy(zip, (void **)&bufencode2, &bufsize);
mu_assert_int_eq(0, strncmp(bufencode1, bufencode2, bufsize));
zip_stream_close(zip);
struct zip_t *zipstream = zip_stream_open(bufencode1, n, 0, 'r');
mu_check(zipstream != NULL);
mu_assert_int_eq(0, zip_entry_open(zipstream, "test/test-1.txt"));
n = zip_entry_read(zipstream, (void **)&buf, NULL);
mu_assert_int_eq(0, strncmp(buf, TESTDATA1, (size_t)n));
mu_assert_int_eq(0, zip_entry_close(zipstream));
zip_stream_close(zipstream);
free(buf);
free(bufencode1);
free(bufencode2);
}
MU_TEST(test_list_entries) {
struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
mu_check(zip != NULL);
int i = 0, n = zip_entries_total(zip);
for (; i < n; ++i) {
mu_assert_int_eq(0, zip_entry_openbyindex(zip, i));
fprintf(stdout, "[%d]: %s", i, zip_entry_name(zip));
if (zip_entry_isdir(zip)) {
fprintf(stdout, " (DIR)");
}
fprintf(stdout, "\n");
mu_assert_int_eq(0, zip_entry_close(zip));
}
zip_close(zip);
}
MU_TEST(test_entries_delete) {
char *entries[] = {"delete.me", "_", "delete/file.1", "deleteme/file.3",
"delete/file.2"};
struct zip_t *zip = zip_open(ZIPNAME, 0, 'd');
mu_check(zip != NULL);
mu_assert_int_eq(5, zip_entries_delete(zip, entries, 5));
zip_close(zip);
zip = zip_open(ZIPNAME, 0, 'r');
mu_check(zip != NULL);
mu_assert_int_eq(ZIP_ENOENT, zip_entry_open(zip, "delete.me"));
mu_assert_int_eq(0, zip_entry_close(zip));
fprintf(stdout, "delete.me: %s\n", zip_strerror(ZIP_ENOENT));
mu_assert_int_eq(ZIP_ENOENT, zip_entry_open(zip, "_"));
mu_assert_int_eq(0, zip_entry_close(zip));
fprintf(stdout, "_: %s\n", zip_strerror(ZIP_ENOENT));
mu_assert_int_eq(ZIP_ENOENT, zip_entry_open(zip, "delete/file.1"));
mu_assert_int_eq(0, zip_entry_close(zip));
fprintf(stdout, "delete/file.1: %s\n", zip_strerror(ZIP_ENOENT));
mu_assert_int_eq(ZIP_ENOENT, zip_entry_open(zip, "deleteme/file.3"));
mu_assert_int_eq(0, zip_entry_close(zip));
fprintf(stdout, "delete/file.3: %s\n", zip_strerror(ZIP_ENOENT));
mu_assert_int_eq(ZIP_ENOENT, zip_entry_open(zip, "delete/file.2"));
mu_assert_int_eq(0, zip_entry_close(zip));
fprintf(stdout, "delete/file.2: %s\n", zip_strerror(ZIP_ENOENT));
mu_assert_int_eq(total_entries - 5, zip_entries_total(zip));
mu_assert_int_eq(0, zip_entry_open(zip, "delete/file.4"));
size_t buftmp = 0;
char *buf = NULL;
ssize_t bufsize = zip_entry_read(zip, (void **)&buf, &buftmp);
mu_assert_int_eq(bufsize, strlen(TESTDATA2));
mu_assert_int_eq((size_t)bufsize, buftmp);
mu_assert_int_eq(0, strncmp(buf, TESTDATA2, bufsize));
mu_assert_int_eq(0, zip_entry_close(zip));
free(buf);
buf = NULL;
zip_close(zip);
}
MU_TEST_SUITE(test_entry_suite) {
MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
MU_RUN_TEST(test_entry_name);
MU_RUN_TEST(test_entry_opencasesensitive);
MU_RUN_TEST(test_entry_index);
MU_RUN_TEST(test_entry_openbyindex);
MU_RUN_TEST(test_entry_read);
MU_RUN_TEST(test_list_entries);
MU_RUN_TEST(test_entries_delete);
}
#define UNUSED(x) (void)x
int main(int argc, char *argv[]) {
UNUSED(argc);
UNUSED(argv);
MU_RUN_SUITE(test_entry_suite);
MU_REPORT();
return MU_EXIT_CODE;
}

153
test/test_extract.c Normal file
View File

@ -0,0 +1,153 @@
#include <stdio.h>
#include <stdlib.h>
#include <zip.h>
#include "minunit.h"
static char ZIPNAME[L_tmpnam + 1] = {0};
#define TESTDATA1 "Some test data 1...\0"
#define TESTDATA2 "Some test data 2...\0"
void test_setup(void) {
strncpy(ZIPNAME, "z-XXXXXX\0", L_tmpnam);
mktemp(ZIPNAME);
struct zip_t *zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
zip_entry_open(zip, "test/test-1.txt");
zip_entry_write(zip, TESTDATA1, strlen(TESTDATA1));
zip_entry_close(zip);
zip_entry_open(zip, "test\\test-2.txt");
zip_entry_write(zip, TESTDATA2, strlen(TESTDATA2));
zip_entry_close(zip);
zip_entry_open(zip, "test\\empty/");
zip_entry_close(zip);
zip_entry_open(zip, "empty/");
zip_entry_close(zip);
zip_entry_open(zip, "dotfiles/.test");
zip_entry_write(zip, TESTDATA2, strlen(TESTDATA2));
zip_entry_close(zip);
zip_close(zip);
}
void test_teardown(void) {
remove("test/test-1.txt");
remove("test/test-2.txt");
remove("test/empty");
remove("test");
remove("empty");
remove("dotfiles/.test");
remove("dotfiles");
remove(ZIPNAME);
}
#define UNUSED(x) (void)x
struct buffer_t {
char *data;
size_t size;
};
static size_t on_extract(void *arg, unsigned long long offset, const void *data,
size_t size) {
UNUSED(offset);
struct buffer_t *buf = (struct buffer_t *)arg;
buf->data = realloc(buf->data, buf->size + size + 1);
memcpy(&(buf->data[buf->size]), data, size);
buf->size += size;
buf->data[buf->size] = 0;
return size;
}
MU_TEST(test_extract) {
struct buffer_t buf;
struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
mu_check(zip != NULL);
memset((void *)&buf, 0, sizeof(struct buffer_t));
mu_assert_int_eq(0, zip_entry_open(zip, "test/test-1.txt"));
mu_assert_int_eq(0, zip_entry_extract(zip, on_extract, &buf));
mu_assert_int_eq(strlen(TESTDATA1), buf.size);
mu_assert_int_eq(0, strncmp(buf.data, TESTDATA1, buf.size));
mu_assert_int_eq(0, zip_entry_close(zip));
free(buf.data);
buf.data = NULL;
buf.size = 0;
memset((void *)&buf, 0, sizeof(struct buffer_t));
mu_assert_int_eq(0, zip_entry_open(zip, "dotfiles/.test"));
mu_assert_int_eq(0, zip_entry_extract(zip, on_extract, &buf));
mu_assert_int_eq(strlen(TESTDATA2), buf.size);
mu_assert_int_eq(0, strncmp(buf.data, TESTDATA2, buf.size));
mu_assert_int_eq(0, zip_entry_close(zip));
free(buf.data);
buf.data = NULL;
buf.size = 0;
zip_close(zip);
}
MU_TEST(test_extract_stream) {
mu_assert_int_eq(
ZIP_ENOINIT,
zip_extract("non_existing_directory/non_existing_archive.zip", ".", NULL,
NULL));
mu_assert_int_eq(ZIP_ENOINIT, zip_stream_extract("", 0, ".", NULL, NULL));
fprintf(stdout, "zip_stream_extract: %s\n", zip_strerror(ZIP_ENOINIT));
FILE *fp = NULL;
#if defined(_MSC_VER)
if (0 != fopen_s(&fp, ZIPNAME, "rb+"))
#else
if (!(fp = fopen(ZIPNAME, "rb+")))
#endif
{
mu_fail("Cannot open filename\n");
}
fseek(fp, 0L, SEEK_END);
size_t filesize = ftell(fp);
fseek(fp, 0L, SEEK_SET);
char *stream = (char *)malloc(filesize * sizeof(char));
memset(stream, 0, filesize);
size_t size = fread(stream, sizeof(char), filesize, fp);
mu_assert_int_eq(filesize, size);
mu_assert_int_eq(0, zip_stream_extract(stream, size, ".", NULL, NULL));
free(stream);
fclose(fp);
}
MU_TEST_SUITE(test_extract_suite) {
MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
MU_RUN_TEST(test_extract);
MU_RUN_TEST(test_extract_stream);
}
int main(int argc, char *argv[]) {
UNUSED(argc);
UNUSED(argv);
MU_RUN_SUITE(test_extract_suite);
MU_REPORT();
return MU_EXIT_CODE;
}

183
test/test_permissions.c Normal file
View File

@ -0,0 +1,183 @@
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <zip.h>
#include "minunit.h"
static char ZIPNAME[L_tmpnam + 1] = {0};
static char XFILE[L_tmpnam + 1] = {0};
static char RFILE[L_tmpnam + 1] = {0};
static char WFILE[L_tmpnam + 1] = {0};
void test_setup(void) {
strncpy(ZIPNAME, "z-XXXXXX\0", L_tmpnam);
strncpy(XFILE, "x-XXXXXX\0", L_tmpnam);
strncpy(RFILE, "r-XXXXXX\0", L_tmpnam);
strncpy(WFILE, "w-XXXXXX\0", L_tmpnam);
mktemp(ZIPNAME);
mktemp(XFILE);
mktemp(RFILE);
mktemp(WFILE);
}
void test_teardown(void) {
remove(WFILE);
remove(RFILE);
remove(XFILE);
remove(ZIPNAME);
}
#if defined(_MSC_VER) || defined(__MINGW32__)
#define MZ_FILE_STAT_STRUCT _stat
#define MZ_FILE_STAT _stat
#else
#define MZ_FILE_STAT_STRUCT stat
#define MZ_FILE_STAT stat
#endif
#define XMODE 0100777
#define RMODE 0100444
#define WMODE 0100666
#define UNIXMODE 0100644
MU_TEST(test_exe_permissions) {
struct MZ_FILE_STAT_STRUCT file_stats;
const char *filenames[] = {XFILE};
FILE *f = fopen(XFILE, "w");
fclose(f);
chmod(XFILE, XMODE);
mu_assert_int_eq(0, zip_create(ZIPNAME, filenames, 1));
remove(XFILE);
mu_assert_int_eq(0, zip_extract(ZIPNAME, ".", NULL, NULL));
mu_assert_int_eq(0, MZ_FILE_STAT(XFILE, &file_stats));
mu_assert_int_eq(XMODE, file_stats.st_mode);
}
MU_TEST(test_read_permissions) {
struct MZ_FILE_STAT_STRUCT file_stats;
const char *filenames[] = {RFILE};
FILE *f = fopen(RFILE, "w");
fclose(f);
chmod(RFILE, RMODE);
mu_assert_int_eq(0, zip_create(ZIPNAME, filenames, 1));
remove(RFILE);
mu_assert_int_eq(0, zip_extract(ZIPNAME, ".", NULL, NULL));
mu_assert_int_eq(0, MZ_FILE_STAT(RFILE, &file_stats));
mu_assert_int_eq(RMODE, file_stats.st_mode);
// chmod from 444 to 666 to be able delete the file on windows
chmod(RFILE, WMODE);
}
MU_TEST(test_write_permissions) {
struct MZ_FILE_STAT_STRUCT file_stats;
const char *filenames[] = {WFILE};
FILE *f = fopen(WFILE, "w");
fclose(f);
chmod(WFILE, WMODE);
mu_assert_int_eq(0, zip_create(ZIPNAME, filenames, 1));
remove(WFILE);
mu_assert_int_eq(0, zip_extract(ZIPNAME, ".", NULL, NULL));
mu_assert_int_eq(0, MZ_FILE_STAT(WFILE, &file_stats));
mu_assert_int_eq(WMODE, file_stats.st_mode);
}
#define TESTDATA1 "Some test data 1...\0"
MU_TEST(test_unix_permissions) {
// UNIX or APPLE
struct MZ_FILE_STAT_STRUCT file_stats;
struct zip_t *zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
mu_check(zip != NULL);
mu_assert_int_eq(0, zip_entry_open(zip, RFILE));
mu_assert_int_eq(0, zip_entry_write(zip, TESTDATA1, strlen(TESTDATA1)));
mu_assert_int_eq(0, zip_entry_close(zip));
zip_close(zip);
mu_assert_int_eq(0, zip_extract(ZIPNAME, ".", NULL, NULL));
mu_assert_int_eq(0, MZ_FILE_STAT(RFILE, &file_stats));
mu_assert_int_eq(UNIXMODE, file_stats.st_mode);
}
MU_TEST(test_mtime) {
struct MZ_FILE_STAT_STRUCT file_stat1, file_stat2;
const char *filename = "test.data";
FILE *stream = NULL;
struct zip_t *zip = NULL;
#if defined(_MSC_VER)
if (0 != fopen_s(&stream, filename, "w+"))
#else
if (!(stream = fopen(filename, "w+")))
#endif
{
mu_fail("Cannot open filename\n");
}
fwrite(TESTDATA1, sizeof(char), strlen(TESTDATA1), stream);
mu_assert_int_eq(0, fclose(stream));
memset(&file_stat1, 0, sizeof(file_stat1));
memset(&file_stat2, 0, sizeof(file_stat2));
zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
mu_check(zip != NULL);
mu_assert_int_eq(0, zip_entry_open(zip, filename));
mu_assert_int_eq(0, zip_entry_fwrite(zip, filename));
mu_assert_int_eq(0, zip_entry_close(zip));
zip_close(zip);
mu_assert_int_eq(0, MZ_FILE_STAT(filename, &file_stat1));
remove(filename);
mu_assert_int_eq(0, zip_extract(ZIPNAME, ".", NULL, NULL));
mu_assert_int_eq(0, MZ_FILE_STAT(filename, &file_stat2));
remove(filename);
fprintf(stdout, "file_stat1.st_mtime: %lu\n", file_stat1.st_mtime);
fprintf(stdout, "file_stat2.st_mtime: %lu\n", file_stat2.st_mtime);
mu_check(labs(file_stat1.st_mtime - file_stat2.st_mtime) <= 1);
}
MU_TEST_SUITE(test_permissions_suite) {
MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
#if defined(_WIN32) || defined(__WIN32__)
#else
MU_RUN_TEST(test_exe_permissions);
MU_RUN_TEST(test_read_permissions);
MU_RUN_TEST(test_write_permissions);
MU_RUN_TEST(test_unix_permissions);
#endif
MU_RUN_TEST(test_mtime);
}
#define UNUSED(x) (void)x
int main(int argc, char *argv[]) {
UNUSED(argc);
UNUSED(argv);
MU_RUN_SUITE(test_permissions_suite);
MU_REPORT();
return MU_EXIT_CODE;
}

140
test/test_read.c Normal file
View File

@ -0,0 +1,140 @@
#include <stdio.h>
#include <stdlib.h>
#include <zip.h>
#include "minunit.h"
static char ZIPNAME[L_tmpnam + 1] = {0};
#define CRC32DATA1 2220805626
#define TESTDATA1 "Some test data 1...\0"
#define TESTDATA2 "Some test data 2...\0"
#define CRC32DATA2 2532008468
void test_setup(void) {
strncpy(ZIPNAME, "z-XXXXXX\0", L_tmpnam);
mktemp(ZIPNAME);
struct zip_t *zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
zip_entry_open(zip, "test/test-1.txt");
zip_entry_write(zip, TESTDATA1, strlen(TESTDATA1));
zip_entry_close(zip);
zip_entry_open(zip, "test\\test-2.txt");
zip_entry_write(zip, TESTDATA2, strlen(TESTDATA2));
zip_entry_close(zip);
zip_entry_open(zip, "test\\empty/");
zip_entry_close(zip);
zip_entry_open(zip, "empty/");
zip_entry_close(zip);
zip_entry_open(zip, "dotfiles/.test");
zip_entry_write(zip, TESTDATA2, strlen(TESTDATA2));
zip_entry_close(zip);
zip_close(zip);
}
void test_teardown(void) { remove(ZIPNAME); }
MU_TEST(test_read) {
char *buf = NULL;
ssize_t bufsize;
size_t buftmp;
struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
mu_check(zip != NULL);
mu_assert_int_eq(1, zip_is64(zip));
mu_assert_int_eq(0, zip_entry_open(zip, "test\\test-1.txt"));
mu_assert_int_eq(strlen(TESTDATA1), zip_entry_size(zip));
mu_check(CRC32DATA1 == zip_entry_crc32(zip));
bufsize = zip_entry_read(zip, (void **)&buf, &buftmp);
mu_assert_int_eq(strlen(TESTDATA1), bufsize);
mu_assert_int_eq((size_t)bufsize, buftmp);
mu_assert_int_eq(0, strncmp(buf, TESTDATA1, bufsize));
mu_assert_int_eq(0, zip_entry_close(zip));
free(buf);
buf = NULL;
mu_assert_int_eq(0, zip_entry_open(zip, "test/test-2.txt"));
mu_assert_int_eq(strlen(TESTDATA2), zip_entry_size(zip));
mu_check(CRC32DATA2 == zip_entry_crc32(zip));
bufsize = zip_entry_read(zip, (void **)&buf, NULL);
mu_assert_int_eq(strlen(TESTDATA2), (size_t)bufsize);
mu_assert_int_eq(0, strncmp(buf, TESTDATA2, (size_t)bufsize));
mu_assert_int_eq(0, zip_entry_close(zip));
free(buf);
buf = NULL;
mu_assert_int_eq(0, zip_entry_open(zip, "test\\empty/"));
mu_assert_int_eq(0, strcmp(zip_entry_name(zip), "test/empty/"));
mu_assert_int_eq(0, zip_entry_size(zip));
mu_assert_int_eq(0, zip_entry_crc32(zip));
mu_assert_int_eq(0, zip_entry_close(zip));
zip_close(zip);
}
MU_TEST(test_noallocread) {
ssize_t bufsize;
size_t buftmp = strlen(TESTDATA2);
char *buf = calloc(buftmp, sizeof(char));
struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
mu_check(zip != NULL);
mu_assert_int_eq(1, zip_is64(zip));
mu_assert_int_eq(0, zip_entry_open(zip, "test/test-2.txt"));
bufsize = zip_entry_noallocread(zip, (void *)buf, buftmp);
mu_assert_int_eq(buftmp, (size_t)bufsize);
mu_assert_int_eq(0, strncmp(buf, TESTDATA2, buftmp));
mu_assert_int_eq(0, zip_entry_close(zip));
free(buf);
buf = NULL;
buftmp = strlen(TESTDATA1);
buf = calloc(buftmp, sizeof(char));
mu_assert_int_eq(0, zip_entry_open(zip, "test/test-1.txt"));
bufsize = zip_entry_noallocread(zip, (void *)buf, buftmp);
mu_assert_int_eq(buftmp, (size_t)bufsize);
mu_assert_int_eq(0, strncmp(buf, TESTDATA1, buftmp));
mu_assert_int_eq(0, zip_entry_close(zip));
free(buf);
buf = NULL;
buftmp = strlen(TESTDATA2);
buf = calloc(buftmp, sizeof(char));
mu_assert_int_eq(0, zip_entry_open(zip, "dotfiles/.test"));
bufsize = zip_entry_noallocread(zip, (void *)buf, buftmp);
mu_assert_int_eq(buftmp, (size_t)bufsize);
mu_assert_int_eq(0, strncmp(buf, TESTDATA2, buftmp));
mu_assert_int_eq(0, zip_entry_close(zip));
free(buf);
buf = NULL;
zip_close(zip);
}
MU_TEST_SUITE(test_read_suite) {
MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
MU_RUN_TEST(test_read);
MU_RUN_TEST(test_noallocread);
}
#define UNUSED(x) (void)x
int main(int argc, char *argv[]) {
UNUSED(argc);
UNUSED(argv);
MU_RUN_SUITE(test_read_suite);
MU_REPORT();
return MU_EXIT_CODE;
}

86
test/test_write.c Normal file
View File

@ -0,0 +1,86 @@
#include <stdio.h>
#include <stdlib.h>
#include <zip.h>
#include "minunit.h"
static char ZIPNAME[L_tmpnam + 1] = {0};
static char WFILE[L_tmpnam + 1] = {0};
void test_setup(void) {
strncpy(ZIPNAME, "z-XXXXXX\0", L_tmpnam);
strncpy(WFILE, "w-XXXXXX\0", L_tmpnam);
mktemp(ZIPNAME);
mktemp(WFILE);
}
void test_teardown(void) {
remove(WFILE);
remove(ZIPNAME);
}
#define CRC32DATA1 2220805626
#define TESTDATA1 "Some test data 1...\0"
MU_TEST(test_write) {
struct zip_t *zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
mu_check(zip != NULL);
mu_assert_int_eq(0, zip_entry_open(zip, "test/test-1.txt"));
mu_assert_int_eq(0, zip_entry_write(zip, TESTDATA1, strlen(TESTDATA1)));
mu_assert_int_eq(0, strcmp(zip_entry_name(zip), "test/test-1.txt"));
mu_assert_int_eq(0, zip_entry_index(zip));
mu_assert_int_eq(strlen(TESTDATA1), zip_entry_size(zip));
mu_check(CRC32DATA1 == zip_entry_crc32(zip));
mu_assert_int_eq(0, zip_entry_close(zip));
mu_assert_int_eq(1, zip_is64(zip));
zip_close(zip);
}
MU_TEST(test_fwrite) {
const char *filename = WFILE;
FILE *stream = NULL;
struct zip_t *zip = NULL;
#if defined(_MSC_VER)
if (0 != fopen_s(&stream, filename, "w+"))
#else
if (!(stream = fopen(filename, "w+")))
#endif
{
// Cannot open filename
mu_fail("Cannot open filename\n");
}
fwrite(TESTDATA1, sizeof(char), strlen(TESTDATA1), stream);
mu_assert_int_eq(0, fclose(stream));
zip = zip_open(ZIPNAME, 9, 'w');
mu_check(zip != NULL);
mu_assert_int_eq(0, zip_entry_open(zip, WFILE));
mu_assert_int_eq(0, zip_entry_fwrite(zip, WFILE));
mu_assert_int_eq(0, zip_entry_close(zip));
mu_assert_int_eq(1, zip_is64(zip));
zip_close(zip);
}
MU_TEST_SUITE(test_write_suite) {
MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
MU_RUN_TEST(test_write);
MU_RUN_TEST(test_fwrite);
}
#define UNUSED(x) (void)x
int main(int argc, char *argv[]) {
UNUSED(argc);
UNUSED(argv);
MU_RUN_SUITE(test_write_suite);
MU_REPORT();
return MU_EXIT_CODE;
}