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