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