From 776bfa84944fbcd5fb24f72b8f059f89252809c5 Mon Sep 17 00:00:00 2001
From: Francesco Paolo Severino <francescopaolo.severino@gmail.com>
Date: Sun, 22 Sep 2024 11:34:20 +0200
Subject: [PATCH] Try again with `swift-system`

---
 Package.swift         |  4 +++
 Sources/Zip/Zip.swift | 63 +++++++++++++++++--------------------------
 2 files changed, 29 insertions(+), 38 deletions(-)

diff --git a/Package.swift b/Package.swift
index 16468037..74401811 100644
--- a/Package.swift
+++ b/Package.swift
@@ -14,6 +14,9 @@ let package = Package(
     products: [
         .library(name: "Zip", targets: ["Zip"])
     ],
+    dependencies: [
+        .package(url: "https://github.com/apple/swift-system", from: "1.3.2"),
+    ],
     targets: [
         .target(
             name: "CMinizip",
@@ -26,6 +29,7 @@ let package = Package(
             name: "Zip",
             dependencies: [
                 .target(name: "CMinizip"),
+                .product(name: "SystemPackage", package: "swift-system"),
             ],
             cSettings: [
                 .define("_CRT_SECURE_NO_WARNINGS", .when(platforms: [.windows])),
diff --git a/Sources/Zip/Zip.swift b/Sources/Zip/Zip.swift
index 6f3ca0df..3c64e566 100644
--- a/Sources/Zip/Zip.swift
+++ b/Sources/Zip/Zip.swift
@@ -11,6 +11,7 @@ import FoundationEssentials
 #else
 import Foundation
 #endif
+import SystemPackage
 @_implementationOnly import CMinizip
 
 /// Main class that handles zipping and unzipping of files.
@@ -69,7 +70,7 @@ public class Zip {
         progressTracker.kind = ProgressKind.file
         
         // Begin unzipping
-        let zip = unzOpen64(path)
+        let zip = unzOpen64(FilePath(path).string)
         defer { unzClose(zip) }
         if unzGoToFirstFile(zip) != UNZ_OK {
             throw ZipError.unzipFail
@@ -99,11 +100,6 @@ public class Zip {
 
             var pathString = String(cString: fileName)
 
-            #if os(Windows)
-            // Colons are not allowed in Windows file names.
-            pathString = pathString.replacingOccurrences(of: ":", with: "_")
-            #endif
-
             guard !pathString.isEmpty else {
                 throw ZipError.unzipFail
             }
@@ -121,24 +117,12 @@ public class Zip {
                 throw ZipError.unzipFail
             }
 
-            let directoryAttributes: [FileAttributeKey: Any]?
-            #if (os(Linux) || os(Windows)) && swift(<6.0)
-                // On Linux and Windows, setting attributes is not yet really implemented.
-                directoryAttributes = nil
-            #else
-                let creationDate = Date()
-                directoryAttributes = [
-                    .creationDate: creationDate,
-                    .modificationDate: creationDate
-                ]
-            #endif
-
             do {
                 if isDirectory {
-                    try FileManager.default.createDirectory(atPath: fullPath, withIntermediateDirectories: true, attributes: directoryAttributes)
+                    try FileManager.default.createDirectory(atPath: fullPath, withIntermediateDirectories: true)
                 } else {
                     let parentDirectory = (fullPath as NSString).deletingLastPathComponent
-                    try FileManager.default.createDirectory(atPath: parentDirectory, withIntermediateDirectories: true, attributes: directoryAttributes)
+                    try FileManager.default.createDirectory(atPath: parentDirectory, withIntermediateDirectories: true)
                 }
             } catch {}
             if FileManager.default.fileExists(atPath: fullPath) && !isDirectory && !overwrite {
@@ -146,19 +130,28 @@ public class Zip {
                 ret = unzGoToNextFile(zip)
             }
 
-            var writeBytes: UInt64 = 0
-            let filePointer: UnsafeMutablePointer<FILE>? = fopen(fullPath, "wb")
-            while let filePointer {
-                let readBytes = unzReadCurrentFile(zip, &buffer, bufferSize)
-                guard readBytes > 0 else { break }
-                guard fwrite(buffer, Int(readBytes), 1, filePointer) == 1 else {
-                    throw ZipError.unzipFail
+            var writeBytes: Int = 0
+            do {
+                let fd = try FileDescriptor.open(
+                    FilePath(fullPath),
+                    .writeOnly,
+                    options: [.append, .create],
+                    permissions: .ownerReadWrite
+                )
+                try fd.closeAfter {
+                    while true {
+                        let readBytes = unzReadCurrentFile(zip, &buffer, bufferSize)
+                        guard readBytes > 0 else { break }
+                        writeBytes += try fd.writeAll(buffer[..<Int(readBytes)])
+                    }
                 }
-                writeBytes += UInt64(readBytes)
+            } catch let error as Errno where error == .isDirectory {
+                // Skip files that are directories
+            } catch {
+                // If they are not directories, re-throw any other error
+                throw error 
             }
 
-            if let filePointer { fclose(filePointer) }
-
             if unzCloseCurrentFile(zip) == UNZ_CRCERROR {
                 throw ZipError.unzipFail
             }
@@ -187,15 +180,9 @@ public class Zip {
             }
             
             if let fileHandler = fileOutputHandler,
-                let encodedString = fullPath.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
-                #if os(Windows)
-                let fileUrlString = "file:///\(encodedString)"
-                #else
-                let fileUrlString = encodedString
-                #endif
-                if let fileUrl = URL(string: fileUrlString) {
+                let encodedString = fullPath.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
+                let fileUrl = URL(string: FilePath(encodedString).string) {
                     fileHandler(fileUrl)
-                }
             }
             
             progressTracker.completedUnitCount = Int64(currentPosition)