"Fossies" - the Fresh Open Source Software Archive 
Member "xdelta3-3.0.11/xdelta3-main.h" (27 Dec 2015, 103567 Bytes) of package /linux/misc/xdelta3-3.0.11.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "xdelta3-main.h" see the
Fossies "Dox" file reference documentation.
1 /* xdelta3 - delta compression tools and library
2 * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
3 * 2009, 2010, 2011, 2012, 2013, 2014, 2015 Joshua P. MacDonald
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20 /* This is all the extra stuff you need for convenience to users in a
21 * command line application. It contains these major components:
22 *
23 * 1. VCDIFF tools 2. external compression support (this is
24 * POSIX-specific). 3. a general read/write loop that handles all of
25 * the Xdelta decode/encode/VCDIFF-print functions 4. command-line
26 * interpreter 5. an Xdelta application header which stores default
27 * filename, external compression settings 6. output/error printing
28 * 7. basic file support and OS interface
29 */
30
31 /* TODO list: 1. do exact gzip-like filename, stdout handling. make a
32 * .vcdiff extension, refuse to encode to stdout without -cf, etc.
33 * 2. Allow the user to add a comment string to the app header without
34 * disturbing the default behavior.
35 */
36
37 /* On error handling and printing:
38 *
39 * The xdelta library sets stream->msg to indicate what condition
40 * caused an internal failure, but many failures originate here and
41 * are printed here. The return convention is 0 for success, as
42 * throughout Xdelta code, but special attention is required here for
43 * the operating system calls with different error handling. See the
44 * main_file_* routines. All errors in this file have a message
45 * printed at the time of occurance. Since some of these calls occur
46 * within calls to the library, the error may end up being printed
47 * again with a more general error message.
48 */
49
50 /*********************************************************************/
51
52 /* Combines xd3_strerror() and strerror() */
53 const char* xd3_mainerror(int err_num);
54
55 #include "xdelta3-internal.h"
56
57 int
58 xsnprintf_func (char *str, int n, const char *fmt, ...)
59 {
60 va_list a;
61 int ret;
62 va_start (a, fmt);
63 ret = vsnprintf_func (str, n, fmt, a);
64 va_end (a);
65 if (ret < 0)
66 {
67 ret = n;
68 }
69 return ret;
70 }
71
72 /* Handle externally-compressed inputs. */
73 #ifndef EXTERNAL_COMPRESSION
74 #define EXTERNAL_COMPRESSION 1
75 #endif
76
77 #define PRINTHDR_SPECIAL -4378291
78
79 /* The number of soft-config variables. */
80 #define XD3_SOFTCFG_VARCNT 7
81
82 /* this is used as in XPR(NT XD3_LIB_ERRMSG (stream, ret)) to print an
83 * error message from the library. */
84 #define XD3_LIB_ERRMSG(stream, ret) "%s: %s\n", \
85 xd3_errstring (stream), xd3_mainerror (ret)
86
87 #if XD3_POSIX
88 #include <unistd.h> /* close, read, write... */
89 #include <sys/types.h>
90 #include <fcntl.h>
91 #endif
92
93 #ifndef _WIN32
94 #include <unistd.h> /* lots */
95 #include <sys/time.h> /* gettimeofday() */
96 #include <sys/stat.h> /* stat() and fstat() */
97 #else
98 #if defined(_MSC_VER)
99 #define strtoll _strtoi64
100 #endif
101 #include <sys/types.h>
102 #include <sys/stat.h>
103 #ifndef WIFEXITED
104 # define WIFEXITED(stat) (((*((int *) &(stat))) & 0xff) == 0)
105 #endif
106 #ifndef WEXITSTATUS
107 # define WEXITSTATUS(stat) (((*((int *) &(stat))) >> 8) & 0xff)
108 #endif
109 #ifndef S_ISREG
110 //# ifdef S_IFREG
111 //# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
112 //# else
113 # define S_ISREG(m) 1
114 //# endif
115 #endif /* !S_ISREG */
116
117 // For standard input/output handles
118 static STARTUPINFO winStartupInfo;
119 #endif
120
121 /**********************************************************************
122 ENUMS and TYPES
123 *********************************************************************/
124
125 /* These flags (mainly pertaining to main_read() operations) are set
126 * in the main_file->flags variable. All are related to with external
127 * decompression support.
128 *
129 * RD_FIRST causes the external decompression check when the input is
130 * first read.
131 *
132 * RD_NONEXTERNAL disables external decompression for reading a
133 * compressed input, in the case of Xdelta inputs. Note: Xdelta is
134 * supported as an external compression type, which makes is the
135 * reason for this flag. An example to justify this is: to create a
136 * delta between two files that are VCDIFF-compressed. Two external
137 * Xdelta decoders are run to supply decompressed source and target
138 * inputs to the Xdelta encoder. */
139 typedef enum
140 {
141 RD_FIRST = (1 << 0),
142 RD_NONEXTERNAL = (1 << 1),
143 RD_DECOMPSET = (1 << 2),
144 RD_MAININPUT = (1 << 3),
145 } xd3_read_flags;
146
147 /* Main commands. For example, CMD_PRINTHDR is the "xdelta printhdr"
148 * command. */
149 typedef enum
150 {
151 CMD_NONE = 0,
152 CMD_PRINTHDR,
153 CMD_PRINTHDRS,
154 CMD_PRINTDELTA,
155 CMD_RECODE,
156 CMD_MERGE_ARG,
157 CMD_MERGE,
158 #if XD3_ENCODER
159 CMD_ENCODE,
160 #endif
161 CMD_DECODE,
162 CMD_TEST,
163 CMD_CONFIG,
164 } xd3_cmd;
165
166 #if XD3_ENCODER
167 #define CMD_DEFAULT CMD_ENCODE
168 #define IS_ENCODE(cmd) (cmd == CMD_ENCODE)
169 #else
170 #define CMD_DEFAULT CMD_DECODE
171 #define IS_ENCODE(cmd) (0)
172 #endif
173
174 typedef struct _main_merge main_merge;
175 typedef struct _main_merge_list main_merge_list;
176
177 /* Various strings and magic values used to detect and call external
178 * compression. See below for examples. */
179 struct _main_extcomp
180 {
181 const char *recomp_cmdname;
182 const char *recomp_options;
183
184 const char *decomp_cmdname;
185 const char *decomp_options;
186
187 const char *ident;
188 const char *magic;
189 usize_t magic_size;
190 int flags;
191 };
192
193 /* Merge state: */
194
195 struct _main_merge_list
196 {
197 main_merge_list *next;
198 main_merge_list *prev;
199 };
200
201 struct _main_merge
202 {
203 const char *filename;
204
205 main_merge_list link;
206 };
207
208 XD3_MAKELIST(main_merge_list,main_merge,link);
209
210 /* TODO: really need to put options in a struct so that internal
211 * callers can easily reset state. */
212
213 #define DEFAULT_VERBOSE 0
214
215 /* Program options: various command line flags and options. */
216 static int option_stdout = 0;
217 static int option_force = 0;
218 static int option_verbose = DEFAULT_VERBOSE;
219 static int option_quiet = 0;
220 static int option_use_appheader = 1;
221 static uint8_t* option_appheader = NULL;
222 static int option_use_secondary = 1;
223 static const char* option_secondary = NULL;
224 static int option_use_checksum = 1;
225 static const char* option_smatch_config = NULL;
226 static int option_no_compress = 0;
227 static int option_no_output = 0; /* do not write output */
228 static const char *option_source_filename = NULL;
229
230 static int option_level = XD3_DEFAULT_LEVEL;
231 static usize_t option_iopt_size = XD3_DEFAULT_IOPT_SIZE;
232 static usize_t option_winsize = XD3_DEFAULT_WINSIZE;
233 /* Note: option_srcwinsz is restricted from [16Kb, 4Gb], because
234 * addresses in the large hash checksum are 32 bits. The flag is read
235 * as xoff_t, so that 4Gb != 0. */
236 static xoff_t option_srcwinsz = XD3_DEFAULT_SRCWINSZ;
237 static usize_t option_sprevsz = XD3_DEFAULT_SPREVSZ;
238
239 /* These variables are supressed to avoid their use w/o support. main() warns
240 * appropriately when external compression is not enabled. */
241 #if EXTERNAL_COMPRESSION
242 static int num_subprocs = 0;
243 static int option_force2 = 0;
244 static int option_decompress_inputs = 1;
245 static int option_recompress_outputs = 1;
246 #endif
247
248 /* This is for comparing "printdelta" output without attention to
249 * copy-instruction modes. */
250 #if VCDIFF_TOOLS
251 static int option_print_cpymode = 1; /* Note: see reset_defaults(). */
252 #endif
253
254 /* Static variables */
255 IF_DEBUG(static int main_mallocs = 0;)
256
257 static char* program_name = NULL;
258 static uint8_t* appheader_used = NULL;
259 static uint8_t* main_bdata = NULL;
260 static usize_t main_bsize = 0;
261
262 /* Hacks for VCDIFF tools, recode command. */
263 static int allow_fake_source = 0;
264
265 /* recode_stream is used by both recode/merge for reading vcdiff inputs */
266 static xd3_stream *recode_stream = NULL;
267
268 /* merge_stream is used by merge commands for storing the source encoding */
269 static xd3_stream *merge_stream = NULL;
270
271 /* This array of compressor types is compiled even if EXTERNAL_COMPRESSION is
272 * false just so the program knows the mapping of IDENT->NAME. */
273 static main_extcomp extcomp_types[] =
274 {
275 { "bzip2", "-c", "bzip2", "-dc", "B", "BZh", 3, 0 },
276 { "gzip", "-c", "gzip", "-dc", "G", "\037\213", 2, 0 },
277 { "compress", "-c", "uncompress", "-c", "Z", "\037\235", 2, 0 },
278
279 /* Xz is lzma with a magic number http://tukaani.org/xz/format.html */
280 { "xz", "-c", "xz", "-dc", "Y", "\xfd\x37\x7a\x58\x5a\x00", 2, 0 },
281 };
282
283 static int main_input (xd3_cmd cmd, main_file *ifile,
284 main_file *ofile, main_file *sfile);
285 static void main_get_appheader (xd3_stream *stream, main_file *ifile,
286 main_file *output, main_file *sfile);
287
288 static int main_getblk_func (xd3_stream *stream,
289 xd3_source *source,
290 xoff_t blkno);
291 static void main_free (void *ptr);
292 static void* main_malloc (size_t size);
293
294 static int main_file_stat (main_file *xfile, xoff_t *size);
295 static int main_file_seek (main_file *xfile, xoff_t pos);
296 static int main_read_primary_input (main_file *file,
297 uint8_t *buf,
298 size_t size,
299 size_t *nread);
300
301 static const char* main_format_bcnt (xoff_t r, shortbuf *buf);
302 static int main_help (void);
303
304 #if XD3_ENCODER
305 static int xd3_merge_input_output (xd3_stream *stream,
306 xd3_whole_state *source);
307 #endif
308
309 /* The code in xdelta3-blk.h is essentially part of this unit, see
310 * comments there. */
311 #include "xdelta3-blkcache.h"
312
313 void (*xprintf_message_func)(const char*msg) = NULL;
314
315 void
316 xprintf (const char *fmt, ...)
317 {
318 char buf[1000];
319 va_list a;
320 int size;
321 va_start (a, fmt);
322 size = vsnprintf_func (buf, 1000, fmt, a);
323 va_end (a);
324 if (size < 0)
325 {
326 size = sizeof(buf) - 1;
327 buf[size] = 0;
328 }
329 if (xprintf_message_func != NULL) {
330 xprintf_message_func(buf);
331 } else {
332 size_t ignore = fwrite(buf, 1, size, stderr);
333 (void) ignore;
334 }
335 }
336
337 static int
338 main_version (void)
339 {
340 /* $Format: " XPR(NTR \"Xdelta version $Xdelta3Version$, Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, Joshua MacDonald\\n\");" $ */
341 XPR(NTR "Xdelta version 3.0.11, Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Joshua MacDonald\n");
342 XPR(NTR "Xdelta comes with ABSOLUTELY NO WARRANTY.\n");
343 XPR(NTR "This is free software, and you are welcome to redistribute it\n");
344 XPR(NTR "under certain conditions; see \"COPYING\" for details.\n");
345 return EXIT_SUCCESS;
346 }
347
348 static int
349 main_config (void)
350 {
351 main_version ();
352
353 XPR(NTR "EXTERNAL_COMPRESSION=%d\n", EXTERNAL_COMPRESSION);
354 XPR(NTR "REGRESSION_TEST=%d\n", REGRESSION_TEST);
355 XPR(NTR "SECONDARY_DJW=%d\n", SECONDARY_DJW);
356 XPR(NTR "SECONDARY_FGK=%d\n", SECONDARY_FGK);
357 XPR(NTR "SECONDARY_LZMA=%d\n", SECONDARY_LZMA);
358 XPR(NTR "UNALIGNED_OK=%d\n", UNALIGNED_OK);
359 XPR(NTR "VCDIFF_TOOLS=%d\n", VCDIFF_TOOLS);
360 XPR(NTR "XD3_ALLOCSIZE=%d\n", XD3_ALLOCSIZE);
361 XPR(NTR "XD3_DEBUG=%d\n", XD3_DEBUG);
362 XPR(NTR "XD3_ENCODER=%d\n", XD3_ENCODER);
363 XPR(NTR "XD3_POSIX=%d\n", XD3_POSIX);
364 XPR(NTR "XD3_STDIO=%d\n", XD3_STDIO);
365 XPR(NTR "XD3_WIN32=%d\n", XD3_WIN32);
366 XPR(NTR "XD3_USE_LARGEFILE64=%d\n", XD3_USE_LARGEFILE64);
367 XPR(NTR "XD3_DEFAULT_LEVEL=%d\n", XD3_DEFAULT_LEVEL);
368 XPR(NTR "XD3_DEFAULT_IOPT_SIZE=%d\n", XD3_DEFAULT_IOPT_SIZE);
369 XPR(NTR "XD3_DEFAULT_SPREVSZ=%d\n", XD3_DEFAULT_SPREVSZ);
370 XPR(NTR "XD3_DEFAULT_SRCWINSZ=%d\n", XD3_DEFAULT_SRCWINSZ);
371 XPR(NTR "XD3_DEFAULT_WINSIZE=%d\n", XD3_DEFAULT_WINSIZE);
372 XPR(NTR "XD3_HARDMAXWINSIZE=%d\n", XD3_HARDMAXWINSIZE);
373 XPR(NTR "sizeof(void*)=%d\n", (int)sizeof(void*));
374 XPR(NTR "sizeof(int)=%d\n", (int)sizeof(int));
375 XPR(NTR "sizeof(long)=%d\n", (int)sizeof(long));
376 XPR(NTR "sizeof(long long)=%d\n", (int)sizeof(long long));
377 XPR(NTR "sizeof(size_t)=%d\n", (int)sizeof(size_t));
378 XPR(NTR "sizeof(uint32_t)=%d\n", (int)sizeof(uint32_t));
379 XPR(NTR "sizeof(uint64_t)=%d\n", (int)sizeof(uint64_t));
380 XPR(NTR "sizeof(usize_t)=%d\n", (int)sizeof(usize_t));
381 XPR(NTR "sizeof(xoff_t)=%d\n", (int)sizeof(xoff_t));
382
383 return EXIT_SUCCESS;
384 }
385
386 static void
387 reset_defaults(void)
388 {
389 option_stdout = 0;
390 option_force = 0;
391 option_verbose = DEFAULT_VERBOSE;
392 option_quiet = 0;
393 option_appheader = NULL;
394 option_use_secondary = 1;
395 option_secondary = NULL;
396 option_smatch_config = NULL;
397 option_no_compress = 0;
398 option_no_output = 0;
399 option_source_filename = NULL;
400 program_name = NULL;
401 appheader_used = NULL;
402 main_bdata = NULL;
403 main_bsize = 0;
404 allow_fake_source = 0;
405 option_smatch_config = NULL;
406
407 main_lru_reset();
408
409 option_use_appheader = 1;
410 option_use_checksum = 1;
411 #if EXTERNAL_COMPRESSION
412 option_force2 = 0;
413 option_decompress_inputs = 1;
414 option_recompress_outputs = 1;
415 num_subprocs = 0;
416 #endif
417 #if VCDIFF_TOOLS
418 option_print_cpymode = 1;
419 #endif
420 option_level = XD3_DEFAULT_LEVEL;
421 option_iopt_size = XD3_DEFAULT_IOPT_SIZE;
422 option_winsize = XD3_DEFAULT_WINSIZE;
423 option_srcwinsz = XD3_DEFAULT_SRCWINSZ;
424 option_sprevsz = XD3_DEFAULT_SPREVSZ;
425 }
426
427 static void*
428 main_malloc1 (size_t size)
429 {
430 void* r = malloc (size);
431 if (r == NULL) { XPR(NT "malloc: %s\n", xd3_mainerror (ENOMEM)); }
432 return r;
433 }
434
435 void* main_bufalloc (size_t size) {
436 #if XD3_WIN32
437 return VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
438 #else
439 return main_malloc1(size);
440 #endif
441 }
442
443 static void*
444 main_malloc (size_t size)
445 {
446 void *r = main_malloc1 (size);
447 if (r) { IF_DEBUG (main_mallocs += 1); }
448 return r;
449 }
450
451 static void*
452 main_alloc (void *opaque,
453 size_t items,
454 usize_t size)
455 {
456 return main_malloc1 (items * size);
457 }
458
459 static void
460 main_free1 (void *opaque, void *ptr)
461 {
462 free (ptr);
463 }
464
465 static void
466 main_free (void *ptr)
467 {
468 if (ptr)
469 {
470 IF_DEBUG (main_mallocs -= 1);
471 main_free1 (NULL, ptr);
472 IF_DEBUG (XD3_ASSERT(main_mallocs >= 0));
473 }
474 }
475
476 void main_buffree (void *ptr) {
477 #if XD3_WIN32
478 VirtualFree(ptr, 0, MEM_RELEASE);
479 #else
480 main_free1(NULL, ptr);
481 #endif
482 }
483
484 /* This ensures that (ret = errno) always indicates failure, in case errno was
485 * accidentally not set. If this prints there's a bug somewhere. */
486 static int
487 get_errno (void)
488 {
489 #ifndef _WIN32
490 if (errno == 0)
491 {
492 XPR(NT "you found a bug: expected errno != 0\n");
493 errno = XD3_INTERNAL;
494 }
495 return errno;
496 #else
497 DWORD err_num = GetLastError();
498 if (err_num == NO_ERROR)
499 {
500 err_num = XD3_INTERNAL;
501 }
502 return err_num;
503 #endif
504 }
505
506 const char*
507 xd3_mainerror(int err_num) {
508 #ifndef _WIN32
509 const char* x = xd3_strerror (err_num);
510 if (x != NULL)
511 {
512 return x;
513 }
514 return strerror(err_num);
515 #else
516 static char err_buf[256];
517 const char* x = xd3_strerror (err_num);
518 if (x != NULL)
519 {
520 return x;
521 }
522 memset (err_buf, 0, 256);
523 FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM |
524 FORMAT_MESSAGE_IGNORE_INSERTS,
525 NULL, err_num,
526 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
527 err_buf, 256, NULL);
528 if (err_buf[0] != 0 && err_buf[strlen(err_buf) - 1] == '\n')
529 {
530 err_buf[strlen(err_buf) - 1] = 0;
531 }
532 return err_buf;
533 #endif
534 }
535
536 static long
537 get_millisecs_now (void)
538 {
539 #ifndef _WIN32
540 struct timeval tv;
541
542 gettimeofday (& tv, NULL);
543
544 return (tv.tv_sec) * 1000L + (tv.tv_usec) / 1000;
545 #else
546 SYSTEMTIME st;
547 FILETIME ft;
548 __int64 *pi = (__int64*)&ft;
549 GetLocalTime(&st);
550 SystemTimeToFileTime(&st, &ft);
551 return (long)((*pi) / 10000);
552 #endif
553 }
554
555 /* Always >= 1 millisec, right? */
556 static long
557 get_millisecs_since (void)
558 {
559 static long last = 0;
560 long now = get_millisecs_now();
561 long diff = now - last;
562 last = now;
563 return diff;
564 }
565
566 static const char*
567 main_format_bcnt (xoff_t r, shortbuf *buf)
568 {
569 static const char* fmts[] = { "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB" };
570 usize_t i;
571
572 for (i = 0; i < SIZEOF_ARRAY(fmts) - 1; i += 1)
573 {
574 xoff_t new_r;
575
576 if (r == 0)
577 {
578 short_sprintf (*buf, "0 %s", fmts[i]);
579 return buf->buf;
580 }
581
582 if (r >= 1 && r < 10)
583 {
584 short_sprintf (*buf, "%.2f %s", (double) r, fmts[i]);
585 return buf->buf;
586 }
587
588 if (r >= 10 && r < 100)
589 {
590 short_sprintf (*buf, "%.1f %s", (double) r, fmts[i]);
591 return buf->buf;
592 }
593
594 if (r >= 100 && r < 1000)
595 {
596 short_sprintf (*buf, "%"Q"u %s", r, fmts[i]);
597 return buf->buf;
598 }
599
600 new_r = r / 1024;
601
602 if (new_r < 10)
603 {
604 short_sprintf (*buf, "%.2f %s", (double) r / 1024.0, fmts[i + 1]);
605 return buf->buf;
606 }
607
608 if (new_r < 100)
609 {
610 short_sprintf (*buf, "%.1f %s", (double) r / 1024.0, fmts[i + 1]);
611 return buf->buf;
612 }
613
614 r = new_r;
615 }
616 XD3_ASSERT (0);
617 return "";
618 }
619
620 static char*
621 main_format_rate (xoff_t bytes, long millis, shortbuf *buf)
622 {
623 xoff_t r = (xoff_t)(1.0 * bytes / (1.0 * millis / 1000.0));
624 static shortbuf lbuf;
625
626 main_format_bcnt (r, &lbuf);
627 short_sprintf (*buf, "%s/s", lbuf.buf);
628 return buf->buf;
629 }
630
631 static char*
632 main_format_millis (long millis, shortbuf *buf)
633 {
634 if (millis < 1000)
635 {
636 short_sprintf (*buf, "%lu ms", millis);
637 }
638 else if (millis < 10000)
639 {
640 short_sprintf (*buf, "%.1f sec", millis / 1000.0);
641 }
642 else
643 {
644 short_sprintf (*buf, "%lu sec", millis / 1000L);
645 }
646 return buf->buf;
647 }
648
649 /* A safe version of strtol for xoff_t. */
650 static int
651 main_strtoxoff (const char* s, xoff_t *xo, char which)
652 {
653 char *e;
654 xoff_t x;
655
656 XD3_ASSERT(s && *s != 0);
657
658 {
659 /* Should check LONG_MIN, LONG_MAX, LLONG_MIN, LLONG_MAX? */
660 #if SIZEOF_XOFF_T == 4
661 long xx = strtol (s, &e, 0);
662 #else
663 long long xx = strtoll (s, &e, 0);
664 #endif
665
666 if (xx < 0)
667 {
668 XPR(NT "-%c: negative integer: %s\n", which, s);
669 return EXIT_FAILURE;
670 }
671
672 x = xx;
673 }
674
675 if (*e != 0)
676 {
677 XPR(NT "-%c: invalid integer: %s\n", which, s);
678 return EXIT_FAILURE;
679 }
680
681 (*xo) = x;
682 return 0;
683 }
684
685 static int
686 main_atoux (const char* arg, xoff_t *xo, xoff_t low,
687 xoff_t high, char which)
688 {
689 xoff_t x;
690 int ret;
691
692 if ((ret = main_strtoxoff (arg, & x, which))) { return ret; }
693
694 if (x < low)
695 {
696 XPR(NT "-%c: minimum value: %"Q"u\n", which, low);
697 return EXIT_FAILURE;
698 }
699 if (high != 0 && x > high)
700 {
701 XPR(NT "-%c: maximum value: %"Q"u\n", which, high);
702 return EXIT_FAILURE;
703 }
704 (*xo) = x;
705 return 0;
706 }
707
708 static int
709 main_atou (const char* arg, usize_t *uo, usize_t low,
710 usize_t high, char which)
711 {
712 int ret;
713 xoff_t xo;
714 if ((ret = main_atoux (arg, &xo, low, high, which)))
715 {
716 return ret;
717 }
718 *uo = (usize_t)xo;
719 return 0;
720 }
721
722 /******************************************************************
723 FILE BASICS
724 ******************************************************************/
725
726 /* With all the variation in file system-call semantics, arguments,
727 * return values and error-handling for the POSIX and STDIO file APIs,
728 * the insides of these functions make me sick, which is why these
729 * wrappers exist. */
730
731 #define XOPEN_OPNAME (xfile->mode == XO_READ ? "read" : "write")
732 #define XOPEN_STDIO (xfile->mode == XO_READ ? "rb" : "wb")
733 #define XOPEN_POSIX (xfile->mode == XO_READ ? \
734 O_RDONLY : O_WRONLY | O_CREAT | O_TRUNC)
735 #define XOPEN_MODE (xfile->mode == XO_READ ? 0 : 0666)
736
737 #define XF_ERROR(op, name, ret) \
738 do { if (!option_quiet) { XPR(NT "file %s failed: %s: %s: %s\n", (op), \
739 XOPEN_OPNAME, (name), xd3_mainerror (ret)); } } while (0)
740
741 #if XD3_STDIO
742 #define XFNO(f) fileno(f->file)
743 #define XSTDOUT_XF(f) { (f)->file = stdout; (f)->filename = "/dev/stdout"; }
744 #define XSTDIN_XF(f) { (f)->file = stdin; (f)->filename = "/dev/stdin"; }
745
746 #elif XD3_POSIX
747 #define XFNO(f) f->file
748 #define XSTDOUT_XF(f) \
749 { (f)->file = STDOUT_FILENO; (f)->filename = "/dev/stdout"; }
750 #define XSTDIN_XF(f) \
751 { (f)->file = STDIN_FILENO; (f)->filename = "/dev/stdin"; }
752
753 #elif XD3_WIN32
754 #define XFNO(f) -1
755 #define XSTDOUT_XF(f) { \
756 (f)->file = GetStdHandle(STD_OUTPUT_HANDLE); \
757 (f)->filename = "(stdout)"; \
758 }
759 #define XSTDIN_XF(f) { \
760 (f)->file = GetStdHandle(STD_INPUT_HANDLE); \
761 (f)->filename = "(stdin)"; \
762 }
763 #endif
764
765 void
766 main_file_init (main_file *xfile)
767 {
768 memset (xfile, 0, sizeof (*xfile));
769
770 #if XD3_POSIX
771 xfile->file = -1;
772 #endif
773 #if XD3_WIN32
774 xfile->file = INVALID_HANDLE_VALUE;
775 #endif
776 }
777
778 int
779 main_file_isopen (main_file *xfile)
780 {
781 #if XD3_STDIO
782 return xfile->file != NULL;
783
784 #elif XD3_POSIX
785 return xfile->file != -1;
786
787 #elif XD3_WIN32
788 return xfile->file != INVALID_HANDLE_VALUE;
789 #endif
790 }
791
792 int
793 main_file_close (main_file *xfile)
794 {
795 int ret = 0;
796
797 if (! main_file_isopen (xfile))
798 {
799 return 0;
800 }
801
802 #if XD3_STDIO
803 ret = fclose (xfile->file);
804 xfile->file = NULL;
805
806 #elif XD3_POSIX
807 ret = close (xfile->file);
808 xfile->file = -1;
809
810 #elif XD3_WIN32
811 if (!CloseHandle(xfile->file)) {
812 ret = get_errno ();
813 }
814 xfile->file = INVALID_HANDLE_VALUE;
815 #endif
816
817 if (ret != 0) { XF_ERROR ("close", xfile->filename, ret = get_errno ()); }
818 return ret;
819 }
820
821 void
822 main_file_cleanup (main_file *xfile)
823 {
824 XD3_ASSERT (xfile != NULL);
825
826 if (main_file_isopen (xfile))
827 {
828 main_file_close (xfile);
829 }
830
831 if (xfile->snprintf_buf != NULL)
832 {
833 main_free(xfile->snprintf_buf);
834 xfile->snprintf_buf = NULL;
835 }
836
837 if (xfile->filename_copy != NULL)
838 {
839 main_free(xfile->filename_copy);
840 xfile->filename_copy = NULL;
841 }
842 }
843
844 int
845 main_file_open (main_file *xfile, const char* name, int mode)
846 {
847 int ret = 0;
848
849 xfile->mode = mode;
850
851 XD3_ASSERT (name != NULL);
852 XD3_ASSERT (! main_file_isopen (xfile));
853 if (name[0] == 0)
854 {
855 XPR(NT "invalid file name: empty string\n");
856 return XD3_INVALID;
857 }
858
859 IF_DEBUG1(DP(RINT "[main] open source %s\n", name));
860
861 #if XD3_STDIO
862 xfile->file = fopen (name, XOPEN_STDIO);
863
864 ret = (xfile->file == NULL) ? get_errno () : 0;
865
866 #elif XD3_POSIX
867 /* TODO: Should retry this call if interrupted, similar to read/write */
868 if ((ret = open (name, XOPEN_POSIX, XOPEN_MODE)) < 0)
869 {
870 ret = get_errno ();
871 }
872 else
873 {
874 xfile->file = ret;
875 ret = 0;
876 }
877
878 #elif XD3_WIN32
879 xfile->file = CreateFile(name,
880 (mode == XO_READ) ? GENERIC_READ : GENERIC_WRITE,
881 FILE_SHARE_READ,
882 NULL,
883 (mode == XO_READ) ?
884 OPEN_EXISTING :
885 (option_force ? CREATE_ALWAYS : CREATE_NEW),
886 FILE_ATTRIBUTE_NORMAL,
887 NULL);
888 if (xfile->file == INVALID_HANDLE_VALUE)
889 {
890 ret = get_errno ();
891 }
892 #endif
893 if (ret) { XF_ERROR ("open", name, ret); }
894 else { xfile->realname = name; xfile->nread = 0; }
895 return ret;
896 }
897
898 int
899 main_file_stat (main_file *xfile, xoff_t *size)
900 {
901 int ret = 0;
902 #if XD3_WIN32
903 if (GetFileType(xfile->file) != FILE_TYPE_DISK)
904 {
905 return -1;
906 }
907 # if (_WIN32_WINNT >= 0x0500)
908 {
909 LARGE_INTEGER li;
910 if (GetFileSizeEx(xfile->file, &li) == 0)
911 {
912 return get_errno ();
913 }
914 *size = li.QuadPart;
915 }
916 # else
917 {
918 DWORD filesize = GetFileSize(xfile->file, NULL);
919 if (filesize == INVALID_FILE_SIZE)
920 {
921 return get_errno ()
922 }
923 *size = filesize;
924 }
925 # endif
926 #else
927 struct stat sbuf;
928 if (fstat (XFNO (xfile), & sbuf) < 0)
929 {
930 ret = get_errno ();
931 return ret;
932 }
933
934 if (! S_ISREG (sbuf.st_mode))
935 {
936 return ESPIPE;
937 }
938 (*size) = sbuf.st_size;
939 #endif
940 return ret;
941 }
942
943 int
944 main_file_exists (main_file *xfile)
945 {
946 struct stat sbuf;
947 return stat (xfile->filename, & sbuf) == 0 && S_ISREG (sbuf.st_mode);
948 }
949
950 #if (XD3_POSIX || EXTERNAL_COMPRESSION)
951 /* POSIX-generic code takes a function pointer to read() or write().
952 * This calls the function repeatedly until the buffer is full or EOF.
953 * The NREAD parameter is not set for write, NULL is passed. Return
954 * is signed, < 0 indicate errors, otherwise byte count. */
955 typedef int (xd3_posix_func) (int fd, uint8_t *buf, usize_t size);
956
957 static int
958 xd3_posix_io (int fd, uint8_t *buf, size_t size,
959 xd3_posix_func *func, size_t *nread)
960 {
961 int ret;
962 size_t nproc = 0;
963
964 while (nproc < size)
965 {
966 size_t tryread = xd3_min(size - nproc, 1U << 30);
967 ssize_t result = (*func) (fd, buf + nproc, tryread);
968
969 if (result < 0)
970 {
971 ret = get_errno ();
972 if (ret != EAGAIN && ret != EINTR)
973 {
974 return ret;
975 }
976 continue;
977 }
978
979 if (nread != NULL && result == 0) { break; }
980
981 nproc += result;
982 }
983 if (nread != NULL) { (*nread) = nproc; }
984 return 0;
985 }
986 #endif
987
988 #if XD3_WIN32
989 static int
990 xd3_win32_io (HANDLE file, uint8_t *buf, size_t size,
991 int is_read, size_t *nread)
992 {
993 int ret = 0;
994 size_t nproc = 0;
995
996 while (nproc < size)
997 {
998 DWORD nproc2 = 0; /* hmm */
999 DWORD nremain = size - nproc;
1000 if ((is_read ?
1001 ReadFile (file, buf + nproc, nremain, &nproc2, NULL) :
1002 WriteFile (file, buf + nproc, nremain, &nproc2, NULL)) == 0)
1003 {
1004 ret = get_errno();
1005 if (ret != ERROR_HANDLE_EOF && ret != ERROR_BROKEN_PIPE)
1006 {
1007 return ret;
1008 }
1009 /* By falling through here, we'll break this loop in the
1010 * read case in case of eof or broken pipe. */
1011 }
1012
1013 nproc += nproc2;
1014
1015 if (nread != NULL && nproc2 == 0) { break; }
1016 }
1017 if (nread != NULL) { (*nread) = nproc; }
1018 return 0;
1019 }
1020 #endif
1021
1022 /* POSIX is unbuffered, while STDIO is buffered. main_file_read()
1023 * should always be called on blocks. */
1024 int
1025 main_file_read (main_file *ifile,
1026 uint8_t *buf,
1027 size_t size,
1028 size_t *nread,
1029 const char *msg)
1030 {
1031 int ret = 0;
1032 IF_DEBUG1(DP(RINT "[main] read %s up to %"Z"u\n", ifile->filename, size));
1033
1034 #if XD3_STDIO
1035 size_t result;
1036
1037 result = fread (buf, 1, size, ifile->file);
1038
1039 if (result < size && ferror (ifile->file))
1040 {
1041 ret = get_errno ();
1042 }
1043 else
1044 {
1045 *nread = result;
1046 }
1047
1048 #elif XD3_POSIX
1049 ret = xd3_posix_io (ifile->file, buf, size, (xd3_posix_func*) &read, nread);
1050 #elif XD3_WIN32
1051 ret = xd3_win32_io (ifile->file, buf, size, 1 /* is_read */, nread);
1052 #endif
1053
1054 if (ret)
1055 {
1056 XPR(NT "%s: %s: %s\n", msg, ifile->filename, xd3_mainerror (ret));
1057 }
1058 else
1059 {
1060 if (option_verbose > 4) { XPR(NT "read %s: %"Z"u bytes\n",
1061 ifile->filename, (*nread)); }
1062 ifile->nread += (*nread);
1063 }
1064
1065 return ret;
1066 }
1067
1068 int
1069 main_file_write (main_file *ofile, uint8_t *buf, usize_t size, const char *msg)
1070 {
1071 int ret = 0;
1072
1073 IF_DEBUG1(DP(RINT "[main] write %u\n bytes", size));
1074
1075 #if XD3_STDIO
1076 usize_t result;
1077
1078 result = fwrite (buf, 1, size, ofile->file);
1079
1080 if (result != size) { ret = get_errno (); }
1081
1082 #elif XD3_POSIX
1083 ret = xd3_posix_io (ofile->file, buf, size, (xd3_posix_func*) &write, NULL);
1084
1085 #elif XD3_WIN32
1086 ret = xd3_win32_io (ofile->file, buf, size, 0, NULL);
1087
1088 #endif
1089
1090 if (ret)
1091 {
1092 XPR(NT "%s: %s: %s\n", msg, ofile->filename, xd3_mainerror (ret));
1093 }
1094 else
1095 {
1096 if (option_verbose > 5) { XPR(NT "write %s: %u bytes\n",
1097 ofile->filename, size); }
1098 ofile->nwrite += size;
1099 }
1100
1101 return ret;
1102 }
1103
1104 static int
1105 main_file_seek (main_file *xfile, xoff_t pos)
1106 {
1107 int ret = 0;
1108
1109 #if XD3_STDIO
1110 if (fseek (xfile->file, pos, SEEK_SET) != 0) { ret = get_errno (); }
1111
1112 #elif XD3_POSIX
1113 if ((xoff_t) lseek (xfile->file, pos, SEEK_SET) != pos)
1114 { ret = get_errno (); }
1115
1116 #elif XD3_WIN32
1117 # if (_WIN32_WINNT >= 0x0500)
1118 LARGE_INTEGER move, out;
1119 move.QuadPart = pos;
1120 if (SetFilePointerEx(xfile->file, move, &out, FILE_BEGIN) == 0)
1121 {
1122 ret = get_errno ();
1123 }
1124 # else
1125 if (SetFilePointer(xfile->file, (LONG)pos, NULL, FILE_BEGIN) ==
1126 INVALID_SET_FILE_POINTER)
1127 {
1128 ret = get_errno ();
1129 }
1130 # endif
1131 #endif
1132
1133 return ret;
1134 }
1135
1136 /* This function simply writes the stream output buffer, if there is
1137 * any, for encode, decode and recode commands. (The VCDIFF tools use
1138 * main_print_func()). */
1139 static int
1140 main_write_output (xd3_stream* stream, main_file *ofile)
1141 {
1142 int ret;
1143
1144 IF_DEBUG1(DP(RINT "[main] write(%s) %u\n bytes", ofile->filename, stream->avail_out));
1145
1146 if (option_no_output)
1147 {
1148 return 0;
1149 }
1150
1151 if (stream->avail_out > 0 &&
1152 (ret = main_file_write (ofile, stream->next_out,
1153 stream->avail_out, "write failed")))
1154 {
1155 return ret;
1156 }
1157
1158 return 0;
1159 }
1160
1161 static int
1162 main_set_secondary_flags (xd3_config *config)
1163 {
1164 int ret;
1165 if (!option_use_secondary)
1166 {
1167 return 0;
1168 }
1169 if (option_secondary == NULL)
1170 {
1171 /* Set a default secondary compressor if LZMA is built in, otherwise
1172 * default to no secondary compressor. */
1173 if (SECONDARY_LZMA)
1174 {
1175 config->flags |= XD3_SEC_LZMA;
1176 }
1177 }
1178 else
1179 {
1180 if (strcmp (option_secondary, "lzma") == 0 && SECONDARY_LZMA)
1181 {
1182 config->flags |= XD3_SEC_LZMA;
1183 }
1184 else if (strcmp (option_secondary, "fgk") == 0 && SECONDARY_FGK)
1185 {
1186 config->flags |= XD3_SEC_FGK;
1187 }
1188 else if (strncmp (option_secondary, "djw", 3) == 0 && SECONDARY_DJW)
1189 {
1190 usize_t level = XD3_DEFAULT_SECONDARY_LEVEL;
1191
1192 config->flags |= XD3_SEC_DJW;
1193
1194 if (strlen (option_secondary) > 3 &&
1195 (ret = main_atou (option_secondary + 3,
1196 &level,
1197 0, 9, 'S')) != 0 &&
1198 !option_quiet)
1199 {
1200 return XD3_INVALID;
1201 }
1202
1203 /* XD3_SEC_NOXXXX flags disable secondary compression on
1204 * a per-section basis. For djw, ngroups=1 indicates
1205 * minimum work, ngroups=0 uses default settings, which
1206 * is > 1 groups by default. */
1207 if (level < 1) { config->flags |= XD3_SEC_NODATA; }
1208 if (level < 7) { config->sec_data.ngroups = 1; }
1209 else { config->sec_data.ngroups = 0; }
1210
1211 if (level < 3) { config->flags |= XD3_SEC_NOINST; }
1212 if (level < 8) { config->sec_inst.ngroups = 1; }
1213 else { config->sec_inst.ngroups = 0; }
1214
1215 if (level < 5) { config->flags |= XD3_SEC_NOADDR; }
1216 if (level < 9) { config->sec_addr.ngroups = 1; }
1217 else { config->sec_addr.ngroups = 0; }
1218 }
1219 else if (*option_secondary == 0 ||
1220 strcmp (option_secondary, "none") == 0)
1221 {
1222 }
1223 else
1224 {
1225 if (!option_quiet)
1226 {
1227 XPR(NT "unrecognized or not compiled secondary compressor: %s\n",
1228 option_secondary);
1229 }
1230 return XD3_INVALID;
1231 }
1232 }
1233
1234 return 0;
1235 }
1236
1237 /******************************************************************
1238 VCDIFF TOOLS
1239 *****************************************************************/
1240
1241 #include "xdelta3-merge.h"
1242
1243 #if VCDIFF_TOOLS
1244
1245 /* The following macros let VCDIFF print using main_file_write(),
1246 * for example:
1247 *
1248 * VC(UT "trying to be portable: %d\n", x)VE;
1249 */
1250 #define SNPRINTF_BUFSIZE 1024
1251 #define VC do { if (((ret = xsnprintf_func
1252 #define UT (char*)xfile->snprintf_buf, SNPRINTF_BUFSIZE,
1253 #define VE ) >= SNPRINTF_BUFSIZE \
1254 && (ret = main_print_overflow(ret)) != 0) \
1255 || (ret = main_file_write(xfile, xfile->snprintf_buf, \
1256 (usize_t)ret, "print")) != 0) \
1257 { return ret; } } while (0)
1258
1259 static int
1260 main_print_overflow (int x)
1261 {
1262 XPR(NT "internal print buffer overflow: %d bytes\n", x);
1263 return XD3_INTERNAL;
1264 }
1265
1266 /* This function prints a single VCDIFF window. */
1267 static int
1268 main_print_window (xd3_stream* stream, main_file *xfile)
1269 {
1270 int ret;
1271 usize_t size = 0;
1272
1273 VC(UT " Offset Code Type1 Size1 @Addr1 + Type2 Size2 @Addr2\n")VE;
1274
1275 while (stream->inst_sect.buf < stream->inst_sect.buf_max)
1276 {
1277 usize_t code = stream->inst_sect.buf[0];
1278 const uint8_t *addr_before = stream->addr_sect.buf;
1279 const uint8_t *inst_before = stream->inst_sect.buf;
1280 usize_t addr_bytes;
1281 usize_t inst_bytes;
1282 usize_t size_before = size;
1283
1284 if ((ret = xd3_decode_instruction (stream)))
1285 {
1286 XPR(NT "instruction decode error at %"Q"u: %s\n",
1287 stream->dec_winstart + size, stream->msg);
1288 return ret;
1289 }
1290
1291 addr_bytes = (usize_t)(stream->addr_sect.buf - addr_before);
1292 inst_bytes = (usize_t)(stream->inst_sect.buf - inst_before);
1293
1294 VC(UT " %06"Q"u %03u %s %6u", stream->dec_winstart + size,
1295 option_print_cpymode ? code : 0,
1296 xd3_rtype_to_string ((xd3_rtype) stream->dec_current1.type,
1297 option_print_cpymode),
1298 stream->dec_current1.size)VE;
1299
1300 if (stream->dec_current1.type != XD3_NOOP)
1301 {
1302 if (stream->dec_current1.type >= XD3_CPY)
1303 {
1304 if (stream->dec_current1.addr >= stream->dec_cpylen)
1305 {
1306 VC(UT " T@%-6u",
1307 stream->dec_current1.addr - stream->dec_cpylen)VE;
1308 }
1309 else
1310 {
1311 VC(UT " S@%-6"Q"u",
1312 stream->dec_cpyoff + stream->dec_current1.addr)VE;
1313 }
1314 }
1315 else
1316 {
1317 VC(UT " ")VE;
1318 }
1319
1320 size += stream->dec_current1.size;
1321 }
1322
1323 if (stream->dec_current2.type != XD3_NOOP)
1324 {
1325 VC(UT " %s %6u",
1326 xd3_rtype_to_string ((xd3_rtype) stream->dec_current2.type,
1327 option_print_cpymode),
1328 stream->dec_current2.size)VE;
1329
1330 if (stream->dec_current2.type >= XD3_CPY)
1331 {
1332 if (stream->dec_current2.addr >= stream->dec_cpylen)
1333 {
1334 VC(UT " T@%-6u",
1335 stream->dec_current2.addr - stream->dec_cpylen)VE;
1336 }
1337 else
1338 {
1339 VC(UT " S@%-6"Q"u",
1340 stream->dec_cpyoff + stream->dec_current2.addr)VE;
1341 }
1342 }
1343
1344 size += stream->dec_current2.size;
1345 }
1346
1347 VC(UT "\n")VE;
1348
1349 if (option_verbose &&
1350 addr_bytes + inst_bytes >= (size - size_before) &&
1351 (stream->dec_current1.type >= XD3_CPY ||
1352 stream->dec_current2.type >= XD3_CPY))
1353 {
1354 VC(UT " %06"Q"u (inefficiency) %u encoded as %u bytes\n",
1355 stream->dec_winstart + size_before,
1356 size - size_before,
1357 addr_bytes + inst_bytes)VE;
1358 }
1359 }
1360
1361 if (stream->dec_tgtlen != size && (stream->flags & XD3_SKIP_WINDOW) == 0)
1362 {
1363 XPR(NT "target window size inconsistency");
1364 return XD3_INTERNAL;
1365 }
1366
1367 if (stream->dec_position != stream->dec_maxpos)
1368 {
1369 XPR(NT "target window position inconsistency");
1370 return XD3_INTERNAL;
1371 }
1372
1373 if (stream->addr_sect.buf != stream->addr_sect.buf_max)
1374 {
1375 XPR(NT "address section inconsistency");
1376 return XD3_INTERNAL;
1377 }
1378
1379 return 0;
1380 }
1381
1382 static int
1383 main_print_vcdiff_file (main_file *xfile, main_file *file, const char *type)
1384 {
1385 int ret; /* Used by above macros */
1386 if (file->filename)
1387 {
1388 VC(UT "XDELTA filename (%s): %s\n", type,
1389 file->filename)VE;
1390 }
1391 if (file->compressor)
1392 {
1393 VC(UT "XDELTA ext comp (%s): %s\n", type,
1394 file->compressor->recomp_cmdname)VE;
1395 }
1396 return 0;
1397 }
1398
1399 /* This function prints a VCDIFF input, mainly for debugging purposes. */
1400 static int
1401 main_print_func (xd3_stream* stream, main_file *xfile)
1402 {
1403 int ret;
1404
1405 if (option_no_output)
1406 {
1407 return 0;
1408 }
1409
1410 if (xfile->snprintf_buf == NULL)
1411 {
1412 if ((xfile->snprintf_buf =
1413 (uint8_t*)main_malloc(SNPRINTF_BUFSIZE)) == NULL)
1414 {
1415 return ENOMEM;
1416 }
1417 }
1418
1419 if (stream->dec_winstart == 0)
1420 {
1421 VC(UT "VCDIFF version: 0\n")VE;
1422 VC(UT "VCDIFF header size: %d\n",
1423 stream->dec_hdrsize)VE;
1424 VC(UT "VCDIFF header indicator: ")VE;
1425 if ((stream->dec_hdr_ind & VCD_SECONDARY) != 0)
1426 VC(UT "VCD_SECONDARY ")VE;
1427 if ((stream->dec_hdr_ind & VCD_CODETABLE) != 0)
1428 VC(UT "VCD_CODETABLE ")VE;
1429 if ((stream->dec_hdr_ind & VCD_APPHEADER) != 0)
1430 VC(UT "VCD_APPHEADER ")VE;
1431 if (stream->dec_hdr_ind == 0)
1432 VC(UT "none")VE;
1433 VC(UT "\n")VE;
1434
1435 IF_SEC(VC(UT "VCDIFF secondary compressor: %s\n",
1436 stream->sec_type ? stream->sec_type->name : "none")VE);
1437 IF_NSEC(VC(UT "VCDIFF secondary compressor: unsupported\n")VE);
1438
1439 if (stream->dec_hdr_ind & VCD_APPHEADER)
1440 {
1441 uint8_t *apphead;
1442 usize_t appheadsz;
1443 ret = xd3_get_appheader (stream, & apphead, & appheadsz);
1444
1445 if (ret == 0 && appheadsz > 0)
1446 {
1447 int sq = option_quiet;
1448 main_file i, o, s;
1449 XD3_ASSERT (apphead != NULL);
1450 VC(UT "VCDIFF application header: ")VE;
1451 if ((ret = main_file_write (xfile, apphead,
1452 appheadsz, "print")) != 0)
1453 { return ret; }
1454 VC(UT "\n")VE;
1455
1456 main_file_init (& i);
1457 main_file_init (& o);
1458 main_file_init (& s);
1459 option_quiet = 1;
1460 main_get_appheader (stream, &i, & o, & s);
1461 option_quiet = sq;
1462 if ((ret = main_print_vcdiff_file (xfile, & o, "output")))
1463 { return ret; }
1464 if ((ret = main_print_vcdiff_file (xfile, & s, "source")))
1465 { return ret; }
1466 main_file_cleanup (& i);
1467 main_file_cleanup (& o);
1468 main_file_cleanup (& s);
1469 }
1470 }
1471 }
1472 else
1473 {
1474 VC(UT "\n")VE;
1475 }
1476
1477 VC(UT "VCDIFF window number: %"Q"u\n", stream->current_window)VE;
1478 VC(UT "VCDIFF window indicator: ")VE;
1479 if ((stream->dec_win_ind & VCD_SOURCE) != 0) VC(UT "VCD_SOURCE ")VE;
1480 if ((stream->dec_win_ind & VCD_TARGET) != 0) VC(UT "VCD_TARGET ")VE;
1481 if ((stream->dec_win_ind & VCD_ADLER32) != 0) VC(UT "VCD_ADLER32 ")VE;
1482 if (stream->dec_win_ind == 0) VC(UT "none")VE;
1483 VC(UT "\n")VE;
1484
1485 if ((stream->dec_win_ind & VCD_ADLER32) != 0)
1486 {
1487 VC(UT "VCDIFF adler32 checksum: %08X\n",
1488 (usize_t)stream->dec_adler32)VE;
1489 }
1490
1491 if (stream->dec_del_ind != 0)
1492 {
1493 VC(UT "VCDIFF delta indicator: ")VE;
1494 if ((stream->dec_del_ind & VCD_DATACOMP) != 0) VC(UT "VCD_DATACOMP ")VE;
1495 if ((stream->dec_del_ind & VCD_INSTCOMP) != 0) VC(UT "VCD_INSTCOMP ")VE;
1496 if ((stream->dec_del_ind & VCD_ADDRCOMP) != 0) VC(UT "VCD_ADDRCOMP ")VE;
1497 if (stream->dec_del_ind == 0) VC(UT "none")VE;
1498 VC(UT "\n")VE;
1499 }
1500
1501 if (stream->dec_winstart != 0)
1502 {
1503 VC(UT "VCDIFF window at offset: %"Q"u\n", stream->dec_winstart)VE;
1504 }
1505
1506 if (SRCORTGT (stream->dec_win_ind))
1507 {
1508 VC(UT "VCDIFF copy window length: %u\n",
1509 (usize_t)stream->dec_cpylen)VE;
1510 VC(UT "VCDIFF copy window offset: %"Q"u\n",
1511 stream->dec_cpyoff)VE;
1512 }
1513
1514 VC(UT "VCDIFF delta encoding length: %u\n",
1515 (usize_t)stream->dec_enclen)VE;
1516 VC(UT "VCDIFF target window length: %u\n",
1517 (usize_t)stream->dec_tgtlen)VE;
1518
1519 VC(UT "VCDIFF data section length: %u\n",
1520 (usize_t)stream->data_sect.size)VE;
1521 VC(UT "VCDIFF inst section length: %u\n",
1522 (usize_t)stream->inst_sect.size)VE;
1523 VC(UT "VCDIFF addr section length: %u\n",
1524 (usize_t)stream->addr_sect.size)VE;
1525
1526 ret = 0;
1527 if ((stream->flags & XD3_JUST_HDR) != 0)
1528 {
1529 /* Print a header -- finished! */
1530 ret = PRINTHDR_SPECIAL;
1531 }
1532 else if ((stream->flags & XD3_SKIP_WINDOW) == 0)
1533 {
1534 ret = main_print_window (stream, xfile);
1535 }
1536
1537 return ret;
1538 }
1539
1540 static int
1541 main_recode_copy (xd3_stream* stream,
1542 xd3_output* output,
1543 xd3_desect* input)
1544 {
1545 int ret;
1546
1547 XD3_ASSERT(output != NULL);
1548 XD3_ASSERT(output->next_page == NULL);
1549
1550 if ((ret = xd3_decode_allocate (recode_stream,
1551 input->size,
1552 &output->base,
1553 &output->avail)))
1554 {
1555 XPR(NT XD3_LIB_ERRMSG (stream, ret));
1556 return ret;
1557 }
1558
1559 memcpy (output->base,
1560 /* Note: decoder advances buf, so get base of buffer with
1561 * buf_max - size */
1562 input->buf_max - input->size,
1563 input->size);
1564 output->next = input->size;
1565 return 0;
1566 }
1567
1568 // Re-encode one window
1569 static int
1570 main_recode_func (xd3_stream* stream, main_file *ofile)
1571 {
1572 int ret;
1573 xd3_source decode_source;
1574
1575 XD3_ASSERT(stream->dec_state == DEC_FINISH);
1576 XD3_ASSERT(recode_stream->enc_state == ENC_INIT ||
1577 recode_stream->enc_state == ENC_INPUT);
1578
1579 // Copy partial decoder output to partial encoder inputs
1580 if ((ret = main_recode_copy (recode_stream,
1581 DATA_HEAD(recode_stream),
1582 &stream->data_sect)) ||
1583 (ret = main_recode_copy (recode_stream,
1584 INST_HEAD(recode_stream),
1585 &stream->inst_sect)) ||
1586 (ret = main_recode_copy (recode_stream,
1587 ADDR_HEAD(recode_stream),
1588 &stream->addr_sect)))
1589 {
1590 return ret;
1591 }
1592
1593 // This jumps to xd3_emit_hdr()
1594 recode_stream->enc_state = ENC_FLUSH;
1595 recode_stream->avail_in = stream->dec_tgtlen;
1596
1597 if (SRCORTGT (stream->dec_win_ind))
1598 {
1599 recode_stream->src = & decode_source;
1600 decode_source.srclen = stream->dec_cpylen;
1601 decode_source.srcbase = stream->dec_cpyoff;
1602 }
1603
1604 if (option_use_checksum &&
1605 (stream->dec_win_ind & VCD_ADLER32) != 0)
1606 {
1607 recode_stream->flags |= XD3_ADLER32_RECODE;
1608 recode_stream->recode_adler32 = stream->dec_adler32;
1609 }
1610
1611 if (option_use_appheader != 0 &&
1612 option_appheader != NULL)
1613 {
1614 xd3_set_appheader (recode_stream, option_appheader,
1615 (usize_t) strlen ((char*) option_appheader));
1616 }
1617 else if (option_use_appheader != 0 &&
1618 option_appheader == NULL)
1619 {
1620 if (stream->dec_appheader != NULL)
1621 {
1622 xd3_set_appheader (recode_stream,
1623 stream->dec_appheader, stream->dec_appheadsz);
1624 }
1625 }
1626
1627 // Output loop
1628 for (;;)
1629 {
1630 switch((ret = xd3_encode_input (recode_stream)))
1631 {
1632 case XD3_INPUT: {
1633 /* finished recoding one window */
1634 stream->total_out = recode_stream->total_out;
1635 return 0;
1636 }
1637 case XD3_OUTPUT: {
1638 /* main_file_write below */
1639 break;
1640 }
1641 case XD3_GOTHEADER:
1642 case XD3_WINSTART:
1643 case XD3_WINFINISH: {
1644 /* ignore */
1645 continue;
1646 }
1647 case XD3_GETSRCBLK:
1648 case 0: {
1649 return XD3_INTERNAL;
1650 }
1651 default:
1652 return ret;
1653 }
1654
1655 if ((ret = main_write_output (recode_stream, ofile)))
1656 {
1657 return ret;
1658 }
1659
1660 xd3_consume_output (recode_stream);
1661 }
1662 }
1663 #endif /* VCDIFF_TOOLS */
1664
1665 /*******************************************************************
1666 VCDIFF merging
1667 ******************************************************************/
1668
1669 #if VCDIFF_TOOLS
1670 /* Modifies static state. */
1671 static int
1672 main_init_recode_stream (void)
1673 {
1674 int ret;
1675 int stream_flags = XD3_ADLER32_NOVER | XD3_SKIP_EMIT;
1676 int recode_flags;
1677 xd3_config recode_config;
1678
1679 XD3_ASSERT (recode_stream == NULL);
1680
1681 if ((recode_stream = (xd3_stream*) main_malloc(sizeof(xd3_stream))) == NULL)
1682 {
1683 return ENOMEM;
1684 }
1685
1686 recode_flags = (stream_flags & XD3_SEC_TYPE);
1687
1688 recode_config.alloc = main_alloc;
1689 recode_config.freef = main_free1;
1690
1691 xd3_init_config(&recode_config, recode_flags);
1692
1693 if ((ret = main_set_secondary_flags (&recode_config)) ||
1694 (ret = xd3_config_stream (recode_stream, &recode_config)) ||
1695 (ret = xd3_encode_init_partial (recode_stream)) ||
1696 (ret = xd3_whole_state_init (recode_stream)))
1697 {
1698 XPR(NT XD3_LIB_ERRMSG (recode_stream, ret));
1699 xd3_free_stream (recode_stream);
1700 recode_stream = NULL;
1701 return ret;
1702 }
1703
1704 return 0;
1705 }
1706
1707 /* This processes the sequence of -m arguments. The final input
1708 * is processed as part of the ordinary main_input() loop. */
1709 static int
1710 main_merge_arguments (main_merge_list* merges)
1711 {
1712 int ret = 0;
1713 int count = 0;
1714 main_merge *merge = NULL;
1715 xd3_stream merge_input;
1716
1717 if (main_merge_list_empty (merges))
1718 {
1719 return 0;
1720 }
1721
1722 if ((ret = xd3_config_stream (& merge_input, NULL)) ||
1723 (ret = xd3_whole_state_init (& merge_input)))
1724 {
1725 XPR(NT XD3_LIB_ERRMSG (& merge_input, ret));
1726 return ret;
1727 }
1728
1729 merge = main_merge_list_front (merges);
1730 while (!main_merge_list_end (merges, merge))
1731 {
1732 main_file mfile;
1733 main_file_init (& mfile);
1734 mfile.filename = merge->filename;
1735 mfile.flags = RD_NONEXTERNAL;
1736
1737 if ((ret = main_file_open (& mfile, merge->filename, XO_READ)))
1738 {
1739 goto error;
1740 }
1741
1742 ret = main_input (CMD_MERGE_ARG, & mfile, NULL, NULL);
1743
1744 if (ret == 0)
1745 {
1746 if (count++ == 0)
1747 {
1748 /* The first merge source is the next merge input. */
1749 xd3_swap_whole_state (& recode_stream->whole_target,
1750 & merge_input.whole_target);
1751 }
1752 else
1753 {
1754 /* Merge the recode_stream with merge_input. */
1755 ret = xd3_merge_input_output (recode_stream,
1756 & merge_input.whole_target);
1757
1758 /* Save the next merge source in merge_input. */
1759 xd3_swap_whole_state (& recode_stream->whole_target,
1760 & merge_input.whole_target);
1761 }
1762 }
1763
1764 main_file_cleanup (& mfile);
1765
1766 if (recode_stream != NULL)
1767 {
1768 xd3_free_stream (recode_stream);
1769 main_free (recode_stream);
1770 recode_stream = NULL;
1771 }
1772
1773 if (main_bdata != NULL)
1774 {
1775 main_buffree (main_bdata);
1776 main_bdata = NULL;
1777 main_bsize = 0;
1778 }
1779
1780 if (ret != 0)
1781 {
1782 goto error;
1783 }
1784
1785 merge = main_merge_list_next (merge);
1786 }
1787
1788 XD3_ASSERT (merge_stream == NULL);
1789
1790 if ((merge_stream = (xd3_stream*) main_malloc (sizeof(xd3_stream))) == NULL)
1791 {
1792 ret = ENOMEM;
1793 goto error;
1794 }
1795
1796 if ((ret = xd3_config_stream (merge_stream, NULL)) ||
1797 (ret = xd3_whole_state_init (merge_stream)))
1798 {
1799 XPR(NT XD3_LIB_ERRMSG (& merge_input, ret));
1800 goto error;
1801 }
1802
1803 xd3_swap_whole_state (& merge_stream->whole_target,
1804 & merge_input.whole_target);
1805 ret = 0;
1806 error:
1807 xd3_free_stream (& merge_input);
1808 return ret;
1809 }
1810
1811 /* This processes each window of the final merge input. This routine
1812 * does not output, it buffers the entire delta into memory. */
1813 static int
1814 main_merge_func (xd3_stream* stream, main_file *no_write)
1815 {
1816 int ret;
1817
1818 if ((ret = xd3_whole_append_window (stream)))
1819 {
1820 return ret;
1821 }
1822
1823 return 0;
1824 }
1825
1826
1827 /* This is called after all windows have been read, as a final step in
1828 * main_input(). This is only called for the final merge step. */
1829 static int
1830 main_merge_output (xd3_stream *stream, main_file *ofile)
1831 {
1832 int ret;
1833 usize_t inst_pos = 0;
1834 xoff_t output_pos = 0;
1835 xd3_source recode_source;
1836 usize_t window_num = 0;
1837 int at_least_once = 0;
1838
1839 /* merge_stream is set if there were arguments. this stream's input
1840 * needs to be applied to the merge_stream source. */
1841 if ((merge_stream != NULL) &&
1842 (ret = xd3_merge_input_output (stream,
1843 & merge_stream->whole_target)))
1844 {
1845 XPR(NT XD3_LIB_ERRMSG (stream, ret));
1846 return ret;
1847 }
1848
1849 if (option_use_appheader != 0 &&
1850 option_appheader != NULL)
1851 {
1852 xd3_set_appheader (recode_stream, option_appheader,
1853 (usize_t) strlen ((char*) option_appheader));
1854 }
1855
1856 /* Enter the ENC_INPUT state and bypass the next_in == NULL test
1857 * and (leftover) input buffering logic. */
1858 XD3_ASSERT(recode_stream->enc_state == ENC_INIT);
1859 recode_stream->enc_state = ENC_INPUT;
1860 recode_stream->next_in = main_bdata;
1861 recode_stream->flags |= XD3_FLUSH;
1862
1863 /* This encodes the entire target. */
1864 while (inst_pos < stream->whole_target.instlen || !at_least_once)
1865 {
1866 xoff_t window_start = output_pos;
1867 int window_srcset = 0;
1868 xoff_t window_srcmin = 0;
1869 xoff_t window_srcmax = 0;
1870 usize_t window_pos = 0;
1871 usize_t window_size;
1872
1873 /* at_least_once ensures that we encode at least one window,
1874 * which handles the 0-byte case. */
1875 at_least_once = 1;
1876
1877 XD3_ASSERT (recode_stream->enc_state == ENC_INPUT);
1878
1879 if ((ret = xd3_encode_input (recode_stream)) != XD3_WINSTART)
1880 {
1881 XPR(NT "invalid merge state: %s\n", xd3_mainerror (ret));
1882 return XD3_INVALID;
1883 }
1884
1885 /* Window sizes must match from the input to the output, so that
1886 * target copies are in-range (and so that checksums carry
1887 * over). */
1888 XD3_ASSERT (window_num < stream->whole_target.wininfolen);
1889 window_size = stream->whole_target.wininfo[window_num].length;
1890
1891 /* Output position should also match. */
1892 if (output_pos != stream->whole_target.wininfo[window_num].offset)
1893 {
1894 XPR(NT "internal merge error: offset mismatch\n");
1895 return XD3_INVALID;
1896 }
1897
1898 if (option_use_checksum &&
1899 (stream->dec_win_ind & VCD_ADLER32) != 0)
1900 {
1901 recode_stream->flags |= XD3_ADLER32_RECODE;
1902 recode_stream->recode_adler32 =
1903 stream->whole_target.wininfo[window_num].adler32;
1904 }
1905
1906 window_num++;
1907
1908 if (main_bsize < window_size)
1909 {
1910 main_buffree (main_bdata);
1911 main_bdata = NULL;
1912 main_bsize = 0;
1913 if ((main_bdata = (uint8_t*)
1914 main_bufalloc (window_size)) == NULL)
1915 {
1916 return ENOMEM;
1917 }
1918 main_bsize = window_size;
1919 }
1920
1921 /* This encodes a single target window. */
1922 while (window_pos < window_size &&
1923 inst_pos < stream->whole_target.instlen)
1924 {
1925 xd3_winst *inst = &stream->whole_target.inst[inst_pos];
1926 usize_t take = xd3_min(inst->size, window_size - window_pos);
1927 xoff_t addr;
1928
1929 switch (inst->type)
1930 {
1931 case XD3_RUN:
1932 if ((ret = xd3_emit_run (recode_stream, window_pos, take,
1933 &stream->whole_target.adds[inst->addr])))
1934 {
1935 return ret;
1936 }
1937 break;
1938
1939 case XD3_ADD:
1940 /* Adds are implicit, put them into the input buffer. */
1941 memcpy (main_bdata + window_pos,
1942 stream->whole_target.adds + inst->addr, take);
1943 break;
1944
1945 default: /* XD3_COPY + copy mode */
1946 if (inst->mode != 0)
1947 {
1948 if (window_srcset) {
1949 window_srcmin = xd3_min (window_srcmin, inst->addr);
1950 window_srcmax = xd3_max (window_srcmax, inst->addr + take);
1951 } else {
1952 window_srcset = 1;
1953 window_srcmin = inst->addr;
1954 window_srcmax = inst->addr + take;
1955 }
1956 addr = inst->addr;
1957 }
1958 else
1959 {
1960 XD3_ASSERT (inst->addr >= window_start);
1961 addr = inst->addr - window_start;
1962 }
1963 IF_DEBUG2 (XPR(NTR "[merge copy] winpos %u take %u addr %"Q"u mode %u\n",
1964 window_pos, take, addr, inst->mode));
1965 if ((ret = xd3_found_match (recode_stream, window_pos, take,
1966 addr, inst->mode != 0)))
1967 {
1968 return ret;
1969 }
1970 break;
1971 }
1972
1973 window_pos += take;
1974 output_pos += take;
1975
1976 if (take == inst->size)
1977 {
1978 inst_pos += 1;
1979 }
1980 else
1981 {
1982 /* Modify the instruction for the next pass. */
1983 if (inst->type != XD3_RUN)
1984 {
1985 inst->addr += take;
1986 }
1987 inst->size -= take;
1988 }
1989 }
1990
1991 xd3_avail_input (recode_stream, main_bdata, window_pos);
1992
1993 recode_stream->enc_state = ENC_INSTR;
1994
1995 if (window_srcset) {
1996 recode_stream->srcwin_decided = 1;
1997 recode_stream->src = &recode_source;
1998 recode_source.srclen = (usize_t)(window_srcmax - window_srcmin);
1999 recode_source.srcbase = window_srcmin;
2000 recode_stream->taroff = recode_source.srclen;
2001
2002 XD3_ASSERT (recode_source.srclen != 0);
2003 } else {
2004 recode_stream->srcwin_decided = 0;
2005 recode_stream->src = NULL;
2006 recode_stream->taroff = 0;
2007 }
2008
2009 for (;;)
2010 {
2011 switch ((ret = xd3_encode_input (recode_stream)))
2012 {
2013 case XD3_INPUT: {
2014 goto done_window;
2015 }
2016 case XD3_OUTPUT: {
2017 /* main_file_write below */
2018 break;
2019 }
2020 case XD3_GOTHEADER:
2021 case XD3_WINSTART:
2022 case XD3_WINFINISH: {
2023 /* ignore */
2024 continue;
2025 }
2026 case XD3_GETSRCBLK:
2027 case 0: {
2028 return XD3_INTERNAL;
2029 }
2030 default:
2031 return ret;
2032 }
2033
2034 if ((ret = main_write_output(recode_stream, ofile)))
2035 {
2036 return ret;
2037 }
2038
2039 xd3_consume_output (recode_stream);
2040 }
2041 done_window:
2042 (void) 0;
2043 }
2044
2045 return 0;
2046 }
2047 #endif
2048
2049 /*******************************************************************
2050 Input decompression, output recompression
2051 ******************************************************************/
2052
2053 #if EXTERNAL_COMPRESSION
2054 /* This is tricky POSIX-specific code with lots of fork(), pipe(),
2055 * dup(), waitpid(), and exec() business. Most of this code
2056 * originated in PRCS1, which did automatic package-file
2057 * decompression. It works with both XD3_POSIX and XD3_STDIO file
2058 * disciplines.
2059 *
2060 * To automatically detect compressed inputs requires a child process
2061 * to reconstruct the input stream, which was advanced in order to
2062 * detect compression, because it may not be seekable. In other
2063 * words, the main program reads part of the input stream, and if it
2064 * detects a compressed input it then forks a pipe copier process,
2065 * which copies the first-read block out of the main-program's memory,
2066 * then streams the remaining compressed input into the
2067 * input-decompression pipe.
2068 */
2069
2070 #include <signal.h>
2071 #include <unistd.h>
2072 #include <sys/stat.h>
2073 #include <sys/wait.h>
2074
2075 /* Remember which pipe FD is which. */
2076 #define PIPE_READ_FD 0
2077 #define PIPE_WRITE_FD 1
2078 #define MAX_SUBPROCS 4 /* max(source + copier + output,
2079 source + copier + input + copier). */
2080 static pid_t ext_subprocs[MAX_SUBPROCS];
2081
2082 /* Like write(), applies to a fd instead of a main_file, for the pipe
2083 * copier subprocess. Does not print an error, to facilitate ignoring
2084 * trailing garbage, see main_pipe_copier(). */
2085 static int
2086 main_pipe_write (int outfd, uint8_t *exist_buf, usize_t remain)
2087 {
2088 int ret;
2089
2090 if ((ret = xd3_posix_io (outfd, exist_buf, remain,
2091 (xd3_posix_func*) &write, NULL)))
2092 {
2093 return ret;
2094 }
2095
2096 return 0;
2097 }
2098
2099 /* A simple error-reporting waitpid interface. */
2100 static int
2101 main_waitpid_check(pid_t pid)
2102 {
2103 int status;
2104 int ret = 0;
2105
2106 if (waitpid (pid, & status, 0) < 0)
2107 {
2108 ret = get_errno ();
2109 XPR(NT "external compression [pid %d] wait: %s\n",
2110 pid, xd3_mainerror (ret));
2111 }
2112 else if (! WIFEXITED (status))
2113 {
2114 // SIGPIPE will be delivered to the child process whenever it
2115 // writes data after this process closes the pipe,
2116 // happens if xdelta does not require access to the entire
2117 // source file. Considered normal.
2118 if (! WIFSIGNALED (status) || WTERMSIG (status) != SIGPIPE)
2119 {
2120 ret = ECHILD;
2121 XPR(NT "external compression [pid %d] signal %d\n", pid,
2122 WIFSIGNALED (status) ? WTERMSIG (status) : WSTOPSIG (status));
2123 }
2124 else if (option_verbose)
2125 {
2126 XPR(NT "external compression sigpipe\n");
2127 }
2128 }
2129 else if (WEXITSTATUS (status) != 0)
2130 {
2131 ret = ECHILD;
2132 if (option_verbose > 1)
2133 {
2134 /* Presumably, the error was printed by the subprocess. */
2135 XPR(NT "external compression [pid %d] exit %d\n",
2136 pid, WEXITSTATUS (status));
2137 }
2138 }
2139
2140 return ret;
2141 }
2142
2143 /* Wait for any existing child processes to check for abnormal exit. */
2144 static int
2145 main_external_compression_finish (void)
2146 {
2147 int i;
2148 int ret;
2149
2150 for (i = 0; i < num_subprocs; i += 1)
2151 {
2152 if (! ext_subprocs[i]) { continue; }
2153
2154 if ((ret = main_waitpid_check (ext_subprocs[i])))
2155 {
2156 return ret;
2157 }
2158
2159 ext_subprocs[i] = 0;
2160 }
2161
2162 return 0;
2163 }
2164
2165 /* Kills any outstanding compression process. */
2166 static void
2167 main_external_compression_cleanup (void)
2168 {
2169 int i;
2170
2171 for (i = 0; i < num_subprocs; i += 1)
2172 {
2173 if (! ext_subprocs[i]) { continue; }
2174
2175 kill (ext_subprocs[i], SIGTERM);
2176
2177 ext_subprocs[i] = 0;
2178 }
2179 }
2180
2181 /* This runs as a forked process of main_input_decompress_setup() to
2182 * copy input to the decompression process. First, the available
2183 * input is copied out of the existing buffer, then the buffer is
2184 * reused to continue reading from the compressed input file. */
2185 static int
2186 main_pipe_copier (uint8_t *pipe_buf,
2187 usize_t pipe_bufsize,
2188 size_t nread,
2189 main_file *ifile,
2190 int outfd)
2191 {
2192 int ret;
2193 xoff_t skipped = 0;
2194
2195 /* Prevent SIGPIPE signals, allow EPIPE return values instead. This
2196 * is safe to comment-out, except that the -F flag will not work
2197 * properly (the parent would need to treat WTERMSIG(status) ==
2198 * SIGPIPE). */
2199 struct sigaction sa;
2200 sa.sa_handler = SIG_IGN;
2201 sigaction (SIGPIPE, &sa, NULL);
2202
2203 for (;;)
2204 {
2205 /* force_drain will be set when option_force and EPIPE cause us
2206 * to skip data. This is reset each time through the loop, so
2207 * the break condition below works. */
2208 int force_drain = 0;
2209 if (nread > 0 && (ret = main_pipe_write (outfd, pipe_buf, nread)))
2210 {
2211 if (ret == EPIPE)
2212 {
2213 /* This causes the loop to continue reading until nread
2214 * == 0. */
2215 skipped += nread;
2216 force_drain = 1;
2217 }
2218 else
2219 {
2220 XPR(NT "pipe write failed: %s\n", xd3_mainerror (ret));
2221 return ret;
2222 }
2223 }
2224
2225 if (nread < pipe_bufsize && !force_drain)
2226 {
2227 break;
2228 }
2229
2230 if ((ret = main_file_read (ifile, pipe_buf, pipe_bufsize,
2231 & nread, "pipe read failed")) < 0)
2232 {
2233 return ret;
2234 }
2235 }
2236
2237 if (option_verbose && skipped != 0)
2238 {
2239 XPR(NT "skipping %"Q"u bytes in %s\n",
2240 skipped, ifile->filename);
2241 }
2242 return 0;
2243 }
2244
2245 /* This function is called after we have read some amount of data from
2246 * the input file and detected a compressed input. Here we start a
2247 * decompression subprocess by forking twice. The first process runs
2248 * the decompression command, the second process copies data to the
2249 * input of the first. */
2250 static int
2251 main_input_decompress_setup (const main_extcomp *decomp,
2252 main_file *ifile,
2253 uint8_t *input_buf,
2254 usize_t input_bufsize,
2255 uint8_t *pipe_buf,
2256 usize_t pipe_bufsize,
2257 usize_t pipe_avail,
2258 size_t *nread)
2259 {
2260 /* The two pipes: input and output file descriptors. */
2261 int outpipefd[2], inpipefd[2];
2262 int input_fd = -1; /* The resulting input_fd (output of decompression). */
2263 pid_t decomp_id, copier_id; /* The two subprocs. */
2264 int ret;
2265
2266 outpipefd[0] = outpipefd[1] = -1;
2267 inpipefd[0] = inpipefd[1] = -1;
2268
2269 if (pipe (outpipefd) || pipe (inpipefd))
2270 {
2271 XPR(NT "pipe failed: %s\n", xd3_mainerror (ret = get_errno ()));
2272 goto pipe_cleanup;
2273 }
2274
2275 if ((decomp_id = fork ()) < 0)
2276 {
2277 XPR(NT "fork failed: %s\n", xd3_mainerror (ret = get_errno ()));
2278 goto pipe_cleanup;
2279 }
2280
2281 /* The first child runs the decompression process: */
2282 if (decomp_id == 0)
2283 {
2284 if (option_verbose > 2)
2285 {
2286 XPR(NT "external decompression pid %d\n", getpid ());
2287 }
2288
2289 /* Setup pipes: write to the outpipe, read from the inpipe. */
2290 if (dup2 (outpipefd[PIPE_WRITE_FD], STDOUT_FILENO) < 0 ||
2291 dup2 (inpipefd[PIPE_READ_FD], STDIN_FILENO) < 0 ||
2292 close (outpipefd[PIPE_READ_FD]) ||
2293 close (outpipefd[PIPE_WRITE_FD]) ||
2294 close (inpipefd[PIPE_READ_FD]) ||
2295 close (inpipefd[PIPE_WRITE_FD]) ||
2296 execlp (decomp->decomp_cmdname, decomp->decomp_cmdname,
2297 decomp->decomp_options,
2298 option_force2 ? "-f" : NULL,
2299 NULL))
2300 {
2301 XPR(NT "child process %s failed to execute: %s\n",
2302 decomp->decomp_cmdname, xd3_mainerror (get_errno ()));
2303 }
2304
2305 _exit (127);
2306 }
2307
2308 XD3_ASSERT(num_subprocs < MAX_SUBPROCS);
2309 ext_subprocs[num_subprocs++] = decomp_id;
2310
2311 if ((copier_id = fork ()) < 0)
2312 {
2313 XPR(NT "fork failed: %s\n", xd3_mainerror (ret = get_errno ()));
2314 goto pipe_cleanup;
2315 }
2316
2317 /* The second child runs the copier process: */
2318 if (copier_id == 0)
2319 {
2320 int exitval = 0;
2321
2322 if (option_verbose > 2)
2323 {
2324 XPR(NT "child pipe-copier pid %d\n", getpid ());
2325 }
2326
2327 if (close (inpipefd[PIPE_READ_FD]) ||
2328 close (outpipefd[PIPE_READ_FD]) ||
2329 close (outpipefd[PIPE_WRITE_FD]) ||
2330 main_pipe_copier (pipe_buf, pipe_bufsize, pipe_avail,
2331 ifile, inpipefd[PIPE_WRITE_FD]) ||
2332 close (inpipefd[PIPE_WRITE_FD]))
2333 {
2334 XPR(NT "child copier process failed: %s\n",
2335 xd3_mainerror (get_errno ()));
2336 exitval = 1;
2337 }
2338
2339 _exit (exitval);
2340 }
2341
2342 XD3_ASSERT(num_subprocs < MAX_SUBPROCS);
2343 ext_subprocs[num_subprocs++] = copier_id;
2344
2345 /* The parent closes both pipes after duplicating the output of
2346 * compression. */
2347 input_fd = dup (outpipefd[PIPE_READ_FD]);
2348
2349 if (input_fd < 0 ||
2350 main_file_close (ifile) ||
2351 close (outpipefd[PIPE_READ_FD]) ||
2352 close (outpipefd[PIPE_WRITE_FD]) ||
2353 close (inpipefd[PIPE_READ_FD]) ||
2354 close (inpipefd[PIPE_WRITE_FD]))
2355 {
2356 XPR(NT "dup/close failed: %s\n", xd3_mainerror (ret = get_errno ()));
2357 goto pipe_cleanup;
2358 }
2359
2360 #if XD3_STDIO
2361 /* Note: fdopen() acquires the fd, closes it when finished. */
2362 if ((ifile->file = fdopen (input_fd, "r")) == NULL)
2363 {
2364 XPR(NT "fdopen failed: %s\n", xd3_mainerror (ret = get_errno ()));
2365 goto pipe_cleanup;
2366 }
2367
2368 #elif XD3_POSIX
2369 ifile->file = input_fd;
2370 #endif
2371
2372 ifile->compressor = decomp;
2373
2374 /* Now the input file is decompressed. */
2375 return main_file_read (ifile, input_buf, input_bufsize,
2376 nread, "input decompression failed");
2377
2378 pipe_cleanup:
2379 close (input_fd);
2380 close (outpipefd[PIPE_READ_FD]);
2381 close (outpipefd[PIPE_WRITE_FD]);
2382 close (inpipefd[PIPE_READ_FD]);
2383 close (inpipefd[PIPE_WRITE_FD]);
2384 return ret;
2385 }
2386
2387
2388 /* This routine is called when the first buffer of input data is read
2389 * by the main program (unless input decompression is disabled by
2390 * command-line option). If it recognizes the magic number of a known
2391 * input type it invokes decompression.
2392 *
2393 * Skips decompression if the decompression type or the file type is
2394 * RD_NONEXTERNAL.
2395 *
2396 * Behaves exactly like main_file_read, otherwise.
2397 *
2398 * This function uses a separate buffer to read the first small block
2399 * of input. If a compressed input is detected, the separate buffer
2400 * is passed to the pipe copier. This avoids using the same size
2401 * buffer in both cases. */
2402 static int
2403 main_secondary_decompress_check (main_file *file,
2404 uint8_t *input_buf,
2405 size_t input_size,
2406 size_t *nread)
2407 {
2408 int ret;
2409 usize_t i;
2410 usize_t try_read = xd3_min (input_size, XD3_ALLOCSIZE);
2411 size_t check_nread = 0;
2412 uint8_t check_buf[XD3_ALLOCSIZE]; /* TODO: heap allocate */
2413 const main_extcomp *decompressor = NULL;
2414
2415 if ((ret = main_file_read (file, check_buf,
2416 try_read,
2417 & check_nread, "input read failed")))
2418 {
2419 return ret;
2420 }
2421
2422 if (file->flags & RD_DECOMPSET)
2423 {
2424 /* This allows the application header to override the magic
2425 * number, for whatever reason. */
2426 decompressor = file->compressor;
2427 }
2428 else
2429 {
2430 for (i = 0; i < SIZEOF_ARRAY (extcomp_types); i += 1)
2431 {
2432 const main_extcomp *decomp = & extcomp_types[i];
2433
2434 if (check_nread > decomp->magic_size)
2435 {
2436 /* The following expr checks if we are trying to read a
2437 * VCDIFF input, in which case do not treat it as
2438 * "secondary" decompression. */
2439 int skip_this_type = (decomp->flags & RD_NONEXTERNAL) &&
2440 (file->flags & RD_NONEXTERNAL);
2441
2442 if (skip_this_type)
2443 {
2444 continue;
2445 }
2446
2447 if (memcmp (check_buf, decomp->magic, decomp->magic_size) == 0)
2448 {
2449 decompressor = decomp;
2450 break;
2451 }
2452 }
2453 }
2454 }
2455
2456 if (decompressor != NULL)
2457 {
2458 if (! option_quiet)
2459 {
2460 XPR(NT "externally compressed input: %s %s%s < %s\n",
2461 decompressor->decomp_cmdname,
2462 decompressor->decomp_options,
2463 (option_force2 ? " -f" : ""),
2464 file->filename);
2465 if (file->flags & RD_MAININPUT)
2466 {
2467 XPR(NT
2468 "WARNING: the encoder is automatically decompressing the input file;\n");
2469 XPR(NT
2470 "WARNING: the decoder will automatically recompress the output file;\n");
2471 XPR(NT
2472 "WARNING: this may result in different compressed data and checksums\n");
2473 XPR(NT
2474 "WARNING: despite being identical data; if this is an issue, use -D\n");
2475 XPR(NT
2476 "WARNING: to avoid decompression and/or use -R to avoid recompression\n");
2477 XPR(NT
2478 "WARNING: and/or manually decompress the input file; if you know the\n");
2479 XPR(NT
2480 "WARNING: compression settings that will produce identical output\n");
2481 XPR(NT
2482 "WARNING: you may set those flags using the environment (e.g., GZIP=-9)\n");
2483 }
2484 }
2485
2486 file->size_known = 0;
2487 return main_input_decompress_setup (decompressor, file,
2488 input_buf, input_size,
2489 check_buf, XD3_ALLOCSIZE,
2490 check_nread, nread);
2491 }
2492
2493 /* Now read the rest of the input block. */
2494 (*nread) = 0;
2495
2496 if (check_nread == try_read)
2497 {
2498 ret = main_file_read (file,
2499 input_buf + try_read,
2500 input_size - try_read,
2501 nread,
2502 "input read failed");
2503 }
2504
2505 memcpy (input_buf, check_buf, check_nread);
2506
2507 (*nread) += check_nread;
2508
2509 return 0;
2510 }
2511
2512 /* Initiate re-compression of the output stream. This is easier than
2513 * input decompression because we know beforehand that the stream will
2514 * be compressed, whereas the input has already been read when we
2515 * decide it should be decompressed. Thus, it only requires one
2516 * subprocess and one pipe. */
2517 static int
2518 main_recompress_output (main_file *ofile)
2519 {
2520 pid_t recomp_id; /* One subproc. */
2521 int pipefd[2]; /* One pipe. */
2522 int output_fd = -1;
2523 int ret;
2524 const main_extcomp *recomp = ofile->compressor;
2525
2526 pipefd[0] = pipefd[1] = -1;
2527
2528 if (pipe (pipefd))
2529 {
2530 XPR(NT "pipe failed: %s\n", xd3_mainerror (ret = get_errno ()));
2531 goto pipe_cleanup;
2532 }
2533
2534 if ((recomp_id = fork ()) < 0)
2535 {
2536 XPR(NT "fork failed: %s\n", xd3_mainerror (ret = get_errno ()));
2537 goto pipe_cleanup;
2538 }
2539
2540 /* The child runs the recompression process: */
2541 if (recomp_id == 0)
2542 {
2543 if (option_verbose > 2)
2544 {
2545 XPR(NT "external recompression pid %d\n", getpid ());
2546 }
2547
2548 /* Setup pipes: write to the output file, read from the pipe. */
2549 if (dup2 (XFNO (ofile), STDOUT_FILENO) < 0 ||
2550 dup2 (pipefd[PIPE_READ_FD], STDIN_FILENO) < 0 ||
2551 close (pipefd[PIPE_READ_FD]) ||
2552 close (pipefd[PIPE_WRITE_FD]) ||
2553 execlp (recomp->recomp_cmdname, recomp->recomp_cmdname,
2554 recomp->recomp_options,
2555 option_force2 ? "-f" : NULL,
2556 NULL))
2557 {
2558 XPR(NT "child process %s failed to execute: %s\n",
2559 recomp->recomp_cmdname, xd3_mainerror (get_errno ()));
2560 }
2561
2562 _exit (127);
2563 }
2564
2565 XD3_ASSERT(num_subprocs < MAX_SUBPROCS);
2566 ext_subprocs[num_subprocs++] = recomp_id;
2567
2568 /* The parent closes both pipes after duplicating the output-fd for
2569 * writing to the compression pipe. */
2570 output_fd = dup (pipefd[PIPE_WRITE_FD]);
2571
2572 if (output_fd < 0 ||
2573 main_file_close (ofile) ||
2574 close (pipefd[PIPE_READ_FD]) ||
2575 close (pipefd[PIPE_WRITE_FD]))
2576 {
2577 XPR(NT "close failed: %s\n", xd3_mainerror (ret = get_errno ()));
2578 goto pipe_cleanup;
2579 }
2580
2581 #if XD3_STDIO
2582 /* Note: fdopen() acquires the fd, closes it when finished. */
2583 if ((ofile->file = fdopen (output_fd, "w")) == NULL)
2584 {
2585 XPR(NT "fdopen failed: %s\n", xd3_mainerror (ret = get_errno ()));
2586 goto pipe_cleanup;
2587 }
2588
2589 #elif XD3_POSIX
2590 ofile->file = output_fd;
2591 #endif
2592
2593 /* Now the output file will be compressed. */
2594 return 0;
2595
2596 pipe_cleanup:
2597 close (output_fd);
2598 close (pipefd[PIPE_READ_FD]);
2599 close (pipefd[PIPE_WRITE_FD]);
2600 return ret;
2601 }
2602 #endif /* EXTERNAL_COMPRESSION */
2603
2604 /* Identify the compressor that was used based on its ident string,
2605 * which is passed in the application header. */
2606 static const main_extcomp*
2607 main_ident_compressor (const char *ident)
2608 {
2609 usize_t i;
2610
2611 for (i = 0; i < SIZEOF_ARRAY (extcomp_types); i += 1)
2612 {
2613 if (strcmp (extcomp_types[i].ident, ident) == 0)
2614 {
2615 return & extcomp_types[i];
2616 }
2617 }
2618
2619 return NULL;
2620 }
2621
2622 /* Return the main_extcomp record to use for this identifier, if possible. */
2623 static const main_extcomp*
2624 main_get_compressor (const char *ident)
2625 {
2626 const main_extcomp *ext = main_ident_compressor (ident);
2627
2628 if (ext == NULL)
2629 {
2630 if (! option_quiet)
2631 {
2632 XPR(NT "warning: cannot recompress output: "
2633 "unrecognized external compression ID: %s\n", ident);
2634 }
2635 return NULL;
2636 }
2637 else if (! EXTERNAL_COMPRESSION)
2638 {
2639 if (! option_quiet)
2640 {
2641 XPR(NT "warning: external support not compiled: "
2642 "original input was compressed: %s\n", ext->recomp_cmdname);
2643 }
2644 return NULL;
2645 }
2646 else
2647 {
2648 return ext;
2649 }
2650 }
2651
2652 /*********************************************************************
2653 APPLICATION HEADER
2654 *******************************************************************/
2655
2656 #if XD3_ENCODER
2657 static const char*
2658 main_apphead_string (const char* x)
2659 {
2660 const char *y;
2661
2662 if (x == NULL) { return ""; }
2663
2664 if (strcmp (x, "/dev/stdin") == 0 ||
2665 strcmp (x, "/dev/stdout") == 0 ||
2666 strcmp (x, "/dev/stderr") == 0) { return "-"; }
2667
2668 // TODO: this is not portable
2669 return (y = strrchr (x, '/')) == NULL ? x : y + 1;
2670 }
2671
2672 static int
2673 main_set_appheader (xd3_stream *stream, main_file *input, main_file *sfile)
2674 {
2675 /* The user may disable the application header. Once the appheader
2676 * is set, this disables setting it again. */
2677 if (appheader_used || ! option_use_appheader) { return 0; }
2678
2679 /* The user may specify the application header, otherwise format the
2680 default header. */
2681 if (option_appheader)
2682 {
2683 appheader_used = option_appheader;
2684 }
2685 else
2686 {
2687 const char *iname;
2688 const char *icomp;
2689 const char *sname;
2690 const char *scomp;
2691 usize_t len;
2692
2693 iname = main_apphead_string (input->filename);
2694 icomp = (input->compressor == NULL) ? "" : input->compressor->ident;
2695 len = (usize_t) strlen (iname) + (usize_t) strlen (icomp) + 2;
2696
2697 if (sfile->filename != NULL)
2698 {
2699 sname = main_apphead_string (sfile->filename);
2700 scomp = (sfile->compressor == NULL) ? "" : sfile->compressor->ident;
2701 len += (usize_t) strlen (sname) + (usize_t) strlen (scomp) + 2;
2702 }
2703 else
2704 {
2705 sname = scomp = "";
2706 }
2707
2708 if ((appheader_used = (uint8_t*) main_malloc (len)) == NULL)
2709 {
2710 return ENOMEM;
2711 }
2712
2713 if (sfile->filename == NULL)
2714 {
2715 snprintf_func ((char*)appheader_used, len, "%s/%s", iname, icomp);
2716 }
2717 else
2718 {
2719 snprintf_func ((char*)appheader_used, len, "%s/%s/%s/%s",
2720 iname, icomp, sname, scomp);
2721 }
2722 }
2723
2724 xd3_set_appheader (stream, appheader_used,
2725 (usize_t) strlen ((char*)appheader_used));
2726
2727 return 0;
2728 }
2729 #endif
2730
2731 static void
2732 main_get_appheader_params (main_file *file, char **parsed,
2733 int output, const char *type,
2734 main_file *other)
2735 {
2736 /* Set the filename if it was not specified. If output, option_stdout (-c)
2737 * overrides. */
2738 if (file->filename == NULL &&
2739 ! (output && option_stdout) &&
2740 strcmp (parsed[0], "-") != 0)
2741 {
2742 file->filename = parsed[0];
2743
2744 if (other->filename != NULL) {
2745 /* Take directory from the other file, if it has one. */
2746 /* TODO: This results in nonsense names like /dev/foo.tar.gz
2747 * and probably the filename-default logic interferes with
2748 * multi-file operation and the standard file extension?
2749 * Possibly the name header is bad, should be off by default.
2750 * Possibly we just want to remember external/compression
2751 * settings. */
2752 const char *last_slash = strrchr(other->filename, '/');
2753
2754 if (last_slash != NULL) {
2755 usize_t dlen = (usize_t) (last_slash - other->filename);
2756
2757 XD3_ASSERT(file->filename_copy == NULL);
2758 file->filename_copy =
2759 (char*) main_malloc(dlen + 2 + (usize_t) strlen(file->filename));
2760
2761 strncpy(file->filename_copy, other->filename, dlen);
2762 file->filename_copy[dlen] = '/';
2763 strcpy(file->filename_copy + dlen + 1, parsed[0]);
2764
2765 file->filename = file->filename_copy;
2766 }
2767 }
2768
2769 if (! option_quiet)
2770 {
2771 XPR(NT "using default %s filename: %s\n", type, file->filename);
2772 }
2773 }
2774
2775 /* Set the compressor, initiate de/recompression later. */
2776 if (file->compressor == NULL && *parsed[1] != 0)
2777 {
2778 file->flags |= RD_DECOMPSET;
2779 file->compressor = main_get_compressor (parsed[1]);
2780 }
2781 }
2782
2783 static void
2784 main_get_appheader (xd3_stream *stream, main_file *ifile,
2785 main_file *output, main_file *sfile)
2786 {
2787 uint8_t *apphead;
2788 usize_t appheadsz;
2789 int ret;
2790
2791 /* The user may disable the application header. Once the appheader
2792 * is set, this disables setting it again. */
2793 if (! option_use_appheader) { return; }
2794
2795 ret = xd3_get_appheader (stream, & apphead, & appheadsz);
2796
2797 /* Ignore failure, it only means we haven't received a header yet. */
2798 if (ret != 0) { return; }
2799
2800 if (appheadsz > 0)
2801 {
2802 char *start = (char*)apphead;
2803 char *slash;
2804 int place = 0;
2805 const int kMaxArgs = 4;
2806 char *parsed[4];
2807
2808 memset (parsed, 0, sizeof (parsed));
2809
2810 while ((slash = strchr (start, '/')) != NULL && place < (kMaxArgs-1))
2811 {
2812 *slash = 0;
2813 parsed[place++] = start;
2814 start = slash + 1;
2815 }
2816
2817 parsed[place++] = start;
2818
2819 /* First take the output parameters. */
2820 if (place == 2 || place == 4)
2821 {
2822 main_get_appheader_params (output, parsed, 1, "output", ifile);
2823 }
2824
2825 /* Then take the source parameters. */
2826 if (place == 4)
2827 {
2828 main_get_appheader_params (sfile, parsed+2, 0, "source", ifile);
2829 }
2830 }
2831
2832 option_use_appheader = 0;
2833 return;
2834 }
2835
2836 /*********************************************************************
2837 Main I/O routines
2838 **********************************************************************/
2839
2840 /* This function acts like the above except it may also try to
2841 * recognize a compressed input (source or target) when the first
2842 * buffer of data is read. The EXTERNAL_COMPRESSION code is called to
2843 * search for magic numbers. */
2844 static int
2845 main_read_primary_input (main_file *file,
2846 uint8_t *buf,
2847 size_t size,
2848 size_t *nread)
2849 {
2850 #if EXTERNAL_COMPRESSION
2851 if (option_decompress_inputs && file->flags & RD_FIRST)
2852 {
2853 file->flags &= ~RD_FIRST;
2854 return main_secondary_decompress_check (file, buf, size, nread);
2855 }
2856 #endif
2857
2858 return main_file_read (file, buf, size, nread, "input read failed");
2859 }
2860
2861 /* Open the main output file, sets a default file name, initiate
2862 * recompression. This function is expected to fprint any error
2863 * messages. */
2864 static int
2865 main_open_output (xd3_stream *stream, main_file *ofile)
2866 {
2867 int ret;
2868
2869 if (option_no_output)
2870 {
2871 return 0;
2872 }
2873
2874 if (ofile->filename == NULL)
2875 {
2876 XSTDOUT_XF (ofile);
2877
2878 if (option_verbose > 1)
2879 {
2880 XPR(NT "using standard output: %s\n", ofile->filename);
2881 }
2882 }
2883 else
2884 {
2885 /* Stat the file to check for overwrite. */
2886 if (option_force == 0 && main_file_exists (ofile))
2887 {
2888 if (!option_quiet)
2889 {
2890 XPR(NT "to overwrite output file specify -f: %s\n",
2891 ofile->filename);
2892 }
2893 return EEXIST;
2894 }
2895
2896 if ((ret = main_file_open (ofile, ofile->filename, XO_WRITE)))
2897 {
2898 return ret;
2899 }
2900
2901 if (option_verbose > 1) { XPR(NT "output %s\n", ofile->filename); }
2902 }
2903
2904 #if EXTERNAL_COMPRESSION
2905 /* Do output recompression. */
2906 if (ofile->compressor != NULL && option_recompress_outputs == 1)
2907 {
2908 if (! option_quiet)
2909 {
2910 XPR(NT "externally compressed output: %s %s%s > %s\n",
2911 ofile->compressor->recomp_cmdname,
2912 ofile->compressor->recomp_options,
2913 (option_force2 ? " -f" : ""),
2914 ofile->filename);
2915 }
2916
2917 if ((ret = main_recompress_output (ofile)))
2918 {
2919 return ret;
2920 }
2921 }
2922 #endif
2923
2924 return 0;
2925 }
2926
2927 static usize_t
2928 main_get_winsize (main_file *ifile) {
2929 xoff_t file_size = 0;
2930 usize_t size = option_winsize;
2931 static shortbuf iszbuf;
2932
2933 if (main_file_stat (ifile, &file_size) == 0)
2934 {
2935 size = (usize_t) xd3_min (file_size, (xoff_t) size);
2936 }
2937
2938 size = xd3_max (size, XD3_ALLOCSIZE);
2939
2940 if (option_verbose > 1)
2941 {
2942 XPR(NT "input %s window size %s\n",
2943 ifile->filename,
2944 main_format_bcnt (size, &iszbuf));
2945 }
2946
2947 return size;
2948 }
2949
2950 /*********************************************************************
2951 Main routines
2952 ********************************************************************/
2953
2954 /* This is a generic input function. It calls the xd3_encode_input or
2955 * xd3_decode_input functions and makes calls to the various input
2956 * handling routines above, which coordinate external decompression.
2957 */
2958 static int
2959 main_input (xd3_cmd cmd,
2960 main_file *ifile,
2961 main_file *ofile,
2962 main_file *sfile)
2963 {
2964 int ret;
2965 xd3_stream stream;
2966 size_t nread = 0;
2967 usize_t winsize;
2968 int stream_flags = 0;
2969 xd3_config config;
2970 xd3_source source;
2971 xoff_t last_total_in = 0;
2972 xoff_t last_total_out = 0;
2973 long start_time;
2974 int stdout_only = 0;
2975 int (*input_func) (xd3_stream*);
2976 int (*output_func) (xd3_stream*, main_file *);
2977
2978 memset (& stream, 0, sizeof (stream));
2979 memset (& source, 0, sizeof (source));
2980 memset (& config, 0, sizeof (config));
2981
2982 config.alloc = main_alloc;
2983 config.freef = main_free1;
2984
2985 config.iopt_size = option_iopt_size;
2986 config.sprevsz = option_sprevsz;
2987
2988 do_src_fifo = 0;
2989
2990 start_time = get_millisecs_now ();
2991
2992 if (option_use_checksum) { stream_flags |= XD3_ADLER32; }
2993
2994 /* main_input setup. */
2995 switch ((int) cmd)
2996 {
2997 #if VCDIFF_TOOLS
2998 if (1) { case CMD_PRINTHDR: stream_flags |= XD3_JUST_HDR; }
2999 else if (1) { case CMD_PRINTHDRS: stream_flags |= XD3_SKIP_WINDOW; }
3000 else { case CMD_PRINTDELTA: stream_flags |= XD3_SKIP_EMIT; }
3001 ifile->flags |= RD_NONEXTERNAL;
3002 input_func = xd3_decode_input;
3003 output_func = main_print_func;
3004 stream_flags |= XD3_ADLER32_NOVER;
3005 stdout_only = 1;
3006 break;
3007
3008 case CMD_RECODE:
3009 case CMD_MERGE:
3010 case CMD_MERGE_ARG:
3011 /* No source will be read */
3012 stream_flags |= XD3_ADLER32_NOVER | XD3_SKIP_EMIT;
3013 ifile->flags |= RD_NONEXTERNAL;
3014 input_func = xd3_decode_input;
3015
3016 if ((ret = main_init_recode_stream ()))
3017 {
3018 return EXIT_FAILURE;
3019 }
3020
3021 if (cmd == CMD_RECODE) { output_func = main_recode_func; }
3022 else { output_func = main_merge_func; }
3023 break;
3024 #endif /* VCDIFF_TOOLS */
3025
3026 #if XD3_ENCODER
3027 case CMD_ENCODE:
3028 do_src_fifo = 1;
3029 input_func = xd3_encode_input;
3030 output_func = main_write_output;
3031
3032 if (option_no_compress) { stream_flags |= XD3_NOCOMPRESS; }
3033 if (option_smatch_config)
3034 {
3035 const char *s = option_smatch_config;
3036 char *e;
3037 int values[XD3_SOFTCFG_VARCNT];
3038 int got;
3039
3040 config.smatch_cfg = XD3_SMATCH_SOFT;
3041
3042 for (got = 0; got < XD3_SOFTCFG_VARCNT; got += 1, s = e + 1)
3043 {
3044 values[got] = strtol (s, &e, 10);
3045
3046 if ((values[got] < 0) ||
3047 (e == s) ||
3048 (got < XD3_SOFTCFG_VARCNT-1 && *e == 0) ||
3049 (got == XD3_SOFTCFG_VARCNT-1 && *e != 0))
3050 {
3051 XPR(NT "invalid string match specifier (-C) %d: %s\n",
3052 got, s);
3053 return EXIT_FAILURE;
3054 }
3055 }
3056
3057 config.smatcher_soft.large_look = values[0];
3058 config.smatcher_soft.large_step = values[1];
3059 config.smatcher_soft.small_look = values[2];
3060 config.smatcher_soft.small_chain = values[3];
3061 config.smatcher_soft.small_lchain = values[4];
3062 config.smatcher_soft.max_lazy = values[5];
3063 config.smatcher_soft.long_enough = values[6];
3064 }
3065 else
3066 {
3067 if (option_verbose > 2)
3068 {
3069 XPR(NT "compression level: %d\n", option_level);
3070 }
3071 if (option_level == 0)
3072 {
3073 stream_flags |= XD3_NOCOMPRESS;
3074 config.smatch_cfg = XD3_SMATCH_FASTEST;
3075 }
3076 else if (option_level == 1)
3077 { config.smatch_cfg = XD3_SMATCH_FASTEST; }
3078 else if (option_level == 2)
3079 { config.smatch_cfg = XD3_SMATCH_FASTER; }
3080 else if (option_level <= 5)
3081 { config.smatch_cfg = XD3_SMATCH_FAST; }
3082 else if (option_level == 6)
3083 { config.smatch_cfg = XD3_SMATCH_DEFAULT; }
3084 else
3085 { config.smatch_cfg = XD3_SMATCH_SLOW; }
3086 }
3087 break;
3088 #endif
3089 case CMD_DECODE:
3090 if (option_use_checksum == 0) { stream_flags |= XD3_ADLER32_NOVER; }
3091 ifile->flags |= RD_NONEXTERNAL;
3092 input_func = xd3_decode_input;
3093 output_func = main_write_output;
3094 break;
3095 default:
3096 XPR(NT "internal error\n");
3097 return EXIT_FAILURE;
3098 }
3099
3100 main_bsize = winsize = main_get_winsize (ifile);
3101
3102 if ((main_bdata = (uint8_t*) main_bufalloc (winsize)) == NULL)
3103 {
3104 return EXIT_FAILURE;
3105 }
3106
3107 config.winsize = winsize;
3108 config.getblk = main_getblk_func;
3109 config.flags = stream_flags;
3110
3111 if ((ret = main_set_secondary_flags (&config)) ||
3112 (ret = xd3_config_stream (& stream, & config)))
3113 {
3114 XPR(NT XD3_LIB_ERRMSG (& stream, ret));
3115 return EXIT_FAILURE;
3116 }
3117
3118 #if VCDIFF_TOOLS
3119 if ((cmd == CMD_MERGE || cmd == CMD_MERGE_ARG) &&
3120 (ret = xd3_whole_state_init (& stream)))
3121 {
3122 XPR(NT XD3_LIB_ERRMSG (& stream, ret));
3123 return EXIT_FAILURE;
3124 }
3125 #endif
3126
3127 if (cmd != CMD_DECODE)
3128 {
3129 /* When not decoding, set source now. The decoder delays this
3130 * step until XD3_GOTHEADER. */
3131 if (sfile && sfile->filename != NULL)
3132 {
3133 if ((ret = main_set_source (& stream, cmd, sfile, & source)))
3134 {
3135 return EXIT_FAILURE;
3136 }
3137
3138 XD3_ASSERT(stream.src != NULL);
3139 }
3140 }
3141
3142 if (cmd == CMD_PRINTHDR ||
3143 cmd == CMD_PRINTHDRS ||
3144 cmd == CMD_PRINTDELTA ||
3145 cmd == CMD_RECODE)
3146 {
3147 if (sfile->filename == NULL)
3148 {
3149 allow_fake_source = 1;
3150 sfile->filename = "<placeholder>";
3151 main_set_source (& stream, cmd, sfile, & source);
3152 }
3153 }
3154
3155 /* This times each window. */
3156 get_millisecs_since ();
3157
3158 /* Main input loop. */
3159 do
3160 {
3161 xoff_t input_offset;
3162 xoff_t input_remain;
3163 usize_t try_read;
3164
3165 input_offset = ifile->nread;
3166
3167 input_remain = XOFF_T_MAX - input_offset;
3168
3169 try_read = (usize_t) xd3_min ((xoff_t) config.winsize, input_remain);
3170
3171 if ((ret = main_read_primary_input (ifile, main_bdata,
3172 try_read, & nread)))
3173 {
3174 return EXIT_FAILURE;
3175 }
3176
3177 /* If we've reached EOF tell the stream to flush. */
3178 if (nread < try_read)
3179 {
3180 stream.flags |= XD3_FLUSH;
3181 }
3182
3183 #if XD3_ENCODER
3184 /* After the first main_read_primary_input completes, we know
3185 * all the information needed to encode the application
3186 * header. */
3187 if (cmd == CMD_ENCODE &&
3188 (ret = main_set_appheader (& stream, ifile, sfile)))
3189 {
3190 return EXIT_FAILURE;
3191 }
3192 #endif
3193 xd3_avail_input (& stream, main_bdata, nread);
3194
3195 /* If we read zero bytes after encoding at least one window... */
3196 if (nread == 0 && stream.current_window > 0) {
3197 break;
3198 }
3199
3200 again:
3201 ret = input_func (& stream);
3202
3203 switch (ret)
3204 {
3205 case XD3_INPUT:
3206 continue;
3207
3208 case XD3_GOTHEADER:
3209 {
3210 XD3_ASSERT (stream.current_window == 0);
3211
3212 /* Need to process the appheader as soon as possible. It may
3213 * contain a suggested default filename/decompression routine for
3214 * the ofile, and it may contain default/decompression routine for
3215 * the sources. */
3216 if (cmd == CMD_DECODE)
3217 {
3218 /* May need to set the sfile->filename if none was given. */
3219 main_get_appheader (& stream, ifile, ofile, sfile);
3220
3221 /* Now open the source file. */
3222 if ((sfile->filename != NULL) &&
3223 (ret = main_set_source (& stream, cmd, sfile, & source)))
3224 {
3225 return EXIT_FAILURE;
3226 }
3227 }
3228 }
3229 /* FALLTHROUGH */
3230 case XD3_WINSTART:
3231 {
3232 /* e.g., set or unset XD3_SKIP_WINDOW. */
3233 goto again;
3234 }
3235
3236 case XD3_OUTPUT:
3237 {
3238 /* Defer opening the output file until the stream produces its
3239 * first output for both encoder and decoder, this way we
3240 * delay long enough for the decoder to receive the
3241 * application header. (Or longer if there are skipped
3242 * windows, but I can't think of any reason not to delay
3243 * open.) */
3244 if (ofile != NULL &&
3245 ! main_file_isopen (ofile) &&
3246 (ret = main_open_output (& stream, ofile)) != 0)
3247 {
3248 return EXIT_FAILURE;
3249 }
3250
3251 if ((ret = output_func (& stream, ofile)) &&
3252 (ret != PRINTHDR_SPECIAL))
3253 {
3254 return EXIT_FAILURE;
3255 }
3256
3257 if (ret == PRINTHDR_SPECIAL)
3258 {
3259 xd3_abort_stream (& stream);
3260 ret = EXIT_SUCCESS;
3261 goto done;
3262 }
3263
3264 ret = 0;
3265
3266 xd3_consume_output (& stream);
3267 goto again;
3268 }
3269
3270 case XD3_WINFINISH:
3271 {
3272 if (IS_ENCODE (cmd) || cmd == CMD_DECODE || cmd == CMD_RECODE)
3273 {
3274 if (! option_quiet && IS_ENCODE (cmd) &&
3275 main_file_isopen (sfile))
3276 {
3277 /* Warn when no source copies are found */
3278 if (option_verbose && ! xd3_encoder_used_source (& stream))
3279 {
3280 XPR(NT "warning: input window %"Q"u..%"Q"u has "
3281 "no source copies\n",
3282 stream.current_window * winsize,
3283 (stream.current_window+1) * winsize);
3284 XD3_ASSERT (stream.src != NULL);
3285 }
3286
3287 /* Limited i-buffer size affects source copies
3288 * when the sourcewin is decided early. */
3289 if (option_verbose > 1 &&
3290 stream.srcwin_decided_early &&
3291 stream.i_slots_used > stream.iopt_size)
3292 {
3293 XPR(NT "warning: input position %"Q"u overflowed "
3294 "instruction buffer, needed %u (vs. %u), "
3295 "consider changing -I\n",
3296 stream.current_window * winsize,
3297 stream.i_slots_used, stream.iopt_size);
3298 }
3299 }
3300
3301 if (option_verbose)
3302 {
3303 shortbuf rrateavg, wrateavg, tm;
3304 shortbuf rdb, wdb;
3305 shortbuf trdb, twdb;
3306 shortbuf srcpos;
3307 long millis = get_millisecs_since ();
3308 usize_t this_read = (usize_t)(stream.total_in -
3309 last_total_in);
3310 usize_t this_write = (usize_t)(stream.total_out -
3311 last_total_out);
3312 last_total_in = stream.total_in;
3313 last_total_out = stream.total_out;
3314
3315 if (option_verbose > 1)
3316 {
3317 XPR(NT "%"Q"u: in %s (%s): out %s (%s): "
3318 "total in %s: out %s: %s: srcpos %s\n",
3319 stream.current_window,
3320 main_format_bcnt (this_read, &rdb),
3321 main_format_rate (this_read, millis, &rrateavg),
3322 main_format_bcnt (this_write, &wdb),
3323 main_format_rate (this_write, millis, &wrateavg),
3324 main_format_bcnt (stream.total_in, &trdb),
3325 main_format_bcnt (stream.total_out, &twdb),
3326 main_format_millis (millis, &tm),
3327 main_format_bcnt (stream.srcwin_cksum_pos, &srcpos));
3328 }
3329 else
3330 {
3331 XPR(NT "%"Q"u: in %s: out %s: total in %s: "
3332 "out %s: %s\n",
3333 stream.current_window,
3334 main_format_bcnt (this_read, &rdb),
3335 main_format_bcnt (this_write, &wdb),
3336 main_format_bcnt (stream.total_in, &trdb),
3337 main_format_bcnt (stream.total_out, &twdb),
3338 main_format_millis (millis, &tm));
3339 }
3340 }
3341 }
3342 goto again;
3343 }
3344
3345 default:
3346 /* input_func() error */
3347 XPR(NT XD3_LIB_ERRMSG (& stream, ret));
3348 if (! option_quiet && ret == XD3_INVALID_INPUT &&
3349 sfile != NULL && sfile->filename != NULL)
3350 {
3351 XPR(NT "normally this indicates that the source file is incorrect\n");
3352 XPR(NT "please verify the source file with sha1sum or equivalent\n");
3353 }
3354 return EXIT_FAILURE;
3355 }
3356 }
3357 while (nread == config.winsize);
3358 done:
3359 /* Close the inputs. (ifile must be open, sfile may be open) */
3360 main_file_close (ifile);
3361 if (sfile != NULL)
3362 {
3363 main_file_close (sfile);
3364 }
3365
3366 #if VCDIFF_TOOLS
3367 if (cmd == CMD_MERGE &&
3368 (ret = main_merge_output (& stream, ofile)))
3369 {
3370 return EXIT_FAILURE;
3371 }
3372
3373 if (cmd == CMD_MERGE_ARG)
3374 {
3375 xd3_swap_whole_state (& stream.whole_target,
3376 & recode_stream->whole_target);
3377 }
3378 #endif /* VCDIFF_TOOLS */
3379
3380 /* If output file is not open yet because of delayed-open, it means
3381 * we never encountered a window in the delta, but it could have had
3382 * a VCDIFF header? TODO: solve this elsewhere. For now, it prints
3383 * "nothing to output" below, but the check doesn't happen in case
3384 * of option_no_output. */
3385 if (! option_no_output && ofile != NULL)
3386 {
3387 if (!stdout_only && ! main_file_isopen (ofile))
3388 {
3389 XPR(NT "nothing to output: %s\n", ifile->filename);
3390 return EXIT_FAILURE;
3391 }
3392
3393 /* Have to close the output before calling
3394 * main_external_compression_finish, or else it hangs. */
3395 if (main_file_close (ofile) != 0)
3396 {
3397 return EXIT_FAILURE;
3398 }
3399 }
3400
3401 #if EXTERNAL_COMPRESSION
3402 if ((ret = main_external_compression_finish ()))
3403 {
3404 XPR(NT "external compression commands failed\n");
3405 return EXIT_FAILURE;
3406 }
3407 #endif
3408
3409 if ((ret = xd3_close_stream (& stream)))
3410 {
3411 XPR(NT XD3_LIB_ERRMSG (& stream, ret));
3412 return EXIT_FAILURE;
3413 }
3414
3415 #if XD3_ENCODER
3416 if (option_verbose > 1 && cmd == CMD_ENCODE)
3417 {
3418 XPR(NT "scanner configuration: %s\n", stream.smatcher.name);
3419 XPR(NT "target hash table size: %u\n", stream.small_hash.size);
3420 if (sfile != NULL && sfile->filename != NULL)
3421 {
3422 XPR(NT "source hash table size: %u\n", stream.large_hash.size);
3423 }
3424 }
3425
3426 if (option_verbose > 2 && cmd == CMD_ENCODE)
3427 {
3428 XPR(NT "source copies: %"Q"u (%"Q"u bytes)\n",
3429 stream.n_scpy, stream.l_scpy);
3430 XPR(NT "target copies: %"Q"u (%"Q"u bytes)\n",
3431 stream.n_tcpy, stream.l_tcpy);
3432 XPR(NT "adds: %"Q"u (%"Q"u bytes)\n", stream.n_add, stream.l_add);
3433 XPR(NT "runs: %"Q"u (%"Q"u bytes)\n", stream.n_run, stream.l_run);
3434 }
3435 #endif
3436
3437 xd3_free_stream (& stream);
3438
3439 if (option_verbose)
3440 {
3441 shortbuf tm;
3442 long end_time = get_millisecs_now ();
3443 xoff_t nwrite = ofile != NULL ? ofile->nwrite : 0;
3444
3445 XPR(NT "finished in %s; input %"Q"u output %"Q"u bytes (%0.2f%%)\n",
3446 main_format_millis (end_time - start_time, &tm),
3447 ifile->nread, nwrite, 100.0 * nwrite / ifile->nread);
3448 }
3449
3450 return EXIT_SUCCESS;
3451 }
3452
3453 /* free memory before exit, reset single-use variables. */
3454 static void
3455 main_cleanup (void)
3456 {
3457 if (appheader_used != NULL &&
3458 appheader_used != option_appheader)
3459 {
3460 main_free (appheader_used);
3461 appheader_used = NULL;
3462 }
3463
3464 main_buffree (main_bdata);
3465 main_bdata = NULL;
3466 main_bsize = 0;
3467
3468 main_lru_cleanup();
3469
3470 if (recode_stream != NULL)
3471 {
3472 xd3_free_stream (recode_stream);
3473 main_free (recode_stream);
3474 recode_stream = NULL;
3475 }
3476
3477 if (merge_stream != NULL)
3478 {
3479 xd3_free_stream (merge_stream);
3480 main_free (merge_stream);
3481 merge_stream = NULL;
3482 }
3483
3484 XD3_ASSERT (main_mallocs == 0);
3485 }
3486
3487 static void
3488 setup_environment (int argc,
3489 char **argv,
3490 int *argc_out,
3491 char ***argv_out,
3492 char ***argv_free,
3493 char **env_free)
3494 {
3495 int n, i, i0;
3496 char *p, *v = getenv("XDELTA");
3497 if (v == NULL) {
3498 (*argc_out) = argc;
3499 (*argv_out) = argv;
3500 (*argv_free) = NULL;
3501 (*env_free) = NULL;
3502 return;
3503 }
3504
3505 (*env_free) = (char*) main_malloc((usize_t) strlen(v) + 1);
3506 strcpy(*env_free, v);
3507
3508 /* Space needed for extra args, at least # of spaces */
3509 n = argc + 1;
3510 for (p = *env_free; *p != 0; ) {
3511 if (*p++ == ' ') {
3512 n++;
3513 }
3514 }
3515
3516 (*argv_free) = (char**) main_malloc(sizeof(char*) * (n + 1));
3517 (*argv_out) = (*argv_free);
3518 (*argv_out)[0] = argv[0];
3519 (*argv_out)[n] = NULL;
3520
3521 i = 1;
3522 for (p = *env_free; *p != 0; ) {
3523 (*argv_out)[i++] = p;
3524 while (*p != ' ' && *p != 0) {
3525 p++;
3526 }
3527 while (*p == ' ') {
3528 *p++ = 0;
3529 }
3530 }
3531
3532 for (i0 = 1; i0 < argc; i0++) {
3533 (*argv_out)[i++] = argv[i0];
3534 }
3535
3536 /* Counting spaces is an upper bound, argv stays NULL terminated. */
3537 (*argc_out) = i;
3538 while (i <= n) {
3539 (*argv_out)[i++] = NULL;
3540 }
3541 }
3542
3543 #if PYTHON_MODULE || SWIG_MODULE || NOT_MAIN
3544 int xd3_main_cmdline (int argc, char **argv)
3545 #else
3546 int main (int argc, char **argv)
3547 #endif
3548 {
3549 static const char *flags =
3550 "0123456789cdefhnqvDFJNORVs:m:B:C:E:I:L:O:M:P:W:A::S::";
3551 xd3_cmd cmd;
3552 main_file ifile;
3553 main_file ofile;
3554 main_file sfile;
3555 main_merge_list merge_order;
3556 main_merge *merge;
3557 int my_optind;
3558 const char *my_optarg;
3559 const char *my_optstr;
3560 const char *sfilename;
3561 int env_argc;
3562 char **env_argv;
3563 char **free_argv; /* malloc() in setup_environment() */
3564 char *free_value; /* malloc() in setup_environment() */
3565 int ret;
3566
3567 #ifdef _WIN32
3568 GetStartupInfo(&winStartupInfo);
3569 setvbuf(stderr, NULL, _IONBF, 0); /* Do not buffer stderr */
3570 #endif
3571
3572 main_file_init (& ifile);
3573 main_file_init (& ofile);
3574 main_file_init (& sfile);
3575 main_merge_list_init (& merge_order);
3576
3577 reset_defaults();
3578
3579 free_argv = NULL;
3580 free_value = NULL;
3581 setup_environment(argc, argv, &env_argc, &env_argv,
3582 &free_argv, &free_value);
3583 cmd = CMD_NONE;
3584 sfilename = NULL;
3585 my_optind = 1;
3586 argv = env_argv;
3587 argc = env_argc;
3588 program_name = env_argv[0];
3589
3590 takearg:
3591 my_optarg = NULL;
3592 my_optstr = argv[my_optind];
3593
3594 /* This doesn't use getopt() because it makes trouble for -P & python which
3595 * reenter main() and thus care about freeing all memory. I never had much
3596 * trust for getopt anyway, it's too opaque. This implements a fairly
3597 * standard non-long-option getopt with support for named operations (e.g.,
3598 * "xdelta3 [encode|decode|printhdr...] < in > out"). */
3599 if (my_optstr)
3600 {
3601 if (*my_optstr == '-') { my_optstr += 1; }
3602 else if (cmd == CMD_NONE) { goto nonflag; }
3603 else { my_optstr = NULL; }
3604 }
3605 while (my_optstr)
3606 {
3607 const char *s;
3608 my_optarg = NULL;
3609 if ((ret = *my_optstr++) == 0) { my_optind += 1; goto takearg; }
3610
3611 /* Option handling: first check for one ':' following the option in
3612 * flags, then check for two. The syntax allows:
3613 *
3614 * 1. -Afoo defines optarg="foo"
3615 * 2. -A foo defines optarg="foo"
3616 * 3. -A "" defines optarg="" (allows empty-string)
3617 * 4. -A [EOA or -moreargs] error (mandatory case)
3618 * 5. -A [EOA -moreargs] defines optarg=NULL (optional case)
3619 * 6. -A=foo defines optarg="foo"
3620 * 7. -A= defines optarg="" (mandatory case)
3621 * 8. -A= defines optarg=NULL (optional case)
3622 *
3623 * See tests in test_command_line_arguments().
3624 */
3625 s = strchr (flags, ret);
3626 if (s && s[1] && s[1] == ':')
3627 {
3628 int option = s[2] && s[2] == ':';
3629
3630 /* Case 1, set optarg to the remaining characters. */
3631 my_optarg = my_optstr;
3632 my_optstr = "";
3633
3634 /* Case 2-5 */
3635 if (*my_optarg == 0)
3636 {
3637 /* Condition 4-5 */
3638 int have_arg = (my_optind < (argc - 1) &&
3639 *argv[my_optind+1] != '-');
3640
3641 if (! have_arg)
3642 {
3643 if (! option)
3644 {
3645 /* Case 4 */
3646 XPR(NT "-%c: requires an argument\n", ret);
3647 ret = EXIT_FAILURE;
3648 goto cleanup;
3649 }
3650 /* Case 5. */
3651 my_optarg = NULL;
3652 }
3653 else
3654 {
3655 /* Case 2-3. */
3656 my_optarg = argv[++my_optind];
3657 }
3658 }
3659 /* Case 6-8. */
3660 else if (*my_optarg == '=')
3661 {
3662 /* Remove the = in all cases. */
3663 my_optarg += 1;
3664
3665 if (option && *my_optarg == 0)
3666 {
3667 /* Case 8. */
3668 my_optarg = NULL;
3669 }
3670 }
3671 }
3672
3673 switch (ret)
3674 {
3675 /* case: if no '-' was found, maybe check for a command name. */
3676 nonflag:
3677 if (strcmp (my_optstr, "decode") == 0) { cmd = CMD_DECODE; }
3678 else if (strcmp (my_optstr, "encode") == 0)
3679 {
3680 #if XD3_ENCODER
3681 cmd = CMD_ENCODE;
3682 #else
3683 XPR(NT "encoder support not compiled\n");
3684 return EXIT_FAILURE;
3685 #endif
3686 }
3687 else if (strcmp (my_optstr, "config") == 0) { cmd = CMD_CONFIG; }
3688 #if REGRESSION_TEST
3689 else if (strcmp (my_optstr, "test") == 0) { cmd = CMD_TEST; }
3690 #endif
3691 #if VCDIFF_TOOLS
3692 else if (strcmp (my_optstr, "printhdr") == 0) { cmd = CMD_PRINTHDR; }
3693 else if (strcmp (my_optstr, "printhdrs") == 0)
3694 { cmd = CMD_PRINTHDRS; }
3695 else if (strcmp (my_optstr, "printdelta") == 0)
3696 { cmd = CMD_PRINTDELTA; }
3697 else if (strcmp (my_optstr, "recode") == 0) { cmd = CMD_RECODE; }
3698 else if (strcmp (my_optstr, "merge") == 0) { cmd = CMD_MERGE; }
3699 #endif
3700
3701 /* If no option was found and still no command, let the default
3702 * command be encode. The remaining args are treated as
3703 * filenames. */
3704 if (cmd == CMD_NONE)
3705 {
3706 cmd = CMD_DEFAULT;
3707 my_optstr = NULL;
3708 break;
3709 }
3710 else
3711 {
3712 /* But if we find a command name, continue the getopt loop. */
3713 my_optind += 1;
3714 goto takearg;
3715 }
3716
3717 /* gzip-like options */
3718 case '0': case '1': case '2': case '3': case '4':
3719 case '5': case '6': case '7': case '8': case '9':
3720 option_level = ret - '0';
3721 break;
3722 case 'f': option_force = 1; break;
3723 case 'F':
3724 #if EXTERNAL_COMPRESSION
3725 option_force2 = 1;
3726 #else
3727 XPR(NT "warning: -F option ignored, "
3728 "external compression support was not compiled\n");
3729 break;
3730 #endif
3731 case 'v': option_verbose += 1; option_quiet = 0; break;
3732 case 'q': option_quiet = 1; option_verbose = 0; break;
3733 case 'c': option_stdout = 1; break;
3734 case 'd':
3735 if (cmd == CMD_NONE) { cmd = CMD_DECODE; }
3736 else { ret = main_help (); goto exit; }
3737 break;
3738 case 'e':
3739 #if XD3_ENCODER
3740 if (cmd == CMD_NONE) { cmd = CMD_ENCODE; }
3741 else { ret = main_help (); goto exit; }
3742 break;
3743 #else
3744 XPR(NT "encoder support not compiled\n");
3745 return EXIT_FAILURE;
3746 #endif
3747
3748 case 'n': option_use_checksum = 0; break;
3749 case 'N': option_no_compress = 1; break;
3750 case 'C': option_smatch_config = my_optarg; break;
3751 case 'J': option_no_output = 1; break;
3752 case 'S': if (my_optarg == NULL)
3753 {
3754 option_use_secondary = 0;
3755 option_secondary = NULL;
3756 }
3757 else
3758 {
3759 option_use_secondary = 1;
3760 option_secondary = my_optarg;
3761 }
3762 break;
3763 case 'A': if (my_optarg == NULL) { option_use_appheader = 0; }
3764 else { option_appheader = (uint8_t*) my_optarg; } break;
3765 case 'B': {
3766 xoff_t bsize;
3767 if ((ret = main_atoux (my_optarg, & bsize,
3768 XD3_MINSRCWINSZ, XD3_MAXSRCWINSZ, 'B')))
3769 {
3770 goto exit;
3771 }
3772 option_srcwinsz = bsize;
3773 break;
3774 }
3775 case 'I':
3776 if ((ret = main_atou (my_optarg, & option_iopt_size, 0,
3777 0, 'I')))
3778 {
3779 goto exit;
3780 }
3781 break;
3782 case 'P':
3783 if ((ret = main_atou (my_optarg, & option_sprevsz, 0,
3784 0, 'P')))
3785 {
3786 goto exit;
3787 }
3788 break;
3789 case 'W':
3790 if ((ret = main_atou (my_optarg, & option_winsize, XD3_ALLOCSIZE,
3791 XD3_HARDMAXWINSIZE, 'W')))
3792 {
3793 goto exit;
3794 }
3795 break;
3796 case 'D':
3797 #if EXTERNAL_COMPRESSION == 0
3798 if (option_verbose > 0)
3799 {
3800 XPR(NT "warning: -D option ignored, "
3801 "external compression support was not compiled\n");
3802 }
3803 #else
3804 option_decompress_inputs = 0;
3805 #endif
3806 break;
3807 case 'R':
3808 #if EXTERNAL_COMPRESSION == 0
3809 if (option_verbose > 0)
3810 {
3811 XPR(NT "warning: -R option ignored, "
3812 "external compression support was not compiled\n");
3813 }
3814 #else
3815 option_recompress_outputs = 0;
3816 #endif
3817 break;
3818 case 's':
3819 if (sfilename != NULL)
3820 {
3821 XPR(NT "specify only one source file\n");
3822 goto cleanup;
3823 }
3824
3825 sfilename = my_optarg;
3826 break;
3827 case 'm':
3828 if ((merge = (main_merge*)
3829 main_malloc (sizeof (main_merge))) == NULL)
3830 {
3831 goto cleanup;
3832 }
3833 main_merge_list_push_back (& merge_order, merge);
3834 merge->filename = my_optarg;
3835 break;
3836 case 'V':
3837 ret = main_version (); goto exit;
3838 default:
3839 ret = main_help (); goto exit;
3840 }
3841 }
3842
3843 option_source_filename = sfilename;
3844
3845 /* In case there were no arguments, set the default command. */
3846 if (cmd == CMD_NONE) { cmd = CMD_DEFAULT; }
3847
3848 argc -= my_optind;
3849 argv += my_optind;
3850
3851 /* There may be up to two more arguments. */
3852 if (argc > 2)
3853 {
3854 XPR(NT "too many filenames: %s ...\n", argv[2]);
3855 goto cleanup;
3856 }
3857
3858 ifile.flags = RD_FIRST | RD_MAININPUT;
3859 sfile.flags = RD_FIRST;
3860 sfile.filename = option_source_filename;
3861
3862 /* The infile takes the next argument, if there is one. But if not, infile
3863 * is set to stdin. */
3864 if (argc > 0)
3865 {
3866 ifile.filename = argv[0];
3867
3868 if ((ret = main_file_open (& ifile, ifile.filename, XO_READ)))
3869 {
3870 goto cleanup;
3871 }
3872 }
3873 else
3874 {
3875 XSTDIN_XF (& ifile);
3876 }
3877
3878 /* The ofile takes the following argument, if there is one. But if not, it
3879 * is left NULL until the application header is processed. It will be set
3880 * in main_open_output. */
3881 if (argc > 1)
3882 {
3883 /* Check for conflicting arguments. */
3884 if (option_stdout && ! option_quiet)
3885 {
3886 XPR(NT "warning: -c option overrides output filename: %s\n",
3887 argv[1]);
3888 }
3889
3890 if (! option_stdout) { ofile.filename = argv[1]; }
3891 }
3892
3893 #if VCDIFF_TOOLS
3894 if (cmd == CMD_MERGE &&
3895 (ret = main_merge_arguments (&merge_order)))
3896 {
3897 goto cleanup;
3898 }
3899 #endif /* VCDIFF_TOOLS */
3900
3901 switch (cmd)
3902 {
3903 case CMD_PRINTHDR:
3904 case CMD_PRINTHDRS:
3905 case CMD_PRINTDELTA:
3906 #if XD3_ENCODER
3907 case CMD_ENCODE:
3908 case CMD_RECODE:
3909 case CMD_MERGE:
3910 #endif
3911 case CMD_DECODE:
3912 ret = main_input (cmd, & ifile, & ofile, & sfile);
3913 break;
3914
3915 #if REGRESSION_TEST
3916 case CMD_TEST:
3917 main_config ();
3918 ret = xd3_selftest ();
3919 break;
3920 #endif
3921
3922 case CMD_CONFIG:
3923 ret = main_config ();
3924 break;
3925
3926 default:
3927 ret = main_help ();
3928 break;
3929 }
3930
3931 if (0)
3932 {
3933 cleanup:
3934 ret = EXIT_FAILURE;
3935 exit:
3936 (void)0;
3937 }
3938
3939 #if EXTERNAL_COMPRESSION
3940 main_external_compression_cleanup ();
3941 #endif
3942
3943 main_file_cleanup (& ifile);
3944 main_file_cleanup (& ofile);
3945 main_file_cleanup (& sfile);
3946
3947 while (! main_merge_list_empty (& merge_order))
3948 {
3949 merge = main_merge_list_pop_front (& merge_order);
3950 main_free (merge);
3951 }
3952
3953 main_free (free_argv);
3954 main_free (free_value);
3955
3956 main_cleanup ();
3957
3958 fflush (stdout);
3959 fflush (stderr);
3960 return ret;
3961 }
3962
3963 static int
3964 main_help (void)
3965 {
3966 main_version();
3967
3968 /* Note: update wiki when command-line features change */
3969 XPR(NTR "usage: xdelta3 [command/options] [input [output]]\n");
3970 XPR(NTR "make patch:\n");
3971 XPR(NTR "\n");
3972 XPR(NTR " xdelta3.exe -e -s old_file new_file delta_file\n");
3973 XPR(NTR "\n");
3974 XPR(NTR "apply patch:\n");
3975 XPR(NTR "\n");
3976 XPR(NTR " xdelta3.exe -d -s old_file delta_file decoded_new_file\n");
3977 XPR(NTR "\n");
3978 XPR(NTR "special command names:\n");
3979 XPR(NTR " config prints xdelta3 configuration\n");
3980 XPR(NTR " decode decompress the input\n");
3981 XPR(NTR " encode compress the input%s\n",
3982 XD3_ENCODER ? "" : " [Not compiled]");
3983 #if REGRESSION_TEST
3984 XPR(NTR " test run the builtin tests\n");
3985 #endif
3986 #if VCDIFF_TOOLS
3987 XPR(NTR "special commands for VCDIFF inputs:\n");
3988 XPR(NTR " printdelta print information about the entire delta\n");
3989 XPR(NTR " printhdr print information about the first window\n");
3990 XPR(NTR " printhdrs print information about all windows\n");
3991 XPR(NTR " recode encode with new application/secondary settings\n");
3992 XPR(NTR " merge merge VCDIFF inputs (see below)\n");
3993 #endif
3994 XPR(NTR "merge patches:\n");
3995 XPR(NTR "\n");
3996 XPR(NTR " xdelta3 merge -m 1.vcdiff -m 2.vcdiff 3.vcdiff merged.vcdiff\n");
3997 XPR(NTR "\n");
3998 XPR(NTR "standard options:\n");
3999 XPR(NTR " -0 .. -9 compression level\n");
4000 XPR(NTR " -c use stdout\n");
4001 XPR(NTR " -d decompress\n");
4002 XPR(NTR " -e compress%s\n",
4003 XD3_ENCODER ? "" : " [Not compiled]");
4004 XPR(NTR " -f force (overwrite, ignore trailing garbage)\n");
4005 #if EXTERNAL_COMPRESSION
4006 XPR(NTR " -F force the external-compression subprocess\n");
4007 #endif
4008 XPR(NTR " -h show help\n");
4009 XPR(NTR " -q be quiet\n");
4010 XPR(NTR " -v be verbose (max 2)\n");
4011 XPR(NTR " -V show version\n");
4012
4013 XPR(NTR "memory options:\n");
4014 XPR(NTR " -B bytes source window size\n");
4015 XPR(NTR " -W bytes input window size\n");
4016 XPR(NTR " -P size compression duplicates window\n");
4017 XPR(NTR " -I size instruction buffer size (0 = unlimited)\n");
4018
4019 XPR(NTR "compression options:\n");
4020 XPR(NTR " -s source source file to copy from (if any)\n");
4021 XPR(NTR " -S [lzma|djw|fgk] enable/disable secondary compression\n");
4022 XPR(NTR " -N disable small string-matching compression\n");
4023 XPR(NTR " -D disable external decompression (encode/decode)\n");
4024 XPR(NTR " -R disable external recompression (decode)\n");
4025 XPR(NTR " -n disable checksum (encode/decode)\n");
4026 XPR(NTR " -C soft config (encode, undocumented)\n");
4027 XPR(NTR " -A [apphead] disable/provide application header (encode)\n");
4028 XPR(NTR " -J disable output (check/compute only)\n");
4029 XPR(NTR " -m arguments for \"merge\"\n");
4030
4031 XPR(NTR "the XDELTA environment variable may contain extra args:\n");
4032 XPR(NTR " XDELTA=\"-s source-x.y.tar.gz\" \\\n");
4033 XPR(NTR " tar --use-compress-program=xdelta3 \\\n");
4034 XPR(NTR " -cf target-x.z.tar.gz.vcdiff target-x.y\n");
4035 return EXIT_FAILURE;
4036 }