summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjosh.macdonald <jmacd@users.noreply.github.com>2010-07-01 03:37:15 +0000
committerjosh.macdonald <jmacd@users.noreply.github.com>2010-07-01 03:37:15 +0000
commitdebec7aae3d3d3e5b1b2da62a9417ab3a0c72c00 (patch)
tree59f621dc4ee725e571266d0f4f0db0cb243d9c9f
parent07cacc99dc5a2f9e5fd7197e1675fd923750d9e4 (diff)
Add -F command and properly handle trailing garbage. Manually tested.
-rw-r--r--xdelta3/xdelta3-main.h191
1 files changed, 144 insertions, 47 deletions
diff --git a/xdelta3/xdelta3-main.h b/xdelta3/xdelta3-main.h
index 5429ad4..447651a 100644
--- a/xdelta3/xdelta3-main.h
+++ b/xdelta3/xdelta3-main.h
@@ -314,8 +314,10 @@ static usize_t option_srcwinsz = XD3_DEFAULT_SRCWINSZ;
314static usize_t option_sprevsz = XD3_DEFAULT_SPREVSZ; 314static usize_t option_sprevsz = XD3_DEFAULT_SPREVSZ;
315 315
316/* These variables are supressed to avoid their use w/o support. main() warns 316/* These variables are supressed to avoid their use w/o support. main() warns
317 * appropriately. */ 317 * appropriately when external compression is not enabled. */
318#if EXTERNAL_COMPRESSION 318#if EXTERNAL_COMPRESSION
319static int num_subprocs = 0;
320static int option_force2 = 0;
319static int option_decompress_inputs = 1; 321static int option_decompress_inputs = 1;
320static int option_recompress_outputs = 1; 322static int option_recompress_outputs = 1;
321#endif 323#endif
@@ -358,19 +360,17 @@ static xd3_stream *merge_stream = NULL;
358 * false just so the program knows the mapping of IDENT->NAME. */ 360 * false just so the program knows the mapping of IDENT->NAME. */
359static main_extcomp extcomp_types[] = 361static main_extcomp extcomp_types[] =
360{ 362{
361 { "bzip2", "-cf", "bzip2", "-dcf", "B", "BZh", 3, 0 }, 363 { "bzip2", "-c", "bzip2", "-dc", "B", "BZh", 3, 0 },
362 { "gzip", "-cf", "gzip", "-dcf", "G", "\037\213", 2, 0 }, 364 { "gzip", "-c", "gzip", "-dc", "G", "\037\213", 2, 0 },
363 { "compress", "-cf", "uncompress", "-cf", "Z", "\037\235", 2, 0 }, 365 { "compress", "-c", "uncompress", "-c", "Z", "\037\235", 2, 0 },
364 366
365 /* TODO: add commandline support for magic-less formats */ 367 /* TODO: add commandline support for magic-less formats */
366 /*{ "lzma", "-cf", "lzma", "-dcf", "M", "]\000", 2, 0 },*/ 368 /*{ "lzma", "-c", "lzma", "-dc", "M", "]\000", 2, 0 },*/
367 369
368 /* Xz is lzma with a magic number http://tukaani.org/xz/ */ 370 /* Xz is lzma with a magic number http://tukaani.org/xz/ */
369 { "xz", "-cf", "xz", "-dcf", "Y", "\xfd\x37\x7a\x58\x5a\x00", 2, 0 }, 371 { "xz", "-c", "xz", "-dc", "Y", "\xfd\x37\x7a\x58\x5a\x00", 2, 0 },
370}; 372};
371 373
372// };
373
374static int main_input (xd3_cmd cmd, main_file *ifile, 374static int main_input (xd3_cmd cmd, main_file *ifile,
375 main_file *ofile, main_file *sfile); 375 main_file *ofile, main_file *sfile);
376static void main_get_appheader (xd3_stream *stream, main_file *ifile, 376static void main_get_appheader (xd3_stream *stream, main_file *ifile,
@@ -459,8 +459,10 @@ reset_defaults(void)
459 option_use_appheader = 1; 459 option_use_appheader = 1;
460 option_use_checksum = 1; 460 option_use_checksum = 1;
461#if EXTERNAL_COMPRESSION 461#if EXTERNAL_COMPRESSION
462 option_force2 = 0;
462 option_decompress_inputs = 1; 463 option_decompress_inputs = 1;
463 option_recompress_outputs = 1; 464 option_recompress_outputs = 1;
465 num_subprocs = 0;
464#endif 466#endif
465#if VCDIFF_TOOLS 467#if VCDIFF_TOOLS
466 option_print_cpymode = 1; 468 option_print_cpymode = 1;
@@ -974,7 +976,7 @@ xd3_posix_io (int fd, uint8_t *buf, usize_t size,
974 { 976 {
975 return ret; 977 return ret;
976 } 978 }
977 result = 0; 979 continue;
978 } 980 }
979 981
980 if (nread != NULL && result == 0) { break; } 982 if (nread != NULL && result == 0) { break; }
@@ -1106,12 +1108,6 @@ main_file_seek (main_file *xfile, xoff_t pos)
1106# endif 1108# endif
1107#endif 1109#endif
1108 1110
1109 if (ret)
1110 {
1111 XPR(NT "seek to %"Q"u failed: %s: %s\n",
1112 pos, xfile->filename, xd3_mainerror (ret));
1113 }
1114
1115 return ret; 1111 return ret;
1116} 1112}
1117 1113
@@ -2088,6 +2084,7 @@ main_merge_output (xd3_stream *stream, main_file *ofile)
2088 * input-decompression pipe. 2084 * input-decompression pipe.
2089 */ 2085 */
2090 2086
2087#include <signal.h>
2091#include <unistd.h> 2088#include <unistd.h>
2092#include <sys/stat.h> 2089#include <sys/stat.h>
2093#include <sys/wait.h> 2090#include <sys/wait.h>
@@ -2095,11 +2092,14 @@ main_merge_output (xd3_stream *stream, main_file *ofile)
2095/* Remember which pipe FD is which. */ 2092/* Remember which pipe FD is which. */
2096#define PIPE_READ_FD 0 2093#define PIPE_READ_FD 0
2097#define PIPE_WRITE_FD 1 2094#define PIPE_WRITE_FD 1
2098 2095#define MAX_SUBPROCS 4 /* max(source + copier + output,
2099static pid_t ext_subprocs[2]; 2096 source + copier + input + copier). */
2097static pid_t ext_subprocs[MAX_SUBPROCS];
2100static char* ext_tmpfile = NULL; 2098static char* ext_tmpfile = NULL;
2101 2099
2102/* Like write(), but makes repeated calls to empty the buffer. */ 2100/* Like write(), applies to a fd instead of a main_file, for the pipe
2101 * copier subprocess. Does not print an error, to facilitate ignoring
2102 * trailing garbage, see main_pipe_copier(). */
2103static int 2103static int
2104main_pipe_write (int outfd, uint8_t *exist_buf, usize_t remain) 2104main_pipe_write (int outfd, uint8_t *exist_buf, usize_t remain)
2105{ 2105{
@@ -2108,7 +2108,6 @@ main_pipe_write (int outfd, uint8_t *exist_buf, usize_t remain)
2108 if ((ret = xd3_posix_io (outfd, exist_buf, remain, 2108 if ((ret = xd3_posix_io (outfd, exist_buf, remain,
2109 (xd3_posix_func*) &write, NULL))) 2109 (xd3_posix_func*) &write, NULL)))
2110 { 2110 {
2111 XPR(NT "pipe write failed: %s", xd3_mainerror (ret));
2112 return ret; 2111 return ret;
2113 } 2112 }
2114 2113
@@ -2125,18 +2124,24 @@ main_waitpid_check(pid_t pid)
2125 if (waitpid (pid, & status, 0) < 0) 2124 if (waitpid (pid, & status, 0) < 0)
2126 { 2125 {
2127 ret = get_errno (); 2126 ret = get_errno ();
2128 XPR(NT "compression subprocess: wait: %s\n", xd3_mainerror (ret)); 2127 XPR(NT "external compression [pid %d] wait: %s\n",
2128 pid, xd3_mainerror (ret));
2129 } 2129 }
2130 else if (! WIFEXITED (status)) 2130 else if (! WIFEXITED (status))
2131 { 2131 {
2132 ret = ECHILD; 2132 ret = ECHILD;
2133 XPR(NT "compression subprocess: signal %d\n", 2133 XPR(NT "external compression [pid %d] signal %d\n",
2134 WIFSIGNALED (status) ? WTERMSIG (status) : WSTOPSIG (status)); 2134 pid, WIFSIGNALED (status) ? WTERMSIG (status) : WSTOPSIG (status));
2135 } 2135 }
2136 else if (WEXITSTATUS (status) != 0) 2136 else if (WEXITSTATUS (status) != 0)
2137 { 2137 {
2138 ret = ECHILD; 2138 ret = ECHILD;
2139 XPR(NT "compression subprocess: exit %d\n", WEXITSTATUS (status)); 2139 if (option_verbose > 1)
2140 {
2141 /* Presumably, the error was printed by the subprocess. */
2142 XPR(NT "external compression [pid %d] exit %d\n",
2143 pid, WEXITSTATUS (status));
2144 }
2140 } 2145 }
2141 2146
2142 return ret; 2147 return ret;
@@ -2149,7 +2154,7 @@ main_external_compression_finish (void)
2149 int i; 2154 int i;
2150 int ret; 2155 int ret;
2151 2156
2152 for (i = 0; i < 2; i += 1) 2157 for (i = 0; i < num_subprocs; i += 1)
2153 { 2158 {
2154 if (! ext_subprocs[i]) { continue; } 2159 if (! ext_subprocs[i]) { continue; }
2155 2160
@@ -2174,15 +2179,55 @@ main_pipe_copier (uint8_t *pipe_buf,
2174 int outfd) 2179 int outfd)
2175{ 2180{
2176 int ret; 2181 int ret;
2182 xoff_t garbage = 0;
2183
2184 /* Prevent SIGPIPE signals, allow EPIPE return values instead. This
2185 * is safe to comment-out, except that the -F flag will not work
2186 * properly (the parent would need to treat WTERMSIG(status) ==
2187 * SIGPIPE). */
2188 struct sigaction sa;
2189 sa.sa_handler = SIG_IGN;
2190 sigaction (SIGPIPE, &sa, NULL);
2177 2191
2178 for (;;) 2192 for (;;)
2179 { 2193 {
2194 /* force_drain will be set when option_force and EPIPE cause us
2195 * to skip data. This is reset each time through the loop, so
2196 * the break condition below works. */
2197 int force_drain = 0;
2180 if (nread > 0 && (ret = main_pipe_write (outfd, pipe_buf, nread))) 2198 if (nread > 0 && (ret = main_pipe_write (outfd, pipe_buf, nread)))
2181 { 2199 {
2182 return ret; 2200 if (option_force && ret == EPIPE)
2201 {
2202 /* This causes the loop to continue reading until nread
2203 * == 0. */
2204 garbage += nread;
2205 force_drain = 1;
2206 }
2207 else if (ret == EPIPE)
2208 {
2209 XPR(NT "external compression closed the pipe\n");
2210 if (option_verbose)
2211 {
2212 if (!option_force2)
2213 {
2214 XPR(NT "use -F to force the subprocess\n");
2215 }
2216 if (!option_force)
2217 {
2218 XPR(NT "use -f to force this process\n");
2219 }
2220 }
2221 return ret;
2222 }
2223 else
2224 {
2225 XPR(NT "pipe write failed: %s\n", xd3_mainerror (ret));
2226 return ret;
2227 }
2183 } 2228 }
2184 2229
2185 if (nread < pipe_bufsize) 2230 if (nread < pipe_bufsize && !force_drain)
2186 { 2231 {
2187 break; 2232 break;
2188 } 2233 }
@@ -2194,6 +2239,11 @@ main_pipe_copier (uint8_t *pipe_buf,
2194 } 2239 }
2195 } 2240 }
2196 2241
2242 if (garbage != 0)
2243 {
2244 XPR(NT "trailing garbage ignored in %s (%"Q"u bytes)\n",
2245 ifile->filename, garbage);
2246 }
2197 return 0; 2247 return 0;
2198} 2248}
2199 2249
@@ -2236,6 +2286,11 @@ main_input_decompress_setup (const main_extcomp *decomp,
2236 /* The first child runs the decompression process: */ 2286 /* The first child runs the decompression process: */
2237 if (decomp_id == 0) 2287 if (decomp_id == 0)
2238 { 2288 {
2289 if (option_verbose > 2)
2290 {
2291 XPR(NT "external decompression pid %d\n", getpid ());
2292 }
2293
2239 /* Setup pipes: write to the outpipe, read from the inpipe. */ 2294 /* Setup pipes: write to the outpipe, read from the inpipe. */
2240 if (dup2 (outpipefd[PIPE_WRITE_FD], STDOUT_FILENO) < 0 || 2295 if (dup2 (outpipefd[PIPE_WRITE_FD], STDOUT_FILENO) < 0 ||
2241 dup2 (inpipefd[PIPE_READ_FD], STDIN_FILENO) < 0 || 2296 dup2 (inpipefd[PIPE_READ_FD], STDIN_FILENO) < 0 ||
@@ -2244,7 +2299,9 @@ main_input_decompress_setup (const main_extcomp *decomp,
2244 close (inpipefd[PIPE_READ_FD]) || 2299 close (inpipefd[PIPE_READ_FD]) ||
2245 close (inpipefd[PIPE_WRITE_FD]) || 2300 close (inpipefd[PIPE_WRITE_FD]) ||
2246 execlp (decomp->decomp_cmdname, decomp->decomp_cmdname, 2301 execlp (decomp->decomp_cmdname, decomp->decomp_cmdname,
2247 decomp->decomp_options, NULL)) 2302 decomp->decomp_options,
2303 option_force2 ? "-f" : NULL,
2304 NULL))
2248 { 2305 {
2249 XPR(NT "child process %s failed to execute: %s\n", 2306 XPR(NT "child process %s failed to execute: %s\n",
2250 decomp->decomp_cmdname, xd3_mainerror (get_errno ())); 2307 decomp->decomp_cmdname, xd3_mainerror (get_errno ()));
@@ -2253,7 +2310,8 @@ main_input_decompress_setup (const main_extcomp *decomp,
2253 _exit (127); 2310 _exit (127);
2254 } 2311 }
2255 2312
2256 ext_subprocs[0] = decomp_id; 2313 XD3_ASSERT(num_subprocs < MAX_SUBPROCS);
2314 ext_subprocs[num_subprocs++] = decomp_id;
2257 2315
2258 if ((copier_id = fork ()) < 0) 2316 if ((copier_id = fork ()) < 0)
2259 { 2317 {
@@ -2266,6 +2324,11 @@ main_input_decompress_setup (const main_extcomp *decomp,
2266 { 2324 {
2267 int exitval = 0; 2325 int exitval = 0;
2268 2326
2327 if (option_verbose > 2)
2328 {
2329 XPR(NT "child pipe-copier pid %d\n", getpid ());
2330 }
2331
2269 if (close (inpipefd[PIPE_READ_FD]) || 2332 if (close (inpipefd[PIPE_READ_FD]) ||
2270 main_pipe_copier (pipe_buf, pipe_bufsize, pipe_avail, 2333 main_pipe_copier (pipe_buf, pipe_bufsize, pipe_avail,
2271 ifile, inpipefd[PIPE_WRITE_FD]) || 2334 ifile, inpipefd[PIPE_WRITE_FD]) ||
@@ -2279,7 +2342,8 @@ main_input_decompress_setup (const main_extcomp *decomp,
2279 _exit (exitval); 2342 _exit (exitval);
2280 } 2343 }
2281 2344
2282 ext_subprocs[1] = copier_id; 2345 XD3_ASSERT(num_subprocs < MAX_SUBPROCS);
2346 ext_subprocs[num_subprocs++] = copier_id;
2283 2347
2284 /* The parent closes both pipes after duplicating the output of 2348 /* The parent closes both pipes after duplicating the output of
2285 * compression. */ 2349 * compression. */
@@ -2369,7 +2433,7 @@ main_secondary_decompress_check (main_file *file,
2369 for (i = 0; i < SIZEOF_ARRAY (extcomp_types); i += 1) 2433 for (i = 0; i < SIZEOF_ARRAY (extcomp_types); i += 1)
2370 { 2434 {
2371 const main_extcomp *decomp = & extcomp_types[i]; 2435 const main_extcomp *decomp = & extcomp_types[i];
2372 2436
2373 if (check_nread > decomp->magic_size) 2437 if (check_nread > decomp->magic_size)
2374 { 2438 {
2375 /* The following expr checks if we are trying to read a 2439 /* The following expr checks if we are trying to read a
@@ -2396,9 +2460,10 @@ main_secondary_decompress_check (main_file *file,
2396 { 2460 {
2397 if (! option_quiet) 2461 if (! option_quiet)
2398 { 2462 {
2399 XPR(NT "externally compressed input: %s %s < %s\n", 2463 XPR(NT "externally compressed input: %s %s%s < %s\n",
2400 decompressor->decomp_cmdname, 2464 decompressor->decomp_cmdname,
2401 decompressor->decomp_options, 2465 decompressor->decomp_options,
2466 (option_force2 ? " -f" : ""),
2402 file->filename); 2467 file->filename);
2403 } 2468 }
2404 2469
@@ -2459,13 +2524,20 @@ main_recompress_output (main_file *ofile)
2459 /* The child runs the recompression process: */ 2524 /* The child runs the recompression process: */
2460 if (recomp_id == 0) 2525 if (recomp_id == 0)
2461 { 2526 {
2527 if (option_verbose > 2)
2528 {
2529 XPR(NT "external recompression pid %d\n", getpid ());
2530 }
2531
2462 /* Setup pipes: write to the output file, read from the pipe. */ 2532 /* Setup pipes: write to the output file, read from the pipe. */
2463 if (dup2 (XFNO (ofile), STDOUT_FILENO) < 0 || 2533 if (dup2 (XFNO (ofile), STDOUT_FILENO) < 0 ||
2464 dup2 (pipefd[PIPE_READ_FD], STDIN_FILENO) < 0 || 2534 dup2 (pipefd[PIPE_READ_FD], STDIN_FILENO) < 0 ||
2465 close (pipefd[PIPE_READ_FD]) || 2535 close (pipefd[PIPE_READ_FD]) ||
2466 close (pipefd[PIPE_WRITE_FD]) || 2536 close (pipefd[PIPE_WRITE_FD]) ||
2467 execlp (recomp->recomp_cmdname, recomp->recomp_cmdname, 2537 execlp (recomp->recomp_cmdname, recomp->recomp_cmdname,
2468 recomp->recomp_options, NULL)) 2538 recomp->recomp_options,
2539 option_force2 ? "-f" : NULL,
2540 NULL))
2469 { 2541 {
2470 XPR(NT "child process %s failed to execute: %s\n", 2542 XPR(NT "child process %s failed to execute: %s\n",
2471 recomp->recomp_cmdname, xd3_mainerror (get_errno ())); 2543 recomp->recomp_cmdname, xd3_mainerror (get_errno ()));
@@ -2474,7 +2546,8 @@ main_recompress_output (main_file *ofile)
2474 _exit (127); 2546 _exit (127);
2475 } 2547 }
2476 2548
2477 ext_subprocs[0] = recomp_id; 2549 XD3_ASSERT(num_subprocs < MAX_SUBPROCS);
2550 ext_subprocs[num_subprocs++] = recomp_id;
2478 2551
2479 /* The parent closes both pipes after duplicating the output-fd for 2552 /* The parent closes both pipes after duplicating the output-fd for
2480 * writing to the compression pipe. */ 2553 * writing to the compression pipe. */
@@ -2816,10 +2889,11 @@ main_open_output (xd3_stream *stream, main_file *ofile)
2816 { 2889 {
2817 if (! option_quiet) 2890 if (! option_quiet)
2818 { 2891 {
2819 XPR(NT "externally compressed output: %s %s > %s\n", 2892 XPR(NT "externally compressed output: %s %s%s > %s\n",
2820 ofile->compressor->recomp_cmdname, 2893 ofile->compressor->recomp_cmdname,
2821 ofile->compressor->recomp_options, 2894 ofile->compressor->recomp_options,
2822 ofile->filename); 2895 (option_force2 ? " -f" : ""),
2896 ofile->filename);
2823 } 2897 }
2824 2898
2825 if ((ret = main_recompress_output (ofile))) 2899 if ((ret = main_recompress_output (ofile)))
@@ -3162,9 +3236,6 @@ main_read_seek_source (xd3_stream *stream,
3162 } 3236 }
3163 } 3237 }
3164 3238
3165 /* There's a chance here, that an genuine lseek error will cause
3166 * xdelta3 to shift into non-seekable mode, entering a degraded
3167 * condition. */
3168 if (sfile->seek_failed || ret != 0) 3239 if (sfile->seek_failed || ret != 0)
3169 { 3240 {
3170 /* For an unseekable file (or other seek error, does it 3241 /* For an unseekable file (or other seek error, does it
@@ -3173,7 +3244,7 @@ main_read_seek_source (xd3_stream *stream,
3173 { 3244 {
3174 /* Could assert !IS_ENCODE(), this shouldn't happen 3245 /* Could assert !IS_ENCODE(), this shouldn't happen
3175 * because of do_src_fifo during encode. */ 3246 * because of do_src_fifo during encode. */
3176 if (option_verbose) 3247 if (!option_quiet)
3177 { 3248 {
3178 XPR(NT "source can't seek backwards; requested block offset " 3249 XPR(NT "source can't seek backwards; requested block offset "
3179 "%"Q"u source position is %"Q"u\n", 3250 "%"Q"u source position is %"Q"u\n",
@@ -3186,11 +3257,19 @@ main_read_seek_source (xd3_stream *stream,
3186 return XD3_TOOFARBACK; 3257 return XD3_TOOFARBACK;
3187 } 3258 }
3188 3259
3189 if (option_verbose > 2 || (option_verbose > 1 && !sfile->seek_failed)) 3260 /* There's a chance here, that an genuine lseek error will cause
3261 * xdelta3 to shift into non-seekable mode, entering a degraded
3262 * condition. */
3263 if (!sfile->seek_failed && option_verbose)
3190 { 3264 {
3191 XPR(NT "non-seekable source skipping %"Q"u bytes @ %"Q"u\n", 3265 XPR(NT "source can't seek, will use FIFO for %s\n",
3192 pos - sfile->source_position, 3266 sfile->filename);
3193 sfile->source_position); 3267
3268 if (option_verbose > 1)
3269 {
3270 XPR(NT "seek error at offset %"Q"u: %s\n",
3271 pos, xd3_mainerror (ret));
3272 }
3194 } 3273 }
3195 3274
3196 sfile->seek_failed = 1; 3275 sfile->seek_failed = 1;
@@ -3215,6 +3294,13 @@ main_read_seek_source (xd3_stream *stream,
3215 3294
3216 XD3_ASSERT (is_new); 3295 XD3_ASSERT (is_new);
3217 3296
3297 if (option_verbose > 1)
3298 {
3299 XPR(NT "non-seekable source skipping %"Q"u bytes @ %"Q"u\n",
3300 pos - sfile->source_position,
3301 sfile->source_position);
3302 }
3303
3218 if ((ret = main_read_primary_input (sfile, 3304 if ((ret = main_read_primary_input (sfile,
3219 (uint8_t*) blru->blk, 3305 (uint8_t*) blru->blk,
3220 source->blksize, 3306 source->blksize,
@@ -3974,7 +4060,7 @@ main (int argc, char **argv)
3974#endif 4060#endif
3975{ 4061{
3976 static const char *flags = 4062 static const char *flags =
3977 "0123456789cdefhnqvDJNORTVs:m:B:C:E:F:I:L:O:M:P:W:A::S::"; 4063 "0123456789cdefhnqvDFJNORTVs:m:B:C:E:I:L:O:M:P:W:A::S::";
3978 xd3_cmd cmd; 4064 xd3_cmd cmd;
3979 main_file ifile; 4065 main_file ifile;
3980 main_file ofile; 4066 main_file ofile;
@@ -4149,6 +4235,14 @@ main (int argc, char **argv)
4149 option_level = ret - '0'; 4235 option_level = ret - '0';
4150 break; 4236 break;
4151 case 'f': option_force = 1; break; 4237 case 'f': option_force = 1; break;
4238 case 'F':
4239#if EXTERNAL_COMPRESSION
4240 option_force2 = 1;
4241#else
4242 XPR(NT "warning: -F option ignored, "
4243 "external compression support was not compiled\n");
4244 break;
4245#endif
4152 case 'v': option_verbose += 1; option_quiet = 0; break; 4246 case 'v': option_verbose += 1; option_quiet = 0; break;
4153 case 'q': option_quiet = 1; option_verbose = 0; break; 4247 case 'q': option_quiet = 1; option_verbose = 0; break;
4154 case 'c': option_stdout = 1; break; 4248 case 'c': option_stdout = 1; break;
@@ -4423,7 +4517,10 @@ main_help (void)
4423 DP(RINT " -d decompress\n"); 4517 DP(RINT " -d decompress\n");
4424 DP(RINT " -e compress%s\n", 4518 DP(RINT " -e compress%s\n",
4425 XD3_ENCODER ? "" : " [Not compiled]"); 4519 XD3_ENCODER ? "" : " [Not compiled]");
4426 DP(RINT " -f force overwrite\n"); 4520 DP(RINT " -f force (overwrite, ignore trailing garbage)\n");
4521#if EXTERNAL_COMPRESSION
4522 DP(RINT " -F force the external-compression subprocess\n");
4523#endif
4427 DP(RINT " -h show help\n"); 4524 DP(RINT " -h show help\n");
4428 DP(RINT " -q be quiet\n"); 4525 DP(RINT " -q be quiet\n");
4429 DP(RINT " -v be verbose (max 2)\n"); 4526 DP(RINT " -v be verbose (max 2)\n");