1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
|
#!/usr/bin/perl
use strict;
use warnings;
use feature ':5.10';
my $usagemessage = "Usage: $0 frobnicate > frobnicate.pod";
if(@ARGV != 1)
{
die "Need exactly one argument on the cmdline.\n$usagemessage";
}
my $path = $ARGV[0];
if( ! (-r $path && -x $path && -f $path) )
{
die "Commandline argument must be an executable file\n$usagemessage";
}
# prepend ./ if no path given. I'm going to run this thing, so we need that
$path = "./$path" unless $path =~ m{^/};
my $helpstring = `$path --help`;
my $helpstring0 = $helpstring;
# I assume the following stucture. If the --help string doesn't fit this
# structure, I barf
#
# usage: frobnicator [-h] [--xxx XXX]
# a [b ...]
#
# Does thing
#
# Synopsis:
#
# $ frobnicator xxx
# ... blah blah
#
# $ more examples ...
#
# long description that talks about stuff
# long description that talks about stuff
# long description that talks about stuff
#
# long description that talks about stuff
#
# long description that talks about stuff
#
# positional arguments:
# a Does what a does
#
# optional arguments:
# b Does what b does
# ...
# usage is the thing up to the first blank line
my ($usage) = $helpstring =~ m/(^usage.*?)\n\n/imsp
or die "Couldn't parse out the usage; helpstring='$helpstring'";
$helpstring = ${^POSTMATCH};
my $helpstring1 = $helpstring;
# Then we have a one-line summary
my ($summary) = $helpstring =~ m/(^.*?)\n\n/p
or die "Couldn't parse out the summary; helpstring='$helpstring'; helpstring0='$helpstring0'";
$helpstring = ${^POSTMATCH};
my $helpstring2 = $helpstring;
# Then the synopsis
my ($synopsis) = $helpstring =~
m/ ^synopsis.*?\n\n # synopsis title
( # capture stuff
(?:(?:[ \t] .+?)? \n)+ # a bunch of lines: empty or beginning with whitespace
) # That's all I want
/xpi
or die "Couldn't parse out the synopsis; helpstring='$helpstring'; helpstring0='$helpstring0'; helpstring1='$helpstring1'";
$helpstring = ${^POSTMATCH};
my $helpstring3 = $helpstring;
$synopsis =~ s/\n*$//g; # cull trailing whitespace
# Now a description: everything until 'xxxx arguments'. I might not have a
# description at all. I might also not have any "arguments" sections.
my ($description, $post) = $helpstring =~ /(^.*?)\n\n((?:\w+ arguments|options):?\n)/ips;
if( defined $description)
{
$helpstring = $post . ${^POSTMATCH};
}
else
{
# no arguments. Everything is a description.
$description = $helpstring;
$helpstring = '';
}
my $helpstring4 = $helpstring;
# Now the arguments
my @args;
while($helpstring !~ /^\s*$/)
{
# I see "required arguments" or "optional arguments" or "options"
my ($argument_kind) = $helpstring =~ /(^\w+ arguments|options):?\n\n?/pi
or die "Couldn't parse out argument kind; helpstring='$helpstring'; helpstring0='$helpstring0'; helpstring1='$helpstring1'; helpstring2='$helpstring2'; helpstring3='$helpstring3'; helpstring4='$helpstring4'";
$helpstring = ${^POSTMATCH};
my $helpstring5 = $helpstring;
my ($argument_what) = $helpstring =~ /(^.*?)(?:\n\n|\n$)/pis
or die "Couldn't parse out argument what; helpstring='$helpstring'; helpstring0='$helpstring0'; helpstring1='$helpstring1'; helpstring2='$helpstring2'; helpstring3='$helpstring3'; helpstring4='$helpstring4'; helpstring5='$helpstring5'";
$helpstring = ${^POSTMATCH};
# I really should parse the table argparse puts out, but let's just finish
# this as is for now
push @args, [uc($argument_kind), $argument_what];
}
# Alrighty. I can now write out this thing
say "=head1 NAME\n";
my ($programname) = $path =~ m{([^/]+$)}
or die "Couldn't parse out the program name";
say "$programname - $summary\n";
say "=head1 SYNOPSIS\n";
say linkify($synopsis);
say "";
if( $description )
{
say "=head1 DESCRIPTION\n";
say linkify($description);
say "";
}
if(@args)
{
say "=head1 OPTIONS\n";
for my $arg (@args)
{
my ($kind,$what) = @$arg;
$kind = "OPTIONAL ARGUMENTS" if $kind eq "OPTIONS";
say "=head2 $kind\n";
say linkify($what);
say "";
}
}
sub linkify
{
$_[0] =~ s{https?://\S+}{L<${^MATCH}>}gps;
return $_[0];
}
__END__
=head1 NAME
make-pod-from-help - creates POD documentation from a commandline tool
=head1 SYNOPSIS
$ make-pod-from-help frobnicate > frobnicate.pod
=head1 DESCRIPTION
This tool produces a POD from a C<--help> string. Primarily it's intended to
work with Python tools using argparse.
This tool generates a POD, which can then be made into a manpage with
C<pod2man>.
Some details L<here|http://notes.secretsauce.net/notes/2018/10/07_generating-manpages-from-python-and-argparse.html>.
=head1 REQUIRED ARGUMENTS
=over
=item <inputprogram>
Tool that we're making a manpage for
=back
=head1 AUTHOR
Dima Kogan, C<< <dima@secretsauce.net> >>
|