| #!/usr/local/bin/perl -w |
| |
| # Generate a short man page from --help and --version output. |
| # Copyright © 1997, 98, 99 Free Software Foundation, Inc. |
| |
| # This program is free software; you can redistribute it and/or modify |
| # it under the terms of the GNU General Public License as published by |
| # the Free Software Foundation; either version 2, or (at your option) |
| # any later version. |
| |
| # This program is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| # GNU General Public License for more details. |
| |
| # You should have received a copy of the GNU General Public License |
| # along with this program; if not, write to the Free Software Foundation, |
| # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| |
| # Written by Brendan O'Dea <bod@compusol.com.au> |
| |
| use 5.004; |
| use strict; |
| use Getopt::Long; |
| use POSIX qw(strftime setlocale LC_TIME); |
| |
| my $this_program = 'help2man'; |
| my $this_version = '1.010'; |
| my $version_info = <<EOT; |
| $this_program $this_version |
| |
| Copyright (C) 1997, 98, 99 Free Software Foundation, Inc. |
| This is free software; see the source for copying conditions. There is NO |
| warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| |
| Written by Brendan O'Dea <bod\@compusol.com.au> |
| EOT |
| |
| my $help_info = <<EOT; |
| `$this_program' generates a man page out of `--help' and `--version' output. |
| |
| Usage: $this_program [OPTION]... EXECUTABLE |
| |
| --name=STRING use `STRING' as the description for the NAME paragraph |
| --include=FILE include material from `FILE' |
| --opt-include=FILE include material from `FILE' if it exists |
| --output=FILE send output to `FILE' |
| --no-info suppress pointer to Texinfo manual |
| --section=SECTION use `SECTION' as the section for the man page |
| --help print this help, then exit |
| --version print $this_program program version number, then exit |
| |
| EXECUTABLE should accept `--help' and `version' options. |
| EOT |
| |
| my ($include, $opt_name, $opt_include, $opt_output, $opt_no_info, |
| $opt_section); |
| |
| # Parse options. |
| GetOptions ( |
| 'name=s' => \$opt_name, |
| 'include=s' => \$include, |
| 'opt-include=s' => \$opt_include, |
| 'output=s' => \$opt_output, |
| 'no-info' => \$opt_no_info, |
| 'section=s' => \$opt_section, |
| help => sub { print $help_info; exit }, |
| version => sub { print $version_info; exit }, |
| ) or die $help_info; |
| |
| die $help_info unless @ARGV == 1; |
| |
| my %include = (); |
| my @include = (); # to retain order |
| |
| # Process include file (if given). Format is: |
| # |
| # [section name] |
| # verbatim text |
| |
| if ($include or $opt_include) |
| { |
| if (open INC, $include || $opt_include) |
| { |
| my $sect; |
| |
| while (<INC>) |
| { |
| if (/^\[([^]]+)\]/) |
| { |
| $sect = uc $1; |
| $sect =~ s/^\s+//; |
| $sect =~ s/\s+$//; |
| next; |
| } |
| |
| # Silently ignore anything before the first |
| # section--allows for comments and revision info. |
| next unless $sect; |
| |
| push @include, $sect unless $include{$sect}; |
| $include{$sect} ||= ''; |
| $include{$sect} .= $_; |
| } |
| |
| close INC; |
| |
| die "$this_program: no valid information found in `$include'\n" |
| unless %include; |
| |
| # Compress trailing blank lines. |
| for (keys %include) |
| { |
| $include{$_} =~ s/\n+$//; |
| $include{$_} .= "\n" unless /^NAME$/; |
| } |
| } |
| else |
| { |
| die "$this_program: can't open `$include' ($!)\n" if $include; |
| } |
| } |
| |
| # Turn off localisation of executable's ouput. |
| @ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; |
| |
| # Turn off localisation of date (for strftime) |
| setlocale LC_TIME, 'C'; |
| |
| # Grab help and version paragraphs from executable |
| my @help = split /\n\n+/, `$ARGV[0] --help 2>/dev/null` |
| or die "$this_program: can't get `--help' info from $ARGV[0]\n"; |
| |
| my @version = split /\n\n+/, `$ARGV[0] --version 2>/dev/null` |
| or die "$this_program: can't get `--version' info from $ARGV[0]\n"; |
| |
| my $date = strftime "%B %Y", localtime; |
| (my $program = $ARGV[0]) =~ s!.*/!!; |
| my $package = $program; |
| my $version; |
| |
| # Set the section to 1 by default. |
| my $section = 1; |
| $section = $opt_section if $opt_section; |
| |
| if ($opt_output) |
| { |
| unlink $opt_output |
| or die "$this_program: can't unlink $opt_output ($!)\n" |
| if -e $opt_output; |
| |
| open STDOUT, ">$opt_output" |
| or die "$this_program: can't create $opt_output ($!)\n"; |
| } |
| |
| # The first line of the --version information is assumed to be in one |
| # of the following formats: |
| # |
| # <version> |
| # <program> <version> |
| # {GNU,Free} <program> <version> |
| # <program> ({GNU,Free} <package>) <version> |
| # <program> - {GNU,Free} <package> <version> |
| # |
| # and seperated from any copyright/author details by a blank line. |
| |
| $_ = shift @version; |
| |
| if (/^(\S+)\s+\(((?:GNU|Free)\s+[^)]+)\)\s+(.*)/ or |
| /^(\S+)\s+-\s*((?:GNU|Free)\s+\S+)\s+(.*)/) |
| { |
| $program = $1; |
| $package = $2; |
| $version = $3; |
| } |
| elsif (/^((?:GNU|Free)\s+)?(\S+)\s+(.*)/) |
| { |
| $program = $2; |
| $package = $1 ? "$1$2" : $2; |
| $version = $3; |
| } |
| else |
| { |
| $version = $_; |
| } |
| |
| $program =~ s!.*/!!; |
| |
| # no info for `info' itself |
| $opt_no_info = 1 if $program eq 'info'; |
| |
| # --name overrides --include contents |
| $include{NAME} = "$program \\- $opt_name" if $opt_name; |
| |
| # Default (useless) NAME paragraph |
| $include{NAME} ||= "$program \\- manual page for $program $version"; |
| |
| # Man pages traditionally have the page title in caps. |
| my $PROGRAM = uc $program; |
| |
| # Header. |
| print <<EOT; |
| .\\" DO NOT MODIFY THIS FILE! It was generated by $this_program $this_version. |
| .TH $PROGRAM "$section" "$date" "$package $version" "FSF" |
| .SH NAME |
| $include{NAME} |
| EOT |
| |
| my $accumulate = 1; |
| my @description = (); |
| |
| sub convert_option; |
| |
| # Output converted --help information. |
| for (@help) |
| { |
| chomp; |
| |
| if (s/^Usage:\s+\S+\s+(.*)\n?//) |
| { |
| # Turn the usage clause into a synopsis. |
| my $synopsis = ''; |
| |
| do { |
| my $syn = $1; |
| $syn =~ s/(([][]|\.\.+)+)/\\fR$1\\fI/g; |
| $syn =~ s/^/\\fI/ unless $syn =~ s/^\\fR//; |
| $syn .= '\fR'; |
| $syn =~ s/\\fI(\s*)\\fR/$1/g; |
| |
| $synopsis .= ".br\n" unless $accumulate; |
| $synopsis .= ".B $program\n"; |
| $synopsis .= "$syn\n"; |
| $accumulate = 0; |
| } while s/^(?:Usage|\s*or):\s+\S+\s+(.*)\n?//; |
| |
| # Include file overrides SYNOPSIS. |
| print ".SH SYNOPSIS\n", $include{SYNOPSIS} || $synopsis; |
| |
| # Dump any accumulated description text. |
| print ".SH DESCRIPTION\n"; |
| print @description; |
| |
| # Add additional description text from include file. |
| if ($include{DESCRIPTION}) |
| { |
| print ".PP\n" unless $include{DESCRIPTION} =~ /^\..P/; |
| print $include{DESCRIPTION}; |
| } |
| |
| next unless $_; |
| } |
| |
| # Accumulate text if the synopsis has not been produced yet. |
| if ($accumulate) |
| { |
| push @description, ".PP\n" if @description; |
| push @description, "$_\n"; |
| next; |
| } |
| |
| # Convert some standard paragraph names |
| if (s/^(Options|Examples):\s*\n//) |
| { |
| print qq(.SH \U$1\n); |
| next unless length; |
| } |
| |
| # Catch bug report text. |
| if (/^Report bugs |^Email bug reports to /) |
| { |
| print qq(.SH "REPORTING BUGS"\n$_\n); |
| next; |
| } |
| |
| # Special case for tar 1.12: --label=NAME\nPATTERN. |
| s{(\n[ \t]*)(-V,[ \t]+--label=NAME.*)\n[ \t]+PATTERN[ \t]+} |
| {$1$2$1\\&...=PATTERN }; |
| |
| # Convert options. |
| s/((?:^|,)\s+)(-[][\w=-]+|\\&\S+)/$1 . convert_option $2/mge; |
| |
| # Option subsections have second line indented. |
| print qq(.SS "$1"\n) if s/^(\S.*)\n(\s)/$2/; |
| |
| my $ind = 0; |
| for (split /\n/) |
| { |
| # indented paragraph |
| if (/^\s/) |
| { |
| # Join continued lines when indented to the same point as |
| # text following at least two spaces on the previous line. |
| if ($ind > 0 and /^ {$ind}\S/) |
| { |
| s/^\s+//; |
| print "$_\n" if $_; |
| } |
| else |
| { |
| # use the words(s) before two or more spaces for the |
| # tag |
| s/^(\s+)//; |
| $ind = length $1; |
| |
| if (s/(\s\s+)/\n/) |
| { |
| $ind += (length $1) + index $_, "\n"; |
| } |
| else |
| { |
| $ind = 0; |
| } |
| |
| print ".TP\n$_\n" if $_; |
| } |
| } |
| # Anything else. |
| else |
| { |
| print ".PP\n" unless $ind < 0; |
| print "$_\n"; |
| $ind = -1; |
| } |
| } |
| } |
| |
| # Print any include items other than the ones we have already dealt |
| # with. |
| for (@include) |
| { |
| print qq(.SH "$_"\n$include{$_}) |
| unless /^(NAME|SYNOPSIS|DESCRIPTION|SEE ALSO)$/; |
| } |
| |
| # Refer to the real documentation. |
| if ($include{'SEE ALSO'} or !$opt_no_info) |
| { |
| print qq(.SH "SEE ALSO"\n); |
| print $include{'SEE ALSO'}, ".PP\n" if $include{'SEE ALSO'}; |
| |
| print <<EOT unless $opt_no_info; |
| The full documentation for |
| .B $program |
| is maintained as a Texinfo manual. If the |
| .B info |
| and |
| .B $program |
| programs are properly installed at your site, the command |
| .IP |
| .B info $program |
| .PP |
| should give you access to the complete manual. |
| EOT |
| } |
| |
| # Output converted --version information. |
| for (@version) |
| { |
| chomp; |
| |
| # Join hyphenated lines. |
| s/([A-Za-z])-\n */$1/g; |
| |
| # Convert copyright symbol or (c) to nroff character. |
| s/Copyright\s+(?:\xa9|\([Cc]\))/Copyright \\(co/g; |
| |
| # Insert appropriate headings for copyright and author. |
| if (/^Copyright\s\\/) { print ".SH COPYRIGHT\n" } |
| elsif (/^Written\s+by/) { print ".SH AUTHOR\n" } |
| else { print ".PP\n"; } |
| |
| # Insert line breaks before additional copyright messages and the |
| # disclaimer. |
| s/(.)\n(Copyright\s|This is free software)/$1\n.br\n$2/g; |
| |
| print "$_\n"; |
| } |
| |
| exit; |
| |
| # Convert option dashes to \- to stop nroff from hyphenating 'em, and |
| # embolden. Option arguments get italicised. |
| sub convert_option |
| { |
| my $option = '\fB' . shift; |
| |
| $option =~ s/-/\\-/g; |
| unless ($option =~ s/\[=(.*)\]$/\\fR[=\\fI$1\\fR]/) |
| { |
| $option =~ s/=(.)/\\fR=\\fI$1/; |
| $option =~ s/ (.)/ \\fI$1/; |
| $option .= '\fR'; |
| } |
| |
| $option; |
| } |