openssl/util/find-doc-nits.pl
Rich Salz 121677b487 Doc nits: callback function typedefs
Enhance find-doc-nits to be better about finding typedefs for
callback functions.  Fix all nits it now finds.  Added some new
typedef names to ssl.h some of which were documented but did not
exist

Reviewed-by: Richard Levitte <levitte@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/2151)
2017-01-09 16:09:50 -05:00

284 lines
7.4 KiB
Perl
Executable file

#! /usr/bin/env perl
# Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the OpenSSL license (the "License"). You may not use
# this file except in compliance with the License. You can obtain a copy
# in the file LICENSE in the source distribution or at
# https://www.openssl.org/source/license.html
require 5.10.0;
use warnings;
use strict;
use Pod::Checker;
use File::Find;
use File::Basename;
use File::Spec::Functions;
use Getopt::Std;
use lib catdir(dirname($0), "perl");
use OpenSSL::Util::Pod;
# Options.
our($opt_s);
our($opt_u);
our($opt_h);
our($opt_n);
sub help()
{
print <<EOF;
Find small errors (nits) in documentation. Options:
-n Print nits in POD pages
-s Also print missing sections in POD pages (implies -n)
-u List undocumented functions
-h Print this help message
EOF
exit;
}
my $temp = '/tmp/docnits.txt';
my $OUT;
my %mandatory_sections =
( '*' => [ 'NAME', 'DESCRIPTION', 'COPYRIGHT' ],
1 => [ 'SYNOPSIS', 'OPTIONS' ],
3 => [ 'SYNOPSIS', 'RETURN VALUES' ],
5 => [ ],
7 => [ ] );
# Cross-check functions in the NAME and SYNOPSIS section.
sub name_synopsis()
{
my $id = shift;
my $filename = shift;
my $contents = shift;
# Get NAME section and all words in it.
return unless $contents =~ /=head1 NAME(.*)=head1 SYNOPSIS/ms;
my $tmp = $1;
$tmp =~ tr/\n/ /;
$tmp =~ s/-.*//g;
$tmp =~ s/,//g;
my $dirname = dirname($filename);
my $simplename = basename($filename);
$simplename =~ s/.pod$//;
my $foundfilename = 0;
my %foundfilenames = ();
my %names;
foreach my $n ( split ' ', $tmp ) {
$names{$n} = 1;
$foundfilename++ if $n eq $simplename;
$foundfilenames{$n} = 1
if -f "$dirname/$n.pod" && $n ne $simplename;
}
print "$id the following exist as other .pod files:\n",
join(" ", sort keys %foundfilenames), "\n"
if %foundfilenames;
print "$id $simplename (filename) missing from NAME section\n",
unless $foundfilename;
# Find all functions in SYNOPSIS
return unless $contents =~ /=head1 SYNOPSIS(.*)=head1 DESCRIPTION/ms;
my $syn = $1;
foreach my $line ( split /\n+/, $syn ) {
my $sym;
$line =~ s/STACK_OF\([^)]+\)/int/g;
$line =~ s/__declspec\([^)]+\)//;
if ( $line =~ /env (\S*)=/ ) {
# environment variable env NAME=...
$sym = $1;
} elsif ( $line =~ /typedef.*\(\*(\S+)\)\(.*/ ) {
# a callback function: typedef ... (*NAME)(...
$sym = $1;
} elsif ( $line =~ /typedef.* (\S+);/ ) {
# a simple typedef: typedef ... NAME;
$sym = $1;
} elsif ( $line =~ /#define ([A-Za-z0-9_]+)/ ) {
$sym = $1;
} elsif ( $line =~ /([A-Za-z0-9_]+)\(/ ) {
$sym = $1;
}
else {
next;
}
print "$id $sym missing from NAME section\n"
unless defined $names{$sym};
$names{$sym} = 2;
# Do some sanity checks on the prototype.
print "$id prototype missing spaces around commas: $line\n"
if ( $line =~ /[a-z0-9],[^ ]/ );
}
foreach my $n ( keys %names ) {
next if $names{$n} == 2;
print "$id $n missing from SYNOPSIS\n";
}
}
sub check()
{
my $filename = shift;
my $dirname = basename(dirname($filename));
my $contents = '';
{
local $/ = undef;
open POD, $filename or die "Couldn't open $filename, $!";
$contents = <POD>;
close POD;
}
my $id = "${filename}:1:";
&name_synopsis($id, $filename, $contents)
unless $contents =~ /=for comment generic/
or $filename =~ m@man[157]/@;
print "$id doesn't start with =pod\n"
if $contents !~ /^=pod/;
print "$id doesn't end with =cut\n"
if $contents !~ /=cut\n$/;
print "$id more than one cut line.\n"
if $contents =~ /=cut.*=cut/ms;
print "$id missing copyright\n"
if $contents !~ /Copyright .* The OpenSSL Project Authors/;
print "$id copyright not last\n"
if $contents =~ /head1 COPYRIGHT.*=head/ms;
print "$id head2 in All uppercase\n"
if $contents =~ /head2\s+[A-Z ]+\n/;
print "$id extra space after head\n"
if $contents =~ /=head\d\s\s+/;
print "$id period in NAME section\n"
if $contents =~ /=head1 NAME.*\.\n.*=head1 SYNOPSIS/ms;
print "$id POD markup in NAME section\n"
if $contents =~ /=head1 NAME.*[<>].*=head1 SYNOPSIS/ms;
# Look for multiple consecutive openssl #include lines.
# Consecutive because of files like md5.pod. Sometimes it's okay
# or necessary, as in ssl/SSL_set1_host.pod
if ( $contents !~ /=for comment multiple includes/ ) {
if ( $contents =~ /=head1 SYNOPSIS(.*)=head1 DESCRIPTION/ms ) {
my $count = 0;
foreach my $line ( split /\n+/, $1 ) {
if ( $line =~ m@include <openssl/@ ) {
if ( ++$count == 2 ) {
print "$id has multiple includes\n";
}
} else {
$count = 0;
}
}
}
}
return unless $opt_s;
# Find what section this page is in. If run from "." assume
# section 3.
my $section = 3;
$section = $1 if $dirname =~ /man([1-9])/;
foreach ((@{$mandatory_sections{'*'}}, @{$mandatory_sections{$section}})) {
print "$id: missing $_ head1 section\n"
if $contents !~ /^=head1\s+${_}\s*$/m;
}
open my $OUT, '>', $temp
or die "Can't open $temp, $!";
podchecker($filename, $OUT);
close $OUT;
open $OUT, '<', $temp
or die "Can't read $temp, $!";
while ( <$OUT> ) {
next if /\(section\) in.*deprecated/;
print;
}
close $OUT;
unlink $temp || warn "Can't remove $temp, $!";
}
my %dups;
sub parsenum()
{
my $file = shift;
my @apis;
open my $IN, '<', $file
or die "Can't open $file, $!, stopped";
while ( <$IN> ) {
next if /\bNOEXIST\b/;
next if /\bEXPORT_VAR_AS_FUNC\b/;
push @apis, $1 if /([^\s]+).\s/;
}
close $IN;
print "# Found ", scalar(@apis), " in $file\n";
return sort @apis;
}
sub getdocced()
{
my $dir = shift;
my %return;
foreach my $pod ( glob("$dir/*.pod") ) {
my %podinfo = extract_pod_info($pod);
foreach my $n ( @{$podinfo{names}} ) {
$return{$n} = $pod;
print "# Duplicate $n in $pod and $dups{$n}\n"
if defined $dups{$n} && $dups{$n} ne $pod;
$dups{$n} = $pod;
}
}
return %return;
}
my %docced;
sub printem()
{
my $libname = shift;
my $numfile = shift;
my $count = 0;
foreach my $func ( &parsenum($numfile) ) {
next if $docced{$func};
# Skip ASN1 utilities
next if $func =~ /^ASN1_/;
print "$libname:$func\n";
$count++;
}
print "# Found $count missing from $numfile\n\n";
}
getopts('nshu');
&help() if ( $opt_h );
die "Need one of -n -s or -u flags.\n"
unless $opt_n or $opt_s or $opt_u;
if ( $opt_n or $opt_s ) {
foreach (@ARGV ? @ARGV : glob('doc/*/*.pod')) {
&check($_);
}
}
if ( $opt_u ) {
my %temp = &getdocced('doc/man3');
foreach ( keys %temp ) {
$docced{$_} = $temp{$_};
}
&printem('crypto', 'util/libcrypto.num');
&printem('ssl', 'util/libssl.num');
}
exit;