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 } xz_file_info; 33 34 #define XZ_FILE_INFO_INIT { NULL, 0, 0, true } 35 36 37 /// Information about a .xz Block 38 typedef struct { 39 /// Size of the Block Header 40 uint32_t header_size; 41 42 /// A few of the Block Flags as a string 43 char flags[3]; 44 45 /// Size of the Compressed Data field in the Block 46 lzma_vli compressed_size; 47 48 /// Decoder memory usage for this Block 49 uint64_t memusage; 50 51 /// The filter chain of this Block in human-readable form 52 char filter_chain[FILTERS_STR_SIZE]; 53 54 } block_header_info; 55 56 57 /// Check ID to string mapping 58 static const char check_names[LZMA_CHECK_ID_MAX + 1][12] = { 59 // TRANSLATORS: Indicates that there is no integrity check. 60 // This string is used in tables, so the width must not 61 // exceed ten columns with a fixed-width font. 62 N_("None"), 63 "CRC32", 64 // TRANSLATORS: Indicates that integrity check name is not known, 65 // but the Check ID is known (here 2). This and other "Unknown-N" 66 // strings are used in tables, so the width must not exceed ten 67 // columns with a fixed-width font. It's OK to omit the dash if 68 // you need space for one extra letter, but don't use spaces. 69 N_("Unknown-2"), 70 N_("Unknown-3"), 71 "CRC64", 72 N_("Unknown-5"), 73 N_("Unknown-6"), 74 N_("Unknown-7"), 75 N_("Unknown-8"), 76 N_("Unknown-9"), 77 "SHA-256", 78 N_("Unknown-11"), 79 N_("Unknown-12"), 80 N_("Unknown-13"), 81 N_("Unknown-14"), 82 N_("Unknown-15"), 83 }; 84 85 /// Buffer size for get_check_names(). This may be a bit ridiculous, 86 /// but at least it's enough if some language needs many multibyte chars. 87 #define CHECKS_STR_SIZE 1024 88 89 90 /// Value of the Check field as hexadecimal string. 91 /// This is set by parse_check_value(). 92 static char check_value[2 * LZMA_CHECK_SIZE_MAX + 1]; 93 94 95 /// Totals that are displayed if there was more than one file. 96 /// The "files" counter is also used in print_info_adv() to show 97 /// the file number. 98 static struct { 99 uint64_t files; 100 uint64_t streams; 101 uint64_t blocks; 102 uint64_t compressed_size; 103 uint64_t uncompressed_size; 104 uint64_t stream_padding; 105 uint64_t memusage_max; 106 uint32_t checks; 107 bool all_have_sizes; 108 } totals = { 0, 0, 0, 0, 0, 0, 0, 0, true }; 109 110 111 /// \brief Parse the Index(es) from the given .xz file 112 /// 113 /// \param xfi Pointer to structure where the decoded information 114 /// is stored. 115 /// \param pair Input file 116 /// 117 /// \return On success, false is returned. On error, true is returned. 118 /// 119 // TODO: This function is pretty big. liblzma should have a function that 120 // takes a callback function to parse the Index(es) from a .xz file to make 121 // it easy for applications. 122 static bool 123 parse_indexes(xz_file_info *xfi, file_pair *pair) 124 { 125 if (pair->src_st.st_size <= 0) { 126 message_error(_("%s: File is empty"), pair->src_name); 127 return true; 128 } 129 130 if (pair->src_st.st_size < 2 * LZMA_STREAM_HEADER_SIZE) { 131 message_error(_("%s: Too small to be a valid .xz file"), 132 pair->src_name); 133 return true; 134 } 135 136 io_buf buf; 137 lzma_stream_flags header_flags; 138 lzma_stream_flags footer_flags; 139 lzma_ret ret; 140 141 // lzma_stream for the Index decoder 142 lzma_stream strm = LZMA_STREAM_INIT; 143 144 // All Indexes decoded so far 145 lzma_index *combined_index = NULL; 146 147 // The Index currently being decoded 148 lzma_index *this_index = NULL; 149 150 // Current position in the file. We parse the file backwards so 151 // initialize it to point to the end of the file. 152 off_t pos = pair->src_st.st_size; 153 154 // Each loop iteration decodes one Index. 155 do { 156 // Check that there is enough data left to contain at least 157 // the Stream Header and Stream Footer. This check cannot 158 // fail in the first pass of this loop. 159 if (pos < 2 * LZMA_STREAM_HEADER_SIZE) { 160 message_error("%s: %s", pair->src_name, 161 message_strm(LZMA_DATA_ERROR)); 162 goto error; 163 } 164 165 pos -= LZMA_STREAM_HEADER_SIZE; 166 lzma_vli stream_padding = 0; 167 168 // Locate the Stream Footer. There may be Stream Padding which 169 // we must skip when reading backwards. 170 while (true) { 171 if (pos < LZMA_STREAM_HEADER_SIZE) { 172 message_error("%s: %s", pair->src_name, 173 message_strm( 174 LZMA_DATA_ERROR)); 175 goto error; 176 } 177 178 if (io_pread(pair, &buf, 179 LZMA_STREAM_HEADER_SIZE, pos)) 180 goto error; 181 182 // Stream Padding is always a multiple of four bytes. 183 int i = 2; 184 if (buf.u32[i] != 0) 185 break; 186 187 // To avoid calling io_pread() for every four bytes 188 // of Stream Padding, take advantage that we read 189 // 12 bytes (LZMA_STREAM_HEADER_SIZE) already and 190 // check them too before calling io_pread() again. 191 do { 192 stream_padding += 4; 193 pos -= 4; 194 --i; 195 } while (i >= 0 && buf.u32[i] == 0); 196 } 197 198 // Decode the Stream Footer. 199 ret = lzma_stream_footer_decode(&footer_flags, buf.u8); 200 if (ret != LZMA_OK) { 201 message_error("%s: %s", pair->src_name, 202 message_strm(ret)); 203 goto error; 204 } 205 206 // Check that the Stream Footer doesn't specify something 207 // that we don't support. This can only happen if the xz 208 // version is older than liblzma and liblzma supports 209 // something new. 210 // 211 // It is enough to check Stream Footer. Stream Header must 212 // match when it is compared against Stream Footer with 213 // lzma_stream_flags_compare(). 214 if (footer_flags.version != 0) { 215 message_error("%s: %s", pair->src_name, 216 message_strm(LZMA_OPTIONS_ERROR)); 217 goto error; 218 } 219 220 // Check that the size of the Index field looks sane. 221 lzma_vli index_size = footer_flags.backward_size; 222 if ((lzma_vli)(pos) < index_size + LZMA_STREAM_HEADER_SIZE) { 223 message_error("%s: %s", pair->src_name, 224 message_strm(LZMA_DATA_ERROR)); 225 goto error; 226 } 227 228 // Set pos to the beginning of the Index. 229 pos -= index_size; 230 231 // See how much memory we can use for decoding this Index. 232 uint64_t memlimit = hardware_memlimit_get(MODE_LIST); 233 uint64_t memused = 0; 234 if (combined_index != NULL) { 235 memused = lzma_index_memused(combined_index); 236 if (memused > memlimit) 237 message_bug(); 238 239 memlimit -= memused; 240 } 241 242 // Decode the Index. 243 ret = lzma_index_decoder(&strm, &this_index, memlimit); 244 if (ret != LZMA_OK) { 245 message_error("%s: %s", pair->src_name, 246 message_strm(ret)); 247 goto error; 248 } 249 250 do { 251 // Don't give the decoder more input than the 252 // Index size. 253 strm.avail_in = my_min(IO_BUFFER_SIZE, index_size); 254 if (io_pread(pair, &buf, strm.avail_in, pos)) 255 goto error; 256 257 pos += strm.avail_in; 258 index_size -= strm.avail_in; 259 260 strm.next_in = buf.u8; 261 ret = lzma_code(&strm, LZMA_RUN); 262 263 } while (ret == LZMA_OK); 264 265 // If the decoding seems to be successful, check also that 266 // the Index decoder consumed as much input as indicated 267 // by the Backward Size field. 268 if (ret == LZMA_STREAM_END) 269 if (index_size != 0 || strm.avail_in != 0) 270 ret = LZMA_DATA_ERROR; 271 272 if (ret != LZMA_STREAM_END) { 273 // LZMA_BUFFER_ERROR means that the Index decoder 274 // would have liked more input than what the Index 275 // size should be according to Stream Footer. 276 // The message for LZMA_DATA_ERROR makes more 277 // sense in that case. 278 if (ret == LZMA_BUF_ERROR) 279 ret = LZMA_DATA_ERROR; 280 281 message_error("%s: %s", pair->src_name, 282 message_strm(ret)); 283 284 // If the error was too low memory usage limit, 285 // show also how much memory would have been needed. 286 if (ret == LZMA_MEMLIMIT_ERROR) { 287 uint64_t needed = lzma_memusage(&strm); 288 if (UINT64_MAX - needed < memused) 289 needed = UINT64_MAX; 290 else 291 needed += memused; 292 293 message_mem_needed(V_ERROR, needed); 294 } 295 296 goto error; 297 } 298 299 // Decode the Stream Header and check that its Stream Flags 300 // match the Stream Footer. 301 pos -= footer_flags.backward_size + LZMA_STREAM_HEADER_SIZE; 302 if ((lzma_vli)(pos) < lzma_index_total_size(this_index)) { 303 message_error("%s: %s", pair->src_name, 304 message_strm(LZMA_DATA_ERROR)); 305 goto error; 306 } 307 308 pos -= lzma_index_total_size(this_index); 309 if (io_pread(pair, &buf, LZMA_STREAM_HEADER_SIZE, pos)) 310 goto error; 311 312 ret = lzma_stream_header_decode(&header_flags, buf.u8); 313 if (ret != LZMA_OK) { 314 message_error("%s: %s", pair->src_name, 315 message_strm(ret)); 316 goto error; 317 } 318 319 ret = lzma_stream_flags_compare(&header_flags, &footer_flags); 320 if (ret != LZMA_OK) { 321 message_error("%s: %s", pair->src_name, 322 message_strm(ret)); 323 goto error; 324 } 325 326 // Store the decoded Stream Flags into this_index. This is 327 // needed so that we can print which Check is used in each 328 // Stream. 329 ret = lzma_index_stream_flags(this_index, &footer_flags); 330 if (ret != LZMA_OK) 331 message_bug(); 332 333 // Store also the size of the Stream Padding field. It is 334 // needed to show the offsets of the Streams correctly. 335 ret = lzma_index_stream_padding(this_index, stream_padding); 336 if (ret != LZMA_OK) 337 message_bug(); 338 339 if (combined_index != NULL) { 340 // Append the earlier decoded Indexes 341 // after this_index. 342 ret = lzma_index_cat( 343 this_index, combined_index, NULL); 344 if (ret != LZMA_OK) { 345 message_error("%s: %s", pair->src_name, 346 message_strm(ret)); 347 goto error; 348 } 349 } 350 351 combined_index = this_index; 352 this_index = NULL; 353 354 xfi->stream_padding += stream_padding; 355 356 } while (pos > 0); 357 358 lzma_end(&strm); 359 360 // All OK. Make combined_index available to the caller. 361 xfi->idx = combined_index; 362 return false; 363 364 error: 365 // Something went wrong, free the allocated memory. 366 lzma_end(&strm); 367 lzma_index_end(combined_index, NULL); 368 lzma_index_end(this_index, NULL); 369 return true; 370 } 371 372 373 /// \brief Parse the Block Header 374 /// 375 /// The result is stored into *bhi. The caller takes care of initializing it. 376 /// 377 /// \return False on success, true on error. 378 static bool 379 parse_block_header(file_pair *pair, const lzma_index_iter *iter, 380 block_header_info *bhi, xz_file_info *xfi) 381 { 382 #if IO_BUFFER_SIZE < LZMA_BLOCK_HEADER_SIZE_MAX 383 # error IO_BUFFER_SIZE < LZMA_BLOCK_HEADER_SIZE_MAX 384 #endif 385 386 // Get the whole Block Header with one read, but don't read past 387 // the end of the Block (or even its Check field). 388 const uint32_t size = my_min(iter->block.total_size 389 - lzma_check_size(iter->stream.flags->check), 390 LZMA_BLOCK_HEADER_SIZE_MAX); 391 io_buf buf; 392 if (io_pread(pair, &buf, size, iter->block.compressed_file_offset)) 393 return true; 394 395 // Zero would mean Index Indicator and thus not a valid Block. 396 if (buf.u8[0] == 0) 397 goto data_error; 398 399 // Initialize the block structure and decode Block Header Size. 400 lzma_filter filters[LZMA_FILTERS_MAX + 1]; 401 lzma_block block; 402 block.version = 0; 403 block.check = iter->stream.flags->check; 404 block.filters = filters; 405 406 block.header_size = lzma_block_header_size_decode(buf.u8[0]); 407 if (block.header_size > size) 408 goto data_error; 409 410 // Decode the Block Header. 411 switch (lzma_block_header_decode(&block, NULL, buf.u8)) { 412 case LZMA_OK: 413 break; 414 415 case LZMA_OPTIONS_ERROR: 416 message_error("%s: %s", pair->src_name, 417 message_strm(LZMA_OPTIONS_ERROR)); 418 return true; 419 420 case LZMA_DATA_ERROR: 421 goto data_error; 422 423 default: 424 message_bug(); 425 } 426 427 // Check the Block Flags. These must be done before calling 428 // lzma_block_compressed_size(), because it overwrites 429 // block.compressed_size. 430 bhi->flags[0] = block.compressed_size != LZMA_VLI_UNKNOWN 431 ? 'c' : '-'; 432 bhi->flags[1] = block.uncompressed_size != LZMA_VLI_UNKNOWN 433 ? 'u' : '-'; 434 bhi->flags[2] = '\0'; 435 436 // Collect information if all Blocks have both Compressed Size 437 // and Uncompressed Size fields. They can be useful e.g. for 438 // multi-threaded decompression so it can be useful to know it. 439 xfi->all_have_sizes &= block.compressed_size != LZMA_VLI_UNKNOWN 440 && block.uncompressed_size != LZMA_VLI_UNKNOWN; 441 442 // Validate or set block.compressed_size. 443 switch (lzma_block_compressed_size(&block, 444 iter->block.unpadded_size)) { 445 case LZMA_OK: 446 // Validate also block.uncompressed_size if it is present. 447 // If it isn't present, there's no need to set it since 448 // we aren't going to actually decompress the Block; if 449 // we were decompressing, then we should set it so that 450 // the Block decoder could validate the Uncompressed Size 451 // that was stored in the Index. 452 if (block.uncompressed_size == LZMA_VLI_UNKNOWN 453 || block.uncompressed_size 454 == iter->block.uncompressed_size) 455 break; 456 457 // If the above fails, the file is corrupt so 458 // LZMA_DATA_ERROR is a good error code. 459 460 case LZMA_DATA_ERROR: 461 // Free the memory allocated by lzma_block_header_decode(). 462 for (size_t i = 0; filters[i].id != LZMA_VLI_UNKNOWN; ++i) 463 free(filters[i].options); 464 465 goto data_error; 466 467 default: 468 message_bug(); 469 } 470 471 // Copy the known sizes. 472 bhi->header_size = block.header_size; 473 bhi->compressed_size = block.compressed_size; 474 475 // Calculate the decoder memory usage and update the maximum 476 // memory usage of this Block. 477 bhi->memusage = lzma_raw_decoder_memusage(filters); 478 if (xfi->memusage_max < bhi->memusage) 479 xfi->memusage_max = bhi->memusage; 480 481 // Convert the filter chain to human readable form. 482 message_filters_to_str(bhi->filter_chain, filters, false); 483 484 // Free the memory allocated by lzma_block_header_decode(). 485 for (size_t i = 0; filters[i].id != LZMA_VLI_UNKNOWN; ++i) 486 free(filters[i].options); 487 488 return false; 489 490 data_error: 491 // Show the error message. 492 message_error("%s: %s", pair->src_name, 493 message_strm(LZMA_DATA_ERROR)); 494 return true; 495 } 496 497 498 /// \brief Parse the Check field and put it into check_value[] 499 /// 500 /// \return False on success, true on error. 501 static bool 502 parse_check_value(file_pair *pair, const lzma_index_iter *iter) 503 { 504 // Don't read anything from the file if there is no integrity Check. 505 if (iter->stream.flags->check == LZMA_CHECK_NONE) { 506 snprintf(check_value, sizeof(check_value), "---"); 507 return false; 508 } 509 510 // Locate and read the Check field. 511 const uint32_t size = lzma_check_size(iter->stream.flags->check); 512 const off_t offset = iter->block.compressed_file_offset 513 + iter->block.total_size - size; 514 io_buf buf; 515 if (io_pread(pair, &buf, size, offset)) 516 return true; 517 518 // CRC32 and CRC64 are in little endian. Guess that all the future 519 // 32-bit and 64-bit Check values are little endian too. It shouldn't 520 // be a too big problem if this guess is wrong. 521 if (size == 4) 522 snprintf(check_value, sizeof(check_value), 523 "%08" PRIx32, conv32le(buf.u32[0])); 524 else if (size == 8) 525 snprintf(check_value, sizeof(check_value), 526 "%016" PRIx64, conv64le(buf.u64[0])); 527 else 528 for (size_t i = 0; i < size; ++i) 529 snprintf(check_value + i * 2, 3, "%02x", buf.u8[i]); 530 531 return false; 532 } 533 534 535 /// \brief Parse detailed information about a Block 536 /// 537 /// Since this requires seek(s), listing information about all Blocks can 538 /// be slow. 539 /// 540 /// \param pair Input file 541 /// \param iter Location of the Block whose Check value should 542 /// be printed. 543 /// \param bhi Pointer to structure where to store the information 544 /// about the Block Header field. 545 /// 546 /// \return False on success, true on error. If an error occurs, 547 /// the error message is printed too so the caller doesn't 548 /// need to worry about that. 549 static bool 550 parse_details(file_pair *pair, const lzma_index_iter *iter, 551 block_header_info *bhi, xz_file_info *xfi) 552 { 553 if (parse_block_header(pair, iter, bhi, xfi)) 554 return true; 555 556 if (parse_check_value(pair, iter)) 557 return true; 558 559 return false; 560 } 561 562 563 /// \brief Get the compression ratio 564 /// 565 /// This has slightly different format than that is used in message.c. 566 static const char * 567 get_ratio(uint64_t compressed_size, uint64_t uncompressed_size) 568 { 569 if (uncompressed_size == 0) 570 return "---"; 571 572 const double ratio = (double)(compressed_size) 573 / (double)(uncompressed_size); 574 if (ratio > 9.999) 575 return "---"; 576 577 static char buf[16]; 578 snprintf(buf, sizeof(buf), "%.3f", ratio); 579 return buf; 580 } 581 582 583 /// \brief Get a comma-separated list of Check names 584 /// 585 /// The check names are translated with gettext except when in robot mode. 586 /// 587 /// \param buf Buffer to hold the resulting string 588 /// \param checks Bit mask of Checks to print 589 /// \param space_after_comma 590 /// It's better to not use spaces in table-like listings, 591 /// but in more verbose formats a space after a comma 592 /// is good for readability. 593 static void 594 get_check_names(char buf[CHECKS_STR_SIZE], 595 uint32_t checks, bool space_after_comma) 596 { 597 assert(checks != 0); 598 599 char *pos = buf; 600 size_t left = CHECKS_STR_SIZE; 601 602 const char *sep = space_after_comma ? ", " : ","; 603 bool comma = false; 604 605 for (size_t i = 0; i <= LZMA_CHECK_ID_MAX; ++i) { 606 if (checks & (UINT32_C(1) << i)) { 607 my_snprintf(&pos, &left, "%s%s", 608 comma ? sep : "", 609 opt_robot ? check_names[i] 610 : _(check_names[i])); 611 comma = true; 612 } 613 } 614 615 return; 616 } 617 618 619 static bool 620 print_info_basic(const xz_file_info *xfi, file_pair *pair) 621 { 622 static bool headings_displayed = false; 623 if (!headings_displayed) { 624 headings_displayed = true; 625 // TRANSLATORS: These are column headings. From Strms (Streams) 626 // to Ratio, the columns are right aligned. Check and Filename 627 // are left aligned. If you need longer words, it's OK to 628 // use two lines here. Test with "xz -l foo.xz". 629 puts(_("Strms Blocks Compressed Uncompressed Ratio " 630 "Check Filename")); 631 } 632 633 char checks[CHECKS_STR_SIZE]; 634 get_check_names(checks, lzma_index_checks(xfi->idx), false); 635 636 const char *cols[7] = { 637 uint64_to_str(lzma_index_stream_count(xfi->idx), 0), 638 uint64_to_str(lzma_index_block_count(xfi->idx), 1), 639 uint64_to_nicestr(lzma_index_file_size(xfi->idx), 640 NICESTR_B, NICESTR_TIB, false, 2), 641 uint64_to_nicestr(lzma_index_uncompressed_size(xfi->idx), 642 NICESTR_B, NICESTR_TIB, false, 3), 643 get_ratio(lzma_index_file_size(xfi->idx), 644 lzma_index_uncompressed_size(xfi->idx)), 645 checks, 646 pair->src_name, 647 }; 648 printf("%*s %*s %*s %*s %*s %-*s %s\n", 649 tuklib_mbstr_fw(cols[0], 5), cols[0], 650 tuklib_mbstr_fw(cols[1], 7), cols[1], 651 tuklib_mbstr_fw(cols[2], 11), cols[2], 652 tuklib_mbstr_fw(cols[3], 11), cols[3], 653 tuklib_mbstr_fw(cols[4], 5), cols[4], 654 tuklib_mbstr_fw(cols[5], 7), cols[5], 655 cols[6]); 656 657 return false; 658 } 659 660 661 static void 662 print_adv_helper(uint64_t stream_count, uint64_t block_count, 663 uint64_t compressed_size, uint64_t uncompressed_size, 664 uint32_t checks, uint64_t stream_padding) 665 { 666 char checks_str[CHECKS_STR_SIZE]; 667 get_check_names(checks_str, checks, true); 668 669 printf(_(" Streams: %s\n"), 670 uint64_to_str(stream_count, 0)); 671 printf(_(" Blocks: %s\n"), 672 uint64_to_str(block_count, 0)); 673 printf(_(" Compressed size: %s\n"), 674 uint64_to_nicestr(compressed_size, 675 NICESTR_B, NICESTR_TIB, true, 0)); 676 printf(_(" Uncompressed size: %s\n"), 677 uint64_to_nicestr(uncompressed_size, 678 NICESTR_B, NICESTR_TIB, true, 0)); 679 printf(_(" Ratio: %s\n"), 680 get_ratio(compressed_size, uncompressed_size)); 681 printf(_(" Check: %s\n"), checks_str); 682 printf(_(" Stream padding: %s\n"), 683 uint64_to_nicestr(stream_padding, 684 NICESTR_B, NICESTR_TIB, true, 0)); 685 return; 686 } 687 688 689 static bool 690 print_info_adv(xz_file_info *xfi, file_pair *pair) 691 { 692 // Print the overall information. 693 print_adv_helper(lzma_index_stream_count(xfi->idx), 694 lzma_index_block_count(xfi->idx), 695 lzma_index_file_size(xfi->idx), 696 lzma_index_uncompressed_size(xfi->idx), 697 lzma_index_checks(xfi->idx), 698 xfi->stream_padding); 699 700 // Size of the biggest Check. This is used to calculate the width 701 // of the CheckVal field. The table would get insanely wide if 702 // we always reserved space for 64-byte Check (128 chars as hex). 703 uint32_t check_max = 0; 704 705 // Print information about the Streams. 706 // 707 // TRANSLATORS: The second line is column headings. All except 708 // Check are right aligned; Check is left aligned. Test with 709 // "xz -lv foo.xz". 710 puts(_(" Streams:\n Stream Blocks" 711 " CompOffset UncompOffset" 712 " CompSize UncompSize Ratio" 713 " Check Padding")); 714 715 lzma_index_iter iter; 716 lzma_index_iter_init(&iter, xfi->idx); 717 718 while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)) { 719 const char *cols1[4] = { 720 uint64_to_str(iter.stream.number, 0), 721 uint64_to_str(iter.stream.block_count, 1), 722 uint64_to_str(iter.stream.compressed_offset, 2), 723 uint64_to_str(iter.stream.uncompressed_offset, 3), 724 }; 725 printf(" %*s %*s %*s %*s ", 726 tuklib_mbstr_fw(cols1[0], 6), cols1[0], 727 tuklib_mbstr_fw(cols1[1], 9), cols1[1], 728 tuklib_mbstr_fw(cols1[2], 15), cols1[2], 729 tuklib_mbstr_fw(cols1[3], 15), cols1[3]); 730 731 const char *cols2[5] = { 732 uint64_to_str(iter.stream.compressed_size, 0), 733 uint64_to_str(iter.stream.uncompressed_size, 1), 734 get_ratio(iter.stream.compressed_size, 735 iter.stream.uncompressed_size), 736 _(check_names[iter.stream.flags->check]), 737 uint64_to_str(iter.stream.padding, 2), 738 }; 739 printf("%*s %*s %*s %-*s %*s\n", 740 tuklib_mbstr_fw(cols2[0], 15), cols2[0], 741 tuklib_mbstr_fw(cols2[1], 15), cols2[1], 742 tuklib_mbstr_fw(cols2[2], 5), cols2[2], 743 tuklib_mbstr_fw(cols2[3], 10), cols2[3], 744 tuklib_mbstr_fw(cols2[4], 7), cols2[4]); 745 746 // Update the maximum Check size. 747 if (lzma_check_size(iter.stream.flags->check) > check_max) 748 check_max = lzma_check_size(iter.stream.flags->check); 749 } 750 751 // Cache the verbosity level to a local variable. 752 const bool detailed = message_verbosity_get() >= V_DEBUG; 753 754 // Information collected from Block Headers 755 block_header_info bhi; 756 757 // Print information about the Blocks but only if there is 758 // at least one Block. 759 if (lzma_index_block_count(xfi->idx) > 0) { 760 // Calculate the width of the CheckVal field. 761 const int checkval_width = my_max(8, 2 * check_max); 762 763 // TRANSLATORS: The second line is column headings. All 764 // except Check are right aligned; Check is left aligned. 765 printf(_(" Blocks:\n Stream Block" 766 " CompOffset UncompOffset" 767 " TotalSize UncompSize Ratio Check")); 768 769 if (detailed) { 770 // TRANSLATORS: These are additional column headings 771 // for the most verbose listing mode. CheckVal 772 // (Check value), Flags, and Filters are left aligned. 773 // Header (Block Header Size), CompSize, and MemUsage 774 // are right aligned. %*s is replaced with 0-120 775 // spaces to make the CheckVal column wide enough. 776 // Test with "xz -lvv foo.xz". 777 printf(_(" CheckVal %*s Header Flags " 778 "CompSize MemUsage Filters"), 779 checkval_width - 8, ""); 780 } 781 782 putchar('\n'); 783 784 lzma_index_iter_init(&iter, xfi->idx); 785 786 // Iterate over the Blocks. 787 while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)) { 788 if (detailed && parse_details(pair, &iter, &bhi, xfi)) 789 return true; 790 791 const char *cols1[4] = { 792 uint64_to_str(iter.stream.number, 0), 793 uint64_to_str( 794 iter.block.number_in_stream, 1), 795 uint64_to_str( 796 iter.block.compressed_file_offset, 2), 797 uint64_to_str( 798 iter.block.uncompressed_file_offset, 3) 799 }; 800 printf(" %*s %*s %*s %*s ", 801 tuklib_mbstr_fw(cols1[0], 6), cols1[0], 802 tuklib_mbstr_fw(cols1[1], 9), cols1[1], 803 tuklib_mbstr_fw(cols1[2], 15), cols1[2], 804 tuklib_mbstr_fw(cols1[3], 15), cols1[3]); 805 806 const char *cols2[4] = { 807 uint64_to_str(iter.block.total_size, 0), 808 uint64_to_str(iter.block.uncompressed_size, 809 1), 810 get_ratio(iter.block.total_size, 811 iter.block.uncompressed_size), 812 _(check_names[iter.stream.flags->check]) 813 }; 814 printf("%*s %*s %*s %-*s", 815 tuklib_mbstr_fw(cols2[0], 15), cols2[0], 816 tuklib_mbstr_fw(cols2[1], 15), cols2[1], 817 tuklib_mbstr_fw(cols2[2], 5), cols2[2], 818 tuklib_mbstr_fw(cols2[3], detailed ? 11 : 1), 819 cols2[3]); 820 821 if (detailed) { 822 const lzma_vli compressed_size 823 = iter.block.unpadded_size 824 - bhi.header_size 825 - lzma_check_size( 826 iter.stream.flags->check); 827 828 const char *cols3[6] = { 829 check_value, 830 uint64_to_str(bhi.header_size, 0), 831 bhi.flags, 832 uint64_to_str(compressed_size, 1), 833 uint64_to_str( 834 round_up_to_mib(bhi.memusage), 835 2), 836 bhi.filter_chain 837 }; 838 // Show MiB for memory usage, because it 839 // is the only size which is not in bytes. 840 printf("%-*s %*s %-5s %*s %*s MiB %s", 841 checkval_width, cols3[0], 842 tuklib_mbstr_fw(cols3[1], 6), cols3[1], 843 cols3[2], 844 tuklib_mbstr_fw(cols3[3], 15), 845 cols3[3], 846 tuklib_mbstr_fw(cols3[4], 7), cols3[4], 847 cols3[5]); 848 } 849 850 putchar('\n'); 851 } 852 } 853 854 if (detailed) { 855 printf(_(" Memory needed: %s MiB\n"), uint64_to_str( 856 round_up_to_mib(xfi->memusage_max), 0)); 857 printf(_(" Sizes in headers: %s\n"), 858 xfi->all_have_sizes ? _("Yes") : _("No")); 859 } 860 861 return false; 862 } 863 864 865 static bool 866 print_info_robot(xz_file_info *xfi, file_pair *pair) 867 { 868 char checks[CHECKS_STR_SIZE]; 869 get_check_names(checks, lzma_index_checks(xfi->idx), false); 870 871 printf("name\t%s\n", pair->src_name); 872 873 printf("file\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 874 "\t%s\t%s\t%" PRIu64 "\n", 875 lzma_index_stream_count(xfi->idx), 876 lzma_index_block_count(xfi->idx), 877 lzma_index_file_size(xfi->idx), 878 lzma_index_uncompressed_size(xfi->idx), 879 get_ratio(lzma_index_file_size(xfi->idx), 880 lzma_index_uncompressed_size(xfi->idx)), 881 checks, 882 xfi->stream_padding); 883 884 if (message_verbosity_get() >= V_VERBOSE) { 885 lzma_index_iter iter; 886 lzma_index_iter_init(&iter, xfi->idx); 887 888 while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)) 889 printf("stream\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 890 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 891 "\t%s\t%s\t%" PRIu64 "\n", 892 iter.stream.number, 893 iter.stream.block_count, 894 iter.stream.compressed_offset, 895 iter.stream.uncompressed_offset, 896 iter.stream.compressed_size, 897 iter.stream.uncompressed_size, 898 get_ratio(iter.stream.compressed_size, 899 iter.stream.uncompressed_size), 900 check_names[iter.stream.flags->check], 901 iter.stream.padding); 902 903 lzma_index_iter_rewind(&iter); 904 block_header_info bhi; 905 906 while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)) { 907 if (message_verbosity_get() >= V_DEBUG 908 && parse_details( 909 pair, &iter, &bhi, xfi)) 910 return true; 911 912 printf("block\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 913 "\t%" PRIu64 "\t%" PRIu64 914 "\t%" PRIu64 "\t%" PRIu64 "\t%s\t%s", 915 iter.stream.number, 916 iter.block.number_in_stream, 917 iter.block.number_in_file, 918 iter.block.compressed_file_offset, 919 iter.block.uncompressed_file_offset, 920 iter.block.total_size, 921 iter.block.uncompressed_size, 922 get_ratio(iter.block.total_size, 923 iter.block.uncompressed_size), 924 check_names[iter.stream.flags->check]); 925 926 if (message_verbosity_get() >= V_DEBUG) 927 printf("\t%s\t%" PRIu32 "\t%s\t%" PRIu64 928 "\t%" PRIu64 "\t%s", 929 check_value, 930 bhi.header_size, 931 bhi.flags, 932 bhi.compressed_size, 933 bhi.memusage, 934 bhi.filter_chain); 935 936 putchar('\n'); 937 } 938 } 939 940 if (message_verbosity_get() >= V_DEBUG) 941 printf("summary\t%" PRIu64 "\t%s\n", 942 xfi->memusage_max, 943 xfi->all_have_sizes ? "yes" : "no"); 944 945 return false; 946 } 947 948 949 static void 950 update_totals(const xz_file_info *xfi) 951 { 952 // TODO: Integer overflow checks 953 ++totals.files; 954 totals.streams += lzma_index_stream_count(xfi->idx); 955 totals.blocks += lzma_index_block_count(xfi->idx); 956 totals.compressed_size += lzma_index_file_size(xfi->idx); 957 totals.uncompressed_size += lzma_index_uncompressed_size(xfi->idx); 958 totals.stream_padding += xfi->stream_padding; 959 totals.checks |= lzma_index_checks(xfi->idx); 960 961 if (totals.memusage_max < xfi->memusage_max) 962 totals.memusage_max = xfi->memusage_max; 963 964 totals.all_have_sizes &= xfi->all_have_sizes; 965 966 return; 967 } 968 969 970 static void 971 print_totals_basic(void) 972 { 973 // Print a separator line. 974 char line[80]; 975 memset(line, '-', sizeof(line)); 976 line[sizeof(line) - 1] = '\0'; 977 puts(line); 978 979 // Get the check names. 980 char checks[CHECKS_STR_SIZE]; 981 get_check_names(checks, totals.checks, false); 982 983 // Print the totals except the file count, which needs 984 // special handling. 985 printf("%5s %7s %11s %11s %5s %-7s ", 986 uint64_to_str(totals.streams, 0), 987 uint64_to_str(totals.blocks, 1), 988 uint64_to_nicestr(totals.compressed_size, 989 NICESTR_B, NICESTR_TIB, false, 2), 990 uint64_to_nicestr(totals.uncompressed_size, 991 NICESTR_B, NICESTR_TIB, false, 3), 992 get_ratio(totals.compressed_size, 993 totals.uncompressed_size), 994 checks); 995 996 // Since we print totals only when there are at least two files, 997 // the English message will always use "%s files". But some other 998 // languages need different forms for different plurals so we 999 // have to translate this with ngettext(). 1000 // 1001 // TRANSLATORS: %s is an integer. Only the plural form of this 1002 // message is used (e.g. "2 files"). Test with "xz -l foo.xz bar.xz". 1003 printf(ngettext("%s file\n", "%s files\n", 1004 totals.files <= ULONG_MAX ? totals.files 1005 : (totals.files % 1000000) + 1000000), 1006 uint64_to_str(totals.files, 0)); 1007 1008 return; 1009 } 1010 1011 1012 static void 1013 print_totals_adv(void) 1014 { 1015 putchar('\n'); 1016 puts(_("Totals:")); 1017 printf(_(" Number of files: %s\n"), 1018 uint64_to_str(totals.files, 0)); 1019 print_adv_helper(totals.streams, totals.blocks, 1020 totals.compressed_size, totals.uncompressed_size, 1021 totals.checks, totals.stream_padding); 1022 1023 if (message_verbosity_get() >= V_DEBUG) { 1024 printf(_(" Memory needed: %s MiB\n"), uint64_to_str( 1025 round_up_to_mib(totals.memusage_max), 0)); 1026 printf(_(" Sizes in headers: %s\n"), 1027 totals.all_have_sizes ? _("Yes") : _("No")); 1028 } 1029 1030 return; 1031 } 1032 1033 1034 static void 1035 print_totals_robot(void) 1036 { 1037 char checks[CHECKS_STR_SIZE]; 1038 get_check_names(checks, totals.checks, false); 1039 1040 printf("totals\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 1041 "\t%s\t%s\t%" PRIu64 "\t%" PRIu64, 1042 totals.streams, 1043 totals.blocks, 1044 totals.compressed_size, 1045 totals.uncompressed_size, 1046 get_ratio(totals.compressed_size, 1047 totals.uncompressed_size), 1048 checks, 1049 totals.stream_padding, 1050 totals.files); 1051 1052 if (message_verbosity_get() >= V_DEBUG) 1053 printf("\t%" PRIu64 "\t%s", 1054 totals.memusage_max, 1055 totals.all_have_sizes ? "yes" : "no"); 1056 1057 putchar('\n'); 1058 1059 return; 1060 } 1061 1062 1063 extern void 1064 list_totals(void) 1065 { 1066 if (opt_robot) { 1067 // Always print totals in --robot mode. It can be convenient 1068 // in some cases and doesn't complicate usage of the 1069 // single-file case much. 1070 print_totals_robot(); 1071 1072 } else if (totals.files > 1) { 1073 // For non-robot mode, totals are printed only if there 1074 // is more than one file. 1075 if (message_verbosity_get() <= V_WARNING) 1076 print_totals_basic(); 1077 else 1078 print_totals_adv(); 1079 } 1080 1081 return; 1082 } 1083 1084 1085 extern void 1086 list_file(const char *filename) 1087 { 1088 if (opt_format != FORMAT_XZ && opt_format != FORMAT_AUTO) 1089 message_fatal(_("--list works only on .xz files " 1090 "(--format=xz or --format=auto)")); 1091 1092 message_filename(filename); 1093 1094 if (filename == stdin_filename) { 1095 message_error(_("--list does not support reading from " 1096 "standard input")); 1097 return; 1098 } 1099 1100 // Unset opt_stdout so that io_open_src() won't accept special files. 1101 // Set opt_force so that io_open_src() will follow symlinks. 1102 opt_stdout = false; 1103 opt_force = true; 1104 file_pair *pair = io_open_src(filename); 1105 if (pair == NULL) 1106 return; 1107 1108 xz_file_info xfi = XZ_FILE_INFO_INIT; 1109 if (!parse_indexes(&xfi, pair)) { 1110 bool fail; 1111 1112 // We have three main modes: 1113 // - --robot, which has submodes if --verbose is specified 1114 // once or twice 1115 // - Normal --list without --verbose 1116 // - --list with one or two --verbose 1117 if (opt_robot) 1118 fail = print_info_robot(&xfi, pair); 1119 else if (message_verbosity_get() <= V_WARNING) 1120 fail = print_info_basic(&xfi, pair); 1121 else 1122 fail = print_info_adv(&xfi, pair); 1123 1124 // Update the totals that are displayed after all 1125 // the individual files have been listed. Don't count 1126 // broken files. 1127 if (!fail) 1128 update_totals(&xfi); 1129 1130 lzma_index_end(xfi.idx, NULL); 1131 } 1132 1133 io_close(pair, false); 1134 return; 1135 } 1136