$treeview $search $mathjax $extrastylesheet
librsync
2.3.1
$projectbrief
|
$projectbrief
|
$searchbox |
00001 /*= -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- 00002 * 00003 * librsync -- the library for network deltas 00004 * 00005 * Copyright (C) 1999, 2000, 2001 by Martin Pool <mbp@sourcefrog.net> 00006 * 00007 * This program is free software; you can redistribute it and/or modify 00008 * it under the terms of the GNU Lesser General Public License as published by 00009 * the Free Software Foundation; either version 2.1 of the License, or 00010 * (at your option) any later version. 00011 * 00012 * This program is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 * GNU Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public License 00018 * along with this program; if not, write to the Free Software 00019 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00020 */ 00021 00022 /*= 00023 | .. after a year and a day, mourning is 00024 | dangerous to the survivor and troublesome 00025 | to the dead. 00026 | -- Harold Bloom 00027 */ 00028 00029 /** \file rdiff.c 00030 * Command-line network-delta tool. 00031 * 00032 * \todo Add a -z option to gzip/gunzip patches. This would be somewhat useful, 00033 * but more importantly a good test of the streaming API. Also add -I for 00034 * bzip2. 00035 * 00036 * \todo If built with debug support and we have mcheck, then turn it on. 00037 * (Optionally?) 00038 * 00039 * \todo popt doesn't handle single dashes very well at the moment: we'd like 00040 * to use them as arguments to indicate stdin/stdout, but it turns them into 00041 * options. I sent a patch to the popt maintainers; hopefully it will be fixed 00042 * in the future. 00043 * 00044 * \todo Add an option for delta to check whether the files are identical. */ 00045 00046 #include "config.h" 00047 #include <stdlib.h> 00048 #include <stdarg.h> 00049 #include <string.h> 00050 #include <popt.h> 00051 #include "librsync.h" 00052 #include "isprefix.h" 00053 00054 static int block_len = 0; 00055 static int strong_len = 0; 00056 00057 static int show_stats = 0; 00058 00059 static int bzip2_level = 0; 00060 static int gzip_level = 0; 00061 static int file_force = 0; 00062 00063 enum { 00064 OPT_GZIP = 1069, OPT_BZIP2 00065 }; 00066 00067 char *rs_hash_name; 00068 char *rs_rollsum_name; 00069 00070 static void rdiff_usage(const char *error, ...) 00071 { 00072 va_list va; 00073 char buf[256]; 00074 00075 va_start(va, error); 00076 vsnprintf(buf, sizeof(buf), error, va); 00077 va_end(va); 00078 fprintf(stderr, "rdiff: %s\n\nTry `rdiff --help' for more information.\n", 00079 buf); 00080 } 00081 00082 static void rdiff_no_more_args(poptContext opcon) 00083 { 00084 if (poptGetArg(opcon)) { 00085 rdiff_usage("Too many arguments."); 00086 exit(RS_SYNTAX_ERROR); 00087 } 00088 } 00089 00090 static void bad_option(poptContext opcon, int error) 00091 { 00092 rdiff_usage("%s: %s", poptStrerror(error), poptBadOption(opcon, 0)); 00093 exit(RS_SYNTAX_ERROR); 00094 } 00095 00096 static void help(void) 00097 { 00098 printf("Usage: rdiff [OPTIONS] signature [BASIS [SIGNATURE]]\n" 00099 " [OPTIONS] delta SIGNATURE [NEWFILE [DELTA]]\n" 00100 " [OPTIONS] patch BASIS [DELTA [NEWFILE]]\n" "\n" 00101 "Options:\n" 00102 " -v, --verbose Trace internal processing\n" 00103 " -V, --version Show program version\n" 00104 " -?, --help Show this help message\n" 00105 " -s, --statistics Show performance statistics\n" 00106 " -f, --force Force overwriting existing files\n" 00107 "Signature generation options:\n" 00108 " -H, --hash=ALG Hash algorithm: blake2 (default), md4\n" 00109 " -R, --rollsum=ALG Rollsum algorithm: rabinkarp (default), rollsum\n" 00110 "Delta-encoding options:\n" 00111 " -b, --block-size=BYTES Signature block size, 0 (default) for recommended\n" 00112 " -S, --sum-size=BYTES Signature strength, 0 (default) for max, -1 for min\n" 00113 "IO options:\n" " -I, --input-size=BYTES Input buffer size\n" 00114 " -O, --output-size=BYTES Output buffer size\n" 00115 " -z, --gzip[=LEVEL] gzip-compress deltas\n" 00116 " -i, --bzip2[=LEVEL] bzip2-compress deltas\n"); 00117 } 00118 00119 static void rdiff_show_version(void) 00120 { 00121 char const *bzlib = "", *zlib = "", *trace = ""; 00122 00123 #if 0 00124 /* Compression isn't implemented so don't mention it. */ 00125 # ifdef HAVE_LIBZ 00126 zlib = ", gzip"; 00127 # endif 00128 00129 # ifdef HAVE_LIBBZ2 00130 bzlib = ", bzip2"; 00131 # endif 00132 #endif 00133 00134 #ifndef DO_RS_TRACE 00135 trace = ", trace disabled"; 00136 #endif 00137 00138 printf("rdiff (%s)\n" 00139 "Copyright (C) 1997-2016 by Martin Pool, Andrew Tridgell and others.\n" 00140 "http://librsync.sourcefrog.net/\n" 00141 "Capabilities: %ld bit files%s%s%s\n" "\n" 00142 "librsync comes with NO WARRANTY, to the extent permitted by law.\n" 00143 "You may redistribute copies of librsync under the terms of the GNU\n" 00144 "Lesser General Public License. For more information about these\n" 00145 "matters, see the files named COPYING.\n", rs_librsync_version, 00146 (long)(8 * sizeof(rs_long_t)), zlib, bzlib, trace); 00147 } 00148 00149 static void rdiff_options(poptContext opcon) 00150 { 00151 int c; 00152 char const *a; 00153 00154 while ((c = poptGetNextOpt(opcon)) != -1) { 00155 switch (c) { 00156 case 'h': 00157 help(); 00158 exit(RS_DONE); 00159 case 'V': 00160 rdiff_show_version(); 00161 exit(RS_DONE); 00162 case 'v': 00163 if (!rs_supports_trace()) { 00164 fprintf(stderr, "rdiff: Library does not support trace.\n"); 00165 } 00166 rs_trace_set_level(RS_LOG_DEBUG); 00167 break; 00168 00169 case OPT_GZIP: 00170 case OPT_BZIP2: 00171 if ((a = poptGetOptArg(opcon))) { 00172 int l = atoi(a); 00173 if (c == OPT_GZIP) 00174 gzip_level = l; 00175 else 00176 bzip2_level = l; 00177 } else { 00178 if (c == OPT_GZIP) 00179 gzip_level = -1; /* library default */ 00180 else 00181 bzip2_level = 9; /* demand the best */ 00182 } 00183 rdiff_usage("Sorry, compression is not implemented yet."); 00184 exit(RS_UNIMPLEMENTED); 00185 00186 default: 00187 bad_option(opcon, c); 00188 } 00189 } 00190 } 00191 00192 /** Generate signature from remaining command line arguments. */ 00193 static rs_result rdiff_sig(poptContext opcon) 00194 { 00195 FILE *basis_file, *sig_file; 00196 rs_stats_t stats; 00197 rs_result result; 00198 rs_long_t sig_magic; 00199 00200 basis_file = rs_file_open(poptGetArg(opcon), "rb", file_force); 00201 sig_file = rs_file_open(poptGetArg(opcon), "wb", file_force); 00202 00203 rdiff_no_more_args(opcon); 00204 00205 if (!rs_hash_name || !strcmp(rs_hash_name, "blake2")) { 00206 sig_magic = RS_BLAKE2_SIG_MAGIC; 00207 } else if (!strcmp(rs_hash_name, "md4")) { 00208 sig_magic = RS_MD4_SIG_MAGIC; 00209 } else { 00210 rdiff_usage("Unknown hash algorithm '%s'.", rs_hash_name); 00211 exit(RS_SYNTAX_ERROR); 00212 } 00213 if (!rs_rollsum_name || !strcmp(rs_rollsum_name, "rabinkarp")) { 00214 /* The RabinKarp magics are 0x10 greater than the rollsum magics. */ 00215 sig_magic += 0x10; 00216 } else if (strcmp(rs_rollsum_name, "rollsum")) { 00217 rdiff_usage("Unknown rollsum algorithm '%s'.", rs_rollsum_name); 00218 exit(RS_SYNTAX_ERROR); 00219 } 00220 00221 result = 00222 rs_sig_file(basis_file, sig_file, block_len, strong_len, sig_magic, 00223 &stats); 00224 00225 rs_file_close(sig_file); 00226 rs_file_close(basis_file); 00227 if (result != RS_DONE) 00228 return result; 00229 00230 if (show_stats) 00231 rs_log_stats(&stats); 00232 00233 return result; 00234 } 00235 00236 static rs_result rdiff_delta(poptContext opcon) 00237 { 00238 FILE *sig_file, *new_file, *delta_file; 00239 char const *sig_name; 00240 rs_result result; 00241 rs_signature_t *sumset; 00242 rs_stats_t stats; 00243 00244 if (!(sig_name = poptGetArg(opcon))) { 00245 rdiff_usage("Usage for delta: " 00246 "rdiff [OPTIONS] delta SIGNATURE [NEWFILE [DELTA]]"); 00247 exit(RS_SYNTAX_ERROR); 00248 } 00249 00250 sig_file = rs_file_open(sig_name, "rb", file_force); 00251 new_file = rs_file_open(poptGetArg(opcon), "rb", file_force); 00252 delta_file = rs_file_open(poptGetArg(opcon), "wb", file_force); 00253 00254 rdiff_no_more_args(opcon); 00255 00256 result = rs_loadsig_file(sig_file, &sumset, &stats); 00257 if (result != RS_DONE) 00258 return result; 00259 00260 if (show_stats) 00261 rs_log_stats(&stats); 00262 00263 if ((result = rs_build_hash_table(sumset)) != RS_DONE) 00264 return result; 00265 00266 result = rs_delta_file(sumset, new_file, delta_file, &stats); 00267 00268 rs_file_close(delta_file); 00269 rs_file_close(new_file); 00270 rs_file_close(sig_file); 00271 00272 if (show_stats) { 00273 rs_signature_log_stats(sumset); 00274 rs_log_stats(&stats); 00275 } 00276 00277 rs_free_sumset(sumset); 00278 00279 return result; 00280 } 00281 00282 static rs_result rdiff_patch(poptContext opcon) 00283 { 00284 /* patch BASIS [DELTA [NEWFILE]] */ 00285 FILE *basis_file, *delta_file, *new_file; 00286 char const *basis_name; 00287 rs_stats_t stats; 00288 rs_result result; 00289 00290 if (!(basis_name = poptGetArg(opcon))) { 00291 rdiff_usage("Usage for patch: " 00292 "rdiff [OPTIONS] patch BASIS [DELTA [NEW]]"); 00293 exit(RS_SYNTAX_ERROR); 00294 } 00295 00296 basis_file = rs_file_open(basis_name, "rb", file_force); 00297 delta_file = rs_file_open(poptGetArg(opcon), "rb", file_force); 00298 new_file = rs_file_open(poptGetArg(opcon), "wb", file_force); 00299 00300 rdiff_no_more_args(opcon); 00301 00302 result = rs_patch_file(basis_file, delta_file, new_file, &stats); 00303 00304 rs_file_close(new_file); 00305 rs_file_close(delta_file); 00306 rs_file_close(basis_file); 00307 00308 if (show_stats) 00309 rs_log_stats(&stats); 00310 00311 return result; 00312 } 00313 00314 static rs_result rdiff_action(poptContext opcon) 00315 { 00316 const char *action; 00317 00318 action = poptGetArg(opcon); 00319 if (!action) ; 00320 else if (isprefix(action, "signature")) 00321 return rdiff_sig(opcon); 00322 else if (isprefix(action, "delta")) 00323 return rdiff_delta(opcon); 00324 else if (isprefix(action, "patch")) 00325 return rdiff_patch(opcon); 00326 00327 rdiff_usage 00328 ("You must specify an action: `signature', `delta', or `patch'."); 00329 exit(RS_SYNTAX_ERROR); 00330 } 00331 00332 int main(const int argc, const char *argv[]) 00333 { 00334 /* Initialize opts at runtime to avoid unknown address values. */ 00335 const struct poptOption opts[] = { 00336 {"verbose", 'v', POPT_ARG_NONE, 0, 'v'}, 00337 {"version", 'V', POPT_ARG_NONE, 0, 'V'}, 00338 {"input-size", 'I', POPT_ARG_INT, &rs_inbuflen}, 00339 {"output-size", 'O', POPT_ARG_INT, &rs_outbuflen}, 00340 {"hash", 'H', POPT_ARG_STRING, &rs_hash_name}, 00341 {"rollsum", 'R', POPT_ARG_STRING, &rs_rollsum_name}, 00342 {"help", '?', POPT_ARG_NONE, 0, 'h'}, 00343 {0, 'h', POPT_ARG_NONE, 0, 'h'}, 00344 {"block-size", 'b', POPT_ARG_INT, &block_len}, 00345 {"sum-size", 'S', POPT_ARG_INT, &strong_len}, 00346 {"statistics", 's', POPT_ARG_NONE, &show_stats}, 00347 {"stats", 0, POPT_ARG_NONE, &show_stats}, 00348 {"gzip", 'z', POPT_ARG_NONE, 0, OPT_GZIP}, 00349 {"bzip2", 'i', POPT_ARG_NONE, 0, OPT_BZIP2}, 00350 {"force", 'f', POPT_ARG_NONE, &file_force}, 00351 {0} 00352 }; 00353 00354 poptContext opcon; 00355 rs_result result; 00356 00357 opcon = poptGetContext("rdiff", argc, argv, opts, 0); 00358 rdiff_options(opcon); 00359 result = rdiff_action(opcon); 00360 00361 if (result != RS_DONE) 00362 fprintf(stderr, "rdiff: Failed, %s.\n", rs_strerror(result)); 00363 00364 poptFreeContext(opcon); 00365 return result; 00366 }