]>
Commit | Line | Data |
---|---|---|
1bc74519 | 1 | #! /usr/bin/env perl |
05ea606a RS |
2 | # Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved. |
3 | # | |
4 | # Licensed under the OpenSSL license (the "License"). You may not use | |
5 | # this file except in compliance with the License. You can obtain a copy | |
6 | # in the file LICENSE in the source distribution or at | |
7 | # https://www.openssl.org/source/license.html | |
8 | ||
1bc74519 RS |
9 | |
10 | require 5.10.0; | |
11 | use warnings; | |
12 | use strict; | |
13 | use Pod::Checker; | |
14 | use File::Find; | |
169a8e39 | 15 | use File::Basename; |
35ea640a RS |
16 | use Getopt::Std; |
17 | ||
18 | our($opt_s); | |
1bc74519 | 19 | |
05ea606a RS |
20 | my $temp = '/tmp/docnits.txt'; |
21 | my $OUT; | |
22 | ||
169a8e39 RL |
23 | my %mandatory_sections = |
24 | ( '*' => [ 'NAME', 'DESCRIPTION', 'COPYRIGHT' ], | |
25 | 1 => [ 'SYNOPSIS', '(COMMAND\s+)?OPTIONS' ], | |
26 | 3 => [ 'SYNOPSIS', 'RETURN\s+VALUES' ], | |
27 | 5 => [ ], | |
28 | 7 => [ ] ); | |
29 | my %default_sections = | |
30 | ( apps => 1, | |
31 | crypto => 3, | |
32 | ssl => 3 ); | |
33 | ||
35ea640a RS |
34 | # Cross-check functions in the NAME and SYNOPSIS section. |
35 | sub name_synopsis() | |
36 | { | |
37 | my $id = shift; | |
38 | my $filename = shift; | |
39 | my $contents = shift; | |
40 | ||
35ea640a RS |
41 | # Get NAME section and all words in it. |
42 | return unless $contents =~ /=head1 NAME(.*)=head1 SYNOPSIS/ms; | |
43 | my $tmp = $1; | |
44 | $tmp =~ tr/\n/ /; | |
45 | $tmp =~ s/-.*//g; | |
46 | $tmp =~ s/,//g; | |
fbba5d11 RS |
47 | |
48 | my $dirname = dirname($filename); | |
49 | my $simplename = basename($filename); | |
50 | $simplename =~ s/.pod$//; | |
51 | my $foundfilename = 0; | |
52 | my %foundfilenames = (); | |
35ea640a RS |
53 | my %names; |
54 | foreach my $n ( split ' ', $tmp ) { | |
55 | $names{$n} = 1; | |
fbba5d11 RS |
56 | $foundfilename++ if $n eq $simplename; |
57 | $foundfilenames{$n} = 1 | |
58 | if -f "$dirname/$n.pod" && $n ne $simplename; | |
35ea640a | 59 | } |
fbba5d11 RS |
60 | print "$id the following exist as other .pod files:\n", |
61 | join(" ", sort keys %foundfilenames), "\n" | |
62 | if %foundfilenames; | |
63 | print "$id $simplename (filename) missing from NAME section\n", | |
64 | unless $foundfilename; | |
35ea640a RS |
65 | |
66 | # Find all functions in SYNOPSIS | |
67 | return unless $contents =~ /=head1 SYNOPSIS(.*)=head1 DESCRIPTION/ms; | |
68 | my $syn = $1; | |
69 | foreach my $line ( split /\n+/, $syn ) { | |
8162f6f5 RS |
70 | my $sym; |
71 | $line =~ s/STACK_OF\([^)]+\)//; | |
72 | if ( $line =~ /typedef.* (\S+);/ ) { | |
73 | $sym = $1; | |
74 | } elsif ( $line =~ /#define (\S+)/ ) { | |
75 | $sym = $1; | |
76 | } elsif ( $line =~ /([A-Za-z0-9_]+)\(/ ) { | |
77 | $sym = $1; | |
78 | } | |
79 | else { | |
80 | next; | |
81 | } | |
82 | print "$id $sym missing from NAME section\n" | |
83 | unless defined $names{$sym}; | |
84 | $names{$sym} = 2; | |
35ea640a RS |
85 | } |
86 | ||
87 | foreach my $n ( keys %names ) { | |
88 | next if $names{$n} == 2; | |
89 | print "$id $n missing from SYNOPSIS\n"; | |
90 | } | |
91 | } | |
92 | ||
1bc74519 RS |
93 | sub check() |
94 | { | |
169a8e39 RL |
95 | my $filename = shift; |
96 | my $dirname = basename(dirname($filename)); | |
843666ff | 97 | |
1bc74519 RS |
98 | my $contents = ''; |
99 | { | |
100 | local $/ = undef; | |
169a8e39 | 101 | open POD, $filename or die "Couldn't open $filename, $!"; |
1bc74519 RS |
102 | $contents = <POD>; |
103 | close POD; | |
104 | } | |
843666ff RS |
105 | |
106 | my $id = "${filename}:1:"; | |
35ea640a | 107 | |
4692340e | 108 | &name_synopsis($id, $filename, $contents) |
8162f6f5 RS |
109 | unless $contents =~ /=for comment generic/ |
110 | or $contents =~ /=for comment openssl_manual_section:7/ | |
111 | or $filename =~ m@/apps/@; | |
35ea640a RS |
112 | |
113 | print "$id doesn't start with =pod\n" | |
05ea606a | 114 | if $contents !~ /^=pod/; |
35ea640a | 115 | print "$id doesn't end with =cut\n" |
05ea606a | 116 | if $contents !~ /=cut\n$/; |
35ea640a | 117 | print "$id more than one cut line.\n" |
05ea606a | 118 | if $contents =~ /=cut.*=cut/ms; |
35ea640a | 119 | print "$id missing copyright\n" |
05ea606a | 120 | if $contents !~ /Copyright .* The OpenSSL Project Authors/; |
35ea640a | 121 | print "$id copyright not last\n" |
05ea606a | 122 | if $contents =~ /head1 COPYRIGHT.*=head/ms; |
35ea640a | 123 | print "$id head2 in All uppercase\n" |
843666ff | 124 | if $contents =~ /head2\s+[A-Z ]+\n/; |
35ea640a RS |
125 | print "$id extra space after head\n" |
126 | if $contents =~ /=head\d\s\s+/; | |
127 | print "$id period in NAME section\n" | |
128 | if $contents =~ /=head1 NAME.*\.\n.*=head1 SYNOPSIS/ms; | |
129 | print "$id POD markup in NAME section\n" | |
130 | if $contents =~ /=head1 NAME.*[<>].*=head1 SYNOPSIS/ms; | |
843666ff RS |
131 | |
132 | # Look for multiple consecutive openssl #include lines. | |
133 | # Consecutive because of files like md5.pod. Sometimes it's okay | |
134 | # or necessary, as in ssl/SSL_set1_host.pod | |
135 | if ( $contents !~ /=for comment multiple includes/ ) { | |
136 | if ( $contents =~ /=head1 SYNOPSIS(.*)=head1 DESCRIPTION/ms ) { | |
137 | my $count = 0; | |
138 | foreach my $line ( split /\n+/, $1 ) { | |
139 | if ( $line =~ m@include <openssl/@ ) { | |
140 | if ( ++$count == 2 ) { | |
35ea640a | 141 | print "$id has multiple includes\n"; |
843666ff RS |
142 | } |
143 | } else { | |
144 | $count = 0; | |
145 | } | |
146 | } | |
147 | } | |
148 | } | |
05ea606a | 149 | |
35ea640a RS |
150 | return unless $opt_s; |
151 | ||
843666ff RS |
152 | # Find what section this page is in. If run from "." assume |
153 | # section 3. | |
154 | my $section = $default_sections{$dirname} || 3; | |
169a8e39 RL |
155 | if ($contents =~ /^=for\s+comment\s+openssl_manual_section:\s*(\d+)\s*$/m) { |
156 | $section = $1; | |
157 | } | |
158 | ||
159 | foreach ((@{$mandatory_sections{'*'}}, @{$mandatory_sections{$section}})) { | |
35ea640a | 160 | print "$id doesn't have a head1 section matching $_\n" |
169a8e39 RL |
161 | if $contents !~ /^=head1\s+${_}\s*$/m; |
162 | } | |
163 | ||
35ea640a RS |
164 | open my $OUT, '>', $temp |
165 | or die "Can't open $temp, $!"; | |
169a8e39 | 166 | podchecker($filename, $OUT); |
35ea640a RS |
167 | close $OUT; |
168 | open $OUT, '<', $temp | |
169 | or die "Can't read $temp, $!"; | |
170 | while ( <$OUT> ) { | |
171 | next if /\(section\) in.*deprecated/; | |
172 | print; | |
173 | } | |
174 | close $OUT; | |
175 | unlink $temp || warn "Can't remove $temp, $!"; | |
05ea606a | 176 | } |
1bc74519 | 177 | |
35ea640a RS |
178 | getopts('s'); |
179 | ||
180 | foreach (@ARGV ? @ARGV : glob('doc/*/*.pod')) { | |
05ea606a | 181 | &check($_); |
1bc74519 | 182 | } |
05ea606a | 183 | |
35ea640a | 184 | exit; |