source: nikanabo/current/xdelta/diy/xdelta3-main.h @ 185

Last change on this file since 185 was 185, checked in by geyser, 14 years ago
File size: 87.3 KB
Line 
1/* xdelta 3 - delta compression tools and library
2 * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007,
3 * 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 command line
21 * application.  It contains these major components:
22 *
23 * 1. VCDIFF tools
24 * 2. external compression support (this is POSIX-specific).
25 * 3. a general read/write loop that handles all of the Xdelta decode/encode/VCDIFF-print
26 *    functions
27 * 4. command-line interpreter
28 * 5. an Xdelta application header which stores default filename, external compression settings
29 * 6. output/error printing
30 * 7. basic file support and OS interface
31 */
32
33/* TODO list:
34 * 1. do exact gzip-like filename, stdout handling.  make a .vcdiff extension, refuse
35 *    to encode to stdout without -cf, etc.
36 * 2. Allow the user to add a comment string to the app header without disturbing the default
37 *    behavior.
38 * 3. "Source file must be seekable" is not actually true for encoding, given current
39 *    behavior.  Allow non-seekable sources?  It would in theory let you use a fifo for
40 *    the source.
41 */
42
43/* On error handling and printing:
44 *
45 * The xdelta library sets stream->msg to indicate what condition caused an internal
46 * failure, but many failures originate here and are printed here.  The return convention
47 * is 0 for success, as throughout Xdelta code, but special attention is required here for
48 * the operating system calls with different error handling.  See the main_file_* routines.
49 * All errors in this file have a message printed at the time of occurance.  Since some of
50 * these calls occur within calls to the library, the error may end up being printed again
51 * with a more general error message.
52 */
53
54/******************************************************************************************/
55
56#ifndef XD3_POSIX
57#define XD3_POSIX 0
58#endif
59#ifndef XD3_STDIO
60#define XD3_STDIO 0
61#endif
62#ifndef XD3_WIN32
63#define XD3_WIN32 0
64#endif
65
66/* Combines xd3_strerror() and strerror() */
67const char* xd3_mainerror(int err_num);
68
69/* XPRINTX (used by main) prefixes an "xdelta3: " to the output. */
70#define XPR fprintf
71#define NT stderr, "xdelta3: "
72
73/* If none are set, default to posix. */
74#if (XD3_POSIX + XD3_STDIO + XD3_WIN32) == 0
75#undef XD3_POSIX
76#define XD3_POSIX 1
77#endif
78
79/* Handle externally-compressed inputs. */
80#ifndef EXTERNAL_COMPRESSION
81#define EXTERNAL_COMPRESSION 1
82#endif
83
84#define PRINTHDR_SPECIAL -4378291
85
86/* The number of soft-config variables.  */
87#define XD3_SOFTCFG_VARCNT 7
88
89/* this is used as in XPR(NT XD3_LIB_ERRMSG (stream, ret)) to print an error message
90 * from the library. */
91#define XD3_LIB_ERRMSG(stream, ret) "%s: %s\n", xd3_errstring (stream), xd3_mainerror (ret)
92
93#include <stdio.h>  /* fprintf */
94
95#if XD3_POSIX
96#include <unistd.h> /* close, read, write... */
97#include <sys/types.h>
98#include <fcntl.h>
99#endif
100
101#ifndef _WIN32
102#include <unistd.h> /* lots */
103#include <sys/time.h> /* gettimeofday() */
104#include <sys/stat.h> /* stat() and fstat() */
105#else
106#define strtoll _strtoi64
107#include <sys/types.h>
108#include <sys/stat.h>
109#ifndef WIFEXITED
110#   define WIFEXITED(stat)  (((*((int *) &(stat))) & 0xff) == 0)
111#endif
112#ifndef WEXITSTATUS
113#   define WEXITSTATUS(stat) (((*((int *) &(stat))) >> 8) & 0xff)
114#endif
115#ifndef S_ISREG
116//#   ifdef S_IFREG
117//#       define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
118//#   else
119#       define S_ISREG(m) 1
120//#   endif
121#endif /* !S_ISREG */
122
123// For standard input/output handles
124static STARTUPINFO winStartupInfo;
125#endif
126
127/******************************************************************************************
128 ENUMS and TYPES
129 ******************************************************************************************/
130
131/* These flags (mainly pertaining to main_read() operations) are set in the
132 * main_file->flags variable.  All are related to with external decompression support.
133 *
134 * RD_FIRST causes the external decompression check when the input is first read.
135 *
136 * RD_NONEXTERNAL disables external decompression for reading a compressed input, in the
137 * case of Xdelta inputs.  Note: Xdelta is supported as an external compression type,
138 * which makes is the reason for this flag.  An example to justify this is: to create a
139 * delta between two files that are VCDIFF-compressed.  Two external Xdelta decoders are
140 * run to supply decompressed source and target inputs to the Xdelta encoder. */
141typedef enum
142{
143  RD_FIRST        = (1 << 0),
144  RD_NONEXTERNAL  = (1 << 1),
145  RD_EXTERNAL_V1  = (1 << 2),
146} xd3_read_flags;
147
148/* main_file->mode values */
149typedef enum
150{
151  XO_READ  = 0,
152  XO_WRITE = 1,
153} main_file_modes;
154
155/* Main commands.  For example, CMD_PRINTHDR is the "xdelta printhdr" command. */
156typedef enum
157{
158  CMD_NONE = 0,
159  CMD_PRINTHDR,
160  CMD_PRINTHDRS,
161  CMD_PRINTDELTA,
162#if XD3_ENCODER
163  CMD_ENCODE,
164#endif
165  CMD_DECODE,
166  CMD_TEST,
167  CMD_CONFIG,
168} xd3_cmd;
169
170#if XD3_ENCODER
171#define CMD_DEFAULT CMD_ENCODE
172#define IS_ENCODE(cmd) (cmd == CMD_ENCODE)
173#else
174#define CMD_DEFAULT CMD_DECODE
175#define IS_ENCODE(cmd) (0)
176#endif
177
178typedef struct _main_file        main_file;
179typedef struct _main_extcomp     main_extcomp;
180typedef struct _main_blklru      main_blklru;
181typedef struct _main_blklru_list main_blklru_list;
182
183/* The main_file object supports abstract system calls like open, close, read, write, seek,
184 * stat.  The program uses these to represent both seekable files and non-seekable files.
185 * Source files must be seekable, but the target input and any output file do not require
186 * seekability.
187 */
188struct _main_file
189{
190#if XD3_STDIO
191  FILE               *file;
192#elif XD3_POSIX
193  int                 file;
194#elif XD3_WIN32
195  HANDLE              file;
196#endif
197
198  int                 mode;          /* XO_READ and XO_WRITE */
199  const char         *filename;      /* File name or /dev/stdin, /dev/stdout, /dev/stderr. */
200  char               *filename_copy; /* File name or /dev/stdin, /dev/stdout, /dev/stderr. */
201  const char         *realname;      /* File name or /dev/stdin, /dev/stdout, /dev/stderr. */
202  const main_extcomp *compressor;    /* External compression struct. */
203  int                 flags;         /* RD_FIRST, RD_NONEXTERNAL, ... */
204  xoff_t              nread;         /* for input position */
205  xoff_t              nwrite;        /* for output position */
206  uint8_t            *snprintf_buf;  /* internal snprintf() use */
207};
208
209/* Various strings and magic values used to detect and call external compression.  See
210 * below for examples. */
211struct _main_extcomp
212{
213  const char    *recomp_cmdname;
214  const char    *recomp_options;
215
216  const char    *decomp_cmdname;
217  const char    *decomp_options;
218
219  const char    *ident;
220  const char    *magic;
221  int            magic_size;
222  int            flags;
223};
224
225/* This file implements a small LRU of source blocks.  For encoding purposes,
226 * we prevent paging in blocks we've already scanned in the source (return
227 * XD3_NOTAVAIL). */
228struct _main_blklru_list
229{
230  main_blklru_list  *next;
231  main_blklru_list  *prev;
232};
233
234struct _main_blklru
235{
236  uint8_t         *blk;
237  xoff_t           blkno;
238  main_blklru_list  link;
239};
240
241#define LRU_SIZE 32U
242#define XD3_MINSRCWINSZ XD3_ALLOCSIZE
243
244/* ... represented as a list (no cache index). */
245XD3_MAKELIST(main_blklru_list,main_blklru,link);
246
247// TODO:
248// struct _main_state
249// {
250
251/* Program options: various command line flags and options. */
252static int         option_stdout             = 0;
253static int         option_force              = 0;
254static int         option_verbose            = 0;
255static int         option_quiet              = 0;
256static int         option_use_appheader      = 1;
257static uint8_t*    option_appheader          = NULL;
258static int         option_use_secondary      = 0;
259static char*       option_secondary          = NULL;
260static int         option_use_checksum       = 1;
261static int         option_use_altcodetable   = 0;
262static char*       option_smatch_config      = NULL;
263static int         option_no_compress        = 0;
264static int         option_no_output          = 0; /* do not open or write output */
265static const char *option_source_filename    = NULL;
266
267static int         option_level              = XD3_DEFAULT_LEVEL;
268static usize_t     option_iopt_size          = XD3_DEFAULT_IOPT_SIZE;
269static usize_t     option_winsize            = XD3_DEFAULT_WINSIZE;
270static usize_t     option_srcwinsz           = XD3_DEFAULT_SRCWINSZ;
271static usize_t     option_sprevsz            = XD3_DEFAULT_SPREVSZ;
272
273/* These variables are supressed to avoid their use w/o support.  main() warns
274 * appropriately. */
275#if EXTERNAL_COMPRESSION
276static int         option_decompress_inputs  = 1;
277static int         option_recompress_outputs = 1;
278#endif
279
280/* This is for comparing "printdelta" output without attention to
281 * copy-instruction modes. */
282#if VCDIFF_TOOLS
283static int         option_print_cpymode = 1;
284#endif
285
286/* Static variables */
287IF_DEBUG(static int main_mallocs = 0;)
288
289static char*          program_name = NULL;
290static uint8_t*       appheader_used = NULL;
291static uint8_t*       main_bdata = NULL;
292
293/* The LRU: obviously this is shared by all callers. */
294static int               lru_size = 0;
295static main_blklru      *lru = NULL;  /* array of lru_size elts */
296static main_blklru_list  lru_list;
297static main_blklru_list  lru_free;
298static int               do_not_lru = 0;  /* set to avoid lru, instead discard oldest */
299
300static int lru_hits   = 0;
301static int lru_misses = 0;
302static int lru_filled = 0;
303
304/* Hacks for VCDIFF tools */
305static int allow_fake_source = 0;
306
307/* This array of compressor types is compiled even if EXTERNAL_COMPRESSION is false just so
308 * the program knows the mapping of IDENT->NAME. */
309static main_extcomp extcomp_types[] =
310{
311  /* The entry for xdelta3 must be 0 because the program_name is set there. */
312  { "xdelta3",  "-cfq",  "xdelta3",    "-dcfq",  "X", "\xd6\xc3\xc4", 3, RD_NONEXTERNAL },
313  { "bzip2",    "-cf",   "bzip2",      "-dcf",   "B", "BZh",          3, 0 },
314  { "gzip",     "-cf",   "gzip",       "-dcf",   "G", "\037\213",     2, 0 },
315  { "compress", "-cf",   "uncompress", "-cf",    "Z", "\037\235",     2, 0 },
316
317  /* TODO: add commandline support for magic-less formats */
318  /*{ "lzma",     "-cf",   "lzma",       "-dcf",   "M", "]\000",        2, 0 },*/
319};
320
321// };
322
323static void main_get_appheader (xd3_stream *stream, main_file *ifile,
324                                main_file *output, main_file *sfile);
325
326static int main_help (void);
327
328static int
329main_version (void)
330{
331  /* $Format: "  DP(RINT \"VERSION=3.$Xdelta3Version$\\n\");" $ */
332  DP(RINT "VERSION=3.0q\n");
333  return EXIT_SUCCESS;
334}
335
336static int
337main_config (void)
338{
339  main_version ();
340
341  DP(RINT "EXTERNAL_COMPRESSION=%d\n", EXTERNAL_COMPRESSION);
342  DP(RINT "GENERIC_ENCODE_TABLES=%d\n", GENERIC_ENCODE_TABLES);
343  DP(RINT "GENERIC_ENCODE_TABLES_COMPUTE=%d\n", GENERIC_ENCODE_TABLES_COMPUTE);
344  DP(RINT "REGRESSION_TEST=%d\n", REGRESSION_TEST);
345  DP(RINT "SECONDARY_DJW=%d\n", SECONDARY_DJW);
346  DP(RINT "SECONDARY_FGK=%d\n", SECONDARY_FGK);
347  DP(RINT "VCDIFF_TOOLS=%d\n", VCDIFF_TOOLS);
348  DP(RINT "XD3_ALLOCSIZE=%d\n", XD3_ALLOCSIZE);
349  DP(RINT "XD3_DEBUG=%d\n", XD3_DEBUG);
350  DP(RINT "XD3_ENCODER=%d\n", XD3_ENCODER);
351  DP(RINT "XD3_POSIX=%d\n", XD3_POSIX);
352  DP(RINT "XD3_STDIO=%d\n", XD3_STDIO);
353  DP(RINT "XD3_WIN32=%d\n", XD3_WIN32);
354  DP(RINT "XD3_USE_LARGEFILE64=%d\n", XD3_USE_LARGEFILE64);
355  DP(RINT "XD3_DEFAULT_LEVEL=%d\n", XD3_DEFAULT_LEVEL);
356  DP(RINT "XD3_DEFAULT_IOPT_SIZE=%d\n", XD3_DEFAULT_IOPT_SIZE);
357  DP(RINT "XD3_DEFAULT_SPREVSZ=%d\n", XD3_DEFAULT_SPREVSZ);
358  DP(RINT "XD3_DEFAULT_SRCWINSZ=%d\n", XD3_DEFAULT_SRCWINSZ);
359  DP(RINT "XD3_DEFAULT_WINSIZE=%d\n", XD3_DEFAULT_WINSIZE);
360  DP(RINT "XD3_HARDMAXWINSIZE=%d\n", XD3_HARDMAXWINSIZE);
361  DP(RINT "XD3_NODECOMPRESSSIZE=%d\n", XD3_NODECOMPRESSSIZE);
362
363  return EXIT_SUCCESS;
364}
365
366static void
367reset_defaults(void)
368{
369  option_stdout = 0;
370  option_force = 0;
371  option_verbose = 0;
372  option_quiet = 0;
373  option_appheader = NULL;
374  option_use_secondary = 0;
375  option_secondary = NULL;
376  option_use_altcodetable = 0;
377  option_smatch_config = NULL;
378  option_no_compress = 0;
379  option_no_output = 0;
380  option_source_filename = NULL;
381  program_name = NULL;
382  appheader_used = NULL;
383  main_bdata = NULL;
384  lru_size = 0;
385  lru = NULL;
386  do_not_lru = 0;
387  lru_hits   = 0;
388  lru_misses = 0;
389  lru_filled = 0;
390  allow_fake_source = 0;
391  option_smatch_config = NULL;
392
393  option_use_appheader = 1;
394  option_use_checksum = 1;
395#if EXTERNAL_COMPRESSION
396  option_decompress_inputs  = 1;
397  option_recompress_outputs = 1;
398#endif
399#if VCDIFF_TOOLS
400  option_print_cpymode = 1;
401#endif
402  option_level = XD3_DEFAULT_LEVEL;
403  option_iopt_size = XD3_DEFAULT_IOPT_SIZE;
404  option_winsize = XD3_DEFAULT_WINSIZE;
405  option_srcwinsz = XD3_DEFAULT_SRCWINSZ;
406  option_sprevsz = XD3_DEFAULT_SPREVSZ;
407}
408
409static void*
410main_malloc1 (usize_t size)
411{
412  void* r = malloc (size);
413  if (r == NULL) { XPR(NT "malloc: %s\n", xd3_mainerror (ENOMEM)); }
414  else if (option_verbose > 3) { XPR(NT "malloc: %u: %p\n", size, r); }
415  return r;
416}
417
418static void*
419main_malloc (usize_t size)
420{
421  void *r = main_malloc1 (size);
422   if (r) { IF_DEBUG (main_mallocs += 1); }
423  return r;
424}
425
426static void*
427main_alloc (void   *opaque,
428            usize_t  items,
429            usize_t  size)
430{
431  return main_malloc1 (items * size);
432}
433
434static void
435main_free1 (void *opaque, void *ptr)
436{
437  if (option_verbose > 3) { XPR(NT "free: %p\n", ptr); }
438  free (ptr);
439}
440
441static void
442main_free (void *ptr)
443{
444  if (ptr)
445    {
446      IF_DEBUG (main_mallocs -= 1);
447      main_free1 (NULL, ptr);
448      IF_DEBUG (XD3_ASSERT(main_mallocs >= 0));
449    }
450}
451
452/* This ensures that (ret = errno) always indicates failure, in case errno was
453 * accidentally not set.  If this prints there's a bug somewhere. */
454static int
455get_errno (void)
456{
457#ifndef _WIN32
458  if (errno == 0)
459    {
460      XPR(NT "you found a bug: expected errno != 0\n");
461      errno = XD3_INTERNAL;
462    }
463  return errno;
464#else
465  DWORD errNum = GetLastError();
466  if (errNum == NO_ERROR) {
467          errNum = XD3_INTERNAL;
468  }
469  return errNum;
470#endif
471}
472
473const char*
474xd3_mainerror(int err_num) {
475#ifndef _WIN32
476        const char* x = xd3_strerror (err_num);
477        if (x != NULL) {
478                return x;
479        }
480        return strerror(err_num);
481#else
482        static char err_buf[256];
483        const char* x = xd3_strerror (err_num);
484        if (x != NULL) {
485                return x;
486        }
487        memset (err_buf, 0, 256);
488        FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
489                NULL, err_num,
490                MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
491                err_buf, 256, NULL);
492        return err_buf;
493#endif
494}
495
496static long
497get_millisecs_now (void)
498{
499#ifndef _WIN32
500  struct timeval tv;
501
502  gettimeofday (& tv, NULL);
503
504  return (tv.tv_sec) * 1000L + (tv.tv_usec) / 1000;
505#else
506  // Found this in an example on www.codeproject.com
507  // It doesn't matter that the offset is Jan 1, 1601
508  // Result is the numbre of 100 nanosecond units
509  // 100ns * 10,000 = 1ms
510  SYSTEMTIME st;
511  FILETIME ft;
512  __int64 *pi = (__int64*)&ft;
513  GetLocalTime(&st);
514  SystemTimeToFileTime(&st, &ft);
515  return (long)((*pi) / 10000);
516#endif
517}
518
519/* Always >= 1 millisec, right? */
520static long
521get_millisecs_since (void)
522{
523  static long last = 0;
524  long now = get_millisecs_now();
525  long diff = now - last;
526  last = now;
527  return diff;
528}
529
530static char*
531main_format_bcnt (xoff_t r, char *buf)
532{
533  static const char* fmts[] = { "B", "KB", "MB", "GB" };
534  int i;
535
536  for (i = 0; i < SIZEOF_ARRAY(fmts); i += 1)
537    {
538      if (r <= (10 * 1024) || i == (-1 + (int)SIZEOF_ARRAY(fmts)))
539            {
540              sprintf (buf, "%"Q"u %s", r, fmts[i]);
541              break;
542            }
543      r /= 1024;
544    }
545  return buf;
546}
547
548static char*
549main_format_rate (xoff_t bytes, long millis, char *buf)
550{
551  xoff_t r = (xoff_t)(1.0 * bytes / (1.0 * millis / 1000.0));
552  static char lbuf[32];
553
554  main_format_bcnt (r, lbuf);
555  sprintf (buf, "%s/sec", lbuf);
556  return buf;
557}
558
559static char*
560main_format_millis (long millis, char *buf)
561{
562  if (millis < 1000)       { sprintf (buf, "%lu ms", millis); }
563  else if (millis < 10000) { sprintf (buf, "%.1f sec", millis / 1000.0); }
564  else                     { sprintf (buf, "%lu sec", millis / 1000L); }
565  return buf;
566}
567
568/* A safe version of strtol for xoff_t. */
569static int
570main_strtoxoff (const char* s, xoff_t *xo, char which)
571{
572  char *e;
573  xoff_t x;
574
575  XD3_ASSERT(s && *s != 0);
576
577  {
578    /* Should check LONG_MIN, LONG_MAX, LLONG_MIN, LLONG_MAX? */
579#if SIZEOF_XOFF_T == 4
580    long xx = strtol (s, &e, 0);
581#else
582    long long xx = strtoll (s, &e, 0);
583#endif
584
585    if (xx < 0)
586      {
587        XPR(NT "-%c: negative integer: %s\n", which, s);
588        return EXIT_FAILURE;
589      }
590
591    x = xx;
592  }
593
594  if (*e != 0)
595    {
596      XPR(NT "-%c: invalid integer: %s\n", which, s);
597      return EXIT_FAILURE;
598    }
599
600  (*xo) = x;
601  return 0;
602}
603
604static int
605main_atou (const char* arg, usize_t *xo, usize_t low, usize_t high, char which)
606{
607  xoff_t x;
608  int ret;
609
610  if ((ret = main_strtoxoff (arg, & x, which))) { return ret; }
611
612  if (x < low)
613    {
614      XPR(NT "-%c: minimum value: %u\n", which, low);
615      return EXIT_FAILURE;
616    }
617  if (high == 0)
618    {
619      high = USIZE_T_MAX;
620    }
621  if (x > high)
622    {
623      XPR(NT "-%c: maximum value: %u\n", which, high);
624      return EXIT_FAILURE;
625    }
626  (*xo) = (usize_t)x;
627  return 0;
628}
629
630/******************************************************************************************
631 FILE BASICS
632 ******************************************************************************************/
633
634/* With all the variation in file system-call semantics, arguments, return values and
635 * error-handling for the POSIX and STDIO file APIs, the insides of these functions make
636 * me sick, which is why these wrappers exist. */
637
638#define XOPEN_OPNAME (xfile->mode == XO_READ ? "read" : "write")
639#define XOPEN_STDIO  (xfile->mode == XO_READ ? "rb" : "wb")
640#define XOPEN_POSIX  (xfile->mode == XO_READ ? O_RDONLY : O_WRONLY | O_CREAT | O_TRUNC)
641#define XOPEN_MODE   (xfile->mode == XO_READ ? 0 : 0666)
642
643#define XF_ERROR(op, name, ret) \
644  XPR(NT "file %s failed: %s: %s: %s\n", (op), XOPEN_OPNAME, (name), xd3_mainerror (ret))
645
646#if XD3_STDIO
647#define XFNO(f) fileno(f->file)
648#define XSTDOUT_XF(f) { (f)->file = stdout; (f)->filename = "/dev/stdout"; }
649#define XSTDIN_XF(f)  { (f)->file = stdin;  (f)->filename = "/dev/stdin"; }
650
651#elif XD3_POSIX
652#define XFNO(f) f->file
653#define XSTDOUT_XF(f) { (f)->file = STDOUT_FILENO; (f)->filename = "/dev/stdout"; }
654#define XSTDIN_XF(f)  { (f)->file = STDIN_FILENO;  (f)->filename = "/dev/stdin"; }
655
656#elif XD3_WIN32
657#define XFNO(f) -1
658#define XSTDOUT_XF(f) { (f)->file = winStartupInfo.hStdOutput; (f)->filename = "(stdout)"; }
659#define XSTDIN_XF(f)  { (f)->file = winStartupInfo.hStdInput;  (f)->filename = "(stdin)"; }
660#endif
661
662static void
663main_file_init (main_file *xfile)
664{
665  memset (xfile, 0, sizeof (*xfile));
666
667#if XD3_POSIX
668  xfile->file = -1;
669#endif
670#if XD3_WIN32
671  xfile->file = INVALID_HANDLE_VALUE;
672#endif
673}
674
675static int
676main_file_isopen (main_file *xfile)
677{
678#if XD3_STDIO
679  return xfile->file != NULL;
680
681#elif XD3_POSIX
682  return xfile->file != -1;
683
684#elif XD3_WIN32
685  return xfile->file != INVALID_HANDLE_VALUE;
686#endif
687}
688
689static int
690main_file_close (main_file *xfile)
691{
692  int ret = 0;
693
694  if (! main_file_isopen (xfile))
695    {
696      return 0;
697    }
698
699#if XD3_STDIO
700  ret = fclose (xfile->file);
701  xfile->file = NULL;
702
703#elif XD3_POSIX
704  ret = close (xfile->file);
705  xfile->file = -1;
706
707#elif XD3_WIN32
708  if (!CloseHandle(xfile->file)) {
709    ret = get_errno ();
710  }
711  xfile->file = INVALID_HANDLE_VALUE;
712#endif
713
714  if (ret != 0) { XF_ERROR ("close", xfile->filename, ret = get_errno ()); }
715  return ret;
716}
717
718static void
719main_file_cleanup (main_file *xfile)
720{
721  if (main_file_isopen (xfile))
722    {
723      main_file_close (xfile);
724    }
725
726  if (xfile->snprintf_buf != NULL)
727    {
728      main_free(xfile->snprintf_buf);
729      xfile->snprintf_buf = NULL;
730    }
731
732  if (xfile->filename_copy != NULL)
733    {
734      main_free(xfile->filename_copy);
735      xfile->filename_copy = NULL;
736    }
737}
738
739static int
740main_file_open (main_file *xfile, const char* name, int mode)
741{
742  int ret = 0;
743
744  xfile->mode = mode;
745
746  XD3_ASSERT (name != NULL);
747  XD3_ASSERT (! main_file_isopen (xfile));
748  if (name[0] == 0)
749    {
750      XPR(NT "invalid file name: empty string\n");
751      return XD3_INVALID;
752    }
753
754#if XD3_STDIO
755  xfile->file = fopen (name, XOPEN_STDIO);
756
757  ret = (xfile->file == NULL) ? get_errno () : 0;
758
759#elif XD3_POSIX
760  if ((ret = open (name, XOPEN_POSIX, XOPEN_MODE)) < 0)
761    {
762      ret = get_errno ();
763    }
764  else
765    {
766      xfile->file = ret;
767      ret = 0;
768    }
769
770#elif XD3_WIN32
771  xfile->file = CreateFile(name,
772          (mode == XO_READ) ? GENERIC_READ : GENERIC_WRITE,
773          FILE_SHARE_READ,
774          NULL,
775          (mode == XO_READ) ? OPEN_EXISTING : (option_force ? CREATE_ALWAYS : CREATE_NEW),
776          FILE_ATTRIBUTE_NORMAL,
777          NULL);
778  if (xfile->file == INVALID_HANDLE_VALUE) {
779          ret = get_errno ();
780  }
781#endif
782  if (ret) { XF_ERROR ("open", name, ret); }
783  else     { xfile->realname = name; xfile->nread = 0; }
784  return ret;
785}
786
787static int
788main_file_stat (main_file *xfile, xoff_t *size, int err_ifnoseek)
789{
790  int ret = 0;
791#if XD3_WIN32
792  LARGE_INTEGER li;
793  if (GetFileSizeEx(xfile->file, &li) == 0) {
794          ret = get_errno ();
795  } else {
796      *size = li.QuadPart;
797  }
798#else
799  struct stat sbuf;
800  if (fstat (XFNO (xfile), & sbuf) < 0)
801    {
802      ret = get_errno ();
803      if (err_ifnoseek) { XF_ERROR ("stat", xfile->filename, ret); }
804      return ret;
805    }
806
807  if (! S_ISREG (sbuf.st_mode))
808    {
809      if (err_ifnoseek) { XPR(NT "source file must be seekable: %s\n", xfile->filename); }
810      return ESPIPE;
811    }
812  (*size) = sbuf.st_size;
813#endif
814  return ret;
815}
816
817static int
818main_file_exists (main_file *xfile)
819{
820  struct stat sbuf;
821  return stat (xfile->filename, & sbuf) == 0 && S_ISREG (sbuf.st_mode);
822}
823
824#if (XD3_POSIX || EXTERNAL_COMPRESSION)
825/* POSIX-generic code takes a function pointer to read() or write().  This calls the
826 * function repeatedly until the buffer is full or EOF.  The NREAD parameter is not
827 * set for write, NULL is passed.  Return is signed, < 0 indicate errors, otherwise
828 * byte count. */
829typedef int (xd3_posix_func) (int fd, uint8_t *buf, usize_t size);
830
831static int
832xd3_posix_io (int fd, uint8_t *buf, usize_t size, xd3_posix_func *func, usize_t *nread)
833{
834  int ret;
835  usize_t nproc = 0;
836
837  while (nproc < size)
838    {
839      int result = (*func) (fd, buf + nproc, size - nproc);
840
841      if (result < 0)
842        {
843          ret = get_errno ();
844          if (ret != EAGAIN && ret != EINTR)
845            {
846              return ret;
847            }
848          result = 0;
849        }
850
851      if (nread != NULL && result == 0) { break; }
852
853      nproc += result;
854    }
855  if (nread != NULL) { (*nread) = nproc; }
856  return 0;
857}
858#endif
859
860/* POSIX is unbuffered, while STDIO is buffered.  main_file_read() should always be called
861 * on blocks. */
862static int
863main_file_read (main_file   *ifile,
864               uint8_t    *buf,
865               usize_t      size,
866               usize_t     *nread,
867               const char *msg)
868{
869  int ret = 0;
870
871#if XD3_STDIO
872  usize_t result;
873
874  result = fread (buf, 1, size, ifile->file);
875
876  if (result < size && ferror (ifile->file))
877    {
878      ret = get_errno ();
879    }
880  else
881    {
882      *nread = result;
883    }
884
885#elif XD3_POSIX
886  ret = xd3_posix_io (ifile->file, buf, size, (xd3_posix_func*) &read, nread);
887
888#elif XD3_WIN32
889  DWORD nread2;
890  if (ReadFile (ifile->file, buf, size, &nread2, NULL) == 0) {
891          ret = get_errno();
892  } else {
893      *nread = (usize_t)nread2;
894  }
895#endif
896
897  if (ret)
898    {
899      XPR(NT "%s: %s: %s\n", msg, ifile->filename, xd3_mainerror (ret));
900    }
901  else
902    {
903      if (option_verbose > 3) { XPR(NT "main read: %s: %u\n", ifile->filename, (*nread)); }
904      ifile->nread += (*nread);
905    }
906
907  return ret;
908}
909
910static int
911main_file_write (main_file *ofile, uint8_t *buf, usize_t size, const char *msg)
912{
913  int ret = 0;
914
915#if XD3_STDIO
916  usize_t result;
917
918  result = fwrite (buf, 1, size, ofile->file);
919
920  if (result != size) { ret = get_errno (); }
921
922#elif XD3_POSIX
923  ret = xd3_posix_io (ofile->file, buf, size, (xd3_posix_func*) &write, NULL);
924
925#elif XD3_WIN32
926  DWORD nwrite;
927  if (WriteFile(ofile->file, buf, size, &nwrite, NULL) == 0) {
928          ret = get_errno ();
929  } else {
930          if (size != nwrite) {
931                  XPR(NT "Incorrect write count");
932                  ret = XD3_INTERNAL;
933          }
934  }
935#endif
936
937  if (ret)
938    {
939      XPR(NT "%s: %s: %s\n", msg, ofile->filename, xd3_mainerror (ret));
940    }
941  else
942    {
943      if (option_verbose > 3) { XPR(NT "main write: %s: %u\n", ofile->filename, size); }
944      ofile->nwrite += size;
945    }
946
947  return ret;
948}
949
950static int
951main_file_seek (main_file *xfile, xoff_t pos)
952{
953  int ret = 0;
954
955#if XD3_STDIO
956  if (fseek (xfile->file, pos, SEEK_SET) != 0) { ret = get_errno (); }
957
958#elif XD3_POSIX
959  if (lseek (xfile->file, pos, SEEK_SET) != pos) { ret = get_errno (); }
960
961#elif XD3_WIN32
962  LARGE_INTEGER move, out;
963  move.QuadPart = pos;
964  if (SetFilePointerEx(xfile->file, move, &out, FILE_BEGIN) == 0) {
965          ret = get_errno ();
966  }
967#endif
968
969  if (ret)
970    {
971      XPR(NT "seek failed: %s: %s\n", xfile->filename, xd3_mainerror (ret));
972    }
973
974  return ret;
975}
976
977/******************************************************************************************
978 VCDIFF TOOLS
979 ******************************************************************************************/
980
981#if VCDIFF_TOOLS
982
983/* The following macros let VCDIFF printing something printf-like with
984 * main_file_write(), e.g.,:
985 *
986 *   VC(UT "trying to be portable: %d\n", x)VE;
987 */
988#define SNPRINTF_BUFSIZE 1024
989#define VC do { if (((ret = snprintf
990#define UT (char*)xfile->snprintf_buf, SNPRINTF_BUFSIZE,
991#define VE ) >= SNPRINTF_BUFSIZE                               \
992  && (ret = main_print_overflow(ret)) != 0)                    \
993  || (ret = main_file_write(xfile, xfile->snprintf_buf,        \
994                            ret, "print")) != 0)               \
995  { return ret; } } while (0)
996
997#ifdef WIN32
998/* According to the internet, Windows vsnprintf() differs from most Unix
999 * implementations regarding the terminating 0 when the boundary condition
1000 * is met. It doesn't matter here, we don't rely on the trailing 0. */
1001#include <stdarg.h>
1002int
1003snprintf (char *str, int n, char *fmt, ...)
1004{
1005  va_list a;
1006  int ret;
1007  va_start (a, fmt);
1008  ret = vsnprintf (str, n, fmt, a);
1009  va_end (a);
1010  return ret;
1011}
1012#endif
1013
1014static int
1015main_print_overflow (int x)
1016{
1017  XPR(NT "internal print buffer overflow: %d bytes\n", x);
1018  return XD3_INTERNAL;
1019}
1020
1021/* This function prints a single VCDIFF window. */
1022static int
1023main_print_window (xd3_stream* stream, main_file *xfile)
1024{
1025  int ret;
1026  usize_t size = 0;
1027
1028  VC(UT "  Offset Code Type1 Size1 @Addr1 + Type2 Size2 @Addr2\n")VE;
1029
1030  while (stream->inst_sect.buf < stream->inst_sect.buf_max)
1031    {
1032      uint   code = stream->inst_sect.buf[0];
1033
1034      if ((ret = xd3_decode_instruction (stream))) { return ret; }
1035
1036      VC(UT "  %06"Q"u %03u  %s %3u", stream->dec_winstart + size, code,
1037         xd3_rtype_to_string (stream->dec_current1.type, option_print_cpymode),
1038         (usize_t) stream->dec_current1.size)VE;
1039
1040      if (stream->dec_current1.type != XD3_NOOP)
1041        {
1042          size += stream->dec_current1.size;
1043          if (stream->dec_current1.type >= XD3_CPY)
1044            {
1045              VC(UT " @%-6u", (usize_t)stream->dec_current1.addr)VE;
1046            }
1047          else
1048            {
1049              VC(UT "        ")VE;
1050            }
1051        }
1052
1053      if (stream->dec_current2.type != XD3_NOOP)
1054        {
1055          size += stream->dec_current2.size;
1056          VC(UT "  %s %3u",
1057             xd3_rtype_to_string (stream->dec_current2.type, option_print_cpymode),
1058             (usize_t)stream->dec_current2.size)VE;
1059
1060          if (stream->dec_current2.type >= XD3_CPY)
1061            {
1062              VC(UT " @%-6u", (usize_t)stream->dec_current2.addr)VE;
1063            }
1064        }
1065
1066      VC(UT "\n")VE;
1067    }
1068
1069  if (stream->dec_tgtlen != size && (stream->flags & XD3_SKIP_WINDOW) == 0)
1070    {
1071      XPR(NT "target window size inconsistency");
1072      return XD3_INTERNAL;
1073    }
1074
1075  if (stream->dec_position != stream->dec_maxpos)
1076    {
1077      XPR(NT "target window position inconsistency");
1078      return XD3_INTERNAL;
1079    }
1080
1081  if (stream->addr_sect.buf != stream->addr_sect.buf_max)
1082    {
1083      XPR(NT "address section inconsistency");
1084      return XD3_INTERNAL;
1085    }
1086
1087  return 0;
1088}
1089
1090static int
1091main_print_vcdiff_file (main_file *xfile, main_file *file, const char *type)
1092{
1093  int ret;  /* Used by above macros */
1094  if (file->filename)
1095    {
1096      VC(UT "XDELTA filename (%s):     %s\n", type, file->filename)VE;
1097    }
1098  if (file->compressor)
1099    {
1100      VC(UT "XDELTA ext comp (%s):     %s\n", type, file->compressor->recomp_cmdname)VE;
1101    }
1102  return 0;
1103}
1104
1105/* This function prints a VCDIFF input, mainly for debugging purposes. */
1106static int
1107main_print_func (xd3_stream* stream, main_file *xfile)
1108{
1109  int ret;
1110
1111  if (xfile->snprintf_buf == NULL)
1112    {
1113      if ((xfile->snprintf_buf = main_malloc(SNPRINTF_BUFSIZE)) == NULL)
1114        {
1115          return ENOMEM;
1116        }
1117    }
1118
1119  if (stream->dec_winstart == 0)
1120    {
1121      VC(UT "VCDIFF version:               0\n")VE;
1122
1123      VC(UT "VCDIFF header size:           %d\n", stream->dec_hdrsize)VE;
1124      VC(UT "VCDIFF header indicator:      ")VE;
1125      if ((stream->dec_hdr_ind & VCD_SECONDARY) != 0) VC(UT "VCD_SECONDARY ")VE;
1126      if ((stream->dec_hdr_ind & VCD_CODETABLE) != 0) VC(UT "VCD_CODETABLE ")VE;
1127      if ((stream->dec_hdr_ind & VCD_APPHEADER) != 0) VC(UT "VCD_APPHEADER ")VE;
1128      if (stream->dec_hdr_ind == 0) VC(UT "none")VE;
1129      VC(UT "\n")VE;
1130
1131      IF_SEC(VC(UT "VCDIFF secondary compressor:  %s\n",
1132                stream->sec_type ? stream->sec_type->name : "none")VE);
1133      IF_NSEC(VC(UT "VCDIFF secondary compressor: unsupported\n")VE);
1134
1135      if (stream->dec_hdr_ind & VCD_APPHEADER)
1136        {
1137          uint8_t *apphead;
1138          usize_t appheadsz;
1139          ret = xd3_get_appheader (stream, & apphead, & appheadsz);
1140
1141          if (ret == 0 && appheadsz > 0)
1142            {
1143              int sq = option_quiet;
1144              main_file i, o, s;
1145              XD3_ASSERT (apphead != NULL);
1146              VC(UT "VCDIFF application header:    ")VE;
1147              if ((ret = main_file_write (xfile, apphead, appheadsz, "print")) != 0) { return ret; }
1148              VC(UT "\n")VE;
1149
1150              main_file_init (& i);
1151              main_file_init (& o);
1152              main_file_init (& s);
1153              option_quiet = 1;
1154              main_get_appheader (stream, &i, & o, & s);
1155              option_quiet = sq;
1156              if ((ret = main_print_vcdiff_file (xfile, & o, "output"))) { return ret; }
1157              if ((ret = main_print_vcdiff_file (xfile, & s, "source"))) { return ret; }
1158              main_file_cleanup (& i);
1159              main_file_cleanup (& o);
1160              main_file_cleanup (& s);
1161            }
1162        }
1163    }
1164  else
1165    {
1166      VC(UT "\n")VE;
1167    }
1168
1169  VC(UT "VCDIFF window number:         %"Q"u\n", stream->current_window)VE;
1170  VC(UT "VCDIFF window indicator:      ")VE;
1171  if ((stream->dec_win_ind & VCD_SOURCE) != 0) VC(UT "VCD_SOURCE ")VE;
1172  if ((stream->dec_win_ind & VCD_TARGET) != 0) VC(UT "VCD_TARGET ")VE;
1173  if ((stream->dec_win_ind & VCD_ADLER32) != 0) VC(UT "VCD_ADLER32 ")VE;
1174  if (stream->dec_win_ind == 0) VC(UT "none")VE;
1175  VC(UT "\n")VE;
1176
1177  if ((stream->dec_win_ind & VCD_ADLER32) != 0)
1178    {
1179      VC(UT "VCDIFF adler32 checksum:      %08X\n", (usize_t)stream->dec_adler32)VE;
1180    }
1181
1182  if (stream->dec_del_ind != 0)
1183    {
1184      VC(UT "VCDIFF delta indicator:       ")VE;
1185      if ((stream->dec_del_ind & VCD_DATACOMP) != 0) VC(UT "VCD_DATACOMP ")VE;
1186      if ((stream->dec_del_ind & VCD_INSTCOMP) != 0) VC(UT "VCD_INSTCOMP ")VE;
1187      if ((stream->dec_del_ind & VCD_ADDRCOMP) != 0) VC(UT "VCD_ADDRCOMP ")VE;
1188      if (stream->dec_del_ind == 0) VC(UT "none")VE;
1189      VC(UT "\n")VE;
1190    }
1191
1192  if (stream->dec_winstart != 0)
1193    {
1194      VC(UT "VCDIFF window at offset:      %"Q"u\n", stream->dec_winstart)VE;
1195    }
1196
1197  if (SRCORTGT (stream->dec_win_ind))
1198    {
1199      VC(UT "VCDIFF copy window length:    %u\n", (usize_t)stream->dec_cpylen)VE;
1200      VC(UT "VCDIFF copy window offset:    %"Q"u\n", stream->dec_cpyoff)VE;
1201    }
1202
1203  VC(UT "VCDIFF delta encoding length: %u\n", (usize_t)stream->dec_enclen)VE;
1204  VC(UT "VCDIFF target window length:  %u\n", (usize_t)stream->dec_tgtlen)VE;
1205
1206  VC(UT "VCDIFF data section length:   %u\n", (usize_t)stream->data_sect.size)VE;
1207  VC(UT "VCDIFF inst section length:   %u\n", (usize_t)stream->inst_sect.size)VE;
1208  VC(UT "VCDIFF addr section length:   %u\n", (usize_t)stream->addr_sect.size)VE;
1209
1210  ret = 0;
1211  if ((stream->flags & XD3_JUST_HDR) != 0)
1212    {
1213      /* Print a header -- finished! */
1214      ret = PRINTHDR_SPECIAL;
1215    }
1216  else if ((stream->flags & XD3_SKIP_WINDOW) == 0)
1217    {
1218      ret = main_print_window (stream, xfile);
1219    }
1220
1221  return ret;
1222}
1223#endif /* VCDIFF_TOOLS */
1224
1225/******************************************************************************************
1226 Input decompression, output recompression
1227 ******************************************************************************************/
1228
1229#if EXTERNAL_COMPRESSION
1230/* This is tricky POSIX-specific code with lots of fork(), pipe(), dup(), waitpid(), and
1231 * exec() business.  Most of this code originated in PRCS1, which did automatic
1232 * package-file decompression.  It works with both XD3_POSIX and XD3_STDIO file
1233 * disciplines.
1234 *
1235 * To automatically detect compressed inputs requires a child process to reconstruct the
1236 * input stream, which was advanced in order to detect compression, because it may not be
1237 * seekable.  In other words, the main program reads part of the input stream, and if it
1238 * detects a compressed input it then forks a pipe copier process, which copies the
1239 * first-read block out of the main-program's memory, then streams the remaining
1240 * compressed input into the input-decompression pipe.
1241 */
1242
1243#include <unistd.h>
1244#include <sys/stat.h>
1245#include <sys/wait.h>
1246
1247/* Remember which pipe FD is which. */
1248#define PIPE_READ_FD  0
1249#define PIPE_WRITE_FD 1
1250
1251static pid_t ext_subprocs[2];
1252static char* ext_tmpfile = NULL;
1253
1254/* Like write(), but makes repeated calls to empty the buffer. */
1255static int
1256main_pipe_write (int outfd, const uint8_t *exist_buf, usize_t remain)
1257{
1258  int ret;
1259
1260  if ((ret = xd3_posix_io (outfd, (uint8_t*) exist_buf, remain, (xd3_posix_func*) &write, NULL)))
1261    {
1262      XPR(NT "pipe write failed: %s", xd3_mainerror (ret));
1263      return ret;
1264    }
1265
1266  return 0;
1267}
1268
1269/* A simple error-reporting waitpid interface. */
1270static int
1271main_waitpid_check(pid_t pid)
1272{
1273  int status;
1274  int ret = 0;
1275
1276  if (waitpid (pid, & status, 0) < 0)
1277    {
1278      ret = get_errno ();
1279      XPR(NT "compression subprocess: wait: %s\n", xd3_mainerror (ret));
1280    }
1281  else if (! WIFEXITED (status))
1282    {
1283      ret = ECHILD;
1284      XPR(NT "compression subprocess: signal %d\n",
1285         WIFSIGNALED (status) ? WTERMSIG (status) : WSTOPSIG (status));
1286    }
1287  else if (WEXITSTATUS (status) != 0)
1288    {
1289      ret = ECHILD;
1290      XPR(NT "compression subprocess: exit %d\n", WEXITSTATUS (status));
1291    }
1292
1293  return ret;
1294}
1295
1296/* Wait for any existing child processes to check for abnormal exit. */
1297static int
1298main_external_compression_finish (void)
1299{
1300  int i;
1301  int ret;
1302
1303  for (i = 0; i < 2; i += 1)
1304    {
1305      if (! ext_subprocs[i]) { continue; }
1306
1307      if ((ret = main_waitpid_check (ext_subprocs[i])))
1308        {
1309          return ret;
1310        }
1311    }
1312
1313  return 0;
1314}
1315
1316/* This runs as a forked process of main_input_decompress_setup() to copy input to the
1317 * decompression process.  First, the available input is copied out of the existing
1318 * buffer, then the buffer is reused to continue reading from the compressed input
1319 * file. */
1320static int
1321main_pipe_copier (uint8_t    *pipe_buf,
1322                  usize_t      pipe_bufsize,
1323                  usize_t      nread,
1324                  main_file   *ifile,
1325                  int         outfd)
1326{
1327  int ret;
1328
1329  for (;;)
1330    {
1331      if (nread > 0 && (ret = main_pipe_write (outfd, pipe_buf, nread)))
1332        {
1333          return ret;
1334        }
1335
1336      if (nread < pipe_bufsize)
1337        {
1338          break;
1339        }
1340
1341      if ((ret = main_file_read (ifile, pipe_buf, pipe_bufsize, & nread, "pipe read failed")) < 0)
1342        {
1343          return ret;
1344        }
1345    }
1346
1347  return 0;
1348}
1349
1350/* This function is called after we have read some amount of data from the input file and
1351 * detected a compressed input.  Here we start a decompression subprocess by forking
1352 * twice.  The first process runs the decompression command, the second process copies
1353 * data to the input of the first. */
1354static int
1355main_input_decompress_setup (const main_extcomp     *decomp,
1356                             main_file              *ifile,
1357                             uint8_t               *input_buf,
1358                             usize_t                 input_bufsize,
1359                             uint8_t               *pipe_buf,
1360                             usize_t                 pipe_bufsize,
1361                             usize_t                 pipe_avail,
1362                             usize_t                *nread)
1363{
1364  int outpipefd[2], inpipefd[2];  /* The two pipes: input and output file descriptors. */
1365  int input_fd = -1;              /* The resulting input_fd (output of decompression). */
1366  pid_t decomp_id, copier_id;     /* The two subprocs. */
1367  int ret;
1368
1369  outpipefd[0] = outpipefd[1] = -1;
1370  inpipefd[0]  = inpipefd[1]  = -1;
1371
1372  if (pipe (outpipefd) || pipe (inpipefd))
1373    {
1374      XPR(NT "pipe failed: %s\n", xd3_mainerror (ret = get_errno ()));
1375      goto pipe_cleanup;
1376    }
1377
1378  if ((decomp_id = fork ()) < 0)
1379    {
1380      XPR(NT "fork failed: %s\n", xd3_mainerror (ret = get_errno ()));
1381      goto pipe_cleanup;
1382    }
1383
1384  /* The first child runs the decompression process: */
1385  if (decomp_id == 0)
1386    {
1387      /* Setup pipes: write to the outpipe, read from the inpipe. */
1388      if (dup2 (outpipefd[PIPE_WRITE_FD], STDOUT_FILENO) < 0 ||
1389          dup2 (inpipefd[PIPE_READ_FD], STDIN_FILENO) < 0 ||
1390          close (outpipefd[PIPE_READ_FD]) ||
1391          close (outpipefd[PIPE_WRITE_FD]) ||
1392          close (inpipefd[PIPE_READ_FD]) ||
1393          close (inpipefd[PIPE_WRITE_FD]) ||
1394          execlp (decomp->decomp_cmdname, decomp->decomp_cmdname, decomp->decomp_options, NULL))
1395        {
1396          XPR(NT "child process %s failed to execute: %s\n", decomp->decomp_cmdname, xd3_mainerror (get_errno ()));
1397        }
1398
1399      _exit (127);
1400    }
1401
1402  ext_subprocs[0] = decomp_id;
1403
1404  if ((copier_id = fork ()) < 0)
1405    {
1406      XPR(NT "fork failed: %s\n", xd3_mainerror (ret = get_errno ()));
1407      goto pipe_cleanup;
1408    }
1409
1410  /* The second child runs the copier process: */
1411  if (copier_id == 0)
1412    {
1413      int exitval = 0;
1414
1415      if (close (inpipefd[PIPE_READ_FD]) ||
1416          main_pipe_copier (pipe_buf, pipe_bufsize, pipe_avail, ifile, inpipefd[PIPE_WRITE_FD]) ||
1417          close (inpipefd[PIPE_WRITE_FD]))
1418        {
1419          XPR(NT "child copier process failed: %s\n", xd3_mainerror (get_errno ()));
1420          exitval = 1;
1421        }
1422
1423      _exit (exitval);
1424    }
1425
1426  ext_subprocs[1] = copier_id;
1427
1428  /* The parent closes both pipes after duplicating the output of compression. */
1429  input_fd = dup (outpipefd[PIPE_READ_FD]);
1430
1431  if (input_fd < 0 ||
1432      main_file_close (ifile) ||
1433      close (outpipefd[PIPE_READ_FD]) ||
1434      close (outpipefd[PIPE_WRITE_FD]) ||
1435      close (inpipefd[PIPE_READ_FD]) ||
1436      close (inpipefd[PIPE_WRITE_FD]))
1437    {
1438      XPR(NT "dup/close failed: %s\n", xd3_mainerror (ret = get_errno ()));
1439      goto pipe_cleanup;
1440    }
1441
1442#if XD3_STDIO
1443  /* Note: fdopen() acquires the fd, closes it when finished. */
1444  if ((ifile->file = fdopen (input_fd, "r")) == NULL)
1445    {
1446      XPR(NT "fdopen failed: %s\n", xd3_mainerror (ret = get_errno ()));
1447      goto pipe_cleanup;
1448    }
1449
1450#elif XD3_POSIX
1451  ifile->file = input_fd;
1452#endif
1453
1454  ifile->compressor = decomp;
1455
1456  /* Now the input file is decompressed. */
1457  return main_file_read (ifile, input_buf, input_bufsize, nread, "input decompression failed");
1458
1459 pipe_cleanup:
1460  close (input_fd);
1461  close (outpipefd[PIPE_READ_FD]);
1462  close (outpipefd[PIPE_WRITE_FD]);
1463  close (inpipefd[PIPE_READ_FD]);
1464  close (inpipefd[PIPE_WRITE_FD]);
1465  return ret;
1466}
1467
1468
1469/* This routine is called when the first buffer of input data is read by the main program
1470 * (unless input decompression is disabled by command-line option).  If it recognizes the
1471 * magic number of a known input type it invokes decompression.
1472 *
1473 * Skips decompression if the decompression type or the file type is RD_NONEXTERNAL.
1474 *
1475 * Behaves exactly like main_file_read, otherwise.
1476 *
1477 * This function uses a separate buffer to read the first small block of input.  If a
1478 * compressed input is detected, the separate buffer is passed to the pipe copier.  This
1479 * avoids using the same size buffer in both cases. */
1480static int
1481main_decompress_input_check (main_file   *ifile,
1482                            uint8_t    *input_buf,
1483                            usize_t      input_size,
1484                            usize_t     *nread)
1485{
1486  int i;
1487  int ret;
1488  uint8_t check_buf[XD3_ALLOCSIZE];
1489  usize_t  check_nread;
1490
1491  if ((ret = main_file_read (ifile, check_buf, min (input_size, XD3_ALLOCSIZE), & check_nread, "input read failed")))
1492    {
1493      return ret;
1494    }
1495
1496  for (i = 0; i < SIZEOF_ARRAY (extcomp_types); i += 1)
1497    {
1498      const main_extcomp *decomp = & extcomp_types[i];
1499
1500      if ((check_nread > decomp->magic_size) &&
1501          /* The following expr skips decompression if we are trying to read a VCDIFF
1502           * input and that is the magic number. */
1503          !((decomp->flags & RD_NONEXTERNAL) && (ifile->flags & RD_NONEXTERNAL)) &&
1504          memcmp (check_buf, decomp->magic, decomp->magic_size) == 0)
1505        {
1506          if (! option_quiet)
1507            {
1508              XPR(NT "%s | %s %s\n",
1509                 ifile->filename,
1510                 decomp->decomp_cmdname,
1511                 decomp->decomp_options);
1512            }
1513
1514          return main_input_decompress_setup (decomp, ifile,
1515                                              input_buf, input_size,
1516                                              check_buf, XD3_ALLOCSIZE,
1517                                              check_nread, nread);
1518        }
1519    }
1520
1521  /* Now read the rest of the input block. */
1522  (*nread) = 0;
1523
1524  if (check_nread == XD3_ALLOCSIZE)
1525    {
1526      ret = main_file_read (ifile, input_buf + XD3_ALLOCSIZE,
1527                            input_size - XD3_ALLOCSIZE, nread,
1528                            "input read failed");
1529    }
1530
1531  memcpy (input_buf, check_buf, check_nread);
1532
1533  (*nread) += check_nread;
1534
1535  return 0;
1536}
1537
1538/* This is called when the source file needs to be decompressed.  We fork/exec a
1539 * decompression command with the proper input and output to a temporary file. */
1540static int
1541main_decompress_source (main_file *sfile, xd3_source *source)
1542{
1543  const main_extcomp *decomp = sfile->compressor;
1544  pid_t decomp_id;  /* One subproc. */
1545  int   input_fd  = -1;
1546  int   output_fd = -1;
1547  int   ret;
1548  char *tmpname = NULL;
1549  char *tmpdir  = getenv ("TMPDIR");
1550  static const char tmpl[] = "/xd3src.XXXXXX";
1551
1552  /* Make a template for mkstmp() */
1553  if (tmpdir == NULL) { tmpdir = "/tmp"; }
1554  if ((tmpname = main_malloc (strlen (tmpdir) + sizeof (tmpl) + 1)) == NULL) { return ENOMEM; }
1555  sprintf (tmpname, "%s%s", tmpdir, tmpl);
1556
1557  XD3_ASSERT (ext_tmpfile == NULL);
1558  ext_tmpfile = tmpname;
1559
1560  /* Open the output FD. */
1561  if ((output_fd = mkstemp (tmpname)) < 0)
1562    {
1563      XPR(NT "mkstemp failed: %s: %s", tmpname, xd3_mainerror (ret = get_errno ()));
1564      goto cleanup;
1565    }
1566
1567  /* Copy the input FD, reset file position. */
1568  XD3_ASSERT (main_file_isopen (sfile));
1569#if XD3_STDIO
1570  if ((input_fd = dup (fileno (sfile->file))) < 0)
1571    {
1572      XPR(NT "dup failed: %s", xd3_mainerror (ret = get_errno ()));
1573      goto cleanup;
1574    }
1575  main_file_close (sfile);
1576  sfile->file = NULL;
1577#elif XD3_POSIX
1578  input_fd = sfile->file;
1579  sfile->file = -1;
1580#endif
1581
1582  if ((ret = lseek (input_fd, SEEK_SET, 0)) != 0)
1583    {
1584      XPR(NT "lseek failed: : %s", xd3_mainerror (ret = get_errno ()));
1585      goto cleanup;
1586    }
1587
1588  if ((decomp_id = fork ()) < 0)
1589    {
1590      XPR(NT "fork failed: %s", xd3_mainerror (ret = get_errno ()));
1591      goto cleanup;
1592    }
1593
1594  /* The child runs the decompression process: */
1595  if (decomp_id == 0)
1596    {
1597      /* Setup pipes: write to the output file, read from the pipe. */
1598      if (dup2 (input_fd, STDIN_FILENO) < 0 ||
1599          dup2 (output_fd, STDOUT_FILENO) < 0 ||
1600          execlp (decomp->decomp_cmdname, decomp->decomp_cmdname, decomp->decomp_options, NULL))
1601        {
1602          XPR(NT "child process %s failed to execute: %s\n",
1603                   decomp->decomp_cmdname, xd3_mainerror (get_errno ()));
1604        }
1605
1606      _exit (127);
1607    }
1608
1609  close (input_fd);
1610  close (output_fd);
1611  input_fd  = -1;
1612  output_fd = -1;
1613
1614  /* Then wait for completion. */
1615  if ((ret = main_waitpid_check (decomp_id)))
1616    {
1617      goto cleanup;
1618    }
1619
1620  /* Open/stat the decompressed source file. */
1621  if ((ret = main_file_open (sfile, tmpname, XO_READ))) { goto cleanup; }
1622  if ((ret = main_file_stat (sfile, & source->size, 1))) { goto cleanup; }
1623  return 0;
1624
1625 cleanup:
1626  close (input_fd);
1627  close (output_fd);
1628  if (tmpname) { free (tmpname); }
1629  ext_tmpfile = NULL;
1630  return ret;
1631}
1632
1633/* Initiate re-compression of the output stream.  This is easier than input decompression
1634 * because we know beforehand that the stream will be compressed, whereas the input has
1635 * already been read when we decide it should be decompressed.  Thus, it only requires one
1636 * subprocess and one pipe. */
1637static int
1638main_recompress_output (main_file *ofile)
1639{
1640  pid_t recomp_id;  /* One subproc. */
1641  int   pipefd[2];  /* One pipe. */
1642  int   output_fd = -1;
1643  int   ret;
1644  const main_extcomp *recomp = ofile->compressor;
1645
1646  pipefd[0] = pipefd[1] = -1;
1647
1648  if (pipe (pipefd))
1649    {
1650      XPR(NT "pipe failed: %s\n", xd3_mainerror (ret = get_errno ()));
1651      goto pipe_cleanup;
1652    }
1653
1654  if ((recomp_id = fork ()) < 0)
1655    {
1656      XPR(NT "fork failed: %s\n", xd3_mainerror (ret = get_errno ()));
1657      goto pipe_cleanup;
1658    }
1659
1660  /* The child runs the recompression process: */
1661  if (recomp_id == 0)
1662    {
1663      /* Setup pipes: write to the output file, read from the pipe. */
1664      if (dup2 (XFNO (ofile), STDOUT_FILENO) < 0 ||
1665          dup2 (pipefd[PIPE_READ_FD], STDIN_FILENO) < 0 ||
1666          close (pipefd[PIPE_READ_FD]) ||
1667          close (pipefd[PIPE_WRITE_FD]) ||
1668          execlp (recomp->recomp_cmdname, recomp->recomp_cmdname, recomp->recomp_options, NULL))
1669        {
1670          XPR(NT "child process %s failed to execute: %s\n", recomp->recomp_cmdname, xd3_mainerror (get_errno ()));
1671        }
1672
1673      _exit (127);
1674    }
1675
1676  ext_subprocs[0] = recomp_id;
1677
1678  /* The parent closes both pipes after duplicating the output-fd for writing to the
1679   * compression pipe. */
1680  output_fd = dup (pipefd[PIPE_WRITE_FD]);
1681
1682  if (output_fd < 0 ||
1683      main_file_close (ofile) ||
1684      close (pipefd[PIPE_READ_FD]) ||
1685      close (pipefd[PIPE_WRITE_FD]))
1686    {
1687      XPR(NT "close failed: %s\n", xd3_mainerror (ret = get_errno ()));
1688      goto pipe_cleanup;
1689    }
1690
1691#if XD3_STDIO
1692  /* Note: fdopen() acquires the fd, closes it when finished. */
1693  if ((ofile->file = fdopen (output_fd, "w")) == NULL)
1694    {
1695      XPR(NT "fdopen failed: %s\n", xd3_mainerror (ret = get_errno ()));
1696      goto pipe_cleanup;
1697    }
1698
1699#elif XD3_POSIX
1700  ofile->file = output_fd;
1701#endif
1702
1703  /* Now the output file will be compressed. */
1704  return 0;
1705
1706 pipe_cleanup:
1707  close (output_fd);
1708  close (pipefd[PIPE_READ_FD]);
1709  close (pipefd[PIPE_WRITE_FD]);
1710  return ret;
1711}
1712#endif /* EXTERNAL_COMPRESSION */
1713
1714/* Identify the compressor that was used based on its ident string, which is passed in the
1715 * application header. */
1716static const main_extcomp*
1717main_ident_compressor (const char *ident)
1718{
1719  int i;
1720
1721  for (i = 0; i < SIZEOF_ARRAY (extcomp_types); i += 1)
1722    {
1723      if (strcmp (extcomp_types[i].ident, ident) == 0)
1724        {
1725          return & extcomp_types[i];
1726        }
1727    }
1728
1729  return NULL;
1730}
1731
1732/* Return the main_extcomp record to use for this identifier, if possible. */
1733static const main_extcomp*
1734main_get_compressor (const char *ident)
1735{
1736  const main_extcomp *ext = main_ident_compressor (ident);
1737
1738  if (ext == NULL)
1739    {
1740      if (! option_quiet)
1741        {
1742          XPR(NT "warning: cannot recompress output: "
1743                   "unrecognized external compression ID: %s\n", ident);
1744        }
1745      return NULL;
1746    }
1747  else if (! EXTERNAL_COMPRESSION)
1748    {
1749      if (! option_quiet)
1750        {
1751          XPR(NT "warning: external support not compiled: "
1752                   "original input was compressed: %s\n", ext->recomp_cmdname);
1753        }
1754      return NULL;
1755    }
1756  else
1757    {
1758      return ext;
1759    }
1760}
1761
1762/******************************************************************************************
1763 APPLICATION HEADER
1764 ******************************************************************************************/
1765
1766#if XD3_ENCODER
1767static const char*
1768main_apphead_string (const char* x)
1769{
1770  const char *y;
1771
1772  if (x == NULL) { return ""; }
1773
1774  if (strcmp (x, "/dev/stdin") == 0 ||
1775      strcmp (x, "/dev/stdout") == 0 ||
1776      strcmp (x, "/dev/stderr") == 0) { return "-"; }
1777
1778  // TODO: this is not portable
1779  return (y = strrchr (x, '/')) == NULL ? x : y + 1;
1780}
1781
1782static int
1783main_set_appheader (xd3_stream *stream, main_file *input, main_file *sfile)
1784{
1785  /* The user may disable the application header.  Once the appheader is set, this
1786   * disables setting it again. */
1787  if (appheader_used || ! option_use_appheader) { return 0; }
1788
1789  /* The user may specify the application header, otherwise format the default header. */
1790  if (option_appheader)
1791    {
1792      appheader_used = option_appheader;
1793    }
1794  else
1795    {
1796      const char *iname;
1797      const char *icomp;
1798      const char *sname;
1799      const char *scomp;
1800      int len;
1801
1802      iname = main_apphead_string (input->filename);
1803      icomp = (input->compressor == NULL) ? "" : input->compressor->ident;
1804      len = strlen (iname) + strlen (icomp) + 2;
1805
1806      if (sfile->filename != NULL)
1807        {
1808          sname = main_apphead_string (sfile->filename);
1809          scomp = (sfile->compressor == NULL) ? "" : sfile->compressor->ident;
1810          len += strlen (sname) + strlen (scomp) + 2;
1811        }
1812      else
1813        {
1814          sname = scomp = "";
1815        }
1816
1817      if ((appheader_used = main_malloc (len)) == NULL)
1818        {
1819          return ENOMEM;
1820        }
1821
1822      if (sfile->filename == NULL)
1823        {
1824          sprintf ((char*)appheader_used, "%s/%s", iname, icomp);
1825        }
1826      else
1827        {
1828          sprintf ((char*)appheader_used, "%s/%s/%s/%s", iname, icomp, sname, scomp);
1829        }
1830    }
1831
1832  xd3_set_appheader (stream, appheader_used, strlen ((char*)appheader_used));
1833
1834  return 0;
1835}
1836#endif
1837
1838static void
1839main_get_appheader_params (main_file *file, char **parsed, int output, const char *type,
1840                           main_file *other)
1841{
1842  /* Set the filename if it was not specified.  If output, option_stdout (-c) overrides. */
1843  if (file->filename == NULL && ! (output && option_stdout) && strcmp (parsed[0], "-") != 0)
1844    {
1845      file->filename = parsed[0];
1846
1847      if (other->filename != NULL) {
1848        /* Take directory from the other file, if it has one. */
1849        /* TODO: This results in nonsense names like /dev/foo.tar.gz
1850         * and probably the filename-default logic interferes with
1851         * multi-file operation and the standard file extension?
1852         * Possibly the name header is bad, should be off by default.
1853         * Possibly we just want to remember external/compression
1854         * settings. */
1855        char *last_slash = strrchr(other->filename, '/');
1856
1857        if (last_slash != NULL) {
1858          int dlen = last_slash - other->filename;
1859
1860          XD3_ASSERT(file->filename_copy == NULL);
1861          file->filename_copy = main_malloc(dlen + 2 + strlen(file->filename));
1862
1863          strncpy(file->filename_copy, other->filename, dlen);
1864          file->filename_copy[dlen] = '/';
1865          strcpy(file->filename_copy + dlen + 1, parsed[0]);
1866
1867          file->filename = file->filename_copy;
1868        }
1869      }
1870
1871      if (! option_quiet)
1872        {
1873          XPR(NT "using default %s filename: %s\n", type, file->filename);
1874        }
1875    }
1876
1877  /* Set the compressor, initiate de/recompression later. */
1878  if (file->compressor == NULL && *parsed[1] != 0)
1879    {
1880      file->compressor = main_get_compressor (parsed[1]);
1881    }
1882}
1883
1884static void
1885main_get_appheader (xd3_stream *stream, main_file *ifile, main_file *output, main_file *sfile)
1886{
1887  uint8_t *apphead;
1888  usize_t appheadsz;
1889  int ret;
1890
1891  /* The user may disable the application header.  Once the appheader is set, this
1892   * disables setting it again. */
1893  if (! option_use_appheader) { return; }
1894
1895  ret = xd3_get_appheader (stream, & apphead, & appheadsz);
1896
1897  /* Ignore failure, it only means we haven't received a header yet. */
1898  if (ret != 0) { return; }
1899
1900  if (appheadsz > 0)
1901    {
1902      char *start = (char*)apphead;
1903      char *slash;
1904      int   place = 0;
1905      char *parsed[4];
1906
1907      memset (parsed, 0, sizeof (parsed));
1908
1909      while ((slash = strchr (start, '/')) != NULL)
1910        {
1911          *slash = 0;
1912          parsed[place++] = start;
1913          start = slash + 1;
1914        }
1915
1916      parsed[place++] = start;
1917
1918      /* First take the output parameters. */
1919      if (place == 2 || place == 4)
1920        {
1921          main_get_appheader_params (output, parsed, 1, "output", ifile);
1922        }
1923
1924      /* Then take the source parameters. */
1925      if (place == 4)
1926        {
1927          main_get_appheader_params (sfile, parsed+2, 0, "source", ifile);
1928        }
1929    }
1930
1931  option_use_appheader = 0;
1932  return;
1933}
1934
1935/******************************************************************************************
1936 Main I/O routines
1937 ******************************************************************************************/
1938
1939/* This function acts like the above except it may also try to recognize a compressed
1940 * input when the first buffer of data is read.  The EXTERNAL_COMPRESSION code is called
1941 * to search for magic numbers. */
1942static int
1943main_read_primary_input (main_file   *ifile,
1944                         uint8_t    *buf,
1945                         usize_t      size,
1946                         usize_t     *nread)
1947{
1948#if EXTERNAL_COMPRESSION
1949  if (option_decompress_inputs && ifile->flags & RD_FIRST)
1950    {
1951      ifile->flags &= ~RD_FIRST;
1952
1953      return main_decompress_input_check (ifile, buf, size, nread);
1954    }
1955#endif
1956
1957  return main_file_read (ifile, buf, size, nread, "input read failed");
1958}
1959
1960/* This function simply writes the stream output buffer, if there is any.  This is used
1961 * for both encode and decode commands.  (The VCDIFF tools use main_print_func()). */
1962static int
1963main_write_output (xd3_stream* stream, main_file *ofile)
1964{
1965  int ret;
1966
1967  if (stream->avail_out > 0 && (ret = main_file_write (ofile, stream->next_out, stream->avail_out, "write failed")))
1968    {
1969      return ret;
1970    }
1971
1972  return 0;
1973}
1974
1975/* Open the main output file, sets a default file name, initiate recompression.  This
1976 * function is expected to fprint any error messages. */
1977static int
1978main_open_output (xd3_stream *stream, main_file *ofile)
1979{
1980  int ret;
1981
1982  if (ofile->filename == NULL)
1983    {
1984      XSTDOUT_XF (ofile);
1985
1986      if (option_verbose > 1) { XPR(NT "using standard output: %s\n", ofile->filename); }
1987    }
1988  else
1989    {
1990      /* Stat the file to check for overwrite. */
1991      if (option_force == 0 && main_file_exists (ofile))
1992        {
1993          XPR(NT "to overwrite output file specify -f: %s\n", ofile->filename);
1994          return EEXIST;
1995        }
1996
1997      if ((ret = main_file_open (ofile, ofile->filename, XO_WRITE)))
1998        {
1999          return ret;
2000        }
2001
2002      if (option_verbose > 1) { XPR(NT "output file: %s\n", ofile->filename); }
2003    }
2004
2005#if EXTERNAL_COMPRESSION
2006  /* Do output recompression. */
2007  if (ofile->compressor != NULL && option_recompress_outputs == 1)
2008    {
2009      if (! option_quiet)
2010        {
2011          XPR(NT "%s %s | %s\n",
2012             ofile->compressor->recomp_cmdname,
2013             ofile->compressor->recomp_options,
2014             ofile->filename);
2015        }
2016
2017      if ((ret = main_recompress_output (ofile)))
2018        {
2019          return ret;
2020        }
2021    }
2022#endif
2023
2024  return 0;
2025}
2026
2027/* This is called at different times for encoding and decoding.  The encoder calls it
2028 * immediately, the decoder delays until the application header is received.
2029 * Stream may be NULL, in which case xd3_set_source is not called. */
2030static int
2031main_set_source (xd3_stream *stream, int cmd, main_file *sfile, xd3_source *source)
2032{
2033  int ret = 0, i;
2034  uint8_t *tmp_buf = NULL;
2035
2036  /* Open it, check for seekability, set required xd3_source fields. */
2037  if (allow_fake_source)
2038    {
2039      sfile->mode = XO_READ;
2040      sfile->realname = sfile->filename;
2041      sfile->nread = 0;
2042      source->size = UINT64_MAX;
2043    }
2044  else
2045    {
2046      if ((ret = main_file_open (sfile, sfile->filename, XO_READ)) ||
2047          (ret = main_file_stat (sfile, & source->size, 1)))
2048        {
2049          goto error;
2050        }
2051    }
2052
2053  source->name     = sfile->filename;
2054  source->ioh      = sfile;
2055  source->curblkno = (xoff_t) -1;
2056  source->curblk   = NULL;
2057 
2058#if EXTERNAL_COMPRESSION
2059  if (option_decompress_inputs)
2060    {
2061      /* If encoding, read the header to check for decompression. */
2062      if (IS_ENCODE (cmd))
2063        {
2064          usize_t nread;
2065          tmp_buf = main_malloc(XD3_ALLOCSIZE);
2066
2067          if ((ret = main_file_read (sfile, tmp_buf, XD3_ALLOCSIZE,
2068                                     & nread, "source read failed")))
2069            {
2070              goto error;
2071            }
2072
2073          /* Check known magic numbers. */
2074          for (i = 0; i < SIZEOF_ARRAY (extcomp_types); i += 1)
2075            {
2076              const main_extcomp *decomp = & extcomp_types[i];
2077
2078              if ((nread > decomp->magic_size) &&
2079                  memcmp (tmp_buf, decomp->magic, decomp->magic_size) == 0)
2080                {
2081                  sfile->compressor = decomp;
2082                  break;
2083                }
2084            }
2085
2086          if (sfile->compressor == NULL)
2087            {
2088              if (option_verbose > 2)
2089                {
2090                  XPR(NT "source block 0 read (not compressed)\n");
2091                }
2092            }
2093        }
2094
2095      /* In either the encoder or decoder, start decompression. */
2096      if (sfile->compressor)
2097        {
2098          xoff_t osize = source->size;
2099
2100          if (osize > XD3_NODECOMPRESSSIZE)
2101            {
2102              XPR(NT "source file too large for external decompression: %s: %"Q"u\n",
2103                       sfile->filename, osize);
2104              ret = XD3_INTERNAL;
2105              goto error;
2106            }
2107
2108          if ((ret = main_decompress_source (sfile, source)))
2109            {
2110              goto error;
2111            }
2112
2113          if (! option_quiet)
2114            {
2115              char s1[32], s2[32];
2116              XPR(NT "%s | %s %s => %s %.1f%% [ %s , %s ]\n",
2117                 sfile->filename,
2118                 sfile->compressor->decomp_cmdname,
2119                 sfile->compressor->decomp_options,
2120                 sfile->realname,
2121                 100.0 * source->size / osize,
2122                 main_format_bcnt (osize, s1),
2123                 main_format_bcnt (source->size, s2));
2124            }
2125        }
2126    }
2127#endif
2128
2129  /* At this point we know source->size.
2130   * Source buffer, blksize, LRU init. */
2131  if (source->size < option_srcwinsz)
2132    {
2133      /* Reduce sizes to actual source size, read whole file */
2134      option_srcwinsz = source->size;
2135      source->blksize = source->size;
2136      lru_size = 1;
2137    }
2138  else
2139    {
2140      option_srcwinsz = max(option_srcwinsz, XD3_MINSRCWINSZ);
2141
2142      source->blksize = (option_srcwinsz / LRU_SIZE);
2143      lru_size = LRU_SIZE;
2144    }
2145
2146  main_blklru_list_init (& lru_list);
2147  main_blklru_list_init (& lru_free);
2148
2149  if (option_verbose)
2150    {
2151      static char buf[32];
2152     
2153      XPR(NT "source %s winsize %s size %"Q"u\n",
2154          sfile->filename, main_format_bcnt(option_srcwinsz, buf), source->size);
2155    }
2156
2157  if (option_verbose > 1)
2158    {
2159      XPR(NT "source block size: %u\n", source->blksize);
2160    }
2161
2162  if ((lru = main_malloc (sizeof (main_blklru) * lru_size)) == NULL)
2163    {
2164      ret = ENOMEM;
2165      goto error;
2166    }
2167
2168  for (i = 0; i < lru_size; i += 1)
2169    {
2170      lru[i].blkno = (xoff_t) -1;
2171
2172      if ((lru[i].blk = main_malloc (source->blksize)) == NULL)
2173        {
2174          ret = ENOMEM;
2175          goto error;
2176        }
2177
2178      main_blklru_list_push_back (& lru_free, & lru[i]);
2179    }
2180
2181  if (stream && (ret = xd3_set_source (stream, source)))
2182    {
2183      XPR(NT XD3_LIB_ERRMSG (stream, ret));
2184      goto error;
2185    }
2186
2187 error:
2188  if (tmp_buf != NULL)
2189    {
2190      main_free (tmp_buf);
2191    }
2192
2193  return ret;
2194}
2195
2196static void
2197main_set_winsize (main_file *ifile) {
2198  xoff_t file_size;
2199
2200  if (main_file_stat (ifile, &file_size, 0) == 0)
2201    {
2202      option_winsize = (usize_t) min(file_size, (xoff_t) option_winsize);
2203    }
2204
2205  option_winsize = max(option_winsize, XD3_ALLOCSIZE);
2206
2207  if (option_verbose > 1)
2208    {
2209      XPR(NT "input window size: %u\n", option_winsize);
2210    }
2211}
2212
2213/******************************************************************************************
2214 Source routines
2215 ******************************************************************************************/
2216
2217/* This is the callback for reading a block of source.  This function is blocking and it
2218 * implements a small LRU.
2219 *
2220 * Note that it is possible for main_input() to handle getblk requests in a non-blocking
2221 * manner.  If the callback is NULL then the caller of xd3_*_input() must handle the
2222 * XD3_GETSRCBLK return value and fill the source in the same way.  See xd3_getblk for
2223 * details.  To see an example of non-blocking getblk, see xdelta-test.h. */
2224static int
2225main_getblk_func (xd3_stream *stream,
2226                  xd3_source *source,
2227                  xoff_t      blkno)
2228{
2229  xoff_t      pos   = blkno * source->blksize;
2230  main_file   *sfile = (main_file*) source->ioh;
2231  main_blklru *blru  = NULL;
2232  usize_t      onblk = xd3_bytes_on_srcblk (source, blkno);
2233  usize_t      nread;
2234  int         ret;
2235  int         i;
2236
2237  if (allow_fake_source)
2238    {
2239      source->curblkno = blkno;
2240      source->onblk    = onblk;
2241      source->curblk   = lru[0].blk;
2242      return 0;
2243    }
2244
2245  if (do_not_lru)
2246    {
2247      /* Direct lookup assumes sequential scan w/o skipping blocks. */
2248      int idx = blkno % lru_size;
2249      if (lru[idx].blkno == blkno)
2250        {
2251          source->curblkno = blkno;
2252          source->onblk    = onblk;
2253          source->curblk   = lru[idx].blk;
2254          lru_hits += 1;
2255          return 0;
2256        }
2257
2258      if (lru[idx].blkno != -1LL &&
2259          lru[idx].blkno != blkno - lru_size)
2260        {
2261          return XD3_TOOFARBACK;
2262        }
2263    }
2264  else
2265    {
2266      /* Sequential search through LRU. */
2267      for (i = 0; i < lru_size; i += 1)
2268        {
2269          if (lru[i].blkno == blkno)
2270            {
2271              main_blklru_list_remove (& lru[i]);
2272              main_blklru_list_push_back (& lru_list, & lru[i]);
2273
2274              source->curblkno = blkno;
2275              source->onblk    = onblk;
2276              source->curblk   = lru[i].blk;
2277              lru_hits += 1;
2278              return 0;
2279            }
2280        }
2281    }
2282
2283  if (! main_blklru_list_empty (& lru_free))
2284    {
2285      blru = main_blklru_list_pop_front (& lru_free);
2286    }
2287  else if (! main_blklru_list_empty (& lru_list))
2288    {
2289      if (do_not_lru) {
2290        blru = & lru[blkno % lru_size];
2291        main_blklru_list_remove(blru);
2292      } else {
2293        blru = main_blklru_list_pop_front (& lru_list);
2294      }
2295      lru_misses += 1;
2296    }
2297
2298  lru_filled += 1;
2299
2300  if ((ret = main_file_seek (sfile, pos)))
2301    {
2302      return ret;
2303    }
2304
2305  if ((ret = main_file_read (sfile, (uint8_t*) blru->blk, source->blksize,
2306                             & nread, "source read failed")))
2307    {
2308      return ret;
2309    }
2310
2311  if (nread != onblk)
2312    {
2313      XPR(NT "source file size change: %s\n", sfile->filename);
2314      return XD3_INTERNAL;
2315    }
2316
2317  main_blklru_list_push_back (& lru_list, blru);
2318
2319  if (option_verbose > 3)
2320    {
2321      if (blru->blkno != -1LL)
2322        {
2323          XPR(NT "source block %"Q"u ejects %"Q"u (lru_hits=%u, lru_misses=%u, lru_filled=%u)\n",
2324              blkno, blru->blkno, lru_hits, lru_misses, lru_filled);
2325        }
2326      else
2327        {
2328          XPR(NT "source block %"Q"u read (lru_hits=%u, lru_misses=%u, lru_filled=%u)\n",
2329                                          blkno, lru_hits, lru_misses, lru_filled);
2330        }
2331    }
2332
2333  blru->blkno      = blkno;
2334  source->curblk   = blru->blk;
2335  source->curblkno = blkno;
2336  source->onblk    = onblk;
2337
2338  return 0;
2339}
2340
2341/******************************************************************************************
2342 Main routines
2343 ******************************************************************************************/
2344
2345/* This is a generic input function.  It calls the xd3_encode_input or xd3_decode_input
2346 * functions and makes calls to the various input handling routines above, which
2347 * coordinate external decompression.
2348 */
2349static int
2350main_input (xd3_cmd     cmd,
2351            main_file   *ifile,
2352            main_file   *ofile,
2353            main_file   *sfile)
2354{
2355  int        ret;
2356  xd3_stream stream;
2357  usize_t    nread;
2358  int        stream_flags = 0;
2359  xd3_config config;
2360  xd3_source source;
2361  xoff_t     last_total_in = 0;
2362  xoff_t     last_total_out = 0;
2363  long       start_time;
2364  int        stdout_only = 0;
2365
2366  int (*input_func) (xd3_stream*);
2367  int (*output_func) (xd3_stream*, main_file *);
2368
2369  memset (& source, 0, sizeof (source));
2370  memset (& config, 0, sizeof (config));
2371
2372  config.alloc = main_alloc;
2373  config.freef = main_free1;
2374  config.sec_data.ngroups = 1;
2375  config.sec_addr.ngroups = 1;
2376  config.sec_inst.ngroups = 1;
2377  config.iopt_size = option_iopt_size;
2378  config.sprevsz = option_sprevsz;
2379
2380  do_not_lru = 0;
2381
2382  start_time = get_millisecs_now ();
2383
2384  /* main_input setup. */
2385  switch ((int) cmd)
2386    {
2387#if VCDIFF_TOOLS
2388           if (1) { case CMD_PRINTHDR:   stream_flags = XD3_JUST_HDR; }
2389      else if (1) { case CMD_PRINTHDRS:  stream_flags = XD3_SKIP_WINDOW; }
2390      else        { case CMD_PRINTDELTA: stream_flags = XD3_SKIP_EMIT; }
2391      ifile->flags |= RD_NONEXTERNAL;
2392      input_func    = xd3_decode_input;
2393      output_func   = main_print_func;
2394      stream_flags |= XD3_ADLER32_NOVER;
2395      stdout_only   = 1;
2396      break;
2397#endif
2398#if XD3_ENCODER
2399    case CMD_ENCODE:
2400      do_not_lru  = 1;
2401      input_func  = xd3_encode_input;
2402      output_func = main_write_output;
2403
2404      if (option_use_checksum) { stream_flags |= XD3_ADLER32; }
2405      if (option_use_secondary)
2406        {
2407          /* The default secondary compressor is DJW, if it's compiled, being used, etc. */
2408          if (option_secondary == NULL)
2409            {
2410              if (SECONDARY_DJW) { stream_flags |= XD3_SEC_DJW; }
2411            }
2412          else
2413            {
2414              if (strcmp (option_secondary, "fgk") == 0 && SECONDARY_FGK)
2415                {
2416                  stream_flags |= XD3_SEC_FGK;
2417                }
2418              else if (strcmp (option_secondary, "djw") == 0 && SECONDARY_DJW)
2419                {
2420                  stream_flags |= XD3_SEC_DJW;
2421                }
2422              else
2423                {
2424                  XPR(NT "unrecognized secondary compressor type: %s\n", option_secondary);
2425                  return EXIT_FAILURE;
2426                }
2427            }
2428        }
2429      if (option_no_compress)      { stream_flags |= XD3_NOCOMPRESS; }
2430      if (option_use_altcodetable) { stream_flags |= XD3_ALT_CODE_TABLE; }
2431      if (option_smatch_config)
2432        {
2433          char *s = option_smatch_config, *e;
2434          int values[XD3_SOFTCFG_VARCNT];
2435          int got;
2436
2437          config.smatch_cfg = XD3_SMATCH_SOFT;
2438
2439          for (got = 0; got < XD3_SOFTCFG_VARCNT; got += 1, s = e + 1)
2440            {
2441              values[got] = strtol (s, &e, 10);
2442
2443              if ((values[got] < 0) ||
2444                  (e == s) ||
2445                  (got < XD3_SOFTCFG_VARCNT-1 && *e == 0) ||
2446                  (got == XD3_SOFTCFG_VARCNT-1 && *e != 0))
2447                {
2448                  XPR(NT "invalid string match specifier (-C) %d: %s\n",
2449                      got, s);
2450                  return EXIT_FAILURE;
2451                }
2452            }
2453
2454          config.smatcher_soft.large_look    = values[0];
2455          config.smatcher_soft.large_step    = values[1];
2456          config.smatcher_soft.small_look    = values[2];
2457          config.smatcher_soft.small_chain   = values[3];
2458          config.smatcher_soft.small_lchain  = values[4];
2459          config.smatcher_soft.max_lazy      = values[5];
2460          config.smatcher_soft.long_enough   = values[6];
2461        }
2462      else
2463        {
2464          if (option_verbose > 1)
2465            {
2466              XPR(NT "compression level: %d\n", option_level);
2467            }
2468          if (option_level == 0)
2469            {
2470              stream_flags |= XD3_NOCOMPRESS;
2471              config.smatch_cfg = XD3_SMATCH_FASTEST;
2472            }
2473          else if (option_level == 1) { config.smatch_cfg = XD3_SMATCH_FASTEST; }
2474          else if (option_level <= 5) { config.smatch_cfg = XD3_SMATCH_FAST; }
2475          else if (option_level == 6) { config.smatch_cfg = XD3_SMATCH_DEFAULT; }
2476          else                        { config.smatch_cfg = XD3_SMATCH_SLOW; }
2477        }
2478      break;
2479#endif
2480    case CMD_DECODE:
2481      if (option_use_checksum == 0) { stream_flags |= XD3_ADLER32_NOVER; }
2482      stream_flags  = 0;
2483      ifile->flags |= RD_NONEXTERNAL;
2484      input_func    = xd3_decode_input;
2485      output_func   = main_write_output;
2486      break;
2487    default:
2488      XPR(NT "internal error\n");
2489      return EXIT_FAILURE;
2490    }
2491
2492  main_set_winsize (ifile);
2493
2494  if ((main_bdata = main_malloc (option_winsize)) == NULL)
2495    {
2496      return EXIT_FAILURE;
2497    }
2498
2499  if (IS_ENCODE (cmd))
2500    {
2501      /* When encoding, open the source file, possibly decompress it.  The decoder delays
2502       * this step until XD3_GOTHEADER. */
2503      if (sfile->filename != NULL && (ret = main_set_source (NULL, cmd, sfile, & source)))
2504        {
2505          return EXIT_FAILURE;
2506        }
2507    }
2508
2509  config.winsize = option_winsize;
2510  config.srcwin_maxsz = option_srcwinsz;
2511  config.getblk = main_getblk_func;
2512  config.flags = stream_flags;
2513
2514  if ((ret = xd3_config_stream (& stream, & config)))
2515    {
2516      XPR(NT XD3_LIB_ERRMSG (& stream, ret));
2517      return EXIT_FAILURE;
2518    }
2519
2520  if (IS_ENCODE (cmd) && sfile->filename != NULL &&
2521      (ret = xd3_set_source (& stream, & source)))
2522    {
2523      XPR(NT XD3_LIB_ERRMSG (& stream, ret));
2524      return EXIT_FAILURE;
2525    }
2526
2527  /* This times each window. */
2528  get_millisecs_since ();
2529
2530  /* Main input loop. */
2531  do
2532    {
2533      xoff_t input_offset;
2534      xoff_t input_remain;
2535      usize_t try_read;
2536
2537      input_offset = ifile->nread;
2538
2539      input_remain = XOFF_T_MAX - input_offset;
2540
2541      try_read = (usize_t) min ((xoff_t) config.winsize, input_remain);
2542
2543      if ((ret = main_read_primary_input (ifile, main_bdata, try_read, & nread)))
2544        {
2545          return EXIT_FAILURE;
2546        }
2547
2548      /* If we've reached EOF tell the stream to flush. */
2549      if (nread < try_read)
2550        {
2551          stream_flags |= XD3_FLUSH;
2552          xd3_set_flags (& stream, stream_flags);
2553        }
2554
2555#if XD3_ENCODER
2556      /* After the first main_read_primary_input completes, we know all the information
2557       * needed to encode the application header. */
2558      if (cmd == CMD_ENCODE && (ret = main_set_appheader (& stream, ifile, sfile)))
2559        {
2560          return EXIT_FAILURE;
2561        }
2562#endif
2563      xd3_avail_input (& stream, main_bdata, nread);
2564
2565      /* If we read zero bytes after encoding at least one window... */
2566      if (nread == 0 && stream.current_window > 0) {
2567        break;
2568      }
2569
2570    again:
2571      ret = input_func (& stream);
2572
2573      switch (ret)
2574        {
2575        case XD3_INPUT:
2576          continue;
2577
2578        case XD3_GOTHEADER:
2579          {
2580            XD3_ASSERT (stream.current_window == 0);
2581
2582            /* Need to process the appheader as soon as possible.  It may contain a
2583             * suggested default filename/decompression routine for the ofile, and it may
2584             * contain default/decompression routine for the sources. */
2585            if (cmd == CMD_DECODE)
2586              {
2587                int have_src = sfile->filename != NULL;
2588                int need_src = xd3_decoder_needs_source (& stream);
2589                int recv_src;
2590
2591                /* May need to set the sfile->filename if none was given. */
2592                main_get_appheader (& stream, ifile, ofile, sfile);
2593
2594                recv_src = sfile->filename != NULL;
2595
2596                /* Check if the user expected a source to be required although it was not. */
2597                if (have_src && ! need_src && ! option_quiet)
2598                  {
2599                    XPR(NT "warning: output window %"Q"u does not copy source\n", stream.current_window);
2600                  }
2601
2602                /* Check if we have no source name and need one. */
2603                /* TODO: this doesn't fire due to cpyblocks_ calculation check? */
2604                if (need_src && ! recv_src)
2605                  {
2606                    XPR(NT "input requires a source file, use -s\n");
2607                    return EXIT_FAILURE;
2608                  }
2609
2610                /* Now open the source file. */
2611                if (need_src && (ret = main_set_source (& stream, cmd, sfile, & source)))
2612                  {
2613                    return EXIT_FAILURE;
2614                  }
2615              }
2616            else if (cmd == CMD_PRINTHDR ||
2617                     cmd == CMD_PRINTHDRS ||
2618                     cmd == CMD_PRINTDELTA)
2619              {
2620                if (xd3_decoder_needs_source (& stream) && sfile->filename == NULL)
2621                  {
2622                    allow_fake_source = 1;
2623                    sfile->filename = "<placeholder>";
2624                    main_set_source (& stream, cmd, sfile, & source);
2625                  }
2626              }
2627          }
2628        /* FALLTHROUGH */
2629        case XD3_WINSTART:
2630          {
2631            /* e.g., set or unset XD3_SKIP_WINDOW. */
2632            /* xd3_set_flags (& stream, stream_flags); */
2633            goto again;
2634          }
2635
2636        case XD3_OUTPUT:
2637          {
2638            if (option_no_output == 0)
2639              {
2640                /* Defer opening the output file until the stream produces its first
2641                 * output for both encoder and decoder, this way we delay long enough for
2642                 * the decoder to receive the application header.  (Or longer if there are
2643                 * skipped windows, but I can't think of any reason not to delay open.) */
2644
2645                if (! main_file_isopen (ofile) && (ret = main_open_output (& stream, ofile)) != 0)
2646                  {
2647                    return EXIT_FAILURE;
2648                  }
2649                if ((ret = output_func (& stream, ofile)) && (ret != PRINTHDR_SPECIAL))
2650                  {
2651                    return EXIT_FAILURE;
2652                  }
2653                if (ret == PRINTHDR_SPECIAL)
2654                  {
2655                    xd3_abort_stream (& stream);
2656                    ret = EXIT_SUCCESS;
2657                    goto done;
2658                  }
2659                ret = 0;
2660              }
2661
2662            xd3_consume_output (& stream);
2663            goto again;
2664          }
2665
2666        case XD3_WINFINISH:
2667          {
2668            if (IS_ENCODE (cmd) || cmd == CMD_DECODE)
2669              {
2670                if (! option_quiet && IS_ENCODE (cmd) && main_file_isopen (sfile))
2671                  {
2672                    /* Warn when no source copies are found */
2673                    if (! xd3_encoder_used_source (& stream))
2674                      {
2675                        XPR(NT "warning: input window %"Q"u..%"Q"u has no source copies\n",
2676                            stream.current_window * option_winsize,
2677                            (stream.current_window+1) * option_winsize);
2678                      }
2679                   
2680                    /* Limited instruction buffer size affects source copies */
2681                    if (option_verbose > 1 && stream.i_slots_used > stream.iopt_size)
2682                      {
2683                        XPR(NT "warning: input position %"Q"u overflowed instruction buffer, "
2684                            "needed %u (vs. %u), consider raising -I\n",
2685                            stream.current_window * option_winsize,
2686                            stream.i_slots_used, stream.iopt_size);
2687                      }
2688                  }
2689
2690                if (option_verbose)
2691                  {
2692                    char rrateavg[32], wrateavg[32], tm[32];
2693                    char rdb[32], wdb[32];
2694                    char trdb[32], twdb[32];
2695                    long millis = get_millisecs_since ();
2696                    usize_t this_read = (usize_t)(stream.total_in - last_total_in);
2697                    usize_t this_write = (usize_t)(stream.total_out - last_total_out);
2698                    last_total_in = stream.total_in;
2699                    last_total_out = stream.total_out;
2700
2701                    if (option_verbose > 1)
2702                      {
2703                        XPR(NT "%"Q"u: in %s (%s): out %s (%s): total in %s: out %s: %s\n",
2704                            stream.current_window,
2705                            main_format_bcnt (this_read, rdb),
2706                            main_format_rate (this_read, millis, rrateavg),
2707                            main_format_bcnt (this_write, wdb),
2708                            main_format_rate (this_write, millis, wrateavg),
2709                            main_format_bcnt (stream.total_in, trdb),
2710                            main_format_bcnt (stream.total_out, twdb),
2711                            main_format_millis (millis, tm));
2712                      }
2713                    else
2714                      {
2715                        XPR(NT "%"Q"u: in %s: out %s: total in %s: out %s: %s\n",
2716                            stream.current_window,
2717                            main_format_bcnt (this_read, rdb),
2718                            main_format_bcnt (this_write, wdb),
2719                            main_format_bcnt (stream.total_in, trdb),
2720                            main_format_bcnt (stream.total_out, twdb),
2721                            main_format_millis (millis, tm));
2722                      }
2723                  }
2724              }
2725            goto again;
2726          }
2727
2728        default:
2729          /* input_func() error */
2730          XPR(NT XD3_LIB_ERRMSG (& stream, ret));
2731          return EXIT_FAILURE;
2732        }
2733    }
2734  while (nread == config.winsize);
2735done:
2736  /* Close the inputs. (ifile must be open, sfile may be open) */
2737  main_file_close (ifile);
2738  main_file_close (sfile);
2739
2740  /* If output file is not open yet because of delayed-open, it means we never encountered
2741   * a window in the delta, but it could have had a VCDIFF header?  TODO: solve this
2742   * elsewhere.  For now, it prints "nothing to output" below, but the check doesn't
2743   * happen in case of option_no_output.  */
2744  if (! option_no_output)
2745    {
2746      if (!stdout_only && ! main_file_isopen (ofile))
2747        {
2748          XPR(NT "nothing to output: %s\n", ifile->filename);
2749          return EXIT_FAILURE;
2750        }
2751
2752      /* Have to close the output before calling main_external_compression_finish, or else it hangs. */
2753      if (main_file_close (ofile) != 0)
2754        {
2755          return EXIT_FAILURE;
2756        }
2757    }
2758
2759#if EXTERNAL_COMPRESSION
2760  if ((ret = main_external_compression_finish ()))
2761    {
2762      XPR(NT "external compression commands failed\n");
2763      return EXIT_FAILURE;
2764    }
2765#endif
2766
2767  if ((ret = xd3_close_stream (& stream)))
2768    {
2769      XPR(NT XD3_LIB_ERRMSG (& stream, ret));
2770      return EXIT_FAILURE;
2771    }
2772
2773  if (option_verbose > 1)
2774    {
2775      XPR(NT "scanner configuration: %s\n", stream.smatcher.name);
2776      XPR(NT "target hash table size: %u\n", stream.small_hash.size);
2777      if (sfile->filename != NULL)
2778        {
2779          XPR(NT "source hash table size: %u\n", stream.large_hash.size);
2780        }
2781    }
2782
2783  if (option_verbose > 2)
2784    {
2785      XPR(NT "source copies: %"Q"u (%"Q"u bytes)\n", stream.n_scpy, stream.l_scpy);
2786      XPR(NT "target copies: %"Q"u (%"Q"u bytes)\n", stream.n_tcpy, stream.l_tcpy);
2787      XPR(NT "adds: %"Q"u (%"Q"u bytes)\n", stream.n_add, stream.l_add);
2788      XPR(NT "runs: %"Q"u (%"Q"u bytes)\n", stream.n_run, stream.l_run);
2789    }
2790
2791  xd3_free_stream (& stream);
2792
2793  if (option_verbose)
2794    {
2795      char tm[32];
2796      long end_time = get_millisecs_now ();
2797      XPR(NT "finished in %s; input %"Q"u output %"Q"u bytes  (%0.2f%%)\n",
2798          main_format_millis (end_time - start_time, tm),
2799          ifile->nread, ofile->nwrite,
2800          100.0 * ofile->nwrite / ifile->nread);
2801    }
2802
2803  return EXIT_SUCCESS;
2804}
2805
2806/* free memory before exit, reset single-use variables. */
2807static void
2808main_cleanup (void)
2809{
2810  int i;
2811
2812  if (appheader_used != NULL &&
2813      appheader_used != option_appheader)
2814    {
2815      main_free (appheader_used);
2816      appheader_used = NULL;
2817    }
2818
2819  main_free (main_bdata);
2820  main_bdata = NULL;
2821
2822#if EXTERNAL_COMPRESSION
2823  main_free (ext_tmpfile);
2824  ext_tmpfile = NULL;
2825#endif
2826
2827  for (i = 0; lru && i < lru_size; i += 1)
2828    {
2829      main_free (lru[i].blk);
2830    }
2831
2832  main_free (lru);
2833  lru = NULL;
2834
2835  lru_hits = 0;
2836  lru_misses = 0;
2837  lru_filled = 0;
2838
2839  XD3_ASSERT (main_mallocs == 0);
2840}
2841
2842static void
2843setup_environment (int argc,
2844                   char **argv,
2845                   int *argc_out,
2846                   char ***argv_out,
2847                   char ***argv_free,
2848                   char **env_free)
2849{
2850  int n, i, i0;
2851  char *p, *v = getenv("XDELTA");
2852  if (v == NULL) {
2853    (*argc_out) = argc;
2854    (*argv_out) = argv;
2855    (*argv_free) = NULL;
2856    (*env_free) = NULL;
2857    return;
2858  }
2859
2860  (*env_free) = main_malloc(strlen(v) + 1);
2861  strcpy(*env_free, v);
2862
2863  /* Space needed for extra args, at least # of spaces */
2864  n = argc + 1;
2865  for (p = *env_free; *p != 0; ) {
2866    if (*p++ == ' ') {
2867      n++;
2868    }
2869  }
2870
2871  (*argv_free) = main_malloc(sizeof(char*) * (n + 1));
2872  (*argv_out) = (*argv_free);
2873  (*argv_out)[0] = argv[0];
2874  (*argv_out)[n] = NULL;
2875
2876  i = 1;
2877  for (p = *env_free; *p != 0; ) {
2878    (*argv_out)[i++] = p;
2879    while (*p != ' ' && *p != 0) {
2880      p++;
2881    }
2882    while (*p == ' ') {
2883      *p++ = 0;
2884    }
2885  }
2886
2887  for (i0 = 1; i0 < argc; i0++) {
2888    (*argv_out)[i++] = argv[i0];
2889  }
2890
2891  /* Counting spaces is an upper bound, argv stays NULL terminated. */
2892  (*argc_out) = i;
2893  while (i <= n) {
2894    (*argv_out)[i++] = NULL;
2895  }
2896}
2897
2898int
2899#if PYTHON_MODULE || SWIG_MODULE
2900xd3_main_cmdline (int argc, char **argv)
2901#else
2902main (int argc, char **argv)
2903#endif
2904{
2905  xd3_cmd cmd;
2906  main_file ifile;
2907  main_file ofile;
2908  main_file sfile;
2909  static const char *flags = "0123456789cdefhnqvDJNORTVs:B:C:E:F:I:L:O:M:P:W:A::S::";
2910  int my_optind;
2911  char *my_optarg;
2912  char *my_optstr;
2913  char *sfilename;
2914  int env_argc;
2915  char **env_argv;
2916  char **free_argv;  /* malloc() in setup_environment() */
2917  char *free_value;  /* malloc() in setup_environment() */
2918  int ret;
2919
2920#ifdef _WIN32
2921  GetStartupInfo(&winStartupInfo);
2922  setvbuf(stderr, NULL, _IONBF, 0);  /* Do not buffer stderr */
2923#endif
2924
2925  main_file_init (& ifile);
2926  main_file_init (& ofile);
2927  main_file_init (& sfile);
2928
2929  reset_defaults();
2930
2931  free_argv = NULL;
2932  free_value = NULL;
2933  setup_environment(argc, argv, &env_argc, &env_argv, &free_argv, &free_value);
2934  cmd = CMD_NONE;
2935  sfilename = NULL;
2936  my_optind = 1;
2937  argv = env_argv;
2938  argc = env_argc;
2939  program_name = env_argv[0];
2940  extcomp_types[0].recomp_cmdname = program_name;
2941  extcomp_types[0].decomp_cmdname = program_name;
2942 takearg:
2943  my_optarg = NULL;
2944  my_optstr = argv[my_optind];
2945  /* This doesn't use getopt() because it makes trouble for -P & python which reenter
2946   * main() and thus care about freeing all memory.  I never had much trust for getopt
2947   * anyway, it's too opaque.  This implements a fairly standard non-long-option getopt
2948   * with support for named operations (e.g., "xdelta3 [encode|decode|printhdr...] < in >
2949   * out"). */
2950  if (my_optstr)
2951    {
2952      if (*my_optstr == '-')    { my_optstr += 1; }
2953      else if (cmd == CMD_NONE) { goto nonflag; }
2954      else                      { my_optstr = NULL; }
2955    }
2956  while (my_optstr)
2957    {
2958      char *s;
2959      my_optarg = NULL;
2960      if ((ret = *my_optstr++) == 0) { my_optind += 1; goto takearg; }
2961
2962      /* Option handling: first check for one ':' following the option in flags, then
2963       * check for two.  The syntax allows:
2964       *
2965       * 1. -Afoo                   defines optarg="foo"
2966       * 2. -A foo                  defines optarg="foo"
2967       * 3. -A ""                   defines optarg="" (allows optional empty-string)
2968       * 4. -A [EOA or -moreargs]   error (mandatory case)
2969       * 5. -A [EOA -moreargs]      defines optarg=NULL (optional case)
2970       * 6. -A=foo                  defines optarg="foo"
2971       * 7. -A=                     defines optarg="" (mandatory case)
2972       * 8. -A=                     defines optarg=NULL (optional case)
2973       *
2974       * See tests in test_command_line_arguments().
2975       */
2976      s = strchr (flags, ret);
2977      if (s && s[1] && s[1] == ':')
2978        {
2979          int eqcase = 0;
2980          int option = s[2] && s[2] == ':';
2981
2982          /* Case 1, set optarg to the remaining characters. */
2983          my_optarg = my_optstr;
2984          my_optstr = "";
2985
2986          /* Case 2-5 */
2987          if (*my_optarg == 0)
2988            {
2989              /* Condition 4-5 */
2990              int have_arg = my_optind < (argc - 1) && *argv[my_optind+1] != '-';
2991
2992              if (! have_arg)
2993                {
2994                  if (! option)
2995                  {
2996                    /* Case 4 */
2997                    XPR(NT "-%c: requires an argument\n", ret);
2998                    ret = EXIT_FAILURE;
2999                    goto cleanup;
3000                  }
3001                  /* Case 5. */
3002                  my_optarg = NULL;
3003                }
3004              else
3005                {
3006                  /* Case 2-3. */
3007                  my_optarg = argv[++my_optind];
3008                }
3009            }
3010          /* Case 6-8. */
3011          else if (*my_optarg == '=')
3012            {
3013              /* Remove the = in all cases. */
3014              my_optarg += 1;
3015              eqcase = 1;
3016
3017              if (option && *my_optarg == 0)
3018                {
3019                  /* Case 8. */
3020                  my_optarg = NULL;
3021                }
3022            }
3023        }
3024
3025      switch (ret)
3026        {
3027        /* case: if no '-' was found, maybe check for a command name. */
3028        nonflag:
3029               if (strcmp (my_optstr, "decode") == 0) { cmd = CMD_DECODE; }
3030          else if (strcmp (my_optstr, "encode") == 0)
3031            {
3032#if XD3_ENCODER
3033              cmd = CMD_ENCODE;
3034#else
3035              XPR(NT "encoder support not compiled\n");
3036              return EXIT_FAILURE;
3037#endif
3038            }
3039          else if (strcmp (my_optstr, "config") == 0) { cmd = CMD_CONFIG; }
3040#if REGRESSION_TEST
3041          else if (strcmp (my_optstr, "test") == 0) { cmd = CMD_TEST; }
3042#endif
3043#if VCDIFF_TOOLS
3044          else if (strcmp (my_optstr, "printhdr") == 0) { cmd = CMD_PRINTHDR; }
3045          else if (strcmp (my_optstr, "printhdrs") == 0) { cmd = CMD_PRINTHDRS; }
3046          else if (strcmp (my_optstr, "printdelta") == 0) { cmd = CMD_PRINTDELTA; }
3047#endif
3048
3049          /* If no option was found and still no command, let the default command be
3050           * encode.  The remaining args are treated as filenames. */
3051          if (cmd == CMD_NONE)
3052            {
3053              cmd = CMD_DEFAULT;
3054              my_optstr = NULL;
3055              break;
3056            }
3057          else
3058            {
3059              /* But if we find a command name, continue the getopt loop. */
3060              my_optind += 1;
3061              goto takearg;
3062            }
3063
3064          /* gzip-like options */
3065        case '0': case '1': case '2': case '3': case '4':
3066        case '5': case '6': case '7': case '8': case '9':
3067          option_level = ret - '0';
3068          break;
3069        case 'f': option_force = 1; break;
3070        case 'v': option_verbose += 1; option_quiet = 0; break;
3071        case 'q': option_quiet = 1; option_verbose = 0; break;
3072        case 'c': option_stdout = 1; break;
3073        case 'd':
3074          if (cmd == CMD_NONE) { cmd = CMD_DECODE; }
3075          else { ret = main_help (); goto exit; }
3076          break;
3077        case 'e':
3078#if XD3_ENCODER
3079          if (cmd == CMD_NONE) { cmd = CMD_ENCODE; }
3080          else { ret = main_help (); goto exit; }
3081          break;
3082#else
3083          XPR(NT "encoder support not compiled\n");
3084          return EXIT_FAILURE;
3085#endif
3086
3087        case 'n': option_use_checksum = 0; break;
3088        case 'N': option_no_compress = 1; break;
3089        case 'T': option_use_altcodetable = 1; break;
3090        case 'C': option_smatch_config = my_optarg; break;
3091        case 'J': option_no_output = 1; break;
3092        case 'S': if (my_optarg == NULL) { option_use_secondary = 0; }
3093                  else { option_use_secondary = 1; option_secondary = my_optarg; } break;
3094        case 'A': if (my_optarg == NULL) { option_use_appheader = 0; }
3095                  else { option_appheader = (uint8_t*) my_optarg; } break;
3096        case 'B':
3097          if ((ret = main_atou (my_optarg, & option_srcwinsz, XD3_MINSRCWINSZ,
3098                                0, 'B')))
3099            {
3100              goto exit;
3101            }
3102          break;
3103        case 'I':
3104          if ((ret = main_atou (my_optarg, & option_iopt_size, 0,
3105                                0, 'I')))
3106            {
3107              goto exit;
3108            }
3109          break;
3110        case 'P':
3111          if ((ret = main_atou (my_optarg, & option_sprevsz, 0,
3112                                0, 'P')))
3113            {
3114              goto exit;
3115            }
3116          break;
3117        case 'W':
3118          if ((ret = main_atou (my_optarg, & option_winsize, XD3_ALLOCSIZE,
3119                                XD3_HARDMAXWINSIZE, 'W')))
3120          {
3121            goto exit;
3122          }
3123          break;
3124        case 'D':
3125#if EXTERNAL_COMPRESSION == 0
3126          if (option_verbose > 0)
3127            {
3128              XPR(NT "warning: -D option ignored, "
3129                       "external compression support was not compiled\n");
3130            }
3131#else
3132          option_decompress_inputs  = 0;
3133#endif
3134          break;
3135        case 'R':
3136#if EXTERNAL_COMPRESSION == 0
3137          if (option_verbose > 0)
3138            {
3139              XPR(NT "warning: -R option ignored, "
3140                       "external compression support was not compiled\n");
3141            }
3142#else
3143          option_recompress_outputs = 0;
3144#endif
3145          break;
3146        case 's':
3147          if (sfilename != NULL)
3148            {
3149              XPR(NT "specify only one source file\n");
3150              goto cleanup;
3151            }
3152
3153          sfilename = my_optarg;
3154          break;
3155
3156        case 'V':
3157          ret = main_version (); goto exit;
3158        default:
3159          ret = main_help (); goto exit;
3160        }
3161    }
3162
3163  option_source_filename = sfilename;
3164
3165  /* In case there were no arguments, set the default command. */
3166  if (cmd == CMD_NONE) { cmd = CMD_DEFAULT; }
3167
3168  argc -= my_optind;
3169  argv += my_optind;
3170
3171  /* There may be up to two more arguments. */
3172  if (argc > 2)
3173    {
3174      XPR(NT "too many filenames: %s ...\n", argv[2]);
3175      ret = EXIT_FAILURE;
3176      goto cleanup;
3177    }
3178
3179  ifile.flags    = RD_FIRST;
3180  sfile.flags    = RD_FIRST;
3181  sfile.filename = option_source_filename;
3182
3183  /* The infile takes the next argument, if there is one.  But if not, infile is set to
3184   * stdin. */
3185  if (argc > 0)
3186    {
3187      ifile.filename = argv[0];
3188
3189      if ((ret = main_file_open (& ifile, ifile.filename, XO_READ)))
3190        {
3191          goto cleanup;
3192        }
3193    }
3194  else
3195    {
3196      XSTDIN_XF (& ifile);
3197    }
3198
3199  /* The ofile takes the following argument, if there is one.  But if not, it is left NULL
3200   * until the application header is processed.  It will be set in main_open_output. */
3201  if (argc > 1)
3202    {
3203      /* Check for conflicting arguments. */
3204      if (option_stdout && ! option_quiet)
3205        {
3206          XPR(NT "warning: -c option overrides output filename: %s\n", argv[1]);
3207        }
3208
3209      if (! option_stdout) { ofile.filename = argv[1]; }
3210    }
3211
3212  switch (cmd)
3213    {
3214    case CMD_PRINTHDR:
3215    case CMD_PRINTHDRS:
3216    case CMD_PRINTDELTA:
3217#if XD3_ENCODER
3218    case CMD_ENCODE:
3219#endif
3220    case CMD_DECODE:
3221      ret = main_input (cmd, & ifile, & ofile, & sfile);
3222      break;
3223
3224#if REGRESSION_TEST
3225    case CMD_TEST:
3226      ret = xd3_selftest ();
3227      break;
3228#endif
3229
3230    case CMD_CONFIG:
3231      ret = main_config ();
3232      break;
3233
3234    default:
3235      ret = main_help ();
3236      break;
3237    }
3238
3239#if EXTERNAL_COMPRESSION
3240  if (ext_tmpfile != NULL)
3241    {
3242      unlink (ext_tmpfile);
3243    }
3244#endif
3245
3246  if (0)
3247    {
3248    cleanup:
3249      ret = EXIT_FAILURE;
3250    exit:
3251      (void)0;
3252    }
3253
3254  main_file_cleanup (& ifile);
3255  main_file_cleanup (& ofile);
3256  main_file_cleanup (& sfile);
3257
3258  main_free (free_argv);
3259  main_free (free_value);
3260
3261  main_cleanup ();
3262
3263  fflush (stdout);
3264  fflush (stderr);
3265  return ret;
3266}
3267
3268static int
3269main_help (void)
3270{
3271  /* Note: update wiki when command-line features change */
3272  main_version ();
3273  DP(RINT "usage: xdelta3 [command/options] [input [output]]\n");
3274  DP(RINT "special command names:\n");
3275  DP(RINT "    config      prints xdelta3 configuration\n");
3276  DP(RINT "    decode      decompress the input\n");
3277  DP(RINT "    encode      compress the input%s\n", XD3_ENCODER ? "" : " [Not compiled]");
3278#if REGRESSION_TEST
3279  DP(RINT "    test        run the builtin tests\n");
3280#endif
3281#if VCDIFF_TOOLS
3282  DP(RINT "special commands for VCDIFF inputs:\n");
3283  DP(RINT "    printdelta  print information about the entire delta\n");
3284  DP(RINT "    printhdr    print information about the first window\n");
3285  DP(RINT "    printhdrs   print information about all windows\n");
3286#endif
3287  DP(RINT "standard options:\n");
3288  DP(RINT "   -0 .. -9     compression level\n");
3289  DP(RINT "   -c           use stdout\n");
3290  DP(RINT "   -d           decompress\n");
3291  DP(RINT "   -e           compress%s\n", XD3_ENCODER ? "" : " [Not compiled]");
3292  DP(RINT "   -f           force overwrite\n");
3293  DP(RINT "   -h           show help\n");
3294  DP(RINT "   -q           be quiet\n");
3295  DP(RINT "   -v           be verbose (max 2)\n");
3296  DP(RINT "   -V           show version\n");
3297
3298  DP(RINT "memory options:\n");
3299  DP(RINT "   -B bytes     source window size\n");
3300  DP(RINT "   -W bytes     input window size\n");
3301  DP(RINT "   -P size      compression duplicates window\n");
3302  DP(RINT "   -I size      instruction buffer size (0 = unlimited)\n");
3303
3304  DP(RINT "compression options:\n");
3305  DP(RINT "   -s source    source file to copy from (if any)\n");
3306  DP(RINT "   -S [djw|fgk] enable/disable secondary compression\n");
3307  DP(RINT "   -N           disable small string-matching compression\n");
3308  DP(RINT "   -D           disable external decompression (encode/decode)\n");
3309  DP(RINT "   -R           disable external recompression (decode)\n");
3310  DP(RINT "   -n           disable checksum (encode/decode)\n");
3311  DP(RINT "   -C           soft config (encode, undocumented)\n");
3312  DP(RINT "   -A [apphead] disable/provide application header (encode)\n");
3313
3314#if XD3_DEBUG > 0
3315  DP(RINT "developer options:\n");
3316  DP(RINT "   -J           disable output (check/compute only)\n");
3317  DP(RINT "   -P           repeat count (for profiling)\n");
3318  DP(RINT "   -T           use alternate code table\n");
3319#endif
3320
3321  DP(RINT "the XDELTA environment variable may contain extra args:\n");
3322  DP(RINT "   XDELTA=\"-s source-x.y.tar.gz\" \\\n");
3323  DP(RINT "   tar --use-compress-program=xdelta3 \\\n");
3324  DP(RINT "       -cf target-x.z.tar.gz.vcdiff target-x.y/\n");
3325  return EXIT_FAILURE;
3326}
3327
Note: See TracBrowser for help on using the repository browser.