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