aboutsummaryrefslogtreecommitdiff
path: root/usr.bin/bsdiff
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/bsdiff')
-rw-r--r--usr.bin/bsdiff/Makefile3
-rw-r--r--usr.bin/bsdiff/Makefile.inc1
-rw-r--r--usr.bin/bsdiff/bsdiff/Makefile16
-rw-r--r--usr.bin/bsdiff/bsdiff/Makefile.depend16
-rw-r--r--usr.bin/bsdiff/bsdiff/bsdiff.186
-rw-r--r--usr.bin/bsdiff/bsdiff/bsdiff.c336
-rw-r--r--usr.bin/bsdiff/bsdiff/config.h81
-rw-r--r--usr.bin/bsdiff/bsdiff/divsufsort64.h180
-rw-r--r--usr.bin/bsdiff/bspatch/Makefile5
-rw-r--r--usr.bin/bsdiff/bspatch/Makefile.depend16
-rw-r--r--usr.bin/bsdiff/bspatch/bspatch.185
-rw-r--r--usr.bin/bsdiff/bspatch/bspatch.c302
12 files changed, 1127 insertions, 0 deletions
diff --git a/usr.bin/bsdiff/Makefile b/usr.bin/bsdiff/Makefile
new file mode 100644
index 000000000000..ed9dd0f8db5a
--- /dev/null
+++ b/usr.bin/bsdiff/Makefile
@@ -0,0 +1,3 @@
+SUBDIR= bsdiff bspatch
+
+.include <bsd.subdir.mk>
diff --git a/usr.bin/bsdiff/Makefile.inc b/usr.bin/bsdiff/Makefile.inc
new file mode 100644
index 000000000000..01b5f23410c8
--- /dev/null
+++ b/usr.bin/bsdiff/Makefile.inc
@@ -0,0 +1 @@
+.include "../Makefile.inc"
diff --git a/usr.bin/bsdiff/bsdiff/Makefile b/usr.bin/bsdiff/bsdiff/Makefile
new file mode 100644
index 000000000000..7af4fc873e13
--- /dev/null
+++ b/usr.bin/bsdiff/bsdiff/Makefile
@@ -0,0 +1,16 @@
+PROG= bsdiff
+
+# libdivsufsort configured with:
+# cmake -DCMAKE_BUILD_TYPE="Release" -DBUILD_DIVSUFSORT64=ON
+.PATH: ${SRCTOP}/contrib/libdivsufsort/lib
+CFLAGS+= -DHAVE_CONFIG_H=1 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
+CFLAGS+= -D_LARGE_FILES -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS
+CFLAGS+= -D__STDC_LIMIT_MACROS -DBUILD_DIVSUFSORT64
+CFLAGS+= -I${SRCTOP}/contrib/libdivsufsort/include -I${.CURDIR}
+SRCS= divsufsort.c sssort.c trsort.c utils.c
+
+SRCS+= bsdiff.c
+
+LIBADD= bz2
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/bsdiff/bsdiff/Makefile.depend b/usr.bin/bsdiff/bsdiff/Makefile.depend
new file mode 100644
index 000000000000..cc83708e9dfd
--- /dev/null
+++ b/usr.bin/bsdiff/bsdiff/Makefile.depend
@@ -0,0 +1,16 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libbz2 \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/usr.bin/bsdiff/bsdiff/bsdiff.1 b/usr.bin/bsdiff/bsdiff/bsdiff.1
new file mode 100644
index 000000000000..3e79d9cb89b8
--- /dev/null
+++ b/usr.bin/bsdiff/bsdiff/bsdiff.1
@@ -0,0 +1,86 @@
+.\"-
+.\" 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.
+.\"
+.Dd May 18, 2003
+.Dt BSDIFF 1
+.Os
+.Sh NAME
+.Nm bsdiff
+.Nd "generate a patch between two binary files"
+.Sh SYNOPSIS
+.Nm
+.Ar oldfile newfile patchfile
+.Sh DESCRIPTION
+The
+.Nm
+utility
+compares
+.Ar oldfile
+to
+.Ar newfile
+and writes to
+.Ar patchfile
+a binary patch suitable for use by
+.Xr bspatch 1 .
+When
+.Ar oldfile
+and
+.Ar newfile
+are two versions of an executable program, the
+patches produced are on average a factor of five smaller
+than those produced by any other binary patch tool known
+to the author.
+.Pp
+The
+.Nm
+utility
+uses memory equal to 17 times the size of
+.Ar oldfile ,
+and requires
+an absolute minimum working set size of 8 times the size of
+.Ar oldfile .
+.Sh SEE ALSO
+.Xr bspatch 1
+.Sh AUTHORS
+.An Colin Percival Aq Mt cperciva@FreeBSD.org
+.Sh BUGS
+The
+.Nm
+utility does not store the hashes of
+.Ar oldfile
+or
+.Ar newfile
+in
+.Ar patchfile .
+As a result, it is possible to apply a patch to the wrong file; this
+will usually produce garbage.
+It is recommended that users of
+.Nm
+store the hashes of
+.Ar oldfile
+and
+.Ar newfile
+and compare against them before and after applying
+.Ar patchfile .
diff --git a/usr.bin/bsdiff/bsdiff/bsdiff.c b/usr.bin/bsdiff/bsdiff/bsdiff.c
new file mode 100644
index 000000000000..06a8812308c8
--- /dev/null
+++ b/usr.bin/bsdiff/bsdiff/bsdiff.c
@@ -0,0 +1,336 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * 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.
+ */
+
+#include <sys/types.h>
+
+#include <bzlib.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#include "divsufsort64.h"
+#define saidx_t saidx64_t
+#define divsufsort divsufsort64
+
+#define MIN(x,y) (((x)<(y)) ? (x) : (y))
+
+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;
+}
+
+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);
+ };
+}
+
+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;
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "usage: bsdiff oldfile newfile patchfile\n");
+ exit(1);
+}
+
+int main(int argc,char *argv[])
+{
+ int fd;
+ u_char *old,*new;
+ off_t oldsize,newsize,xnewsize;
+ saidx_t *I;
+ off_t scan,pos,len;
+ off_t lastscan,lastpos,lastoffset;
+ off_t oldscore,scsc;
+ off_t s,Sf,lenf,Sb,lenb;
+ off_t overlap,Ss,lens;
+ off_t i;
+ off_t dblen,eblen;
+ u_char *db,*eb;
+ u_char buf[8];
+ u_char header[32];
+ FILE * pf;
+ BZFILE * pfbz2;
+ int bz2err;
+
+ if (argc != 4)
+ usage();
+
+ /* 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|O_BINARY,0))<0) ||
+ ((oldsize=lseek(fd,0,SEEK_END))==-1))
+ err(1, "%s", argv[1]);
+
+ if (oldsize > SSIZE_MAX ||
+ (uintmax_t)oldsize >= SIZE_T_MAX / sizeof(off_t) ||
+ oldsize == OFF_MAX) {
+ errno = EFBIG;
+ err(1, "%s", argv[1]);
+ }
+
+ old = mmap(NULL, oldsize+1, PROT_READ, MAP_SHARED, fd, 0);
+ if (old == MAP_FAILED || close(fd) == -1)
+ err(1, "%s", argv[1]);
+
+ if(((I=malloc((oldsize+1)*sizeof(saidx_t)))==NULL)) err(1,NULL);
+
+ if(divsufsort(old, I, oldsize)) err(1, "divsufsort");
+
+ /* 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|O_BINARY,0))<0) ||
+ ((newsize=lseek(fd,0,SEEK_END))==-1))
+ err(1, "%s", argv[2]);
+
+ if (newsize > SSIZE_MAX || (uintmax_t)newsize >= SIZE_T_MAX ||
+ newsize == OFF_MAX) {
+ errno = EFBIG;
+ err(1, "%s", argv[2]);
+ }
+
+ new = mmap(NULL, newsize+1, PROT_READ, MAP_SHARED, fd, 0);
+ if (new == MAP_FAILED || 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], "wb")) == 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;pos=0;
+ lastscan=0;lastpos=0;lastoffset=0;
+ while(scan<newsize) {
+ oldscore=0;
+
+ for(scsc=scan+=len;scan<newsize;scan++) {
+ len=search(I,old,oldsize,new+scan,newsize-scan,
+ 0,oldsize-1,&pos);
+
+ for(;scsc<scan+len;scsc++)
+ if((scsc+lastoffset<oldsize) &&
+ (old[scsc+lastoffset] == new[scsc]))
+ oldscore++;
+
+ if(((len==oldscore) && (len!=0)) ||
+ (len>oldscore+8)) break;
+
+ if((scan+lastoffset<oldsize) &&
+ (old[scan+lastoffset] == new[scan]))
+ oldscore--;
+ }
+
+ if((len!=oldscore) || (scan==newsize)) {
+ 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; }
+ }
+
+ 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(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;
+ }
+
+ for(i=0;i<lenf;i++)
+ db[dblen+i]=new[lastscan+i]-old[lastpos+i];
+ for(i=0;i<(scan-lenb)-(lastscan+lenf);i++)
+ eb[eblen+i]=new[lastscan+lenf+i];
+
+ dblen+=lenf;
+ eblen+=(scan-lenb)-(lastscan+lenf);
+
+ 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);
+
+ 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 ((xnewsize = ftello(pf)) == -1)
+ err(1, "ftello");
+ offtout(xnewsize - 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);
+ munmap(old, oldsize+1);
+ munmap(new, newsize+1);
+
+ return 0;
+}
diff --git a/usr.bin/bsdiff/bsdiff/config.h b/usr.bin/bsdiff/bsdiff/config.h
new file mode 100644
index 000000000000..fb4e71261cd2
--- /dev/null
+++ b/usr.bin/bsdiff/bsdiff/config.h
@@ -0,0 +1,81 @@
+/*
+ * config.h for libdivsufsort
+ * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _CONFIG_H
+#define _CONFIG_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/** Define to the version of this package. **/
+#define PROJECT_VERSION_FULL "2.0.1-14-g5f60d6f"
+
+/** Define to 1 if you have the header files. **/
+#define HAVE_INTTYPES_H 1
+#define HAVE_STDDEF_H 1
+#define HAVE_STDINT_H 1
+#define HAVE_STDLIB_H 1
+#define HAVE_STRING_H 1
+#define HAVE_STRINGS_H 1
+#define HAVE_MEMORY_H 1
+#define HAVE_SYS_TYPES_H 1
+
+/** for WinIO **/
+/* #undef HAVE_IO_H */
+/* #undef HAVE_FCNTL_H */
+/* #undef HAVE__SETMODE */
+/* #undef HAVE_SETMODE */
+/* #undef HAVE__FILENO */
+/* #undef HAVE_FOPEN_S */
+/* #undef HAVE__O_BINARY */
+#ifndef HAVE__SETMODE
+# if HAVE_SETMODE
+# define _setmode setmode
+# define HAVE__SETMODE 1
+# endif
+# if HAVE__SETMODE && !HAVE__O_BINARY
+# define _O_BINARY 0
+# define HAVE__O_BINARY 1
+# endif
+#endif
+
+/** for inline **/
+#ifndef INLINE
+# define INLINE inline
+#endif
+
+/** for VC++ warning **/
+#ifdef _MSC_VER
+#pragma warning(disable: 4127)
+#endif
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif /* __cplusplus */
+
+#endif /* _CONFIG_H */
diff --git a/usr.bin/bsdiff/bsdiff/divsufsort64.h b/usr.bin/bsdiff/bsdiff/divsufsort64.h
new file mode 100644
index 000000000000..2f1c375ba45e
--- /dev/null
+++ b/usr.bin/bsdiff/bsdiff/divsufsort64.h
@@ -0,0 +1,180 @@
+/*
+ * divsufsort64.h for libdivsufsort64
+ * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _DIVSUFSORT64_H
+#define _DIVSUFSORT64_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include <inttypes.h>
+
+#ifndef DIVSUFSORT_API
+# ifdef DIVSUFSORT_BUILD_DLL
+# define DIVSUFSORT_API
+# else
+# define DIVSUFSORT_API
+# endif
+#endif
+
+/*- Datatypes -*/
+#ifndef SAUCHAR_T
+#define SAUCHAR_T
+typedef uint8_t sauchar_t;
+#endif /* SAUCHAR_T */
+#ifndef SAINT_T
+#define SAINT_T
+typedef int32_t saint_t;
+#endif /* SAINT_T */
+#ifndef SAIDX64_T
+#define SAIDX64_T
+typedef int64_t saidx64_t;
+#endif /* SAIDX64_T */
+#ifndef PRIdSAINT_T
+#define PRIdSAINT_T PRId32
+#endif /* PRIdSAINT_T */
+#ifndef PRIdSAIDX64_T
+#define PRIdSAIDX64_T PRId64
+#endif /* PRIdSAIDX64_T */
+
+
+/*- Prototypes -*/
+
+/**
+ * Constructs the suffix array of a given string.
+ * @param T[0..n-1] The input string.
+ * @param SA[0..n-1] The output array of suffixes.
+ * @param n The length of the given string.
+ * @return 0 if no error occurred, -1 or -2 otherwise.
+ */
+DIVSUFSORT_API
+saint_t
+divsufsort64(const sauchar_t *T, saidx64_t *SA, saidx64_t n);
+
+/**
+ * Constructs the burrows-wheeler transformed string of a given string.
+ * @param T[0..n-1] The input string.
+ * @param U[0..n-1] The output string. (can be T)
+ * @param A[0..n-1] The temporary array. (can be NULL)
+ * @param n The length of the given string.
+ * @return The primary index if no error occurred, -1 or -2 otherwise.
+ */
+DIVSUFSORT_API
+saidx64_t
+divbwt64(const sauchar_t *T, sauchar_t *U, saidx64_t *A, saidx64_t n);
+
+/**
+ * Returns the version of the divsufsort library.
+ * @return The version number string.
+ */
+DIVSUFSORT_API
+const char *
+divsufsort64_version(void);
+
+
+/**
+ * Constructs the burrows-wheeler transformed string of a given string and suffix array.
+ * @param T[0..n-1] The input string.
+ * @param U[0..n-1] The output string. (can be T)
+ * @param SA[0..n-1] The suffix array. (can be NULL)
+ * @param n The length of the given string.
+ * @param idx The output primary index.
+ * @return 0 if no error occurred, -1 or -2 otherwise.
+ */
+DIVSUFSORT_API
+saint_t
+bw_transform64(const sauchar_t *T, sauchar_t *U,
+ saidx64_t *SA /* can NULL */,
+ saidx64_t n, saidx64_t *idx);
+
+/**
+ * Inverse BW-transforms a given BWTed string.
+ * @param T[0..n-1] The input string.
+ * @param U[0..n-1] The output string. (can be T)
+ * @param A[0..n-1] The temporary array. (can be NULL)
+ * @param n The length of the given string.
+ * @param idx The primary index.
+ * @return 0 if no error occurred, -1 or -2 otherwise.
+ */
+DIVSUFSORT_API
+saint_t
+inverse_bw_transform64(const sauchar_t *T, sauchar_t *U,
+ saidx64_t *A /* can NULL */,
+ saidx64_t n, saidx64_t idx);
+
+/**
+ * Checks the correctness of a given suffix array.
+ * @param T[0..n-1] The input string.
+ * @param SA[0..n-1] The input suffix array.
+ * @param n The length of the given string.
+ * @param verbose The verbose mode.
+ * @return 0 if no error occurred.
+ */
+DIVSUFSORT_API
+saint_t
+sufcheck64(const sauchar_t *T, const saidx64_t *SA, saidx64_t n, saint_t verbose);
+
+/**
+ * Search for the pattern P in the string T.
+ * @param T[0..Tsize-1] The input string.
+ * @param Tsize The length of the given string.
+ * @param P[0..Psize-1] The input pattern string.
+ * @param Psize The length of the given pattern string.
+ * @param SA[0..SAsize-1] The input suffix array.
+ * @param SAsize The length of the given suffix array.
+ * @param idx The output index.
+ * @return The count of matches if no error occurred, -1 otherwise.
+ */
+DIVSUFSORT_API
+saidx64_t
+sa_search64(const sauchar_t *T, saidx64_t Tsize,
+ const sauchar_t *P, saidx64_t Psize,
+ const saidx64_t *SA, saidx64_t SAsize,
+ saidx64_t *left);
+
+/**
+ * Search for the character c in the string T.
+ * @param T[0..Tsize-1] The input string.
+ * @param Tsize The length of the given string.
+ * @param SA[0..SAsize-1] The input suffix array.
+ * @param SAsize The length of the given suffix array.
+ * @param c The input character.
+ * @param idx The output index.
+ * @return The count of matches if no error occurred, -1 otherwise.
+ */
+DIVSUFSORT_API
+saidx64_t
+sa_simplesearch64(const sauchar_t *T, saidx64_t Tsize,
+ const saidx64_t *SA, saidx64_t SAsize,
+ saint_t c, saidx64_t *left);
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif /* __cplusplus */
+
+#endif /* _DIVSUFSORT64_H */
diff --git a/usr.bin/bsdiff/bspatch/Makefile b/usr.bin/bsdiff/bspatch/Makefile
new file mode 100644
index 000000000000..12b67fe97577
--- /dev/null
+++ b/usr.bin/bsdiff/bspatch/Makefile
@@ -0,0 +1,5 @@
+PROG= bspatch
+
+LIBADD= bz2
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/bsdiff/bspatch/Makefile.depend b/usr.bin/bsdiff/bspatch/Makefile.depend
new file mode 100644
index 000000000000..cc83708e9dfd
--- /dev/null
+++ b/usr.bin/bsdiff/bspatch/Makefile.depend
@@ -0,0 +1,16 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libbz2 \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/usr.bin/bsdiff/bspatch/bspatch.1 b/usr.bin/bsdiff/bspatch/bspatch.1
new file mode 100644
index 000000000000..291f9f2be348
--- /dev/null
+++ b/usr.bin/bsdiff/bspatch/bspatch.1
@@ -0,0 +1,85 @@
+.\"-
+.\" 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.
+.\"
+.Dd December 22, 2022
+.Dt BSPATCH 1
+.Os
+.Sh NAME
+.Nm bspatch
+.Nd apply a patch built with
+.Xr bsdiff 1
+.Sh SYNOPSIS
+.Nm
+.Ar oldfile newfile patchfile
+.Sh DESCRIPTION
+The
+.Nm
+utility
+generates
+.Ar newfile
+from
+.Ar oldfile
+and
+.Ar patchfile
+where
+.Ar patchfile
+is a binary patch built by
+.Xr bsdiff 1 .
+.Pp
+The
+.Nm
+utility
+uses memory equal to the size of
+.Ar oldfile
+plus the size of
+.Ar newfile ,
+but can tolerate a very small working set without a dramatic loss
+of performance.
+.Sh SEE ALSO
+.Xr bsdiff 1
+.Sh AUTHORS
+.An Colin Percival Aq Mt cperciva@FreeBSD.org
+.Sh BUGS
+The
+.Nm
+utility does not verify that
+.Ar oldfile
+is the correct source file for
+.Ar patchfile .
+Attempting to apply a patch to the wrong file will usually produce
+garbage; consequently it is strongly recommended that users of
+.Nm
+verify that
+.Ar oldfile
+matches the source file from which
+.Ar patchfile
+was built, by comparing cryptographic hashes, for example.
+Users may also wish to verify after running
+.Nm
+that
+.Ar newfile
+matches the target file from which
+.Ar patchfile
+was built.
diff --git a/usr.bin/bsdiff/bspatch/bspatch.c b/usr.bin/bsdiff/bspatch/bspatch.c
new file mode 100644
index 000000000000..ea43d78b12a1
--- /dev/null
+++ b/usr.bin/bsdiff/bspatch/bspatch.c
@@ -0,0 +1,302 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef WITHOUT_CAPSICUM
+#include <sys/capsicum.h>
+#endif
+
+#include <bzlib.h>
+#include <err.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stdckdint.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#define HEADER_SIZE 32
+
+static char *newfile;
+static int dirfd = -1;
+
+static void
+exit_cleanup(void)
+{
+
+ if (dirfd != -1 && newfile != NULL)
+ if (unlinkat(dirfd, newfile, 0))
+ warn("unlinkat");
+}
+
+static inline off_t
+add_off_t(off_t a, off_t b)
+{
+ off_t result;
+
+ if (ckd_add(&result, a, b))
+ errx(1, "Corrupt patch");
+ return result;
+}
+
+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);
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "usage: bspatch oldfile newfile patchfile\n");
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ FILE *f, *cpf, *dpf, *epf;
+ BZFILE *cpfbz2, *dpfbz2, *epfbz2;
+ char *directory, *namebuf;
+ int cbz2err, dbz2err, ebz2err;
+ int newfd, oldfd;
+ off_t oldsize, newsize;
+ off_t bzctrllen, bzdatalen;
+ u_char header[HEADER_SIZE], buf[8];
+ u_char *old, *new;
+ off_t oldpos, newpos;
+ off_t ctrl[3];
+ off_t i, lenread, offset;
+#ifndef WITHOUT_CAPSICUM
+ cap_rights_t rights_dir, rights_ro, rights_wr;
+#endif
+
+ if (argc != 4)
+ usage();
+
+ /* Open patch file */
+ if ((f = fopen(argv[3], "rb")) == NULL)
+ err(1, "fopen(%s)", argv[3]);
+ /* Open patch file for control block */
+ if ((cpf = fopen(argv[3], "rb")) == NULL)
+ err(1, "fopen(%s)", argv[3]);
+ /* open patch file for diff block */
+ if ((dpf = fopen(argv[3], "rb")) == NULL)
+ err(1, "fopen(%s)", argv[3]);
+ /* open patch file for extra block */
+ if ((epf = fopen(argv[3], "rb")) == NULL)
+ err(1, "fopen(%s)", argv[3]);
+ /* open oldfile */
+ if ((oldfd = open(argv[1], O_RDONLY | O_BINARY, 0)) < 0)
+ err(1, "open(%s)", argv[1]);
+ /* open directory where we'll write newfile */
+ if ((namebuf = strdup(argv[2])) == NULL ||
+ (directory = dirname(namebuf)) == NULL ||
+ (dirfd = open(directory, O_DIRECTORY)) < 0)
+ err(1, "open %s", argv[2]);
+ free(namebuf);
+ if ((newfile = basename(argv[2])) == NULL)
+ err(1, "basename");
+ /* open newfile */
+ if ((newfd = openat(dirfd, newfile,
+ O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0666)) < 0)
+ err(1, "open(%s)", argv[2]);
+ atexit(exit_cleanup);
+
+#ifndef WITHOUT_CAPSICUM
+ if (cap_enter() < 0)
+ err(1, "failed to enter security sandbox");
+
+ cap_rights_init(&rights_ro, CAP_READ, CAP_FSTAT, CAP_SEEK, CAP_MMAP_R);
+ cap_rights_init(&rights_wr, CAP_WRITE);
+ cap_rights_init(&rights_dir, CAP_UNLINKAT);
+
+ if (cap_rights_limit(fileno(f), &rights_ro) < 0 ||
+ cap_rights_limit(fileno(cpf), &rights_ro) < 0 ||
+ cap_rights_limit(fileno(dpf), &rights_ro) < 0 ||
+ cap_rights_limit(fileno(epf), &rights_ro) < 0 ||
+ cap_rights_limit(oldfd, &rights_ro) < 0 ||
+ cap_rights_limit(newfd, &rights_wr) < 0 ||
+ cap_rights_limit(dirfd, &rights_dir) < 0)
+ err(1, "cap_rights_limit() failed, could not restrict"
+ " capabilities");
+#endif
+
+ /*
+ 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, HEADER_SIZE, f) < HEADER_SIZE) {
+ if (feof(f))
+ errx(1, "Corrupt patch");
+ err(1, "fread(%s)", argv[3]);
+ }
+
+ /* Check for appropriate magic */
+ if (memcmp(header, "BSDIFF40", 8) != 0)
+ errx(1, "Corrupt patch");
+
+ /* Read lengths from header */
+ bzctrllen = offtin(header + 8);
+ bzdatalen = offtin(header + 16);
+ newsize = offtin(header + 24);
+ if (bzctrllen < 0 || bzctrllen > OFF_MAX - HEADER_SIZE ||
+ bzdatalen < 0 || bzctrllen + HEADER_SIZE > OFF_MAX - bzdatalen ||
+ newsize < 0 || newsize > SSIZE_MAX)
+ errx(1, "Corrupt patch");
+
+ /* Close patch file and re-open it via libbzip2 at the right places */
+ if (fclose(f))
+ err(1, "fclose(%s)", argv[3]);
+ offset = HEADER_SIZE;
+ if (fseeko(cpf, offset, SEEK_SET))
+ err(1, "fseeko(%s, %jd)", argv[3], (intmax_t)offset);
+ if ((cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0)) == NULL)
+ errx(1, "BZ2_bzReadOpen, bz2err = %d", cbz2err);
+ offset = add_off_t(offset, bzctrllen);
+ if (fseeko(dpf, offset, SEEK_SET))
+ err(1, "fseeko(%s, %jd)", argv[3], (intmax_t)offset);
+ if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL)
+ errx(1, "BZ2_bzReadOpen, bz2err = %d", dbz2err);
+ offset = add_off_t(offset, bzdatalen);
+ if (fseeko(epf, offset, SEEK_SET))
+ err(1, "fseeko(%s, %jd)", argv[3], (intmax_t)offset);
+ if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL)
+ errx(1, "BZ2_bzReadOpen, bz2err = %d", ebz2err);
+
+ if ((oldsize = lseek(oldfd, 0, SEEK_END)) == -1 ||
+ oldsize > SSIZE_MAX)
+ err(1, "%s", argv[1]);
+
+ old = mmap(NULL, oldsize+1, PROT_READ, MAP_SHARED, oldfd, 0);
+ if (old == MAP_FAILED || close(oldfd) != 0)
+ err(1, "%s", argv[1]);
+
+ if ((new = malloc(newsize)) == 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");
+ ctrl[i] = offtin(buf);
+ }
+
+ /* Sanity-check */
+ if (ctrl[0] < 0 || ctrl[0] > INT_MAX ||
+ ctrl[1] < 0 || ctrl[1] > INT_MAX)
+ errx(1, "Corrupt patch");
+
+ /* Sanity-check */
+ if (add_off_t(newpos, ctrl[0]) > newsize)
+ errx(1, "Corrupt patch");
+
+ /* 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");
+
+ /* Add old data to diff string */
+ for (i = 0; i < ctrl[0]; i++)
+ if (add_off_t(oldpos, i) < oldsize)
+ new[newpos + i] += old[oldpos + i];
+
+ /* Adjust pointers */
+ newpos = add_off_t(newpos, ctrl[0]);
+ oldpos = add_off_t(oldpos, ctrl[0]);
+
+ /* Sanity-check */
+ if (add_off_t(newpos, ctrl[1]) > newsize)
+ errx(1, "Corrupt patch");
+
+ /* 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");
+
+ /* Adjust pointers */
+ newpos = add_off_t(newpos, ctrl[1]);
+ oldpos = add_off_t(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 (write(newfd, new, newsize) != newsize || close(newfd) == -1)
+ err(1, "%s", argv[2]);
+ /* Disable atexit cleanup */
+ newfile = NULL;
+
+ free(new);
+ munmap(old, oldsize+1);
+
+ return (0);
+}