| Index: src/xz/list.c
 | 
| ===================================================================
 | 
| --- src/xz/list.c	(revision 50504)
 | 
| +++ src/xz/list.c	(working copy)
 | 
| @@ -49,32 +49,44 @@
 | 
|  	uint64_t memusage;
 | 
|  
 | 
|  	/// The filter chain of this Block in human-readable form
 | 
| -	const char *filter_chain;
 | 
| +	char filter_chain[FILTERS_STR_SIZE];
 | 
|  
 | 
|  } block_header_info;
 | 
|  
 | 
|  
 | 
|  /// Check ID to string mapping
 | 
|  static const char check_names[LZMA_CHECK_ID_MAX + 1][12] = {
 | 
| -	"None",
 | 
| +	// TRANSLATORS: Indicates that there is no integrity check.
 | 
| +	// This string is used in tables, so the width must not
 | 
| +	// exceed ten columns with a fixed-width font.
 | 
| +	N_("None"),
 | 
|  	"CRC32",
 | 
| -	"Unknown-2",
 | 
| -	"Unknown-3",
 | 
| +	// TRANSLATORS: Indicates that integrity check name is not known,
 | 
| +	// but the Check ID is known (here 2). This and other "Unknown-N"
 | 
| +	// strings are used in tables, so the width must not exceed ten
 | 
| +	// columns with a fixed-width font. It's OK to omit the dash if
 | 
| +	// you need space for one extra letter, but don't use spaces.
 | 
| +	N_("Unknown-2"),
 | 
| +	N_("Unknown-3"),
 | 
|  	"CRC64",
 | 
| -	"Unknown-5",
 | 
| -	"Unknown-6",
 | 
| -	"Unknown-7",
 | 
| -	"Unknown-8",
 | 
| -	"Unknown-9",
 | 
| +	N_("Unknown-5"),
 | 
| +	N_("Unknown-6"),
 | 
| +	N_("Unknown-7"),
 | 
| +	N_("Unknown-8"),
 | 
| +	N_("Unknown-9"),
 | 
|  	"SHA-256",
 | 
| -	"Unknown-11",
 | 
| -	"Unknown-12",
 | 
| -	"Unknown-13",
 | 
| -	"Unknown-14",
 | 
| -	"Unknown-15",
 | 
| +	N_("Unknown-11"),
 | 
| +	N_("Unknown-12"),
 | 
| +	N_("Unknown-13"),
 | 
| +	N_("Unknown-14"),
 | 
| +	N_("Unknown-15"),
 | 
|  };
 | 
|  
 | 
| +/// Buffer size for get_check_names(). This may be a bit ridiculous,
 | 
| +/// but at least it's enough if some language needs many multibyte chars.
 | 
| +#define CHECKS_STR_SIZE 1024
 | 
|  
 | 
| +
 | 
|  /// Value of the Check field as hexadecimal string.
 | 
|  /// This is set by parse_check_value().
 | 
|  static char check_value[2 * LZMA_CHECK_SIZE_MAX + 1];
 | 
| @@ -203,7 +215,7 @@
 | 
|  		pos -= index_size;
 | 
|  
 | 
|  		// See how much memory we can use for decoding this Index.
 | 
| -		uint64_t memlimit = hardware_memlimit_get();
 | 
| +		uint64_t memlimit = hardware_memlimit_get(MODE_LIST);
 | 
|  		uint64_t memused = 0;
 | 
|  		if (combined_index != NULL) {
 | 
|  			memused = lzma_index_memused(combined_index);
 | 
| @@ -442,7 +454,7 @@
 | 
|  		xfi->memusage_max = bhi->memusage;
 | 
|  
 | 
|  	// Convert the filter chain to human readable form.
 | 
| -	bhi->filter_chain = message_filters_to_str(filters, false);
 | 
| +	message_filters_to_str(bhi->filter_chain, filters, false);
 | 
|  
 | 
|  	// Free the memory allocated by lzma_block_header_decode().
 | 
|  	for (size_t i = 0; filters[i].id != LZMA_VLI_UNKNOWN; ++i)
 | 
| @@ -533,7 +545,7 @@
 | 
|  
 | 
|  /// \brief      Get the compression ratio
 | 
|  ///
 | 
| -/// This has slightly different format than that is used by in message.c.
 | 
| +/// This has slightly different format than that is used in message.c.
 | 
|  static const char *
 | 
|  get_ratio(uint64_t compressed_size, uint64_t uncompressed_size)
 | 
|  {
 | 
| @@ -545,7 +557,7 @@
 | 
|  	if (ratio > 9.999)
 | 
|  		return "---";
 | 
|  
 | 
| -	static char buf[6];
 | 
| +	static char buf[16];
 | 
|  	snprintf(buf, sizeof(buf), "%.3f", ratio);
 | 
|  	return buf;
 | 
|  }
 | 
| @@ -553,19 +565,22 @@
 | 
|  
 | 
|  /// \brief      Get a comma-separated list of Check names
 | 
|  ///
 | 
| +/// The check names are translated with gettext except when in robot mode.
 | 
| +///
 | 
| +/// \param      buf     Buffer to hold the resulting string
 | 
|  /// \param      checks  Bit mask of Checks to print
 | 
|  /// \param      space_after_comma
 | 
|  ///                     It's better to not use spaces in table-like listings,
 | 
|  ///                     but in more verbose formats a space after a comma
 | 
|  ///                     is good for readability.
 | 
| -static const char *
 | 
| -get_check_names(uint32_t checks, bool space_after_comma)
 | 
| +static void
 | 
| +get_check_names(char buf[CHECKS_STR_SIZE],
 | 
| +		uint32_t checks, bool space_after_comma)
 | 
|  {
 | 
|  	assert(checks != 0);
 | 
|  
 | 
| -	static char buf[sizeof(check_names)];
 | 
|  	char *pos = buf;
 | 
| -	size_t left = sizeof(buf);
 | 
| +	size_t left = CHECKS_STR_SIZE;
 | 
|  
 | 
|  	const char *sep = space_after_comma ? ", " : ",";
 | 
|  	bool comma = false;
 | 
| @@ -573,12 +588,14 @@
 | 
|  	for (size_t i = 0; i <= LZMA_CHECK_ID_MAX; ++i) {
 | 
|  		if (checks & (UINT32_C(1) << i)) {
 | 
|  			my_snprintf(&pos, &left, "%s%s",
 | 
| -					comma ? sep : "", check_names[i]);
 | 
| +					comma ? sep : "",
 | 
| +					opt_robot ? check_names[i]
 | 
| +						: _(check_names[i]));
 | 
|  			comma = true;
 | 
|  		}
 | 
|  	}
 | 
|  
 | 
| -	return buf;
 | 
| +	return;
 | 
|  }
 | 
|  
 | 
|  
 | 
| @@ -588,27 +605,38 @@
 | 
|  	static bool headings_displayed = false;
 | 
|  	if (!headings_displayed) {
 | 
|  		headings_displayed = true;
 | 
| -		// TRANSLATORS: These are column titles. From Strms (Streams)
 | 
| +		// TRANSLATORS: These are column headings. From Strms (Streams)
 | 
|  		// to Ratio, the columns are right aligned. Check and Filename
 | 
|  		// are left aligned. If you need longer words, it's OK to
 | 
| -		// use two lines here. Test with xz --list.
 | 
| +		// use two lines here. Test with "xz -l foo.xz".
 | 
|  		puts(_("Strms  Blocks   Compressed Uncompressed  Ratio  "
 | 
|  				"Check   Filename"));
 | 
|  	}
 | 
|  
 | 
| -	printf("%5s %7s  %11s  %11s  %5s  %-7s %s\n",
 | 
| -			uint64_to_str(lzma_index_stream_count(xfi->idx), 0),
 | 
| -			uint64_to_str(lzma_index_block_count(xfi->idx), 1),
 | 
| -			uint64_to_nicestr(lzma_index_file_size(xfi->idx),
 | 
| -				NICESTR_B, NICESTR_TIB, false, 2),
 | 
| -			uint64_to_nicestr(
 | 
| -				lzma_index_uncompressed_size(xfi->idx),
 | 
| -				NICESTR_B, NICESTR_TIB, false, 3),
 | 
| -			get_ratio(lzma_index_file_size(xfi->idx),
 | 
| -				lzma_index_uncompressed_size(xfi->idx)),
 | 
| -			get_check_names(lzma_index_checks(xfi->idx), false),
 | 
| -			pair->src_name);
 | 
| +	char checks[CHECKS_STR_SIZE];
 | 
| +	get_check_names(checks, lzma_index_checks(xfi->idx), false);
 | 
|  
 | 
| +	const char *cols[7] = {
 | 
| +		uint64_to_str(lzma_index_stream_count(xfi->idx), 0),
 | 
| +		uint64_to_str(lzma_index_block_count(xfi->idx), 1),
 | 
| +		uint64_to_nicestr(lzma_index_file_size(xfi->idx),
 | 
| +			NICESTR_B, NICESTR_TIB, false, 2),
 | 
| +		uint64_to_nicestr(lzma_index_uncompressed_size(xfi->idx),
 | 
| +			NICESTR_B, NICESTR_TIB, false, 3),
 | 
| +		get_ratio(lzma_index_file_size(xfi->idx),
 | 
| +			lzma_index_uncompressed_size(xfi->idx)),
 | 
| +		checks,
 | 
| +		pair->src_name,
 | 
| +	};
 | 
| +	printf("%*s %*s  %*s  %*s  %*s  %-*s %s\n",
 | 
| +			tuklib_mbstr_fw(cols[0], 5), cols[0],
 | 
| +			tuklib_mbstr_fw(cols[1], 7), cols[1],
 | 
| +			tuklib_mbstr_fw(cols[2], 11), cols[2],
 | 
| +			tuklib_mbstr_fw(cols[3], 11), cols[3],
 | 
| +			tuklib_mbstr_fw(cols[4], 5), cols[4],
 | 
| +			tuklib_mbstr_fw(cols[5], 7), cols[5],
 | 
| +			cols[6]);
 | 
| +
 | 
|  	return false;
 | 
|  }
 | 
|  
 | 
| @@ -618,6 +646,9 @@
 | 
|  		uint64_t compressed_size, uint64_t uncompressed_size,
 | 
|  		uint32_t checks, uint64_t stream_padding)
 | 
|  {
 | 
| +	char checks_str[CHECKS_STR_SIZE];
 | 
| +	get_check_names(checks_str, checks, true);
 | 
| +
 | 
|  	printf(_("  Streams:            %s\n"),
 | 
|  			uint64_to_str(stream_count, 0));
 | 
|  	printf(_("  Blocks:             %s\n"),
 | 
| @@ -630,8 +661,7 @@
 | 
|  				NICESTR_B, NICESTR_TIB, true, 0));
 | 
|  	printf(_("  Ratio:              %s\n"),
 | 
|  			get_ratio(compressed_size, uncompressed_size));
 | 
| -	printf(_("  Check:              %s\n"),
 | 
| -			get_check_names(checks, true));
 | 
| +	printf(_("  Check:              %s\n"), checks_str);
 | 
|  	printf(_("  Stream padding:     %s\n"),
 | 
|  			uint64_to_nicestr(stream_padding,
 | 
|  				NICESTR_B, NICESTR_TIB, true, 0));
 | 
| @@ -656,6 +686,10 @@
 | 
|  	uint32_t check_max = 0;
 | 
|  
 | 
|  	// Print information about the Streams.
 | 
| +	//
 | 
| +	// TRANSLATORS: The second line is column headings. All except
 | 
| +	// Check are right aligned; Check is left aligned. Test with
 | 
| +	// "xz -lv foo.xz".
 | 
|  	puts(_("  Streams:\n    Stream    Blocks"
 | 
|  			"      CompOffset    UncompOffset"
 | 
|  			"        CompSize      UncompSize  Ratio"
 | 
| @@ -665,22 +699,33 @@
 | 
|  	lzma_index_iter_init(&iter, xfi->idx);
 | 
|  
 | 
|  	while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)) {
 | 
| -		printf("    %6s %9s %15s %15s ",
 | 
| -				uint64_to_str(iter.stream.number, 0),
 | 
| -				uint64_to_str(iter.stream.block_count, 1),
 | 
| -				uint64_to_str(
 | 
| -					iter.stream.compressed_offset, 2),
 | 
| -				uint64_to_str(
 | 
| -					iter.stream.uncompressed_offset, 3));
 | 
| -		printf("%15s %15s  %5s  %-10s %7s\n",
 | 
| -				uint64_to_str(iter.stream.compressed_size, 0),
 | 
| -				uint64_to_str(
 | 
| -					iter.stream.uncompressed_size, 1),
 | 
| -				get_ratio(iter.stream.compressed_size,
 | 
| -					iter.stream.uncompressed_size),
 | 
| -				check_names[iter.stream.flags->check],
 | 
| -				uint64_to_str(iter.stream.padding, 2));
 | 
| +		const char *cols1[4] = {
 | 
| +			uint64_to_str(iter.stream.number, 0),
 | 
| +			uint64_to_str(iter.stream.block_count, 1),
 | 
| +			uint64_to_str(iter.stream.compressed_offset, 2),
 | 
| +			uint64_to_str(iter.stream.uncompressed_offset, 3),
 | 
| +		};
 | 
| +		printf("    %*s %*s %*s %*s ",
 | 
| +				tuklib_mbstr_fw(cols1[0], 6), cols1[0],
 | 
| +				tuklib_mbstr_fw(cols1[1], 9), cols1[1],
 | 
| +				tuklib_mbstr_fw(cols1[2], 15), cols1[2],
 | 
| +				tuklib_mbstr_fw(cols1[3], 15), cols1[3]);
 | 
|  
 | 
| +		const char *cols2[5] = {
 | 
| +			uint64_to_str(iter.stream.compressed_size, 0),
 | 
| +			uint64_to_str(iter.stream.uncompressed_size, 1),
 | 
| +			get_ratio(iter.stream.compressed_size,
 | 
| +				iter.stream.uncompressed_size),
 | 
| +			_(check_names[iter.stream.flags->check]),
 | 
| +			uint64_to_str(iter.stream.padding, 2),
 | 
| +		};
 | 
| +		printf("%*s %*s  %*s  %-*s %*s\n",
 | 
| +				tuklib_mbstr_fw(cols2[0], 15), cols2[0],
 | 
| +				tuklib_mbstr_fw(cols2[1], 15), cols2[1],
 | 
| +				tuklib_mbstr_fw(cols2[2], 5), cols2[2],
 | 
| +				tuklib_mbstr_fw(cols2[3], 10), cols2[3],
 | 
| +				tuklib_mbstr_fw(cols2[4], 7), cols2[4]);
 | 
| +
 | 
|  		// Update the maximum Check size.
 | 
|  		if (lzma_check_size(iter.stream.flags->check) > check_max)
 | 
|  			check_max = lzma_check_size(iter.stream.flags->check);
 | 
| @@ -698,15 +743,24 @@
 | 
|  		// Calculate the width of the CheckVal field.
 | 
|  		const int checkval_width = my_max(8, 2 * check_max);
 | 
|  
 | 
| -		// Print the headings.
 | 
| +		// TRANSLATORS: The second line is column headings. All
 | 
| +		// except Check are right aligned; Check is left aligned.
 | 
|  		printf(_("  Blocks:\n    Stream     Block"
 | 
|  			"      CompOffset    UncompOffset"
 | 
|  			"       TotalSize      UncompSize  Ratio  Check"));
 | 
|  
 | 
| -		if (detailed)
 | 
| -			printf(_("      %-*s  Header  Flags        CompSize"
 | 
| -					"    MemUsage  Filters"),
 | 
| -					checkval_width, _("CheckVal"));
 | 
| +		if (detailed) {
 | 
| +			// TRANSLATORS: These are additional column headings
 | 
| +			// for the most verbose listing mode. CheckVal
 | 
| +			// (Check value), Flags, and Filters are left aligned.
 | 
| +			// Header (Block Header Size), CompSize, and MemUsage
 | 
| +			// are right aligned. %*s is replaced with 0-120
 | 
| +			// spaces to make the CheckVal column wide enough.
 | 
| +			// Test with "xz -lvv foo.xz".
 | 
| +			printf(_("      CheckVal %*s Header  Flags        "
 | 
| +					"CompSize    MemUsage  Filters"),
 | 
| +					checkval_width - 8, "");
 | 
| +		}
 | 
|  
 | 
|  		putchar('\n');
 | 
|  
 | 
| @@ -717,41 +771,63 @@
 | 
|  			if (detailed && parse_details(pair, &iter, &bhi, xfi))
 | 
|  					return true;
 | 
|  
 | 
| -			printf("    %6s %9s %15s %15s ",
 | 
| +			const char *cols1[4] = {
 | 
|  				uint64_to_str(iter.stream.number, 0),
 | 
|  				uint64_to_str(
 | 
|  					iter.block.number_in_stream, 1),
 | 
|  				uint64_to_str(
 | 
|  					iter.block.compressed_file_offset, 2),
 | 
|  				uint64_to_str(
 | 
| -					iter.block.uncompressed_file_offset,
 | 
| -					3));
 | 
| -			printf("%15s %15s  %5s  %-*s",
 | 
| +					iter.block.uncompressed_file_offset, 3)
 | 
| +			};
 | 
| +			printf("    %*s %*s %*s %*s ",
 | 
| +				tuklib_mbstr_fw(cols1[0], 6), cols1[0],
 | 
| +				tuklib_mbstr_fw(cols1[1], 9), cols1[1],
 | 
| +				tuklib_mbstr_fw(cols1[2], 15), cols1[2],
 | 
| +				tuklib_mbstr_fw(cols1[3], 15), cols1[3]);
 | 
| +
 | 
| +			const char *cols2[4] = {
 | 
|  				uint64_to_str(iter.block.total_size, 0),
 | 
|  				uint64_to_str(iter.block.uncompressed_size,
 | 
|  						1),
 | 
|  				get_ratio(iter.block.total_size,
 | 
|  					iter.block.uncompressed_size),
 | 
| -				detailed ? 11 : 1,
 | 
| -				check_names[iter.stream.flags->check]);
 | 
| +				_(check_names[iter.stream.flags->check])
 | 
| +			};
 | 
| +			printf("%*s %*s  %*s  %-*s",
 | 
| +				tuklib_mbstr_fw(cols2[0], 15), cols2[0],
 | 
| +				tuklib_mbstr_fw(cols2[1], 15), cols2[1],
 | 
| +				tuklib_mbstr_fw(cols2[2], 5), cols2[2],
 | 
| +				tuklib_mbstr_fw(cols2[3], detailed ? 11 : 1),
 | 
| +					cols2[3]);
 | 
|  
 | 
|  			if (detailed) {
 | 
| -				// Show MiB for memory usage, because it
 | 
| -				// is the only size which is not in bytes.
 | 
|  				const lzma_vli compressed_size
 | 
|  						= iter.block.unpadded_size
 | 
|  						- bhi.header_size
 | 
|  						- lzma_check_size(
 | 
|  						iter.stream.flags->check);
 | 
| -				printf("%-*s  %6s  %-5s %15s %7s MiB  %s",
 | 
| -					checkval_width, check_value,
 | 
| +
 | 
| +				const char *cols3[6] = {
 | 
| +					check_value,
 | 
|  					uint64_to_str(bhi.header_size, 0),
 | 
|  					bhi.flags,
 | 
|  					uint64_to_str(compressed_size, 1),
 | 
|  					uint64_to_str(
 | 
|  						round_up_to_mib(bhi.memusage),
 | 
|  						2),
 | 
| -					bhi.filter_chain);
 | 
| +					bhi.filter_chain
 | 
| +				};
 | 
| +				// Show MiB for memory usage, because it
 | 
| +				// is the only size which is not in bytes.
 | 
| +				printf("%-*s  %*s  %-5s %*s %*s MiB  %s",
 | 
| +					checkval_width, cols3[0],
 | 
| +					tuklib_mbstr_fw(cols3[1], 6), cols3[1],
 | 
| +					cols3[2],
 | 
| +					tuklib_mbstr_fw(cols3[3], 15),
 | 
| +						cols3[3],
 | 
| +					tuklib_mbstr_fw(cols3[4], 7), cols3[4],
 | 
| +					cols3[5]);
 | 
|  			}
 | 
|  
 | 
|  			putchar('\n');
 | 
| @@ -772,6 +848,9 @@
 | 
|  static bool
 | 
|  print_info_robot(xz_file_info *xfi, file_pair *pair)
 | 
|  {
 | 
| +	char checks[CHECKS_STR_SIZE];
 | 
| +	get_check_names(checks, lzma_index_checks(xfi->idx), false);
 | 
| +
 | 
|  	printf("name\t%s\n", pair->src_name);
 | 
|  
 | 
|  	printf("file\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64
 | 
| @@ -782,7 +861,7 @@
 | 
|  			lzma_index_uncompressed_size(xfi->idx),
 | 
|  			get_ratio(lzma_index_file_size(xfi->idx),
 | 
|  				lzma_index_uncompressed_size(xfi->idx)),
 | 
| -			get_check_names(lzma_index_checks(xfi->idx), false),
 | 
| +			checks,
 | 
|  			xfi->stream_padding);
 | 
|  
 | 
|  	if (message_verbosity_get() >= V_VERBOSE) {
 | 
| @@ -880,6 +959,10 @@
 | 
|  	line[sizeof(line) - 1] = '\0';
 | 
|  	puts(line);
 | 
|  
 | 
| +	// Get the check names.
 | 
| +	char checks[CHECKS_STR_SIZE];
 | 
| +	get_check_names(checks, totals.checks, false);
 | 
| +
 | 
|  	// Print the totals except the file count, which needs
 | 
|  	// special handling.
 | 
|  	printf("%5s %7s  %11s  %11s  %5s  %-7s ",
 | 
| @@ -891,16 +974,16 @@
 | 
|  				NICESTR_B, NICESTR_TIB, false, 3),
 | 
|  			get_ratio(totals.compressed_size,
 | 
|  				totals.uncompressed_size),
 | 
| -			get_check_names(totals.checks, false));
 | 
| +			checks);
 | 
|  
 | 
|  	// Since we print totals only when there are at least two files,
 | 
|  	// the English message will always use "%s files". But some other
 | 
|  	// languages need different forms for different plurals so we
 | 
| -	// have to translate this string still.
 | 
| +	// have to translate this with ngettext().
 | 
|  	//
 | 
| -	// TRANSLATORS: This simply indicates the number of files shown
 | 
| -	// by --list even though the format string uses %s.
 | 
| -	printf(N_("%s file", "%s files\n",
 | 
| +	// TRANSLATORS: %s is an integer. Only the plural form of this
 | 
| +	// message is used (e.g. "2 files"). Test with "xz -l foo.xz bar.xz".
 | 
| +	printf(ngettext("%s file\n", "%s files\n",
 | 
|  			totals.files <= ULONG_MAX ? totals.files
 | 
|  				: (totals.files % 1000000) + 1000000),
 | 
|  			uint64_to_str(totals.files, 0));
 | 
| @@ -934,6 +1017,9 @@
 | 
|  static void
 | 
|  print_totals_robot(void)
 | 
|  {
 | 
| +	char checks[CHECKS_STR_SIZE];
 | 
| +	get_check_names(checks, totals.checks, false);
 | 
| +
 | 
|  	printf("totals\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64
 | 
|  			"\t%s\t%s\t%" PRIu64 "\t%" PRIu64,
 | 
|  			totals.streams,
 | 
| @@ -942,7 +1028,7 @@
 | 
|  			totals.uncompressed_size,
 | 
|  			get_ratio(totals.compressed_size,
 | 
|  				totals.uncompressed_size),
 | 
| -			get_check_names(totals.checks, false),
 | 
| +			checks,
 | 
|  			totals.stream_padding,
 | 
|  			totals.files);
 | 
|  
 | 
| 
 |