test1173.pl 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. #!/usr/bin/env perl
  2. #***************************************************************************
  3. # _ _ ____ _
  4. # Project ___| | | | _ \| |
  5. # / __| | | | |_) | |
  6. # | (__| |_| | _ <| |___
  7. # \___|\___/|_| \_\_____|
  8. #
  9. # Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
  10. #
  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.
  14. #
  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.
  18. #
  19. # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  20. # KIND, either express or implied.
  21. #
  22. # SPDX-License-Identifier: curl
  23. #
  24. ###########################################################################
  25. #
  26. # Scan man page(s) and detect some simple and yet common formatting mistakes.
  27. #
  28. # Output all deviances to stderr.
  29. use strict;
  30. use warnings;
  31. use File::Basename;
  32. # get the file name first
  33. my $symbolsinversions=shift @ARGV;
  34. # we may get the dir roots pointed out
  35. my @manpages=@ARGV;
  36. my $errors = 0;
  37. my %docsdirs;
  38. my %optblessed;
  39. my %funcblessed;
  40. my @optorder = (
  41. 'NAME',
  42. 'SYNOPSIS',
  43. 'DESCRIPTION',
  44. #'DEFAULT', # CURLINFO_ has no default
  45. 'PROTOCOLS',
  46. 'EXAMPLE',
  47. 'AVAILABILITY',
  48. 'RETURN VALUE',
  49. 'SEE ALSO'
  50. );
  51. my @funcorder = (
  52. 'NAME',
  53. 'SYNOPSIS',
  54. 'DESCRIPTION',
  55. 'EXAMPLE',
  56. 'AVAILABILITY',
  57. 'RETURN VALUE',
  58. 'SEE ALSO'
  59. );
  60. my %shline; # section => line number
  61. my %symbol;
  62. # some CURLINFO_ symbols are not actual options for curl_easy_getinfo,
  63. # mark them as "deprecated" to hide them from link-warnings
  64. my %deprecated = (
  65. CURLINFO_TEXT => 1,
  66. CURLINFO_HEADER_IN => 1,
  67. CURLINFO_HEADER_OUT => 1,
  68. CURLINFO_DATA_IN => 1,
  69. CURLINFO_DATA_OUT => 1,
  70. CURLINFO_SSL_DATA_IN => 1,
  71. CURLINFO_SSL_DATA_OUT => 1,
  72. CURLOPT_EGDSOCKET => 1,
  73. CURLOPT_RANDOM_FILE => 1,
  74. );
  75. sub allsymbols {
  76. open(my $f, "<", "$symbolsinversions") ||
  77. die "$symbolsinversions: $|";
  78. while(<$f>) {
  79. if($_ =~ /^([^ ]*) +(.*)/) {
  80. my ($name, $info) = ($1, $2);
  81. $symbol{$name}=$name;
  82. if($info =~ /([0-9.]+) +([0-9.]+)/) {
  83. $deprecated{$name}=$info;
  84. }
  85. }
  86. }
  87. close($f);
  88. }
  89. my %ref = (
  90. 'curl.1' => 1
  91. );
  92. sub checkref {
  93. my ($f, $sec, $file, $line)=@_;
  94. my $present = 0;
  95. #print STDERR "check $f.$sec\n";
  96. if($ref{"$f.$sec"}) {
  97. # present
  98. return;
  99. }
  100. foreach my $d (keys %docsdirs) {
  101. if( -f "$d/$f.$sec") {
  102. $present = 1;
  103. $ref{"$f.$sec"}=1;
  104. last;
  105. }
  106. }
  107. if(!$present) {
  108. print STDERR "$file:$line broken reference to $f($sec)\n";
  109. $errors++;
  110. }
  111. }
  112. sub scanmanpage {
  113. my ($file) = @_;
  114. my $reqex = 0;
  115. my $inseealso = 0;
  116. my $inex = 0;
  117. my $insynop = 0;
  118. my $exsize = 0;
  119. my $synopsize = 0;
  120. my $shc = 0;
  121. my $optpage = 0; # option or function
  122. my @sh;
  123. my $SH="";
  124. my @separators;
  125. my @sepline;
  126. open(my $m, "<", "$file") ||
  127. die "test1173.pl could not open $file";
  128. if($file =~ /[\/\\](CURL|curl_)([^\/\\]*).3/) {
  129. # This is a man page for libcurl. It requires an example unless it's
  130. # considered deprecated.
  131. $reqex = 1 unless defined $deprecated{'CURL'.$2};
  132. if($1 eq "CURL") {
  133. $optpage = 1;
  134. }
  135. }
  136. my $line = 1;
  137. while(<$m>) {
  138. chomp;
  139. if($_ =~ /^.so /) {
  140. # this man page is just a referral
  141. close($m);
  142. return;
  143. }
  144. if(($_ =~ /^\.SH SYNOPSIS/i) && ($reqex)) {
  145. # this is for libcurl man page SYNOPSIS checks
  146. $insynop = 1;
  147. $inex = 0;
  148. }
  149. elsif($_ =~ /^\.SH EXAMPLE/i) {
  150. $insynop = 0;
  151. $inex = 1;
  152. }
  153. elsif($_ =~ /^\.SH \"SEE ALSO\"/i) {
  154. $inseealso = 1;
  155. }
  156. elsif($_ =~ /^\.SH/i) {
  157. $insynop = 0;
  158. $inex = 0;
  159. }
  160. elsif($inseealso) {
  161. if($_ =~ /^\.BR (.*)/i) {
  162. my $f = $1;
  163. if($f =~ /^(lib|)curl/i) {
  164. $f =~ s/[\n\r]//g;
  165. if($f =~ s/([a-z_0-9-]*) \(([13])\)([, ]*)//i) {
  166. push @separators, $3;
  167. push @sepline, $line;
  168. checkref($1, $2, $file, $line);
  169. }
  170. if($f !~ /^ *$/) {
  171. print STDERR "$file:$line bad SEE ALSO format\n";
  172. $errors++;
  173. }
  174. }
  175. else {
  176. if($f =~ /.*(, *)\z/) {
  177. push @separators, $1;
  178. push @sepline, $line;
  179. }
  180. else {
  181. push @separators, " ";
  182. push @sepline, $line;
  183. }
  184. }
  185. }
  186. }
  187. elsif($inex) {
  188. $exsize++;
  189. if($_ =~ /[^\\]\\n/) {
  190. print STDERR "$file:$line '\\n' need to be '\\\\n'!\n";
  191. }
  192. }
  193. elsif($insynop) {
  194. $synopsize++;
  195. if(($synopsize == 1) && ($_ !~ /\.nf/)) {
  196. print STDERR "$file:$line:1:ERROR: be .nf for proper formatting\n";
  197. }
  198. }
  199. if($_ =~ /^\.SH ([^\r\n]*)/i) {
  200. my $n = $1;
  201. # remove enclosing quotes
  202. $n =~ s/\"(.*)\"\z/$1/;
  203. push @sh, $n;
  204. $shline{$n} = $line;
  205. $SH = $n;
  206. }
  207. if($_ =~ /^\'/) {
  208. print STDERR "$file:$line line starts with single quote!\n";
  209. $errors++;
  210. }
  211. if($_ =~ /\\f([BI])(.*)/) {
  212. my ($format, $rest) = ($1, $2);
  213. if($rest !~ /\\fP/) {
  214. print STDERR "$file:$line missing \\f${format} terminator!\n";
  215. $errors++;
  216. }
  217. }
  218. my $c = $_;
  219. while($c =~ s/\\f([BI])((lib|)curl[a-z_0-9-]*)\(([13])\)//i) {
  220. checkref($2, $4, $file, $line);
  221. }
  222. 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-]+)(.)/) &&
  223. ($4 ne "(")) {
  224. print STDERR "$file:$line curl ref to $2 without section\n";
  225. $errors++;
  226. }
  227. if($_ =~ /(.*)\\f([^BIP])/) {
  228. my ($pre, $format) = ($1, $2);
  229. if($pre !~ /\\\z/) {
  230. # only if there wasn't another backslash before the \f
  231. print STDERR "$file:$line suspicious \\f format!\n";
  232. $errors++;
  233. }
  234. }
  235. if(($SH =~ /^(DESCRIPTION|RETURN VALUE|AVAILABILITY)/i) &&
  236. ($_ =~ /(.*)((curl_multi|curl_easy|curl_url|curl_global|curl_url|curl_share)[a-zA-Z_0-9-]+)/) &&
  237. ($1 !~ /\\fI$/)) {
  238. print STDERR "$file:$line unrefed curl call: $2\n";
  239. $errors++;
  240. }
  241. if($optpage && $SH && ($SH !~ /^(SYNOPSIS|EXAMPLE|NAME|SEE ALSO)/i) &&
  242. ($_ =~ /(.*)(CURL(OPT_|MOPT_|INFO_|SHOPT_)[A-Z0-9_]*)/)) {
  243. # an option with its own man page, check that it is tagged
  244. # for linking
  245. my ($pref, $symbol) = ($1, $2);
  246. if($deprecated{$symbol}) {
  247. # let it be
  248. }
  249. elsif($pref !~ /\\fI\z/) {
  250. print STDERR "$file:$line option $symbol missing \\fI tagging\n";
  251. $errors++;
  252. }
  253. }
  254. if($_ =~ /[ \t]+$/) {
  255. print STDERR "$file:$line trailing whitespace\n";
  256. $errors++;
  257. }
  258. $line++;
  259. }
  260. close($m);
  261. if(@separators) {
  262. # all except the last one need comma
  263. for(0 .. $#separators - 1) {
  264. my $l = $_;
  265. my $sep = $separators[$l];
  266. if($sep ne ",") {
  267. printf STDERR "$file:%d: bad not-last SEE ALSO separator: '%s'\n",
  268. $sepline[$l], $sep;
  269. $errors++;
  270. }
  271. }
  272. # the last one should not do comma
  273. my $sep = $separators[$#separators];
  274. if($sep eq ",") {
  275. printf STDERR "$file:%d: superfluous comma separator\n",
  276. $sepline[$#separators];
  277. $errors++;
  278. }
  279. }
  280. if($reqex) {
  281. # only for libcurl options man-pages
  282. my $shcount = scalar(@sh); # before @sh gets shifted
  283. if($exsize < 2) {
  284. print STDERR "$file:$line missing EXAMPLE section\n";
  285. $errors++;
  286. }
  287. if($shcount < 3) {
  288. print STDERR "$file:$line too few man page sections!\n";
  289. $errors++;
  290. return;
  291. }
  292. my $got = "start";
  293. my $i = 0;
  294. my $shused = 1;
  295. my @shorig = @sh;
  296. my @order = $optpage ? @optorder : @funcorder;
  297. my $blessed = $optpage ? \%optblessed : \%funcblessed;
  298. while($got) {
  299. my $finesh;
  300. $got = shift(@sh);
  301. if($got) {
  302. if($$blessed{$got}) {
  303. $i = $$blessed{$got};
  304. $finesh = $got; # a mandatory one
  305. }
  306. }
  307. if($i && defined($finesh)) {
  308. # mandatory section
  309. if($i != $shused) {
  310. printf STDERR "$file:%u Got %s, when %s was expected\n",
  311. $shline{$finesh},
  312. $finesh,
  313. $order[$shused-1];
  314. $errors++;
  315. return;
  316. }
  317. $shused++;
  318. if($i == scalar(@order)) {
  319. # last mandatory one, exit
  320. last;
  321. }
  322. }
  323. }
  324. if($i != scalar(@order)) {
  325. printf STDERR "$file:$line missing mandatory section: %s\n",
  326. $order[$i];
  327. printf STDERR "$file:$line section found at index %u: '%s'\n",
  328. $i, $shorig[$i];
  329. printf STDERR " Found %u used sections\n", $shcount;
  330. $errors++;
  331. }
  332. }
  333. }
  334. allsymbols();
  335. if(!$symbol{'CURLALTSVC_H1'}) {
  336. print STDERR "didn't get the symbols-in-version!\n";
  337. exit;
  338. }
  339. my $ind = 1;
  340. for my $s (@optorder) {
  341. $optblessed{$s} = $ind++
  342. }
  343. $ind = 1;
  344. for my $s (@funcorder) {
  345. $funcblessed{$s} = $ind++
  346. }
  347. for my $m (@manpages) {
  348. $docsdirs{dirname($m)}++;
  349. }
  350. for my $m (@manpages) {
  351. scanmanpage($m);
  352. }
  353. print STDERR "ok\n" if(!$errors);
  354. exit $errors;