1#!/usr/bin/perl -w 2 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 Free Software 6# Foundation, Inc. 7 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) 11# any later version. 12 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. 17 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/>. 20 21# Written by Brendan O'Dea <bod@debian.org> 22# Available from https://ftp.gnu.org/gnu/help2man/ 23 24use 5.008; 25use strict; 26use Getopt::Long; 27use Text::ParseWords qw(shellwords); 28use Text::Tabs qw(expand); 29use POSIX qw(strftime setlocale LC_ALL); 30 31my $this_program = 'help2man'; 32my $this_version = '1.48.5'; 33 34sub _ { $_[0] } 35sub configure_locale 36{ 37 my $locale = shift; 38 die "$this_program: no locale support (Locale::gettext required)\n" 39 unless $locale eq 'C'; 40} 41 42sub dec { $_[0] } 43sub enc { $_[0] } 44sub enc_user { $_[0] } 45sub kark { die +(sprintf shift, @_), "\n" } 46sub N_ { $_[0] } 47 48sub program_basename; 49sub get_option_value; 50sub convert_option; 51sub fix_italic_spacing; 52 53my $version_info = enc_user sprintf _(<<'EOT'), $this_program, $this_version; 54GNU %s %s 55 56Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2009, 572010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2020, 2021 Free Software 58Foundation, Inc. 59This is free software; see the source for copying conditions. There is NO 60warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 61 62Written by Brendan O'Dea <bod@debian.org> 63EOT 64 65my $help_info = enc_user sprintf _(<<'EOT'), $this_program, $this_program; 66`%s' generates a man page out of `--help' and `--version' output. 67 68Usage: %s [OPTION]... EXECUTABLE 69 70 -n, --name=STRING description for the NAME paragraph 71 -s, --section=SECTION section number for manual page (1, 6, 8) 72 -m, --manual=TEXT name of manual (User Commands, ...) 73 -S, --source=TEXT source of program (FSF, Debian, ...) 74 -L, --locale=STRING select locale (default "C") 75 -i, --include=FILE include material from `FILE' 76 -I, --opt-include=FILE include material from `FILE' if it exists 77 -o, --output=FILE send output to `FILE' 78 -p, --info-page=TEXT name of Texinfo manual 79 -N, --no-info suppress pointer to Texinfo manual 80 -l, --libtool exclude the `lt-' from the program name 81 -b, --bold-refs apply bold style to references 82 --help print this help, then exit 83 --version print version number, then exit 84 85EXECUTABLE should accept `--help' and `--version' options and produce output on 86stdout although alternatives may be specified using: 87 88 -h, --help-option=STRING help option string 89 -v, --version-option=STRING version option string 90 --version-string=STRING version string 91 --no-discard-stderr include stderr when parsing option output 92 93Report bugs to <bug-help2man@gnu.org>. 94EOT 95 96my $section = 1; 97my $manual = ''; 98my $source = ''; 99my $help_option = '--help'; 100my $version_option = '--version'; 101my $discard_stderr = 1; 102my ($opt_name, @opt_include, $opt_output, $opt_info, $opt_no_info, $opt_libtool, 103 $opt_bold_refs, $version_text); 104 105my %opt_def = ( 106 'n|name=s' => \$opt_name, 107 's|section=s' => \$section, 108 'm|manual=s' => \$manual, 109 'S|source=s' => \$source, 110 'L|locale=s' => sub { configure_locale pop }, 111 'i|include=s' => sub { push @opt_include, [ pop, 1 ] }, 112 'I|opt-include=s' => sub { push @opt_include, [ pop, 0 ] }, 113 'o|output=s' => \$opt_output, 114 'p|info-page=s' => \$opt_info, 115 'N|no-info' => \$opt_no_info, 116 'l|libtool' => \$opt_libtool, 117 'b|bold-refs' => \$opt_bold_refs, 118 'help' => sub { print $help_info; exit }, 119 'version' => sub { print $version_info; exit }, 120 'h|help-option=s' => \$help_option, 121 'v|version-option=s' => \$version_option, 122 'version-string=s' => \$version_text, 123 'discard-stderr!' => \$discard_stderr, 124); 125 126# Parse options. 127Getopt::Long::config('bundling'); 128die $help_info unless GetOptions %opt_def and @ARGV == 1; 129 130my %include = (); 131my %replace = (); 132my %append = (); 133my %append_match = (); 134my @sections = (); # retain order of include file or in-line *section*s 135 136# Process include file (if given). Format is: 137# 138# Optional initial text, ignored. May include lines starting with `-' 139# which are processed as options. 140# 141# [section] 142# Verbatim text to be included in the named section. By default at 143# the start, but in the case of `name' and `synopsis' the content 144# will replace the autogenerated contents. 145# 146# [<section] 147# Verbatim text to be inserted at the start of the named section. 148# 149# [=section] 150# Verbatim text to replace the named section. 151# 152# [>section] 153# Verbatim text to be appended to the end of the named section. 154# 155# /pattern/ 156# Verbatim text for inclusion below a paragraph matching `pattern'. 157# 158 159while (@opt_include) 160{ 161 my ($inc, $required) = @{shift @opt_include}; 162 163 next unless -f $inc or $required; 164 kark N_("%s: can't open `%s' (%s)"), $this_program, $inc, $! 165 unless open INC, $inc; 166 167 my $key; 168 my $hash; 169 170 while (<INC>) 171 { 172 # Convert input to internal Perl format, so that multibyte 173 # sequences are treated as single characters. 174 $_ = dec $_; 175 176 # [section] 177 if (/^\[([^]]+)\]\s*$/) 178 { 179 $key = uc $1; 180 $key =~ s/^\s+//; 181 $key =~ s/\s+$//; 182 $hash = \%include; 183 # Handle explicit [<section], [=section] and [>section] 184 if ($key =~ s/^([<>=])\s*//) 185 { 186 if ($1 eq '>') { $hash = \%append; } 187 elsif ($1 eq '=') { $hash = \%replace; } 188 } 189 # NAME/SYNOPSIS replace by default 190 elsif ($key eq _('NAME') or $key eq _('SYNOPSIS')) 191 { 192 $hash = \%replace; 193 } 194 else 195 { 196 $hash = \%include; 197 } 198 199 push @sections, $key; 200 next; 201 } 202 203 # /pattern/ 204 if (m!^/(.*)/([ims]*)\s*$!) 205 { 206 my $pat = $2 ? "(?$2)$1" : $1; 207 208 # Check pattern. 209 eval { $key = qr($pat) }; 210 if ($@) 211 { 212 $@ =~ s/ at .*? line \d.*//; 213 die "$inc:$.:$@"; 214 } 215 216 $hash = \%append_match; 217 next; 218 } 219 220 # Check for options before the first section--anything else is 221 # silently ignored, allowing the first for comments and 222 # revision info. 223 unless ($key) 224 { 225 # handle options 226 if (/^-/) 227 { 228 local @ARGV = shellwords $_; 229 GetOptions %opt_def; 230 } 231 232 next; 233 } 234 235 $hash->{$key} .= $_; 236 } 237 238 close INC; 239 240 kark N_("%s: no valid information found in `%s'"), $this_program, $inc 241 unless $key; 242} 243 244# Compress trailing blank lines. 245for my $hash (\(%include, %replace, %append, %append_match)) 246{ 247 for (keys %$hash) { $hash->{$_} =~ s/\n+$/\n/ } 248} 249 250# Grab help and version info from executable. 251my $help_text = get_option_value $ARGV[0], $help_option; 252$version_text ||= get_option_value $ARGV[0], $version_option; 253 254# By default the generated manual pages will include the current date. This may 255# however be overridden by setting the environment variable $SOURCE_DATE_EPOCH 256# to an integer value of the seconds since the UNIX epoch. This is primarily 257# intended to support reproducible builds (wiki.debian.org/ReproducibleBuilds) 258# and will additionally ensure that the output date string is UTC. 259my $epoch_secs = time; 260if (exists $ENV{SOURCE_DATE_EPOCH} and $ENV{SOURCE_DATE_EPOCH} =~ /^(\d+)$/) 261{ 262 $epoch_secs = $1; 263 $ENV{TZ} = 'UTC0'; 264} 265 266# Translators: the following message is a strftime(3) format string, which in 267# the English version expands to the month as a word and the full year. It 268# is used on the footer of the generated manual pages. If in doubt, you may 269# just use %x as the value (which should be the full locale-specific date). 270my $date = enc strftime _("%B %Y"), localtime $epoch_secs; 271my $program = program_basename $ARGV[0]; 272my $package = $program; 273my $version; 274 275if ($opt_output) 276{ 277 unlink $opt_output or kark N_("%s: can't unlink %s (%s)"), 278 $this_program, $opt_output, $! if -e $opt_output; 279 280 open STDOUT, ">$opt_output" 281 or kark N_("%s: can't create %s (%s)"), $this_program, $opt_output, $!; 282} 283 284# The first line of the --version information is assumed to be in one 285# of the following formats: 286# 287# <version> 288# <program> <version> 289# {GNU,Free} <program> <version> 290# <program> ({GNU,Free,} <package>) <version> 291# <program> - {GNU,Free,} <package> <version> 292# <program> - {GNU,Free,} <package> - <version> 293# 294# and separated from any copyright/author details by a blank line. 295 296($_, $version_text) = ((split /\n+/, $version_text, 2), ''); 297 298if (/^(\S+) +\(((?:(?:GNU|Free) +)?[^)]+)\) +(\S.*)$/ or 299 /^(\S+) +- +((?:(?:GNU|Free) +)?\S.*) +- +(\S.*)$/ or 300 /^(\S+) +- +((?:(?:GNU|Free) +)?\S+) +(\S.*)$/) 301{ 302 $program = program_basename $1; 303 $package = $2; 304 $version = $3; 305} 306elsif (/^((?:GNU|Free) +)?(\S+) +(\S.*)$/) 307{ 308 $program = program_basename $2; 309 $package = $1 ? "$1$program" : $program; 310 $version = $3; 311} 312else 313{ 314 $version = $_; 315} 316 317# No info for `info' itself. 318$opt_no_info = 1 if $program eq 'info'; 319 320if ($opt_name) 321{ 322 # --name overrides --include contents. 323 $replace{_('NAME')} = "$program \\- $opt_name\n"; 324} 325 326# Translators: "NAME", "SYNOPSIS" and other one or two word strings in all 327# upper case are manual page section headings. The man(1) manual page in your 328# language, if available should provide the conventional translations. 329for ($replace{_('NAME')} || ($include{_('NAME')} ||= '')) 330{ 331 if ($_) # Use first name given as $program 332 { 333 $program = $1 if /^([^\s,]+)(?:,?\s*[^\s,\\-]+)*\s+\\?-/; 334 } 335 else # Set a default (useless) NAME paragraph. 336 { 337 $_ = sprintf _("%s \\- manual page for %s %s") . "\n", $program, 338 $program, $version; 339 } 340} 341 342# Man pages traditionally have the page title in caps. 343my $PROGRAM = uc $program; 344 345# Set default page head/footers 346$source ||= "$package $version"; 347unless ($manual) 348{ 349 for ($section) 350 { 351 if (/^(1[Mm]|8)/) { $manual = enc _('System Administration Utilities') } 352 elsif (/^6/) { $manual = enc _('Games') } 353 else { $manual = enc _('User Commands') } 354 } 355} 356 357# Extract usage clause(s) [if any] for SYNOPSIS. 358# Translators: "Usage" and "or" here are patterns (regular expressions) which 359# are used to match the usage synopsis in program output. An example from cp 360# (GNU coreutils) which contains both strings: 361# Usage: cp [OPTION]... [-T] SOURCE DEST 362# or: cp [OPTION]... SOURCE... DIRECTORY 363# or: cp [OPTION]... -t DIRECTORY SOURCE... 364my $PAT_USAGE = _('Usage'); 365my $PAT_USAGE_CONT = _('or'); 366if ($help_text =~ s/^($PAT_USAGE):( +(\S+))(.*)((?:\n(?: {6}\1| *($PAT_USAGE_CONT): +\S).*)*)//om) 367{ 368 my @syn = $3 . $4; 369 370 if ($_ = $5) 371 { 372 s/^\n//; 373 for (split /\n/) { s/^ *(($PAT_USAGE_CONT): +)?//o; push @syn, $_ } 374 } 375 376 my $synopsis = ''; 377 for (@syn) 378 { 379 $synopsis .= ".br\n" if $synopsis; 380 s!^\S*/!!; 381 s/^lt-// if $opt_libtool; 382 s/^(\S+) *//; 383 $synopsis .= ".B $1\n"; 384 s/\s+$//; 385 s/(([][]|\.\.+)+)/\\fR$1\\fI/g; 386 s/^/\\fI/ unless s/^\\fR//; 387 $_ .= '\fR'; 388 s/(\\fI)( *)/$2$1/g; 389 s/\\fI\\fR//g; 390 s/^\\fR//; 391 s/\\fI$//; 392 s/^\./\\&./; 393 394 $_ = fix_italic_spacing $_; 395 $synopsis .= "$_\n"; 396 } 397 398 $include{_('SYNOPSIS')} .= $synopsis; 399} 400 401# Process text, initial section is DESCRIPTION. 402my $sect = _('DESCRIPTION'); 403$_ = "$help_text\n\n$version_text"; 404 405# Normalise paragraph breaks. 406s/^\n+//; 407s/\n*$/\n/; 408s/\n\n+/\n\n/g; 409 410# Join hyphenated lines. 411s/([A-Za-z])-\n *([A-Za-z])/$1$2/g; 412 413# Temporarily exchange leading dots, apostrophes and backslashes for 414# tokens. 415s/^\./\x80/mg; 416s/^'/\x81/mg; 417s/\\/\x82/g; 418 419# Translators: patterns are used to match common program output. In the source 420# these strings are all of the form of "my $PAT_something = _('...');" and are 421# regular expressions. If there is more than one commonly used string, you 422# may separate alternatives with "|". Spaces in these expressions are written 423# as " +" to indicate that more than one space may be matched. The string 424# "(?:[\\w-]+ +)?" in the bug reporting pattern is used to indicate an 425# optional word, so that either "Report bugs" or "Report _program_ bugs" will 426# be matched. 427my $PAT_BUGS = _('Report +(?:[\w-]+ +)?bugs|' . 428 'Email +bug +reports +to|' . 429 '.* +online +help:'); 430my $PAT_AUTHOR = _('Written +by'); 431my $PAT_OPTIONS = _('Options'); 432my $PAT_ENVIRONMENT = _('Environment'); 433my $PAT_FILES = _('Files'); 434my $PAT_EXAMPLES = _('Examples'); 435my $PAT_FREE_SOFTWARE = _('This +is +free +software'); 436my $PAT_SEE_ALSO = _('Full +documentation'); 437 438# Start a new paragraph (if required) for these. 439s/([^\n])\n($PAT_BUGS|$PAT_AUTHOR|$PAT_SEE_ALSO) /$1\n\n$2 /og; 440 441# Convert iso-8859-1 copyright symbol or (c) to nroff 442# character. 443s/^Copyright +(?:\xa9|\([Cc]\))/Copyright \\(co/mg; 444 445while (length) 446{ 447 # Convert some standard paragraph names. 448 if (s/^($PAT_OPTIONS): *\n+//o) 449 { 450 $sect = _('OPTIONS'); 451 next; 452 } 453 if (s/^($PAT_ENVIRONMENT): *\n+//o) 454 { 455 $sect = _('ENVIRONMENT'); 456 next; 457 } 458 if (s/^($PAT_FILES): *\n+//o) 459 { 460 $sect = _('FILES'); 461 next; 462 } 463 elsif (s/^($PAT_EXAMPLES): *\n+//o) 464 { 465 $sect = _('EXAMPLES'); 466 next; 467 } 468 469 # Custom section indicated by a line containing "*Section Name*". 470 if (s/^\*(\w(.*\w)?)\* *\n+//) 471 { 472 $sect = uc $1; 473 $sect =~ tr/*/ /; # also accept *Section*Name* 474 push @sections, $sect; 475 next; 476 } 477 478 # Copyright section. 479 if (/^Copyright /) 480 { 481 $sect = _('COPYRIGHT'); 482 } 483 484 # Bug reporting section. 485 elsif (/^($PAT_BUGS) /o) 486 { 487 $sect = _('REPORTING BUGS'); 488 } 489 490 # Author section. 491 elsif (/^($PAT_AUTHOR)/o) 492 { 493 $sect = _('AUTHOR'); 494 } 495 496 elsif (/^($PAT_SEE_ALSO)/o) 497 { 498 $sect = _('SEE ALSO'); 499 $opt_no_info = 1; 500 } 501 502 # Examples, indicated by an indented leading $, % or > are 503 # rendered in a constant width font. 504 if (/^( +)([\$\%>] )\S/) 505 { 506 my $indent = $1; 507 my $prefix = $2; 508 my $break = '.IP'; 509 while (s/^$indent\Q$prefix\E(\S.*)\n*//) 510 { 511 $include{$sect} .= "$break\n\\f(CW$prefix$1\\fR\n"; 512 $break = '.br'; 513 } 514 515 next; 516 } 517 518 my $matched = ''; 519 520 # Sub-sections have a trailing colon and the second line indented. 521 if (s/^(\S.*:) *\n / /) 522 { 523 $matched .= $& if %append_match; 524 $include{$sect} .= qq(.SS "$1"\n); 525 } 526 527 my $indent = 0; 528 my $content = ''; 529 530 # Option with description. 531 if (s/^( {1,10}([+-]\S.*?))(?:( +(?!-))|\n( {20,}))(\S.*)\n//) 532 { 533 $matched .= $& if %append_match; 534 $indent = length ($4 || "$1$3"); 535 $content = ".TP\n\x84$2\n\x84$5\n"; 536 unless ($4) 537 { 538 # Indent may be different on second line. 539 $indent = length $& if /^ {20,}/; 540 } 541 } 542 543 # Option without description. 544 elsif (s/^ {1,10}([+-]\S.*)\n//) 545 { 546 $matched .= $& if %append_match; 547 $content = ".HP\n\x84$1\n"; 548 $indent = 80; # not continued 549 } 550 551 # Indented paragraph with tag. 552 elsif (s/^( +(\S.*?) +)(\S.*)\n//) 553 { 554 $matched .= $& if %append_match; 555 $indent = length $1; 556 $content = ".TP\n\x84$2\n\x84$3\n"; 557 } 558 559 # Indented paragraph. 560 elsif (s/^( +)(\S.*)\n//) 561 { 562 $matched .= $& if %append_match; 563 $indent = length $1; 564 $content = ".IP\n\x84$2\n"; 565 } 566 567 # Left justified paragraph. 568 else 569 { 570 s/(.*)\n//; 571 $matched .= $& if %append_match; 572 $content = ".PP\n" if $include{$sect}; 573 $content .= "$1\n"; 574 } 575 576 # Append continuations. 577 while ($indent ? s/^ {$indent}(\S.*)\n// : s/^(\S.*)\n//) 578 { 579 $matched .= $& if %append_match; 580 $content .= "\x84$1\n"; 581 } 582 583 # Move to next paragraph. 584 s/^\n+//; 585 586 for ($content) 587 { 588 # Leading dot and apostrophe protection. 589 s/\x84\./\x80/g; 590 s/\x84'/\x81/g; 591 s/\x84//g; 592 593 # Examples should be verbatim. 594 unless ($sect eq _('EXAMPLES')) 595 { 596 # Convert options. 597 s/(^|[ (])(-[][\w=-]+)/$1 . convert_option $2/mge; 598 599 # Italicise filenames: /a/b, $VAR/c/d, ~/e/f 600 s! 601 (^|[ (]) # space/punctuation before 602 ( 603 (?:\$\w+|~)? # leading variable, or tilde 604 (?:/\w(?:[\w.-]*\w)?)+ # path components 605 ) 606 ($|[ ,;.)]) # space/punctuation after 607 !$1\\fI$2\\fP$3!xmg; 608 609 $_ = fix_italic_spacing $_; 610 } 611 612 # Escape remaining hyphens. 613 s/-/\x83/g; 614 615 if ($sect eq _('COPYRIGHT')) 616 { 617 # Insert line breaks before additional copyright messages 618 # and the disclaimer. 619 s/\n(Copyright |$PAT_FREE_SOFTWARE)/\n.br\n$1/og; 620 } 621 elsif ($sect eq _('REPORTING BUGS')) 622 { 623 # Handle multi-line bug reporting sections of the form: 624 # 625 # Report <program> bugs to <addr> 626 # GNU <package> home page: <url> 627 # ... 628 s/\n([[:upper:]])/\n.br\n$1/g; 629 } 630 elsif ($sect eq _('SEE ALSO')) 631 { 632 # Handle external references of the form: 633 # 634 # GNU <package> online resources: <addr> 635 # Full documentation at: <addr> 636 # or available locally via: info ... 637 # 638 s/\'/\\(aq/g; # shell quotes for info command 639 s/\n(.)/\n.br\n$1/g; # separate lines for each item 640 } 641 } 642 643 # Check if matched paragraph contains /pat/. 644 if (%append_match) 645 { 646 for my $pat (keys %append_match) 647 { 648 if ($matched =~ $pat) 649 { 650 $content .= ".PP\n" unless $append_match{$pat} =~ /^\./; 651 $content .= $append_match{$pat}; 652 } 653 } 654 } 655 656 $include{$sect} .= $content; 657} 658 659# Refer to the real documentation. 660unless ($opt_no_info) 661{ 662 my $info_page = $opt_info || $program; 663 664 $sect = _('SEE ALSO'); 665 $include{$sect} .= ".PP\n" if $include{$sect}; 666 $include{$sect} .= sprintf _(<<'EOT'), $program, $program, $info_page; 667The full documentation for 668.B %s 669is maintained as a Texinfo manual. If the 670.B info 671and 672.B %s 673programs are properly installed at your site, the command 674.IP 675.B info %s 676.PP 677should give you access to the complete manual. 678EOT 679} 680 681# Append additional text. 682while (my ($sect, $text) = each %append) 683{ 684 $include{$sect} .= $append{$sect}; 685} 686 687# Replace sections. 688while (my ($sect, $text) = each %replace) 689{ 690 $include{$sect} = $replace{$sect}; 691} 692 693# Output header. 694print <<EOT; 695.\\" DO NOT MODIFY THIS FILE! It was generated by $this_program $this_version. 696.TH $PROGRAM "$section" "$date" "$source" "$manual" 697EOT 698 699# Section ordering. 700my @pre = (_('NAME'), _('SYNOPSIS'), _('DESCRIPTION'), _('OPTIONS'), 701 _('EXAMPLES')); 702my @post = (_('ENVIRONMENT'), _('FILES'), _('AUTHOR'), 703 _('REPORTING BUGS'), _('COPYRIGHT'), _('SEE ALSO')); 704my %filter = map { $_ => 1 } @pre, @post; 705 706# Output content. 707my %done; 708for my $sect (@pre, (grep !$filter{$_}, @sections), @post) 709{ 710 next if $done{$sect}++; # ignore duplicates 711 next unless $include{$sect}; 712 if ($include{$sect}) 713 { 714 my $quote = $sect =~ /\W/ ? '"' : ''; 715 print enc ".SH $quote$sect$quote\n"; 716 717 for ($include{$sect}) 718 { 719 # Add bold style around referenced pages. 720 if ($opt_bold_refs) 721 { 722 # This will ignore entries already marked up (with \) 723 s/(^|\s|,)([\[\w\x83]+)\(([1-9][[:lower:]]?)\)/$1\\fB$2\\fP($3)/g; 724 } 725 726 # Replace leading dot, apostrophe, backslash and hyphen 727 # tokens. 728 s/\x80/\\&./g; 729 s/\x81/\\&'/g; 730 s/\x82/\\e/g; 731 s/\x83/\\-/g; 732 733 # Convert some latin1 chars to troff equivalents 734 s/\xa0/\\ /g; # non-breaking space 735 736 print enc $_; 737 } 738 } 739} 740 741close STDOUT or kark N_("%s: error writing to %s (%s)"), $this_program, 742 $opt_output || 'stdout', $!; 743 744exit; 745 746# Get program basename, and strip libtool "lt-" prefix if required. 747sub program_basename 748{ 749 local $_ = shift; 750 s!.*/!!; 751 s/^lt-// if $opt_libtool; 752 $_; 753} 754 755# Call program with given option and return results. 756sub get_option_value 757{ 758 my ($prog, $opt) = @_; 759 my $stderr = $discard_stderr ? '/dev/null' : '&1'; 760 my $value = join '', 761 map { s/ +$//; expand $_ } 762 map { dec $_ } 763 `$prog $opt 2>$stderr`; 764 765 unless ($value) 766 { 767 my $err = N_("%s: can't get `%s' info from %s%s"); 768 my $extra = $discard_stderr 769 ? "\n" . N_("Try `--no-discard-stderr' if option outputs to stderr") 770 : ''; 771 772 kark $err, $this_program, $opt, $prog, $extra; 773 } 774 775 $value; 776} 777 778# Convert option dashes to \- to stop nroff from hyphenating 'em, and 779# embolden. Option arguments get italicised. 780sub convert_option 781{ 782 local $_ = '\fB' . shift; 783 784 s/-/\x83/g; 785 unless (s/\[=(.*)\]$/\\fR[=\\fI$1\\fR]/) 786 { 787 s/=(.)/\\fR=\\fI$1/; 788 s/ (.)/ \\fI$1/; 789 $_ .= '\fR'; 790 } 791 792 $_; 793} 794 795# Insert spacing escape characters \, and \/ before and after italic text. See 796# https://www.gnu.org/software/groff/manual/html_node/Ligatures-and-Kerning.html 797sub fix_italic_spacing 798{ 799 local $_ = shift; 800 s!\\fI(.*?)\\f([BRP])!\\fI\\,$1\\/\\f$2!g; 801 return $_; 802} 803