1#!/usr/local/bin/perl -w 2 3# Generate a short man page from --help and --version output. 4# Copyright � 1997, 98, 99 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 22use 5.004; 23use strict; 24use Getopt::Long; 25use Text::Tabs qw(expand); 26use POSIX qw(strftime setlocale LC_TIME); 27 28my $this_program = 'help2man'; 29my $this_version = '1.013'; 30my $version_info = <<EOT; 31$this_program $this_version 32 33Copyright (C) 1997, 98, 99 Free Software Foundation, Inc. 34This is free software; see the source for copying conditions. There is NO 35warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 36 37Written by Brendan O'Dea <bod\@compusol.com.au> 38EOT 39 40my $help_info = <<EOT; 41`$this_program' generates a man page out of `--help' and `--version' output. 42 43Usage: $this_program [OPTION]... EXECUTABLE 44 45 -n, --name=STRING use `STRING' as the description for the NAME paragraph 46 -s, --section=SECTION use `SECTION' as the section for the man page 47 -i, --include=FILE include material from `FILE' 48 -I, --opt-include=FILE include material from `FILE' if it exists 49 -o, --output=FILE send output to `FILE' 50 -N, --no-info suppress pointer to Texinfo manual 51 --help print this help, then exit 52 --version print $this_program program version number, then exit 53 54EXECUTABLE should accept `--help' and `--version' options. 55EOT 56 57my $section = 1; 58my ($include, $opt_name, $opt_include, $opt_output, $opt_no_info); 59 60# Parse options. 61Getopt::Long::config('bundling'); 62GetOptions ( 63 'n|name=s' => \$opt_name, 64 's|section=s' => \$section, 65 'i|include=s' => \$include, 66 'I|opt-include=s' => \$opt_include, 67 'o|output=s' => \$opt_output, 68 'N|no-info' => \$opt_no_info, 69 help => sub { print $help_info; exit }, 70 version => sub { print $version_info; exit }, 71) or die $help_info; 72 73die $help_info unless @ARGV == 1; 74 75my %include = (); 76my @include = (); # to retain order 77 78# Process include file (if given). Format is: 79# 80# [section name] 81# verbatim text 82 83if ($include or $opt_include) 84{ 85 if (open INC, $include || $opt_include) 86 { 87 my $sect; 88 89 while (<INC>) 90 { 91 if (/^\[([^]]+)\]/) 92 { 93 $sect = uc $1; 94 $sect =~ s/^\s+//; 95 $sect =~ s/\s+$//; 96 next; 97 } 98 99 # Silently ignore anything before the first 100 # section--allows for comments and revision info. 101 next unless $sect; 102 103 push @include, $sect unless $include{$sect}; 104 $include{$sect} ||= ''; 105 $include{$sect} .= $_; 106 } 107 108 close INC; 109 110 die "$this_program: no valid information found in `$include'\n" 111 unless %include; 112 113 # Compress trailing blank lines. 114 for (keys %include) 115 { 116 $include{$_} =~ s/\n+$//; 117 $include{$_} .= "\n" unless /^NAME$/; 118 } 119 } 120 else 121 { 122 die "$this_program: can't open `$include' ($!)\n" if $include; 123 } 124} 125 126# Turn off localisation of executable's ouput. 127@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; 128 129# Turn off localisation of date (for strftime) 130setlocale LC_TIME, 'C'; 131 132# Expand tabs, strip trailing spaces and break into paragraphs 133sub paragraphs { split /\n\n+/, join '', expand @_ } 134 135# Grab help and version paragraphs from executable 136my @help = paragraphs `$ARGV[0] --help 2>/dev/null` 137 or die "$this_program: can't get `--help' info from $ARGV[0]\n"; 138 139my @version = paragraphs `$ARGV[0] --version 2>/dev/null` 140 or die "$this_program: can't get `--version' info from $ARGV[0]\n"; 141 142my $date = strftime "%B %Y", localtime; 143(my $program = $ARGV[0]) =~ s!.*/!!; 144my $package = $program; 145my $version; 146 147if ($opt_output) 148{ 149 unlink $opt_output 150 or die "$this_program: can't unlink $opt_output ($!)\n" 151 if -e $opt_output; 152 153 open STDOUT, ">$opt_output" 154 or die "$this_program: can't create $opt_output ($!)\n"; 155} 156 157# The first line of the --version information is assumed to be in one 158# of the following formats: 159# 160# <version> 161# <program> <version> 162# {GNU,Free} <program> <version> 163# <program> ({GNU,Free} <package>) <version> 164# <program> - {GNU,Free} <package> <version> 165# 166# and seperated from any copyright/author details by a blank line. 167 168$_ = shift @version; 169 170if (/^(\S+) +\(((?:GNU|Free) +[^)]+)\) +(.*)/ or 171 /^(\S+) +- *((?:GNU|Free) +\S+) +(.*)/) 172{ 173 $program = $1; 174 $package = $2; 175 $version = $3; 176} 177elsif (/^((?:GNU|Free) +)?(\S+) +(.*)/) 178{ 179 $program = $2; 180 $package = $1 ? "$1$2" : $2; 181 $version = $3; 182} 183else 184{ 185 $version = $_; 186} 187 188$program =~ s!.*/!!; 189 190# no info for `info' itself 191$opt_no_info = 1 if $program eq 'info'; 192 193# --name overrides --include contents 194$include{NAME} = "$program \\- $opt_name" if $opt_name; 195 196# Default (useless) NAME paragraph 197$include{NAME} ||= "$program \\- manual page for $program $version"; 198 199# Man pages traditionally have the page title in caps. 200my $PROGRAM = uc $program; 201 202# Header. 203print <<EOT; 204.\\" DO NOT MODIFY THIS FILE! It was generated by $this_program $this_version. 205.TH $PROGRAM "$section" "$date" "$package $version" FSF 206.SH NAME 207$include{NAME} 208EOT 209 210my $break; 211my $accumulate = 1; 212my @description = (); 213 214sub convert_option; 215 216# Output converted --help information. 217for (@help) 218{ 219 chomp; 220 221 if (s/^Usage: +\S+ +(.*)\n?//) 222 { 223 # Turn the usage clause into a synopsis. 224 my $synopsis = ''; 225 226 do { 227 my $syn = $1; 228 $syn =~ s/(([][]|\.\.+)+)/\\fR$1\\fI/g; 229 $syn =~ s/^/\\fI/ unless $syn =~ s/^\\fR//; 230 $syn .= '\fR'; 231 $syn =~ s/\\fI( *)\\fR/$1/g; 232 233 $synopsis .= ".br\n" unless $accumulate; 234 $synopsis .= ".B $program\n"; 235 $synopsis .= "$syn\n"; 236 $accumulate = 0; 237 } while s/^(?:Usage| *or): +\S+ +(.*)\n?//; 238 239 # Include file overrides SYNOPSIS. 240 print ".SH SYNOPSIS\n", $include{SYNOPSIS} || $synopsis; 241 242 # Dump any accumulated description text. 243 print ".SH DESCRIPTION\n"; 244 print @description; 245 246 # Add additional description text from include file. 247 if ($include{DESCRIPTION}) 248 { 249 print ".PP\n" unless $include{DESCRIPTION} =~ /^\..P/; 250 print $include{DESCRIPTION}; 251 } 252 253 $break = 1; 254 next unless $_; 255 } 256 257 # Accumulate text if the synopsis has not been produced yet. 258 if ($accumulate) 259 { 260 push @description, ".PP\n" if @description; 261 push @description, "$_\n"; 262 next; 263 } 264 265 # Convert some standard paragraph names 266 if (s/^(Options|Examples): *\n//) 267 { 268 print qq(.SH \U$1\n); 269 $break = ''; 270 next unless length; 271 } 272 273 # Catch bug report text. 274 if (/^Report bugs |^Email bug reports to /) 275 { 276 print qq(.SH "REPORTING BUGS"\n$_\n); 277 $break = ''; 278 next; 279 } 280 281 # Option subsections have second line indented. 282 if (s/^(\S.*)\n / /) 283 { 284 print qq(.SS "$1"\n); 285 $break = ''; 286 } 287 288 my $output = ''; 289 while (length) 290 { 291 my $indent = 0; 292 293 # Tagged paragraph 294 if (s/^( +(\S.*?) +)(\S.*)\n?//) 295 { 296 $indent = length $1; 297 $output .= ".TP\n$2\n$3\n"; 298 $break = 1; 299 } 300 301 # Indented paragraph 302 elsif (s/^( +)(\S.*)\n?//) 303 { 304 $indent = length $1; 305 $output .= ".IP\n$2\n"; 306 $break = 1; 307 } 308 309 # Left justified paragraph 310 else 311 { 312 s/(.*)\n?//; 313 $output .= ".PP\n" if $break; 314 $output .= "$1\n"; 315 $break = 1; 316 } 317 318 # Continuations 319 $output .= "$1\n" while s/^ {$indent}(\S.*)\n?//; 320 } 321 322 $_ = $output; 323 324 # Escape backslashes. 325 s/\\/\\e/g; 326 327 # Convert options. 328 s/(^| )(-[][\w=-]+)/$1 . convert_option $2/mge; 329 print; 330} 331 332# Print any include items other than the ones we have already dealt 333# with. 334for (@include) 335{ 336 print qq(.SH "$_"\n$include{$_}) 337 unless /^(NAME|SYNOPSIS|DESCRIPTION|SEE ALSO)$/; 338} 339 340# Refer to the real documentation. 341if ($include{'SEE ALSO'} or !$opt_no_info) 342{ 343 print qq(.SH "SEE ALSO"\n); 344 print $include{'SEE ALSO'}, ".PP\n" if $include{'SEE ALSO'}; 345 346 print <<EOT unless $opt_no_info; 347The full documentation for 348.B $program 349is maintained as a Texinfo manual. If the 350.B info 351and 352.B $program 353programs are properly installed at your site, the command 354.IP 355.B info $program 356.PP 357should give you access to the complete manual. 358EOT 359} 360 361# Output converted --version information. 362for (@version) 363{ 364 chomp; 365 366 # Join hyphenated lines. 367 s/([A-Za-z])-\n */$1/g; 368 369 # Convert copyright symbol or (c) to nroff character. 370 s/Copyright +(?:\xa9|\([Cc]\))/Copyright \\(co/g; 371 372 # Insert appropriate headings for copyright and author. 373 if (/^Copyright \\/) { print ".SH COPYRIGHT\n" } 374 elsif (/^Written +by/) { print ".SH AUTHOR\n" } 375 else { print ".PP\n"; } 376 377 # Insert line breaks before additional copyright messages and the 378 # disclaimer. 379 s/(.)\n(Copyright |This is free software)/$1\n.br\n$2/g; 380 381 print "$_\n"; 382} 383 384exit; 385 386# Convert option dashes to \- to stop nroff from hyphenating 'em, and 387# embolden. Option arguments get italicised. 388sub convert_option 389{ 390 my $option = '\fB' . shift; 391 392 $option =~ s/-/\\-/g; 393 unless ($option =~ s/\[=(.*)\]$/\\fR[=\\fI$1\\fR]/) 394 { 395 $option =~ s/=(.)/\\fR=\\fI$1/; 396 $option =~ s/ (.)/ \\fI$1/; 397 $option .= '\fR'; 398 } 399 400 $option; 401} 402