use warnings;
use strict;
+use Carp qw(:DEFAULT cluck);
use Pod::Checker;
use File::Find;
use File::Basename;
use File::Spec::Functions;
use Getopt::Std;
-use lib catdir(dirname($0), "perl");
+use FindBin;
+use lib "$FindBin::Bin/perl";
+
use OpenSSL::Util::Pod;
+use lib '.';
+use configdata;
+
# Set to 1 for debug output
my $debug = 0;
+# Where to find openssl command
+my $openssl = "./util/opensslwrap.sh";
+
# Options.
our($opt_d);
our($opt_e);
my %public;
my $status = 0;
+my @sections = ( 'man1', 'man3', 'man5', 'man7' );
my %mandatory_sections = (
'*' => [ 'NAME', 'DESCRIPTION', 'COPYRIGHT' ],
1 => [ 'SYNOPSIS', 'OPTIONS' ],
7 => [ ]
);
+# Collect all POD files, both internal and public, and regardless of location
+# We collect them in a hash table with each file being a key, so we can attach
+# tags to them. For example, internal docs will have the word "internal"
+# attached to them.
+my %files = ();
+# We collect files names on the fly, on known tag basis
+my %collected_tags = ();
+# We cache results based on tags
+my %collected_results = ();
+
+# files OPTIONS
+#
+# Example:
+#
+# files(TAGS => 'manual');
+# files(TAGS => [ 'manual', 'man1' ]);
+#
+# This function returns an array of files corresponding to a set of tags
+# given with the options "TAGS". The value of this option can be a single
+# word, or an array of several words, which work as inclusive or exclusive
+# selectors. Inclusive selectors are used to add one more set of files to
+# the returned array, while exclusive selectors limit the set of files added
+# to the array. The recognised tag values are:
+#
+# 'public_manual' - inclusive selector, adds public manuals to the
+# returned array of files.
+# 'internal_manual' - inclusive selector, adds internal manuals to the
+# returned array of files.
+# 'manual' - inclusive selector, adds any manual to the returned
+# array of files. This is really a shorthand for
+# 'public_manual' and 'internal_manual' combined.
+# 'public_header' - inclusive selector, adds public headers to the
+# returned array of files.
+# 'header' - inclusive selector, adds any header file to the
+# returned array of files. Since we currently only
+# care about public headers, this is exactly
+# equivalent to 'public_header', but is present for
+# consistency.
+#
+# 'man1', 'man3', 'man5', 'man7'
+# - exclusive selectors, only applicable together with
+# any of the manual selectors. If any of these are
+# present, only the manuals from the given sections
+# will be include. If none of these are present,
+# the manuals from all sections will be returned.
+#
+# All returned manual files come from configdata.pm.
+# All returned header files come from looking inside
+# "$config{sourcedir}/include/openssl"
+#
+sub files {
+ my %opts = ( @_ ); # Make a copy of the arguments
+
+ $opts{TAGS} = [ $opts{TAGS} ] if ref($opts{TAGS}) eq '';
+
+ croak "No tags given, or not an array"
+ unless exists $opts{TAGS} && ref($opts{TAGS}) eq 'ARRAY';
+
+ my %tags = map { $_ => 1 } @{$opts{TAGS}};
+ $tags{public_manual} = 1
+ if $tags{manual} && ($tags{public} // !$tags{internal});
+ $tags{internal_manual} = 1
+ if $tags{manual} && ($tags{internal} // !$tags{public});
+ $tags{public_header} = 1
+ if $tags{header} && ($tags{public} // !$tags{internal});
+ delete $tags{manual};
+ delete $tags{header};
+ delete $tags{public};
+ delete $tags{internal};
+
+ my $tags_as_key = join(':', sort keys %tags);
+
+ cluck "DEBUG[files]: This is how we got here!" if $debug;
+ print STDERR "DEBUG[files]: tags: $tags_as_key\n" if $debug;
+
+ my %tags_to_collect = ( map { $_ => 1 }
+ grep { !exists $collected_tags{$_} }
+ keys %tags );
+
+ if ($tags_to_collect{public_manual}) {
+ print STDERR "DEBUG[files]: collecting public manuals\n"
+ if $debug;
+
+ # The structure in configdata.pm is that $unified_info{mandocs}
+ # contains lists of man files, and in turn, $unified_info{depends}
+ # contains hash tables showing which POD file each of those man
+ # files depend on. We use that information to find the POD files,
+ # and to attach the man section they belong to as tags
+ foreach my $mansect ( @sections ) {
+ foreach ( map { @{$unified_info{depends}->{$_}} }
+ @{$unified_info{mandocs}->{$mansect}} ) {
+ $files{$_} = { $mansect => 1, public_manual => 1 };
+ }
+ }
+ $collected_tags{public_manual} = 1;
+ }
+
+ if ($tags_to_collect{internal_manual}) {
+ print STDERR "DEBUG[files]: collecting internal manuals\n"
+ if $debug;
+
+ # We don't have the internal docs in configdata.pm. However, they
+ # are all in the source tree, so they're easy to find.
+ foreach my $mansect ( @sections ) {
+ foreach ( glob(catfile($config{sourcedir},
+ 'doc', 'internal', $mansect, '*.pod')) ) {
+ $files{$_} = { $mansect => 1, internal_manual => 1 };
+ }
+ }
+ $collected_tags{internal_manual} = 1;
+ }
+
+ if ($tags_to_collect{public_header}) {
+ print STDERR "DEBUG[files]: collecting public headers\n"
+ if $debug;
+
+ foreach ( glob(catfile($config{sourcedir},
+ 'include', 'openssl', '*.h')) ) {
+ $files{$_} = { public_header => 1 };
+ }
+ }
+
+ my @result = @{$collected_results{$tags_as_key} // []};
+
+ if (!@result) {
+ # Produce a result based on caller tags
+ foreach my $type ( ( 'public_manual', 'internal_manual' ) ) {
+ next unless $tags{$type};
+
+ # If caller asked for specific sections, we care about sections.
+ # Otherwise, we give back all of them.
+ my @selected_sections =
+ grep { $tags{$_} } @sections;
+ @selected_sections = @sections unless @selected_sections;
+
+ foreach my $section ( ( @selected_sections ) ) {
+ push @result,
+ ( sort { basename($a) cmp basename($b) }
+ grep { $files{$_}->{$type} && $files{$_}->{$section} }
+ keys %files );
+ }
+ }
+ if ($tags{public_header}) {
+ push @result,
+ ( sort { basename($a) cmp basename($b) }
+ grep { $files{$_}->{public_header} }
+ keys %files );
+ }
+
+ if ($debug) {
+ print STDERR "DEBUG[files]: result:\n";
+ print STDERR "DEBUG[files]: $_\n" foreach @result;
+ }
+ $collected_results{$tags_as_key} = [ @result ];
+ }
+
+ return @result;
+}
# Print error message, set $status.
sub err {
return unless $contents =~ /=head1 NAME(.*)=head1 SYNOPSIS/ms;
my $tmp = $1;
$tmp =~ tr/\n/ /;
- err($id, "trailing comma before - in NAME")
+ err($id, "Trailing comma before - in NAME")
if $tmp =~ /, *-/;
$tmp =~ s/ -.*//g;
err($id, "POD markup among the names in NAME")
if $tmp =~ /[<>]/;
$tmp =~ s/ */ /g;
- err($id, "missing comma in NAME")
+ err($id, "Missing comma in NAME")
if $tmp =~ /[^,] /;
my $dirname = dirname($filename);
- my $simplename = basename(basename($filename, ".in"), ".pod");
+ my $section = basename($dirname);
+ my $simplename = basename($filename, ".pod");
my $foundfilename = 0;
my %foundfilenames = ();
my %names;
foreach my $n ( split ',', $tmp ) {
$n =~ s/^\s+//;
$n =~ s/\s+$//;
- err($id, "the name '$n' contains white-space")
+ err($id, "The name '$n' contains white-space")
if $n =~ /\s/;
$names{$n} = 1;
$foundfilename++ if $n eq $simplename;
$foundfilenames{$n} = 1
- if -f "$dirname/$n.pod" && $n ne $simplename;
+ if ( ( grep { basename($_) eq "$n.pod" }
+ files(TAGS => [ 'manual', $section ]) )
+ && $n ne $simplename );
}
- err($id, "the following exist as other .pod files:",
+ err($id, "The following exist as other .pod files:",
sort keys %foundfilenames)
if %foundfilenames;
err($id, "$simplename (filename) missing from NAME section")
foreach my $line ( split /\n+/, $syn ) {
next unless $line =~ /^\s/;
my $sym;
+ my $is_prototype = 1;
$line =~ s/STACK_OF\([^)]+\)/int/g;
$line =~ s/SPARSE_ARRAY_OF\([^)]+\)/int/g;
$line =~ s/__declspec\([^)]+\)//;
+ if ( $line =~ /typedef.*\(\*\S+\)\s+\(/ ) {
+ # a callback function with whitespace before the argument list:
+ # typedef ... (*NAME) (...
+ err($id, "Function typedef has space before arg list: $line");
+ }
if ( $line =~ /env (\S*)=/ ) {
# environment variable env NAME=...
$sym = $1;
- } elsif ( $line =~ /typedef.*\(\*(\S+)\)\(.*/ ) {
+ } elsif ( $line =~ /typedef.*\(\*(\S+)\)\s*\(/ ) {
# a callback function pointer: typedef ... (*NAME)(...
$sym = $1;
- } elsif ( $line =~ /typedef.* (\S+)\(.*/ ) {
+ } elsif ( $line =~ /typedef.* (\S+)\(/ ) {
# a callback function signature: typedef ... NAME(...
$sym = $1;
} elsif ( $line =~ /typedef.* (\S+);/ ) {
# a simple typedef: typedef ... NAME;
+ $is_prototype = 0;
$sym = $1;
} elsif ( $line =~ /enum (\S*) \{/ ) {
# an enumeration: enum ... {
$sym = $1;
} elsif ( $line =~ /#(?:define|undef) ([A-Za-z0-9_]+)/ ) {
+ $is_prototype = 0;
$sym = $1;
} elsif ( $line =~ /([A-Za-z0-9_]+)\(/ ) {
$sym = $1;
$names{$sym} = 2;
# Do some sanity checks on the prototype.
- err($id, "prototype missing spaces around commas: $line")
- if ( $line =~ /[a-z0-9],[^ ]/ );
+ err($id, "Prototype missing spaces around commas: $line")
+ if $is_prototype && $line =~ /[a-z0-9],[^ ]/;
}
foreach my $n ( keys %names ) {
foreach my $line ( split /\n+/, $contents ) {
next unless $line =~ /^=head/;
if ( $line =~ /head1/ ) {
- err($id, "duplicate section $line")
+ err($id, "Duplicate section $line")
if defined $head1{$line};
$head1{$line} = 1;
%subheads = ();
} else {
- err($id, "duplicate subsection $line")
+ err($id, "Duplicate subsection $line")
if defined $subheads{$line};
$subheads{$line} = 1;
}
- err($id, "period in =head")
+ err($id, "Period in =head")
if $line =~ /\.[^\w]/ or $line =~ /\.$/;
err($id, "not all uppercase in =head1")
if $line =~ /head1.*[a-z]/;
- err($id, "all uppercase in subhead")
+ err($id, "All uppercase in subhead")
if $line =~ /head[234][ A-Z0-9]+$/;
}
}
my $markup_re =
qr/( # Capture group
[BIL]< # The start of what we recurse on
- (?:(?-1)|.)*? # recurse the whole regexp (refering to
+ (?:(?-1)|.)*? # recurse the whole regexp (referring to
# the last opened capture group, i.e. the
# start of this regexp), or pick next
# character. Do NOT be greedy!
# Sigh, trademark
next if $k eq 'file system'
and $contents =~ /Microsoft Encrypted File System/;
- err($id, "found '$k' should use '$preferred_words{$k}'")
+ err($id, "Found '$k' should use '$preferred_words{$k}'")
if $contents =~ /\b\Q$k\E\b/i;
}
- err($id, "found 'epoch' should use 'Epoch'")
+ err($id, "Found 'epoch' should use 'Epoch'")
if $contents =~ /\bepoch\b/;
+ if ( $id =~ m@man1/@ ) {
+ err($id, "found 'tool' in NAME, should use 'command'")
+ if $contents =~ /=head1 NAME.*\btool\b.*=head1 SYNOPSIS/s;
+ err($id, "found 'utility' in NAME, should use 'command'")
+ if $contents =~ /NAME.*\butility\b.*=head1 SYNOPSIS/s;
+
+ }
}
# Perform all sorts of nit/error checks on a manpage
check_section_location($id, $contents, "EXAMPLES", "SEE ALSO");
}
+ # Make sure every link has a section.
+ while ( $contents =~ /$markup_re/msg ) {
+ my $target = $1;
+ next unless $target =~ /^L<(.*)>$/; # Skip if not L<...>
+ $target = $1; # Peal away L< and >
+ $target =~ s/\/[^\/]*$//; # Peal away possible anchor
+ $target =~ s/.*\|//g; # Peal away possible link text
+ next if $target eq ''; # Skip if links within page, or
+ next if $target =~ /::/; # links to a Perl module, or
+ next if $target =~ /^https?:/; # is a URL link, or
+ next if $target =~ /\([1357]\)$/; # it has a section
+ err($id, "Section missing in $target")
+ }
+ # Check for proper links to commands.
+ while ( $contents =~ /L<([^>]*)\(1\)(?:\/.*)?>/g ) {
+ my $target = $1;
+ next if $target =~ /openssl-?/;
+ next if ( grep { basename($_) eq "$target.pod" }
+ files(TAGS => [ 'manual', 'man1' ]) );
+ # TODO: Filter out "foreign manual" links.
+ next if $target =~ /ps|apropos|sha1sum|procmail|perl/;
+ err($id, "Bad command link L<$target(1)>");
+ }
+ # Check for proper in-man-3 API links.
+ while ( $contents =~ /L<([^>]*)\(3\)(?:\/.*)?>/g ) {
+ my $target = $1;
+ err($id, "Bad L<$target>")
+ unless $target =~ /^[_[:alpha:]][_[:alnum:]]*$/
+ }
+
unless ( $contents =~ /=for openssl generic/ ) {
if ( $filename =~ m|man3/| ) {
name_synopsis($id, $filename, $contents);
wording($id, $contents);
- err($id, "doesn't start with =pod")
+ err($id, "Doesn't start with =pod")
if $contents !~ /^=pod/;
- err($id, "doesn't end with =cut")
+ err($id, "Doesn't end with =cut")
if $contents !~ /=cut\n$/;
- err($id, "more than one cut line.")
+ err($id, "More than one cut line.")
if $contents =~ /=cut.*=cut/ms;
err($id, "EXAMPLE not EXAMPLES section.")
if $contents =~ /=head1 EXAMPLE[^S]/;
err($id, "WARNING not WARNINGS section.")
if $contents =~ /=head1 WARNING[^S]/;
- err($id, "missing copyright")
+ err($id, "Missing copyright")
if $contents !~ /Copyright .* The OpenSSL Project Authors/;
- err($id, "copyright not last")
+ err($id, "Copyright not last")
if $contents =~ /head1 COPYRIGHT.*=head/ms;
err($id, "head2 in All uppercase")
if $contents =~ /head2\s+[A-Z ]+\n/;
- err($id, "extra space after head")
+ err($id, "Extra space after head")
if $contents =~ /=head\d\s\s+/;
- err($id, "period in NAME section")
+ err($id, "Period in NAME section")
if $contents =~ /=head1 NAME.*\.\n.*=head1 SYNOPSIS/ms;
err($id, "Duplicate $1 in L<>")
if $contents =~ /L<([^>]*)\|([^>]*)>/ && $1 eq $2;
my $count = 0;
foreach my $line ( split /\n+/, $1 ) {
if ( $line =~ m@include <openssl/@ ) {
- err($id, "has multiple includes")
+ err($id, "Has multiple includes")
if ++$count == 2;
} else {
$count = 0;
$section = $1 if $dirname =~ /man([1-9])/;
foreach ( (@{$mandatory_sections{'*'}}, @{$mandatory_sections{$section}}) ) {
- err($id, "missing $_ head1 section")
+ err($id, "Missing $_ head1 section")
if $contents !~ /^=head1\s+${_}\s*$/m;
}
}
my $file = shift;
my @apis;
- open my $IN, '<', $file
+ open my $IN, '<', catfile($config{sourcedir}, $file)
or die "Can't open $file, $!, stopped";
while ( <$IN> ) {
next if /^#/;
next if /\bNOEXIST\b/;
my @fields = split();
- die "Malformed line $_"
+ die "Malformed line $. in $file: $_"
if scalar @fields != 2 && scalar @fields != 4;
push @apis, $fields[0];
}
# Parse all the manpages, getting return map of what they document
# (by looking at their NAME sections).
-sub getdocced
-{
- my $dir = shift;
- my %return;
- my %dups;
-
- foreach my $pod ( glob("$dir/*.pod") ) {
- my %podinfo = extract_pod_info($pod);
- foreach my $n ( @{$podinfo{names}} ) {
- $return{$n} = $pod;
- err("# Duplicate $n in $pod and $dups{$n}")
- if defined $dups{$n} && $dups{$n} ne $pod;
- $dups{$n} = $pod;
- }
- }
-
- return %return;
-}
-
-# Map of documented functions; function => manpage
-my %docced;
# Map of links in each POD file; filename => [ "foo(1)", "bar(3)", ... ]
my %link_map = ();
# Map of names in each POD file; "name(s)" => filename
my $missingfile = shift;
my @missing;
- open FH, $missingfile
- || die "Can't open $missingfile";
+ open FH, catfile($config{sourcedir}, $missingfile)
+ or die "Can't open $missingfile";
while ( <FH> ) {
chomp;
next if /^#/;
}
close FH;
+ for (@missing) {
+ err("$missingfile:", "$_ is documented in $name_map{$_}")
+ if !$opt_o && exists $name_map{$_} && defined $name_map{$_};
+ }
+
return @missing;
}
@missing = loadmissing('util/missingmacro.txt');
}
- foreach my $f ( glob('include/openssl/*.h') ) {
+ foreach my $f ( files(TAGS => 'public_header') ) {
# Skip some internals we don't want to document yet.
- next if $f eq 'include/openssl/asn1.h';
- next if $f eq 'include/openssl/asn1t.h';
- next if $f eq 'include/openssl/err.h';
- open(IN, $f) || die "Can't open $f, $!";
+ my $b = basename($f);
+ next if $b eq 'asn1.h';
+ next if $b eq 'asn1t.h';
+ next if $b eq 'err.h';
+ open(IN, $f)
+ or die "Can't open $f, $!";
while ( <IN> ) {
next unless /^#\s*define\s*(\S+)\(/;
- my $macro = $1;
- next if $docced{$macro} || defined $seen{$macro};
- next if $macro =~ /i2d_/
- || $macro =~ /d2i_/
- || $macro =~ /DEPRECATEDIN/
- || $macro =~ /IMPLEMENT_/
- || $macro =~ /DECLARE_/;
+ my $macro = "$1(3)"; # We know they're all in section 3
+ next if exists $name_map{$macro} || defined $seen{$macro};
+ next if $macro =~ /^i2d_/
+ || $macro =~ /^d2i_/
+ || $macro =~ /^DEPRECATEDIN/
+ || $macro =~ /\Q_fnsig(3)\E$/
+ || $macro =~ /^IMPLEMENT_/
+ || $macro =~ /^_?DECLARE_/;
# Skip macros known to be missing
- next if $opt_v && grep( /^$macro$/, @missing);
-
+ next if $opt_v && grep( /^\Q$macro\E$/, @missing);
+
err("$f:", "macro $macro undocumented")
if $opt_d || $opt_e;
$count++;
my $count = 0;
my %seen;
- my @missing = loadmissing($missingfile) if ( $opt_v );
+ my @missing = loadmissing($missingfile) if $opt_v;
foreach my $func ( parsenum($numfile) ) {
- next if $docced{$func} || defined $seen{$func};
-
- # Skip ASN1 utilities
- next if $func =~ /^ASN1_/;
+ $func .= '(3)'; # We know they're all in section 3
+ next if exists $name_map{$func} || defined $seen{$func};
- # Skip functions known to be missing
- next if $opt_v && grep( /^$func$/, @missing);
+ # Skip functions known to be missing.
+ next if $opt_v && grep( /^\Q$func\E$/, @missing);
err("$libname:", "function $func undocumented")
if $opt_d || $opt_e;
my $section = $1;
my $simplename = basename($filename, ".pod");
my $id = "${filename}:1:";
+ my %podinfo = extract_pod_info($filename, { debug => $debug });
- my $contents = '';
- {
- local $/ = undef;
- open POD, $filename or die "Couldn't open $filename, $!";
- $contents = <POD>;
- close POD;
- }
-
- $contents =~ /=head1 NAME([^=]*)=head1 /ms;
- my $tmp = $1;
- unless ( defined $tmp ) {
- err($id, "weird name section");
- return;
+ unless ( grep { $simplename eq $_ } @{$podinfo{names}} ) {
+ err($id, "$simplename not in NAME section");
+ push @{$podinfo{names}}, $simplename;
}
- $tmp =~ tr/\n/ /;
- $tmp =~ s/ -.*//g;
-
- my @names =
- map { s|/|-|g; $_ } # Treat slash as dash
- map { s/^\s+//g; s/\s+$//g; $_ } # Trim prefix and suffix blanks
- split(/,/, $tmp);
- unless ( grep { $simplename eq $_ } @names ) {
- err($id, "missing $simplename");
- push @names, $simplename;
- }
- foreach my $name (@names) {
+ foreach my $name ( @{$podinfo{names}} ) {
next if $name eq "";
- if ( $name =~ /\s/ ) {
- err($id, "'$name' contains white space")
- }
+ err($id, "'$name' contains white space")
+ if $name =~ /\s/;
my $name_sec = "$name($section)";
if ( !exists $name_map{$name_sec} ) {
$name_map{$name_sec} = $filename;
} elsif ( $filename eq $name_map{$name_sec} ) {
- err($id, "$name_sec repeated in NAME section of",
+ err($id, "$name_sec duplicated in NAME section of",
$name_map{$name_sec});
} else {
err($id, "$name_sec also in NAME section of",
}
}
- my @foreign_names =
- map { map { s/\s+//g; $_ } split(/,/, $_) }
- $contents =~ /=for\s+comment\s+foreign\s+manuals:\s*(.*)\n\n/;
- foreach ( @foreign_names ) {
- $name_map{$_} = undef; # It still exists!
+ if ( $podinfo{contents} =~ /=for openssl foreign manual (.*)\n/ ) {
+ foreach my $f ( split / /, $1 ) {
+ $name_map{$f} = undef; # It still exists!
+ }
}
- my @links = $contents =~ /L<
+ my @links =
+ $podinfo{contents} =~ /L<
# if the link is of the form L<something|name(s)>,
# then remove 'something'. Note that 'something'
# may contain POD codes as well...
# Look for L<> ("link") references that point to files that do not exist.
sub checklinks {
- foreach my $filename (sort keys %link_map) {
- foreach my $link (@{$link_map{$filename}}) {
+ foreach my $filename ( sort keys %link_map ) {
+ foreach my $link ( @{$link_map{$filename}} ) {
err("${filename}:1:", "reference to non-existing $link")
unless exists $name_map{$link};
}
my %localskips;
# Get the list of options in the command.
- open CFH, "./apps/openssl list --options $cmd|"
- || die "Can list options for $cmd, $!";
+ open CFH, "$openssl list --options $cmd|"
+ or die "Can list options for $cmd, $!";
while ( <CFH> ) {
chop;
s/ .$//;
# Get the list of flags from the synopsis
open CFH, "<$doc"
- || die "Can't open $doc, $!";
+ or die "Can't open $doc, $!";
while ( <CFH> ) {
chop;
last if /DESCRIPTION/;
}
next;
}
- next unless /\[B<-([^ >]+)/;
- my $opt = $1;
+ my $opt;
+ if ( /\[B<-([^ >]+)/ ) {
+ $opt = $1;
+ } elsif ( /^B<-([^ >]+)/ ) {
+ $opt = $1;
+ } else {
+ next;
+ }
$opt = $1 if $opt =~ /I<(.*)/;
$docopts{$1} = 1;
}
my @unimpl = sort grep { !defined $cmdopts{$_} } keys %docopts;
foreach ( @unimpl ) {
next if defined $skips{$_} || defined $localskips{$_};
- err("$cmd documented but not implemented -$_");
+ err("$doc: $cmd does not implement -$_");
}
}
my @commands = ();
# Get list of commands.
- open FH, "./apps/openssl list -1 -commands|"
- || die "Can't list commands, $!";
+ open FH, "$openssl list -1 -commands|"
+ or die "Can't list commands, $!";
while ( <FH> ) {
chop;
push @commands, $_;
# See if each has a manpage.
foreach my $cmd ( @commands ) {
next if $cmd eq 'help' || $cmd eq 'exit';
- my $doc = "doc/man1/$cmd.pod";
- $doc = "doc/man1/openssl-$cmd.pod" if -f "doc/man1/openssl-$cmd.pod";
- if ( ! -f "$doc" ) {
- err("$doc does not exist");
+ my @doc = ( grep { basename($_) eq "openssl-$cmd.pod"
+ # For "tsget" and "CA.pl" pod pages
+ || basename($_) eq "$cmd.pod" }
+ files(TAGS => [ 'manual', 'man1' ]) );
+ my $num = scalar @doc;
+ if ($num > 1) {
+ err("$num manuals for 'openssl $cmd': ".join(", ", @doc));
+ } elsif ($num < 1) {
+ err("no manual for 'openssl $cmd'");
} else {
- checkflags($cmd, $doc);
+ checkflags($cmd, @doc);
}
}
# See what help is missing.
- open FH, "./apps/openssl list --missing-help |"
- || die "Can't list missing help, $!";
+ open FH, "$openssl list --missing-help |"
+ or die "Can't list missing help, $!";
while ( <FH> ) {
chop;
my ($cmd, $flag) = split;
exit $status;
}
-if ( $opt_l ) {
- foreach ( glob('doc/*/*.pod doc/internal/*/*.pod') ) {
+# Preparation for some options, populate %name_map and %link_map
+if ( $opt_l || $opt_u || $opt_v ) {
+ foreach ( files(TAGS => 'manual') ) {
collectnames($_);
}
+}
+
+if ( $opt_l ) {
+ foreach my $func ( loadmissing("util/missingcrypto.txt") ) {
+ $name_map{$func} = undef;
+ }
checklinks();
}
if ( $opt_n ) {
publicize();
- foreach ( @ARGV ? @ARGV : glob('doc/*/*.pod doc/internal/*/*.pod') ) {
+ foreach ( @ARGV ? @ARGV : files(TAGS => 'manual') ) {
check($_);
}
# If not given args, check that all man1 commands are named properly.
if ( scalar @ARGV == 0 ) {
- foreach (glob('doc/man1/*.pod')) {
+ foreach ( files(TAGS => [ 'public_manual', 'man1' ]) ) {
next if /CA.pl/ || /openssl\.pod/ || /tsget\.pod/;
err("$_ doesn't start with openssl-") unless /openssl-/;
}
}
if ( $opt_u || $opt_v) {
- my %temp = getdocced('doc/man3');
- foreach ( keys %temp ) {
- $docced{$_} = $temp{$_};
- }
if ( $opt_o ) {
printem('crypto', 'util/libcrypto.num', 'util/missingcrypto111.txt');
printem('ssl', 'util/libssl.num', 'util/missingssl111.txt');