aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLasse Collin <lasse.collin@tukaani.org>2019-03-04 01:07:59 +0200
committerLasse Collin <lasse.collin@tukaani.org>2019-03-04 21:16:59 +0200
commit6cb42e8aa1dc37bf403a9f5acbd07e86036b7e77 (patch)
tree7ea4d17f0016382a32ddea771ad87fdf1c4ac6e8
parentWindows/VS2017: Omit WindowsTargetPlatformVersion from project files. (diff)
downloadxz-6cb42e8aa1dc37bf403a9f5acbd07e86036b7e77.tar.xz
xz: Automatically align strings ending in a colon in --list output.
This should avoid alignment errors in translations with these strings.
-rw-r--r--src/xz/list.c114
1 files changed, 102 insertions, 12 deletions
diff --git a/src/xz/list.c b/src/xz/list.c
index ef93314c..98809b59 100644
--- a/src/xz/list.c
+++ b/src/xz/list.c
@@ -57,6 +57,49 @@ typedef struct {
} block_header_info;
+/// Strings ending in a colon. These are used for lines like
+/// " Foo: 123 MiB". These are groupped because translated strings
+/// may have different maximum string length, and we want to pad all
+/// strings so that the values are aligned nicely.
+static const char *colon_strs[] = {
+ N_("Streams:"),
+ N_("Blocks:"),
+ N_("Compressed size:"),
+ N_("Uncompressed size:"),
+ N_("Ratio:"),
+ N_("Check:"),
+ N_("Stream Padding:"),
+ N_("Memory needed:"),
+ N_("Sizes in headers:"),
+ // This won't be aligned because it's so long:
+ //N_("Minimum XZ Utils version:"),
+ N_("Number of files:"),
+};
+
+/// Enum matching the above strings.
+enum {
+ COLON_STR_STREAMS,
+ COLON_STR_BLOCKS,
+ COLON_STR_COMPRESSED_SIZE,
+ COLON_STR_UNCOMPRESSED_SIZE,
+ COLON_STR_RATIO,
+ COLON_STR_CHECK,
+ COLON_STR_STREAM_PADDING,
+ COLON_STR_MEMORY_NEEDED,
+ COLON_STR_SIZES_IN_HEADERS,
+ //COLON_STR_MINIMUM_XZ_VERSION,
+ COLON_STR_NUMBER_OF_FILES,
+};
+
+/// Field widths to use with printf to pad the strings to use the same number
+/// of columns on a terminal.
+static int colon_strs_fw[ARRAY_SIZE(colon_strs)];
+
+/// Convenience macro to get the translated string and its field width
+/// using a COLON_STR_foo enum.
+#define COLON_STR(num) colon_strs_fw[num], _(colon_strs[num])
+
+
/// Check ID to string mapping
static const char check_names[LZMA_CHECK_ID_MAX + 1][12] = {
// TRANSLATORS: Indicates that there is no integrity check.
@@ -112,6 +155,47 @@ static struct {
} totals = { 0, 0, 0, 0, 0, 0, 0, 0, 50000002, true };
+/// Initialize the printf field widths that are needed to get nicely aligned
+/// output with translated strings.
+static void
+init_field_widths(void)
+{
+ // Lengths of translated strings as bytes.
+ size_t lens[ARRAY_SIZE(colon_strs)];
+
+ // Lengths of translated strings as columns.
+ size_t widths[ARRAY_SIZE(colon_strs)];
+
+ // Maximum number of columns needed by a translated string.
+ size_t width_max = 0;
+
+ for (unsigned i = 0; i < ARRAY_SIZE(colon_strs); ++i) {
+ widths[i] = tuklib_mbstr_width(colon_strs[i], &lens[i]);
+
+ // If debugging is enabled, catch invalid strings with
+ // an assertion. However, when not debugging, use the
+ // byte count as the fallback width. This shouldn't
+ // ever happen unless there is a bad string in the
+ // translations, but in such case I guess it's better
+ // to try to print something useful instead of failing
+ // completely.
+ assert(widths[i] != (size_t)-1);
+ if (widths[i] == (size_t)-1)
+ widths[i] = lens[i];
+
+ if (widths[i] > width_max)
+ width_max = widths[i];
+ }
+
+ // Calculate the field width for printf("%*s") so that the strings
+ // will use width_max columns on a terminal.
+ for (unsigned i = 0; i < ARRAY_SIZE(colon_strs); ++i)
+ colon_strs_fw[i] = (int)(lens[i] + width_max - widths[i]);
+
+ return;
+}
+
+
/// Convert XZ Utils version number to a string.
static const char *
xz_ver_to_str(uint32_t ver)
@@ -548,20 +632,20 @@ print_adv_helper(uint64_t stream_count, uint64_t block_count,
char checks_str[CHECKS_STR_SIZE];
get_check_names(checks_str, checks, true);
- printf(_(" Streams: %s\n"),
+ printf(" %-*s %s\n", COLON_STR(COLON_STR_STREAMS),
uint64_to_str(stream_count, 0));
- printf(_(" Blocks: %s\n"),
+ printf(" %-*s %s\n", COLON_STR(COLON_STR_BLOCKS),
uint64_to_str(block_count, 0));
- printf(_(" Compressed size: %s\n"),
+ printf(" %-*s %s\n", COLON_STR(COLON_STR_COMPRESSED_SIZE),
uint64_to_nicestr(compressed_size,
NICESTR_B, NICESTR_TIB, true, 0));
- printf(_(" Uncompressed size: %s\n"),
+ printf(" %-*s %s\n", COLON_STR(COLON_STR_UNCOMPRESSED_SIZE),
uint64_to_nicestr(uncompressed_size,
NICESTR_B, NICESTR_TIB, true, 0));
- printf(_(" Ratio: %s\n"),
+ printf(" %-*s %s\n", COLON_STR(COLON_STR_RATIO),
get_ratio(compressed_size, uncompressed_size));
- printf(_(" Check: %s\n"), checks_str);
- printf(_(" Stream padding: %s\n"),
+ printf(" %-*s %s\n", COLON_STR(COLON_STR_CHECK), checks_str);
+ printf(" %-*s %s\n", COLON_STR(COLON_STR_STREAM_PADDING),
uint64_to_nicestr(stream_padding,
NICESTR_B, NICESTR_TIB, true, 0));
return;
@@ -734,10 +818,12 @@ print_info_adv(xz_file_info *xfi, file_pair *pair)
}
if (detailed) {
- printf(_(" Memory needed: %s MiB\n"), uint64_to_str(
+ printf(" %-*s %s MiB\n", COLON_STR(COLON_STR_MEMORY_NEEDED),
+ uint64_to_str(
round_up_to_mib(xfi->memusage_max), 0));
- printf(_(" Sizes in headers: %s\n"),
+ printf(" %-*s %s\n", COLON_STR(COLON_STR_SIZES_IN_HEADERS),
xfi->all_have_sizes ? _("Yes") : _("No"));
+ //printf(" %-*s %s\n", COLON_STR(COLON_STR_MINIMUM_XZ_VERSION),
printf(_(" Minimum XZ Utils version: %s\n"),
xz_ver_to_str(xfi->min_version));
}
@@ -902,17 +988,19 @@ print_totals_adv(void)
{
putchar('\n');
puts(_("Totals:"));
- printf(_(" Number of files: %s\n"),
+ printf(" %-*s %s\n", COLON_STR(COLON_STR_NUMBER_OF_FILES),
uint64_to_str(totals.files, 0));
print_adv_helper(totals.streams, totals.blocks,
totals.compressed_size, totals.uncompressed_size,
totals.checks, totals.stream_padding);
if (message_verbosity_get() >= V_DEBUG) {
- printf(_(" Memory needed: %s MiB\n"), uint64_to_str(
+ printf(" %-*s %s MiB\n", COLON_STR(COLON_STR_MEMORY_NEEDED),
+ uint64_to_str(
round_up_to_mib(totals.memusage_max), 0));
- printf(_(" Sizes in headers: %s\n"),
+ printf(" %-*s %s\n", COLON_STR(COLON_STR_SIZES_IN_HEADERS),
totals.all_have_sizes ? _("Yes") : _("No"));
+ //printf(" %-*s %s\n", COLON_STR(COLON_STR_MINIMUM_XZ_VERSION),
printf(_(" Minimum XZ Utils version: %s\n"),
xz_ver_to_str(totals.min_version));
}
@@ -988,6 +1076,8 @@ list_file(const char *filename)
return;
}
+ init_field_widths();
+
// Unset opt_stdout so that io_open_src() won't accept special files.
// Set opt_force so that io_open_src() will follow symlinks.
opt_stdout = false;