123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329 |
- #! /usr/bin/env perl
- # Copyright 2018-2023 The OpenSSL Project Authors. All Rights Reserved.
- #
- # Licensed under the Apache License 2.0 (the "License"). You may not use
- # this file except in compliance with the License. You can obtain a copy
- # in the file LICENSE in the source distribution or at
- # https://www.openssl.org/source/license.html
- package OpenSSL::Util;
- use strict;
- use warnings;
- use Carp;
- use Exporter;
- use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
- $VERSION = "0.1";
- @ISA = qw(Exporter);
- @EXPORT = qw(cmp_versions quotify1 quotify_l fixup_cmd_elements fixup_cmd
- dump_data);
- @EXPORT_OK = qw();
- =head1 NAME
- OpenSSL::Util - small OpenSSL utilities
- =head1 SYNOPSIS
- use OpenSSL::Util;
- $versiondiff = cmp_versions('1.0.2k', '3.0.1');
- # $versiondiff should be -1
- $versiondiff = cmp_versions('1.1.0', '1.0.2a');
- # $versiondiff should be 1
- $versiondiff = cmp_versions('1.1.1', '1.1.1');
- # $versiondiff should be 0
- =head1 DESCRIPTION
- =over
- =item B<cmp_versions "VERSION1", "VERSION2">
- Compares VERSION1 with VERSION2, paying attention to OpenSSL versioning.
- Returns 1 if VERSION1 is greater than VERSION2, 0 if they are equal, and
- -1 if VERSION1 is less than VERSION2.
- =back
- =cut
- # Until we're rid of everything with the old version scheme,
- # we need to be able to handle older style x.y.zl versions.
- # In terms of comparison, the x.y.zl and the x.y.z schemes
- # are compatible... mostly because the latter starts at a
- # new major release with a new major number.
- sub _ossl_versionsplit {
- my $textversion = shift;
- return $textversion if $textversion eq '*';
- my ($major,$minor,$edit,$letter) =
- $textversion =~ /^(\d+)\.(\d+)\.(\d+)([a-z]{0,2})$/;
- return ($major,$minor,$edit,$letter);
- }
- sub cmp_versions {
- my @a_split = _ossl_versionsplit(shift);
- my @b_split = _ossl_versionsplit(shift);
- my $verdict = 0;
- while (@a_split) {
- # The last part is a letter sequence (or a '*')
- if (scalar @a_split == 1) {
- $verdict = $a_split[0] cmp $b_split[0];
- } else {
- $verdict = $a_split[0] <=> $b_split[0];
- }
- shift @a_split;
- shift @b_split;
- last unless $verdict == 0;
- }
- return $verdict;
- }
- # It might be practical to quotify some strings and have them protected
- # from possible harm. These functions primarily quote things that might
- # be interpreted wrongly by a perl eval.
- =over 4
- =item quotify1 STRING
- This adds quotes (") around the given string, and escapes any $, @, \,
- " and ' by prepending a \ to them.
- =back
- =cut
- sub quotify1 {
- my $s = shift @_;
- $s =~ s/([\$\@\\"'])/\\$1/g;
- '"'.$s.'"';
- }
- =over 4
- =item quotify_l LIST
- For each defined element in LIST (i.e. elements that aren't undef), have
- it quotified with 'quotify1'.
- Undefined elements are ignored.
- =cut
- sub quotify_l {
- map {
- if (!defined($_)) {
- ();
- } else {
- quotify1($_);
- }
- } @_;
- }
- =over 4
- =item fixup_cmd_elements LIST
- Fixes up the command line elements given by LIST in a platform specific
- manner.
- The result of this function is a copy of LIST with strings where quotes and
- escapes have been injected as necessary depending on the content of each
- LIST string.
- This can also be used to put quotes around the executable of a command.
- I<This must never ever be done on VMS.>
- =back
- =cut
- sub fixup_cmd_elements {
- # A formatter for the command arguments, defaulting to the Unix setup
- my $arg_formatter =
- sub { $_ = shift;
- ($_ eq '' || /\s|[\{\}\\\$\[\]\*\?\|\&:;<>]/) ? "'$_'" : $_ };
- if ( $^O eq "VMS") { # VMS setup
- $arg_formatter = sub {
- $_ = shift;
- if ($_ eq '' || /\s|[!"[:upper:]]/) {
- s/"/""/g;
- '"'.$_.'"';
- } else {
- $_;
- }
- };
- } elsif ( $^O eq "MSWin32") { # MSWin setup
- $arg_formatter = sub {
- $_ = shift;
- if ($_ eq '' || /\s|["\|\&\*\;<>]/) {
- s/(["\\])/\\$1/g;
- '"'.$_.'"';
- } else {
- $_;
- }
- };
- }
- return ( map { $arg_formatter->($_) } @_ );
- }
- =over 4
- =item fixup_cmd LIST
- This is a sibling of fixup_cmd_elements() that expects the LIST to be a
- complete command line. It does the same thing as fixup_cmd_elements(),
- expect that it treats the first LIST element specially on VMS.
- =back
- =cut
- sub fixup_cmd {
- return fixup_cmd_elements(@_) unless $^O eq 'VMS';
- # The rest is VMS specific
- my $cmd = shift;
- # Prefix to be applied as needed. Essentially, we need to determine
- # if the command is an executable file (something.EXE), and invoke it
- # with the MCR command in that case. MCR is an old PDP-11 command that
- # stuck around.
- my @prefix;
- if ($cmd =~ m|^\@|) {
- # The command is an invocation of a command procedure (also known as
- # "script"), no modification needed.
- @prefix = ();
- } elsif ($cmd =~ m|^MCR$|) {
- # The command is MCR, so there's nothing much to do apart from
- # making sure that the file name following it isn't treated with
- # fixup_cmd_elements(), 'cause MCR doesn't like strings.
- @prefix = ( $cmd );
- $cmd = shift;
- } else {
- # All that's left now is to check whether the command is an executable
- # file, and if it's not, simply assume that it is a DCL command.
- # Make sure we have a proper file name, i.e. add the default
- # extension '.exe' if there isn't one already.
- my $executable = ($cmd =~ m|.[a-z0-9\$]*$|) ? $cmd : $cmd . '.exe';
- if (-e $executable) {
- # It seems to be an executable, so we make sure to prefix it
- # with MCR, for proper invocation. We also make sure that
- # there's a directory specification, or otherwise, MCR will
- # assume that the executable is in SYS$SYSTEM:
- @prefix = ( 'MCR' );
- $cmd = '[]' . $cmd unless $cmd =~ /^(?:[\$a-z0-9_]+:)?[<\[]/i;
- } else {
- # If it isn't an executable, then we assume that it's a DCL
- # command, and do no further processing, apart from argument
- # fixup.
- @prefix = ();
- }
- }
- return ( @prefix, $cmd, fixup_cmd_elements(@_) );
- }
- =item dump_data REF, OPTS
- Dump the data from REF into a string that can be evaluated into the same
- data by Perl.
- OPTS is the rest of the arguments, expected to be pairs formed with C<< => >>.
- The following OPTS keywords are understood:
- =over 4
- =item B<delimiters =E<gt> 0 | 1>
- Include the outer delimiter of the REF type in the resulting string if C<1>,
- otherwise not.
- =item B<indent =E<gt> num>
- The indentation of the caller, i.e. an initial value. If not given, there
- will be no indentation at all, and the string will only be one line.
- =back
- =cut
- sub dump_data {
- my $ref = shift;
- # Available options:
- # indent => callers indentation ( undef for no indentation,
- # an integer otherwise )
- # delimiters => 1 if outer delimiters should be added
- my %opts = @_;
- my $indent = $opts{indent} // 1;
- # Indentation of the whole structure, where applicable
- my $nlindent1 = defined $opts{indent} ? "\n" . ' ' x $indent : ' ';
- # Indentation of individual items, where applicable
- my $nlindent2 = defined $opts{indent} ? "\n" . ' ' x ($indent + 4) : ' ';
- my %subopts = ();
- $subopts{delimiters} = 1;
- $subopts{indent} = $opts{indent} + 4 if defined $opts{indent};
- my $product; # Finished product, or reference to a function that
- # produces a string, given $_
- # The following are only used when $product is a function reference
- my $delim_l; # Left delimiter of structure
- my $delim_r; # Right delimiter of structure
- my $separator; # Item separator
- my @items; # Items to iterate over
- if (ref($ref) eq "ARRAY") {
- if (scalar @$ref == 0) {
- $product = $opts{delimiters} ? '[]' : '';
- } else {
- $product = sub {
- dump_data(\$_, %subopts)
- };
- $delim_l = ($opts{delimiters} ? '[' : '').$nlindent2;
- $delim_r = $nlindent1.($opts{delimiters} ? ']' : '');
- $separator = ",$nlindent2";
- @items = @$ref;
- }
- } elsif (ref($ref) eq "HASH") {
- if (scalar keys %$ref == 0) {
- $product = $opts{delimiters} ? '{}' : '';
- } else {
- $product = sub {
- quotify1($_) . " => " . dump_data($ref->{$_}, %subopts);
- };
- $delim_l = ($opts{delimiters} ? '{' : '').$nlindent2;
- $delim_r = $nlindent1.($opts{delimiters} ? '}' : '');
- $separator = ",$nlindent2";
- @items = sort keys %$ref;
- }
- } elsif (ref($ref) eq "SCALAR") {
- $product = defined $$ref ? quotify1 $$ref : "undef";
- } else {
- $product = defined $ref ? quotify1 $ref : "undef";
- }
- if (ref($product) eq "CODE") {
- $delim_l . join($separator, map { &$product } @items) . $delim_r;
- } else {
- $product;
- }
- }
- =back
- =cut
- 1;
|