666 lines
20 KiB
C
666 lines
20 KiB
C
/*
|
|
* Output command-line help to stdout.
|
|
*
|
|
* Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023-2026 Andrew Wood
|
|
*
|
|
* License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "pv.h"
|
|
#include <stdio.h>
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#if defined(ENABLE_NLS) && defined(HAVE_WCHAR_H)
|
|
#include <wchar.h>
|
|
#if defined(HAVE_WCTYPE_H)
|
|
#include <wctype.h>
|
|
#endif
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Return the number of display columns needed to show the given
|
|
* null-terminated string.
|
|
*
|
|
* If NLS is disabled, or the string cannot be converted, this is just the
|
|
* same as "strlen()".
|
|
*/
|
|
static size_t display_width(const char *string)
|
|
{
|
|
size_t bytes;
|
|
|
|
if (NULL == string)
|
|
return 0;
|
|
|
|
bytes = strlen(string); /* flawfinder: ignore */
|
|
/*
|
|
* flawfinder rationale: it is explicitly required of the caller to
|
|
* provide a null-terminated string.
|
|
*/
|
|
|
|
return pv_strwidth(string, bytes);
|
|
}
|
|
|
|
|
|
/*
|
|
* The 7-bit ASCII version of display_word_wrap() below - does not
|
|
* understand multi-byte characters.
|
|
*/
|
|
static void display_word_wrap_7bit(const char *string, size_t display_width, size_t first_line_start,
|
|
size_t left_margin)
|
|
{
|
|
const char *start;
|
|
const char *end;
|
|
size_t wrap_at_width;
|
|
|
|
if (NULL == string)
|
|
return;
|
|
|
|
start = string;
|
|
wrap_at_width = display_width;
|
|
if (wrap_at_width > first_line_start)
|
|
wrap_at_width -= first_line_start;
|
|
|
|
/* Wrap lines that are too long. */
|
|
while (strlen(start) > wrap_at_width) { /* flawfinder: ignore */
|
|
/* flawfinder rationale: see above. */
|
|
|
|
/*
|
|
* Find the last space before the end of the display line,
|
|
* or if there isn't one, behave as if there was one at the
|
|
* end of the display line.
|
|
*/
|
|
end = start + wrap_at_width;
|
|
while ((end > start) && (end[0] != ' '))
|
|
end--;
|
|
if (end == start) {
|
|
end = start + wrap_at_width;
|
|
} else {
|
|
end++;
|
|
}
|
|
/* Display the string up to that space. */
|
|
printf("%.*s", (int) (end - start), start);
|
|
if (end == start)
|
|
end++;
|
|
start = end;
|
|
/*
|
|
* If there's more text left, start a new display line and
|
|
* pad it with spaces to the left margin, and change the
|
|
* wrap width to the display width minus the left margin.
|
|
*/
|
|
if (start[0] != '\0') {
|
|
printf("\n%*s", (int) left_margin, "");
|
|
if (display_width > left_margin) {
|
|
wrap_at_width = display_width - left_margin;
|
|
} else {
|
|
wrap_at_width = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Display the remainder of the string. */
|
|
printf("%s", start);
|
|
}
|
|
|
|
|
|
/*
|
|
* Output a null-terminated string to standard output, word wrapping to
|
|
* "display_width" display character positions, assuming that the first line
|
|
* starts with the cursor already at "first_line_start" display character
|
|
* positions from the left, and left-padding any new lines after the first
|
|
* one with "left_margin" spaces.
|
|
*
|
|
* Wide characters are handled if NLS is enabled, but if they can't be, this
|
|
* falls back to a version which just counts bytes as characters.
|
|
*/
|
|
static void display_word_wrap(const char *string, size_t display_width, size_t first_line_start, size_t left_margin)
|
|
{
|
|
#if defined(ENABLE_NLS) && defined(HAVE_WCHAR_H)
|
|
size_t wrap_at_width;
|
|
size_t wide_char_count;
|
|
size_t wide_string_buffer_size;
|
|
wchar_t *wide_string;
|
|
size_t start_idx, chars_remaining;
|
|
|
|
debug("[%s], display_width=%d, first_line_start=%d, left_margin=%d", string, (int) display_width,
|
|
(int) first_line_start, (int) left_margin);
|
|
|
|
if (NULL == string)
|
|
return;
|
|
|
|
/* Calculate how many wide characters are in the multibyte string. */
|
|
|
|
/*@-nullpass@ */
|
|
/* splint note: see mbstowcs() call in pv_strwidth(). */
|
|
wide_char_count = mbstowcs(NULL, string, 0);
|
|
/*@+nullpass@ */
|
|
if (wide_char_count == (size_t) -1) {
|
|
debug("%s: %s: %s", "mbstowcs", string, strerror(errno));
|
|
display_word_wrap_7bit(string, display_width, first_line_start, left_margin);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Allocate a buffer in which to convert the multibyte string to a
|
|
* wide-character string, plus one character for zero padding.
|
|
*/
|
|
|
|
wide_string_buffer_size = sizeof(*wide_string) * (1 + wide_char_count);
|
|
wide_string = malloc(wide_string_buffer_size);
|
|
if (NULL == wide_string) {
|
|
perror("malloc");
|
|
display_word_wrap_7bit(string, display_width, first_line_start, left_margin);
|
|
return;
|
|
}
|
|
memset(wide_string, 0, wide_string_buffer_size);
|
|
|
|
/* Convert the multibyte string to a wide-character string. */
|
|
|
|
if (mbstowcs(wide_string, string, 1 + wide_char_count) == (size_t) -1) {
|
|
debug("%s: %s: %s", "mbstowcs", string, strerror(errno));
|
|
free(wide_string);
|
|
display_word_wrap_7bit(string, display_width, first_line_start, left_margin);
|
|
return;
|
|
}
|
|
|
|
start_idx = 0;
|
|
wrap_at_width = display_width;
|
|
if (wrap_at_width > first_line_start)
|
|
wrap_at_width -= first_line_start;
|
|
chars_remaining = wide_char_count;
|
|
|
|
/* Wrap lines that are too long. */
|
|
|
|
/*@-unrecog@ *//* splint doesn't see the prototype for wcswidth(). */
|
|
while (chars_remaining > 0 && wcswidth(&(wide_string[start_idx]), chars_remaining) > (int) wrap_at_width) {
|
|
/*@+unrecog@ */
|
|
size_t next_idx, end_idx;
|
|
|
|
/*
|
|
* Find the last space before the end of the display line,
|
|
* or if there isn't one, behave as if there was one at the
|
|
* end of the display line.
|
|
*/
|
|
|
|
end_idx = start_idx + wrap_at_width;
|
|
while ((end_idx > start_idx) && (!iswspace(wide_string[end_idx])))
|
|
end_idx--;
|
|
if (end_idx == start_idx) {
|
|
end_idx = start_idx + wrap_at_width;
|
|
} else {
|
|
end_idx++;
|
|
}
|
|
|
|
next_idx = end_idx;
|
|
if (end_idx == start_idx)
|
|
next_idx++;
|
|
|
|
/* Display the string up to that space, a character at a time. */
|
|
while (start_idx < end_idx && start_idx < wide_char_count) {
|
|
char multi_byte_string[MB_CUR_MAX + 1]; /* flawfinder: ignore */
|
|
/*
|
|
* flawfinder rationale: the array is explicitly
|
|
* cleared, large enough according to the wctomb()
|
|
* manual, and the string is explicitly terminated.
|
|
*/
|
|
memset(multi_byte_string, 0, MB_CUR_MAX + 1);
|
|
if (wctomb(multi_byte_string, wide_string[start_idx]) >= 0) {
|
|
multi_byte_string[MB_CUR_MAX] = '\0';
|
|
printf("%s", multi_byte_string);
|
|
}
|
|
start_idx++;
|
|
}
|
|
|
|
start_idx = next_idx;
|
|
/*
|
|
* If there's more text left, start a new display line and
|
|
* pad it with spaces to the left margin, and change the
|
|
* wrap width to the display width minus the left margin.
|
|
*/
|
|
if (start_idx < wide_char_count) {
|
|
printf("\n%*s", (int) left_margin, "");
|
|
if (display_width > left_margin) {
|
|
wrap_at_width = display_width - left_margin;
|
|
} else {
|
|
wrap_at_width = 1;
|
|
}
|
|
chars_remaining = wide_char_count - start_idx;
|
|
} else {
|
|
/* Explicitly set to 0 to avoid underflow. */
|
|
chars_remaining = 0;
|
|
}
|
|
}
|
|
|
|
/* Display the remainder of the string, a character at a time. */
|
|
while (start_idx < wide_char_count) {
|
|
char multi_byte_string[MB_CUR_MAX + 1]; /* flawfinder: ignore */
|
|
/* flawfinder rationale as above. */
|
|
memset(multi_byte_string, 0, MB_CUR_MAX + 1);
|
|
if (wctomb(multi_byte_string, wide_string[start_idx]) >= 0) {
|
|
multi_byte_string[MB_CUR_MAX] = '\0';
|
|
printf("%s", multi_byte_string);
|
|
}
|
|
start_idx++;
|
|
}
|
|
|
|
free(wide_string);
|
|
|
|
#else /* ! defined(ENABLE_NLS) && defined(HAVE_WCHAR_H) */
|
|
display_word_wrap_7bit(string, display_width, first_line_start, left_margin);
|
|
#endif /* defined(ENABLE_NLS) && defined(HAVE_WCHAR_H) */
|
|
}
|
|
|
|
|
|
/*
|
|
* Structure holding the displayed descriptions of each option - the short
|
|
* option such as "-s", its long counterpart such as "--size", the name of
|
|
* its argument such as "SIZE", and the description of what the option does.
|
|
* Any of them may be NULL.
|
|
*
|
|
* In the initialiser for this structure, translatable strings are wrapped
|
|
* with N_() to indicate that they are to be translated into the operator's
|
|
* language by this function at run-time.
|
|
*
|
|
* Note that "opt_short" and "opt_long" should never be marked as
|
|
* translatable, as they must remain consistent across all locales.
|
|
*
|
|
* The list is terminated with a NULL opt_short value - to leave a gap, set
|
|
* opt_short to an empty string instead.
|
|
*/
|
|
struct option_definition_s {
|
|
/*@null@ */ const char *opt_short;
|
|
/*@null@ */ const char *opt_long;
|
|
/*@null@ *//*@observer@ */ const char *opt_argument;
|
|
/*@null@ *//*@observer@ */ const char *opt_description;
|
|
struct {
|
|
/*
|
|
* Structure holding the width, in display character
|
|
* positions, of the individual parts of the description of
|
|
* each option - calculated after initialisation, so that
|
|
* translation can be performed.
|
|
*/
|
|
size_t opt_short;
|
|
size_t opt_long;
|
|
size_t opt_argument;
|
|
size_t opt_description;
|
|
} width;
|
|
};
|
|
|
|
|
|
/*
|
|
* Display command-line help.
|
|
*/
|
|
void display_help(void)
|
|
{
|
|
struct option_definition_s option_definitions[] = {
|
|
{ "-p", "--progress", NULL,
|
|
N_("show progress bar"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-t", "--timer", NULL,
|
|
N_("show elapsed time"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-e", "--eta", NULL,
|
|
N_("show estimated time of arrival (completion)"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-I", "--fineta", NULL,
|
|
N_("show absolute estimated time of arrival (completion)"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-r", "--rate", NULL,
|
|
N_("show data transfer rate counter"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-a", "--average-rate", NULL,
|
|
N_("show data transfer average rate counter"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-b", "--bytes", NULL,
|
|
N_("show number of bytes transferred"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-T", "--buffer-percent", NULL,
|
|
N_("show percentage of transfer buffer in use"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-A", "--last-written", _("NUM"),
|
|
N_("show NUM bytes last written"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-F", "--format", N_("FORMAT"),
|
|
N_("set output format to FORMAT"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-n", "--numeric", NULL,
|
|
N_("output percentages, not visual information"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-q", "--quiet", NULL,
|
|
N_("do not output any transfer information at all"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "", NULL, NULL, NULL, { 0, 0, 0, 0} },
|
|
{ "-8", "--bits", NULL,
|
|
N_("show number of bits transferred"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-k", "--si", NULL,
|
|
N_("treat suffixes as multiples of 1000 rather than 1024"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-W", "--wait", NULL,
|
|
N_("display nothing until first byte transferred"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-D", "--delay-start", N_("SEC"),
|
|
N_("display nothing until SEC seconds have passed"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-s", "--size", N_("SIZE"),
|
|
N_("set estimated data size to SIZE bytes"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-g", "--gauge", NULL,
|
|
N_("if size unknown, show rate vs max rate"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-l", "--line-mode", NULL,
|
|
N_("count lines instead of bytes"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-0", "--null", NULL,
|
|
N_("lines are null-terminated"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-i", "--interval", N_("SEC"),
|
|
N_("update every SEC seconds"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-m", "--average-rate-window", N_("SEC"),
|
|
N_("compute average rate over past SEC seconds (default 30s)"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-w", "--width", N_("WIDTH"),
|
|
N_("assume terminal is WIDTH characters wide"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-H", "--height", N_("HEIGHT"),
|
|
N_("assume terminal is HEIGHT rows high"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-N", "--name", N_("NAME"),
|
|
N_("prefix visual information with NAME"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-u", "--bar-style", N_("STYLE"),
|
|
N_("set default bar style to STYLE"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-x", "--extra-display", N_("SPEC"),
|
|
N_("also send progress to SPEC"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-9", "--conemu", NULL,
|
|
N_("set terminal progress state with OSC 9;4 (ConEmu)"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-v", "--stats", NULL,
|
|
N_("output transfer statistics at the end"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-f", "--force", NULL,
|
|
N_("output even if standard error is not a terminal"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-c", "--cursor", NULL,
|
|
N_("use cursor positioning escape sequences"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "", NULL, NULL, NULL, { 0, 0, 0, 0} },
|
|
{ "-o", "--output", N_("FILE"),
|
|
N_("write output to FILE instead of stdout"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-L", "--rate-limit", N_("RATE"),
|
|
N_("limit transfer to RATE bytes per second"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-B", "--buffer-size", N_("BYTES"),
|
|
N_("use a buffer size of BYTES"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-C", "--no-splice", NULL,
|
|
N_("never use splice(), always use read/write"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-J", "--pipe-buffer-size", N_("BYTES"),
|
|
N_("set the pipe buffer size to BYTES"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-E", "--skip-errors", NULL,
|
|
N_("skip read errors in input"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-Z", "--error-skip-block", N_("BYTES"),
|
|
N_("skip errors in BYTES blocks at a time"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-S", "--stop-at-size", NULL,
|
|
N_("stop after --size bytes have been transferred"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-Y", "--sync", NULL,
|
|
N_("flush cache to disk after every write"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-K", "--direct-io", NULL,
|
|
N_("use direct I/O to bypass cache"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-O", "--sparse", NULL,
|
|
N_("try to seek instead of writing null bytes"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-X", "--discard", NULL,
|
|
N_("discard input instead of writing to output"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-U", "--store-and-forward", N_("FILE"),
|
|
N_("write all input to FILE before writing to output"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "", NULL, NULL, NULL, { 0, 0, 0, 0} },
|
|
{ "-d", "--watchfd", N_("PID[:FD]|=NAME|@LISTFILE"),
|
|
N_("watch file FD opened by process PID"),
|
|
{ 0, 0, 0, 0} },
|
|
#ifdef PV_REMOTE_CONTROL
|
|
{ "-R", "--remote", N_("PID"),
|
|
N_("update settings of process PID"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-Q", "--query", N_("PID"),
|
|
N_("show progress of process PID"),
|
|
{ 0, 0, 0, 0} },
|
|
#endif /* PV_REMOTE_CONTROL */
|
|
{ "-M", "--monitor", "in|0|out|1|both|2",
|
|
N_("run a command and monitor its standard input, output, or both"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "", NULL, NULL, NULL, { 0, 0, 0, 0} },
|
|
{ "-P", "--pidfile", N_("FILE"),
|
|
N_("save process ID in FILE"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-h", "--help", NULL,
|
|
N_("show this help and exit"),
|
|
{ 0, 0, 0, 0} },
|
|
{ "-V", "--version", NULL,
|
|
N_("show version information and exit"),
|
|
{ 0, 0, 0, 0} },
|
|
#ifdef ENABLE_DEBUGGING
|
|
{ "-!", "--debug", N_("FILE"),
|
|
N_("write debug logs to FILE"),
|
|
{ 0, 0, 0, 0} },
|
|
#endif
|
|
{ NULL, NULL, NULL, NULL, { 0, 0, 0, 0} }
|
|
};
|
|
unsigned int option_index;
|
|
size_t widest_option_width = 0;
|
|
size_t description_left_margin = 0;
|
|
size_t min_description_width = 50;
|
|
size_t right_margin = 77;
|
|
const char *program_description;
|
|
const char *bug_report_note;
|
|
char *format_sequences = NULL;
|
|
unsigned int terminal_width = 0, terminal_height = 0;
|
|
|
|
pv_screensize(&terminal_width, &terminal_height);
|
|
if (terminal_width > 5) {
|
|
right_margin = (size_t) (terminal_width - 3);
|
|
}
|
|
|
|
/*@-formatconst@ */
|
|
/*
|
|
* splint note: unavoidable use of %s in translated string. Should
|
|
* be hard to exploit - the message catalogue would have to be
|
|
* replaced or forced to load from another location.
|
|
*/
|
|
printf(_("Usage: %s [OPTION] [FILE]..."), PACKAGE_NAME);
|
|
/*@+formatconst@ */
|
|
|
|
printf("\n");
|
|
|
|
/*@-mustfreefresh@ */
|
|
/*
|
|
* splint note: the gettext calls made by _() cause memory leak
|
|
* warnings, but in this case it's unavoidable, and mitigated by the
|
|
* fact that each string is only translated once.
|
|
*/
|
|
program_description = _("Concatenate FILE(s), or standard input, to standard output, with monitoring.");
|
|
if (NULL != program_description) {
|
|
display_word_wrap(program_description, right_margin, 0, 0);
|
|
printf("\n");
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
/*
|
|
* Translate the help text, and calculate the displayed width of
|
|
* each part of each option definition. The total display width of
|
|
* the short option, long option, and option argument together form
|
|
* the "option" width. The widest one is used to calculate the left
|
|
* margin for all of the descriptions to start at.
|
|
*/
|
|
for (option_index = 0; NULL != option_definitions[option_index].opt_short; option_index++) {
|
|
struct option_definition_s *definition;
|
|
size_t option_width;
|
|
|
|
definition = &(option_definitions[option_index]);
|
|
option_width = 0;
|
|
|
|
definition->width.opt_short = display_width(definition->opt_short);
|
|
definition->width.opt_long = 0;
|
|
definition->width.opt_argument = 0;
|
|
definition->width.opt_description = 0;
|
|
|
|
if (NULL != definition->opt_long) {
|
|
definition->width.opt_long = display_width(definition->opt_long);
|
|
}
|
|
|
|
if (NULL != definition->opt_argument) {
|
|
/*@observer@ */ const char *translated;
|
|
translated = _(definition->opt_argument);
|
|
if (NULL != translated) {
|
|
definition->opt_argument = translated;
|
|
}
|
|
definition->width.opt_argument = display_width(definition->opt_argument);
|
|
}
|
|
|
|
if (NULL != definition->opt_description) {
|
|
/*@observer@ */ const char *translated;
|
|
translated = _(definition->opt_description);
|
|
if (NULL != translated) {
|
|
definition->opt_description = translated;
|
|
}
|
|
definition->width.opt_description = display_width(definition->opt_description);
|
|
}
|
|
|
|
/*
|
|
* The option_width is padded with a left margin of 2
|
|
* spaces, a ", " between the short and long options, a
|
|
* space between the long option and the argument, and two
|
|
* spaces after the argument:
|
|
*
|
|
* " <short>, <long> <arg> <description>"
|
|
*
|
|
* If getopt_long() is unavailable then ", <long>" is
|
|
* omitted.
|
|
*/
|
|
option_width += 2 + definition->width.opt_short; /* " short" */
|
|
#ifdef HAVE_GETOPT_LONG
|
|
option_width += 2 + definition->width.opt_long; /* ", <long>" */
|
|
#endif
|
|
option_width += 1 + definition->width.opt_argument; /* " ARG" */
|
|
option_width += 2; /* final 2 spaces */
|
|
|
|
if (option_width > widest_option_width) {
|
|
widest_option_width = option_width;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set the left margin for the option descriptions, based on the
|
|
* widest option width, or (right margin - min_description_width),
|
|
* whichever is less, so that there is always room for
|
|
* "min_description_width" characters of description. If there's
|
|
* not even room for that, set the left margin to 2.
|
|
*/
|
|
description_left_margin = 2;
|
|
if (right_margin > (min_description_width + 2))
|
|
description_left_margin = right_margin - min_description_width;
|
|
if (widest_option_width < description_left_margin) {
|
|
description_left_margin = widest_option_width;
|
|
}
|
|
|
|
debug("%s: description_left_margin=%d, widest_option_width=%d, right_margin=%d", "help display",
|
|
(int) description_left_margin, (int) widest_option_width, (int) right_margin);
|
|
|
|
/*
|
|
* Display each of the option definitions, word wrapping the
|
|
* descriptions.
|
|
*/
|
|
for (option_index = 0; NULL != option_definitions[option_index].opt_short; option_index++) {
|
|
struct option_definition_s *definition;
|
|
size_t option_width;
|
|
|
|
definition = &(option_definitions[option_index]);
|
|
option_width = 0;
|
|
|
|
if (definition->width.opt_short > 0 && NULL != definition->opt_short) {
|
|
printf(" %s", definition->opt_short);
|
|
option_width += 2 + definition->width.opt_short;
|
|
}
|
|
#ifdef HAVE_GETOPT_LONG
|
|
if (definition->width.opt_long > 0 && NULL != definition->opt_long) {
|
|
printf(", %s", definition->opt_long);
|
|
option_width += 2 + definition->width.opt_long;
|
|
}
|
|
#endif
|
|
if (definition->width.opt_argument > 0 && NULL != definition->opt_argument) {
|
|
printf(" %s", definition->opt_argument);
|
|
option_width += 1 + definition->width.opt_argument;
|
|
}
|
|
|
|
/* Just start a new line if there's no description. */
|
|
if ((0 == definition->width.opt_description) || (NULL == definition->opt_description)) {
|
|
printf("\n");
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* If the option (with 2 trailing spaces) is too wide, start
|
|
* a new line for the description. Either way, pad with
|
|
* spaces up to the description left margin.
|
|
*/
|
|
if ((option_width + 2) > description_left_margin) {
|
|
printf("\n%*s", (int) description_left_margin, "");
|
|
} else if (option_width < description_left_margin) {
|
|
printf("%*s", (int) (description_left_margin - option_width), "");
|
|
}
|
|
|
|
/* Output the description, word wrapped. */
|
|
display_word_wrap(definition->opt_description, right_margin, description_left_margin,
|
|
description_left_margin);
|
|
|
|
printf("\n");
|
|
}
|
|
|
|
format_sequences = pv_format_sequences();
|
|
if (NULL != format_sequences) {
|
|
const char *format_sequence_header;
|
|
format_sequence_header = _("Supported format sequences:");
|
|
if (NULL != format_sequence_header) {
|
|
printf("\n");
|
|
display_word_wrap(format_sequence_header, right_margin, 0, 0);
|
|
printf("\n");
|
|
}
|
|
printf("\n ");
|
|
display_word_wrap(format_sequences, right_margin, 2, 2);
|
|
free(format_sequences);
|
|
printf("\n");
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
bug_report_note = _("Please report any bugs to: %s");
|
|
if (NULL != bug_report_note) {
|
|
/*@-formatconst@ */
|
|
/*
|
|
* splint note: see earlier "formatconst" note.
|
|
* flawfinder: same reason.
|
|
*/
|
|
printf(bug_report_note, PACKAGE_BUGREPORT); /* flawfinder: ignore */
|
|
/*@+formatconst@ */
|
|
}
|
|
|
|
printf("\n");
|
|
}
|