2 #***************************************************************************
4 # Project ___| | | | _ \| |
6 # | (__| |_| | _ <| |___
7 # \___|\___/|_| \_\_____|
9 # Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
11 # This software is licensed as described in the file COPYING, which
12 # you should have received as part of this distribution. The terms
13 # are also available at https://curl.se/docs/copyright.html.
15 # You may opt to use, copy, modify, merge, publish, distribute and/or sell
16 # copies of the Software, and permit persons to whom the Software is
17 # furnished to do so, under the terms of the COPYING file.
19 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 # KIND, either express or implied.
22 # SPDX-License-Identifier: curl
24 ###########################################################################
26 # Scan man page(s) and detect some simple and yet common formatting mistakes.
28 # Output all deviances to stderr.
34 # get the file name first
35 my $symbolsinversions=shift @ARGV;
37 # we may get the dir roots pointed out
48 #'DEFAULT', # CURLINFO_ has no default
64 my %shline; # section => line number
68 # some CURLINFO_ symbols are not actual options for curl_easy_getinfo,
69 # mark them as "deprecated" to hide them from link-warnings
72 CURLINFO_HEADER_IN => 1,
73 CURLINFO_HEADER_OUT => 1,
74 CURLINFO_DATA_IN => 1,
75 CURLINFO_DATA_OUT => 1,
76 CURLINFO_SSL_DATA_IN => 1,
77 CURLINFO_SSL_DATA_OUT => 1,
80 open(my $f, "<", "$symbolsinversions") ||
81 die "$symbolsinversions: $|";
83 if($_ =~ /^([^ ]*) +(.*)/) {
84 my ($name, $info) = ($1, $2);
87 if($info =~ /([0-9.]+) +([0-9.]+)/) {
88 $deprecated{$name}=$info;
100 my ($f, $sec, $file, $line)=@_;
102 #print STDERR "check $f.$sec\n";
103 if($ref{"$f.$sec"}) {
107 foreach my $d (keys %docsdirs) {
108 if( -f "$d/$f.$sec") {
115 print STDERR "$file:$line broken reference to $f($sec)\n";
129 my $optpage = 0; # option or function
135 open(my $m, "<", "$file") || die "no such file: $file";
136 if($file =~ /[\/\\](CURL|curl_)[^\/\\]*.3/) {
137 # This is a man page for libcurl. It requires an example!
147 # this man page is just a referral
151 if(($_ =~ /^\.SH SYNOPSIS/i) && ($reqex)) {
152 # this is for libcurl man page SYNOPSIS checks
156 elsif($_ =~ /^\.SH EXAMPLE/i) {
160 elsif($_ =~ /^\.SH \"SEE ALSO\"/i) {
163 elsif($_ =~ /^\.SH/i) {
168 if($_ =~ /^\.BR (.*)/i) {
170 if($f =~ /^(lib|)curl/i) {
172 if($f =~ s/([a-z_0-9-]*) \(([13])\)([, ]*)//i) {
173 push @separators, $3;
174 push @sepline, $line;
175 checkref($1, $2, $file, $line);
178 print STDERR "$file:$line bad SEE ALSO format\n";
183 if($f =~ /.*(, *)\z/) {
184 push @separators, $1;
185 push @sepline, $line;
188 push @separators, " ";
189 push @sepline, $line;
196 if($_ =~ /[^\\]\\n/) {
197 print STDERR "$file:$line '\\n' need to be '\\\\n'!\n";
202 if(($synopsize == 1) && ($_ !~ /\.nf/)) {
203 print STDERR "$file:$line:1:ERROR: be .nf for proper formatting\n";
206 if($_ =~ /^\.SH ([^\r\n]*)/i) {
208 # remove enclosing quotes
209 $n =~ s/\"(.*)\"\z/$1/;
216 print STDERR "$file:$line line starts with single quote!\n";
219 if($_ =~ /\\f([BI])(.*)/) {
220 my ($format, $rest) = ($1, $2);
221 if($rest !~ /\\fP/) {
222 print STDERR "$file:$line missing \\f${format} terminator!\n";
227 while($c =~ s/\\f([BI])((lib|)curl[a-z_0-9-]*)\(([13])\)//i) {
228 checkref($2, $4, $file, $line);
230 if(($_ =~ /\\f([BI])((libcurl|CURLOPT_|CURLSHOPT_|CURLINFO_|CURLMOPT_|curl_easy_|curl_multi_|curl_url|curl_mime|curl_global|curl_share)[a-zA-Z_0-9-]+)(.)/) &&
232 print STDERR "$file:$line curl ref to $2 without section\n";
235 if($_ =~ /(.*)\\f([^BIP])/) {
236 my ($pre, $format) = ($1, $2);
238 # only if there wasn't another backslash before the \f
239 print STDERR "$file:$line suspicious \\f format!\n";
243 if(($SH =~ /^(DESCRIPTION|RETURN VALUE|AVAILABILITY)/i) &&
244 ($_ =~ /(.*)((curl_multi|curl_easy|curl_url|curl_global|curl_url|curl_share)[a-zA-Z_0-9-]+)/) &&
246 print STDERR "$file:$line unrefed curl call: $2\n";
251 if($optpage && $SH && ($SH !~ /^(SYNOPSIS|EXAMPLE|NAME|SEE ALSO)/i) &&
252 ($_ =~ /(.*)(CURL(OPT_|MOPT_|INFO_|SHOPT_)[A-Z0-9_]*)/)) {
253 # an option with its own man page, check that it is tagged
255 my ($pref, $symbol) = ($1, $2);
256 if($deprecated{$symbol}) {
259 elsif($pref !~ /\\fI\z/) {
260 print STDERR "$file:$line option $symbol missing \\fI tagging\n";
264 if($_ =~ /[ \t]+$/) {
265 print STDERR "$file:$line trailing whitespace\n";
273 # all except the last one need comma
274 for(0 .. $#separators - 1) {
276 my $sep = $separators[$l];
278 printf STDERR "$file:%d: bad not-last SEE ALSO separator: '%s'\n",
283 # the last one should not do comma
284 my $sep = $separators[$#separators];
286 printf STDERR "$file:%d: superfluous comma separator\n",
287 $sepline[$#separators];
293 # only for libcurl options man-pages
295 my $shcount = scalar(@sh); # before @sh gets shifted
297 print STDERR "$file:$line missing EXAMPLE section\n";
302 print STDERR "$file:$line too few man page sections!\n";
311 my @order = $optpage ? @optorder : @funcorder;
312 my $blessed = $optpage ? \%optblessed : \%funcblessed;
318 if($$blessed{$got}) {
319 $i = $$blessed{$got};
320 $finesh = $got; # a mandatory one
323 if($i && defined($finesh)) {
327 printf STDERR "$file:%u Got %s, when %s was expected\n",
335 if($i == scalar(@order)) {
336 # last mandatory one, exit
342 if($i != scalar(@order)) {
343 printf STDERR "$file:$line missing mandatory section: %s\n",
345 printf STDERR "$file:$line section found at index %u: '%s'\n",
347 printf STDERR " Found %u used sections\n", $shcount;
355 if(!$symbol{'CURLALTSVC_H1'}) {
356 print STDERR "didn't get the symbols-in-version!\n";
361 for my $s (@optorder) {
362 $optblessed{$s} = $ind++
365 for my $s (@funcorder) {
366 $funcblessed{$s} = $ind++
369 for my $m (@manpages) {
370 $docsdirs{dirname($m)}++;
373 for my $m (@manpages) {
377 print STDERR "ok\n" if(!$errors);