1#!/usr/bin/perl 2# 3# Copyright (c) 2021 Ingo Schwarze <schwarze@openbsd.org> 4# 5# Permission to use, copy, modify, and distribute this software for any 6# purpose with or without fee is hereby granted, provided that the above 7# copyright notice and this permission notice appear in all copies. 8# 9# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 17use strict; 18use warnings; 19 20my %internal = ( 21 asn1 => [qw( 22 ASN1_ENCODING 23 ASN1_OBJECT_FLAG_CRITICAL ASN1_OBJECT_FLAG_DYNAMIC 24 ASN1_OBJECT_FLAG_DYNAMIC_DATA ASN1_OBJECT_FLAG_DYNAMIC_STRINGS 25 ASN1_STRING_FLAG_BITS_LEFT ASN1_STRING_FLAG_CONT 26 ASN1_STRING_FLAG_MSTRING ASN1_STRING_FLAG_NDEF 27 CHARTYPE_FIRST_ESC_2253 CHARTYPE_LAST_ESC_2253 CHARTYPE_PRINTABLESTRING 28 )], 29 objects => [qw( 30 OBJ_bsearch OBJ_bsearch_ OBJ_bsearch_ex OBJ_bsearch_ex_ 31 )], 32 x509_vfy => [qw( 33 X509_VERIFY_PARAM_ID 34 )] 35); 36 37my %obsolete = ( 38 asn1 => [qw( 39 ASN1_const_CTX ASN1_CTX 40 ASN1_dup ASN1_d2i_bio ASN1_d2i_bio_of ASN1_d2i_fp ASN1_d2i_fp_of 41 ASN1_i2d_bio ASN1_i2d_bio_of ASN1_i2d_bio_of_const 42 ASN1_i2d_fp ASN1_i2d_fp_of ASN1_i2d_fp_of_const 43 ASN1_LONG_UNDEF 44 d2i_NETSCAPE_X509 i2d_NETSCAPE_X509 45 NETSCAPE_X509 NETSCAPE_X509_free NETSCAPE_X509_new 46 ub_title 47 V_ASN1_PRIMATIVE_TAG 48 X509_algor_st 49 )], 50 objects => [qw( 51 _DECLARE_OBJ_BSEARCH_CMP_FN 52 DECLARE_OBJ_BSEARCH_CMP_FN DECLARE_OBJ_BSEARCH_GLOBAL_CMP_FN 53 IMPLEMENT_OBJ_BSEARCH_CMP_FN IMPLEMENT_OBJ_BSEARCH_GLOBAL_CMP_FN 54 )], 55 x509 => [qw( 56 X509_EX_V_INIT X509_EX_V_NETSCAPE_HACK 57 X509_EXT_PACK_STRING X509_EXT_PACK_UNKNOWN 58 )] 59); 60 61my %postponed = ( 62 asn1 => [qw( 63 ASN1_ITEM_EXP ASN1_ITEM_ptr ASN1_ITEM_ref ASN1_ITEM_rptr 64 ASN1_TEMPLATE ASN1_TLC 65 CHECKED_D2I_OF CHECKED_I2D_OF CHECKED_NEW_OF 66 CHECKED_PPTR_OF CHECKED_PTR_OF 67 DECLARE_ASN1_ALLOC_FUNCTIONS DECLARE_ASN1_ALLOC_FUNCTIONS_name 68 DECLARE_ASN1_ENCODE_FUNCTIONS DECLARE_ASN1_ENCODE_FUNCTIONS_const 69 DECLARE_ASN1_FUNCTIONS DECLARE_ASN1_FUNCTIONS_const 70 DECLARE_ASN1_FUNCTIONS_fname DECLARE_ASN1_FUNCTIONS_name 71 DECLARE_ASN1_ITEM 72 DECLARE_ASN1_NDEF_FUNCTION 73 DECLARE_ASN1_PRINT_FUNCTION DECLARE_ASN1_PRINT_FUNCTION_fname 74 DECLARE_ASN1_SET_OF 75 D2I_OF 76 IMPLEMENT_ASN1_SET_OF 77 I2D_OF I2D_OF_const 78 TYPEDEF_D2I_OF TYPEDEF_D2I2D_OF TYPEDEF_I2D_OF 79 )], 80 x509 => [qw( 81 d2i_PBEPARAM d2i_PBE2PARAM d2i_PBKDF2PARAM 82 i2d_PBEPARAM i2d_PBE2PARAM i2d_PBKDF2PARAM 83 NETSCAPE_SPKAC NETSCAPE_SPKI 84 PBEPARAM PBEPARAM_free PBEPARAM_new 85 PBE2PARAM PBE2PARAM_free PBE2PARAM_new 86 PBKDF2PARAM PBKDF2PARAM_free PBKDF2PARAM_new 87 PKCS5_pbe_set PKCS5_pbe_set0_algor 88 PKCS5_pbe2_set PKCS5_pbe2_set_iv 89 PKCS5_pbkdf2_set 90 )] 91); 92 93my $MANW = 'man -M /usr/share/man -w'; 94my $srcdir = '/usr/src/lib/libcrypto/man'; 95my $hfile = '/usr/include/openssl'; 96 97my $in_cplusplus = 0; 98my $in_comment = 0; 99my $in_define = 0; 100my $in_function = 0; 101my $in_struct = 0; 102my $in_typedef_struct = 0; 103my %undoc = (); 104my $verbose = 0; 105 106if (defined $ARGV[0] && $ARGV[0] eq '-v') { 107 $verbose = 1; 108 shift @ARGV; 109} 110$#ARGV == 0 or die "usage: $0 [-v] headername"; 111$hfile .= "/$ARGV[0].h"; 112open my $in_fh, '<', $hfile or die "$hfile: $!"; 113 114$undoc{$_} = 1 foreach @{$internal{$ARGV[0]}}; 115$undoc{$_} = 1 foreach @{$obsolete{$ARGV[0]}}; 116$undoc{$_} = 1 foreach @{$postponed{$ARGV[0]}}; 117 118while (<$in_fh>) { 119try_again: 120 chomp; 121 my $line = $_; 122 123 # C language comments. 124 125 if ($in_comment) { 126 unless (s/.*?\*\///) { 127 print "-- $line\n" if $verbose; 128 next; 129 } 130 $in_comment = 0; 131 } 132 while (/\/\*/) { 133 s/\s*\/\*.*?\*\/// and next; 134 s/\s*\/\*.*// and $in_comment = 1; 135 } 136 137 # End C++ stuff. 138 139 if ($in_cplusplus) { 140 /^#endif$/ and $in_cplusplus = 0; 141 print "-- $line\n" if $verbose; 142 next; 143 } 144 145 # End declarations of structs. 146 147 if ($in_struct) { 148 if (/^\s*union\s+{$/) { 149 print "-s $line\n" if $verbose; 150 $in_struct++; 151 next; 152 } 153 unless (s/^\s*\}//) { 154 print "-s $line\n" if $verbose; 155 next; 156 } 157 if (--$in_struct && /^\s+\w+;$/) { 158 print "-s $line\n" if $verbose; 159 next; 160 } 161 unless ($in_typedef_struct) { 162 /^\s*;$/ or die "at end of struct: $_"; 163 print "-s $line\n" if $verbose; 164 next; 165 } 166 $in_typedef_struct = 0; 167 my ($id) = /^\s*(\w+);$/ 168 or die "at end of typedef struct: $_"; 169 unless (system "$MANW -k Vt=$id > /dev/null 2>&1") { 170 print "Vt $line\n" if $verbose; 171 next; 172 } 173 if ($undoc{$id}) { 174 print "V- $line\n" if $verbose; 175 delete $undoc{$id}; 176 next; 177 } 178 if ($verbose) { 179 print "XX $line\n"; 180 } else { 181 warn "not found: typedef struct $id"; 182 } 183 next; 184 } 185 186 # End macro definitions. 187 188 if ($in_define) { 189 /\\$/ or $in_define = 0; 190 print "-d $line\n" if $verbose; 191 next; 192 } 193 194 # End function declarations. 195 196 if ($in_function) { 197 /^\s/ or die "function arguments not indented: $_"; 198 /\);$/ and $in_function = 0; 199 print "-f $line\n" if $verbose; 200 next; 201 } 202 203 # Begin C++ stuff. 204 205 if (/^#ifdef\s+__cplusplus$/) { 206 $in_cplusplus = 1; 207 print "-- $line\n" if $verbose; 208 next; 209 } 210 211 # Uninteresting lines. 212 213 if (/^\s*$/ || 214 /^DECLARE_STACK_OF\(\w+\)$/ || 215 /^TYPEDEF_D2I2D_OF\(\w+\);$/ || 216 /^#define HEADER_\w+_H$/ || 217 /^#define USE_OBJ_MAC$/ || 218 /^#endif$/ || 219 /^#else$/ || 220 /^extern\s+const\s+ASN1_ITEM\s+\w+_it;$/ || 221 /^#include\s/ || 222 /^#ifn?def\s/ || 223 /^#if defined/) { 224 print "-- $line\n" if $verbose; 225 next; 226 } 227 228 # Begin declarations of structs. 229 230 if (/^(typedef )?(?:struct|enum)(?: \w+)? \{$/) { 231 $in_struct = 1; 232 $1 and $in_typedef_struct = 1; 233 print "-s $line\n" if $verbose; 234 next; 235 } 236 237 # Handle macros. 238 239 if (my ($id) = /^#\s*define\s+(\w+)\s+\S/) { 240 /\\$/ and $in_define = 1; 241 unless (system "$MANW -k Dv=$id > /dev/null 2>&1") { 242 print "Dv $line\n" if $verbose; 243 next; 244 } 245 unless (system "$MANW $id > /dev/null 2>&1") { 246 print "Fn $line\n" if $verbose; 247 next; 248 } 249 unless (system qw/grep -qR/, '^\.\\\\" .*\<' . $id . '\>', 250 "$srcdir/") { 251 print "D- $line\n" if $verbose; 252 next; 253 } 254 if ($id =~ /^ASN1_PCTX_FLAGS_\w+$/) { 255 print "D- $line\n" if $verbose; 256 next; 257 } 258 if ($id =~ /^(?:ASN1|X509(?:V3)?)_[FR]_\w+$/) { 259 print "D- $line\n" if $verbose; 260 next; 261 } 262 if ($id =~ /^X509_V_ERR_\w+$/) { 263 print "D- $line\n" if $verbose; 264 next; 265 } 266 if ($id =~ /^(?:SN|LN|NID|OBJ)_\w+$/) { 267 print "D- $line\n" if $verbose; 268 next; 269 } 270 if ($undoc{$id}) { 271 print "D- $line\n" if $verbose; 272 delete $undoc{$id}; 273 next; 274 } 275 if ($verbose) { 276 print "XX $line\n"; 277 } else { 278 warn "not found: #define $id"; 279 } 280 next; 281 } 282 if (my ($id) = /^#\s*define\s+(\w+)\(/) { 283 /\\$/ and $in_define = 1; 284 unless (system "$MANW $id > /dev/null 2>&1") { 285 print "Fn $line\n" if $verbose; 286 next; 287 } 288 unless (system qw/grep -qR/, '^\.\\\\" .*\<' . $id . '\>', 289 "$srcdir/") { 290 print "F- $line\n" if $verbose; 291 next; 292 } 293 if ($undoc{$id}) { 294 print "F- $line\n" if $verbose; 295 delete $undoc{$id}; 296 next; 297 } 298 if ($verbose) { 299 print "XX $line\n"; 300 } else { 301 warn "not found: #define $id()"; 302 } 303 next; 304 } 305 306 # Handle global variables. 307 308 if (my ($id) = /^extern\s+int\s+(\w+);$/) { 309 unless (system "$MANW -k Va=$id > /dev/null 2>&1") { 310 print "Va $line\n" if $verbose; 311 next; 312 } 313 if ($verbose) { 314 print "XX $line\n"; 315 } else { 316 warn "not found: extern int $id"; 317 } 318 next; 319 } 320 321 # Handle variable type declarations. 322 323 if (my ($id) = /^struct\s+(\w+);$/) { 324 unless (system "$MANW -k Vt=$id > /dev/null 2>&1") { 325 print "Vt $line\n" if $verbose; 326 next; 327 } 328 if ($undoc{$id}) { 329 print "V- $line\n" if $verbose; 330 delete $undoc{$id}; 331 next; 332 } 333 if ($verbose) { 334 print "XX $line\n"; 335 } else { 336 warn "not found: struct $id"; 337 } 338 next; 339 } 340 341 if (my ($id) = /^typedef\s+(?:const\s+)?(?:struct\s+)?\S+\s+(\w+);$/) { 342 unless (system "$MANW -k Vt=$id > /dev/null 2>&1") { 343 print "Vt $line\n" if $verbose; 344 next; 345 } 346 if ($undoc{$id}) { 347 print "V- $line\n" if $verbose; 348 delete $undoc{$id}; 349 next; 350 } 351 if ($verbose) { 352 print "XX $line\n"; 353 } else { 354 warn "not found: typedef $id"; 355 } 356 next; 357 } 358 359 if (my ($id) =/^typedef\s+\w+(?:\s+\*)?\s+\(\*(\w+)\)\(/) { 360 /\);$/ or $in_function = 1; 361 unless (system "$MANW $id > /dev/null 2>&1") { 362 print "Fn $line\n" if $verbose; 363 next; 364 } 365 if ($verbose) { 366 print "XX $line\n"; 367 } else { 368 warn "not found: function type (*$id)()"; 369 } 370 next; 371 } 372 373 # Handle function declarations. 374 375 if (/^\w+(?:\(\w+\))?(?:\s+\w+)*\s+(?:\(?\*\s*)?(\w+)\(/) { 376 my $id = $1; 377 /\);$/ or $in_function = 1; 378 unless (system "$MANW $id > /dev/null 2>&1") { 379 print "Fn $line\n" if $verbose; 380 next; 381 } 382 # These functions are still provided by OpenSSL 383 # and still used by the Python test suite, 384 # but intentionally undocumented because nothing 385 # else uses them according to tb@, Dec 3, 2021. 386 if ($id =~ /NETSCAPE_(?:CERT_SEQUENCE|SPKAC|SPKI)/) { 387 print "F- $line\n" if $verbose; 388 next; 389 } 390 unless (system qw/grep -qR/, '^\.\\\\" .*\<' . $id . '\>', 391 "$srcdir/") { 392 print "F- $line\n" if $verbose; 393 next; 394 } 395 if ($undoc{$id}) { 396 print "F- $line\n" if $verbose; 397 delete $undoc{$id}; 398 next; 399 } 400 if ($id =~ /^ASN1_PCTX_\w+$/) { 401 print "F- $line\n" if $verbose; 402 next; 403 } 404 if ($verbose) { 405 print "XX $line\n"; 406 } else { 407 warn "not found: function $id()"; 408 } 409 next; 410 } 411 if (/ \*$/) { 412 $_ .= <$in_fh>; 413 goto try_again; 414 } 415 die "parse error: $_"; 416} 417close $in_fh; 418warn "expected as undocumented but not found: $_" foreach keys %undoc; 419exit 0; 420