xref: /plan9-contrib/sys/src/cmd/lzip/main.c (revision 13d37d7716a3e781f408392d7869dff5927c6669)
1 /*
2  *  Clzip - LZMA lossless data compressor
3  * Copyright (C) 2010-2017 Antonio Diaz Diaz.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 /*
19  * Exit status: 0 for a normal exit, 1 for environmental problems
20  * (file not found, invalid flags, I/O errors, etc), 2 to indicate a
21  * corrupt or invalid input file, 3 for an internal consistency error
22  * (eg, bug) which caused lzip to panic.
23  */
24 
25 #define _DEFINE_INLINES
26 #include "lzip.h"
27 #include "decoder.h"
28 #include "encoder_base.h"
29 #include "encoder.h"
30 #include "fast_encoder.h"
31 
32 int	verbosity = 0;
33 
34 char *argv0 = "lzip";
35 
36 struct {
37 	char * from;
38 	char * to;
39 } known_extensions[] = {
40 	{ ".lz",  ""     },
41 	{ ".tlz", ".tar" },
42 	{ 0,      0      }
43 };
44 
45 typedef struct Lzma_options Lzma_options;
46 struct Lzma_options {
47 	int	dict_size;		/* 4 KiB .. 512 MiB */
48 	int	match_len_limit;	/* 5 .. 273 */
49 };
50 
51 enum Mode { m_compress, m_decompress, };
52 
53 char	*output_filename = nil;
54 int	outfd = -1;
55 bool delete_output_on_interrupt = false;
56 
57 static void
usage(void)58 usage(void)
59 {
60 	fprintf(stderr, "Usage: %s [-[0-9]cdv] [file...]\n", argv0);
61 	exit(2);
62 }
63 
64 char *
bad_version(unsigned version)65 bad_version(unsigned version)
66 {
67 	static char buf[80];
68 
69 	snprintf(buf, sizeof buf, "Version %ud member format not supported.",
70 	    version);
71 	return buf;
72 }
73 
74 char *
format_ds(unsigned dict_size)75 format_ds(unsigned dict_size)
76 {
77 	enum { bufsize = 16, factor = 1024 };
78 	char *prefix[8] = { "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi" };
79 	char *p = "";
80 	char *np = "  ";
81 	unsigned num = dict_size, i;
82 	bool exact = (num % factor == 0);
83 	static char buf[bufsize];
84 
85 	for (i = 0; i < 8 && (num > 9999 || (exact && num >= factor)); ++i) {
86 		num /= factor;
87 		if (num % factor != 0)
88 			exact = false;
89 		p = prefix[i];
90 		np = "";
91 	}
92 	snprintf( buf, bufsize, "%s%4ud %sB", np, num, p );
93 	return buf;
94 }
95 
96 static void
show_header(unsigned dict_size)97 show_header(unsigned dict_size)
98 {
99 	if (verbosity >= 3)
100 		fprintf(stderr, "dictionary %s.  ", format_ds( dict_size) );
101 }
102 
103 static uvlong
getnum(char * ptr,uvlong llimit,uvlong ulimit)104 getnum(char *ptr, uvlong llimit, uvlong ulimit)
105 {
106 	int bad;
107 	uvlong result;
108 	char	*tail;
109 
110 	bad = 0;
111 	result = strtoull(ptr, &tail, 0);
112 	if (tail == ptr) {
113 		show_error( "Bad or missing numerical argument.", 0, true );
114 		exit(1);
115 	}
116 
117 	if (!errno && tail[0]) {
118 		unsigned factor = (tail[1] == 'i') ? 1024 : 1000;
119 		int	i, exponent = 0;		/* 0 = bad multiplier */
120 
121 		switch (tail[0]) {
122 		case 'Y':
123 			exponent = 8;
124 			break;
125 		case 'Z':
126 			exponent = 7;
127 			break;
128 		case 'E':
129 			exponent = 6;
130 			break;
131 		case 'P':
132 			exponent = 5;
133 			break;
134 		case 'T':
135 			exponent = 4;
136 			break;
137 		case 'G':
138 			exponent = 3;
139 			break;
140 		case 'M':
141 			exponent = 2;
142 			break;
143 		case 'K':
144 			if (factor == 1024)
145 				exponent = 1;
146 			break;
147 		case 'k':
148 			if (factor == 1000)
149 				exponent = 1;
150 			break;
151 		}
152 		if (exponent <= 0) {
153 			show_error( "Bad multiplier in numerical argument.", 0, true );
154 			exit(1);
155 		}
156 		for (i = 0; i < exponent; ++i) {
157 			if (ulimit / factor >= result)
158 				result *= factor;
159 			else {
160 				bad++;
161 				break;
162 			}
163 		}
164 	}
165 	if (bad || result < llimit || result > ulimit) {
166 		show_error( "Numerical argument out of limits.", 0, false );
167 		exit(1);
168 	}
169 	return result;
170 }
171 
172 static int
get_dict_size(char * arg)173 get_dict_size(char *arg)
174 {
175 	char	*tail;
176 	long bits = strtol(arg, &tail, 0);
177 
178 	if (bits >= min_dict_bits &&
179 	    bits <= max_dict_bits && *tail == 0)
180 		return (1 << bits);
181 	return getnum(arg, min_dict_size, max_dict_size);
182 }
183 
184 void
set_mode(enum Mode * program_modep,enum Mode new_mode)185 set_mode(enum Mode *program_modep, enum Mode new_mode)
186 {
187 	if (*program_modep != m_compress && *program_modep != new_mode) {
188 		show_error( "Only one operation can be specified.", 0, true );
189 		exit(1);
190 	}
191 	*program_modep = new_mode;
192 }
193 
194 static int
extension_index(char * name)195 extension_index(char *name)
196 {
197 	int	eindex;
198 
199 	for (eindex = 0; known_extensions[eindex].from; ++eindex) {
200 		char * ext = known_extensions[eindex].from;
201 		unsigned name_len = strlen(name);
202 		unsigned ext_len = strlen(ext);
203 
204 		if (name_len > ext_len &&
205 		    strncmp(name + name_len - ext_len, ext, ext_len) == 0)
206 			return eindex;
207 	}
208 	return - 1;
209 }
210 
211 int
open_instream(char * name,Dir *,bool,bool)212 open_instream(char *name, Dir *, bool, bool)
213 {
214 	int infd = open(name, OREAD);
215 
216 	if (infd < 0)
217 		show_file_error( name, "Can't open input file", errno );
218 	return infd;
219 }
220 
221 static int
open_instream2(char * name,Dir * in_statsp,enum Mode program_mode,int eindex,bool recompress,bool to_stdout)222 open_instream2(char *name, Dir *in_statsp, enum Mode program_mode,
223 	int eindex, bool recompress, bool to_stdout)
224 {
225 	bool no_ofile = to_stdout;
226 
227 	if (program_mode == m_compress && !recompress && eindex >= 0) {
228 		if (verbosity >= 0)
229 			fprintf( stderr, "%s: Input file '%s' already has '%s' suffix.\n",
230 			    argv0, name, known_extensions[eindex].from);
231 		return - 1;
232 	}
233 	return open_instream(name, in_statsp, no_ofile, false);
234 }
235 
236 /* assure at least a minimum size for buffer 'buf' */
237 void *
resize_buffer(void * buf,unsigned min_size)238 resize_buffer(void *buf, unsigned min_size)
239 {
240 	buf = realloc(buf, min_size);
241 	if (!buf) {
242 		show_error("Not enough memory.", 0, false);
243 		cleanup_and_fail(1);
244 	}
245 	return buf;
246 }
247 
248 static void
set_c_outname(char * name,bool multifile)249 set_c_outname(char *name, bool multifile)
250 {
251 	output_filename = resize_buffer(output_filename, strlen(name) + 5 +
252 	    strlen(known_extensions[0].from) + 1);
253 	strcpy(output_filename, name);
254 	if (multifile)
255 		strcat( output_filename, "00001" );
256 	strcat(output_filename, known_extensions[0].from);
257 }
258 
259 static void
set_d_outname(char * name,int eindex)260 set_d_outname(char *name, int eindex)
261 {
262 	unsigned name_len = strlen(name);
263 	if (eindex >= 0) {
264 		char * from = known_extensions[eindex].from;
265 		unsigned from_len = strlen(from);
266 
267 		if (name_len > from_len) {
268 			output_filename = resize_buffer(output_filename, name_len +
269 			    strlen(known_extensions[eindex].to) + 1);
270 			strcpy(output_filename, name);
271 			strcpy(output_filename + name_len - from_len, known_extensions[eindex].to);
272 			return;
273 		}
274 	}
275 	output_filename = resize_buffer(output_filename, name_len + 4 + 1);
276 	strcpy(output_filename, name);
277 	strcat(output_filename, ".out");
278 	if (verbosity >= 1)
279 		fprintf( stderr, "%s: Can't guess original name for '%s' -- using '%s'\n",
280 		    argv0, name, output_filename);
281 }
282 
283 static bool
open_outstream(bool force,bool)284 open_outstream(bool force, bool)
285 {
286 	int flags = OWRITE;
287 
288 	if (force)
289 		flags |= OTRUNC;
290 	else
291 		flags |= OEXCL;
292 
293 	outfd = create(output_filename, flags, 0666);
294 	if (outfd >= 0)
295 		delete_output_on_interrupt = true;
296 	else if (verbosity >= 0)
297 		fprintf(stderr, "%s: Can't create output file '%s': %r\n",
298 		    argv0, output_filename);
299 	return outfd >= 0;
300 }
301 
302 static bool
check_tty(int,enum Mode program_mode)303 check_tty(int, enum Mode program_mode)
304 {
305 	if (program_mode == m_compress && isatty(outfd) ||
306 	    program_mode == m_decompress && isatty(infd)) {
307 		usage();
308 		return false;
309 	}
310 	return true;
311 }
312 
313 void
cleanup_and_fail(int retval)314 cleanup_and_fail(int retval)
315 {
316 	if (delete_output_on_interrupt) {
317 		delete_output_on_interrupt = false;
318 		if (verbosity >= 0)
319 			fprintf(stderr, "%s: Deleting output file '%s', if it exists.\n",
320 			    argv0, output_filename);
321 		if (outfd >= 0) {
322 			close(outfd);
323 			outfd = -1;
324 		}
325 		if (remove(output_filename) != 0)
326 			fprintf(stderr, "%s: can't remove output file %s: %r\n",
327 				argv0, output_filename);
328 	}
329 	exit(retval);
330 }
331 
332 /* Set permissions, owner and times. */
333 static void
close_and_set_permissions(Dir *)334 close_and_set_permissions(Dir *)
335 {
336 	if (close(outfd) != 0) {
337 		show_error( "Error closing output file", errno, false );
338 		cleanup_and_fail(1);
339 	}
340 	outfd = -1;
341 	delete_output_on_interrupt = false;
342 }
343 
344 static bool
next_filename(void)345 next_filename(void)
346 {
347 	int i, j;
348 	unsigned name_len = strlen(output_filename);
349 	unsigned ext_len = strlen(known_extensions[0].from);
350 
351 	if ( name_len >= ext_len + 5 )			/* "*00001.lz" */
352 		for (i = name_len - ext_len - 1, j = 0; j < 5; --i, ++j) {
353 			if (output_filename[i] < '9') {
354 				++output_filename[i];
355 				return true;
356 			} else
357 				output_filename[i] = '0';
358 		}
359 	return false;
360 }
361 
362 typedef struct Poly_encoder Poly_encoder;
363 struct Poly_encoder {
364 	LZ_encoder_base *eb;
365 	LZ_encoder *e;
366 	FLZ_encoder *fe;
367 };
368 
369 static int
compress(uvlong member_size,uvlong volume_size,int infd,Lzma_options * encoder_options,Pretty_print * pp,Dir * in_statsp,bool zero)370 compress(uvlong member_size, uvlong volume_size,
371 	int infd, Lzma_options *encoder_options, Pretty_print *pp,
372 	Dir *in_statsp, bool zero)
373 {
374 	int retval = 0;
375 	uvlong in_size = 0, out_size = 0, partial_volume_size = 0;
376 	uvlong cfile_size = in_statsp? in_statsp->length / 100: 0;
377 	Poly_encoder encoder = { 0, 0, 0 }; /* polymorphic encoder */
378 	bool error = false;
379 
380 	if (verbosity >= 1)
381 		Pp_show_msg(pp, 0);
382 
383 	if (zero) {
384 		encoder.fe = (FLZ_encoder *)malloc(sizeof * encoder.fe);
385 		if (!encoder.fe || !FLZe_init(encoder.fe, infd, outfd))
386 			error = true;
387 		else
388 			encoder.eb = &encoder.fe->eb;
389 	} else {
390 		File_header header;
391 
392 		if (Fh_set_dict_size(header, encoder_options->dict_size) &&
393 		    encoder_options->match_len_limit >= min_match_len_limit &&
394 		    encoder_options->match_len_limit <= max_match_len)
395 			encoder.e = (LZ_encoder *)malloc(sizeof * encoder.e);
396 		else
397 			internal_error( "invalid argument to encoder." );
398 		if (!encoder.e || !LZe_init(encoder.e, Fh_get_dict_size(header),
399 		    encoder_options->match_len_limit, infd, outfd))
400 			error = true;
401 		else
402 			encoder.eb = &encoder.e->eb;
403 	}
404 	if (error) {
405 		Pp_show_msg( pp, "Not enough memory. Try a smaller dictionary size." );
406 		return 1;
407 	}
408 
409 	for(;;) {			/* encode one member per iteration */
410 		uvlong size;
411 		vlong freevolsz;
412 
413 		size = member_size;
414 		if (volume_size > 0) {
415 			freevolsz = volume_size - partial_volume_size;
416 			if (size > freevolsz)
417 				size = freevolsz;	/* limit size */
418 		}
419 		show_progress(in_size, &encoder.eb->mb, pp, cfile_size); /* init */
420 		if ((zero && !FLZe_encode_member(encoder.fe, size)) ||
421 		    (!zero && !LZe_encode_member(encoder.e, size))) {
422 			Pp_show_msg( pp, "Encoder error." );
423 			retval = 1;
424 			break;
425 		}
426 		in_size += Mb_data_position(&encoder.eb->mb);
427 		out_size += Re_member_position(&encoder.eb->renc);
428 		if (Mb_data_finished(&encoder.eb->mb))
429 			break;
430 		if (volume_size > 0) {
431 			partial_volume_size += Re_member_position(&encoder.eb->renc);
432 			if (partial_volume_size >= volume_size - min_dict_size) {
433 				partial_volume_size = 0;
434 				if (delete_output_on_interrupt) {
435 					close_and_set_permissions(in_statsp);
436 					if (!next_filename()) {
437 						Pp_show_msg( pp, "Too many volume files." );
438 						retval = 1;
439 						break;
440 					}
441 					if (!open_outstream(true, !in_statsp)) {
442 						retval = 1;
443 						break;
444 					}
445 				}
446 			}
447 		}
448 		if (zero)
449 			FLZe_reset(encoder.fe);
450 		else
451 			LZe_reset(encoder.e);
452 	}
453 
454 	if (retval == 0 && verbosity >= 1)
455 		if (in_size == 0 || out_size == 0)
456 			fputs( " no data compressed.\n", stderr );
457 		else {
458 			if (0)
459 				fprintf(stderr,
460 				    "%6.3f:1, %6.3f bits/byte, %5.2f%% saved, ",
461 				    (double)in_size / out_size,
462 				    (8.0 * out_size) / in_size,
463 				    100.0 * (1.0 - (double)out_size/in_size));
464 			fprintf(stderr, "%llud in, %llud out.\n",
465 				in_size, out_size);
466 		}
467 	LZeb_free(encoder.eb);
468 	if (zero)
469 		free(encoder.fe);
470 	else
471 		free(encoder.e);
472 	return retval;
473 }
474 
475 static uchar
xdigit(unsigned value)476 xdigit(unsigned value)
477 {
478 	if (value <= 9)
479 		return '0' + value;
480 	if (value <= 15)
481 		return 'A' + value - 10;
482 	return 0;
483 }
484 
485 static bool
show_trailing_data(uchar * data,int size,Pretty_print * pp,bool all,bool ignore_trailing)486 show_trailing_data(uchar *data, int size, Pretty_print *pp, bool all,
487 	bool ignore_trailing)
488 {
489 	if (verbosity >= 4 || !ignore_trailing) {
490 		char buf[128];
491 		int i, len = snprintf(buf, sizeof buf, "%strailing data = ",
492 		    all? "": "first bytes of ");
493 
494 		if (len < 0)
495 			len = 0;
496 		for (i = 0; i < size && len + 2 < sizeof buf; ++i) {
497 			buf[len++] = xdigit(data[i] >> 4);
498 			buf[len++] = xdigit(data[i] & 0x0F);
499 			buf[len++] = ' ';
500 		}
501 		if (len < sizeof buf)
502 			buf[len++] = '\'';
503 		for (i = 0; i < size && len < sizeof buf; ++i) {
504 			if (isprint(data[i]))
505 				buf[len++] = data[i];
506 			else
507 				buf[len++] = '.';
508 		}
509 		if (len < sizeof buf)
510 			buf[len++] = '\'';
511 		if (len < sizeof buf)
512 			buf[len] = 0;
513 		else
514 			buf[sizeof buf - 1] = 0;
515 		Pp_show_msg(pp, buf);
516 		if (!ignore_trailing)
517 			show_file_error(pp->name, trailing_msg, 0);
518 	}
519 	return ignore_trailing;
520 }
521 
522 static int
decompress(int infd,Pretty_print * pp,bool ignore_trailing)523 decompress(int infd, Pretty_print *pp, bool ignore_trailing)
524 {
525 	uvlong partial_file_pos = 0;
526 	Range_decoder rdec;
527 	int retval = 0;
528 	bool first_member;
529 
530 	if (!Rd_init(&rdec, infd)) {
531 		show_error( "Not enough memory.", 0, false );
532 		cleanup_and_fail(1);
533 	}
534 
535 	for (first_member = true; ; first_member = false) {
536 		int result, size;
537 		unsigned dict_size;
538 		File_header header;
539 		LZ_decoder decoder;
540 
541 		Rd_reset_member_position(&rdec);
542 		size = Rd_read_data(&rdec, header, Fh_size);
543 		if (Rd_finished(&rdec))		/* End Of File */ {
544 			if (first_member || Fh_verify_prefix(header, size)) {
545 				Pp_show_msg( pp, "File ends unexpectedly at member header." );
546 				retval = 2;
547 			} else if (size > 0 && !show_trailing_data(header, size, pp,
548 			    true, ignore_trailing))
549 				retval = 2;
550 			break;
551 		}
552 		if (!Fh_verify_magic(header)) {
553 			if (first_member) {
554 				show_file_error(pp->name, bad_magic_msg, 0);
555 				retval = 2;
556 			} else if (!show_trailing_data(header, size, pp,
557 			    false, ignore_trailing))
558 				retval = 2;
559 			break;
560 		}
561 		if (!Fh_verify_version(header)) {
562 			Pp_show_msg(pp, bad_version(Fh_version(header)));
563 			retval = 2;
564 			break;
565 		}
566 		dict_size = Fh_get_dict_size(header);
567 		if (!isvalid_ds(dict_size)) {
568 			Pp_show_msg(pp, bad_dict_msg);
569 			retval = 2;
570 			break;
571 		}
572 
573 		if (verbosity >= 2 || (verbosity == 1 && first_member)) {
574 			Pp_show_msg(pp, 0);
575 			show_header(dict_size);
576 		}
577 
578 		if (!LZd_init(&decoder, &rdec, dict_size, outfd)) {
579 			Pp_show_msg( pp, "Not enough memory." );
580 			retval = 1;
581 			break;
582 		}
583 		result = LZd_decode_member(&decoder, pp);
584 		partial_file_pos += Rd_member_position(&rdec);
585 		LZd_free(&decoder);
586 		if (result != 0) {
587 			if (verbosity >= 0 && result <= 2) {
588 				Pp_show_msg(pp, 0);
589 				fprintf(stderr, "%s: %s at pos %llud\n",
590 					argv0, (result == 2?
591 					"file ends unexpectedly":
592 					"decoder error"), partial_file_pos);
593 			}
594 			retval = 2;
595 			break;
596 		}
597 		if (verbosity >= 2) {
598 			fputs("done\n", stderr);
599 			Pp_reset(pp);
600 		}
601 	}
602 	Rd_free(&rdec);
603 	if (verbosity == 1 && retval == 0)
604 		fputs("done\n", stderr);
605 	return retval;
606 }
607 
608 void
signal_handler(int sig)609 signal_handler(int sig)
610 {
611 	USED(sig);
612 	show_error("interrupt caught, quitting.", 0, false);
613 	cleanup_and_fail(1);
614 }
615 
616 static void
set_signals(void)617 set_signals(void)
618 {
619 }
620 
621 void
show_error(char * msg,int,bool help)622 show_error(char *msg, int, bool help)
623 {
624 	if (verbosity < 0)
625 		return;
626 	if (msg && msg[0])
627 		fprintf(stderr, "%s: %s: %r\n", argv0, msg);
628 	if (help)
629 		fprintf(stderr, "Try '%s --help' for more information.\n",
630 		    argv0);
631 }
632 
633 void
show_file_error(char * filename,char * msg,int errcode)634 show_file_error(char *filename, char *msg, int errcode)
635 {
636 	if (verbosity < 0)
637 		return;
638 	fprintf(stderr, "%s: %s: %s", argv0, filename, msg);
639 	if (errcode > 0)
640 		fprintf(stderr, ": %r");
641 	fputc('\n', stderr);
642 }
643 
644 void
internal_error(char * msg)645 internal_error(char *msg)
646 {
647 	if (verbosity >= 0)
648 		fprintf( stderr, "%s: internal error: %s\n", argv0, msg );
649 	exit(3);
650 }
651 
652 void
show_progress(uvlong partial_size,Matchfinder_base * m,Pretty_print * p,uvlong cfile_size)653 show_progress(uvlong partial_size, Matchfinder_base *m,
654 	Pretty_print *p, uvlong cfile_size)
655 {
656 	static uvlong psize = 0, csize = 0; /* csize=file_size/100 */
657 	static Matchfinder_base *mb = 0;
658 	static Pretty_print *pp = 0;
659 
660 	if (verbosity < 2)
661 		return;
662 	if (m) {				/* initialize static vars */
663 		csize = cfile_size;
664 		psize = partial_size;
665 		mb = m;
666 		pp = p;
667 	}
668 	if (mb && pp) {
669 		uvlong pos = psize + Mb_data_position(mb);
670 
671 		if (csize > 0)
672 			fprintf( stderr, "%4llud%%", pos / csize );
673 		fprintf( stderr, "  %.1f MB\r", pos / 1000000.0 );
674 		Pp_reset(pp);
675 		Pp_show_msg(pp, 0);	/* restore cursor position */
676 	}
677 }
678 
679 /*
680  * Mapping from gzip/bzip2 style 1..9 compression modes to the corresponding
681  * LZMA compression modes.
682  */
683 static Lzma_options option_mapping[] = {
684 	{ 1 << 16,  16 },
685 	{ 1 << 20,   5 },
686 	{ 3 << 19,   6 },
687 	{ 1 << 21,   8 },
688 	{ 3 << 20,  12 },
689 	{ 1 << 22,  20 },
690 	{ 1 << 23,  36 },
691 	{ 1 << 24,  68 },
692 	{ 3 << 23, 132 },
693 //	{ 1 << 25, max_match_len },	// TODO
694 	{ 1 << 26, max_match_len },
695 };
696 
697 void
main(int argc,char * argv[])698 main(int argc, char *argv[])
699 {
700 	int num_filenames, infd, i, retval = 0;
701 	bool filenames_given = false, force = false, ignore_trailing = true,
702 		recompress = false,
703 		stdin_used = false, to_stdout = false, zero = false;
704 	uvlong max_member_size = 0x0008000000000000ULL;
705 	uvlong max_volume_size = 0x4000000000000000ULL;
706 	uvlong member_size = max_member_size;
707 	uvlong volume_size = 0;
708 	char *default_output_filename = "";
709 	char **filenames = nil;
710 	enum Mode program_mode = m_compress;
711 	Lzma_options encoder_options = option_mapping[6];  /* default = "-6" */
712 	Pretty_print pp;
713 
714 	CRC32_init();
715 
716 	ARGBEGIN {
717 	case '0':
718 	case '1':
719 	case '2':
720 	case '3':
721 	case '4':
722 	case '5':
723 	case '6':
724 	case '7':
725 	case '8':
726 	case '9':
727 		zero = (ARGC() == '0');
728 		encoder_options = option_mapping[ARGC() - '0'];
729 		break;
730 	case 'a':
731 		ignore_trailing = false;
732 		break;
733 	case 'b':
734 		member_size = getnum(EARGF(usage()), 100000, max_member_size);
735 		break;
736 	case 'c':
737 		to_stdout = true;
738 		break;
739 	case 'd':
740 		set_mode(&program_mode, m_decompress);
741 		break;
742 	case 'f':
743 		force = true;
744 		break;
745 	case 'F':
746 		recompress = true;
747 		break;
748 	case 'm':
749 		encoder_options.match_len_limit =
750 		    getnum(EARGF(usage()), min_match_len_limit, max_match_len);
751 		zero = false;
752 		break;
753 	case 'o':
754 		default_output_filename = EARGF(usage());
755 		break;
756 	case 'q':
757 		verbosity = -1;
758 		break;
759 	case 's':
760 		encoder_options.dict_size = get_dict_size(EARGF(usage()));
761 		zero = false;
762 		break;
763 	case 'S':
764 		volume_size = getnum(EARGF(usage()), 100000, max_volume_size);
765 		break;
766 	case 'v':
767 		if (verbosity < 4)
768 			++verbosity;
769 		break;
770 	default:
771 		usage();
772 	} ARGEND
773 
774 	num_filenames = max(1, argc);
775 	filenames = resize_buffer(filenames, num_filenames * sizeof filenames[0]);
776 	filenames[0] = "-";
777 	for (i = 0; i < argc; ++i) {
778 		filenames[i] = argv[i];
779 		if (strcmp(filenames[i], "-") != 0)
780 			filenames_given = true;
781 	}
782 
783 	if (program_mode == m_compress) {
784 		Dis_slots_init();
785 		Prob_prices_init();
786 	}
787 
788 	if (!to_stdout && (filenames_given || default_output_filename[0]))
789 		set_signals();
790 
791 	Pp_init(&pp, filenames, num_filenames, verbosity);
792 
793 	output_filename = resize_buffer(output_filename, 1);
794 	for (i = 0; i < num_filenames; ++i) {
795 		char *input_filename = "";
796 		int tmp, eindex;
797 		Dir in_stats;
798 		Dir *in_statsp;
799 
800 		output_filename[0] = 0;
801 		if ( !filenames[i][0] || strcmp( filenames[i], "-" ) == 0 ) {
802 			if (stdin_used)
803 				continue;
804 			else
805 				stdin_used = true;
806 			infd = 0;
807 			if (to_stdout || !default_output_filename[0])
808 				outfd = 1;
809 			else {
810 				if (program_mode == m_compress)
811 					set_c_outname(default_output_filename,
812 						volume_size > 0);
813 				else {
814 					output_filename = resize_buffer(output_filename,
815 					    strlen(default_output_filename)+1);
816 					strcpy(output_filename,
817 						default_output_filename);
818 				}
819 				if (!open_outstream(force, true)) {
820 					if (retval < 1)
821 						retval = 1;
822 					close(infd);
823 					continue;
824 				}
825 			}
826 		} else {
827 			eindex = extension_index(input_filename = filenames[i]);
828 			infd = open_instream2(input_filename, &in_stats,
829 				program_mode, eindex, recompress, to_stdout);
830 			if (infd < 0) {
831 				if (retval < 1)
832 					retval = 1;
833 				continue;
834 			}
835 			if (to_stdout)
836 				outfd = 1;
837 			else {
838 				if (program_mode == m_compress)
839 					set_c_outname(input_filename,
840 						volume_size > 0);
841 				else
842 					set_d_outname(input_filename, eindex);
843 				if (!open_outstream(force, false)) {
844 					if (retval < 1)
845 						retval = 1;
846 					close(infd);
847 					continue;
848 				}
849 			}
850 		}
851 
852 		Pp_set_name(&pp, input_filename);
853 		if (!check_tty(infd, program_mode)) {
854 			if (retval < 1)
855 				retval = 1;
856 			cleanup_and_fail(retval);
857 		}
858 
859 		in_statsp = input_filename[0]? &in_stats: nil;
860 		if (program_mode == m_compress)
861 			tmp = compress(member_size, volume_size, infd,
862 				&encoder_options, &pp, in_statsp, zero);
863 		else
864 			tmp = decompress(infd, &pp, ignore_trailing);
865 		if (tmp > retval)
866 			retval = tmp;
867 		if (tmp)
868 			cleanup_and_fail(retval);
869 
870 		if (delete_output_on_interrupt)
871 			close_and_set_permissions(in_statsp);
872 		if (input_filename[0])
873 			close(infd);
874 	}
875 	if (outfd >= 0 && close(outfd) != 0) {
876 		show_error("Can't close stdout", errno, false);
877 		if (retval < 1)
878 			retval = 1;
879 	}
880 	free(output_filename);
881 	free(filenames);
882 	exit(retval);
883 }
884