1#!/usr/bin/env perl 2#*************************************************************************** 3# _ _ ____ _ 4# Project ___| | | | _ \| | 5# / __| | | | |_) | | 6# | (__| |_| | _ <| |___ 7# \___|\___/|_| \_\_____| 8# 9# Copyright (C) 2016 - 2021, 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########################################################################### 23# 24# Scan symbols-in-version (which is verified to be correct by test 1119), then 25# verify that each option mention in there that should have its own man page 26# actually does. 27# 28# In addition, make sure that every current option to curl_easy_setopt, 29# curl_easy_getinfo and curl_multi_setopt are also mentioned in their 30# corresponding main (index) man page. 31# 32# src/tool_getparam.c lists all options curl can parse 33# docs/curl.1 documents all command line options 34# src/tool_help.c outputs all options with curl -h 35# - make sure they're all in sync 36# 37# Output all deviances to stderr. 38 39use strict; 40use warnings; 41 42# we may get the dir roots pointed out 43my $root=$ARGV[0] || "."; 44my $buildroot=$ARGV[1] || "."; 45my $syms = "$root/docs/libcurl/symbols-in-versions"; 46my $curlh = "$root/include/curl/curl.h"; 47my $errors=0; 48 49# the prepopulated alias list is the CURLINFO_* defines that are used for the 50# debug function callback and the fact that they use the same prefix as the 51# curl_easy_getinfo options was a mistake. 52my %alias = ( 53 'CURLINFO_DATA_IN' => 'none', 54 'CURLINFO_DATA_OUT' => 'none', 55 'CURLINFO_END' => 'none', 56 'CURLINFO_HEADER_IN' => 'none', 57 'CURLINFO_HEADER_OUT' => 'none', 58 'CURLINFO_LASTONE' => 'none', 59 'CURLINFO_NONE' => 'none', 60 'CURLINFO_SSL_DATA_IN' => 'none', 61 'CURLINFO_SSL_DATA_OUT' => 'none', 62 'CURLINFO_TEXT' => 'none' 63 ); 64 65sub scanmanpage { 66 my ($file, @words) = @_; 67 68 open(M, "<$file"); 69 my @m; 70 while(<M>) { 71 if($_ =~ /^\.IP (.*)/) { 72 my $w = $1; 73 # "unquote" minuses 74 $w =~ s/\\-/-/g; 75 push @m, $w; 76 } 77 } 78 close(M); 79 80 foreach my $m (@words) { 81 my @g = grep(/$m/, @m); 82 if(!$g[0]) { 83 print STDERR "Missing mention of $m in $file\n"; 84 $errors++; 85 } 86 } 87} 88 89# check for define alises 90open(R, "<$curlh") || 91 die "no curl.h"; 92while(<R>) { 93 if(/^\#define (CURL(OPT|INFO|MOPT)_\w+) (.*)/) { 94 $alias{$1}=$3; 95 } 96} 97close(R); 98 99my @curlopt; 100my @curlinfo; 101my @curlmopt; 102open(R, "<$syms") || 103 die "no input file"; 104while(<R>) { 105 chomp; 106 my $l= $_; 107 if($l =~ /(CURL(OPT|INFO|MOPT)_\w+) *([0-9.]*) *([0-9.-]*) *([0-9.]*)/) { 108 my ($opt, $type, $add, $dep, $rem) = ($1, $2, $3, $4, $5); 109 110 if($alias{$opt}) { 111 #print "$opt => $alias{$opt}\n"; 112 } 113 elsif($rem) { 114 # $opt was removed in $rem 115 # so don't check for that 116 } 117 else { 118 if($type eq "OPT") { 119 push @curlopt, $opt, 120 } 121 elsif($type eq "INFO") { 122 push @curlinfo, $opt, 123 } 124 elsif($type eq "MOPT") { 125 push @curlmopt, $opt, 126 } 127 if(! -f "$root/docs/libcurl/opts/$opt.3") { 128 print STDERR "Missing $opt.3\n"; 129 $errors++; 130 } 131 } 132 } 133} 134close(R); 135 136scanmanpage("$root/docs/libcurl/curl_easy_setopt.3", @curlopt); 137scanmanpage("$root/docs/libcurl/curl_easy_getinfo.3", @curlinfo); 138scanmanpage("$root/docs/libcurl/curl_multi_setopt.3", @curlmopt); 139 140# using this hash array, we can skip specific options 141my %opts = ( 142 # pretend these --no options exists in tool_getparam.c 143 '--no-alpn' => 1, 144 '--no-npn' => 1, 145 '-N, --no-buffer' => 1, 146 '--no-sessionid' => 1, 147 '--no-keepalive' => 1, 148 '--no-progress-meter' => 1, 149 150 # pretend these options without -no exist in curl.1 and tool_help.c 151 '--alpn' => 6, 152 '--npn' => 6, 153 '--eprt' => 6, 154 '--epsv' => 6, 155 '--keepalive' => 6, 156 '-N, --buffer' => 6, 157 '--sessionid' => 6, 158 '--progress-meter' => 6, 159 160 # deprecated options do not need to be in tool_help.c nor curl.1 161 '--krb4' => 6, 162 '--ftp-ssl' => 6, 163 '--ftp-ssl-reqd' => 6, 164 165 # for tests and debug only, can remain hidden 166 '--test-event' => 6, 167 '--wdebug' => 6, 168 ); 169 170 171######################################################################### 172# parse the curl code that parses the command line arguments! 173open(R, "<$root/src/tool_getparam.c") || 174 die "no input file"; 175my $list; 176my @getparam; # store all parsed parameters 177 178while(<R>) { 179 chomp; 180 my $l= $_; 181 if(/struct LongShort aliases/) { 182 $list=1; 183 } 184 elsif($list) { 185 if( /^ \{([^,]*), *([^ ]*)/) { 186 my ($s, $l)=($1, $2); 187 my $sh; 188 my $lo; 189 my $title; 190 if($l =~ /\"(.*)\"/) { 191 # long option 192 $lo = $1; 193 $title="--$lo"; 194 } 195 if($s =~ /\"(.)\"/) { 196 # a short option 197 $sh = $1; 198 $title="-$sh, $title"; 199 } 200 push @getparam, $title; 201 $opts{$title} |= 1; 202 } 203 } 204} 205close(R); 206 207######################################################################### 208# parse the curl.1 man page, extract all documented command line options 209# The man page may or may not be rebuilt, so check both possible locations 210open(R, "<$buildroot/docs/curl.1") || open(R, "<$root/docs/curl.1") || 211 die "no input file"; 212my @manpage; # store all parsed parameters 213while(<R>) { 214 chomp; 215 my $l= $_; 216 $l =~ s/\\-/-/g; 217 if($l =~ /^\.IP \"(-[^\"]*)\"/) { 218 my $str = $1; 219 my $combo; 220 if($str =~ /^-(.), --([a-z0-9.-]*)/) { 221 # figure out the -short, --long combo 222 $combo = "-$1, --$2"; 223 } 224 elsif($str =~ /^--([a-z0-9.-]*)/) { 225 # figure out the --long name 226 $combo = "--$1"; 227 } 228 if($combo) { 229 push @manpage, $combo; 230 $opts{$combo} |= 2; 231 } 232 } 233} 234close(R); 235 236 237######################################################################### 238# parse the curl code that outputs the curl -h list 239open(R, "<$root/src/tool_help.c") || 240 die "no input file"; 241my @toolhelp; # store all parsed parameters 242while(<R>) { 243 chomp; 244 my $l= $_; 245 if(/^ \{\" *(.*)/) { 246 my $str=$1; 247 my $combo; 248 if($str =~ /^-(.), --([a-z0-9.-]*)/) { 249 # figure out the -short, --long combo 250 $combo = "-$1, --$2"; 251 } 252 elsif($str =~ /^--([a-z0-9.-]*)/) { 253 # figure out the --long name 254 $combo = "--$1"; 255 } 256 if($combo) { 257 push @toolhelp, $combo; 258 $opts{$combo} |= 4; 259 } 260 261 } 262} 263close(R); 264 265# 266# Now we have three arrays with options to cross-reference. 267 268foreach my $o (keys %opts) { 269 my $where = $opts{$o}; 270 271 if($where != 7) { 272 # this is not in all three places 273 $errors++; 274 my $exists; 275 my $missing; 276 if($where & 1) { 277 $exists=" tool_getparam.c"; 278 } 279 else { 280 $missing=" tool_getparam.c"; 281 } 282 if($where & 2) { 283 $exists.= " curl.1"; 284 } 285 else { 286 $missing.= " curl.1"; 287 } 288 if($where & 4) { 289 $exists .= " tool_help.c"; 290 } 291 else { 292 $missing .= " tool_help.c"; 293 } 294 295 print STDERR "$o is not in$missing (but in$exists)\n"; 296 } 297} 298 299print STDERR "$errors\n"; 300