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 = (int)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 = (int)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, 582 message_strm(str_ret)); 583 return true; 584 } 585 586 return false; 587 588 data_error: 589 // Show the error message. 590 message_error(_("%s: %s"), pair->src_name, 591 message_strm(LZMA_DATA_ERROR)); 592 return true; 593 } 594 595 596 /// \brief Parse the Check field and put it into check_value[] 597 /// 598 /// \return False on success, true on error. 599 static bool 600 parse_check_value(file_pair *pair, const lzma_index_iter *iter) 601 { 602 // Don't read anything from the file if there is no integrity Check. 603 if (iter->stream.flags->check == LZMA_CHECK_NONE) { 604 snprintf(check_value, sizeof(check_value), "---"); 605 return false; 606 } 607 608 // Locate and read the Check field. 609 const uint32_t size = lzma_check_size(iter->stream.flags->check); 610 const uint64_t offset = iter->block.compressed_file_offset 611 + iter->block.total_size - size; 612 io_buf buf; 613 if (io_pread(pair, &buf, size, offset)) 614 return true; 615 616 // CRC32 and CRC64 are in little endian. Guess that all the future 617 // 32-bit and 64-bit Check values are little endian too. It shouldn't 618 // be a too big problem if this guess is wrong. 619 if (size == 4) 620 snprintf(check_value, sizeof(check_value), 621 "%08" PRIx32, conv32le(buf.u32[0])); 622 else if (size == 8) 623 snprintf(check_value, sizeof(check_value), 624 "%016" PRIx64, conv64le(buf.u64[0])); 625 else 626 for (size_t i = 0; i < size; ++i) 627 snprintf(check_value + i * 2, 3, "%02x", buf.u8[i]); 628 629 return false; 630 } 631 632 633 /// \brief Parse detailed information about a Block 634 /// 635 /// Since this requires seek(s), listing information about all Blocks can 636 /// be slow. 637 /// 638 /// \param pair Input file 639 /// \param iter Location of the Block whose Check value should 640 /// be printed. 641 /// \param bhi Pointer to structure where to store the information 642 /// about the Block Header field. 643 /// 644 /// \return False on success, true on error. If an error occurs, 645 /// the error message is printed too so the caller doesn't 646 /// need to worry about that. 647 static bool 648 parse_details(file_pair *pair, const lzma_index_iter *iter, 649 block_header_info *bhi, xz_file_info *xfi) 650 { 651 if (parse_block_header(pair, iter, bhi, xfi)) 652 return true; 653 654 if (parse_check_value(pair, iter)) 655 return true; 656 657 return false; 658 } 659 660 661 /// \brief Get the compression ratio 662 /// 663 /// This has slightly different format than that is used in message.c. 664 static const char * 665 get_ratio(uint64_t compressed_size, uint64_t uncompressed_size) 666 { 667 if (uncompressed_size == 0) 668 return "---"; 669 670 const double ratio = (double)(compressed_size) 671 / (double)(uncompressed_size); 672 if (ratio > 9.999) 673 return "---"; 674 675 static char buf[16]; 676 snprintf(buf, sizeof(buf), "%.3f", ratio); 677 return buf; 678 } 679 680 681 /// \brief Get a comma-separated list of Check names 682 /// 683 /// The check names are translated with gettext except when in robot mode. 684 /// 685 /// \param buf Buffer to hold the resulting string 686 /// \param checks Bit mask of Checks to print 687 /// \param space_after_comma 688 /// It's better to not use spaces in table-like listings, 689 /// but in more verbose formats a space after a comma 690 /// is good for readability. 691 static void 692 get_check_names(char buf[CHECKS_STR_SIZE], 693 uint32_t checks, bool space_after_comma) 694 { 695 // If we get called when there are no Checks to print, set checks 696 // to 1 so that we print "None". This can happen in the robot mode 697 // when printing the totals line if there are no valid input files. 698 if (checks == 0) 699 checks = 1; 700 701 char *pos = buf; 702 size_t left = CHECKS_STR_SIZE; 703 704 const char *sep = space_after_comma ? ", " : ","; 705 bool comma = false; 706 707 for (size_t i = 0; i <= LZMA_CHECK_ID_MAX; ++i) { 708 if (checks & (UINT32_C(1) << i)) { 709 my_snprintf(&pos, &left, "%s%s", 710 comma ? sep : "", 711 opt_robot ? check_names[i] 712 : _(check_names[i])); 713 comma = true; 714 } 715 } 716 717 return; 718 } 719 720 721 static bool 722 print_info_basic(const xz_file_info *xfi, file_pair *pair) 723 { 724 static bool headings_displayed = false; 725 if (!headings_displayed) { 726 headings_displayed = true; 727 // TRANSLATORS: These are column headings. From Strms (Streams) 728 // to Ratio, the columns are right aligned. Check and Filename 729 // are left aligned. If you need longer words, it's OK to 730 // use two lines here. Test with "xz -l foo.xz". 731 puts(_("Strms Blocks Compressed Uncompressed Ratio " 732 "Check Filename")); 733 } 734 735 char checks[CHECKS_STR_SIZE]; 736 get_check_names(checks, lzma_index_checks(xfi->idx), false); 737 738 const char *cols[7] = { 739 uint64_to_str(lzma_index_stream_count(xfi->idx), 0), 740 uint64_to_str(lzma_index_block_count(xfi->idx), 1), 741 uint64_to_nicestr(lzma_index_file_size(xfi->idx), 742 NICESTR_B, NICESTR_TIB, false, 2), 743 uint64_to_nicestr(lzma_index_uncompressed_size(xfi->idx), 744 NICESTR_B, NICESTR_TIB, false, 3), 745 get_ratio(lzma_index_file_size(xfi->idx), 746 lzma_index_uncompressed_size(xfi->idx)), 747 checks, 748 pair->src_name, 749 }; 750 printf("%*s %*s %*s %*s %*s %-*s %s\n", 751 tuklib_mbstr_fw(cols[0], 5), cols[0], 752 tuklib_mbstr_fw(cols[1], 7), cols[1], 753 tuklib_mbstr_fw(cols[2], 11), cols[2], 754 tuklib_mbstr_fw(cols[3], 11), cols[3], 755 tuklib_mbstr_fw(cols[4], 5), cols[4], 756 tuklib_mbstr_fw(cols[5], 7), cols[5], 757 cols[6]); 758 759 return false; 760 } 761 762 763 static void 764 print_adv_helper(uint64_t stream_count, uint64_t block_count, 765 uint64_t compressed_size, uint64_t uncompressed_size, 766 uint32_t checks, uint64_t stream_padding) 767 { 768 char checks_str[CHECKS_STR_SIZE]; 769 get_check_names(checks_str, checks, true); 770 771 printf(" %-*s %s\n", COLON_STR(COLON_STR_STREAMS), 772 uint64_to_str(stream_count, 0)); 773 printf(" %-*s %s\n", COLON_STR(COLON_STR_BLOCKS), 774 uint64_to_str(block_count, 0)); 775 printf(" %-*s %s\n", COLON_STR(COLON_STR_COMPRESSED_SIZE), 776 uint64_to_nicestr(compressed_size, 777 NICESTR_B, NICESTR_TIB, true, 0)); 778 printf(" %-*s %s\n", COLON_STR(COLON_STR_UNCOMPRESSED_SIZE), 779 uint64_to_nicestr(uncompressed_size, 780 NICESTR_B, NICESTR_TIB, true, 0)); 781 printf(" %-*s %s\n", COLON_STR(COLON_STR_RATIO), 782 get_ratio(compressed_size, uncompressed_size)); 783 printf(" %-*s %s\n", COLON_STR(COLON_STR_CHECK), checks_str); 784 printf(" %-*s %s\n", COLON_STR(COLON_STR_STREAM_PADDING), 785 uint64_to_nicestr(stream_padding, 786 NICESTR_B, NICESTR_TIB, true, 0)); 787 return; 788 } 789 790 791 static bool 792 print_info_adv(xz_file_info *xfi, file_pair *pair) 793 { 794 // Print the overall information. 795 print_adv_helper(lzma_index_stream_count(xfi->idx), 796 lzma_index_block_count(xfi->idx), 797 lzma_index_file_size(xfi->idx), 798 lzma_index_uncompressed_size(xfi->idx), 799 lzma_index_checks(xfi->idx), 800 xfi->stream_padding); 801 802 // Size of the biggest Check. This is used to calculate the width 803 // of the CheckVal field. The table would get insanely wide if 804 // we always reserved space for 64-byte Check (128 chars as hex). 805 uint32_t check_max = 0; 806 807 // Print information about the Streams. 808 // 809 // All except Check are right aligned; Check is left aligned. 810 // Test with "xz -lv foo.xz". 811 printf(" %s\n %*s %*s %*s %*s %*s %*s %*s %-*s %*s\n", 812 _(colon_strs[COLON_STR_STREAMS]), 813 HEADING_STR(HEADING_STREAM), 814 HEADING_STR(HEADING_BLOCKS), 815 HEADING_STR(HEADING_COMPOFFSET), 816 HEADING_STR(HEADING_UNCOMPOFFSET), 817 HEADING_STR(HEADING_COMPSIZE), 818 HEADING_STR(HEADING_UNCOMPSIZE), 819 HEADING_STR(HEADING_RATIO), 820 HEADING_STR(HEADING_CHECK), 821 HEADING_STR(HEADING_PADDING)); 822 823 lzma_index_iter iter; 824 lzma_index_iter_init(&iter, xfi->idx); 825 826 while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)) { 827 const char *cols1[4] = { 828 uint64_to_str(iter.stream.number, 0), 829 uint64_to_str(iter.stream.block_count, 1), 830 uint64_to_str(iter.stream.compressed_offset, 2), 831 uint64_to_str(iter.stream.uncompressed_offset, 3), 832 }; 833 printf(" %*s %*s %*s %*s ", 834 tuklib_mbstr_fw(cols1[0], 835 headings[HEADING_STREAM].columns), 836 cols1[0], 837 tuklib_mbstr_fw(cols1[1], 838 headings[HEADING_BLOCKS].columns), 839 cols1[1], 840 tuklib_mbstr_fw(cols1[2], 841 headings[HEADING_COMPOFFSET].columns), 842 cols1[2], 843 tuklib_mbstr_fw(cols1[3], 844 headings[HEADING_UNCOMPOFFSET].columns), 845 cols1[3]); 846 847 const char *cols2[5] = { 848 uint64_to_str(iter.stream.compressed_size, 0), 849 uint64_to_str(iter.stream.uncompressed_size, 1), 850 get_ratio(iter.stream.compressed_size, 851 iter.stream.uncompressed_size), 852 _(check_names[iter.stream.flags->check]), 853 uint64_to_str(iter.stream.padding, 2), 854 }; 855 printf("%*s %*s %*s %-*s %*s\n", 856 tuklib_mbstr_fw(cols2[0], 857 headings[HEADING_COMPSIZE].columns), 858 cols2[0], 859 tuklib_mbstr_fw(cols2[1], 860 headings[HEADING_UNCOMPSIZE].columns), 861 cols2[1], 862 tuklib_mbstr_fw(cols2[2], 863 headings[HEADING_RATIO].columns), 864 cols2[2], 865 tuklib_mbstr_fw(cols2[3], 866 headings[HEADING_CHECK].columns), 867 cols2[3], 868 tuklib_mbstr_fw(cols2[4], 869 headings[HEADING_PADDING].columns), 870 cols2[4]); 871 872 // Update the maximum Check size. 873 if (lzma_check_size(iter.stream.flags->check) > check_max) 874 check_max = lzma_check_size(iter.stream.flags->check); 875 } 876 877 // Cache the verbosity level to a local variable. 878 const bool detailed = message_verbosity_get() >= V_DEBUG; 879 880 // Print information about the Blocks but only if there is 881 // at least one Block. 882 if (lzma_index_block_count(xfi->idx) > 0) { 883 // Calculate the width of the CheckVal column. This can be 884 // used as is as the field width for printf() when printing 885 // the actual check value as it is hexadecimal. However, to 886 // print the column heading, further calculation is needed 887 // to handle a translated string (it's done a few lines later). 888 assert(check_max <= LZMA_CHECK_SIZE_MAX); 889 const int checkval_width = my_max( 890 headings[HEADING_CHECKVAL].columns, 891 (int)(2 * check_max)); 892 893 // All except Check are right aligned; Check is left aligned. 894 printf(" %s\n %*s %*s %*s %*s %*s %*s %*s %-*s", 895 _(colon_strs[COLON_STR_BLOCKS]), 896 HEADING_STR(HEADING_STREAM), 897 HEADING_STR(HEADING_BLOCK), 898 HEADING_STR(HEADING_COMPOFFSET), 899 HEADING_STR(HEADING_UNCOMPOFFSET), 900 HEADING_STR(HEADING_TOTALSIZE), 901 HEADING_STR(HEADING_UNCOMPSIZE), 902 HEADING_STR(HEADING_RATIO), 903 detailed ? headings[HEADING_CHECK].fw : 1, 904 _(headings[HEADING_CHECK].str)); 905 906 if (detailed) { 907 // CheckVal (Check value), Flags, and Filters are 908 // left aligned. Block Header Size, CompSize, and 909 // MemUsage are right aligned. Test with 910 // "xz -lvv foo.xz". 911 printf(" %-*s %*s %-*s %*s %*s %s", 912 headings[HEADING_CHECKVAL].fw 913 + checkval_width 914 - headings[HEADING_CHECKVAL].columns, 915 _(headings[HEADING_CHECKVAL].str), 916 HEADING_STR(HEADING_HEADERSIZE), 917 HEADING_STR(HEADING_HEADERFLAGS), 918 HEADING_STR(HEADING_COMPSIZE), 919 HEADING_STR(HEADING_MEMUSAGE), 920 _(headings[HEADING_FILTERS].str)); 921 } 922 923 putchar('\n'); 924 925 lzma_index_iter_init(&iter, xfi->idx); 926 927 // Iterate over the Blocks. 928 while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)) { 929 // If in detailed mode, collect the information from 930 // Block Header before starting to print the next line. 931 block_header_info bhi = BLOCK_HEADER_INFO_INIT; 932 if (detailed && parse_details(pair, &iter, &bhi, xfi)) 933 return true; 934 935 const char *cols1[4] = { 936 uint64_to_str(iter.stream.number, 0), 937 uint64_to_str( 938 iter.block.number_in_stream, 1), 939 uint64_to_str( 940 iter.block.compressed_file_offset, 2), 941 uint64_to_str( 942 iter.block.uncompressed_file_offset, 3) 943 }; 944 printf(" %*s %*s %*s %*s ", 945 tuklib_mbstr_fw(cols1[0], 946 headings[HEADING_STREAM].columns), 947 cols1[0], 948 tuklib_mbstr_fw(cols1[1], 949 headings[HEADING_BLOCK].columns), 950 cols1[1], 951 tuklib_mbstr_fw(cols1[2], 952 headings[HEADING_COMPOFFSET].columns), 953 cols1[2], 954 tuklib_mbstr_fw(cols1[3], headings[ 955 HEADING_UNCOMPOFFSET].columns), 956 cols1[3]); 957 958 const char *cols2[4] = { 959 uint64_to_str(iter.block.total_size, 0), 960 uint64_to_str(iter.block.uncompressed_size, 961 1), 962 get_ratio(iter.block.total_size, 963 iter.block.uncompressed_size), 964 _(check_names[iter.stream.flags->check]) 965 }; 966 printf("%*s %*s %*s %-*s", 967 tuklib_mbstr_fw(cols2[0], 968 headings[HEADING_TOTALSIZE].columns), 969 cols2[0], 970 tuklib_mbstr_fw(cols2[1], 971 headings[HEADING_UNCOMPSIZE].columns), 972 cols2[1], 973 tuklib_mbstr_fw(cols2[2], 974 headings[HEADING_RATIO].columns), 975 cols2[2], 976 tuklib_mbstr_fw(cols2[3], detailed 977 ? headings[HEADING_CHECK].columns : 1), 978 cols2[3]); 979 980 if (detailed) { 981 const lzma_vli compressed_size 982 = iter.block.unpadded_size 983 - bhi.header_size 984 - lzma_check_size( 985 iter.stream.flags->check); 986 987 const char *cols3[6] = { 988 check_value, 989 uint64_to_str(bhi.header_size, 0), 990 bhi.flags, 991 uint64_to_str(compressed_size, 1), 992 uint64_to_str( 993 round_up_to_mib(bhi.memusage), 994 2), 995 bhi.filter_chain 996 }; 997 // Show MiB for memory usage, because it 998 // is the only size which is not in bytes. 999 printf(" %-*s %*s %-*s %*s %*s MiB %s", 1000 checkval_width, cols3[0], 1001 tuklib_mbstr_fw(cols3[1], headings[ 1002 HEADING_HEADERSIZE].columns), 1003 cols3[1], 1004 tuklib_mbstr_fw(cols3[2], headings[ 1005 HEADING_HEADERFLAGS].columns), 1006 cols3[2], 1007 tuklib_mbstr_fw(cols3[3], headings[ 1008 HEADING_COMPSIZE].columns), 1009 cols3[3], 1010 tuklib_mbstr_fw(cols3[4], headings[ 1011 HEADING_MEMUSAGE].columns - 4), 1012 cols3[4], 1013 cols3[5]); 1014 } 1015 1016 putchar('\n'); 1017 block_header_info_end(&bhi); 1018 } 1019 } 1020 1021 if (detailed) { 1022 printf(" %-*s %s MiB\n", COLON_STR(COLON_STR_MEMORY_NEEDED), 1023 uint64_to_str( 1024 round_up_to_mib(xfi->memusage_max), 0)); 1025 printf(" %-*s %s\n", COLON_STR(COLON_STR_SIZES_IN_HEADERS), 1026 xfi->all_have_sizes ? _("Yes") : _("No")); 1027 //printf(" %-*s %s\n", COLON_STR(COLON_STR_MINIMUM_XZ_VERSION), 1028 printf(_(" Minimum XZ Utils version: %s\n"), 1029 xz_ver_to_str(xfi->min_version)); 1030 } 1031 1032 return false; 1033 } 1034 1035 1036 static bool 1037 print_info_robot(xz_file_info *xfi, file_pair *pair) 1038 { 1039 char checks[CHECKS_STR_SIZE]; 1040 get_check_names(checks, lzma_index_checks(xfi->idx), false); 1041 1042 printf("name\t%s\n", pair->src_name); 1043 1044 printf("file\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 1045 "\t%s\t%s\t%" PRIu64 "\n", 1046 lzma_index_stream_count(xfi->idx), 1047 lzma_index_block_count(xfi->idx), 1048 lzma_index_file_size(xfi->idx), 1049 lzma_index_uncompressed_size(xfi->idx), 1050 get_ratio(lzma_index_file_size(xfi->idx), 1051 lzma_index_uncompressed_size(xfi->idx)), 1052 checks, 1053 xfi->stream_padding); 1054 1055 if (message_verbosity_get() >= V_VERBOSE) { 1056 lzma_index_iter iter; 1057 lzma_index_iter_init(&iter, xfi->idx); 1058 1059 while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)) 1060 printf("stream\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 1061 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 1062 "\t%s\t%s\t%" PRIu64 "\n", 1063 iter.stream.number, 1064 iter.stream.block_count, 1065 iter.stream.compressed_offset, 1066 iter.stream.uncompressed_offset, 1067 iter.stream.compressed_size, 1068 iter.stream.uncompressed_size, 1069 get_ratio(iter.stream.compressed_size, 1070 iter.stream.uncompressed_size), 1071 check_names[iter.stream.flags->check], 1072 iter.stream.padding); 1073 1074 lzma_index_iter_rewind(&iter); 1075 1076 while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)) { 1077 block_header_info bhi = BLOCK_HEADER_INFO_INIT; 1078 if (message_verbosity_get() >= V_DEBUG 1079 && parse_details( 1080 pair, &iter, &bhi, xfi)) 1081 return true; 1082 1083 printf("block\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 1084 "\t%" PRIu64 "\t%" PRIu64 1085 "\t%" PRIu64 "\t%" PRIu64 "\t%s\t%s", 1086 iter.stream.number, 1087 iter.block.number_in_stream, 1088 iter.block.number_in_file, 1089 iter.block.compressed_file_offset, 1090 iter.block.uncompressed_file_offset, 1091 iter.block.total_size, 1092 iter.block.uncompressed_size, 1093 get_ratio(iter.block.total_size, 1094 iter.block.uncompressed_size), 1095 check_names[iter.stream.flags->check]); 1096 1097 if (message_verbosity_get() >= V_DEBUG) 1098 printf("\t%s\t%" PRIu32 "\t%s\t%" PRIu64 1099 "\t%" PRIu64 "\t%s", 1100 check_value, 1101 bhi.header_size, 1102 bhi.flags, 1103 bhi.compressed_size, 1104 bhi.memusage, 1105 bhi.filter_chain); 1106 1107 putchar('\n'); 1108 block_header_info_end(&bhi); 1109 } 1110 } 1111 1112 if (message_verbosity_get() >= V_DEBUG) 1113 printf("summary\t%" PRIu64 "\t%s\t%" PRIu32 "\n", 1114 xfi->memusage_max, 1115 xfi->all_have_sizes ? "yes" : "no", 1116 xfi->min_version); 1117 1118 return false; 1119 } 1120 1121 1122 static void 1123 update_totals(const xz_file_info *xfi) 1124 { 1125 // TODO: Integer overflow checks 1126 ++totals.files; 1127 totals.streams += lzma_index_stream_count(xfi->idx); 1128 totals.blocks += lzma_index_block_count(xfi->idx); 1129 totals.compressed_size += lzma_index_file_size(xfi->idx); 1130 totals.uncompressed_size += lzma_index_uncompressed_size(xfi->idx); 1131 totals.stream_padding += xfi->stream_padding; 1132 totals.checks |= lzma_index_checks(xfi->idx); 1133 1134 if (totals.memusage_max < xfi->memusage_max) 1135 totals.memusage_max = xfi->memusage_max; 1136 1137 if (totals.min_version < xfi->min_version) 1138 totals.min_version = xfi->min_version; 1139 1140 totals.all_have_sizes &= xfi->all_have_sizes; 1141 1142 return; 1143 } 1144 1145 1146 static void 1147 print_totals_basic(void) 1148 { 1149 // Print a separator line. 1150 char line[80]; 1151 memset(line, '-', sizeof(line)); 1152 line[sizeof(line) - 1] = '\0'; 1153 puts(line); 1154 1155 // Get the check names. 1156 char checks[CHECKS_STR_SIZE]; 1157 get_check_names(checks, totals.checks, false); 1158 1159 // Print the totals except the file count, which needs 1160 // special handling. 1161 printf("%5s %7s %11s %11s %5s %-7s ", 1162 uint64_to_str(totals.streams, 0), 1163 uint64_to_str(totals.blocks, 1), 1164 uint64_to_nicestr(totals.compressed_size, 1165 NICESTR_B, NICESTR_TIB, false, 2), 1166 uint64_to_nicestr(totals.uncompressed_size, 1167 NICESTR_B, NICESTR_TIB, false, 3), 1168 get_ratio(totals.compressed_size, 1169 totals.uncompressed_size), 1170 checks); 1171 1172 // Since we print totals only when there are at least two files, 1173 // the English message will always use "%s files". But some other 1174 // languages need different forms for different plurals so we 1175 // have to translate this with ngettext(). 1176 // 1177 // TRANSLATORS: %s is an integer. Only the plural form of this 1178 // message is used (e.g. "2 files"). Test with "xz -l foo.xz bar.xz". 1179 printf(ngettext("%s file\n", "%s files\n", 1180 totals.files <= ULONG_MAX ? totals.files 1181 : (totals.files % 1000000) + 1000000), 1182 uint64_to_str(totals.files, 0)); 1183 1184 return; 1185 } 1186 1187 1188 static void 1189 print_totals_adv(void) 1190 { 1191 putchar('\n'); 1192 puts(_("Totals:")); 1193 printf(" %-*s %s\n", COLON_STR(COLON_STR_NUMBER_OF_FILES), 1194 uint64_to_str(totals.files, 0)); 1195 print_adv_helper(totals.streams, totals.blocks, 1196 totals.compressed_size, totals.uncompressed_size, 1197 totals.checks, totals.stream_padding); 1198 1199 if (message_verbosity_get() >= V_DEBUG) { 1200 printf(" %-*s %s MiB\n", COLON_STR(COLON_STR_MEMORY_NEEDED), 1201 uint64_to_str( 1202 round_up_to_mib(totals.memusage_max), 0)); 1203 printf(" %-*s %s\n", COLON_STR(COLON_STR_SIZES_IN_HEADERS), 1204 totals.all_have_sizes ? _("Yes") : _("No")); 1205 //printf(" %-*s %s\n", COLON_STR(COLON_STR_MINIMUM_XZ_VERSION), 1206 printf(_(" Minimum XZ Utils version: %s\n"), 1207 xz_ver_to_str(totals.min_version)); 1208 } 1209 1210 return; 1211 } 1212 1213 1214 static void 1215 print_totals_robot(void) 1216 { 1217 char checks[CHECKS_STR_SIZE]; 1218 get_check_names(checks, totals.checks, false); 1219 1220 printf("totals\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 1221 "\t%s\t%s\t%" PRIu64 "\t%" PRIu64, 1222 totals.streams, 1223 totals.blocks, 1224 totals.compressed_size, 1225 totals.uncompressed_size, 1226 get_ratio(totals.compressed_size, 1227 totals.uncompressed_size), 1228 checks, 1229 totals.stream_padding, 1230 totals.files); 1231 1232 if (message_verbosity_get() >= V_DEBUG) 1233 printf("\t%" PRIu64 "\t%s\t%" PRIu32, 1234 totals.memusage_max, 1235 totals.all_have_sizes ? "yes" : "no", 1236 totals.min_version); 1237 1238 putchar('\n'); 1239 1240 return; 1241 } 1242 1243 1244 extern void 1245 list_totals(void) 1246 { 1247 if (opt_robot) { 1248 // Always print totals in --robot mode. It can be convenient 1249 // in some cases and doesn't complicate usage of the 1250 // single-file case much. 1251 print_totals_robot(); 1252 1253 } else if (totals.files > 1) { 1254 // For non-robot mode, totals are printed only if there 1255 // is more than one file. 1256 if (message_verbosity_get() <= V_WARNING) 1257 print_totals_basic(); 1258 else 1259 print_totals_adv(); 1260 } 1261 1262 return; 1263 } 1264 1265 1266 extern void 1267 list_file(const char *filename) 1268 { 1269 if (opt_format != FORMAT_XZ && opt_format != FORMAT_AUTO) 1270 message_fatal(_("--list works only on .xz files " 1271 "(--format=xz or --format=auto)")); 1272 1273 message_filename(filename); 1274 1275 if (filename == stdin_filename) { 1276 message_error(_("--list does not support reading from " 1277 "standard input")); 1278 return; 1279 } 1280 1281 init_field_widths(); 1282 1283 // Unset opt_stdout so that io_open_src() won't accept special files. 1284 // Set opt_force so that io_open_src() will follow symlinks. 1285 opt_stdout = false; 1286 opt_force = true; 1287 file_pair *pair = io_open_src(filename); 1288 if (pair == NULL) 1289 return; 1290 1291 xz_file_info xfi = XZ_FILE_INFO_INIT; 1292 if (!parse_indexes(&xfi, pair)) { 1293 bool fail; 1294 1295 // We have three main modes: 1296 // - --robot, which has submodes if --verbose is specified 1297 // once or twice 1298 // - Normal --list without --verbose 1299 // - --list with one or two --verbose 1300 if (opt_robot) 1301 fail = print_info_robot(&xfi, pair); 1302 else if (message_verbosity_get() <= V_WARNING) 1303 fail = print_info_basic(&xfi, pair); 1304 else 1305 fail = print_info_adv(&xfi, pair); 1306 1307 // Update the totals that are displayed after all 1308 // the individual files have been listed. Don't count 1309 // broken files. 1310 if (!fail) 1311 update_totals(&xfi); 1312 1313 lzma_index_end(xfi.idx, NULL); 1314 } 1315 1316 io_close(pair, false); 1317 return; 1318 } 1319