2 # Copyright 2018 The OpenSSL Project Authors. All Rights Reserved.
4 # Licensed under the Apache License 2.0 (the "License"). You may not use
5 # this file except in compliance with the License. You can obtain a copy
6 # in the file LICENSE in the source distribution or at
7 # https://www.openssl.org/source/license.html
9 package OpenSSL::Ordinals;
14 use Scalar::Util qw(blessed);
18 # "magic" filters, see the filters at the end of the file
25 OpenSSL::Ordinals - a private module to read and walk through ordinals
29 use OpenSSL::Ordinals;
31 my $ordinals = OpenSSL::Ordinals->new(from => "foo.num");
33 my $ordinals = OpenSSL::Ordinals->new();
34 $ordinals->load("foo.num");
36 foreach ($ordinals->items(comparator => by_name()) {
37 print $_->name(), "\n";
42 This is a OpenSSL private module to load an ordinals (F<.num>) file and
43 write out the data you want, sorted and filtered according to your rules.
45 An ordinals file is a file that enumerates all the symbols that a shared
46 library or loadable module must export. Each of them have a unique
47 assigned number as well as other attributes to indicate if they only exist
48 on a subset of the supported platforms, or if they are specific to certain
51 The unique numbers each symbol gets assigned needs to be maintained for a
52 shared library or module to stay compatible with previous versions on
53 platforms that maintain a transfer vector indexed by position rather than
54 by name. They also help keep information on certain symbols that are
55 aliases for others for certain platforms, or that have different forms
56 on different platforms.
64 =item B<new> I<%options>
66 Creates a new instance of the C<OpenSSL::Ordinals> class. It takes options
67 in keyed pair form, i.e. a series of C<< key => value >> pairs. Available
72 =item B<< from => FILENAME >>
74 Not only create a new instance, but immediately load it with data from the
75 ordinals file FILENAME.
86 filename => undef, # File name registered when loading
87 loaded_maxnum => 0, # Highest allocated item number when loading
88 loaded_contents => [], # Loaded items, if loading there was
89 maxassigned => 0, # Current highest assigned item number
90 maxnum => 0, # Current highest allocated item number
91 contents => [], # Items, indexed by number
92 name2num => {}, # Name to number dictionary
93 aliases => {}, # Aliases cache.
94 stats => {}, # Statistics, see 'sub validate'
95 debug => $opts{debug},
97 bless $instance, $class;
99 $instance->set_version($opts{version});
100 $instance->load($opts{from}) if defined($opts{from});
105 =item B<< $ordinals->load FILENAME >>
107 Loads the data from FILENAME into the instance. Any previously loaded data
110 Two internal databases are created. One database is simply a copy of the file
111 contents and is treated as read-only. The other database is an exact copy of
112 the first, but is treated as a work database, i.e. it can be modified and added
119 my $filename = shift;
121 croak "Undefined filename" unless defined($filename);
123 my @tmp_contents = ();
124 my %tmp_name2num = ();
125 my $max_assigned = 0;
127 open F, '<', $filename or croak "Unable to open $filename";
129 s|\R$||; # Better chomp
133 my $item = OpenSSL::Ordinals::Item->new(from => $_);
135 my $num = $item->number();
138 } elsif ($num eq '?+') {
141 croak "Disordered ordinals, number sequence restarted"
142 if $max_num > $max_assigned && $num < $max_num;
143 croak "Disordered ordinals, $num < $max_num"
145 $max_assigned = $max_num = $num;
149 push @{$tmp_contents[$num]}, $item;
150 $tmp_name2num{$item->name()} = $num;
154 $self->{contents} = [ @tmp_contents ];
155 $self->{name2num} = { %tmp_name2num };
156 $self->{maxassigned} = $max_assigned;
157 $self->{maxnum} = $max_num;
158 $self->{filename} = $filename;
160 # Make a deep copy, allowing {contents} to be an independent work array
161 foreach my $i (1..$max_num) {
162 if ($tmp_contents[$i]) {
163 $self->{loaded_contents}->[$i] =
164 [ map { OpenSSL::Ordinals::Item->new($_) }
165 @{$tmp_contents[$i]} ];
168 $self->{loaded_maxnum} = $max_num;
172 =item B<< $ordinals->renumber >>
174 Renumber any item that doesn't have an assigned number yet.
181 my $max_assigned = 0;
182 foreach ($self->items(sort => by_number())) {
183 $_->number($_->intnum()) if $_->number() =~ m|^\?|;
184 if ($max_assigned < $_->number()) {
185 $max_assigned = $_->number();
188 $self->{maxassigned} = $max_assigned;
191 =item B<< $ordinals->rewrite >>
193 =item B<< $ordinals->rewrite >>, I<%options>
195 If an ordinals file has been loaded, it gets rewritten with the data from
196 the current work database.
198 If there are more arguments, they are used as I<%options> with the
199 same semantics as for B<< $ordinals->items >> described below, apart
200 from B<sort>, which is forbidden here.
208 $self->write($self->{filename}, %opts);
211 =item B<< $ordinals->write FILENAME >>
213 =item B<< $ordinals->write FILENAME >>, I<%options>
215 Writes the current work database data to the ordinals file FILENAME.
216 This also validates the data, see B<< $ordinals->validate >> below.
218 If there are more arguments, they are used as I<%options> with the
219 same semantics as for B<< $ordinals->items >> described next, apart
220 from B<sort>, which is forbidden here.
226 my $filename = shift;
229 croak "Undefined filename" unless defined($filename);
230 croak "The 'sort' option is not allowed" if $opts{sort};
234 open F, '>', $filename or croak "Unable to open $filename";
235 foreach ($self->items(%opts, sort => by_number())) {
236 print F $_->to_string(),"\n";
239 $self->{filename} = $filename;
240 $self->{loaded_maxnum} = $self->{maxnum};
244 =item B<< $ordinals->items >> I<%options>
246 Returns a list of items according to a set of criteria. The criteria is
247 given in form keyed pair form, i.e. a series of C<< key => value >> pairs.
248 Available options are:
252 =item B<< sort => SORTFUNCTION >>
254 SORTFUNCTION is a reference to a function that takes two arguments, which
255 correspond to the classic C<$a> and C<$b> that are available in a C<sort>
258 =item B<< filter => FILTERFUNCTION >>
260 FILTERFUNCTION is a reference to a function that takes one argument, which
261 is every OpenSSL::Ordinals::Item element available.
271 my $comparator = $opts{sort};
272 my $filter = $opts{filter} // sub { 1; };
275 if (ref($filter) eq 'ARRAY') {
276 # run a "magic" filter
277 if ($filter->[0] == F_NUMBER) {
278 my $index = $filter->[1];
279 @l = $index ? @{$self->{contents}->[$index] // []} : ();
280 } elsif ($filter->[0] == F_NAME) {
281 my $index = $self->{name2num}->{$filter->[1]};
282 @l = $index ? @{$self->{contents}->[$index] // []} : ();
284 croak __PACKAGE__."->items called with invalid filter";
286 } elsif (ref($filter) eq 'CODE') {
287 @l = grep { $filter->($_) }
289 @{$self->{contents}};
291 croak __PACKAGE__."->items called with invalid filter";
294 return sort { $comparator->($a, $b); } @l
295 if (defined $comparator);
299 # Put an array of items back into the object after having checked consistency
300 # If there are exactly two items:
301 # - They MUST have the same number
302 # - For platforms, both MUST hold the same ones, but with opposite values
303 # - For features, both MUST hold the same ones.
304 # If there's just one item, just put it in the slot of its number
305 # In all other cases, something is wrong
310 if (scalar @items < 1 || scalar @items > 2) {
311 croak "Wrong number of items: ", scalar @items, " : ",
312 join(", ", map { $_->name() } @items), "\n";
314 if (scalar @items == 2) {
320 $numbers{$_->intnum()} = 1;
321 $versions{$_->version()} = 1;
322 foreach ($_->features()) {
327 # Check that all items we're trying to put back have the same number
328 croak "Items don't have the same numeral: ",
329 join(", ", map { $_->name()." => ".$_->intnum() } @items), "\n"
330 if (scalar keys %numbers > 1);
331 croak "Items don't have the same version: ",
332 join(", ", map { $_->name()." => ".$_->version() } @items), "\n"
333 if (scalar keys %versions > 1);
335 # Check that both items run with the same features
338 foreach (keys %features) {
339 delete $features{$_} if $features{$_} == 2;
341 croak "Features not in common between ",
342 $items[0]->name(), " and ", $items[1]->name(), ":",
343 join(", ", sort keys %features), "\n"
346 # Check that all platforms exist in both items, and have opposite values
347 my @platforms = ( { $items[0]->platforms() },
348 { $items[1]->platforms() } );
349 foreach my $platform (keys %{$platforms[0]}) {
350 if (exists $platforms[1]->{$platform}) {
351 if ($platforms[0]->{$platform} != !$platforms[1]->{$platform}) {
352 croak "Platforms aren't opposite: ",
354 map { my %tmp_h = $_->platforms();
355 $_->name().":".$platform
357 .$tmp_h{$platform} } @items),
361 # We're done with these
362 delete $platforms[0]->{$platform};
363 delete $platforms[1]->{$platform};
366 # If there are any remaining platforms, something's wrong
367 if (%{$platforms[0]} || %{$platforms[0]}) {
368 croak "There are platforms not in common between ",
369 $items[0]->name(), " and ", $items[1]->name(), "\n";
372 $self->{contents}->[$items[0]->intnum()] = [ @items ];
375 sub _parse_platforms {
382 my $op = !(defined $1 && $1 eq '!');
385 if ($def =~ m{^_?WIN32$}) { $platforms{$&} = $op; }
386 if ($def =~ m{^__FreeBSD__$}) { $platforms{$&} = $op; }
388 # if ($def =~ m{^__DragonFly__$}) { $platforms{$&} = $op; }
389 # if ($def =~ m{^__OpenBSD__$}) { $platforms{$&} = $op; }
390 # if ($def =~ m{^__NetBSD__$}) { $platforms{$&} = $op; }
391 if ($def =~ m{^OPENSSL_SYS_}) { $platforms{$'} = $op; }
397 sub _parse_features {
404 my $op = !(defined $1 && $1 eq '!');
407 if ($def =~ m{^ZLIB$}) { $features{$&} = $op; }
408 if ($def =~ m{^OPENSSL_USE_}) { $features{$'} = $op; }
409 if ($def =~ m{^OPENSSL_NO_}) { $features{$'} = !$op; }
415 sub _adjust_version {
418 my $baseversion = $self->{baseversion};
420 $version = $baseversion
421 if ($baseversion ne '*' && $version ne '*'
422 && cmp_versions($baseversion, $version) > 0);
427 =item B<< $ordinals->add NAME, TYPE, LIST >>
429 Adds a new item named NAME with the type TYPE, and a set of C macros in
430 LIST that are expected to be defined or undefined to use this symbol, if
431 any. For undefined macros, they each must be prefixed with a C<!>.
433 If this symbol already exists in loaded data, it will be rewritten using
434 the new input data, but will keep the same ordinal number and version.
435 If it's entirely new, it will get a '?' and the current default version.
442 my $type = shift; # FUNCTION or VARIABLE
443 my @defs = @_; # Macros from #ifdef and #ifndef
444 # (the latter prefixed with a '!')
446 # call signature for debug output
447 my $verbsig = "add('$name' , '$type' , [ " . join(', ', @defs) . " ])";
449 croak __PACKAGE__."->add got a bad type '$type'"
450 unless $type eq 'FUNCTION' || $type eq 'VARIABLE';
452 my %platforms = _parse_platforms(@defs);
453 my %features = _parse_features(@defs);
455 my @items = $self->items(filter => f_name($name));
456 my $version = @items ? $items[0]->version() : $self->{currversion};
457 my $intnum = @items ? $items[0]->intnum() : ++$self->{maxnum};
458 my $number = @items ? $items[0]->number() : '?';
459 print STDERR "DEBUG[",__PACKAGE__,":add] $verbsig\n",
460 @items ? map { "\t".$_->to_string()."\n" } @items : "No previous items\n",
462 @items = grep { $_->exists() } @items;
465 OpenSSL::Ordinals::Item->new( name => $name,
470 $self->_adjust_version($version),
472 platforms => { %platforms },
474 grep { $features{$_} } keys %features
477 push @items, $new_item;
478 print STDERR "DEBUG[",__PACKAGE__,"::add] $verbsig\n", map { "\t".$_->to_string()."\n" } @items
480 $self->_putback(@items);
482 # If an alias was defined beforehand, add an item for it now
483 my $alias = $self->{aliases}->{$name};
484 delete $self->{aliases}->{$name};
486 # For the caller to show
487 my @returns = ( $new_item );
488 push @returns, $self->add_alias($alias->{name}, $name, @{$alias->{defs}})
493 =item B<< $ordinals->add_alias ALIAS, NAME, LIST >>
495 Adds an alias ALIAS for the symbol NAME, and a set of C macros in LIST
496 that are expected to be defined or undefined to use this symbol, if any.
497 For undefined macros, they each must be prefixed with a C<!>.
499 If this symbol already exists in loaded data, it will be rewritten using
500 the new input data. Otherwise, the data will just be store away, to wait
501 that the symbol NAME shows up.
507 my $alias = shift; # This is the alias being added
508 my $name = shift; # For this name (assuming it exists)
509 my @defs = @_; # Platform attributes for the alias
511 # call signature for debug output
513 "add_alias('$alias' , '$name' , [ " . join(', ', @defs) . " ])";
515 croak "You're kidding me..." if $alias eq $name;
517 my %platforms = _parse_platforms(@defs);
518 my %features = _parse_features(@defs);
520 croak "Alias with associated features is forbidden\n"
523 my $f_byalias = f_name($alias);
524 my $f_byname = f_name($name);
525 my @items = $self->items(filter => $f_byalias);
526 foreach my $item ($self->items(filter => $f_byname)) {
527 push @items, $item unless grep { $_ == $item } @items;
529 @items = grep { $_->exists() } @items;
531 croak "Alias already exists ($alias => $name)"
532 if scalar @items > 1;
533 if (scalar @items == 0) {
534 # The item we want to alias for doesn't exist yet, so we cache the
535 # alias and hope the item we're making an alias of shows up later
536 $self->{aliases}->{$name} = { name => $alias, defs => [ @defs ] };
538 print STDERR "DEBUG[",__PACKAGE__,":add_alias] $verbsig\n",
539 "\tSet future alias $alias => $name\n"
542 } elsif (scalar @items == 1) {
543 # The rule is that an alias is more or less a copy of the original
544 # item, just with another name. Also, the platforms given here are
545 # given to the original item as well, with opposite values.
546 my %alias_platforms = $items[0]->platforms();
547 foreach (keys %platforms) {
548 $alias_platforms{$_} = !$platforms{$_};
550 # We supposedly do now know how to do this... *ahem*
551 $items[0]->{platforms} = { %alias_platforms };
554 $items[0]->number() =~ m|^\?| ? '?+' : $items[0]->number();
555 my $alias_item = OpenSSL::Ordinals::Item->new(
557 type => $items[0]->type(),
559 intnum => $items[0]->intnum(),
560 version => $self->_adjust_version($items[0]->version()),
561 exists => $items[0]->exists(),
562 platforms => { %platforms },
563 features => [ $items[0]->features() ]
565 push @items, $alias_item;
567 print STDERR "DEBUG[",__PACKAGE__,":add_alias] $verbsig\n",
568 map { "\t".$_->to_string()."\n" } @items
570 $self->_putback(@items);
572 # For the caller to show
573 return ( $alias_item->to_string() );
575 croak "$name has an alias already (trying to add alias $alias)\n",
576 "\t", join(", ", map { $_->name() } @items), "\n";
579 =item B<< $ordinals->set_version VERSION >>
581 =item B<< $ordinals->set_version VERSION BASEVERSION >>
583 Sets the default version for new symbol to VERSION.
585 If given, BASEVERSION sets the base version, i.e. the minimum version
586 for all symbols. If not given, it will be calculated as follows:
590 If the given version is '*', then the base version will also be '*'.
592 If the given version starts with '0.', the base version will be '0.0.0'.
594 If the given version starts with '1.0.', the base version will be '1.0.0'.
596 If the given version starts with '1.1.', the base version will be '1.1.0'.
598 If the given version has a first number C<N> that's greater than 1, the
599 base version will be formed from C<N>: 'N.0.0'.
607 # '*' is for "we don't care"
608 my $version = shift // '*';
609 my $baseversion = shift // '*';
611 $version =~ s|-.*||g;
613 if ($baseversion eq '*') {
614 $baseversion = $version;
615 if ($baseversion ne '*') {
616 if ($baseversion =~ m|^(\d+)\.|, $1 > 1) {
617 $baseversion = "$1.0.0";
619 $baseversion =~ s|^0\..*$|0.0.0|;
620 $baseversion =~ s|^1\.0\..*$|1.0.0|;
621 $baseversion =~ s|^1\.1\..*$|1.1.0|;
623 die 'Invalid version'
624 if ($baseversion ne '0.0.0'
625 && $baseversion !~ m|^1\.[01]\.0$|);
630 die 'Invalid base version'
631 if ($baseversion ne '*' && $version ne '*'
632 && cmp_versions($baseversion, $version) > 0);
634 $self->{currversion} = $version;
635 $self->{baseversion} = $baseversion;
636 foreach ($self->items(filter => sub { $_[0] eq '*' })) {
637 $_->{version} = $self->{currversion};
642 =item B<< $ordinals->invalidate >>
644 Invalidates the whole working database. The practical effect is that all
645 symbols are set to not exist, but are kept around in the database to retain
646 ordinal numbers and versions.
653 foreach (@{$self->{contents}}) {
654 foreach (@{$_ // []}) {
661 =item B<< $ordinals->validate >>
663 Validates the current working database by collection statistics on how many
664 symbols were added and how many were changed. These numbers can be retrieved
665 with B<< $ordinals->stats >>.
673 for my $i (1..$self->{maxnum}) {
674 if ($i > $self->{loaded_maxnum}
675 || (!@{$self->{loaded_contents}->[$i] // []}
676 && @{$self->{contents}->[$i] // []})) {
677 $self->{stats}->{new}++;
679 if ($i <= $self->{maxassigned}) {
680 $self->{stats}->{assigned}++;
682 $self->{stats}->{unassigned}++;
684 next if ($i > $self->{loaded_maxnum});
687 map { $_->to_string() } @{$self->{loaded_contents}->[$i] // []};
688 my @current_strings =
689 map { $_->to_string() } @{$self->{contents}->[$i] // []};
691 foreach my $str (@current_strings) {
692 @loaded_strings = grep { $str ne $_ } @loaded_strings;
694 if (@loaded_strings) {
695 $self->{stats}->{modified}++;
700 =item B<< $ordinals->stats >>
702 Returns the statistics that B<validate> calculate.
709 return %{$self->{stats}};
716 Data elements, which is each line in an ordinals file, are instances
717 of a separate class, OpenSSL::Ordinals::Item, with its own methods:
723 package OpenSSL::Ordinals::Item;
729 =item B<new> I<%options>
731 Creates a new instance of the C<OpenSSL::Ordinals::Item> class. It takes
732 options in keyed pair form, i.e. a series of C<< key => value >> pairs.
733 Available options are:
737 =item B<< from => STRING >>
739 This will create a new item, filled with data coming from STRING.
741 STRING must conform to the following EBNF description:
743 ordinal string = symbol, spaces, ordinal, spaces, version, spaces,
744 exist, ":", platforms, ":", type, ":", features;
745 spaces = space, { space };
747 symbol = ( letter | "_" ), { letter | digit | "_" };
748 ordinal = number | "?" | "?+";
749 version = number, "_", number, "_", number, [ letter, [ letter ] ];
750 exist = "EXIST" | "NOEXIST";
751 platforms = platform, { ",", platform };
752 platform = ( letter | "_" ) { letter | digit | "_" };
753 type = "FUNCTION" | "VARIABLE";
754 features = feature, { ",", feature };
755 feature = ( letter | "_" ) { letter | digit | "_" };
756 number = digit, { digit };
758 (C<letter> and C<digit> are assumed self evident)
760 =item B<< name => STRING >>, B<< number => NUMBER >>, B<< version => STRING >>,
761 B<< exists => BOOLEAN >>, B<< type => STRING >>,
762 B<< platforms => HASHref >>, B<< features => LISTref >>
764 This will create a new item with data coming from the arguments.
773 if (ref($_[0]) eq $class) {
774 return $class->new( map { $_ => $_[0]->{$_} } keys %{$_[0]} );
779 croak "No argument given" unless %opts;
781 my $instance = undef;
783 my @a = split /\s+/, $opts{from};
785 croak "Badly formatted ordinals string: $opts{from}"
786 unless ( scalar @a == 4
787 && $a[0] =~ /^[A-Za-z_][A-Za-z_0-9]*$/
788 && $a[1] =~ /^\d+|\?\+?$/
789 && $a[2] =~ /^(?:\*|\d+_\d+_\d+[a-z]{0,2})$/
793 (?:FUNCTION|VARIABLE):
798 my @b = split /:/, $a[3];
799 %opts = ( name => $a[0],
802 exists => $b[0] eq 'EXIST',
803 platforms => { map { m|^(!)?|; $' => !$1 }
806 features => [ split /,/,$b[3] // '' ] );
809 if ($opts{name} && $opts{version} && defined $opts{exists} && $opts{type}
810 && ref($opts{platforms} // {}) eq 'HASH'
811 && ref($opts{features} // []) eq 'ARRAY') {
812 my $version = $opts{version};
815 $instance = { name => $opts{name},
817 number => $opts{number},
818 intnum => $opts{intnum},
820 exists => !!$opts{exists},
821 platforms => { %{$opts{platforms} // {}} },
822 features => [ sort @{$opts{features} // []} ] };
824 croak __PACKAGE__."->new() called with bad arguments\n".
825 join("", map { " $_\t=> ".$opts{$_}."\n" } sort keys %opts);
828 return bless $instance, $class;
834 =item B<< $item->name >>
836 The symbol name for this item.
838 =item B<< $item->number >> (read-write)
840 The positional number for this item.
842 This may be '?' for an unassigned symbol, or '?+' for an unassigned symbol
843 that's an alias for the previous symbol. '?' and '?+' must be properly
844 handled by the caller. The caller may change this to an actual number.
846 =item B<< $item->version >> (read-only)
848 The version number for this item. Please note that these version numbers
849 have underscore (C<_>) as a separator the the version parts.
851 =item B<< $item->exists >> (read-only)
853 A boolean that tells if this symbol exists in code or not.
855 =item B<< $item->platforms >> (read-only)
857 A hash table reference. The keys of the hash table are the names of
858 the specified platforms, with a value of 0 to indicate that this symbol
859 isn't available on that platform, and 1 to indicate that it is. Platforms
860 that aren't mentioned default to 1.
862 =item B<< $item->type >> (read-only)
864 C<FUNCTION> or C<VARIABLE>, depending on what the symbol represents.
865 Some platforms do not care about this, others do.
867 =item B<< $item->features >> (read-only)
869 An array reference, where every item indicates a feature where this symbol
870 is available. If no features are mentioned, the symbol is always available.
871 If any feature is mentioned, this symbol is I<only> available when those
872 features are enabled.
881 my $funcname = $AUTOLOAD;
882 (my $item = $funcname) =~ s|.*::||g;
884 croak "$funcname called as setter" if @_;
885 croak "$funcname invalid" unless exists $self->{$item};
886 return $self->{$item} if ref($self->{$item}) eq '';
887 return @{$self->{$item}} if ref($self->{$item}) eq 'ARRAY';
888 return %{$self->{$item}} if ref($self->{$item}) eq 'HASH';
891 =item B<< $item->intnum >> (read-write)
893 Internal positional number. If I<< $item->number >> is '?' or '?+', the
894 caller can use this to set a number for its purposes.
895 If I<< $item->number >> is a number, I<< $item->intnum >> should be the
906 croak "$item called with extra arguments" if @_;
907 $self->{$item} = "$value" if defined $value;
908 return $self->{$item};
916 croak "$item called with extra arguments" if @_;
917 $self->{$item} = "$value" if defined $value;
918 return $self->{$item};
921 =item B<< $item->to_string >>
923 Converts the item to a string that can be saved in an ordinals file.
930 croak "Too many arguments" if @_;
931 my %platforms = $self->platforms();
932 my @features = $self->features();
933 my $version = $self->version();
934 $version =~ s|\.|_|g;
935 return sprintf "%-39s %s\t%s\t%s:%s:%s:%s",
939 $self->exists() ? 'EXIST' : 'NOEXIST',
940 join(',', (map { ($platforms{$_} ? '' : '!') . $_ }
941 sort keys %platforms)),
943 join(',', @features);
948 =head2 Comparators and filters
950 For the B<< $ordinals->items >> method, there are a few functions to create
951 comparators based on specific data:
957 # Go back to the main package to create comparators and filters
958 package OpenSSL::Ordinals;
964 Returns a comparator that will compare the names of two OpenSSL::Ordinals::Item
970 return sub { $_[0]->name() cmp $_[1]->name() };
975 Returns a comparator that will compare the ordinal numbers of two
976 OpenSSL::Ordinals::Item objects.
981 return sub { $_[0]->intnum() <=> $_[1]->intnum() };
986 Returns a comparator that will compare the version of two
987 OpenSSL::Ordinals::Item objects.
993 # cmp_versions comes from OpenSSL::Util
994 return cmp_versions($_[0]->version(), $_[1]->version());
1000 There are also the following filters:
1006 # Filters... these are called by grep, the return sub must use $_ for
1009 =item B<f_version VERSION>
1011 Returns a filter that only lets through symbols with a version number
1012 matching B<VERSION>.
1017 my $version = shift;
1019 croak "No version specified"
1020 unless $version && $version =~ /^\d+\.\d+\.\d+[a-z]{0,2}$/;
1022 return sub { $_[0]->version() eq $version };
1025 =item B<f_number NUMBER>
1027 Returns a filter that only lets through symbols with the ordinal number
1030 NOTE that this returns a "magic" value that can not be used as a function.
1031 It's only useful when passed directly as a filter to B<items>.
1038 croak "No number specified"
1039 unless $number && $number =~ /^\d+$/;
1041 return [ F_NUMBER, $number ];
1045 =item B<f_name NAME>
1047 Returns a filter that only lets through symbols with the symbol name
1050 NOTE that this returns a "magic" value that can not be used as a function.
1051 It's only useful when passed directly as a filter to B<items>.
1058 croak "No name specified"
1061 return [ F_NAME, $name ];
1068 Richard Levitte E<lt>levitte@openssl.orgE<gt>.