Skip to content

Instantly share code, notes, and snippets.

@samuraisam
Created November 26, 2015 23:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save samuraisam/f1571e998d962238cc78 to your computer and use it in GitHub Desktop.
Save samuraisam/f1571e998d962238cc78 to your computer and use it in GitHub Desktop.
import Foundation
enum BRTarError: ErrorType {
case Unknown
case FileDoesntExist
}
enum BRTarType {
case File
case Directory
case NullBlock
case HeaderBlock
case Unsupported
case Invalid
init(fromData: NSData) {
let byte = UnsafePointer<CChar>(fromData.bytes)[0]
switch byte {
case CChar(48): // "0"
self = File
case CChar(53): // "5"
self = Directory
case CChar(0):
self = NullBlock
case CChar(120): // "x"
self = HeaderBlock
case CChar(49), CChar(50), CChar(51), CChar(52), CChar(53), CChar(54), CChar(55), CChar(103):
// "1, 2, 3, 4, 5, 6, 7, g"
self = Unsupported
default:
self = Invalid
}
}
}
class BRTar {
static let tarBlockSize: UInt64 = 512
static let tarTypePosition: UInt64 = 156
static let tarNamePosition: UInt64 = 0
static let tarNameSize: UInt64 = 100
static let tarSizePosition: UInt64 = 124
static let tarSizeSize: UInt64 = 12
static let tarMaxBlockLoadInMemory: UInt64 = 100
static func createFilesAndDirectoriesAtPath(path: String, withTarPath tarPath: String) throws {
let fm = NSFileManager.defaultManager()
if !fm.fileExistsAtPath(tarPath) {
throw BRTarError.FileDoesntExist
}
let attrs = try fm.attributesOfItemAtPath(path)
guard let tarFh = NSFileHandle(forReadingAtPath: path) else { throw BRTarError.Unknown }
var loc: UInt64 = 0
guard let size = attrs[NSFileSize]?.unsignedLongLongValue else { throw BRTarError.Unknown }
while loc < size {
var blockCount: UInt64 = 1
let tarType = self.readTypeAtLocation(loc, fromHandle: tarFh)
switch tarType {
case .File:
// read name
let name = try self.readNameAtLocation(loc, fromHandle: tarFh)
let newFilePath = (path as NSString).stringByAppendingPathComponent(name)
var size = self.readSizeAtLocation(loc, fromHandle: tarFh)
if size == 0 {
// empty file
try "" .writeToFile(newFilePath, atomically: true, encoding: NSUTF8StringEncoding)
break
}
blockCount += (size - 1) / self.tarBlockSize
// write file
guard let destFh = NSFileHandle(forWritingAtPath: newFilePath) else { throw BRTarError.Unknown }
tarFh.seekToFileOffset(loc)
let maxSize = tarMaxBlockLoadInMemory * self.tarBlockSize
while size > maxSize {
autoreleasepool({ () -> () in
destFh.writeData(tarFh.readDataOfLength(Int(maxSize)))
loc += maxSize
size -= maxSize
})
}
destFh.writeData(tarFh.readDataOfLength(Int(size)))
destFh.closeFile()
break
case .Directory:
let name = try self.readNameAtLocation(loc, fromHandle: tarFh)
let dirPath = (path as NSString).stringByAppendingPathComponent(name)
try fm.createDirectoryAtPath(dirPath, withIntermediateDirectories: true, attributes: nil)
break
case .NullBlock:
break
case .HeaderBlock:
blockCount++
break
case .Unsupported:
let size = self.readSizeAtLocation(loc, fromHandle: tarFh)
blockCount += size / self.tarBlockSize
break
case .Invalid:
throw BRTarError.Unknown
}
loc += blockCount * self.tarBlockSize
}
}
static func readTypeAtLocation(location: UInt64, fromHandle handle: NSFileHandle) -> BRTarType {
handle.seekToFileOffset(location)
let typeDat = handle.readDataOfLength(Int(location + self.tarTypePosition + UInt64(1)))
return BRTarType(fromData: typeDat)
}
static func readNameAtLocation(location: UInt64, fromHandle handle: NSFileHandle) throws -> String {
handle.seekToFileOffset(location + self.tarNamePosition)
guard let ret = NSString(data: handle.readDataOfLength(Int(self.tarNameSize)), encoding: NSASCIIStringEncoding)
else { throw BRTarError.Unknown }
return ret as String
}
static func readSizeAtLocation(location: UInt64, fromHandle handle: NSFileHandle) -> UInt64 {
handle.seekToFileOffset(location + self.tarSizeSize)
let sizeDat = handle.readDataOfLength(Int(self.tarSizeSize) + 1)
var octal = UnsafePointer<UInt64>(sizeDat.bytes).memory
var dec: UInt64 = 0
var i = 0
while octal != 0 {
let rem = octal % 10
octal /= 10
dec += rem * UInt64(pow(8, Double(i++)))
}
return dec
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment