]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - contrib/xz/src/xz/list.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / contrib / xz / src / xz / list.c
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 /// Totals that are displayed if there was more than one file.
18 /// The "files" counter is also used in print_info_adv() to show
19 /// the file number.
20 static struct {
21         uint64_t files;
22         uint64_t streams;
23         uint64_t blocks;
24         uint64_t compressed_size;
25         uint64_t uncompressed_size;
26         uint32_t checks;
27 } totals = { 0, 0, 0, 0, 0, 0 };
28
29
30 /// \brief      Parse the Index(es) from the given .xz file
31 ///
32 /// \param      idx     If decoding is successful, *idx will be set to point
33 ///                     to lzma_index containing the decoded information.
34 ///                     On error, *idx is not modified.
35 /// \param      pair    Input file
36 ///
37 /// \return     On success, false is returned. On error, true is returned.
38 ///
39 // TODO: This function is pretty big. liblzma should have a function that
40 // takes a callback function to parse the Index(es) from a .xz file to make
41 // it easy for applications.
42 static bool
43 parse_indexes(lzma_index **idx, file_pair *pair)
44 {
45         if (pair->src_st.st_size <= 0) {
46                 message_error(_("%s: File is empty"), pair->src_name);
47                 return true;
48         }
49
50         if (pair->src_st.st_size < 2 * LZMA_STREAM_HEADER_SIZE) {
51                 message_error(_("%s: Too small to be a valid .xz file"),
52                                 pair->src_name);
53                 return true;
54         }
55
56         io_buf buf;
57         lzma_stream_flags header_flags;
58         lzma_stream_flags footer_flags;
59         lzma_ret ret;
60
61         // lzma_stream for the Index decoder
62         lzma_stream strm = LZMA_STREAM_INIT;
63
64         // All Indexes decoded so far
65         lzma_index *combined_index = NULL;
66
67         // The Index currently being decoded
68         lzma_index *this_index = NULL;
69
70         // Current position in the file. We parse the file backwards so
71         // initialize it to point to the end of the file.
72         off_t pos = pair->src_st.st_size;
73
74         // Each loop iteration decodes one Index.
75         do {
76                 // Check that there is enough data left to contain at least
77                 // the Stream Header and Stream Footer. This check cannot
78                 // fail in the first pass of this loop.
79                 if (pos < 2 * LZMA_STREAM_HEADER_SIZE) {
80                         message_error("%s: %s", pair->src_name,
81                                         message_strm(LZMA_DATA_ERROR));
82                         goto error;
83                 }
84
85                 pos -= LZMA_STREAM_HEADER_SIZE;
86                 lzma_vli stream_padding = 0;
87
88                 // Locate the Stream Footer. There may be Stream Padding which
89                 // we must skip when reading backwards.
90                 while (true) {
91                         if (pos < LZMA_STREAM_HEADER_SIZE) {
92                                 message_error("%s: %s", pair->src_name,
93                                                 message_strm(
94                                                         LZMA_DATA_ERROR));
95                                 goto error;
96                         }
97
98                         if (io_pread(pair, &buf,
99                                         LZMA_STREAM_HEADER_SIZE, pos))
100                                 goto error;
101
102                         // Stream Padding is always a multiple of four bytes.
103                         int i = 2;
104                         if (buf.u32[i] != 0)
105                                 break;
106
107                         // To avoid calling io_pread() for every four bytes
108                         // of Stream Padding, take advantage that we read
109                         // 12 bytes (LZMA_STREAM_HEADER_SIZE) already and
110                         // check them too before calling io_pread() again.
111                         do {
112                                 stream_padding += 4;
113                                 pos -= 4;
114                                 --i;
115                         } while (i >= 0 && buf.u32[i] == 0);
116                 }
117
118                 // Decode the Stream Footer.
119                 ret = lzma_stream_footer_decode(&footer_flags, buf.u8);
120                 if (ret != LZMA_OK) {
121                         message_error("%s: %s", pair->src_name,
122                                         message_strm(ret));
123                         goto error;
124                 }
125
126                 // Check that the size of the Index field looks sane.
127                 lzma_vli index_size = footer_flags.backward_size;
128                 if ((lzma_vli)(pos) < index_size + LZMA_STREAM_HEADER_SIZE) {
129                         message_error("%s: %s", pair->src_name,
130                                         message_strm(LZMA_DATA_ERROR));
131                         goto error;
132                 }
133
134                 // Set pos to the beginning of the Index.
135                 pos -= index_size;
136
137                 // See how much memory we can use for decoding this Index.
138                 uint64_t memlimit = hardware_memlimit_get();
139                 uint64_t memused = 0;
140                 if (combined_index != NULL) {
141                         memused = lzma_index_memused(combined_index);
142                         if (memused > memlimit)
143                                 message_bug();
144
145                         memlimit -= memused;
146                 }
147
148                 // Decode the Index.
149                 ret = lzma_index_decoder(&strm, &this_index, memlimit);
150                 if (ret != LZMA_OK) {
151                         message_error("%s: %s", pair->src_name,
152                                         message_strm(ret));
153                         goto error;
154                 }
155
156                 do {
157                         // Don't give the decoder more input than the
158                         // Index size.
159                         strm.avail_in = MIN(IO_BUFFER_SIZE, index_size);
160                         if (io_pread(pair, &buf, strm.avail_in, pos))
161                                 goto error;
162
163                         pos += strm.avail_in;
164                         index_size -= strm.avail_in;
165
166                         strm.next_in = buf.u8;
167                         ret = lzma_code(&strm, LZMA_RUN);
168
169                 } while (ret == LZMA_OK);
170
171                 // If the decoding seems to be successful, check also that
172                 // the Index decoder consumed as much input as indicated
173                 // by the Backward Size field.
174                 if (ret == LZMA_STREAM_END)
175                         if (index_size != 0 || strm.avail_in != 0)
176                                 ret = LZMA_DATA_ERROR;
177
178                 if (ret != LZMA_STREAM_END) {
179                         // LZMA_BUFFER_ERROR means that the Index decoder
180                         // would have liked more input than what the Index
181                         // size should be according to Stream Footer.
182                         // The message for LZMA_DATA_ERROR makes more
183                         // sense in that case.
184                         if (ret == LZMA_BUF_ERROR)
185                                 ret = LZMA_DATA_ERROR;
186
187                         message_error("%s: %s", pair->src_name,
188                                         message_strm(ret));
189
190                         // If the error was too low memory usage limit,
191                         // show also how much memory would have been needed.
192                         if (ret == LZMA_MEMLIMIT_ERROR) {
193                                 uint64_t needed = lzma_memusage(&strm);
194                                 if (UINT64_MAX - needed < memused)
195                                         needed = UINT64_MAX;
196                                 else
197                                         needed += memused;
198
199                                 message_mem_needed(V_ERROR, needed);
200                         }
201
202                         goto error;
203                 }
204
205                 // Decode the Stream Header and check that its Stream Flags
206                 // match the Stream Footer.
207                 pos -= footer_flags.backward_size + LZMA_STREAM_HEADER_SIZE;
208                 if ((lzma_vli)(pos) < lzma_index_total_size(this_index)) {
209                         message_error("%s: %s", pair->src_name,
210                                         message_strm(LZMA_DATA_ERROR));
211                         goto error;
212                 }
213
214                 pos -= lzma_index_total_size(this_index);
215                 if (io_pread(pair, &buf, LZMA_STREAM_HEADER_SIZE, pos))
216                         goto error;
217
218                 ret = lzma_stream_header_decode(&header_flags, buf.u8);
219                 if (ret != LZMA_OK) {
220                         message_error("%s: %s", pair->src_name,
221                                         message_strm(ret));
222                         goto error;
223                 }
224
225                 ret = lzma_stream_flags_compare(&header_flags, &footer_flags);
226                 if (ret != LZMA_OK) {
227                         message_error("%s: %s", pair->src_name,
228                                         message_strm(ret));
229                         goto error;
230                 }
231
232                 // Store the decoded Stream Flags into this_index. This is
233                 // needed so that we can print which Check is used in each
234                 // Stream.
235                 ret = lzma_index_stream_flags(this_index, &footer_flags);
236                 if (ret != LZMA_OK)
237                         message_bug();
238
239                 // Store also the size of the Stream Padding field. It is
240                 // needed to show the offsets of the Streams correctly.
241                 ret = lzma_index_stream_padding(this_index, stream_padding);
242                 if (ret != LZMA_OK)
243                         message_bug();
244
245                 if (combined_index != NULL) {
246                         // Append the earlier decoded Indexes
247                         // after this_index.
248                         ret = lzma_index_cat(
249                                         this_index, combined_index, NULL);
250                         if (ret != LZMA_OK) {
251                                 message_error("%s: %s", pair->src_name,
252                                                 message_strm(ret));
253                                 goto error;
254                         }
255                 }
256
257                 combined_index = this_index;
258                 this_index = NULL;
259
260         } while (pos > 0);
261
262         lzma_end(&strm);
263
264         // All OK. Make combined_index available to the caller.
265         *idx = combined_index;
266         return false;
267
268 error:
269         // Something went wrong, free the allocated memory.
270         lzma_end(&strm);
271         lzma_index_end(combined_index, NULL);
272         lzma_index_end(this_index, NULL);
273         return true;
274 }
275
276
277 /// \brief      Get the compression ratio
278 ///
279 /// This has slightly different format than that is used by in message.c.
280 static const char *
281 get_ratio(uint64_t compressed_size, uint64_t uncompressed_size)
282 {
283         if (uncompressed_size == 0)
284                 return "---";
285
286         const double ratio = (double)(compressed_size)
287                         / (double)(uncompressed_size);
288         if (ratio > 9.999)
289                 return "---";
290
291         static char buf[6];
292         snprintf(buf, sizeof(buf), "%.3f", ratio);
293         return buf;
294 }
295
296
297 static const char check_names[LZMA_CHECK_ID_MAX + 1][12] = {
298         "None",
299         "CRC32",
300         "Unknown-2",
301         "Unknown-3",
302         "CRC64",
303         "Unknown-5",
304         "Unknown-6",
305         "Unknown-7",
306         "Unknown-8",
307         "Unknown-9",
308         "SHA-256",
309         "Unknown-11",
310         "Unknown-12",
311         "Unknown-13",
312         "Unknown-14",
313         "Unknown-15",
314 };
315
316
317 /// \brief      Get a comma-separated list of Check names
318 ///
319 /// \param      checks  Bit mask of Checks to print
320 /// \param      space_after_comma
321 ///                     It's better to not use spaces in table-like listings,
322 ///                     but in more verbose formats a space after a comma
323 ///                     is good for readability.
324 static const char *
325 get_check_names(uint32_t checks, bool space_after_comma)
326 {
327         assert(checks != 0);
328
329         static char buf[sizeof(check_names)];
330         char *pos = buf;
331         size_t left = sizeof(buf);
332
333         const char *sep = space_after_comma ? ", " : ",";
334         bool comma = false;
335
336         for (size_t i = 0; i <= LZMA_CHECK_ID_MAX; ++i) {
337                 if (checks & (UINT32_C(1) << i)) {
338                         my_snprintf(&pos, &left, "%s%s",
339                                         comma ? sep : "", check_names[i]);
340                         comma = true;
341                 }
342         }
343
344         return buf;
345 }
346
347
348 /// \brief      Read the Check value from the .xz file and print it
349 ///
350 /// Since this requires a seek, listing all Check values for all Blocks can
351 /// be slow.
352 ///
353 /// \param      pair    Input file
354 /// \param      iter    Location of the Block whose Check value should
355 ///                     be printed.
356 ///
357 /// \return     False on success, true on I/O error.
358 static bool
359 print_check_value(file_pair *pair, const lzma_index_iter *iter)
360 {
361         // Don't read anything from the file if there is no integrity Check.
362         if (iter->stream.flags->check == LZMA_CHECK_NONE) {
363                 printf("---");
364                 return false;
365         }
366
367         // Locate and read the Check field.
368         const uint32_t size = lzma_check_size(iter->stream.flags->check);
369         const off_t offset = iter->block.compressed_file_offset
370                         + iter->block.total_size - size;
371         io_buf buf;
372         if (io_pread(pair, &buf, size, offset))
373                 return true;
374
375         // CRC32 and CRC64 are in little endian. Guess that all the future
376         // 32-bit and 64-bit Check values are little endian too. It shouldn't
377         // be a too big problem if this guess is wrong.
378         if (size == 4) {
379                 printf("%08" PRIx32, conv32le(buf.u32[0]));
380         } else if (size == 8) {
381                 printf("%016" PRIx64, conv64le(buf.u64[0]));
382         } else {
383                 for (size_t i = 0; i < size; ++i)
384                         printf("%02x", buf.u8[i]);
385         }
386
387         return false;
388 }
389
390
391 static void
392 print_info_basic(const lzma_index *idx, file_pair *pair)
393 {
394         static bool headings_displayed = false;
395         if (!headings_displayed) {
396                 headings_displayed = true;
397                 // TRANSLATORS: These are column titles. From Strms (Streams)
398                 // to Ratio, the columns are right aligned. Check and Filename
399                 // are left aligned. If you need longer words, it's OK to
400                 // use two lines here. Test with xz --list.
401                 puts(_("Strms  Blocks   Compressed Uncompressed  Ratio  "
402                                 "Check   Filename"));
403         }
404
405         printf("%5s %7s  %11s  %11s  %5s  %-7s %s\n",
406                         uint64_to_str(lzma_index_stream_count(idx), 0),
407                         uint64_to_str(lzma_index_block_count(idx), 1),
408                         uint64_to_nicestr(lzma_index_file_size(idx),
409                                 NICESTR_B, NICESTR_TIB, false, 2),
410                         uint64_to_nicestr(lzma_index_uncompressed_size(idx),
411                                 NICESTR_B, NICESTR_TIB, false, 3),
412                         get_ratio(lzma_index_file_size(idx),
413                                 lzma_index_uncompressed_size(idx)),
414                         get_check_names(lzma_index_checks(idx), false),
415                         pair->src_name);
416
417         return;
418 }
419
420
421 static void
422 print_adv_helper(uint64_t stream_count, uint64_t block_count,
423                 uint64_t compressed_size, uint64_t uncompressed_size,
424                 uint32_t checks)
425 {
426         printf(_("  Stream count:       %s\n"),
427                         uint64_to_str(stream_count, 0));
428         printf(_("  Block count:        %s\n"),
429                         uint64_to_str(block_count, 0));
430         printf(_("  Compressed size:    %s\n"),
431                         uint64_to_nicestr(compressed_size,
432                                 NICESTR_B, NICESTR_TIB, true, 0));
433         printf(_("  Uncompressed size:  %s\n"),
434                         uint64_to_nicestr(uncompressed_size,
435                                 NICESTR_B, NICESTR_TIB, true, 0));
436         printf(_("  Ratio:              %s\n"),
437                         get_ratio(compressed_size, uncompressed_size));
438         printf(_("  Check:              %s\n"),
439                         get_check_names(checks, true));
440         return;
441 }
442
443
444 static void
445 print_info_adv(const lzma_index *idx, file_pair *pair)
446 {
447         // Print the overall information.
448         print_adv_helper(lzma_index_stream_count(idx),
449                         lzma_index_block_count(idx),
450                         lzma_index_file_size(idx),
451                         lzma_index_uncompressed_size(idx),
452                         lzma_index_checks(idx));
453
454         // TODO: The rest of this function needs some work. Currently
455         // the offsets are not printed, which could be useful even when
456         // printed in a less accurate format. On the other hand, maybe
457         // this should print the information with exact byte values,
458         // or maybe there should be at least an option to do that.
459         //
460         // We could also display some other info. E.g. it could be useful
461         // to quickly see how big is the biggest Block (uncompressed size)
462         // and if all Blocks have Compressed Size and Uncompressed Size
463         // fields present, which can be used e.g. for multithreaded
464         // decompression.
465
466         // Avoid printing Stream and Block lists when they wouldn't be useful.
467         bool show_blocks = false;
468         if (lzma_index_stream_count(idx) > 1) {
469                 puts(_("  Streams:"));
470                 puts(_("      Number      Blocks    Compressed   "
471                                 "Uncompressed   Ratio   Check"));
472
473                 lzma_index_iter iter;
474                 lzma_index_iter_init(&iter, idx);
475                 while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)) {
476                         if (iter.stream.block_count > 1)
477                                 show_blocks = true;
478
479                         printf("    %8s  %10s   %11s    %11s   %5s   %s\n",
480                                 uint64_to_str(iter.stream.number, 0),
481                                 uint64_to_str(iter.stream.block_count, 1),
482                                 uint64_to_nicestr(
483                                         iter.stream.compressed_size,
484                                         NICESTR_B, NICESTR_TIB, false, 2),
485                                 uint64_to_nicestr(
486                                         iter.stream.uncompressed_size,
487                                         NICESTR_B, NICESTR_TIB, false, 3),
488                                 get_ratio(iter.stream.compressed_size,
489                                         iter.stream.uncompressed_size),
490                                 check_names[iter.stream.flags->check]);
491                 }
492         }
493
494         if (show_blocks || lzma_index_block_count(idx)
495                                 > lzma_index_stream_count(idx)
496                         || message_verbosity_get() >= V_DEBUG) {
497                 puts(_("  Blocks:"));
498                 // FIXME: Number in Stream/file, which one is better?
499                 puts(_("      Stream      Number    Compressed   "
500                                 "Uncompressed   Ratio   Check"));
501
502                 lzma_index_iter iter;
503                 lzma_index_iter_init(&iter, idx);
504                 while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)) {
505                         printf("    %8s  %10s   %11s    %11s   %5s   %-7s",
506                                 uint64_to_str(iter.stream.number, 0),
507                                 uint64_to_str(iter.block.number_in_stream, 1),
508                                 uint64_to_nicestr(iter.block.total_size,
509                                         NICESTR_B, NICESTR_TIB, false, 2),
510                                 uint64_to_nicestr(
511                                         iter.block.uncompressed_size,
512                                         NICESTR_B, NICESTR_TIB, false, 3),
513                                 get_ratio(iter.block.total_size,
514                                         iter.block.uncompressed_size),
515                                 check_names[iter.stream.flags->check]);
516
517                         if (message_verbosity_get() >= V_DEBUG)
518                                 if (print_check_value(pair, &iter))
519                                         return;
520
521                         putchar('\n');
522                 }
523         }
524 }
525
526
527 static void
528 print_info_robot(const lzma_index *idx, file_pair *pair)
529 {
530         printf("file\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64
531                         "\t%s\t%s\t%s\n",
532                         lzma_index_stream_count(idx),
533                         lzma_index_block_count(idx),
534                         lzma_index_file_size(idx),
535                         lzma_index_uncompressed_size(idx),
536                         get_ratio(lzma_index_file_size(idx),
537                                 lzma_index_uncompressed_size(idx)),
538                         get_check_names(lzma_index_checks(idx), false),
539                         pair->src_name);
540
541         if (message_verbosity_get() >= V_VERBOSE) {
542                 lzma_index_iter iter;
543                 lzma_index_iter_init(&iter, idx);
544
545                 while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM))
546                         printf("stream\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64
547                                 "\t%" PRIu64 "\t%" PRIu64
548                                 "\t%s\t%" PRIu64 "\t%s\n",
549                                 iter.stream.number,
550                                 iter.stream.compressed_offset,
551                                 iter.stream.uncompressed_offset,
552                                 iter.stream.compressed_size,
553                                 iter.stream.uncompressed_size,
554                                 get_ratio(iter.stream.compressed_size,
555                                         iter.stream.uncompressed_size),
556                                 iter.stream.padding,
557                                 check_names[iter.stream.flags->check]);
558
559                 lzma_index_iter_rewind(&iter);
560                 while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)) {
561                         printf("block\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64
562                                         "\t%" PRIu64 "\t%" PRIu64
563                                         "\t%" PRIu64 "\t%" PRIu64 "\t%s\t%s",
564                                         iter.stream.number,
565                                         iter.block.number_in_stream,
566                                         iter.block.number_in_file,
567                                         iter.block.compressed_file_offset,
568                                         iter.block.uncompressed_file_offset,
569                                         iter.block.total_size,
570                                         iter.block.uncompressed_size,
571                                         get_ratio(iter.block.total_size,
572                                                 iter.block.uncompressed_size),
573                                         check_names[iter.stream.flags->check]);
574
575                         if (message_verbosity_get() >= V_DEBUG) {
576                                 putchar('\t');
577                                 if (print_check_value(pair, &iter))
578                                         return;
579                         }
580
581                         putchar('\n');
582                 }
583         }
584
585         return;
586 }
587
588
589 static void
590 update_totals(const lzma_index *idx)
591 {
592         // TODO: Integer overflow checks
593         ++totals.files;
594         totals.streams += lzma_index_stream_count(idx);
595         totals.blocks += lzma_index_block_count(idx);
596         totals.compressed_size += lzma_index_file_size(idx);
597         totals.uncompressed_size += lzma_index_uncompressed_size(idx);
598         totals.checks |= lzma_index_checks(idx);
599         return;
600 }
601
602
603 static void
604 print_totals_basic(void)
605 {
606         // Print a separator line.
607         char line[80];
608         memset(line, '-', sizeof(line));
609         line[sizeof(line) - 1] = '\0';
610         puts(line);
611
612         // Print the totals except the file count, which needs
613         // special handling.
614         printf("%5s %7s  %11s  %11s  %5s  %-7s ",
615                         uint64_to_str(totals.streams, 0),
616                         uint64_to_str(totals.blocks, 1),
617                         uint64_to_nicestr(totals.compressed_size,
618                                 NICESTR_B, NICESTR_TIB, false, 2),
619                         uint64_to_nicestr(totals.uncompressed_size,
620                                 NICESTR_B, NICESTR_TIB, false, 3),
621                         get_ratio(totals.compressed_size,
622                                 totals.uncompressed_size),
623                         get_check_names(totals.checks, false));
624
625         // Since we print totals only when there are at least two files,
626         // the English message will always use "%s files". But some other
627         // languages need different forms for different plurals so we
628         // have to translate this string still.
629         //
630         // TRANSLATORS: This simply indicates the number of files shown
631         // by --list even though the format string uses %s.
632         printf(N_("%s file", "%s files\n",
633                         totals.files <= ULONG_MAX ? totals.files
634                                 : (totals.files % 1000000) + 1000000),
635                         uint64_to_str(totals.files, 0));
636
637         return;
638 }
639
640
641 static void
642 print_totals_adv(void)
643 {
644         putchar('\n');
645         puts(_("Totals:"));
646         printf(_("  Number of files:    %s\n"),
647                         uint64_to_str(totals.files, 0));
648         print_adv_helper(totals.streams, totals.blocks,
649                         totals.compressed_size, totals.uncompressed_size,
650                         totals.checks);
651
652         return;
653 }
654
655
656 static void
657 print_totals_robot(void)
658 {
659         printf("totals\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64
660                         "\t%s\t%s\t%" PRIu64 "\n",
661                         totals.streams,
662                         totals.blocks,
663                         totals.compressed_size,
664                         totals.uncompressed_size,
665                         get_ratio(totals.compressed_size,
666                                 totals.uncompressed_size),
667                         get_check_names(totals.checks, false),
668                         totals.files);
669
670         return;
671 }
672
673
674 extern void
675 list_totals(void)
676 {
677         if (opt_robot) {
678                 // Always print totals in --robot mode. It can be convenient
679                 // in some cases and doesn't complicate usage of the
680                 // single-file case much.
681                 print_totals_robot();
682
683         } else if (totals.files > 1) {
684                 // For non-robot mode, totals are printed only if there
685                 // is more than one file.
686                 if (message_verbosity_get() <= V_WARNING)
687                         print_totals_basic();
688                 else
689                         print_totals_adv();
690         }
691
692         return;
693 }
694
695
696 extern void
697 list_file(const char *filename)
698 {
699         if (opt_format != FORMAT_XZ && opt_format != FORMAT_AUTO)
700                 message_fatal(_("--list works only on .xz files "
701                                 "(--format=xz or --format=auto)"));
702
703         message_filename(filename);
704
705         if (filename == stdin_filename) {
706                 message_error(_("--list does not support reading from "
707                                 "standard input"));
708                 return;
709         }
710
711         // Unset opt_stdout so that io_open_src() won't accept special files.
712         // Set opt_force so that io_open_src() will follow symlinks.
713         opt_stdout = false;
714         opt_force = true;
715         file_pair *pair = io_open_src(filename);
716         if (pair == NULL)
717                 return;
718
719         lzma_index *idx;
720         if (!parse_indexes(&idx, pair)) {
721                 // Update the totals that are displayed after all
722                 // the individual files have been listed.
723                 update_totals(idx);
724
725                 // We have three main modes:
726                 //  - --robot, which has submodes if --verbose is specified
727                 //    once or twice
728                 //  - Normal --list without --verbose
729                 //  - --list with one or two --verbose
730                 if (opt_robot)
731                         print_info_robot(idx, pair);
732                 else if (message_verbosity_get() <= V_WARNING)
733                         print_info_basic(idx, pair);
734                 else
735                         print_info_adv(idx, pair);
736
737                 lzma_index_end(idx, NULL);
738         }
739
740         io_close(pair, false);
741         return;
742 }