Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge commit 'bdash/delta-updates'

This is a big one, guys. Haven't tested the new features in the merged branch yet.
  • Loading branch information...
commit 9bc63ccf7e5f73cadbd72b4e836578841c6f0e1d 2 parents 2e3ed79 + ab23fed
Andy Matuschak andymatuschak authored
9 Configurations/ConfigBinaryDelta.xcconfig
View
@@ -0,0 +1,9 @@
+// BinaryDelta tool only
+
+PRODUCT_NAME = BinaryDelta
+GCC_PREFIX_HEADER =
+SDKROOT = macosx10.5
+MACOSX_DEPLOYMENT_TARGET[arch=i386] = 10.5
+MACOSX_DEPLOYMENT_TARGET[arch=ppc64] = 10.5
+MACOSX_DEPLOYMENT_TARGET[arch=ppc] = 10.5
+MACOSX_DEPLOYMENT_TARGET[arch=x86_64] = 10.5
5 Configurations/ConfigBinaryDeltaDebug.xcconfig
View
@@ -0,0 +1,5 @@
+#include "ConfigCommon.xcconfig"
+#include "ConfigCommonDebug.xcconfig"
+#include "ConfigBinaryDelta.xcconfig"
+
+OTHER_CFLAGS = -fsingle-precision-constant -DDEBUG
3  Configurations/ConfigBinaryDeltaRelease.xcconfig
View
@@ -0,0 +1,3 @@
+#include "ConfigCommon.xcconfig"
+#include "ConfigCommonRelease.xcconfig"
+#include "ConfigBinaryDelta.xcconfig"
1  Configurations/ConfigCommon.xcconfig
View
@@ -47,3 +47,4 @@ GCC_WARN_UNUSED_PARAMETER = YES
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES
GCC_WARN_UNDECLARED_SELECTOR = YES
WARNING_CFLAGS = -Wall -Wundef -Wendif-labels -Wpointer-arith -Wcast-align -Wwrite-strings -Wmissing-noreturn -Wmissing-format-attribute -Wpacked -Wredundant-decls -Winline -Wdisabled-optimization -Wformat=2 -Wlarger-than-32768 -Winvalid-pch
+LIBRARY_SEARCH_PATHS = $(inherited) $(SRCROOT)
29 License.txt
View
@@ -123,4 +123,31 @@ Original SSLeay License
* derivative of this code cannot be changed. i.e. this code cannot simply be
* copied and put under another distribution licence
* [including the GNU Public Licence.]
-*/
+*/
+
+License for bspatch.c and bsdiff.c, from bsdiff 4.3 (<http://www.daemonology.net/bsdiff/>:
+/*-
+ * Copyright 2003-2005 Colin Percival
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted providing that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
39 SUAppcast.m
View
@@ -9,6 +9,23 @@
#import "Sparkle.h"
#import "SUAppcast.h"
+@interface NSXMLElement (SUAppcastExtensions)
+- (NSDictionary *)attributesAsDictionary;
+@end
+
+@implementation NSXMLElement (SUAppcastExtensions)
+- (NSDictionary *)attributesAsDictionary
+{
+ NSEnumerator *attributeEnum = [[self attributes] objectEnumerator];
+ NSXMLNode *attribute;
+ NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
+
+ while ((attribute = [attributeEnum nextObject]))
+ [dictionary setObject:[attribute stringValue] forKey:[attribute name]];
+ return dictionary;
+}
+@end
+
@interface SUAppcast (Private)
- (void)reportError:(NSError *)error;
- (NSXMLNode *)bestNodeInNodes:(NSArray *)nodes;
@@ -121,12 +138,7 @@ - (void)downloadDidFinish:(NSURLDownload *)aDownload
if ([name isEqualToString:@"enclosure"])
{
// enclosure is flattened as a separate dictionary for some reason
- NSEnumerator *attributeEnum = [[(NSXMLElement *)node attributes] objectEnumerator];
- NSXMLNode *attribute;
- NSMutableDictionary *encDict = [NSMutableDictionary dictionary];
-
- while ((attribute = [attributeEnum nextObject]))
- [encDict setObject:[attribute stringValue] forKey:[attribute name]];
+ NSDictionary *encDict = [(NSXMLElement *)node attributesAsDictionary];
[dict setObject:encDict forKey:@"enclosure"];
}
@@ -137,8 +149,19 @@ - (void)downloadDidFinish:(NSURLDownload *)aDownload
if (date)
[dict setObject:date forKey:name];
}
- else if (name != nil)
- {
+ else if ([name isEqualToString:@"sparkle:deltas"])
+ {
+ NSMutableArray *deltas = [NSMutableArray array];
+ NSEnumerator *childEnum = [[node children] objectEnumerator];
+ NSXMLNode *child;
+ while ((child = [childEnum nextObject])) {
+ if ([[child name] isEqualToString:@"enclosure"])
+ [deltas addObject:[(NSXMLElement *)child attributesAsDictionary]];
+ }
+ [dict setObject:deltas forKey:@"deltas"];
+ }
+ else if (name != nil)
+ {
// add all other values as strings
[dict setObject:[[node stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] forKey:name];
}
6 SUAppcastItem.h
View
@@ -24,7 +24,9 @@
NSURL *fileURL;
NSString *versionString;
NSString *displayVersionString;
-
+
+ NSDictionary *deltaUpdates;
+
NSDictionary *propertiesDictionary;
}
@@ -41,6 +43,8 @@
- (NSURL *)fileURL;
- (NSString *)DSASignature;
- (NSString *)minimumSystemVersion;
+- (NSDictionary *)deltaUpdates;
+- (BOOL)isDeltaUpdate;
// Returns the dictionary provided in initWithDictionary; this might be useful later for extensions.
- (NSDictionary *)propertiesDictionary;
89 SUAppcastItem.m
View
@@ -101,6 +101,19 @@ - (void)setMinimumSystemVersion:(NSString *)systemVersionString
minimumSystemVersion = [systemVersionString copy];
}
+- (NSDictionary *)deltaUpdates { return [[deltaUpdates retain] autorelease]; }
+- (void)setDeltaUpdates:(NSDictionary *)updates
+{
+ if (deltaUpdates == updates) return;
+ [deltaUpdates release];
+ deltaUpdates = [updates copy];
+}
+
+- (BOOL)isDeltaUpdate
+{
+ return [[propertiesDictionary objectForKey:@"enclosure"] objectForKey:@"sparkle:deltaFrom"] != nil;
+}
+
- initWithDictionary:(NSDictionary *)dict
{
return [self initWithDictionary:dict failureReason:nil];
@@ -155,30 +168,58 @@ - (void)setMinimumSystemVersion:(NSString *)systemVersionString
return nil;
}
- propertiesDictionary = [[NSMutableDictionary alloc] initWithDictionary:dict];
- [self setTitle:[dict objectForKey:@"title"]];
- [self setDate:[dict objectForKey:@"pubDate"]];
- [self setItemDescription:[dict objectForKey:@"description"]];
-
- [self setFileURL:[NSURL URLWithString:[[enclosure objectForKey:@"url"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
- [self setDSASignature:[enclosure objectForKey:@"sparkle:dsaSignature"]];
-
- [self setVersionString:newVersion];
- [self setMinimumSystemVersion:[dict objectForKey:@"sparkle:minimumSystemVersion"]];
-
- NSString *shortVersionString = [enclosure objectForKey:@"sparkle:shortVersionString"];
- if (shortVersionString)
- [self setDisplayVersionString:shortVersionString];
- else
- [self setDisplayVersionString:[self versionString]];
-
- // Find the appropriate release notes URL.
- if ([dict objectForKey:@"sparkle:releaseNotesLink"])
- [self setReleaseNotesURL:[NSURL URLWithString:[dict objectForKey:@"sparkle:releaseNotesLink"]]];
- else if ([[self itemDescription] hasPrefix:@"http://"]) // if the description starts with http://, use that.
- [self setReleaseNotesURL:[NSURL URLWithString:[self itemDescription]]];
- else
- [self setReleaseNotesURL:nil];
+ if (enclosure == nil || [enclosure objectForKey:@"url"] == nil || newVersion == nil)
+ {
+ [self release];
+ self = nil;
+ }
+ else
+ {
+ propertiesDictionary = [[NSMutableDictionary alloc] initWithDictionary:dict];
+ [self setTitle:[dict objectForKey:@"title"]];
+ [self setDate:[dict objectForKey:@"pubDate"]];
+ [self setItemDescription:[dict objectForKey:@"description"]];
+
+ [self setFileURL:[NSURL URLWithString:[[enclosure objectForKey:@"url"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
+ [self setDSASignature:[enclosure objectForKey:@"sparkle:dsaSignature"]];
+
+ [self setVersionString:newVersion];
+ [self setMinimumSystemVersion:[dict objectForKey:@"sparkle:minimumSystemVersion"]];
+
+ NSString *shortVersionString = [enclosure objectForKey:@"sparkle:shortVersionString"];
+ if (shortVersionString)
+ [self setDisplayVersionString:shortVersionString];
+ else
+ [self setDisplayVersionString:[self versionString]];
+
+ // Find the appropriate release notes URL.
+ if ([dict objectForKey:@"sparkle:releaseNotesLink"])
+ [self setReleaseNotesURL:[NSURL URLWithString:[dict objectForKey:@"sparkle:releaseNotesLink"]]];
+ else if ([[self itemDescription] hasPrefix:@"http://"]) // if the description starts with http://, use that.
+ [self setReleaseNotesURL:[NSURL URLWithString:[self itemDescription]]];
+ else
+ [self setReleaseNotesURL:nil];
+
+ if ([dict objectForKey:@"deltas"])
+ {
+ NSMutableDictionary *deltas = [NSMutableDictionary dictionary];
+ NSArray *deltaDictionaries = [dict objectForKey:@"deltas"];
+ NSEnumerator *deltaDictionariesEnum = [deltaDictionaries objectEnumerator];
+ NSDictionary *deltaDictionary;
+ while ((deltaDictionary = [deltaDictionariesEnum nextObject]))
+ {
+ NSMutableDictionary *fakeAppCastDict = [dict mutableCopy];
+ [fakeAppCastDict removeObjectForKey:@"deltas"];
+ [fakeAppCastDict setObject:deltaDictionary forKey:@"enclosure"];
+ SUAppcastItem *deltaItem = [[[self class] alloc] initWithDictionary:fakeAppCastDict];
+ [fakeAppCastDict release];
+
+ [deltas setObject:deltaItem forKey:[deltaDictionary objectForKey:@"sparkle:deltaFrom"]];
+ [deltaItem release];
+ }
+ [self setDeltaUpdates:deltas];
+ }
+ }
}
return self;
}
2  SUBasicUpdateDriver.h
View
@@ -15,6 +15,7 @@
@class SUAppcastItem, SUUnarchiver, SUAppcast, SUUnarchiver, SUHost;
@interface SUBasicUpdateDriver : SUUpdateDriver {
SUAppcastItem *updateItem;
+ SUAppcastItem *nonDeltaUpdateItem;
NSURLDownload *download;
NSString *downloadPath;
@@ -42,6 +43,7 @@
- (void)extractUpdate;
- (void)unarchiverDidFinish:(SUUnarchiver *)ua;
- (void)unarchiverDidFail:(SUUnarchiver *)ua;
+- (void)failedToApplyDeltaUpdate;
- (void)installUpdate;
- (void)installerFinishedForHost:(SUHost *)host;
25 SUBasicUpdateDriver.m
View
@@ -91,6 +91,12 @@ - (void)appcastDidFinishLoading:(SUAppcast *)ac
do {
item = [updateEnumerator nextObject];
} while (item && ![self hostSupportsItem:item]);
+
+ SUAppcastItem *deltaUpdateItem = [[item deltaUpdates] objectForKey:[host version]];
+ if (deltaUpdateItem && [self hostSupportsItem:deltaUpdateItem]) {
+ nonDeltaUpdateItem = [item retain];
+ item = deltaUpdateItem;
+ }
}
updateItem = [item retain];
@@ -200,7 +206,7 @@ - (BOOL)download:(NSURLDownload *)download shouldDecodeSourceDataOfMIMEType:(NSS
- (void)extractUpdate
{
- SUUnarchiver *unarchiver = [SUUnarchiver unarchiverForPath:downloadPath];
+ SUUnarchiver *unarchiver = [SUUnarchiver unarchiverForPath:downloadPath updatingHost:host];
if (!unarchiver)
{
NSLog(@"Sparkle Error: No valid unarchiver for %@!", downloadPath);
@@ -212,6 +218,16 @@ - (void)extractUpdate
[unarchiver start];
}
+- (void)failedToApplyDeltaUpdate
+{
+ // When a delta update fails to apply we fall back on updating via a full install.
+ [updateItem release];
+ updateItem = nonDeltaUpdateItem;
+ nonDeltaUpdateItem = nil;
+
+ [self downloadUpdate];
+}
+
- (void)unarchiverDidFinish:(SUUnarchiver *)ua
{
if (ua) { CFRelease(ua); }
@@ -221,6 +237,12 @@ - (void)unarchiverDidFinish:(SUUnarchiver *)ua
- (void)unarchiverDidFail:(SUUnarchiver *)ua
{
if (ua) { CFRelease(ua); }
+
+ if ([updateItem isDeltaUpdate]) {
+ [self failedToApplyDeltaUpdate];
+ return;
+ }
+
[self abortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUUnarchivingError userInfo:[NSDictionary dictionaryWithObject:SULocalizedString(@"An error occurred while extracting the archive. Please try again later.", nil) forKey:NSLocalizedDescriptionKey]]];
}
@@ -328,6 +350,7 @@ - (void)abortUpdateWithError:(NSError *)error
- (void)dealloc
{
[updateItem release];
+ [nonDeltaUpdateItem release];
[download release];
[downloadPath release];
[relaunchPath release];
15 SUBinaryDeltaApply.h
View
@@ -0,0 +1,15 @@
+//
+// SUBinaryDeltaApply.h
+// Sparkle
+//
+// Created by Mark Rowe on 2009-06-01.
+// Copyright 2009 Mark Rowe. All rights reserved.
+//
+
+#ifndef SUBINARYDELTAAPPLY_H
+#define SUBINARYDELTAAPPLY_H
+
+@class NSString;
+int applyBinaryDelta(NSString *source, NSString *destination, NSString *patchFile);
+
+#endif
101 SUBinaryDeltaApply.m
View
@@ -0,0 +1,101 @@
+//
+// SUBinaryDeltaApply.m
+// Sparkle
+//
+// Created by Mark Rowe on 2009-06-01.
+// Copyright 2009 Mark Rowe. All rights reserved.
+//
+
+#import "SUBinaryDeltaApply.h"
+#import "SUBinaryDeltaCommon.h"
+#import <CommonCrypto/CommonDigest.h>
+#import <Foundation/Foundation.h>
+#import <bspatch.h>
+#import <stdio.h>
+#import <stdlib.h>
+#import <xar/xar.h>
+
+static void applyBinaryDeltaToFile(xar_t x, xar_file_t file, NSString *sourceFilePath, NSString *destinationFilePath)
+{
+ NSString *patchFile = temporaryFilename(@"apply-binary-delta");
+ xar_extract_tofile(x, file, [patchFile fileSystemRepresentation]);
+ const char *argv[] = {"/usr/bin/bspatch", [sourceFilePath fileSystemRepresentation], [destinationFilePath fileSystemRepresentation], [patchFile fileSystemRepresentation]};
+ bspatch(4, (char **)argv);
+ unlink([patchFile fileSystemRepresentation]);
+}
+
+int applyBinaryDelta(NSString *source, NSString *destination, NSString *patchFile)
+{
+ xar_t x = xar_open([patchFile UTF8String], READ);
+ if (!x) {
+ fprintf(stderr, "Unable to open %s. Giving up.\n", [patchFile UTF8String]);
+ return 1;
+ }
+
+ NSString *expectedBeforeHash = nil;
+ NSString *expectedAfterHash = nil;
+ xar_subdoc_t subdoc;
+ for (subdoc = xar_subdoc_first(x); subdoc; subdoc = xar_subdoc_next(subdoc)) {
+ if (!strcmp(xar_subdoc_name(subdoc), "binary-delta-attributes")) {
+ const char *value = 0;
+ xar_subdoc_prop_get(subdoc, "before-sha1", &value);
+ if (value)
+ expectedBeforeHash = [NSString stringWithUTF8String:value];
+
+ xar_subdoc_prop_get(subdoc, "after-sha1", &value);
+ if (value)
+ expectedAfterHash = [NSString stringWithUTF8String:value];
+ }
+ }
+
+ if (!expectedBeforeHash || !expectedAfterHash) {
+ fprintf(stderr, "Unable to find before-sha1 or after-sha1 metadata in delta. Giving up.\n");
+ return 1;
+ }
+
+ fprintf(stderr, "Verifying source... ");
+ NSString *beforeHash = hashOfTree(source);
+
+ if (![beforeHash isEqualToString:expectedBeforeHash]) {
+ fprintf(stderr, "Source doesn't have expected hash (%s != %s). Giving up.\n", [expectedBeforeHash UTF8String], [beforeHash UTF8String]);
+ return 1;
+ }
+
+ fprintf(stderr, "\nCopying files... ");
+ removeTree(destination);
+ copyTree(source, destination);
+
+ fprintf(stderr, "\nPatching... ");
+ xar_file_t file;
+ xar_iter_t iter = xar_iter_new();
+ for (file = xar_file_first(x, iter); file; file = xar_file_next(iter)) {
+ NSString *path = [NSString stringWithUTF8String:xar_get_path(file)];
+ NSString *sourceFilePath = [source stringByAppendingPathComponent:path];
+ NSString *destinationFilePath = [destination stringByAppendingPathComponent:path];
+
+ const char *value;
+ if (!xar_prop_get(file, "delete", &value) || !xar_prop_get(file, "delete-then-extract", &value)) {
+ removeTree(destinationFilePath);
+ if (!xar_prop_get(file, "delete", &value))
+ continue;
+ }
+
+ if (!xar_prop_get(file, "binary-delta", &value))
+ applyBinaryDeltaToFile(x, file, sourceFilePath, destinationFilePath);
+ else
+ xar_extract_tofile(x, file, [destinationFilePath fileSystemRepresentation]);
+ }
+ xar_close(x);
+
+ fprintf(stderr, "\nVerifying destination... ");
+ NSString *afterHash = hashOfTree(destination);
+
+ if (![afterHash isEqualToString:expectedAfterHash]) {
+ fprintf(stderr, "Destination doesn't have expected hash (%s != %s). Giving up.\n", [expectedAfterHash UTF8String], [afterHash UTF8String]);
+ removeTree(destination);
+ return 1;
+ }
+
+ fprintf(stderr, "\nDone!\n");
+ return 0;
+}
25 SUBinaryDeltaCommon.h
View
@@ -0,0 +1,25 @@
+//
+// SUBinaryDeltaCommon.h
+// Sparkle
+//
+// Created by Mark Rowe on 2009-06-01.
+// Copyright 2009 Mark Rowe. All rights reserved.
+//
+
+#ifndef SUBINARYDELTACOMMON_H
+#define SUBINARYDELTACOMMON_H
+
+#include <fts.h>
+
+@class NSString;
+@class NSData;
+
+extern int compareFiles(const FTSENT **a, const FTSENT **b);
+extern NSData *hashOfFile(FTSENT *ent);
+extern NSString *hashOfTree(NSString *path);
+extern void removeTree(NSString *path);
+extern void copyTree(NSString *source, NSString *dest);
+extern NSString *pathRelativeToDirectory(NSString *directory, NSString *path);
+NSString *temporaryFilename(NSString *base);
+
+#endif
150 SUBinaryDeltaCommon.m
View
@@ -0,0 +1,150 @@
+//
+// SUBinaryDeltaCommon.m
+// Sparkle
+//
+// Created by Mark Rowe on 2009-06-01.
+// Copyright 2009 Mark Rowe. All rights reserved.
+//
+
+#include "SUBinaryDeltaCommon.h"
+#include <CommonCrypto/CommonDigest.h>
+#include <Foundation/Foundation.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+int compareFiles(const FTSENT **a, const FTSENT **b)
+{
+ return strcoll((*a)->fts_name, (*b)->fts_name);
+}
+
+NSString *pathRelativeToDirectory(NSString *directory, NSString *path)
+{
+ NSUInteger directoryLength = [directory length];
+ if ([path hasPrefix:directory])
+ return [path substringFromIndex:directoryLength];
+
+ return path;
+}
+
+NSString *temporaryFilename(NSString *base)
+{
+ NSString *template = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.XXXXXXXXXX", base]];
+ char buffer[MAXPATHLEN];
+ strcpy(buffer, [template fileSystemRepresentation]);
+ return [NSString stringWithUTF8String:mktemp(buffer)];
+}
+
+static void _hashOfBuffer(unsigned char *hash, const char* buffer, size_t bufferLength)
+{
+ assert(bufferLength <= UINT32_MAX);
+ CC_SHA1_CTX hashContext;
+ CC_SHA1_Init(&hashContext);
+ CC_SHA1_Update(&hashContext, buffer, (CC_LONG)bufferLength);
+ CC_SHA1_Final(hash, &hashContext);
+}
+
+static void _hashOfFile(unsigned char* hash, FTSENT *ent)
+{
+ if (ent->fts_info == FTS_SL) {
+ char linkDestination[MAXPATHLEN + 1];
+ size_t linkDestinationLength = readlink(ent->fts_path, linkDestination, MAXPATHLEN);
+ if (linkDestinationLength < 0) {
+ perror("readlink");
+ return;
+ }
+
+ _hashOfBuffer(hash, linkDestination, linkDestinationLength);
+ return;
+ }
+
+ if (ent->fts_info == FTS_F) {
+ int fileDescriptor = open(ent->fts_path, O_RDONLY);
+ if (fileDescriptor == -1) {
+ perror("open");
+ return;
+ }
+
+ size_t fileSize = (size_t)ent->fts_statp->st_size;
+ void *buffer = mmap(0, fileSize, PROT_READ, MAP_FILE | MAP_PRIVATE, fileDescriptor, 0);
+ if (buffer == (void*)-1) {
+ close(fileDescriptor);
+ perror("mmap");
+ return;
+ }
+
+ _hashOfBuffer(hash, buffer, fileSize);
+ munmap(buffer, fileSize);
+ close(fileDescriptor);
+ return;
+ }
+
+ if (ent->fts_info == FTS_D)
+ memset(hash, 0xdd, CC_SHA1_DIGEST_LENGTH);
+}
+
+NSData *hashOfFile(FTSENT *ent)
+{
+ unsigned char fileHash[CC_SHA1_DIGEST_LENGTH];
+ _hashOfFile(fileHash, ent);
+ return [NSData dataWithBytes:fileHash length:CC_SHA1_DIGEST_LENGTH];
+}
+
+NSString *hashOfTree(NSString *path)
+{
+ const char *sourcePaths[] = {[path UTF8String], 0};
+ FTS *fts = fts_open((char* const*)sourcePaths, FTS_PHYSICAL | FTS_NOCHDIR, compareFiles);
+ if (!fts) {
+ perror("fts_open");
+ return nil;
+ }
+
+ CC_SHA1_CTX hashContext;
+ CC_SHA1_Init(&hashContext);
+
+ FTSENT *ent = 0;
+ while ((ent = fts_read(fts))) {
+ if (ent->fts_info != FTS_F && ent->fts_info != FTS_SL)
+ continue;
+
+ unsigned char fileHash[CC_SHA1_DIGEST_LENGTH];
+ _hashOfFile(fileHash, ent);
+ CC_SHA1_Update(&hashContext, fileHash, sizeof(fileHash));
+
+ NSString *relativePath = pathRelativeToDirectory(path, [NSString stringWithUTF8String:ent->fts_path]);
+ NSData *relativePathBytes = [relativePath dataUsingEncoding:NSUTF8StringEncoding];
+ CC_SHA1_Update(&hashContext, [relativePathBytes bytes], (uint32_t)[relativePathBytes length]);
+ }
+ fts_close(fts);
+
+ unsigned char hash[CC_SHA1_DIGEST_LENGTH];
+ CC_SHA1_Final(hash, &hashContext);
+
+ char hexHash[CC_SHA1_DIGEST_LENGTH * 2 + 1];
+ size_t i;
+ for (i = 0; i < CC_SHA1_DIGEST_LENGTH; i++)
+ sprintf(hexHash + i * 2, "%02x", hash[i]);
+
+ return [NSString stringWithUTF8String:hexHash];
+}
+
+void removeTree(NSString *path)
+{
+#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4
+ [[NSFileManager defaultManager] removeItemAtPath:path error:0];
+#else
+ [[NSFileManager defaultManager] removeFileAtPath:path handler:nil];
+#endif
+}
+
+void copyTree(NSString *source, NSString *dest)
+{
+#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4
+ [[NSFileManager defaultManager] copyItemAtPath:source toPath:dest error:0];
+#else
+ [[NSFileManager defaultManager] copyPath:source toPath:dest handler:nil];
+#endif
+}
269 SUBinaryDeltaTool.m
View
@@ -0,0 +1,269 @@
+//
+// SUBinaryDeltaTool.m
+// Sparkle
+//
+// Created by Mark Rowe on 2009-06-01.
+// Copyright 2009 Mark Rowe. All rights reserved.
+//
+
+#define _DARWIN_NO_64_BIT_INODE 1
+
+#include "SUBinaryDeltaCommon.h"
+#include "SUBinaryDeltaApply.h"
+#include <CommonCrypto/CommonDigest.h>
+#include <Foundation/Foundation.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <xar/xar.h>
+
+extern int bsdiff(int argc, const char **argv);
+
+@interface CreateBinaryDeltaOperation : NSOperation
+{
+ NSString *_relativePath;
+ NSString *_fromPath;
+ NSString *_toPath;
+ NSString *_resultPath;
+}
+- (id)initWithRelativePath:(NSString *)relativePath oldTree:(NSString *)oldTree newTree:(NSString *)newTree;
+
+- (NSString *)relativePath;
+- (NSString *)resultPath;
+@end
+
+@implementation CreateBinaryDeltaOperation
+
+- (id)initWithRelativePath:(NSString *)relativePath oldTree:(NSString *)oldTree newTree:(NSString *)newTree
+{
+ if ((self = [super init])) {
+ _relativePath = [relativePath copy];
+ _fromPath = [[oldTree stringByAppendingPathComponent:relativePath] retain];
+ _toPath = [[newTree stringByAppendingPathComponent:relativePath] retain];
+ }
+ return self;
+}
+
+- (NSString *)relativePath
+{
+ return [[_relativePath retain] autorelease];
+}
+
+- (NSString *)resultPath
+{
+ return [[_resultPath retain] autorelease];
+}
+
+- (void)main
+{
+ NSString *temporaryFile = temporaryFilename(@"BinaryDelta");
+ const char *argv[] = {"/usr/bin/bsdiff", [_fromPath fileSystemRepresentation], [_toPath fileSystemRepresentation], [temporaryFile fileSystemRepresentation]};
+ int result = bsdiff(4, argv);
+ if (!result)
+ _resultPath = [temporaryFile retain];
+}
+
+@end
+
+static NSDictionary *infoForFile(FTSENT *ent)
+{
+ NSData *hash = hashOfFile(ent);
+ NSNumber *size = nil;
+ if (ent->fts_info != FTS_D)
+ size = [NSNumber numberWithUnsignedLongLong:ent->fts_statp->st_size];
+ return [NSDictionary dictionaryWithObjectsAndKeys:hash, @"hash", [NSNumber numberWithUnsignedShort:ent->fts_info], @"type", size, @"size", nil];
+}
+
+static NSString *absolutePath(NSString *path)
+{
+ NSURL *url = [[[NSURL alloc] initFileURLWithPath:path] autorelease];
+ return [[url absoluteURL] path];
+}
+
+static NSString *temporaryPatchFile(NSString *patchFile)
+{
+ NSString *path = absolutePath(patchFile);
+ NSString *directory = [path stringByDeletingLastPathComponent];
+ NSString *file = [path lastPathComponent];
+ return [NSString stringWithFormat:@"%@/.%@.tmp", directory, file];
+}
+
+static BOOL shouldSkipDeltaCompression(NSString *key, NSDictionary* originalInfo, NSDictionary *newInfo)
+{
+ unsigned long long fileSize = [[newInfo objectForKey:@"size"] unsignedLongLongValue];
+ if (fileSize < 4096)
+ return YES;
+
+ if (!originalInfo)
+ return YES;
+
+ if ([[originalInfo objectForKey:@"type"] unsignedShortValue] != [[newInfo objectForKey:@"type"] unsignedShortValue])
+ return YES;
+
+ return NO;
+}
+
+static BOOL shouldDeleteThenExtract(NSString *key, NSDictionary* originalInfo, NSDictionary *newInfo)
+{
+ if (!originalInfo)
+ return NO;
+
+ if ([[originalInfo objectForKey:@"type"] unsignedShortValue] != [[newInfo objectForKey:@"type"] unsignedShortValue])
+ return YES;
+
+ return NO;
+}
+
+int main(int argc, char **argv)
+{
+ if (argc != 5) {
+usage:
+ fprintf(stderr, "Usage: BinaryDelta [create | apply] before-tree after-tree patch-file\n");
+ exit(1);
+ }
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ NSString *command = [NSString stringWithUTF8String:argv[1]];
+ NSString *oldPath = [NSString stringWithUTF8String:argv[2]];
+ NSString *newPath = [NSString stringWithUTF8String:argv[3]];
+ NSString *patchFile = [NSString stringWithUTF8String:argv[4]];
+
+ if ([command isEqualToString:@"apply"]) {
+ int result = applyBinaryDelta(oldPath, newPath, patchFile);
+ [pool drain];
+ return result;
+ }
+ if (![command isEqualToString:@"create"]) {
+ [pool drain];
+ goto usage;
+ }
+
+ NSMutableDictionary *originalTreeState = [NSMutableDictionary dictionary];
+
+ const char *sourcePaths[] = {[oldPath fileSystemRepresentation], 0};
+ FTS *fts = fts_open((char* const*)sourcePaths, FTS_PHYSICAL | FTS_NOCHDIR, compareFiles);
+ if (!fts) {
+ [pool drain];
+ perror("fts_open");
+ return 1;
+ }
+
+ fprintf(stderr, "Processing %s...", [oldPath UTF8String]);
+ FTSENT *ent = 0;
+ while ((ent = fts_read(fts))) {
+ if (ent->fts_info != FTS_F && ent->fts_info != FTS_SL && ent->fts_info != FTS_D)
+ continue;
+
+ NSString *key = pathRelativeToDirectory(oldPath, [NSString stringWithUTF8String:ent->fts_path]);
+ if (![key length])
+ continue;
+
+ NSDictionary *info = infoForFile(ent);
+ [originalTreeState setObject:info forKey:key];
+ }
+ fts_close(fts);
+
+ NSString *beforeHash = hashOfTree(oldPath);
+
+ NSMutableDictionary *newTreeState = [NSMutableDictionary dictionary];
+ for (NSString *key in originalTreeState)
+ {
+ [newTreeState setObject:[NSNull null] forKey:key];
+ }
+
+ fprintf(stderr, "\nProcessing %s... ", [newPath UTF8String]);
+ sourcePaths[0] = [newPath fileSystemRepresentation];
+ fts = fts_open((char* const*)sourcePaths, FTS_PHYSICAL | FTS_NOCHDIR, compareFiles);
+ if (!fts) {
+ [pool drain];
+ perror("fts_open");
+ return 1;
+ }
+
+
+ while ((ent = fts_read(fts))) {
+ if (ent->fts_info != FTS_F && ent->fts_info != FTS_SL && ent->fts_info != FTS_D)
+ continue;
+
+ NSString *key = pathRelativeToDirectory(newPath, [NSString stringWithUTF8String:ent->fts_path]);
+ if (![key length])
+ continue;
+
+ NSDictionary *info = infoForFile(ent);
+ NSDictionary *oldInfo = [originalTreeState objectForKey:key];
+
+ if ([info isEqual:oldInfo])
+ [newTreeState removeObjectForKey:key];
+ else
+ [newTreeState setObject:info forKey:key];
+ }
+ fts_close(fts);
+
+ NSString *afterHash = hashOfTree(newPath);
+
+ fprintf(stderr, "\nGenerating delta... ");
+
+ NSString *temporaryFile = temporaryPatchFile(patchFile);
+ xar_t x = xar_open([temporaryFile fileSystemRepresentation], WRITE);
+ xar_opt_set(x, XAR_OPT_COMPRESSION, "bzip2");
+ xar_subdoc_t attributes = xar_subdoc_new(x, "binary-delta-attributes");
+ xar_subdoc_prop_set(attributes, "before-sha1", [beforeHash UTF8String]);
+ xar_subdoc_prop_set(attributes, "after-sha1", [afterHash UTF8String]);
+
+ NSOperationQueue *deltaQueue = [[NSOperationQueue alloc] init];
+ NSMutableArray *deltaOperations = [NSMutableArray array];
+
+ NSArray *keys = [[newTreeState allKeys] sortedArrayUsingSelector:@selector(compare:)];
+ for (NSString* key in keys) {
+ id value = [newTreeState valueForKey:key];
+
+ if ([value isEqual:[NSNull null]]) {
+ xar_file_t newFile = xar_add_frombuffer(x, 0, [key fileSystemRepresentation], "", 1);
+ assert(newFile);
+ xar_prop_set(newFile, "delete", "true");
+ continue;
+ }
+
+ NSDictionary *originalInfo = [originalTreeState objectForKey:key];
+ NSDictionary *newInfo = [newTreeState objectForKey:key];
+ if (shouldSkipDeltaCompression(key, originalInfo, newInfo)) {
+ NSString *path = [newPath stringByAppendingPathComponent:key];
+ xar_file_t newFile = xar_add_frompath(x, 0, [key fileSystemRepresentation], [path fileSystemRepresentation]);
+ assert(newFile);
+ if (shouldDeleteThenExtract(key, originalInfo, newInfo))
+ xar_prop_set(newFile, "delete-then-extract", "true");
+ } else {
+ CreateBinaryDeltaOperation *operation = [[CreateBinaryDeltaOperation alloc] initWithRelativePath:key oldTree:oldPath newTree:newPath];
+ [deltaQueue addOperation:operation];
+ [deltaOperations addObject:operation];
+ [operation release];
+ }
+ }
+
+ [deltaQueue waitUntilAllOperationsAreFinished];
+ [deltaQueue release];
+
+ for (CreateBinaryDeltaOperation *operation in deltaOperations) {
+ NSString *resultPath = [operation resultPath];
+ xar_file_t newFile = xar_add_frompath(x, 0, [[operation relativePath] fileSystemRepresentation], [resultPath fileSystemRepresentation]);
+ assert(newFile);
+ xar_prop_set(newFile, "binary-delta", "true");
+ unlink([resultPath fileSystemRepresentation]);
+ }
+
+ xar_close(x);
+
+ unlink([patchFile fileSystemRepresentation]);
+ link([temporaryFile fileSystemRepresentation], [patchFile fileSystemRepresentation]);
+ unlink([temporaryFile fileSystemRepresentation]);
+ fprintf(stderr, "Done!\n");
+
+ [pool drain];
+ return 0;
+}
20 SUBinaryDeltaUnarchiver.h
View
@@ -0,0 +1,20 @@
+//
+// SUBinaryDeltaUnarchiver.h
+// Sparkle
+//
+// Created by Mark Rowe on 2009-06-03.
+// Copyright 2009 Mark Rowe. All rights reserved.
+//
+
+#ifndef SUBINARYDELTAUNARCHIVER_H
+#define SUBINARYDELTAUNARCHIVER_H
+
+#import <Cocoa/Cocoa.h>
+#import "SUUnarchiver.h"
+
+@interface SUBinaryDeltaUnarchiver : SUUnarchiver {
+}
+
+@end
+
+#endif
47 SUBinaryDeltaUnarchiver.m
View
@@ -0,0 +1,47 @@
+//
+// SUBinaryDeltaUnarchiver.m
+// Sparkle
+//
+// Created by Mark Rowe on 2009-06-03.
+// Copyright 2009 Mark Rowe. All rights reserved.
+//
+
+#import "SUBinaryDeltaUnarchiver.h"
+#import "SUBinaryDeltaApply.h"
+#import "SUUnarchiver_Private.h"
+#import "SUHost.h"
+#import "NTSynchronousTask.h"
+
+@implementation SUBinaryDeltaUnarchiver
+
++ (BOOL)canUnarchivePath:(NSString *)path
+{
+ return [[path pathExtension] isEqualToString:@"delta"];
+}
+
+- (void)applyBinaryDelta
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ NSString *sourcePath = [[updateHost bundle] bundlePath];
+ NSString *targetPath = [[archivePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:[sourcePath lastPathComponent]];
+
+ int result = applyBinaryDelta(sourcePath, targetPath, archivePath);
+ if (!result)
+ [self performSelectorOnMainThread:@selector(notifyDelegateOfSuccess) withObject:nil waitUntilDone:NO];
+ else
+ [self performSelectorOnMainThread:@selector(notifyDelegateOfFailure) withObject:nil waitUntilDone:NO];
+
+ [pool drain];
+}
+
+- (void)start
+{
+ [NSThread detachNewThreadSelector:@selector(applyBinaryDelta) toTarget:self withObject:nil];
+}
+
++ (void)load
+{
+ [self registerImplementation:self];
+}
+
+@end
5 SUUnarchiver.h
View
@@ -9,12 +9,15 @@
#ifndef SUUNARCHIVER_H
#define SUUNARCHIVER_H
+@class SUHost;
+
@interface SUUnarchiver : NSObject {
id delegate;
NSString *archivePath;
+ SUHost *updateHost;
}
-+ (SUUnarchiver *)unarchiverForPath:(NSString *)path;
++ (SUUnarchiver *)unarchiverForPath:(NSString *)path updatingHost:(SUHost *)host;
- (void)setDelegate:delegate;
- (void)start;
4 SUUnarchiver.m
View
@@ -13,14 +13,14 @@
@implementation SUUnarchiver
-+ (SUUnarchiver *)unarchiverForPath:(NSString *)path
++ (SUUnarchiver *)unarchiverForPath:(NSString *)path updatingHost:(SUHost *)host
{
NSEnumerator *implementationEnumerator = [[self unarchiverImplementations] objectEnumerator];
id current;
while ((current = [implementationEnumerator nextObject]))
{
if ([current canUnarchivePath:path])
- return [[[current alloc] initWithPath:path] autorelease];
+ return [[[current alloc] initWithPath:path host:host] autorelease];
}
return nil;
}
2  SUUnarchiver_Private.h
View
@@ -16,7 +16,7 @@
+ (void)registerImplementation:(Class)implementation;
+ (NSArray *)unarchiverImplementations;
+ (BOOL)canUnarchivePath:(NSString *)path;
-- (id)initWithPath:(NSString *)path;
+- (id)initWithPath:(NSString *)path host:(SUHost *)host;;
- (void)notifyDelegateOfExtractedLength:(NSNumber *)length;
- (void)notifyDelegateOfSuccess;
6 SUUnarchiver_Private.m
View
@@ -10,16 +10,20 @@
@implementation SUUnarchiver (Private)
-- (id)initWithPath:(NSString *)path
+- (id)initWithPath:(NSString *)path host:(SUHost *)host
{
if ((self = [super init]))
+ {
archivePath = [path copy];
+ updateHost = [host retain];
+ }
return self;
}
- (void)dealloc
{
[archivePath release];
+ [updateHost release];
[super dealloc];
}
177 Sparkle.xcodeproj/project.pbxproj
View
@@ -7,6 +7,25 @@
objects = {
/* Begin PBXBuildFile section */
+ 5D06E8D80FD68C8E005AE3F6 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* Sparkle.framework */; };
+ 5D06E8E90FD68CDB005AE3F6 /* bsdiff.c in Sources */ = {isa = PBXBuildFile; fileRef = 5D06E8DB0FD68CB9005AE3F6 /* bsdiff.c */; settings = {COMPILER_FLAGS = "-Wno-shorten-64-to-32"; }; };
+ 5D06E8EA0FD68CDB005AE3F6 /* SUBinaryDeltaTool.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D06E8E30FD68CC7005AE3F6 /* SUBinaryDeltaTool.m */; };
+ 5D06E8EB0FD68CE4005AE3F6 /* bspatch.c in Sources */ = {isa = PBXBuildFile; fileRef = 5D06E8DC0FD68CB9005AE3F6 /* bspatch.c */; settings = {COMPILER_FLAGS = "-Wno-shorten-64-to-32"; }; };
+ 5D06E8EC0FD68CE4005AE3F6 /* SUBinaryDeltaApply.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D06E8E00FD68CC7005AE3F6 /* SUBinaryDeltaApply.m */; };
+ 5D06E8ED0FD68CE4005AE3F6 /* SUBinaryDeltaCommon.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D06E8E20FD68CC7005AE3F6 /* SUBinaryDeltaCommon.m */; };
+ 5D06E8FD0FD68D6B005AE3F6 /* libbz2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D06E8FB0FD68D61005AE3F6 /* libbz2.dylib */; };
+ 5D06E8FF0FD68D6D005AE3F6 /* libbz2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D06E8FB0FD68D61005AE3F6 /* libbz2.dylib */; };
+ 5D06E9050FD68D7D005AE3F6 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; };
+ 5D06E9390FD69271005AE3F6 /* SUBinaryDeltaUnarchiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D06E9370FD69271005AE3F6 /* SUBinaryDeltaUnarchiver.h */; };
+ 5D06E93A0FD69271005AE3F6 /* SUBinaryDeltaUnarchiver.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D06E9380FD69271005AE3F6 /* SUBinaryDeltaUnarchiver.m */; };
+ 5D1AF58A0FD7678C0065DB48 /* libxar.1.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D1AF5890FD7678C0065DB48 /* libxar.1.dylib */; };
+ 5D1AF58B0FD7678C0065DB48 /* libxar.1.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D1AF5890FD7678C0065DB48 /* libxar.1.dylib */; };
+ 5D1AF5900FD767AD0065DB48 /* libxml2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D1AF58F0FD767AD0065DB48 /* libxml2.dylib */; };
+ 5D1AF5940FD767B90065DB48 /* libxml2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D1AF58F0FD767AD0065DB48 /* libxml2.dylib */; };
+ 5D1AF59A0FD767E50065DB48 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D1AF5990FD767E50065DB48 /* libz.dylib */; };
+ 5D1AF59C0FD768010065DB48 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D1AF59B0FD768010065DB48 /* libcrypto.dylib */; };
+ 5D1AF82B0FD768180065DB48 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D1AF5990FD767E50065DB48 /* libz.dylib */; };
+ 5D1AF82C0FD7681A0065DB48 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D1AF59B0FD768010065DB48 /* libcrypto.dylib */; };
610134730DD250470049ACDF /* SUUpdateDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 610134710DD250470049ACDF /* SUUpdateDriver.h */; settings = {ATTRIBUTES = (); }; };
610134740DD250470049ACDF /* SUUpdateDriver.m in Sources */ = {isa = PBXBuildFile; fileRef = 610134720DD250470049ACDF /* SUUpdateDriver.m */; };
6101347B0DD2541A0049ACDF /* SUProbingUpdateDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 610134790DD2541A0049ACDF /* SUProbingUpdateDriver.h */; settings = {ATTRIBUTES = (); }; };
@@ -19,6 +38,7 @@
610D5A1A0A1661B8004AAD9C /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* Sparkle.framework */; };
610D5A750A1670A4004AAD9C /* SUStatus.nib in Resources */ = {isa = PBXBuildFile; fileRef = 610D5A740A1670A4004AAD9C /* SUStatus.nib */; };
610EC1E00CF3A5FE00AE239E /* NTSynchronousTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 610EC1BF0CF3914D00AE239E /* NTSynchronousTask.m */; };
+ 611142E910FB1BE5009810AA /* bspatch.h in Headers */ = {isa = PBXBuildFile; fileRef = 611142E810FB1BE5009810AA /* bspatch.h */; };
61177A1F0D1112E900749C97 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6117796E0D1112E000749C97 /* IOKit.framework */; };
61180BCA0D64138900B4E0D1 /* SUWindowController.h in Headers */ = {isa = PBXBuildFile; fileRef = 61180BC80D64138900B4E0D1 /* SUWindowController.h */; settings = {ATTRIBUTES = (); }; };
61180BCB0D64138900B4E0D1 /* SUWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 61180BC90D64138900B4E0D1 /* SUWindowController.m */; };
@@ -101,6 +121,13 @@
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
+ 5D06E8D50FD68C86005AE3F6 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 8DC2EF4F0486A6940098B216;
+ remoteInfo = Sparkle;
+ };
61227AB90DB5C4BB00AB99EA /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
@@ -147,6 +174,24 @@
/* Begin PBXFileReference section */
0867D69BFE84028FC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
0867D6A5FE840307C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
+ 5D06E8D00FD68C7C005AE3F6 /* BinaryDelta */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = BinaryDelta; sourceTree = BUILT_PRODUCTS_DIR; };
+ 5D06E8DB0FD68CB9005AE3F6 /* bsdiff.c */ = {isa = PBXFileReference; comments = "-Wno-shorten-64-to-32"; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bsdiff.c; sourceTree = "<group>"; };
+ 5D06E8DC0FD68CB9005AE3F6 /* bspatch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bspatch.c; sourceTree = "<group>"; };
+ 5D06E8DF0FD68CC7005AE3F6 /* SUBinaryDeltaApply.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUBinaryDeltaApply.h; sourceTree = "<group>"; };
+ 5D06E8E00FD68CC7005AE3F6 /* SUBinaryDeltaApply.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUBinaryDeltaApply.m; sourceTree = "<group>"; };
+ 5D06E8E10FD68CC7005AE3F6 /* SUBinaryDeltaCommon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUBinaryDeltaCommon.h; sourceTree = "<group>"; };
+ 5D06E8E20FD68CC7005AE3F6 /* SUBinaryDeltaCommon.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUBinaryDeltaCommon.m; sourceTree = "<group>"; };
+ 5D06E8E30FD68CC7005AE3F6 /* SUBinaryDeltaTool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUBinaryDeltaTool.m; sourceTree = "<group>"; };
+ 5D06E8F10FD68D21005AE3F6 /* ConfigBinaryDelta.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ConfigBinaryDelta.xcconfig; sourceTree = "<group>"; };
+ 5D06E8F20FD68D21005AE3F6 /* ConfigBinaryDeltaDebug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ConfigBinaryDeltaDebug.xcconfig; sourceTree = "<group>"; };
+ 5D06E8F30FD68D21005AE3F6 /* ConfigBinaryDeltaRelease.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ConfigBinaryDeltaRelease.xcconfig; sourceTree = "<group>"; };
+ 5D06E8FB0FD68D61005AE3F6 /* libbz2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libbz2.dylib; path = /usr/lib/libbz2.dylib; sourceTree = "<absolute>"; };
+ 5D06E9370FD69271005AE3F6 /* SUBinaryDeltaUnarchiver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUBinaryDeltaUnarchiver.h; sourceTree = "<group>"; };
+ 5D06E9380FD69271005AE3F6 /* SUBinaryDeltaUnarchiver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUBinaryDeltaUnarchiver.m; sourceTree = "<group>"; };
+ 5D1AF5890FD7678C0065DB48 /* libxar.1.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libxar.1.dylib; path = /usr/lib/libxar.1.dylib; sourceTree = "<absolute>"; };
+ 5D1AF58F0FD767AD0065DB48 /* libxml2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libxml2.dylib; path = /usr/lib/libxml2.dylib; sourceTree = "<absolute>"; };
+ 5D1AF5990FD767E50065DB48 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = /usr/lib/libz.dylib; sourceTree = "<absolute>"; };
+ 5D1AF59B0FD768010065DB48 /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.dylib; path = /usr/lib/libcrypto.dylib; sourceTree = "<absolute>"; };
610134710DD250470049ACDF /* SUUpdateDriver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUUpdateDriver.h; sourceTree = "<group>"; };
610134720DD250470049ACDF /* SUUpdateDriver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUUpdateDriver.m; sourceTree = "<group>"; };
610134790DD2541A0049ACDF /* SUProbingUpdateDriver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUProbingUpdateDriver.h; sourceTree = "<group>"; };
@@ -160,6 +205,7 @@
610D5A740A1670A4004AAD9C /* SUStatus.nib */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; path = SUStatus.nib; sourceTree = "<group>"; };
610EC1BF0CF3914D00AE239E /* NTSynchronousTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NTSynchronousTask.m; sourceTree = "<group>"; };
610EC1C00CF3914D00AE239E /* NTSynchronousTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NTSynchronousTask.h; sourceTree = "<group>"; };
+ 611142E810FB1BE5009810AA /* bspatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bspatch.h; sourceTree = "<group>"; };
61131A050F846CE600E97AF6 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Sparkle.strings; sourceTree = "<group>"; };
61131A090F846D0A00E97AF6 /* zh_CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh_CN; path = zh_CN.lproj/Sparkle.strings; sourceTree = "<group>"; };
61131A0A0F846D1100E97AF6 /* zh_TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh_TW; path = zh_TW.lproj/Sparkle.strings; sourceTree = "<group>"; };
@@ -318,6 +364,20 @@
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
+ 5D06E8CE0FD68C7C005AE3F6 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 5D06E8D80FD68C8E005AE3F6 /* Sparkle.framework in Frameworks */,
+ 5D06E8FF0FD68D6D005AE3F6 /* libbz2.dylib in Frameworks */,
+ 5D06E9050FD68D7D005AE3F6 /* Foundation.framework in Frameworks */,
+ 5D1AF58B0FD7678C0065DB48 /* libxar.1.dylib in Frameworks */,
+ 5D1AF5900FD767AD0065DB48 /* libxml2.dylib in Frameworks */,
+ 5D1AF59A0FD767E50065DB48 /* libz.dylib in Frameworks */,
+ 5D1AF59C0FD768010065DB48 /* libcrypto.dylib in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
612279D60DB5470200AB99EA /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -346,6 +406,11 @@
61177A1F0D1112E900749C97 /* IOKit.framework in Frameworks */,
FAEFA2F70D94AA7500472538 /* Foundation.framework in Frameworks */,
FAEFA2F80D94AA7900472538 /* AppKit.framework in Frameworks */,
+ 5D06E8FD0FD68D6B005AE3F6 /* libbz2.dylib in Frameworks */,
+ 5D1AF58A0FD7678C0065DB48 /* libxar.1.dylib in Frameworks */,
+ 5D1AF5940FD767B90065DB48 /* libxml2.dylib in Frameworks */,
+ 5D1AF82B0FD768180065DB48 /* libz.dylib in Frameworks */,
+ 5D1AF82C0FD7681A0065DB48 /* libcrypto.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -367,6 +432,7 @@
61B5F90209C4CEE200B25A18 /* Sparkle Test App.app */,
DAAEFC960DA571DF0051E0D0 /* relaunch */,
612279D90DB5470200AB99EA /* Sparkle Unit Tests.octest */,
+ 5D06E8D00FD68C7C005AE3F6 /* BinaryDelta */,
);
name = Products;
sourceTree = "<group>";
@@ -398,6 +464,11 @@
61B5F8F609C4CEB300B25A18 /* Security.framework */,
0867D6A5FE840307C02AAC07 /* AppKit.framework */,
0867D69BFE84028FC02AAC07 /* Foundation.framework */,
+ 5D06E8FB0FD68D61005AE3F6 /* libbz2.dylib */,
+ 5D1AF58F0FD767AD0065DB48 /* libxml2.dylib */,
+ 5D1AF5890FD7678C0065DB48 /* libxar.1.dylib */,
+ 5D1AF5990FD767E50065DB48 /* libz.dylib */,
+ 5D1AF59B0FD768010065DB48 /* libcrypto.dylib */,
);
name = "Apple Frameworks and Libraries";
sourceTree = "<group>";
@@ -417,9 +488,27 @@
name = "Framework Resources";
sourceTree = "<group>";
};
+ 5D06E8D90FD68C95005AE3F6 /* Binary Delta */ = {
+ isa = PBXGroup;
+ children = (
+ 5D06E8DB0FD68CB9005AE3F6 /* bsdiff.c */,
+ 5D06E8DC0FD68CB9005AE3F6 /* bspatch.c */,
+ 5D06E8DF0FD68CC7005AE3F6 /* SUBinaryDeltaApply.h */,
+ 5D06E8E00FD68CC7005AE3F6 /* SUBinaryDeltaApply.m */,
+ 5D06E8E10FD68CC7005AE3F6 /* SUBinaryDeltaCommon.h */,
+ 5D06E8E20FD68CC7005AE3F6 /* SUBinaryDeltaCommon.m */,
+ 5D06E8E30FD68CC7005AE3F6 /* SUBinaryDeltaTool.m */,
+ 5D06E9370FD69271005AE3F6 /* SUBinaryDeltaUnarchiver.h */,
+ 5D06E9380FD69271005AE3F6 /* SUBinaryDeltaUnarchiver.m */,
+ 611142E810FB1BE5009810AA /* bspatch.h */,
+ );
+ name = "Binary Delta";
+ sourceTree = "<group>";
+ };
6101354A0DD25B7F0049ACDF /* Unarchiving */ = {
isa = PBXGroup;
children = (
+ 5D06E8D90FD68C95005AE3F6 /* Binary Delta */,
61299A8B09CA790200B7442F /* SUUnarchiver.h */,
61299A8C09CA790200B7442F /* SUUnarchiver.m */,
6102FE590E08C7EC00F85D09 /* SUUnarchiver_Private.h */,
@@ -572,6 +661,9 @@
FA1941C40D94A6EA00DD942E /* Configurations */ = {
isa = PBXGroup;
children = (
+ 5D06E8F10FD68D21005AE3F6 /* ConfigBinaryDelta.xcconfig */,
+ 5D06E8F20FD68D21005AE3F6 /* ConfigBinaryDeltaDebug.xcconfig */,
+ 5D06E8F30FD68D21005AE3F6 /* ConfigBinaryDeltaRelease.xcconfig */,
FA1941D00D94A70100DD942E /* ConfigCommon.xcconfig */,
FA1941CF0D94A70100DD942E /* ConfigCommonDebug.xcconfig */,
FA1941CC0D94A70100DD942E /* ConfigCommonRelease.xcconfig */,
@@ -634,12 +726,32 @@
6102FE5B0E08C7EC00F85D09 /* SUUnarchiver_Private.h in Headers */,
61EF67590E25C5B400F754E0 /* SUHost.h in Headers */,
61CFB3290E385186007A1735 /* Sparkle.pch in Headers */,
+ 5D06E9390FD69271005AE3F6 /* SUBinaryDeltaUnarchiver.h in Headers */,
+ 611142E910FB1BE5009810AA /* bspatch.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
+ 5D06E8CF0FD68C7C005AE3F6 /* BinaryDelta */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 5D06E8DA0FD68C95005AE3F6 /* Build configuration list for PBXNativeTarget "BinaryDelta" */;
+ buildPhases = (
+ 5D06E8CD0FD68C7C005AE3F6 /* Sources */,
+ 5D06E8CE0FD68C7C005AE3F6 /* Frameworks */,
+ 5D06E90D0FD68DA3005AE3F6 /* Fix Install Name */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 5D06E8D60FD68C86005AE3F6 /* PBXTargetDependency */,
+ );
+ name = BinaryDelta;
+ productName = BinaryDelta;
+ productReference = 5D06E8D00FD68C7C005AE3F6 /* BinaryDelta */;
+ productType = "com.apple.product-type.tool";
+ };
612279D80DB5470200AB99EA /* Sparkle Unit Tests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 612279DD0DB5470300AB99EA /* Build configuration list for PBXNativeTarget "Sparkle Unit Tests" */;
@@ -771,6 +883,7 @@
61B5F90109C4CEE200B25A18 /* Sparkle Test App */,
DAAEFC950DA571DF0051E0D0 /* relaunch tool */,
612279D80DB5470200AB99EA /* Sparkle Unit Tests */,
+ 5D06E8CF0FD68C7C005AE3F6 /* BinaryDelta */,
);
};
/* End PBXProject section */
@@ -812,6 +925,21 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
+ 5D06E90D0FD68DA3005AE3F6 /* Fix Install Name */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Fix Install Name";
+ outputPaths = (
+ "$(CONFIGURATION_BUILD_DIR)/BinaryDelta",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "install_name_tool -change \"@loader_path/../Frameworks/Sparkle.framework/Versions/A/Sparkle\" \"@loader_path/Sparkle.framework/Versions/A/Sparkle\" \"${CONFIGURATION_BUILD_DIR}/BinaryDelta\"";
+ };
612279D70DB5470200AB99EA /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -872,6 +1000,15 @@
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
+ 5D06E8CD0FD68C7C005AE3F6 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 5D06E8E90FD68CDB005AE3F6 /* bsdiff.c in Sources */,
+ 5D06E8EA0FD68CDB005AE3F6 /* SUBinaryDeltaTool.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
612279D50DB5470200AB99EA /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -923,6 +1060,10 @@
6102FE5C0E08C7EC00F85D09 /* SUUnarchiver_Private.m in Sources */,
61D85D6D0E10B2ED00F9B4A9 /* SUPipedUnarchiver.m in Sources */,
61EF67560E25B58D00F754E0 /* SUHost.m in Sources */,
+ 5D06E8EB0FD68CE4005AE3F6 /* bspatch.c in Sources */,
+ 5D06E8EC0FD68CE4005AE3F6 /* SUBinaryDeltaApply.m in Sources */,
+ 5D06E8ED0FD68CE4005AE3F6 /* SUBinaryDeltaCommon.m in Sources */,
+ 5D06E93A0FD69271005AE3F6 /* SUBinaryDeltaUnarchiver.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -937,6 +1078,11 @@
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
+ 5D06E8D60FD68C86005AE3F6 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 8DC2EF4F0486A6940098B216 /* Sparkle */;
+ targetProxy = 5D06E8D50FD68C86005AE3F6 /* PBXContainerItemProxy */;
+ };
61227ABA0DB5C4BB00AB99EA /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 612279D80DB5470200AB99EA /* Sparkle Unit Tests */;
@@ -1104,6 +1250,27 @@
};
name = Release;
};
+ 5D06E8D20FD68C7D005AE3F6 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 5D06E8F20FD68D21005AE3F6 /* ConfigBinaryDeltaDebug.xcconfig */;
+ buildSettings = {
+ };
+ name = Debug;
+ };
+ 5D06E8D30FD68C7D005AE3F6 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 5D06E8F30FD68D21005AE3F6 /* ConfigBinaryDeltaRelease.xcconfig */;
+ buildSettings = {
+ };
+ name = Release;
+ };
+ 5D06E8D40FD68C7D005AE3F6 /* Release (GC dual-mode; 10.5-only) */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 5D06E8F30FD68D21005AE3F6 /* ConfigBinaryDeltaRelease.xcconfig */;
+ buildSettings = {
+ };
+ name = "Release (GC dual-mode; 10.5-only)";
+ };
61072EAD0DF263BD008FE88B /* Release (GC dual-mode; 10.5-only) */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -1204,6 +1371,16 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ 5D06E8DA0FD68C95005AE3F6 /* Build configuration list for PBXNativeTarget "BinaryDelta" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 5D06E8D20FD68C7D005AE3F6 /* Debug */,
+ 5D06E8D30FD68C7D005AE3F6 /* Release */,
+ 5D06E8D40FD68C7D005AE3F6 /* Release (GC dual-mode; 10.5-only) */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
612279DD0DB5470300AB99EA /* Build configuration list for PBXNativeTarget "Sparkle Unit Tests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
522 bsdiff.c
View
@@ -0,0 +1,522 @@
+/*-
+ * Copyright 2003 - 2005 Colin Percival
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted providing that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if 0
+__FBSDID("$FreeBSD: src/usr.bin/bsdiff/bsdiff/bsdiff.c, v 1.1 2005/08/06 01:59:05 cperciva Exp $");
+#endif
+
+#include <sys/types.h>
+
+#include <bzlib.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define MIN(x, y) (((x)<(y)) ? (x) : (y))
+
+static void split(off_t *I, off_t *V, off_t start, off_t len, off_t h)
+{
+ off_t i, j, k, x, tmp, jj, kk;
+
+ if (len < 16) {
+ for (k = start; k < start + len; k += j) {
+ j = 1; x = V[I[k] + h];
+ for (i = 1; k + i < start + len; i++) {
+ if (V[I[k + i] + h] < x) {
+ x = V[I[k + i] + h];
+ j = 0;
+ };
+ if (V[I[k + i] + h] == x) {
+ tmp = I[k + j]; I[k + j] = I[k + i]; I[k + i] = tmp;
+ j++;
+ };
+ };
+ for (i = 0; i < j; i++)
+ V[I[k + i]] = k + j - 1;
+ if (j == 1)
+ I[k] = -1;
+ };
+ return;
+ };
+
+ x = V[I[start + len/2] + h];
+ jj = 0; kk = 0;
+ for (i = start; i < start + len; i++) {
+ if (V[I[i] + h] < x)
+ jj++;
+ if (V[I[i] + h] == x)
+ kk++;
+ };
+ jj += start; kk += jj;
+
+ i = start; j = 0; k = 0;
+ while (i < jj) {
+ if (V[I[i] + h] < x) {
+ i++;
+ } else if (V[I[i] + h] == x) {
+ tmp = I[i]; I[i] = I[jj + j]; I[jj + j] = tmp;
+ j++;
+ } else {
+ tmp = I[i]; I[i] = I[kk + k]; I[kk + k] = tmp;
+ k++;
+ };
+ };
+
+ while (jj + j < kk) {
+ if (V[I[jj + j] + h] == x) {
+ j++;
+ } else {
+ tmp = I[jj + j]; I[jj + j] = I[kk + k]; I[kk + k] = tmp;
+ k++;
+ };
+ };
+
+ if (jj > start)
+ split(I, V, start, jj - start, h);
+
+ for (i = 0; i < kk - jj; i++)
+ V[I[jj + i]] = kk - 1;
+ if (jj == kk - 1)
+ I[jj] = -1;
+
+ if (start + len > kk)
+ split(I, V, kk, start + len - kk, h);
+}
+
+/* qsufsort(I, V, old, oldsize)
+ *
+ * Computes the suffix sort of the string at 'old' and stores the resulting
+ * indices in 'I', using 'V' as a temporary array for the computation. */
+static void qsufsort(off_t *I, off_t *V, u_char *old, off_t oldsize)
+{
+ off_t buckets[256];
+ off_t i, h, len;
+
+ /* count number of each byte */
+ for (i = 0; i < 256; i++)
+ buckets[i] = 0;
+ for (i = 0; i < oldsize; i++)
+ buckets[old[i]]++;
+ /* make buckets cumulative */
+ for (i = 1; i < 256; i++)
+ buckets[i] += buckets[i - 1];
+ /* shift right by one */
+ for (i = 255; i > 0; i--)
+ buckets[i] = buckets[i - 1];
+ buckets[0] = 0;
+ /* at this point, buckets[c] is the number of bytes in the old file with
+ * value less than c. */
+
+ /* set up the sort order of the suffixes based solely on the first
+ * character */
+ for (i = 0; i < oldsize; i++)
+ I[++buckets[old[i]]] = i;
+ I[0] = oldsize;
+ /* ? */
+ for (i = 0; i < oldsize; i++)
+ V[i] = buckets[old[i]];
+ V[oldsize] = 0;
+ /* forward any entries in the ordering which have the same initial
+ * character */
+ for (i = 1; i < 256; i++) {
+ if (buckets[i] == buckets[i - 1] + 1)
+ I[buckets[i]] = -1;
+ }
+ I[0] = -1;
+
+ for (h = 1; I[0] != -(oldsize + 1); h += h) {
+ len = 0;
+ for (i = 0; i < oldsize + 1;) {
+ if (I[i] < 0) {
+ len -= I[i];
+ i -= I[i];
+ } else {
+ if (len)
+ I[i - len] = -len;
+ len = V[I[i]] + 1 - i;
+ split(I, V, i, len, h);
+ i += len;
+ len = 0;
+ }
+ }
+ if (len)
+ I[i - len] = -len;
+ };
+
+ for (i = 0; i < oldsize + 1; i++) I[V[i]] = i;
+}
+
+/* matchlen(old, oldsize, new, newsize)
+ *
+ * Returns the length of the longest common prefix between 'old' and 'new'. */
+static off_t matchlen(u_char *old, off_t oldsize, u_char *new, off_t newsize)
+{
+ off_t i;
+
+ for (i = 0; (i < oldsize) && (i < newsize); i++)
+ {
+ if (old[i] != new[i])
+ break;
+ }
+
+ return i;
+}
+
+/* search(I, old, oldsize, new, newsize, st, en, pos)
+ *
+ * Searches for the longest prefix of 'new' that occurs in 'old', stores its
+ * offset in '*pos', and returns its length. 'I' should be the suffix sort of
+ * 'old', and 'st' and 'en' are the lowest and highest indices in the suffix
+ * sort to consider. If you're searching all suffixes, 'st = 0' and 'en =
+ * oldsize - 1'. */
+static off_t search(off_t *I, u_char *old, off_t oldsize,
+ u_char *new, off_t newsize, off_t st, off_t en, off_t *pos)
+{
+ off_t x, y;
+
+ if (en - st < 2) {
+ x = matchlen(old + I[st], oldsize - I[st], new, newsize);
+ y = matchlen(old + I[en], oldsize - I[en], new, newsize);
+
+ if (x > y) {
+ *pos = I[st];
+ return x;
+ } else {
+ *pos = I[en];
+ return y;
+ }
+ }
+
+ x = st + (en - st)/2;
+ if (memcmp(old + I[x], new, MIN(oldsize - I[x], newsize)) < 0) {
+ return search(I, old, oldsize, new, newsize, x, en, pos);
+ } else {
+ return search(I, old, oldsize, new, newsize, st, x, pos);
+ };
+}
+
+/* offtout(x, buf)
+ *
+ * Writes the off_t 'x' portably to the array 'buf'. */
+static void offtout(off_t x, u_char *buf)
+{
+ off_t y;
+
+ if (x < 0)
+ y = -x;
+ else
+ y = x;
+
+ buf[0] = y % 256;
+ y -= buf[0];
+ y = y/256; buf[1] = y%256; y -= buf[1];
+ y = y/256; buf[2] = y%256; y -= buf[2];
+ y = y/256; buf[3] = y%256; y -= buf[3];
+ y = y/256; buf[4] = y%256; y -= buf[4];
+ y = y/256; buf[5] = y%256; y -= buf[5];
+ y = y/256; buf[6] = y%256; y -= buf[6];
+ y = y/256; buf[7] = y%256;
+
+ if (x < 0)
+ buf[7] |= 0x80;
+}
+
+int bsdiff(int argc, char *argv[])
+{
+ int fd;
+ u_char *old,*new; /* contents of old, new files */
+ off_t oldsize, newsize; /* length of old, new files */
+ off_t *I,*V; /* arrays used for suffix sort; I is ordering */
+ off_t scan; /* position of current match in old file */
+ off_t pos; /* position of current match in new file */
+ off_t len; /* length of current match */
+ off_t lastscan; /* position of previous match in old file */
+ off_t lastpos; /* position of previous match in new file */
+ off_t lastoffset; /* lastpos - lastscan */
+ off_t oldscore, scsc; /* temp variables in match search */
+ off_t s, Sf, lenf, Sb, lenb; /* temp vars in match extension */
+ off_t overlap, Ss, lens;
+ off_t i;
+ off_t dblen, eblen; /* length of diff, extra sections */
+ u_char *db,*eb; /* contents of diff, extra sections */
+ u_char buf[8];
+ u_char header[32];
+ FILE * pf;
+ BZFILE * pfbz2;
+ int bz2err;
+
+ if (argc != 4)
+ errx(1,"usage: %s oldfile newfile patchfile\n", argv[0]);
+
+ /* Allocate oldsize + 1 bytes instead of oldsize bytes to ensure
+ that we never try to malloc(0) and get a NULL pointer */
+ if (((fd = open(argv[1], O_RDONLY, 0)) < 0) ||
+ ((oldsize = lseek(fd, 0, SEEK_END)) == -1) ||
+ ((old = malloc(oldsize + 1)) == NULL) ||
+ (lseek(fd, 0, SEEK_SET) != 0) ||
+ (read(fd, old, oldsize) != oldsize) ||
+ (close(fd) == -1))
+ err(1,"%s", argv[1]);
+
+ if (((I = malloc((oldsize + 1) * sizeof(off_t))) == NULL) ||
+ ((V = malloc((oldsize + 1) * sizeof(off_t))) == NULL))
+ err(1, NULL);
+
+ /* Do a suffix sort on the old file. */
+ qsufsort(I, V, old, oldsize);
+
+ free(V);
+
+ /* Allocate newsize + 1 bytes instead of newsize bytes to ensure
+ that we never try to malloc(0) and get a NULL pointer */
+ if (((fd = open(argv[2], O_RDONLY, 0)) < 0) ||
+ ((newsize = lseek(fd, 0, SEEK_END)) == -1) ||
+ ((new = malloc(newsize + 1)) == NULL) ||
+ (lseek(fd, 0, SEEK_SET) != 0) ||
+ (read(fd, new, newsize) != newsize) ||
+ (close(fd) == -1))
+ err(1,"%s", argv[2]);
+
+ if (((db = malloc(newsize + 1)) == NULL) ||
+ ((eb = malloc(newsize + 1)) == NULL))
+ err(1, NULL);
+ dblen = 0;
+ eblen = 0;
+
+ /* Create the patch file */
+ if ((pf = fopen(argv[3], "w")) == NULL)
+ err(1, "%s", argv[3]);
+
+ /* Header is
+ 0 8 "BSDIFF40"
+ 8 8 length of bzip2ed ctrl block
+ 16 8 length of bzip2ed diff block
+ 24 8 length of new file */
+ /* File is
+ 0 32 Header
+ 32 ?? Bzip2ed ctrl block
+ ?? ?? Bzip2ed diff block
+ ?? ?? Bzip2ed extra block */
+ memcpy(header, "BSDIFF40", 8);
+ offtout(0, header + 8);
+ offtout(0, header + 16);
+ offtout(newsize, header + 24);
+ if (fwrite(header, 32, 1, pf) != 1)
+ err(1, "fwrite(%s)", argv[3]);
+
+ /* Compute the differences, writing ctrl as we go */
+ if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
+ errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
+ scan = 0;
+ len = 0;
+ lastscan = 0;
+ lastpos = 0;
+ lastoffset = 0;
+ while (scan < newsize) {
+ oldscore = 0;
+
+ for (scsc = scan += len; scan < newsize; scan++) {
+ /* 'oldscore' is the number of characters that match between the
+ * substrings 'old[lastoffset + scan:lastoffset + scsc]' and
+ * 'new[scan:scsc]'. */
+ len = search(I, old, oldsize, new + scan, newsize - scan,
+ 0, oldsize, &pos);
+
+ /* If this match extends further than the last one, add any new
+ * matching characters to 'oldscore'. */
+ for (; scsc < scan + len; scsc++) {
+ if ((scsc + lastoffset < oldsize) &&
+ (old[scsc + lastoffset] == new[scsc]))
+ oldscore++;
+ }
+
+ /* Choose this as our match if it contains more than eight
+ * characters that would be wrong if matched with a forward
+ * extension of the previous match instead. */
+ if (((len == oldscore) && (len != 0)) ||
+ (len > oldscore + 8))
+ break;
+
+ /* Since we're advancing 'scan' by 1, remove the character under it
+ * from 'oldscore' if it matches. */
+ if ((scan + lastoffset < oldsize) &&
+ (old[scan + lastoffset] == new[scan]))
+ oldscore--;
+ }
+
+ /* Skip this section if we found an exact match that would be
+ * better serviced by a forward extension of the previous match. */
+ if ((len != oldscore) || (scan == newsize)) {
+ /* Figure out how far forward the previous match should be
+ * extended... */
+ s = 0;
+ Sf = 0;
+ lenf = 0;
+ for (i = 0; (lastscan + i < scan) && (lastpos + i < oldsize);) {
+ if (old[lastpos + i] == new[lastscan + i])
+ s++;
+ i++;
+ if (s * 2 - i > Sf * 2 - lenf) {
+ Sf = s;
+ lenf = i;
+ }
+ }
+
+ /* ... and how far backwards the next match should be extended. */
+ lenb = 0;
+ if (scan < newsize) {
+ s = 0;
+ Sb = 0;
+ for (i = 1; (scan >= lastscan + i) && (pos >= i); i++) {
+ if (old[pos - i] == new[scan - i])
+ s++;
+ if (s * 2 - i > Sb * 2 - lenb) {
+ Sb = s;
+ lenb = i;
+ }
+ }
+ }
+
+ /* If there is an overlap between the extensions, find the best
+ * dividing point in the middle and reset 'lenf' and 'lenb'
+ * accordingly. */
+ if (lastscan + lenf > scan - lenb) {
+ overlap = (lastscan + lenf) - (scan - lenb);
+ s = 0;
+ Ss = 0;
+ lens = 0;
+ for (i = 0; i < overlap; i++) {
+ if (new[lastscan + lenf - overlap + i] ==
+ old[lastpos + lenf - overlap + i])
+ s++;
+ if (new[scan - lenb + i] == old[pos - lenb + i])
+ s--;
+ if (s > Ss) {
+ Ss = s;
+ lens = i + 1;
+ }
+ }
+
+ lenf += lens - overlap;
+ lenb -= lens;
+ }
+
+ /* Write the diff data for the last match to the diff section... */
+ for (i = 0; i < lenf; i++)
+ db[dblen + i] = new[lastscan + i] - old[lastpos + i];
+ /* ... and, if there's a gap between the extensions just
+ * calculated, write the data in that gap to the extra section. */
+ for (i = 0; i< (scan - lenb) - (lastscan + lenf); i++)
+ eb[eblen + i] = new[lastscan + lenf + i];
+
+ /* Update the diff and extra section lengths accordingly. */
+ dblen += lenf;
+ eblen += (scan - lenb) - (lastscan + lenf);
+
+ /* Write the following triple of integers to the control section:
+ * - length of the diff
+ * - length of the extra section
+ * - offset between the end of the diff and the start of the next
+ * diff, in the old file
+ */
+ offtout(lenf, buf);
+ BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
+ if (bz2err != BZ_OK)
+ errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
+
+ offtout((scan - lenb) - (lastscan + lenf), buf);
+ BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
+ if (bz2err != BZ_OK)
+ errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
+
+ offtout((pos - lenb) - (lastpos + lenf), buf);
+ BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
+ if (bz2err != BZ_OK)
+ errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
+
+ /* Update the variables describing the last match. Note that
+ * 'lastscan' is set to the start of the current match _after_ the
+ * backwards extension; the data in that extension will be written
+ * in the next pass. */
+ lastscan = scan - lenb;
+ lastpos = pos - lenb;
+ lastoffset = pos - scan;
+ }
+ }
+ BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
+ if (bz2err != BZ_OK)
+ errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err);
+
+ /* Compute size of compressed ctrl data */
+ if ((len = ftello(pf)) == -1)
+ err(1, "ftello");
+ offtout(len - 32, header + 8);
+
+ /* Write compressed diff data */
+ if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
+ errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
+ BZ2_bzWrite(&bz2err, pfbz2, db, dblen);
+ if (bz2err != BZ_OK)
+ errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
+ BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
+ if (bz2err != BZ_OK)
+ errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err);
+
+ /* Compute size of compressed diff data */
+ if ((newsize = ftello(pf)) == -1)
+ err(1, "ftello");
+ offtout(newsize - len, header + 16);
+
+ /* Write compressed extra data */
+ if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
+ errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
+ BZ2_bzWrite(&bz2err, pfbz2, eb, eblen);
+ if (bz2err != BZ_OK)
+ errx(1, "BZ2_bzWrite, bz2err = %d", bz2err);
+ BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
+ if (bz2err != BZ_OK)
+ errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err);
+
+ /* Seek to the beginning, write the header, and close the file */
+ if (fseeko(pf, 0, SEEK_SET))
+ err(1, "fseeko");
+ if (fwrite(header, 32, 1, pf) != 1)
+ err(1, "fwrite(%s)", argv[3]);
+ if (fclose(pf))
+ err(1, "fclose");
+
+ /* Free the memory we used */
+ free(db);
+ free(eb);
+ free(I);
+ free(old);
+ free(new);
+
+ return 0;
+}
209 bspatch.c
View
@@ -0,0 +1,209 @@
+/*-
+ * Copyright 2003-2005 Colin Percival
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted providing that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if 0
+__FBSDID("$FreeBSD: src/usr.bin/bsdiff/bspatch/bspatch.c,v 1.1 2005/08/06 01:59:06 cperciva Exp $");
+#endif
+
+#include <bspatch.h>
+#include <bzlib.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <err.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#ifndef u_char
+typedef unsigned char u_char;
+#endif
+
+static off_t offtin(u_char *buf)
+{
+ off_t y;
+
+ y=buf[7]&0x7F;
+ y=y*256;y+=buf[6];
+ y=y*256;y+=buf[5];
+ y=y*256;y+=buf[4];
+ y=y*256;y+=buf[3];
+ y=y*256;y+=buf[2];
+ y=y*256;y+=buf[1];
+ y=y*256;y+=buf[0];
+
+ if(buf[7]&0x80) y=-y;
+
+ return y;
+}
+
+int bspatch(int argc,char * argv[])
+{
+ FILE * f, * cpf, * dpf, * epf;
+ BZFILE * cpfbz2, * dpfbz2, * epfbz2;
+ int cbz2err, dbz2err, ebz2err;
+ int fd;
+ ssize_t oldsize,newsize;
+ ssize_t bzctrllen,bzdatalen;
+ u_char header[32],buf[8];
+ u_char *old, *new;
+ off_t oldpos,newpos;
+ off_t ctrl[3];
+ off_t lenread;
+ off_t i;
+
+ if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]);
+
+ /* Open patch file */
+ if ((f = fopen(argv[3], "r")) == NULL)
+ err(1, "fopen(%s)", argv[3]);
+
+ /*
+ File format:
+ 0 8 "BSDIFF40"
+ 8 8 X
+ 16 8 Y
+ 24 8 sizeof(newfile)
+ 32 X bzip2(control block)
+ 32+X Y bzip2(diff block)
+ 32+X+Y ??? bzip2(extra block)
+ with control block a set of triples (x,y,z) meaning "add x bytes
+ from oldfile to x bytes from the diff block; copy y bytes from the
+ extra block; seek forwards in oldfile by z bytes".
+ */
+
+ /* Read header */
+ if (fread(header, 1, 32, f) < 32) {
+ if (feof(f))
+ errx(1, "Corrupt patch\n");
+ err(1, "fread(%s)", argv[3]);
+ }
+
+ /* Check for appropriate magic */
+ if (memcmp(header, "BSDIFF40", 8) != 0)
+ errx(1, "Corrupt patch\n");
+
+ /* Read lengths from header */
+ bzctrllen=offtin(header+8);
+ bzdatalen=offtin(header+16);
+ newsize=offtin(header+24);
+ if((bzctrllen<0) || (bzdatalen<0) || (newsize<0))
+ errx(1,"Corrupt patch\n");
+
+ /* Close patch file and re-open it via libbzip2 at the right places */
+ if (fclose(f))
+ err(1, "fclose(%s)", argv[3]);
+ if ((cpf = fopen(argv[3], "r")) == NULL)
+ err(1, "fopen(%s)", argv[3]);
+ if (fseeko(cpf, 32, SEEK_SET))
+ err(1, "fseeko(%s, %lld)", argv[3],
+ (long long)32);
+ if ((cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0)) == NULL)
+ errx(1, "BZ2_bzReadOpen, bz2err = %d", cbz2err);
+ if ((dpf = fopen(argv[3], "r")) == NULL)
+ err(1, "fopen(%s)", argv[3]);
+ if (fseeko(dpf, 32 + bzctrllen, SEEK_SET))
+ err(1, "fseeko(%s, %lld)", argv[3],
+ (long long)(32 + bzctrllen));
+ if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL)
+ errx(1, "BZ2_bzReadOpen, bz2err = %d", dbz2err);
+ if ((epf = fopen(argv[3], "r")) == NULL)
+ err(1, "fopen(%s)", argv[3]);
+ if (fseeko(epf, 32 + bzctrllen + bzdatalen, SEEK_SET))
+ err(1, "fseeko(%s, %lld)", argv[3],
+ (long long)(32 + bzctrllen + bzdatalen));
+ if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL)
+ errx(1, "BZ2_bzReadOpen, bz2err = %d", ebz2err);
+
+ if(((fd=open(argv[1],O_RDONLY,0))<0) ||
+ ((oldsize=lseek(fd,0,SEEK_END))==-1) ||
+ ((old=malloc(oldsize+1))==NULL) ||
+ (lseek(fd,0,SEEK_SET)!=0) ||
+ (read(fd,old,oldsize)!=oldsize) ||
+ (close(fd)==-1)) err(1,"%s",argv[1]);
+ if((new=malloc(newsize+1))==NULL) err(1,NULL);
+
+ oldpos=0;newpos=0;
+ while(newpos<newsize) {
+ /* Read control data */
+ for(i=0;i<=2;i++) {
+ lenread = BZ2_bzRead(&cbz2err, cpfbz2, buf, 8);
+ if ((lenread < 8) || ((cbz2err != BZ_OK) &&
+ (cbz2err != BZ_STREAM_END)))
+ errx(1, "Corrupt patch\n");
+ ctrl[i]=offtin(buf);
+ };
+
+ /* Sanity-check */
+ if(newpos+ctrl[0]>newsize)
+ errx(1,"Corrupt patch\n");
+
+ /* Read diff string */
+ lenread = BZ2_bzRead(&dbz2err, dpfbz2, new + newpos, ctrl[0]);
+ if ((lenread < ctrl[0]) ||
+ ((dbz2err != BZ_OK) && (dbz2err != BZ_STREAM_END)))
+ errx(1, "Corrupt patch\n");
+
+ /* Add old data to diff string */
+ for(i=0;i<ctrl[0];i++)
+ if((oldpos+i>=0) && (oldpos+i<oldsize))
+ new[newpos+i]+=old[oldpos+i];
+
+ /* Adjust pointers */
+ newpos+=ctrl[0];
+ oldpos+=ctrl[0];
+
+ /* Sanity-check */
+ if(newpos+ctrl[1]>newsize)
+ errx(1,"Corrupt patch\n");
+
+ /* Read extra string */
+ lenread = BZ2_bzRead(&ebz2err, epfbz2, new + newpos, ctrl[1]);
+ if ((lenread < ctrl[1]) ||
+ ((ebz2err != BZ_OK) && (ebz2err != BZ_STREAM_END)))
+ errx(1, "Corrupt patch\n");
+
+ /* Adjust pointers */
+ newpos+=ctrl[1];
+ oldpos+=ctrl[2];
+ };
+
+ /* Clean up the bzip2 reads */
+ BZ2_bzReadClose(&cbz2err, cpfbz2);
+ BZ2_bzReadClose(&dbz2err, dpfbz2);
+ BZ2_bzReadClose(&ebz2err, epfbz2);
+ if (fclose(cpf) || fclose(dpf) || fclose(epf))
+ err(1, "fclose(%s)", argv[3]);
+
+ /* Write the new file */
+ if(((fd=open(argv[2],O_CREAT|O_TRUNC|O_WRONLY,0666))<0) ||
+ (write(fd,new,newsize)!=newsize) || (close(fd)==-1))
+ err(1,"%s",argv[2]);
+
+ free(new);
+ free(old);
+
+ return 0;
+}
12 bspatch.h
View
@@ -0,0 +1,12 @@
+/*
+ * bspatch.h
+ * Sparkle
+ *
+ * Created by Andy Matuschak on 1/11/10.
+ * Copyright 2010 Andy Matuschak. All rights reserved.
+ *
+ */
+
+// So that we can use this method in SUBinaryDeltaApply.m.
+// Silences the GCC warning that the prototype doesn't exist.
+int bspatch(int argc, char * argv[]);
Please sign in to comment.
Something went wrong with that request. Please try again.