summaryrefslogtreecommitdiff
path: root/rsync/rdiff.c
Unidiff
Diffstat (limited to 'rsync/rdiff.c') (more/less context) (ignore whitespace changes)
-rw-r--r--rsync/rdiff.c358
1 files changed, 358 insertions, 0 deletions
diff --git a/rsync/rdiff.c b/rsync/rdiff.c
new file mode 100644
index 0000000..e08095a
--- a/dev/null
+++ b/rsync/rdiff.c
@@ -0,0 +1,358 @@
1/*= -*- c-basic-offset: 4; indent-tabs-mode: nil; -*-
2 *
3 * librsync -- the library for network deltas
4 * $Id$
5 *
6 * Copyright (C) 1999, 2000, 2001 by Martin Pool <mbp@samba.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 /*
24 | .. after a year and a day, mourning is
25 | dangerous to the survivor and troublesome
26 | to the dead.
27 | -- Harold Bloom
28 */
29
30/*
31 * rdiff.c -- Command-line network-delta tool.
32 *
33 * TODO: Add a -z option to gzip/gunzip patches. This would be
34 * somewhat useful, but more importantly a good test of the streaming
35 * API. Also add -I for bzip2.
36 *
37 * If built with debug support and we have mcheck, then turn it on.
38 * (Optionally?)
39 */
40
41#include <config_rsync.h>
42
43#include <stdlib.h>
44#include <stdio.h>
45#include <fcntl.h>
46#include <popt.h>
47
48#ifdef HAVE_ZLIB_H
49#include <zlib.h>
50#endif
51
52#ifdef HAVE_BZLIB_H
53#include <bzlib.h>
54#endif
55
56#include "rsync.h"
57#include "fileutil.h"
58#include "util.h"
59#include "trace.h"
60#include "isprefix.h"
61
62
63#define PROGRAM "rdiff"
64
65static size_t block_len = RS_DEFAULT_BLOCK_LEN;
66static size_t strong_len = RS_DEFAULT_STRONG_LEN;
67
68static int show_stats = 0;
69
70static int bzip2_level = 0;
71static int gzip_level = 0;
72
73
74enum {
75 OPT_GZIP = 1069, OPT_BZIP2
76};
77
78extern int rs_roll_paranoia;
79
80const struct poptOption opts[] = {
81 { "verbose", 'v', POPT_ARG_NONE, 0, 'v' },
82 { "version", 'V', POPT_ARG_NONE, 0, 'V' },
83 { "input-size", 'I', POPT_ARG_INT, &rs_inbuflen },
84 { "output-size", 'O', POPT_ARG_INT, &rs_outbuflen },
85 { "help", '?', POPT_ARG_NONE, 0, 'h' },
86 { 0, 'h', POPT_ARG_NONE, 0, 'h' },
87 { "block-size", 'b', POPT_ARG_INT, &block_len },
88 { "sum-size", 'S', POPT_ARG_INT, &strong_len },
89 { "statistics", 's', POPT_ARG_NONE, &show_stats },
90 { "stats", 0, POPT_ARG_NONE, &show_stats },
91 { "gzip", 0, POPT_ARG_NONE, 0, OPT_GZIP },
92 { "bzip2", 0, POPT_ARG_NONE, 0, OPT_BZIP2 },
93 { "paranoia", 0, POPT_ARG_NONE, &rs_roll_paranoia },
94 { 0 }
95};
96
97
98static void rdiff_usage(const char *error)
99{
100 fprintf(stderr, "%s\n"
101 "Try `%s --help' for more information.\n",
102 error, PROGRAM);
103}
104
105
106static void rdiff_no_more_args(poptContext opcon)
107{
108 if (poptGetArg(opcon)) {
109 rdiff_usage("rdiff: too many arguments");
110 exit(RS_SYNTAX_ERROR);
111 }
112}
113
114
115static void bad_option(poptContext opcon, int error)
116{
117 char msgbuf[1000];
118
119 snprintf(msgbuf, sizeof msgbuf-1, "%s: %s: %s",
120 PROGRAM, poptStrerror(error), poptBadOption(opcon, 0));
121 rdiff_usage(msgbuf);
122
123 exit(RS_SYNTAX_ERROR);
124}
125
126
127static void help(void) {
128 printf("Usage: rdiff [OPTIONS] signature [BASIS [SIGNATURE]]\n"
129 " [OPTIONS] delta SIGNATURE [NEWFILE [DELTA]]\n"
130 " [OPTIONS] patch BASIS [DELTA [NEWFILE]]\n"
131 "\n"
132 "Options:\n"
133 " -v, --verbose Trace internal processing\n"
134 " -V, --version Show program version\n"
135 " -?, --help Show this help message\n"
136 " -s, --statistics Show performance statistics\n"
137 "Delta-encoding options:\n"
138 " -b, --block-size=BYTES Signature block size\n"
139 " -S, --sum-size=BYTES Set signature strength\n"
140 " --paranoia Verify all rolling checksums\n"
141 "IO options:\n"
142 " -I, --input-size=BYTES Input buffer size\n"
143 " -O, --output-size=BYTES Output buffer size\n"
144 " -z, --gzip[=LEVEL] gzip-compress deltas\n"
145 " -i, --bzip2[=LEVEL] bzip2-compress deltas\n"
146 );
147}
148
149
150static void rdiff_show_version(void)
151{
152 /*
153 * This little declaration is dedicated to Stephen Kapp and Reaper
154 * Technologies, who by all appearances redistributed a modified but
155 * unacknowledged version of GNU Keyring in violation of the licence
156 * and all laws of politeness and good taste.
157 */
158 char const *bzlib = "", *zlib = "", *trace = "";
159
160#ifdef HAVE_LIBZ
161 zlib = ", gzip";
162#endif
163
164#ifdef HAVE_LIBBZ2
165 bzlib = ", bzip2";
166#endif
167
168#ifndef DO_RS_TRACE
169 trace = ", trace disabled";
170#endif
171
172 printf("rdiff (%s) [%s]\n"
173 "Copyright (C) 1997-2001 by Martin Pool, Andrew Tridgell and others.\n"
174 "http://rproxy.samba.org/\n"
175 "Capabilities: %d bit files%s%s%s\n"
176 "\n"
177 "librsync comes with NO WARRANTY, to the extent permitted by law.\n"
178 "You may redistribute copies of librsync under the terms of the GNU\n"
179 "Lesser General Public License. For more information about these\n"
180 "matters, see the files named COPYING.\n",
181 rs_librsync_version, RS_CANONICAL_HOST,
182 8 * sizeof(rs_long_t), zlib, bzlib, trace);
183}
184
185
186
187static void rdiff_options(poptContext opcon)
188{
189 int c;
190 char const *a;
191
192 while ((c = poptGetNextOpt(opcon)) != -1) {
193 switch (c) {
194 case 'h':
195 help();
196 exit(RS_DONE);
197 case 'V':
198 rdiff_show_version();
199 exit(RS_DONE);
200 case 'v':
201 if (!rs_supports_trace()) {
202 rs_error("library does not support trace");
203 }
204 rs_trace_set_level(RS_LOG_DEBUG);
205 break;
206
207 case OPT_GZIP:
208 case OPT_BZIP2:
209 if ((a = poptGetOptArg(opcon))) {
210 int l = atoi(a);
211 if (c == OPT_GZIP)
212 gzip_level = l;
213 else
214 bzip2_level = l;
215 } else {
216 if (c == OPT_GZIP)
217 gzip_level = -1; /* library default */
218 else
219 bzip2_level = 9; /* demand the best */
220 }
221 rs_error("sorry, compression is not really implemented yet");
222 exit(RS_UNIMPLEMENTED);
223
224 default:
225 bad_option(opcon, c);
226 }
227 }
228}
229
230
231/**
232 * Generate signature from remaining command line arguments.
233 */
234static rs_result rdiff_sig(poptContext opcon)
235{
236 FILE *basis_file, *sig_file;
237 rs_stats_t stats;
238 rs_result result;
239
240 basis_file = rs_file_open(poptGetArg(opcon), "rb");
241 sig_file = rs_file_open(poptGetArg(opcon), "wb");
242
243 rdiff_no_more_args(opcon);
244
245 result = rs_sig_file(basis_file, sig_file, block_len, strong_len, &stats);
246 if (result != RS_DONE)
247 return result;
248
249 if (show_stats)
250 rs_log_stats(&stats);
251
252 return result;
253}
254
255
256static rs_result rdiff_delta(poptContext opcon)
257{
258 FILE *sig_file, *new_file, *delta_file;
259 char const *sig_name;
260 rs_result result;
261 rs_signature_t *sumset;
262 rs_stats_t stats;
263
264 if (!(sig_name = poptGetArg(opcon))) {
265 rdiff_usage("Usage for delta: "
266 "rdiff [OPTIONS] delta SIGNATURE [NEWFILE [DELTA]]");
267 return RS_SYNTAX_ERROR;
268 }
269
270 sig_file = rs_file_open(sig_name, "rb");
271 new_file = rs_file_open(poptGetArg(opcon), "rb");
272 delta_file = rs_file_open(poptGetArg(opcon), "wb");
273
274 rdiff_no_more_args(opcon);
275
276 result = rs_loadsig_file(sig_file, &sumset, &stats);
277 if (result != RS_DONE)
278 return result;
279
280 if (show_stats)
281 rs_log_stats(&stats);
282
283 if ((result = rs_build_hash_table(sumset)) != RS_DONE)
284 return result;
285
286 result = rs_delta_file(sumset, new_file, delta_file, &stats);
287
288 if (show_stats)
289 rs_log_stats(&stats);
290
291 return result;
292}
293
294
295
296static rs_result rdiff_patch(poptContext opcon)
297{
298 /* patch BASIS [DELTA [NEWFILE]] */
299 FILE *basis_file, *delta_file, *new_file;
300 char const *basis_name;
301 rs_stats_t stats;
302 rs_result result;
303
304 if (!(basis_name = poptGetArg(opcon))) {
305 rdiff_usage("Usage for patch: "
306 "rdiff [OPTIONS] patch BASIS [DELTA [NEW]]");
307 return RS_SYNTAX_ERROR;
308 }
309
310 basis_file = rs_file_open(basis_name, "rb");
311 delta_file = rs_file_open(poptGetArg(opcon), "rb");
312 new_file = rs_file_open(poptGetArg(opcon), "wb");
313
314 rdiff_no_more_args(opcon);
315
316 result = rs_patch_file(basis_file, delta_file, new_file, &stats);
317
318 if (show_stats)
319 rs_log_stats(&stats);
320
321 return result;
322}
323
324
325
326static rs_result rdiff_action(poptContext opcon)
327{
328 const char *action;
329
330 action = poptGetArg(opcon);
331 if (!action)
332 ;
333 else if (isprefix(action, "signature"))
334 return rdiff_sig(opcon);
335 else if (isprefix(action, "delta"))
336 return rdiff_delta(opcon);
337 else if (isprefix(action, "patch"))
338 return rdiff_patch(opcon);
339
340 rdiff_usage("rdiff: You must specify an action: `signature', `delta', or `patch'.");
341 return RS_SYNTAX_ERROR;
342}
343
344
345int main(const int argc, const char *argv[])
346{
347 poptContext opcon;
348 rs_result result;
349
350 opcon = poptGetContext(PROGRAM, argc, argv, opts, 0);
351 rdiff_options(opcon);
352 result = rdiff_action(opcon);
353
354 if (result != RS_DONE)
355 rs_log(RS_LOG_ERR|RS_LOG_NONAME, "%s", rs_strerror(result));
356
357 return result;
358}