help2man revision 1.1.1.1
111394Sswallace#!/usr/bin/perl -w 211394Sswallace 311397Sswallace# Generate a short man page from --help and --version output. 411394Sswallace# Copyright � 1997, 1998, 1999, 2000 Free Software Foundation, Inc. 511394Sswallace 611394Sswallace# This program is free software; you can redistribute it and/or modify 711394Sswallace# it under the terms of the GNU General Public License as published by 811394Sswallace# the Free Software Foundation; either version 2, or (at your option) 911394Sswallace# any later version. 1011394Sswallace 1111394Sswallace# This program is distributed in the hope that it will be useful, 1211394Sswallace# but WITHOUT ANY WARRANTY; without even the implied warranty of 1311394Sswallace# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1411394Sswallace# GNU General Public License for more details. 1511394Sswallace 1611394Sswallace# You should have received a copy of the GNU General Public License 1711394Sswallace# along with this program; if not, write to the Free Software Foundation, 1811394Sswallace# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 1911394Sswallace 2011394Sswallace# Written by Brendan O'Dea <bod@compusol.com.au> 2111394Sswallace# Available from ftp://ftp.gnu.org/gnu/help2man/ 2211394Sswallace 2311394Sswallaceuse 5.004; 2411394Sswallaceuse strict; 2511394Sswallaceuse Getopt::Long; 2611394Sswallaceuse Text::Tabs qw(expand); 2711394Sswallaceuse POSIX qw(strftime setlocale LC_TIME); 2811394Sswallace 29115684Sobrienmy $this_program = 'help2man'; 30115684Sobrienmy $this_version = '1.24'; 31115684Sobrienmy $version_info = <<EOT; 3211394SswallaceGNU $this_program $this_version 3311394Sswallace 3411394SswallaceCopyright (C) 1997, 1998, 1999, 2000 Free Software Foundation, Inc. 3511394SswallaceThis is free software; see the source for copying conditions. There is NO 3611394Sswallacewarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 3711394Sswallace 3891388SrobertWritten by Brendan O'Dea <bod\@compusol.com.au> 3911394SswallaceEOT 4011394Sswallace 4111394Sswallacemy $help_info = <<EOT; 4211397Sswallace`$this_program' generates a man page out of `--help' and `--version' output. 4311397Sswallace 4411394SswallaceUsage: $this_program [OPTION]... EXECUTABLE 4511397Sswallace 4611397Sswallace -n, --name=STRING use `STRING' as the description for the NAME paragraph 4711397Sswallace -s, --section=SECTION use `SECTION' as the section for the man page 4811397Sswallace -i, --include=FILE include material from `FILE' 4911397Sswallace -I, --opt-include=FILE include material from `FILE' if it exists 5011397Sswallace -o, --output=FILE send output to `FILE' 5111394Sswallace -N, --no-info suppress pointer to Texinfo manual 5254655Seivind --help print this help, then exit 5392761Salfred --version print version number, then exit 5492761Salfred 5511394SswallaceEXECUTABLE should accept `--help' and `--version' options. 5611394Sswallace 5711394SswallaceReport bugs to <bug-help2man\@gnu.org>. 5811397SswallaceEOT 5911394Sswallace 6011394Sswallacemy $section = 1; 6111394Sswallacemy ($opt_name, @opt_include, $opt_output, $opt_no_info); 6211397Sswallacemy %opt_def = ( 6311397Sswallace 'n|name=s' => \$opt_name, 6411394Sswallace 's|section=s' => \$section, 6511397Sswallace 'i|include=s' => sub { push @opt_include, [ pop, 1 ] }, 6611397Sswallace 'I|opt-include=s' => sub { push @opt_include, [ pop, 0 ] }, 6711397Sswallace 'o|output=s' => \$opt_output, 6811394Sswallace 'N|no-info' => \$opt_no_info, 6911397Sswallace); 7011397Sswallace 7111397Sswallace# Parse options. 7211397SswallaceGetopt::Long::config('bundling'); 7311394SswallaceGetOptions (%opt_def, 7411394Sswallace help => sub { print $help_info; exit }, 7511394Sswallace version => sub { print $version_info; exit }, 7611394Sswallace) or die $help_info; 7711394Sswallace 7811394Sswallacedie $help_info unless @ARGV == 1; 7911394Sswallace 8011394Sswallacemy %include = (); 8111394Sswallacemy %append = (); 8211394Sswallacemy @include = (); # retain order given in include file 8311394Sswallace 8411394Sswallace# Provide replacement `quote-regex' operator for pre-5.005. 8511394SswallaceBEGIN { eval q(sub qr { '' =~ $_[0]; $_[0] }) if $] < 5.005 } 8611394Sswallace 8711394Sswallace# Process include file (if given). Format is: 8811394Sswallace# 8911394Sswallace# [section name] 9011394Sswallace# verbatim text 9111394Sswallace# 9211394Sswallace# or 9311394Sswallace# 9411394Sswallace# /pattern/ 9511394Sswallace# verbatim text 9611394Sswallace# 9711394Sswallace 9811394Sswallacewhile (@opt_include) 9911394Sswallace{ 10083366Sjulian my ($inc, $required) = @{shift @opt_include}; 10183366Sjulian 10211394Sswallace next unless -f $inc or $required; 10311394Sswallace die "$this_program: can't open `$inc' ($!)\n" 10411394Sswallace unless open INC, $inc; 10511394Sswallace 10611394Sswallace my $key; 10711394Sswallace my $hash = \%include; 10811394Sswallace 10911394Sswallace while (<INC>) 110107849Salfred { 111107849Salfred # [section] 11243314Sdillon if (/^\[([^]]+)\]/) 11311394Sswallace { 11454655Seivind $key = uc $1; 11511394Sswallace $key =~ s/^\s+//; 11611394Sswallace $key =~ s/\s+$//; 11711394Sswallace $hash = \%include; 11883366Sjulian push @include, $key unless $include{$key}; 11911394Sswallace next; 12011394Sswallace } 121107849Salfred 12211394Sswallace # /pattern/ 12311394Sswallace if (m!^/(.*)/([ims]*)!) 12411394Sswallace { 12583366Sjulian my $pat = $2 ? "(?$2)$1" : $1; 12683366Sjulian 12711394Sswallace # Check pattern. 12811394Sswallace eval { $key = qr($pat) }; 12911394Sswallace if ($@) 13011394Sswallace { 13111394Sswallace $@ =~ s/ at .*? line \d.*//; 13211394Sswallace die "$inc:$.:$@"; 13311394Sswallace } 134107849Salfred 13511394Sswallace $hash = \%append; 136109153Sdillon next; 13711394Sswallace } 13889306Salfred 13989306Salfred # Check for options before the first section--anything else is 14089306Salfred # silently ignored, allowing the first for comments and 14111394Sswallace # revision info. 14211394Sswallace unless ($key) 143107849Salfred { 14411394Sswallace # handle options 14511394Sswallace if (/^-/) 14611394Sswallace { 14783366Sjulian local @ARGV = split; 14883366Sjulian GetOptions %opt_def; 14911394Sswallace } 15011394Sswallace 15111397Sswallace next; 15211394Sswallace } 15311397Sswallace 15411394Sswallace $hash->{$key} ||= ''; 15511394Sswallace $hash->{$key} .= $_; 15611394Sswallace } 157107849Salfred 158107849Salfred close INC; 159107849Salfred 16011397Sswallace die "$this_program: no valid information found in `$inc'\n" 16183366Sjulian unless $key; 16211394Sswallace} 16311397Sswallace 164107849Salfred# Compress trailing blank lines. 16511394Sswallacefor my $hash (\(%include, %append)) 16611394Sswallace{ 167107849Salfred for (keys %$hash) { $hash->{$_} =~ s/\n+$/\n/ } 16811394Sswallace} 16911394Sswallace 17011394Sswallace# Turn off localisation of executable's ouput. 17111394Sswallace@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; 17283366Sjulian 17383366Sjulian# Turn off localisation of date (for strftime). 17411394Sswallacesetlocale LC_TIME, 'C'; 17511394Sswallace 17611397Sswallace# Grab help and version info from executable. 17711394Sswallacemy ($help_text, $version_text) = map { 17811397Sswallace join '', map { s/ +$//; expand $_ } `$ARGV[0] --$_ 2>/dev/null` 17911394Sswallace or die "$this_program: can't get `--$_' info from $ARGV[0]\n" 18011394Sswallace} qw(help version); 18111394Sswallace 182107849Salfredmy $date = strftime "%B %Y", localtime; 183107849Salfred(my $program = $ARGV[0]) =~ s!.*/!!; 184107849Salfredmy $package = $program; 18511397Sswallacemy $version; 18683366Sjulian 18711394Sswallaceif ($opt_output) 18811397Sswallace{ 189107849Salfred unlink $opt_output 19011394Sswallace or die "$this_program: can't unlink $opt_output ($!)\n" 19111394Sswallace if -e $opt_output; 192107849Salfred 19311394Sswallace open STDOUT, ">$opt_output" 19411394Sswallace or die "$this_program: can't create $opt_output ($!)\n"; 19511394Sswallace} 19611394Sswallace 19783366Sjulian# The first line of the --version information is assumed to be in one 19883366Sjulian# of the following formats: 19911394Sswallace# 20011394Sswallace# <version> 20111397Sswallace# <program> <version> 20211394Sswallace# {GNU,Free} <program> <version> 20311397Sswallace# <program> ({GNU,Free} <package>) <version> 20411394Sswallace# <program> - {GNU,Free} <package> <version> 20511394Sswallace# 20611394Sswallace# and seperated from any copyright/author details by a blank line. 207107849Salfred 208107849Salfred($_, $version_text) = split /\n+/, $version_text, 2; 20911397Sswallace 21083366Sjulianif (/^(\S+) +\(((?:GNU|Free) +[^)]+)\) +(.*)/ or 21111394Sswallace /^(\S+) +- *((?:GNU|Free) +\S+) +(.*)/) 21211397Sswallace{ 213107849Salfred $program = $1; 21411394Sswallace $package = $2; 21511394Sswallace $version = $3; 216107849Salfred} 21711394Sswallaceelsif (/^((?:GNU|Free) +)?(\S+) +(.*)/) 21811394Sswallace{ 21911394Sswallace $program = $2; 22011394Sswallace $package = $1 ? "$1$2" : $2; 22183366Sjulian $version = $3; 22283366Sjulian} 22311394Sswallaceelse 22411394Sswallace{ 225107849Salfred $version = $_; 22611394Sswallace} 22711394Sswallace 22816193Snate$program =~ s!.*/!!; 22911394Sswallace 23011525Sswallace# No info for `info' itself. 23111394Sswallace$opt_no_info = 1 if $program eq 'info'; 23241514Sarchie 23341514Sarchie# --name overrides --include contents. 23441514Sarchie$include{NAME} = "$program \\- $opt_name\n" if $opt_name; 23541514Sarchie 23641514Sarchie# Default (useless) NAME paragraph. 23741514Sarchie$include{NAME} ||= "$program \\- manual page for $program $version\n"; 23891393Srobert 23991388Srobert# Man pages traditionally have the page title in caps. 24016193Snatemy $PROGRAM = uc $program; 24116193Snate 24216193Snate# Extract usage clause(s) [if any] for SYNOPSIS. 24341514Sarchieif ($help_text =~ s/^Usage:( +(\S+))(.*)((?:\n(?: {6}\1| *or: +\S).*)*)//m) 24441514Sarchie{ 24511394Sswallace my @syn = $2 . $3; 24611525Sswallace 24711525Sswallace if ($_ = $4) 24811525Sswallace { 249107849Salfred s/^\n//; 25011394Sswallace for (split /\n/) { s/^ *(or: +)?//; push @syn, $_ } 25111394Sswallace } 25211394Sswallace 25311394Sswallace my $synopsis = ''; 25411394Sswallace for (@syn) 25511394Sswallace { 25611394Sswallace $synopsis .= ".br\n" if $synopsis; 25711394Sswallace s!^\S*/!!; 25811394Sswallace s/^(\S+) *//; 25911394Sswallace $synopsis .= ".B $1\n"; 26011394Sswallace s/\s+$//; 26111394Sswallace s/(([][]|\.\.+)+)/\\fR$1\\fI/g; 262 s/^/\\fI/ unless s/^\\fR//; 263 $_ .= '\fR'; 264 s/(\\fI)( *)/$2$1/g; 265 s/\\fI\\fR//g; 266 s/^\\fR//; 267 s/\\fI$//; 268 s/^\./\\&./; 269 270 $synopsis .= "$_\n"; 271 } 272 273 $include{SYNOPSIS} ||= $synopsis; 274} 275 276# Process text, initial section is DESCRIPTION. 277my $sect = 'DESCRIPTION'; 278$_ = "$help_text\n\n$version_text"; 279 280# Normalise paragraph breaks. 281s/^\n+//; 282s/\n*$/\n/; 283s/\n\n+/\n\n/g; 284 285# Temporarily exchange leading dots, apostrophes and backslashes for 286# tokens. 287s/^\./\x80/mg; 288s/^'/\x81/mg; 289s/\\/\x82/g; 290 291# Start a new paragraph (if required) for these. 292s/([^\n])\n(Report +bugs|Email +bug +reports +to|Written +by)/$1\n\n$2/g; 293 294sub convert_option; 295 296while (length) 297{ 298 # Convert some standard paragraph names. 299 if (s/^(Options|Examples): *\n//) 300 { 301 $sect = uc $1; 302 next; 303 } 304 305 # Copyright section 306 if (/^Copyright +[(\xa9]/) 307 { 308 $sect = 'COPYRIGHT'; 309 $include{$sect} ||= ''; 310 $include{$sect} .= ".PP\n" if $include{$sect}; 311 312 my $copy; 313 ($copy, $_) = split /\n\n/, $_, 2; 314 315 for ($copy) 316 { 317 # Add back newline 318 s/\n*$/\n/; 319 320 # Convert iso9959-1 copyright symbol or (c) to nroff 321 # character. 322 s/^Copyright +(?:\xa9|\([Cc]\))/Copyright \\(co/mg; 323 324 # Insert line breaks before additional copyright messages 325 # and the disclaimer. 326 s/(.)\n(Copyright |This +is +free +software)/$1\n.br\n$2/g; 327 328 # Join hyphenated lines. 329 s/([A-Za-z])-\n */$1/g; 330 } 331 332 $include{$sect} .= $copy; 333 $_ ||= ''; 334 next; 335 } 336 337 # Catch bug report text. 338 if (/^(Report +bugs|Email +bug +reports +to) /) 339 { 340 $sect = 'REPORTING BUGS'; 341 } 342 343 # Author section. 344 elsif (/^Written +by/) 345 { 346 $sect = 'AUTHOR'; 347 } 348 349 # Examples, indicated by an indented leading $, % or > are 350 # rendered in a constant width font. 351 if (/^( +)([\$\%>] )\S/) 352 { 353 my $indent = $1; 354 my $prefix = $2; 355 my $break = '.IP'; 356 $include{$sect} ||= ''; 357 while (s/^$indent\Q$prefix\E(\S.*)\n*//) 358 { 359 $include{$sect} .= "$break\n\\f(CW$prefix$1\\fR\n"; 360 $break = '.br'; 361 } 362 363 next; 364 } 365 366 my $matched = ''; 367 $include{$sect} ||= ''; 368 369 # Sub-sections have a trailing colon and the second line indented. 370 if (s/^(\S.*:) *\n / /) 371 { 372 $matched .= $& if %append; 373 $include{$sect} .= qq(.SS "$1"\n); 374 } 375 376 my $indent = 0; 377 my $content = ''; 378 379 # Option with description. 380 if (s/^( {1,10}([+-]\S.*?))(?:( +)|\n( {20,}))(\S.*)\n//) 381 { 382 $matched .= $& if %append; 383 $indent = length ($4 || "$1$3"); 384 $content = ".TP\n\x83$2\n\x83$5\n"; 385 unless ($4) 386 { 387 # Indent may be different on second line. 388 $indent = length $& if /^ {20,}/; 389 } 390 } 391 392 # Option without description. 393 elsif (s/^ {1,10}([+-]\S.*)\n//) 394 { 395 $matched .= $& if %append; 396 $content = ".HP\n\x83$1\n"; 397 $indent = 80; # not continued 398 } 399 400 # Indented paragraph with tag. 401 elsif (s/^( +(\S.*?) +)(\S.*)\n//) 402 { 403 $matched .= $& if %append; 404 $indent = length $1; 405 $content = ".TP\n\x83$2\n\x83$3\n"; 406 } 407 408 # Indented paragraph. 409 elsif (s/^( +)(\S.*)\n//) 410 { 411 $matched .= $& if %append; 412 $indent = length $1; 413 $content = ".IP\n\x83$2\n"; 414 } 415 416 # Left justified paragraph. 417 else 418 { 419 s/(.*)\n//; 420 $matched .= $& if %append; 421 $content = ".PP\n" if $include{$sect}; 422 $content .= "$1\n"; 423 } 424 425 # Append continuations. 426 while (s/^ {$indent}(\S.*)\n//) 427 { 428 $matched .= $& if %append; 429 $content .= "\x83$1\n" 430 } 431 432 # Move to next paragraph. 433 s/^\n+//; 434 435 for ($content) 436 { 437 # Leading dot and apostrophe protection. 438 s/\x83\./\x80/g; 439 s/\x83'/\x81/g; 440 s/\x83//g; 441 442 # Convert options. 443 s/(^| )(-[][\w=-]+)/$1 . convert_option $2/mge; 444 } 445 446 # Check if matched paragraph contains /pat/. 447 if (%append) 448 { 449 for my $pat (keys %append) 450 { 451 if ($matched =~ $pat) 452 { 453 $content .= ".PP\n" unless $append{$pat} =~ /^\./; 454 $content .= $append{$pat}; 455 } 456 } 457 } 458 459 $include{$sect} .= $content; 460} 461 462# Refer to the real documentation. 463unless ($opt_no_info) 464{ 465 $sect = 'SEE ALSO'; 466 $include{$sect} ||= ''; 467 $include{$sect} .= ".PP\n" if $include{$sect}; 468 $include{$sect} .= <<EOT; 469The full documentation for 470.B $program 471is maintained as a Texinfo manual. If the 472.B info 473and 474.B $program 475programs are properly installed at your site, the command 476.IP 477.B info $program 478.PP 479should give you access to the complete manual. 480EOT 481} 482 483# Output header. 484print <<EOT; 485.\\" DO NOT MODIFY THIS FILE! It was generated by $this_program $this_version. 486.TH $PROGRAM "$section" "$date" "$package $version" GNU 487EOT 488 489# Section ordering. 490my @pre = qw(NAME SYNOPSIS DESCRIPTION OPTIONS EXAMPLES); 491my @post = ('AUTHOR', 'REPORTING BUGS', 'COPYRIGHT', 'SEE ALSO'); 492my $filter = join '|', @pre, @post; 493 494# Output content. 495for (@pre, (grep ! /^($filter)$/o, @include), @post) 496{ 497 if ($include{$_}) 498 { 499 my $quote = /\W/ ? '"' : ''; 500 print ".SH $quote$_$quote\n"; 501 502 for ($include{$_}) 503 { 504 # Replace leading dot, apostrophe and backslash tokens. 505 s/\x80/\\&./g; 506 s/\x81/\\&'/g; 507 s/\x82/\\e/g; 508 print; 509 } 510 } 511} 512 513exit; 514 515# Convert option dashes to \- to stop nroff from hyphenating 'em, and 516# embolden. Option arguments get italicised. 517sub convert_option 518{ 519 local $_ = '\fB' . shift; 520 521 s/-/\\-/g; 522 unless (s/\[=(.*)\]$/\\fR[=\\fI$1\\fR]/) 523 { 524 s/=(.)/\\fR=\\fI$1/; 525 s/ (.)/ \\fI$1/; 526 $_ .= '\fR'; 527 } 528 529 $_; 530} 531