@@ -97,22 +97,24 @@ func octal11(_ value: Int) -> String {
9797}
9898
9999// These ranges define the offsets of the standard fields in a Tar header.
100- let name = 0 ..< 100
101- let mode = 100 ..< 108
102- let uid = 108 ..< 116
103- let gid = 116 ..< 124
104- let size = 124 ..< 136
105- let mtime = 136 ..< 148
106- let chksum = 148 ..< 156
107- let typeflag = 156 ..< 157
108- let linkname = 157 ..< 257
109- let magic = 257 ..< 264
110- let version = 263 ..< 265
111- let uname = 265 ..< 297
112- let gname = 297 ..< 329
113- let devmajor = 329 ..< 337
114- let devminor = 337 ..< 345
115- let prefix = 345 ..< 500
100+ enum Field {
101+ static let name = 0 ..< 100
102+ static let mode = 100 ..< 108
103+ static let uid = 108 ..< 116
104+ static let gid = 116 ..< 124
105+ static let size = 124 ..< 136
106+ static let mtime = 136 ..< 148
107+ static let chksum = 148 ..< 156
108+ static let typeflag = 156 ..< 157
109+ static let linkname = 157 ..< 257
110+ static let magic = 257 ..< 264
111+ static let version = 263 ..< 265
112+ static let uname = 265 ..< 297
113+ static let gname = 297 ..< 329
114+ static let devmajor = 329 ..< 337
115+ static let devminor = 337 ..< 345
116+ static let prefix = 345 ..< 500
117+ }
116118
117119/// Calculates a checksum over the contents of a tar header.
118120/// - Parameter header: Tar header to checksum.
@@ -142,63 +144,119 @@ let TVERSION = "00" // Version used by macOS tar
142144
143145let INIT_CHECKSUM = " " // Initial value of the checksum field before checksum calculation
144146
145- // Typeflag values
146- let REGTYPE = " 0 " // regular file
147- let AREGTYPE = " \0 " // regular file
148- let LNKTYPE = " 1 " // link
149- let SYMTYPE = " 2 " // reserved
150- let CHRTYPE = " 3 " // character special
151- let BLKTYPE = " 4 " // block special
152- let DIRTYPE = " 5 " // directory
153- let FIFOTYPE = " 6 " // FIFO special
154- let CONTTYPE = " 7 " // reserved
155- let XHDTYPE = " x " // Extended header referring to the next file in the archive
156- let XGLTYPE = " g " // Global extended header
157-
158- /// Creates a tar header for a single file
159- /// - Parameters:
160- /// - filesize: The size of the file
161- /// - filename: The file's name in the archive
162- /// - Returns: A tar header representing the file
163- /// - Throws: If the filename is invalid
164- public func tarHeader( filesize: Int , filename: String = " app " ) throws -> [ UInt8 ] {
165- // A file entry consists of a file header followed by the
166- // contents of the file. The header includes information such as
167- // the file name, size and permissions. Different versions of
168- // tar added extra header fields.
169- //
170- // The file data is padded with nulls to a multiple of 512 bytes.
147+ // Typeflag
148+ enum MemberType : String {
149+ case REGTYPE = " 0 " // regular file
150+ case AREGTYPE = " \0 " // regular file
151+ case LNKTYPE = " 1 " // link
152+ case SYMTYPE = " 2 " // reserved
153+ case CHRTYPE = " 3 " // character special
154+ case BLKTYPE = " 4 " // block special
155+ case DIRTYPE = " 5 " // directory
156+ case FIFOTYPE = " 6 " // FIFO special
157+ case CONTTYPE = " 7 " // reserved
158+ case XHDTYPE = " x " // Extended header referring to the next file in the archive
159+ case XGLTYPE = " g " // Global extended header
160+ }
161+
162+ // maybe limited string, octal6 and octal11 should be separate types
163+
164+ public struct TarHeader {
165+ var name : String // do we need an explicit constructor to check this and throw?
166+ var mode : Int = 555
167+ var uid : Int = 0
168+ var gid : Int = 0
169+ var size : Int = 0
170+ var mtime : Int = 0
171+ var checksum : String = INIT_CHECKSUM // maybe not
172+ var typeflag : MemberType = . REGTYPE // better as enum
173+ var linkname : String = " "
174+ var magic : String = TMAGIC
175+ var version : String = TVERSION
176+ var uname : String = " "
177+ var gname : String = " "
178+ var devmajor : Int = 0
179+ var devminor : Int = 0
180+ var prefix : String = " "
171181
172- // Archive member name cannot be empty because a Unix filename cannot be the empty string
173- // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_170
174- guard filename. count > 0 else {
175- throw TarError . invalidName ( filename)
182+ init (
183+ name: String ,
184+ mode: Int = 0o555 ,
185+ uid: Int = 0 ,
186+ gid: Int = 0 ,
187+ size: Int = 0 ,
188+ mtime: Int = 0 ,
189+ typeflag: MemberType = . REGTYPE,
190+ linkname: String = " " ,
191+ uname: String = " " ,
192+ gname: String = " " ,
193+ devmajor: Int = 0 ,
194+ devminor: Int = 0 ,
195+ prefix: String = " "
196+ ) throws {
197+ // Archive member name cannot be empty because a Unix filename cannot be the empty string
198+ // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_170
199+ guard name. count > 0 else {
200+ throw TarError . invalidName ( name)
201+ }
202+
203+ self . name = name
204+ self . mode = mode
205+ self . uid = uid
206+ self . gid = gid
207+ self . size = size
208+ self . mtime = mtime
209+ self . checksum = INIT_CHECKSUM
210+ self . typeflag = typeflag
211+ self . linkname = linkname
212+ self . magic = TMAGIC
213+ self . version = TVERSION
214+ self . uname = uname
215+ self . gname = gname
216+ self . devmajor = devmajor
217+ self . devminor = devminor
218+ self . prefix = prefix
176219 }
220+ }
221+
222+ extension TarHeader {
223+ /// Creates a tar header for a single file
224+ /// - Parameters:
225+ /// - hdr: The header structure of the file
226+ /// - Returns: A tar header representing the file
227+ var bytes : [ UInt8 ] {
228+ // A file entry consists of a file header followed by the
229+ // contents of the file. The header includes information such as
230+ // the file name, size and permissions. Different versions of
231+ // tar added extra header fields.
232+ //
233+ // The file data is padded with nulls to a multiple of 512 bytes.
177234
178- var hdr = [ UInt8] ( repeating: 0 , count: 512 )
179-
180- // Construct a POSIX ustar header for the file
181- hdr. writeString ( filename, inField: name, withTermination: . null)
182- hdr. writeString ( octal6 ( 0o555 ) , inField: mode, withTermination: . spaceAndNull)
183- hdr. writeString ( octal6 ( 0o000000 ) , inField: uid, withTermination: . spaceAndNull)
184- hdr. writeString ( octal6 ( 0o000000 ) , inField: gid, withTermination: . spaceAndNull)
185- hdr. writeString ( octal11 ( filesize) , inField: size, withTermination: . space)
186- hdr. writeString ( octal11 ( 0 ) , inField: mtime, withTermination: . space)
187- hdr. writeString ( INIT_CHECKSUM, inField: chksum, withTermination: . none)
188- hdr. writeString ( REGTYPE, inField: typeflag, withTermination: . none)
189- hdr. writeString ( " " , inField: linkname, withTermination: . null)
190- hdr. writeString ( TMAGIC, inField: magic, withTermination: . null)
191- hdr. writeString ( TVERSION, inField: version, withTermination: . none)
192- hdr. writeString ( " " , inField: uname, withTermination: . null)
193- hdr. writeString ( " " , inField: gname, withTermination: . null)
194- hdr. writeString ( octal6 ( 0o000000 ) , inField: devmajor, withTermination: . spaceAndNull)
195- hdr. writeString ( octal6 ( 0o000000 ) , inField: devminor, withTermination: . spaceAndNull)
196- hdr. writeString ( " " , inField: prefix, withTermination: . null)
197-
198- // Fill in the checksum.
199- hdr. writeString ( octal6 ( checksum ( header: hdr) ) , inField: chksum, withTermination: . nullAndSpace)
200-
201- return hdr
235+ var bytes = [ UInt8] ( repeating: 0 , count: 512 )
236+
237+ // Construct a POSIX ustar header for the file
238+ bytes. writeString ( self . name, inField: Field . name, withTermination: . null)
239+ bytes. writeString ( octal6 ( self . mode) , inField: Field . mode, withTermination: . spaceAndNull)
240+ bytes. writeString ( octal6 ( self . uid) , inField: Field . uid, withTermination: . spaceAndNull)
241+ bytes. writeString ( octal6 ( self . gid) , inField: Field . gid, withTermination: . spaceAndNull)
242+ bytes. writeString ( octal11 ( self . size) , inField: Field . size, withTermination: . space)
243+ bytes. writeString ( octal11 ( self . mtime) , inField: Field . mtime, withTermination: . space)
244+ bytes. writeString ( INIT_CHECKSUM, inField: Field . chksum, withTermination: . none)
245+ bytes. writeString ( self . typeflag. rawValue, inField: Field . typeflag, withTermination: . none)
246+ bytes. writeString ( self . linkname, inField: Field . linkname, withTermination: . null)
247+ bytes. writeString ( TMAGIC, inField: Field . magic, withTermination: . null)
248+ bytes. writeString ( TVERSION, inField: Field . version, withTermination: . none)
249+ bytes. writeString ( self . uname, inField: Field . uname, withTermination: . null)
250+ bytes. writeString ( self . gname, inField: Field . gname, withTermination: . null)
251+ bytes. writeString ( octal6 ( self . devmajor) , inField: Field . devmajor, withTermination: . spaceAndNull)
252+ bytes. writeString ( octal6 ( self . devminor) , inField: Field . devminor, withTermination: . spaceAndNull)
253+ bytes. writeString ( self . prefix, inField: Field . prefix, withTermination: . null)
254+
255+ // Fill in the checksum.
256+ bytes. writeString ( octal6 ( Tar . checksum ( header: bytes) ) , inField: Field . chksum, withTermination: . nullAndSpace)
257+
258+ return bytes
259+ }
202260}
203261
204262let blockSize = 512
@@ -218,19 +276,19 @@ func padding(_ len: Int) -> Int {
218276/// - Returns: A tar archive containing the file
219277/// - Throws: If the filename is invalid
220278public func tar( _ bytes: [ UInt8 ] , filename: String = " app " ) throws -> [ UInt8 ] {
221- var hdr = try tarHeader ( filesize : bytes . count , filename : filename )
279+ var archive = try TarHeader ( name : filename , size : bytes . count ) . bytes
222280
223281 // Append the file data to the header
224- hdr . append ( contentsOf: bytes)
282+ archive . append ( contentsOf: bytes)
225283
226284 // Pad the file data to a multiple of 512 bytes
227285 let padding = [ UInt8] ( repeating: 0 , count: padding ( bytes. count) )
228- hdr . append ( contentsOf: padding)
286+ archive . append ( contentsOf: padding)
229287
230288 // Append the end of file marker
231289 let marker = [ UInt8] ( repeating: 0 , count: 2 * 512 )
232- hdr . append ( contentsOf: marker)
233- return hdr
290+ archive . append ( contentsOf: marker)
291+ return archive
234292}
235293
236294/// Creates a tar archive containing a single file
0 commit comments