1*c9488674SCy Schubert# Copyright 2021 The OpenSSL Project Authors. All Rights Reserved.
2*c9488674SCy Schubert#
3*c9488674SCy Schubert# Licensed under the Apache License 2.0 (the "License").  You may not use
4*c9488674SCy Schubert# this file except in compliance with the License.  You can obtain a copy
5*c9488674SCy Schubert# in the file LICENSE in the source distribution or at
6*c9488674SCy Schubert# https://www.openssl.org/source/license.html
7*c9488674SCy Schubert
8*c9488674SCy Schubertpackage OpenSSL::Config::Query;
9*c9488674SCy Schubert
10*c9488674SCy Schubertuse 5.10.0;
11*c9488674SCy Schubertuse strict;
12*c9488674SCy Schubertuse warnings;
13*c9488674SCy Schubertuse Carp;
14*c9488674SCy Schubert
15*c9488674SCy Schubert=head1 NAME
16*c9488674SCy Schubert
17*c9488674SCy SchubertOpenSSL::Config::Query - Query OpenSSL configuration info
18*c9488674SCy Schubert
19*c9488674SCy Schubert=head1 SYNOPSIS
20*c9488674SCy Schubert
21*c9488674SCy Schubert    use OpenSSL::Config::Info;
22*c9488674SCy Schubert
23*c9488674SCy Schubert    my $query = OpenSSL::Config::Query->new(info => \%unified_info);
24*c9488674SCy Schubert
25*c9488674SCy Schubert    # Query for something that's expected to give a scalar back
26*c9488674SCy Schubert    my $variable = $query->method(... args ...);
27*c9488674SCy Schubert
28*c9488674SCy Schubert    # Query for something that's expected to give a list back
29*c9488674SCy Schubert    my @variable = $query->method(... args ...);
30*c9488674SCy Schubert
31*c9488674SCy Schubert=head1 DESCRIPTION
32*c9488674SCy Schubert
33*c9488674SCy SchubertThe unified info structure, commonly known as the %unified_info table, has
34*c9488674SCy Schubertbecome quite complex, and a bit overwhelming to look through directly.  This
35*c9488674SCy Schubertmodule makes querying this structure simpler, through diverse methods.
36*c9488674SCy Schubert
37*c9488674SCy Schubert=head2 Constructor
38*c9488674SCy Schubert
39*c9488674SCy Schubert=over 4
40*c9488674SCy Schubert
41*c9488674SCy Schubert=item B<new> I<%options>
42*c9488674SCy Schubert
43*c9488674SCy SchubertCreates an instance of the B<OpenSSL::Config::Query> class.  It takes options
44*c9488674SCy Schubertin keyed pair form, i.e. a series of C<< key => value >> pairs.  Available
45*c9488674SCy Schubertoptions are:
46*c9488674SCy Schubert
47*c9488674SCy Schubert=over 4
48*c9488674SCy Schubert
49*c9488674SCy Schubert=item B<info> =E<gt> I<HASHREF>
50*c9488674SCy Schubert
51*c9488674SCy SchubertA reference to a unified information hash table, most commonly known as
52*c9488674SCy Schubert%unified_info.
53*c9488674SCy Schubert
54*c9488674SCy Schubert=item B<config> =E<gt> I<HASHREF>
55*c9488674SCy Schubert
56*c9488674SCy SchubertA reference to a config information hash table, most commonly known as
57*c9488674SCy Schubert%config.
58*c9488674SCy Schubert
59*c9488674SCy Schubert=back
60*c9488674SCy Schubert
61*c9488674SCy SchubertExample:
62*c9488674SCy Schubert
63*c9488674SCy Schubert    my $info = OpenSSL::Config::Info->new(info => \%unified_info);
64*c9488674SCy Schubert
65*c9488674SCy Schubert=back
66*c9488674SCy Schubert
67*c9488674SCy Schubert=cut
68*c9488674SCy Schubert
69*c9488674SCy Schubertsub new {
70*c9488674SCy Schubert    my $class = shift;
71*c9488674SCy Schubert    my %opts = @_;
72*c9488674SCy Schubert
73*c9488674SCy Schubert    my @messages = _check_accepted_options(\%opts,
74*c9488674SCy Schubert                                           info => 'HASH',
75*c9488674SCy Schubert                                           config => 'HASH');
76*c9488674SCy Schubert    croak $messages[0] if @messages;
77*c9488674SCy Schubert
78*c9488674SCy Schubert    # We make a shallow copy of the input structure.  We might make
79*c9488674SCy Schubert    # a different choice in the future...
80*c9488674SCy Schubert    my $instance = { info => $opts{info} // {},
81*c9488674SCy Schubert                     config => $opts{config} // {} };
82*c9488674SCy Schubert    bless $instance, $class;
83*c9488674SCy Schubert
84*c9488674SCy Schubert    return $instance;
85*c9488674SCy Schubert}
86*c9488674SCy Schubert
87*c9488674SCy Schubert=head2 Query methods
88*c9488674SCy Schubert
89*c9488674SCy Schubert=over 4
90*c9488674SCy Schubert
91*c9488674SCy Schubert=item B<get_sources> I<LIST>
92*c9488674SCy Schubert
93*c9488674SCy SchubertLIST is expected to be the collection of names of end products, such as
94*c9488674SCy Schubertprograms, modules, libraries.
95*c9488674SCy Schubert
96*c9488674SCy SchubertThe returned result is a hash table reference, with each key being one of
97*c9488674SCy Schubertthese end product names, and its value being a reference to an array of
98*c9488674SCy Schubertsource file names that constitutes everything that will or may become part
99*c9488674SCy Schubertof that end product.
100*c9488674SCy Schubert
101*c9488674SCy Schubert=cut
102*c9488674SCy Schubert
103*c9488674SCy Schubertsub get_sources {
104*c9488674SCy Schubert    my $self = shift;
105*c9488674SCy Schubert
106*c9488674SCy Schubert    my $result = {};
107*c9488674SCy Schubert    foreach (@_) {
108*c9488674SCy Schubert        my @sources = @{$self->{info}->{sources}->{$_} // []};
109*c9488674SCy Schubert        my @staticlibs =
110*c9488674SCy Schubert            grep { $_ =~ m|\.a$| } @{$self->{info}->{depends}->{$_} // []};
111*c9488674SCy Schubert
112*c9488674SCy Schubert        my %parts = ( %{$self->get_sources(@sources)},
113*c9488674SCy Schubert                      %{$self->get_sources(@staticlibs)} );
114*c9488674SCy Schubert        my @parts = map { @{$_} } values %parts;
115*c9488674SCy Schubert
116*c9488674SCy Schubert        my @generator =
117*c9488674SCy Schubert            ( ( $self->{info}->{generate}->{$_} // [] ) -> [0] // () );
118*c9488674SCy Schubert        my %generator_parts = %{$self->get_sources(@generator)};
119*c9488674SCy Schubert        # if there are any generator parts, we ignore it, because that means
120*c9488674SCy Schubert        # it's a compiled program and thus NOT part of the source that's
121*c9488674SCy Schubert        # queried.
122*c9488674SCy Schubert        @generator = () if %generator_parts;
123*c9488674SCy Schubert
124*c9488674SCy Schubert        my @partial_result =
125*c9488674SCy Schubert            ( ( map { @{$_} } values %parts ),
126*c9488674SCy Schubert              ( grep { !defined($parts{$_}) } @sources, @generator ) );
127*c9488674SCy Schubert
128*c9488674SCy Schubert        # Push conditionally, to avoid creating $result->{$_} with an empty
129*c9488674SCy Schubert        # value
130*c9488674SCy Schubert        push @{$result->{$_}}, @partial_result if @partial_result;
131*c9488674SCy Schubert    }
132*c9488674SCy Schubert
133*c9488674SCy Schubert    return $result;
134*c9488674SCy Schubert}
135*c9488674SCy Schubert
136*c9488674SCy Schubert=item B<get_config> I<LIST>
137*c9488674SCy Schubert
138*c9488674SCy SchubertLIST is expected to be the collection of names of configuration data, such
139*c9488674SCy Schubertas build_infos, sourcedir, ...
140*c9488674SCy Schubert
141*c9488674SCy SchubertThe returned result is a hash table reference, with each key being one of
142*c9488674SCy Schubertthese configuration data names, and its value being a reference to the value
143*c9488674SCy Schubertcorresponding to that name.
144*c9488674SCy Schubert
145*c9488674SCy Schubert=cut
146*c9488674SCy Schubert
147*c9488674SCy Schubertsub get_config {
148*c9488674SCy Schubert    my $self = shift;
149*c9488674SCy Schubert
150*c9488674SCy Schubert    return { map { $_ => $self->{config}->{$_} } @_ };
151*c9488674SCy Schubert}
152*c9488674SCy Schubert
153*c9488674SCy Schubert########
154*c9488674SCy Schubert#
155*c9488674SCy Schubert#  Helper functions
156*c9488674SCy Schubert#
157*c9488674SCy Schubert
158*c9488674SCy Schubertsub _check_accepted_options {
159*c9488674SCy Schubert    my $opts = shift;           # HASH reference (hopefully)
160*c9488674SCy Schubert    my %conds = @_;             # key => type
161*c9488674SCy Schubert
162*c9488674SCy Schubert    my @messages;
163*c9488674SCy Schubert    my %optnames = map { $_ => 1 } keys %$opts;
164*c9488674SCy Schubert    foreach (keys %conds) {
165*c9488674SCy Schubert        delete $optnames{$_};
166*c9488674SCy Schubert    }
167*c9488674SCy Schubert    push @messages, "Unknown options: " . join(', ', sort keys %optnames)
168*c9488674SCy Schubert        if keys %optnames;
169*c9488674SCy Schubert    foreach (sort keys %conds) {
170*c9488674SCy Schubert        push @messages, "'$_' value not a $conds{$_} reference"
171*c9488674SCy Schubert            if (defined $conds{$_} && defined $opts->{$_}
172*c9488674SCy Schubert                && ref $opts->{$_} ne $conds{$_});
173*c9488674SCy Schubert    }
174*c9488674SCy Schubert    return @messages;
175*c9488674SCy Schubert}
176*c9488674SCy Schubert
177*c9488674SCy Schubert1;
178