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