"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 }