1b0d17251Schristos#! /usr/bin/env perl 2*4170684fSchristos# Copyright 2018-2023 The OpenSSL Project Authors. All Rights Reserved. 3b0d17251Schristos# 4b0d17251Schristos# Licensed under the Apache License 2.0 (the "License"). You may not use 5b0d17251Schristos# this file except in compliance with the License. You can obtain a copy 6b0d17251Schristos# in the file LICENSE in the source distribution or at 7b0d17251Schristos# https://www.openssl.org/source/license.html 8b0d17251Schristos 9b0d17251Schristospackage OpenSSL::Ordinals; 10b0d17251Schristos 11b0d17251Schristosuse strict; 12b0d17251Schristosuse warnings; 13b0d17251Schristosuse Carp; 14b0d17251Schristosuse Scalar::Util qw(blessed); 15b0d17251Schristosuse OpenSSL::Util; 16b0d17251Schristos 17b0d17251Schristosuse constant { 18b0d17251Schristos # "magic" filters, see the filters at the end of the file 19b0d17251Schristos F_NAME => 1, 20b0d17251Schristos F_NUMBER => 2, 21b0d17251Schristos}; 22b0d17251Schristos 23b0d17251Schristos=head1 NAME 24b0d17251Schristos 25b0d17251SchristosOpenSSL::Ordinals - a private module to read and walk through ordinals 26b0d17251Schristos 27b0d17251Schristos=head1 SYNOPSIS 28b0d17251Schristos 29b0d17251Schristos use OpenSSL::Ordinals; 30b0d17251Schristos 31b0d17251Schristos my $ordinals = OpenSSL::Ordinals->new(from => "foo.num"); 32b0d17251Schristos # or alternatively 33b0d17251Schristos my $ordinals = OpenSSL::Ordinals->new(); 34b0d17251Schristos $ordinals->load("foo.num"); 35b0d17251Schristos 36b0d17251Schristos foreach ($ordinals->items(comparator => by_name()) { 37b0d17251Schristos print $_->name(), "\n"; 38b0d17251Schristos } 39b0d17251Schristos 40b0d17251Schristos=head1 DESCRIPTION 41b0d17251Schristos 42b0d17251SchristosThis is a OpenSSL private module to load an ordinals (F<.num>) file and 43b0d17251Schristoswrite out the data you want, sorted and filtered according to your rules. 44b0d17251Schristos 45b0d17251SchristosAn ordinals file is a file that enumerates all the symbols that a shared 46b0d17251Schristoslibrary or loadable module must export. Each of them have a unique 47b0d17251Schristosassigned number as well as other attributes to indicate if they only exist 48b0d17251Schristoson a subset of the supported platforms, or if they are specific to certain 49b0d17251Schristosfeatures. 50b0d17251Schristos 51b0d17251SchristosThe unique numbers each symbol gets assigned needs to be maintained for a 52b0d17251Schristosshared library or module to stay compatible with previous versions on 53b0d17251Schristosplatforms that maintain a transfer vector indexed by position rather than 54b0d17251Schristosby name. They also help keep information on certain symbols that are 55b0d17251Schristosaliases for others for certain platforms, or that have different forms 56b0d17251Schristoson different platforms. 57b0d17251Schristos 58b0d17251Schristos=head2 Main methods 59b0d17251Schristos 60b0d17251Schristos=over 4 61b0d17251Schristos 62b0d17251Schristos=cut 63b0d17251Schristos 64b0d17251Schristos=item B<new> I<%options> 65b0d17251Schristos 66b0d17251SchristosCreates a new instance of the C<OpenSSL::Ordinals> class. It takes options 67b0d17251Schristosin keyed pair form, i.e. a series of C<< key => value >> pairs. Available 68b0d17251Schristosoptions are: 69b0d17251Schristos 70b0d17251Schristos=over 4 71b0d17251Schristos 72b0d17251Schristos=item B<< from => FILENAME >> 73b0d17251Schristos 74b0d17251SchristosNot only create a new instance, but immediately load it with data from the 75b0d17251Schristosordinals file FILENAME. 76b0d17251Schristos 77b0d17251Schristos=back 78b0d17251Schristos 79b0d17251Schristos=cut 80b0d17251Schristos 81b0d17251Schristossub new { 82b0d17251Schristos my $class = shift; 83b0d17251Schristos my %opts = @_; 84b0d17251Schristos 85b0d17251Schristos my $instance = { 86b0d17251Schristos filename => undef, # File name registered when loading 87b0d17251Schristos loaded_maxnum => 0, # Highest allocated item number when loading 88b0d17251Schristos loaded_contents => [], # Loaded items, if loading there was 89b0d17251Schristos maxassigned => 0, # Current highest assigned item number 90b0d17251Schristos maxnum => 0, # Current highest allocated item number 91b0d17251Schristos contents => [], # Items, indexed by number 92b0d17251Schristos name2num => {}, # Name to number dictionary 93b0d17251Schristos aliases => {}, # Aliases cache. 94b0d17251Schristos stats => {}, # Statistics, see 'sub validate' 95b0d17251Schristos debug => $opts{debug}, 96b0d17251Schristos }; 97b0d17251Schristos bless $instance, $class; 98b0d17251Schristos 99b0d17251Schristos $instance->set_version($opts{version}); 100b0d17251Schristos $instance->load($opts{from}) if defined($opts{from}); 101b0d17251Schristos 102b0d17251Schristos return $instance; 103b0d17251Schristos} 104b0d17251Schristos 105b0d17251Schristos=item B<< $ordinals->load FILENAME >> 106b0d17251Schristos 107b0d17251SchristosLoads the data from FILENAME into the instance. Any previously loaded data 108b0d17251Schristosis dropped. 109b0d17251Schristos 110b0d17251SchristosTwo internal databases are created. One database is simply a copy of the file 111b0d17251Schristoscontents and is treated as read-only. The other database is an exact copy of 112b0d17251Schristosthe first, but is treated as a work database, i.e. it can be modified and added 113b0d17251Schristosto. 114b0d17251Schristos 115b0d17251Schristos=cut 116b0d17251Schristos 117b0d17251Schristossub load { 118b0d17251Schristos my $self = shift; 119b0d17251Schristos my $filename = shift; 120b0d17251Schristos 121b0d17251Schristos croak "Undefined filename" unless defined($filename); 122b0d17251Schristos 123b0d17251Schristos my @tmp_contents = (); 124b0d17251Schristos my %tmp_name2num = (); 125b0d17251Schristos my $max_assigned = 0; 126b0d17251Schristos my $max_num = 0; 127b0d17251Schristos open F, '<', $filename or croak "Unable to open $filename"; 128b0d17251Schristos while (<F>) { 129b0d17251Schristos s|\R$||; # Better chomp 130b0d17251Schristos s|#.*||; 131b0d17251Schristos next if /^\s*$/; 132b0d17251Schristos 133b0d17251Schristos my $item = OpenSSL::Ordinals::Item->new(source => $filename, from => $_); 134b0d17251Schristos 135b0d17251Schristos my $num = $item->number(); 136b0d17251Schristos if ($num eq '?') { 137b0d17251Schristos $num = ++$max_num; 138b0d17251Schristos } elsif ($num eq '?+') { 139b0d17251Schristos $num = $max_num; 140b0d17251Schristos } else { 141b0d17251Schristos croak "Disordered ordinals, number sequence restarted" 142b0d17251Schristos if $max_num > $max_assigned && $num < $max_num; 143b0d17251Schristos croak "Disordered ordinals, $num < $max_num" 144b0d17251Schristos if $num < $max_num; 145b0d17251Schristos $max_assigned = $max_num = $num; 146b0d17251Schristos } 147b0d17251Schristos 148b0d17251Schristos $item->intnum($num); 149b0d17251Schristos push @{$tmp_contents[$num]}, $item; 150b0d17251Schristos $tmp_name2num{$item->name()} = $num; 151b0d17251Schristos } 152b0d17251Schristos close F; 153b0d17251Schristos 154b0d17251Schristos $self->{contents} = [ @tmp_contents ]; 155b0d17251Schristos $self->{name2num} = { %tmp_name2num }; 156b0d17251Schristos $self->{maxassigned} = $max_assigned; 157b0d17251Schristos $self->{maxnum} = $max_num; 158b0d17251Schristos $self->{filename} = $filename; 159b0d17251Schristos 160b0d17251Schristos # Make a deep copy, allowing {contents} to be an independent work array 161b0d17251Schristos foreach my $i (1..$max_num) { 162b0d17251Schristos if ($tmp_contents[$i]) { 163b0d17251Schristos $self->{loaded_contents}->[$i] = 164b0d17251Schristos [ map { OpenSSL::Ordinals::Item->new($_) } 165b0d17251Schristos @{$tmp_contents[$i]} ]; 166b0d17251Schristos } 167b0d17251Schristos } 168b0d17251Schristos $self->{loaded_maxnum} = $max_num; 169b0d17251Schristos return 1; 170b0d17251Schristos} 171b0d17251Schristos 172b0d17251Schristos=item B<< $ordinals->renumber >> 173b0d17251Schristos 174b0d17251SchristosRenumber any item that doesn't have an assigned number yet. 175b0d17251Schristos 176b0d17251Schristos=cut 177b0d17251Schristos 178b0d17251Schristossub renumber { 179b0d17251Schristos my $self = shift; 180b0d17251Schristos 181b0d17251Schristos my $max_assigned = 0; 182b0d17251Schristos foreach ($self->items(sort => by_number())) { 183b0d17251Schristos $_->number($_->intnum()) if $_->number() =~ m|^\?|; 184b0d17251Schristos if ($max_assigned < $_->number()) { 185b0d17251Schristos $max_assigned = $_->number(); 186b0d17251Schristos } 187b0d17251Schristos } 188b0d17251Schristos $self->{maxassigned} = $max_assigned; 189b0d17251Schristos} 190b0d17251Schristos 191b0d17251Schristos=item B<< $ordinals->rewrite >> 192b0d17251Schristos 193b0d17251Schristos=item B<< $ordinals->rewrite >>, I<%options> 194b0d17251Schristos 195b0d17251SchristosIf an ordinals file has been loaded, it gets rewritten with the data from 196b0d17251Schristosthe current work database. 197b0d17251Schristos 198b0d17251SchristosIf there are more arguments, they are used as I<%options> with the 199b0d17251Schristossame semantics as for B<< $ordinals->items >> described below, apart 200b0d17251Schristosfrom B<sort>, which is forbidden here. 201b0d17251Schristos 202b0d17251Schristos=cut 203b0d17251Schristos 204b0d17251Schristossub rewrite { 205b0d17251Schristos my $self = shift; 206b0d17251Schristos my %opts = @_; 207b0d17251Schristos 208b0d17251Schristos $self->write($self->{filename}, %opts); 209b0d17251Schristos} 210b0d17251Schristos 211b0d17251Schristos=item B<< $ordinals->write FILENAME >> 212b0d17251Schristos 213b0d17251Schristos=item B<< $ordinals->write FILENAME >>, I<%options> 214b0d17251Schristos 215b0d17251SchristosWrites the current work database data to the ordinals file FILENAME. 216b0d17251SchristosThis also validates the data, see B<< $ordinals->validate >> below. 217b0d17251Schristos 218b0d17251SchristosIf there are more arguments, they are used as I<%options> with the 219b0d17251Schristossame semantics as for B<< $ordinals->items >> described next, apart 220b0d17251Schristosfrom B<sort>, which is forbidden here. 221b0d17251Schristos 222b0d17251Schristos=cut 223b0d17251Schristos 224b0d17251Schristossub write { 225b0d17251Schristos my $self = shift; 226b0d17251Schristos my $filename = shift; 227b0d17251Schristos my %opts = @_; 228b0d17251Schristos 229b0d17251Schristos croak "Undefined filename" unless defined($filename); 230b0d17251Schristos croak "The 'sort' option is not allowed" if $opts{sort}; 231b0d17251Schristos 232b0d17251Schristos $self->validate(); 233b0d17251Schristos 234b0d17251Schristos open F, '>', $filename or croak "Unable to open $filename"; 235b0d17251Schristos foreach ($self->items(%opts, sort => by_number())) { 236b0d17251Schristos print F $_->to_string(),"\n"; 237b0d17251Schristos } 238b0d17251Schristos close F; 239b0d17251Schristos $self->{filename} = $filename; 240b0d17251Schristos $self->{loaded_maxnum} = $self->{maxnum}; 241b0d17251Schristos return 1; 242b0d17251Schristos} 243b0d17251Schristos 244b0d17251Schristos=item B<< $ordinals->items >> I<%options> 245b0d17251Schristos 246b0d17251SchristosReturns a list of items according to a set of criteria. The criteria is 247b0d17251Schristosgiven in form keyed pair form, i.e. a series of C<< key => value >> pairs. 248b0d17251SchristosAvailable options are: 249b0d17251Schristos 250b0d17251Schristos=over 4 251b0d17251Schristos 252b0d17251Schristos=item B<< sort => SORTFUNCTION >> 253b0d17251Schristos 254b0d17251SchristosSORTFUNCTION is a reference to a function that takes two arguments, which 255b0d17251Schristoscorrespond to the classic C<$a> and C<$b> that are available in a C<sort> 256b0d17251Schristosblock. 257b0d17251Schristos 258b0d17251Schristos=item B<< filter => FILTERFUNCTION >> 259b0d17251Schristos 260b0d17251SchristosFILTERFUNCTION is a reference to a function that takes one argument, which 261b0d17251Schristosis every OpenSSL::Ordinals::Item element available. 262b0d17251Schristos 263b0d17251Schristos=back 264b0d17251Schristos 265b0d17251Schristos=cut 266b0d17251Schristos 267b0d17251Schristossub items { 268b0d17251Schristos my $self = shift; 269b0d17251Schristos my %opts = @_; 270b0d17251Schristos 271b0d17251Schristos my $comparator = $opts{sort}; 272b0d17251Schristos my $filter = $opts{filter} // sub { 1; }; 273b0d17251Schristos 274b0d17251Schristos my @l = undef; 275b0d17251Schristos if (ref($filter) eq 'ARRAY') { 276b0d17251Schristos # run a "magic" filter 277b0d17251Schristos if ($filter->[0] == F_NUMBER) { 278b0d17251Schristos my $index = $filter->[1]; 279b0d17251Schristos @l = $index ? @{$self->{contents}->[$index] // []} : (); 280b0d17251Schristos } elsif ($filter->[0] == F_NAME) { 281b0d17251Schristos my $index = $self->{name2num}->{$filter->[1]}; 282b0d17251Schristos @l = $index ? @{$self->{contents}->[$index] // []} : (); 283b0d17251Schristos } else { 284b0d17251Schristos croak __PACKAGE__."->items called with invalid filter"; 285b0d17251Schristos } 286b0d17251Schristos } elsif (ref($filter) eq 'CODE') { 287b0d17251Schristos @l = grep { $filter->($_) } 288b0d17251Schristos map { @{$_ // []} } 289b0d17251Schristos @{$self->{contents}}; 290b0d17251Schristos } else { 291b0d17251Schristos croak __PACKAGE__."->items called with invalid filter"; 292b0d17251Schristos } 293b0d17251Schristos 294b0d17251Schristos return sort { $comparator->($a, $b); } @l 295b0d17251Schristos if (defined $comparator); 296b0d17251Schristos return @l; 297b0d17251Schristos} 298b0d17251Schristos 299b0d17251Schristos# Put an array of items back into the object after having checked consistency 300b0d17251Schristos# If there are exactly two items: 301b0d17251Schristos# - They MUST have the same number 302b0d17251Schristos# - They MUST have the same version 303b0d17251Schristos# - For platforms, both MUST hold the same ones, but with opposite values 304b0d17251Schristos# - For features, both MUST hold the same ones. 305b0d17251Schristos# - They MUST NOT have identical name, type, numeral, version, platforms, and features 306b0d17251Schristos# If there's just one item, just put it in the slot of its number 307b0d17251Schristos# In all other cases, something is wrong 308b0d17251Schristossub _putback { 309b0d17251Schristos my $self = shift; 310b0d17251Schristos my @items = @_; 311b0d17251Schristos 312b0d17251Schristos if (scalar @items < 1 || scalar @items > 2) { 313b0d17251Schristos croak "Wrong number of items: ", scalar @items, "\n ", 314b0d17251Schristos join("\n ", map { $_->{source}.": ".$_->name() } @items), "\n"; 315b0d17251Schristos } 316b0d17251Schristos if (scalar @items == 2) { 317b0d17251Schristos # Collect some data 318b0d17251Schristos my %numbers = (); 319b0d17251Schristos my %versions = (); 320b0d17251Schristos my %features = (); 321b0d17251Schristos foreach (@items) { 322b0d17251Schristos $numbers{$_->intnum()} = 1; 323b0d17251Schristos $versions{$_->version()} = 1; 324b0d17251Schristos foreach ($_->features()) { 325b0d17251Schristos $features{$_}++; 326b0d17251Schristos } 327b0d17251Schristos } 328b0d17251Schristos 329b0d17251Schristos # Check that all items we're trying to put back have the same number 330b0d17251Schristos croak "Items don't have the same numeral: ", 331b0d17251Schristos join(", ", map { $_->name()." => ".$_->intnum() } @items), "\n" 332b0d17251Schristos if (scalar keys %numbers > 1); 333b0d17251Schristos croak "Items don't have the same version: ", 334b0d17251Schristos join(", ", map { $_->name()." => ".$_->version() } @items), "\n" 335b0d17251Schristos if (scalar keys %versions > 1); 336b0d17251Schristos 337b0d17251Schristos # Check that both items run with the same features 338b0d17251Schristos foreach (@items) { 339b0d17251Schristos } 340b0d17251Schristos foreach (keys %features) { 341b0d17251Schristos delete $features{$_} if $features{$_} == 2; 342b0d17251Schristos } 343b0d17251Schristos croak "Features not in common between ", 344b0d17251Schristos $items[0]->name(), " and ", $items[1]->name(), ":", 345b0d17251Schristos join(", ", sort keys %features), "\n" 346b0d17251Schristos if %features; 347b0d17251Schristos 348b0d17251Schristos # Check for in addition identical name, type, and platforms 349b0d17251Schristos croak "Duplicate entries for ".$items[0]->name()." from ". 350b0d17251Schristos $items[0]->source()." and ".$items[1]->source()."\n" 351b0d17251Schristos if $items[0]->name() eq $items[1]->name() 352*4170684fSchristos && $items[0]->type() eq $items[1]->type() 353b0d17251Schristos && $items[0]->platforms() eq $items[1]->platforms(); 354b0d17251Schristos 355b0d17251Schristos # Check that all platforms exist in both items, and have opposite values 356b0d17251Schristos my @platforms = ( { $items[0]->platforms() }, 357b0d17251Schristos { $items[1]->platforms() } ); 358b0d17251Schristos foreach my $platform (keys %{$platforms[0]}) { 359b0d17251Schristos if (exists $platforms[1]->{$platform}) { 360b0d17251Schristos if ($platforms[0]->{$platform} != !$platforms[1]->{$platform}) { 361b0d17251Schristos croak "Platforms aren't opposite: ", 362b0d17251Schristos join(", ", 363b0d17251Schristos map { my %tmp_h = $_->platforms(); 364b0d17251Schristos $_->name().":".$platform 365b0d17251Schristos ." => " 366b0d17251Schristos .$tmp_h{$platform} } @items), 367b0d17251Schristos "\n"; 368b0d17251Schristos } 369b0d17251Schristos 370b0d17251Schristos # We're done with these 371b0d17251Schristos delete $platforms[0]->{$platform}; 372b0d17251Schristos delete $platforms[1]->{$platform}; 373b0d17251Schristos } 374b0d17251Schristos } 375b0d17251Schristos # If there are any remaining platforms, something's wrong 376b0d17251Schristos if (%{$platforms[0]} || %{$platforms[0]}) { 377b0d17251Schristos croak "There are platforms not in common between ", 378b0d17251Schristos $items[0]->name(), " and ", $items[1]->name(), "\n"; 379b0d17251Schristos } 380b0d17251Schristos } 381b0d17251Schristos $self->{contents}->[$items[0]->intnum()] = [ @items ]; 382b0d17251Schristos} 383b0d17251Schristos 384b0d17251Schristossub _parse_platforms { 385b0d17251Schristos my $self = shift; 386b0d17251Schristos my @defs = @_; 387b0d17251Schristos 388b0d17251Schristos my %platforms = (); 389b0d17251Schristos foreach (@defs) { 390b0d17251Schristos m{^(!)?}; 391b0d17251Schristos my $op = !(defined $1 && $1 eq '!'); 392b0d17251Schristos my $def = $'; 393b0d17251Schristos 394b0d17251Schristos if ($def =~ m{^_?WIN32$}) { $platforms{$&} = $op; } 395b0d17251Schristos if ($def =~ m{^__FreeBSD__$}) { $platforms{$&} = $op; } 396b0d17251Schristos# For future support 397b0d17251Schristos# if ($def =~ m{^__DragonFly__$}) { $platforms{$&} = $op; } 398b0d17251Schristos# if ($def =~ m{^__OpenBSD__$}) { $platforms{$&} = $op; } 399b0d17251Schristos# if ($def =~ m{^__NetBSD__$}) { $platforms{$&} = $op; } 400b0d17251Schristos if ($def =~ m{^OPENSSL_SYS_}) { $platforms{$'} = $op; } 401b0d17251Schristos } 402b0d17251Schristos 403b0d17251Schristos return %platforms; 404b0d17251Schristos} 405b0d17251Schristos 406b0d17251Schristossub _parse_features { 407b0d17251Schristos my $self = shift; 408b0d17251Schristos my @defs = @_; 409b0d17251Schristos 410b0d17251Schristos my %features = (); 411b0d17251Schristos foreach (@defs) { 412b0d17251Schristos m{^(!)?}; 413b0d17251Schristos my $op = !(defined $1 && $1 eq '!'); 414b0d17251Schristos my $def = $'; 415b0d17251Schristos 416b0d17251Schristos if ($def =~ m{^ZLIB$}) { $features{$&} = $op; } 417b0d17251Schristos if ($def =~ m{^OPENSSL_USE_}) { $features{$'} = $op; } 418b0d17251Schristos if ($def =~ m{^OPENSSL_NO_}) { $features{$'} = !$op; } 419b0d17251Schristos } 420b0d17251Schristos 421b0d17251Schristos return %features; 422b0d17251Schristos} 423b0d17251Schristos 424b0d17251Schristossub _adjust_version { 425b0d17251Schristos my $self = shift; 426b0d17251Schristos my $version = shift; 427b0d17251Schristos my $baseversion = $self->{baseversion}; 428b0d17251Schristos 429b0d17251Schristos $version = $baseversion 430b0d17251Schristos if ($baseversion ne '*' && $version ne '*' 431b0d17251Schristos && cmp_versions($baseversion, $version) > 0); 432b0d17251Schristos 433b0d17251Schristos return $version; 434b0d17251Schristos} 435b0d17251Schristos 436b0d17251Schristos=item B<< $ordinals->add SOURCE, NAME, TYPE, LIST >> 437b0d17251Schristos 438b0d17251SchristosAdds a new item from file SOURCE named NAME with the type TYPE, 439b0d17251Schristosand a set of C macros in 440b0d17251SchristosLIST that are expected to be defined or undefined to use this symbol, if 441b0d17251Schristosany. For undefined macros, they each must be prefixed with a C<!>. 442b0d17251Schristos 443b0d17251SchristosIf this symbol already exists in loaded data, it will be rewritten using 444b0d17251Schristosthe new input data, but will keep the same ordinal number and version. 445b0d17251SchristosIf it's entirely new, it will get a '?' and the current default version. 446b0d17251Schristos 447b0d17251Schristos=cut 448b0d17251Schristos 449b0d17251Schristossub add { 450b0d17251Schristos my $self = shift; 451b0d17251Schristos my $source = shift; # file where item was defined 452b0d17251Schristos my $name = shift; 453b0d17251Schristos my $type = shift; # FUNCTION or VARIABLE 454b0d17251Schristos my @defs = @_; # Macros from #ifdef and #ifndef 455b0d17251Schristos # (the latter prefixed with a '!') 456b0d17251Schristos 457b0d17251Schristos # call signature for debug output 458b0d17251Schristos my $verbsig = "add('$name' , '$type' , [ " . join(', ', @defs) . " ])"; 459b0d17251Schristos 460b0d17251Schristos croak __PACKAGE__."->add got a bad type '$type'" 461b0d17251Schristos unless $type eq 'FUNCTION' || $type eq 'VARIABLE'; 462b0d17251Schristos 463b0d17251Schristos my %platforms = _parse_platforms(@defs); 464b0d17251Schristos my %features = _parse_features(@defs); 465b0d17251Schristos 466b0d17251Schristos my @items = $self->items(filter => f_name($name)); 467b0d17251Schristos my $version = @items ? $items[0]->version() : $self->{currversion}; 468b0d17251Schristos my $intnum = @items ? $items[0]->intnum() : ++$self->{maxnum}; 469b0d17251Schristos my $number = @items ? $items[0]->number() : '?'; 470b0d17251Schristos print STDERR "DEBUG[",__PACKAGE__,":add] $verbsig\n", 471b0d17251Schristos @items ? map { "\t".$_->to_string()."\n" } @items : "No previous items\n", 472b0d17251Schristos if $self->{debug}; 473b0d17251Schristos @items = grep { $_->exists() } @items; 474b0d17251Schristos 475b0d17251Schristos my $new_item = 476b0d17251Schristos OpenSSL::Ordinals::Item->new( source => $source, 477b0d17251Schristos name => $name, 478b0d17251Schristos type => $type, 479b0d17251Schristos number => $number, 480b0d17251Schristos intnum => $intnum, 481b0d17251Schristos version => 482b0d17251Schristos $self->_adjust_version($version), 483b0d17251Schristos exists => 1, 484b0d17251Schristos platforms => { %platforms }, 485b0d17251Schristos features => [ 486b0d17251Schristos grep { $features{$_} } keys %features 487b0d17251Schristos ] ); 488b0d17251Schristos 489b0d17251Schristos push @items, $new_item; 490b0d17251Schristos print STDERR "DEBUG[",__PACKAGE__,"::add] $verbsig\n", map { "\t".$_->to_string()."\n" } @items 491b0d17251Schristos if $self->{debug}; 492b0d17251Schristos $self->_putback(@items); 493b0d17251Schristos 494b0d17251Schristos # If an alias was defined beforehand, add an item for it now 495b0d17251Schristos my $alias = $self->{aliases}->{$name}; 496b0d17251Schristos delete $self->{aliases}->{$name}; 497b0d17251Schristos 498b0d17251Schristos # For the caller to show 499b0d17251Schristos my @returns = ( $new_item ); 500b0d17251Schristos push @returns, $self->add_alias($source, $alias->{name}, $name, @{$alias->{defs}}) 501b0d17251Schristos if defined $alias; 502b0d17251Schristos return @returns; 503b0d17251Schristos} 504b0d17251Schristos 505b0d17251Schristos=item B<< $ordinals->add_alias SOURCE, ALIAS, NAME, LIST >> 506b0d17251Schristos 507b0d17251SchristosAdds an alias ALIAS for the symbol NAME from file SOURCE, and a set of C macros 508b0d17251Schristosin LIST that are expected to be defined or undefined to use this symbol, if any. 509b0d17251SchristosFor undefined macros, they each must be prefixed with a C<!>. 510b0d17251Schristos 511b0d17251SchristosIf this symbol already exists in loaded data, it will be rewritten using 512b0d17251Schristosthe new input data. Otherwise, the data will just be store away, to wait 513b0d17251Schristosthat the symbol NAME shows up. 514b0d17251Schristos 515b0d17251Schristos=cut 516b0d17251Schristos 517b0d17251Schristossub add_alias { 518b0d17251Schristos my $self = shift; 519b0d17251Schristos my $source = shift; 520b0d17251Schristos my $alias = shift; # This is the alias being added 521b0d17251Schristos my $name = shift; # For this name (assuming it exists) 522b0d17251Schristos my @defs = @_; # Platform attributes for the alias 523b0d17251Schristos 524b0d17251Schristos # call signature for debug output 525b0d17251Schristos my $verbsig = 526b0d17251Schristos "add_alias('$source' , '$alias' , '$name' , [ " . join(', ', @defs) . " ])"; 527b0d17251Schristos 528b0d17251Schristos croak "You're kidding me... $alias == $name" if $alias eq $name; 529b0d17251Schristos 530b0d17251Schristos my %platforms = _parse_platforms(@defs); 531b0d17251Schristos my %features = _parse_features(@defs); 532b0d17251Schristos 533b0d17251Schristos croak "Alias with associated features is forbidden\n" 534b0d17251Schristos if %features; 535b0d17251Schristos 536b0d17251Schristos my $f_byalias = f_name($alias); 537b0d17251Schristos my $f_byname = f_name($name); 538b0d17251Schristos my @items = $self->items(filter => $f_byalias); 539b0d17251Schristos foreach my $item ($self->items(filter => $f_byname)) { 540b0d17251Schristos push @items, $item unless grep { $_ == $item } @items; 541b0d17251Schristos } 542b0d17251Schristos @items = grep { $_->exists() } @items; 543b0d17251Schristos 544b0d17251Schristos croak "Alias already exists ($alias => $name)" 545b0d17251Schristos if scalar @items > 1; 546b0d17251Schristos if (scalar @items == 0) { 547b0d17251Schristos # The item we want to alias for doesn't exist yet, so we cache the 548b0d17251Schristos # alias and hope the item we're making an alias of shows up later 549b0d17251Schristos $self->{aliases}->{$name} = { source => $source, 550b0d17251Schristos name => $alias, defs => [ @defs ] }; 551b0d17251Schristos 552b0d17251Schristos print STDERR "DEBUG[",__PACKAGE__,":add_alias] $verbsig\n", 553b0d17251Schristos "\tSet future alias $alias => $name\n" 554b0d17251Schristos if $self->{debug}; 555b0d17251Schristos return (); 556b0d17251Schristos } elsif (scalar @items == 1) { 557b0d17251Schristos # The rule is that an alias is more or less a copy of the original 558b0d17251Schristos # item, just with another name. Also, the platforms given here are 559b0d17251Schristos # given to the original item as well, with opposite values. 560b0d17251Schristos my %alias_platforms = $items[0]->platforms(); 561b0d17251Schristos foreach (keys %platforms) { 562b0d17251Schristos $alias_platforms{$_} = !$platforms{$_}; 563b0d17251Schristos } 564b0d17251Schristos # We supposedly do now know how to do this... *ahem* 565b0d17251Schristos $items[0]->{platforms} = { %alias_platforms }; 566b0d17251Schristos 567b0d17251Schristos my $number = 568b0d17251Schristos $items[0]->number() =~ m|^\?| ? '?+' : $items[0]->number(); 569b0d17251Schristos my $alias_item = OpenSSL::Ordinals::Item->new( 570b0d17251Schristos source => $source, 571b0d17251Schristos name => $alias, 572b0d17251Schristos type => $items[0]->type(), 573b0d17251Schristos number => $number, 574b0d17251Schristos intnum => $items[0]->intnum(), 575b0d17251Schristos version => $self->_adjust_version($items[0]->version()), 576b0d17251Schristos exists => $items[0]->exists(), 577b0d17251Schristos platforms => { %platforms }, 578b0d17251Schristos features => [ $items[0]->features() ] 579b0d17251Schristos ); 580b0d17251Schristos push @items, $alias_item; 581b0d17251Schristos 582b0d17251Schristos print STDERR "DEBUG[",__PACKAGE__,":add_alias] $verbsig\n", 583b0d17251Schristos map { "\t".$_->to_string()."\n" } @items 584b0d17251Schristos if $self->{debug}; 585b0d17251Schristos $self->_putback(@items); 586b0d17251Schristos 587b0d17251Schristos # For the caller to show 588b0d17251Schristos return ( $alias_item->to_string() ); 589b0d17251Schristos } 590b0d17251Schristos croak "$name has an alias already (trying to add alias $alias)\n", 591b0d17251Schristos "\t", join(", ", map { $_->name() } @items), "\n"; 592b0d17251Schristos} 593b0d17251Schristos 594b0d17251Schristos=item B<< $ordinals->set_version VERSION >> 595b0d17251Schristos 596b0d17251Schristos=item B<< $ordinals->set_version VERSION BASEVERSION >> 597b0d17251Schristos 598b0d17251SchristosSets the default version for new symbol to VERSION. 599b0d17251Schristos 600b0d17251SchristosIf given, BASEVERSION sets the base version, i.e. the minimum version 601b0d17251Schristosfor all symbols. If not given, it will be calculated as follows: 602b0d17251Schristos 603b0d17251Schristos=over 4 604b0d17251Schristos 605b0d17251SchristosIf the given version is '*', then the base version will also be '*'. 606b0d17251Schristos 607b0d17251SchristosIf the given version starts with '0.', the base version will be '0.0.0'. 608b0d17251Schristos 609b0d17251SchristosIf the given version starts with '1.0.', the base version will be '1.0.0'. 610b0d17251Schristos 611b0d17251SchristosIf the given version starts with '1.1.', the base version will be '1.1.0'. 612b0d17251Schristos 613b0d17251SchristosIf the given version has a first number C<N> that's greater than 1, the 614b0d17251Schristosbase version will be formed from C<N>: 'N.0.0'. 615b0d17251Schristos 616b0d17251Schristos=back 617b0d17251Schristos 618b0d17251Schristos=cut 619b0d17251Schristos 620b0d17251Schristossub set_version { 621b0d17251Schristos my $self = shift; 622b0d17251Schristos # '*' is for "we don't care" 623b0d17251Schristos my $version = shift // '*'; 624b0d17251Schristos my $baseversion = shift // '*'; 625b0d17251Schristos 626b0d17251Schristos if ($baseversion eq '*') { 627b0d17251Schristos $baseversion = $version; 628b0d17251Schristos if ($baseversion ne '*') { 629b0d17251Schristos if ($baseversion =~ m|^(\d+)\.|, $1 > 1) { 630b0d17251Schristos $baseversion = "$1.0.0"; 631b0d17251Schristos } else { 632b0d17251Schristos $baseversion =~ s|^0\..*$|0.0.0|; 633b0d17251Schristos $baseversion =~ s|^1\.0\..*$|1.0.0|; 634b0d17251Schristos $baseversion =~ s|^1\.1\..*$|1.1.0|; 635b0d17251Schristos 636b0d17251Schristos die 'Invalid version' 637b0d17251Schristos if ($baseversion ne '0.0.0' 638b0d17251Schristos && $baseversion !~ m|^1\.[01]\.0$|); 639b0d17251Schristos } 640b0d17251Schristos } 641b0d17251Schristos } 642b0d17251Schristos 643b0d17251Schristos die 'Invalid base version' 644b0d17251Schristos if ($baseversion ne '*' && $version ne '*' 645b0d17251Schristos && cmp_versions($baseversion, $version) > 0); 646b0d17251Schristos 647b0d17251Schristos $self->{currversion} = $version; 648b0d17251Schristos $self->{baseversion} = $baseversion; 649b0d17251Schristos foreach ($self->items(filter => sub { $_[0] eq '*' })) { 650b0d17251Schristos $_->{version} = $self->{currversion}; 651b0d17251Schristos } 652b0d17251Schristos return 1; 653b0d17251Schristos} 654b0d17251Schristos 655b0d17251Schristos=item B<< $ordinals->invalidate >> 656b0d17251Schristos 657b0d17251SchristosInvalidates the whole working database. The practical effect is that all 658b0d17251Schristossymbols are set to not exist, but are kept around in the database to retain 659b0d17251Schristosordinal numbers and versions. 660b0d17251Schristos 661b0d17251Schristos=cut 662b0d17251Schristos 663b0d17251Schristossub invalidate { 664b0d17251Schristos my $self = shift; 665b0d17251Schristos 666b0d17251Schristos foreach (@{$self->{contents}}) { 667b0d17251Schristos foreach (@{$_ // []}) { 668b0d17251Schristos $_->{exists} = 0; 669b0d17251Schristos } 670b0d17251Schristos } 671b0d17251Schristos $self->{stats} = {}; 672b0d17251Schristos} 673b0d17251Schristos 674b0d17251Schristos=item B<< $ordinals->validate >> 675b0d17251Schristos 676b0d17251SchristosValidates the current working database by collection statistics on how many 677b0d17251Schristossymbols were added and how many were changed. These numbers can be retrieved 678b0d17251Schristoswith B<< $ordinals->stats >>. 679b0d17251Schristos 680b0d17251Schristos=cut 681b0d17251Schristos 682b0d17251Schristossub validate { 683b0d17251Schristos my $self = shift; 684b0d17251Schristos 685b0d17251Schristos $self->{stats} = {}; 686b0d17251Schristos for my $i (1..$self->{maxnum}) { 687b0d17251Schristos if ($i > $self->{loaded_maxnum} 688b0d17251Schristos || (!@{$self->{loaded_contents}->[$i] // []} 689b0d17251Schristos && @{$self->{contents}->[$i] // []})) { 690b0d17251Schristos $self->{stats}->{new}++; 691b0d17251Schristos } 692b0d17251Schristos if ($i <= $self->{maxassigned}) { 693b0d17251Schristos $self->{stats}->{assigned}++; 694b0d17251Schristos } else { 695b0d17251Schristos $self->{stats}->{unassigned}++; 696b0d17251Schristos } 697b0d17251Schristos next if ($i > $self->{loaded_maxnum}); 698b0d17251Schristos 699b0d17251Schristos my @loaded_strings = 700b0d17251Schristos map { $_->to_string() } @{$self->{loaded_contents}->[$i] // []}; 701b0d17251Schristos my @current_strings = 702b0d17251Schristos map { $_->to_string() } @{$self->{contents}->[$i] // []}; 703b0d17251Schristos 704b0d17251Schristos foreach my $str (@current_strings) { 705b0d17251Schristos @loaded_strings = grep { $str ne $_ } @loaded_strings; 706b0d17251Schristos } 707b0d17251Schristos if (@loaded_strings) { 708b0d17251Schristos $self->{stats}->{modified}++; 709b0d17251Schristos } 710b0d17251Schristos } 711b0d17251Schristos} 712b0d17251Schristos 713b0d17251Schristos=item B<< $ordinals->stats >> 714b0d17251Schristos 715b0d17251SchristosReturns the statistics that B<validate> calculate. 716b0d17251Schristos 717b0d17251Schristos=cut 718b0d17251Schristos 719b0d17251Schristossub stats { 720b0d17251Schristos my $self = shift; 721b0d17251Schristos 722b0d17251Schristos return %{$self->{stats}}; 723b0d17251Schristos} 724b0d17251Schristos 725b0d17251Schristos=back 726b0d17251Schristos 727b0d17251Schristos=head2 Data elements 728b0d17251Schristos 729b0d17251SchristosData elements, which is each line in an ordinals file, are instances 730b0d17251Schristosof a separate class, OpenSSL::Ordinals::Item, with its own methods: 731b0d17251Schristos 732b0d17251Schristos=over 4 733b0d17251Schristos 734b0d17251Schristos=cut 735b0d17251Schristos 736b0d17251Schristospackage OpenSSL::Ordinals::Item; 737b0d17251Schristos 738b0d17251Schristosuse strict; 739b0d17251Schristosuse warnings; 740b0d17251Schristosuse Carp; 741b0d17251Schristos 742b0d17251Schristos=item B<new> I<%options> 743b0d17251Schristos 744b0d17251SchristosCreates a new instance of the C<OpenSSL::Ordinals::Item> class. It takes 745b0d17251Schristosoptions in keyed pair form, i.e. a series of C<< key => value >> pairs. 746b0d17251SchristosAvailable options are: 747b0d17251Schristos 748b0d17251Schristos=over 4 749b0d17251Schristos 750b0d17251Schristos=item B<< source => FILENAME >>, B<< from => STRING >> 751b0d17251Schristos 752b0d17251SchristosThis will create a new item from FILENAME, filled with data coming from STRING. 753b0d17251Schristos 754b0d17251SchristosSTRING must conform to the following EBNF description: 755b0d17251Schristos 756b0d17251Schristos ordinal string = symbol, spaces, ordinal, spaces, version, spaces, 757b0d17251Schristos exist, ":", platforms, ":", type, ":", features; 758b0d17251Schristos spaces = space, { space }; 759b0d17251Schristos space = " " | "\t"; 760b0d17251Schristos symbol = ( letter | "_" ), { letter | digit | "_" }; 761b0d17251Schristos ordinal = number | "?" | "?+"; 762b0d17251Schristos version = number, "_", number, "_", number, [ letter, [ letter ] ]; 763b0d17251Schristos exist = "EXIST" | "NOEXIST"; 764b0d17251Schristos platforms = platform, { ",", platform }; 765b0d17251Schristos platform = ( letter | "_" ) { letter | digit | "_" }; 766b0d17251Schristos type = "FUNCTION" | "VARIABLE"; 767b0d17251Schristos features = feature, { ",", feature }; 768b0d17251Schristos feature = ( letter | "_" ) { letter | digit | "_" }; 769b0d17251Schristos number = digit, { digit }; 770b0d17251Schristos 771b0d17251Schristos(C<letter> and C<digit> are assumed self evident) 772b0d17251Schristos 773b0d17251Schristos=item B<< source => FILENAME >>, B<< name => STRING >>, B<< number => NUMBER >>, 774b0d17251Schristos B<< version => STRING >>, B<< exists => BOOLEAN >>, B<< type => STRING >>, 775b0d17251Schristos B<< platforms => HASHref >>, B<< features => LISTref >> 776b0d17251Schristos 777b0d17251SchristosThis will create a new item with data coming from the arguments. 778b0d17251Schristos 779b0d17251Schristos=back 780b0d17251Schristos 781b0d17251Schristos=cut 782b0d17251Schristos 783b0d17251Schristossub new { 784b0d17251Schristos my $class = shift; 785b0d17251Schristos 786b0d17251Schristos if (ref($_[0]) eq $class) { 787b0d17251Schristos return $class->new( map { $_ => $_[0]->{$_} } keys %{$_[0]} ); 788b0d17251Schristos } 789b0d17251Schristos 790b0d17251Schristos my %opts = @_; 791b0d17251Schristos 792b0d17251Schristos croak "No argument given" unless %opts; 793b0d17251Schristos 794b0d17251Schristos my $instance = undef; 795b0d17251Schristos if ($opts{from}) { 796b0d17251Schristos my @a = split /\s+/, $opts{from}; 797b0d17251Schristos 798b0d17251Schristos croak "Badly formatted ordinals string: $opts{from}" 799b0d17251Schristos unless ( scalar @a == 4 800b0d17251Schristos && $a[0] =~ /^[A-Za-z_][A-Za-z_0-9]*$/ 801b0d17251Schristos && $a[1] =~ /^\d+|\?\+?$/ 802b0d17251Schristos && $a[2] =~ /^(?:\*|\d+_\d+_\d+[a-z]{0,2})$/ 803b0d17251Schristos && $a[3] =~ /^ 804b0d17251Schristos (?:NO)?EXIST: 805b0d17251Schristos [^:]*: 806b0d17251Schristos (?:FUNCTION|VARIABLE): 807b0d17251Schristos [^:]* 808b0d17251Schristos $ 809b0d17251Schristos /x ); 810b0d17251Schristos 811b0d17251Schristos my @b = split /:/, $a[3]; 812b0d17251Schristos %opts = ( source => $opts{source}, 813b0d17251Schristos name => $a[0], 814b0d17251Schristos number => $a[1], 815b0d17251Schristos version => $a[2], 816b0d17251Schristos exists => $b[0] eq 'EXIST', 817b0d17251Schristos platforms => { map { m|^(!)?|; $' => !$1 } 818b0d17251Schristos split /,/,$b[1] }, 819b0d17251Schristos type => $b[2], 820b0d17251Schristos features => [ split /,/,$b[3] // '' ] ); 821b0d17251Schristos } 822b0d17251Schristos 823b0d17251Schristos if ($opts{name} && $opts{version} && defined $opts{exists} && $opts{type} 824b0d17251Schristos && ref($opts{platforms} // {}) eq 'HASH' 825b0d17251Schristos && ref($opts{features} // []) eq 'ARRAY') { 826b0d17251Schristos my $version = $opts{version}; 827b0d17251Schristos $version =~ s|_|.|g; 828b0d17251Schristos 829b0d17251Schristos $instance = { source => $opts{source}, 830b0d17251Schristos name => $opts{name}, 831b0d17251Schristos type => $opts{type}, 832b0d17251Schristos number => $opts{number}, 833b0d17251Schristos intnum => $opts{intnum}, 834b0d17251Schristos version => $version, 835b0d17251Schristos exists => !!$opts{exists}, 836b0d17251Schristos platforms => { %{$opts{platforms} // {}} }, 837b0d17251Schristos features => [ sort @{$opts{features} // []} ] }; 838b0d17251Schristos } else { 839b0d17251Schristos croak __PACKAGE__."->new() called with bad arguments\n". 840b0d17251Schristos join("", map { " $_\t=> ".$opts{$_}."\n" } sort keys %opts); 841b0d17251Schristos } 842b0d17251Schristos 843b0d17251Schristos return bless $instance, $class; 844b0d17251Schristos} 845b0d17251Schristos 846b0d17251Schristossub DESTROY { 847b0d17251Schristos} 848b0d17251Schristos 849b0d17251Schristos=item B<< $item->name >> 850b0d17251Schristos 851b0d17251SchristosThe symbol name for this item. 852b0d17251Schristos 853b0d17251Schristos=item B<< $item->number >> (read-write) 854b0d17251Schristos 855b0d17251SchristosThe positional number for this item. 856b0d17251Schristos 857b0d17251SchristosThis may be '?' for an unassigned symbol, or '?+' for an unassigned symbol 858b0d17251Schristosthat's an alias for the previous symbol. '?' and '?+' must be properly 859b0d17251Schristoshandled by the caller. The caller may change this to an actual number. 860b0d17251Schristos 861b0d17251Schristos=item B<< $item->version >> (read-only) 862b0d17251Schristos 863b0d17251SchristosThe version number for this item. Please note that these version numbers 864b0d17251Schristoshave underscore (C<_>) as a separator for the version parts. 865b0d17251Schristos 866b0d17251Schristos=item B<< $item->exists >> (read-only) 867b0d17251Schristos 868b0d17251SchristosA boolean that tells if this symbol exists in code or not. 869b0d17251Schristos 870b0d17251Schristos=item B<< $item->platforms >> (read-only) 871b0d17251Schristos 872b0d17251SchristosA hash table reference. The keys of the hash table are the names of 873b0d17251Schristosthe specified platforms, with a value of 0 to indicate that this symbol 874b0d17251Schristosisn't available on that platform, and 1 to indicate that it is. Platforms 875b0d17251Schristosthat aren't mentioned default to 1. 876b0d17251Schristos 877b0d17251Schristos=item B<< $item->type >> (read-only) 878b0d17251Schristos 879b0d17251SchristosC<FUNCTION> or C<VARIABLE>, depending on what the symbol represents. 880b0d17251SchristosSome platforms do not care about this, others do. 881b0d17251Schristos 882b0d17251Schristos=item B<< $item->features >> (read-only) 883b0d17251Schristos 884b0d17251SchristosAn array reference, where every item indicates a feature where this symbol 885b0d17251Schristosis available. If no features are mentioned, the symbol is always available. 886b0d17251SchristosIf any feature is mentioned, this symbol is I<only> available when those 887b0d17251Schristosfeatures are enabled. 888b0d17251Schristos 889b0d17251Schristos=cut 890b0d17251Schristos 891b0d17251Schristosour $AUTOLOAD; 892b0d17251Schristos 893b0d17251Schristos# Generic getter 894b0d17251Schristossub AUTOLOAD { 895b0d17251Schristos my $self = shift; 896b0d17251Schristos my $funcname = $AUTOLOAD; 897b0d17251Schristos (my $item = $funcname) =~ s|.*::||g; 898b0d17251Schristos 899b0d17251Schristos croak "$funcname called as setter" if @_; 900b0d17251Schristos croak "$funcname invalid" unless exists $self->{$item}; 901b0d17251Schristos return $self->{$item} if ref($self->{$item}) eq ''; 902b0d17251Schristos return @{$self->{$item}} if ref($self->{$item}) eq 'ARRAY'; 903b0d17251Schristos return %{$self->{$item}} if ref($self->{$item}) eq 'HASH'; 904b0d17251Schristos} 905b0d17251Schristos 906b0d17251Schristos=item B<< $item->intnum >> (read-write) 907b0d17251Schristos 908b0d17251SchristosInternal positional number. If I<< $item->number >> is '?' or '?+', the 909b0d17251Schristoscaller can use this to set a number for its purposes. 910b0d17251SchristosIf I<< $item->number >> is a number, I<< $item->intnum >> should be the 911b0d17251Schristossame 912b0d17251Schristos 913b0d17251Schristos=cut 914b0d17251Schristos 915b0d17251Schristos# Getter/setters 916b0d17251Schristossub intnum { 917b0d17251Schristos my $self = shift; 918b0d17251Schristos my $value = shift; 919b0d17251Schristos my $item = 'intnum'; 920b0d17251Schristos 921b0d17251Schristos croak "$item called with extra arguments" if @_; 922b0d17251Schristos $self->{$item} = "$value" if defined $value; 923b0d17251Schristos return $self->{$item}; 924b0d17251Schristos} 925b0d17251Schristos 926b0d17251Schristossub number { 927b0d17251Schristos my $self = shift; 928b0d17251Schristos my $value = shift; 929b0d17251Schristos my $item = 'number'; 930b0d17251Schristos 931b0d17251Schristos croak "$item called with extra arguments" if @_; 932b0d17251Schristos $self->{$item} = "$value" if defined $value; 933b0d17251Schristos return $self->{$item}; 934b0d17251Schristos} 935b0d17251Schristos 936b0d17251Schristos=item B<< $item->to_string >> 937b0d17251Schristos 938b0d17251SchristosConverts the item to a string that can be saved in an ordinals file. 939b0d17251Schristos 940b0d17251Schristos=cut 941b0d17251Schristos 942b0d17251Schristossub to_string { 943b0d17251Schristos my $self = shift; 944b0d17251Schristos 945b0d17251Schristos croak "Too many arguments" if @_; 946b0d17251Schristos my %platforms = $self->platforms(); 947b0d17251Schristos my @features = $self->features(); 948b0d17251Schristos my $version = $self->version(); 949b0d17251Schristos $version =~ s|\.|_|g; 950b0d17251Schristos return sprintf "%-39s %s\t%s\t%s:%s:%s:%s", 951b0d17251Schristos $self->name(), 952b0d17251Schristos $self->number(), 953b0d17251Schristos $version, 954b0d17251Schristos $self->exists() ? 'EXIST' : 'NOEXIST', 955b0d17251Schristos join(',', (map { ($platforms{$_} ? '' : '!') . $_ } 956b0d17251Schristos sort keys %platforms)), 957b0d17251Schristos $self->type(), 958b0d17251Schristos join(',', @features); 959b0d17251Schristos} 960b0d17251Schristos 961b0d17251Schristos=back 962b0d17251Schristos 963b0d17251Schristos=head2 Comparators and filters 964b0d17251Schristos 965b0d17251SchristosFor the B<< $ordinals->items >> method, there are a few functions to create 966b0d17251Schristoscomparators based on specific data: 967b0d17251Schristos 968b0d17251Schristos=over 4 969b0d17251Schristos 970b0d17251Schristos=cut 971b0d17251Schristos 972b0d17251Schristos# Go back to the main package to create comparators and filters 973b0d17251Schristospackage OpenSSL::Ordinals; 974b0d17251Schristos 975b0d17251Schristos# Comparators... 976b0d17251Schristos 977b0d17251Schristos=item B<by_name> 978b0d17251Schristos 979b0d17251SchristosReturns a comparator that will compare the names of two OpenSSL::Ordinals::Item 980b0d17251Schristosobjects. 981b0d17251Schristos 982b0d17251Schristos=cut 983b0d17251Schristos 984b0d17251Schristossub by_name { 985b0d17251Schristos return sub { $_[0]->name() cmp $_[1]->name() }; 986b0d17251Schristos} 987b0d17251Schristos 988b0d17251Schristos=item B<by_number> 989b0d17251Schristos 990b0d17251SchristosReturns a comparator that will compare the ordinal numbers of two 991b0d17251SchristosOpenSSL::Ordinals::Item objects. 992b0d17251Schristos 993b0d17251Schristos=cut 994b0d17251Schristos 995b0d17251Schristossub by_number { 996b0d17251Schristos return sub { $_[0]->intnum() <=> $_[1]->intnum() }; 997b0d17251Schristos} 998b0d17251Schristos 999b0d17251Schristos=item B<by_version> 1000b0d17251Schristos 1001b0d17251SchristosReturns a comparator that will compare the version of two 1002b0d17251SchristosOpenSSL::Ordinals::Item objects. 1003b0d17251Schristos 1004b0d17251Schristos=cut 1005b0d17251Schristos 1006b0d17251Schristossub by_version { 1007b0d17251Schristos return sub { 1008b0d17251Schristos # cmp_versions comes from OpenSSL::Util 1009b0d17251Schristos return cmp_versions($_[0]->version(), $_[1]->version()); 1010b0d17251Schristos } 1011b0d17251Schristos} 1012b0d17251Schristos 1013b0d17251Schristos=back 1014b0d17251Schristos 1015b0d17251SchristosThere are also the following filters: 1016b0d17251Schristos 1017b0d17251Schristos=over 4 1018b0d17251Schristos 1019b0d17251Schristos=cut 1020b0d17251Schristos 1021b0d17251Schristos# Filters... these are called by grep, the return sub must use $_ for 1022b0d17251Schristos# the item to check 1023b0d17251Schristos 1024b0d17251Schristos=item B<f_version VERSION> 1025b0d17251Schristos 1026b0d17251SchristosReturns a filter that only lets through symbols with a version number 1027b0d17251Schristosmatching B<VERSION>. 1028b0d17251Schristos 1029b0d17251Schristos=cut 1030b0d17251Schristos 1031b0d17251Schristossub f_version { 1032b0d17251Schristos my $version = shift; 1033b0d17251Schristos 1034b0d17251Schristos croak "No version specified" 1035b0d17251Schristos unless $version && $version =~ /^\d+\.\d+\.\d+[a-z]{0,2}$/; 1036b0d17251Schristos 1037b0d17251Schristos return sub { $_[0]->version() eq $version }; 1038b0d17251Schristos} 1039b0d17251Schristos 1040b0d17251Schristos=item B<f_number NUMBER> 1041b0d17251Schristos 1042b0d17251SchristosReturns a filter that only lets through symbols with the ordinal number 1043b0d17251Schristosmatching B<NUMBER>. 1044b0d17251Schristos 1045b0d17251SchristosNOTE that this returns a "magic" value that can not be used as a function. 1046b0d17251SchristosIt's only useful when passed directly as a filter to B<items>. 1047b0d17251Schristos 1048b0d17251Schristos=cut 1049b0d17251Schristos 1050b0d17251Schristossub f_number { 1051b0d17251Schristos my $number = shift; 1052b0d17251Schristos 1053b0d17251Schristos croak "No number specified" 1054b0d17251Schristos unless $number && $number =~ /^\d+$/; 1055b0d17251Schristos 1056b0d17251Schristos return [ F_NUMBER, $number ]; 1057b0d17251Schristos} 1058b0d17251Schristos 1059b0d17251Schristos 1060b0d17251Schristos=item B<f_name NAME> 1061b0d17251Schristos 1062b0d17251SchristosReturns a filter that only lets through symbols with the symbol name 1063b0d17251Schristosmatching B<NAME>. 1064b0d17251Schristos 1065b0d17251SchristosNOTE that this returns a "magic" value that can not be used as a function. 1066b0d17251SchristosIt's only useful when passed directly as a filter to B<items>. 1067b0d17251Schristos 1068b0d17251Schristos=cut 1069b0d17251Schristos 1070b0d17251Schristossub f_name { 1071b0d17251Schristos my $name = shift; 1072b0d17251Schristos 1073b0d17251Schristos croak "No name specified" 1074b0d17251Schristos unless $name; 1075b0d17251Schristos 1076b0d17251Schristos return [ F_NAME, $name ]; 1077b0d17251Schristos} 1078b0d17251Schristos 1079b0d17251Schristos=back 1080b0d17251Schristos 1081b0d17251Schristos=head1 AUTHORS 1082b0d17251Schristos 1083b0d17251SchristosRichard Levitte E<lt>levitte@openssl.orgE<gt>. 1084b0d17251Schristos 1085b0d17251Schristos=cut 1086b0d17251Schristos 1087b0d17251Schristos1; 1088