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 die "could not open $file"; 72 my @m; 73 while(<$mh>) { 74 if($_ =~ /^\.IP (.*)/) { 75 my $w = $1; 76 # "unquote" minuses 77 $w =~ s/\\-/-/g; 78 push @m, $w; 79 } 80 } 81 close($mh); 82 83 foreach my $m (@words) { 84 my @g = grep(/$m/, @m); 85 if(!$g[0]) { 86 print STDERR "Missing mention of $m in $file\n"; 87 $errors++; 88 } 89 } 90} 91 92my $r; 93 94# check for define aliases 95open($r, "<", "$curlh") || 96 die "no curl.h"; 97while(<$r>) { 98 if(/^\#define (CURL(OPT|INFO|MOPT)_\w+) (.*)/) { 99 $alias{$1}=$3; 100 } 101} 102close($r); 103 104my @curlopt; 105my @curlinfo; 106my @curlmopt; 107open($r, "<", "$syms") || 108 die "no input file"; 109while(<$r>) { 110 chomp; 111 my $l= $_; 112 if($l =~ /(CURL(OPT|INFO|MOPT)_\w+) *([0-9.]*) *([0-9.-]*) *([0-9.]*)/) { 113 my ($opt, $type, $add, $dep, $rem) = ($1, $2, $3, $4, $5); 114 115 if($alias{$opt}) { 116 #print "$opt => $alias{$opt}\n"; 117 } 118 elsif($rem) { 119 # $opt was removed in $rem 120 # so don't check for that 121 } 122 else { 123 if($type eq "OPT") { 124 push @curlopt, $opt, 125 } 126 elsif($type eq "INFO") { 127 push @curlinfo, $opt, 128 } 129 elsif($type eq "MOPT") { 130 push @curlmopt, $opt, 131 } 132 if(! -f "$buildroot/docs/libcurl/opts/$opt.3") { 133 print STDERR "Missing $opt.3\n"; 134 $errors++; 135 } 136 } 137 } 138} 139close($r); 140 141scanmanpage("$buildroot/docs/libcurl/curl_easy_setopt.3", @curlopt); 142scanmanpage("$buildroot/docs/libcurl/curl_easy_getinfo.3", @curlinfo); 143scanmanpage("$buildroot/docs/libcurl/curl_multi_setopt.3", @curlmopt); 144 145# using this hash array, we can skip specific options 146my %opts = ( 147 # pretend these --no options exists in tool_getparam.c 148 '--no-alpn' => 1, 149 '--no-npn' => 1, 150 '-N, --no-buffer' => 1, 151 '--no-sessionid' => 1, 152 '--no-keepalive' => 1, 153 '--no-progress-meter' => 1, 154 '--no-clobber' => 1, 155 156 # pretend these options without -no exist in curl.1 and tool_listhelp.c 157 '--alpn' => 6, 158 '--npn' => 6, 159 '--eprt' => 6, 160 '--epsv' => 6, 161 '--keepalive' => 6, 162 '-N, --buffer' => 6, 163 '--sessionid' => 6, 164 '--progress-meter' => 6, 165 '--clobber' => 6, 166 167 # deprecated options do not need to be in tool_help.c nor curl.1 168 '--krb4' => 6, 169 '--ftp-ssl' => 6, 170 '--ftp-ssl-reqd' => 6, 171 172 # for tests and debug only, can remain hidden 173 '--test-event' => 6, 174 '--wdebug' => 6, 175 ); 176 177 178######################################################################### 179# parse the curl code that parses the command line arguments! 180open($r, "<", "$root/src/tool_getparam.c") || 181 die "no input file"; 182my $list; 183my @getparam; # store all parsed parameters 184 185my $prevlong = ""; 186my $no = 0; 187while(<$r>) { 188 $no++; 189 chomp; 190 if(/struct LongShort aliases/) { 191 $list=1; 192 } 193 elsif($list) { 194 if( /^ \{(\"[^,]*\").*\'(.)\', (.*)\}/) { 195 my ($l, $s, $rd)=($1, $2, $3); 196 my $sh; 197 my $lo; 198 my $title; 199 if(($l cmp $prevlong) < 0) { 200 print STDERR "tool_getparam.c:$no: '$l' is NOT placed in alpha-order\n"; 201 } 202 if($l =~ /\"(.*)\"/) { 203 # long option 204 $lo = $1; 205 $title="--$lo"; 206 } 207 if($s ne " ") { 208 # a short option 209 $sh = $s; 210 $title="-$sh, $title"; 211 } 212 push @getparam, $title; 213 $opts{$title} |= 1; 214 $prevlong = $l; 215 } 216 } 217} 218close($r); 219 220######################################################################### 221# parse the curl.1 man page, extract all documented command line options 222# The man page may or may not be rebuilt, so check both possible locations 223open($r, "<", "$buildroot/docs/cmdline-opts/curl.1") || open($r, "<", "$root/docs/cmdline-opts/curl.1") || 224 die "failed getting curl.1"; 225my @manpage; # store all parsed parameters 226while(<$r>) { 227 chomp; 228 my $l= $_; 229 $l =~ s/\\-/-/g; 230 if($l =~ /^\.IP \"(-[^\"]*)\"/) { 231 my $str = $1; 232 my $combo; 233 if($str =~ /^-(.), --([a-z0-9.-]*)/) { 234 # figure out the -short, --long combo 235 $combo = "-$1, --$2"; 236 } 237 elsif($str =~ /^--([a-z0-9.-]*)/) { 238 # figure out the --long name 239 $combo = "--$1"; 240 } 241 if($combo) { 242 push @manpage, $combo; 243 $opts{$combo} |= 2; 244 } 245 } 246} 247close($r); 248 249 250######################################################################### 251# parse the curl code that outputs the curl -h list 252open($r, "<", "$root/src/tool_listhelp.c") || 253 die "no input file"; 254my @toolhelp; # store all parsed parameters 255while(<$r>) { 256 chomp; 257 my $l= $_; 258 if(/^ \{\" *(.*)/) { 259 my $str=$1; 260 my $combo; 261 if($str =~ /^-(.), --([a-z0-9.-]*)/) { 262 # figure out the -short, --long combo 263 $combo = "-$1, --$2"; 264 } 265 elsif($str =~ /^--([a-z0-9.-]*)/) { 266 # figure out the --long name 267 $combo = "--$1"; 268 } 269 if($combo) { 270 push @toolhelp, $combo; 271 $opts{$combo} |= 4; 272 } 273 274 } 275} 276close($r); 277 278# 279# Now we have three arrays with options to cross-reference. 280 281foreach my $o (keys %opts) { 282 my $where = $opts{$o}; 283 284 if($where != 7) { 285 # this is not in all three places 286 $errors++; 287 my $exists; 288 my $missing; 289 if($where & 1) { 290 $exists=" tool_getparam.c"; 291 } 292 else { 293 $missing=" tool_getparam.c"; 294 } 295 if($where & 2) { 296 $exists.= " curl.1"; 297 } 298 else { 299 $missing.= " curl.1"; 300 } 301 if($where & 4) { 302 $exists .= " tool_listhelp.c"; 303 } 304 else { 305 $missing .= " tool_listhelp.c"; 306 } 307 308 print STDERR "$o is not in$missing (but in$exists)\n"; 309 } 310} 311 312print STDERR "$errors\n"; 313