jwilkins-rb-bsdiff 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README ADDED
@@ -0,0 +1,6 @@
1
+ Ruby bindings to bsdiff and bspatch
2
+
3
+ BSDiff.diff('ext/b0', 'ext/b1', 'p0')
4
+
5
+ # apply patch to bspatch.o as bspatch2.o
6
+ BSDiff.patch('ext/b0', 'b3', 'p0')
data/Rakefile ADDED
@@ -0,0 +1,40 @@
1
+ require 'rake/clean'
2
+ require 'rake/testtask'
3
+
4
+ CLEAN.include '**/*.o'
5
+ CLEAN.include "**/*.#{Config::MAKEFILE_CONFIG['DLEXT']}"
6
+ CLOBBER.include '**/Makefile'
7
+ CLOBBER.include '**/bsdiff_config.h'
8
+ CLOBBER.include '**/mkmf.log'
9
+
10
+ BSDIFF_SO = "ext/bsdiff.#{Config::MAKEFILE_CONFIG['DLEXT']}"
11
+
12
+ MAKECMD = ENV['MAKE_CMD'] || 'make'
13
+ MAKEOPTS = ENV['MAKE_OPTS'] || ''
14
+
15
+ desc "Test project"
16
+ task :default => ['compile', 'test']
17
+
18
+ file 'ext/Makefile' => 'ext/extconf.rb' do
19
+ Dir.chdir('ext') do
20
+ ruby "extconf.rb #{ENV['EXTCONF_OPTS']}"
21
+ end
22
+ end
23
+
24
+ # Let make handle dependencies between c/o/so - we'll just run it.
25
+ file BSDIFF_SO => (['ext/Makefile'] + Dir['ext/*.c'] + Dir['ext/*.h']) do
26
+ m = 0
27
+ Dir.chdir('ext') do
28
+ pid = system("#{MAKECMD} #{MAKEOPTS}")
29
+ m = $?.exitstatus
30
+ end
31
+ fail "Make failed (status #{m})" unless m == 0
32
+ end
33
+
34
+ desc "Compile the shared object"
35
+ task :compile => [BSDIFF_SO]
36
+
37
+ Rake::TestTask.new(:test) do |t|
38
+ t.test_files = FileList['test.rb']
39
+ t.verbose = false
40
+ end
data/ext/b0 ADDED
Binary file
data/ext/b1 ADDED
Binary file
data/ext/bsdiff.c ADDED
@@ -0,0 +1,417 @@
1
+ /*-
2
+ * Copyright 2003-2005 Colin Percival
3
+ * All rights reserved
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted providing that the following conditions
7
+ * are met:
8
+ * 1. Redistributions of source code must retain the above copyright
9
+ * notice, this list of conditions and the following disclaimer.
10
+ * 2. Redistributions in binary form must reproduce the above copyright
11
+ * notice, this list of conditions and the following disclaimer in the
12
+ * documentation and/or other materials provided with the distribution.
13
+ *
14
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
18
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
23
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24
+ * POSSIBILITY OF SUCH DAMAGE.
25
+ */
26
+
27
+ #if 0
28
+ __FBSDID("$FreeBSD: src/usr.bin/bsdiff/bsdiff/bsdiff.c,v 1.1 2005/08/06 01:59:05 cperciva Exp $");
29
+ #endif
30
+
31
+ #include <sys/types.h>
32
+
33
+ #include <bzlib.h>
34
+ #include <fcntl.h>
35
+ #include <stdio.h>
36
+ #include <stdlib.h>
37
+ #include <string.h>
38
+ #include <unistd.h>
39
+
40
+ #include <ruby.h>
41
+
42
+ #define MIN(x,y) (((x)<(y)) ? (x) : (y))
43
+
44
+ static void split(off_t *I,off_t *V,off_t start,off_t len,off_t h)
45
+ {
46
+ off_t i,j,k,x,tmp,jj,kk;
47
+
48
+ if(len<16) {
49
+ for(k=start;k<start+len;k+=j) {
50
+ j=1;x=V[I[k]+h];
51
+ for(i=1;k+i<start+len;i++) {
52
+ if(V[I[k+i]+h]<x) {
53
+ x=V[I[k+i]+h];
54
+ j=0;
55
+ };
56
+ if(V[I[k+i]+h]==x) {
57
+ tmp=I[k+j];I[k+j]=I[k+i];I[k+i]=tmp;
58
+ j++;
59
+ };
60
+ };
61
+ for(i=0;i<j;i++) V[I[k+i]]=k+j-1;
62
+ if(j==1) I[k]=-1;
63
+ };
64
+ return;
65
+ };
66
+
67
+ x=V[I[start+len/2]+h];
68
+ jj=0;kk=0;
69
+ for(i=start;i<start+len;i++) {
70
+ if(V[I[i]+h]<x) jj++;
71
+ if(V[I[i]+h]==x) kk++;
72
+ };
73
+ jj+=start;kk+=jj;
74
+
75
+ i=start;j=0;k=0;
76
+ while(i<jj) {
77
+ if(V[I[i]+h]<x) {
78
+ i++;
79
+ } else if(V[I[i]+h]==x) {
80
+ tmp=I[i];I[i]=I[jj+j];I[jj+j]=tmp;
81
+ j++;
82
+ } else {
83
+ tmp=I[i];I[i]=I[kk+k];I[kk+k]=tmp;
84
+ k++;
85
+ };
86
+ };
87
+
88
+ while(jj+j<kk) {
89
+ if(V[I[jj+j]+h]==x) {
90
+ j++;
91
+ } else {
92
+ tmp=I[jj+j];I[jj+j]=I[kk+k];I[kk+k]=tmp;
93
+ k++;
94
+ };
95
+ };
96
+
97
+ if(jj>start) split(I,V,start,jj-start,h);
98
+
99
+ for(i=0;i<kk-jj;i++) V[I[jj+i]]=kk-1;
100
+ if(jj==kk-1) I[jj]=-1;
101
+
102
+ if(start+len>kk) split(I,V,kk,start+len-kk,h);
103
+ }
104
+
105
+ static void qsufsort(off_t *I,off_t *V,u_char *old,off_t oldsize)
106
+ {
107
+ off_t buckets[256];
108
+ off_t i,h,len;
109
+
110
+ for(i=0;i<256;i++) buckets[i]=0;
111
+ for(i=0;i<oldsize;i++) buckets[old[i]]++;
112
+ for(i=1;i<256;i++) buckets[i]+=buckets[i-1];
113
+ for(i=255;i>0;i--) buckets[i]=buckets[i-1];
114
+ buckets[0]=0;
115
+
116
+ for(i=0;i<oldsize;i++) I[++buckets[old[i]]]=i;
117
+ I[0]=oldsize;
118
+ for(i=0;i<oldsize;i++) V[i]=buckets[old[i]];
119
+ V[oldsize]=0;
120
+ for(i=1;i<256;i++) if(buckets[i]==buckets[i-1]+1) I[buckets[i]]=-1;
121
+ I[0]=-1;
122
+
123
+ for(h=1;I[0]!=-(oldsize+1);h+=h) {
124
+ len=0;
125
+ for(i=0;i<oldsize+1;) {
126
+ if(I[i]<0) {
127
+ len-=I[i];
128
+ i-=I[i];
129
+ } else {
130
+ if(len) I[i-len]=-len;
131
+ len=V[I[i]]+1-i;
132
+ split(I,V,i,len,h);
133
+ i+=len;
134
+ len=0;
135
+ };
136
+ };
137
+ if(len) I[i-len]=-len;
138
+ };
139
+
140
+ for(i=0;i<oldsize+1;i++) I[V[i]]=i;
141
+ }
142
+
143
+ static off_t matchlen(u_char *old,off_t oldsize,u_char *new,off_t newsize)
144
+ {
145
+ off_t i;
146
+
147
+ for(i=0;(i<oldsize)&&(i<newsize);i++)
148
+ if(old[i]!=new[i]) break;
149
+
150
+ return i;
151
+ }
152
+
153
+ static off_t search(off_t *I,u_char *old,off_t oldsize,
154
+ u_char *new,off_t newsize,off_t st,off_t en,off_t *pos)
155
+ {
156
+ off_t x,y;
157
+
158
+ if(en-st<2) {
159
+ x=matchlen(old+I[st],oldsize-I[st],new,newsize);
160
+ y=matchlen(old+I[en],oldsize-I[en],new,newsize);
161
+
162
+ if(x>y) {
163
+ *pos=I[st];
164
+ return x;
165
+ } else {
166
+ *pos=I[en];
167
+ return y;
168
+ }
169
+ };
170
+
171
+ x=st+(en-st)/2;
172
+ if(memcmp(old+I[x],new,MIN(oldsize-I[x],newsize))<0) {
173
+ return search(I,old,oldsize,new,newsize,x,en,pos);
174
+ } else {
175
+ return search(I,old,oldsize,new,newsize,st,x,pos);
176
+ };
177
+ }
178
+
179
+ static void offtout(off_t x,u_char *buf)
180
+ {
181
+ off_t y;
182
+
183
+ if(x<0) y=-x; else y=x;
184
+
185
+ buf[0]=y%256;y-=buf[0];
186
+ y=y/256;buf[1]=y%256;y-=buf[1];
187
+ y=y/256;buf[2]=y%256;y-=buf[2];
188
+ y=y/256;buf[3]=y%256;y-=buf[3];
189
+ y=y/256;buf[4]=y%256;y-=buf[4];
190
+ y=y/256;buf[5]=y%256;y-=buf[5];
191
+ y=y/256;buf[6]=y%256;y-=buf[6];
192
+ y=y/256;buf[7]=y%256;
193
+
194
+ if(x<0) buf[7]|=0x80;
195
+ }
196
+
197
+ /* create a patch file from oldfile diff'ed by newfile */
198
+ int bsdiff_files(const char *oldfile, const char *newfile, const char *patchfile)
199
+ {
200
+ int fd;
201
+ u_char *old,*new;
202
+ off_t oldsize,newsize;
203
+ off_t *I,*V;
204
+ off_t scan,pos=0,len;
205
+ off_t lastscan,lastpos,lastoffset;
206
+ off_t oldscore,scsc;
207
+ off_t s,Sf,lenf,Sb,lenb;
208
+ off_t overlap,Ss,lens;
209
+ off_t i;
210
+ off_t dblen,eblen;
211
+ u_char *db,*eb;
212
+ u_char buf[8];
213
+ u_char header[32];
214
+ FILE * pf;
215
+ BZFILE * pfbz2;
216
+ int bz2err;
217
+
218
+ /* Allocate oldsize+1 bytes instead of oldsize bytes to ensure
219
+ that we never try to malloc(0) and get a NULL pointer */
220
+ if(((fd=open(oldfile,O_RDONLY,0))<0) ||
221
+ ((oldsize=lseek(fd,0,SEEK_END))==-1) ||
222
+ ((old=malloc(oldsize+1))==NULL) ||
223
+ (lseek(fd,0,SEEK_SET)!=0) ||
224
+ (read(fd,old,oldsize)!=oldsize) ||
225
+ (close(fd)==-1)) {
226
+ rb_raise(rb_eRuntimeError, "%s",oldfile);
227
+ }
228
+
229
+ if(((I=malloc((oldsize+1)*sizeof(off_t)))==NULL) ||
230
+ ((V=malloc((oldsize+1)*sizeof(off_t)))==NULL)) rb_raise(rb_eRuntimeError, "malloc error");
231
+
232
+ qsufsort(I,V,old,oldsize);
233
+
234
+ free(V);
235
+
236
+ /* Allocate newsize+1 bytes instead of newsize bytes to ensure
237
+ that we never try to malloc(0) and get a NULL pointer */
238
+ if(((fd=open(newfile,O_RDONLY,0))<0) ||
239
+ ((newsize=lseek(fd,0,SEEK_END))==-1) ||
240
+ ((new=malloc(newsize+1))==NULL) ||
241
+ (lseek(fd,0,SEEK_SET)!=0) ||
242
+ (read(fd,new,newsize)!=newsize) ||
243
+ (close(fd)==-1)) rb_raise(rb_eRuntimeError, "%s",newfile);
244
+
245
+ if(((db=malloc(newsize+1))==NULL) ||
246
+ ((eb=malloc(newsize+1))==NULL)) rb_raise(rb_eRuntimeError,"malloc error");
247
+ dblen=0;
248
+ eblen=0;
249
+
250
+ /* Create the patch file */
251
+ if ((pf = fopen(patchfile, "w")) == NULL)
252
+ rb_raise(rb_eRuntimeError, "%s", patchfile);
253
+
254
+ /* Header is
255
+ 0 8 "BSDIFF40"
256
+ 8 8 length of bzip2ed ctrl block
257
+ 16 8 length of bzip2ed diff block
258
+ 24 8 length of new file */
259
+ /* File is
260
+ 0 32 Header
261
+ 32 ?? Bzip2ed ctrl block
262
+ ?? ?? Bzip2ed diff block
263
+ ?? ?? Bzip2ed extra block */
264
+ memcpy(header,"BSDIFF40",8);
265
+ offtout(0, header + 8);
266
+ offtout(0, header + 16);
267
+ offtout(newsize, header + 24);
268
+ if (fwrite(header, 32, 1, pf) != 1)
269
+ rb_raise(rb_eRuntimeError, "fwrite(%s)", patchfile);
270
+
271
+ /* Compute the differences, writing ctrl as we go */
272
+ if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
273
+ rb_raise(rb_eRuntimeError, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
274
+ scan=0;len=0;
275
+ lastscan=0;lastpos=0;lastoffset=0;
276
+ while(scan<newsize) {
277
+ oldscore=0;
278
+
279
+ for(scsc=scan+=len;scan<newsize;scan++) {
280
+ len=search(I,old,oldsize,new+scan,newsize-scan,
281
+ 0,oldsize,&pos);
282
+
283
+ for(;scsc<scan+len;scsc++)
284
+ if((scsc+lastoffset<oldsize) &&
285
+ (old[scsc+lastoffset] == new[scsc]))
286
+ oldscore++;
287
+
288
+ if(((len==oldscore) && (len!=0)) ||
289
+ (len>oldscore+8)) break;
290
+
291
+ if((scan+lastoffset<oldsize) &&
292
+ (old[scan+lastoffset] == new[scan]))
293
+ oldscore--;
294
+ };
295
+
296
+ if((len!=oldscore) || (scan==newsize)) {
297
+ s=0;Sf=0;lenf=0;
298
+ for(i=0;(lastscan+i<scan)&&(lastpos+i<oldsize);) {
299
+ if(old[lastpos+i]==new[lastscan+i]) s++;
300
+ i++;
301
+ if(s*2-i>Sf*2-lenf) { Sf=s; lenf=i; };
302
+ };
303
+
304
+ lenb=0;
305
+ if(scan<newsize) {
306
+ s=0;Sb=0;
307
+ for(i=1;(scan>=lastscan+i)&&(pos>=i);i++) {
308
+ if(old[pos-i]==new[scan-i]) s++;
309
+ if(s*2-i>Sb*2-lenb) { Sb=s; lenb=i; };
310
+ };
311
+ };
312
+
313
+ if(lastscan+lenf>scan-lenb) {
314
+ overlap=(lastscan+lenf)-(scan-lenb);
315
+ s=0;Ss=0;lens=0;
316
+ for(i=0;i<overlap;i++) {
317
+ if(new[lastscan+lenf-overlap+i]==
318
+ old[lastpos+lenf-overlap+i]) s++;
319
+ if(new[scan-lenb+i]==
320
+ old[pos-lenb+i]) s--;
321
+ if(s>Ss) { Ss=s; lens=i+1; };
322
+ };
323
+
324
+ lenf+=lens-overlap;
325
+ lenb-=lens;
326
+ };
327
+
328
+ for(i=0;i<lenf;i++)
329
+ db[dblen+i]=new[lastscan+i]-old[lastpos+i];
330
+ for(i=0;i<(scan-lenb)-(lastscan+lenf);i++)
331
+ eb[eblen+i]=new[lastscan+lenf+i];
332
+
333
+ dblen+=lenf;
334
+ eblen+=(scan-lenb)-(lastscan+lenf);
335
+
336
+ offtout(lenf,buf);
337
+ BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
338
+ if (bz2err != BZ_OK)
339
+ rb_raise(rb_eRuntimeError, "BZ2_bzWrite, bz2err = %d", bz2err);
340
+
341
+ offtout((scan-lenb)-(lastscan+lenf),buf);
342
+ BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
343
+ if (bz2err != BZ_OK)
344
+ rb_raise(rb_eRuntimeError, "BZ2_bzWrite, bz2err = %d", bz2err);
345
+
346
+ offtout((pos-lenb)-(lastpos+lenf),buf);
347
+ BZ2_bzWrite(&bz2err, pfbz2, buf, 8);
348
+ if (bz2err != BZ_OK)
349
+ rb_raise(rb_eRuntimeError, "BZ2_bzWrite, bz2err = %d", bz2err);
350
+
351
+ lastscan=scan-lenb;
352
+ lastpos=pos-lenb;
353
+ lastoffset=pos-scan;
354
+ };
355
+ };
356
+ BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
357
+ if (bz2err != BZ_OK)
358
+ rb_raise(rb_eRuntimeError, "BZ2_bzWriteClose, bz2err = %d", bz2err);
359
+
360
+ /* Compute size of compressed ctrl data */
361
+ if ((len = ftello(pf)) == -1)
362
+ rb_raise(rb_eRuntimeError, "ftello");
363
+ offtout(len-32, header + 8);
364
+
365
+ /* Write compressed diff data */
366
+ if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
367
+ rb_raise(rb_eRuntimeError, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
368
+ BZ2_bzWrite(&bz2err, pfbz2, db, dblen);
369
+ if (bz2err != BZ_OK)
370
+ rb_raise(rb_eRuntimeError, "BZ2_bzWrite, bz2err = %d", bz2err);
371
+ BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
372
+ if (bz2err != BZ_OK)
373
+ rb_raise(rb_eRuntimeError, "BZ2_bzWriteClose, bz2err = %d", bz2err);
374
+
375
+ /* Compute size of compressed diff data */
376
+ if ((newsize = ftello(pf)) == -1)
377
+ rb_raise(rb_eRuntimeError, "ftello");
378
+ offtout(newsize - len, header + 16);
379
+
380
+ /* Write compressed extra data */
381
+ if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
382
+ rb_raise(rb_eRuntimeError, "BZ2_bzWriteOpen, bz2err = %d", bz2err);
383
+ BZ2_bzWrite(&bz2err, pfbz2, eb, eblen);
384
+ if (bz2err != BZ_OK)
385
+ rb_raise(rb_eRuntimeError, "BZ2_bzWrite, bz2err = %d", bz2err);
386
+ BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL);
387
+ if (bz2err != BZ_OK)
388
+ rb_raise(rb_eRuntimeError, "BZ2_bzWriteClose, bz2err = %d", bz2err);
389
+
390
+ /* Seek to the beginning, write the header, and close the file */
391
+ if (fseeko(pf, 0, SEEK_SET))
392
+ rb_raise(rb_eRuntimeError, "fseeko");
393
+ if (fwrite(header, 32, 1, pf) != 1)
394
+ rb_raise(rb_eRuntimeError, "fwrite(%s)", patchfile);
395
+ if (fclose(pf))
396
+ rb_raise(rb_eRuntimeError, "fclose");
397
+
398
+ /* Free the memory we used */
399
+ free(db);
400
+ free(eb);
401
+ free(I);
402
+ free(old);
403
+ free(new);
404
+
405
+ return 0;
406
+ }
407
+
408
+ /*
409
+ * create a patch buffer from oldbfufer diff'ed by newbfufer
410
+ *
411
+ * same as bsdiff_files, except works in memory
412
+ *
413
+ * TODO
414
+ */
415
+ //char* bsdiff_buffer(const char *oldbuffer, const char *newbuffer)
416
+ //{
417
+ //}
data/ext/bsdiff.h ADDED
@@ -0,0 +1,6 @@
1
+ #ifndef RB_BSDIFF_H
2
+ #define RB_BSDIFF_H
3
+
4
+ int bsdiff_files(const char *oldfile, const char *newfile, const char *patchfile);
5
+
6
+ #endif
data/ext/bspatch.c ADDED
@@ -0,0 +1,203 @@
1
+ /*-
2
+ * Copyright 2003-2005 Colin Percival
3
+ * All rights reserved
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted providing that the following conditions
7
+ * are met:
8
+ * 1. Redistributions of source code must retain the above copyright
9
+ * notice, this list of conditions and the following disclaimer.
10
+ * 2. Redistributions in binary form must reproduce the above copyright
11
+ * notice, this list of conditions and the following disclaimer in the
12
+ * documentation and/or other materials provided with the distribution.
13
+ *
14
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
18
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
23
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24
+ * POSSIBILITY OF SUCH DAMAGE.
25
+ */
26
+
27
+ #if 0
28
+ __FBSDID("$FreeBSD: src/usr.bin/bsdiff/bspatch/bspatch.c,v 1.1 2005/08/06 01:59:06 cperciva Exp $");
29
+ #endif
30
+
31
+ #include <bzlib.h>
32
+ #include <stdlib.h>
33
+ #include <stdio.h>
34
+ #include <string.h>
35
+ #include <unistd.h>
36
+ #include <fcntl.h>
37
+
38
+ #include <ruby.h>
39
+
40
+ static off_t offtin(u_char *buf)
41
+ {
42
+ off_t y;
43
+
44
+ y=buf[7]&0x7F;
45
+ y=y*256;y+=buf[6];
46
+ y=y*256;y+=buf[5];
47
+ y=y*256;y+=buf[4];
48
+ y=y*256;y+=buf[3];
49
+ y=y*256;y+=buf[2];
50
+ y=y*256;y+=buf[1];
51
+ y=y*256;y+=buf[0];
52
+
53
+ if(buf[7]&0x80) y=-y;
54
+
55
+ return y;
56
+ }
57
+
58
+ int bspatch_files(const char *oldfile, const char *newfile, const char *patchfile)
59
+ {
60
+ FILE * f, * cpf, * dpf, * epf;
61
+ BZFILE * cpfbz2, * dpfbz2, * epfbz2;
62
+ int cbz2err, dbz2err, ebz2err;
63
+ int fd;
64
+ ssize_t oldsize,newsize;
65
+ ssize_t bzctrllen,bzdatalen;
66
+ u_char header[32],buf[8];
67
+ u_char *old, *new;
68
+ off_t oldpos,newpos;
69
+ off_t ctrl[3];
70
+ off_t lenread;
71
+ off_t i;
72
+
73
+ /* Open patch file */
74
+ if ((f = fopen(patchfile, "r")) == NULL)
75
+ rb_raise(rb_eRuntimeError, "fopen(%s)", patchfile);
76
+
77
+ /*
78
+ File format:
79
+ 0 8 "BSDIFF40"
80
+ 8 8 X
81
+ 16 8 Y
82
+ 24 8 sizeof(newfile)
83
+ 32 X bzip2(control block)
84
+ 32+X Y bzip2(diff block)
85
+ 32+X+Y ??? bzip2(extra block)
86
+ with control block a set of triples (x,y,z) meaning "add x bytes
87
+ from oldfile to x bytes from the diff block; copy y bytes from the
88
+ extra block; seek forwards in oldfile by z bytes".
89
+ */
90
+
91
+ /* Read header */
92
+ if (fread(header, 1, 32, f) < 32) {
93
+ if (feof(f))
94
+ rb_raise(rb_eRuntimeError,"Corrupt patch\n");
95
+ rb_raise(rb_eRuntimeError, "fread(%s)", patchfile);
96
+ }
97
+
98
+ /* Check for appropriate magic */
99
+ if (memcmp(header, "BSDIFF40", 8) != 0)
100
+ rb_raise(rb_eRuntimeError, "Corrupt patch\n");
101
+
102
+ /* Read lengths from header */
103
+ bzctrllen=offtin(header+8);
104
+ bzdatalen=offtin(header+16);
105
+ newsize=offtin(header+24);
106
+ if((bzctrllen<0) || (bzdatalen<0) || (newsize<0))
107
+ rb_raise(rb_eRuntimeError,"Corrupt patch\n");
108
+
109
+ /* Close patch file and re-open it via libbzip2 at the right places */
110
+ if (fclose(f))
111
+ rb_raise(rb_eRuntimeError, "fclose(%s)", patchfile);
112
+ if ((cpf = fopen(patchfile, "r")) == NULL)
113
+ rb_raise(rb_eRuntimeError, "fopen(%s)", patchfile);
114
+ if (fseeko(cpf, 32, SEEK_SET))
115
+ rb_raise(rb_eRuntimeError, "fseeko(%s, %lld)", patchfile,
116
+ (long long)32);
117
+ if ((cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0)) == NULL)
118
+ rb_raise(rb_eRuntimeError, "BZ2_bzReadOpen, bz2err = %d", cbz2err);
119
+ if ((dpf = fopen(patchfile, "r")) == NULL)
120
+ rb_raise(rb_eRuntimeError, "fopen(%s)", patchfile);
121
+ if (fseeko(dpf, 32 + bzctrllen, SEEK_SET))
122
+ rb_raise(rb_eRuntimeError, "fseeko(%s, %lld)", patchfile,
123
+ (long long)(32 + bzctrllen));
124
+ if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL)
125
+ rb_raise(rb_eRuntimeError, "BZ2_bzReadOpen, bz2err = %d", dbz2err);
126
+ if ((epf = fopen(patchfile, "r")) == NULL)
127
+ rb_raise(rb_eRuntimeError, "fopen(%s)", patchfile);
128
+ if (fseeko(epf, 32 + bzctrllen + bzdatalen, SEEK_SET))
129
+ rb_raise(rb_eRuntimeError, "fseeko(%s, %lld)", patchfile,
130
+ (long long)(32 + bzctrllen + bzdatalen));
131
+ if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL)
132
+ rb_raise(rb_eRuntimeError, "BZ2_bzReadOpen, bz2err = %d", ebz2err);
133
+
134
+ if(((fd=open(oldfile,O_RDONLY,0))<0) ||
135
+ ((oldsize=lseek(fd,0,SEEK_END))==-1) ||
136
+ ((old=malloc(oldsize+1))==NULL) ||
137
+ (lseek(fd,0,SEEK_SET)!=0) ||
138
+ (read(fd,old,oldsize)!=oldsize) ||
139
+ (close(fd)==-1)) rb_raise(rb_eRuntimeError,"%s",oldfile);
140
+ if((new=malloc(newsize+1))==NULL) rb_raise(rb_eRuntimeError,"malloc");
141
+
142
+ oldpos=0;newpos=0;
143
+ while(newpos<newsize) {
144
+ /* Read control data */
145
+ for(i=0;i<=2;i++) {
146
+ lenread = BZ2_bzRead(&cbz2err, cpfbz2, buf, 8);
147
+ if ((lenread < 8) || ((cbz2err != BZ_OK) &&
148
+ (cbz2err != BZ_STREAM_END)))
149
+ rb_raise(rb_eRuntimeError, "Corrupt patch\n");
150
+ ctrl[i]=offtin(buf);
151
+ };
152
+
153
+ /* Sanity-check */
154
+ if(newpos+ctrl[0]>newsize)
155
+ rb_raise(rb_eRuntimeError,"Corrupt patch\n");
156
+
157
+ /* Read diff string */
158
+ lenread = BZ2_bzRead(&dbz2err, dpfbz2, new + newpos, ctrl[0]);
159
+ if ((lenread < ctrl[0]) ||
160
+ ((dbz2err != BZ_OK) && (dbz2err != BZ_STREAM_END)))
161
+ rb_raise(rb_eRuntimeError, "Corrupt patch\n");
162
+
163
+ /* Add old data to diff string */
164
+ for(i=0;i<ctrl[0];i++)
165
+ if((oldpos+i>=0) && (oldpos+i<oldsize))
166
+ new[newpos+i]+=old[oldpos+i];
167
+
168
+ /* Adjust pointers */
169
+ newpos+=ctrl[0];
170
+ oldpos+=ctrl[0];
171
+
172
+ /* Sanity-check */
173
+ if(newpos+ctrl[1]>newsize)
174
+ rb_raise(rb_eRuntimeError,"Corrupt patch\n");
175
+
176
+ /* Read extra string */
177
+ lenread = BZ2_bzRead(&ebz2err, epfbz2, new + newpos, ctrl[1]);
178
+ if ((lenread < ctrl[1]) ||
179
+ ((ebz2err != BZ_OK) && (ebz2err != BZ_STREAM_END)))
180
+ rb_raise(rb_eRuntimeError, "Corrupt patch\n");
181
+
182
+ /* Adjust pointers */
183
+ newpos+=ctrl[1];
184
+ oldpos+=ctrl[2];
185
+ };
186
+
187
+ /* Clean up the bzip2 reads */
188
+ BZ2_bzReadClose(&cbz2err, cpfbz2);
189
+ BZ2_bzReadClose(&dbz2err, dpfbz2);
190
+ BZ2_bzReadClose(&ebz2err, epfbz2);
191
+ if (fclose(cpf) || fclose(dpf) || fclose(epf))
192
+ rb_raise(rb_eRuntimeError, "fclose(%s)", patchfile);
193
+
194
+ /* Write the new file */
195
+ if(((fd=open(newfile,O_CREAT|O_TRUNC|O_WRONLY,0666))<0) ||
196
+ (write(fd,new,newsize)!=newsize) || (close(fd)==-1))
197
+ rb_raise(rb_eRuntimeError,"%s",newfile);
198
+
199
+ free(new);
200
+ free(old);
201
+
202
+ return 0;
203
+ }
data/ext/bspatch.h ADDED
@@ -0,0 +1,6 @@
1
+ #ifndef BS_PATCH_H
2
+ #define BS_PATCH_H
3
+
4
+ int bspatch_files(const char *oldfile, const char *newfile, const char *patchfile);
5
+
6
+ #endif
data/ext/extconf.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'mkmf'
2
+
3
+ dir_config('bsdiff')
4
+ fail unless have_header('unistd.h')
5
+ fail unless have_header('bzlib.h')
6
+ fail unless have_library('bz2')
7
+ fail unless have_func('BZ2_bzWrite','bzlib.h')
8
+ fail unless have_macro('BZ_OK','bzlib.h')
9
+ create_header('bsdiff_config.h')
10
+ create_makefile('bsdiff')
data/ext/rb_bsdiff.c ADDED
@@ -0,0 +1,30 @@
1
+ #include <ruby.h>
2
+ #include "bsdiff.h"
3
+ #include "bspatch.h"
4
+
5
+ VALUE BSDiff;
6
+
7
+ static VALUE bsdiff_diff(VALUE mod, VALUE oldfile, VALUE newfile, VALUE patchfile)
8
+ {
9
+ if( bsdiff_files(StringValuePtr(oldfile), StringValuePtr(newfile), StringValuePtr(patchfile)) ) {
10
+ return Qfalse;
11
+ }
12
+ return Qtrue;
13
+ }
14
+
15
+ static VALUE bsdiff_patch(VALUE mod, VALUE oldfile, VALUE newfile, VALUE patchfile)
16
+ {
17
+ if( bspatch_files(StringValuePtr(oldfile), StringValuePtr(newfile), StringValuePtr(patchfile)) ) {
18
+ return Qfalse;
19
+ }
20
+ return Qtrue;
21
+ }
22
+
23
+ /* main entry point */
24
+ void Init_bsdiff()
25
+ {
26
+ BSDiff = rb_define_module("BSDiff");
27
+
28
+ rb_define_singleton_method(BSDiff, "diff", bsdiff_diff, 3);
29
+ rb_define_singleton_method(BSDiff, "patch", bsdiff_patch, 3);
30
+ }
data/rb-bsdiff.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{rb-bsdiff}
5
+ s.version = "0.1.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Todd Fisher"]
9
+ s.date = %q{2009-07-19}
10
+ s.description = %q{Ruby bindings to bindary diff tools bsdiff and bspatch}
11
+ s.email = %q{todd.fisher@gmail.com}
12
+ s.extensions = ["ext/extconf.rb"]
13
+ s.files = ["README", "Rakefile", "rb-bsdiff.gemspec", "test.rb", "ext/b0",
14
+ "ext/b1", "ext/bsdiff.c", "ext/bsdiff.h", "ext/bspatch.c",
15
+ "ext/bspatch.h", "ext/extconf.rb", "ext/rb_bsdiff.c"]
16
+ s.has_rdoc = true
17
+ s.homepage = %q{http://github.com/taf2/rb-bsdiff}
18
+ s.rdoc_options = ["--inline-source", "--charset=UTF-8"]
19
+ s.require_paths = ["lib"]
20
+ s.rubyforge_project = %q{rb-bsdiff}
21
+ s.rubygems_version = %q{1.3.0}
22
+ s.summary = %q{Ruby bindings to bindary diff tools bsdiff and bspatch}
23
+
24
+ if s.respond_to? :specification_version then
25
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
26
+ s.specification_version = 2
27
+ end
28
+ end
29
+
data/test.rb ADDED
@@ -0,0 +1,38 @@
1
+ require 'openssl'
2
+ require 'test/unit'
3
+ require File.join(File.dirname(__FILE__),'ext','bsdiff')
4
+
5
+ class TestPatch < Test::Unit::TestCase
6
+
7
+ def setup
8
+ File.unlink('b3') if File.exist?('b3')
9
+ File.unlink('p0') if File.exist?('p0')
10
+ end
11
+
12
+ def teardown
13
+ File.unlink('b3') if File.exist?('b3')
14
+ File.unlink('p0') if File.exist?('p0')
15
+ end
16
+
17
+ def test_diff_and_patch
18
+
19
+ #../bsdiff b0 b1 p0
20
+ #../bspatch b0 b3 p0
21
+
22
+ b0_chk = OpenSSL::Digest::MD5.hexdigest(File.read('ext/b0'))
23
+ b1_chk = OpenSSL::Digest::MD5.hexdigest(File.read('ext/b1'))
24
+
25
+ # create patch file from bspatch.o to bsdiff.o
26
+ BSDiff.diff('ext/b0', 'ext/b1', 'p0')
27
+
28
+ # apply patch to bspatch.o as bspatch2.o
29
+ BSDiff.patch('ext/b0', 'b3', 'p0')
30
+
31
+ b3_chk = OpenSSL::Digest::MD5.hexdigest(File.read('b3'))
32
+
33
+ # bspatch2.o should equal bsdiff.o
34
+ assert_equal(b1_chk,b3_chk)
35
+
36
+ end
37
+
38
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jwilkins-rb-bsdiff
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Todd Fisher
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-07-19 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Ruby bindings to bindary diff tools bsdiff and bspatch
17
+ email: todd.fisher@gmail.com
18
+ executables: []
19
+
20
+ extensions:
21
+ - ext/extconf.rb
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - README
26
+ - Rakefile
27
+ - rb-bsdiff.gemspec
28
+ - test.rb
29
+ - ext/b0
30
+ - ext/b1
31
+ - ext/bsdiff.c
32
+ - ext/bsdiff.h
33
+ - ext/bspatch.c
34
+ - ext/bspatch.h
35
+ - ext/extconf.rb
36
+ - ext/rb_bsdiff.c
37
+ has_rdoc: true
38
+ homepage: http://github.com/taf2/rb-bsdiff
39
+ post_install_message:
40
+ rdoc_options:
41
+ - --inline-source
42
+ - --charset=UTF-8
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ requirements: []
58
+
59
+ rubyforge_project: rb-bsdiff
60
+ rubygems_version: 1.2.0
61
+ signing_key:
62
+ specification_version: 2
63
+ summary: Ruby bindings to bindary diff tools bsdiff and bspatch
64
+ test_files: []
65
+