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