Skip to content

Instantly share code, notes, and snippets.

@davepeck
Created February 1, 2011 17:20
Show Gist options
  • Save davepeck/806192 to your computer and use it in GitHub Desktop.
Save davepeck/806192 to your computer and use it in GitHub Desktop.
How to use libarchive to unextract on iOS
/* call processNextArchiveEntry in some kind of loop! */
- (int)processNextArchiveEntry:(struct archive *)archive
{
struct archive_entry *entry = NULL;
int result = ARCHIVE_OK;
result = archive_read_next_header(archive, &entry);
if (result == ARCHIVE_OK)
{
if (archive_entry_filetype(entry) == AE_IFDIR)
{
result = [self processDirectory:entry inArchive:archive];
}
else if (archive_entry_filetype(entry) == AE_IFREG)
{
result = [self processFile:entry inArchive:archive];
}
}
return result;
}
- (int)processDirectory:(struct archive_entry *)entry inArchive:(struct archive *)archive
{
NSString *path = nil;
NSError *error = nil;
BOOL exists, isDirectory;
BOOL ok = NO;
path = [self fullDestinationPathForEntry:entry];
exists = [fileManager fileExistsAtPath:path isDirectory:&isDirectory];
if (!exists)
{
ok = [fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error];
}
else if (exists && isDirectory)
{
// we allow users to re-download content, in which case this can happen.
ok = YES;
}
return ok ? ARCHIVE_OK : ARCHIVE_FATAL;
}
- (int)processFile:(struct archive_entry *)entry inArchive:(struct archive *)archive
{
NSFileHandle *file = nil;
NSString *path = nil;
BOOL ok = NO;
int result = ARCHIVE_FAILED;
path = [self fullDestinationPathForEntry:entry];
ok = [fileManager createFileAtPath:path contents:nil attributes:nil];
if (ok)
{
file = [NSFileHandle fileHandleForWritingAtPath:path];
}
if(file != nil)
{
result = archive_read_data_into_fd(archive, [file fileDescriptor]);
[file closeFile];
}
return result;
}
- (NSString *)fullDestinationPathForEntry:(struct archive_entry *)entry
{
NSString *path = nil;
const char *entry_path = NULL;
entry_path = archive_entry_pathname(entry);
if (entry_path != NULL)
{
path = [fileManager stringWithFileSystemRepresentation:entry_path length:strlen(entry_path)];
}
if (path != nil)
{
path = [tempDirectory stringByAppendingPathComponent:path];
}
return path;
}
/* this is the 'main' method of my "download & unarchive" operation. */
- (void)main
{
struct archive *archive;
int result;
// kick off downloading the archive file from my web site
[self beginDownload];
// initialize the libarchive reading structures
result = [self initArchive:&archive];
// here's the loop that both signals for progress updates and deals with next entries
while (result == ARCHIVE_OK && ![self isCancelled])
{
[self updateProgressForArchive:archive];
result = [self processNextArchiveEntry:archive];
}
// all good loops must come to an end
if (result == ARCHIVE_EOF)
{
result = archive_read_finish(archive);
}
// this is somewhat lame, but we _are_ in an NSOperation so it's polite
if ([self isCancelled])
{
NSLog(@"MyDownloadOperation was canceled");
[downloadThread cancel];
[[pipe fileHandleForReading] readDataToEndOfFile]; // Unblock the download thread
result = ARCHIVE_RETRY;
}
// everything worked!
if (result == ARCHIVE_OK)
{
[self moveContentFromStagingAreaToFinalArea]; // this is how my app rolls; yours may be different
}
else
{
[self removeTempDirectory]; // this is how my app rolls; yours may be different
}
}
- (void)beginDownload
{
pipe = [NSPipe new];
downloadThread = [[MyDownloadThread alloc] initWithURL:myDownloadUrl stream:[pipe fileHandleForWriting]];
[downloadThread start];
}
- (int)initArchive:(struct archive **)outArchive
{
struct archive *archive = NULL;
int result = ARCHIVE_OK;
archive = archive_read_new();
archive_read_support_compression_bzip2(archive);
archive_read_support_compression_gzip(archive);
archive_read_support_format_tar(archive);
archive_read_support_format_gnutar(archive);
result = archive_read_open_fd(archive, [[pipe fileHandleForReading] fileDescriptor], getpagesize());
if(result == ARCHIVE_OK && outArchive != NULL)
{
*outArchive = archive;
}
lastArchivePosition = 0;
return result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment