Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add a command-line tool that can generate and apply a binary delta be…

…tween two versions of an application.

The delta file is a custom format created from the xar container format.  It describes the necessary
modifications to transform the contents of the old directory in to the contents of the new.  Binary
diffs are used to compress the differences between large files such as Mach-O binaries.  The binary
diffs are generated by bsdiff and applied via bspatch, both from bsdiff 4.3, the source of which is
included and used under a BSD-style license.
  • Loading branch information...
commit c3d825432414cdd0f1104f9e53bd629c40811e46 1 parent 150718a
Mark Rowe authored
View
5 Configurations/ConfigBinaryDelta.xcconfig
@@ -0,0 +1,5 @@
+// BinaryDelta tool only
+
+PRODUCT_NAME = BinaryDelta
+GCC_PREFIX_HEADER =
+SDKROOT = macosx10.6
View
5 Configurations/ConfigBinaryDeltaDebug.xcconfig
@@ -0,0 +1,5 @@
+#include "ConfigCommon.xcconfig"
+#include "ConfigCommonDebug.xcconfig"
+#include "ConfigBinaryDelta.xcconfig"
+
+OTHER_CFLAGS = -fsingle-precision-constant -DDEBUG
View
3  Configurations/ConfigBinaryDeltaRelease.xcconfig
@@ -0,0 +1,3 @@
+#include "ConfigCommon.xcconfig"
+#include "ConfigCommonRelease.xcconfig"
+#include "ConfigBinaryDelta.xcconfig"
View
29 License.txt
@@ -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.
+ */
View
15 SUBinaryDeltaApply.h
@@ -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
View
101 SUBinaryDeltaApply.m
@@ -0,0 +1,101 @@
+//
+// SUBinaryDeltaApply.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 <stdio.h>
+#include <stdlib.h>
+#include <xar/xar.h>
+
+extern int bspatch(int argc, const char **argv);
+
+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, 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;
+}
View
25 SUBinaryDeltaCommon.h
@@ -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
View
150 SUBinaryDeltaCommon.m
@@ -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 = 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
+}
View
236 SUBinaryDeltaTool.m
@@ -0,0 +1,236 @@
+//
+// SUBinaryDeltaTool.m
+// Sparkle
+//
+// Created by Mark Rowe on 2009-06-01.
+// Copyright 2009 Mark Rowe. All rights reserved.
+//
+
+#include "SUBinaryDeltaCommon.h"
+#include "SUBinaryDeltaApply.h"
+#include <CommonCrypto/CommonDigest.h>
+#include <Foundation/Foundation.h>
+#include <dispatch/dispatch.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);
+
+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 void addBinaryDelta(dispatch_group_t deltaGroup, dispatch_queue_t xarQueue, xar_t x, NSString *relativePath, NSString *oldBasePath, NSString *newBasePath)
+{
+ NSString *oldPath = [oldBasePath stringByAppendingPathComponent:relativePath];
+ NSString *newPath = [newBasePath stringByAppendingPathComponent:relativePath];
+ NSString *temporaryFile = temporaryFilename(@"create-binary-delta");
+
+ dispatch_queue_t bsdiffQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+ dispatch_retain(xarQueue);
+ dispatch_retain(deltaGroup);
+
+ dispatch_group_async(deltaGroup, bsdiffQueue, ^{
+ const char *argv[] = {"/usr/bin/bsdiff", [oldPath fileSystemRepresentation], [newPath fileSystemRepresentation], [temporaryFile fileSystemRepresentation]};
+ int result = bsdiff(4, argv);
+
+ if (!result) {
+ dispatch_group_async(deltaGroup, xarQueue, ^{
+ xar_file_t newFile = xar_add_frompath(x, 0, [relativePath fileSystemRepresentation], [temporaryFile fileSystemRepresentation]);
+ assert(newFile);
+ xar_prop_set(newFile, "binary-delta", "true");
+ unlink([temporaryFile fileSystemRepresentation]);
+ });
+ }
+ dispatch_release(xarQueue);
+ dispatch_release(deltaGroup);
+ });
+}
+
+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)
+{
+ size_t 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"])
+ return applyBinaryDelta(oldPath, newPath, patchFile);
+ if (![command isEqualToString:@"create"])
+ goto usage;
+
+ NSMutableDictionary *originalTreeState = [NSMutableDictionary new];
+
+ const char *sourcePaths[] = {[oldPath fileSystemRepresentation], 0};
+ FTS *fts = fts_open((char* const*)sourcePaths, FTS_PHYSICAL | FTS_NOCHDIR, compareFiles);
+ if (!fts) {
+ 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 new];
+ 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) {
+ 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... ");
+
+ dispatch_group_t deltaGroup = dispatch_group_create();
+ dispatch_queue_t xarQueue = dispatch_queue_create("xar", 0);
+
+ NSString *temporaryFile = temporaryPatchFile(patchFile);
+ __block xar_t x;
+ dispatch_sync(xarQueue, ^{
+ 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]);
+ });
+
+ NSArray *keys = [[newTreeState allKeys] sortedArrayUsingSelector:@selector(compare:)];
+ for (NSString* key in keys) {
+ id value = [newTreeState valueForKey:key];
+
+ if ([value isEqual:[NSNull null]]) {
+ dispatch_group_async(deltaGroup, xarQueue, ^{
+ 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];
+ __block BOOL deleteFirst = shouldDeleteThenExtract(key, originalInfo, newInfo);
+ dispatch_group_async(deltaGroup, xarQueue, ^{
+ xar_file_t newFile = xar_add_frompath(x, 0, [key fileSystemRepresentation], [path fileSystemRepresentation]);
+ assert(newFile);
+ if (deleteFirst)
+ xar_prop_set(newFile, "delete-then-extract", "true");
+ });
+ } else
+ addBinaryDelta(deltaGroup, xarQueue, x, key, oldPath, newPath);
+ }
+
+ dispatch_group_wait(deltaGroup, UINT64_MAX);
+ dispatch_sync(xarQueue, ^{ xar_close(x); });
+
+ unlink([patchFile fileSystemRepresentation]);
+ link([temporaryFile fileSystemRepresentation], [patchFile fileSystemRepresentation]);
+ unlink([temporaryFile fileSystemRepresentation]);
+ fprintf(stderr, "Done!\n");
+
+ [pool drain];
+ return 0;
+}
View
147 Sparkle.xcodeproj/project.pbxproj
@@ -7,6 +7,17 @@
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 */; };
+ 5D06E8FE0FD68D6B005AE3F6 /* libxar.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D06E8F90FD68D53005AE3F6 /* libxar.dylib */; };
+ 5D06E8FF0FD68D6D005AE3F6 /* libbz2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D06E8FB0FD68D61005AE3F6 /* libbz2.dylib */; };
+ 5D06E9000FD68D6D005AE3F6 /* libxar.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D06E8F90FD68D53005AE3F6 /* libxar.dylib */; };
+ 5D06E9050FD68D7D005AE3F6 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; };
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 = (); }; };
@@ -99,6 +110,13 @@
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
+ 5D06E8D50FD68C86005AE3F6 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 8DC2EF4F0486A6940098B216 /* Sparkle */;
+ remoteInfo = Sparkle;
+ };
61227AB90DB5C4BB00AB99EA /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
@@ -145,6 +163,19 @@
/* 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>"; };
+ 5D06E8F90FD68D53005AE3F6 /* libxar.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libxar.dylib; path = /usr/lib/libxar.dylib; sourceTree = "<absolute>"; };
+ 5D06E8FB0FD68D61005AE3F6 /* libbz2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libbz2.dylib; path = /usr/lib/libbz2.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>"; };
@@ -291,6 +322,17 @@
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
+ 5D06E8CE0FD68C7C005AE3F6 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 5D06E8D80FD68C8E005AE3F6 /* Sparkle.framework in Frameworks */,
+ 5D06E8FF0FD68D6D005AE3F6 /* libbz2.dylib in Frameworks */,
+ 5D06E9000FD68D6D005AE3F6 /* libxar.dylib in Frameworks */,
+ 5D06E9050FD68D7D005AE3F6 /* Foundation.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
612279D60DB5470200AB99EA /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -319,6 +361,8 @@
61177A1F0D1112E900749C97 /* IOKit.framework in Frameworks */,
FAEFA2F70D94AA7500472538 /* Foundation.framework in Frameworks */,
FAEFA2F80D94AA7900472538 /* AppKit.framework in Frameworks */,
+ 5D06E8FD0FD68D6B005AE3F6 /* libbz2.dylib in Frameworks */,
+ 5D06E8FE0FD68D6B005AE3F6 /* libxar.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -340,6 +384,7 @@
61B5F90209C4CEE200B25A18 /* Sparkle Test App.app */,
DAAEFC960DA571DF0051E0D0 /* relaunch */,
612279D90DB5470200AB99EA /* Sparkle Unit Tests.octest */,
+ 5D06E8D00FD68C7C005AE3F6 /* BinaryDelta */,
);
name = Products;
sourceTree = "<group>";
@@ -371,6 +416,8 @@
61B5F8F609C4CEB300B25A18 /* Security.framework */,
0867D6A5FE840307C02AAC07 /* AppKit.framework */,
0867D69BFE84028FC02AAC07 /* Foundation.framework */,
+ 5D06E8FB0FD68D61005AE3F6 /* libbz2.dylib */,
+ 5D06E8F90FD68D53005AE3F6 /* libxar.dylib */,
);
name = "Apple Frameworks and Libraries";
sourceTree = "<group>";
@@ -390,9 +437,24 @@
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 */,
+ );
+ name = "Binary Delta";
+ sourceTree = "<group>";
+ };
6101354A0DD25B7F0049ACDF /* Unarchiving */ = {
isa = PBXGroup;
children = (
+ 5D06E8D90FD68C95005AE3F6 /* Binary Delta */,
61299A8B09CA790200B7442F /* SUUnarchiver.h */,
61299A8C09CA790200B7442F /* SUUnarchiver.m */,
6102FE590E08C7EC00F85D09 /* SUUnarchiver_Private.h */,
@@ -545,6 +607,9 @@
FA1941C40D94A6EA00DD942E /* Configurations */ = {
isa = PBXGroup;
children = (
+ 5D06E8F10FD68D21005AE3F6 /* ConfigBinaryDelta.xcconfig */,
+ 5D06E8F20FD68D21005AE3F6 /* ConfigBinaryDeltaDebug.xcconfig */,
+ 5D06E8F30FD68D21005AE3F6 /* ConfigBinaryDeltaRelease.xcconfig */,
FA1941D00D94A70100DD942E /* ConfigCommon.xcconfig */,
FA1941CF0D94A70100DD942E /* ConfigCommonDebug.xcconfig */,
FA1941CC0D94A70100DD942E /* ConfigCommonRelease.xcconfig */,
@@ -608,6 +673,24 @@
/* 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" */;
@@ -736,6 +819,7 @@
61B5F90109C4CEE200B25A18 /* Sparkle Test App */,
DAAEFC950DA571DF0051E0D0 /* relaunch tool */,
612279D80DB5470200AB99EA /* Sparkle Unit Tests */,
+ 5D06E8CF0FD68C7C005AE3F6 /* BinaryDelta */,
);
};
/* End PBXProject section */
@@ -777,6 +861,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;
@@ -823,6 +922,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;
@@ -872,6 +980,9 @@
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 */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -886,6 +997,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 */;
@@ -1033,6 +1149,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 = {
@@ -1199,6 +1336,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 = (
View
522 bsdiff.c
@@ -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;
+}
View
208 bspatch.c
@@ -0,0 +1,208 @@
+/*-
+ * 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 <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;
+}
Please sign in to comment.
Something went wrong with that request. Please try again.