From b9ca5f83bdfe2f553267013bc4eba521743667e7 Mon Sep 17 00:00:00 2001 From: Roy Marmelstein Date: Tue, 12 Jan 2016 23:38:43 +0100 Subject: [PATCH] Zip progress --- Zip/Zip.swift | 239 ++++++++++++++++++++++++++++++++++++++++++- Zip/ZipHelpers.swift | 37 +++++++ 2 files changed, 274 insertions(+), 2 deletions(-) create mode 100644 Zip/ZipHelpers.swift diff --git a/Zip/Zip.swift b/Zip/Zip.swift index 40b4c54..43d6e83 100644 --- a/Zip/Zip.swift +++ b/Zip/Zip.swift @@ -8,8 +8,243 @@ import Foundation import minizip +import Darwin -class Zip { - func helloWorld() { +public enum ZipError: ErrorType { + case UnzipError + case FileError + + public var description: String { + switch self { + case .UnzipError: return NSLocalizedString("Failed to open zip file.", comment: "") + case .FileError: return NSLocalizedString("File error.", comment: "") + } + } +} + + +public class Zip { + + public func unzipFile(path: String, destination: String, overwrite: Bool, password: String?) throws { + guard let zip: zipFile = unzOpen(path) else { + throw ZipError.UnzipError + } + let fileAttributes = try NSFileManager.defaultManager().attributesOfItemAtPath(path) + let fileSize = fileAttributes[NSFileSize] + var currentPosition = 0.0 + var globalInfo: unz_global_info = unz_global_info(number_entry: 0, number_disk_with_CD: 0, size_comment: 0) + unzGetGlobalInfo(zip, &globalInfo) + // Begin unzipping + if unzGoToFirstFile(zip) != UNZ_OK { + throw ZipError.UnzipError + } + let canceled = false + var ret: Int32 = 0 + var crc_ret: Int32 = 0 + + let bufferSize = 4096 + var buffer = Array(count: bufferSize, repeatedValue: 0) + let fileManager = NSFileManager.defaultManager() + var directoriesModificationDates = NSMutableSet() + if let password = password where password.characters.count > 0 { + ret = unzOpenCurrentFilePassword(zip, password.cStringUsingEncoding(NSASCIIStringEncoding)!) + } + else { + ret = unzOpenCurrentFile(zip) + } + if ret != UNZ_OK { + throw ZipError.UnzipError + } + var fileInfo = unz_file_info() + memset(&fileInfo, 0, sizeof(unz_file_info)) + + ret = unzGetCurrentFileInfo(zip, &fileInfo, nil, 0, nil, 0, nil, 0) + if ret != UNZ_OK { + unzCloseCurrentFile(zip) + throw ZipError.UnzipError + } + + currentPosition = currentPosition + Double(fileInfo.compressed_size) + let fileNameSize = Int(fileInfo.size_filename) + 1 + let fileName = UnsafeMutablePointer.alloc(fileNameSize) + if fileName == nil { + throw ZipError.UnzipError + } + unzGetCurrentFileInfo(zip, &fileInfo, fileName, UInt(fileNameSize), nil, 0, nil, 0) + fileName[Int(fileInfo.size_filename)] = 0 + + var strPath = String.fromCString(fileName)! as NSString + var isDirectory = false + let fileInfoSizeFileName = Int(fileInfo.size_filename-1) + if (fileName[fileInfoSizeFileName] == "/".cStringUsingEncoding(NSUTF8StringEncoding)!.first! || fileName[fileInfoSizeFileName] == "\\".cStringUsingEncoding(NSUTF8StringEncoding)!.first!) { + isDirectory = true; + } + free(fileName) + + if (strPath.rangeOfCharacterFromSet(NSCharacterSet(charactersInString: "/\\")).location != NSNotFound) { + strPath = strPath.stringByReplacingOccurrencesOfString("\\", withString: "/") + } + let fullPath = (destination as NSString).stringByAppendingPathComponent(strPath as String) + // TODO: GET DOS DATE FROM FILEINFO + let modDate = NSDate() + let directoryAttributes = [NSFileCreationDate: modDate, NSFileModificationDate: modDate] + if isDirectory { + try fileManager.createDirectoryAtPath(fullPath, withIntermediateDirectories: true, attributes: directoryAttributes) + } + else { + try fileManager.createDirectoryAtPath((fullPath as NSString).stringByDeletingLastPathComponent, withIntermediateDirectories: true, attributes: directoryAttributes) + } + + if fileManager.fileExistsAtPath(fullPath) && !isDirectory && !overwrite { + unzCloseCurrentFile(zip) + ret = unzGoToNextFile(zip) + } + + var filePointer: UnsafeMutablePointer + filePointer = try ZIPFopen(fullPath) + let readBytes = unzReadCurrentFile(zip, &buffer, 4096) + fwrite(buffer, Int(readBytes), 1, filePointer) + if filePointer != nil { + if ((fullPath as NSString).pathExtension.lowercaseString == "zip") { + // nested zip + try unzipFile(fullPath, destination: (fullPath as NSString).stringByDeletingLastPathComponent, overwrite: overwrite, password: password) + } + } + fclose(filePointer) + crc_ret = unzCloseCurrentFile(zip) + if crc_ret == UNZ_CRCERROR { + throw ZipError.UnzipError + } + ret = unzGoToNextFile(zip) + currentPosition++ + } + // crc_ret = unzCloseCurrentFile( zip ); + // if (crc_ret == UNZ_CRCERROR) { + // //CRC ERROR + // return NO; + // } + // ret = unzGoToNextFile( zip ); + // + // // Message delegate + // if ([delegate respondsToSelector:@selector(zipArchiveDidUnzipFileAtIndex:totalFiles:archivePath:fileInfo:)]) { + // [delegate zipArchiveDidUnzipFileAtIndex:currentFileNumber totalFiles:(NSInteger)globalInfo.number_entry + // archivePath:path fileInfo:fileInfo]; + // } else if ([delegate respondsToSelector: @selector(zipArchiveDidUnzipFileAtIndex:totalFiles:archivePath:unzippedFilePath:)]) { + // [delegate zipArchiveDidUnzipFileAtIndex: currentFileNumber totalFiles: (NSInteger)globalInfo.number_entry + // archivePath:path unzippedFilePath: fullPath]; + // } + // + // currentFileNumber++; + // if (progressHandler) + // { + // progressHandler(strPath, fileInfo, currentFileNumber, globalInfo.number_entry); + // } + +// +// // Set the original datetime property +// if (fileInfo.dosDate != 0) { +// NSDate *orgDate = [[self class] _dateWithMSDOSFormat:(UInt32)fileInfo.dosDate]; +// NSDictionary *attr = @{NSFileModificationDate: orgDate}; +// +// if (attr) { +// if ([fileManager setAttributes:attr ofItemAtPath:fullPath error:nil] == NO) { +// // Can't set attributes +// NSLog(@"[SSZipArchive] Failed to set attributes - whilst setting modification date"); +// } +// } +// } +// +// // Set the original permissions on the file +// uLong permissions = fileInfo.external_fa >> 16; +// if (permissions != 0) { +// // Store it into a NSNumber +// NSNumber *permissionsValue = @(permissions); +// +// // Retrieve any existing attributes +// NSMutableDictionary *attrs = [[NSMutableDictionary alloc] initWithDictionary:[fileManager attributesOfItemAtPath:fullPath error:nil]]; +// +// // Set the value in the attributes dict +// attrs[NSFilePosixPermissions] = permissionsValue; +// +// // Update attributes +// if ([fileManager setAttributes:attrs ofItemAtPath:fullPath error:nil] == NO) { +// // Unable to set the permissions attribute +// NSLog(@"[SSZipArchive] Failed to set attributes - whilst setting permissions"); +// } +// +// #if !__has_feature(objc_arc) +// [attrs release]; +// #endif +// } +// } +// } +// else +// { +// // Assemble the path for the symbolic link +// NSMutableString* destinationPath = [NSMutableString string]; +// int bytesRead = 0; +// while((bytesRead = unzReadCurrentFile(zip, buffer, 4096)) > 0) +// { +// buffer[bytesRead] = (int)0; +// [destinationPath appendString:@((const char*)buffer)]; +// } +// +// // Create the symbolic link (making sure it stays relative if it was relative before) +// int symlinkError = symlink([destinationPath cStringUsingEncoding:NSUTF8StringEncoding], +// [fullPath cStringUsingEncoding:NSUTF8StringEncoding]); +// +// if(symlinkError != 0) +// { +// NSLog(@"Failed to create symbolic link at \"%@\" to \"%@\". symlink() error code: %d", fullPath, destinationPath, errno); +// } +// } +// +// crc_ret = unzCloseCurrentFile( zip ); +// if (crc_ret == UNZ_CRCERROR) { +// //CRC ERROR +// return NO; +// } +// ret = unzGoToNextFile( zip ); +// +// // Message delegate +// if ([delegate respondsToSelector:@selector(zipArchiveDidUnzipFileAtIndex:totalFiles:archivePath:fileInfo:)]) { +// [delegate zipArchiveDidUnzipFileAtIndex:currentFileNumber totalFiles:(NSInteger)globalInfo.number_entry +// archivePath:path fileInfo:fileInfo]; +// } else if ([delegate respondsToSelector: @selector(zipArchiveDidUnzipFileAtIndex:totalFiles:archivePath:unzippedFilePath:)]) { +// [delegate zipArchiveDidUnzipFileAtIndex: currentFileNumber totalFiles: (NSInteger)globalInfo.number_entry +// archivePath:path unzippedFilePath: fullPath]; +// } +// +// currentFileNumber++; +// if (progressHandler) +// { +// progressHandler(strPath, fileInfo, currentFileNumber, globalInfo.number_entry); +// } +// } +//} while(ret == UNZ_OK && ret != UNZ_END_OF_LIST_OF_FILE); +// +//// Close +//unzClose(zip); +// +//// The process of decompressing the .zip archive causes the modification times on the folders +//// to be set to the present time. So, when we are done, they need to be explicitly set. +//// set the modification date on all of the directories. +//NSError * err = nil; +//for (NSDictionary * d in directoriesModificationDates) { +// if (![[NSFileManager defaultManager] setAttributes:@{NSFileModificationDate: d[@"modDate"]} ofItemAtPath:d[@"path"] error:&err]) { +// NSLog(@"[SSZipArchive] Set attributes failed for directory: %@.", d[@"path"]); +// } +// if (err) { +// NSLog(@"[SSZipArchive] Error setting directory file modification date attribute: %@",err.localizedDescription); +// } +//} +// +//#if !__has_feature(objc_arc) +//[directoriesModificationDates release]; +//#endif +// +//// Message delegate +//} + } \ No newline at end of file diff --git a/Zip/ZipHelpers.swift b/Zip/ZipHelpers.swift new file mode 100644 index 0000000..a1fb528 --- /dev/null +++ b/Zip/ZipHelpers.swift @@ -0,0 +1,37 @@ +// +// ZipHelpers.swift +// Zip +// +// Created by Roy Marmelstein on 12/01/2016. +// Copyright © 2016 Roy Marmelstein. All rights reserved. +// + +import Foundation + +public enum FopenMode: String { + case Read = "r" + case Write = "w" +} + +public func ZIPFopen(path: String..., mode: FopenMode = .Read) throws -> UnsafeMutablePointer { + let path = joinPathComponents(path) + let f = fopen(path, mode.rawValue) + guard f != nil else { throw ZipError.FileError } + return f +} + +/** + Joins path components, unless a component is an absolute + path, in which case it discards all previous path components. + */ +func joinPathComponents(join: [String]) -> String { + guard join.count > 0 else { return "" } + + return join.dropFirst(1).reduce(join[0]) { + if $1.hasPrefix("/") { + return $1 + } else { + return $0 + "/" + $1 + } + } +} \ No newline at end of file -- 2.26.2