xref: /freebsd/contrib/xz/src/xz/args.c (revision f126890a)
1 // SPDX-License-Identifier: 0BSD
2 
3 ///////////////////////////////////////////////////////////////////////////////
4 //
5 /// \file       args.c
6 /// \brief      Argument parsing
7 ///
8 /// \note       Filter-specific options parsing is in options.c.
9 //
10 //  Authors:    Lasse Collin
11 //              Jia Tan
12 //
13 ///////////////////////////////////////////////////////////////////////////////
14 
15 #include "private.h"
16 
17 #include "getopt.h"
18 #include <ctype.h>
19 
20 
21 bool opt_stdout = false;
22 bool opt_force = false;
23 bool opt_keep_original = false;
24 bool opt_robot = false;
25 bool opt_ignore_check = false;
26 
27 // We don't modify or free() this, but we need to assign it in some
28 // non-const pointers.
29 const char stdin_filename[] = "(stdin)";
30 
31 
32 /// Parse and set the memory usage limit for compression, decompression,
33 /// and/or multithreaded decompression.
34 static void
35 parse_memlimit(const char *name, const char *name_percentage, const char *str,
36 		bool set_compress, bool set_decompress, bool set_mtdec)
37 {
38 	bool is_percentage = false;
39 	uint64_t value;
40 
41 	const size_t len = strlen(str);
42 	if (len > 0 && str[len - 1] == '%') {
43 		// Make a copy so that we can get rid of %.
44 		//
45 		// In the past str wasn't const and we modified it directly
46 		// but that modified argv[] and thus affected what was visible
47 		// in "ps auxf" or similar tools which was confusing. For
48 		// example, --memlimit=50% would show up as --memlimit=50
49 		// since the percent sign was overwritten here.
50 		char *s = xstrdup(str);
51 		s[len - 1] = '\0';
52 		is_percentage = true;
53 		value = str_to_uint64(name_percentage, s, 1, 100);
54 		free(s);
55 	} else {
56 		// On 32-bit systems, SIZE_MAX would make more sense than
57 		// UINT64_MAX. But use UINT64_MAX still so that scripts
58 		// that assume > 4 GiB values don't break.
59 		value = str_to_uint64(name, str, 0, UINT64_MAX);
60 	}
61 
62 	hardware_memlimit_set(value, set_compress, set_decompress, set_mtdec,
63 			is_percentage);
64 	return;
65 }
66 
67 
68 static void
69 parse_block_list(const char *str_const)
70 {
71 	// We need a modifiable string in the for-loop.
72 	char *str_start = xstrdup(str_const);
73 	char *str = str_start;
74 
75 	// It must be non-empty and not begin with a comma.
76 	if (str[0] == '\0' || str[0] == ',')
77 		message_fatal(_("%s: Invalid argument to --block-list"), str);
78 
79 	// Count the number of comma-separated strings.
80 	size_t count = 1;
81 	for (size_t i = 0; str[i] != '\0'; ++i)
82 		if (str[i] == ',')
83 			++count;
84 
85 	// Prevent an unlikely integer overflow.
86 	if (count > SIZE_MAX / sizeof(block_list_entry) - 1)
87 		message_fatal(_("%s: Too many arguments to --block-list"),
88 				str);
89 
90 	// Allocate memory to hold all the sizes specified.
91 	// If --block-list was specified already, its value is forgotten.
92 	free(opt_block_list);
93 	opt_block_list = xmalloc((count + 1) * sizeof(block_list_entry));
94 
95 	for (size_t i = 0; i < count; ++i) {
96 		// Locate the next comma and replace it with \0.
97 		char *p = strchr(str, ',');
98 		if (p != NULL)
99 			*p = '\0';
100 
101 		// Use the default filter chain unless overridden.
102 		opt_block_list[i].filters_index = 0;
103 
104 		// To specify a filter chain, the block list entry may be
105 		// prepended with "[filter-chain-number]:". The size is
106 		// still required for every block.
107 		// For instance:
108 		// --block-list=2:10MiB,1:5MiB,,8MiB,0:0
109 		//
110 		// Translates to:
111 		// 1. Block of 10 MiB using filter chain 2
112 		// 2. Block of 5 MiB using filter chain 1
113 		// 3. Block of 5 MiB using filter chain 1
114 		// 4. Block of 8 MiB using the default filter chain
115 		// 5. The last block uses the default filter chain
116 		//
117 		// The block list:
118 		// --block-list=2:MiB,1:,0
119 		//
120 		// Is not allowed because the second block does not specify
121 		// the block size, only the filter chain.
122 		if (str[0] >= '0' && str[0] <= '9' && str[1] == ':') {
123 			if (str[2] == '\0')
124 				message_fatal(_("In --block-list, block "
125 						"size is missing after "
126 						"filter chain number '%c:'"),
127 						str[0]);
128 
129 			int filter_num = str[0] - '0';
130 			opt_block_list[i].filters_index =
131 					(uint32_t)filter_num;
132 			str += 2;
133 		}
134 
135 		if (str[0] == '\0') {
136 			// There is no string, that is, a comma follows
137 			// another comma. Use the previous value.
138 			//
139 			// NOTE: We checked earlier that the first char
140 			// of the whole list cannot be a comma.
141 			assert(i > 0);
142 			opt_block_list[i] = opt_block_list[i - 1];
143 		} else {
144 			opt_block_list[i].size = str_to_uint64("block-list",
145 					str, 0, UINT64_MAX);
146 
147 			// Zero indicates no more new Blocks.
148 			if (opt_block_list[i].size == 0) {
149 				if (i + 1 != count)
150 					message_fatal(_("0 can only be used "
151 							"as the last element "
152 							"in --block-list"));
153 
154 				opt_block_list[i].size = UINT64_MAX;
155 			}
156 		}
157 
158 		str = p + 1;
159 	}
160 
161 	// Terminate the array.
162 	opt_block_list[count].size = 0;
163 
164 	free(str_start);
165 	return;
166 }
167 
168 
169 static void
170 parse_real(args_info *args, int argc, char **argv)
171 {
172 	enum {
173 		OPT_FILTERS = INT_MIN,
174 		OPT_FILTERS1,
175 		OPT_FILTERS2,
176 		OPT_FILTERS3,
177 		OPT_FILTERS4,
178 		OPT_FILTERS5,
179 		OPT_FILTERS6,
180 		OPT_FILTERS7,
181 		OPT_FILTERS8,
182 		OPT_FILTERS9,
183 		OPT_FILTERS_HELP,
184 
185 		OPT_X86,
186 		OPT_POWERPC,
187 		OPT_IA64,
188 		OPT_ARM,
189 		OPT_ARMTHUMB,
190 		OPT_ARM64,
191 		OPT_SPARC,
192 		OPT_RISCV,
193 		OPT_DELTA,
194 		OPT_LZMA1,
195 		OPT_LZMA2,
196 
197 		OPT_SINGLE_STREAM,
198 		OPT_NO_SPARSE,
199 		OPT_FILES,
200 		OPT_FILES0,
201 		OPT_BLOCK_SIZE,
202 		OPT_BLOCK_LIST,
203 		OPT_MEM_COMPRESS,
204 		OPT_MEM_DECOMPRESS,
205 		OPT_MEM_MT_DECOMPRESS,
206 		OPT_NO_ADJUST,
207 		OPT_INFO_MEMORY,
208 		OPT_ROBOT,
209 		OPT_FLUSH_TIMEOUT,
210 		OPT_IGNORE_CHECK,
211 	};
212 
213 	static const char short_opts[]
214 			= "cC:defF:hHlkM:qQrS:tT:vVz0123456789";
215 
216 	static const struct option long_opts[] = {
217 		// Operation mode
218 		{ "compress",     no_argument,       NULL,  'z' },
219 		{ "decompress",   no_argument,       NULL,  'd' },
220 		{ "uncompress",   no_argument,       NULL,  'd' },
221 		{ "test",         no_argument,       NULL,  't' },
222 		{ "list",         no_argument,       NULL,  'l' },
223 
224 		// Operation modifiers
225 		{ "keep",         no_argument,       NULL,  'k' },
226 		{ "force",        no_argument,       NULL,  'f' },
227 		{ "stdout",       no_argument,       NULL,  'c' },
228 		{ "to-stdout",    no_argument,       NULL,  'c' },
229 		{ "single-stream", no_argument,      NULL,  OPT_SINGLE_STREAM },
230 		{ "no-sparse",    no_argument,       NULL,  OPT_NO_SPARSE },
231 		{ "suffix",       required_argument, NULL,  'S' },
232 		// { "recursive",      no_argument,       NULL,  'r' }, // TODO
233 		{ "files",        optional_argument, NULL,  OPT_FILES },
234 		{ "files0",       optional_argument, NULL,  OPT_FILES0 },
235 
236 		// Basic compression settings
237 		{ "format",       required_argument, NULL,  'F' },
238 		{ "check",        required_argument, NULL,  'C' },
239 		{ "ignore-check", no_argument,       NULL,  OPT_IGNORE_CHECK },
240 		{ "block-size",   required_argument, NULL,  OPT_BLOCK_SIZE },
241 		{ "block-list",  required_argument, NULL,  OPT_BLOCK_LIST },
242 		{ "memlimit-compress",   required_argument, NULL, OPT_MEM_COMPRESS },
243 		{ "memlimit-decompress", required_argument, NULL, OPT_MEM_DECOMPRESS },
244 		{ "memlimit-mt-decompress", required_argument, NULL, OPT_MEM_MT_DECOMPRESS },
245 		{ "memlimit",     required_argument, NULL,  'M' },
246 		{ "memory",       required_argument, NULL,  'M' }, // Old alias
247 		{ "no-adjust",    no_argument,       NULL,  OPT_NO_ADJUST },
248 		{ "threads",      required_argument, NULL,  'T' },
249 		{ "flush-timeout", required_argument, NULL, OPT_FLUSH_TIMEOUT },
250 
251 		{ "extreme",      no_argument,       NULL,  'e' },
252 		{ "fast",         no_argument,       NULL,  '0' },
253 		{ "best",         no_argument,       NULL,  '9' },
254 
255 		// Filters
256 		{ "filters",      optional_argument, NULL,  OPT_FILTERS},
257 		{ "filters1",     optional_argument, NULL,  OPT_FILTERS1},
258 		{ "filters2",     optional_argument, NULL,  OPT_FILTERS2},
259 		{ "filters3",     optional_argument, NULL,  OPT_FILTERS3},
260 		{ "filters4",     optional_argument, NULL,  OPT_FILTERS4},
261 		{ "filters5",     optional_argument, NULL,  OPT_FILTERS5},
262 		{ "filters6",     optional_argument, NULL,  OPT_FILTERS6},
263 		{ "filters7",     optional_argument, NULL,  OPT_FILTERS7},
264 		{ "filters8",     optional_argument, NULL,  OPT_FILTERS8},
265 		{ "filters9",     optional_argument, NULL,  OPT_FILTERS9},
266 		{ "filters-help", optional_argument, NULL,  OPT_FILTERS_HELP},
267 
268 		{ "lzma1",        optional_argument, NULL,  OPT_LZMA1 },
269 		{ "lzma2",        optional_argument, NULL,  OPT_LZMA2 },
270 		{ "x86",          optional_argument, NULL,  OPT_X86 },
271 		{ "powerpc",      optional_argument, NULL,  OPT_POWERPC },
272 		{ "ia64",         optional_argument, NULL,  OPT_IA64 },
273 		{ "arm",          optional_argument, NULL,  OPT_ARM },
274 		{ "armthumb",     optional_argument, NULL,  OPT_ARMTHUMB },
275 		{ "arm64",        optional_argument, NULL,  OPT_ARM64 },
276 		{ "sparc",        optional_argument, NULL,  OPT_SPARC },
277 		{ "riscv",        optional_argument, NULL,  OPT_RISCV },
278 		{ "delta",        optional_argument, NULL,  OPT_DELTA },
279 
280 		// Other options
281 		{ "quiet",        no_argument,       NULL,  'q' },
282 		{ "verbose",      no_argument,       NULL,  'v' },
283 		{ "no-warn",      no_argument,       NULL,  'Q' },
284 		{ "robot",        no_argument,       NULL,  OPT_ROBOT },
285 		{ "info-memory",  no_argument,       NULL,  OPT_INFO_MEMORY },
286 		{ "help",         no_argument,       NULL,  'h' },
287 		{ "long-help",    no_argument,       NULL,  'H' },
288 		{ "version",      no_argument,       NULL,  'V' },
289 
290 		{ NULL,           0,                 NULL,   0 }
291 	};
292 
293 	int c;
294 
295 	while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL))
296 			!= -1) {
297 		switch (c) {
298 		// Compression preset (also for decompression if --format=raw)
299 		case '0': case '1': case '2': case '3': case '4':
300 		case '5': case '6': case '7': case '8': case '9':
301 			coder_set_preset((uint32_t)(c - '0'));
302 			break;
303 
304 		// --memlimit-compress
305 		case OPT_MEM_COMPRESS:
306 			parse_memlimit("memlimit-compress",
307 					"memlimit-compress%", optarg,
308 					true, false, false);
309 			break;
310 
311 		// --memlimit-decompress
312 		case OPT_MEM_DECOMPRESS:
313 			parse_memlimit("memlimit-decompress",
314 					"memlimit-decompress%", optarg,
315 					false, true, false);
316 			break;
317 
318 		// --memlimit-mt-decompress
319 		case OPT_MEM_MT_DECOMPRESS:
320 			parse_memlimit("memlimit-mt-decompress",
321 					"memlimit-mt-decompress%", optarg,
322 					false, false, true);
323 			break;
324 
325 		// --memlimit
326 		case 'M':
327 			parse_memlimit("memlimit", "memlimit%", optarg,
328 					true, true, true);
329 			break;
330 
331 		// --suffix
332 		case 'S':
333 			suffix_set(optarg);
334 			break;
335 
336 		case 'T': {
337 			// Since xz 5.4.0: Ignore leading '+' first.
338 			const char *s = optarg;
339 			if (optarg[0] == '+')
340 				++s;
341 
342 			// The max is from src/liblzma/common/common.h.
343 			uint32_t t = str_to_uint64("threads", s, 0, 16384);
344 
345 			// If leading '+' was used then use multi-threaded
346 			// mode even if exactly one thread was specified.
347 			if (t == 1 && optarg[0] == '+')
348 				t = UINT32_MAX;
349 
350 			hardware_threads_set(t);
351 			break;
352 		}
353 
354 		// --version
355 		case 'V':
356 			// This doesn't return.
357 			message_version();
358 
359 		// --stdout
360 		case 'c':
361 			opt_stdout = true;
362 			break;
363 
364 		// --decompress
365 		case 'd':
366 			opt_mode = MODE_DECOMPRESS;
367 			break;
368 
369 		// --extreme
370 		case 'e':
371 			coder_set_extreme();
372 			break;
373 
374 		// --force
375 		case 'f':
376 			opt_force = true;
377 			break;
378 
379 		// --info-memory
380 		case OPT_INFO_MEMORY:
381 			// This doesn't return.
382 			hardware_memlimit_show();
383 
384 		// --help
385 		case 'h':
386 			// This doesn't return.
387 			message_help(false);
388 
389 		// --long-help
390 		case 'H':
391 			// This doesn't return.
392 			message_help(true);
393 
394 		// --list
395 		case 'l':
396 			opt_mode = MODE_LIST;
397 			break;
398 
399 		// --keep
400 		case 'k':
401 			opt_keep_original = true;
402 			break;
403 
404 		// --quiet
405 		case 'q':
406 			message_verbosity_decrease();
407 			break;
408 
409 		case 'Q':
410 			set_exit_no_warn();
411 			break;
412 
413 		case 't':
414 			opt_mode = MODE_TEST;
415 			break;
416 
417 		// --verbose
418 		case 'v':
419 			message_verbosity_increase();
420 			break;
421 
422 		// --robot
423 		case OPT_ROBOT:
424 			opt_robot = true;
425 
426 			// This is to make sure that floating point numbers
427 			// always have a dot as decimal separator.
428 			setlocale(LC_NUMERIC, "C");
429 			break;
430 
431 		case 'z':
432 			opt_mode = MODE_COMPRESS;
433 			break;
434 
435 		// --filters
436 		case OPT_FILTERS:
437 			coder_add_filters_from_str(optarg);
438 			break;
439 
440 		// --filters1...--filters9
441 		case OPT_FILTERS1:
442 		case OPT_FILTERS2:
443 		case OPT_FILTERS3:
444 		case OPT_FILTERS4:
445 		case OPT_FILTERS5:
446 		case OPT_FILTERS6:
447 		case OPT_FILTERS7:
448 		case OPT_FILTERS8:
449 		case OPT_FILTERS9:
450 			coder_add_block_filters(optarg,
451 					(size_t)(c - OPT_FILTERS));
452 			break;
453 
454 		// --filters-help
455 		case OPT_FILTERS_HELP:
456 			// This doesn't return.
457 			message_filters_help();
458 			break;
459 
460 		case OPT_X86:
461 			coder_add_filter(LZMA_FILTER_X86,
462 					options_bcj(optarg));
463 			break;
464 
465 		case OPT_POWERPC:
466 			coder_add_filter(LZMA_FILTER_POWERPC,
467 					options_bcj(optarg));
468 			break;
469 
470 		case OPT_IA64:
471 			coder_add_filter(LZMA_FILTER_IA64,
472 					options_bcj(optarg));
473 			break;
474 
475 		case OPT_ARM:
476 			coder_add_filter(LZMA_FILTER_ARM,
477 					options_bcj(optarg));
478 			break;
479 
480 		case OPT_ARMTHUMB:
481 			coder_add_filter(LZMA_FILTER_ARMTHUMB,
482 					options_bcj(optarg));
483 			break;
484 
485 		case OPT_ARM64:
486 			coder_add_filter(LZMA_FILTER_ARM64,
487 					options_bcj(optarg));
488 			break;
489 
490 		case OPT_SPARC:
491 			coder_add_filter(LZMA_FILTER_SPARC,
492 					options_bcj(optarg));
493 			break;
494 
495 		case OPT_RISCV:
496 			coder_add_filter(LZMA_FILTER_RISCV,
497 					options_bcj(optarg));
498 			break;
499 
500 		case OPT_DELTA:
501 			coder_add_filter(LZMA_FILTER_DELTA,
502 					options_delta(optarg));
503 			break;
504 
505 		case OPT_LZMA1:
506 			coder_add_filter(LZMA_FILTER_LZMA1,
507 					options_lzma(optarg));
508 			break;
509 
510 		case OPT_LZMA2:
511 			coder_add_filter(LZMA_FILTER_LZMA2,
512 					options_lzma(optarg));
513 			break;
514 
515 		// Other
516 
517 		// --format
518 		case 'F': {
519 			// Just in case, support both "lzma" and "alone" since
520 			// the latter was used for forward compatibility in
521 			// LZMA Utils 4.32.x.
522 			static const struct {
523 				char str[8];
524 				enum format_type format;
525 			} types[] = {
526 				{ "auto",   FORMAT_AUTO },
527 				{ "xz",     FORMAT_XZ },
528 				{ "lzma",   FORMAT_LZMA },
529 				{ "alone",  FORMAT_LZMA },
530 #ifdef HAVE_LZIP_DECODER
531 				{ "lzip",   FORMAT_LZIP },
532 #endif
533 				{ "raw",    FORMAT_RAW },
534 			};
535 
536 			size_t i = 0;
537 			while (strcmp(types[i].str, optarg) != 0)
538 				if (++i == ARRAY_SIZE(types))
539 					message_fatal(_("%s: Unknown file "
540 							"format type"),
541 							optarg);
542 
543 			opt_format = types[i].format;
544 			break;
545 		}
546 
547 		// --check
548 		case 'C': {
549 			static const struct {
550 				char str[8];
551 				lzma_check check;
552 			} types[] = {
553 				{ "none",   LZMA_CHECK_NONE },
554 				{ "crc32",  LZMA_CHECK_CRC32 },
555 				{ "crc64",  LZMA_CHECK_CRC64 },
556 				{ "sha256", LZMA_CHECK_SHA256 },
557 			};
558 
559 			size_t i = 0;
560 			while (strcmp(types[i].str, optarg) != 0) {
561 				if (++i == ARRAY_SIZE(types))
562 					message_fatal(_("%s: Unsupported "
563 							"integrity "
564 							"check type"), optarg);
565 			}
566 
567 			// Use a separate check in case we are using different
568 			// liblzma than what was used to compile us.
569 			if (!lzma_check_is_supported(types[i].check))
570 				message_fatal(_("%s: Unsupported integrity "
571 						"check type"), optarg);
572 
573 			coder_set_check(types[i].check);
574 			break;
575 		}
576 
577 		case OPT_IGNORE_CHECK:
578 			opt_ignore_check = true;
579 			break;
580 
581 		case OPT_BLOCK_SIZE:
582 			opt_block_size = str_to_uint64("block-size", optarg,
583 					0, LZMA_VLI_MAX);
584 			break;
585 
586 		case OPT_BLOCK_LIST: {
587 			parse_block_list(optarg);
588 			break;
589 		}
590 
591 		case OPT_SINGLE_STREAM:
592 			opt_single_stream = true;
593 			break;
594 
595 		case OPT_NO_SPARSE:
596 			io_no_sparse();
597 			break;
598 
599 		case OPT_FILES:
600 			args->files_delim = '\n';
601 
602 		// Fall through
603 
604 		case OPT_FILES0:
605 			if (args->files_name != NULL)
606 				message_fatal(_("Only one file can be "
607 						"specified with '--files' "
608 						"or '--files0'."));
609 
610 			if (optarg == NULL) {
611 				args->files_name = stdin_filename;
612 				args->files_file = stdin;
613 			} else {
614 				args->files_name = optarg;
615 				args->files_file = fopen(optarg,
616 						c == OPT_FILES ? "r" : "rb");
617 				if (args->files_file == NULL)
618 					// TRANSLATORS: This is a translatable
619 					// string because French needs a space
620 					// before the colon ("%s : %s").
621 					message_fatal(_("%s: %s"), optarg,
622 							strerror(errno));
623 			}
624 
625 			break;
626 
627 		case OPT_NO_ADJUST:
628 			opt_auto_adjust = false;
629 			break;
630 
631 		case OPT_FLUSH_TIMEOUT:
632 			opt_flush_timeout = str_to_uint64("flush-timeout",
633 					optarg, 0, UINT64_MAX);
634 			break;
635 
636 		default:
637 			message_try_help();
638 			tuklib_exit(E_ERROR, E_ERROR, false);
639 		}
640 	}
641 
642 	return;
643 }
644 
645 
646 static void
647 parse_environment(args_info *args, char *argv0, const char *varname)
648 {
649 	char *env = getenv(varname);
650 	if (env == NULL)
651 		return;
652 
653 	// We modify the string, so make a copy of it.
654 	env = xstrdup(env);
655 
656 	// Calculate the number of arguments in env. argc stats at one
657 	// to include space for the program name.
658 	int argc = 1;
659 	bool prev_was_space = true;
660 	for (size_t i = 0; env[i] != '\0'; ++i) {
661 		// NOTE: Cast to unsigned char is needed so that correct
662 		// value gets passed to isspace(), which expects
663 		// unsigned char cast to int. Casting to int is done
664 		// automatically due to integer promotion, but we need to
665 		// force char to unsigned char manually. Otherwise 8-bit
666 		// characters would get promoted to wrong value if
667 		// char is signed.
668 		if (isspace((unsigned char)env[i])) {
669 			prev_was_space = true;
670 		} else if (prev_was_space) {
671 			prev_was_space = false;
672 
673 			// Keep argc small enough to fit into a signed int
674 			// and to keep it usable for memory allocation.
675 			if (++argc == my_min(
676 					INT_MAX, SIZE_MAX / sizeof(char *)))
677 				message_fatal(_("The environment variable "
678 						"%s contains too many "
679 						"arguments"), varname);
680 		}
681 	}
682 
683 	// Allocate memory to hold pointers to the arguments. Add one to get
684 	// space for the terminating NULL (if some systems happen to need it).
685 	char **argv = xmalloc(((size_t)(argc) + 1) * sizeof(char *));
686 	argv[0] = argv0;
687 	argv[argc] = NULL;
688 
689 	// Go through the string again. Split the arguments using '\0'
690 	// characters and add pointers to the resulting strings to argv.
691 	argc = 1;
692 	prev_was_space = true;
693 	for (size_t i = 0; env[i] != '\0'; ++i) {
694 		if (isspace((unsigned char)env[i])) {
695 			prev_was_space = true;
696 			env[i] = '\0';
697 		} else if (prev_was_space) {
698 			prev_was_space = false;
699 			argv[argc++] = env + i;
700 		}
701 	}
702 
703 	// Parse the argument list we got from the environment. All non-option
704 	// arguments i.e. filenames are ignored.
705 	parse_real(args, argc, argv);
706 
707 	// Reset the state of the getopt_long() so that we can parse the
708 	// command line options too. There are two incompatible ways to
709 	// do it.
710 #ifdef HAVE_OPTRESET
711 	// BSD
712 	optind = 1;
713 	optreset = 1;
714 #else
715 	// GNU, Solaris
716 	optind = 0;
717 #endif
718 
719 	// We don't need the argument list from environment anymore.
720 	free(argv);
721 	free(env);
722 
723 	return;
724 }
725 
726 
727 extern void
728 args_parse(args_info *args, int argc, char **argv)
729 {
730 	// Initialize those parts of *args that we need later.
731 	args->files_name = NULL;
732 	args->files_file = NULL;
733 	args->files_delim = '\0';
734 
735 	// Check how we were called.
736 	{
737 		// Remove the leading path name, if any.
738 		const char *name = strrchr(argv[0], '/');
739 		if (name == NULL)
740 			name = argv[0];
741 		else
742 			++name;
743 
744 		// NOTE: It's possible that name[0] is now '\0' if argv[0]
745 		// is weird, but it doesn't matter here.
746 
747 		// Look for full command names instead of substrings like
748 		// "un", "cat", and "lz" to reduce possibility of false
749 		// positives when the programs have been renamed.
750 		if (strstr(name, "xzcat") != NULL) {
751 			opt_mode = MODE_DECOMPRESS;
752 			opt_stdout = true;
753 		} else if (strstr(name, "unxz") != NULL) {
754 			opt_mode = MODE_DECOMPRESS;
755 		} else if (strstr(name, "lzcat") != NULL) {
756 			opt_format = FORMAT_LZMA;
757 			opt_mode = MODE_DECOMPRESS;
758 			opt_stdout = true;
759 		} else if (strstr(name, "unlzma") != NULL) {
760 			opt_format = FORMAT_LZMA;
761 			opt_mode = MODE_DECOMPRESS;
762 		} else if (strstr(name, "lzma") != NULL) {
763 			opt_format = FORMAT_LZMA;
764 		}
765 	}
766 
767 	// First the flags from the environment
768 	parse_environment(args, argv[0], "XZ_DEFAULTS");
769 	parse_environment(args, argv[0], "XZ_OPT");
770 
771 	// Then from the command line
772 	parse_real(args, argc, argv);
773 
774 	// If encoder or decoder support was omitted at build time,
775 	// show an error now so that the rest of the code can rely on
776 	// that whatever is in opt_mode is also supported.
777 #ifndef HAVE_ENCODERS
778 	if (opt_mode == MODE_COMPRESS)
779 		message_fatal(_("Compression support was disabled "
780 				"at build time"));
781 #endif
782 #ifndef HAVE_DECODERS
783 	// Even MODE_LIST cannot work without decoder support so MODE_COMPRESS
784 	// is the only valid choice.
785 	if (opt_mode != MODE_COMPRESS)
786 		message_fatal(_("Decompression support was disabled "
787 				"at build time"));
788 #endif
789 
790 #ifdef HAVE_LZIP_DECODER
791 	if (opt_mode == MODE_COMPRESS && opt_format == FORMAT_LZIP)
792 		message_fatal(_("Compression of lzip files (.lz) "
793 				"is not supported"));
794 #endif
795 
796 	// Never remove the source file when the destination is not on disk.
797 	// In test mode the data is written nowhere, but setting opt_stdout
798 	// will make the rest of the code behave well.
799 	if (opt_stdout || opt_mode == MODE_TEST) {
800 		opt_keep_original = true;
801 		opt_stdout = true;
802 	}
803 
804 	// When compressing, if no --format flag was used, or it
805 	// was --format=auto, we compress to the .xz format.
806 	if (opt_mode == MODE_COMPRESS && opt_format == FORMAT_AUTO)
807 		opt_format = FORMAT_XZ;
808 
809 	// Set opt_block_list to NULL if we are not compressing to the .xz
810 	// format. This option cannot be used outside of this case, and
811 	// simplifies the implementation later.
812 	if ((opt_mode != MODE_COMPRESS || opt_format != FORMAT_XZ)
813 			&& opt_block_list != NULL) {
814 		message(V_WARNING, _("--block-list is ignored unless "
815 				"compressing to the .xz format"));
816 		free(opt_block_list);
817 		opt_block_list = NULL;
818 	}
819 
820 	// If raw format is used and a custom suffix is not provided,
821 	// then only stdout mode can be used when compressing or
822 	// decompressing.
823 	if (opt_format == FORMAT_RAW && !suffix_is_set() && !opt_stdout
824 			&& (opt_mode == MODE_COMPRESS
825 				|| opt_mode == MODE_DECOMPRESS)) {
826 		if (args->files_name != NULL)
827 			message_fatal(_("With --format=raw, "
828 					"--suffix=.SUF is required "
829 					"unless writing to stdout"));
830 
831 		// If all of the filenames provided are "-" (more than one
832 		// "-" could be specified) or no filenames are provided,
833 		// then we are only going to be writing to standard out.
834 		for (int i = optind; i < argc; i++) {
835 			if (strcmp(argv[i], "-") != 0)
836 				message_fatal(_("With --format=raw, "
837 						"--suffix=.SUF is required "
838 						"unless writing to stdout"));
839 		}
840 	}
841 
842 	// Compression settings need to be validated (options themselves and
843 	// their memory usage) when compressing to any file format. It has to
844 	// be done also when uncompressing raw data, since for raw decoding
845 	// the options given on the command line are used to know what kind
846 	// of raw data we are supposed to decode.
847 	if (opt_mode == MODE_COMPRESS || (opt_format == FORMAT_RAW
848 			&& opt_mode != MODE_LIST))
849 		coder_set_compression_settings();
850 
851 	// If no filenames are given, use stdin.
852 	if (argv[optind] == NULL && args->files_name == NULL) {
853 		// We don't modify or free() the "-" constant. The caller
854 		// modifies this so don't make the struct itself const.
855 		static char *names_stdin[2] = { (char *)"-", NULL };
856 		args->arg_names = names_stdin;
857 		args->arg_count = 1;
858 	} else {
859 		// We got at least one filename from the command line, or
860 		// --files or --files0 was specified.
861 		args->arg_names = argv + optind;
862 		args->arg_count = (unsigned int)(argc - optind);
863 	}
864 
865 	return;
866 }
867 
868 
869 #ifndef NDEBUG
870 extern void
871 args_free(void)
872 {
873 	free(opt_block_list);
874 	return;
875 }
876 #endif
877