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