mirror of
https://github.com/tsolomko/SWCompression.git
synced 2026-06-23 14:56:41 +00:00
245 lines
9.8 KiB
Swift
245 lines
9.8 KiB
Swift
// Copyright (c) 2026 Timofey Solomko
|
|
// Licensed under MIT License
|
|
//
|
|
// See LICENSE for license information
|
|
|
|
import Foundation
|
|
|
|
/// Provides access to information about an entry from the TAR container.
|
|
public struct TarEntryInfo: ContainerEntryInfo {
|
|
|
|
// MARK: ContainerEntryInfo
|
|
|
|
/**
|
|
Entry's name.
|
|
|
|
Depending on the particular format of the container, different container's structures are used
|
|
to set this property, in the following preference order:
|
|
1. Local PAX extended header "path" property.
|
|
2. Global PAX extended header "path" property.
|
|
3. GNU format type "L" (LongName) entry.
|
|
4. Default TAR header.
|
|
|
|
- Note: When a new TAR container is created, if `name` cannot be encoded with ASCII or its ASCII byte-representation
|
|
is longer than 100 bytes then a PAX extended header will be created to represent this value correctly, unless other
|
|
format is forced.
|
|
- Note: When creating new TAR container, `name` is always encoded with UTF-8 in the basic TAR header.
|
|
*/
|
|
public var name: String
|
|
|
|
/**
|
|
Entry's data size.
|
|
|
|
- Note: This property cannot be directly modified. Instead it is updated automatically to be equal to its parent's
|
|
`entry.data.count`.
|
|
- Note: When a new TAR container is created, if `size` is bigger than 8589934591 then a PAX extended header will be
|
|
created to represent this value correctly, unless other format is forced. In addition, base-256 encoding will be
|
|
used to encode this value in the basic TAR header.
|
|
*/
|
|
public internal(set) var size: Int?
|
|
|
|
public let type: ContainerEntryType
|
|
|
|
/**
|
|
Entry's last access time (only available for PAX format; `nil` otherwise).
|
|
|
|
- Note: When a new TAR container is created, if `accessTime` is not `nil` then a PAX extended header will be created
|
|
to store this property, unless other format is forced.
|
|
*/
|
|
public var accessTime: Date?
|
|
|
|
/**
|
|
Entry's creation time (only available for PAX format; `nil` otherwise).
|
|
|
|
- Note: When a new TAR container is created, if `creationTime` is not `nil` then a PAX extended header will be
|
|
created to store this property, unless other format is forced.
|
|
*/
|
|
public var creationTime: Date?
|
|
|
|
/**
|
|
Entry's last modification time.
|
|
|
|
- Note: When a new TAR container is created, if `modificationTime` is bigger than 8589934591 then a PAX extended
|
|
header will be created to represent this value correctly, unless other format is forced. In addition, base-256
|
|
encoding will be used to encode this value in the basic TAR header.
|
|
*/
|
|
public var modificationTime: Date?
|
|
|
|
public var permissions: Permissions?
|
|
|
|
// MARK: TAR specific
|
|
|
|
/// Entry's compression method. Always `.copy` for the entries of TAR containers.
|
|
public let compressionMethod = CompressionMethod.copy
|
|
|
|
/**
|
|
ID of entry's owner.
|
|
|
|
- Note: When a new TAR container is created, if `ownerID` is bigger than 2097151 then a PAX extended header will be
|
|
created to represent this value correctly, unless other format is forced. In addition, base-256 encoding will be
|
|
used to encode this value in the basic TAR header.
|
|
*/
|
|
public var ownerID: Int?
|
|
|
|
/**
|
|
ID of the group of entry's owner.
|
|
|
|
- Note: When a new TAR container is created, if `groupID` is bigger than 2097151 then a PAX extended header will be
|
|
created to represent this value correctly, unless other format is forced. In addition, base-256 encoding will be
|
|
used to encode this value in the basic TAR header.
|
|
*/
|
|
public var groupID: Int?
|
|
|
|
/**
|
|
User name of entry's owner.
|
|
|
|
- Note: When a new TAR container is created, if `ownerUserName` cannot be encoded with ASCII or its ASCII
|
|
byte-representation is longer than 32 bytes then a PAX extended header will be created to represent this value
|
|
correctly, unless other format is forced.
|
|
- Note: When creating new TAR container, `ownerUserName` is always encoded with UTF-8 in the ustar header.
|
|
*/
|
|
public var ownerUserName: String?
|
|
|
|
/**
|
|
Name of the group of entry's owner.
|
|
|
|
- Note: When new TAR container is created, if `ownerGroupName` cannot be encoded with ASCII or its ASCII
|
|
byte-representation is longer than 32 bytes then a PAX extended header will be created to represent this value
|
|
correctly, unless other format is forced.
|
|
- Note: When creating new TAR container, `ownerGroupName` is always encoded with UTF-8 in the ustar header.
|
|
*/
|
|
public var ownerGroupName: String?
|
|
|
|
/**
|
|
Device major number (used when entry is either block or character special file).
|
|
|
|
- Note: When new TAR container is created, if `deviceMajorNumber` is bigger than 2097151 then base-256 encoding
|
|
will be used to encode this value in ustar header.
|
|
*/
|
|
public var deviceMajorNumber: Int?
|
|
|
|
/**
|
|
Device minor number (used when entry is either block or character special file).
|
|
|
|
- Note: When new TAR container is created, if `deviceMinorNumber` is bigger than 2097151 then base-256 encoding
|
|
will be used to encode this value in ustar header.
|
|
*/
|
|
public var deviceMinorNumber: Int?
|
|
|
|
/**
|
|
Name of the character set used to encode entry's data (only available for PAX format; `nil` otherwise).
|
|
|
|
- Note: When new TAR container is created, if `charset` is not `nil` then a PAX extended header will be created to
|
|
store this property, unless other format is forced.
|
|
*/
|
|
public var charset: String?
|
|
|
|
/**
|
|
Entry's comment (only available for PAX format; `nil` otherwise).
|
|
|
|
- Note: When new TAR container is created, if `comment` is not `nil` then a PAX extended header will be created to
|
|
store this property, unless other format is forced.
|
|
*/
|
|
public var comment: String?
|
|
|
|
/**
|
|
Path to a linked file for symbolic link entry.
|
|
|
|
Depending on the particular format of the container, different container's structures are used
|
|
to set this property, in the following preference order:
|
|
1. Local PAX extended header "linkpath" property.
|
|
2. Global PAX extended header "linkpath" property.
|
|
3. GNU format type "K" (LongLink) entry.
|
|
4. Default TAR header.
|
|
|
|
- Note: When new TAR container is created, if `linkName` cannot be encoded with ASCII or its ASCII
|
|
byte-representation is longer than 100 bytes then a PAX extended header will be created to represent this value
|
|
correctly, unless other format is forced.
|
|
- Note: When creating new TAR container, `linkName` is always encoded with UTF-8 in basic TAR header.
|
|
*/
|
|
public var linkName: String
|
|
|
|
/**
|
|
All custom (unknown) records from global and local PAX extended headers. `nil`, if there were no headers.
|
|
|
|
- Note: When new TAR container is created, if `unknownExtendedHeaderRecords` is not `nil` then a *local* PAX
|
|
extended header will be created to store this property, unless other format is forced.
|
|
*/
|
|
public var unknownExtendedHeaderRecords: [String: String]?
|
|
|
|
/**
|
|
Initializes the entry's info with its name and type.
|
|
|
|
- Note: Entry's type cannot be modified after initialization.
|
|
|
|
- Parameter name: Entry's name.
|
|
- Parameter type: Entry's type.
|
|
*/
|
|
public init(name: String, type: ContainerEntryType) {
|
|
self.name = name
|
|
self.type = type
|
|
self.linkName = ""
|
|
}
|
|
|
|
init(_ header: TarHeader, _ global: TarExtendedHeader?, _ local: TarExtendedHeader?,
|
|
_ longName: String?, _ longLinkName: String?) {
|
|
self.permissions = header.permissions
|
|
self.ownerID = (local?.uid ?? global?.uid) ?? header.uid
|
|
self.groupID = (local?.gid ?? global?.gid) ?? header.gid
|
|
self.size = (local?.size ?? global?.size) ?? header.size
|
|
if let paxMtime = local?.mtime ?? global?.mtime {
|
|
self.modificationTime = Date(timeIntervalSince1970: paxMtime)
|
|
} else {
|
|
self.modificationTime = header.mtime
|
|
}
|
|
|
|
// File type
|
|
guard case .normal(let entryType) = header.type
|
|
else { fatalError("TarEntryInfo.init: unexpected TarHeader.type, \(header.type)") }
|
|
self.type = entryType
|
|
|
|
self.ownerUserName = (local?.uname ?? global?.uname) ?? header.uname
|
|
self.ownerGroupName = (local?.gname ?? global?.gname) ?? header.gname
|
|
self.deviceMajorNumber = header.deviceMajorNumber
|
|
self.deviceMinorNumber = header.deviceMinorNumber
|
|
|
|
// Set `name` and `linkName` to values from PAX or GNU format if possible.
|
|
var name = header.name
|
|
if let prefix = header.prefix, prefix != "" {
|
|
if prefix.last == "/" {
|
|
name = prefix + name
|
|
} else {
|
|
name = prefix + "/" + name
|
|
}
|
|
}
|
|
self.name = ((local?.path ?? global?.path) ?? longName) ?? name
|
|
self.linkName = ((local?.linkpath ?? global?.linkpath) ?? longLinkName) ?? header.linkName
|
|
|
|
// Set additional properties from PAX extended headers.
|
|
if let atime = local?.atime ?? global?.atime {
|
|
self.accessTime = Date(timeIntervalSince1970: atime)
|
|
} else {
|
|
self.accessTime = header.atime
|
|
}
|
|
|
|
if let ctime = local?.ctime ?? global?.ctime {
|
|
self.creationTime = Date(timeIntervalSince1970: ctime)
|
|
} else {
|
|
self.creationTime = header.ctime
|
|
}
|
|
|
|
self.charset = local?.charset ?? global?.charset
|
|
self.comment = local?.comment ?? global?.comment
|
|
if let localUnknownRecords = local?.unknownRecords {
|
|
if let globalUnknownRecords = global?.unknownRecords {
|
|
self.unknownExtendedHeaderRecords = globalUnknownRecords.merging(localUnknownRecords) { $1 }
|
|
} else {
|
|
self.unknownExtendedHeaderRecords = localUnknownRecords
|
|
}
|
|
} else {
|
|
self.unknownExtendedHeaderRecords = global?.unknownRecords
|
|
}
|
|
}
|
|
|
|
}
|