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