1 /*
2    Copyright (C) 2006-2016 Con Kolivas
3    Copyright (C) 2011 Peter Hyman
4    Copyright (C) 1998-2003 Andrew Tridgell
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19 /* lrzip compression - main program */
20 
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24 
25 #include <signal.h>
26 #ifdef HAVE_UNISTD_H
27 # include <unistd.h>
28 #endif
29 #ifdef HAVE_SYS_TIME_H
30 # include <sys/time.h>
31 #endif
32 #ifdef HAVE_SYS_RESOURCE_H
33 # include <sys/resource.h>
34 #endif
35 #ifdef HAVE_SYS_TYPES_H
36 # include <sys/types.h>
37 #endif
38 #ifdef HAVE_SYS_STAT_H
39 # include <sys/stat.h>
40 #endif
41 
42 #include <termios.h>
43 #ifdef HAVE_ENDIAN_H
44 # include <endian.h>
45 #elif HAVE_SYS_ENDIAN_H
46 # include <sys/endian.h>
47 #endif
48 #ifdef HAVE_ARPA_INET_H
49 # include <arpa/inet.h>
50 #endif
51 
52 #include <dirent.h>
53 #include <getopt.h>
54 #include <libgen.h>
55 
56 #include "rzip.h"
57 #include "lrzip_core.h"
58 #include "util.h"
59 #include "stream.h"
60 
61 /* needed for CRC routines */
62 #include "lzma/C/7zCrc.h"
63 
64 #define MAX_PATH_LEN 4096
65 
66 static rzip_control base_control, local_control, *control;
67 
usage(bool compat)68 static void usage(bool compat)
69 {
70 	print_output("lrz%s version %s\n", compat ? "" : "ip", PACKAGE_VERSION);
71 	print_output("Copyright (C) Con Kolivas 2006-2016\n");
72 	print_output("Based on rzip ");
73 	print_output("Copyright (C) Andrew Tridgell 1998-2003\n\n");
74 	print_output("Usage: lrz%s [options] <file...>\n", compat ? "" : "ip");
75 	print_output("General options:\n");
76 	if (compat) {
77 		print_output("	-c, --stdout		output to STDOUT\n");
78 		print_output("	-C, --check		check integrity of file written on decompression\n");
79 	} else
80 		print_output("	-c, -C, --check		check integrity of file written on decompression\n");
81 	print_output("	-d, --decompress	decompress\n");
82 	print_output("	-e, --encrypt		password protected sha512/aes128 encryption on compression\n");
83 	print_output("	-h, -?, --help		show help\n");
84 	print_output("	-H, --hash		display md5 hash integrity information\n");
85 	print_output("	-i, --info		show compressed file information\n");
86 	if (compat) {
87 		print_output("	-L, --license		display software version and license\n");
88 		print_output("	-P, --progress		show compression progress\n");
89 	} else
90 		print_output("	-q, --quiet		don't show compression progress\n");
91 	print_output("	-r, --recursive		operate recursively on directories\n");
92 	print_output("	-t, --test		test compressed file integrity\n");
93 	print_output("	-v[v%s], --verbose	Increase verbosity\n", compat ? "v" : "");
94 	print_output("	-V, --version		show version\n");
95 	print_output("Options affecting output:\n");
96 	if (!compat)
97 		print_output("	-D, --delete		delete existing files\n");
98 	print_output("	-f, --force		force overwrite of any existing files\n");
99 	if (compat)
100 		print_output("	-k, --keep		don't delete source files on de/compression\n");
101 	print_output("	-K, --keep-broken	keep broken or damaged output files\n");
102 	print_output("	-o, --outfile filename	specify the output file name and/or path\n");
103 	print_output("	-O, --outdir directory	specify the output directory when -o is not used\n");
104 	print_output("	-S, --suffix suffix	specify compressed suffix (default '.lrz')\n");
105 	print_output("Options affecting compression:\n");
106 	print_output("	--lzma			lzma compression (default)\n");
107 	print_output("	-b, --bzip2		bzip2 compression\n");
108 	print_output("	-g, --gzip		gzip compression using zlib\n");
109 	print_output("	-l, --lzo		lzo compression (ultra fast)\n");
110 	print_output("	-n, --no-compress	no backend compression - prepare for other compressor\n");
111 	print_output("	-z, --zpaq		zpaq compression (best, extreme compression, extremely slow)\n");
112 	print_output("Low level options:\n");
113 	if (compat) {
114 		print_output("	-1 .. -9		set lzma/bzip2/gzip compression level (1-9, default 7)\n");
115 		print_output("	--fast			alias for -1\n");
116 		print_output("	--best			alias for -9\n");
117 	}
118 	if (!compat)
119 		print_output("	-L, --level level	set lzma/bzip2/gzip compression level (1-9, default 7)\n");
120 	print_output("	-N, --nice-level value	Set nice value to value (default %d)\n", compat ? 0 : 19);
121 	print_output("	-p, --threads value	Set processor count to override number of threads\n");
122 	print_output("	-m, --maxram size	Set maximim available ram in hundreds of MB\n");
123 	print_output("				overrides detected ammount of available ram\n");
124 	print_output("	-T, --threshold		Disable LZO compressibility testing\n");
125 	print_output("	-U, --unlimited		Use unlimited window size beyond ramsize (potentially much slower)\n");
126 	print_output("	-w, --window size	maximum compression window in hundreds of MB\n");
127 	print_output("				default chosen by heuristic dependent on ram and chosen compression\n");
128 	print_output("\nLRZIP=NOCONFIG environment variable setting can be used to bypass lrzip.conf.\n");
129 	print_output("TMP environment variable will be used for storage of temporary files when needed.\n");
130 	print_output("TMPDIR may also be stored in lrzip.conf file.\n");
131 	print_output("\nIf no filenames or \"-\" is specified, stdin/out will be used.\n");
132 
133 }
134 
license(void)135 static void license(void)
136 {
137 	print_output("lrz version %s\n", PACKAGE_VERSION);
138 	print_output("Copyright (C) Con Kolivas 2006-2016\n");
139 	print_output("Based on rzip ");
140 	print_output("Copyright (C) Andrew Tridgell 1998-2003\n\n");
141 	print_output("This is free software.  You may redistribute copies of it under the terms of\n");
142 	print_output("the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.\n");
143 	print_output("There is NO WARRANTY, to the extent permitted by law.\n");
144 }
145 
sighandler(int sig __UNUSED__)146 static void sighandler(int sig __UNUSED__)
147 {
148 	struct termios termios_p;
149 
150 	/* Make sure we haven't died after disabling stdin echo */
151 	tcgetattr(fileno(stdin), &termios_p);
152 	termios_p.c_lflag |= ECHO;
153 	tcsetattr(fileno(stdin), 0, &termios_p);
154 
155 	unlink_files(control);
156 	exit(0);
157 }
158 
show_summary(void)159 static void show_summary(void)
160 {
161 	/* OK, if verbosity set, print summary of options selected */
162 	if (!INFO) {
163 		if (!TEST_ONLY)
164 			print_verbose("The following options are in effect for this %s.\n",
165 				      DECOMPRESS ? "DECOMPRESSION" : "COMPRESSION");
166 		print_verbose("Threading is %s. Number of CPUs detected: %d\n", control->threads > 1? "ENABLED" : "DISABLED",
167 			      control->threads);
168 		print_verbose("Detected %lld bytes ram\n", control->ramsize);
169 		print_verbose("Compression level %d\n", control->compression_level);
170 		print_verbose("Nice Value: %d\n", control->nice_val);
171 		print_verbose("Show Progress\n");
172 		print_maxverbose("Max ");
173 		print_verbose("Verbose\n");
174 		if (FORCE_REPLACE)
175 			print_verbose("Overwrite Files\n");
176 		if (!KEEP_FILES)
177 			print_verbose("Remove input files on completion\n");
178 		if (control->outdir)
179 			print_verbose("Output Directory Specified: %s\n", control->outdir);
180 		else if (control->outname)
181 			print_verbose("Output Filename Specified: %s\n", control->outname);
182 		if (TEST_ONLY)
183 			print_verbose("Test file integrity\n");
184 		if (control->tmpdir)
185 			print_verbose("Temporary Directory set as: %s\n", control->tmpdir);
186 
187 		/* show compression options */
188 		if (!DECOMPRESS && !TEST_ONLY) {
189 			print_verbose("Compression mode is: ");
190 			if (LZMA_COMPRESS)
191 				print_verbose("LZMA. LZO Compressibility testing %s\n", (LZO_TEST? "enabled" : "disabled"));
192 			else if (LZO_COMPRESS)
193 				print_verbose("LZO\n");
194 			else if (BZIP2_COMPRESS)
195 				print_verbose("BZIP2. LZO Compressibility testing %s\n", (LZO_TEST? "enabled" : "disabled"));
196 			else if (ZLIB_COMPRESS)
197 				print_verbose("GZIP\n");
198 			else if (ZPAQ_COMPRESS)
199 				print_verbose("ZPAQ. LZO Compressibility testing %s\n", (LZO_TEST? "enabled" : "disabled"));
200 			else if (NO_COMPRESS)
201 				print_verbose("RZIP pre-processing only\n");
202 			if (control->window)
203 				print_verbose("Compression Window: %lld = %lldMB\n", control->window, control->window * 100ull);
204 			/* show heuristically computed window size */
205 			if (!control->window && !UNLIMITED) {
206 				i64 temp_chunk, temp_window;
207 
208 				if (STDOUT || STDIN)
209 					temp_chunk = control->maxram;
210 				else
211 					temp_chunk = control->ramsize * 2 / 3;
212 				temp_window = temp_chunk / (100 * 1024 * 1024);
213 				print_verbose("Heuristically Computed Compression Window: %lld = %lldMB\n", temp_window, temp_window * 100ull);
214 			}
215 			if (UNLIMITED)
216 				print_verbose("Using Unlimited Window size\n");
217 		}
218 		if (!DECOMPRESS && !TEST_ONLY)
219 			print_maxverbose("Storage time in seconds %lld\n", control->secs);
220 		if (ENCRYPT)
221 			print_maxverbose("Encryption hash loops %lld\n", control->encloops);
222 	}
223 }
224 
225 static struct option long_options[] = {
226 	{"bzip2",	no_argument,	0,	'b'}, /* 0 */
227 	{"check",	no_argument,	0,	'c'},
228 	{"check",	no_argument,	0,	'C'},
229 	{"decompress",	no_argument,	0,	'd'},
230 	{"delete",	no_argument,	0,	'D'},
231 	{"encrypt",	no_argument,	0,	'e'}, /* 5 */
232 	{"force",	no_argument,	0,	'f'},
233 	{"gzip",	no_argument,	0,	'g'},
234 	{"help",	no_argument,	0,	'h'},
235 	{"hash",	no_argument,	0,	'H'},
236 	{"info",	no_argument,	0,	'i'}, /* 10 */
237 	{"keep-broken",	no_argument,	0,	'k'},
238 	{"keep-broken",	no_argument,	0,	'K'},
239 	{"lzo",		no_argument,	0,	'l'},
240 	{"lzma",       	no_argument,	0,	'/'},
241 	{"level",	optional_argument,	0,	'L'}, /* 15 */
242 	{"license",	no_argument,	0,	'L'},
243 	{"maxram",	required_argument,	0,	'm'},
244 	{"no-compress",	no_argument,	0,	'n'},
245 	{"nice-level",	required_argument,	0,	'N'},
246 	{"outfile",	required_argument,	0,	'o'},
247 	{"outdir",	required_argument,	0,	'O'}, /* 20 */
248 	{"threads",	required_argument,	0,	'p'},
249 	{"progress",	no_argument,	0,	'P'},
250 	{"quiet",	no_argument,	0,	'q'},
251 	{"recursive",	no_argument,	0,	'r'},
252 	{"suffix",	required_argument,	0,	'S'},
253 	{"test",	no_argument,	0,	't'},  /* 25 */
254 	{"threshold",	required_argument,	0,	'T'},
255 	{"unlimited",	no_argument,	0,	'U'},
256 	{"verbose",	no_argument,	0,	'v'},
257 	{"version",	no_argument,	0,	'V'},
258 	{"window",	required_argument,	0,	'w'},  /* 30 */
259 	{"zpaq",	no_argument,	0,	'z'},
260 	{"fast",	no_argument,	0,	'1'},
261 	{"best",	no_argument,	0,	'9'},
262 	{0,	0,	0,	0},
263 };
264 
set_stdout(struct rzip_control * control)265 static void set_stdout(struct rzip_control *control)
266 {
267 	control->flags |= FLAG_STDOUT;
268 	control->outFILE = stdout;
269 	control->msgout = stderr;
270 	register_outputfile(control, control->msgout);
271 }
272 
273 /* Recursively enter all directories, adding all regular files to the dirlist array */
recurse_dirlist(char * indir,char ** dirlist,int * entries)274 static void recurse_dirlist(char *indir, char **dirlist, int *entries)
275 {
276 	char fname[MAX_PATH_LEN];
277 	struct stat istat;
278 	struct dirent *dp;
279 	DIR *dirp;
280 
281 	dirp = opendir(indir);
282 	if (unlikely(!dirp))
283 		failure("Unable to open directory %s\n", indir);
284 	while ((dp = readdir(dirp)) != NULL) {
285 		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
286 			continue;
287 		sprintf(fname, "%s/%s", indir, dp->d_name);
288 		if (unlikely(stat(fname, &istat)))
289 			failure("Unable to stat file %s\n", fname);
290 		if (S_ISDIR(istat.st_mode)) {
291 			recurse_dirlist(fname, dirlist, entries);
292 			continue;
293 		}
294 		if (!S_ISREG(istat.st_mode)) {
295 			print_err("Not regular file %s\n", fname);
296 			continue;
297 		}
298 		print_maxverbose("Added file %s\n", fname);
299 		*dirlist = realloc(*dirlist, MAX_PATH_LEN * (*entries + 1));
300 		strcpy(*dirlist + MAX_PATH_LEN * (*entries)++, fname);
301 	}
302 	closedir(dirp);
303 }
304 
305 static const char *loptions = "bcCdDefghHiKlL:nN:o:O:p:PqrS:tTUm:vVw:z?";
306 static const char *coptions = "bcCdefghHikKlLnN:o:O:p:PrS:tTUm:vVw:z?123456789";
307 
main(int argc,char * argv[])308 int main(int argc, char *argv[])
309 {
310 	bool lrzcat = false, compat = false, recurse = false;
311 	struct timeval start_time, end_time;
312 	struct sigaction handler;
313 	double seconds,total_time; // for timers
314 	int c, i;
315 	int hours,minutes;
316 	extern int optind;
317 	char *eptr, *av; /* for environment */
318 
319         control = &base_control;
320 
321 	initialise_control(control);
322 
323 	av = basename(argv[0]);
324 	if (!strcmp(av, "lrunzip"))
325 		control->flags |= FLAG_DECOMPRESS;
326 	else if (!strcmp(av, "lrzcat")) {
327 		control->flags |= FLAG_DECOMPRESS | FLAG_STDOUT;
328 		lrzcat = true;
329 	} else if (!strcmp(av, "lrz")) {
330 		/* Called in gzip compatible command line mode */
331 		control->flags &= ~FLAG_SHOW_PROGRESS;
332 		control->nice_val = 0;
333 		control->flags &= ~FLAG_KEEP_FILES;
334 		compat = true;
335 		long_options[1].name = "stdout";
336 		long_options[11].name = "keep";
337 	}
338 
339 	/* generate crc table */
340 	CrcGenerateTable();
341 
342 	/* Get Preloaded Defaults from lrzip.conf
343 	 * Look in ., $HOME/.lrzip/, /etc/lrzip.
344 	 * If LRZIP=NOCONFIG is set, then ignore config
345 	 */
346 	eptr = getenv("LRZIP");
347 	if (eptr == NULL)
348 		read_config(control);
349 	else if (!strstr(eptr,"NOCONFIG"))
350 		read_config(control);
351 
352 	while ((c = getopt_long(argc, argv, compat ? coptions : loptions, long_options, &i)) != -1) {
353 		switch (c) {
354 		case 'b':
355 			if (control->flags & FLAG_NOT_LZMA)
356 				failure("Can only use one of -l, -b, -g, -z or -n\n");
357 			control->flags |= FLAG_BZIP2_COMPRESS;
358 			break;
359 		case 'c':
360 			if (compat) {
361 				control->flags |= FLAG_KEEP_FILES;
362 				set_stdout(control);
363 				break;
364 			}
365 		case 'C':
366 			control->flags |= FLAG_CHECK;
367 			control->flags |= FLAG_HASH;
368 			break;
369 		case 'd':
370 			control->flags |= FLAG_DECOMPRESS;
371 			break;
372 		case 'D':
373 			control->flags &= ~FLAG_KEEP_FILES;
374 			break;
375 		case 'e':
376 			control->flags |= FLAG_ENCRYPT;
377 			break;
378 		case 'f':
379 			control->flags |= FLAG_FORCE_REPLACE;
380 			break;
381 		case 'g':
382 			if (control->flags & FLAG_NOT_LZMA)
383 				failure("Can only use one of -l, -b, -g, -z or -n\n");
384 			control->flags |= FLAG_ZLIB_COMPRESS;
385 			break;
386 		case 'h':
387 		case '?':
388 			usage(compat);
389 			return -1;
390 		case 'H':
391 			control->flags |= FLAG_HASH;
392 			break;
393 		case 'i':
394 			control->flags |= FLAG_INFO;
395 			break;
396 		case 'k':
397 			if (compat) {
398 				control->flags |= FLAG_KEEP_FILES;
399 				break;
400 			}
401 		case 'K':
402 			control->flags |= FLAG_KEEP_BROKEN;
403 			break;
404 		case 'l':
405 			if (control->flags & FLAG_NOT_LZMA)
406 				failure("Can only use one of -l, -b, -g, -z or -n\n");
407 			control->flags |= FLAG_LZO_COMPRESS;
408 			break;
409 		case 'L':
410 			if (compat) {
411 				license();
412 				exit(0);
413 			}
414 			control->compression_level = atoi(optarg);
415 			if (control->compression_level < 1 || control->compression_level > 9)
416 				failure("Invalid compression level (must be 1-9)\n");
417 			break;
418 		case 'm':
419 			control->ramsize = atol(optarg) * 1024 * 1024 * 100;
420 			break;
421 		case 'n':
422 			if (control->flags & FLAG_NOT_LZMA)
423 				failure("Can only use one of -l, -b, -g, -z or -n\n");
424 			control->flags |= FLAG_NO_COMPRESS;
425 			break;
426 		case 'N':
427 			control->nice_val = atoi(optarg);
428 			if (control->nice_val < -20 || control->nice_val > 19)
429 				failure("Invalid nice value (must be -20..19)\n");
430 			break;
431 		case 'o':
432 			if (control->outdir)
433 				failure("Cannot have -o and -O together\n");
434 			if (unlikely(STDOUT))
435 				failure("Cannot specify an output filename when outputting to stdout\n");
436 			control->outname = optarg;
437 			control->suffix = "";
438 			break;
439 		case 'O':
440 			if (control->outname)	/* can't mix -o and -O */
441 				failure("Cannot have options -o and -O together\n");
442 			if (unlikely(STDOUT))
443 				failure("Cannot specify an output directory when outputting to stdout\n");
444 			control->outdir = malloc(strlen(optarg) + 2);
445 			if (control->outdir == NULL)
446 				fatal("Failed to allocate for outdir\n");
447 			strcpy(control->outdir,optarg);
448 			if (strcmp(optarg+strlen(optarg) - 1, "/")) 	/* need a trailing slash */
449 				strcat(control->outdir, "/");
450 			break;
451 		case 'p':
452 			control->threads = atoi(optarg);
453 			if (control->threads < 1)
454 				failure("Must have at least one thread\n");
455 			break;
456 		case 'P':
457 			control->flags |= FLAG_SHOW_PROGRESS;
458 			break;
459 		case 'q':
460 			control->flags &= ~FLAG_SHOW_PROGRESS;
461 			break;
462 		case 'r':
463 			recurse = true;
464 			break;
465 		case 'S':
466 			if (control->outname)
467 				failure("Specified output filename already, can't specify an extension.\n");
468 			if (unlikely(STDOUT))
469 				failure("Cannot specify a filename suffix when outputting to stdout\n");
470 			control->suffix = optarg;
471 			break;
472 		case 't':
473 			if (control->outname)
474 				failure("Cannot specify an output file name when just testing.\n");
475 			if (compat)
476 				control->flags |= FLAG_KEEP_FILES;
477 			if (!KEEP_FILES)
478 				failure("Doubt that you want to delete a file when just testing.\n");
479 			control->flags |= FLAG_TEST_ONLY;
480 			break;
481 		case 'T':
482 			control->flags &= ~FLAG_THRESHOLD;
483 			break;
484 		case 'U':
485 			control->flags |= FLAG_UNLIMITED;
486 			break;
487 		case 'v':
488 			/* set verbosity flag */
489 			if (!(control->flags & FLAG_SHOW_PROGRESS))
490 				control->flags |= FLAG_SHOW_PROGRESS;
491 			else if (!(control->flags & FLAG_VERBOSITY) && !(control->flags & FLAG_VERBOSITY_MAX))
492 				control->flags |= FLAG_VERBOSITY;
493 			else if ((control->flags & FLAG_VERBOSITY)) {
494 				control->flags &= ~FLAG_VERBOSITY;
495 				control->flags |= FLAG_VERBOSITY_MAX;
496 			}
497 			break;
498 		case 'V':
499 			print_output("lrzip version %s\n", PACKAGE_VERSION);
500 			exit(0);
501 			break;
502 		case 'w':
503 			control->window = atol(optarg);
504 			break;
505 		case 'z':
506 			if (control->flags & FLAG_NOT_LZMA)
507 				failure("Can only use one of -l, -b, -g, -z or -n\n");
508 			control->flags |= FLAG_ZPAQ_COMPRESS;
509 			break;
510 		case '1':
511 		case '2':
512 		case '3':
513 		case '4':
514 		case '5':
515 		case '6':
516 		case '7':
517 		case '8':
518 		case '9':
519 			control->compression_level = c - '0';
520 			break;
521 		}
522 	}
523 
524 	argc -= optind;
525 	argv += optind;
526 
527 	if (control->outname) {
528 		if (argc > 1)
529 			failure("Cannot specify output filename with more than 1 file\n");
530 		if (recurse)
531 			failure("Cannot specify output filename with recursive\n");
532 	}
533 
534 	if (VERBOSE && !SHOW_PROGRESS) {
535 		print_err("Cannot have -v and -q options. -v wins.\n");
536 		control->flags |= FLAG_SHOW_PROGRESS;
537 	}
538 
539 	if (UNLIMITED && control->window) {
540 		print_err("If -U used, cannot specify a window size with -w.\n");
541 		control->window = 0;
542 	}
543 
544 	if (argc < 1)
545 		control->flags |= FLAG_STDIN;
546 
547 	if (UNLIMITED && STDIN) {
548 		print_err("Cannot have -U and stdin, unlimited mode disabled.\n");
549 		control->flags &= ~FLAG_UNLIMITED;
550 	}
551 
552 	setup_overhead(control);
553 
554 	/* Set the main nice value to half that of the backend threads since
555 	 * the rzip stage is usually the rate limiting step */
556 	if (control->nice_val > 0 && !NO_COMPRESS) {
557 		if (unlikely(setpriority(PRIO_PROCESS, 0, control->nice_val / 2) == -1))
558 			print_err("Warning, unable to set nice value\n");
559 	} else {
560 		if (unlikely(setpriority(PRIO_PROCESS, 0, control->nice_val) == -1))
561 			print_err("Warning, unable to set nice value\n");
562 	}
563 
564 	/* One extra iteration for the case of no parameters means we will default to stdin/out */
565 	for (i = 0; i <= argc; i++) {
566 		char *dirlist = NULL, *infile = NULL;
567 		int direntries = 0, curentry = 0;
568 
569 		if (i < argc)
570 			infile = argv[i];
571 		else if (!(i == 0 && STDIN))
572 			break;
573 		if (infile) {
574 			if ((strcmp(infile, "-") == 0))
575 				control->flags |= FLAG_STDIN;
576 			else {
577 				bool isdir = false;
578 				struct stat istat;
579 
580 				if (unlikely(stat(infile, &istat)))
581 					failure("Failed to stat %s\n", infile);
582 				isdir = S_ISDIR(istat.st_mode);
583 				if (!recurse && (isdir || !S_ISREG(istat.st_mode))) {
584 					failure("lrzip only works directly on regular FILES.\n"
585 					"Use -r recursive, lrztar or pipe through tar for compressing directories.\n");
586 				}
587 				if (recurse && !isdir)
588 					failure("%s not a directory, -r recursive needs a directory\n", infile);
589 			}
590 		}
591 
592 		if (recurse) {
593 			if (unlikely(STDIN || STDOUT))
594 				failure("Cannot use -r recursive with STDIO\n");
595 			recurse_dirlist(infile, &dirlist, &direntries);
596 		}
597 
598 		if (INFO && STDIN)
599 			failure("Will not get file info from STDIN\n");
600 recursion:
601 		if (recurse) {
602 			if (curentry >= direntries) {
603 				infile = NULL;
604 				continue;
605 			}
606 			infile = dirlist + MAX_PATH_LEN * curentry++;
607 		}
608 		control->infile = infile;
609 
610 		/* If no output filename is specified, and we're using
611 		 * stdin, use stdout */
612 		if ((control->outname && (strcmp(control->outname, "-") == 0)) ||
613 		    (!control->outname && STDIN) || lrzcat)
614 				set_stdout(control);
615 
616 		if (lrzcat) {
617 			control->msgout = stderr;
618 			control->outFILE = stdout;
619 			register_outputfile(control, control->msgout);
620 		}
621 
622 		if (!STDOUT) {
623 			control->msgout = stdout;
624 			register_outputfile(control, control->msgout);
625 		}
626 
627 		if (STDIN)
628 			control->inFILE = stdin;
629 		/* Implement signal handler only once flags are set */
630 		sigemptyset(&handler.sa_mask);
631 		handler.sa_flags = 0;
632 		handler.sa_handler = &sighandler;
633 		sigaction(SIGTERM, &handler, 0);
634 		sigaction(SIGINT, &handler, 0);
635 
636 		if (!FORCE_REPLACE) {
637 			if (STDIN && isatty(fileno((FILE *)stdin))) {
638 				print_err("Will not read stdin from a terminal. Use -f to override.\n");
639 				usage(compat);
640 				exit (1);
641 			}
642 			if (!TEST_ONLY && STDOUT && isatty(fileno((FILE *)stdout)) && !compat) {
643 				print_err("Will not write stdout to a terminal. Use -f to override.\n");
644 				usage(compat);
645 				exit (1);
646 			}
647 		}
648 
649 		if (CHECK_FILE) {
650 			if (!DECOMPRESS) {
651 				print_err("Can only check file written on decompression.\n");
652 				control->flags &= ~FLAG_CHECK;
653 			} else if (STDOUT) {
654 				print_err("Can't check file written when writing to stdout. Checking disabled.\n");
655 				control->flags &= ~FLAG_CHECK;
656 			}
657 		}
658 
659 		setup_ram(control);
660 		show_summary();
661 
662 		gettimeofday(&start_time, NULL);
663 
664 		if (unlikely(STDIN && ENCRYPT))
665 			failure("Unable to work from STDIN while reading password\n");
666 
667 		memcpy(&local_control, &base_control, sizeof(rzip_control));
668 		if (DECOMPRESS || TEST_ONLY)
669 			decompress_file(&local_control);
670 		else if (INFO)
671 			get_fileinfo(&local_control);
672 		else
673 			compress_file(&local_control);
674 
675 		/* compute total time */
676 		gettimeofday(&end_time, NULL);
677 		total_time = (end_time.tv_sec + (double)end_time.tv_usec / 1000000) -
678 			      (start_time.tv_sec + (double)start_time.tv_usec / 1000000);
679 		hours = (int)total_time / 3600;
680 		minutes = (int)(total_time / 60) % 60;
681 		seconds = total_time - hours * 3600 - minutes * 60;
682 		if (!INFO)
683 			print_progress("Total time: %02d:%02d:%05.2f\n", hours, minutes, seconds);
684 		if (recurse)
685 			goto recursion;
686 	}
687 
688 	return 0;
689 }
690