Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(417)

Side by Side Diff: xz/src/xz/list.c

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

Powered by Google App Engine
This is Rietveld 408576698