help2man 11 KB

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