xref: /dragonfly/contrib/xz/src/xz/args.c (revision e151908b)
12940b44dSPeter Avalos ///////////////////////////////////////////////////////////////////////////////
22940b44dSPeter Avalos //
32940b44dSPeter Avalos /// \file       args.c
42940b44dSPeter Avalos /// \brief      Argument parsing
52940b44dSPeter Avalos ///
62940b44dSPeter Avalos /// \note       Filter-specific options parsing is in options.c.
72940b44dSPeter Avalos //
82940b44dSPeter Avalos //  Author:     Lasse Collin
92940b44dSPeter Avalos //
102940b44dSPeter Avalos //  This file has been put into the public domain.
112940b44dSPeter Avalos //  You can do whatever you want with this file.
122940b44dSPeter Avalos //
132940b44dSPeter Avalos ///////////////////////////////////////////////////////////////////////////////
142940b44dSPeter Avalos 
152940b44dSPeter Avalos #include "private.h"
162940b44dSPeter Avalos 
172940b44dSPeter Avalos #include "getopt.h"
182940b44dSPeter Avalos #include <ctype.h>
192940b44dSPeter Avalos 
202940b44dSPeter Avalos 
212940b44dSPeter Avalos bool opt_stdout = false;
222940b44dSPeter Avalos bool opt_force = false;
232940b44dSPeter Avalos bool opt_keep_original = false;
242940b44dSPeter Avalos bool opt_robot = false;
2515ab8c86SJohn Marino bool opt_ignore_check = false;
262940b44dSPeter Avalos 
272940b44dSPeter Avalos // We don't modify or free() this, but we need to assign it in some
282940b44dSPeter Avalos // non-const pointers.
292940b44dSPeter Avalos const char stdin_filename[] = "(stdin)";
302940b44dSPeter Avalos 
312940b44dSPeter Avalos 
322940b44dSPeter Avalos /// Parse and set the memory usage limit for compression and/or decompression.
332940b44dSPeter Avalos static void
parse_memlimit(const char * name,const char * name_percentage,char * str,bool set_compress,bool set_decompress)342940b44dSPeter Avalos parse_memlimit(const char *name, const char *name_percentage, char *str,
352940b44dSPeter Avalos 		bool set_compress, bool set_decompress)
362940b44dSPeter Avalos {
372940b44dSPeter Avalos 	bool is_percentage = false;
382940b44dSPeter Avalos 	uint64_t value;
392940b44dSPeter Avalos 
402940b44dSPeter Avalos 	const size_t len = strlen(str);
412940b44dSPeter Avalos 	if (len > 0 && str[len - 1] == '%') {
422940b44dSPeter Avalos 		str[len - 1] = '\0';
432940b44dSPeter Avalos 		is_percentage = true;
442940b44dSPeter Avalos 		value = str_to_uint64(name_percentage, str, 1, 100);
452940b44dSPeter Avalos 	} else {
462940b44dSPeter Avalos 		// On 32-bit systems, SIZE_MAX would make more sense than
472940b44dSPeter Avalos 		// UINT64_MAX. But use UINT64_MAX still so that scripts
482940b44dSPeter Avalos 		// that assume > 4 GiB values don't break.
492940b44dSPeter Avalos 		value = str_to_uint64(name, str, 0, UINT64_MAX);
502940b44dSPeter Avalos 	}
512940b44dSPeter Avalos 
522940b44dSPeter Avalos 	hardware_memlimit_set(
532940b44dSPeter Avalos 			value, set_compress, set_decompress, is_percentage);
542940b44dSPeter Avalos 	return;
552940b44dSPeter Avalos }
562940b44dSPeter Avalos 
572940b44dSPeter Avalos 
582940b44dSPeter Avalos static void
parse_block_list(char * str)5915ab8c86SJohn Marino parse_block_list(char *str)
6015ab8c86SJohn Marino {
6115ab8c86SJohn Marino 	// It must be non-empty and not begin with a comma.
6215ab8c86SJohn Marino 	if (str[0] == '\0' || str[0] == ',')
6315ab8c86SJohn Marino 		message_fatal(_("%s: Invalid argument to --block-list"), str);
6415ab8c86SJohn Marino 
6515ab8c86SJohn Marino 	// Count the number of comma-separated strings.
6615ab8c86SJohn Marino 	size_t count = 1;
6715ab8c86SJohn Marino 	for (size_t i = 0; str[i] != '\0'; ++i)
6815ab8c86SJohn Marino 		if (str[i] == ',')
6915ab8c86SJohn Marino 			++count;
7015ab8c86SJohn Marino 
7115ab8c86SJohn Marino 	// Prevent an unlikely integer overflow.
7215ab8c86SJohn Marino 	if (count > SIZE_MAX / sizeof(uint64_t) - 1)
7315ab8c86SJohn Marino 		message_fatal(_("%s: Too many arguments to --block-list"),
7415ab8c86SJohn Marino 				str);
7515ab8c86SJohn Marino 
7615ab8c86SJohn Marino 	// Allocate memory to hold all the sizes specified.
7715ab8c86SJohn Marino 	// If --block-list was specified already, its value is forgotten.
7815ab8c86SJohn Marino 	free(opt_block_list);
7915ab8c86SJohn Marino 	opt_block_list = xmalloc((count + 1) * sizeof(uint64_t));
8015ab8c86SJohn Marino 
8115ab8c86SJohn Marino 	for (size_t i = 0; i < count; ++i) {
8215ab8c86SJohn Marino 		// Locate the next comma and replace it with \0.
8315ab8c86SJohn Marino 		char *p = strchr(str, ',');
8415ab8c86SJohn Marino 		if (p != NULL)
8515ab8c86SJohn Marino 			*p = '\0';
8615ab8c86SJohn Marino 
8715ab8c86SJohn Marino 		if (str[0] == '\0') {
8815ab8c86SJohn Marino 			// There is no string, that is, a comma follows
8915ab8c86SJohn Marino 			// another comma. Use the previous value.
9015ab8c86SJohn Marino 			//
91*e151908bSDaniel Fojt 			// NOTE: We checked earlier that the first char
9215ab8c86SJohn Marino 			// of the whole list cannot be a comma.
9315ab8c86SJohn Marino 			assert(i > 0);
9415ab8c86SJohn Marino 			opt_block_list[i] = opt_block_list[i - 1];
9515ab8c86SJohn Marino 		} else {
9615ab8c86SJohn Marino 			opt_block_list[i] = str_to_uint64("block-list", str,
9715ab8c86SJohn Marino 					0, UINT64_MAX);
9815ab8c86SJohn Marino 
9915ab8c86SJohn Marino 			// Zero indicates no more new Blocks.
10015ab8c86SJohn Marino 			if (opt_block_list[i] == 0) {
10115ab8c86SJohn Marino 				if (i + 1 != count)
10215ab8c86SJohn Marino 					message_fatal(_("0 can only be used "
10315ab8c86SJohn Marino 							"as the last element "
10415ab8c86SJohn Marino 							"in --block-list"));
10515ab8c86SJohn Marino 
10615ab8c86SJohn Marino 				opt_block_list[i] = UINT64_MAX;
10715ab8c86SJohn Marino 			}
10815ab8c86SJohn Marino 		}
10915ab8c86SJohn Marino 
11015ab8c86SJohn Marino 		str = p + 1;
11115ab8c86SJohn Marino 	}
11215ab8c86SJohn Marino 
11315ab8c86SJohn Marino 	// Terminate the array.
11415ab8c86SJohn Marino 	opt_block_list[count] = 0;
11515ab8c86SJohn Marino 	return;
11615ab8c86SJohn Marino }
11715ab8c86SJohn Marino 
11815ab8c86SJohn Marino 
11915ab8c86SJohn Marino static void
parse_real(args_info * args,int argc,char ** argv)1202940b44dSPeter Avalos parse_real(args_info *args, int argc, char **argv)
1212940b44dSPeter Avalos {
1222940b44dSPeter Avalos 	enum {
1232940b44dSPeter Avalos 		OPT_X86 = INT_MIN,
1242940b44dSPeter Avalos 		OPT_POWERPC,
1252940b44dSPeter Avalos 		OPT_IA64,
1262940b44dSPeter Avalos 		OPT_ARM,
1272940b44dSPeter Avalos 		OPT_ARMTHUMB,
1282940b44dSPeter Avalos 		OPT_SPARC,
1292940b44dSPeter Avalos 		OPT_DELTA,
1302940b44dSPeter Avalos 		OPT_LZMA1,
1312940b44dSPeter Avalos 		OPT_LZMA2,
1322940b44dSPeter Avalos 
13315ab8c86SJohn Marino 		OPT_SINGLE_STREAM,
1342940b44dSPeter Avalos 		OPT_NO_SPARSE,
1352940b44dSPeter Avalos 		OPT_FILES,
1362940b44dSPeter Avalos 		OPT_FILES0,
13715ab8c86SJohn Marino 		OPT_BLOCK_SIZE,
13815ab8c86SJohn Marino 		OPT_BLOCK_LIST,
1392940b44dSPeter Avalos 		OPT_MEM_COMPRESS,
1402940b44dSPeter Avalos 		OPT_MEM_DECOMPRESS,
1412940b44dSPeter Avalos 		OPT_NO_ADJUST,
1422940b44dSPeter Avalos 		OPT_INFO_MEMORY,
1432940b44dSPeter Avalos 		OPT_ROBOT,
14415ab8c86SJohn Marino 		OPT_FLUSH_TIMEOUT,
14515ab8c86SJohn Marino 		OPT_IGNORE_CHECK,
1462940b44dSPeter Avalos 	};
1472940b44dSPeter Avalos 
1482940b44dSPeter Avalos 	static const char short_opts[]
1492940b44dSPeter Avalos 			= "cC:defF:hHlkM:qQrS:tT:vVz0123456789";
1502940b44dSPeter Avalos 
1512940b44dSPeter Avalos 	static const struct option long_opts[] = {
1522940b44dSPeter Avalos 		// Operation mode
1532940b44dSPeter Avalos 		{ "compress",     no_argument,       NULL,  'z' },
1542940b44dSPeter Avalos 		{ "decompress",   no_argument,       NULL,  'd' },
1552940b44dSPeter Avalos 		{ "uncompress",   no_argument,       NULL,  'd' },
1562940b44dSPeter Avalos 		{ "test",         no_argument,       NULL,  't' },
1572940b44dSPeter Avalos 		{ "list",         no_argument,       NULL,  'l' },
1582940b44dSPeter Avalos 
1592940b44dSPeter Avalos 		// Operation modifiers
1602940b44dSPeter Avalos 		{ "keep",         no_argument,       NULL,  'k' },
1612940b44dSPeter Avalos 		{ "force",        no_argument,       NULL,  'f' },
1622940b44dSPeter Avalos 		{ "stdout",       no_argument,       NULL,  'c' },
1632940b44dSPeter Avalos 		{ "to-stdout",    no_argument,       NULL,  'c' },
16415ab8c86SJohn Marino 		{ "single-stream", no_argument,      NULL,  OPT_SINGLE_STREAM },
1652940b44dSPeter Avalos 		{ "no-sparse",    no_argument,       NULL,  OPT_NO_SPARSE },
1662940b44dSPeter Avalos 		{ "suffix",       required_argument, NULL,  'S' },
1672940b44dSPeter Avalos 		// { "recursive",      no_argument,       NULL,  'r' }, // TODO
1682940b44dSPeter Avalos 		{ "files",        optional_argument, NULL,  OPT_FILES },
1692940b44dSPeter Avalos 		{ "files0",       optional_argument, NULL,  OPT_FILES0 },
1702940b44dSPeter Avalos 
1712940b44dSPeter Avalos 		// Basic compression settings
1722940b44dSPeter Avalos 		{ "format",       required_argument, NULL,  'F' },
1732940b44dSPeter Avalos 		{ "check",        required_argument, NULL,  'C' },
17415ab8c86SJohn Marino 		{ "ignore-check", no_argument,       NULL,  OPT_IGNORE_CHECK },
17515ab8c86SJohn Marino 		{ "block-size",   required_argument, NULL,  OPT_BLOCK_SIZE },
17615ab8c86SJohn Marino 		{ "block-list",  required_argument, NULL,  OPT_BLOCK_LIST },
1772940b44dSPeter Avalos 		{ "memlimit-compress",   required_argument, NULL, OPT_MEM_COMPRESS },
1782940b44dSPeter Avalos 		{ "memlimit-decompress", required_argument, NULL, OPT_MEM_DECOMPRESS },
1792940b44dSPeter Avalos 		{ "memlimit",     required_argument, NULL,  'M' },
1802940b44dSPeter Avalos 		{ "memory",       required_argument, NULL,  'M' }, // Old alias
1812940b44dSPeter Avalos 		{ "no-adjust",    no_argument,       NULL,  OPT_NO_ADJUST },
1822940b44dSPeter Avalos 		{ "threads",      required_argument, NULL,  'T' },
18315ab8c86SJohn Marino 		{ "flush-timeout", required_argument, NULL, OPT_FLUSH_TIMEOUT },
1842940b44dSPeter Avalos 
1852940b44dSPeter Avalos 		{ "extreme",      no_argument,       NULL,  'e' },
1862940b44dSPeter Avalos 		{ "fast",         no_argument,       NULL,  '0' },
1872940b44dSPeter Avalos 		{ "best",         no_argument,       NULL,  '9' },
1882940b44dSPeter Avalos 
1892940b44dSPeter Avalos 		// Filters
1902940b44dSPeter Avalos 		{ "lzma1",        optional_argument, NULL,  OPT_LZMA1 },
1912940b44dSPeter Avalos 		{ "lzma2",        optional_argument, NULL,  OPT_LZMA2 },
1922940b44dSPeter Avalos 		{ "x86",          optional_argument, NULL,  OPT_X86 },
1932940b44dSPeter Avalos 		{ "powerpc",      optional_argument, NULL,  OPT_POWERPC },
1942940b44dSPeter Avalos 		{ "ia64",         optional_argument, NULL,  OPT_IA64 },
1952940b44dSPeter Avalos 		{ "arm",          optional_argument, NULL,  OPT_ARM },
1962940b44dSPeter Avalos 		{ "armthumb",     optional_argument, NULL,  OPT_ARMTHUMB },
1972940b44dSPeter Avalos 		{ "sparc",        optional_argument, NULL,  OPT_SPARC },
1982940b44dSPeter Avalos 		{ "delta",        optional_argument, NULL,  OPT_DELTA },
1992940b44dSPeter Avalos 
2002940b44dSPeter Avalos 		// Other options
2012940b44dSPeter Avalos 		{ "quiet",        no_argument,       NULL,  'q' },
2022940b44dSPeter Avalos 		{ "verbose",      no_argument,       NULL,  'v' },
2032940b44dSPeter Avalos 		{ "no-warn",      no_argument,       NULL,  'Q' },
2042940b44dSPeter Avalos 		{ "robot",        no_argument,       NULL,  OPT_ROBOT },
2052940b44dSPeter Avalos 		{ "info-memory",  no_argument,       NULL,  OPT_INFO_MEMORY },
2062940b44dSPeter Avalos 		{ "help",         no_argument,       NULL,  'h' },
2072940b44dSPeter Avalos 		{ "long-help",    no_argument,       NULL,  'H' },
2082940b44dSPeter Avalos 		{ "version",      no_argument,       NULL,  'V' },
2092940b44dSPeter Avalos 
2102940b44dSPeter Avalos 		{ NULL,           0,                 NULL,   0 }
2112940b44dSPeter Avalos 	};
2122940b44dSPeter Avalos 
2132940b44dSPeter Avalos 	int c;
2142940b44dSPeter Avalos 
2152940b44dSPeter Avalos 	while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL))
2162940b44dSPeter Avalos 			!= -1) {
2172940b44dSPeter Avalos 		switch (c) {
2182940b44dSPeter Avalos 		// Compression preset (also for decompression if --format=raw)
2192940b44dSPeter Avalos 		case '0': case '1': case '2': case '3': case '4':
2202940b44dSPeter Avalos 		case '5': case '6': case '7': case '8': case '9':
221*e151908bSDaniel Fojt 			coder_set_preset((uint32_t)(c - '0'));
2222940b44dSPeter Avalos 			break;
2232940b44dSPeter Avalos 
2242940b44dSPeter Avalos 		// --memlimit-compress
2252940b44dSPeter Avalos 		case OPT_MEM_COMPRESS:
2262940b44dSPeter Avalos 			parse_memlimit("memlimit-compress",
2272940b44dSPeter Avalos 					"memlimit-compress%", optarg,
2282940b44dSPeter Avalos 					true, false);
2292940b44dSPeter Avalos 			break;
2302940b44dSPeter Avalos 
2312940b44dSPeter Avalos 		// --memlimit-decompress
2322940b44dSPeter Avalos 		case OPT_MEM_DECOMPRESS:
2332940b44dSPeter Avalos 			parse_memlimit("memlimit-decompress",
2342940b44dSPeter Avalos 					"memlimit-decompress%", optarg,
2352940b44dSPeter Avalos 					false, true);
2362940b44dSPeter Avalos 			break;
2372940b44dSPeter Avalos 
2382940b44dSPeter Avalos 		// --memlimit
2392940b44dSPeter Avalos 		case 'M':
2402940b44dSPeter Avalos 			parse_memlimit("memlimit", "memlimit%", optarg,
2412940b44dSPeter Avalos 					true, true);
2422940b44dSPeter Avalos 			break;
2432940b44dSPeter Avalos 
2442940b44dSPeter Avalos 		// --suffix
2452940b44dSPeter Avalos 		case 'S':
2462940b44dSPeter Avalos 			suffix_set(optarg);
2472940b44dSPeter Avalos 			break;
2482940b44dSPeter Avalos 
2492940b44dSPeter Avalos 		case 'T':
25015ab8c86SJohn Marino 			// The max is from src/liblzma/common/common.h.
25115ab8c86SJohn Marino 			hardware_threads_set(str_to_uint64("threads",
25215ab8c86SJohn Marino 					optarg, 0, 16384));
2532940b44dSPeter Avalos 			break;
2542940b44dSPeter Avalos 
2552940b44dSPeter Avalos 		// --version
2562940b44dSPeter Avalos 		case 'V':
2572940b44dSPeter Avalos 			// This doesn't return.
2582940b44dSPeter Avalos 			message_version();
2592940b44dSPeter Avalos 
2602940b44dSPeter Avalos 		// --stdout
2612940b44dSPeter Avalos 		case 'c':
2622940b44dSPeter Avalos 			opt_stdout = true;
2632940b44dSPeter Avalos 			break;
2642940b44dSPeter Avalos 
2652940b44dSPeter Avalos 		// --decompress
2662940b44dSPeter Avalos 		case 'd':
2672940b44dSPeter Avalos 			opt_mode = MODE_DECOMPRESS;
2682940b44dSPeter Avalos 			break;
2692940b44dSPeter Avalos 
2702940b44dSPeter Avalos 		// --extreme
2712940b44dSPeter Avalos 		case 'e':
2722940b44dSPeter Avalos 			coder_set_extreme();
2732940b44dSPeter Avalos 			break;
2742940b44dSPeter Avalos 
2752940b44dSPeter Avalos 		// --force
2762940b44dSPeter Avalos 		case 'f':
2772940b44dSPeter Avalos 			opt_force = true;
2782940b44dSPeter Avalos 			break;
2792940b44dSPeter Avalos 
2802940b44dSPeter Avalos 		// --info-memory
2812940b44dSPeter Avalos 		case OPT_INFO_MEMORY:
2822940b44dSPeter Avalos 			// This doesn't return.
2832940b44dSPeter Avalos 			hardware_memlimit_show();
2842940b44dSPeter Avalos 
2852940b44dSPeter Avalos 		// --help
2862940b44dSPeter Avalos 		case 'h':
2872940b44dSPeter Avalos 			// This doesn't return.
2882940b44dSPeter Avalos 			message_help(false);
2892940b44dSPeter Avalos 
2902940b44dSPeter Avalos 		// --long-help
2912940b44dSPeter Avalos 		case 'H':
2922940b44dSPeter Avalos 			// This doesn't return.
2932940b44dSPeter Avalos 			message_help(true);
2942940b44dSPeter Avalos 
2952940b44dSPeter Avalos 		// --list
2962940b44dSPeter Avalos 		case 'l':
2972940b44dSPeter Avalos 			opt_mode = MODE_LIST;
2982940b44dSPeter Avalos 			break;
2992940b44dSPeter Avalos 
3002940b44dSPeter Avalos 		// --keep
3012940b44dSPeter Avalos 		case 'k':
3022940b44dSPeter Avalos 			opt_keep_original = true;
3032940b44dSPeter Avalos 			break;
3042940b44dSPeter Avalos 
3052940b44dSPeter Avalos 		// --quiet
3062940b44dSPeter Avalos 		case 'q':
3072940b44dSPeter Avalos 			message_verbosity_decrease();
3082940b44dSPeter Avalos 			break;
3092940b44dSPeter Avalos 
3102940b44dSPeter Avalos 		case 'Q':
3112940b44dSPeter Avalos 			set_exit_no_warn();
3122940b44dSPeter Avalos 			break;
3132940b44dSPeter Avalos 
3142940b44dSPeter Avalos 		case 't':
3152940b44dSPeter Avalos 			opt_mode = MODE_TEST;
3162940b44dSPeter Avalos 			break;
3172940b44dSPeter Avalos 
3182940b44dSPeter Avalos 		// --verbose
3192940b44dSPeter Avalos 		case 'v':
3202940b44dSPeter Avalos 			message_verbosity_increase();
3212940b44dSPeter Avalos 			break;
3222940b44dSPeter Avalos 
3232940b44dSPeter Avalos 		// --robot
3242940b44dSPeter Avalos 		case OPT_ROBOT:
3252940b44dSPeter Avalos 			opt_robot = true;
3262940b44dSPeter Avalos 
3272940b44dSPeter Avalos 			// This is to make sure that floating point numbers
3282940b44dSPeter Avalos 			// always have a dot as decimal separator.
3292940b44dSPeter Avalos 			setlocale(LC_NUMERIC, "C");
3302940b44dSPeter Avalos 			break;
3312940b44dSPeter Avalos 
3322940b44dSPeter Avalos 		case 'z':
3332940b44dSPeter Avalos 			opt_mode = MODE_COMPRESS;
3342940b44dSPeter Avalos 			break;
3352940b44dSPeter Avalos 
3362940b44dSPeter Avalos 		// Filter setup
3372940b44dSPeter Avalos 
3382940b44dSPeter Avalos 		case OPT_X86:
3392940b44dSPeter Avalos 			coder_add_filter(LZMA_FILTER_X86,
3402940b44dSPeter Avalos 					options_bcj(optarg));
3412940b44dSPeter Avalos 			break;
3422940b44dSPeter Avalos 
3432940b44dSPeter Avalos 		case OPT_POWERPC:
3442940b44dSPeter Avalos 			coder_add_filter(LZMA_FILTER_POWERPC,
3452940b44dSPeter Avalos 					options_bcj(optarg));
3462940b44dSPeter Avalos 			break;
3472940b44dSPeter Avalos 
3482940b44dSPeter Avalos 		case OPT_IA64:
3492940b44dSPeter Avalos 			coder_add_filter(LZMA_FILTER_IA64,
3502940b44dSPeter Avalos 					options_bcj(optarg));
3512940b44dSPeter Avalos 			break;
3522940b44dSPeter Avalos 
3532940b44dSPeter Avalos 		case OPT_ARM:
3542940b44dSPeter Avalos 			coder_add_filter(LZMA_FILTER_ARM,
3552940b44dSPeter Avalos 					options_bcj(optarg));
3562940b44dSPeter Avalos 			break;
3572940b44dSPeter Avalos 
3582940b44dSPeter Avalos 		case OPT_ARMTHUMB:
3592940b44dSPeter Avalos 			coder_add_filter(LZMA_FILTER_ARMTHUMB,
3602940b44dSPeter Avalos 					options_bcj(optarg));
3612940b44dSPeter Avalos 			break;
3622940b44dSPeter Avalos 
3632940b44dSPeter Avalos 		case OPT_SPARC:
3642940b44dSPeter Avalos 			coder_add_filter(LZMA_FILTER_SPARC,
3652940b44dSPeter Avalos 					options_bcj(optarg));
3662940b44dSPeter Avalos 			break;
3672940b44dSPeter Avalos 
3682940b44dSPeter Avalos 		case OPT_DELTA:
3692940b44dSPeter Avalos 			coder_add_filter(LZMA_FILTER_DELTA,
3702940b44dSPeter Avalos 					options_delta(optarg));
3712940b44dSPeter Avalos 			break;
3722940b44dSPeter Avalos 
3732940b44dSPeter Avalos 		case OPT_LZMA1:
3742940b44dSPeter Avalos 			coder_add_filter(LZMA_FILTER_LZMA1,
3752940b44dSPeter Avalos 					options_lzma(optarg));
3762940b44dSPeter Avalos 			break;
3772940b44dSPeter Avalos 
3782940b44dSPeter Avalos 		case OPT_LZMA2:
3792940b44dSPeter Avalos 			coder_add_filter(LZMA_FILTER_LZMA2,
3802940b44dSPeter Avalos 					options_lzma(optarg));
3812940b44dSPeter Avalos 			break;
3822940b44dSPeter Avalos 
3832940b44dSPeter Avalos 		// Other
3842940b44dSPeter Avalos 
3852940b44dSPeter Avalos 		// --format
3862940b44dSPeter Avalos 		case 'F': {
3872940b44dSPeter Avalos 			// Just in case, support both "lzma" and "alone" since
3882940b44dSPeter Avalos 			// the latter was used for forward compatibility in
3892940b44dSPeter Avalos 			// LZMA Utils 4.32.x.
3902940b44dSPeter Avalos 			static const struct {
3912940b44dSPeter Avalos 				char str[8];
3922940b44dSPeter Avalos 				enum format_type format;
3932940b44dSPeter Avalos 			} types[] = {
3942940b44dSPeter Avalos 				{ "auto",   FORMAT_AUTO },
3952940b44dSPeter Avalos 				{ "xz",     FORMAT_XZ },
3962940b44dSPeter Avalos 				{ "lzma",   FORMAT_LZMA },
3972940b44dSPeter Avalos 				{ "alone",  FORMAT_LZMA },
3982940b44dSPeter Avalos 				// { "gzip",   FORMAT_GZIP },
3992940b44dSPeter Avalos 				// { "gz",     FORMAT_GZIP },
4002940b44dSPeter Avalos 				{ "raw",    FORMAT_RAW },
4012940b44dSPeter Avalos 			};
4022940b44dSPeter Avalos 
4032940b44dSPeter Avalos 			size_t i = 0;
4042940b44dSPeter Avalos 			while (strcmp(types[i].str, optarg) != 0)
4052940b44dSPeter Avalos 				if (++i == ARRAY_SIZE(types))
4062940b44dSPeter Avalos 					message_fatal(_("%s: Unknown file "
4072940b44dSPeter Avalos 							"format type"),
4082940b44dSPeter Avalos 							optarg);
4092940b44dSPeter Avalos 
4102940b44dSPeter Avalos 			opt_format = types[i].format;
4112940b44dSPeter Avalos 			break;
4122940b44dSPeter Avalos 		}
4132940b44dSPeter Avalos 
4142940b44dSPeter Avalos 		// --check
4152940b44dSPeter Avalos 		case 'C': {
4162940b44dSPeter Avalos 			static const struct {
4172940b44dSPeter Avalos 				char str[8];
4182940b44dSPeter Avalos 				lzma_check check;
4192940b44dSPeter Avalos 			} types[] = {
4202940b44dSPeter Avalos 				{ "none",   LZMA_CHECK_NONE },
4212940b44dSPeter Avalos 				{ "crc32",  LZMA_CHECK_CRC32 },
4222940b44dSPeter Avalos 				{ "crc64",  LZMA_CHECK_CRC64 },
4232940b44dSPeter Avalos 				{ "sha256", LZMA_CHECK_SHA256 },
4242940b44dSPeter Avalos 			};
4252940b44dSPeter Avalos 
4262940b44dSPeter Avalos 			size_t i = 0;
4272940b44dSPeter Avalos 			while (strcmp(types[i].str, optarg) != 0) {
4282940b44dSPeter Avalos 				if (++i == ARRAY_SIZE(types))
4292940b44dSPeter Avalos 					message_fatal(_("%s: Unsupported "
4302940b44dSPeter Avalos 							"integrity "
4312940b44dSPeter Avalos 							"check type"), optarg);
4322940b44dSPeter Avalos 			}
4332940b44dSPeter Avalos 
4342940b44dSPeter Avalos 			// Use a separate check in case we are using different
4352940b44dSPeter Avalos 			// liblzma than what was used to compile us.
4362940b44dSPeter Avalos 			if (!lzma_check_is_supported(types[i].check))
4372940b44dSPeter Avalos 				message_fatal(_("%s: Unsupported integrity "
4382940b44dSPeter Avalos 						"check type"), optarg);
4392940b44dSPeter Avalos 
4402940b44dSPeter Avalos 			coder_set_check(types[i].check);
4412940b44dSPeter Avalos 			break;
4422940b44dSPeter Avalos 		}
4432940b44dSPeter Avalos 
44415ab8c86SJohn Marino 		case OPT_IGNORE_CHECK:
44515ab8c86SJohn Marino 			opt_ignore_check = true;
44615ab8c86SJohn Marino 			break;
44715ab8c86SJohn Marino 
44815ab8c86SJohn Marino 		case OPT_BLOCK_SIZE:
44915ab8c86SJohn Marino 			opt_block_size = str_to_uint64("block-size", optarg,
45015ab8c86SJohn Marino 					0, LZMA_VLI_MAX);
45115ab8c86SJohn Marino 			break;
45215ab8c86SJohn Marino 
45315ab8c86SJohn Marino 		case OPT_BLOCK_LIST: {
45415ab8c86SJohn Marino 			parse_block_list(optarg);
45515ab8c86SJohn Marino 			break;
45615ab8c86SJohn Marino 		}
45715ab8c86SJohn Marino 
45815ab8c86SJohn Marino 		case OPT_SINGLE_STREAM:
45915ab8c86SJohn Marino 			opt_single_stream = true;
46015ab8c86SJohn Marino 			break;
46115ab8c86SJohn Marino 
4622940b44dSPeter Avalos 		case OPT_NO_SPARSE:
4632940b44dSPeter Avalos 			io_no_sparse();
4642940b44dSPeter Avalos 			break;
4652940b44dSPeter Avalos 
4662940b44dSPeter Avalos 		case OPT_FILES:
4672940b44dSPeter Avalos 			args->files_delim = '\n';
4682940b44dSPeter Avalos 
4692940b44dSPeter Avalos 		// Fall through
4702940b44dSPeter Avalos 
4712940b44dSPeter Avalos 		case OPT_FILES0:
4722940b44dSPeter Avalos 			if (args->files_name != NULL)
4732940b44dSPeter Avalos 				message_fatal(_("Only one file can be "
4742940b44dSPeter Avalos 						"specified with `--files' "
4752940b44dSPeter Avalos 						"or `--files0'."));
4762940b44dSPeter Avalos 
4772940b44dSPeter Avalos 			if (optarg == NULL) {
4782940b44dSPeter Avalos 				args->files_name = (char *)stdin_filename;
4792940b44dSPeter Avalos 				args->files_file = stdin;
4802940b44dSPeter Avalos 			} else {
4812940b44dSPeter Avalos 				args->files_name = optarg;
4822940b44dSPeter Avalos 				args->files_file = fopen(optarg,
4832940b44dSPeter Avalos 						c == OPT_FILES ? "r" : "rb");
4842940b44dSPeter Avalos 				if (args->files_file == NULL)
4852940b44dSPeter Avalos 					message_fatal("%s: %s", optarg,
4862940b44dSPeter Avalos 							strerror(errno));
4872940b44dSPeter Avalos 			}
4882940b44dSPeter Avalos 
4892940b44dSPeter Avalos 			break;
4902940b44dSPeter Avalos 
4912940b44dSPeter Avalos 		case OPT_NO_ADJUST:
4922940b44dSPeter Avalos 			opt_auto_adjust = false;
4932940b44dSPeter Avalos 			break;
4942940b44dSPeter Avalos 
49515ab8c86SJohn Marino 		case OPT_FLUSH_TIMEOUT:
49615ab8c86SJohn Marino 			opt_flush_timeout = str_to_uint64("flush-timeout",
49715ab8c86SJohn Marino 					optarg, 0, UINT64_MAX);
49815ab8c86SJohn Marino 			break;
49915ab8c86SJohn Marino 
5002940b44dSPeter Avalos 		default:
5012940b44dSPeter Avalos 			message_try_help();
5022940b44dSPeter Avalos 			tuklib_exit(E_ERROR, E_ERROR, false);
5032940b44dSPeter Avalos 		}
5042940b44dSPeter Avalos 	}
5052940b44dSPeter Avalos 
5062940b44dSPeter Avalos 	return;
5072940b44dSPeter Avalos }
5082940b44dSPeter Avalos 
5092940b44dSPeter Avalos 
5102940b44dSPeter Avalos static void
parse_environment(args_info * args,char * argv0,const char * varname)5112940b44dSPeter Avalos parse_environment(args_info *args, char *argv0, const char *varname)
5122940b44dSPeter Avalos {
5132940b44dSPeter Avalos 	char *env = getenv(varname);
5142940b44dSPeter Avalos 	if (env == NULL)
5152940b44dSPeter Avalos 		return;
5162940b44dSPeter Avalos 
5172940b44dSPeter Avalos 	// We modify the string, so make a copy of it.
5182940b44dSPeter Avalos 	env = xstrdup(env);
5192940b44dSPeter Avalos 
5202940b44dSPeter Avalos 	// Calculate the number of arguments in env. argc stats at one
5212940b44dSPeter Avalos 	// to include space for the program name.
5222940b44dSPeter Avalos 	int argc = 1;
5232940b44dSPeter Avalos 	bool prev_was_space = true;
5242940b44dSPeter Avalos 	for (size_t i = 0; env[i] != '\0'; ++i) {
5252940b44dSPeter Avalos 		// NOTE: Cast to unsigned char is needed so that correct
5262940b44dSPeter Avalos 		// value gets passed to isspace(), which expects
5272940b44dSPeter Avalos 		// unsigned char cast to int. Casting to int is done
5282940b44dSPeter Avalos 		// automatically due to integer promotion, but we need to
5292940b44dSPeter Avalos 		// force char to unsigned char manually. Otherwise 8-bit
5302940b44dSPeter Avalos 		// characters would get promoted to wrong value if
5312940b44dSPeter Avalos 		// char is signed.
5322940b44dSPeter Avalos 		if (isspace((unsigned char)env[i])) {
5332940b44dSPeter Avalos 			prev_was_space = true;
5342940b44dSPeter Avalos 		} else if (prev_was_space) {
5352940b44dSPeter Avalos 			prev_was_space = false;
5362940b44dSPeter Avalos 
537b892b6baSPeter Avalos 			// Keep argc small enough to fit into a signed int
5382940b44dSPeter Avalos 			// and to keep it usable for memory allocation.
5392940b44dSPeter Avalos 			if (++argc == my_min(
5402940b44dSPeter Avalos 					INT_MAX, SIZE_MAX / sizeof(char *)))
5412940b44dSPeter Avalos 				message_fatal(_("The environment variable "
5422940b44dSPeter Avalos 						"%s contains too many "
5432940b44dSPeter Avalos 						"arguments"), varname);
5442940b44dSPeter Avalos 		}
5452940b44dSPeter Avalos 	}
5462940b44dSPeter Avalos 
5472940b44dSPeter Avalos 	// Allocate memory to hold pointers to the arguments. Add one to get
5482940b44dSPeter Avalos 	// space for the terminating NULL (if some systems happen to need it).
5492940b44dSPeter Avalos 	char **argv = xmalloc(((size_t)(argc) + 1) * sizeof(char *));
5502940b44dSPeter Avalos 	argv[0] = argv0;
5512940b44dSPeter Avalos 	argv[argc] = NULL;
5522940b44dSPeter Avalos 
5532940b44dSPeter Avalos 	// Go through the string again. Split the arguments using '\0'
5542940b44dSPeter Avalos 	// characters and add pointers to the resulting strings to argv.
5552940b44dSPeter Avalos 	argc = 1;
5562940b44dSPeter Avalos 	prev_was_space = true;
5572940b44dSPeter Avalos 	for (size_t i = 0; env[i] != '\0'; ++i) {
5582940b44dSPeter Avalos 		if (isspace((unsigned char)env[i])) {
5592940b44dSPeter Avalos 			prev_was_space = true;
5602940b44dSPeter Avalos 			env[i] = '\0';
5612940b44dSPeter Avalos 		} else if (prev_was_space) {
5622940b44dSPeter Avalos 			prev_was_space = false;
5632940b44dSPeter Avalos 			argv[argc++] = env + i;
5642940b44dSPeter Avalos 		}
5652940b44dSPeter Avalos 	}
5662940b44dSPeter Avalos 
5672940b44dSPeter Avalos 	// Parse the argument list we got from the environment. All non-option
5682940b44dSPeter Avalos 	// arguments i.e. filenames are ignored.
5692940b44dSPeter Avalos 	parse_real(args, argc, argv);
5702940b44dSPeter Avalos 
5712940b44dSPeter Avalos 	// Reset the state of the getopt_long() so that we can parse the
5722940b44dSPeter Avalos 	// command line options too. There are two incompatible ways to
5732940b44dSPeter Avalos 	// do it.
5742940b44dSPeter Avalos #ifdef HAVE_OPTRESET
5752940b44dSPeter Avalos 	// BSD
5762940b44dSPeter Avalos 	optind = 1;
5772940b44dSPeter Avalos 	optreset = 1;
5782940b44dSPeter Avalos #else
5792940b44dSPeter Avalos 	// GNU, Solaris
5802940b44dSPeter Avalos 	optind = 0;
5812940b44dSPeter Avalos #endif
5822940b44dSPeter Avalos 
5832940b44dSPeter Avalos 	// We don't need the argument list from environment anymore.
5842940b44dSPeter Avalos 	free(argv);
5852940b44dSPeter Avalos 	free(env);
5862940b44dSPeter Avalos 
5872940b44dSPeter Avalos 	return;
5882940b44dSPeter Avalos }
5892940b44dSPeter Avalos 
5902940b44dSPeter Avalos 
5912940b44dSPeter Avalos extern void
args_parse(args_info * args,int argc,char ** argv)5922940b44dSPeter Avalos args_parse(args_info *args, int argc, char **argv)
5932940b44dSPeter Avalos {
5942940b44dSPeter Avalos 	// Initialize those parts of *args that we need later.
5952940b44dSPeter Avalos 	args->files_name = NULL;
5962940b44dSPeter Avalos 	args->files_file = NULL;
5972940b44dSPeter Avalos 	args->files_delim = '\0';
5982940b44dSPeter Avalos 
5992940b44dSPeter Avalos 	// Check how we were called.
6002940b44dSPeter Avalos 	{
6012940b44dSPeter Avalos 		// Remove the leading path name, if any.
6022940b44dSPeter Avalos 		const char *name = strrchr(argv[0], '/');
6032940b44dSPeter Avalos 		if (name == NULL)
6042940b44dSPeter Avalos 			name = argv[0];
6052940b44dSPeter Avalos 		else
6062940b44dSPeter Avalos 			++name;
6072940b44dSPeter Avalos 
6082940b44dSPeter Avalos 		// NOTE: It's possible that name[0] is now '\0' if argv[0]
6092940b44dSPeter Avalos 		// is weird, but it doesn't matter here.
6102940b44dSPeter Avalos 
6112940b44dSPeter Avalos 		// Look for full command names instead of substrings like
6122940b44dSPeter Avalos 		// "un", "cat", and "lz" to reduce possibility of false
6132940b44dSPeter Avalos 		// positives when the programs have been renamed.
6142940b44dSPeter Avalos 		if (strstr(name, "xzcat") != NULL) {
6152940b44dSPeter Avalos 			opt_mode = MODE_DECOMPRESS;
6162940b44dSPeter Avalos 			opt_stdout = true;
6172940b44dSPeter Avalos 		} else if (strstr(name, "unxz") != NULL) {
6182940b44dSPeter Avalos 			opt_mode = MODE_DECOMPRESS;
6192940b44dSPeter Avalos 		} else if (strstr(name, "lzcat") != NULL) {
6202940b44dSPeter Avalos 			opt_format = FORMAT_LZMA;
6212940b44dSPeter Avalos 			opt_mode = MODE_DECOMPRESS;
6222940b44dSPeter Avalos 			opt_stdout = true;
6232940b44dSPeter Avalos 		} else if (strstr(name, "unlzma") != NULL) {
6242940b44dSPeter Avalos 			opt_format = FORMAT_LZMA;
6252940b44dSPeter Avalos 			opt_mode = MODE_DECOMPRESS;
6262940b44dSPeter Avalos 		} else if (strstr(name, "lzma") != NULL) {
6272940b44dSPeter Avalos 			opt_format = FORMAT_LZMA;
6282940b44dSPeter Avalos 		}
6292940b44dSPeter Avalos 	}
6302940b44dSPeter Avalos 
6312940b44dSPeter Avalos 	// First the flags from the environment
6322940b44dSPeter Avalos 	parse_environment(args, argv[0], "XZ_DEFAULTS");
6332940b44dSPeter Avalos 	parse_environment(args, argv[0], "XZ_OPT");
6342940b44dSPeter Avalos 
6352940b44dSPeter Avalos 	// Then from the command line
6362940b44dSPeter Avalos 	parse_real(args, argc, argv);
6372940b44dSPeter Avalos 
63846a2189dSzrj 	// If encoder or decoder support was omitted at build time,
63946a2189dSzrj 	// show an error now so that the rest of the code can rely on
64046a2189dSzrj 	// that whatever is in opt_mode is also supported.
64146a2189dSzrj #ifndef HAVE_ENCODERS
64246a2189dSzrj 	if (opt_mode == MODE_COMPRESS)
64346a2189dSzrj 		message_fatal(_("Compression support was disabled "
64446a2189dSzrj 				"at build time"));
64546a2189dSzrj #endif
64646a2189dSzrj #ifndef HAVE_DECODERS
64746a2189dSzrj 	// Even MODE_LIST cannot work without decoder support so MODE_COMPRESS
64846a2189dSzrj 	// is the only valid choice.
64946a2189dSzrj 	if (opt_mode != MODE_COMPRESS)
65046a2189dSzrj 		message_fatal(_("Decompression support was disabled "
65146a2189dSzrj 				"at build time"));
65246a2189dSzrj #endif
65346a2189dSzrj 
6542940b44dSPeter Avalos 	// Never remove the source file when the destination is not on disk.
6552940b44dSPeter Avalos 	// In test mode the data is written nowhere, but setting opt_stdout
6562940b44dSPeter Avalos 	// will make the rest of the code behave well.
6572940b44dSPeter Avalos 	if (opt_stdout || opt_mode == MODE_TEST) {
6582940b44dSPeter Avalos 		opt_keep_original = true;
6592940b44dSPeter Avalos 		opt_stdout = true;
6602940b44dSPeter Avalos 	}
6612940b44dSPeter Avalos 
6622940b44dSPeter Avalos 	// When compressing, if no --format flag was used, or it
6632940b44dSPeter Avalos 	// was --format=auto, we compress to the .xz format.
6642940b44dSPeter Avalos 	if (opt_mode == MODE_COMPRESS && opt_format == FORMAT_AUTO)
6652940b44dSPeter Avalos 		opt_format = FORMAT_XZ;
6662940b44dSPeter Avalos 
6672940b44dSPeter Avalos 	// Compression settings need to be validated (options themselves and
6682940b44dSPeter Avalos 	// their memory usage) when compressing to any file format. It has to
6692940b44dSPeter Avalos 	// be done also when uncompressing raw data, since for raw decoding
6702940b44dSPeter Avalos 	// the options given on the command line are used to know what kind
6712940b44dSPeter Avalos 	// of raw data we are supposed to decode.
6722940b44dSPeter Avalos 	if (opt_mode == MODE_COMPRESS || opt_format == FORMAT_RAW)
6732940b44dSPeter Avalos 		coder_set_compression_settings();
6742940b44dSPeter Avalos 
6752940b44dSPeter Avalos 	// If no filenames are given, use stdin.
6762940b44dSPeter Avalos 	if (argv[optind] == NULL && args->files_name == NULL) {
6772940b44dSPeter Avalos 		// We don't modify or free() the "-" constant. The caller
6782940b44dSPeter Avalos 		// modifies this so don't make the struct itself const.
6792940b44dSPeter Avalos 		static char *names_stdin[2] = { (char *)"-", NULL };
6802940b44dSPeter Avalos 		args->arg_names = names_stdin;
6812940b44dSPeter Avalos 		args->arg_count = 1;
6822940b44dSPeter Avalos 	} else {
6832940b44dSPeter Avalos 		// We got at least one filename from the command line, or
6842940b44dSPeter Avalos 		// --files or --files0 was specified.
6852940b44dSPeter Avalos 		args->arg_names = argv + optind;
686*e151908bSDaniel Fojt 		args->arg_count = (unsigned int)(argc - optind);
6872940b44dSPeter Avalos 	}
6882940b44dSPeter Avalos 
6892940b44dSPeter Avalos 	return;
6902940b44dSPeter Avalos }
69115ab8c86SJohn Marino 
69215ab8c86SJohn Marino 
69315ab8c86SJohn Marino #ifndef NDEBUG
69415ab8c86SJohn Marino extern void
args_free(void)69515ab8c86SJohn Marino args_free(void)
69615ab8c86SJohn Marino {
69715ab8c86SJohn Marino 	free(opt_block_list);
69815ab8c86SJohn Marino 	return;
69915ab8c86SJohn Marino }
70015ab8c86SJohn Marino #endif
701