Files
2026-01-31 15:30:24 +08:00

184 lines
6.4 KiB
Swift

// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
import Foundation
import SWCompression
import SwiftCLI
extension TarEntry {
static func createEntries(_ inputPath: String, _ verbose: Bool) throws -> [TarEntry] {
let inputURL = URL(fileURLWithPath: inputPath)
let fileManager = FileManager.default
let fileAttributes = try fileManager.attributesOfItem(atPath: inputPath)
let name = inputURL.relativePath
let entryType: ContainerEntryType
if let typeFromAttributes = fileAttributes[.type] as? FileAttributeType {
switch typeFromAttributes {
case .typeBlockSpecial:
entryType = .blockSpecial
case .typeCharacterSpecial:
entryType = .characterSpecial
case .typeDirectory:
entryType = .directory
case .typeRegular:
entryType = .regular
case .typeSocket:
entryType = .socket
case .typeSymbolicLink:
entryType = .symbolicLink
case .typeUnknown:
entryType = .unknown
default:
entryType = .unknown
}
} else {
entryType = .unknown
}
var info = TarEntryInfo(name: name, type: entryType)
info.creationTime = fileAttributes[.creationDate] as? Date
info.groupID = (fileAttributes[.groupOwnerAccountID] as? NSNumber)?.intValue
info.ownerGroupName = fileAttributes[.groupOwnerAccountName] as? String
info.modificationTime = fileAttributes[.modificationDate] as? Date
info.ownerID = (fileAttributes[.ownerAccountID] as? NSNumber)?.intValue
info.ownerUserName = fileAttributes[.ownerAccountName] as? String
if let posixPermissions = (fileAttributes[.posixPermissions] as? NSNumber)?.intValue {
info.permissions = Permissions(rawValue: UInt32(truncatingIfNeeded: posixPermissions))
}
var entryData = Data()
if entryType == .symbolicLink {
info.linkName = try fileManager.destinationOfSymbolicLink(atPath: inputPath)
} else if entryType != .directory {
entryData = try Data(contentsOf: URL(fileURLWithPath: inputPath))
}
if verbose {
var log = ""
switch entryType {
case .regular:
log += "f: "
case .directory:
log += "d: "
case .symbolicLink:
log += "l:"
default:
log += "u: "
}
log += name
if entryType == .symbolicLink {
log += " -> " + info.linkName
}
print(log)
}
let entry = TarEntry(info: info, data: entryData)
var entries = [TarEntry]()
entries.append(entry)
if entryType == .directory {
for subPath in try fileManager.contentsOfDirectory(atPath: inputPath) {
entries.append(contentsOf: try self.createEntries(inputURL.appendingPathComponent(subPath).relativePath,
verbose))
}
}
return entries
}
static func generateEntries(_ writer: inout TarWriter, _ inputPath: String, _ verbose: Bool) throws {
let inputURL = URL(fileURLWithPath: inputPath)
let fileManager = FileManager.default
let fileAttributes = try fileManager.attributesOfItem(atPath: inputPath)
let name = inputURL.relativePath
let entryType: ContainerEntryType
if let typeFromAttributes = fileAttributes[.type] as? FileAttributeType {
switch typeFromAttributes {
case .typeBlockSpecial:
entryType = .blockSpecial
case .typeCharacterSpecial:
entryType = .characterSpecial
case .typeDirectory:
entryType = .directory
case .typeRegular:
entryType = .regular
case .typeSocket:
entryType = .socket
case .typeSymbolicLink:
entryType = .symbolicLink
case .typeUnknown:
entryType = .unknown
default:
entryType = .unknown
}
} else {
entryType = .unknown
}
var info = TarEntryInfo(name: name, type: entryType)
info.creationTime = fileAttributes[.creationDate] as? Date
info.groupID = (fileAttributes[.groupOwnerAccountID] as? NSNumber)?.intValue
info.ownerGroupName = fileAttributes[.groupOwnerAccountName] as? String
info.modificationTime = fileAttributes[.modificationDate] as? Date
info.ownerID = (fileAttributes[.ownerAccountID] as? NSNumber)?.intValue
info.ownerUserName = fileAttributes[.ownerAccountName] as? String
if let posixPermissions = (fileAttributes[.posixPermissions] as? NSNumber)?.intValue {
info.permissions = Permissions(rawValue: UInt32(truncatingIfNeeded: posixPermissions))
}
try autoreleasepool {
var entryData = Data()
if entryType == .symbolicLink {
info.linkName = try fileManager.destinationOfSymbolicLink(atPath: inputPath)
} else if entryType != .directory {
entryData = try Data(contentsOf: URL(fileURLWithPath: inputPath))
}
let entry = TarEntry(info: info, data: entryData)
try writer.append(entry)
}
if verbose {
var log = ""
switch entryType {
case .regular:
log += "f: "
case .directory:
log += "d: "
case .symbolicLink:
log += "l:"
default:
log += "u: "
}
log += name
if entryType == .symbolicLink {
log += " -> " + info.linkName
}
print(log)
}
if entryType == .directory {
for subPath in try fileManager.contentsOfDirectory(atPath: inputPath) {
try self.generateEntries(&writer, inputURL.appendingPathComponent(subPath).relativePath, verbose)
}
}
}
}
#if os(Linux) || os(Windows)
@discardableResult
fileprivate func autoreleasepool<T>(_ block: () throws -> T) rethrows -> T {
return try block()
}
#endif