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