1207753Smm///////////////////////////////////////////////////////////////////////////////
2207753Smm//
3207753Smm/// \file       args.c
4207753Smm/// \brief      Argument parsing
5207753Smm///
6207753Smm/// \note       Filter-specific options parsing is in options.c.
7207753Smm//
8207753Smm//  Author:     Lasse Collin
9207753Smm//
10207753Smm//  This file has been put into the public domain.
11207753Smm//  You can do whatever you want with this file.
12207753Smm//
13207753Smm///////////////////////////////////////////////////////////////////////////////
14207753Smm
15207753Smm#include "private.h"
16207753Smm
17207753Smm#include "getopt.h"
18207753Smm#include <ctype.h>
19207753Smm
20207753Smm
21207753Smmbool opt_stdout = false;
22207753Smmbool opt_force = false;
23207753Smmbool opt_keep_original = false;
24207753Smmbool opt_robot = false;
25292588Sdelphijbool opt_ignore_check = false;
26207753Smm
27207753Smm// We don't modify or free() this, but we need to assign it in some
28207753Smm// non-const pointers.
29213700Smmconst char stdin_filename[] = "(stdin)";
30207753Smm
31207753Smm
32213700Smm/// Parse and set the memory usage limit for compression and/or decompression.
33207753Smmstatic void
34213700Smmparse_memlimit(const char *name, const char *name_percentage, char *str,
35213700Smm		bool set_compress, bool set_decompress)
36213700Smm{
37213700Smm	bool is_percentage = false;
38213700Smm	uint64_t value;
39213700Smm
40213700Smm	const size_t len = strlen(str);
41213700Smm	if (len > 0 && str[len - 1] == '%') {
42213700Smm		str[len - 1] = '\0';
43213700Smm		is_percentage = true;
44213700Smm		value = str_to_uint64(name_percentage, str, 1, 100);
45213700Smm	} else {
46213700Smm		// On 32-bit systems, SIZE_MAX would make more sense than
47213700Smm		// UINT64_MAX. But use UINT64_MAX still so that scripts
48213700Smm		// that assume > 4 GiB values don't break.
49213700Smm		value = str_to_uint64(name, str, 0, UINT64_MAX);
50213700Smm	}
51213700Smm
52213700Smm	hardware_memlimit_set(
53213700Smm			value, set_compress, set_decompress, is_percentage);
54213700Smm	return;
55213700Smm}
56213700Smm
57213700Smm
58213700Smmstatic void
59292588Sdelphijparse_block_list(char *str)
60292588Sdelphij{
61292588Sdelphij	// It must be non-empty and not begin with a comma.
62292588Sdelphij	if (str[0] == '\0' || str[0] == ',')
63292588Sdelphij		message_fatal(_("%s: Invalid argument to --block-list"), str);
64292588Sdelphij
65292588Sdelphij	// Count the number of comma-separated strings.
66292588Sdelphij	size_t count = 1;
67292588Sdelphij	for (size_t i = 0; str[i] != '\0'; ++i)
68292588Sdelphij		if (str[i] == ',')
69292588Sdelphij			++count;
70292588Sdelphij
71292588Sdelphij	// Prevent an unlikely integer overflow.
72292588Sdelphij	if (count > SIZE_MAX / sizeof(uint64_t) - 1)
73292588Sdelphij		message_fatal(_("%s: Too many arguments to --block-list"),
74292588Sdelphij				str);
75292588Sdelphij
76292588Sdelphij	// Allocate memory to hold all the sizes specified.
77292588Sdelphij	// If --block-list was specified already, its value is forgotten.
78292588Sdelphij	free(opt_block_list);
79292588Sdelphij	opt_block_list = xmalloc((count + 1) * sizeof(uint64_t));
80292588Sdelphij
81292588Sdelphij	for (size_t i = 0; i < count; ++i) {
82292588Sdelphij		// Locate the next comma and replace it with \0.
83292588Sdelphij		char *p = strchr(str, ',');
84292588Sdelphij		if (p != NULL)
85292588Sdelphij			*p = '\0';
86292588Sdelphij
87292588Sdelphij		if (str[0] == '\0') {
88292588Sdelphij			// There is no string, that is, a comma follows
89292588Sdelphij			// another comma. Use the previous value.
90292588Sdelphij			//
91292588Sdelphij			// NOTE: We checked earler that the first char
92292588Sdelphij			// of the whole list cannot be a comma.
93292588Sdelphij			assert(i > 0);
94292588Sdelphij			opt_block_list[i] = opt_block_list[i - 1];
95292588Sdelphij		} else {
96292588Sdelphij			opt_block_list[i] = str_to_uint64("block-list", str,
97292588Sdelphij					0, UINT64_MAX);
98292588Sdelphij
99292588Sdelphij			// Zero indicates no more new Blocks.
100292588Sdelphij			if (opt_block_list[i] == 0) {
101292588Sdelphij				if (i + 1 != count)
102292588Sdelphij					message_fatal(_("0 can only be used "
103292588Sdelphij							"as the last element "
104292588Sdelphij							"in --block-list"));
105292588Sdelphij
106292588Sdelphij				opt_block_list[i] = UINT64_MAX;
107292588Sdelphij			}
108292588Sdelphij		}
109292588Sdelphij
110292588Sdelphij		str = p + 1;
111292588Sdelphij	}
112292588Sdelphij
113292588Sdelphij	// Terminate the array.
114292588Sdelphij	opt_block_list[count] = 0;
115292588Sdelphij	return;
116292588Sdelphij}
117292588Sdelphij
118292588Sdelphij
119292588Sdelphijstatic void
120207753Smmparse_real(args_info *args, int argc, char **argv)
121207753Smm{
122207753Smm	enum {
123213700Smm		OPT_X86 = INT_MIN,
124207753Smm		OPT_POWERPC,
125207753Smm		OPT_IA64,
126207753Smm		OPT_ARM,
127207753Smm		OPT_ARMTHUMB,
128207753Smm		OPT_SPARC,
129207753Smm		OPT_DELTA,
130207753Smm		OPT_LZMA1,
131207753Smm		OPT_LZMA2,
132207753Smm
133292588Sdelphij		OPT_SINGLE_STREAM,
134207753Smm		OPT_NO_SPARSE,
135207753Smm		OPT_FILES,
136207753Smm		OPT_FILES0,
137292588Sdelphij		OPT_BLOCK_SIZE,
138292588Sdelphij		OPT_BLOCK_LIST,
139213700Smm		OPT_MEM_COMPRESS,
140213700Smm		OPT_MEM_DECOMPRESS,
141213700Smm		OPT_NO_ADJUST,
142207753Smm		OPT_INFO_MEMORY,
143207753Smm		OPT_ROBOT,
144292588Sdelphij		OPT_FLUSH_TIMEOUT,
145292588Sdelphij		OPT_IGNORE_CHECK,
146207753Smm	};
147207753Smm
148207753Smm	static const char short_opts[]
149207753Smm			= "cC:defF:hHlkM:qQrS:tT:vVz0123456789";
150207753Smm
151207753Smm	static const struct option long_opts[] = {
152207753Smm		// Operation mode
153207753Smm		{ "compress",     no_argument,       NULL,  'z' },
154207753Smm		{ "decompress",   no_argument,       NULL,  'd' },
155207753Smm		{ "uncompress",   no_argument,       NULL,  'd' },
156207753Smm		{ "test",         no_argument,       NULL,  't' },
157207753Smm		{ "list",         no_argument,       NULL,  'l' },
158207753Smm
159207753Smm		// Operation modifiers
160207753Smm		{ "keep",         no_argument,       NULL,  'k' },
161207753Smm		{ "force",        no_argument,       NULL,  'f' },
162207753Smm		{ "stdout",       no_argument,       NULL,  'c' },
163207753Smm		{ "to-stdout",    no_argument,       NULL,  'c' },
164292588Sdelphij		{ "single-stream", no_argument,      NULL,  OPT_SINGLE_STREAM },
165207753Smm		{ "no-sparse",    no_argument,       NULL,  OPT_NO_SPARSE },
166207753Smm		{ "suffix",       required_argument, NULL,  'S' },
167207753Smm		// { "recursive",      no_argument,       NULL,  'r' }, // TODO
168207753Smm		{ "files",        optional_argument, NULL,  OPT_FILES },
169207753Smm		{ "files0",       optional_argument, NULL,  OPT_FILES0 },
170207753Smm
171207753Smm		// Basic compression settings
172207753Smm		{ "format",       required_argument, NULL,  'F' },
173207753Smm		{ "check",        required_argument, NULL,  'C' },
174292588Sdelphij		{ "ignore-check", no_argument,       NULL,  OPT_IGNORE_CHECK },
175292588Sdelphij		{ "block-size",   required_argument, NULL,  OPT_BLOCK_SIZE },
176292588Sdelphij		{ "block-list",  required_argument, NULL,  OPT_BLOCK_LIST },
177213700Smm		{ "memlimit-compress",   required_argument, NULL, OPT_MEM_COMPRESS },
178213700Smm		{ "memlimit-decompress", required_argument, NULL, OPT_MEM_DECOMPRESS },
179213700Smm		{ "memlimit",     required_argument, NULL,  'M' },
180213700Smm		{ "memory",       required_argument, NULL,  'M' }, // Old alias
181213700Smm		{ "no-adjust",    no_argument,       NULL,  OPT_NO_ADJUST },
182207753Smm		{ "threads",      required_argument, NULL,  'T' },
183292588Sdelphij		{ "flush-timeout", required_argument, NULL, OPT_FLUSH_TIMEOUT },
184207753Smm
185207753Smm		{ "extreme",      no_argument,       NULL,  'e' },
186207753Smm		{ "fast",         no_argument,       NULL,  '0' },
187207753Smm		{ "best",         no_argument,       NULL,  '9' },
188207753Smm
189207753Smm		// Filters
190207753Smm		{ "lzma1",        optional_argument, NULL,  OPT_LZMA1 },
191207753Smm		{ "lzma2",        optional_argument, NULL,  OPT_LZMA2 },
192207753Smm		{ "x86",          optional_argument, NULL,  OPT_X86 },
193207753Smm		{ "powerpc",      optional_argument, NULL,  OPT_POWERPC },
194207753Smm		{ "ia64",         optional_argument, NULL,  OPT_IA64 },
195207753Smm		{ "arm",          optional_argument, NULL,  OPT_ARM },
196207753Smm		{ "armthumb",     optional_argument, NULL,  OPT_ARMTHUMB },
197207753Smm		{ "sparc",        optional_argument, NULL,  OPT_SPARC },
198207753Smm		{ "delta",        optional_argument, NULL,  OPT_DELTA },
199207753Smm
200207753Smm		// Other options
201207753Smm		{ "quiet",        no_argument,       NULL,  'q' },
202207753Smm		{ "verbose",      no_argument,       NULL,  'v' },
203207753Smm		{ "no-warn",      no_argument,       NULL,  'Q' },
204207753Smm		{ "robot",        no_argument,       NULL,  OPT_ROBOT },
205207753Smm		{ "info-memory",  no_argument,       NULL,  OPT_INFO_MEMORY },
206207753Smm		{ "help",         no_argument,       NULL,  'h' },
207207753Smm		{ "long-help",    no_argument,       NULL,  'H' },
208207753Smm		{ "version",      no_argument,       NULL,  'V' },
209207753Smm
210213700Smm		{ NULL,           0,                 NULL,   0 }
211207753Smm	};
212207753Smm
213207753Smm	int c;
214207753Smm
215207753Smm	while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL))
216207753Smm			!= -1) {
217207753Smm		switch (c) {
218207753Smm		// Compression preset (also for decompression if --format=raw)
219207753Smm		case '0': case '1': case '2': case '3': case '4':
220207753Smm		case '5': case '6': case '7': case '8': case '9':
221207753Smm			coder_set_preset(c - '0');
222207753Smm			break;
223207753Smm
224213700Smm		// --memlimit-compress
225213700Smm		case OPT_MEM_COMPRESS:
226213700Smm			parse_memlimit("memlimit-compress",
227213700Smm					"memlimit-compress%", optarg,
228213700Smm					true, false);
229213700Smm			break;
230207753Smm
231213700Smm		// --memlimit-decompress
232213700Smm		case OPT_MEM_DECOMPRESS:
233213700Smm			parse_memlimit("memlimit-decompress",
234213700Smm					"memlimit-decompress%", optarg,
235213700Smm					false, true);
236207753Smm			break;
237207753Smm
238213700Smm		// --memlimit
239213700Smm		case 'M':
240213700Smm			parse_memlimit("memlimit", "memlimit%", optarg,
241213700Smm					true, true);
242213700Smm			break;
243213700Smm
244207753Smm		// --suffix
245207753Smm		case 'S':
246207753Smm			suffix_set(optarg);
247207753Smm			break;
248207753Smm
249207753Smm		case 'T':
250292588Sdelphij			// The max is from src/liblzma/common/common.h.
251292588Sdelphij			hardware_threads_set(str_to_uint64("threads",
252292588Sdelphij					optarg, 0, 16384));
253207753Smm			break;
254207753Smm
255207753Smm		// --version
256207753Smm		case 'V':
257207753Smm			// This doesn't return.
258207753Smm			message_version();
259207753Smm
260207753Smm		// --stdout
261207753Smm		case 'c':
262207753Smm			opt_stdout = true;
263207753Smm			break;
264207753Smm
265207753Smm		// --decompress
266207753Smm		case 'd':
267207753Smm			opt_mode = MODE_DECOMPRESS;
268207753Smm			break;
269207753Smm
270207753Smm		// --extreme
271207753Smm		case 'e':
272207753Smm			coder_set_extreme();
273207753Smm			break;
274207753Smm
275207753Smm		// --force
276207753Smm		case 'f':
277207753Smm			opt_force = true;
278207753Smm			break;
279207753Smm
280207753Smm		// --info-memory
281207753Smm		case OPT_INFO_MEMORY:
282207753Smm			// This doesn't return.
283213700Smm			hardware_memlimit_show();
284207753Smm
285207753Smm		// --help
286207753Smm		case 'h':
287207753Smm			// This doesn't return.
288207753Smm			message_help(false);
289207753Smm
290207753Smm		// --long-help
291207753Smm		case 'H':
292207753Smm			// This doesn't return.
293207753Smm			message_help(true);
294207753Smm
295207753Smm		// --list
296207753Smm		case 'l':
297207753Smm			opt_mode = MODE_LIST;
298207753Smm			break;
299207753Smm
300207753Smm		// --keep
301207753Smm		case 'k':
302207753Smm			opt_keep_original = true;
303207753Smm			break;
304207753Smm
305207753Smm		// --quiet
306207753Smm		case 'q':
307207753Smm			message_verbosity_decrease();
308207753Smm			break;
309207753Smm
310207753Smm		case 'Q':
311207753Smm			set_exit_no_warn();
312207753Smm			break;
313207753Smm
314207753Smm		case 't':
315207753Smm			opt_mode = MODE_TEST;
316207753Smm			break;
317207753Smm
318207753Smm		// --verbose
319207753Smm		case 'v':
320207753Smm			message_verbosity_increase();
321207753Smm			break;
322207753Smm
323207753Smm		// --robot
324207753Smm		case OPT_ROBOT:
325207753Smm			opt_robot = true;
326207753Smm
327207753Smm			// This is to make sure that floating point numbers
328207753Smm			// always have a dot as decimal separator.
329207753Smm			setlocale(LC_NUMERIC, "C");
330207753Smm			break;
331207753Smm
332207753Smm		case 'z':
333207753Smm			opt_mode = MODE_COMPRESS;
334207753Smm			break;
335207753Smm
336207753Smm		// Filter setup
337207753Smm
338207753Smm		case OPT_X86:
339207753Smm			coder_add_filter(LZMA_FILTER_X86,
340207753Smm					options_bcj(optarg));
341207753Smm			break;
342207753Smm
343207753Smm		case OPT_POWERPC:
344207753Smm			coder_add_filter(LZMA_FILTER_POWERPC,
345207753Smm					options_bcj(optarg));
346207753Smm			break;
347207753Smm
348207753Smm		case OPT_IA64:
349207753Smm			coder_add_filter(LZMA_FILTER_IA64,
350207753Smm					options_bcj(optarg));
351207753Smm			break;
352207753Smm
353207753Smm		case OPT_ARM:
354207753Smm			coder_add_filter(LZMA_FILTER_ARM,
355207753Smm					options_bcj(optarg));
356207753Smm			break;
357207753Smm
358207753Smm		case OPT_ARMTHUMB:
359207753Smm			coder_add_filter(LZMA_FILTER_ARMTHUMB,
360207753Smm					options_bcj(optarg));
361207753Smm			break;
362207753Smm
363207753Smm		case OPT_SPARC:
364207753Smm			coder_add_filter(LZMA_FILTER_SPARC,
365207753Smm					options_bcj(optarg));
366207753Smm			break;
367207753Smm
368207753Smm		case OPT_DELTA:
369207753Smm			coder_add_filter(LZMA_FILTER_DELTA,
370207753Smm					options_delta(optarg));
371207753Smm			break;
372207753Smm
373207753Smm		case OPT_LZMA1:
374207753Smm			coder_add_filter(LZMA_FILTER_LZMA1,
375207753Smm					options_lzma(optarg));
376207753Smm			break;
377207753Smm
378207753Smm		case OPT_LZMA2:
379207753Smm			coder_add_filter(LZMA_FILTER_LZMA2,
380207753Smm					options_lzma(optarg));
381207753Smm			break;
382207753Smm
383207753Smm		// Other
384207753Smm
385207753Smm		// --format
386207753Smm		case 'F': {
387207753Smm			// Just in case, support both "lzma" and "alone" since
388207753Smm			// the latter was used for forward compatibility in
389207753Smm			// LZMA Utils 4.32.x.
390207753Smm			static const struct {
391207753Smm				char str[8];
392207753Smm				enum format_type format;
393207753Smm			} types[] = {
394207753Smm				{ "auto",   FORMAT_AUTO },
395207753Smm				{ "xz",     FORMAT_XZ },
396207753Smm				{ "lzma",   FORMAT_LZMA },
397207753Smm				{ "alone",  FORMAT_LZMA },
398207753Smm				// { "gzip",   FORMAT_GZIP },
399207753Smm				// { "gz",     FORMAT_GZIP },
400207753Smm				{ "raw",    FORMAT_RAW },
401207753Smm			};
402207753Smm
403207753Smm			size_t i = 0;
404207753Smm			while (strcmp(types[i].str, optarg) != 0)
405207753Smm				if (++i == ARRAY_SIZE(types))
406207753Smm					message_fatal(_("%s: Unknown file "
407207753Smm							"format type"),
408207753Smm							optarg);
409207753Smm
410207753Smm			opt_format = types[i].format;
411207753Smm			break;
412207753Smm		}
413207753Smm
414207753Smm		// --check
415207753Smm		case 'C': {
416207753Smm			static const struct {
417207753Smm				char str[8];
418207753Smm				lzma_check check;
419207753Smm			} types[] = {
420207753Smm				{ "none",   LZMA_CHECK_NONE },
421207753Smm				{ "crc32",  LZMA_CHECK_CRC32 },
422207753Smm				{ "crc64",  LZMA_CHECK_CRC64 },
423207753Smm				{ "sha256", LZMA_CHECK_SHA256 },
424207753Smm			};
425207753Smm
426207753Smm			size_t i = 0;
427207753Smm			while (strcmp(types[i].str, optarg) != 0) {
428207753Smm				if (++i == ARRAY_SIZE(types))
429207753Smm					message_fatal(_("%s: Unsupported "
430207753Smm							"integrity "
431207753Smm							"check type"), optarg);
432207753Smm			}
433207753Smm
434207753Smm			// Use a separate check in case we are using different
435207753Smm			// liblzma than what was used to compile us.
436207753Smm			if (!lzma_check_is_supported(types[i].check))
437207753Smm				message_fatal(_("%s: Unsupported integrity "
438207753Smm						"check type"), optarg);
439207753Smm
440207753Smm			coder_set_check(types[i].check);
441207753Smm			break;
442207753Smm		}
443207753Smm
444292588Sdelphij		case OPT_IGNORE_CHECK:
445292588Sdelphij			opt_ignore_check = true;
446292588Sdelphij			break;
447292588Sdelphij
448292588Sdelphij		case OPT_BLOCK_SIZE:
449292588Sdelphij			opt_block_size = str_to_uint64("block-size", optarg,
450292588Sdelphij					0, LZMA_VLI_MAX);
451292588Sdelphij			break;
452292588Sdelphij
453292588Sdelphij		case OPT_BLOCK_LIST: {
454292588Sdelphij			parse_block_list(optarg);
455292588Sdelphij			break;
456292588Sdelphij		}
457292588Sdelphij
458292588Sdelphij		case OPT_SINGLE_STREAM:
459292588Sdelphij			opt_single_stream = true;
460292588Sdelphij			break;
461292588Sdelphij
462207753Smm		case OPT_NO_SPARSE:
463207753Smm			io_no_sparse();
464207753Smm			break;
465207753Smm
466207753Smm		case OPT_FILES:
467207753Smm			args->files_delim = '\n';
468207753Smm
469207753Smm		// Fall through
470207753Smm
471207753Smm		case OPT_FILES0:
472207753Smm			if (args->files_name != NULL)
473207753Smm				message_fatal(_("Only one file can be "
474207753Smm						"specified with `--files' "
475207753Smm						"or `--files0'."));
476207753Smm
477207753Smm			if (optarg == NULL) {
478207753Smm				args->files_name = (char *)stdin_filename;
479207753Smm				args->files_file = stdin;
480207753Smm			} else {
481207753Smm				args->files_name = optarg;
482207753Smm				args->files_file = fopen(optarg,
483207753Smm						c == OPT_FILES ? "r" : "rb");
484207753Smm				if (args->files_file == NULL)
485207753Smm					message_fatal("%s: %s", optarg,
486207753Smm							strerror(errno));
487207753Smm			}
488207753Smm
489207753Smm			break;
490207753Smm
491213700Smm		case OPT_NO_ADJUST:
492213700Smm			opt_auto_adjust = false;
493213700Smm			break;
494213700Smm
495292588Sdelphij		case OPT_FLUSH_TIMEOUT:
496292588Sdelphij			opt_flush_timeout = str_to_uint64("flush-timeout",
497292588Sdelphij					optarg, 0, UINT64_MAX);
498292588Sdelphij			break;
499292588Sdelphij
500207753Smm		default:
501207753Smm			message_try_help();
502207753Smm			tuklib_exit(E_ERROR, E_ERROR, false);
503207753Smm		}
504207753Smm	}
505207753Smm
506207753Smm	return;
507207753Smm}
508207753Smm
509207753Smm
510207753Smmstatic void
511213700Smmparse_environment(args_info *args, char *argv0, const char *varname)
512207753Smm{
513213700Smm	char *env = getenv(varname);
514207753Smm	if (env == NULL)
515207753Smm		return;
516207753Smm
517207753Smm	// We modify the string, so make a copy of it.
518207753Smm	env = xstrdup(env);
519207753Smm
520207753Smm	// Calculate the number of arguments in env. argc stats at one
521207753Smm	// to include space for the program name.
522207753Smm	int argc = 1;
523207753Smm	bool prev_was_space = true;
524207753Smm	for (size_t i = 0; env[i] != '\0'; ++i) {
525207753Smm		// NOTE: Cast to unsigned char is needed so that correct
526207753Smm		// value gets passed to isspace(), which expects
527207753Smm		// unsigned char cast to int. Casting to int is done
528207753Smm		// automatically due to integer promotion, but we need to
529207753Smm		// force char to unsigned char manually. Otherwise 8-bit
530207753Smm		// characters would get promoted to wrong value if
531207753Smm		// char is signed.
532207753Smm		if (isspace((unsigned char)env[i])) {
533207753Smm			prev_was_space = true;
534207753Smm		} else if (prev_was_space) {
535207753Smm			prev_was_space = false;
536207753Smm
537244601Smm			// Keep argc small enough to fit into a signed int
538207753Smm			// and to keep it usable for memory allocation.
539213700Smm			if (++argc == my_min(
540213700Smm					INT_MAX, SIZE_MAX / sizeof(char *)))
541207753Smm				message_fatal(_("The environment variable "
542213700Smm						"%s contains too many "
543213700Smm						"arguments"), varname);
544207753Smm		}
545207753Smm	}
546207753Smm
547207753Smm	// Allocate memory to hold pointers to the arguments. Add one to get
548207753Smm	// space for the terminating NULL (if some systems happen to need it).
549207753Smm	char **argv = xmalloc(((size_t)(argc) + 1) * sizeof(char *));
550207753Smm	argv[0] = argv0;
551207753Smm	argv[argc] = NULL;
552207753Smm
553207753Smm	// Go through the string again. Split the arguments using '\0'
554207753Smm	// characters and add pointers to the resulting strings to argv.
555207753Smm	argc = 1;
556207753Smm	prev_was_space = true;
557207753Smm	for (size_t i = 0; env[i] != '\0'; ++i) {
558207753Smm		if (isspace((unsigned char)env[i])) {
559207753Smm			prev_was_space = true;
560207753Smm			env[i] = '\0';
561207753Smm		} else if (prev_was_space) {
562207753Smm			prev_was_space = false;
563207753Smm			argv[argc++] = env + i;
564207753Smm		}
565207753Smm	}
566207753Smm
567207753Smm	// Parse the argument list we got from the environment. All non-option
568207753Smm	// arguments i.e. filenames are ignored.
569207753Smm	parse_real(args, argc, argv);
570207753Smm
571207753Smm	// Reset the state of the getopt_long() so that we can parse the
572207753Smm	// command line options too. There are two incompatible ways to
573207753Smm	// do it.
574207753Smm#ifdef HAVE_OPTRESET
575207753Smm	// BSD
576207753Smm	optind = 1;
577207753Smm	optreset = 1;
578207753Smm#else
579207753Smm	// GNU, Solaris
580207753Smm	optind = 0;
581207753Smm#endif
582207753Smm
583207753Smm	// We don't need the argument list from environment anymore.
584207753Smm	free(argv);
585207753Smm	free(env);
586207753Smm
587207753Smm	return;
588207753Smm}
589207753Smm
590207753Smm
591207753Smmextern void
592207753Smmargs_parse(args_info *args, int argc, char **argv)
593207753Smm{
594207753Smm	// Initialize those parts of *args that we need later.
595207753Smm	args->files_name = NULL;
596207753Smm	args->files_file = NULL;
597207753Smm	args->files_delim = '\0';
598207753Smm
599207753Smm	// Check how we were called.
600207753Smm	{
601207753Smm		// Remove the leading path name, if any.
602207753Smm		const char *name = strrchr(argv[0], '/');
603207753Smm		if (name == NULL)
604207753Smm			name = argv[0];
605207753Smm		else
606207753Smm			++name;
607207753Smm
608207753Smm		// NOTE: It's possible that name[0] is now '\0' if argv[0]
609207753Smm		// is weird, but it doesn't matter here.
610207753Smm
611207753Smm		// Look for full command names instead of substrings like
612207753Smm		// "un", "cat", and "lz" to reduce possibility of false
613207753Smm		// positives when the programs have been renamed.
614207753Smm		if (strstr(name, "xzcat") != NULL) {
615207753Smm			opt_mode = MODE_DECOMPRESS;
616207753Smm			opt_stdout = true;
617207753Smm		} else if (strstr(name, "unxz") != NULL) {
618207753Smm			opt_mode = MODE_DECOMPRESS;
619207753Smm		} else if (strstr(name, "lzcat") != NULL) {
620207753Smm			opt_format = FORMAT_LZMA;
621207753Smm			opt_mode = MODE_DECOMPRESS;
622207753Smm			opt_stdout = true;
623207753Smm		} else if (strstr(name, "unlzma") != NULL) {
624207753Smm			opt_format = FORMAT_LZMA;
625207753Smm			opt_mode = MODE_DECOMPRESS;
626207753Smm		} else if (strstr(name, "lzma") != NULL) {
627207753Smm			opt_format = FORMAT_LZMA;
628207753Smm		}
629207753Smm	}
630207753Smm
631213700Smm	// First the flags from the environment
632213700Smm	parse_environment(args, argv[0], "XZ_DEFAULTS");
633213700Smm	parse_environment(args, argv[0], "XZ_OPT");
634207753Smm
635207753Smm	// Then from the command line
636207753Smm	parse_real(args, argc, argv);
637207753Smm
638312518Sdelphij	// If encoder or decoder support was omitted at build time,
639312518Sdelphij	// show an error now so that the rest of the code can rely on
640312518Sdelphij	// that whatever is in opt_mode is also supported.
641312518Sdelphij#ifndef HAVE_ENCODERS
642312518Sdelphij	if (opt_mode == MODE_COMPRESS)
643312518Sdelphij		message_fatal(_("Compression support was disabled "
644312518Sdelphij				"at build time"));
645312518Sdelphij#endif
646312518Sdelphij#ifndef HAVE_DECODERS
647312518Sdelphij	// Even MODE_LIST cannot work without decoder support so MODE_COMPRESS
648312518Sdelphij	// is the only valid choice.
649312518Sdelphij	if (opt_mode != MODE_COMPRESS)
650312518Sdelphij		message_fatal(_("Decompression support was disabled "
651312518Sdelphij				"at build time"));
652312518Sdelphij#endif
653312518Sdelphij
654207753Smm	// Never remove the source file when the destination is not on disk.
655207753Smm	// In test mode the data is written nowhere, but setting opt_stdout
656207753Smm	// will make the rest of the code behave well.
657207753Smm	if (opt_stdout || opt_mode == MODE_TEST) {
658207753Smm		opt_keep_original = true;
659207753Smm		opt_stdout = true;
660207753Smm	}
661207753Smm
662207753Smm	// When compressing, if no --format flag was used, or it
663207753Smm	// was --format=auto, we compress to the .xz format.
664207753Smm	if (opt_mode == MODE_COMPRESS && opt_format == FORMAT_AUTO)
665207753Smm		opt_format = FORMAT_XZ;
666207753Smm
667207753Smm	// Compression settings need to be validated (options themselves and
668207753Smm	// their memory usage) when compressing to any file format. It has to
669207753Smm	// be done also when uncompressing raw data, since for raw decoding
670207753Smm	// the options given on the command line are used to know what kind
671207753Smm	// of raw data we are supposed to decode.
672207753Smm	if (opt_mode == MODE_COMPRESS || opt_format == FORMAT_RAW)
673207753Smm		coder_set_compression_settings();
674207753Smm
675207753Smm	// If no filenames are given, use stdin.
676207753Smm	if (argv[optind] == NULL && args->files_name == NULL) {
677207753Smm		// We don't modify or free() the "-" constant. The caller
678207753Smm		// modifies this so don't make the struct itself const.
679207753Smm		static char *names_stdin[2] = { (char *)"-", NULL };
680207753Smm		args->arg_names = names_stdin;
681207753Smm		args->arg_count = 1;
682207753Smm	} else {
683207753Smm		// We got at least one filename from the command line, or
684207753Smm		// --files or --files0 was specified.
685207753Smm		args->arg_names = argv + optind;
686207753Smm		args->arg_count = argc - optind;
687207753Smm	}
688207753Smm
689207753Smm	return;
690207753Smm}
691292588Sdelphij
692292588Sdelphij
693292588Sdelphij#ifndef NDEBUG
694292588Sdelphijextern void
695292588Sdelphijargs_free(void)
696292588Sdelphij{
697292588Sdelphij	free(opt_block_list);
698292588Sdelphij	return;
699292588Sdelphij}
700292588Sdelphij#endif
701