help2man revision 67064
1#!/usr/bin/perl -w 2 3# Generate a short man page from --help and --version output. 4# Copyright � 1997, 1998, 1999, 2000 Free Software Foundation, Inc. 5 6# This program is free software; you can redistribute it and/or modify 7# it under the terms of the GNU General Public License as published by 8# the Free Software Foundation; either version 2, or (at your option) 9# any later version. 10 11# This program is distributed in the hope that it will be useful, 12# but WITHOUT ANY WARRANTY; without even the implied warranty of 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14# GNU General Public License for more details. 15 16# You should have received a copy of the GNU General Public License 17# along with this program; if not, write to the Free Software Foundation, 18# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 19 20# Written by Brendan O'Dea <bod@compusol.com.au> 21# Available from ftp://ftp.gnu.org/gnu/help2man/ 22 23use 5.004; 24use strict; 25use Getopt::Long; 26use Text::Tabs qw(expand); 27use POSIX qw(strftime setlocale LC_TIME); 28 29my $this_program = 'help2man'; 30my $this_version = '1.022'; 31my $version_info = <<EOT; 32GNU $this_program $this_version 33 34Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation, Inc. 35This is free software; see the source for copying conditions. There is NO 36warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 37 38Written by Brendan O'Dea <bod\@compusol.com.au> 39EOT 40 41my $help_info = <<EOT; 42`$this_program' generates a man page out of `--help' and `--version' output. 43 44Usage: $this_program [OPTION]... EXECUTABLE 45 46 -n, --name=STRING use `STRING' as the description for the NAME paragraph 47 -s, --section=SECTION use `SECTION' as the section for the man page 48 -i, --include=FILE include material from `FILE' 49 -I, --opt-include=FILE include material from `FILE' if it exists 50 -o, --output=FILE send output to `FILE' 51 -N, --no-info suppress pointer to Texinfo manual 52 --help print this help, then exit 53 --version print version number, then exit 54 55EXECUTABLE should accept `--help' and `--version' options. 56 57Report bugs to <bug-help2man\@gnu.org>. 58EOT 59 60my $section = 1; 61my ($opt_name, @opt_include, $opt_output, $opt_no_info); 62 63# Parse options. 64Getopt::Long::config('bundling'); 65GetOptions ( 66 'n|name=s' => \$opt_name, 67 's|section=s' => \$section, 68 'i|include=s' => sub { push @opt_include, [ pop, 1 ] }, 69 'I|opt-include=s' => sub { push @opt_include, [ pop, 0 ] }, 70 'o|output=s' => \$opt_output, 71 'N|no-info' => \$opt_no_info, 72 help => sub { print $help_info; exit }, 73 version => sub { print $version_info; exit }, 74) or die $help_info; 75 76die $help_info unless @ARGV == 1; 77 78my %include = (); 79my %append = (); 80my @include = (); # retain order given in include file 81 82# Provide replacement `quote-regex' operator for pre-5.005. 83BEGIN { eval q(sub qr { '' =~ $_[0]; $_[0] }) if $] < 5.005 } 84 85# Process include file (if given). Format is: 86# 87# [section name] 88# verbatim text 89# 90# or 91# 92# /pattern/ 93# verbatim text 94# 95 96for (@opt_include) 97{ 98 my ($inc, $required) = @$_; 99 100 next unless -f $inc or $required; 101 die "$this_program: can't open `$inc' ($!)\n" 102 unless open INC, $inc; 103 104 my $key; 105 my $hash = \%include; 106 107 while (<INC>) 108 { 109 # [section] 110 if (/^\[([^]]+)\]/) 111 { 112 $key = uc $1; 113 $key =~ s/^\s+//; 114 $key =~ s/\s+$//; 115 $hash = \%include; 116 push @include, $key unless $include{$key}; 117 next; 118 } 119 120 # /pattern/ 121 if (m!^/(.*)/([ims]*)!) 122 { 123 my $pat = $2 ? "(?$2)$1" : $1; 124 125 # Check pattern. 126 eval { $key = qr($pat) }; 127 if ($@) 128 { 129 $@ =~ s/ at .*? line \d.*//; 130 die "$inc:$.:$@"; 131 } 132 133 $hash = \%append; 134 next; 135 } 136 137 # Silently ignore anything before the first 138 # section--allows for comments and revision info. 139 next unless $key; 140 141 $hash->{$key} ||= ''; 142 $hash->{$key} .= $_; 143 } 144 145 close INC; 146 147 die "$this_program: no valid information found in `$inc'\n" 148 unless $key; 149} 150 151# Compress trailing blank lines. 152for my $hash (\(%include, %append)) 153{ 154 for (keys %$hash) { $hash->{$_} =~ s/\n+$/\n/ } 155} 156 157# Turn off localisation of executable's ouput. 158@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; 159 160# Turn off localisation of date (for strftime). 161setlocale LC_TIME, 'C'; 162 163# Grab help and version info from executable. 164my ($help_text, $version_text) = map { 165 join '', map { s/ +$//; expand $_ } `$ARGV[0] --$_ 2>/dev/null` 166 or die "$this_program: can't get `--$_' info from $ARGV[0]\n" 167} qw(help version); 168 169my $date = strftime "%B %Y", localtime; 170(my $program = $ARGV[0]) =~ s!.*/!!; 171my $package = $program; 172my $version; 173 174if ($opt_output) 175{ 176 unlink $opt_output 177 or die "$this_program: can't unlink $opt_output ($!)\n" 178 if -e $opt_output; 179 180 open STDOUT, ">$opt_output" 181 or die "$this_program: can't create $opt_output ($!)\n"; 182} 183 184# The first line of the --version information is assumed to be in one 185# of the following formats: 186# 187# <version> 188# <program> <version> 189# {GNU,Free} <program> <version> 190# <program> ({GNU,Free} <package>) <version> 191# <program> - {GNU,Free} <package> <version> 192# 193# and seperated from any copyright/author details by a blank line. 194 195($_, $version_text) = split /\n+/, $version_text, 2; 196 197if (/^(\S+) +\(((?:GNU|Free) +[^)]+)\) +(.*)/ or 198 /^(\S+) +- *((?:GNU|Free) +\S+) +(.*)/) 199{ 200 $program = $1; 201 $package = $2; 202 $version = $3; 203} 204elsif (/^((?:GNU|Free) +)?(\S+) +(.*)/) 205{ 206 $program = $2; 207 $package = $1 ? "$1$2" : $2; 208 $version = $3; 209} 210else 211{ 212 $version = $_; 213} 214 215$program =~ s!.*/!!; 216 217# No info for `info' itself. 218$opt_no_info = 1 if $program eq 'info'; 219 220# --name overrides --include contents. 221$include{NAME} = "$program \\- $opt_name\n" if $opt_name; 222 223# Default (useless) NAME paragraph. 224$include{NAME} ||= "$program \\- manual page for $program $version\n"; 225 226# Man pages traditionally have the page title in caps. 227my $PROGRAM = uc $program; 228 229# Extract usage clause(s) [if any] for SYNOPSIS. 230if ($help_text =~ s/^Usage:( +(\S+))(.*)((?:\n(?: {6}\1| *or: +\S).*)*)//m) 231{ 232 my @syn = $2 . $3; 233 234 if ($_ = $4) 235 { 236 s/^\n//; 237 for (split /\n/) { s/^ *(or: +)?//; push @syn, $_ } 238 } 239 240 my $synopsis = ''; 241 for (@syn) 242 { 243 $synopsis .= ".br\n" if $synopsis; 244 s!^\S*/!!; 245 s/^(\S+) *//; 246 $synopsis .= ".B $1\n"; 247 s/\s+$//; 248 s/(([][]|\.\.+)+)/\\fR$1\\fI/g; 249 s/^/\\fI/ unless s/^\\fR//; 250 $_ .= '\fR'; 251 s/(\\fI)( *)/$2$1/g; 252 s/\\fI\\fR//g; 253 s/^\\fR//; 254 s/\\fI$//; 255 s/^\./\\&./; 256 257 $synopsis .= "$_\n"; 258 } 259 260 $include{SYNOPSIS} ||= $synopsis; 261} 262 263# Process text, initial section is DESCRIPTION. 264my $sect = 'DESCRIPTION'; 265$_ = "$help_text\n\n$version_text"; 266 267# Normalise paragraph breaks. 268s/^\n+//; 269s/\n*$/\n/; 270s/\n\n+/\n\n/g; 271 272# Temporarily exchange leading dots and backslashes for tokens. 273s/^\./\x80/mg; 274s/\\/\x81/g; 275 276# Start a new paragraph (if required) for these. 277s/([^\n])\n(Report +bugs|Email +bug +reports +to|Written +by)/$1\n\n$2/g; 278 279sub convert_option; 280 281while (length) 282{ 283 # Convert some standard paragraph names. 284 if (s/^(Options|Examples): *\n//) 285 { 286 $sect = uc $1; 287 next; 288 } 289 290 # Copyright section 291 if (/^Copyright +[(\xa9]/) 292 { 293 $sect = 'COPYRIGHT'; 294 $include{$sect} ||= ''; 295 $include{$sect} .= ".PP\n" if $include{$sect}; 296 297 my $copy; 298 ($copy, $_) = split /\n\n/, $_, 2; 299 300 for ($copy) 301 { 302 # Add back newline 303 s/\n*$/\n/; 304 305 # Convert iso9959-1 copyright symbol or (c) to nroff 306 # character. 307 s/^Copyright +(?:\xa9|\([Cc]\))/Copyright \\(co/mg; 308 309 # Insert line breaks before additional copyright messages 310 # and the disclaimer. 311 s/(.)\n(Copyright |This +is +free +software)/$1\n.br\n$2/g; 312 313 # Join hyphenated lines. 314 s/([A-Za-z])-\n */$1/g; 315 } 316 317 $include{$sect} .= $copy; 318 $_ ||= ''; 319 next; 320 } 321 322 # Catch bug report text. 323 if (/^(Report +bugs|Email +bug +reports +to) /) 324 { 325 $sect = 'REPORTING BUGS'; 326 } 327 328 # Author section. 329 elsif (/^Written +by/) 330 { 331 $sect = 'AUTHOR'; 332 } 333 334 # Examples, indicated by an indented leading $, % or > are 335 # rendered in a constant width font. 336 if (/^( +)([\$\%>] )\S/) 337 { 338 my $indent = $1; 339 my $prefix = $2; 340 my $break = '.IP'; 341 $include{$sect} ||= ''; 342 while (s/^$indent\Q$prefix\E(\S.*)\n*//) 343 { 344 $include{$sect} .= "$break\n\\f(CW$prefix$1\\fR\n"; 345 $break = '.br'; 346 } 347 348 next; 349 } 350 351 my $matched = ''; 352 $include{$sect} ||= ''; 353 354 # Sub-sections have a trailing colon and the second line indented. 355 if (s/^(\S.*:) *\n / /) 356 { 357 $matched .= $& if %append; 358 $include{$sect} .= qq(.SS "$1"\n); 359 } 360 361 my $indent = 0; 362 my $content = ''; 363 364 # Option with description. 365 if (s/^( {1,10}([+-]\S.*?))(?:( +)|\n( {20,}))(\S.*)\n//) 366 { 367 $matched .= $& if %append; 368 $indent = length ($4 || "$1$3"); 369 $content = ".TP\n\x82$2\n\x82$5\n"; 370 unless ($4) 371 { 372 # Indent may be different on second line. 373 $indent = length $& if /^ {20,}/; 374 } 375 } 376 377 # Option without description. 378 elsif (s/^ {1,10}([+-]\S.*)\n//) 379 { 380 $matched .= $& if %append; 381 $content = ".HP\n\x82$1\n"; 382 $indent = 80; # not continued 383 } 384 385 # Indented paragraph with tag. 386 elsif (s/^( +(\S.*?) +)(\S.*)\n//) 387 { 388 $matched .= $& if %append; 389 $indent = length $1; 390 $content = ".TP\n\x82$2\n\x82$3\n"; 391 } 392 393 # Indented paragraph. 394 elsif (s/^( +)(\S.*)\n//) 395 { 396 $matched .= $& if %append; 397 $indent = length $1; 398 $content = ".IP\n\x82$2\n"; 399 } 400 401 # Left justified paragraph. 402 else 403 { 404 s/(.*)\n//; 405 $matched .= $& if %append; 406 $content = ".PP\n" if $include{$sect}; 407 $content .= "$1\n"; 408 } 409 410 # Append continuations. 411 while (s/^ {$indent}(\S.*)\n//) 412 { 413 $matched .= $& if %append; 414 $content .= "\x82$1\n" 415 } 416 417 # Move to next paragraph. 418 s/^\n+//; 419 420 for ($content) 421 { 422 # Leading dot protection. 423 s/\x82\./\x80/g; 424 s/\x82//g; 425 426 # Convert options. 427 s/(^| )(-[][\w=-]+)/$1 . convert_option $2/mge; 428 } 429 430 # Check if matched paragraph contains /pat/. 431 if (%append) 432 { 433 for my $pat (keys %append) 434 { 435 if ($matched =~ $pat) 436 { 437 $content .= ".PP\n" unless $append{$pat} =~ /^\./; 438 $content .= $append{$pat}; 439 } 440 } 441 } 442 443 $include{$sect} .= $content; 444} 445 446# Refer to the real documentation. 447unless ($opt_no_info) 448{ 449 $sect = 'SEE ALSO'; 450 $include{$sect} ||= ''; 451 $include{$sect} .= ".PP\n" if $include{$sect}; 452 $include{$sect} .= <<EOT; 453The full documentation for 454.B $program 455is maintained as a Texinfo manual. If the 456.B info 457and 458.B $program 459programs are properly installed at your site, the command 460.IP 461.B info $program 462.PP 463should give you access to the complete manual. 464EOT 465} 466 467# Output header. 468print <<EOT; 469.\\" DO NOT MODIFY THIS FILE! It was generated by $this_program $this_version. 470.TH $PROGRAM "$section" "$date" "$package $version" FSF 471EOT 472 473# Section ordering. 474my @pre = qw(NAME SYNOPSIS DESCRIPTION OPTIONS EXAMPLES); 475my @post = ('AUTHOR', 'REPORTING BUGS', 'COPYRIGHT', 'SEE ALSO'); 476my $filter = join '|', @pre, @post; 477 478# Output content. 479for (@pre, (grep ! /^($filter)$/o, @include), @post) 480{ 481 if ($include{$_}) 482 { 483 my $quote = /\W/ ? '"' : ''; 484 print ".SH $quote$_$quote\n"; 485 486 for ($include{$_}) 487 { 488 # Replace leading dot an backslash tokens. 489 s/\x80/\\&./g; 490 s/\x81/\\e/g; 491 print; 492 } 493 } 494} 495 496exit; 497 498# Convert option dashes to \- to stop nroff from hyphenating 'em, and 499# embolden. Option arguments get italicised. 500sub convert_option 501{ 502 local $_ = '\fB' . shift; 503 504 s/-/\\-/g; 505 unless (s/\[=(.*)\]$/\\fR[=\\fI$1\\fR]/) 506 { 507 s/=(.)/\\fR=\\fI$1/; 508 s/ (.)/ \\fI$1/; 509 $_ .= '\fR'; 510 } 511 512 $_; 513} 514