[185] | 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 |
|
---|