diff --git a/Package.swift b/Package.swift
index fd33704c..e601c2fe 100644
--- a/Package.swift
+++ b/Package.swift
@@ -1,12 +1,35 @@
-// swift-tools-version:5.1
+// swift-tools-version:5.8
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
+let defines: [SwiftSetting] = [
+ .define("SYSTEM_ICONV", .when(platforms: [.linux]))
+]
+
+#if os(Linux)
+let dependencies: [PackageDescription.Package.Dependency] = [
+ .package(url: "https://github.com/0xfeedface1993/iconv.git", branch: "main"),
+ .package(url: "https://github.com/0xfeedface1993/CEnca.git", from: "0.1.3"),
+]
+let products: [Target.Dependency] = [
+ .product(name: "iconv", package: "iconv"),
+ .product(name: "EncodingWrapper", package: "CEnca"),
+]
+#else
+let dependencies: [PackageDescription.Package.Dependency] = [
+ .package(url: "https://github.com/apple/swift-log.git", from: "1.5.2"),
+]
+let products: [Target.Dependency] = [
+ .product(name: "Logging", package: "swift-log"),
+]
+#endif
+
let package = Package(
name: "Zip",
products: [
.library(name: "Zip", targets: ["Zip"])
],
+ dependencies: dependencies,
targets: [
.target(
name: "Minizip",
@@ -18,12 +41,14 @@ let package = Package(
]),
.target(
name: "Zip",
- dependencies: ["Minizip"],
+ dependencies: ["Minizip"] + products,
path: "Zip",
- exclude: ["minizip", "zlib"]),
+ exclude: ["minizip", "zlib"],
+ swiftSettings: defines),
.testTarget(
name: "ZipTests",
dependencies: ["Zip"],
- path: "ZipTests"),
+ path: "ZipTests",
+ resources: [.process("Resources")]),
]
)
diff --git a/Package@swift-5.8.swift b/Package@swift-5.8.swift
new file mode 100644
index 00000000..e601c2fe
--- /dev/null
+++ b/Package@swift-5.8.swift
@@ -0,0 +1,54 @@
+// swift-tools-version:5.8
+// The swift-tools-version declares the minimum version of Swift required to build this package.
+import PackageDescription
+
+let defines: [SwiftSetting] = [
+ .define("SYSTEM_ICONV", .when(platforms: [.linux]))
+]
+
+#if os(Linux)
+let dependencies: [PackageDescription.Package.Dependency] = [
+ .package(url: "https://github.com/0xfeedface1993/iconv.git", branch: "main"),
+ .package(url: "https://github.com/0xfeedface1993/CEnca.git", from: "0.1.3"),
+]
+let products: [Target.Dependency] = [
+ .product(name: "iconv", package: "iconv"),
+ .product(name: "EncodingWrapper", package: "CEnca"),
+]
+#else
+let dependencies: [PackageDescription.Package.Dependency] = [
+ .package(url: "https://github.com/apple/swift-log.git", from: "1.5.2"),
+]
+let products: [Target.Dependency] = [
+ .product(name: "Logging", package: "swift-log"),
+]
+#endif
+
+let package = Package(
+ name: "Zip",
+ products: [
+ .library(name: "Zip", targets: ["Zip"])
+ ],
+ dependencies: dependencies,
+ targets: [
+ .target(
+ name: "Minizip",
+ dependencies: [],
+ path: "Zip/minizip",
+ exclude: ["module"],
+ linkerSettings: [
+ .linkedLibrary("z")
+ ]),
+ .target(
+ name: "Zip",
+ dependencies: ["Minizip"] + products,
+ path: "Zip",
+ exclude: ["minizip", "zlib"],
+ swiftSettings: defines),
+ .testTarget(
+ name: "ZipTests",
+ dependencies: ["Zip"],
+ path: "ZipTests",
+ resources: [.process("Resources")]),
+ ]
+)
diff --git a/Zip.podspec b/Zip.podspec
index 907757f9..5842cd03 100644
--- a/Zip.podspec
+++ b/Zip.podspec
@@ -8,7 +8,7 @@
Pod::Spec.new do |s|
s.name = "Zip"
- s.version = "2.1.2"
+ s.version = "2.1.3"
s.summary = "Zip and unzip files in Swift."
s.swift_version = "5.3"
s.swift_versions = ["4.2", "5.0", "5.1", "5.3"]
diff --git a/Zip/Info-tvOS.plist b/Zip/Info-tvOS.plist
index bc21553c..41c0d687 100644
--- a/Zip/Info-tvOS.plist
+++ b/Zip/Info-tvOS.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.1.2
+ 2.1.3
CFBundleSignature
????
CFBundleVersion
diff --git a/Zip/Info.plist b/Zip/Info.plist
index bc21553c..41c0d687 100644
--- a/Zip/Info.plist
+++ b/Zip/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.1.2
+ 2.1.3
CFBundleSignature
????
CFBundleVersion
diff --git a/Zip/Zip.swift b/Zip/Zip.swift
index af2d453e..ac98281a 100644
--- a/Zip/Zip.swift
+++ b/Zip/Zip.swift
@@ -160,8 +160,15 @@ public class Zip {
unzGetCurrentFileInfo64(zip, &fileInfo, fileName, UInt(fileNameSize), nil, 0, nil, 0)
fileName[Int(fileInfo.size_filename)] = 0
- var pathString = String(cString: fileName)
-
+ let encodingPathString = AutoEncodingString(data: .init(bytes: fileName, count: fileNameSize)).text()
+ var pathString = encodingPathString.isEmpty ? String(cString: fileName):encodingPathString
+#if DEBUG
+ if encodingPathString.isEmpty {
+ print("encoding string failed, use cString \(pathString)")
+ } else {
+ print("encoding string \(pathString)")
+ }
+#endif
guard pathString.count > 0 else {
throw ZipError.unzipFail
}
diff --git a/Zip/ZipUtilities.swift b/Zip/ZipUtilities.swift
index 0bcec54e..b3369f57 100644
--- a/Zip/ZipUtilities.swift
+++ b/Zip/ZipUtilities.swift
@@ -7,6 +7,14 @@
//
import Foundation
+import Logging
+
+#if SYSTEM_ICONV && canImport(iconv) && canImport(EncodingWrapper)
+import iconv
+import EncodingWrapper
+#endif
+
+fileprivate let logger = Logger(label: "com.zip.utilties")
internal class ZipUtilities {
@@ -19,21 +27,21 @@ internal class ZipUtilities {
As true:
$ zip -r Test.zip Test/
$ unzip -l Test.zip
- Test/
- Test/A.txt
- Test/B.txt
+ Test/
+ Test/A.txt
+ Test/B.txt
As false:
$ zip -r Test.zip Test/
$ unzip -l Test.zip
- A.txt
- B.txt
- */
+ A.txt
+ B.txt
+ */
let includeRootDirectory = true
-
+
// File manager
let fileManager = FileManager.default
-
+
/**
* ProcessedFilePath struct
*/
@@ -49,12 +57,12 @@ internal class ZipUtilities {
//MARK: Path processing
/**
- Process zip paths
-
- - parameter paths: Paths as NSURL.
-
- - returns: Array of ProcessedFilePath structs.
- */
+ Process zip paths
+
+ - parameter paths: Paths as NSURL.
+
+ - returns: Array of ProcessedFilePath structs.
+ */
internal func processZipPaths(_ paths: [URL]) -> [ProcessedFilePath]{
var processedFilePaths = [ProcessedFilePath]()
for path in paths {
@@ -88,7 +96,7 @@ internal class ZipUtilities {
while let filePathComponent = enumerator.nextObject() as? String {
let path = directory.appendingPathComponent(filePathComponent)
let filePath = path.path
-
+
var isDirectory: ObjCBool = false
_ = fileManager.fileExists(atPath: filePath, isDirectory: &isDirectory)
if !isDirectory.boolValue {
@@ -104,5 +112,55 @@ internal class ZipUtilities {
}
return processedFilePaths
}
+}
+#if SYSTEM_ICONV && canImport(iconv) && canImport(EncodingWrapper)
+extension Iconv.CodePage {
+ init(name: String) throws {
+ guard let sourceCodePage = Iconv.CodePage(rawValue: name) else {
+ throw CocoaError(.fileReadUnknownStringEncoding)
+ }
+ self = sourceCodePage
+ }
+}
+#endif
+
+public struct AutoEncodingString {
+ /// Raw sting data
+ public let data: Data
+
+ public init(data: Data) {
+ self.data = data
+ }
+
+#if SYSTEM_ICONV && canImport(iconv) && canImport(EncodingWrapper)
+ /// Auto detect string encode
+ /// - Returns: string with valid encoding, it will be empty string if encoding failed.
+ public func text() -> String {
+ do {
+ let encodeName = try EncodingWrapper(data).style(.iconv).guessAllLanguageFoEncodingString()
+ let sourceCodePage = try Iconv.CodePage(name: encodeName)
+ var buffer = data.withUnsafeBytes(Array.init(_:))
+ if buffer.last != 0 {
+ buffer.append(0)
+ }
+ let unicodeString = try Iconv(from: sourceCodePage, to: .UTF8).utf8(buf: buffer)
+ return unicodeString ?? ""
+ } catch {
+ logger.error("string encoding process failed, \(error)")
+ return ""
+ }
+ }
+#else
+ /// Auto detect string encode
+ /// - Returns: string with valid encoding, it will be empty string if encoding failed.
+ public func text() -> String {
+ var lossy = ObjCBool(false)
+ let encodingValue = NSString.stringEncoding(for: data, encodingOptions: nil, convertedString: nil, usedLossyConversion: &lossy)
+ let encoding = String.Encoding(rawValue: encodingValue)
+ logger.info("detect string encoding \(encoding), usedLossyConversion \(lossy.boolValue)")
+ let string = String(data: data, encoding: encoding)
+ return string ?? ""
+ }
+#endif
}
diff --git a/ZipTests/Info.plist b/ZipTests/Info.plist
index db36cdf0..c8548fd8 100644
--- a/ZipTests/Info.plist
+++ b/ZipTests/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
BNDL
CFBundleShortVersionString
- 2.1.2
+ 2.1.3
CFBundleSignature
????
CFBundleVersion
diff --git a/ZipTests/Resources/gb2312.zip b/ZipTests/Resources/gb2312.zip
new file mode 100644
index 00000000..f5daad75
Binary files /dev/null and b/ZipTests/Resources/gb2312.zip differ
diff --git a/ZipTests/ZipTests.swift b/ZipTests/ZipTests.swift
index 70f689ff..aad9052b 100644
--- a/ZipTests/ZipTests.swift
+++ b/ZipTests/ZipTests.swift
@@ -33,7 +33,7 @@ class ZipTests: XCTestCase {
private func url(forResource resource: String, withExtension ext: String? = nil) -> URL? {
#if Xcode
- return Bundle(for: ZipTests.self).url(forResource: resource, withExtension: ext)
+ return Bundle.module.url(forResource: resource, withExtension: ext)
#else
let testDirPath = URL(fileURLWithPath: String(#file)).deletingLastPathComponent()
let resourcePath = testDirPath.appendingPathComponent("Resources").appendingPathComponent(resource)
@@ -271,4 +271,13 @@ class ZipTests: XCTestCase {
XCTAssertTrue(Zip.isValidFileExtension("zip"))
XCTAssertTrue(Zip.isValidFileExtension("cbz"))
}
+
+ func testGB2312File() throws {
+ let filePath = url(forResource: "gb2312", withExtension: "zip")!
+ let destinationURL = try Zip.quickUnzipFile(filePath)
+ addTeardownBlock {
+ try? FileManager.default.removeItem(at: destinationURL)
+ }
+ XCTAssertTrue(FileManager.default.fileExists(atPath: destinationURL.path))
+ }
}