librsync  2.3.4
rdiff.c
Go to the documentation of this file.
1 /*= -*- c-basic-offset: 4; indent-tabs-mode: nil; -*-
2  *
3  * librsync -- the library for network deltas
4  *
5  * Copyright (C) 1999, 2000, 2001 by Martin Pool <mbp@sourcefrog.net>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU Lesser General Public License as published by
9  * the Free Software Foundation; either version 2.1 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21 
22  /*=
23  | .. after a year and a day, mourning is
24  | dangerous to the survivor and troublesome
25  | to the dead.
26  | -- Harold Bloom
27  */
28 
29 /** \file rdiff.c
30  * Command-line network-delta tool.
31  *
32  * \todo Add a -z option to gzip/gunzip patches. This would be somewhat useful,
33  * but more importantly a good test of the streaming API. Also add -I for
34  * bzip2.
35  *
36  * \todo If built with debug support and we have mcheck, then turn it on.
37  * (Optionally?)
38  *
39  * \todo popt doesn't handle single dashes very well at the moment: we'd like
40  * to use them as arguments to indicate stdin/stdout, but it turns them into
41  * options. I sent a patch to the popt maintainers; hopefully it will be fixed
42  * in the future.
43  *
44  * \todo Add an option for delta to check whether the files are identical. */
45 
46 #include "config.h" /* IWYU pragma: keep */
47 #include <stdlib.h>
48 #include <stdarg.h>
49 #include <string.h>
50 #include <popt.h>
51 #include <stdio.h>
52 #include "librsync.h"
53 #include "isprefix.h"
54 
55 static int block_len = 0;
56 static int strong_len = 0;
57 
58 static int show_stats = 0;
59 
60 static int bzip2_level = 0;
61 static int gzip_level = 0;
62 static int file_force = 0;
63 
64 enum {
65  OPT_GZIP = 1069, OPT_BZIP2
66 };
67 
68 char *rs_hash_name;
69 char *rs_rollsum_name;
70 
71 static void rdiff_usage(const char *error, ...)
72 {
73  va_list va;
74  char buf[256];
75 
76  va_start(va, error);
77  vsnprintf(buf, sizeof(buf), error, va);
78  va_end(va);
79  fprintf(stderr, "rdiff: %s\n\nTry `rdiff --help' for more information.\n",
80  buf);
81 }
82 
83 static void rdiff_no_more_args(poptContext opcon)
84 {
85  if (poptGetArg(opcon)) {
86  rdiff_usage("Too many arguments.");
87  exit(RS_SYNTAX_ERROR);
88  }
89 }
90 
91 static void bad_option(poptContext opcon, int error)
92 {
93  rdiff_usage("%s: %s", poptStrerror(error), poptBadOption(opcon, 0));
94  exit(RS_SYNTAX_ERROR);
95 }
96 
97 static void help(void)
98 {
99  printf("Usage: rdiff [OPTIONS] signature [BASIS [SIGNATURE]]\n"
100  " [OPTIONS] delta SIGNATURE [NEWFILE [DELTA]]\n"
101  " [OPTIONS] patch BASIS [DELTA [NEWFILE]]\n" "\n"
102  "Options:\n"
103  " -v, --verbose Trace internal processing\n"
104  " -V, --version Show program version\n"
105  " -?, --help Show this help message\n"
106  " -s, --statistics Show performance statistics\n"
107  " -f, --force Force overwriting existing files\n"
108  "Signature generation options:\n"
109  " -H, --hash=ALG Hash algorithm: blake2 (default), md4\n"
110  " -R, --rollsum=ALG Rollsum algorithm: rabinkarp (default), rollsum\n"
111  "Delta-encoding options:\n"
112  " -b, --block-size=BYTES Signature block size, 0 (default) for recommended\n"
113  " -S, --sum-size=BYTES Signature strength, 0 (default) for max, -1 for min\n"
114  "IO options:\n" " -I, --input-size=BYTES Input buffer size\n"
115  " -O, --output-size=BYTES Output buffer size\n"
116  " -z, --gzip[=LEVEL] gzip-compress deltas\n"
117  " -i, --bzip2[=LEVEL] bzip2-compress deltas\n");
118 }
119 
120 static void rdiff_show_version(void)
121 {
122  char const *bzlib = "", *zlib = "", *trace = "";
123 
124 #if 0
125  /* Compression isn't implemented so don't mention it. */
126 # ifdef HAVE_LIBZ
127  zlib = ", gzip";
128 # endif
129 
130 # ifdef HAVE_LIBBZ2
131  bzlib = ", bzip2";
132 # endif
133 #endif
134 
135 #ifndef DO_RS_TRACE
136  trace = ", trace disabled";
137 #endif
138 
139  printf("rdiff (%s)\n"
140  "Copyright (C) 1997-2016 by Martin Pool, Andrew Tridgell and others.\n"
141  "http://librsync.sourcefrog.net/\n"
142  "Capabilities: %ld bit files%s%s%s\n" "\n"
143  "librsync comes with NO WARRANTY, to the extent permitted by law.\n"
144  "You may redistribute copies of librsync under the terms of the GNU\n"
145  "Lesser General Public License. For more information about these\n"
146  "matters, see the files named COPYING.\n", rs_librsync_version,
147  (long)(8 * sizeof(rs_long_t)), zlib, bzlib, trace);
148 }
149 
150 static void rdiff_options(poptContext opcon)
151 {
152  int c;
153  char const *a;
154 
155  while ((c = poptGetNextOpt(opcon)) != -1) {
156  switch (c) {
157  case 'h':
158  help();
159  exit(RS_DONE);
160  case 'V':
161  rdiff_show_version();
162  exit(RS_DONE);
163  case 'v':
164  if (!rs_supports_trace()) {
165  fprintf(stderr, "rdiff: Library does not support trace.\n");
166  }
168  break;
169 
170  case OPT_GZIP:
171  case OPT_BZIP2:
172  if ((a = poptGetOptArg(opcon))) {
173  int l = atoi(a);
174  if (c == OPT_GZIP)
175  gzip_level = l;
176  else
177  bzip2_level = l;
178  } else {
179  if (c == OPT_GZIP)
180  gzip_level = -1; /* library default */
181  else
182  bzip2_level = 9; /* demand the best */
183  }
184  rdiff_usage("Sorry, compression is not implemented yet.");
185  exit(RS_UNIMPLEMENTED);
186 
187  default:
188  bad_option(opcon, c);
189  }
190  }
191 }
192 
193 /** Generate signature from remaining command line arguments. */
194 static rs_result rdiff_sig(poptContext opcon)
195 {
196  FILE *basis_file, *sig_file;
197  rs_stats_t stats;
198  rs_result result;
199  rs_magic_number sig_magic;
200 
201  basis_file = rs_file_open(poptGetArg(opcon), "rb", file_force);
202  sig_file = rs_file_open(poptGetArg(opcon), "wb", file_force);
203 
204  rdiff_no_more_args(opcon);
205 
206  if (!rs_hash_name || !strcmp(rs_hash_name, "blake2")) {
207  sig_magic = RS_BLAKE2_SIG_MAGIC;
208  } else if (!strcmp(rs_hash_name, "md4")) {
209  sig_magic = RS_MD4_SIG_MAGIC;
210  } else {
211  rdiff_usage("Unknown hash algorithm '%s'.", rs_hash_name);
212  exit(RS_SYNTAX_ERROR);
213  }
214  if (!rs_rollsum_name || !strcmp(rs_rollsum_name, "rabinkarp")) {
215  /* The RabinKarp magics are 0x10 greater than the rollsum magics. */
216  sig_magic += 0x10;
217  } else if (strcmp(rs_rollsum_name, "rollsum")) {
218  rdiff_usage("Unknown rollsum algorithm '%s'.", rs_rollsum_name);
219  exit(RS_SYNTAX_ERROR);
220  }
221 
222  result =
223  rs_sig_file(basis_file, sig_file, block_len, strong_len, sig_magic,
224  &stats);
225 
226  rs_file_close(sig_file);
227  rs_file_close(basis_file);
228  if (result != RS_DONE)
229  return result;
230 
231  if (show_stats)
232  rs_log_stats(&stats);
233 
234  return result;
235 }
236 
237 static rs_result rdiff_delta(poptContext opcon)
238 {
239  FILE *sig_file, *new_file, *delta_file;
240  char const *sig_name;
241  rs_result result;
242  rs_signature_t *sumset;
243  rs_stats_t stats;
244 
245  if (!(sig_name = poptGetArg(opcon))) {
246  rdiff_usage("Usage for delta: "
247  "rdiff [OPTIONS] delta SIGNATURE [NEWFILE [DELTA]]");
248  exit(RS_SYNTAX_ERROR);
249  }
250 
251  sig_file = rs_file_open(sig_name, "rb", file_force);
252  new_file = rs_file_open(poptGetArg(opcon), "rb", file_force);
253  delta_file = rs_file_open(poptGetArg(opcon), "wb", file_force);
254 
255  rdiff_no_more_args(opcon);
256 
257  result = rs_loadsig_file(sig_file, &sumset, &stats);
258  if (result != RS_DONE)
259  return result;
260 
261  if (show_stats)
262  rs_log_stats(&stats);
263 
264  if ((result = rs_build_hash_table(sumset)) != RS_DONE)
265  return result;
266 
267  result = rs_delta_file(sumset, new_file, delta_file, &stats);
268 
269  rs_file_close(delta_file);
270  rs_file_close(new_file);
271  rs_file_close(sig_file);
272 
273  if (show_stats) {
274  rs_signature_log_stats(sumset);
275  rs_log_stats(&stats);
276  }
277 
278  rs_free_sumset(sumset);
279 
280  return result;
281 }
282 
283 static rs_result rdiff_patch(poptContext opcon)
284 {
285  /* patch BASIS [DELTA [NEWFILE]] */
286  FILE *basis_file, *delta_file, *new_file;
287  char const *basis_name;
288  rs_stats_t stats;
289  rs_result result;
290 
291  if (!(basis_name = poptGetArg(opcon))) {
292  rdiff_usage("Usage for patch: "
293  "rdiff [OPTIONS] patch BASIS [DELTA [NEW]]");
294  exit(RS_SYNTAX_ERROR);
295  }
296 
297  basis_file = rs_file_open(basis_name, "rb", file_force);
298  delta_file = rs_file_open(poptGetArg(opcon), "rb", file_force);
299  new_file = rs_file_open(poptGetArg(opcon), "wb", file_force);
300 
301  rdiff_no_more_args(opcon);
302 
303  result = rs_patch_file(basis_file, delta_file, new_file, &stats);
304 
305  rs_file_close(new_file);
306  rs_file_close(delta_file);
307  rs_file_close(basis_file);
308 
309  if (show_stats)
310  rs_log_stats(&stats);
311 
312  return result;
313 }
314 
315 static rs_result rdiff_action(poptContext opcon)
316 {
317  const char *action;
318 
319  action = poptGetArg(opcon);
320  if (!action) ;
321  else if (isprefix(action, "signature"))
322  return rdiff_sig(opcon);
323  else if (isprefix(action, "delta"))
324  return rdiff_delta(opcon);
325  else if (isprefix(action, "patch"))
326  return rdiff_patch(opcon);
327 
328  rdiff_usage
329  ("You must specify an action: `signature', `delta', or `patch'.");
330  exit(RS_SYNTAX_ERROR);
331 }
332 
333 int main(const int argc, const char *argv[])
334 {
335  /* Initialize opts at runtime to avoid unknown address values. */
336  const struct poptOption opts[] = {
337  {"verbose", 'v', POPT_ARG_NONE, 0, 'v'},
338  {"version", 'V', POPT_ARG_NONE, 0, 'V'},
339  {"input-size", 'I', POPT_ARG_INT, &rs_inbuflen},
340  {"output-size", 'O', POPT_ARG_INT, &rs_outbuflen},
341  {"hash", 'H', POPT_ARG_STRING, &rs_hash_name},
342  {"rollsum", 'R', POPT_ARG_STRING, &rs_rollsum_name},
343  {"help", '?', POPT_ARG_NONE, 0, 'h'},
344  {0, 'h', POPT_ARG_NONE, 0, 'h'},
345  {"block-size", 'b', POPT_ARG_INT, &block_len},
346  {"sum-size", 'S', POPT_ARG_INT, &strong_len},
347  {"statistics", 's', POPT_ARG_NONE, &show_stats},
348  {"stats", 0, POPT_ARG_NONE, &show_stats},
349  {"gzip", 'z', POPT_ARG_NONE, 0, OPT_GZIP},
350  {"bzip2", 'i', POPT_ARG_NONE, 0, OPT_BZIP2},
351  {"force", 'f', POPT_ARG_NONE, &file_force},
352  {0}
353  };
354 
355  poptContext opcon;
356  rs_result result;
357 
358  opcon = poptGetContext("rdiff", argc, argv, opts, 0);
359  rdiff_options(opcon);
360  result = rdiff_action(opcon);
361 
362  if (result != RS_DONE)
363  fprintf(stderr, "rdiff: Failed, %s.\n", rs_strerror(result));
364 
365  poptFreeContext(opcon);
366  return result;
367 }
Command line syntax error.
Definition: librsync.h:188
LIBRSYNC_EXPORT rs_result rs_delta_file(rs_signature_t *, FILE *new_file, FILE *delta_file, rs_stats_t *)
Generate a delta between a signature and a new file into a delta file.
Definition: whole.c:108
A signature file using the BLAKE2 hash.
Definition: librsync.h:89
LIBRSYNC_EXPORT int rs_log_stats(rs_stats_t const *stats)
Write statistics into the current log as text.
Definition: stats.c:31
LIBRSYNC_EXPORT rs_result rs_patch_file(FILE *basis_file, FILE *delta_file, FILE *new_file, rs_stats_t *)
Apply a patch, relative to a basis, into a new file.
Definition: whole.c:124
LIBRSYNC_EXPORT int rs_inbuflen
Buffer sizes for file IO.
Definition: whole.c:41
LIBRSYNC_EXPORT char const * rs_strerror(rs_result r)
Return an English description of a rs_result value.
Definition: msg.c:46
A signature file with MD4 signatures.
Definition: librsync.h:82
Public header for librsync.
LIBRSYNC_EXPORT void rs_trace_set_level(rs_loglevel level)
Set the least important message severity that will be output.
Definition: trace.c:62
LIBRSYNC_EXPORT int rs_file_close(FILE *file)
Close a file with special handling for stdin or stdout.
Definition: fileutil.c:120
Signature of a whole file.
Definition: sumset.h:44
Author is lazy.
Definition: librsync.h:197
LIBRSYNC_EXPORT void rs_free_sumset(rs_signature_t *)
Deep deallocation of checksums.
Definition: sumset.c:294
LIBRSYNC_EXPORT rs_result rs_build_hash_table(rs_signature_t *sums)
Call this after loading a signature to index it.
Definition: sumset.c:274
Performance statistics from a librsync encoding or decoding operation.
Definition: librsync.h:210
int isprefix(char const *tip, char const *iceberg)
Return true if TIP is a prefix of ICEBERG.
Definition: isprefix.c:24
rs_result
Return codes from nonblocking rsync operations.
Definition: librsync.h:180
LIBRSYNC_EXPORT int rs_supports_trace(void)
Check whether the library was compiled with debugging trace.
Definition: trace.c:102
LIBRSYNC_EXPORT rs_result rs_loadsig_file(FILE *sig_file, rs_signature_t **sumset, rs_stats_t *stats)
Load signatures from a signature file into memory.
Definition: whole.c:90
static rs_result rdiff_sig(poptContext opcon)
Generate signature from remaining command line arguments.
Definition: rdiff.c:194
LIBRSYNC_EXPORT FILE * rs_file_open(char const *filename, char const *mode, int force)
Open a file with special handling for stdin or stdout.
Definition: fileutil.c:81
LIBRSYNC_EXPORT rs_result rs_sig_file(FILE *old_file, FILE *sig_file, size_t block_len, size_t strong_len, rs_magic_number sig_magic, rs_stats_t *stats)
Generate the signature of a basis file, and write it out to another.
Definition: whole.c:67
rs_magic_number
A uint32 magic number, emitted in bigendian/network order at the start of librsync files...
Definition: librsync.h:65
LIBRSYNC_EXPORT void rs_signature_log_stats(rs_signature_t const *sig)
Log the rs_signature_delta match stats.
Definition: sumset.c:256
Completed successfully.
Definition: librsync.h:181
LIBRSYNC_EXPORT char const rs_librsync_version[]
Library version string.
Definition: version.c:25
String prefix text function.
Debug-level messages.
Definition: librsync.h:126