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