2 # Copyright 2018 The OpenSSL Project Authors. All Rights Reserved.
4 # Licensed under the OpenSSL license (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 OpenSSL::Ordinals - a private module to read and walk through ordinals
22 use OpenSSL::Ordinals;
24 my $ordinals = OpenSSL::Ordinals->new(from => "foo.num");
26 my $ordinals = OpenSSL::Ordinals->new();
27 $ordinals->load("foo.num");
29 foreach ($ordinals->items(comparator => by_name()) {
30 print $_->name(), "\n";
35 This is a OpenSSL private module to load an ordinals (F<.num>) file and
36 write out the data you want, sorted and filtered according to your rules.
38 An ordinals file is a file that enumerates all the symbols that a shared
39 library or loadable module must export. Each of them have a unique
40 assigned number as well as other attributes to indicate if they only exist
41 on a subset of the supported platforms, or if they are specific to certain
44 The unique numbers each symbol gets assigned needs to be maintained for a
45 shared library or module to stay compatible with previous versions on
46 platforms that maintain a transfer vector indexed by position rather than
47 by name. They also help keep information on certain symbols that are
48 aliases for others for certain platforms, or that have different forms
49 on different platforms.
57 =item B<new> I<%options>
59 Creates a new instance of the C<OpenSSL::Ordinals> class. It takes options
60 in keyed pair form, i.e. a series of C<key =E<gt> value> pairs. Available
65 =item B<from =E<gt> FILENAME>
67 Not only create a new instance, but immediately load it with data from the
68 ordinals file FILENAME.
79 contents => [], # The items themselves
81 bless $instance, $class;
83 $instance->load($opts{from}) if defined($opts{from});
88 =item B<$ordinals-E<gt>load FILENAME>
90 Loads the data from FILENAME into the instance. Any previously loaded data
99 croak "Bad instance" unless blessed($self);
100 croak "Undefined filename" unless defined($filename);
104 open F, '<', $filename or croak "Unable to open $filename";
106 s|\R$||; # Better chomp
110 my $item = OpenSSL::Ordinals::Item->new(from => $_);
111 my $num = $item->number();
113 croak "Disordered ordinals, $num < $max_num"
116 push @tmp_contents, $item;
120 $self->{contents} = [ @tmp_contents ];
124 =item B<$ordinals-E<gt>items> I<%options>
126 Returns a list of items according to a set of criteria. The criteria is
127 given in form keyed pair form, i.e. a series of C<key =E<gt> value> pairs.
128 Available options are:
132 =item B<sort =E<gt> SORTFUNCTION>
134 SORTFUNCTION is a reference to a function that takes two arguments, which
135 correspond to the classic C<$a> and C<$b> that are available in a C<sort>
138 =item B<filter =E<gt> FILTERFUNCTION>
140 FILTERFUNTION is a reference to a function that takes one argument, which
141 is every OpenSSL::Ordinals::Item element available.
151 my $comparator = $opts{sort};
152 my $filter = $opts{filter} // sub { 1; };
154 my @l = grep { $filter->($_) } @{$self->{contents}};
155 return sort { $comparator->($a, $b); } @l
156 if (defined $comparator);
164 Data elements, which is each line in an ordinals file, are instances
165 of a separate class, OpenSSL::Ordinals::Item, with its own methods:
171 package OpenSSL::Ordinals::Item;
177 =item B<new> I<%options>
179 Creates a new instance of the C<OpenSSL::Ordinals::Item> class. It takes
180 options in keyed pair form, i.e. a series of C<key =E<gt> value> pairs.
181 Available options are:
185 =item B<from =E<gt> STRING>
189 This will create a new item, filled with data coming from STRING.
191 STRING must conform to the following EBNF description:
193 ordinal string = symbol, spaces, ordinal, spaces, version, spaces,
194 exist, ":", platforms, ":", type, ":", features;
195 spaces = space, { space };
197 symbol = ( letter | "_"), { letter | digit | "_" };
199 version = number, "_", number, "_", number, letter, [ letter ];
200 exist = "EXIST" | "NOEXIST";
201 platforms = platform, { ",", platform };
202 platform = ( letter | "_" ) { letter | digit | "_" };
203 type = "FUNCTION" | "VARIABLE";
204 features = feature, { ",", feature };
205 feature = ( letter | "_" ) { letter | digit | "_" };
206 number = digit, { digit };
208 (C<letter> and C<digit> are assumed self evident)
218 my $string = $opts{from};
220 croak "No ordinals string given" unless defined $string;
222 my @a = split /\s+/, $string;
224 croak "Badly formatted ordinals string: $string"
225 unless ( scalar @a == 4
226 && $a[0] =~ /^[A-Za-z_][A-Za-z_0-9]*$/
228 && $a[2] =~ /^\d+_\d+_\d+(?:[a-z]{0,2})$/
232 (?:FUNCTION|VARIABLE):
237 my @b = split /:/, $a[3];
238 my $instance = { name => $a[0],
241 exists => $b[0] eq 'EXIST',
242 platforms => { map { m|^(!)?|; $' => !$1 }
245 features => [ split /,/,$b[3] // '' ] };
247 return bless $instance, $class;
253 =item B<$item-E<gt>name>
255 The symbol name for this item.
257 =item B<$item-E<gt>number>
259 The positional number for this item.
261 =item B<$item-E<gt>version>
263 The version number for this item. Please note that these version numbers
264 have underscore (C<_>) as a separator the the version parts.
266 =item B<$item-E<gt>exists>
268 A boolean that tells if this symbol exists in code or not.
270 =item B<$item-E<gt>platforms>
272 A hash table reference. The keys of the hash table are the names of
273 the specified platforms, with a value of 0 to indicate that this symbol
274 isn't available on that platform, and 1 to indicate that it is. Platforms
275 that aren't mentioned default to 1.
277 =item B<$item-E<gt>type>
279 C<FUNCTION> or C<VARIABLE>, depending on what the symbol represents.
280 Some platforms do not care about this, others do.
282 =item B<$item-E<gt>features>
284 An array reference, where every item indicates a feature where this symbol
285 is available. If no features are mentioned, the symbol is always available.
286 If any feature is mentioned, this symbol is I<only> available when those
287 features are enabled.
296 my $funcname = $AUTOLOAD;
297 (my $item = $funcname) =~ s|.*::||g;
299 croak "$funcname called as setter" if @_;
300 croak "$funcname invalid" unless exists $self->{$item};
301 return $self->{$item} if ref($self->{$item}) eq '';
302 return @{$self->{$item}} if ref($self->{$item}) eq 'ARRAY';
303 return %{$self->{$item}} if ref($self->{$item}) eq 'HASH';
306 =item B<$item-E<gt>to_string>
308 Converts the item to a string that can be saved in an ordinals file.
315 croak "Too many arguments" if @_;
316 my %platforms = $self->platforms();
317 my @features = $self->features();
318 return sprintf "%-39s %d\t%s\t%s:%s:%s:%s",
322 $self->exists() ? 'EXIST' : 'NOEXIST',
323 join(',', (map { ($platforms{$_} ? '' : '!') . $_ }
324 sort keys %platforms)),
326 join(',', @features);
331 =head2 Comparators and filters
333 For the B<$ordinals-E<gt>items> method, there are a few functions to create
334 comparators based on specific data:
340 # Go back to the main package to create comparators and filters
341 package OpenSSL::Ordinals;
347 Returns a comparator that will compare the names of two OpenSSL::Ordinals::Item
353 return sub { $_[0]->name() cmp $_[1]->name() };
358 Returns a comparator that will compare the ordinal numbers of two
359 OpenSSL::Ordinals::Item objects.
364 return sub { $_[0]->number() <=> $_[1]->number() };
369 Returns a comparator that will compare the version of two
370 OpenSSL::Ordinals::Item objects.
375 sub _ossl_versionsplit {
376 my $textversion = shift;
377 my ($major,$minor,$edit,$patch) =
378 $textversion =~ /^(\d+)_(\d+)_(\d+)([a-z]{0,2})$/;
379 return ($major,$minor,$edit,$patch);
383 my @a_split = _ossl_versionsplit($_[0]->version());
384 my @b_split = _ossl_versionsplit($_[1]->version());
387 if (scalar @a_split == 1) {
388 $verdict = $a_split[0] cmp $b_split[0];
390 $verdict = $a_split[0] <=> $b_split[0];
394 last unless $verdict == 0;
402 There are also the following filters:
408 # Filters... these are called by grep, the return sub must use $_ for
411 =item B<f_version VERSION>
413 Returns a filter that only lets through symbols with a version number
421 $version =~ s|\.|_|g if $version;
422 croak "No version specified"
423 unless $version && $version =~ /^\d_\d_\d[a-z]{0,2}$/;
425 return sub { $_[0]->version() eq $version };
432 Richard Levitte E<lt>levitte@openssl.orgE<gt>.