xref: /freebsd/contrib/xz/src/xz/list.c (revision 535af610)
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 /// \file       list.c
4 /// \brief      Listing information about .xz files
5 //
6 //  Author:     Lasse Collin
7 //
8 //  This file has been put into the public domain.
9 //  You can do whatever you want with this file.
10 //
11 ///////////////////////////////////////////////////////////////////////////////
12 
13 #include "private.h"
14 #include "tuklib_integer.h"
15 
16 
17 /// Information about a .xz file
18 typedef struct {
19 	/// Combined Index of all Streams in the file
20 	lzma_index *idx;
21 
22 	/// Total amount of Stream Padding
23 	uint64_t stream_padding;
24 
25 	/// Highest memory usage so far
26 	uint64_t memusage_max;
27 
28 	/// True if all Blocks so far have Compressed Size and
29 	/// Uncompressed Size fields
30 	bool all_have_sizes;
31 
32 	/// Oldest XZ Utils version that will decompress the file
33 	uint32_t min_version;
34 
35 } xz_file_info;
36 
37 #define XZ_FILE_INFO_INIT { NULL, 0, 0, true, 50000002 }
38 
39 
40 /// Information about a .xz Block
41 typedef struct {
42 	/// Size of the Block Header
43 	uint32_t header_size;
44 
45 	/// A few of the Block Flags as a string
46 	char flags[3];
47 
48 	/// Size of the Compressed Data field in the Block
49 	lzma_vli compressed_size;
50 
51 	/// Decoder memory usage for this Block
52 	uint64_t memusage;
53 
54 	/// The filter chain of this Block in human-readable form
55 	char *filter_chain;
56 
57 } block_header_info;
58 
59 #define BLOCK_HEADER_INFO_INIT { .filter_chain = NULL }
60 #define block_header_info_end(bhi) free((bhi)->filter_chain)
61 
62 
63 /// Strings ending in a colon. These are used for lines like
64 /// "  Foo:   123 MiB". These are grouped because translated strings
65 /// may have different maximum string length, and we want to pad all
66 /// strings so that the values are aligned nicely.
67 static const char *colon_strs[] = {
68 	N_("Streams:"),
69 	N_("Blocks:"),
70 	N_("Compressed size:"),
71 	N_("Uncompressed size:"),
72 	N_("Ratio:"),
73 	N_("Check:"),
74 	N_("Stream Padding:"),
75 	N_("Memory needed:"),
76 	N_("Sizes in headers:"),
77 	// This won't be aligned because it's so long:
78 	//N_("Minimum XZ Utils version:"),
79 	N_("Number of files:"),
80 };
81 
82 /// Enum matching the above strings.
83 enum {
84 	COLON_STR_STREAMS,
85 	COLON_STR_BLOCKS,
86 	COLON_STR_COMPRESSED_SIZE,
87 	COLON_STR_UNCOMPRESSED_SIZE,
88 	COLON_STR_RATIO,
89 	COLON_STR_CHECK,
90 	COLON_STR_STREAM_PADDING,
91 	COLON_STR_MEMORY_NEEDED,
92 	COLON_STR_SIZES_IN_HEADERS,
93 	//COLON_STR_MINIMUM_XZ_VERSION,
94 	COLON_STR_NUMBER_OF_FILES,
95 };
96 
97 /// Field widths to use with printf to pad the strings to use the same number
98 /// of columns on a terminal.
99 static int colon_strs_fw[ARRAY_SIZE(colon_strs)];
100 
101 /// Convenience macro to get the translated string and its field width
102 /// using a COLON_STR_foo enum.
103 #define COLON_STR(num) colon_strs_fw[num], _(colon_strs[num])
104 
105 
106 /// Column headings
107 static struct {
108 	/// Table column heading string
109 	const char *str;
110 
111 	/// Number of terminal-columns to use for this table-column.
112 	/// If a translated string is longer than the initial value,
113 	/// this value will be increased in init_headings().
114 	int columns;
115 
116 	/// Field width to use for printf() to pad "str" to use "columns"
117 	/// number of columns on a terminal. This is calculated in
118 	/// init_headings().
119 	int fw;
120 
121 } headings[] = {
122 	{ N_("Stream"), 6, 0 },
123 	{ N_("Block"), 9, 0 },
124 	{ N_("Blocks"), 9, 0 },
125 	{ N_("CompOffset"), 15, 0 },
126 	{ N_("UncompOffset"), 15, 0 },
127 	{ N_("CompSize"), 15, 0 },
128 	{ N_("UncompSize"), 15, 0 },
129 	{ N_("TotalSize"), 15, 0 },
130 	{ N_("Ratio"), 5, 0 },
131 	{ N_("Check"), 10, 0 },
132 	{ N_("CheckVal"), 1, 0 },
133 	{ N_("Padding"), 7, 0 },
134 	{ N_("Header"), 5, 0 },
135 	{ N_("Flags"), 2, 0 },
136 	{ N_("MemUsage"), 7 + 4, 0 }, // +4 is for " MiB"
137 	{ N_("Filters"), 1, 0 },
138 };
139 
140 /// Enum matching the above strings.
141 enum {
142 	HEADING_STREAM,
143 	HEADING_BLOCK,
144 	HEADING_BLOCKS,
145 	HEADING_COMPOFFSET,
146 	HEADING_UNCOMPOFFSET,
147 	HEADING_COMPSIZE,
148 	HEADING_UNCOMPSIZE,
149 	HEADING_TOTALSIZE,
150 	HEADING_RATIO,
151 	HEADING_CHECK,
152 	HEADING_CHECKVAL,
153 	HEADING_PADDING,
154 	HEADING_HEADERSIZE,
155 	HEADING_HEADERFLAGS,
156 	HEADING_MEMUSAGE,
157 	HEADING_FILTERS,
158 };
159 
160 #define HEADING_STR(num) headings[num].fw, _(headings[num].str)
161 
162 
163 /// Check ID to string mapping
164 static const char check_names[LZMA_CHECK_ID_MAX + 1][12] = {
165 	// TRANSLATORS: Indicates that there is no integrity check.
166 	// This string is used in tables. In older xz version this
167 	// string was limited to ten columns in a fixed-width font, but
168 	// nowadays there is no strict length restriction anymore.
169 	N_("None"),
170 	"CRC32",
171 	// TRANSLATORS: Indicates that integrity check name is not known,
172 	// but the Check ID is known (here 2). In older xz version these
173 	// strings were limited to ten columns in a fixed-width font, but
174 	// nowadays there is no strict length restriction anymore.
175 	N_("Unknown-2"),
176 	N_("Unknown-3"),
177 	"CRC64",
178 	N_("Unknown-5"),
179 	N_("Unknown-6"),
180 	N_("Unknown-7"),
181 	N_("Unknown-8"),
182 	N_("Unknown-9"),
183 	"SHA-256",
184 	N_("Unknown-11"),
185 	N_("Unknown-12"),
186 	N_("Unknown-13"),
187 	N_("Unknown-14"),
188 	N_("Unknown-15"),
189 };
190 
191 /// Buffer size for get_check_names(). This may be a bit ridiculous,
192 /// but at least it's enough if some language needs many multibyte chars.
193 #define CHECKS_STR_SIZE 1024
194 
195 
196 /// Value of the Check field as hexadecimal string.
197 /// This is set by parse_check_value().
198 static char check_value[2 * LZMA_CHECK_SIZE_MAX + 1];
199 
200 
201 /// Totals that are displayed if there was more than one file.
202 /// The "files" counter is also used in print_info_adv() to show
203 /// the file number.
204 static struct {
205 	uint64_t files;
206 	uint64_t streams;
207 	uint64_t blocks;
208 	uint64_t compressed_size;
209 	uint64_t uncompressed_size;
210 	uint64_t stream_padding;
211 	uint64_t memusage_max;
212 	uint32_t checks;
213 	uint32_t min_version;
214 	bool all_have_sizes;
215 } totals = { 0, 0, 0, 0, 0, 0, 0, 0, 50000002, true };
216 
217 
218 /// Initialize colon_strs_fw[].
219 static void
220 init_colon_strs(void)
221 {
222 	// Lengths of translated strings as bytes.
223 	size_t lens[ARRAY_SIZE(colon_strs)];
224 
225 	// Lengths of translated strings as columns.
226 	size_t widths[ARRAY_SIZE(colon_strs)];
227 
228 	// Maximum number of columns needed by a translated string.
229 	size_t width_max = 0;
230 
231 	for (unsigned i = 0; i < ARRAY_SIZE(colon_strs); ++i) {
232 		widths[i] = tuklib_mbstr_width(_(colon_strs[i]), &lens[i]);
233 
234 		// If debugging is enabled, catch invalid strings with
235 		// an assertion. However, when not debugging, use the
236 		// byte count as the fallback width. This shouldn't
237 		// ever happen unless there is a bad string in the
238 		// translations, but in such case I guess it's better
239 		// to try to print something useful instead of failing
240 		// completely.
241 		assert(widths[i] != (size_t)-1);
242 		if (widths[i] == (size_t)-1)
243 			widths[i] = lens[i];
244 
245 		if (widths[i] > width_max)
246 			width_max = widths[i];
247 	}
248 
249 	// Calculate the field width for printf("%*s") so that the strings
250 	// will use width_max columns on a terminal.
251 	for (unsigned i = 0; i < ARRAY_SIZE(colon_strs); ++i)
252 		colon_strs_fw[i] = (int)(lens[i] + width_max - widths[i]);
253 
254 	return;
255 }
256 
257 
258 /// Initialize headings[].
259 static void
260 init_headings(void)
261 {
262 	// Before going through the heading strings themselves, treat
263 	// the Check heading specially: Look at the widths of the various
264 	// check names and increase the width of the Check column if needed.
265 	// The width of the heading name "Check" will then be handled normally
266 	// with other heading names in the second loop in this function.
267 	for (unsigned i = 0; i < ARRAY_SIZE(check_names); ++i) {
268 		size_t len;
269 		size_t w = tuklib_mbstr_width(_(check_names[i]), &len);
270 
271 		// Error handling like in init_colon_strs().
272 		assert(w != (size_t)-1);
273 		if (w == (size_t)-1)
274 			w = len;
275 
276 		// If the translated string is wider than the minimum width
277 		// set at compile time, increase the width.
278 		if ((size_t)(headings[HEADING_CHECK].columns) < w)
279 			headings[HEADING_CHECK].columns = (int)w;
280 	}
281 
282 	for (unsigned i = 0; i < ARRAY_SIZE(headings); ++i) {
283 		size_t len;
284 		size_t w = tuklib_mbstr_width(_(headings[i].str), &len);
285 
286 		// Error handling like in init_colon_strs().
287 		assert(w != (size_t)-1);
288 		if (w == (size_t)-1)
289 			w = len;
290 
291 		// If the translated string is wider than the minimum width
292 		// set at compile time, increase the width.
293 		if ((size_t)(headings[i].columns) < w)
294 			headings[i].columns = (int)w;
295 
296 		// Calculate the field width for printf("%*s") so that
297 		// the string uses .columns number of columns on a terminal.
298 		headings[i].fw = (int)(len + (size_t)headings[i].columns - w);
299 	}
300 
301 	return;
302 }
303 
304 
305 /// Initialize the printf field widths that are needed to get nicely aligned
306 /// output with translated strings.
307 static void
308 init_field_widths(void)
309 {
310 	init_colon_strs();
311 	init_headings();
312 	return;
313 }
314 
315 
316 /// Convert XZ Utils version number to a string.
317 static const char *
318 xz_ver_to_str(uint32_t ver)
319 {
320 	static char buf[32];
321 
322 	unsigned int major = ver / 10000000U;
323 	ver -= major * 10000000U;
324 
325 	unsigned int minor = ver / 10000U;
326 	ver -= minor * 10000U;
327 
328 	unsigned int patch = ver / 10U;
329 	ver -= patch * 10U;
330 
331 	const char *stability = ver == 0 ? "alpha" : ver == 1 ? "beta" : "";
332 
333 	snprintf(buf, sizeof(buf), "%u.%u.%u%s",
334 			major, minor, patch, stability);
335 	return buf;
336 }
337 
338 
339 /// \brief      Parse the Index(es) from the given .xz file
340 ///
341 /// \param      xfi     Pointer to structure where the decoded information
342 ///                     is stored.
343 /// \param      pair    Input file
344 ///
345 /// \return     On success, false is returned. On error, true is returned.
346 ///
347 static bool
348 parse_indexes(xz_file_info *xfi, file_pair *pair)
349 {
350 	if (pair->src_st.st_size <= 0) {
351 		message_error(_("%s: File is empty"), pair->src_name);
352 		return true;
353 	}
354 
355 	if (pair->src_st.st_size < 2 * LZMA_STREAM_HEADER_SIZE) {
356 		message_error(_("%s: Too small to be a valid .xz file"),
357 				pair->src_name);
358 		return true;
359 	}
360 
361 	io_buf buf;
362 	lzma_stream strm = LZMA_STREAM_INIT;
363 	lzma_index *idx = NULL;
364 
365 	lzma_ret ret = lzma_file_info_decoder(&strm, &idx,
366 			hardware_memlimit_get(MODE_LIST),
367 			(uint64_t)(pair->src_st.st_size));
368 	if (ret != LZMA_OK) {
369 		message_error(_("%s: %s"), pair->src_name, message_strm(ret));
370 		return true;
371 	}
372 
373 	while (true) {
374 		if (strm.avail_in == 0) {
375 			strm.next_in = buf.u8;
376 			strm.avail_in = io_read(pair, &buf, IO_BUFFER_SIZE);
377 			if (strm.avail_in == SIZE_MAX)
378 				goto error;
379 		}
380 
381 		ret = lzma_code(&strm, LZMA_RUN);
382 
383 		switch (ret) {
384 		case LZMA_OK:
385 			break;
386 
387 		case LZMA_SEEK_NEEDED:
388 			// liblzma won't ask us to seek past the known size
389 			// of the input file.
390 			assert(strm.seek_pos
391 					<= (uint64_t)(pair->src_st.st_size));
392 			if (io_seek_src(pair, strm.seek_pos))
393 				goto error;
394 
395 			// avail_in must be zero so that we will read new
396 			// input.
397 			strm.avail_in = 0;
398 			break;
399 
400 		case LZMA_STREAM_END: {
401 			lzma_end(&strm);
402 			xfi->idx = idx;
403 
404 			// Calculate xfi->stream_padding.
405 			lzma_index_iter iter;
406 			lzma_index_iter_init(&iter, xfi->idx);
407 			while (!lzma_index_iter_next(&iter,
408 					LZMA_INDEX_ITER_STREAM))
409 				xfi->stream_padding += iter.stream.padding;
410 
411 			return false;
412 		}
413 
414 		default:
415 			message_error(_("%s: %s"), pair->src_name,
416 					message_strm(ret));
417 
418 			// If the error was too low memory usage limit,
419 			// show also how much memory would have been needed.
420 			if (ret == LZMA_MEMLIMIT_ERROR)
421 				message_mem_needed(V_ERROR,
422 						lzma_memusage(&strm));
423 
424 			goto error;
425 		}
426 	}
427 
428 error:
429 	lzma_end(&strm);
430 	return true;
431 }
432 
433 
434 /// \brief      Parse the Block Header
435 ///
436 /// The result is stored into *bhi. The caller takes care of initializing it.
437 ///
438 /// \return     False on success, true on error.
439 static bool
440 parse_block_header(file_pair *pair, const lzma_index_iter *iter,
441 		block_header_info *bhi, xz_file_info *xfi)
442 {
443 #if IO_BUFFER_SIZE < LZMA_BLOCK_HEADER_SIZE_MAX
444 #	error IO_BUFFER_SIZE < LZMA_BLOCK_HEADER_SIZE_MAX
445 #endif
446 
447 	// Get the whole Block Header with one read, but don't read past
448 	// the end of the Block (or even its Check field).
449 	const uint32_t size = my_min(iter->block.total_size
450 				- lzma_check_size(iter->stream.flags->check),
451 			LZMA_BLOCK_HEADER_SIZE_MAX);
452 	io_buf buf;
453 	if (io_pread(pair, &buf, size, iter->block.compressed_file_offset))
454 		return true;
455 
456 	// Zero would mean Index Indicator and thus not a valid Block.
457 	if (buf.u8[0] == 0)
458 		goto data_error;
459 
460 	// Initialize the block structure and decode Block Header Size.
461 	lzma_filter filters[LZMA_FILTERS_MAX + 1];
462 	lzma_block block;
463 	block.version = 0;
464 	block.check = iter->stream.flags->check;
465 	block.filters = filters;
466 
467 	block.header_size = lzma_block_header_size_decode(buf.u8[0]);
468 	if (block.header_size > size)
469 		goto data_error;
470 
471 	// Decode the Block Header.
472 	switch (lzma_block_header_decode(&block, NULL, buf.u8)) {
473 	case LZMA_OK:
474 		break;
475 
476 	case LZMA_OPTIONS_ERROR:
477 		message_error(_("%s: %s"), pair->src_name,
478 				message_strm(LZMA_OPTIONS_ERROR));
479 		return true;
480 
481 	case LZMA_DATA_ERROR:
482 		goto data_error;
483 
484 	default:
485 		message_bug();
486 	}
487 
488 	// Check the Block Flags. These must be done before calling
489 	// lzma_block_compressed_size(), because it overwrites
490 	// block.compressed_size.
491 	//
492 	// NOTE: If you add new characters here, update the minimum number of
493 	// columns in headings[HEADING_HEADERFLAGS] to match the number of
494 	// characters used here.
495 	bhi->flags[0] = block.compressed_size != LZMA_VLI_UNKNOWN
496 			? 'c' : '-';
497 	bhi->flags[1] = block.uncompressed_size != LZMA_VLI_UNKNOWN
498 			? 'u' : '-';
499 	bhi->flags[2] = '\0';
500 
501 	// Collect information if all Blocks have both Compressed Size
502 	// and Uncompressed Size fields. They can be useful e.g. for
503 	// multi-threaded decompression so it can be useful to know it.
504 	xfi->all_have_sizes &= block.compressed_size != LZMA_VLI_UNKNOWN
505 			&& block.uncompressed_size != LZMA_VLI_UNKNOWN;
506 
507 	// Validate or set block.compressed_size.
508 	switch (lzma_block_compressed_size(&block,
509 			iter->block.unpadded_size)) {
510 	case LZMA_OK:
511 		// Validate also block.uncompressed_size if it is present.
512 		// If it isn't present, there's no need to set it since
513 		// we aren't going to actually decompress the Block; if
514 		// we were decompressing, then we should set it so that
515 		// the Block decoder could validate the Uncompressed Size
516 		// that was stored in the Index.
517 		if (block.uncompressed_size == LZMA_VLI_UNKNOWN
518 				|| block.uncompressed_size
519 					== iter->block.uncompressed_size)
520 			break;
521 
522 		// If the above fails, the file is corrupt so
523 		// LZMA_DATA_ERROR is a good error code.
524 
525 	// Fall through
526 
527 	case LZMA_DATA_ERROR:
528 		// Free the memory allocated by lzma_block_header_decode().
529 		lzma_filters_free(filters, NULL);
530 		goto data_error;
531 
532 	default:
533 		message_bug();
534 	}
535 
536 	// Copy the known sizes.
537 	bhi->header_size = block.header_size;
538 	bhi->compressed_size = block.compressed_size;
539 
540 	// Calculate the decoder memory usage and update the maximum
541 	// memory usage of this Block.
542 	bhi->memusage = lzma_raw_decoder_memusage(filters);
543 	if (xfi->memusage_max < bhi->memusage)
544 		xfi->memusage_max = bhi->memusage;
545 
546 	// Determine the minimum XZ Utils version that supports this Block.
547 	//
548 	//   - ARM64 filter needs 5.4.0.
549 	//
550 	//   - 5.0.0 doesn't support empty LZMA2 streams and thus empty
551 	//     Blocks that use LZMA2. This decoder bug was fixed in 5.0.2.
552 	if (xfi->min_version < 50040002U) {
553 		for (size_t i = 0; filters[i].id != LZMA_VLI_UNKNOWN; ++i) {
554 			if (filters[i].id == LZMA_FILTER_ARM64) {
555 				xfi->min_version = 50040002U;
556 				break;
557 			}
558 		}
559 	}
560 
561 	if (xfi->min_version < 50000022U) {
562 		size_t i = 0;
563 		while (filters[i + 1].id != LZMA_VLI_UNKNOWN)
564 			++i;
565 
566 		if (filters[i].id == LZMA_FILTER_LZMA2
567 				&& iter->block.uncompressed_size == 0)
568 			xfi->min_version = 50000022U;
569 	}
570 
571 	// Convert the filter chain to human readable form.
572 	const lzma_ret str_ret = lzma_str_from_filters(
573 			&bhi->filter_chain, filters,
574 			LZMA_STR_DECODER | LZMA_STR_GETOPT_LONG, NULL);
575 
576 	// Free the memory allocated by lzma_block_header_decode().
577 	lzma_filters_free(filters, NULL);
578 
579 	// Check if the stringification succeeded.
580 	if (str_ret != LZMA_OK) {
581 		message_error(_("%s: %s"), pair->src_name,
582 				message_strm(str_ret));
583 		return true;
584 	}
585 
586 	return false;
587 
588 data_error:
589 	// Show the error message.
590 	message_error(_("%s: %s"), pair->src_name,
591 			message_strm(LZMA_DATA_ERROR));
592 	return true;
593 }
594 
595 
596 /// \brief      Parse the Check field and put it into check_value[]
597 ///
598 /// \return     False on success, true on error.
599 static bool
600 parse_check_value(file_pair *pair, const lzma_index_iter *iter)
601 {
602 	// Don't read anything from the file if there is no integrity Check.
603 	if (iter->stream.flags->check == LZMA_CHECK_NONE) {
604 		snprintf(check_value, sizeof(check_value), "---");
605 		return false;
606 	}
607 
608 	// Locate and read the Check field.
609 	const uint32_t size = lzma_check_size(iter->stream.flags->check);
610 	const uint64_t offset = iter->block.compressed_file_offset
611 			+ iter->block.total_size - size;
612 	io_buf buf;
613 	if (io_pread(pair, &buf, size, offset))
614 		return true;
615 
616 	// CRC32 and CRC64 are in little endian. Guess that all the future
617 	// 32-bit and 64-bit Check values are little endian too. It shouldn't
618 	// be a too big problem if this guess is wrong.
619 	if (size == 4)
620 		snprintf(check_value, sizeof(check_value),
621 				"%08" PRIx32, conv32le(buf.u32[0]));
622 	else if (size == 8)
623 		snprintf(check_value, sizeof(check_value),
624 				"%016" PRIx64, conv64le(buf.u64[0]));
625 	else
626 		for (size_t i = 0; i < size; ++i)
627 			snprintf(check_value + i * 2, 3, "%02x", buf.u8[i]);
628 
629 	return false;
630 }
631 
632 
633 /// \brief      Parse detailed information about a Block
634 ///
635 /// Since this requires seek(s), listing information about all Blocks can
636 /// be slow.
637 ///
638 /// \param      pair    Input file
639 /// \param      iter    Location of the Block whose Check value should
640 ///                     be printed.
641 /// \param      bhi     Pointer to structure where to store the information
642 ///                     about the Block Header field.
643 ///
644 /// \return     False on success, true on error. If an error occurs,
645 ///             the error message is printed too so the caller doesn't
646 ///             need to worry about that.
647 static bool
648 parse_details(file_pair *pair, const lzma_index_iter *iter,
649 		block_header_info *bhi, xz_file_info *xfi)
650 {
651 	if (parse_block_header(pair, iter, bhi, xfi))
652 		return true;
653 
654 	if (parse_check_value(pair, iter))
655 		return true;
656 
657 	return false;
658 }
659 
660 
661 /// \brief      Get the compression ratio
662 ///
663 /// This has slightly different format than that is used in message.c.
664 static const char *
665 get_ratio(uint64_t compressed_size, uint64_t uncompressed_size)
666 {
667 	if (uncompressed_size == 0)
668 		return "---";
669 
670 	const double ratio = (double)(compressed_size)
671 			/ (double)(uncompressed_size);
672 	if (ratio > 9.999)
673 		return "---";
674 
675 	static char buf[16];
676 	snprintf(buf, sizeof(buf), "%.3f", ratio);
677 	return buf;
678 }
679 
680 
681 /// \brief      Get a comma-separated list of Check names
682 ///
683 /// The check names are translated with gettext except when in robot mode.
684 ///
685 /// \param      buf     Buffer to hold the resulting string
686 /// \param      checks  Bit mask of Checks to print
687 /// \param      space_after_comma
688 ///                     It's better to not use spaces in table-like listings,
689 ///                     but in more verbose formats a space after a comma
690 ///                     is good for readability.
691 static void
692 get_check_names(char buf[CHECKS_STR_SIZE],
693 		uint32_t checks, bool space_after_comma)
694 {
695 	// If we get called when there are no Checks to print, set checks
696 	// to 1 so that we print "None". This can happen in the robot mode
697 	// when printing the totals line if there are no valid input files.
698 	if (checks == 0)
699 		checks = 1;
700 
701 	char *pos = buf;
702 	size_t left = CHECKS_STR_SIZE;
703 
704 	const char *sep = space_after_comma ? ", " : ",";
705 	bool comma = false;
706 
707 	for (size_t i = 0; i <= LZMA_CHECK_ID_MAX; ++i) {
708 		if (checks & (UINT32_C(1) << i)) {
709 			my_snprintf(&pos, &left, "%s%s",
710 					comma ? sep : "",
711 					opt_robot ? check_names[i]
712 						: _(check_names[i]));
713 			comma = true;
714 		}
715 	}
716 
717 	return;
718 }
719 
720 
721 static bool
722 print_info_basic(const xz_file_info *xfi, file_pair *pair)
723 {
724 	static bool headings_displayed = false;
725 	if (!headings_displayed) {
726 		headings_displayed = true;
727 		// TRANSLATORS: These are column headings. From Strms (Streams)
728 		// to Ratio, the columns are right aligned. Check and Filename
729 		// are left aligned. If you need longer words, it's OK to
730 		// use two lines here. Test with "xz -l foo.xz".
731 		puts(_("Strms  Blocks   Compressed Uncompressed  Ratio  "
732 				"Check   Filename"));
733 	}
734 
735 	char checks[CHECKS_STR_SIZE];
736 	get_check_names(checks, lzma_index_checks(xfi->idx), false);
737 
738 	const char *cols[7] = {
739 		uint64_to_str(lzma_index_stream_count(xfi->idx), 0),
740 		uint64_to_str(lzma_index_block_count(xfi->idx), 1),
741 		uint64_to_nicestr(lzma_index_file_size(xfi->idx),
742 			NICESTR_B, NICESTR_TIB, false, 2),
743 		uint64_to_nicestr(lzma_index_uncompressed_size(xfi->idx),
744 			NICESTR_B, NICESTR_TIB, false, 3),
745 		get_ratio(lzma_index_file_size(xfi->idx),
746 			lzma_index_uncompressed_size(xfi->idx)),
747 		checks,
748 		pair->src_name,
749 	};
750 	printf("%*s %*s  %*s  %*s  %*s  %-*s %s\n",
751 			tuklib_mbstr_fw(cols[0], 5), cols[0],
752 			tuklib_mbstr_fw(cols[1], 7), cols[1],
753 			tuklib_mbstr_fw(cols[2], 11), cols[2],
754 			tuklib_mbstr_fw(cols[3], 11), cols[3],
755 			tuklib_mbstr_fw(cols[4], 5), cols[4],
756 			tuklib_mbstr_fw(cols[5], 7), cols[5],
757 			cols[6]);
758 
759 	return false;
760 }
761 
762 
763 static void
764 print_adv_helper(uint64_t stream_count, uint64_t block_count,
765 		uint64_t compressed_size, uint64_t uncompressed_size,
766 		uint32_t checks, uint64_t stream_padding)
767 {
768 	char checks_str[CHECKS_STR_SIZE];
769 	get_check_names(checks_str, checks, true);
770 
771 	printf("  %-*s %s\n", COLON_STR(COLON_STR_STREAMS),
772 			uint64_to_str(stream_count, 0));
773 	printf("  %-*s %s\n", COLON_STR(COLON_STR_BLOCKS),
774 			uint64_to_str(block_count, 0));
775 	printf("  %-*s %s\n", COLON_STR(COLON_STR_COMPRESSED_SIZE),
776 			uint64_to_nicestr(compressed_size,
777 				NICESTR_B, NICESTR_TIB, true, 0));
778 	printf("  %-*s %s\n", COLON_STR(COLON_STR_UNCOMPRESSED_SIZE),
779 			uint64_to_nicestr(uncompressed_size,
780 				NICESTR_B, NICESTR_TIB, true, 0));
781 	printf("  %-*s %s\n", COLON_STR(COLON_STR_RATIO),
782 			get_ratio(compressed_size, uncompressed_size));
783 	printf("  %-*s %s\n", COLON_STR(COLON_STR_CHECK), checks_str);
784 	printf("  %-*s %s\n", COLON_STR(COLON_STR_STREAM_PADDING),
785 			uint64_to_nicestr(stream_padding,
786 				NICESTR_B, NICESTR_TIB, true, 0));
787 	return;
788 }
789 
790 
791 static bool
792 print_info_adv(xz_file_info *xfi, file_pair *pair)
793 {
794 	// Print the overall information.
795 	print_adv_helper(lzma_index_stream_count(xfi->idx),
796 			lzma_index_block_count(xfi->idx),
797 			lzma_index_file_size(xfi->idx),
798 			lzma_index_uncompressed_size(xfi->idx),
799 			lzma_index_checks(xfi->idx),
800 			xfi->stream_padding);
801 
802 	// Size of the biggest Check. This is used to calculate the width
803 	// of the CheckVal field. The table would get insanely wide if
804 	// we always reserved space for 64-byte Check (128 chars as hex).
805 	uint32_t check_max = 0;
806 
807 	// Print information about the Streams.
808 	//
809 	// All except Check are right aligned; Check is left aligned.
810 	// Test with "xz -lv foo.xz".
811 	printf("  %s\n    %*s %*s %*s %*s %*s %*s  %*s  %-*s %*s\n",
812 			_(colon_strs[COLON_STR_STREAMS]),
813 			HEADING_STR(HEADING_STREAM),
814 			HEADING_STR(HEADING_BLOCKS),
815 			HEADING_STR(HEADING_COMPOFFSET),
816 			HEADING_STR(HEADING_UNCOMPOFFSET),
817 			HEADING_STR(HEADING_COMPSIZE),
818 			HEADING_STR(HEADING_UNCOMPSIZE),
819 			HEADING_STR(HEADING_RATIO),
820 			HEADING_STR(HEADING_CHECK),
821 			HEADING_STR(HEADING_PADDING));
822 
823 	lzma_index_iter iter;
824 	lzma_index_iter_init(&iter, xfi->idx);
825 
826 	while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)) {
827 		const char *cols1[4] = {
828 			uint64_to_str(iter.stream.number, 0),
829 			uint64_to_str(iter.stream.block_count, 1),
830 			uint64_to_str(iter.stream.compressed_offset, 2),
831 			uint64_to_str(iter.stream.uncompressed_offset, 3),
832 		};
833 		printf("    %*s %*s %*s %*s ",
834 			tuklib_mbstr_fw(cols1[0],
835 				headings[HEADING_STREAM].columns),
836 			cols1[0],
837 			tuklib_mbstr_fw(cols1[1],
838 				headings[HEADING_BLOCKS].columns),
839 			cols1[1],
840 			tuklib_mbstr_fw(cols1[2],
841 				headings[HEADING_COMPOFFSET].columns),
842 			cols1[2],
843 			tuklib_mbstr_fw(cols1[3],
844 				headings[HEADING_UNCOMPOFFSET].columns),
845 			cols1[3]);
846 
847 		const char *cols2[5] = {
848 			uint64_to_str(iter.stream.compressed_size, 0),
849 			uint64_to_str(iter.stream.uncompressed_size, 1),
850 			get_ratio(iter.stream.compressed_size,
851 				iter.stream.uncompressed_size),
852 			_(check_names[iter.stream.flags->check]),
853 			uint64_to_str(iter.stream.padding, 2),
854 		};
855 		printf("%*s %*s  %*s  %-*s %*s\n",
856 			tuklib_mbstr_fw(cols2[0],
857 				headings[HEADING_COMPSIZE].columns),
858 			cols2[0],
859 			tuklib_mbstr_fw(cols2[1],
860 				headings[HEADING_UNCOMPSIZE].columns),
861 			cols2[1],
862 			tuklib_mbstr_fw(cols2[2],
863 				headings[HEADING_RATIO].columns),
864 			cols2[2],
865 			tuklib_mbstr_fw(cols2[3],
866 				headings[HEADING_CHECK].columns),
867 			cols2[3],
868 			tuklib_mbstr_fw(cols2[4],
869 				headings[HEADING_PADDING].columns),
870 			cols2[4]);
871 
872 		// Update the maximum Check size.
873 		if (lzma_check_size(iter.stream.flags->check) > check_max)
874 			check_max = lzma_check_size(iter.stream.flags->check);
875 	}
876 
877 	// Cache the verbosity level to a local variable.
878 	const bool detailed = message_verbosity_get() >= V_DEBUG;
879 
880 	// Print information about the Blocks but only if there is
881 	// at least one Block.
882 	if (lzma_index_block_count(xfi->idx) > 0) {
883 		// Calculate the width of the CheckVal column. This can be
884 		// used as is as the field width for printf() when printing
885 		// the actual check value as it is hexadecimal. However, to
886 		// print the column heading, further calculation is needed
887 		// to handle a translated string (it's done a few lines later).
888 		assert(check_max <= LZMA_CHECK_SIZE_MAX);
889 		const int checkval_width = my_max(
890 				headings[HEADING_CHECKVAL].columns,
891 				(int)(2 * check_max));
892 
893 		// All except Check are right aligned; Check is left aligned.
894 		printf("  %s\n    %*s %*s %*s %*s %*s %*s  %*s  %-*s",
895 				_(colon_strs[COLON_STR_BLOCKS]),
896 				HEADING_STR(HEADING_STREAM),
897 				HEADING_STR(HEADING_BLOCK),
898 				HEADING_STR(HEADING_COMPOFFSET),
899 				HEADING_STR(HEADING_UNCOMPOFFSET),
900 				HEADING_STR(HEADING_TOTALSIZE),
901 				HEADING_STR(HEADING_UNCOMPSIZE),
902 				HEADING_STR(HEADING_RATIO),
903 				detailed ? headings[HEADING_CHECK].fw : 1,
904 				_(headings[HEADING_CHECK].str));
905 
906 		if (detailed) {
907 			// CheckVal (Check value), Flags, and Filters are
908 			// left aligned. Block Header Size, CompSize, and
909 			// MemUsage are right aligned. Test with
910 			// "xz -lvv foo.xz".
911 			printf(" %-*s  %*s  %-*s %*s %*s  %s",
912 				headings[HEADING_CHECKVAL].fw
913 					+ checkval_width
914 					- headings[HEADING_CHECKVAL].columns,
915 				_(headings[HEADING_CHECKVAL].str),
916 				HEADING_STR(HEADING_HEADERSIZE),
917 				HEADING_STR(HEADING_HEADERFLAGS),
918 				HEADING_STR(HEADING_COMPSIZE),
919 				HEADING_STR(HEADING_MEMUSAGE),
920 				_(headings[HEADING_FILTERS].str));
921 		}
922 
923 		putchar('\n');
924 
925 		lzma_index_iter_init(&iter, xfi->idx);
926 
927 		// Iterate over the Blocks.
928 		while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)) {
929 			// If in detailed mode, collect the information from
930 			// Block Header before starting to print the next line.
931 			block_header_info bhi = BLOCK_HEADER_INFO_INIT;
932 			if (detailed && parse_details(pair, &iter, &bhi, xfi))
933 				return true;
934 
935 			const char *cols1[4] = {
936 				uint64_to_str(iter.stream.number, 0),
937 				uint64_to_str(
938 					iter.block.number_in_stream, 1),
939 				uint64_to_str(
940 					iter.block.compressed_file_offset, 2),
941 				uint64_to_str(
942 					iter.block.uncompressed_file_offset, 3)
943 			};
944 			printf("    %*s %*s %*s %*s ",
945 				tuklib_mbstr_fw(cols1[0],
946 					headings[HEADING_STREAM].columns),
947 				cols1[0],
948 				tuklib_mbstr_fw(cols1[1],
949 					headings[HEADING_BLOCK].columns),
950 				cols1[1],
951 				tuklib_mbstr_fw(cols1[2],
952 					headings[HEADING_COMPOFFSET].columns),
953 				cols1[2],
954 				tuklib_mbstr_fw(cols1[3], headings[
955 					HEADING_UNCOMPOFFSET].columns),
956 				cols1[3]);
957 
958 			const char *cols2[4] = {
959 				uint64_to_str(iter.block.total_size, 0),
960 				uint64_to_str(iter.block.uncompressed_size,
961 						1),
962 				get_ratio(iter.block.total_size,
963 					iter.block.uncompressed_size),
964 				_(check_names[iter.stream.flags->check])
965 			};
966 			printf("%*s %*s  %*s  %-*s",
967 				tuklib_mbstr_fw(cols2[0],
968 					headings[HEADING_TOTALSIZE].columns),
969 				cols2[0],
970 				tuklib_mbstr_fw(cols2[1],
971 					headings[HEADING_UNCOMPSIZE].columns),
972 				cols2[1],
973 				tuklib_mbstr_fw(cols2[2],
974 					headings[HEADING_RATIO].columns),
975 				cols2[2],
976 				tuklib_mbstr_fw(cols2[3], detailed
977 					? headings[HEADING_CHECK].columns : 1),
978 				cols2[3]);
979 
980 			if (detailed) {
981 				const lzma_vli compressed_size
982 						= iter.block.unpadded_size
983 						- bhi.header_size
984 						- lzma_check_size(
985 						iter.stream.flags->check);
986 
987 				const char *cols3[6] = {
988 					check_value,
989 					uint64_to_str(bhi.header_size, 0),
990 					bhi.flags,
991 					uint64_to_str(compressed_size, 1),
992 					uint64_to_str(
993 						round_up_to_mib(bhi.memusage),
994 						2),
995 					bhi.filter_chain
996 				};
997 				// Show MiB for memory usage, because it
998 				// is the only size which is not in bytes.
999 				printf(" %-*s  %*s  %-*s %*s %*s MiB  %s",
1000 					checkval_width, cols3[0],
1001 					tuklib_mbstr_fw(cols3[1], headings[
1002 						HEADING_HEADERSIZE].columns),
1003 					cols3[1],
1004 					tuklib_mbstr_fw(cols3[2], headings[
1005 						HEADING_HEADERFLAGS].columns),
1006 					cols3[2],
1007 					tuklib_mbstr_fw(cols3[3], headings[
1008 						HEADING_COMPSIZE].columns),
1009 					cols3[3],
1010 					tuklib_mbstr_fw(cols3[4], headings[
1011 						HEADING_MEMUSAGE].columns - 4),
1012 					cols3[4],
1013 					cols3[5]);
1014 			}
1015 
1016 			putchar('\n');
1017 			block_header_info_end(&bhi);
1018 		}
1019 	}
1020 
1021 	if (detailed) {
1022 		printf("  %-*s %s MiB\n", COLON_STR(COLON_STR_MEMORY_NEEDED),
1023 				uint64_to_str(
1024 				round_up_to_mib(xfi->memusage_max), 0));
1025 		printf("  %-*s %s\n", COLON_STR(COLON_STR_SIZES_IN_HEADERS),
1026 				xfi->all_have_sizes ? _("Yes") : _("No"));
1027 		//printf("  %-*s %s\n", COLON_STR(COLON_STR_MINIMUM_XZ_VERSION),
1028 		printf(_("  Minimum XZ Utils version: %s\n"),
1029 				xz_ver_to_str(xfi->min_version));
1030 	}
1031 
1032 	return false;
1033 }
1034 
1035 
1036 static bool
1037 print_info_robot(xz_file_info *xfi, file_pair *pair)
1038 {
1039 	char checks[CHECKS_STR_SIZE];
1040 	get_check_names(checks, lzma_index_checks(xfi->idx), false);
1041 
1042 	printf("name\t%s\n", pair->src_name);
1043 
1044 	printf("file\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64
1045 			"\t%s\t%s\t%" PRIu64 "\n",
1046 			lzma_index_stream_count(xfi->idx),
1047 			lzma_index_block_count(xfi->idx),
1048 			lzma_index_file_size(xfi->idx),
1049 			lzma_index_uncompressed_size(xfi->idx),
1050 			get_ratio(lzma_index_file_size(xfi->idx),
1051 				lzma_index_uncompressed_size(xfi->idx)),
1052 			checks,
1053 			xfi->stream_padding);
1054 
1055 	if (message_verbosity_get() >= V_VERBOSE) {
1056 		lzma_index_iter iter;
1057 		lzma_index_iter_init(&iter, xfi->idx);
1058 
1059 		while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM))
1060 			printf("stream\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64
1061 				"\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64
1062 				"\t%s\t%s\t%" PRIu64 "\n",
1063 				iter.stream.number,
1064 				iter.stream.block_count,
1065 				iter.stream.compressed_offset,
1066 				iter.stream.uncompressed_offset,
1067 				iter.stream.compressed_size,
1068 				iter.stream.uncompressed_size,
1069 				get_ratio(iter.stream.compressed_size,
1070 					iter.stream.uncompressed_size),
1071 				check_names[iter.stream.flags->check],
1072 				iter.stream.padding);
1073 
1074 		lzma_index_iter_rewind(&iter);
1075 
1076 		while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)) {
1077 			block_header_info bhi = BLOCK_HEADER_INFO_INIT;
1078 			if (message_verbosity_get() >= V_DEBUG
1079 					&& parse_details(
1080 						pair, &iter, &bhi, xfi))
1081 				return true;
1082 
1083 			printf("block\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64
1084 					"\t%" PRIu64 "\t%" PRIu64
1085 					"\t%" PRIu64 "\t%" PRIu64 "\t%s\t%s",
1086 					iter.stream.number,
1087 					iter.block.number_in_stream,
1088 					iter.block.number_in_file,
1089 					iter.block.compressed_file_offset,
1090 					iter.block.uncompressed_file_offset,
1091 					iter.block.total_size,
1092 					iter.block.uncompressed_size,
1093 					get_ratio(iter.block.total_size,
1094 						iter.block.uncompressed_size),
1095 					check_names[iter.stream.flags->check]);
1096 
1097 			if (message_verbosity_get() >= V_DEBUG)
1098 				printf("\t%s\t%" PRIu32 "\t%s\t%" PRIu64
1099 						"\t%" PRIu64 "\t%s",
1100 						check_value,
1101 						bhi.header_size,
1102 						bhi.flags,
1103 						bhi.compressed_size,
1104 						bhi.memusage,
1105 						bhi.filter_chain);
1106 
1107 			putchar('\n');
1108 			block_header_info_end(&bhi);
1109 		}
1110 	}
1111 
1112 	if (message_verbosity_get() >= V_DEBUG)
1113 		printf("summary\t%" PRIu64 "\t%s\t%" PRIu32 "\n",
1114 				xfi->memusage_max,
1115 				xfi->all_have_sizes ? "yes" : "no",
1116 				xfi->min_version);
1117 
1118 	return false;
1119 }
1120 
1121 
1122 static void
1123 update_totals(const xz_file_info *xfi)
1124 {
1125 	// TODO: Integer overflow checks
1126 	++totals.files;
1127 	totals.streams += lzma_index_stream_count(xfi->idx);
1128 	totals.blocks += lzma_index_block_count(xfi->idx);
1129 	totals.compressed_size += lzma_index_file_size(xfi->idx);
1130 	totals.uncompressed_size += lzma_index_uncompressed_size(xfi->idx);
1131 	totals.stream_padding += xfi->stream_padding;
1132 	totals.checks |= lzma_index_checks(xfi->idx);
1133 
1134 	if (totals.memusage_max < xfi->memusage_max)
1135 		totals.memusage_max = xfi->memusage_max;
1136 
1137 	if (totals.min_version < xfi->min_version)
1138 		totals.min_version = xfi->min_version;
1139 
1140 	totals.all_have_sizes &= xfi->all_have_sizes;
1141 
1142 	return;
1143 }
1144 
1145 
1146 static void
1147 print_totals_basic(void)
1148 {
1149 	// Print a separator line.
1150 	char line[80];
1151 	memset(line, '-', sizeof(line));
1152 	line[sizeof(line) - 1] = '\0';
1153 	puts(line);
1154 
1155 	// Get the check names.
1156 	char checks[CHECKS_STR_SIZE];
1157 	get_check_names(checks, totals.checks, false);
1158 
1159 	// Print the totals except the file count, which needs
1160 	// special handling.
1161 	printf("%5s %7s  %11s  %11s  %5s  %-7s ",
1162 			uint64_to_str(totals.streams, 0),
1163 			uint64_to_str(totals.blocks, 1),
1164 			uint64_to_nicestr(totals.compressed_size,
1165 				NICESTR_B, NICESTR_TIB, false, 2),
1166 			uint64_to_nicestr(totals.uncompressed_size,
1167 				NICESTR_B, NICESTR_TIB, false, 3),
1168 			get_ratio(totals.compressed_size,
1169 				totals.uncompressed_size),
1170 			checks);
1171 
1172 	// Since we print totals only when there are at least two files,
1173 	// the English message will always use "%s files". But some other
1174 	// languages need different forms for different plurals so we
1175 	// have to translate this with ngettext().
1176 	//
1177 	// TRANSLATORS: %s is an integer. Only the plural form of this
1178 	// message is used (e.g. "2 files"). Test with "xz -l foo.xz bar.xz".
1179 	printf(ngettext("%s file\n", "%s files\n",
1180 			totals.files <= ULONG_MAX ? totals.files
1181 				: (totals.files % 1000000) + 1000000),
1182 			uint64_to_str(totals.files, 0));
1183 
1184 	return;
1185 }
1186 
1187 
1188 static void
1189 print_totals_adv(void)
1190 {
1191 	putchar('\n');
1192 	puts(_("Totals:"));
1193 	printf("  %-*s %s\n", COLON_STR(COLON_STR_NUMBER_OF_FILES),
1194 			uint64_to_str(totals.files, 0));
1195 	print_adv_helper(totals.streams, totals.blocks,
1196 			totals.compressed_size, totals.uncompressed_size,
1197 			totals.checks, totals.stream_padding);
1198 
1199 	if (message_verbosity_get() >= V_DEBUG) {
1200 		printf("  %-*s %s MiB\n", COLON_STR(COLON_STR_MEMORY_NEEDED),
1201 				uint64_to_str(
1202 				round_up_to_mib(totals.memusage_max), 0));
1203 		printf("  %-*s %s\n", COLON_STR(COLON_STR_SIZES_IN_HEADERS),
1204 				totals.all_have_sizes ? _("Yes") : _("No"));
1205 		//printf("  %-*s %s\n", COLON_STR(COLON_STR_MINIMUM_XZ_VERSION),
1206 		printf(_("  Minimum XZ Utils version: %s\n"),
1207 				xz_ver_to_str(totals.min_version));
1208 	}
1209 
1210 	return;
1211 }
1212 
1213 
1214 static void
1215 print_totals_robot(void)
1216 {
1217 	char checks[CHECKS_STR_SIZE];
1218 	get_check_names(checks, totals.checks, false);
1219 
1220 	printf("totals\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64
1221 			"\t%s\t%s\t%" PRIu64 "\t%" PRIu64,
1222 			totals.streams,
1223 			totals.blocks,
1224 			totals.compressed_size,
1225 			totals.uncompressed_size,
1226 			get_ratio(totals.compressed_size,
1227 				totals.uncompressed_size),
1228 			checks,
1229 			totals.stream_padding,
1230 			totals.files);
1231 
1232 	if (message_verbosity_get() >= V_DEBUG)
1233 		printf("\t%" PRIu64 "\t%s\t%" PRIu32,
1234 				totals.memusage_max,
1235 				totals.all_have_sizes ? "yes" : "no",
1236 				totals.min_version);
1237 
1238 	putchar('\n');
1239 
1240 	return;
1241 }
1242 
1243 
1244 extern void
1245 list_totals(void)
1246 {
1247 	if (opt_robot) {
1248 		// Always print totals in --robot mode. It can be convenient
1249 		// in some cases and doesn't complicate usage of the
1250 		// single-file case much.
1251 		print_totals_robot();
1252 
1253 	} else if (totals.files > 1) {
1254 		// For non-robot mode, totals are printed only if there
1255 		// is more than one file.
1256 		if (message_verbosity_get() <= V_WARNING)
1257 			print_totals_basic();
1258 		else
1259 			print_totals_adv();
1260 	}
1261 
1262 	return;
1263 }
1264 
1265 
1266 extern void
1267 list_file(const char *filename)
1268 {
1269 	if (opt_format != FORMAT_XZ && opt_format != FORMAT_AUTO)
1270 		message_fatal(_("--list works only on .xz files "
1271 				"(--format=xz or --format=auto)"));
1272 
1273 	message_filename(filename);
1274 
1275 	if (filename == stdin_filename) {
1276 		message_error(_("--list does not support reading from "
1277 				"standard input"));
1278 		return;
1279 	}
1280 
1281 	init_field_widths();
1282 
1283 	// Unset opt_stdout so that io_open_src() won't accept special files.
1284 	// Set opt_force so that io_open_src() will follow symlinks.
1285 	opt_stdout = false;
1286 	opt_force = true;
1287 	file_pair *pair = io_open_src(filename);
1288 	if (pair == NULL)
1289 		return;
1290 
1291 	xz_file_info xfi = XZ_FILE_INFO_INIT;
1292 	if (!parse_indexes(&xfi, pair)) {
1293 		bool fail;
1294 
1295 		// We have three main modes:
1296 		//  - --robot, which has submodes if --verbose is specified
1297 		//    once or twice
1298 		//  - Normal --list without --verbose
1299 		//  - --list with one or two --verbose
1300 		if (opt_robot)
1301 			fail = print_info_robot(&xfi, pair);
1302 		else if (message_verbosity_get() <= V_WARNING)
1303 			fail = print_info_basic(&xfi, pair);
1304 		else
1305 			fail = print_info_adv(&xfi, pair);
1306 
1307 		// Update the totals that are displayed after all
1308 		// the individual files have been listed. Don't count
1309 		// broken files.
1310 		if (!fail)
1311 			update_totals(&xfi);
1312 
1313 		lzma_index_end(xfi.idx, NULL);
1314 	}
1315 
1316 	io_close(pair, false);
1317 	return;
1318 }
1319