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