xref: /netbsd-src/crypto/external/bsd/openssl/dist/util/perl/OpenSSL/Util.pm (revision b0d1725196a7921d003d2c66a14f186abda4176b)
1*b0d17251Schristos#! /usr/bin/env perl
2*b0d17251Schristos# Copyright 2018-2021 The OpenSSL Project Authors. All Rights Reserved.
3*b0d17251Schristos#
4*b0d17251Schristos# Licensed under the Apache License 2.0 (the "License").  You may not use
5*b0d17251Schristos# this file except in compliance with the License.  You can obtain a copy
6*b0d17251Schristos# in the file LICENSE in the source distribution or at
7*b0d17251Schristos# https://www.openssl.org/source/license.html
8*b0d17251Schristos
9*b0d17251Schristospackage OpenSSL::Util;
10*b0d17251Schristos
11*b0d17251Schristosuse strict;
12*b0d17251Schristosuse warnings;
13*b0d17251Schristosuse Carp;
14*b0d17251Schristos
15*b0d17251Schristosuse Exporter;
16*b0d17251Schristosuse vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
17*b0d17251Schristos$VERSION = "0.1";
18*b0d17251Schristos@ISA = qw(Exporter);
19*b0d17251Schristos@EXPORT = qw(cmp_versions quotify1 quotify_l fixup_cmd_elements fixup_cmd
20*b0d17251Schristos             dump_data);
21*b0d17251Schristos@EXPORT_OK = qw();
22*b0d17251Schristos
23*b0d17251Schristos=head1 NAME
24*b0d17251Schristos
25*b0d17251SchristosOpenSSL::Util - small OpenSSL utilities
26*b0d17251Schristos
27*b0d17251Schristos=head1 SYNOPSIS
28*b0d17251Schristos
29*b0d17251Schristos  use OpenSSL::Util;
30*b0d17251Schristos
31*b0d17251Schristos  $versiondiff = cmp_versions('1.0.2k', '3.0.1');
32*b0d17251Schristos  # $versiondiff should be -1
33*b0d17251Schristos
34*b0d17251Schristos  $versiondiff = cmp_versions('1.1.0', '1.0.2a');
35*b0d17251Schristos  # $versiondiff should be 1
36*b0d17251Schristos
37*b0d17251Schristos  $versiondiff = cmp_versions('1.1.1', '1.1.1');
38*b0d17251Schristos  # $versiondiff should be 0
39*b0d17251Schristos
40*b0d17251Schristos=head1 DESCRIPTION
41*b0d17251Schristos
42*b0d17251Schristos=over
43*b0d17251Schristos
44*b0d17251Schristos=item B<cmp_versions "VERSION1", "VERSION2">
45*b0d17251Schristos
46*b0d17251SchristosCompares VERSION1 with VERSION2, paying attention to OpenSSL versioning.
47*b0d17251Schristos
48*b0d17251SchristosReturns 1 if VERSION1 is greater than VERSION2, 0 if they are equal, and
49*b0d17251Schristos-1 if VERSION1 is less than VERSION2.
50*b0d17251Schristos
51*b0d17251Schristos=back
52*b0d17251Schristos
53*b0d17251Schristos=cut
54*b0d17251Schristos
55*b0d17251Schristos# Until we're rid of everything with the old version scheme,
56*b0d17251Schristos# we need to be able to handle older style x.y.zl versions.
57*b0d17251Schristos# In terms of comparison, the x.y.zl and the x.y.z schemes
58*b0d17251Schristos# are compatible...  mostly because the latter starts at a
59*b0d17251Schristos# new major release with a new major number.
60*b0d17251Schristossub _ossl_versionsplit {
61*b0d17251Schristos    my $textversion = shift;
62*b0d17251Schristos    return $textversion if $textversion eq '*';
63*b0d17251Schristos    my ($major,$minor,$edit,$letter) =
64*b0d17251Schristos        $textversion =~ /^(\d+)\.(\d+)\.(\d+)([a-z]{0,2})$/;
65*b0d17251Schristos
66*b0d17251Schristos    return ($major,$minor,$edit,$letter);
67*b0d17251Schristos}
68*b0d17251Schristos
69*b0d17251Schristossub cmp_versions {
70*b0d17251Schristos    my @a_split = _ossl_versionsplit(shift);
71*b0d17251Schristos    my @b_split = _ossl_versionsplit(shift);
72*b0d17251Schristos    my $verdict = 0;
73*b0d17251Schristos
74*b0d17251Schristos    while (@a_split) {
75*b0d17251Schristos        # The last part is a letter sequence (or a '*')
76*b0d17251Schristos        if (scalar @a_split == 1) {
77*b0d17251Schristos            $verdict = $a_split[0] cmp $b_split[0];
78*b0d17251Schristos        } else {
79*b0d17251Schristos            $verdict = $a_split[0] <=> $b_split[0];
80*b0d17251Schristos        }
81*b0d17251Schristos        shift @a_split;
82*b0d17251Schristos        shift @b_split;
83*b0d17251Schristos        last unless $verdict == 0;
84*b0d17251Schristos    }
85*b0d17251Schristos
86*b0d17251Schristos    return $verdict;
87*b0d17251Schristos}
88*b0d17251Schristos
89*b0d17251Schristos# It might be practical to quotify some strings and have them protected
90*b0d17251Schristos# from possible harm.  These functions primarily quote things that might
91*b0d17251Schristos# be interpreted wrongly by a perl eval.
92*b0d17251Schristos
93*b0d17251Schristos=over 4
94*b0d17251Schristos
95*b0d17251Schristos=item quotify1 STRING
96*b0d17251Schristos
97*b0d17251SchristosThis adds quotes (") around the given string, and escapes any $, @, \,
98*b0d17251Schristos" and ' by prepending a \ to them.
99*b0d17251Schristos
100*b0d17251Schristos=back
101*b0d17251Schristos
102*b0d17251Schristos=cut
103*b0d17251Schristos
104*b0d17251Schristossub quotify1 {
105*b0d17251Schristos    my $s = shift @_;
106*b0d17251Schristos    $s =~ s/([\$\@\\"'])/\\$1/g;
107*b0d17251Schristos    '"'.$s.'"';
108*b0d17251Schristos}
109*b0d17251Schristos
110*b0d17251Schristos=over 4
111*b0d17251Schristos
112*b0d17251Schristos=item quotify_l LIST
113*b0d17251Schristos
114*b0d17251SchristosFor each defined element in LIST (i.e. elements that aren't undef), have
115*b0d17251Schristosit quotified with 'quotify1'.
116*b0d17251SchristosUndefined elements are ignored.
117*b0d17251Schristos
118*b0d17251Schristos=cut
119*b0d17251Schristos
120*b0d17251Schristossub quotify_l {
121*b0d17251Schristos    map {
122*b0d17251Schristos        if (!defined($_)) {
123*b0d17251Schristos            ();
124*b0d17251Schristos        } else {
125*b0d17251Schristos            quotify1($_);
126*b0d17251Schristos        }
127*b0d17251Schristos    } @_;
128*b0d17251Schristos}
129*b0d17251Schristos
130*b0d17251Schristos=over 4
131*b0d17251Schristos
132*b0d17251Schristos=item fixup_cmd_elements LIST
133*b0d17251Schristos
134*b0d17251SchristosFixes up the command line elements given by LIST in a platform specific
135*b0d17251Schristosmanner.
136*b0d17251Schristos
137*b0d17251SchristosThe result of this function is a copy of LIST with strings where quotes and
138*b0d17251Schristosescapes have been injected as necessary depending on the content of each
139*b0d17251SchristosLIST string.
140*b0d17251Schristos
141*b0d17251SchristosThis can also be used to put quotes around the executable of a command.
142*b0d17251SchristosI<This must never ever be done on VMS.>
143*b0d17251Schristos
144*b0d17251Schristos=back
145*b0d17251Schristos
146*b0d17251Schristos=cut
147*b0d17251Schristos
148*b0d17251Schristossub fixup_cmd_elements {
149*b0d17251Schristos    # A formatter for the command arguments, defaulting to the Unix setup
150*b0d17251Schristos    my $arg_formatter =
151*b0d17251Schristos        sub { $_ = shift;
152*b0d17251Schristos              ($_ eq '' || /\s|[\{\}\\\$\[\]\*\?\|\&:;<>]/) ? "'$_'" : $_ };
153*b0d17251Schristos
154*b0d17251Schristos    if ( $^O eq "VMS") {        # VMS setup
155*b0d17251Schristos        $arg_formatter = sub {
156*b0d17251Schristos            $_ = shift;
157*b0d17251Schristos            if ($_ eq '' || /\s|[!"[:upper:]]/) {
158*b0d17251Schristos                s/"/""/g;
159*b0d17251Schristos                '"'.$_.'"';
160*b0d17251Schristos            } else {
161*b0d17251Schristos                $_;
162*b0d17251Schristos            }
163*b0d17251Schristos        };
164*b0d17251Schristos    } elsif ( $^O eq "MSWin32") { # MSWin setup
165*b0d17251Schristos        $arg_formatter = sub {
166*b0d17251Schristos            $_ = shift;
167*b0d17251Schristos            if ($_ eq '' || /\s|["\|\&\*\;<>]/) {
168*b0d17251Schristos                s/(["\\])/\\$1/g;
169*b0d17251Schristos                '"'.$_.'"';
170*b0d17251Schristos            } else {
171*b0d17251Schristos                $_;
172*b0d17251Schristos            }
173*b0d17251Schristos        };
174*b0d17251Schristos    }
175*b0d17251Schristos
176*b0d17251Schristos    return ( map { $arg_formatter->($_) } @_ );
177*b0d17251Schristos}
178*b0d17251Schristos
179*b0d17251Schristos=over 4
180*b0d17251Schristos
181*b0d17251Schristos=item fixup_cmd LIST
182*b0d17251Schristos
183*b0d17251SchristosThis is a sibling of fixup_cmd_elements() that expects the LIST to be a
184*b0d17251Schristoscomplete command line.  It does the same thing as fixup_cmd_elements(),
185*b0d17251Schristosexpect that it treats the first LIST element specially on VMS.
186*b0d17251Schristos
187*b0d17251Schristos=back
188*b0d17251Schristos
189*b0d17251Schristos=cut
190*b0d17251Schristos
191*b0d17251Schristossub fixup_cmd {
192*b0d17251Schristos    return fixup_cmd_elements(@_) unless $^O eq 'VMS';
193*b0d17251Schristos
194*b0d17251Schristos    # The rest is VMS specific
195*b0d17251Schristos    my $prog = shift;
196*b0d17251Schristos
197*b0d17251Schristos    # On VMS, running random executables without having a command symbol
198*b0d17251Schristos    # means running them with the MCR command.  This is an old PDP-11
199*b0d17251Schristos    # command that stuck around.
200*b0d17251Schristos    # This assumes that we're passed the name of an executable.  This is a
201*b0d17251Schristos    # safe assumption for OpenSSL command lines
202*b0d17251Schristos    my $prefix = 'MCR';
203*b0d17251Schristos
204*b0d17251Schristos    if ($prog =~ /^MCR$/i) {
205*b0d17251Schristos        # If the first element is "MCR" (independent of case) already, then
206*b0d17251Schristos        # we assume that the program it runs is already written the way it
207*b0d17251Schristos        # should, and just grab it.
208*b0d17251Schristos        $prog = shift;
209*b0d17251Schristos    } else {
210*b0d17251Schristos        # If the command itself doesn't have a directory spec, make sure
211*b0d17251Schristos        # that there is one.  Otherwise, MCR assumes that the program
212*b0d17251Schristos        # resides in SYS$SYSTEM:
213*b0d17251Schristos        $prog = '[]' . $prog unless $prog =~ /^(?:[\$a-z0-9_]+:)?[<\[]/i;
214*b0d17251Schristos    }
215*b0d17251Schristos
216*b0d17251Schristos    return ( $prefix, $prog, fixup_cmd_elements(@_) );
217*b0d17251Schristos}
218*b0d17251Schristos
219*b0d17251Schristos=item dump_data REF, OPTS
220*b0d17251Schristos
221*b0d17251SchristosDump the data from REF into a string that can be evaluated into the same
222*b0d17251Schristosdata by Perl.
223*b0d17251Schristos
224*b0d17251SchristosOPTS is the rest of the arguments, expected to be pairs formed with C<< => >>.
225*b0d17251SchristosThe following OPTS keywords are understood:
226*b0d17251Schristos
227*b0d17251Schristos=over 4
228*b0d17251Schristos
229*b0d17251Schristos=item B<delimiters =E<gt> 0 | 1>
230*b0d17251Schristos
231*b0d17251SchristosInclude the outer delimiter of the REF type in the resulting string if C<1>,
232*b0d17251Schristosotherwise not.
233*b0d17251Schristos
234*b0d17251Schristos=item B<indent =E<gt> num>
235*b0d17251Schristos
236*b0d17251SchristosThe indentation of the caller, i.e. an initial value.  If not given, there
237*b0d17251Schristoswill be no indentation at all, and the string will only be one line.
238*b0d17251Schristos
239*b0d17251Schristos=back
240*b0d17251Schristos
241*b0d17251Schristos=cut
242*b0d17251Schristos
243*b0d17251Schristossub dump_data {
244*b0d17251Schristos    my $ref = shift;
245*b0d17251Schristos    # Available options:
246*b0d17251Schristos    # indent           => callers indentation ( undef for no indentation,
247*b0d17251Schristos    #                     an integer otherwise )
248*b0d17251Schristos    # delimiters       => 1 if outer delimiters should be added
249*b0d17251Schristos    my %opts = @_;
250*b0d17251Schristos
251*b0d17251Schristos    my $indent = $opts{indent} // 1;
252*b0d17251Schristos    # Indentation of the whole structure, where applicable
253*b0d17251Schristos    my $nlindent1 = defined $opts{indent} ? "\n" . ' ' x $indent : ' ';
254*b0d17251Schristos    # Indentation of individual items, where applicable
255*b0d17251Schristos    my $nlindent2 = defined $opts{indent} ? "\n" . ' ' x ($indent + 4) : ' ';
256*b0d17251Schristos    my %subopts = ();
257*b0d17251Schristos
258*b0d17251Schristos    $subopts{delimiters} = 1;
259*b0d17251Schristos    $subopts{indent} = $opts{indent} + 4 if defined $opts{indent};
260*b0d17251Schristos
261*b0d17251Schristos    my $product;      # Finished product, or reference to a function that
262*b0d17251Schristos                      # produces a string, given $_
263*b0d17251Schristos    # The following are only used when $product is a function reference
264*b0d17251Schristos    my $delim_l;      # Left delimiter of structure
265*b0d17251Schristos    my $delim_r;      # Right delimiter of structure
266*b0d17251Schristos    my $separator;    # Item separator
267*b0d17251Schristos    my @items;        # Items to iterate over
268*b0d17251Schristos
269*b0d17251Schristos     if (ref($ref) eq "ARRAY") {
270*b0d17251Schristos         if (scalar @$ref == 0) {
271*b0d17251Schristos             $product = $opts{delimiters} ? '[]' : '';
272*b0d17251Schristos         } else {
273*b0d17251Schristos             $product = sub {
274*b0d17251Schristos                 dump_data(\$_, %subopts)
275*b0d17251Schristos             };
276*b0d17251Schristos             $delim_l = ($opts{delimiters} ? '[' : '').$nlindent2;
277*b0d17251Schristos             $delim_r = $nlindent1.($opts{delimiters} ? ']' : '');
278*b0d17251Schristos             $separator = ",$nlindent2";
279*b0d17251Schristos             @items = @$ref;
280*b0d17251Schristos         }
281*b0d17251Schristos     } elsif (ref($ref) eq "HASH") {
282*b0d17251Schristos         if (scalar keys %$ref == 0) {
283*b0d17251Schristos             $product = $opts{delimiters} ? '{}' : '';
284*b0d17251Schristos         } else {
285*b0d17251Schristos             $product = sub {
286*b0d17251Schristos                 quotify1($_) . " => " . dump_data($ref->{$_}, %subopts);
287*b0d17251Schristos             };
288*b0d17251Schristos             $delim_l = ($opts{delimiters} ? '{' : '').$nlindent2;
289*b0d17251Schristos             $delim_r = $nlindent1.($opts{delimiters} ? '}' : '');
290*b0d17251Schristos             $separator = ",$nlindent2";
291*b0d17251Schristos             @items = sort keys %$ref;
292*b0d17251Schristos         }
293*b0d17251Schristos     } elsif (ref($ref) eq "SCALAR") {
294*b0d17251Schristos         $product = defined $$ref ? quotify1 $$ref : "undef";
295*b0d17251Schristos     } else {
296*b0d17251Schristos         $product = defined $ref ? quotify1 $ref : "undef";
297*b0d17251Schristos     }
298*b0d17251Schristos
299*b0d17251Schristos     if (ref($product) eq "CODE") {
300*b0d17251Schristos         $delim_l . join($separator, map { &$product } @items) . $delim_r;
301*b0d17251Schristos     } else {
302*b0d17251Schristos         $product;
303*b0d17251Schristos     }
304*b0d17251Schristos}
305*b0d17251Schristos
306*b0d17251Schristos=back
307*b0d17251Schristos
308*b0d17251Schristos=cut
309*b0d17251Schristos
310*b0d17251Schristos1;
311