diff --git a/.gitattributes b/.gitattributes index 6a0e590c..206ef7e6 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1 @@ -Zip/minizip/* linguist-vendored \ No newline at end of file +Zip/minizip/* linguist-vendored diff --git a/.gitignore b/.gitignore index 4f490432..16a48119 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,8 @@ DerivedData # Carthage/Checkouts Carthage/Build + +# Swift Package Manager +.build/ +.swiftpm/ +Packages/ diff --git a/.swift-version b/.swift-version index 9f55b2cc..d346e2ab 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -3.0 +5.3 diff --git a/.travis.yml b/.travis.yml index 273f79b4..ea4417a9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ -osx_image: xcode8 -language: objective-c +osx_image: xcode12 +language: swift env: global: - LC_CTYPE=en_US.UTF-8 @@ -9,6 +9,7 @@ before_install: install: echo "<3" env: - MODE=framework + - MODE=spm - MODE=examples script: ./build.sh $MODE diff --git a/LinuxMain.swift b/LinuxMain.swift new file mode 100644 index 00000000..e0a76d08 --- /dev/null +++ b/LinuxMain.swift @@ -0,0 +1,8 @@ +import XCTest + +import ZipTests + +var tests = [XCTestCaseEntry]() +tests += ZipTests.__allTests() + +XCTMain(tests) diff --git a/Package.swift b/Package.swift new file mode 100644 index 00000000..fd33704c --- /dev/null +++ b/Package.swift @@ -0,0 +1,29 @@ +// swift-tools-version:5.1 +// The swift-tools-version declares the minimum version of Swift required to build this package. +import PackageDescription + +let package = Package( + name: "Zip", + products: [ + .library(name: "Zip", targets: ["Zip"]) + ], + targets: [ + .target( + name: "Minizip", + dependencies: [], + path: "Zip/minizip", + exclude: ["module"], + linkerSettings: [ + .linkedLibrary("z") + ]), + .target( + name: "Zip", + dependencies: ["Minizip"], + path: "Zip", + exclude: ["minizip", "zlib"]), + .testTarget( + name: "ZipTests", + dependencies: ["Zip"], + path: "ZipTests"), + ] +) diff --git a/Package@swift-4.2.swift b/Package@swift-4.2.swift new file mode 100644 index 00000000..6cb1c985 --- /dev/null +++ b/Package@swift-4.2.swift @@ -0,0 +1,30 @@ +// swift-tools-version:4.2 +// The swift-tools-version declares the minimum version of Swift required to build this package. +import PackageDescription + +let package = Package( + name: "Zip", + products: [ + .library(name: "Zip", targets: ["Zip"]) + ], + targets: [ + .systemLibrary( + name: "CZlib", + path: "Zip/zlib", + pkgConfig: "zlib"), + .target( + name: "Minizip", + dependencies: ["CZlib"], + path: "Zip/minizip", + exclude: ["module"]), + .target( + name: "Zip", + dependencies: ["Minizip"], + path: "Zip", + exclude: ["minizip", "zlib"]), + .testTarget( + name: "ZipTests", + dependencies: ["Zip"], + path: "ZipTests"), + ] +) diff --git a/Package@swift-5.0.swift b/Package@swift-5.0.swift new file mode 100644 index 00000000..03bcf3a5 --- /dev/null +++ b/Package@swift-5.0.swift @@ -0,0 +1,29 @@ +// swift-tools-version:5.0 +// The swift-tools-version declares the minimum version of Swift required to build this package. +import PackageDescription + +let package = Package( + name: "Zip", + products: [ + .library(name: "Zip", targets: ["Zip"]) + ], + targets: [ + .target( + name: "Minizip", + dependencies: [], + path: "Zip/minizip", + exclude: ["module"], + linkerSettings: [ + .linkedLibrary("z") + ]), + .target( + name: "Zip", + dependencies: ["Minizip"], + path: "Zip", + exclude: ["minizip", "zlib"]), + .testTarget( + name: "ZipTests", + dependencies: ["Zip"], + path: "ZipTests"), + ] +) diff --git a/README.md b/README.md index 417fe950..a5877eb8 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ ![Zip - Zip and unzip files in Swift](https://cloud.githubusercontent.com/assets/889949/12374908/252373d0-bcac-11e5-8ece-6933aeae8222.png) -[![Build Status](https://travis-ci.org/marmelroy/Zip.svg?branch=master)](https://travis-ci.org/marmelroy/Zip) [![Version](http://img.shields.io/cocoapods/v/Zip.svg)](http://cocoapods.org/?q=Zip) -[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +[![Build Status](https://travis-ci.org/marmelroy/Zip.svg?branch=master)](https://travis-ci.org/marmelroy/Zip) [![Version](http://img.shields.io/cocoapods/v/Zip.svg)](http://cocoapods.org/?q=Zip) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![SPM supported](https://img.shields.io/badge/SPM-supported-brightgreen.svg?style=flat)](https://swift.org/package-manager) + # Zip -A Swift 3.0 framework for zipping and unzipping files. Simple and quick to use. Built on top of [minizip](https://github.com/nmoinvaz/minizip). +A Swift framework for zipping and unzipping files. Simple and quick to use. Built on top of [minizip](https://github.com/nmoinvaz/minizip). ## Usage @@ -53,29 +53,26 @@ catch { ## Custom File Extensions Zip supports '.zip' and '.cbz' files out of the box. To support additional zip-derivative file extensions: -``` +```swift Zip.addCustomFileExtension("file-extension-here") ``` +### [Preferred] Setting up with [Swift Package Manager](https://swift.org/package-manager) +To use Zip with Swift Package Manager, add it to your package's dependencies: +```swift +.package(url: "https://github.com/marmelroy/Zip.git", .upToNextMinor(from: "2.1")) +``` + ### Setting up with [CocoaPods](http://cocoapods.org/?q=Zip) ```ruby source 'https://github.com/CocoaPods/Specs.git' -pod 'Zip', '~> 0.6' -``` - -### Setting up with Carthage - -[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that automates the process of adding frameworks to your Cocoa application. - -You can install Carthage with [Homebrew](http://brew.sh/) using the following command: - -```bash -$ brew update -$ brew install carthage +pod 'Zip', '~> 2.1' ``` +### Setting up with [Carthage](https://github.com/Carthage/Carthage) To integrate Zip into your Xcode project using Carthage, specify it in your `Cartfile`: ```ogdl -github "marmelroy/Zip" +github "marmelroy/Zip" ~> 2.1 ``` + diff --git a/Zip.podspec b/Zip.podspec index e1c0086b..907757f9 100644 --- a/Zip.podspec +++ b/Zip.podspec @@ -8,8 +8,10 @@ Pod::Spec.new do |s| s.name = "Zip" - s.version = "0.6.0" + s.version = "2.1.2" s.summary = "Zip and unzip files in Swift." + s.swift_version = "5.3" + s.swift_versions = ["4.2", "5.0", "5.1", "5.3"] # This description is used to generate tags and improve search results. # * Think: What does it do? Why did you write it? What is the focus? @@ -21,24 +23,20 @@ Pod::Spec.new do |s| DESC s.homepage = "https://github.com/marmelroy/Zip" - # s.screenshots = "www.example.com/screenshots_1", "www.example.com/screenshots_2" s.license = 'MIT' s.author = { "Roy Marmelstein" => "marmelroy@gmail.com" } s.source = { :git => "https://github.com/marmelroy/Zip.git", :tag => s.version.to_s} s.social_media_url = "http://twitter.com/marmelroy" - s.ios.deployment_target = '8.0' + s.ios.deployment_target = '9.0' s.tvos.deployment_target = '9.0' - s.watchos.deployment_target = '2.0' + s.watchos.deployment_target = '3.0' s.osx.deployment_target = '10.9' s.requires_arc = true - s.source_files = 'Zip/*.{swift,h}', 'Zip/minizip/*.{c,h}', 'Zip/minizip/aes/*.{c,h}' + s.source_files = 'Zip/*.{swift,h}', 'Zip/minizip/*.{c,h}', 'Zip/minizip/include/*.{c,h}' s.public_header_files = 'Zip/*.h' s.pod_target_xcconfig = {'SWIFT_INCLUDE_PATHS' => '$(SRCROOT)/Zip/Zip/minizip/**','LIBRARY_SEARCH_PATHS' => '$(SRCROOT)/Zip/Zip/'} - # s.public_header_files = 'Pod/Classes/**/*.h' s.libraries = 'z' - s.preserve_paths = 'Zip/minizip/module.modulemap' - - # s.dependency 'AFNetworking', '~> 2.3' + s.preserve_paths = 'Zip/minizip/module/module.modulemap' end diff --git a/Zip.xcodeproj/project.pbxproj b/Zip.xcodeproj/project.pbxproj index 1b153191..3f6da7db 100644 --- a/Zip.xcodeproj/project.pbxproj +++ b/Zip.xcodeproj/project.pbxproj @@ -7,6 +7,21 @@ objects = { /* Begin PBXBuildFile section */ + 0770AECE224BDC030040B364 /* Minizip.h in Headers */ = {isa = PBXBuildFile; fileRef = 0770AECD224BDC030040B364 /* Minizip.h */; }; + 0770AECF224BDC120040B364 /* Minizip.h in Headers */ = {isa = PBXBuildFile; fileRef = 0770AECD224BDC030040B364 /* Minizip.h */; }; + 0770AED0224BDC130040B364 /* Minizip.h in Headers */ = {isa = PBXBuildFile; fileRef = 0770AECD224BDC030040B364 /* Minizip.h */; }; + 07B2360221C2C697001E8634 /* crypt.h in Headers */ = {isa = PBXBuildFile; fileRef = 07B235FD21C2C674001E8634 /* crypt.h */; }; + 07B2360321C2C697001E8634 /* zip.h in Headers */ = {isa = PBXBuildFile; fileRef = 07B235FE21C2C674001E8634 /* zip.h */; }; + 07B2360421C2C697001E8634 /* unzip.h in Headers */ = {isa = PBXBuildFile; fileRef = 07B235FF21C2C674001E8634 /* unzip.h */; }; + 07B2360521C2C697001E8634 /* ioapi.h in Headers */ = {isa = PBXBuildFile; fileRef = 07B2360121C2C674001E8634 /* ioapi.h */; }; + 07B2360621C2C697001E8634 /* crypt.h in Headers */ = {isa = PBXBuildFile; fileRef = 07B235FD21C2C674001E8634 /* crypt.h */; }; + 07B2360721C2C697001E8634 /* zip.h in Headers */ = {isa = PBXBuildFile; fileRef = 07B235FE21C2C674001E8634 /* zip.h */; }; + 07B2360821C2C697001E8634 /* unzip.h in Headers */ = {isa = PBXBuildFile; fileRef = 07B235FF21C2C674001E8634 /* unzip.h */; }; + 07B2360921C2C697001E8634 /* ioapi.h in Headers */ = {isa = PBXBuildFile; fileRef = 07B2360121C2C674001E8634 /* ioapi.h */; }; + 07B2360A21C2C698001E8634 /* crypt.h in Headers */ = {isa = PBXBuildFile; fileRef = 07B235FD21C2C674001E8634 /* crypt.h */; }; + 07B2360B21C2C698001E8634 /* zip.h in Headers */ = {isa = PBXBuildFile; fileRef = 07B235FE21C2C674001E8634 /* zip.h */; }; + 07B2360C21C2C698001E8634 /* unzip.h in Headers */ = {isa = PBXBuildFile; fileRef = 07B235FF21C2C674001E8634 /* unzip.h */; }; + 07B2360D21C2C698001E8634 /* ioapi.h in Headers */ = {isa = PBXBuildFile; fileRef = 07B2360121C2C674001E8634 /* ioapi.h */; }; 303B4F6E1E4CBE5000DC1633 /* permissions.zip in Resources */ = {isa = PBXBuildFile; fileRef = 303B4F6D1E4CBE5000DC1633 /* permissions.zip */; }; 305237181E64595700CA46D1 /* unsupported_permissions.zip in Resources */ = {isa = PBXBuildFile; fileRef = 305237171E64595700CA46D1 /* unsupported_permissions.zip */; }; 342545901CE525B200336074 /* Zip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 347E3AD71C1E04C900A11FD3 /* Zip.swift */; }; @@ -16,31 +31,19 @@ 342545991CE525B200336074 /* zip.c in Sources */ = {isa = PBXBuildFile; fileRef = 342FC0ED1C5044DC0023A3C3 /* zip.c */; }; 3425459A1CE525B200336074 /* ZipUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 343625BA1C5827DC0023C4C6 /* ZipUtilities.swift */; }; 3425459E1CE525B200336074 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 3430F61F1C45C805007473A6 /* libz.tbd */; }; - 342545A11CE525B200336074 /* zip.h in Headers */ = {isa = PBXBuildFile; fileRef = 342FC0EE1C5044DC0023A3C3 /* zip.h */; }; - 342545A41CE525B200336074 /* unzip.h in Headers */ = {isa = PBXBuildFile; fileRef = 342FC0EC1C5044DC0023A3C3 /* unzip.h */; }; - 342545A71CE525B200336074 /* crypt.h in Headers */ = {isa = PBXBuildFile; fileRef = 342FC0E71C5044DC0023A3C3 /* crypt.h */; }; - 342545AB1CE525B200336074 /* ioapi.h in Headers */ = {isa = PBXBuildFile; fileRef = 342FC0E91C5044DC0023A3C3 /* ioapi.h */; }; 342545AE1CE525B200336074 /* Zip.h in Headers */ = {isa = PBXBuildFile; fileRef = 347E3A771C1DFFB500A11FD3 /* Zip.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 342FC1041C5044DC0023A3C3 /* crypt.h in Headers */ = {isa = PBXBuildFile; fileRef = 342FC0E71C5044DC0023A3C3 /* crypt.h */; }; 342FC1051C5044DC0023A3C3 /* ioapi.c in Sources */ = {isa = PBXBuildFile; fileRef = 342FC0E81C5044DC0023A3C3 /* ioapi.c */; }; - 342FC1061C5044DC0023A3C3 /* ioapi.h in Headers */ = {isa = PBXBuildFile; fileRef = 342FC0E91C5044DC0023A3C3 /* ioapi.h */; }; 342FC1071C5044DC0023A3C3 /* unzip.c in Sources */ = {isa = PBXBuildFile; fileRef = 342FC0EB1C5044DC0023A3C3 /* unzip.c */; }; - 342FC1081C5044DC0023A3C3 /* unzip.h in Headers */ = {isa = PBXBuildFile; fileRef = 342FC0EC1C5044DC0023A3C3 /* unzip.h */; }; 342FC1091C5044DC0023A3C3 /* zip.c in Sources */ = {isa = PBXBuildFile; fileRef = 342FC0ED1C5044DC0023A3C3 /* zip.c */; }; - 342FC10A1C5044DC0023A3C3 /* zip.h in Headers */ = {isa = PBXBuildFile; fileRef = 342FC0EE1C5044DC0023A3C3 /* zip.h */; }; 3430F6201C45C805007473A6 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 3430F61F1C45C805007473A6 /* libz.tbd */; }; 343625BB1C5827DC0023C4C6 /* ZipUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 343625BA1C5827DC0023C4C6 /* ZipUtilities.swift */; }; 343F51091C8DAEEC0028C434 /* Zip.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 343F50FF1C8DAEEC0028C434 /* Zip.framework */; }; 343F51161C8DAF3F0028C434 /* ZipUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 343625BA1C5827DC0023C4C6 /* ZipUtilities.swift */; }; 343F51171C8DAF410028C434 /* QuickZip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3443A3F51C4AB8A3004AD173 /* QuickZip.swift */; }; 343F51181C8DAF450028C434 /* Zip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 347E3AD71C1E04C900A11FD3 /* Zip.swift */; }; - 343F51191C8DAFD50028C434 /* crypt.h in Headers */ = {isa = PBXBuildFile; fileRef = 342FC0E71C5044DC0023A3C3 /* crypt.h */; }; 343F511A1C8DAFD90028C434 /* ioapi.c in Sources */ = {isa = PBXBuildFile; fileRef = 342FC0E81C5044DC0023A3C3 /* ioapi.c */; }; - 343F511B1C8DAFDB0028C434 /* ioapi.h in Headers */ = {isa = PBXBuildFile; fileRef = 342FC0E91C5044DC0023A3C3 /* ioapi.h */; }; 343F511C1C8DAFE00028C434 /* unzip.c in Sources */ = {isa = PBXBuildFile; fileRef = 342FC0EB1C5044DC0023A3C3 /* unzip.c */; }; - 343F511D1C8DAFE30028C434 /* unzip.h in Headers */ = {isa = PBXBuildFile; fileRef = 342FC0EC1C5044DC0023A3C3 /* unzip.h */; }; 343F511E1C8DAFE50028C434 /* zip.c in Sources */ = {isa = PBXBuildFile; fileRef = 342FC0ED1C5044DC0023A3C3 /* zip.c */; }; - 343F511F1C8DAFE80028C434 /* zip.h in Headers */ = {isa = PBXBuildFile; fileRef = 342FC0EE1C5044DC0023A3C3 /* zip.h */; }; 343F51351C8DB0480028C434 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 3430F61F1C45C805007473A6 /* libz.tbd */; }; 343F51361C8DB0720028C434 /* Zip.h in Headers */ = {isa = PBXBuildFile; fileRef = 347E3A771C1DFFB500A11FD3 /* Zip.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3443A3F61C4AB8A3004AD173 /* QuickZip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3443A3F51C4AB8A3004AD173 /* QuickZip.swift */; }; @@ -71,17 +74,18 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 0770AECD224BDC030040B364 /* Minizip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Minizip.h; sourceTree = ""; }; + 0770AED3224BDF080040B364 /* module.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; + 07B235FD21C2C674001E8634 /* crypt.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = crypt.h; sourceTree = ""; }; + 07B235FE21C2C674001E8634 /* zip.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = zip.h; sourceTree = ""; }; + 07B235FF21C2C674001E8634 /* unzip.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = unzip.h; sourceTree = ""; }; + 07B2360121C2C674001E8634 /* ioapi.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ioapi.h; sourceTree = ""; }; 303B4F6D1E4CBE5000DC1633 /* permissions.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = permissions.zip; sourceTree = ""; }; 305237171E64595700CA46D1 /* unsupported_permissions.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = unsupported_permissions.zip; sourceTree = ""; }; 342545B51CE525B200336074 /* Zip.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Zip.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 342FC0E71C5044DC0023A3C3 /* crypt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = crypt.h; sourceTree = ""; }; 342FC0E81C5044DC0023A3C3 /* ioapi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ioapi.c; sourceTree = ""; }; - 342FC0E91C5044DC0023A3C3 /* ioapi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ioapi.h; sourceTree = ""; }; - 342FC0EA1C5044DC0023A3C3 /* module.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; 342FC0EB1C5044DC0023A3C3 /* unzip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = unzip.c; sourceTree = ""; }; - 342FC0EC1C5044DC0023A3C3 /* unzip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = unzip.h; sourceTree = ""; }; 342FC0ED1C5044DC0023A3C3 /* zip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = zip.c; sourceTree = ""; }; - 342FC0EE1C5044DC0023A3C3 /* zip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = zip.h; sourceTree = ""; }; 3430F61F1C45C805007473A6 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; 343625BA1C5827DC0023C4C6 /* ZipUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZipUtilities.swift; sourceTree = ""; }; 343F50FF1C8DAEEC0028C434 /* Zip.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Zip.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -96,8 +100,8 @@ 347E3A831C1DFFB500A11FD3 /* ZipTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZipTests.swift; sourceTree = ""; }; 347E3A851C1DFFB500A11FD3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 347E3AD71C1E04C900A11FD3 /* Zip.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Zip.swift; sourceTree = ""; }; - 34940A221C58876200D41574 /* 3crBXeO.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; name = 3crBXeO.gif; path = Images/3crBXeO.gif; sourceTree = ""; }; - 34940A231C58876200D41574 /* kYkLkPf.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; name = kYkLkPf.gif; path = Images/kYkLkPf.gif; sourceTree = ""; }; + 34940A221C58876200D41574 /* 3crBXeO.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = 3crBXeO.gif; sourceTree = ""; }; + 34940A231C58876200D41574 /* kYkLkPf.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = kYkLkPf.gif; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -144,17 +148,46 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 0770AED2224BDF080040B364 /* module */ = { + isa = PBXGroup; + children = ( + 0770AED3224BDF080040B364 /* module.modulemap */, + ); + path = module; + sourceTree = ""; + }; + 07B235FC21C2C674001E8634 /* include */ = { + isa = PBXGroup; + children = ( + 0770AECD224BDC030040B364 /* Minizip.h */, + 07B235FD21C2C674001E8634 /* crypt.h */, + 07B235FE21C2C674001E8634 /* zip.h */, + 07B235FF21C2C674001E8634 /* unzip.h */, + 07B2360121C2C674001E8634 /* ioapi.h */, + ); + path = include; + sourceTree = ""; + }; + 07E4424421A5C385009BE4AB /* Resources */ = { + isa = PBXGroup; + children = ( + 305237171E64595700CA46D1 /* unsupported_permissions.zip */, + 303B4F6D1E4CBE5000DC1633 /* permissions.zip */, + 34940A221C58876200D41574 /* 3crBXeO.gif */, + 34940A231C58876200D41574 /* kYkLkPf.gif */, + 3443A3FC1C4AD199004AD173 /* bb8.zip */, + ); + path = Resources; + sourceTree = ""; + }; 342FC0D01C5044DC0023A3C3 /* minizip */ = { isa = PBXGroup; children = ( - 342FC0E71C5044DC0023A3C3 /* crypt.h */, + 0770AED2224BDF080040B364 /* module */, + 07B235FC21C2C674001E8634 /* include */, 342FC0E81C5044DC0023A3C3 /* ioapi.c */, - 342FC0E91C5044DC0023A3C3 /* ioapi.h */, - 342FC0EA1C5044DC0023A3C3 /* module.modulemap */, 342FC0EB1C5044DC0023A3C3 /* unzip.c */, - 342FC0EC1C5044DC0023A3C3 /* unzip.h */, 342FC0ED1C5044DC0023A3C3 /* zip.c */, - 342FC0EE1C5044DC0023A3C3 /* zip.h */, ); path = minizip; sourceTree = ""; @@ -198,11 +231,7 @@ 347E3A821C1DFFB500A11FD3 /* ZipTests */ = { isa = PBXGroup; children = ( - 305237171E64595700CA46D1 /* unsupported_permissions.zip */, - 303B4F6D1E4CBE5000DC1633 /* permissions.zip */, - 34940A221C58876200D41574 /* 3crBXeO.gif */, - 34940A231C58876200D41574 /* kYkLkPf.gif */, - 3443A3FC1C4AD199004AD173 /* bb8.zip */, + 07E4424421A5C385009BE4AB /* Resources */, 347E3A831C1DFFB500A11FD3 /* ZipTests.swift */, 347E3A851C1DFFB500A11FD3 /* Info.plist */, ); @@ -216,11 +245,12 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 342545A11CE525B200336074 /* zip.h in Headers */, - 342545A41CE525B200336074 /* unzip.h in Headers */, - 342545A71CE525B200336074 /* crypt.h in Headers */, - 342545AB1CE525B200336074 /* ioapi.h in Headers */, + 07B2360B21C2C698001E8634 /* zip.h in Headers */, 342545AE1CE525B200336074 /* Zip.h in Headers */, + 07B2360A21C2C698001E8634 /* crypt.h in Headers */, + 0770AED0224BDC130040B364 /* Minizip.h in Headers */, + 07B2360C21C2C698001E8634 /* unzip.h in Headers */, + 07B2360D21C2C698001E8634 /* ioapi.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -228,11 +258,12 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 07B2360721C2C697001E8634 /* zip.h in Headers */, 343F51361C8DB0720028C434 /* Zip.h in Headers */, - 343F511B1C8DAFDB0028C434 /* ioapi.h in Headers */, - 343F51191C8DAFD50028C434 /* crypt.h in Headers */, - 343F511F1C8DAFE80028C434 /* zip.h in Headers */, - 343F511D1C8DAFE30028C434 /* unzip.h in Headers */, + 07B2360621C2C697001E8634 /* crypt.h in Headers */, + 0770AECF224BDC120040B364 /* Minizip.h in Headers */, + 07B2360821C2C697001E8634 /* unzip.h in Headers */, + 07B2360921C2C697001E8634 /* ioapi.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -240,11 +271,12 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 342FC10A1C5044DC0023A3C3 /* zip.h in Headers */, - 342FC1081C5044DC0023A3C3 /* unzip.h in Headers */, - 342FC1041C5044DC0023A3C3 /* crypt.h in Headers */, - 342FC1061C5044DC0023A3C3 /* ioapi.h in Headers */, 347E3A781C1DFFB500A11FD3 /* Zip.h in Headers */, + 07B2360221C2C697001E8634 /* crypt.h in Headers */, + 07B2360521C2C697001E8634 /* ioapi.h in Headers */, + 0770AECE224BDC030040B364 /* Minizip.h in Headers */, + 07B2360421C2C697001E8634 /* unzip.h in Headers */, + 07B2360321C2C697001E8634 /* zip.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -348,7 +380,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0800; + LastUpgradeCheck = 1320; ORGANIZATIONNAME = "Roy Marmelstein"; TargetAttributes = { 343F50FE1C8DAEEC0028C434 = { @@ -359,20 +391,21 @@ }; 347E3A731C1DFFB500A11FD3 = { CreatedOnToolsVersion = 7.2; - LastSwiftMigration = 0800; + LastSwiftMigration = 1020; }; 347E3A7D1C1DFFB500A11FD3 = { CreatedOnToolsVersion = 7.2; - LastSwiftMigration = 0800; + LastSwiftMigration = 1020; }; }; }; buildConfigurationList = 347E3A6E1C1DFFB500A11FD3 /* Build configuration list for PBXProject "Zip" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, + Base, ); mainGroup = 347E3A6A1C1DFFB500A11FD3; productRefGroup = 347E3A751C1DFFB500A11FD3 /* Products */; @@ -507,9 +540,9 @@ buildSettings = { CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 8; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = "Zip/Info-tvOS.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -532,9 +565,9 @@ buildSettings = { CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 8; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = "Zip/Info-tvOS.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -557,10 +590,8 @@ buildSettings = { CODE_SIGN_IDENTITY = "-"; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 8; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 8; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_VERSION = A; INFOPLIST_FILE = Zip/Info.plist; @@ -580,10 +611,8 @@ buildSettings = { CODE_SIGN_IDENTITY = "-"; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 8; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 8; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_VERSION = A; INFOPLIST_FILE = Zip/Info.plist; @@ -634,23 +663,37 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; APPLICATION_EXTENSION_API_ONLY = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 8; + CURRENT_PROJECT_VERSION = 17; DEBUG_INFORMATION_FORMAT = dwarf; + DYLIB_CURRENT_VERSION = 17; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -671,8 +714,9 @@ MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = Xcode; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -684,23 +728,36 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; APPLICATION_EXTENSION_API_ONLY = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 8; + CURRENT_PROJECT_VERSION = 17; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DYLIB_CURRENT_VERSION = 17; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -714,7 +771,9 @@ IPHONEOS_DEPLOYMENT_TARGET = 9.2; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; - SWIFT_VERSION = 3.0; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = Xcode; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; @@ -727,13 +786,13 @@ buildSettings = { CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 8; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Zip/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = com.roymarmelstein.Zip; @@ -749,13 +808,13 @@ buildSettings = { CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 8; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Zip/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = com.roymarmelstein.Zip; diff --git a/Zip.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Zip.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/Zip.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Zip.xcodeproj/xcshareddata/xcschemes/Zip OSX.xcscheme b/Zip.xcodeproj/xcshareddata/xcschemes/Zip OSX.xcscheme index dbc19180..e6beda45 100644 --- a/Zip.xcodeproj/xcshareddata/xcschemes/Zip OSX.xcscheme +++ b/Zip.xcodeproj/xcshareddata/xcschemes/Zip OSX.xcscheme @@ -1,6 +1,6 @@ + + + + @@ -39,17 +48,6 @@ - - - - - - - - - - - - + + + + @@ -39,17 +48,6 @@ - - - - - - - - - - - - CFBundlePackageType FMWK CFBundleShortVersionString - 0.5.0 + 2.1.2 CFBundleSignature ???? CFBundleVersion - 8 + 17 NSPrincipalClass diff --git a/Zip/Info.plist b/Zip/Info.plist index 43de941f..bc21553c 100644 --- a/Zip/Info.plist +++ b/Zip/Info.plist @@ -15,11 +15,11 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.5.0 + 2.1.2 CFBundleSignature ???? CFBundleVersion - 8 + 17 NSPrincipalClass diff --git a/Zip/QuickZip.swift b/Zip/QuickZip.swift index c8f0ee77..779912a8 100644 --- a/Zip/QuickZip.swift +++ b/Zip/QuickZip.swift @@ -10,6 +10,21 @@ import Foundation extension Zip { + /** + Get search path directory. For tvOS Documents directory doesn't exist. + + - returns: Search path directory + */ + fileprivate class func searchPathDirectory() -> FileManager.SearchPathDirectory { + var searchPathDirectory: FileManager.SearchPathDirectory = .documentDirectory + + #if os(tvOS) + searchPathDirectory = .cachesDirectory + #endif + + return searchPathDirectory + } + //MARK: Quick Unzip /** @@ -44,7 +59,14 @@ extension Zip { let fileName = path.lastPathComponent let directoryName = fileName.replacingOccurrences(of: ".\(fileExtension)", with: "") - let documentsUrl = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0] as URL + + #if os(Linux) + // urls(for:in:) is not yet implemented on Linux + // See https://github.com/apple/swift-corelibs-foundation/blob/swift-4.2-branch/Foundation/FileManager.swift#L125 + let documentsUrl = fileManager.temporaryDirectory + #else + let documentsUrl = fileManager.urls(for: self.searchPathDirectory(), in: .userDomainMask)[0] + #endif do { let destinationUrl = documentsUrl.appendingPathComponent(directoryName, isDirectory: true) try self.unzipFile(path, destination: destinationUrl, overwrite: true, password: nil, progress: progress) @@ -87,7 +109,13 @@ extension Zip { */ public class func quickZipFiles(_ paths: [URL], fileName: String, progress: ((_ progress: Double) -> ())?) throws -> URL { let fileManager = FileManager.default - let documentsUrl = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0] as URL + #if os(Linux) + // urls(for:in:) is not yet implemented on Linux + // See https://github.com/apple/swift-corelibs-foundation/blob/swift-4.2-branch/Foundation/FileManager.swift#L125 + let documentsUrl = fileManager.temporaryDirectory + #else + let documentsUrl = fileManager.urls(for: self.searchPathDirectory(), in: .userDomainMask)[0] as URL + #endif let destinationUrl = documentsUrl.appendingPathComponent("\(fileName).zip") try self.zipFiles(paths: paths, zipFilePath: destinationUrl, password: nil, progress: progress) return destinationUrl diff --git a/Zip/Zip.swift b/Zip/Zip.swift index 80722dc7..4908f658 100644 --- a/Zip/Zip.swift +++ b/Zip/Zip.swift @@ -7,7 +7,7 @@ // import Foundation -import minizip +@_implementationOnly import Minizip /// Zip error type public enum ZipError: Error { @@ -48,6 +48,20 @@ public enum ZipCompression: Int { } } +/// Data in memory that will be archived as a file. +public struct ArchiveFile { + var filename:String + var data:NSData + var modifiedTime:Date? + + public init(filename:String, data:NSData, modifiedTime:Date?) { + self.filename = filename + self.data = data + self.modifiedTime = modifiedTime + } +} + + /// Zip class public class Zip { @@ -82,7 +96,7 @@ public class Zip { - notes: Supports implicit progress composition */ - public class func unzipFile(_ zipFilePath: URL, destination: URL, overwrite: Bool, password: String?, progress: ((_ progress: Double) -> ())?) throws { + public class func unzipFile(_ zipFilePath: URL, destination: URL, overwrite: Bool, password: String?, progress: ((_ progress: Double) -> ())? = nil, fileOutputHandler: ((_ unzippedFile: URL) -> Void)? = nil) throws { // File manager let fileManager = FileManager.default @@ -148,7 +162,7 @@ public class Zip { var pathString = String(cString: fileName) - guard pathString.characters.count > 0 else { + guard pathString.count > 0 else { throw ZipError.unzipFail } @@ -162,11 +176,26 @@ public class Zip { pathString = pathString.replacingOccurrences(of: "\\", with: "/") } - let fullPath = destination.appendingPathComponent(pathString).path + let fullPath = destination.appendingPathComponent(pathString).standardized.path + // .standardized removes any ".. to move a level up". + // If we then check that the fullPath starts with the destination directory we know we are not extracting "outside" te destination. + guard fullPath.starts(with: destination.standardized.path) else { + throw ZipError.unzipFail + } let creationDate = Date() - let directoryAttributes = [FileAttributeKey.creationDate.rawValue : creationDate, - FileAttributeKey.modificationDate.rawValue : creationDate] + + let directoryAttributes: [FileAttributeKey: Any]? + #if os(Linux) + // On Linux, setting attributes is not yet really implemented. + // In Swift 4.2, the only settable attribute is `.posixPermissions`. + // See https://github.com/apple/swift-corelibs-foundation/blob/swift-4.2-branch/Foundation/FileManager.swift#L182-L196 + directoryAttributes = nil + #else + directoryAttributes = [.creationDate : creationDate, + .modificationDate : creationDate] + #endif + do { if isDirectory { try fileManager.createDirectory(atPath: fullPath, withIntermediateDirectories: true, attributes: directoryAttributes) @@ -180,23 +209,32 @@ public class Zip { unzCloseCurrentFile(zip) ret = unzGoToNextFile(zip) } + + var writeBytes: UInt64 = 0 var filePointer: UnsafeMutablePointer? filePointer = fopen(fullPath, "wb") while filePointer != nil { let readBytes = unzReadCurrentFile(zip, &buffer, bufferSize) if readBytes > 0 { - fwrite(buffer, Int(readBytes), 1, filePointer) + guard fwrite(buffer, Int(readBytes), 1, filePointer) == 1 else { + throw ZipError.unzipFail + } + writeBytes += UInt64(readBytes) } else { break } } - fclose(filePointer) + if let fp = filePointer { fclose(fp) } + crc_ret = unzCloseCurrentFile(zip) if crc_ret == UNZ_CRCERROR { throw ZipError.unzipFail } + guard writeBytes == fileInfo.uncompressed_size else { + throw ZipError.unzipFail + } //Set file permissions from current fileInfo if fileInfo.external_fa != 0 { @@ -218,6 +256,12 @@ public class Zip { progressHandler((currentPosition/totalSize)) } + if let fileHandler = fileOutputHandler, + let encodedString = fullPath.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), + let fileUrl = URL(string: encodedString) { + fileHandler(fileUrl) + } + progressTracker.completedUnitCount = Int64(currentPosition) } while (ret == UNZ_OK && ret != UNZ_END_OF_LIST_OF_FILE) @@ -287,12 +331,12 @@ public class Zip { for path in processedPaths { let filePath = path.filePath() var isDirectory: ObjCBool = false - fileManager.fileExists(atPath: filePath, isDirectory: &isDirectory) + _ = fileManager.fileExists(atPath: filePath, isDirectory: &isDirectory) if !isDirectory.boolValue { - let input = fopen(filePath, "r") - if input == nil { + guard let input = fopen(filePath, "r") else { throw ZipError.zipFail } + defer { fclose(input) } let fileName = path.fileName var zipInfo: zip_fileinfo = zip_fileinfo(tmz_date: tm_zip(tm_sec: 0, tm_min: 0, tm_hour: 0, tm_mday: 0, tm_mon: 0, tm_year: 0), dosDate: 0, internal_fa: 0, external_fa: 0) do { @@ -311,7 +355,9 @@ public class Zip { } } catch {} - let buffer = malloc(chunkSize) + guard let buffer = malloc(chunkSize) else { + throw ZipError.zipFail + } if let password = password, let fileName = fileName { zipOpenNewFileInZip3(zip, fileName, &zipInfo, nil, 0, nil, 0, nil,Z_DEFLATED, compression.minizipCompression, 0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, password, 0) } @@ -327,16 +373,17 @@ public class Zip { zipWriteInFileInZip(zip, buffer, UInt32(length)) } - // Update progress handler - if let progressHandler = progress{ - progressHandler((currentPosition/totalSize)) + // Update progress handler, only if progress is not 1, because + // if we call it when progress == 1, the user will receive + // a progress handler call with value 1.0 twice. + if let progressHandler = progress, currentPosition / totalSize != 1 { + progressHandler(currentPosition/totalSize) } progressTracker.completedUnitCount = Int64(currentPosition) zipCloseFileInZip(zip) free(buffer) - fclose(input) } } zipClose(zip, nil) @@ -348,6 +395,102 @@ public class Zip { progressTracker.completedUnitCount = Int64(totalSize) } + + /** + Zip data in memory. + + - parameter archiveFiles:Array of Archive Files. + - parameter zipFilePath: Destination NSURL, should lead to a .zip filepath. + - parameter password: Password string. Optional. + - parameter compression: Compression strategy + - parameter progress: A progress closure called after unzipping each file in the archive. Double value betweem 0 and 1. + + - throws: Error if zipping fails. + + - notes: Supports implicit progress composition + */ + public class func zipData(archiveFiles:[ArchiveFile], zipFilePath:URL, password: String?, compression: ZipCompression = .DefaultCompression, progress: ((_ progress: Double) -> ())?) throws { + + let destinationPath = zipFilePath.path + + // Progress handler set up + var currentPosition: Int = 0 + var totalSize: Int = 0 + + for archiveFile in archiveFiles { + totalSize += archiveFile.data.length + } + + let progressTracker = Progress(totalUnitCount: Int64(totalSize)) + progressTracker.isCancellable = false + progressTracker.isPausable = false + progressTracker.kind = ProgressKind.file + + // Begin Zipping + let zip = zipOpen(destinationPath, APPEND_STATUS_CREATE) + + for archiveFile in archiveFiles { + + // Skip empty data + if archiveFile.data.length == 0 { + continue + } + + // Setup the zip file info + var zipInfo = zip_fileinfo(tmz_date: tm_zip(tm_sec: 0, tm_min: 0, tm_hour: 0, tm_mday: 0, tm_mon: 0, tm_year: 0), + dosDate: 0, + internal_fa: 0, + external_fa: 0) + + if let modifiedTime = archiveFile.modifiedTime { + let calendar = Calendar.current + zipInfo.tmz_date.tm_sec = UInt32(calendar.component(.second, from: modifiedTime)) + zipInfo.tmz_date.tm_min = UInt32(calendar.component(.minute, from: modifiedTime)) + zipInfo.tmz_date.tm_hour = UInt32(calendar.component(.hour, from: modifiedTime)) + zipInfo.tmz_date.tm_mday = UInt32(calendar.component(.day, from: modifiedTime)) + zipInfo.tmz_date.tm_mon = UInt32(calendar.component(.month, from: modifiedTime)) + zipInfo.tmz_date.tm_year = UInt32(calendar.component(.year, from: modifiedTime)) + } + + // Write the data as a file to zip + zipOpenNewFileInZip3(zip, + archiveFile.filename, + &zipInfo, + nil, + 0, + nil, + 0, + nil, + Z_DEFLATED, + compression.minizipCompression, + 0, + -MAX_WBITS, + DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, + password, + 0) + zipWriteInFileInZip(zip, archiveFile.data.bytes, UInt32(archiveFile.data.length)) + zipCloseFileInZip(zip) + + // Update progress handler + currentPosition += archiveFile.data.length + + if let progressHandler = progress{ + progressHandler((Double(currentPosition/totalSize))) + } + + progressTracker.completedUnitCount = Int64(currentPosition) + } + + zipClose(zip, nil) + + // Completed. Update progress handler. + if let progressHandler = progress{ + progressHandler(1.0) + } + + progressTracker.completedUnitCount = Int64(totalSize) + } /** Check if file extension is invalid. diff --git a/Zip/ZipUtilities.swift b/Zip/ZipUtilities.swift index 4759336f..0bcec54e 100644 --- a/Zip/ZipUtilities.swift +++ b/Zip/ZipUtilities.swift @@ -10,6 +10,27 @@ import Foundation internal class ZipUtilities { + /* + Include root directory. + Default is true. + + e.g. The Test directory contains two files A.txt and B.txt. + + As true: + $ zip -r Test.zip Test/ + $ unzip -l Test.zip + Test/ + Test/A.txt + Test/B.txt + + As false: + $ zip -r Test.zip Test/ + $ unzip -l Test.zip + A.txt + B.txt + */ + let includeRootDirectory = true + // File manager let fileManager = FileManager.default @@ -39,7 +60,7 @@ internal class ZipUtilities { for path in paths { let filePath = path.path var isDirectory: ObjCBool = false - fileManager.fileExists(atPath: filePath, isDirectory: &isDirectory) + _ = fileManager.fileExists(atPath: filePath, isDirectory: &isDirectory) if !isDirectory.boolValue { let processedPath = ProcessedFilePath(filePathURL: path, fileName: path.lastPathComponent) processedFilePaths.append(processedPath) @@ -54,7 +75,7 @@ internal class ZipUtilities { /** - Recursive function to expand directory contents and parse them into ProcessedFilePath structs. + Expand directory contents and parse them into ProcessedFilePath structs. - parameter directory: Path of folder as NSURL. @@ -67,12 +88,15 @@ internal class ZipUtilities { while let filePathComponent = enumerator.nextObject() as? String { let path = directory.appendingPathComponent(filePathComponent) let filePath = path.path - let directoryName = directory.lastPathComponent var isDirectory: ObjCBool = false - fileManager.fileExists(atPath: filePath, isDirectory: &isDirectory) + _ = fileManager.fileExists(atPath: filePath, isDirectory: &isDirectory) if !isDirectory.boolValue { - let fileName = (directoryName as NSString).appendingPathComponent(filePathComponent) + var fileName = filePathComponent + if includeRootDirectory { + let directoryName = directory.lastPathComponent + fileName = (directoryName as NSString).appendingPathComponent(filePathComponent) + } let processedPath = ProcessedFilePath(filePathURL: path, fileName: fileName) processedFilePaths.append(processedPath) } diff --git a/Zip/minizip/include/Minizip.h b/Zip/minizip/include/Minizip.h new file mode 100644 index 00000000..c50f6324 --- /dev/null +++ b/Zip/minizip/include/Minizip.h @@ -0,0 +1,16 @@ +// +// Minizip.h +// Zip +// +// Created by Florian Friedrich on 3/27/19. +// Copyright © 2019 Roy Marmelstein. All rights reserved. +// + +#ifndef Minizip_h +#define Minizip_h + +#import "crypt.h" +#import "unzip.h" +#import "zip.h" + +#endif /* Minizip_h */ diff --git a/Zip/minizip/crypt.h b/Zip/minizip/include/crypt.h similarity index 100% rename from Zip/minizip/crypt.h rename to Zip/minizip/include/crypt.h diff --git a/Zip/minizip/ioapi.h b/Zip/minizip/include/ioapi.h similarity index 99% rename from Zip/minizip/ioapi.h rename to Zip/minizip/include/ioapi.h index f0edcaca..79dc5ef1 100644 --- a/Zip/minizip/ioapi.h +++ b/Zip/minizip/include/ioapi.h @@ -31,7 +31,10 @@ #include #include -#include "zlib.h" + +#ifndef _ZLIB_H +#include +#endif #if defined(USE_FILE32API) # define fopen64 fopen diff --git a/Zip/minizip/unzip.h b/Zip/minizip/include/unzip.h similarity index 98% rename from Zip/minizip/unzip.h rename to Zip/minizip/include/unzip.h index 217c2029..5645215d 100644 --- a/Zip/minizip/unzip.h +++ b/Zip/minizip/include/unzip.h @@ -22,7 +22,7 @@ extern "C" { #endif #ifndef _ZLIB_H -#include "zlib.h" +#include #endif #ifndef _ZLIBIOAPI_H @@ -133,13 +133,13 @@ extern unzFile ZEXPORT unzOpen OF((const char *path)); extern unzFile ZEXPORT unzOpen64 OF((const void *path)); /* Open a Zip file. - path should contain the full pathname (by example, on a Windows XP computer - "c:\\zlib\\zlib113.zip" or on an Unix computer "zlib/zlib113.zip". + path should contain the full pathname (by example, on a Windows XP computer + "c:\\zlib\\zlib113.zip" or on an Unix computer "zlib/zlib113.zip". return NULL if zipfile cannot be opened or doesn't exist return unzFile handle if no error NOTE: The "64" function take a const void* pointer, because the path is just the value passed to the - open64_file_func callback. Under Windows, if UNICODE is defined, using fill_fopen64_filefunc, the path + open64_file_func callback. Under Windows, if UNICODE is defined, using fill_fopen64_filefunc, the path is a pointer to a wide unicode string (LPCTSTR is LPCWSTR), so const char* does not describe the reality */ extern unzFile ZEXPORT unzOpen2 OF((const char *path, zlib_filefunc_def* pzlib_filefunc_def)); @@ -199,17 +199,17 @@ extern int ZEXPORT unzReadCurrentFile OF((unzFile file, voidp buf, unsigned len) return 0 if the end of file was reached return <0 with error code if there is an error (UNZ_ERRNO for IO error, or zLib error for uncompress error) */ -extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, unz_file_info *pfile_info, char *filename, +extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, unz_file_info *pfile_info, char *filename, uLong filename_size, void *extrafield, uLong extrafield_size, char *comment, uLong comment_size)); extern int ZEXPORT unzGetCurrentFileInfo64 OF((unzFile file, unz_file_info64 *pfile_info, char *filename, uLong filename_size, void *extrafield, uLong extrafield_size, char *comment, uLong comment_size)); /* Get Info about the current file pfile_info if != NULL, the *pfile_info structure will contain somes info about the current file - filename if != NULL, the file name string will be copied in filename + filename if != NULL, the file name string will be copied in filename filename_size is the size of the filename buffer extrafield if != NULL, the extra field information from the central header will be copied in to - extrafield_size is the size of the extraField buffer + extrafield_size is the size of the extraField buffer comment if != NULL, the comment string of the file will be copied in to comment_size is the size of the comment buffer */ @@ -245,7 +245,7 @@ extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); extern int ZEXPORT unzGoToFirstFile2 OF((unzFile file, unz_file_info64 *pfile_info, char *filename, uLong filename_size, void *extrafield, uLong extrafield_size, char *comment, uLong comment_size)); -/* Set the current file of the zipfile to the first file and retrieves the current info on success. +/* Set the current file of the zipfile to the first file and retrieves the current info on success. Not as seek intensive as unzGoToFirstFile + unzGetCurrentFileInfo. return UNZ_OK if no error */ @@ -258,7 +258,7 @@ extern int ZEXPORT unzGoToNextFile OF((unzFile file)); extern int ZEXPORT unzGoToNextFile2 OF((unzFile file, unz_file_info64 *pfile_info, char *filename, uLong filename_size, void *extrafield, uLong extrafield_size, char *comment, uLong comment_size)); -/* Set the current file of the zipfile to the next file and retrieves the current +/* Set the current file of the zipfile to the next file and retrieves the current info on success. Does less seeking around than unzGotoNextFile + unzGetCurrentFileInfo. return UNZ_OK if no error diff --git a/Zip/minizip/zip.h b/Zip/minizip/include/zip.h similarity index 92% rename from Zip/minizip/zip.h rename to Zip/minizip/include/zip.h index 60208a35..5eb963b8 100644 --- a/Zip/minizip/zip.h +++ b/Zip/minizip/include/zip.h @@ -20,7 +20,7 @@ extern "C" { #endif #ifndef _ZLIB_H -# include "zlib.h" +# include #endif #ifndef _ZLIBIOAPI_H @@ -88,36 +88,36 @@ extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append)); extern zipFile ZEXPORT zipOpen64 OF((const void *pathname, int append)); /* Create a zipfile. - pathname should contain the full pathname (by example, on a Windows XP computer - "c:\\zlib\\zlib113.zip" or on an Unix computer "zlib/zlib113.zip". + pathname should contain the full pathname (by example, on a Windows XP computer + "c:\\zlib\\zlib113.zip" or on an Unix computer "zlib/zlib113.zip". return NULL if zipfile cannot be opened return zipFile handle if no error If the file pathname exist and append == APPEND_STATUS_CREATEAFTER, the zip will be created at the end of the file. (useful if the file contain a self extractor code) - If the file pathname exist and append == APPEND_STATUS_ADDINZIP, we will add files in existing + If the file pathname exist and append == APPEND_STATUS_ADDINZIP, we will add files in existing zip (be sure you don't add file that doesn't exist) - NOTE: There is no delete function into a zipfile. If you want delete file into a zipfile, + NOTE: There is no delete function into a zipfile. If you want delete file into a zipfile, you must open a zipfile, and create another. Of course, you can use RAW reading and writing to copy the file you did not want delete. */ -extern zipFile ZEXPORT zipOpen2 OF((const char *pathname, int append, const char ** globalcomment, +extern zipFile ZEXPORT zipOpen2 OF((const char *pathname, int append, const char ** globalcomment, zlib_filefunc_def* pzlib_filefunc_def)); -extern zipFile ZEXPORT zipOpen2_64 OF((const void *pathname, int append, const char ** globalcomment, +extern zipFile ZEXPORT zipOpen2_64 OF((const void *pathname, int append, const char ** globalcomment, zlib_filefunc64_def* pzlib_filefunc_def)); -extern zipFile ZEXPORT zipOpen3 OF((const char *pathname, int append, ZPOS64_T disk_size, +extern zipFile ZEXPORT zipOpen3 OF((const char *pathname, int append, ZPOS64_T disk_size, const char ** globalcomment, zlib_filefunc_def* pzlib_filefunc_def)); /* Same as zipOpen2 but allows specification of spanned zip size */ -extern zipFile ZEXPORT zipOpen3_64 OF((const void *pathname, int append, ZPOS64_T disk_size, +extern zipFile ZEXPORT zipOpen3_64 OF((const void *pathname, int append, ZPOS64_T disk_size, const char ** globalcomment, zlib_filefunc64_def* pzlib_filefunc_def)); extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, - const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, + const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level)); /* Open a file in the ZIP for writing. @@ -134,23 +134,23 @@ extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file, const char* filename, c this MUST be '1' if the uncompressed size is >= 0xffffffff. */ extern int ZEXPORT zipOpenNewFileInZip64 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, - const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, + const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int zip64)); /* Same as zipOpenNewFileInZip with zip64 support */ extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, - const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, + const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw)); /* Same as zipOpenNewFileInZip, except if raw=1, we write raw file */ extern int ZEXPORT zipOpenNewFileInZip2_64 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, - const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, + const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int zip64)); /* Same as zipOpenNewFileInZip3 with zip64 support */ extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, - const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, - uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits, int memLevel, + const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, + uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits, int memLevel, int strategy, const char* password, uLong crcForCrypting)); /* Same as zipOpenNewFileInZip2, except windowBits, memLevel, strategy : see parameter strategy in deflateInit2 @@ -158,20 +158,20 @@ extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, const char* filename, crcForCrypting : crc of file to compress (needed for crypting) */ extern int ZEXPORT zipOpenNewFileInZip3_64 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, - const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, - uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits, int memLevel, + const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, + uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits, int memLevel, int strategy, const char* password, uLong crcForCrypting, int zip64)); /* Same as zipOpenNewFileInZip3 with zip64 support */ extern int ZEXPORT zipOpenNewFileInZip4 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, - const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, - uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits, int memLevel, + const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, + uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits, int memLevel, int strategy, const char* password, uLong crcForCrypting, uLong versionMadeBy, uLong flagBase)); /* Same as zipOpenNewFileInZip3 except versionMadeBy & flag fields */ extern int ZEXPORT zipOpenNewFileInZip4_64 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, - const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, - uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits, int memLevel, + const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, + uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits, int memLevel, int strategy, const char* password, uLong crcForCrypting, uLong versionMadeBy, uLong flagBase, int zip64)); /* Same as zipOpenNewFileInZip4 with zip64 support */ diff --git a/Zip/minizip/module.modulemap b/Zip/minizip/module.modulemap deleted file mode 100644 index 45a6e582..00000000 --- a/Zip/minizip/module.modulemap +++ /dev/null @@ -1,5 +0,0 @@ -module minizip [system][extern_c] { - header "unzip.h" - header "zip.h" - export * -} diff --git a/Zip/minizip/module/module.modulemap b/Zip/minizip/module/module.modulemap new file mode 100644 index 00000000..59eaacd7 --- /dev/null +++ b/Zip/minizip/module/module.modulemap @@ -0,0 +1,5 @@ +module Minizip [system][extern_c] { + header "../include/Minizip.h" + link "z" + export * +} diff --git a/Zip/minizip/unzip.c b/Zip/minizip/unzip.c index a3b98346..3a257407 100644 --- a/Zip/minizip/unzip.c +++ b/Zip/minizip/unzip.c @@ -24,7 +24,7 @@ # define NOUNCRYPT #endif*/ -#include "zlib.h" +#include #include "unzip.h" #ifdef STDC @@ -635,7 +635,7 @@ local int unzGoToNextDisk(unzFile file) } else { - s->filestream = ZOPENDISK64(s->z_filefunc, s->filestream_with_CD, number_disk_next, + s->filestream = ZOPENDISK64(s->z_filefunc, s->filestream_with_CD, (int)number_disk_next, ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_EXISTING); } @@ -982,7 +982,7 @@ extern int ZEXPORT unzGetCurrentFileInfo(unzFile file, unz_file_info * pfile_inf pfile_info->internal_fa = file_info64.internal_fa; pfile_info->external_fa = file_info64.external_fa; - pfile_info->tmu_date = file_info64.tmu_date, + pfile_info->tmu_date = file_info64.tmu_date; pfile_info->compressed_size = (uLong)file_info64.compressed_size; pfile_info->uncompressed_size = (uLong)file_info64.uncompressed_size; @@ -1455,19 +1455,19 @@ extern int ZEXPORT unzReadCurrentFile(unzFile file, voidp buf, unsigned len) s->pfile_in_zip_read->bstream.avail_in = s->pfile_in_zip_read->stream.avail_in; s->pfile_in_zip_read->bstream.total_in_lo32 = (uInt)s->pfile_in_zip_read->stream.total_in; s->pfile_in_zip_read->bstream.total_in_hi32 = s->pfile_in_zip_read->stream.total_in >> 32; - + s->pfile_in_zip_read->bstream.next_out = (char*)s->pfile_in_zip_read->stream.next_out; s->pfile_in_zip_read->bstream.avail_out = s->pfile_in_zip_read->stream.avail_out; s->pfile_in_zip_read->bstream.total_out_lo32 = (uInt)s->pfile_in_zip_read->stream.total_out; s->pfile_in_zip_read->bstream.total_out_hi32 = s->pfile_in_zip_read->stream.total_out >> 32; - total_out_before = s->pfile_in_zip_read->bstream.total_out_lo32 + + total_out_before = s->pfile_in_zip_read->bstream.total_out_lo32 + (((uLong)s->pfile_in_zip_read->bstream.total_out_hi32) << 32); buf_before = (const Bytef *)s->pfile_in_zip_read->bstream.next_out; err = BZ2_bzDecompress(&s->pfile_in_zip_read->bstream); - total_out_after = s->pfile_in_zip_read->bstream.total_out_lo32 + + total_out_after = s->pfile_in_zip_read->bstream.total_out_lo32 + (((uLong)s->pfile_in_zip_read->bstream.total_out_hi32) << 32); out_bytes = total_out_after-total_out_before; diff --git a/Zip/minizip/zip.c b/Zip/minizip/zip.c index 0dc5dea1..21dadb23 100755 --- a/Zip/minizip/zip.c +++ b/Zip/minizip/zip.c @@ -18,7 +18,7 @@ #include #include #include -#include "zlib.h" +#include #include "zip.h" #ifdef STDC @@ -183,7 +183,7 @@ typedef struct } zip64_internal; /* Allocate a new data block */ -local linkedlist_datablock_internal* allocate_new_datablock OF(()); +local linkedlist_datablock_internal* allocate_new_datablock OF((void)); local linkedlist_datablock_internal* allocate_new_datablock() { linkedlist_datablock_internal* ldi; diff --git a/Zip/zlib/module.modulemap b/Zip/zlib/module.modulemap new file mode 100644 index 00000000..e69de29b diff --git a/ZipTests/Info.plist b/ZipTests/Info.plist index 900e133d..db36cdf0 100644 --- a/ZipTests/Info.plist +++ b/ZipTests/Info.plist @@ -15,10 +15,10 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 0.5.0 + 2.1.2 CFBundleSignature ???? CFBundleVersion - 8 + 17 diff --git a/ZipTests/Images/3crBXeO.gif b/ZipTests/Resources/3crBXeO.gif similarity index 100% rename from ZipTests/Images/3crBXeO.gif rename to ZipTests/Resources/3crBXeO.gif diff --git a/ZipTests/bb8.zip b/ZipTests/Resources/bb8.zip similarity index 100% rename from ZipTests/bb8.zip rename to ZipTests/Resources/bb8.zip diff --git a/ZipTests/Images/kYkLkPf.gif b/ZipTests/Resources/kYkLkPf.gif similarity index 100% rename from ZipTests/Images/kYkLkPf.gif rename to ZipTests/Resources/kYkLkPf.gif diff --git a/ZipTests/Resources/pathTraversal.zip b/ZipTests/Resources/pathTraversal.zip new file mode 100644 index 00000000..55cafa97 Binary files /dev/null and b/ZipTests/Resources/pathTraversal.zip differ diff --git a/ZipTests/permissions.zip b/ZipTests/Resources/permissions.zip similarity index 100% rename from ZipTests/permissions.zip rename to ZipTests/Resources/permissions.zip diff --git a/ZipTests/unsupported_permissions.zip b/ZipTests/Resources/unsupported_permissions.zip similarity index 100% rename from ZipTests/unsupported_permissions.zip rename to ZipTests/Resources/unsupported_permissions.zip diff --git a/ZipTests/XCTestManifests.swift b/ZipTests/XCTestManifests.swift new file mode 100644 index 00000000..b90685d6 --- /dev/null +++ b/ZipTests/XCTestManifests.swift @@ -0,0 +1,39 @@ +#if !canImport(ObjectiveC) +import XCTest + +extension ZipTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__ZipTests = [ + ("testAddedCustomFileExtensionIsValid", testAddedCustomFileExtensionIsValid), + ("testDefaultFileExtensionsIsNotRemoved", testDefaultFileExtensionsIsNotRemoved), + ("testDefaultFileExtensionsIsValid", testDefaultFileExtensionsIsValid), + ("testFileExtensionIsInvalidForInvalidUrl", testFileExtensionIsInvalidForInvalidUrl), + ("testFileExtensionIsNotInvalidForValidUrl", testFileExtensionIsNotInvalidForValidUrl), + ("testImplicitProgressUnzip", testImplicitProgressUnzip), + ("testImplicitProgressZip", testImplicitProgressZip), + ("testQuickUnzip", testQuickUnzip), + ("testQuickUnzipNonExistingPath", testQuickUnzipNonExistingPath), + ("testQuickUnzipNonZipPath", testQuickUnzipNonZipPath), + ("testQuickUnzipOnlineURL", testQuickUnzipOnlineURL), + ("testQuickUnzipProgress", testQuickUnzipProgress), + ("testQuickUnzipSubDir", testQuickUnzipSubDir), + ("testQuickZip", testQuickZip), + ("testQuickZipFolder", testQuickZipFolder), + ("testRemovedCustomFileExtensionIsInvalid", testRemovedCustomFileExtensionIsInvalid), + ("testUnzip", testUnzip), + ("testUnzipPermissions", testUnzipPermissions), + ("testUnzipProtectsAgainstPathTraversal", testUnzipProtectsAgainstPathTraversal), + ("testUnzipWithUnsupportedPermissions", testUnzipWithUnsupportedPermissions), + ("testZip", testZip), + ("testZipUnzipPassword", testZipUnzipPassword), + ] +} + +public func __allTests() -> [XCTestCaseEntry] { + return [ + testCase(ZipTests.__allTests__ZipTests), + ] +} +#endif diff --git a/ZipTests/ZipTests.swift b/ZipTests/ZipTests.swift index 282a3e36..957ef51e 100644 --- a/ZipTests/ZipTests.swift +++ b/ZipTests/ZipTests.swift @@ -10,279 +10,251 @@ import XCTest @testable import Zip class ZipTests: XCTestCase { - + + #if os(Linux) + private let tearDownBlocksQueue = DispatchQueue(label: "XCTest.XCTestCase.tearDownBlocks.lock") + private var tearDownBlocks: [() -> Void] = [] + func addTeardownBlock(_ block: @escaping () -> Void) { + tearDownBlocksQueue.sync { tearDownBlocks.append(block) } + } + #endif + override func setUp() { super.setUp() } override func tearDown() { super.tearDown() + #if os(Linux) + var blocks = tearDownBlocksQueue.sync { tearDownBlocks } + while let next = blocks.popLast() { next() } + #endif } - - func testQuickUnzip() { - do { - let filePath = Bundle(for: ZipTests.self).url(forResource: "bb8", withExtension: "zip")! - let destinationURL = try Zip.quickUnzipFile(filePath) - let fileManager = FileManager.default - XCTAssertTrue(fileManager.fileExists(atPath: destinationURL.path)) + + private func url(forResource resource: String, withExtension ext: String? = nil) -> URL? { + #if Xcode + return Bundle(for: ZipTests.self).url(forResource: resource, withExtension: ext) + #else + let testDirPath = URL(fileURLWithPath: String(#file)).deletingLastPathComponent() + let resourcePath = testDirPath.appendingPathComponent("Resources").appendingPathComponent(resource) + return ext.map { resourcePath.appendingPathExtension($0) } ?? resourcePath + #endif + } + + private func temporaryDirectory() -> URL { + if #available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) { + return FileManager.default.temporaryDirectory + } else { + return URL(fileURLWithPath: NSTemporaryDirectory()) + } + } + + private func autoRemovingSandbox() throws -> URL { + let sandbox = temporaryDirectory().appendingPathComponent("ZipTests_" + UUID().uuidString, isDirectory: true) + // We can always create it. UUID should be unique. + try FileManager.default.createDirectory(at: sandbox, withIntermediateDirectories: true, attributes: nil) + // Schedule the teardown block _after_ creating the directory has been created (so that if it fails, no teardown block is registered). + addTeardownBlock { + do { + try FileManager.default.removeItem(at: sandbox) + } catch { + print("Could not remove test sandbox at '\(sandbox.path)': \(error)") + } } - catch { - XCTFail() + return sandbox + } + + func testQuickUnzip() throws { + let filePath = url(forResource: "bb8", withExtension: "zip")! + let destinationURL = try Zip.quickUnzipFile(filePath) + addTeardownBlock { + try? FileManager.default.removeItem(at: destinationURL) } + XCTAssertTrue(FileManager.default.fileExists(atPath: destinationURL.path)) } func testQuickUnzipNonExistingPath() { - do { - let filePathURL = Bundle(for: ZipTests.self).resourcePath - let filePath = NSURL(string:"\(filePathURL!)/bb9.zip") - let destinationURL = try Zip.quickUnzipFile(filePath! as URL) - let fileManager = FileManager.default - XCTAssertFalse(fileManager.fileExists(atPath:destinationURL.path)) - } - catch { - XCTAssert(true) - } + let filePath = URL(fileURLWithPath: "/some/path/to/nowhere/bb9.zip") + XCTAssertThrowsError(try Zip.quickUnzipFile(filePath)) } func testQuickUnzipNonZipPath() { - do { - let filePath = Bundle(for: ZipTests.self).url(forResource: "3crBXeO", withExtension: "gif")! - let destinationURL = try Zip.quickUnzipFile(filePath) - let fileManager = FileManager.default - XCTAssertFalse(fileManager.fileExists(atPath:destinationURL.path)) - } - catch { - XCTAssert(true) - } + let filePath = url(forResource: "3crBXeO", withExtension: "gif")! + XCTAssertThrowsError(try Zip.quickUnzipFile(filePath)) } - func testQuickUnzipProgress() { - do { - let filePath = Bundle(for: ZipTests.self).url(forResource: "bb8", withExtension: "zip")! - _ = try Zip.quickUnzipFile(filePath, progress: { (progress) -> () in - XCTAssert(true) - }) - } - catch { - XCTFail() + func testQuickUnzipProgress() throws { + let filePath = url(forResource: "bb8", withExtension: "zip")! + let destinationURL = try Zip.quickUnzipFile(filePath, progress: { progress in + XCTAssertFalse(progress.isNaN) + }) + addTeardownBlock { + try? FileManager.default.removeItem(at: destinationURL) } } func testQuickUnzipOnlineURL() { - do { - let filePath = NSURL(string: "http://www.google.com/google.zip")! - let destinationURL = try Zip.quickUnzipFile(filePath as URL) - let fileManager = FileManager.default - XCTAssertFalse(fileManager.fileExists(atPath:destinationURL.path)) - } - catch { - XCTAssert(true) - } + let filePath = URL(string: "http://www.google.com/google.zip")! + XCTAssertThrowsError(try Zip.quickUnzipFile(filePath)) } - func testUnzip() { - do { - let filePath = Bundle(for: ZipTests.self).url(forResource: "bb8", withExtension: "zip")! - let documentsFolder = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as NSURL - - try Zip.unzipFile(filePath, destination: documentsFolder as URL, overwrite: true, password: "password", progress: { (progress) -> () in - print(progress) - }) - - let fileManager = FileManager.default - XCTAssertTrue(fileManager.fileExists(atPath:documentsFolder.path!)) - } - catch { - XCTFail() - } + func testUnzip() throws { + let filePath = url(forResource: "bb8", withExtension: "zip")! + let destinationPath = try autoRemovingSandbox() + + try Zip.unzipFile(filePath, destination: destinationPath, overwrite: true, password: "password", progress: nil) + + XCTAssertTrue(FileManager.default.fileExists(atPath: destinationPath.path)) } - func testImplicitProgressUnzip() { - do { - let progress = Progress() - progress.totalUnitCount = 1 - - let filePath = Bundle(for: ZipTests.self).url(forResource: "bb8", withExtension: "zip")! - let documentsFolder = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as NSURL - - progress.becomeCurrent(withPendingUnitCount: 1) - try Zip.unzipFile(filePath, destination: documentsFolder as URL, overwrite: true, password: "password", progress: nil) - progress.resignCurrent() - - XCTAssertTrue(progress.totalUnitCount == progress.completedUnitCount) - } - catch { - XCTFail() - } - + func testImplicitProgressUnzip() throws { + let progress = Progress(totalUnitCount: 1) + + let filePath = url(forResource: "bb8", withExtension: "zip")! + let destinationPath = try autoRemovingSandbox() + + progress.becomeCurrent(withPendingUnitCount: 1) + try Zip.unzipFile(filePath, destination: destinationPath, overwrite: true, password: "password", progress: nil) + progress.resignCurrent() + + XCTAssertTrue(progress.totalUnitCount == progress.completedUnitCount) } - func testImplicitProgressZip() { - do { - let progress = Progress() - progress.totalUnitCount = 1 - - let imageURL1 = Bundle(for: ZipTests.self).url(forResource: "3crBXeO", withExtension: "gif")! - let imageURL2 = Bundle(for: ZipTests.self).url(forResource: "kYkLkPf", withExtension: "gif")! - let documentsFolder = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as NSURL - let zipFilePath = documentsFolder.appendingPathComponent("archive.zip") - - progress.becomeCurrent(withPendingUnitCount: 1) - try Zip.zipFiles(paths: [imageURL1, imageURL2], zipFilePath: zipFilePath!, password: nil, progress: nil) - progress.resignCurrent() - - XCTAssertTrue(progress.totalUnitCount == progress.completedUnitCount) - } - catch { - XCTFail() - } + func testImplicitProgressZip() throws { + let progress = Progress(totalUnitCount: 1) + + let imageURL1 = url(forResource: "3crBXeO", withExtension: "gif")! + let imageURL2 = url(forResource: "kYkLkPf", withExtension: "gif")! + let sandboxFolder = try autoRemovingSandbox() + let zipFilePath = sandboxFolder.appendingPathComponent("archive.zip") + + progress.becomeCurrent(withPendingUnitCount: 1) + try Zip.zipFiles(paths: [imageURL1, imageURL2], zipFilePath: zipFilePath, password: nil, progress: nil) + progress.resignCurrent() + + XCTAssertTrue(progress.totalUnitCount == progress.completedUnitCount) } - - func testQuickZip() { - do { - let imageURL1 = Bundle(for: ZipTests.self).url(forResource: "3crBXeO", withExtension: "gif")! - let imageURL2 = Bundle(for: ZipTests.self).url(forResource: "kYkLkPf", withExtension: "gif")! - let destinationURL = try Zip.quickZipFiles([imageURL1, imageURL2], fileName: "archive") - let fileManager = FileManager.default - XCTAssertTrue(fileManager.fileExists(atPath:destinationURL.path)) - } - catch { - XCTFail() + func testQuickZip() throws { + let imageURL1 = url(forResource: "3crBXeO", withExtension: "gif")! + let imageURL2 = url(forResource: "kYkLkPf", withExtension: "gif")! + let destinationURL = try Zip.quickZipFiles([imageURL1, imageURL2], fileName: "archive") + XCTAssertTrue(FileManager.default.fileExists(atPath:destinationURL.path)) + addTeardownBlock { + try? FileManager.default.removeItem(at: destinationURL) } } - func testQuickZipFolder() { - do { - let fileManager = FileManager.default - let imageURL1 = Bundle(for: ZipTests.self).url(forResource: "3crBXeO", withExtension: "gif")! - let imageURL2 = Bundle(for: ZipTests.self).url(forResource: "kYkLkPf", withExtension: "gif")! - let folderURL = Bundle(for: ZipTests.self).bundleURL.appendingPathComponent("Directory") - let targetImageURL1 = folderURL.appendingPathComponent("3crBXeO.gif") - let targetImageURL2 = folderURL.appendingPathComponent("kYkLkPf.gif") - if fileManager.fileExists(atPath:folderURL.path) { - try fileManager.removeItem(at: folderURL) - } - try fileManager.createDirectory(at: folderURL, withIntermediateDirectories: false, attributes: nil) - try fileManager.copyItem(at: imageURL1, to: targetImageURL1) - try fileManager.copyItem(at: imageURL2, to: targetImageURL2) - let destinationURL = try Zip.quickZipFiles([folderURL], fileName: "directory") - XCTAssertTrue(fileManager.fileExists(atPath:destinationURL.path)) - } - catch { - XCTFail() + func testQuickZipFolder() throws { + let fileManager = FileManager.default + let imageURL1 = url(forResource: "3crBXeO", withExtension: "gif")! + let imageURL2 = url(forResource: "kYkLkPf", withExtension: "gif")! + let folderURL = try autoRemovingSandbox() + let targetImageURL1 = folderURL.appendingPathComponent("3crBXeO.gif") + let targetImageURL2 = folderURL.appendingPathComponent("kYkLkPf.gif") + try fileManager.copyItem(at: imageURL1, to: targetImageURL1) + try fileManager.copyItem(at: imageURL2, to: targetImageURL2) + let destinationURL = try Zip.quickZipFiles([folderURL], fileName: "directory") + XCTAssertTrue(fileManager.fileExists(atPath: destinationURL.path)) + addTeardownBlock { + try? FileManager.default.removeItem(at: destinationURL) } } - - - func testZip() { - do { - let imageURL1 = Bundle(for: ZipTests.self).url(forResource: "3crBXeO", withExtension: "gif")! - let imageURL2 = Bundle(for: ZipTests.self).url(forResource: "kYkLkPf", withExtension: "gif")! - let documentsFolder = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as NSURL - let zipFilePath = documentsFolder.appendingPathComponent("archive.zip") - try Zip.zipFiles(paths: [imageURL1, imageURL2], zipFilePath: zipFilePath!, password: nil, progress: { (progress) -> () in - print(progress) - }) - let fileManager = FileManager.default - XCTAssertTrue(fileManager.fileExists(atPath:(zipFilePath?.path)!)) - } - catch { - XCTFail() - } + + func testZip() throws { + let imageURL1 = url(forResource: "3crBXeO", withExtension: "gif")! + let imageURL2 = url(forResource: "kYkLkPf", withExtension: "gif")! + let sandboxFolder = try autoRemovingSandbox() + let zipFilePath = sandboxFolder.appendingPathComponent("archive.zip") + try Zip.zipFiles(paths: [imageURL1, imageURL2], zipFilePath: zipFilePath, password: nil, progress: nil) + XCTAssertTrue(FileManager.default.fileExists(atPath: zipFilePath.path)) } - func testZipUnzipPassword() { - do { - let imageURL1 = Bundle(for: ZipTests.self).url(forResource: "3crBXeO", withExtension: "gif")! - let imageURL2 = Bundle(for: ZipTests.self).url(forResource: "kYkLkPf", withExtension: "gif")! - let documentsFolder = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as NSURL - let zipFilePath = documentsFolder.appendingPathComponent("archive.zip") - try Zip.zipFiles(paths: [imageURL1, imageURL2], zipFilePath: zipFilePath!, password: "password", progress: { (progress) -> () in - print(progress) - }) - let fileManager = FileManager.default - XCTAssertTrue(fileManager.fileExists(atPath:(zipFilePath?.path)!)) - guard let fileExtension = zipFilePath?.pathExtension, let fileName = zipFilePath?.lastPathComponent else { - throw ZipError.unzipFail - } - let directoryName = fileName.replacingOccurrences(of: ".\(fileExtension)", with: "") - let documentsUrl = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0] as NSURL - let destinationUrl = documentsUrl.appendingPathComponent(directoryName, isDirectory: true) - try Zip.unzipFile(zipFilePath!, destination: destinationUrl!, overwrite: true, password: "password", progress: nil) - XCTAssertTrue(fileManager.fileExists(atPath:(destinationUrl?.path)!)) - } - catch { - XCTFail() - } + func testZipUnzipPassword() throws { + let imageURL1 = url(forResource: "3crBXeO", withExtension: "gif")! + let imageURL2 = url(forResource: "kYkLkPf", withExtension: "gif")! + let zipFilePath = try autoRemovingSandbox().appendingPathComponent("archive.zip") + try Zip.zipFiles(paths: [imageURL1, imageURL2], zipFilePath: zipFilePath, password: "password", progress: nil) + let fileManager = FileManager.default + XCTAssertTrue(fileManager.fileExists(atPath: zipFilePath.path)) + let directoryName = zipFilePath.lastPathComponent.replacingOccurrences(of: ".\(zipFilePath.pathExtension)", with: "") + let destinationUrl = try autoRemovingSandbox().appendingPathComponent(directoryName, isDirectory: true) + try Zip.unzipFile(zipFilePath, destination: destinationUrl, overwrite: true, password: "password", progress: nil) + XCTAssertTrue(fileManager.fileExists(atPath: destinationUrl.path)) } - func testUnzipWithUnsupportedPermissions() { - do { - let permissionsURL = Bundle(for: ZipTests.self).url(forResource: "unsupported_permissions", withExtension: "zip")! - let unzipDestination = try Zip.quickUnzipFile(permissionsURL) - print(unzipDestination) - let fileManager = FileManager.default - let permission644 = unzipDestination.appendingPathComponent("unsupported_permission").appendingPathExtension("txt") - do { - let attributes644 = try fileManager.attributesOfItem(atPath: permission644.path) - XCTAssertEqual(attributes644[.posixPermissions] as? Int, 0o644) - } catch { - XCTFail("Failed to get file attributes \(error)") - } - } catch { - XCTFail("Failed extract unsupported_permissions.zip") + func testUnzipWithUnsupportedPermissions() throws { + let permissionsURL = url(forResource: "unsupported_permissions", withExtension: "zip")! + let unzipDestination = try Zip.quickUnzipFile(permissionsURL) + let permission644 = unzipDestination.appendingPathComponent("unsupported_permission").appendingPathExtension("txt") + let foundPermissions = try FileManager.default.attributesOfItem(atPath: permission644.path)[.posixPermissions] as? Int + #if os(Linux) + let expectedPermissions = 0o664 + #else + let expectedPermissions = 0o644 + #endif + XCTAssertNotNil(foundPermissions) + XCTAssertEqual(foundPermissions, expectedPermissions, + "\(foundPermissions.map { String($0, radix: 8) } ?? "nil") is not equal to \(String(expectedPermissions, radix: 8))") + } + + func testUnzipPermissions() throws { + let permissionsURL = url(forResource: "permissions", withExtension: "zip")! + let unzipDestination = try Zip.quickUnzipFile(permissionsURL) + addTeardownBlock { + try? FileManager.default.removeItem(at: unzipDestination) } + let fileManager = FileManager.default + let permission777 = unzipDestination.appendingPathComponent("permission_777").appendingPathExtension("txt") + let permission600 = unzipDestination.appendingPathComponent("permission_600").appendingPathExtension("txt") + let permission604 = unzipDestination.appendingPathComponent("permission_604").appendingPathExtension("txt") + + let attributes777 = try fileManager.attributesOfItem(atPath: permission777.path) + let attributes600 = try fileManager.attributesOfItem(atPath: permission600.path) + let attributes604 = try fileManager.attributesOfItem(atPath: permission604.path) + XCTAssertEqual(attributes777[.posixPermissions] as? Int, 0o777) + XCTAssertEqual(attributes600[.posixPermissions] as? Int, 0o600) + XCTAssertEqual(attributes604[.posixPermissions] as? Int, 0o604) } - func testUnzipPermissions() { + // Tests if https://github.com/marmelroy/Zip/issues/245 does not uccor anymore. + func testUnzipProtectsAgainstPathTraversal() throws { + let filePath = url(forResource: "pathTraversal", withExtension: "zip")! + let destinationPath = try autoRemovingSandbox() + do { - let permissionsURL = Bundle(for: ZipTests.self).url(forResource: "permissions", withExtension: "zip")! - let unzipDestination = try Zip.quickUnzipFile(permissionsURL) - let fileManager = FileManager.default - let permission777 = unzipDestination.appendingPathComponent("permission_777").appendingPathExtension("txt") - let permission600 = unzipDestination.appendingPathComponent("permission_600").appendingPathExtension("txt") - let permission604 = unzipDestination.appendingPathComponent("permission_604").appendingPathExtension("txt") - - do { - let attributes777 = try fileManager.attributesOfItem(atPath: permission777.path) - let attributes600 = try fileManager.attributesOfItem(atPath: permission600.path) - let attributes604 = try fileManager.attributesOfItem(atPath: permission604.path) - XCTAssertEqual(attributes777[.posixPermissions] as? Int, 0o777) - XCTAssertEqual(attributes600[.posixPermissions] as? Int, 0o600) - XCTAssertEqual(attributes604[.posixPermissions] as? Int, 0o604) - } catch { - XCTFail("Failed to get file attributes \(error)") - } - } catch { - XCTFail("Failed extract permissions.zip") + try Zip.unzipFile(filePath, destination: destinationPath, overwrite: true, password: "password", progress: nil) + XCTFail("ZipError.unzipFail expected.") } + catch {} + + let fileManager = FileManager.default + XCTAssertFalse(fileManager.fileExists(atPath: destinationPath.appendingPathComponent("../naughtyFile.txt").path)) } - - func testQuickUnzipSubDir() { - do { - let bookURL = Bundle(for: ZipTests.self).url(forResource: "bb8", withExtension: "zip")! - let unzipDestination = try Zip.quickUnzipFile(bookURL) - let fileManager = FileManager.default - let subDir = unzipDestination.appendingPathComponent("subDir") - let imageURL = subDir.appendingPathComponent("r2W9yu9").appendingPathExtension("gif") - - XCTAssertTrue(fileManager.fileExists(atPath:unzipDestination.path)) - XCTAssertTrue(fileManager.fileExists(atPath:subDir.path)) - XCTAssertTrue(fileManager.fileExists(atPath:imageURL.path)) - } catch { - XCTFail() + + func testQuickUnzipSubDir() throws { + let bookURL = url(forResource: "bb8", withExtension: "zip")! + let unzipDestination = try Zip.quickUnzipFile(bookURL) + addTeardownBlock { + try? FileManager.default.removeItem(at: unzipDestination) } + let fileManager = FileManager.default + let subDir = unzipDestination.appendingPathComponent("subDir") + let imageURL = subDir.appendingPathComponent("r2W9yu9").appendingPathExtension("gif") + + XCTAssertTrue(fileManager.fileExists(atPath: unzipDestination.path)) + XCTAssertTrue(fileManager.fileExists(atPath: subDir.path)) + XCTAssertTrue(fileManager.fileExists(atPath: imageURL.path)) } func testFileExtensionIsNotInvalidForValidUrl() { - let fileUrl = NSURL(string: "file.cbz") + let fileUrl = URL(string: "file.cbz") let result = Zip.fileExtensionIsInvalid(fileUrl?.pathExtension) XCTAssertFalse(result) } func testFileExtensionIsInvalidForInvalidUrl() { - let fileUrl = NSURL(string: "file.xyz") + let fileUrl = URL(string: "file.xyz") let result = Zip.fileExtensionIsInvalid(fileUrl?.pathExtension) XCTAssertTrue(result) } @@ -314,5 +286,4 @@ class ZipTests: XCTestCase { XCTAssertTrue(Zip.isValidFileExtension("zip")) XCTAssertTrue(Zip.isValidFileExtension("cbz")) } - } diff --git a/build-universal-xcframework.sh b/build-universal-xcframework.sh new file mode 100755 index 00000000..33bfe88f --- /dev/null +++ b/build-universal-xcframework.sh @@ -0,0 +1,79 @@ +#!/bin/sh + +# build-universal-xcframework.sh +# Zip +# +# Created by Rohan on 13/01/21. +# Copyright © 2021 Roy Marmelstein. All rights reserved. + +set -e + +BUILD_DIR=build +NAME=Zip + +# clean build folders +if [ -d ${BUILD_DIR} ]; then + rm -rf ${BUILD_DIR} +fi + +if [ -d "${NAME}.xcframework" ]; then + rm -rf "${NAME}.xcframework" +fi + +mkdir ${BUILD_DIR} + +# iOS devices +TARGET=iphoneos +xcodebuild archive \ + -scheme ${NAME} \ + -archivePath "./${BUILD_DIR}/${NAME}-${TARGET}.xcarchive" \ + -sdk ${TARGET} \ + SKIP_INSTALL=NO \ + BUILD_LIBRARY_FOR_DISTRIBUTION=YES + +# iOS simulator +TARGET=iphonesimulator +xcodebuild archive \ + -scheme ${NAME} \ + -archivePath "./${BUILD_DIR}/${NAME}-${TARGET}.xcarchive" \ + -sdk ${TARGET} \ + SKIP_INSTALL=NO \ + BUILD_LIBRARY_FOR_DISTRIBUTION=YES + +# tvOS devices +TARGET=appletvos +xcodebuild archive \ + -scheme "${NAME} tvOS" \ + -archivePath "./${BUILD_DIR}/${NAME}-${TARGET}.xcarchive" \ + -sdk ${TARGET} \ + SKIP_INSTALL=NO \ + BUILD_LIBRARY_FOR_DISTRIBUTION=YES + +# tvOS simulator +TARGET=appletvsimulator +xcodebuild archive \ + -scheme "${NAME} tvOS" \ + -archivePath "./${BUILD_DIR}/${NAME}-${TARGET}.xcarchive" \ + -sdk ${TARGET} \ + SKIP_INSTALL=NO \ + BUILD_LIBRARY_FOR_DISTRIBUTION=YES + +# macOS devices +TARGET=macosx +xcodebuild archive \ + -scheme "${NAME} OSX" \ + -archivePath "./${BUILD_DIR}/${NAME}-${TARGET}.xcarchive" \ + -sdk ${TARGET} \ + SKIP_INSTALL=NO \ + BUILD_LIBRARY_FOR_DISTRIBUTION=YES + +# packing .framework to .xcframework +FWMK_FILES=$(find "./${BUILD_DIR}" -name "*.framework") +for FWMK_FILE in ${FWMK_FILES} +do + FWMK_FILES_CMD="-framework ${FWMK_FILE} ${FWMK_FILES_CMD}" +done + +xcodebuild -create-xcframework \ + ${FWMK_FILES_CMD} \ + -output "${NAME}.xcframework" diff --git a/build.sh b/build.sh index e1a3cae1..6135daaf 100755 --- a/build.sh +++ b/build.sh @@ -1,9 +1,8 @@ #!/bin/bash # **** Update me when new Xcode versions are released! **** -PLATFORM="platform=iOS Simulator,OS=10.0,name=iPhone 7" -SDK="iphonesimulator10.0" - +PLATFORM="platform=iOS Simulator,OS=14.0,name=iPhone 11" +SDK="iphonesimulator" # It is pitch black. set -e @@ -14,7 +13,6 @@ function trap_handler() { } trap trap_handler INT TERM EXIT - MODE="$1" if [ "$MODE" = "framework" ]; then @@ -29,14 +27,20 @@ if [ "$MODE" = "framework" ]; then exit 0 fi +if [ "$MODE" = "spm" ]; then + echo "Building and testing Zip with SPM." + swift test + trap - EXIT + exit 0 +fi + if [ "$MODE" = "examples" ]; then - echo "Building and testing all Zip examples." + echo "Building all Zip examples." for example in examples/*/; do echo "Building $example." - pod install --project-directory=$example xcodebuild \ - -workspace "${example}Sample.xcworkspace" \ + -project "${example}Sample.xcodeproj" \ -scheme Sample \ -sdk "$SDK" \ -destination "$PLATFORM" diff --git a/examples/Sample/Podfile b/examples/Sample/Podfile deleted file mode 100644 index e1dee5c8..00000000 --- a/examples/Sample/Podfile +++ /dev/null @@ -1,7 +0,0 @@ -source 'https://github.com/CocoaPods/Specs.git' -platform :ios, "9.0" -use_frameworks! - -target ‘Sample’ do -pod 'Zip', :git => 'https://github.com/marmelroy/Zip.git', :submodules => true -end \ No newline at end of file diff --git a/examples/Sample/Sample.xcodeproj/project.pbxproj b/examples/Sample/Sample.xcodeproj/project.pbxproj index d8a737be..9138945b 100644 --- a/examples/Sample/Sample.xcodeproj/project.pbxproj +++ b/examples/Sample/Sample.xcodeproj/project.pbxproj @@ -3,10 +3,11 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 52; objects = { /* Begin PBXBuildFile section */ + 34220B42251D38BB007597C4 /* Zip in Frameworks */ = {isa = PBXBuildFile; productRef = 34220B41251D38BB007597C4 /* Zip */; }; 3430F6711C45C930007473A6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3430F6701C45C930007473A6 /* AppDelegate.swift */; }; 3430F6761C45C930007473A6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3430F6741C45C930007473A6 /* Main.storyboard */; }; 3430F6781C45C930007473A6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3430F6771C45C930007473A6 /* Assets.xcassets */; }; @@ -64,6 +65,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 34220B42251D38BB007597C4 /* Zip in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -138,6 +140,9 @@ dependencies = ( ); name = Sample; + packageProductDependencies = ( + 34220B41251D38BB007597C4 /* Zip */, + ); productName = Sample; productReference = 3430F66D1C45C930007473A6 /* Sample.app */; productType = "com.apple.product-type.application"; @@ -167,7 +172,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0800; + LastUpgradeCheck = 1200; ORGANIZATIONNAME = "Roy Marmelstein"; TargetAttributes = { 3430F66C1C45C930007473A6 = { @@ -183,13 +188,16 @@ }; buildConfigurationList = 3430F6681C45C930007473A6 /* Build configuration list for PBXProject "Sample" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 3430F6641C45C930007473A6; + packageReferences = ( + 34220B40251D38BA007597C4 /* XCRemoteSwiftPackageReference "Zip" */, + ); productRefGroup = 3430F66E1C45C930007473A6 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -276,18 +284,28 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -310,12 +328,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.2; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -323,18 +341,28 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -351,11 +379,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.2; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 3.0; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; VALIDATE_PRODUCT = YES; }; name = Release; @@ -366,7 +395,10 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = Sample/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.roymarmelstein.Sample; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -378,7 +410,10 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = Sample/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.roymarmelstein.Sample; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -389,7 +424,11 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; INFOPLIST_FILE = SampleTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.roymarmelstein.SampleTests; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Sample.app/Sample"; @@ -401,7 +440,11 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; INFOPLIST_FILE = SampleTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.roymarmelstein.SampleTests; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Sample.app/Sample"; @@ -439,6 +482,25 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 34220B40251D38BA007597C4 /* XCRemoteSwiftPackageReference "Zip" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/marmelroy/Zip.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.0.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 34220B41251D38BB007597C4 /* Zip */ = { + isa = XCSwiftPackageProductDependency; + package = 34220B40251D38BA007597C4 /* XCRemoteSwiftPackageReference "Zip" */; + productName = Zip; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 3430F6651C45C930007473A6 /* Project object */; } diff --git a/examples/Sample/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/examples/Sample/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata index a80c0382..919434a6 100644 --- a/examples/Sample/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/examples/Sample/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/examples/Sample/Sample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/examples/Sample/Sample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/examples/Sample/Sample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/examples/Sample/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme b/examples/Sample/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme index cc659cfd..9a1824b1 100644 --- a/examples/Sample/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme +++ b/examples/Sample/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme @@ -1,6 +1,6 @@ + + + + @@ -39,17 +48,6 @@ - - - - - - - -