From ce6dc3c0a891f23a862f80ec08d3b6f0beb2a562 Mon Sep 17 00:00:00 2001 From: Lasse Collin Date: Tue, 1 Jun 2010 15:51:44 +0300 Subject: [PATCH] Major update to xz --list. --- src/xz/list.c | 644 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 467 insertions(+), 177 deletions(-) diff --git a/src/xz/list.c b/src/xz/list.c index 3de838cd..dda7c9bd 100644 --- a/src/xz/list.c +++ b/src/xz/list.c @@ -14,6 +14,72 @@ #include "tuklib_integer.h" +/// Information about a .xz file +typedef struct { + /// Combined Index of all Streams in the file + lzma_index *idx; + + /// Total amount of Stream Padding + uint64_t stream_padding; + + /// Highest memory usage so far + uint64_t memusage_max; + + /// True if all Blocks so far have Compressed Size and + /// Uncompressed Size fields + bool all_have_sizes; + +} xz_file_info; + +#define XZ_FILE_INFO_INIT { NULL, 0, 0, true } + + +/// Information about a .xz Block +typedef struct { + /// Size of the Block Header + uint32_t header_size; + + /// A few of the Block Flags as a string + char flags[3]; + + /// Size of the Compressed Data field in the Block + lzma_vli compressed_size; + + /// Decoder memory usage for this Block + uint64_t memusage; + + /// The filter chain of this Block in human-readable form + const char *filter_chain; + +} block_header_info; + + +/// Check ID to string mapping +static const char check_names[LZMA_CHECK_ID_MAX + 1][12] = { + "None", + "CRC32", + "Unknown-2", + "Unknown-3", + "CRC64", + "Unknown-5", + "Unknown-6", + "Unknown-7", + "Unknown-8", + "Unknown-9", + "SHA-256", + "Unknown-11", + "Unknown-12", + "Unknown-13", + "Unknown-14", + "Unknown-15", +}; + + +/// Value of the Check field as hexadecimal string. +/// This is set by parse_check_value(). +static char check_value[2 * LZMA_CHECK_SIZE_MAX + 1]; + + /// Totals that are displayed if there was more than one file. /// The "files" counter is also used in print_info_adv() to show /// the file number. @@ -23,15 +89,17 @@ static struct { uint64_t blocks; uint64_t compressed_size; uint64_t uncompressed_size; + uint64_t stream_padding; + uint64_t memusage_max; uint32_t checks; -} totals = { 0, 0, 0, 0, 0, 0 }; + bool all_have_sizes; +} totals = { 0, 0, 0, 0, 0, 0, 0, 0, true }; /// \brief Parse the Index(es) from the given .xz file /// -/// \param idx If decoding is successful, *idx will be set to point -/// to lzma_index containing the decoded information. -/// On error, *idx is not modified. +/// \param xfi Pointer to structure where the decoded information +/// is stored. /// \param pair Input file /// /// \return On success, false is returned. On error, true is returned. @@ -40,7 +108,7 @@ static struct { // takes a callback function to parse the Index(es) from a .xz file to make // it easy for applications. static bool -parse_indexes(lzma_index **idx, file_pair *pair) +parse_indexes(xz_file_info *xfi, file_pair *pair) { if (pair->src_st.st_size <= 0) { message_error(_("%s: File is empty"), pair->src_name); @@ -257,12 +325,14 @@ parse_indexes(lzma_index **idx, file_pair *pair) combined_index = this_index; this_index = NULL; + xfi->stream_padding += stream_padding; + } while (pos > 0); lzma_end(&strm); // All OK. Make combined_index available to the caller. - *idx = combined_index; + xfi->idx = combined_index; return false; error: @@ -274,6 +344,193 @@ error: } +/// \brief Parse the Block Header +/// +/// The result is stored into *bhi. The caller takes care of initializing it. +/// +/// \return False on success, true on error. +static bool +parse_block_header(file_pair *pair, const lzma_index_iter *iter, + block_header_info *bhi, xz_file_info *xfi) +{ +#if IO_BUFFER_SIZE < LZMA_BLOCK_HEADER_SIZE_MAX +# error IO_BUFFER_SIZE < LZMA_BLOCK_HEADER_SIZE_MAX +#endif + + // Get the whole Block Header with one read, but don't read past + // the end of the Block (or even its Check field). + const uint32_t size = my_min(iter->block.total_size + - lzma_check_size(iter->stream.flags->check), + LZMA_BLOCK_HEADER_SIZE_MAX); + io_buf buf; + if (io_pread(pair, &buf, size, iter->block.compressed_file_offset)) + return true; + + // Zero would mean Index Indicator and thus not a valid Block. + if (buf.u8[0] == 0) + goto data_error; + + lzma_block block; + lzma_filter filters[LZMA_FILTERS_MAX + 1]; + + // Initialize the pointers so that they can be passed to free(). + for (size_t i = 0; i < ARRAY_SIZE(filters); ++i) + filters[i].options = NULL; + + // Initialize the block structure and decode Block Header Size. + block.version = 0; + block.check = iter->stream.flags->check; + block.filters = filters; + + block.header_size = lzma_block_header_size_decode(buf.u8[0]); + if (block.header_size > size) + goto data_error; + + // Decode the Block Header. + switch (lzma_block_header_decode(&block, NULL, buf.u8)) { + case LZMA_OK: + break; + + case LZMA_OPTIONS_ERROR: + message_error("%s: %s", pair->src_name, + message_strm(LZMA_OPTIONS_ERROR)); + return true; + + case LZMA_DATA_ERROR: + goto data_error; + + default: + message_bug(); + } + + // Check the Block Flags. These must be done before calling + // lzma_block_compressed_size(), because it overwrites + // block.compressed_size. + bhi->flags[0] = block.compressed_size != LZMA_VLI_UNKNOWN + ? 'c' : '-'; + bhi->flags[1] = block.uncompressed_size != LZMA_VLI_UNKNOWN + ? 'u' : '-'; + bhi->flags[2] = '\0'; + + // Collect information if all Blocks have both Compressed Size + // and Uncompressed Size fields. They can be useful e.g. for + // multi-threaded decompression so it can be useful to know it. + xfi->all_have_sizes &= block.compressed_size != LZMA_VLI_UNKNOWN + && block.uncompressed_size != LZMA_VLI_UNKNOWN; + + // Validate or set block.compressed_size. + switch (lzma_block_compressed_size(&block, + iter->block.unpadded_size)) { + case LZMA_OK: + break; + + case LZMA_DATA_ERROR: + goto data_error; + + default: + message_bug(); + } + + // Copy the known sizes. + bhi->header_size = block.header_size; + bhi->compressed_size = block.compressed_size; + + // Calculate the decoder memory usage and update the maximum + // memory usage of this Block. + bhi->memusage = lzma_raw_decoder_memusage(filters); + if (xfi->memusage_max < bhi->memusage) + xfi->memusage_max = bhi->memusage; + + // Convert the filter chain to human readable form. + bhi->filter_chain = message_filters_to_str(filters, false); + + // Free the memory allocated by lzma_block_header_decode(). + for (size_t i = 0; filters[i].id != LZMA_VLI_UNKNOWN; ++i) + free(filters[i].options); + + return false; + +data_error: + // Show the error message. + message_error("%s: %s", pair->src_name, + message_strm(LZMA_DATA_ERROR)); + + // Free the memory allocated by lzma_block_header_decode(). + // This is truly needed only if we get here after a succcessful + // call to lzma_block_header_decode() but it doesn't hurt to + // always do it. + for (size_t i = 0; filters[i].id != LZMA_VLI_UNKNOWN; ++i) + free(filters[i].options); + + return true; +} + + +/// \brief Parse the Check field and put it into check_value[] +/// +/// \return False on success, true on error. +static bool +parse_check_value(file_pair *pair, const lzma_index_iter *iter) +{ + // Don't read anything from the file if there is no integrity Check. + if (iter->stream.flags->check == LZMA_CHECK_NONE) { + snprintf(check_value, sizeof(check_value), "---"); + return false; + } + + // Locate and read the Check field. + const uint32_t size = lzma_check_size(iter->stream.flags->check); + const off_t offset = iter->block.compressed_file_offset + + iter->block.total_size - size; + io_buf buf; + if (io_pread(pair, &buf, size, offset)) + return true; + + // CRC32 and CRC64 are in little endian. Guess that all the future + // 32-bit and 64-bit Check values are little endian too. It shouldn't + // be a too big problem if this guess is wrong. + if (size == 4) + snprintf(check_value, sizeof(check_value), + "%08" PRIx32, conv32le(buf.u32[0])); + else if (size == 8) + snprintf(check_value, sizeof(check_value), + "%016" PRIx64, conv64le(buf.u64[0])); + else + for (size_t i = 0; i < size; ++i) + snprintf(check_value + i * 2, 3, "%02x", buf.u8[i]); + + return false; +} + + +/// \brief Parse detailed information about a Block +/// +/// Since this requires seek(s), listing information about all Blocks can +/// be slow. +/// +/// \param pair Input file +/// \param iter Location of the Block whose Check value should +/// be printed. +/// \param bhi Pointer to structure where to store the information +/// about the Block Header field. +/// +/// \return False on success, true on error. If an error occurs, +/// the error message is printed too so the caller doesn't +/// need to worry about that. +static bool +parse_details(file_pair *pair, const lzma_index_iter *iter, + block_header_info *bhi, xz_file_info *xfi) +{ + if (parse_block_header(pair, iter, bhi, xfi)) + return true; + + if (parse_check_value(pair, iter)) + return true; + + return false; +} + + /// \brief Get the compression ratio /// /// This has slightly different format than that is used by in message.c. @@ -294,26 +551,6 @@ get_ratio(uint64_t compressed_size, uint64_t uncompressed_size) } -static const char check_names[LZMA_CHECK_ID_MAX + 1][12] = { - "None", - "CRC32", - "Unknown-2", - "Unknown-3", - "CRC64", - "Unknown-5", - "Unknown-6", - "Unknown-7", - "Unknown-8", - "Unknown-9", - "SHA-256", - "Unknown-11", - "Unknown-12", - "Unknown-13", - "Unknown-14", - "Unknown-15", -}; - - /// \brief Get a comma-separated list of Check names /// /// \param checks Bit mask of Checks to print @@ -345,51 +582,8 @@ get_check_names(uint32_t checks, bool space_after_comma) } -/// \brief Read the Check value from the .xz file and print it -/// -/// Since this requires a seek, listing all Check values for all Blocks can -/// be slow. -/// -/// \param pair Input file -/// \param iter Location of the Block whose Check value should -/// be printed. -/// -/// \return False on success, true on I/O error. static bool -print_check_value(file_pair *pair, const lzma_index_iter *iter) -{ - // Don't read anything from the file if there is no integrity Check. - if (iter->stream.flags->check == LZMA_CHECK_NONE) { - printf("---"); - return false; - } - - // Locate and read the Check field. - const uint32_t size = lzma_check_size(iter->stream.flags->check); - const off_t offset = iter->block.compressed_file_offset - + iter->block.total_size - size; - io_buf buf; - if (io_pread(pair, &buf, size, offset)) - return true; - - // CRC32 and CRC64 are in little endian. Guess that all the future - // 32-bit and 64-bit Check values are little endian too. It shouldn't - // be a too big problem if this guess is wrong. - if (size == 4) { - printf("%08" PRIx32, conv32le(buf.u32[0])); - } else if (size == 8) { - printf("%016" PRIx64, conv64le(buf.u64[0])); - } else { - for (size_t i = 0; i < size; ++i) - printf("%02x", buf.u8[i]); - } - - return false; -} - - -static void -print_info_basic(const lzma_index *idx, file_pair *pair) +print_info_basic(const xz_file_info *xfi, file_pair *pair) { static bool headings_displayed = false; if (!headings_displayed) { @@ -403,29 +597,30 @@ print_info_basic(const lzma_index *idx, file_pair *pair) } printf("%5s %7s %11s %11s %5s %-7s %s\n", - uint64_to_str(lzma_index_stream_count(idx), 0), - uint64_to_str(lzma_index_block_count(idx), 1), - uint64_to_nicestr(lzma_index_file_size(idx), + uint64_to_str(lzma_index_stream_count(xfi->idx), 0), + uint64_to_str(lzma_index_block_count(xfi->idx), 1), + uint64_to_nicestr(lzma_index_file_size(xfi->idx), NICESTR_B, NICESTR_TIB, false, 2), - uint64_to_nicestr(lzma_index_uncompressed_size(idx), + uint64_to_nicestr( + lzma_index_uncompressed_size(xfi->idx), NICESTR_B, NICESTR_TIB, false, 3), - get_ratio(lzma_index_file_size(idx), - lzma_index_uncompressed_size(idx)), - get_check_names(lzma_index_checks(idx), false), + get_ratio(lzma_index_file_size(xfi->idx), + lzma_index_uncompressed_size(xfi->idx)), + get_check_names(lzma_index_checks(xfi->idx), false), pair->src_name); - return; + return false; } static void print_adv_helper(uint64_t stream_count, uint64_t block_count, uint64_t compressed_size, uint64_t uncompressed_size, - uint32_t checks) + uint32_t checks, uint64_t stream_padding) { - printf(_(" Stream count: %s\n"), + printf(_(" Streams: %s\n"), uint64_to_str(stream_count, 0)); - printf(_(" Block count: %s\n"), + printf(_(" Blocks: %s\n"), uint64_to_str(block_count, 0)); printf(_(" Compressed size: %s\n"), uint64_to_nicestr(compressed_size, @@ -437,127 +632,187 @@ print_adv_helper(uint64_t stream_count, uint64_t block_count, get_ratio(compressed_size, uncompressed_size)); printf(_(" Check: %s\n"), get_check_names(checks, true)); + printf(_(" Stream padding: %s\n"), + uint64_to_nicestr(stream_padding, + NICESTR_B, NICESTR_TIB, true, 0)); return; } -static void -print_info_adv(const lzma_index *idx, file_pair *pair) +static bool +print_info_adv(xz_file_info *xfi, file_pair *pair) { // Print the overall information. - print_adv_helper(lzma_index_stream_count(idx), - lzma_index_block_count(idx), - lzma_index_file_size(idx), - lzma_index_uncompressed_size(idx), - lzma_index_checks(idx)); + print_adv_helper(lzma_index_stream_count(xfi->idx), + lzma_index_block_count(xfi->idx), + lzma_index_file_size(xfi->idx), + lzma_index_uncompressed_size(xfi->idx), + lzma_index_checks(xfi->idx), + xfi->stream_padding); - // TODO: The rest of this function needs some work. Currently - // the offsets are not printed, which could be useful even when - // printed in a less accurate format. On the other hand, maybe - // this should print the information with exact byte values, - // or maybe there should be at least an option to do that. - // - // We could also display some other info. E.g. it could be useful - // to quickly see how big is the biggest Block (uncompressed size) - // and if all Blocks have Compressed Size and Uncompressed Size - // fields present, which can be used e.g. for multithreaded - // decompression. + // Size of the biggest Check. This is used to calculate the width + // of the CheckVal field. The table would get insanely wide if + // we always reserved space for 64-byte Check (128 chars as hex). + uint32_t check_max = 0; - // Avoid printing Stream and Block lists when they wouldn't be useful. - bool show_blocks = false; - if (lzma_index_stream_count(idx) > 1) { - puts(_(" Streams:")); - puts(_(" Number Blocks Compressed " - "Uncompressed Ratio Check")); + // Print information about the Streams. + puts(_(" Streams:\n Stream Blocks" + " CompOffset UncompOffset" + " CompSize UncompSize Ratio" + " Check Padding")); - lzma_index_iter iter; - lzma_index_iter_init(&iter, idx); - while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)) { - if (iter.stream.block_count > 1) - show_blocks = true; + lzma_index_iter iter; + lzma_index_iter_init(&iter, xfi->idx); - printf(" %8s %10s %11s %11s %5s %s\n", + while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)) { + printf(" %6s %9s %15s %15s ", uint64_to_str(iter.stream.number, 0), uint64_to_str(iter.stream.block_count, 1), - uint64_to_nicestr( - iter.stream.compressed_size, - NICESTR_B, NICESTR_TIB, false, 2), - uint64_to_nicestr( - iter.stream.uncompressed_size, - NICESTR_B, NICESTR_TIB, false, 3), + uint64_to_str( + iter.stream.compressed_offset, 2), + uint64_to_str( + iter.stream.uncompressed_offset, 3)); + printf("%15s %15s %5s %-10s %7s\n", + uint64_to_str(iter.stream.compressed_size, 0), + uint64_to_str( + iter.stream.uncompressed_size, 1), get_ratio(iter.stream.compressed_size, iter.stream.uncompressed_size), - check_names[iter.stream.flags->check]); - } + check_names[iter.stream.flags->check], + uint64_to_str(iter.stream.padding, 2)); + + // Update the maximum Check size. + if (lzma_check_size(iter.stream.flags->check) > check_max) + check_max = lzma_check_size(iter.stream.flags->check); } - if (show_blocks || lzma_index_block_count(idx) - > lzma_index_stream_count(idx) - || message_verbosity_get() >= V_DEBUG) { - puts(_(" Blocks:")); - // FIXME: Number in Stream/file, which one is better? - puts(_(" Stream Number Compressed " - "Uncompressed Ratio Check")); + // Cache the verbosity level to a local variable. + const bool detailed = message_verbosity_get() >= V_DEBUG; - lzma_index_iter iter; - lzma_index_iter_init(&iter, idx); + // Information collected from Block Headers + block_header_info bhi; + + // Print information about the Blocks but only if there is + // at least one Block. + if (lzma_index_block_count(xfi->idx) > 0) { + // Calculate the width of the CheckVal field. + const int checkval_width = my_max(8, 2 * check_max); + + // Print the headings. + printf(_(" Blocks:\n Stream Block" + " CompOffset UncompOffset" + " TotalSize UncompSize Ratio Check")); + + if (detailed) + printf(_(" %-*s Header Flags CompSize" + " MemUsage Filters"), + checkval_width, _("CheckVal")); + + putchar('\n'); + + lzma_index_iter_init(&iter, xfi->idx); + + // Iterate over the Blocks. while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)) { - printf(" %8s %10s %11s %11s %5s %-7s", + if (detailed && parse_details(pair, &iter, &bhi, xfi)) + return true; + + printf(" %6s %9s %15s %15s ", uint64_to_str(iter.stream.number, 0), - uint64_to_str(iter.block.number_in_stream, 1), - uint64_to_nicestr(iter.block.total_size, - NICESTR_B, NICESTR_TIB, false, 2), - uint64_to_nicestr( - iter.block.uncompressed_size, - NICESTR_B, NICESTR_TIB, false, 3), + uint64_to_str( + iter.block.number_in_stream, 1), + uint64_to_str( + iter.block.compressed_file_offset, 2), + uint64_to_str( + iter.block.uncompressed_file_offset, + 3)); + printf("%15s %15s %5s %-*s", + uint64_to_str(iter.block.total_size, 0), + uint64_to_str(iter.block.uncompressed_size, + 1), get_ratio(iter.block.total_size, iter.block.uncompressed_size), + detailed ? 11 : 1, check_names[iter.stream.flags->check]); - if (message_verbosity_get() >= V_DEBUG) - if (print_check_value(pair, &iter)) - return; + if (detailed) { + // Show MiB for memory usage, because it + // is the only size which is not in bytes. + const lzma_vli compressed_size + = iter.block.unpadded_size + - bhi.header_size + - lzma_check_size( + iter.stream.flags->check); + printf("%-*s %6s %-5s %15s %7s MiB %s", + checkval_width, check_value, + uint64_to_str(bhi.header_size, 0), + bhi.flags, + uint64_to_str(compressed_size, 1), + uint64_to_str( + round_up_to_mib(bhi.memusage), + 2), + bhi.filter_chain); + } putchar('\n'); } } + + if (detailed) { + printf(_(" Memory needed: %s MiB\n"), uint64_to_str( + round_up_to_mib(xfi->memusage_max), 0)); + printf(_(" Sizes in headers: %s\n"), + xfi->all_have_sizes ? _("Yes") : _("No")); + } + + return false; } -static void -print_info_robot(const lzma_index *idx, file_pair *pair) +static bool +print_info_robot(xz_file_info *xfi, file_pair *pair) { + printf("name\t%s\n", pair->src_name); + printf("file\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 - "\t%s\t%s\t%s\n", - lzma_index_stream_count(idx), - lzma_index_block_count(idx), - lzma_index_file_size(idx), - lzma_index_uncompressed_size(idx), - get_ratio(lzma_index_file_size(idx), - lzma_index_uncompressed_size(idx)), - get_check_names(lzma_index_checks(idx), false), - pair->src_name); + "\t%s\t%s\t%" PRIu64 "\n", + lzma_index_stream_count(xfi->idx), + lzma_index_block_count(xfi->idx), + lzma_index_file_size(xfi->idx), + lzma_index_uncompressed_size(xfi->idx), + get_ratio(lzma_index_file_size(xfi->idx), + lzma_index_uncompressed_size(xfi->idx)), + get_check_names(lzma_index_checks(xfi->idx), false), + xfi->stream_padding); if (message_verbosity_get() >= V_VERBOSE) { lzma_index_iter iter; - lzma_index_iter_init(&iter, idx); + lzma_index_iter_init(&iter, xfi->idx); while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)) printf("stream\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 - "\t%" PRIu64 "\t%" PRIu64 - "\t%s\t%" PRIu64 "\t%s\n", + "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 + "\t%s\t%s\t%" PRIu64 "\n", iter.stream.number, + iter.stream.block_count, iter.stream.compressed_offset, iter.stream.uncompressed_offset, iter.stream.compressed_size, iter.stream.uncompressed_size, get_ratio(iter.stream.compressed_size, iter.stream.uncompressed_size), - iter.stream.padding, - check_names[iter.stream.flags->check]); + check_names[iter.stream.flags->check], + iter.stream.padding); lzma_index_iter_rewind(&iter); + block_header_info bhi; + while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)) { + if (message_verbosity_get() >= V_DEBUG + && parse_details( + pair, &iter, &bhi, xfi)) + return true; + printf("block\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%s\t%s", @@ -572,30 +827,46 @@ print_info_robot(const lzma_index *idx, file_pair *pair) iter.block.uncompressed_size), check_names[iter.stream.flags->check]); - if (message_verbosity_get() >= V_DEBUG) { - putchar('\t'); - if (print_check_value(pair, &iter)) - return; - } + if (message_verbosity_get() >= V_DEBUG) + printf("\t%s\t%" PRIu32 "\t%s\t%" PRIu64 + "\t%" PRIu64 "\t%s", + check_value, + bhi.header_size, + bhi.flags, + bhi.compressed_size, + bhi.memusage, + bhi.filter_chain); putchar('\n'); } } - return; + if (message_verbosity_get() >= V_DEBUG) + printf("summary\t%" PRIu64 "\t%s\n", + xfi->memusage_max, + xfi->all_have_sizes ? "yes" : "no"); + + return false; } static void -update_totals(const lzma_index *idx) +update_totals(const xz_file_info *xfi) { // TODO: Integer overflow checks ++totals.files; - totals.streams += lzma_index_stream_count(idx); - totals.blocks += lzma_index_block_count(idx); - totals.compressed_size += lzma_index_file_size(idx); - totals.uncompressed_size += lzma_index_uncompressed_size(idx); - totals.checks |= lzma_index_checks(idx); + totals.streams += lzma_index_stream_count(xfi->idx); + totals.blocks += lzma_index_block_count(xfi->idx); + totals.compressed_size += lzma_index_file_size(xfi->idx); + totals.uncompressed_size += lzma_index_uncompressed_size(xfi->idx); + totals.stream_padding += xfi->stream_padding; + totals.checks |= lzma_index_checks(xfi->idx); + + if (totals.memusage_max < xfi->memusage_max) + totals.memusage_max = xfi->memusage_max; + + totals.all_have_sizes &= xfi->all_have_sizes; + return; } @@ -647,7 +918,14 @@ print_totals_adv(void) uint64_to_str(totals.files, 0)); print_adv_helper(totals.streams, totals.blocks, totals.compressed_size, totals.uncompressed_size, - totals.checks); + totals.checks, totals.stream_padding); + + if (message_verbosity_get() >= V_DEBUG) { + printf(_(" Memory needed: %s MiB\n"), uint64_to_str( + round_up_to_mib(totals.memusage_max), 0)); + printf(_(" Sizes in headers: %s\n"), + totals.all_have_sizes ? _("Yes") : _("No")); + } return; } @@ -657,7 +935,7 @@ static void print_totals_robot(void) { printf("totals\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 - "\t%s\t%s\t%" PRIu64 "\n", + "\t%s\t%s\t%" PRIu64 "\t%" PRIu64, totals.streams, totals.blocks, totals.compressed_size, @@ -665,8 +943,16 @@ print_totals_robot(void) get_ratio(totals.compressed_size, totals.uncompressed_size), get_check_names(totals.checks, false), + totals.stream_padding, totals.files); + if (message_verbosity_get() >= V_DEBUG) + printf("\t%" PRIu64 "\t%s", + totals.memusage_max, + totals.all_have_sizes ? "yes" : "no"); + + putchar('\n'); + return; } @@ -716,11 +1002,9 @@ list_file(const char *filename) if (pair == NULL) return; - lzma_index *idx; - if (!parse_indexes(&idx, pair)) { - // Update the totals that are displayed after all - // the individual files have been listed. - update_totals(idx); + xz_file_info xfi = XZ_FILE_INFO_INIT; + if (!parse_indexes(&xfi, pair)) { + bool fail; // We have three main modes: // - --robot, which has submodes if --verbose is specified @@ -728,13 +1012,19 @@ list_file(const char *filename) // - Normal --list without --verbose // - --list with one or two --verbose if (opt_robot) - print_info_robot(idx, pair); + fail = print_info_robot(&xfi, pair); else if (message_verbosity_get() <= V_WARNING) - print_info_basic(idx, pair); + fail = print_info_basic(&xfi, pair); else - print_info_adv(idx, pair); + fail = print_info_adv(&xfi, pair); - lzma_index_end(idx, NULL); + // Update the totals that are displayed after all + // the individual files have been listed. Don't count + // broken files. + if (!fail) + update_totals(&xfi); + + lzma_index_end(xfi.idx, NULL); } io_close(pair, false);