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