find-doc-nits revision 1.1 1 1.1 christos #! /usr/bin/env perl
2 1.1 christos # Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved.
3 1.1 christos #
4 1.1 christos # Licensed under the OpenSSL license (the "License"). You may not use
5 1.1 christos # this file except in compliance with the License. You can obtain a copy
6 1.1 christos # in the file LICENSE in the source distribution or at
7 1.1 christos # https://www.openssl.org/source/license.html
8 1.1 christos
9 1.1 christos
10 1.1 christos require 5.10.0;
11 1.1 christos use warnings;
12 1.1 christos use strict;
13 1.1 christos use Pod::Checker;
14 1.1 christos use File::Find;
15 1.1 christos use File::Basename;
16 1.1 christos use File::Spec::Functions;
17 1.1 christos use Getopt::Std;
18 1.1 christos use lib catdir(dirname($0), "perl");
19 1.1 christos use OpenSSL::Util::Pod;
20 1.1 christos
21 1.1 christos # Options.
22 1.1 christos our($opt_d);
23 1.1 christos our($opt_h);
24 1.1 christos our($opt_l);
25 1.1 christos our($opt_n);
26 1.1 christos our($opt_p);
27 1.1 christos our($opt_s);
28 1.1 christos our($opt_u);
29 1.1 christos our($opt_c);
30 1.1 christos
31 1.1 christos sub help()
32 1.1 christos {
33 1.1 christos print <<EOF;
34 1.1 christos Find small errors (nits) in documentation. Options:
35 1.1 christos -d Detailed list of undocumented (implies -u)
36 1.1 christos -l Print bogus links
37 1.1 christos -n Print nits in POD pages
38 1.1 christos -s Also print missing sections in POD pages (implies -n)
39 1.1 christos -p Warn if non-public name documented (implies -n)
40 1.1 christos -u List undocumented functions
41 1.1 christos -h Print this help message
42 1.1 christos -c List undocumented commands and options
43 1.1 christos EOF
44 1.1 christos exit;
45 1.1 christos }
46 1.1 christos
47 1.1 christos my $temp = '/tmp/docnits.txt';
48 1.1 christos my $OUT;
49 1.1 christos my %public;
50 1.1 christos
51 1.1 christos my %mandatory_sections =
52 1.1 christos ( '*' => [ 'NAME', 'DESCRIPTION', 'COPYRIGHT' ],
53 1.1 christos 1 => [ 'SYNOPSIS', 'OPTIONS' ],
54 1.1 christos 3 => [ 'SYNOPSIS', 'RETURN VALUES' ],
55 1.1 christos 5 => [ ],
56 1.1 christos 7 => [ ] );
57 1.1 christos
58 1.1 christos # Cross-check functions in the NAME and SYNOPSIS section.
59 1.1 christos sub name_synopsis()
60 1.1 christos {
61 1.1 christos my $id = shift;
62 1.1 christos my $filename = shift;
63 1.1 christos my $contents = shift;
64 1.1 christos
65 1.1 christos # Get NAME section and all words in it.
66 1.1 christos return unless $contents =~ /=head1 NAME(.*)=head1 SYNOPSIS/ms;
67 1.1 christos my $tmp = $1;
68 1.1 christos $tmp =~ tr/\n/ /;
69 1.1 christos print "$id trailing comma before - in NAME\n" if $tmp =~ /, *-/;
70 1.1 christos $tmp =~ s/ -.*//g;
71 1.1 christos $tmp =~ s/ */ /g;
72 1.1 christos print "$id missing comma in NAME\n" if $tmp =~ /[^,] /;
73 1.1 christos $tmp =~ s/,//g;
74 1.1 christos
75 1.1 christos my $dirname = dirname($filename);
76 1.1 christos my $simplename = basename($filename);
77 1.1 christos $simplename =~ s/.pod$//;
78 1.1 christos my $foundfilename = 0;
79 1.1 christos my %foundfilenames = ();
80 1.1 christos my %names;
81 1.1 christos foreach my $n ( split ' ', $tmp ) {
82 1.1 christos $names{$n} = 1;
83 1.1 christos $foundfilename++ if $n eq $simplename;
84 1.1 christos $foundfilenames{$n} = 1
85 1.1 christos if -f "$dirname/$n.pod" && $n ne $simplename;
86 1.1 christos }
87 1.1 christos print "$id the following exist as other .pod files:\n",
88 1.1 christos join(" ", sort keys %foundfilenames), "\n"
89 1.1 christos if %foundfilenames;
90 1.1 christos print "$id $simplename (filename) missing from NAME section\n"
91 1.1 christos unless $foundfilename;
92 1.1 christos foreach my $n ( keys %names ) {
93 1.1 christos print "$id $n is not public\n"
94 1.1 christos if $opt_p and !defined $public{$n};
95 1.1 christos }
96 1.1 christos
97 1.1 christos # Find all functions in SYNOPSIS
98 1.1 christos return unless $contents =~ /=head1 SYNOPSIS(.*)=head1 DESCRIPTION/ms;
99 1.1 christos my $syn = $1;
100 1.1 christos foreach my $line ( split /\n+/, $syn ) {
101 1.1 christos my $sym;
102 1.1 christos $line =~ s/STACK_OF\([^)]+\)/int/g;
103 1.1 christos $line =~ s/__declspec\([^)]+\)//;
104 1.1 christos if ( $line =~ /env (\S*)=/ ) {
105 1.1 christos # environment variable env NAME=...
106 1.1 christos $sym = $1;
107 1.1 christos } elsif ( $line =~ /typedef.*\(\*(\S+)\)\(.*/ ) {
108 1.1 christos # a callback function pointer: typedef ... (*NAME)(...
109 1.1 christos $sym = $1;
110 1.1 christos } elsif ( $line =~ /typedef.* (\S+)\(.*/ ) {
111 1.1 christos # a callback function signature: typedef ... NAME(...
112 1.1 christos $sym = $1;
113 1.1 christos } elsif ( $line =~ /typedef.* (\S+);/ ) {
114 1.1 christos # a simple typedef: typedef ... NAME;
115 1.1 christos $sym = $1;
116 1.1 christos } elsif ( $line =~ /enum (\S*) \{/ ) {
117 1.1 christos # an enumeration: enum ... {
118 1.1 christos $sym = $1;
119 1.1 christos } elsif ( $line =~ /#define ([A-Za-z0-9_]+)/ ) {
120 1.1 christos $sym = $1;
121 1.1 christos } elsif ( $line =~ /([A-Za-z0-9_]+)\(/ ) {
122 1.1 christos $sym = $1;
123 1.1 christos }
124 1.1 christos else {
125 1.1 christos next;
126 1.1 christos }
127 1.1 christos print "$id $sym missing from NAME section\n"
128 1.1 christos unless defined $names{$sym};
129 1.1 christos $names{$sym} = 2;
130 1.1 christos
131 1.1 christos # Do some sanity checks on the prototype.
132 1.1 christos print "$id prototype missing spaces around commas: $line\n"
133 1.1 christos if ( $line =~ /[a-z0-9],[^ ]/ );
134 1.1 christos }
135 1.1 christos
136 1.1 christos foreach my $n ( keys %names ) {
137 1.1 christos next if $names{$n} == 2;
138 1.1 christos print "$id $n missing from SYNOPSIS\n";
139 1.1 christos }
140 1.1 christos }
141 1.1 christos
142 1.1 christos sub check()
143 1.1 christos {
144 1.1 christos my $filename = shift;
145 1.1 christos my $dirname = basename(dirname($filename));
146 1.1 christos
147 1.1 christos my $contents = '';
148 1.1 christos {
149 1.1 christos local $/ = undef;
150 1.1 christos open POD, $filename or die "Couldn't open $filename, $!";
151 1.1 christos $contents = <POD>;
152 1.1 christos close POD;
153 1.1 christos }
154 1.1 christos
155 1.1 christos my $id = "${filename}:1:";
156 1.1 christos
157 1.1 christos # Find what section this page is in; assume 3.
158 1.1 christos my $section = 3;
159 1.1 christos $section = 1 if $dirname eq 'apps';
160 1.1 christos $section = $1 if ( $contents =~ /=for comment openssl_manual_section:(\d)/);
161 1.1 christos
162 1.1 christos &name_synopsis($id, $filename, $contents)
163 1.1 christos unless $contents =~ /=for comment generic/
164 1.1 christos or $section != 3;
165 1.1 christos
166 1.1 christos print "$id doesn't start with =pod\n"
167 1.1 christos if $contents !~ /^=pod/;
168 1.1 christos print "$id doesn't end with =cut\n"
169 1.1 christos if $contents !~ /=cut\n$/;
170 1.1 christos print "$id more than one cut line.\n"
171 1.1 christos if $contents =~ /=cut.*=cut/ms;
172 1.1 christos print "$id missing copyright\n"
173 1.1 christos if $contents !~ /Copyright .* The OpenSSL Project Authors/;
174 1.1 christos print "$id copyright not last\n"
175 1.1 christos if $contents =~ /head1 COPYRIGHT.*=head/ms;
176 1.1 christos print "$id head2 in All uppercase\n"
177 1.1 christos if $contents =~ /head2\s+[A-Z ]+\n/;
178 1.1 christos print "$id extra space after head\n"
179 1.1 christos if $contents =~ /=head\d\s\s+/;
180 1.1 christos print "$id period in NAME section\n"
181 1.1 christos if $contents =~ /=head1 NAME.*\.\n.*=head1 SYNOPSIS/ms;
182 1.1 christos print "$id POD markup in NAME section\n"
183 1.1 christos if $contents =~ /=head1 NAME.*[<>].*=head1 SYNOPSIS/ms;
184 1.1 christos print "$id Duplicate $1 in L<>\n"
185 1.1 christos if $contents =~ /L<([^>]*)\|([^>]*)>/ && $1 eq $2;
186 1.1 christos print "$id Bad =over $1\n"
187 1.1 christos if $contents =~ /=over([^ ][^24])/;
188 1.1 christos print "$id Possible version style issue\n"
189 1.1 christos if $contents =~ /OpenSSL version [019]/;
190 1.1 christos
191 1.1 christos if ( $contents !~ /=for comment multiple includes/ ) {
192 1.1 christos # Look for multiple consecutive openssl #include lines
193 1.1 christos # (non-consecutive lines are okay; see crypto/MD5.pod).
194 1.1 christos if ( $contents =~ /=head1 SYNOPSIS(.*)=head1 DESCRIPTION/ms ) {
195 1.1 christos my $count = 0;
196 1.1 christos foreach my $line ( split /\n+/, $1 ) {
197 1.1 christos if ( $line =~ m@include <openssl/@ ) {
198 1.1 christos print "$id has multiple includes\n" if ++$count == 2;
199 1.1 christos } else {
200 1.1 christos $count = 0;
201 1.1 christos }
202 1.1 christos }
203 1.1 christos }
204 1.1 christos }
205 1.1 christos
206 1.1 christos open my $OUT, '>', $temp
207 1.1 christos or die "Can't open $temp, $!";
208 1.1 christos podchecker($filename, $OUT);
209 1.1 christos close $OUT;
210 1.1 christos open $OUT, '<', $temp
211 1.1 christos or die "Can't read $temp, $!";
212 1.1 christos while ( <$OUT> ) {
213 1.1 christos next if /\(section\) in.*deprecated/;
214 1.1 christos print;
215 1.1 christos }
216 1.1 christos close $OUT;
217 1.1 christos unlink $temp || warn "Can't remove $temp, $!";
218 1.1 christos
219 1.1 christos foreach ((@{$mandatory_sections{'*'}}, @{$mandatory_sections{$section}})) {
220 1.1 christos # Skip "return values" if not -s
221 1.1 christos next if $_ eq 'RETURN VALUES' and not $opt_s;
222 1.1 christos print "$id: missing $_ head1 section\n"
223 1.1 christos if $contents !~ /^=head1\s+${_}\s*$/m;
224 1.1 christos }
225 1.1 christos }
226 1.1 christos
227 1.1 christos my %dups;
228 1.1 christos
229 1.1 christos sub parsenum()
230 1.1 christos {
231 1.1 christos my $file = shift;
232 1.1 christos my @apis;
233 1.1 christos
234 1.1 christos open my $IN, '<', $file
235 1.1 christos or die "Can't open $file, $!, stopped";
236 1.1 christos
237 1.1 christos while ( <$IN> ) {
238 1.1 christos next if /^#/;
239 1.1 christos next if /\bNOEXIST\b/;
240 1.1 christos next if /\bEXPORT_VAR_AS_FUNC\b/;
241 1.1 christos my @fields = split();
242 1.1 christos die "Malformed line $_"
243 1.1 christos if scalar @fields != 2 && scalar @fields != 4;
244 1.1 christos push @apis, $fields[0];
245 1.1 christos }
246 1.1 christos
247 1.1 christos close $IN;
248 1.1 christos
249 1.1 christos print "# Found ", scalar(@apis), " in $file\n" unless $opt_p;
250 1.1 christos return sort @apis;
251 1.1 christos }
252 1.1 christos
253 1.1 christos sub getdocced()
254 1.1 christos {
255 1.1 christos my $dir = shift;
256 1.1 christos my %return;
257 1.1 christos
258 1.1 christos foreach my $pod ( glob("$dir/*.pod") ) {
259 1.1 christos my %podinfo = extract_pod_info($pod);
260 1.1 christos foreach my $n ( @{$podinfo{names}} ) {
261 1.1 christos $return{$n} = $pod;
262 1.1 christos print "# Duplicate $n in $pod and $dups{$n}\n"
263 1.1 christos if defined $dups{$n} && $dups{$n} ne $pod;
264 1.1 christos $dups{$n} = $pod;
265 1.1 christos }
266 1.1 christos }
267 1.1 christos
268 1.1 christos return %return;
269 1.1 christos }
270 1.1 christos
271 1.1 christos my %docced;
272 1.1 christos
273 1.1 christos sub checkmacros()
274 1.1 christos {
275 1.1 christos my $count = 0;
276 1.1 christos
277 1.1 christos print "# Checking macros (approximate)\n";
278 1.1 christos foreach my $f ( glob('include/openssl/*.h') ) {
279 1.1 christos # Skip some internals we don't want to document yet.
280 1.1 christos next if $f eq 'include/openssl/asn1.h';
281 1.1 christos next if $f eq 'include/openssl/asn1t.h';
282 1.1 christos next if $f eq 'include/openssl/err.h';
283 1.1 christos open(IN, $f) || die "Can't open $f, $!";
284 1.1 christos while ( <IN> ) {
285 1.1 christos next unless /^#\s*define\s*(\S+)\(/;
286 1.1 christos my $macro = $1;
287 1.1 christos next if $docced{$macro};
288 1.1 christos next if $macro =~ /i2d_/
289 1.1 christos || $macro =~ /d2i_/
290 1.1 christos || $macro =~ /DEPRECATEDIN/
291 1.1 christos || $macro =~ /IMPLEMENT_/
292 1.1 christos || $macro =~ /DECLARE_/;
293 1.1 christos print "$f:$macro\n" if $opt_d;
294 1.1 christos $count++;
295 1.1 christos }
296 1.1 christos close(IN);
297 1.1 christos }
298 1.1 christos print "# Found $count macros missing (not all should be documented)\n"
299 1.1 christos }
300 1.1 christos
301 1.1 christos sub printem()
302 1.1 christos {
303 1.1 christos my $libname = shift;
304 1.1 christos my $numfile = shift;
305 1.1 christos my $count = 0;
306 1.1 christos
307 1.1 christos foreach my $func ( &parsenum($numfile) ) {
308 1.1 christos next if $docced{$func};
309 1.1 christos
310 1.1 christos # Skip ASN1 utilities
311 1.1 christos next if $func =~ /^ASN1_/;
312 1.1 christos
313 1.1 christos print "$libname:$func\n" if $opt_d;
314 1.1 christos $count++;
315 1.1 christos }
316 1.1 christos print "# Found $count missing from $numfile\n\n";
317 1.1 christos }
318 1.1 christos
319 1.1 christos
320 1.1 christos # Collection of links in each POD file.
321 1.1 christos # filename => [ "foo(1)", "bar(3)", ... ]
322 1.1 christos my %link_collection = ();
323 1.1 christos # Collection of names in each POD file.
324 1.1 christos # "name(s)" => filename
325 1.1 christos my %name_collection = ();
326 1.1 christos
327 1.1 christos sub collectnames {
328 1.1 christos my $filename = shift;
329 1.1 christos $filename =~ m|man(\d)/|;
330 1.1 christos my $section = $1;
331 1.1 christos my $simplename = basename($filename, ".pod");
332 1.1 christos my $id = "${filename}:1:";
333 1.1 christos
334 1.1 christos my $contents = '';
335 1.1 christos {
336 1.1 christos local $/ = undef;
337 1.1 christos open POD, $filename or die "Couldn't open $filename, $!";
338 1.1 christos $contents = <POD>;
339 1.1 christos close POD;
340 1.1 christos }
341 1.1 christos
342 1.1 christos $contents =~ /=head1 NAME([^=]*)=head1 /ms;
343 1.1 christos my $tmp = $1;
344 1.1 christos unless (defined $tmp) {
345 1.1 christos print "$id weird name section\n";
346 1.1 christos return;
347 1.1 christos }
348 1.1 christos $tmp =~ tr/\n/ /;
349 1.1 christos $tmp =~ s/-.*//g;
350 1.1 christos
351 1.1 christos my @names = map { s/\s+//g; $_ } split(/,/, $tmp);
352 1.1 christos unless (grep { $simplename eq $_ } @names) {
353 1.1 christos print "$id missing $simplename\n";
354 1.1 christos push @names, $simplename;
355 1.1 christos }
356 1.1 christos foreach my $name (@names) {
357 1.1 christos next if $name eq "";
358 1.1 christos my $name_sec = "$name($section)";
359 1.1 christos if (! exists $name_collection{$name_sec}) {
360 1.1 christos $name_collection{$name_sec} = $filename;
361 1.1 christos } else { #elsif ($filename ne $name_collection{$name_sec}) {
362 1.1 christos print "$id $name_sec also in $name_collection{$name_sec}\n";
363 1.1 christos }
364 1.1 christos }
365 1.1 christos
366 1.1 christos my @foreign_names =
367 1.1 christos map { map { s/\s+//g; $_ } split(/,/, $_) }
368 1.1 christos $contents =~ /=for\s+comment\s+foreign\s+manuals:\s*(.*)\n\n/;
369 1.1 christos foreach (@foreign_names) {
370 1.1 christos $name_collection{$_} = undef; # It still exists!
371 1.1 christos }
372 1.1 christos
373 1.1 christos my @links = $contents =~ /L<
374 1.1 christos # if the link is of the form L<something|name(s)>,
375 1.1 christos # then remove 'something'. Note that 'something'
376 1.1 christos # may contain POD codes as well...
377 1.1 christos (?:(?:[^\|]|<[^>]*>)*\|)?
378 1.1 christos # we're only interested in referenses that have
379 1.1 christos # a one digit section number
380 1.1 christos ([^\/>\(]+\(\d\))
381 1.1 christos /gx;
382 1.1 christos $link_collection{$filename} = [ @links ];
383 1.1 christos }
384 1.1 christos
385 1.1 christos sub checklinks {
386 1.1 christos foreach my $filename (sort keys %link_collection) {
387 1.1 christos foreach my $link (@{$link_collection{$filename}}) {
388 1.1 christos print "${filename}:1: reference to non-existing $link\n"
389 1.1 christos unless exists $name_collection{$link};
390 1.1 christos }
391 1.1 christos }
392 1.1 christos }
393 1.1 christos
394 1.1 christos sub publicize() {
395 1.1 christos foreach my $name ( &parsenum('util/libcrypto.num') ) {
396 1.1 christos $public{$name} = 1;
397 1.1 christos }
398 1.1 christos foreach my $name ( &parsenum('util/libssl.num') ) {
399 1.1 christos $public{$name} = 1;
400 1.1 christos }
401 1.1 christos foreach my $name ( &parsenum('util/private.num') ) {
402 1.1 christos $public{$name} = 1;
403 1.1 christos }
404 1.1 christos }
405 1.1 christos
406 1.1 christos my %skips = (
407 1.1 christos 'aes128' => 1,
408 1.1 christos 'aes192' => 1,
409 1.1 christos 'aes256' => 1,
410 1.1 christos 'aria128' => 1,
411 1.1 christos 'aria192' => 1,
412 1.1 christos 'aria256' => 1,
413 1.1 christos 'camellia128' => 1,
414 1.1 christos 'camellia192' => 1,
415 1.1 christos 'camellia256' => 1,
416 1.1 christos 'des' => 1,
417 1.1 christos 'des3' => 1,
418 1.1 christos 'idea' => 1,
419 1.1 christos '[cipher]' => 1,
420 1.1 christos '[digest]' => 1,
421 1.1 christos );
422 1.1 christos
423 1.1 christos sub checkflags() {
424 1.1 christos my $cmd = shift;
425 1.1 christos my %cmdopts;
426 1.1 christos my %docopts;
427 1.1 christos my $ok = 1;
428 1.1 christos
429 1.1 christos # Get the list of options in the command.
430 1.1 christos open CFH, "./apps/openssl list --options $cmd|"
431 1.1 christos || die "Can list options for $cmd, $!";
432 1.1 christos while ( <CFH> ) {
433 1.1 christos chop;
434 1.1 christos s/ .$//;
435 1.1 christos $cmdopts{$_} = 1;
436 1.1 christos }
437 1.1 christos close CFH;
438 1.1 christos
439 1.1 christos # Get the list of flags from the synopsis
440 1.1 christos open CFH, "<doc/apps/$cmd.pod"
441 1.1 christos || die "Can't open $cmd.pod, $!";
442 1.1 christos while ( <CFH> ) {
443 1.1 christos chop;
444 1.1 christos last if /DESCRIPTION/;
445 1.1 christos next unless /\[B<-([^ >]+)/;
446 1.1 christos $docopts{$1} = 1;
447 1.1 christos }
448 1.1 christos close CFH;
449 1.1 christos
450 1.1 christos # See what's in the command not the manpage.
451 1.1 christos my @undocced = ();
452 1.1 christos foreach my $k ( keys %cmdopts ) {
453 1.1 christos push @undocced, $k unless $docopts{$k};
454 1.1 christos }
455 1.1 christos if ( scalar @undocced > 0 ) {
456 1.1 christos $ok = 0;
457 1.1 christos foreach ( @undocced ) {
458 1.1 christos print "doc/apps/$cmd.pod: Missing -$_\n";
459 1.1 christos }
460 1.1 christos }
461 1.1 christos
462 1.1 christos # See what's in the command not the manpage.
463 1.1 christos my @unimpl = ();
464 1.1 christos foreach my $k ( keys %docopts ) {
465 1.1 christos push @unimpl, $k unless $cmdopts{$k};
466 1.1 christos }
467 1.1 christos if ( scalar @unimpl > 0 ) {
468 1.1 christos $ok = 0;
469 1.1 christos foreach ( @unimpl ) {
470 1.1 christos next if defined $skips{$_};
471 1.1 christos print "doc/apps/$cmd.pod: Not implemented -$_\n";
472 1.1 christos }
473 1.1 christos }
474 1.1 christos
475 1.1 christos return $ok;
476 1.1 christos }
477 1.1 christos
478 1.1 christos getopts('cdlnsphu');
479 1.1 christos
480 1.1 christos &help() if $opt_h;
481 1.1 christos $opt_n = 1 if $opt_s or $opt_p;
482 1.1 christos $opt_u = 1 if $opt_d;
483 1.1 christos
484 1.1 christos die "Need one of -[cdlnspu] flags.\n"
485 1.1 christos unless $opt_c or $opt_l or $opt_n or $opt_u;
486 1.1 christos
487 1.1 christos if ( $opt_c ) {
488 1.1 christos my $ok = 1;
489 1.1 christos my @commands = ();
490 1.1 christos
491 1.1 christos # Get list of commands.
492 1.1 christos open FH, "./apps/openssl list -1 -commands|"
493 1.1 christos || die "Can't list commands, $!";
494 1.1 christos while ( <FH> ) {
495 1.1 christos chop;
496 1.1 christos push @commands, $_;
497 1.1 christos }
498 1.1 christos close FH;
499 1.1 christos
500 1.1 christos # See if each has a manpage.
501 1.1 christos foreach ( @commands ) {
502 1.1 christos next if $_ eq 'help' || $_ eq 'exit';
503 1.1 christos if ( ! -f "doc/apps/$_.pod" ) {
504 1.1 christos print "doc/apps/$_.pod does not exist\n";
505 1.1 christos $ok = 0;
506 1.1 christos } else {
507 1.1 christos $ok = 0 if not &checkflags($_);
508 1.1 christos }
509 1.1 christos }
510 1.1 christos
511 1.1 christos # See what help is missing.
512 1.1 christos open FH, "./apps/openssl list --missing-help |"
513 1.1 christos || die "Can't list missing help, $!";
514 1.1 christos while ( <FH> ) {
515 1.1 christos chop;
516 1.1 christos my ($cmd, $flag) = split;
517 1.1 christos print "$cmd has no help for -$flag\n";
518 1.1 christos $ok = 0;
519 1.1 christos }
520 1.1 christos close FH;
521 1.1 christos
522 1.1 christos exit 1 if not $ok;
523 1.1 christos }
524 1.1 christos
525 1.1 christos if ( $opt_l ) {
526 1.1 christos foreach (@ARGV ? @ARGV : glob('doc/*/*.pod')) {
527 1.1 christos collectnames($_);
528 1.1 christos }
529 1.1 christos checklinks();
530 1.1 christos }
531 1.1 christos
532 1.1 christos if ( $opt_n ) {
533 1.1 christos &publicize() if $opt_p;
534 1.1 christos foreach (@ARGV ? @ARGV : glob('doc/*/*.pod')) {
535 1.1 christos &check($_);
536 1.1 christos }
537 1.1 christos }
538 1.1 christos
539 1.1 christos if ( $opt_u ) {
540 1.1 christos my %temp = &getdocced('doc/crypto');
541 1.1 christos foreach ( keys %temp ) {
542 1.1 christos $docced{$_} = $temp{$_};
543 1.1 christos }
544 1.1 christos &printem('crypto', 'util/libcrypto.num');
545 1.1 christos &printem('ssl', 'util/libssl.num');
546 1.1 christos &checkmacros();
547 1.1 christos }
548 1.1 christos
549 1.1 christos exit;
550