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