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 }