check-deprecated.pl 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  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. #
  27. # Check that the deprecated statuses of functions and enum values in header
  28. # files, man pages and symbols-in-versions are in sync.
  29. use strict;
  30. use warnings;
  31. use File::Basename;
  32. my $root=$ARGV[0] || ".";
  33. my $incdir = "$root/include/curl";
  34. my $docdir = "$root/docs";
  35. my $libdocdir = "$docdir/libcurl";
  36. my $errcode = 0;
  37. # Symbol-indexed hashes.
  38. # Values are:
  39. # X Not deprecated
  40. # ? Deprecated in unknown version
  41. # x.yy.z Deprecated in version x.yy.z
  42. my %syminver; # Symbols-in-versions deprecations.
  43. my %hdr; # Public header files deprecations.
  44. my %funcman; # Function man pages deprecations.
  45. my %optman; # Option man pages deprecations.
  46. # Scan header file for public function and enum values. Flag them with
  47. # the version they are deprecated in, if some.
  48. sub scan_header {
  49. my ($f)=@_;
  50. my $line = "";
  51. my $incomment = 0;
  52. my $inenum = 0;
  53. open H, "<$f";
  54. while(<H>) {
  55. s/^\s*(.*?)\s*$/$1/; # Trim.
  56. # Remove multi-line comment trail.
  57. if($incomment) {
  58. if($_ !~ /.*?\*\/\s*(.*)$/) {
  59. next;
  60. }
  61. $_ = $1;
  62. $incomment = 0;
  63. }
  64. if($line ne "") {
  65. # Unfold line.
  66. $_ = "$line $1";
  67. $line = "";
  68. }
  69. # Remove comments.
  70. while($_ =~ /^(.*?)\/\*.*?\*\/(.*)$/) {
  71. $_ = "$1 $2";
  72. }
  73. if($_ =~ /^(.*)\/\*/) {
  74. $_ = "$1 ";
  75. $incomment = 1;
  76. }
  77. s/^\s*(.*?)\s*$/$1/; # Trim again.
  78. # Ignore preprocessor directives and blank lines.
  79. if($_ =~ /^(?:#|$)/) {
  80. next;
  81. }
  82. # Handle lines that may be continued as if they were folded.
  83. if($_ !~ /[;,{}]$/) {
  84. # Folded line.
  85. $line = $_;
  86. next;
  87. }
  88. if($_ =~ /CURLOPTDEPRECATED\(/) {
  89. # Handle deprecated CURLOPT_* option.
  90. if($_ !~ /CURLOPTDEPRECATED\(\s*(\S+)\s*,(?:.*?,){2}\s*(.*?)\s*,.*"\)/) {
  91. # Folded line.
  92. $line = $_;
  93. next;
  94. }
  95. $hdr{$1} = $2;
  96. }
  97. elsif($_ =~ /CURLOPT\(/) {
  98. # Handle non-deprecated CURLOPT_* option.
  99. if($_ !~ /CURLOPT\(\s*(\S+)\s*(?:,.*?){2}\)/) {
  100. # Folded line.
  101. $line = $_;
  102. next;
  103. }
  104. $hdr{$1} = "X";
  105. }
  106. else {
  107. my $version = "X";
  108. # Get other kind of deprecation from this line.
  109. if($_ =~ /CURL_DEPRECATED\(/) {
  110. if($_ !~ /^(.*)CURL_DEPRECATED\(\s*(\S+?)\s*,.*?"\)(.*)$/) {
  111. # Folded line.
  112. $line = $_;
  113. next;
  114. }
  115. $version = $2;
  116. $_ = "$1 $3";
  117. }
  118. if($_ =~ /^CURL_EXTERN\s+.*\s+(\S+?)\s*\(/) {
  119. # Flag public function.
  120. $hdr{$1} = $version;
  121. }
  122. elsif($inenum && $_ =~ /(\w+)\s*[,=}]/) {
  123. # Flag enum value.
  124. $hdr{$1} = $version;
  125. }
  126. }
  127. # Remember if we are in an enum definition.
  128. $inenum |= ($_ =~ /\benum\b/);
  129. if($_ =~ /}/) {
  130. $inenum = 0;
  131. }
  132. }
  133. close H;
  134. }
  135. # Scan function man page for options.
  136. # Each option has to be declared as ".IP <option>" where <option> starts with
  137. # the prefix. Flag each option with its deprecation version, if some.
  138. sub scan_man_for_opts {
  139. my ($f, $prefix)=@_;
  140. my $opt = "";
  141. my $line = "";
  142. open M, "<$f";
  143. while(<M>) {
  144. if($_ =~ /^\./) {
  145. # roff directive found: end current option paragraph.
  146. my $o = $opt;
  147. $opt = "";
  148. if($_ =~ /^\.IP\s+((?:$prefix)_\w+)/) {
  149. # A new option has been found.
  150. $opt = $1;
  151. }
  152. $_ = $line; # Get full paragraph.
  153. $line = "";
  154. s/\\f.//g; # Remove font formatting.
  155. s/\s+/ /g; # One line with single space only.
  156. if($o) {
  157. $funcman{$o} = "X";
  158. # Check if paragraph is mentioning deprecation.
  159. while($_ =~ /(?:deprecated|obsoleted?)\b\s*(?:in\b|since\b)?\s*(?:version\b|curl\b|libcurl\b)?\s*(\d[0-9.]*\d)?\b\s*(.*)$/i) {
  160. $funcman{$o} = $1 || "?";
  161. $_ = $2;
  162. }
  163. }
  164. }
  165. else {
  166. # Text line: accumulate.
  167. $line .= $_;
  168. }
  169. }
  170. close M;
  171. }
  172. # Scan man page for deprecation in DESCRIPTION and/or AVAILABILITY sections.
  173. sub scan_man_page {
  174. my ($path, $sym, $table)=@_;
  175. my $version = "X";
  176. my $fh;
  177. if(open $fh, "<$path") {
  178. my $section = "";
  179. my $line = "";
  180. while(<$fh>) {
  181. if($_ =~ /\.so\s+man3\/(.*\.3\b)/) {
  182. # Handle man page inclusion.
  183. scan_man_page(dirname($path) . "/$1", $sym, $table);
  184. $version = exists($$table{$sym})? $$table{$sym}: $version;
  185. }
  186. elsif($_ =~ /^\./) {
  187. # Line is a roff directive.
  188. if($_ =~ /^\.SH\b\s*(\w*)/) {
  189. # Section starts. End previous one.
  190. my $sh = $section;
  191. $section = $1;
  192. $_ = $line; # Previous section text.
  193. $line = "";
  194. s/\\f.//g;
  195. s/\s+/ /g;
  196. s/\\f.//g; # Remove font formatting.
  197. s/\s+/ /g; # One line with single space only.
  198. if($sh =~ /DESCRIPTION|AVAILABILITY/) {
  199. while($_ =~ /(?:deprecated|obsoleted?)\b\s*(?:in\b|since\b)?\s*(?:version\b|curl\b|libcurl\b)?\s*(\d[0-9.]*\d)?\b\s*(.*)$/i) {
  200. # Flag deprecation status.
  201. if($version ne "X" && $version ne "?") {
  202. if($1 && $1 ne $version) {
  203. print "error: $sym man page lists unmatching deprecation versions $version and $1\n";
  204. $errcode++;
  205. }
  206. }
  207. else {
  208. $version = $1 || "?";
  209. }
  210. $_ = $2;
  211. }
  212. }
  213. }
  214. }
  215. else {
  216. # Text line: accumulate.
  217. $line .= $_;
  218. }
  219. }
  220. close $fh;
  221. $$table{$sym} = $version;
  222. }
  223. }
  224. # Read symbols-in-versions.
  225. open(F, "<$libdocdir/symbols-in-versions") ||
  226. die "$libdocdir/symbols-in-versions";
  227. while(<F>) {
  228. if($_ =~ /^((?:CURL|LIBCURL)\S+)\s+\S+\s*(\S*)\s*(\S*)$/) {
  229. if($3 eq "") {
  230. $syminver{$1} = "X";
  231. if($2 ne "" && $2 ne ".") {
  232. $syminver{$1} = $2;
  233. }
  234. }
  235. }
  236. }
  237. close(F);
  238. # Get header file names,
  239. opendir(my $dh, $incdir) || die "Can't opendir $incdir";
  240. my @hfiles = grep { /\.h$/ } readdir($dh);
  241. closedir $dh;
  242. # Get functions and enum symbols from header files.
  243. for(@hfiles) {
  244. scan_header("$incdir/$_");
  245. }
  246. # Get function statuses from man pages.
  247. foreach my $sym (keys %hdr) {
  248. if($sym =~/^(?:curl|curlx)_\w/) {
  249. scan_man_page("$libdocdir/$sym.3", $sym, \%funcman);
  250. }
  251. }
  252. # Get options from function man pages.
  253. scan_man_for_opts("$libdocdir/curl_easy_setopt.3", "CURLOPT");
  254. scan_man_for_opts("$libdocdir/curl_easy_getinfo.3", "CURLINFO");
  255. # Get deprecation status from option man pages.
  256. foreach my $sym (keys %syminver) {
  257. if($sym =~ /^(?:CURLOPT|CURLINFO)_\w+$/) {
  258. scan_man_page("$libdocdir/opts/$sym.3", $sym, \%optman);
  259. }
  260. }
  261. # Print results.
  262. my %keys = (%syminver, %funcman, %optman, %hdr);
  263. my $leader = <<HEADER
  264. Legend:
  265. <empty> Not listed
  266. X Not deprecated
  267. ? Deprecated in unknown version
  268. x.yy.z Deprecated in version x.yy.z
  269. Symbol symbols-in func man opt man .h
  270. -versions
  271. HEADER
  272. ;
  273. foreach my $sym (sort {$a cmp $b} keys %keys) {
  274. if($sym =~ /^(?:CURLOPT|CURLINFO|curl|curlx)_\w/) {
  275. my $s = exists($syminver{$sym})? $syminver{$sym}: " ";
  276. my $f = exists($funcman{$sym})? $funcman{$sym}: " ";
  277. my $o = exists($optman{$sym})? $optman{$sym}: " ";
  278. my $h = exists($hdr{$sym})? $hdr{$sym}: " ";
  279. my $r = " ";
  280. # There are deprecated symbols in symbols-in-versions that are aliases
  281. # and thus not listed anywhere else. Ignore them.
  282. "$f$o$h" =~ /[X ]{3}/ && next;
  283. # Check for inconsistencies between deprecations from the different sources.
  284. foreach my $k ($s, $f, $o, $h) {
  285. $r = $r eq " "? $k: $r;
  286. if($k ne " " && $r ne $k) {
  287. if($r eq "?") {
  288. $r = $k ne "X"? $k: "!";
  289. }
  290. elsif($r eq "X" || $k ne "?") {
  291. $r = "!";
  292. }
  293. }
  294. }
  295. if($r eq "!") {
  296. print $leader;
  297. $leader = "";
  298. printf("%-38s %-11s %-9s %-9s %s\n", $sym, $s, $f, $o, $h);
  299. $errcode++;
  300. }
  301. }
  302. }
  303. exit $errcode;