| 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() */ | 
 
 
 
 
 | 67 | const 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 | 
 
 
 
 
 | 124 | static 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. */ | 
 
 
 
 
 | 141 | typedef 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 */ | 
 
 
 
 
 | 149 | typedef 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. */ | 
 
 
 
 
 | 156 | typedef 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 |  | 
 
 
 
 
 | 178 | typedef struct _main_file        main_file; | 
 
 
 
 
 | 179 | typedef struct _main_extcomp     main_extcomp; | 
 
 
 
 
 | 180 | typedef struct _main_blklru      main_blklru; | 
 
 
 
 
 | 181 | typedef 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 | */ | 
 
 
 
 
 | 188 | struct _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. */ | 
 
 
 
 
 | 211 | struct _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). */ | 
 
 
 
 
 | 228 | struct _main_blklru_list | 
 
 
 
 
 | 229 | { | 
 
 
 
 
 | 230 | main_blklru_list  *next; | 
 
 
 
 
 | 231 | main_blklru_list  *prev; | 
 
 
 
 
 | 232 | }; | 
 
 
 
 
 | 233 |  | 
 
 
 
 
 | 234 | struct _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). */ | 
 
 
 
 
 | 245 | XD3_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. */ | 
 
 
 
 
 | 252 | static int         option_stdout             = 0; | 
 
 
 
 
 | 253 | static int         option_force              = 0; | 
 
 
 
 
 | 254 | static int         option_verbose            = 0; | 
 
 
 
 
 | 255 | static int         option_quiet              = 0; | 
 
 
 
 
 | 256 | static int         option_use_appheader      = 1; | 
 
 
 
 
 | 257 | static uint8_t*    option_appheader          = NULL; | 
 
 
 
 
 | 258 | static int         option_use_secondary      = 0; | 
 
 
 
 
 | 259 | static char*       option_secondary          = NULL; | 
 
 
 
 
 | 260 | static int         option_use_checksum       = 1; | 
 
 
 
 
 | 261 | static int         option_use_altcodetable   = 0; | 
 
 
 
 
 | 262 | static char*       option_smatch_config      = NULL; | 
 
 
 
 
 | 263 | static int         option_no_compress        = 0; | 
 
 
 
 
 | 264 | static int         option_no_output          = 0; /* do not open or write output */ | 
 
 
 
 
 | 265 | static const char *option_source_filename    = NULL; | 
 
 
 
 
 | 266 |  | 
 
 
 
 
 | 267 | static int         option_level              = XD3_DEFAULT_LEVEL; | 
 
 
 
 
 | 268 | static usize_t     option_iopt_size          = XD3_DEFAULT_IOPT_SIZE; | 
 
 
 
 
 | 269 | static usize_t     option_winsize            = XD3_DEFAULT_WINSIZE; | 
 
 
 
 
 | 270 | static usize_t     option_srcwinsz           = XD3_DEFAULT_SRCWINSZ; | 
 
 
 
 
 | 271 | static 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 | 
 
 
 
 
 | 276 | static int         option_decompress_inputs  = 1; | 
 
 
 
 
 | 277 | static 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 | 
 
 
 
 
 | 283 | static int         option_print_cpymode = 1; | 
 
 
 
 
 | 284 | #endif | 
 
 
 
 
 | 285 |  | 
 
 
 
 
 | 286 | /* Static variables */ | 
 
 
 
 
 | 287 | IF_DEBUG(static int main_mallocs = 0;) | 
 
 
 
 
 | 288 |  | 
 
 
 
 
 | 289 | static char*          program_name = NULL; | 
 
 
 
 
 | 290 | static uint8_t*       appheader_used = NULL; | 
 
 
 
 
 | 291 | static uint8_t*       main_bdata = NULL; | 
 
 
 
 
 | 292 |  | 
 
 
 
 
 | 293 | /* The LRU: obviously this is shared by all callers. */ | 
 
 
 
 
 | 294 | static int               lru_size = 0; | 
 
 
 
 
 | 295 | static main_blklru      *lru = NULL;  /* array of lru_size elts */ | 
 
 
 
 
 | 296 | static main_blklru_list  lru_list; | 
 
 
 
 
 | 297 | static main_blklru_list  lru_free; | 
 
 
 
 
 | 298 | static int               do_not_lru = 0;  /* set to avoid lru, instead discard oldest */ | 
 
 
 
 
 | 299 |  | 
 
 
 
 
 | 300 | static int lru_hits   = 0; | 
 
 
 
 
 | 301 | static int lru_misses = 0; | 
 
 
 
 
 | 302 | static int lru_filled = 0; | 
 
 
 
 
 | 303 |  | 
 
 
 
 
 | 304 | /* Hacks for VCDIFF tools */ | 
 
 
 
 
 | 305 | static 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. */ | 
 
 
 
 
 | 309 | static 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 |  | 
 
 
 
 
 | 323 | static void main_get_appheader (xd3_stream *stream, main_file *ifile, | 
 
 
 
 
 | 324 | main_file *output, main_file *sfile); | 
 
 
 
 
 | 325 |  | 
 
 
 
 
 | 326 | static int main_help (void); | 
 
 
 
 
 | 327 |  | 
 
 
 
 
 | 328 | static int | 
 
 
 
 
 | 329 | main_version (void) | 
 
 
 
 
 | 330 | { | 
 
 
 
 
 | 331 | /* $Format: "  DP(RINT \"VERSION=3.$Xdelta3Version$\\n\");" $ */ | 
 
 
 
 
 | 332 | DP(RINT "VERSION=3.0q\n"); | 
 
 
 
 
 | 333 | return EXIT_SUCCESS; | 
 
 
 
 
 | 334 | } | 
 
 
 
 
 | 335 |  | 
 
 
 
 
 | 336 | static int | 
 
 
 
 
 | 337 | main_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 |  | 
 
 
 
 
 | 366 | static void | 
 
 
 
 
 | 367 | reset_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 |  | 
 
 
 
 
 | 409 | static void* | 
 
 
 
 
 | 410 | main_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 |  | 
 
 
 
 
 | 418 | static void* | 
 
 
 
 
 | 419 | main_malloc (usize_t size) | 
 
 
 
 
 | 420 | { | 
 
 
 
 
 | 421 | void *r = main_malloc1 (size); | 
 
 
 
 
 | 422 | if (r) { IF_DEBUG (main_mallocs += 1); } | 
 
 
 
 
 | 423 | return r; | 
 
 
 
 
 | 424 | } | 
 
 
 
 
 | 425 |  | 
 
 
 
 
 | 426 | static void* | 
 
 
 
 
 | 427 | main_alloc (void   *opaque, | 
 
 
 
 
 | 428 | usize_t  items, | 
 
 
 
 
 | 429 | usize_t  size) | 
 
 
 
 
 | 430 | { | 
 
 
 
 
 | 431 | return main_malloc1 (items * size); | 
 
 
 
 
 | 432 | } | 
 
 
 
 
 | 433 |  | 
 
 
 
 
 | 434 | static void | 
 
 
 
 
 | 435 | main_free1 (void *opaque, void *ptr) | 
 
 
 
 
 | 436 | { | 
 
 
 
 
 | 437 | if (option_verbose > 3) { XPR(NT "free: %p\n", ptr); } | 
 
 
 
 
 | 438 | free (ptr); | 
 
 
 
 
 | 439 | } | 
 
 
 
 
 | 440 |  | 
 
 
 
 
 | 441 | static void | 
 
 
 
 
 | 442 | main_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. */ | 
 
 
 
 
 | 454 | static int | 
 
 
 
 
 | 455 | get_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 |  | 
 
 
 
 
 | 473 | const char* | 
 
 
 
 
 | 474 | xd3_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 |  | 
 
 
 
 
 | 496 | static long | 
 
 
 
 
 | 497 | get_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? */ | 
 
 
 
 
 | 520 | static long | 
 
 
 
 
 | 521 | get_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 |  | 
 
 
 
 
 | 530 | static char* | 
 
 
 
 
 | 531 | main_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 |  | 
 
 
 
 
 | 548 | static char* | 
 
 
 
 
 | 549 | main_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 |  | 
 
 
 
 
 | 559 | static char* | 
 
 
 
 
 | 560 | main_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. */ | 
 
 
 
 
 | 569 | static int | 
 
 
 
 
 | 570 | main_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 |  | 
 
 
 
 
 | 604 | static int | 
 
 
 
 
 | 605 | main_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 |  | 
 
 
 
 
 | 662 | static void | 
 
 
 
 
 | 663 | main_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 |  | 
 
 
 
 
 | 675 | static int | 
 
 
 
 
 | 676 | main_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 |  | 
 
 
 
 
 | 689 | static int | 
 
 
 
 
 | 690 | main_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 |  | 
 
 
 
 
 | 718 | static void | 
 
 
 
 
 | 719 | main_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 |  | 
 
 
 
 
 | 739 | static int | 
 
 
 
 
 | 740 | main_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 |  | 
 
 
 
 
 | 787 | static int | 
 
 
 
 
 | 788 | main_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 |  | 
 
 
 
 
 | 817 | static int | 
 
 
 
 
 | 818 | main_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. */ | 
 
 
 
 
 | 829 | typedef int (xd3_posix_func) (int fd, uint8_t *buf, usize_t size); | 
 
 
 
 
 | 830 |  | 
 
 
 
 
 | 831 | static int | 
 
 
 
 
 | 832 | xd3_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. */ | 
 
 
 
 
 | 862 | static int | 
 
 
 
 
 | 863 | main_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 |  | 
 
 
 
 
 | 910 | static int | 
 
 
 
 
 | 911 | main_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 |  | 
 
 
 
 
 | 950 | static int | 
 
 
 
 
 | 951 | main_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> | 
 
 
 
 
 | 1002 | int | 
 
 
 
 
 | 1003 | snprintf (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 |  | 
 
 
 
 
 | 1014 | static int | 
 
 
 
 
 | 1015 | main_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. */ | 
 
 
 
 
 | 1022 | static int | 
 
 
 
 
 | 1023 | main_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 |  | 
 
 
 
 
 | 1090 | static int | 
 
 
 
 
 | 1091 | main_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. */ | 
 
 
 
 
 | 1106 | static int | 
 
 
 
 
 | 1107 | main_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 |  | 
 
 
 
 
 | 1251 | static pid_t ext_subprocs[2]; | 
 
 
 
 
 | 1252 | static char* ext_tmpfile = NULL; | 
 
 
 
 
 | 1253 |  | 
 
 
 
 
 | 1254 | /* Like write(), but makes repeated calls to empty the buffer. */ | 
 
 
 
 
 | 1255 | static int | 
 
 
 
 
 | 1256 | main_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. */ | 
 
 
 
 
 | 1270 | static int | 
 
 
 
 
 | 1271 | main_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. */ | 
 
 
 
 
 | 1297 | static int | 
 
 
 
 
 | 1298 | main_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. */ | 
 
 
 
 
 | 1320 | static int | 
 
 
 
 
 | 1321 | main_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. */ | 
 
 
 
 
 | 1354 | static int | 
 
 
 
 
 | 1355 | main_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. */ | 
 
 
 
 
 | 1480 | static int | 
 
 
 
 
 | 1481 | main_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. */ | 
 
 
 
 
 | 1540 | static int | 
 
 
 
 
 | 1541 | main_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. */ | 
 
 
 
 
 | 1637 | static int | 
 
 
 
 
 | 1638 | main_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. */ | 
 
 
 
 
 | 1716 | static const main_extcomp* | 
 
 
 
 
 | 1717 | main_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. */ | 
 
 
 
 
 | 1733 | static const main_extcomp* | 
 
 
 
 
 | 1734 | main_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 | 
 
 
 
 
 | 1767 | static const char* | 
 
 
 
 
 | 1768 | main_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 |  | 
 
 
 
 
 | 1782 | static int | 
 
 
 
 
 | 1783 | main_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 |  | 
 
 
 
 
 | 1838 | static void | 
 
 
 
 
 | 1839 | main_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 |  | 
 
 
 
 
 | 1884 | static void | 
 
 
 
 
 | 1885 | main_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. */ | 
 
 
 
 
 | 1942 | static int | 
 
 
 
 
 | 1943 | main_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()). */ | 
 
 
 
 
 | 1962 | static int | 
 
 
 
 
 | 1963 | main_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. */ | 
 
 
 
 
 | 1977 | static int | 
 
 
 
 
 | 1978 | main_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. */ | 
 
 
 
 
 | 2030 | static int | 
 
 
 
 
 | 2031 | main_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 |  | 
 
 
 
 
 | 2196 | static void | 
 
 
 
 
 | 2197 | main_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. */ | 
 
 
 
 
 | 2224 | static int | 
 
 
 
 
 | 2225 | main_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 | */ | 
 
 
 
 
 | 2349 | static int | 
 
 
 
 
 | 2350 | main_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); | 
 
 
 
 
 | 2735 | done: | 
 
 
 
 
 | 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. */ | 
 
 
 
 
 | 2807 | static void | 
 
 
 
 
 | 2808 | main_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 |  | 
 
 
 
 
 | 2842 | static void | 
 
 
 
 
 | 2843 | setup_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 |  | 
 
 
 
 
 | 2898 | int | 
 
 
 
 
 | 2899 | #if PYTHON_MODULE || SWIG_MODULE | 
 
 
 
 
 | 2900 | xd3_main_cmdline (int argc, char **argv) | 
 
 
 
 
 | 2901 | #else | 
 
 
 
 
 | 2902 | main (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 |  | 
 
 
 
 
 | 3268 | static int | 
 
 
 
 
 | 3269 | main_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 |  |