#! /usr/bin/env perl
# Copyright 2002-2019 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the Apache License 2.0 (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;
my $debug = 0; # Set to 1 for debug output
# Options.
our($opt_d);
our($opt_e);
our($opt_s);
our($opt_o);
our($opt_h);
our($opt_l);
our($opt_n);
our($opt_p);
our($opt_u);
our($opt_v);
our($opt_c);
sub help {
print <<EOF;
Find small errors (nits) in documentation. Options:
-d Detailed list of undocumented (implies -u)
-e Detailed list of new undocumented (implies -v)
-s Same as -e except no output is generated if nothing is undocumented
-o Causes -e/-v to count symbols added since 1.1.1 as new (implies -v)
-l Print bogus links
-n Print nits in POD pages
-p Warn if non-public name documented (implies -n)
-u Count undocumented functions
-v Count new undocumented functions
-h Print this help message
-c List undocumented commands and options
EOF
exit;
}
my $temp = '/tmp/docnits.txt';
my $OUT;
my %public;
my $status = 0;
my %mandatory_sections =
( '*' => [ 'NAME', 'DESCRIPTION', 'COPYRIGHT' ],
1 => [ 'SYNOPSIS', 'OPTIONS' ],
3 => [ 'SYNOPSIS', 'RETURN VALUES' ],
5 => [ ],
7 => [ ] );
# Print error message, set $status.
sub err {
print join(" ", @_), "\n";
$status = 1
}
# 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/ /;
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")
if $tmp =~ /[^,] /;
my $dirname = dirname($filename);
my $simplename = basename(basename($filename, ".in"), ".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")
if $n =~ /\s/;
$names{$n} = 1;
$foundfilename++ if $n eq $simplename;
$foundfilenames{$n} = 1
if ((-f "$dirname/$n.pod.in" || -f "$dirname/$n.pod")
&& $n ne $simplename);
}
err($id, "the following exist as other .pod or .pod.in files:",
sort keys %foundfilenames)
if %foundfilenames;
err($id, "$simplename (filename) missing from NAME section")
unless $foundfilename;
foreach my $n ( keys %names ) {
err($id, "$n is not public")
if $opt_p and !defined $public{$n};
}
# Find all functions in SYNOPSIS
return unless $contents =~ /=head1 SYNOPSIS(.*)=head1 DESCRIPTION/ms;
my $syn = $1;
foreach my $line ( split /\n+/, $syn ) {
next unless $line =~ /^\s/;
my $sym;
$line =~ s/STACK_OF\([^)]+\)/int/g;
$line =~ s/SPARSE_ARRAY_OF\([^)]+\)/int/g;
$line =~ s/__declspec\([^)]+\)//;
if ( $line =~ /env (\S*)=/ ) {
# environment variable env NAME=...
$sym = $1;
} elsif ( $line =~ /typedef.*\(\*(\S+)\)\(.*/ ) {
# a callback function pointer: typedef ... (*NAME)(...
$sym = $1;
} elsif ( $line =~ /typedef.* (\S+)\(.*/ ) {
# a callback function signature: typedef ... NAME(...
$sym = $1;
} elsif ( $line =~ /typedef.* (\S+);/ ) {
# a simple typedef: typedef ... NAME;
$sym = $1;
} elsif ( $line =~ /enum (\S*) \{/ ) {
# an enumeration: enum ... {
$sym = $1;
} elsif ( $line =~ /#(?:define|undef) ([A-Za-z0-9_]+)/ ) {
$sym = $1;
} elsif ( $line =~ /([A-Za-z0-9_]+)\(/ ) {
$sym = $1;
}
else {
next;
}
err($id, "$sym missing from NAME section")
unless defined $names{$sym};
$names{$sym} = 2;
# Do some sanity checks on the prototype.
err($id, "prototype missing spaces around commas: $line")
if ( $line =~ /[a-z0-9],[^ ]/