diff options
Diffstat (limited to 'xdelta3/xdelta3-main.h')
-rwxr-xr-x | xdelta3/xdelta3-main.h | 5896 |
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 */ |
115 | typedef 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. |
123 | typedef 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 |
130 | typedef enum | 130 | * run to supply decompressed source and target inputs to the Xdelta encoder. */ |
131 | { | 131 | typedef 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, | 139 | typedef 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) | 146 | typedef 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, | |
152 | typedef struct _main_file main_file; | 152 | #if XD3_ENCODER |
153 | typedef struct _main_extcomp main_extcomp; | 153 | CMD_ENCODE, |
154 | typedef struct _main_blklru main_blklru; | 154 | #endif |
155 | typedef 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 |
162 | struct _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 | 168 | typedef struct _main_file main_file; |
169 | 169 | typedef struct _main_extcomp main_extcomp; | |
170 | int mode; /* XO_READ and XO_WRITE */ | 170 | typedef struct _main_blklru main_blklru; |
171 | const char *filename; /* File name or /dev/stdin, /dev/stdout, /dev/stderr. */ | 171 | typedef 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 | }; | 178 | struct _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; |
182 | struct _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). */ | 198 | struct _main_extcomp |
199 | struct _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; | |
205 | struct _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, |
213 | XD3_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. */ | 215 | struct _main_blklru_list |
216 | static int option_stdout = 0; | 216 | { |
217 | static int option_force = 0; | 217 | main_blklru_list *next; |
218 | static int option_verbose = 0; | 218 | main_blklru_list *prev; |
219 | static int option_quiet = 0; | 219 | }; |
220 | static int option_level = 6; | 220 | |
221 | static int option_use_appheader = 1; | 221 | struct _main_blklru |
222 | static uint8_t* option_appheader = NULL; | 222 | { |
223 | static int option_use_secondary = /* until-standardized, leave this off */ 0; | 223 | uint8_t *blk; |
224 | static char* option_secondary = NULL; | 224 | xoff_t blkno; |
225 | static int option_use_checksum = 1; | 225 | main_blklru_list link; |
226 | static int option_use_altcodetable = 0; | 226 | }; |
227 | static char* option_smatch_config = NULL; | 227 | |
228 | static int option_no_compress = 0; | 228 | /* ... represented as a list (no cache index). */ |
229 | static int option_no_output = 0; /* go through the motions, but do not open or write output */ | 229 | XD3_MAKELIST(main_blklru_list,main_blklru,link); |
230 | static const char *option_source_filename = NULL; | 230 | |
231 | static const char *option_xdelta1 = "xdelta1"; | 231 | /* Program options: various command line flags and options. */ |
232 | 232 | static int option_stdout = 0; | |
233 | static usize_t option_winsize = XD3_DEFAULT_WINSIZE; | 233 | static int option_force = 0; |
234 | static usize_t option_srcwinsz = XD3_DEFAULT_SRCWINSZ; | 234 | static int option_verbose = 0; |
235 | static usize_t option_memsize = XD3_DEFAULT_MEMSIZE; | 235 | static int option_quiet = 0; |
236 | 236 | static int option_level = 6; | |
237 | /* This controls the number of times main repeats itself, only for profiling. */ | 237 | static int option_use_appheader = 1; |
238 | static int option_profile_cnt = 0; | 238 | static uint8_t* option_appheader = NULL; |
239 | 239 | static int option_use_secondary = /* until-standardized, leave this off */ 0; | |
240 | /* These variables are supressed to avoid their use w/o support. main() warns | 240 | static char* option_secondary = NULL; |
241 | * appropriately. */ | 241 | static int option_use_checksum = 1; |
242 | #if EXTERNAL_COMPRESSION | 242 | static int option_use_altcodetable = 0; |
243 | static int option_decompress_inputs = 1; | 243 | static char* option_smatch_config = NULL; |
244 | static int option_recompress_outputs = 1; | 244 | static int option_no_compress = 0; |
245 | #endif | 245 | static int option_no_output = 0; /* go through the motions, but do not open or write output */ |
246 | 246 | static const char *option_source_filename = NULL; | |
247 | /* This is for comparing "printdelta" output without attention to | 247 | static const char *option_xdelta1 = "xdelta1"; |
248 | * copy-instruction modes, useful for reverse engineering. */ | 248 | |
249 | #if VCDIFF_TOOLS | 249 | static usize_t option_winsize = XD3_DEFAULT_WINSIZE; |
250 | static int option_print_cpymode = 1; | 250 | static usize_t option_srcwinsz = XD3_DEFAULT_SRCWINSZ; |
251 | #endif | 251 | static 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. */ |
254 | IF_DEBUG(static int main_mallocs = 0;) | 254 | static int option_profile_cnt = 0; |
255 | 255 | ||
256 | static char* program_name = NULL; | 256 | /* These variables are supressed to avoid their use w/o support. main() warns |
257 | static uint8_t* appheader_used = NULL; | 257 | * appropriately. */ |
258 | static uint8_t* main_bdata = NULL; | 258 | #if EXTERNAL_COMPRESSION |
259 | 259 | static int option_decompress_inputs = 1; | |
260 | /* The LRU: obviously this is shared by all callers. */ | 260 | static int option_recompress_outputs = 1; |
261 | static int lru_size = 0; | 261 | #endif |
262 | static main_blklru *lru = NULL; /* array of lru_size elts */ | 262 | |
263 | static main_blklru_list lru_list; | 263 | /* This is for comparing "printdelta" output without attention to |
264 | static main_blklru_list lru_free; | 264 | * copy-instruction modes, useful for reverse engineering. */ |
265 | static int do_not_lru = 0; /* set to avoid lru, instead discard oldest */ | 265 | #if VCDIFF_TOOLS |
266 | 266 | static int option_print_cpymode = 1; | |
267 | static int lru_hits = 0; | 267 | #endif |
268 | static int lru_misses = 0; | 268 | |
269 | static int lru_filled = 0; | 269 | /* Static variables */ |
270 | 270 | IF_DEBUG(static int main_mallocs = 0;) | |
271 | /* Hacks for VCDIFF tools */ | 271 | |
272 | static int allow_fake_source = 0; | 272 | static char* program_name = NULL; |
273 | 273 | static uint8_t* appheader_used = NULL; | |
274 | /* This array of compressor types is compiled even if EXTERNAL_COMPRESSION is false just so | 274 | static uint8_t* main_bdata = NULL; |
275 | * the program knows the mapping of IDENT->NAME. */ | 275 | |
276 | static main_extcomp extcomp_types[] = | 276 | /* The LRU: obviously this is shared by all callers. */ |
277 | { | 277 | static int lru_size = 0; |
278 | /* The entry for xdelta3/1 must be 0/1 because the program_names are set there. */ | 278 | static main_blklru *lru = NULL; /* array of lru_size elts */ |
279 | { "xdelta3", "-cfq", "xdelta3", "-dcfq", "X", "\xd6\xc3\xc4", 3, RD_NONEXTERNAL }, | 279 | static main_blklru_list lru_list; |
280 | { "xdelta1", "delta", "xdelta1", "patch", "1", "%XD", 3, RD_EXTERNAL_V1 }, | 280 | static main_blklru_list lru_free; |
281 | { "bzip2", "-cf", "bzip2", "-dcf", "B", "BZh", 3, 0 }, | 281 | static 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 }, | 283 | static int lru_hits = 0; |
284 | }; | 284 | static int lru_misses = 0; |
285 | 285 | static int lru_filled = 0; | |
286 | static void main_get_appheader (xd3_stream *stream, main_file *ifile, | 286 | |
287 | main_file *output, main_file *sfile); | 287 | /* Hacks for VCDIFF tools */ |
288 | 288 | static int allow_fake_source = 0; | |
289 | static int main_help (void); | 289 | |
290 | 290 | /* This array of compressor types is compiled even if EXTERNAL_COMPRESSION is false just so | |
291 | static int | 291 | * the program knows the mapping of IDENT->NAME. */ |
292 | main_version (void) | 292 | static 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 }, | |
299 | static int | 299 | { "compress", "-cf", "uncompress", "-cf", "Z", "\037\235", 2, 0 }, |
300 | main_config (void) | 300 | }; |
301 | { | 301 | |
302 | main_version (); | 302 | static 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); | 305 | static 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); | 307 | static int |
308 | P(RINT "SECONDARY_DJW=%d\n", SECONDARY_DJW); | 308 | main_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); | 315 | static int |
316 | P(RINT "XD3_DEFAULT_SPREVSZ=%d\n", XD3_DEFAULT_SPREVSZ); | 316 | main_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); | |
328 | static void* | 328 | P(RINT "XD3_DEBUG=%d\n", XD3_DEBUG); |
329 | main_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); | |
337 | static void* | 337 | P(RINT "XD3_NODECOMPRESSSIZE=%d\n", XD3_NODECOMPRESSSIZE); |
338 | main_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 | 344 | static void* | |
345 | static void* | 345 | main_malloc1 (usize_t size) |
346 | main_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 | ||
353 | static void | 353 | static void* |
354 | main_free1 (void *opaque, void *ptr) | 354 | main_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 | } | |
360 | static void | 360 | |
361 | main_free (void *ptr) | 361 | static void* |
362 | { | 362 | main_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 | 369 | static void | |
370 | /* This ensures that (ret = errno) always indicates failure, in case errno was | 370 | main_free1 (void *opaque, void *ptr) |
371 | * accidentally not set. If this prints there's a bug somewhere. */ | 371 | { |
372 | static int | 372 | if (option_verbose > 2) { XPR(NT "free: %p\n", ptr); } |
373 | get_errno (void) | 373 | free (ptr); |
374 | { | 374 | } |
375 | if (errno == 0) | 375 | |
376 | { | 376 | static void |
377 | XPR(NT "you found a bug: expected errno != 0\n"); | 377 | main_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); | |
383 | static long | 383 | } |
384 | get_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); | 388 | static int |
389 | 389 | get_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"); |
394 | static long | 394 | errno = XD3_INTERNAL; |
395 | get_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 | 401 | static long | |
402 | gettimeofday (& tv, NULL); | 402 | get_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 | |
413 | static char* | 413 | // Result is the numbre of 100 nanosecond units |
414 | main_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 | } | 425 | static long |
426 | r /= 1000; | 426 | get_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; | |
431 | static char* | 431 | last = now; |
432 | main_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]; | 435 | static char* |
436 | 436 | main_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) | |
442 | static char* | 442 | { |
443 | main_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 | } |
452 | static int | 452 | |
453 | main_strtoxoff (const char* s, xoff_t *xo, char which) | 453 | static char* |
454 | { | 454 | main_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 | 464 | static char* |
465 | long long xx = strtoll (s, &e, 0); | 465 | main_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; | 474 | static int |
475 | } | 475 | main_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 | |
487 | static int | 487 | long long xx = strtoll (s, &e, 0); |
488 | main_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 | 509 | static int |
510 | * me sick, which is why these wrappers exist. */ | 510 | main_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 | |
532 | static void | 532 | * me sick, which is why these wrappers exist. */ |
533 | main_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 | |
542 | static void | 542 | #define XFNO(f) fileno(f->file) |
543 | main_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"; } | |
550 | static int | 550 | #define XSTDERR_XF(f) { (f)->file = STDERR_FILENO; (f)->filename = "/dev/stderr"; } |
551 | main_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; | 554 | static void |
555 | 555 | main_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; | |
561 | static int | 561 | #endif |
562 | main_file_close (main_file *xfile) | 562 | } |
563 | { | 563 | |
564 | int ret = 0; | 564 | static void |
565 | 565 | main_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); | 572 | static int |
573 | xfile->file = NULL; | 573 | main_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 | 583 | static int | |
584 | static int | 584 | main_file_close (main_file *xfile) |
585 | main_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; | 606 | static int |
607 | } | 607 | main_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)); | |
614 | static int | 614 | |
615 | main_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; | 636 | static int |
637 | } | 637 | main_file_stat (main_file *xfile, xoff_t *size, int err_ifnoseek) |
638 | 638 | { | |
639 | static int | 639 | int ret; |
640 | main_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 | |
651 | typedef int (xd3_posix_func) (int fd, uint8_t *buf, usize_t size); | 651 | if (! S_ISREG (sbuf.st_mode)) |
652 | 652 | { | |
653 | static int | 653 | if (err_ifnoseek) { XPR(NT "source file must be seekable: %s\n", xfile->filename); } |
654 | xd3_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); | 661 | static int |
662 | 662 | main_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; } | 673 | typedef int (xd3_posix_func) (int fd, uint8_t *buf, usize_t size); |
674 | 674 | ||
675 | nproc += result; | 675 | static int |
676 | } | 676 | xd3_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); |
684 | static int | 684 | |
685 | main_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 | 706 | static int | |
707 | #elif XD3_POSIX | 707 | main_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 | } | |
724 | static int | 724 | else |
725 | main_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); } | 746 | static int |
747 | ofile->nwrite += size; | 747 | main_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; | |
753 | static int | 753 | |
754 | main_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 | 775 | static int | |
776 | #if VCDIFF_TOOLS | 776 | main_file_seek (main_file *xfile, xoff_t pos) |
777 | /* This function prints a single VCDIFF window, mainly for debugging purposes. */ | 777 | { |
778 | static int | 778 | int ret = 0; |
779 | main_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 | { | 800 | static int |
801 | VC(UT " @%-6u", stream->dec_current1.addr); | 801 | main_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) | |
848 | static void | 848 | { |
849 | main_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"); |
856 | static int | 856 | return XD3_INTERNAL; |
857 | main_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 | 870 | static void |
871 | XD3_ASSERT (vcout); | 871 | main_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 "); | 878 | static int |
879 | if ((stream->dec_hdr_ind & VCD_CODETABLE) != 0) VC(UT "VCD_CODETABLE "); | 879 | main_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 | ||
1004 | static pid_t ext_subprocs[2]; | 1004 | #if EXTERNAL_COMPRESSION |
1005 | static 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 |
1008 | static int | 1008 | * disciplines. |
1009 | main_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. */ |
1023 | static int | 1023 | #define PIPE_READ_FD 0 |
1024 | main_waitpid_check(pid_t pid) | 1024 | #define PIPE_WRITE_FD 1 |
1025 | { | 1025 | |
1026 | int status; | 1026 | static pid_t ext_subprocs[2]; |
1027 | int ret = 0; | 1027 | static 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 | { | 1030 | static int |
1031 | ret = get_errno (); | 1031 | main_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 | 1045 | static int | |
1046 | return ret; | 1046 | main_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; |
1050 | static int | 1050 | |
1051 | main_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. */ | 1072 | static int |
1073 | static int | 1073 | main_external_compression_finish (void) |
1074 | main_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 | { | 1095 | static int |
1096 | return ret; | 1096 | main_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))) |
1107 | static int | 1107 | { |
1108 | main_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 | } | 1129 | static int |
1130 | 1130 | main_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"); |
1233 | static int | 1233 | |
1234 | main_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. */ | 1255 | static int |
1256 | !((decomp->flags & RD_NONEXTERNAL) && (ifile->flags & RD_NONEXTERNAL)) && | 1256 | main_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); |
1293 | static int | 1293 | } |
1294 | main_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 | { | 1315 | static int |
1316 | XPR(NT "mkstemp failed: %s: %s", tmpname, xd3_strerror (ret = get_errno ())); | 1316 | main_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. */ |
1390 | static int | 1390 | if ((ret = main_waitpid_check (decomp_id))) |
1391 | main_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 | 1412 | static int | |
1413 | /* The child runs the recompression process: */ | 1413 | main_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) |
1469 | static const main_extcomp* | 1469 | { |
1470 | main_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; |
1486 | static const main_extcomp* | 1486 | } |
1487 | main_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) | 1491 | static const main_extcomp* |
1492 | { | 1492 | main_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 | } | 1508 | static const main_extcomp* |
1509 | else | 1509 | main_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 | } |
1520 | static const char* | 1520 | return NULL; |
1521 | main_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 | } | |
1535 | static int | 1535 | } |
1536 | main_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. */ | 1542 | static const char* |
1543 | if (option_appheader) | 1543 | main_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; | 1557 | static int |
1558 | 1558 | main_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 | } | |
1591 | static void | 1591 | |
1592 | main_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 | } | 1613 | static void |
1614 | } | 1614 | main_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)); | |
1629 | static void | 1629 | |
1630 | main_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 | 1651 | static void | |
1652 | memset (parsed, 0, sizeof (parsed)); | 1652 | main_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) |
1687 | static int | 1687 | { |
1688 | main_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 |
1707 | static int | 1707 | * input when the first buffer of data is read. The EXTERNAL_COMPRESSION code is called |
1708 | main_write_output (xd3_stream* stream, main_file *ofile) | 1708 | * to search for magic numbers. */ |
1709 | { | 1709 | static int |
1710 | int ret; | 1710 | main_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 | } |
1722 | static int | 1722 | #endif |
1723 | main_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); | 1729 | static int |
1730 | 1730 | main_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; | 1744 | static int |
1745 | } | 1745 | main_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. */ |
1774 | static int | 1774 | if (ofile->compressor != NULL && option_recompress_outputs == 1) |
1775 | main_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; | 1796 | static int |
1797 | 1797 | main_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 | |
1927 | static int | 1927 | if (option_verbose > 1) { XPR(NT "source file: %s: %"Q"u bytes\n", sfile->realname, source->size); } |
1928 | main_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 | { | 1949 | static int |
1950 | /* Direct lookup assumes sequential scan w/o skipping blocks. */ | 1950 | main_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 |
2052 | static int | 2052 | { |
2053 | main_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; | 2074 | static int |
2075 | config.freef = main_free1; | 2075 | main_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 | } |
2425 | done: | 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; | 2447 | done: |
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 | } |
2478 | static void | 2478 | |
2479 | main_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; | 2500 | static void |
2501 | lru_filled = 0; | 2501 | main_cleanup (void) |
2502 | 2502 | { | |
2503 | XD3_ASSERT (main_mallocs == 0); | 2503 | int i; |
2504 | } | 2504 | |
2505 | 2505 | if (option_appheader) { appheader_used = NULL; } | |
2506 | int | 2506 | |
2507 | #if PYTHON_MODULE | 2507 | main_free (appheader_used); |
2508 | xd3_main_cmdline (int argc, char **argv) | 2508 | main_free (main_bdata); |
2509 | #else | 2509 | |
2510 | main (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); | 2528 | int |
2529 | 2529 | #if PYTHON_MODULE | |
2530 | go: /* Go. */ | 2530 | xd3_main_cmdline (int argc, char **argv) |
2531 | cmd = CMD_NONE; | 2531 | #else |
2532 | sfilename = NULL; | 2532 | main (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 | |
2885 | static int | 2885 | if (ext_tmpfile != NULL) { unlink (ext_tmpfile); } |
2886 | main_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"); | 2907 | static int |
2908 | P(RINT " -e compress%s\n", XD3_ENCODER ? "" : " [Not compiled]"); | 2908 | main_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 | } | ||