xz: Use automatic word wrapping for help texts

--long-help is now one line longer because --lzma1 is now on its
own line.
This commit is contained in:
Lasse Collin 2024-12-17 10:26:10 +02:00
parent a0eecc9eb2
commit 3e9177fd20
No known key found for this signature in database
GPG Key ID: 38EE757D69184620
3 changed files with 317 additions and 178 deletions

View File

@ -1996,6 +1996,8 @@ if(XZ_TOOL_XZ)
src/common/tuklib_mbstr.h src/common/tuklib_mbstr.h
src/common/tuklib_mbstr_fw.c src/common/tuklib_mbstr_fw.c
src/common/tuklib_mbstr_width.c src/common/tuklib_mbstr_width.c
src/common/tuklib_mbstr_wrap.c
src/common/tuklib_mbstr_wrap.h
src/common/tuklib_open_stdxxx.c src/common/tuklib_open_stdxxx.c
src/common/tuklib_open_stdxxx.h src/common/tuklib_open_stdxxx.h
src/common/tuklib_progname.c src/common/tuklib_progname.c

View File

@ -32,8 +32,9 @@ xz_SOURCES = \
../common/tuklib_open_stdxxx.c \ ../common/tuklib_open_stdxxx.c \
../common/tuklib_progname.c \ ../common/tuklib_progname.c \
../common/tuklib_exit.c \ ../common/tuklib_exit.c \
../common/tuklib_mbstr_fw.c \
../common/tuklib_mbstr_width.c \ ../common/tuklib_mbstr_width.c \
../common/tuklib_mbstr_fw.c ../common/tuklib_mbstr_wrap.c
if COND_MAIN_DECODER if COND_MAIN_DECODER
xz_SOURCES += \ xz_SOURCES += \

View File

@ -11,7 +11,7 @@
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#include "private.h" #include "private.h"
#include "tuklib_mbstr_wrap.h"
#include <stdarg.h> #include <stdarg.h>
@ -936,213 +936,344 @@ message_version(void)
} }
static void
detect_wrapping_errors(int error_mask)
{
#ifndef NDEBUG
// This might help in catching problematic strings in translations.
// It's a debug message so don't translate this.
if (error_mask & TUKLIB_WRAP_WARN_OVERLONG)
message_fatal("The help text contains overlong lines");
#endif
if (error_mask & ~TUKLIB_WRAP_WARN_OVERLONG)
message_fatal(_("Error printing the help text "
"(error code %d)"), error_mask);
return;
}
extern void extern void
message_help(bool long_help) message_help(bool long_help)
{ {
printf(_("Usage: %s [OPTION]... [FILE]...\n" static const struct tuklib_wrap_opt wrap0 = { 0, 0, 0, 0, 79 };
"Compress or decompress FILEs in the .xz format.\n\n"), static const struct tuklib_wrap_opt wrap1 = { 1, 1, 1, 1, 79 };
progname); static const struct tuklib_wrap_opt wrap2 = { 2, 2, 22, 22, 79 };
static const struct tuklib_wrap_opt wrap3 = { 24, 24, 36, 36, 79 };
// NOTE: The short help doesn't currently have options that // Accumulated error codes from tuklib_wraps() and tuklib_wrapf()
// take arguments. int e = 0;
if (long_help)
puts(_("Mandatory arguments to long options are mandatory "
"for short options too.\n"));
if (long_help) printf(_("Usage: %s [OPTION]... [FILE]...\n"), progname);
puts(_(" Operation mode:\n")); e |= tuklib_wraps(stdout, &wrap0,
W_("Compress or decompress FILEs in the .xz format."));
putchar('\n');
puts(_( e |= tuklib_wraps(stdout, &wrap0,
" -z, --compress force compression\n" W_("Mandatory arguments to long options are "
" -d, --decompress force decompression\n" "mandatory for short options too."));
" -t, --test test compressed file integrity\n" putchar('\n');
" -l, --list list information about .xz files"));
if (long_help) if (long_help) {
puts(_("\n Operation modifiers:\n")); e |= tuklib_wraps(stdout, &wrap1, W_("Operation mode:"));
putchar('\n');
}
puts(_( e |= tuklib_wrapf(stdout, &wrap2,
" -k, --keep keep (don't delete) input files\n" "-z, --compress\v%s\r"
" -f, --force force overwrite of output file and (de)compress links\n" "-d, --decompress\v%s\r"
" -c, --stdout write to standard output and don't delete input files")); "-t, --test\v%s\r"
"-l, --list\v%s",
W_("force compression"),
W_("force decompression"),
W_("test compressed file integrity"),
W_("list information about .xz files"));
if (long_help) {
putchar('\n');
e |= tuklib_wraps(stdout, &wrap1, W_("Operation modifiers:"));
putchar('\n');
}
e |= tuklib_wrapf(stdout, &wrap2,
"-k, --keep\v%s\r"
"-f, --force\v%s\r"
"-c, --stdout\v%s",
W_("keep (don't delete) input files"),
W_("force overwrite of output file and (de)compress links"),
W_("write to standard output and don't delete input files"));
// NOTE: --to-stdout isn't included above because it's not // NOTE: --to-stdout isn't included above because it's not
// the recommended spelling. It was copied from gzip but other // the recommended spelling. It was copied from gzip but other
// compressors with gzip-like syntax don't support it. // compressors with gzip-like syntax don't support it.
if (long_help) { if (long_help) {
puts(_( e |= tuklib_wrapf(stdout, &wrap2,
" --single-stream decompress only the first stream, and silently\n" " --single-stream\v%s\r"
" ignore possible remaining input data")); " --no-sparse\v%s\r"
puts(_( "-S, --suffix=%s\v%s\r"
" --no-sparse do not create sparse files when decompressing\n" " --files[=%s]\v%s\r"
" -S, --suffix=.SUF use the suffix '.SUF' on compressed files\n" " --files0[=%s]\v%s\r",
" --files[=FILE] read filenames to process from FILE; if FILE is\n" W_("decompress only the first stream, and silently "
" omitted, filenames are read from the standard input;\n" "ignore possible remaining input data"),
" filenames must be terminated with the newline character\n" W_("do not create sparse files when decompressing"),
" --files0[=FILE] like --files but use the null character as terminator")); _(".SUF"),
W_("use the suffix '.SUF' on compressed files"),
_("FILE"),
W_("read filenames to process from FILE; "
"if FILE is omitted, "
"filenames are read from the standard input; "
"filenames must be terminated with "
"the newline character"),
_("FILE"),
W_("like --files but use the null character as "
"terminator"));
e |= tuklib_wraps(stdout, &wrap1,
W_("Basic file format and compression options:"));
e |= tuklib_wrapf(stdout, &wrap2,
"\n"
"-F, --format=%s\v%s\r"
"-C, --check=%s\v%s\r"
" --ignore-check\v%s",
_("FORMAT"),
W_("file format to encode or decode; possible values "
"are 'auto' (default), 'xz', 'lzma', 'lzip', "
"and 'raw'"),
_("NAME"),
W_("integrity check type: 'none' (use with caution), "
"'crc32', 'crc64' (default), or 'sha256'"),
W_("don't verify the integrity check when "
"decompressing"));
}
e |= tuklib_wrapf(stdout, &wrap2,
"-0 ... -9\v%s\r"
"-e, --extreme\v%s\r"
"-T, --threads=%s\v%s",
W_("compression preset; default is 6; take compressor *and* "
"decompressor memory usage into account before "
"using 7-9!"),
W_("try to improve compression ratio by using more CPU time; "
"does not affect decompressor memory requirements"),
// TRANSLATORS: Short for NUMBER. A longer string is fine but
// wider than 5 columns makes --long-help a few lines longer.
_("NUM"),
W_("use at most NUM threads; the default is 0 which uses "
"as many threads as there are processor cores"));
if (long_help) {
e |= tuklib_wrapf(stdout, &wrap2,
" --block-size=%s\v%s\r"
" --block-list=%s\v%s\r"
" --flush-timeout=%s\v%s",
_("SIZE"),
W_("start a new .xz block after every SIZE bytes "
"of input; use this to set the block size "
"for threaded compression"),
_("BLOCKS"),
W_("start a new .xz block after the given "
"comma-separated intervals of uncompressed "
"data; optionally, specify a "
"filter chain number (0-9) followed by "
"a ':' before the uncompressed data size"),
_("NUM"),
W_("when compressing, if more than NUM "
"milliseconds has passed since the previous "
"flush and reading more input would block, "
"all pending data is flushed out"));
e |= tuklib_wrapf(stdout, &wrap2,
" --memlimit-compress=%1$s\n"
" --memlimit-decompress=%1$s\n"
" --memlimit-mt-decompress=%1$s\n"
"-M, --memlimit=%1$s\v%2$s\r"
" --no-adjust\v%3$s",
_("LIMIT"),
// xgettext:no-c-format
W_("set memory usage limit for compression, "
"decompression, threaded decompression, "
"or all of these; LIMIT is in "
"bytes, % of RAM, or 0 for defaults"),
W_("if compression settings exceed the "
"memory usage limit, "
"give an error instead of adjusting "
"the settings downwards"));
} }
if (long_help) { if (long_help) {
puts(_("\n Basic file format and compression options:\n")); putchar('\n');
puts(_(
" -F, --format=FMT file format to encode or decode; possible values are\n"
" 'auto' (default), 'xz', 'lzma', 'lzip', and 'raw'\n"
" -C, --check=CHECK integrity check type: 'none' (use with caution),\n"
" 'crc32', 'crc64' (default), or 'sha256'"));
puts(_(
" --ignore-check don't verify the integrity check when decompressing"));
}
puts(_( e |= tuklib_wraps(stdout, &wrap1,
" -0 ... -9 compression preset; default is 6; take compressor *and*\n" W_("Custom filter chain for compression "
" decompressor memory usage into account before using 7-9!")); "(an alternative to using presets):"));
puts(_( e |= tuklib_wrapf(stdout, &wrap2,
" -e, --extreme try to improve compression ratio by using more CPU time;\n" "\n"
" does not affect decompressor memory requirements")); "--filters=%1$s\v%2$s\r"
"--filters1=%1$s ... --filters9=%1$s\v%3$s\r"
puts(_( "--filters-help\v%4$s",
" -T, --threads=NUM use at most NUM threads; the default is 0 which uses\n" _("FILTERS"),
" as many threads as there are processor cores")); W_("set the filter chain using the "
"liblzma filter string syntax; "
if (long_help) { "use --filters-help for more information"),
puts(_( W_("set additional filter chains using the "
" --block-size=SIZE\n" "liblzma filter string syntax to use "
" start a new .xz block after every SIZE bytes of input;\n" "with --block-list"),
" use this to set the block size for threaded compression")); W_("display more information about the "
puts(_( "liblzma filter string syntax and exit"));
" --block-list=BLOCKS\n"
" start a new .xz block after the given comma-separated\n"
" intervals of uncompressed data; optionally, specify a\n"
" filter chain number (0-9) followed by a ':' before the\n"
" uncompressed data size"));
puts(_(
" --flush-timeout=TIMEOUT\n"
" when compressing, if more than TIMEOUT milliseconds has\n"
" passed since the previous flush and reading more input\n"
" would block, all pending data is flushed out"
));
puts(_( // xgettext:no-c-format
" --memlimit-compress=LIMIT\n"
" --memlimit-decompress=LIMIT\n"
" --memlimit-mt-decompress=LIMIT\n"
" -M, --memlimit=LIMIT\n"
" set memory usage limit for compression, decompression,\n"
" threaded decompression, or all of these; LIMIT is in\n"
" bytes, % of RAM, or 0 for defaults"));
puts(_(
" --no-adjust if compression settings exceed the memory usage limit,\n"
" give an error instead of adjusting the settings downwards"));
}
if (long_help) {
puts(_(
"\n Custom filter chain for compression (alternative for using presets):"));
puts(_(
"\n"
" --filters=FILTERS set the filter chain using the liblzma filter string\n"
" syntax; use --filters-help for more information"
));
puts(_(
" --filters1=FILTERS ... --filters9=FILTERS\n"
" set additional filter chains using the liblzma filter\n"
" string syntax to use with --block-list"
));
puts(_(
" --filters-help display more information about the liblzma filter string\n"
" syntax and exit."
));
#if defined(HAVE_ENCODER_LZMA1) || defined(HAVE_DECODER_LZMA1) \ #if defined(HAVE_ENCODER_LZMA1) || defined(HAVE_DECODER_LZMA1) \
|| defined(HAVE_ENCODER_LZMA2) || defined(HAVE_DECODER_LZMA2) || defined(HAVE_ENCODER_LZMA2) || defined(HAVE_DECODER_LZMA2)
// TRANSLATORS: The word "literal" in "literal context bits" e |= tuklib_wrapf(stdout, &wrap2,
// means how many "context bits" to use when encoding "\n"
// literals. A literal is a single 8-bit byte. It doesn't "--lzma1[=%1$s]\n"
// mean "literally" here. "--lzma2[=%1$s]\v%2$s",
puts(_( // TRANSLATORS: Short for OPTIONS.
"\n" _("OPTS"),
" --lzma1[=OPTS] LZMA1 or LZMA2; OPTS is a comma-separated list of zero or\n" // TRANSLATORS: Use semicolon (or its fullwidth form)
" --lzma2[=OPTS] more of the following options (valid values; default):\n" // in "(valid values; default)" even if it is weird in
" preset=PRE reset options to a preset (0-9[e])\n" // your language. There are non-translatable strings
" dict=NUM dictionary size (4KiB - 1536MiB; 8MiB)\n" // that look like "(foo, bar, baz; foo)" which list
" lc=NUM number of literal context bits (0-4; 3)\n" // the supported values and the default value.
" lp=NUM number of literal position bits (0-4; 0)\n" W_("LZMA1 or LZMA2; OPTS is a comma-separated list "
" pb=NUM number of position bits (0-4; 2)\n" "of zero or more of the following options "
" mode=MODE compression mode (fast, normal; normal)\n" "(valid values; default):"));
" nice=NUM nice length of a match (2-273; 64)\n"
" mf=NAME match finder (hc3, hc4, bt2, bt3, bt4; bt4)\n" e |= tuklib_wrapf(stdout, &wrap3,
" depth=NUM maximum search depth; 0=automatic (default)")); "preset=%s\v%s (0-9[e])\r"
"dict=%s\v%s \b(4KiB - 1536MiB; 8MiB)\b\r"
"lc=%s\v%s \b(0-4; 3)\b\r"
"lp=%s\v%s \b(0-4; 0)\b\r"
"pb=%s\v%s \b(0-4; 2)\b\r"
"mode=%s\v%s (fast, normal; normal)\r"
"nice=%s\v%s \b(2-273; 64)\b\r"
"mf=%s\v%s (hc3, hc4, bt2, bt3, bt4; bt4)\r"
"depth=%s\v%s",
// TRANSLATORS: Short for PRESET. A longer string is
// fine but wider than 4 columns makes --long-help
// one line longer.
_("PRE"),
W_("reset options to a preset"),
_("NUM"), W_("dictionary size"),
_("NUM"),
// TRANSLATORS: The word "literal" in "literal context
// bits" means how many "context bits" to use when
// encoding literals. A literal is a single 8-bit
// byte. It doesn't mean "literally" here.
W_("number of literal context bits"),
_("NUM"), W_("number of literal position bits"),
_("NUM"), W_("number of position bits"),
_("MODE"), W_("compression mode"),
_("NUM"), W_("nice length of a match"),
_("NAME"), W_("match finder"),
_("NUM"), W_("maximum search depth; "
"0=automatic (default)"));
#endif #endif
puts(_( e |= tuklib_wrapf(stdout, &wrap2,
"\n" "\n"
" --x86[=OPTS] x86 BCJ filter (32-bit and 64-bit)\n" "--x86[=%1$s]\v%2$s\r"
" --arm[=OPTS] ARM BCJ filter\n" "--arm[=%1$s]\v%3$s\r"
" --armthumb[=OPTS] ARM-Thumb BCJ filter\n" "--armthumb[=%1$s]\v%4$s\r"
" --arm64[=OPTS] ARM64 BCJ filter\n" "--arm64[=%1$s]\v%5$s\r"
" --powerpc[=OPTS] PowerPC BCJ filter (big endian only)\n" "--powerpc[=%1$s]\v%6$s\r"
" --ia64[=OPTS] IA-64 (Itanium) BCJ filter\n" "--ia64[=%1$s]\v%7$s\r"
" --sparc[=OPTS] SPARC BCJ filter\n" "--sparc[=%1$s]\v%8$s\r"
" --riscv[=OPTS] RISC-V BCJ filter\n" "--riscv[=%1$s]\v%9$s\r"
" Valid OPTS for all BCJ filters:\n" "\v%10$s",
" start=NUM start offset for conversions (default=0)")); _("OPTS"),
W_("x86 BCJ filter (32-bit and 64-bit)"),
W_("ARM BCJ filter"),
W_("ARM-Thumb BCJ filter"),
W_("ARM64 BCJ filter"),
W_("PowerPC BCJ filter (big endian only)"),
W_("IA-64 (Itanium) BCJ filter"),
W_("SPARC BCJ filter"),
W_("RISC-V BCJ filter"),
W_("Valid OPTS for all BCJ filters:"));
e |= tuklib_wrapf(stdout, &wrap3,
"start=%s\v%s",
_("NUM"),
W_("start offset for conversions (default=0)"));
#if defined(HAVE_ENCODER_DELTA) || defined(HAVE_DECODER_DELTA) #if defined(HAVE_ENCODER_DELTA) || defined(HAVE_DECODER_DELTA)
puts(_( e |= tuklib_wrapf(stdout, &wrap2,
"\n" "\n"
" --delta[=OPTS] Delta filter; valid OPTS (valid values; default):\n" "--delta[=%s]\v%s",
" dist=NUM distance between bytes being subtracted\n" _("OPTS"),
" from each other (1-256; 1)")); W_("Delta filter; valid OPTS "
"(valid values; default):"));
e |= tuklib_wrapf(stdout, &wrap3,
"dist=%s\v%s \b(1-256; 1)\b",
_("NUM"),
W_("distance between bytes being subtracted "
"from each other"));
#endif #endif
} }
if (long_help)
puts(_("\n Other options:\n"));
puts(_(
" -q, --quiet suppress warnings; specify twice to suppress errors too\n"
" -v, --verbose be verbose; specify twice for even more verbose"));
if (long_help) { if (long_help) {
puts(_( putchar('\n');
" -Q, --no-warn make warnings not affect the exit status")); e |= tuklib_wraps(stdout, &wrap1, W_("Other options:"));
puts(_( putchar('\n');
" --robot use machine-parsable messages (useful for scripts)"));
puts("");
puts(_(
" --info-memory display the total amount of RAM and the currently active\n"
" memory usage limits, and exit"));
puts(_(
" -h, --help display the short help (lists only the basic options)\n"
" -H, --long-help display this long help and exit"));
} else {
puts(_(
" -h, --help display this short help and exit\n"
" -H, --long-help display the long help (lists also the advanced options)"));
} }
puts(_( e |= tuklib_wrapf(stdout, &wrap2,
" -V, --version display the version number and exit")); "-q, --quiet\v%s\r"
"-v, --verbose\v%s",
W_("suppress warnings; specify twice to suppress errors too"),
W_("be verbose; specify twice for even more verbose"));
puts(_("\nWith no FILE, or when FILE is -, read standard input.\n")); if (long_help) {
e |= tuklib_wrapf(stdout, &wrap2,
"-Q, --no-warn\v%s\r"
" --robot\v%s\r"
"\n"
" --info-memory\v%s\r"
"-h, --help\v%s\r"
"-H, --long-help\v%s",
W_("make warnings not affect the exit status"),
W_("use machine-parsable messages (useful for scripts)"),
W_("display the total amount of RAM and the currently active "
"memory usage limits, and exit"),
W_("display the short help (lists only the basic options)"),
W_("display this long help and exit"));
} else {
e |= tuklib_wrapf(stdout, &wrap2,
"-h, --help\v%s\r"
"-H, --long-help\v%s",
W_("display this short help and exit"),
W_("display the long help (lists also the advanced options)"));
}
// TRANSLATORS: This message indicates the bug reporting address e |= tuklib_wrapf(stdout, &wrap2, "-V, --version\v%s",
// for this package. Please add _another line_ saying W_("display the version number and exit"));
// "Report translation bugs to <...>\n" with the email or WWW
// address for translation bugs. Thanks. putchar('\n');
printf(_("Report bugs to <%s> (in English or Finnish).\n"), e |= tuklib_wraps(stdout, &wrap0,
PACKAGE_BUGREPORT); W_("With no FILE, or when FILE is -, read standard input."));
printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL); putchar('\n');
e |= tuklib_wrapf(stdout, &wrap0,
// TRANSLATORS: This message indicates the bug reporting
// address for this package. Please add another line saying
// "\nReport translation bugs to <...>." with the email or WWW
// address for translation bugs. Thanks!
W_("Report bugs to <%s> (in English or Finnish)."),
PACKAGE_BUGREPORT);
e |= tuklib_wrapf(stdout, &wrap0,
// TRANSLATORS: The first %s is the name of this software.
// The second <%s> is an URL.
W_("%s home page: <%s>"), PACKAGE_NAME, PACKAGE_URL);
#if LZMA_VERSION_STABILITY != LZMA_VERSION_STABILITY_STABLE #if LZMA_VERSION_STABILITY != LZMA_VERSION_STABILITY_STABLE
puts(_( e |= tuklib_wraps(stdout, &wrap0, W_(
"THIS IS A DEVELOPMENT VERSION NOT INTENDED FOR PRODUCTION USE.")); "THIS IS A DEVELOPMENT VERSION NOT INTENDED FOR PRODUCTION USE."));
#endif #endif
detect_wrapping_errors(e);
tuklib_exit(E_SUCCESS, E_ERROR, verbosity != V_SILENT); tuklib_exit(E_SUCCESS, E_ERROR, verbosity != V_SILENT);
} }
@ -1150,20 +1281,25 @@ message_help(bool long_help)
extern void extern void
message_filters_help(void) message_filters_help(void)
{ {
static const struct tuklib_wrap_opt wrap = { .right_margin = 76 };
char *encoder_options; char *encoder_options;
if (lzma_str_list_filters(&encoder_options, LZMA_VLI_UNKNOWN, if (lzma_str_list_filters(&encoder_options, LZMA_VLI_UNKNOWN,
LZMA_STR_ENCODER, NULL) != LZMA_OK) LZMA_STR_ENCODER, NULL) != LZMA_OK)
message_bug(); message_bug();
if (!opt_robot) { if (!opt_robot) {
puts(_( int e = tuklib_wrapf(stdout, &wrap,
"Filter chains are set using the --filters=FILTERS or\n" W_("Filter chains are set using the --filters=FILTERS or "
"--filters1=FILTERS ... --filters9=FILTERS options. Each filter in the chain\n" "--filters1=FILTERS ... --filters9=FILTERS options. "
"can be separated by spaces or '--'. Alternatively a preset <0-9>[e] can be\n" "Each filter in the chain can be separated by spaces or '--'. "
"specified instead of a filter chain.\n" "Alternatively a preset %s can be specified instead of a filter chain."),
)); "<0-9>[e]");
putchar('\n');
e |= tuklib_wraps(stdout, &wrap,
W_("The supported filters and their options are:"));
puts(_("The supported filters and their options are:")); detect_wrapping_errors(e);
} }
puts(encoder_options); puts(encoder_options);