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 ///
659 /// \return False on success, true on error. If an error occurs,
660 /// the error message is printed too so the caller doesn't
661 /// need to worry about that.
662 static bool
parse_details(file_pair * pair,const lzma_index_iter * iter,block_header_info * bhi,xz_file_info * xfi)663 parse_details(file_pair *pair, const lzma_index_iter *iter,
664 block_header_info *bhi, xz_file_info *xfi)
665 {
666 if (parse_block_header(pair, iter, bhi, xfi))
667 return true;
668
669 if (parse_check_value(pair, iter))
670 return true;
671
672 return false;
673 }
674
675
676 /// \brief Get the compression ratio
677 ///
678 /// This has slightly different format than that is used in message.c.
679 static const char *
get_ratio(uint64_t compressed_size,uint64_t uncompressed_size)680 get_ratio(uint64_t compressed_size, uint64_t uncompressed_size)
681 {
682 if (uncompressed_size == 0)
683 return "---";
684
685 const double ratio = (double)(compressed_size)
686 / (double)(uncompressed_size);
687 if (ratio > 9.999)
688 return "---";
689
690 static char buf[16];
691 snprintf(buf, sizeof(buf), "%.3f", ratio);
692 return buf;
693 }
694
695
696 /// \brief Get a comma-separated list of Check names
697 ///
698 /// The check names are translated with gettext except when in robot mode.
699 ///
700 /// \param buf Buffer to hold the resulting string
701 /// \param checks Bit mask of Checks to print
702 /// \param space_after_comma
703 /// It's better to not use spaces in table-like listings,
704 /// but in more verbose formats a space after a comma
705 /// is good for readability.
706 static void
get_check_names(char buf[CHECKS_STR_SIZE],uint32_t checks,bool space_after_comma)707 get_check_names(char buf[CHECKS_STR_SIZE],
708 uint32_t checks, bool space_after_comma)
709 {
710 // If we get called when there are no Checks to print, set checks
711 // to 1 so that we print "None". This can happen in the robot mode
712 // when printing the totals line if there are no valid input files.
713 if (checks == 0)
714 checks = 1;
715
716 char *pos = buf;
717 size_t left = CHECKS_STR_SIZE;
718
719 const char *sep = space_after_comma ? ", " : ",";
720 bool comma = false;
721
722 for (size_t i = 0; i <= LZMA_CHECK_ID_MAX; ++i) {
723 if (checks & (UINT32_C(1) << i)) {
724 my_snprintf(&pos, &left, "%s%s",
725 comma ? sep : "",
726 opt_robot ? check_names[i]
727 : _(check_names[i]));
728 comma = true;
729 }
730 }
731
732 return;
733 }
734
735
736 static bool
print_info_basic(const xz_file_info * xfi,file_pair * pair)737 print_info_basic(const xz_file_info *xfi, file_pair *pair)
738 {
739 static bool headings_displayed = false;
740 if (!headings_displayed) {
741 headings_displayed = true;
742 // TRANSLATORS: These are column headings. From Strms (Streams)
743 // to Ratio, the columns are right aligned. Check and Filename
744 // are left aligned. If you need longer words, it's OK to
745 // use two lines here. Test with "xz -l foo.xz".
746 puts(_("Strms Blocks Compressed Uncompressed Ratio "
747 "Check Filename"));
748 }
749
750 char checks[CHECKS_STR_SIZE];
751 get_check_names(checks, lzma_index_checks(xfi->idx), false);
752
753 const char *cols[6] = {
754 uint64_to_str(lzma_index_stream_count(xfi->idx), 0),
755 uint64_to_str(lzma_index_block_count(xfi->idx), 1),
756 uint64_to_nicestr(lzma_index_file_size(xfi->idx),
757 NICESTR_B, NICESTR_TIB, false, 2),
758 uint64_to_nicestr(lzma_index_uncompressed_size(xfi->idx),
759 NICESTR_B, NICESTR_TIB, false, 3),
760 get_ratio(lzma_index_file_size(xfi->idx),
761 lzma_index_uncompressed_size(xfi->idx)),
762 checks,
763 };
764 printf("%*s %*s %*s %*s %*s %-*s %s\n",
765 tuklib_mbstr_fw(cols[0], 5), cols[0],
766 tuklib_mbstr_fw(cols[1], 7), cols[1],
767 tuklib_mbstr_fw(cols[2], 11), cols[2],
768 tuklib_mbstr_fw(cols[3], 11), cols[3],
769 tuklib_mbstr_fw(cols[4], 5), cols[4],
770 tuklib_mbstr_fw(cols[5], 7), cols[5],
771 tuklib_mask_nonprint(pair->src_name));
772
773 return false;
774 }
775
776
777 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)778 print_adv_helper(uint64_t stream_count, uint64_t block_count,
779 uint64_t compressed_size, uint64_t uncompressed_size,
780 uint32_t checks, uint64_t stream_padding)
781 {
782 char checks_str[CHECKS_STR_SIZE];
783 get_check_names(checks_str, checks, true);
784
785 printf(" %-*s %s\n", COLON_STR(COLON_STR_STREAMS),
786 uint64_to_str(stream_count, 0));
787 printf(" %-*s %s\n", COLON_STR(COLON_STR_BLOCKS),
788 uint64_to_str(block_count, 0));
789 printf(" %-*s %s\n", COLON_STR(COLON_STR_COMPRESSED_SIZE),
790 uint64_to_nicestr(compressed_size,
791 NICESTR_B, NICESTR_TIB, true, 0));
792 printf(" %-*s %s\n", COLON_STR(COLON_STR_UNCOMPRESSED_SIZE),
793 uint64_to_nicestr(uncompressed_size,
794 NICESTR_B, NICESTR_TIB, true, 0));
795 printf(" %-*s %s\n", COLON_STR(COLON_STR_RATIO),
796 get_ratio(compressed_size, uncompressed_size));
797 printf(" %-*s %s\n", COLON_STR(COLON_STR_CHECK), checks_str);
798 printf(" %-*s %s\n", COLON_STR(COLON_STR_STREAM_PADDING),
799 uint64_to_nicestr(stream_padding,
800 NICESTR_B, NICESTR_TIB, true, 0));
801 return;
802 }
803
804
805 static bool
print_info_adv(xz_file_info * xfi,file_pair * pair)806 print_info_adv(xz_file_info *xfi, file_pair *pair)
807 {
808 // Print the overall information.
809 print_adv_helper(lzma_index_stream_count(xfi->idx),
810 lzma_index_block_count(xfi->idx),
811 lzma_index_file_size(xfi->idx),
812 lzma_index_uncompressed_size(xfi->idx),
813 lzma_index_checks(xfi->idx),
814 xfi->stream_padding);
815
816 // Size of the biggest Check. This is used to calculate the width
817 // of the CheckVal field. The table would get insanely wide if
818 // we always reserved space for 64-byte Check (128 chars as hex).
819 uint32_t check_max = 0;
820
821 // Print information about the Streams.
822 //
823 // All except Check are right aligned; Check is left aligned.
824 // Test with "xz -lv foo.xz".
825 printf(" %s\n %*s %*s %*s %*s %*s %*s %*s %-*s %*s\n",
826 _(colon_strs[COLON_STR_STREAMS]),
827 HEADING_STR(HEADING_STREAM),
828 HEADING_STR(HEADING_BLOCKS),
829 HEADING_STR(HEADING_COMPOFFSET),
830 HEADING_STR(HEADING_UNCOMPOFFSET),
831 HEADING_STR(HEADING_COMPSIZE),
832 HEADING_STR(HEADING_UNCOMPSIZE),
833 HEADING_STR(HEADING_RATIO),
834 HEADING_STR(HEADING_CHECK),
835 HEADING_STR(HEADING_PADDING));
836
837 lzma_index_iter iter;
838 lzma_index_iter_init(&iter, xfi->idx);
839
840 while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)) {
841 const char *cols1[4] = {
842 uint64_to_str(iter.stream.number, 0),
843 uint64_to_str(iter.stream.block_count, 1),
844 uint64_to_str(iter.stream.compressed_offset, 2),
845 uint64_to_str(iter.stream.uncompressed_offset, 3),
846 };
847 printf(" %*s %*s %*s %*s ",
848 tuklib_mbstr_fw(cols1[0],
849 headings[HEADING_STREAM].columns),
850 cols1[0],
851 tuklib_mbstr_fw(cols1[1],
852 headings[HEADING_BLOCKS].columns),
853 cols1[1],
854 tuklib_mbstr_fw(cols1[2],
855 headings[HEADING_COMPOFFSET].columns),
856 cols1[2],
857 tuklib_mbstr_fw(cols1[3],
858 headings[HEADING_UNCOMPOFFSET].columns),
859 cols1[3]);
860
861 const char *cols2[5] = {
862 uint64_to_str(iter.stream.compressed_size, 0),
863 uint64_to_str(iter.stream.uncompressed_size, 1),
864 get_ratio(iter.stream.compressed_size,
865 iter.stream.uncompressed_size),
866 _(check_names[iter.stream.flags->check]),
867 uint64_to_str(iter.stream.padding, 2),
868 };
869 printf("%*s %*s %*s %-*s %*s\n",
870 tuklib_mbstr_fw(cols2[0],
871 headings[HEADING_COMPSIZE].columns),
872 cols2[0],
873 tuklib_mbstr_fw(cols2[1],
874 headings[HEADING_UNCOMPSIZE].columns),
875 cols2[1],
876 tuklib_mbstr_fw(cols2[2],
877 headings[HEADING_RATIO].columns),
878 cols2[2],
879 tuklib_mbstr_fw(cols2[3],
880 headings[HEADING_CHECK].columns),
881 cols2[3],
882 tuklib_mbstr_fw(cols2[4],
883 headings[HEADING_PADDING].columns),
884 cols2[4]);
885
886 // Update the maximum Check size.
887 if (lzma_check_size(iter.stream.flags->check) > check_max)
888 check_max = lzma_check_size(iter.stream.flags->check);
889 }
890
891 // Cache the verbosity level to a local variable.
892 const bool detailed = message_verbosity_get() >= V_DEBUG;
893
894 // Print information about the Blocks but only if there is
895 // at least one Block.
896 if (lzma_index_block_count(xfi->idx) > 0) {
897 // Calculate the width of the CheckVal column. This can be
898 // used as is as the field width for printf() when printing
899 // the actual check value as it is hexadecimal. However, to
900 // print the column heading, further calculation is needed
901 // to handle a translated string (it's done a few lines later).
902 assert(check_max <= LZMA_CHECK_SIZE_MAX);
903 const int checkval_width = my_max(
904 headings[HEADING_CHECKVAL].columns,
905 (int)(2 * check_max));
906
907 // All except Check are right aligned; Check is left aligned.
908 printf(" %s\n %*s %*s %*s %*s %*s %*s %*s %-*s",
909 _(colon_strs[COLON_STR_BLOCKS]),
910 HEADING_STR(HEADING_STREAM),
911 HEADING_STR(HEADING_BLOCK),
912 HEADING_STR(HEADING_COMPOFFSET),
913 HEADING_STR(HEADING_UNCOMPOFFSET),
914 HEADING_STR(HEADING_TOTALSIZE),
915 HEADING_STR(HEADING_UNCOMPSIZE),
916 HEADING_STR(HEADING_RATIO),
917 detailed ? headings[HEADING_CHECK].fw : 1,
918 _(headings[HEADING_CHECK].str));
919
920 if (detailed) {
921 // CheckVal (Check value), Flags, and Filters are
922 // left aligned. Block Header Size, CompSize, and
923 // MemUsage are right aligned. Test with
924 // "xz -lvv foo.xz".
925 printf(" %-*s %*s %-*s %*s %*s %s",
926 headings[HEADING_CHECKVAL].fw
927 + checkval_width
928 - headings[HEADING_CHECKVAL].columns,
929 _(headings[HEADING_CHECKVAL].str),
930 HEADING_STR(HEADING_HEADERSIZE),
931 HEADING_STR(HEADING_HEADERFLAGS),
932 HEADING_STR(HEADING_COMPSIZE),
933 HEADING_STR(HEADING_MEMUSAGE),
934 _(headings[HEADING_FILTERS].str));
935 }
936
937 putchar('\n');
938
939 lzma_index_iter_init(&iter, xfi->idx);
940
941 // Iterate over the Blocks.
942 while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)) {
943 // If in detailed mode, collect the information from
944 // Block Header before starting to print the next line.
945 block_header_info bhi = BLOCK_HEADER_INFO_INIT;
946 if (detailed && parse_details(pair, &iter, &bhi, xfi))
947 return true;
948
949 const char *cols1[4] = {
950 uint64_to_str(iter.stream.number, 0),
951 uint64_to_str(
952 iter.block.number_in_stream, 1),
953 uint64_to_str(
954 iter.block.compressed_file_offset, 2),
955 uint64_to_str(
956 iter.block.uncompressed_file_offset, 3)
957 };
958 printf(" %*s %*s %*s %*s ",
959 tuklib_mbstr_fw(cols1[0],
960 headings[HEADING_STREAM].columns),
961 cols1[0],
962 tuklib_mbstr_fw(cols1[1],
963 headings[HEADING_BLOCK].columns),
964 cols1[1],
965 tuklib_mbstr_fw(cols1[2],
966 headings[HEADING_COMPOFFSET].columns),
967 cols1[2],
968 tuklib_mbstr_fw(cols1[3], headings[
969 HEADING_UNCOMPOFFSET].columns),
970 cols1[3]);
971
972 const char *cols2[4] = {
973 uint64_to_str(iter.block.total_size, 0),
974 uint64_to_str(iter.block.uncompressed_size,
975 1),
976 get_ratio(iter.block.total_size,
977 iter.block.uncompressed_size),
978 _(check_names[iter.stream.flags->check])
979 };
980 printf("%*s %*s %*s %-*s",
981 tuklib_mbstr_fw(cols2[0],
982 headings[HEADING_TOTALSIZE].columns),
983 cols2[0],
984 tuklib_mbstr_fw(cols2[1],
985 headings[HEADING_UNCOMPSIZE].columns),
986 cols2[1],
987 tuklib_mbstr_fw(cols2[2],
988 headings[HEADING_RATIO].columns),
989 cols2[2],
990 tuklib_mbstr_fw(cols2[3], detailed
991 ? headings[HEADING_CHECK].columns : 1),
992 cols2[3]);
993
994 if (detailed) {
995 const lzma_vli compressed_size
996 = iter.block.unpadded_size
997 - bhi.header_size
998 - lzma_check_size(
999 iter.stream.flags->check);
1000
1001 const char *cols3[6] = {
1002 check_value,
1003 uint64_to_str(bhi.header_size, 0),
1004 bhi.flags,
1005 uint64_to_str(compressed_size, 1),
1006 uint64_to_str(
1007 round_up_to_mib(bhi.memusage),
1008 2),
1009 bhi.filter_chain
1010 };
1011 // Show MiB for memory usage, because it
1012 // is the only size which is not in bytes.
1013 printf(" %-*s %*s %-*s %*s %*s MiB %s",
1014 checkval_width, cols3[0],
1015 tuklib_mbstr_fw(cols3[1], headings[
1016 HEADING_HEADERSIZE].columns),
1017 cols3[1],
1018 tuklib_mbstr_fw(cols3[2], headings[
1019 HEADING_HEADERFLAGS].columns),
1020 cols3[2],
1021 tuklib_mbstr_fw(cols3[3], headings[
1022 HEADING_COMPSIZE].columns),
1023 cols3[3],
1024 tuklib_mbstr_fw(cols3[4], headings[
1025 HEADING_MEMUSAGE].columns - 4),
1026 cols3[4],
1027 cols3[5]);
1028 }
1029
1030 putchar('\n');
1031 block_header_info_end(&bhi);
1032 }
1033 }
1034
1035 if (detailed) {
1036 printf(" %-*s %s MiB\n", COLON_STR(COLON_STR_MEMORY_NEEDED),
1037 uint64_to_str(
1038 round_up_to_mib(xfi->memusage_max), 0));
1039 printf(" %-*s %s\n", COLON_STR(COLON_STR_SIZES_IN_HEADERS),
1040 xfi->all_have_sizes ? _("Yes") : _("No"));
1041 //printf(" %-*s %s\n", COLON_STR(COLON_STR_MINIMUM_XZ_VERSION),
1042 printf(" %s %s\n", _("Minimum XZ Utils version:"),
1043 xz_ver_to_str(xfi->min_version));
1044 }
1045
1046 return false;
1047 }
1048
1049
1050 static bool
print_info_robot(xz_file_info * xfi,file_pair * pair)1051 print_info_robot(xz_file_info *xfi, file_pair *pair)
1052 {
1053 char checks[CHECKS_STR_SIZE];
1054 get_check_names(checks, lzma_index_checks(xfi->idx), false);
1055
1056 // Robot mode has to mask at least some control chars to prevent
1057 // the output from getting out of sync if filename is malicious.
1058 // Masking all non-printable chars is more than we need but
1059 // perhaps this is good enough in practice.
1060 printf("name\t%s\n", tuklib_mask_nonprint(pair->src_name));
1061
1062 printf("file\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64
1063 "\t%s\t%s\t%" PRIu64 "\n",
1064 lzma_index_stream_count(xfi->idx),
1065 lzma_index_block_count(xfi->idx),
1066 lzma_index_file_size(xfi->idx),
1067 lzma_index_uncompressed_size(xfi->idx),
1068 get_ratio(lzma_index_file_size(xfi->idx),
1069 lzma_index_uncompressed_size(xfi->idx)),
1070 checks,
1071 xfi->stream_padding);
1072
1073 if (message_verbosity_get() >= V_VERBOSE) {
1074 lzma_index_iter iter;
1075 lzma_index_iter_init(&iter, xfi->idx);
1076
1077 while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM))
1078 printf("stream\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64
1079 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64
1080 "\t%s\t%s\t%" PRIu64 "\n",
1081 iter.stream.number,
1082 iter.stream.block_count,
1083 iter.stream.compressed_offset,
1084 iter.stream.uncompressed_offset,
1085 iter.stream.compressed_size,
1086 iter.stream.uncompressed_size,
1087 get_ratio(iter.stream.compressed_size,
1088 iter.stream.uncompressed_size),
1089 check_names[iter.stream.flags->check],
1090 iter.stream.padding);
1091
1092 lzma_index_iter_rewind(&iter);
1093
1094 while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)) {
1095 block_header_info bhi = BLOCK_HEADER_INFO_INIT;
1096 if (message_verbosity_get() >= V_DEBUG
1097 && parse_details(
1098 pair, &iter, &bhi, xfi))
1099 return true;
1100
1101 printf("block\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64
1102 "\t%" PRIu64 "\t%" PRIu64
1103 "\t%" PRIu64 "\t%" PRIu64 "\t%s\t%s",
1104 iter.stream.number,
1105 iter.block.number_in_stream,
1106 iter.block.number_in_file,
1107 iter.block.compressed_file_offset,
1108 iter.block.uncompressed_file_offset,
1109 iter.block.total_size,
1110 iter.block.uncompressed_size,
1111 get_ratio(iter.block.total_size,
1112 iter.block.uncompressed_size),
1113 check_names[iter.stream.flags->check]);
1114
1115 if (message_verbosity_get() >= V_DEBUG)
1116 printf("\t%s\t%" PRIu32 "\t%s\t%" PRIu64
1117 "\t%" PRIu64 "\t%s",
1118 check_value,
1119 bhi.header_size,
1120 bhi.flags,
1121 bhi.compressed_size,
1122 bhi.memusage,
1123 bhi.filter_chain);
1124
1125 putchar('\n');
1126 block_header_info_end(&bhi);
1127 }
1128 }
1129
1130 if (message_verbosity_get() >= V_DEBUG)
1131 printf("summary\t%" PRIu64 "\t%s\t%" PRIu32 "\n",
1132 xfi->memusage_max,
1133 xfi->all_have_sizes ? "yes" : "no",
1134 xfi->min_version);
1135
1136 return false;
1137 }
1138
1139
1140 static void
update_totals(const xz_file_info * xfi)1141 update_totals(const xz_file_info *xfi)
1142 {
1143 // TODO: Integer overflow checks
1144 ++totals.files;
1145 totals.streams += lzma_index_stream_count(xfi->idx);
1146 totals.blocks += lzma_index_block_count(xfi->idx);
1147 totals.compressed_size += lzma_index_file_size(xfi->idx);
1148 totals.uncompressed_size += lzma_index_uncompressed_size(xfi->idx);
1149 totals.stream_padding += xfi->stream_padding;
1150 totals.checks |= lzma_index_checks(xfi->idx);
1151
1152 if (totals.memusage_max < xfi->memusage_max)
1153 totals.memusage_max = xfi->memusage_max;
1154
1155 if (totals.min_version < xfi->min_version)
1156 totals.min_version = xfi->min_version;
1157
1158 totals.all_have_sizes &= xfi->all_have_sizes;
1159
1160 return;
1161 }
1162
1163
1164 static void
print_totals_basic(void)1165 print_totals_basic(void)
1166 {
1167 // Print a separator line.
1168 char line[80];
1169 memset(line, '-', sizeof(line));
1170 line[sizeof(line) - 1] = '\0';
1171 puts(line);
1172
1173 // Get the check names.
1174 char checks[CHECKS_STR_SIZE];
1175 get_check_names(checks, totals.checks, false);
1176
1177 // Print the totals except the file count, which needs
1178 // special handling.
1179 printf("%5s %7s %11s %11s %5s %-7s ",
1180 uint64_to_str(totals.streams, 0),
1181 uint64_to_str(totals.blocks, 1),
1182 uint64_to_nicestr(totals.compressed_size,
1183 NICESTR_B, NICESTR_TIB, false, 2),
1184 uint64_to_nicestr(totals.uncompressed_size,
1185 NICESTR_B, NICESTR_TIB, false, 3),
1186 get_ratio(totals.compressed_size,
1187 totals.uncompressed_size),
1188 checks);
1189
1190 #if defined(__sun) && (defined(__GNUC__) || defined(__clang__))
1191 # pragma GCC diagnostic push
1192 # pragma GCC diagnostic ignored "-Wformat-nonliteral"
1193 #endif
1194 // Since we print totals only when there are at least two files,
1195 // the English message will always use "%s files". But some other
1196 // languages need different forms for different plurals so we
1197 // have to translate this with ngettext().
1198 //
1199 // TRANSLATORS: %s is an integer. Only the plural form of this
1200 // message is used (e.g. "2 files"). Test with "xz -l foo.xz bar.xz".
1201 printf(ngettext("%s file\n", "%s files\n",
1202 totals.files <= ULONG_MAX ? totals.files
1203 : (totals.files % 1000000) + 1000000),
1204 uint64_to_str(totals.files, 0));
1205 #if defined(__sun) && (defined(__GNUC__) || defined(__clang__))
1206 # pragma GCC diagnostic pop
1207 #endif
1208
1209 return;
1210 }
1211
1212
1213 static void
print_totals_adv(void)1214 print_totals_adv(void)
1215 {
1216 putchar('\n');
1217 puts(_("Totals:"));
1218 printf(" %-*s %s\n", COLON_STR(COLON_STR_NUMBER_OF_FILES),
1219 uint64_to_str(totals.files, 0));
1220 print_adv_helper(totals.streams, totals.blocks,
1221 totals.compressed_size, totals.uncompressed_size,
1222 totals.checks, totals.stream_padding);
1223
1224 if (message_verbosity_get() >= V_DEBUG) {
1225 printf(" %-*s %s MiB\n", COLON_STR(COLON_STR_MEMORY_NEEDED),
1226 uint64_to_str(
1227 round_up_to_mib(totals.memusage_max), 0));
1228 printf(" %-*s %s\n", COLON_STR(COLON_STR_SIZES_IN_HEADERS),
1229 totals.all_have_sizes ? _("Yes") : _("No"));
1230 //printf(" %-*s %s\n", COLON_STR(COLON_STR_MINIMUM_XZ_VERSION),
1231 printf(" %s %s\n", _("Minimum XZ Utils version:"),
1232 xz_ver_to_str(totals.min_version));
1233 }
1234
1235 return;
1236 }
1237
1238
1239 static void
print_totals_robot(void)1240 print_totals_robot(void)
1241 {
1242 char checks[CHECKS_STR_SIZE];
1243 get_check_names(checks, totals.checks, false);
1244
1245 printf("totals\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64
1246 "\t%s\t%s\t%" PRIu64 "\t%" PRIu64,
1247 totals.streams,
1248 totals.blocks,
1249 totals.compressed_size,
1250 totals.uncompressed_size,
1251 get_ratio(totals.compressed_size,
1252 totals.uncompressed_size),
1253 checks,
1254 totals.stream_padding,
1255 totals.files);
1256
1257 if (message_verbosity_get() >= V_DEBUG)
1258 printf("\t%" PRIu64 "\t%s\t%" PRIu32,
1259 totals.memusage_max,
1260 totals.all_have_sizes ? "yes" : "no",
1261 totals.min_version);
1262
1263 putchar('\n');
1264
1265 return;
1266 }
1267
1268
1269 extern void
list_totals(void)1270 list_totals(void)
1271 {
1272 if (opt_robot) {
1273 // Always print totals in --robot mode. It can be convenient
1274 // in some cases and doesn't complicate usage of the
1275 // single-file case much.
1276 print_totals_robot();
1277
1278 } else if (totals.files > 1) {
1279 // For non-robot mode, totals are printed only if there
1280 // is more than one file.
1281 if (message_verbosity_get() <= V_WARNING)
1282 print_totals_basic();
1283 else
1284 print_totals_adv();
1285 }
1286
1287 return;
1288 }
1289
1290
1291 extern void
list_file(const char * filename)1292 list_file(const char *filename)
1293 {
1294 if (opt_format != FORMAT_XZ && opt_format != FORMAT_AUTO) {
1295 // The 'lzmainfo' message is printed only when --format=lzma
1296 // is used (it is implied if using "lzma" as the command
1297 // name). Thus instead of using message_fatal(), print
1298 // the messages separately and then call tuklib_exit()
1299 // like message_fatal() does.
1300 message(V_ERROR, _("--list works only on .xz files "
1301 "(--format=xz or --format=auto)"));
1302
1303 if (opt_format == FORMAT_LZMA)
1304 message(V_ERROR,
1305 _("Try 'lzmainfo' with .lzma files."));
1306
1307 tuklib_exit(E_ERROR, E_ERROR, false);
1308 }
1309
1310 message_filename(filename);
1311
1312 if (filename == stdin_filename) {
1313 message_error(_("--list does not support reading from "
1314 "standard input"));
1315 return;
1316 }
1317
1318 init_field_widths();
1319
1320 // Unset opt_stdout so that io_open_src() won't accept special files.
1321 // Set opt_force so that io_open_src() will follow symlinks.
1322 opt_stdout = false;
1323 opt_force = true;
1324 file_pair *pair = io_open_src(filename);
1325 if (pair == NULL)
1326 return;
1327
1328 xz_file_info xfi = XZ_FILE_INFO_INIT;
1329 if (!parse_indexes(&xfi, pair)) {
1330 bool fail;
1331
1332 // We have three main modes:
1333 // - --robot, which has submodes if --verbose is specified
1334 // once or twice
1335 // - Normal --list without --verbose
1336 // - --list with one or two --verbose
1337 if (opt_robot)
1338 fail = print_info_robot(&xfi, pair);
1339 else if (message_verbosity_get() <= V_WARNING)
1340 fail = print_info_basic(&xfi, pair);
1341 else
1342 fail = print_info_adv(&xfi, pair);
1343
1344 // Update the totals that are displayed after all
1345 // the individual files have been listed. Don't count
1346 // broken files.
1347 if (!fail)
1348 update_totals(&xfi);
1349
1350 lzma_index_end(xfi.idx, NULL);
1351 }
1352
1353 io_close(pair, false);
1354 return;
1355 }
1356