xref: /minix/external/public-domain/xz/dist/src/xz/list.c (revision 5a645f22)
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