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