Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Non-utf8 encding directory name Fixed #241

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
33 changes: 29 additions & 4 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -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",
Expand All @@ -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")]),
]
)
54 changes: 54 additions & 0 deletions [email protected]
Original file line number Diff line number Diff line change
@@ -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")]),
]
)
2 changes: 1 addition & 1 deletion Zip.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
2 changes: 1 addition & 1 deletion Zip/Info-tvOS.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>2.1.2</string>
<string>2.1.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
Expand Down
2 changes: 1 addition & 1 deletion Zip/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>2.1.2</string>
<string>2.1.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
Expand Down
11 changes: 9 additions & 2 deletions Zip/Zip.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
88 changes: 73 additions & 15 deletions Zip/ZipUtilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -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
*/
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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
}
2 changes: 1 addition & 1 deletion ZipTests/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>2.1.2</string>
<string>2.1.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
Expand Down
Binary file added ZipTests/Resources/gb2312.zip
Binary file not shown.
11 changes: 10 additions & 1 deletion ZipTests/ZipTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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))
}
}