3 # Generate a short man page from --help and --version output.
4 # Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2009,
5 # 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2020, 2021, 2022, 2025
6 # Free Software Foundation, Inc.
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 3, or (at your option)
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, see <https://www.gnu.org/licenses/>.
21 # Written by Brendan O'Dea <bod@debian.org>
22 # Available from https://ftp.gnu.org/gnu/help2man/
27 use Text
::ParseWords
qw(shellwords);
28 use Text
::Tabs
qw(expand);
29 use POSIX
qw(strftime setlocale LC_ALL);
31 my $this_program = 'help2man';
32 my $this_version = '1.50.1';
38 die "$this_program: no locale support (Locale::gettext required)\n"
39 unless $locale eq 'C';
44 sub enc_user
{ $_[0] }
45 sub kark
{ die +(sprintf shift, @_), "\n" }
51 sub fix_italic_spacing
;
54 my $version_info = enc_user
sprintf _
(<<'EOT'), $this_program, $this_version;
57 Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2009,
58 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2020, 2021, 2022, 2025 Free
59 Software Foundation, Inc.
60 This is free software; see the source for copying conditions. There is NO
61 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
63 Written by Brendan O'Dea <bod@debian.org>
66 my $help_info = enc_user
sprintf _
(<<'EOT'), $this_program, $this_program;
67 `%s' generates a man page out of `--help' and `--version' output.
69 Usage: %s [OPTION]... EXECUTABLE
71 -n, --name=STRING description for the NAME paragraph
72 -s, --section=SECTION section number for manual page (1, 6, 8)
73 -m, --manual=TEXT name of manual (User Commands, ...)
74 -S, --source=TEXT source of program (FSF, Debian, ...)
75 -L, --locale=STRING select locale (default "C")
76 -i, --include=FILE include material from `FILE'
77 -I, --opt-include=FILE include material from `FILE' if it exists
78 -o, --output=FILE send output to `FILE'
79 -p, --info-page=TEXT name of Texinfo manual
80 -N, --no-info suppress pointer to Texinfo manual
81 -l, --libtool exclude the `lt-' from the program name
82 --loose-indent relax matching of indented continuation lines
83 -b, --bold-refs apply bold style to references
84 --help print this help, then exit
85 --version print version number, then exit
87 EXECUTABLE should accept `--help' and `--version' options and produce output on
88 stdout although alternatives may be specified using:
90 -h, --help-option=STRING help option string
91 -v, --version-option=STRING version option string
92 --version-string=STRING version string
93 --no-discard-stderr include stderr when parsing option output
95 Report bugs to <bug-help2man@gnu.org>.
101 my $help_option = '--help';
102 my $version_option = '--version';
103 my $loose_indent = 0;
104 my $discard_stderr = 1;
105 my ($opt_name, @opt_include, $opt_output, $opt_info, $opt_no_info, $opt_libtool,
106 $opt_bold_refs, $version_text);
109 'n|name=s' => \
$opt_name,
110 's|section=s' => \
$section,
111 'm|manual=s' => \
$manual,
112 'S|source=s' => \
$source,
113 'L|locale=s' => sub { configure_locale
pop },
114 'i|include=s' => sub { push @opt_include, [ pop, 1 ] },
115 'I|opt-include=s' => sub { push @opt_include, [ pop, 0 ] },
116 'o|output=s' => \
$opt_output,
117 'p|info-page=s' => \
$opt_info,
118 'N|no-info' => \
$opt_no_info,
119 'l|libtool' => \
$opt_libtool,
120 'loose-indent!' => \
$loose_indent,
121 'b|bold-refs' => \
$opt_bold_refs,
122 'help' => sub { print $help_info; exit },
123 'version' => sub { print $version_info; exit },
124 'h|help-option=s' => \
$help_option,
125 'v|version-option=s' => \
$version_option,
126 'version-string=s' => \
$version_text,
127 'discard-stderr!' => \
$discard_stderr,
131 Getopt
::Long
::config
('bundling');
132 die $help_info unless GetOptions
%opt_def and @ARGV == 1;
137 my %append_match = ();
138 my @sections = (); # retain order of include file or in-line *section*s
140 # Process include file (if given). Format is:
142 # Optional initial text, ignored. May include lines starting with `-'
143 # which are processed as options.
146 # Verbatim text to be included in the named section. By default at
147 # the start, but in the case of `name' and `synopsis' the content
148 # will replace the autogenerated contents.
151 # Verbatim text to be inserted at the start of the named section.
154 # Verbatim text to replace the named section.
157 # Verbatim text to be appended to the end of the named section.
160 # Verbatim text for inclusion below a paragraph matching `pattern'.
165 my ($inc, $required) = @
{shift @opt_include};
167 next unless -f
$inc or $required;
168 kark N_
("%s: can't open `%s' (%s)"), $this_program, $inc, $!
169 unless open INC
, $inc;
176 # Convert input to internal Perl format, so that multibyte
177 # sequences are treated as single characters.
181 if (/^\[([^]]+)\]\s*$/)
187 # Handle explicit [<section], [=section] and [>section].
188 if ($key =~ s/^([<>=])\s*//)
190 if ($1 eq '>') { $hash = \
%append; }
191 elsif ($1 eq '=') { $hash = \
%replace; }
193 # NAME/SYNOPSIS replace by default.
194 elsif ($key eq _
('NAME') or $key eq _
('SYNOPSIS'))
203 push @sections, $key;
208 if (m!^/(.*)/([ims]*)\s*$!)
210 my $pat = $2 ?
"(?$2)$1" : $1;
213 eval { $key = qr
($pat) };
216 $@
=~ s/ at .*? line \d.*//;
220 $hash = \
%append_match;
224 # Check for options before the first section--anything else is
225 # silently ignored, allowing the first for comments and
232 local @ARGV = shellwords
$_;
244 kark N_
("%s: no valid information found in `%s'"), $this_program, $inc
248 # Compress trailing blank lines.
249 for my $hash (\
(%include, %replace, %append, %append_match))
251 for (keys %$hash) { $hash->{$_} =~ s/\n+$/\n/ }
254 # Grab help and version info from executable.
255 my $help_text = get_option_value
$ARGV[0], $help_option;
256 $version_text ||= get_option_value
$ARGV[0], $version_option;
258 # By default the generated manual pages will include the current date. This may
259 # however be overridden by setting the environment variable $SOURCE_DATE_EPOCH
260 # to an integer value of the seconds since the UNIX epoch. This is primarily
261 # intended to support reproducible builds (wiki.debian.org/ReproducibleBuilds)
262 # and will additionally ensure that the output date string is UTC.
263 my $epoch_secs = time;
264 if (exists $ENV{SOURCE_DATE_EPOCH
} and $ENV{SOURCE_DATE_EPOCH
} =~ /^(\d+)$/)
270 # Translators: the following message is a strftime(3) format string, which in
271 # the English version expands to the month as a word and the full year. It
272 # is used on the footer of the generated manual pages. If in doubt, you may
273 # just use %x as the value (which should be the full locale-specific date).
274 my $date = strftime _
("%B %Y"), localtime $epoch_secs;
275 my $program = program_basename
$ARGV[0];
276 my $package = $program;
281 unlink $opt_output or kark N_
("%s: can't unlink %s (%s)"),
282 $this_program, $opt_output, $! if -e
$opt_output;
284 open STDOUT
, ">$opt_output"
285 or kark N_
("%s: can't create %s (%s)"), $this_program, $opt_output, $!;
288 # The first line of the --version information is assumed to be in one
289 # of the following formats:
292 # <program> <version>
293 # {GNU,Free} <program> <version>
294 # <program> ({GNU,Free,} <package>) <version>
295 # <program> - {GNU,Free,} <package> <version>
296 # <program> - {GNU,Free,} <package> - <version>
298 # and separated from any copyright/author details by a blank line.
300 ($_, $version_text) = ((split /\n+/, $version_text, 2), '');
302 if (/^(\S+) +\(((?:(?:GNU|Free) +)?[^)]+)\) +(\S.*)$/ or
303 /^(\S+) +- +((?:(?:GNU|Free) +)?\S.*) +- +(\S.*)$/ or
304 /^(\S+) +- +((?:(?:GNU|Free) +)?\S+) +(\S.*)$/)
306 $program = program_basename
$1;
310 elsif (/^((?:GNU|Free) +)?(\S+) +(\S.*)$/)
312 $program = program_basename
$2;
313 $package = $1 ?
"$1$program" : $program;
321 # No info for `info' itself.
322 $opt_no_info = 1 if $program eq 'info';
326 # --name overrides --include contents.
327 $replace{_
('NAME')} = "$program \\- $opt_name\n";
330 # Translators: "NAME", "SYNOPSIS" and other one or two word strings in all
331 # upper case are manual page section headings. The man(1) manual page in your
332 # language, if available should provide the conventional translations.
333 for ($replace{_
('NAME')} || ($include{_
('NAME')} ||= ''))
335 if ($_) # Use first name given as $program
337 $program = $1 if /^([^\s,]+)(?:,?\s*[^\s,\\-]+)*\s+\\?-/;
339 else # Set a default (useless) NAME paragraph.
341 $_ = sprintf _
("%s \\- manual page for %s %s") . "\n", $program,
346 # Man pages traditionally have the page title in caps.
347 my $PROGRAM = uc $program;
349 # Set default page head/footers.
350 $source ||= "$package $version";
355 if (/^(1[Mm]|8)/) { $manual = _
('System Administration Utilities') }
356 elsif (/^6/) { $manual = _
('Games') }
357 else { $manual = _
('User Commands') }
361 # Extract usage clause(s) [if any] for SYNOPSIS.
362 # Translators: "Usage" and "or" here are patterns (regular expressions) which
363 # are used to match the usage synopsis in program output. An example from cp
364 # (GNU coreutils) which contains both strings:
365 # Usage: cp [OPTION]... [-T] SOURCE DEST
366 # or: cp [OPTION]... SOURCE... DIRECTORY
367 # or: cp [OPTION]... -t DIRECTORY SOURCE...
368 my $PAT_USAGE = _
('Usage');
369 my $PAT_USAGE_CONT = _
('or');
370 if ($help_text =~ s/^($PAT_USAGE):( +(\S+))(.*)((?:\n(?: {6}\1| *($PAT_USAGE_CONT): +\S).*)*)//om)
377 for (split /\n/) { s/^ *(($PAT_USAGE_CONT): +)?//o; push @syn, $_ }
383 $synopsis .= ".br\n" if $synopsis;
385 s/^lt-// if $opt_libtool;
387 $synopsis .= ".B $1\n";
389 s/(([][]|\.\.+)+)/\\fR$1\\fI/g;
390 s/^/\\fI/ unless s/^\\fR//;
398 $_ = fix_italic_spacing
$_;
402 $include{_
('SYNOPSIS')} .= $synopsis;
405 # Process text, initial section is DESCRIPTION.
406 my $sect = _
('DESCRIPTION');
407 $_ = "$help_text\n\n$version_text";
409 # Normalise paragraph breaks.
414 # Join hyphenated lines.
415 s/([A-Za-z])-\n *([A-Za-z])/$1$2/g;
417 # Temporarily exchange leading dots, apostrophes and backslashes for
423 # Translators: patterns are used to match common program output. In the source
424 # these strings are all of the form of "my $PAT_something = _('...');" and are
425 # regular expressions. If there is more than one commonly used string, you
426 # may separate alternatives with "|". Spaces in these expressions are written
427 # as " +" to indicate that more than one space may be matched. The string
428 # "(?:[\\w-]+ +)?" in the bug reporting pattern is used to indicate an
429 # optional word, so that either "Report bugs" or "Report _program_ bugs" will
431 my $PAT_BUGS = _
('Report +(?:[\w-]+ +)?bugs|' .
432 'Email +bug +reports +to|' .
433 '.* +online +help:');
434 my $PAT_AUTHOR = _
('Written +by');
435 my $PAT_OPTIONS = _
('Options');
436 my $PAT_ENVIRONMENT = _
('Environment');
437 my $PAT_FILES = _
('Files');
438 my $PAT_EXAMPLES = _
('Examples');
439 my $PAT_FREE_SOFTWARE = _
('This +is +free +software');
440 my $PAT_SEE_ALSO = _
('Full +documentation');
442 # Start a new paragraph (if required) for these.
443 s/([^\n])\n($PAT_BUGS|$PAT_AUTHOR|$PAT_SEE_ALSO) /$1\n\n$2 /og;
445 # Convert iso-8859-1 copyright symbol or (c) to nroff
447 s/^Copyright +(?:\xa9|\([Cc]\))/Copyright \\(co/mg;
449 my $require_mono = 0;
452 # Convert some standard paragraph names.
453 if (s/^($PAT_OPTIONS): *\n+//o)
455 $sect = _
('OPTIONS');
458 if (s/^($PAT_ENVIRONMENT): *\n+//o)
460 $sect = _
('ENVIRONMENT');
463 if (s/^($PAT_FILES): *\n+//o)
468 elsif (s/^($PAT_EXAMPLES): *\n+//o)
470 $sect = _
('EXAMPLES');
474 # Custom section indicated by a line containing "*Section Name*".
475 if (s/^\*(\w(.*\w)?)\* *\n+//)
478 $sect =~ tr/*/ /; # also accept *Section*Name*
479 push @sections, $sect;
486 $sect = _
('COPYRIGHT');
489 # Bug reporting section.
490 elsif (/^($PAT_BUGS) /o)
492 $sect = _
('REPORTING BUGS');
496 elsif (/^($PAT_AUTHOR)/o)
501 elsif (/^($PAT_SEE_ALSO)/o)
503 $sect = _
('SEE ALSO');
507 # Examples, indicated by an indented leading $, % or > are
508 # rendered in a monospace font when using groff in troff mode.
509 if (/^( +)([\$\%>] )\S/)
514 while (s/^$spaces\Q$prefix\E(\S.*)\n*//)
516 $include{$sect} .= "$break\n\\*[mono]$prefix$1\\*[/mono]\n";
526 # Sub-sections have a trailing colon and the second line indented.
527 if (s/^(\S.*:) *\n / /)
529 $matched .= $& if %append_match;
530 $include{$sect} .= qq(.SS
"$1"\n);
536 # Option with description.
537 if (s/^( {1,10}([+-]\S.*?))(?:( +(?!-))|\n( {20,}))(\S.*)\n//)
539 $matched .= $& if %append_match;
540 $indent = set_indent
length ($4 || "$1$3");
541 $content = ".TP\n\x84$2\n\x84$5\n";
544 # Indent may be different on second line.
545 $indent = set_indent
length $& if /^ {20,}/;
549 # Option without description.
550 elsif (s/^ {1,10}([+-]\S.*)\n//)
552 $matched .= $& if %append_match;
553 $content = ".HP\n\x84$1\n";
554 $indent = 80; # not continued
557 # Indented paragraph with tag.
558 elsif (s/^( +(\S.*?) +)(\S.*)\n//)
560 $matched .= $& if %append_match;
561 $indent = set_indent
length $1;
562 $content = ".TP\n\x84$2\n\x84$3\n";
565 # Indented paragraph.
566 elsif (s/^( +)(\S.*)\n//)
568 $matched .= $& if %append_match;
569 $indent = set_indent
length $1;
570 $content = ".IP\n\x84$2\n";
573 # Left justified paragraph.
577 $matched .= $& if %append_match;
578 $content = ".PP\n" if $include{$sect};
582 # Append continuations.
583 while ($indent ?
s/^ {$indent}(\S.*)\n// : s/^(\S.*)\n//)
585 $matched .= $& if %append_match;
586 $content .= "\x84$1\n";
589 # Move to next paragraph.
594 # Leading dot and apostrophe protection.
599 # Examples should be verbatim.
600 unless ($sect eq _
('EXAMPLES'))
603 s/(^|[ (])(-[][\w=-]+)/$1 . convert_option $2/mge;
605 # Italicise filenames: /a/b, $VAR/c/d, ~/e/f
607 (^|[ (]) # space/punctuation before
609 (?
:\
$\w
+|~)?
# leading variable, or tilde
610 (?
:/\w
(?
:[\w
.-]*\w
)?
)+ # path components
612 ($|[ ,;.)]) # space/punctuation after
615 $_ = fix_italic_spacing
$_;
618 # Escape remaining hyphens.
621 if ($sect eq _
('COPYRIGHT'))
623 # Insert line breaks before additional copyright messages
624 # and the disclaimer.
625 s/\n(Copyright |$PAT_FREE_SOFTWARE)/\n.br\n$1/og;
627 elsif ($sect eq _
('REPORTING BUGS'))
629 # Handle multi-line bug reporting sections of the form:
631 # Report <program> bugs to <addr>
632 # GNU <package> home page: <url>
634 s/\n([[:upper:]])/\n.br\n$1/g;
636 elsif ($sect eq _
('SEE ALSO'))
638 # Handle external references of the form:
640 # GNU <package> online resources: <addr>
641 # Full documentation at: <addr>
642 # or available locally via: info ...
644 s/\'/\\(aq/g; # shell quotes for info command
645 s/\n(.)/\n.br\n$1/g; # separate lines for each item
649 # Check if matched paragraph contains /pat/.
652 for my $pat (keys %append_match)
654 if ($matched =~ $pat)
656 $content .= ".PP\n" unless $append_match{$pat} =~ /^\./;
657 $content .= $append_match{$pat};
662 $include{$sect} .= $content;
665 # Refer to the real documentation.
666 unless ($opt_no_info)
668 my $info_page = $opt_info || $program;
670 $sect = _
('SEE ALSO');
671 $include{$sect} .= ".PP\n" if $include{$sect};
672 $include{$sect} .= sprintf _
(<<'EOT'), $program, $program, $info_page;
673 The full documentation for
675 is maintained as a Texinfo manual. If the
679 programs are properly installed at your site, the command
683 should give you access to the complete manual.
687 # Append additional text.
688 while (my ($sect, $text) = each %append)
690 $require_mono++ if $text =~ /\\\*\[mono\]/;
691 $include{$sect} .= $text;
695 while (my ($sect, $text) = each %replace)
697 $include{$sect} = $replace{$sect};
702 .\\" DO NOT MODIFY THIS FILE! It was generated by $this_program $this_version.
703 .TH $PROGRAM "$section" "$date" "$source" "$manual"
706 # If monospace was used emit macros for groff.
707 print enc
<<'EOT' if $require_mono;
708 .\" Define monospaced roman font for groff in troff mode.
716 my @pre = (_
('NAME'), _
('SYNOPSIS'), _
('DESCRIPTION'), _
('OPTIONS'),
718 my @post = (_
('ENVIRONMENT'), _
('FILES'), _
('AUTHOR'),
719 _
('REPORTING BUGS'), _
('COPYRIGHT'), _
('SEE ALSO'));
720 my %filter = map { $_ => 1 } @pre, @post;
724 for my $sect (@pre, (grep !$filter{$_}, @sections), @post)
726 next if $done{$sect}++; # ignore duplicates
727 next unless $include{$sect};
730 my $quote = $sect =~ /\W/ ?
'"' : '';
731 print enc
".SH $quote$sect$quote\n";
733 for ($include{$sect})
735 # Add bold style around referenced pages.
738 # This will ignore entries already marked up (with \)
739 s/(^|\s|,)([\[\w\x83]+)\(([1-9][[:lower:]]?)\)/$1\\fB$2\\fP($3)/g;
742 # Replace leading dot, apostrophe, backslash and hyphen
749 # Convert some latin1 chars to troff equivalents.
750 s/\xa0/\\ /g; # non-breaking space
757 close STDOUT
or kark N_
("%s: error writing to %s (%s)"), $this_program,
758 $opt_output || 'stdout', $!;
762 # Get program basename, and strip libtool "lt-" prefix if required.
767 s/^lt-// if $opt_libtool;
771 # Call program with given option and return results.
774 my ($prog, $opt) = @_;
775 my $stderr = $discard_stderr ?
'/dev/null' : '&1';
777 map { s/ +$//; expand
$_ }
779 `$prog $opt 2>$stderr`;
783 my $err = N_
("%s: can't get `%s' info from %s%s");
784 my $extra = $discard_stderr
785 ?
"\n" . N_
("Try `--no-discard-stderr' if option outputs to stderr")
788 kark
$err, $this_program, $opt, $prog, $extra;
794 # Convert option dashes to \- to stop nroff from hyphenating 'em, and
795 # embolden. Option arguments get italicised.
798 local $_ = '\fB' . shift;
801 unless (s/\[=(.*)\]$/\\fR[=\\fI$1\\fR]/)
811 # Insert spacing escape characters \, and \/ before and after italic text. See
812 # https://www.gnu.org/software/groff/manual/html_node/Ligatures-and-Kerning.html
813 sub fix_italic_spacing
816 s!\\fI(.*?)\\f([BRP])!\\fI\\,$1\\/\\f$2!g;
820 # Return indent to use: either the value passed in, or $v,$v+4 if
821 # loose index matching is used. The resulting string is used in a
822 # regex as " {$indent}", so will match either the exact number of
823 # spaces passed in, or up to four more. See the --loose-indent
828 $i .= ',' . ($_[0] + 4) if $loose_indent;