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