-rw-r--r-- | rsync/rdiff.c | 358 |
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 @@ +/*= -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- + * + * librsync -- the library for network deltas + * $Id$ + * + * Copyright (C) 1999, 2000, 2001 by Martin Pool <mbp@samba.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + /* + | .. after a year and a day, mourning is + | dangerous to the survivor and troublesome + | to the dead. + | -- Harold Bloom + */ + +/* + * rdiff.c -- Command-line network-delta tool. + * + * TODO: Add a -z option to gzip/gunzip patches. This would be + * somewhat useful, but more importantly a good test of the streaming + * API. Also add -I for bzip2. + * + * If built with debug support and we have mcheck, then turn it on. + * (Optionally?) + */ + +#include <config_rsync.h> + +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <popt.h> + +#ifdef HAVE_ZLIB_H +#include <zlib.h> +#endif + +#ifdef HAVE_BZLIB_H +#include <bzlib.h> +#endif + +#include "rsync.h" +#include "fileutil.h" +#include "util.h" +#include "trace.h" +#include "isprefix.h" + + +#define PROGRAM "rdiff" + +static size_t block_len = RS_DEFAULT_BLOCK_LEN; +static size_t strong_len = RS_DEFAULT_STRONG_LEN; + +static int show_stats = 0; + +static int bzip2_level = 0; +static int gzip_level = 0; + + +enum { + OPT_GZIP = 1069, OPT_BZIP2 +}; + +extern int rs_roll_paranoia; + +const struct poptOption opts[] = { + { "verbose", 'v', POPT_ARG_NONE, 0, 'v' }, + { "version", 'V', POPT_ARG_NONE, 0, 'V' }, + { "input-size", 'I', POPT_ARG_INT, &rs_inbuflen }, + { "output-size", 'O', POPT_ARG_INT, &rs_outbuflen }, + { "help", '?', POPT_ARG_NONE, 0, 'h' }, + { 0, 'h', POPT_ARG_NONE, 0, 'h' }, + { "block-size", 'b', POPT_ARG_INT, &block_len }, + { "sum-size", 'S', POPT_ARG_INT, &strong_len }, + { "statistics", 's', POPT_ARG_NONE, &show_stats }, + { "stats", 0, POPT_ARG_NONE, &show_stats }, + { "gzip", 0, POPT_ARG_NONE, 0, OPT_GZIP }, + { "bzip2", 0, POPT_ARG_NONE, 0, OPT_BZIP2 }, + { "paranoia", 0, POPT_ARG_NONE, &rs_roll_paranoia }, + { 0 } +}; + + +static void rdiff_usage(const char *error) +{ + fprintf(stderr, "%s\n" + "Try `%s --help' for more information.\n", + error, PROGRAM); +} + + +static void rdiff_no_more_args(poptContext opcon) +{ + if (poptGetArg(opcon)) { + rdiff_usage("rdiff: too many arguments"); + exit(RS_SYNTAX_ERROR); + } +} + + +static void bad_option(poptContext opcon, int error) +{ + char msgbuf[1000]; + + snprintf(msgbuf, sizeof msgbuf-1, "%s: %s: %s", + PROGRAM, poptStrerror(error), poptBadOption(opcon, 0)); + rdiff_usage(msgbuf); + + exit(RS_SYNTAX_ERROR); +} + + +static void help(void) { + printf("Usage: rdiff [OPTIONS] signature [BASIS [SIGNATURE]]\n" + " [OPTIONS] delta SIGNATURE [NEWFILE [DELTA]]\n" + " [OPTIONS] patch BASIS [DELTA [NEWFILE]]\n" + "\n" + "Options:\n" + " -v, --verbose Trace internal processing\n" + " -V, --version Show program version\n" + " -?, --help Show this help message\n" + " -s, --statistics Show performance statistics\n" + "Delta-encoding options:\n" + " -b, --block-size=BYTES Signature block size\n" + " -S, --sum-size=BYTES Set signature strength\n" + " --paranoia Verify all rolling checksums\n" + "IO options:\n" + " -I, --input-size=BYTES Input buffer size\n" + " -O, --output-size=BYTES Output buffer size\n" + " -z, --gzip[=LEVEL] gzip-compress deltas\n" + " -i, --bzip2[=LEVEL] bzip2-compress deltas\n" + ); +} + + +static void rdiff_show_version(void) +{ + /* + * This little declaration is dedicated to Stephen Kapp and Reaper + * Technologies, who by all appearances redistributed a modified but + * unacknowledged version of GNU Keyring in violation of the licence + * and all laws of politeness and good taste. + */ + char const *bzlib = "", *zlib = "", *trace = ""; + +#ifdef HAVE_LIBZ + zlib = ", gzip"; +#endif + +#ifdef HAVE_LIBBZ2 + bzlib = ", bzip2"; +#endif + +#ifndef DO_RS_TRACE + trace = ", trace disabled"; +#endif + + printf("rdiff (%s) [%s]\n" + "Copyright (C) 1997-2001 by Martin Pool, Andrew Tridgell and others.\n" + "http://rproxy.samba.org/\n" + "Capabilities: %d bit files%s%s%s\n" + "\n" + "librsync comes with NO WARRANTY, to the extent permitted by law.\n" + "You may redistribute copies of librsync under the terms of the GNU\n" + "Lesser General Public License. For more information about these\n" + "matters, see the files named COPYING.\n", + rs_librsync_version, RS_CANONICAL_HOST, + 8 * sizeof(rs_long_t), zlib, bzlib, trace); +} + + + +static void rdiff_options(poptContext opcon) +{ + int c; + char const *a; + + while ((c = poptGetNextOpt(opcon)) != -1) { + switch (c) { + case 'h': + help(); + exit(RS_DONE); + case 'V': + rdiff_show_version(); + exit(RS_DONE); + case 'v': + if (!rs_supports_trace()) { + rs_error("library does not support trace"); + } + rs_trace_set_level(RS_LOG_DEBUG); + break; + + case OPT_GZIP: + case OPT_BZIP2: + if ((a = poptGetOptArg(opcon))) { + int l = atoi(a); + if (c == OPT_GZIP) + gzip_level = l; + else + bzip2_level = l; + } else { + if (c == OPT_GZIP) + gzip_level = -1; /* library default */ + else + bzip2_level = 9; /* demand the best */ + } + rs_error("sorry, compression is not really implemented yet"); + exit(RS_UNIMPLEMENTED); + + default: + bad_option(opcon, c); + } + } +} + + +/** + * Generate signature from remaining command line arguments. + */ +static rs_result rdiff_sig(poptContext opcon) +{ + FILE *basis_file, *sig_file; + rs_stats_t stats; + rs_result result; + + basis_file = rs_file_open(poptGetArg(opcon), "rb"); + sig_file = rs_file_open(poptGetArg(opcon), "wb"); + + rdiff_no_more_args(opcon); + + result = rs_sig_file(basis_file, sig_file, block_len, strong_len, &stats); + if (result != RS_DONE) + return result; + + if (show_stats) + rs_log_stats(&stats); + + return result; +} + + +static rs_result rdiff_delta(poptContext opcon) +{ + FILE *sig_file, *new_file, *delta_file; + char const *sig_name; + rs_result result; + rs_signature_t *sumset; + rs_stats_t stats; + + if (!(sig_name = poptGetArg(opcon))) { + rdiff_usage("Usage for delta: " + "rdiff [OPTIONS] delta SIGNATURE [NEWFILE [DELTA]]"); + return RS_SYNTAX_ERROR; + } + + sig_file = rs_file_open(sig_name, "rb"); + new_file = rs_file_open(poptGetArg(opcon), "rb"); + delta_file = rs_file_open(poptGetArg(opcon), "wb"); + + rdiff_no_more_args(opcon); + + result = rs_loadsig_file(sig_file, &sumset, &stats); + if (result != RS_DONE) + return result; + + if (show_stats) + rs_log_stats(&stats); + + if ((result = rs_build_hash_table(sumset)) != RS_DONE) + return result; + + result = rs_delta_file(sumset, new_file, delta_file, &stats); + + if (show_stats) + rs_log_stats(&stats); + + return result; +} + + + +static rs_result rdiff_patch(poptContext opcon) +{ + /* patch BASIS [DELTA [NEWFILE]] */ + FILE *basis_file, *delta_file, *new_file; + char const *basis_name; + rs_stats_t stats; + rs_result result; + + if (!(basis_name = poptGetArg(opcon))) { + rdiff_usage("Usage for patch: " + "rdiff [OPTIONS] patch BASIS [DELTA [NEW]]"); + return RS_SYNTAX_ERROR; + } + + basis_file = rs_file_open(basis_name, "rb"); + delta_file = rs_file_open(poptGetArg(opcon), "rb"); + new_file = rs_file_open(poptGetArg(opcon), "wb"); + + rdiff_no_more_args(opcon); + + result = rs_patch_file(basis_file, delta_file, new_file, &stats); + + if (show_stats) + rs_log_stats(&stats); + + return result; +} + + + +static rs_result rdiff_action(poptContext opcon) +{ + const char *action; + + action = poptGetArg(opcon); + if (!action) + ; + else if (isprefix(action, "signature")) + return rdiff_sig(opcon); + else if (isprefix(action, "delta")) + return rdiff_delta(opcon); + else if (isprefix(action, "patch")) + return rdiff_patch(opcon); + + rdiff_usage("rdiff: You must specify an action: `signature', `delta', or `patch'."); + return RS_SYNTAX_ERROR; +} + + +int main(const int argc, const char *argv[]) +{ + poptContext opcon; + rs_result result; + + opcon = poptGetContext(PROGRAM, argc, argv, opts, 0); + rdiff_options(opcon); + result = rdiff_action(opcon); + + if (result != RS_DONE) + rs_log(RS_LOG_ERR|RS_LOG_NONAME, "%s", rs_strerror(result)); + + return result; +} |