Initial commit
This commit is contained in:
commit
f4bfb0e367
71 changed files with 10399 additions and 0 deletions
728
Dam/Util.pm
Normal file
728
Dam/Util.pm
Normal file
|
@ -0,0 +1,728 @@
|
|||
=head1 NAME
|
||||
|
||||
Dam::Util
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Global functions for applications created using the Dam framework.
|
||||
|
||||
=head1 FUNCTIONS
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
package Dam::Util;
|
||||
|
||||
use Exporter qw(import);
|
||||
our @EXPORT = qw(
|
||||
TRUE FALSE
|
||||
ARG_OPTIONAL ARG_REQUIRED ARG_DEFAULT
|
||||
_DIVIDER_
|
||||
|
||||
is_true is_empty is_eq is_num
|
||||
numval pctval sumval
|
||||
strval strval_trio strval_join trim
|
||||
one_space escape_quotes
|
||||
array in_array index_in_array occurrences_in_array match_arrays array_uniq
|
||||
fatal warning info
|
||||
check_arguments
|
||||
format_num format_pct format_date_dmy get_today_ymd
|
||||
csv_header csv_line
|
||||
);
|
||||
|
||||
use Date::Calc qw(Today);
|
||||
use Scalar::Util qw(looks_like_number);
|
||||
|
||||
|
||||
|
||||
use constant {
|
||||
TRUE => 1,
|
||||
FALSE => 0,
|
||||
|
||||
ARG_OPTIONAL => 0,
|
||||
ARG_REQUIRED => 1,
|
||||
ARG_DEFAULT => 2,
|
||||
|
||||
_DIVIDER_ => '_DIVIDER_'
|
||||
};
|
||||
|
||||
|
||||
|
||||
=head2 is_true($var)
|
||||
|
||||
Returns C<TRUE> if C<$var> is not C<undef> and has a value other than C<0>, or
|
||||
C<'0'> or the empty string C<''>. Or C<FALSE> otherwise.
|
||||
|
||||
=head3 Arguments:
|
||||
|
||||
- B<$var> (must): Variable to check.
|
||||
|
||||
=cut
|
||||
|
||||
sub is_true {
|
||||
my $var = shift;
|
||||
return defined($var) && $var ne '' && $var ne '0' ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
=head2 is_empty($var)
|
||||
|
||||
Returns C<TRUE> if C<$var> is C<undef> or the empty string C<''>. Or C<FALSE>
|
||||
otherwise.
|
||||
|
||||
=head3 Arguments:
|
||||
|
||||
- B<$var> (must): Variable to check.
|
||||
|
||||
=cut
|
||||
|
||||
sub is_empty {
|
||||
my $var = shift;
|
||||
return !defined($var) || $var eq '' ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
=head2 is_eq($var, $str)
|
||||
|
||||
Returns C<TRUE> if C<$var> is not C<undef>, C<$str> is not C<undef> and they are
|
||||
equals. Or C<FALSE> otherwise.
|
||||
|
||||
=head3 Arguments:
|
||||
|
||||
- B<$var> (must): Variable to check.
|
||||
- B<$str> (must): Value to compare.
|
||||
|
||||
=cut
|
||||
|
||||
sub is_eq {
|
||||
my ($var, $str) = @_;
|
||||
return defined($var) && defined($str) && $var eq $str ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
=head2 is_num($num)
|
||||
|
||||
Returns C<TRUE> if C<$num> is a number, or C<FALSE> otherwise.
|
||||
|
||||
=head3 Arguments:
|
||||
|
||||
- B<$num> (must): Number to check.
|
||||
|
||||
=cut
|
||||
|
||||
sub is_num {
|
||||
return looks_like_number(shift) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
=head2 numval($num)
|
||||
|
||||
Returns C<$num> when C<is_num($num)>. Returns C<0> when C<is_empty($num)>. And
|
||||
C<undef> otherwise.
|
||||
|
||||
=head3 Arguments:
|
||||
|
||||
- B<$num> (must): Number to check.
|
||||
|
||||
=cut
|
||||
|
||||
sub numval {
|
||||
my $num = shift;
|
||||
|
||||
return $num if is_num($num);
|
||||
return 0 if is_empty($num);
|
||||
warning('Invalid number');
|
||||
return undef;
|
||||
}
|
||||
|
||||
|
||||
|
||||
=head2 pctval($numerator, $denominator, $decimals, $byzero)
|
||||
|
||||
Returns (C<$numerator> * 100) / C<$denominator> if C<is_num($numerator)>,
|
||||
C<is_num($denominator)> and C<$denominator> is not C<0>. Otherwise it will
|
||||
return C<undef>.
|
||||
|
||||
=head3 Arguments:
|
||||
|
||||
- B<$numerator> (must): The numerator of the percentage to calculate.
|
||||
- B<$denominator> (must): The denominator of the percentage to calculate.
|
||||
- B<$decimals> (optional): Maximum number of decimal places for the result.
|
||||
- B<$byzero> (optional): If C<is_true($byzero)> it will send a warning message
|
||||
when C<$denominator> is zero.
|
||||
|
||||
=cut
|
||||
|
||||
sub pctval {
|
||||
my ($numerator, $denominator, $decimals, $byzero) = @_;
|
||||
|
||||
$numerator = 0 if is_empty($numerator);
|
||||
if (is_num($numerator) && is_num($denominator)) {
|
||||
warning('Invalid number of decimals') if !is_empty($decimals) && (!is_num($decimals) || $decimals < 0 || !($decimals - int($decimals)));
|
||||
if ($denominator != 0) {
|
||||
my $pctval = $numerator * 100 / $denominator;
|
||||
return is_empty($decimals) ? $pctval : sprintf("%.${decimals}f", $pctval);
|
||||
}
|
||||
warning('Division by zero') if is_true($byzero);
|
||||
return undef;
|
||||
}
|
||||
warning('Invalid numerator') if !is_num($numerator);
|
||||
warning('Invalid denominator') if !is_num($denominator);
|
||||
return undef;
|
||||
}
|
||||
|
||||
|
||||
|
||||
=head2 sumval(@items)
|
||||
|
||||
Sum all the items in C<@items> considering each one as C<numval($item)>.
|
||||
|
||||
=head3 Arguments:
|
||||
|
||||
- B<@items> (must): List of items to add.
|
||||
|
||||
=cut
|
||||
|
||||
sub sumval {
|
||||
my $sum = 0;
|
||||
foreach my $item (@_) {
|
||||
$sum += numval($item);
|
||||
}
|
||||
return $sum;
|
||||
}
|
||||
|
||||
|
||||
|
||||
=head2 strval(@str)
|
||||
|
||||
Returns the concatenation of all strings in C<@str>, considering C<undef> as the
|
||||
empty string C<''>.
|
||||
|
||||
=head3 Arguments:
|
||||
|
||||
- B<@str> (must): Array of strings to concatenate.
|
||||
|
||||
=cut
|
||||
|
||||
sub strval {
|
||||
my $strval = '';
|
||||
foreach my $str (@_) {
|
||||
$strval .= defined($str) ? $str : '';
|
||||
}
|
||||
return $strval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
=head2 strval_trio($str1, $separator, $str2)
|
||||
|
||||
Returns the concatenation of the strings C<$str1>, C<$separator> and C<$str2> if
|
||||
not C<is_empty($str1)> and not C<is_empty($str2)>. Otherwise it returns the
|
||||
string C<$str1> or C<$str2> that is not empty, or the empty string C<''> if
|
||||
both are.
|
||||
|
||||
=head3 Arguments:
|
||||
|
||||
- B<$str1> (optional): First string.
|
||||
- B<$separator> (must): Separation string.
|
||||
- B<$str2> (optional): Second string.
|
||||
|
||||
=cut
|
||||
|
||||
sub strval_trio {
|
||||
my ($str1, $separator, $str2) = @_;
|
||||
|
||||
return strval($str1, $separator, $str2) if !is_empty($str1) && !is_empty($str2);
|
||||
return strval($str1) if is_empty($str2);
|
||||
return strval($str2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
=head2 strval_join($separator, @str)
|
||||
|
||||
Returns the concatenation of the (not empty) strings of C<@str> or the
|
||||
referenced array of strings instead, using the string C<$separator> as the
|
||||
separation between each one.
|
||||
|
||||
=head3 Arguments:
|
||||
|
||||
- B<$separator> (must): Separation string.
|
||||
- B<@str> (must): Strings or reference to the array of strings to concatenate.
|
||||
|
||||
=cut
|
||||
|
||||
sub strval_join {
|
||||
my ($separator, @str) = @_;
|
||||
|
||||
return '' if !@str;
|
||||
@str = @{$str[0]} if scalar(@str) == 1 && ref($str[0]) eq 'ARRAY';
|
||||
$separator = '' if is_empty($separator);
|
||||
return join($separator, grep { !is_empty($_) } @str);
|
||||
}
|
||||
|
||||
|
||||
|
||||
=head2 trim($str)
|
||||
|
||||
Returns a string whose leading and trailing spaces have been removed from
|
||||
C<$str>. Or the empty string C<''> if C<is_empty($str)>.
|
||||
|
||||
=head3 Arguments:
|
||||
|
||||
- B<$str> (must): String to process.
|
||||
|
||||
=cut
|
||||
|
||||
sub trim {
|
||||
my $str = shift;
|
||||
|
||||
return '' if is_empty($str);
|
||||
$str =~ s/^\s+|\s+$//g;
|
||||
return $str;
|
||||
}
|
||||
|
||||
|
||||
|
||||
=head2 one_space($str)
|
||||
|
||||
Returns a string that converts the sequences of two or more consecutive spaces
|
||||
of C<$str> into a single space. Or the empty string C<''> if
|
||||
C<is_empty($str)>.
|
||||
|
||||
=head3 Arguments:
|
||||
|
||||
- B<$str> (must): String to process.
|
||||
|
||||
=cut
|
||||
|
||||
sub one_space {
|
||||
my $str = shift;
|
||||
|
||||
return '' if is_empty($str);
|
||||
$str =~ s/\s+/ /g;
|
||||
return $str;
|
||||
}
|
||||
|
||||
|
||||
|
||||
=head2 escape_quotes($str)
|
||||
|
||||
Returns the same string C<$str> by putting an escape character in front of each
|
||||
escape character, single quote or double quote. Or the empty string C<''> if
|
||||
C<is_empty($str)>.
|
||||
|
||||
=head3 Arguments:
|
||||
|
||||
- B<$str> (must): String to process.
|
||||
|
||||
=cut
|
||||
|
||||
sub escape_quotes {
|
||||
my $str = shift;
|
||||
|
||||
return '' if is_empty($str);
|
||||
$str =~ s/('|"|\\)/\\$1/g;
|
||||
return $str;
|
||||
}
|
||||
|
||||
|
||||
|
||||
=head2 array($var)
|
||||
|
||||
If C<$var> is a reference to an array then it returns the array. If it is a
|
||||
variable then it returns an array with that element. It returns an empty array
|
||||
otherwise.
|
||||
|
||||
=head3 Arguments:
|
||||
|
||||
- B<$var> (must): Variable (or reference to the array) to check.
|
||||
|
||||
=cut
|
||||
|
||||
sub array {
|
||||
my $var = shift;
|
||||
|
||||
return () if !defined($var);
|
||||
return ref($var) eq 'ARRAY' ? @{$var} : ( $var );
|
||||
}
|
||||
|
||||
|
||||
|
||||
=head2 in_array($element, @array)
|
||||
|
||||
Returns C<TRUE> if C<$element> is in array C<@array> or in the referenced array
|
||||
instead. Or C<FALSE> otherwise.
|
||||
|
||||
=head3 Arguments:
|
||||
|
||||
- B<$element> (must): Element to search.
|
||||
- B<@array> (must): Array (or reference to the array) in which the element is
|
||||
searched.
|
||||
|
||||
=cut
|
||||
|
||||
sub in_array {
|
||||
my ($element, @array) = @_;
|
||||
|
||||
return FALSE if !defined($element) || !@array;
|
||||
@array = @{$array[0]} if scalar(@array) == 1 && ref($array[0]) eq 'ARRAY';
|
||||
|
||||
my %hash_array = map { $_ => 1 } @array;
|
||||
return defined($hash_array{$element}) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
=head2 index_in_array($element, @array)
|
||||
|
||||
Returns the position where C<$element> is in array C<@array> or in the array
|
||||
referenced instead, with C<0> being the first position in the array. Or it
|
||||
returns C<-1> if there are no arguments or the element is not found.
|
||||
|
||||
=head3 Arguments:
|
||||
|
||||
- B<$element> (must): Element to search.
|
||||
- B<@array> (must): Array (or reference to the array) in which the element is
|
||||
searched.
|
||||
|
||||
=cut
|
||||
|
||||
sub index_in_array {
|
||||
my ($element, @array) = @_;
|
||||
|
||||
return -1 if !defined($element) || !@array;
|
||||
@array = @{$array[0]} if scalar(@array) == 1 && ref($array[0]) eq 'ARRAY';
|
||||
|
||||
my $index = 0;
|
||||
foreach my $current (@array) {
|
||||
return $index if $current eq $element;
|
||||
$index++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
=head2 occurrences_in_array($element, @array)
|
||||
|
||||
Returns the number of occurrences of C<$element> in array C<@array> or in the
|
||||
referenced array instead. Or it returns C<-1> if there are no arguments.
|
||||
|
||||
=head3 Arguments:
|
||||
|
||||
- B<$element> (must): Element to search.
|
||||
- B<@array> (must): Array (or reference to the array) in which the element is
|
||||
searched.
|
||||
|
||||
=cut
|
||||
|
||||
sub occurrences_in_array {
|
||||
my ($element, @array) = @_;
|
||||
|
||||
return -1 if !defined($element) || !@array;
|
||||
@array = @{$array[0]} if scalar(@array) == 1 && ref($array[0]) eq 'ARRAY';
|
||||
|
||||
return grep { $_ eq $element } @array;
|
||||
}
|
||||
|
||||
|
||||
|
||||
=head2 match_arrays($array_ref1, $array_ref2)
|
||||
|
||||
Returns C<TRUE> if arrays C<@$array_ref1> and C<@$array_ref2> have one or more
|
||||
equal elements. Or C<FALSE> otherwise.
|
||||
|
||||
=head3 Arguments:
|
||||
|
||||
- B<$array_ref1> (required): Reference to the first array.
|
||||
- B<$array_ref2> (required): Reference to the second array.
|
||||
|
||||
=cut
|
||||
|
||||
sub match_arrays {
|
||||
my ($array_ref1, $array_ref2) = @_;
|
||||
|
||||
foreach my $match (@{$array_ref1}) {
|
||||
return TRUE if in_array($match, $array_ref2);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
=head2 array_uniq(@array)
|
||||
|
||||
Returns a new array without duplicate elements of array C<@array>.
|
||||
|
||||
=head3 Arguments:
|
||||
|
||||
- B<@array> (required): Array to process.
|
||||
|
||||
=cut
|
||||
|
||||
sub array_uniq {
|
||||
my %seen;
|
||||
grep !is_empty($_) && !$seen{$_}++, @_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
=head2 fatal(@message)
|
||||
|
||||
Sends error message C<strval(@message)> to STDERR and abort program execution.
|
||||
|
||||
=head3 Arguments:
|
||||
|
||||
- B<@message> (optional): Error message consisting of one or more strings.
|
||||
|
||||
=cut
|
||||
|
||||
sub fatal {
|
||||
my $message = strval(@_);
|
||||
|
||||
$message .= '. ' if !is_empty($message);
|
||||
$message .= 'Fatal error';
|
||||
|
||||
my @call1 = caller(2);
|
||||
my @call2 = caller(1);
|
||||
|
||||
die strval($message, ' at ', $call1[1], ' line ', $call1[2], '. See ', $call1[3], ' at ', $call2[1], ' line ', $call2[2], '. Info');
|
||||
}
|
||||
|
||||
|
||||
|
||||
=head2 warning(@message)
|
||||
|
||||
Sends error message C<strval(@message)> to STDERR but does not abort program
|
||||
execution.
|
||||
|
||||
=head3 Arguments:
|
||||
|
||||
- B<@message> (optional): Warning message consisting of one or more strings.
|
||||
|
||||
=cut
|
||||
|
||||
sub warning {
|
||||
my $message = strval(@_);
|
||||
|
||||
$message .= '. ' if !is_empty($message);
|
||||
$message .= 'Warning';
|
||||
|
||||
my @call1 = caller(2);
|
||||
my @call2 = caller(1);
|
||||
|
||||
print STDERR strval($message, ' at ', $call1[1], ' line ', $call1[2], '. See ', $call1[3], ' at ', $call2[1], ' line ', $call2[2], "\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
=head2 info(@message)
|
||||
|
||||
Sends information message C<strval(@message)> to STDERR.
|
||||
|
||||
=head3 Arguments:
|
||||
|
||||
- B<@message> (optional): Information message consisting of one or more
|
||||
strings.
|
||||
|
||||
=cut
|
||||
|
||||
sub info {
|
||||
print STDERR strval(@_, "\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
sub check_arguments {
|
||||
my ($arg_ref, %ARGUMENTS) = @_;
|
||||
|
||||
my @valid_args = keys(%ARGUMENTS);
|
||||
|
||||
foreach my $arg (keys(%$arg_ref)) {
|
||||
fatal('Invalid "', $arg, '" argument') if !in_array($arg, \@valid_args);
|
||||
}
|
||||
|
||||
foreach my $arg (@valid_args) {
|
||||
my @values = array($ARGUMENTS{$arg});
|
||||
if (@values) {
|
||||
my $required = shift(@values);
|
||||
my $ref_value_0 = ref($values[0]);
|
||||
if ($required == ARG_DEFAULT) {
|
||||
if (is_empty($$arg_ref{$arg}) && @values) {
|
||||
if (is_empty($ref_value_0)) {
|
||||
$$arg_ref{$arg} = $values[0];
|
||||
}
|
||||
elsif (is_eq($ref_value_0, 'ARRAY')) {
|
||||
$$arg_ref{$arg} = (@{$values[0]});
|
||||
}
|
||||
elsif (is_eq($ref_value_0, 'HASH')) {
|
||||
$$arg_ref{$arg} = {%{$values[0]}};
|
||||
}
|
||||
else {
|
||||
$$arg_ref{$arg} = ${$values[0]};
|
||||
}
|
||||
}
|
||||
fatal('Default value for "', $arg, '" is required') if !defined($$arg_ref{$arg});
|
||||
push(@values, $$arg_ref{$arg}) if !in_array($$arg_ref{$arg}, \@values);
|
||||
}
|
||||
elsif ($required == ARG_REQUIRED) {
|
||||
fatal('Value for "', $arg, '" is required') if !defined($$arg_ref{$arg});
|
||||
}
|
||||
elsif ($required != ARG_OPTIONAL) {
|
||||
fatal('Type of argument not recognized');
|
||||
}
|
||||
fatal('Invalid "', $arg, '" value "', $$arg_ref{$arg}, '"') if @values && !is_empty($$arg_ref{$arg}) && !in_array($$arg_ref{$arg}, \@values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
sub format_num {
|
||||
my ($number, %arg) = @_;
|
||||
check_arguments(\%arg,
|
||||
FORMAT => [ ARG_OPTIONAL ],
|
||||
ZERO => [ ARG_DEFAULT, FALSE, TRUE ],
|
||||
DECIMALS => [ ARG_DEFAULT, 0 ],
|
||||
DEC_POINT => [ ARG_DEFAULT, ',' ],
|
||||
THOUSANDS_SEP => [ ARG_DEFAULT, '.', 'none' ]
|
||||
);
|
||||
|
||||
$number = trim($number);
|
||||
return '' if is_empty($number);
|
||||
return $number if in_array($number, '∞', '~');
|
||||
return '' if !is_num($number);
|
||||
$number = numval($number);
|
||||
|
||||
return '' if $number == 0 && !is_true($arg{ZERO});
|
||||
|
||||
return sprintf($arg{FORMAT}, $number) if !is_empty($arg{FORMAT});
|
||||
|
||||
$number = sprintf("%.$arg{DECIMALS}f", $number);
|
||||
eval "\$number =~ tr/./$arg{DEC_POINT}/";
|
||||
eval "\$number =~ s/(\\d)(?=(\\d{3})+(\\D|\$))/\$1\$arg{THOUSANDS_SEP}/g" if !is_eq($arg{THOUSANDS_SEP}, 'none');
|
||||
return $number;
|
||||
}
|
||||
|
||||
|
||||
|
||||
sub format_pct {
|
||||
my ($number, %arg) = @_;
|
||||
check_arguments(\%arg,
|
||||
ZERO => [ ARG_DEFAULT, FALSE, TRUE ],
|
||||
DECIMALS => [ ARG_DEFAULT, 2 ]
|
||||
);
|
||||
|
||||
$number = format_num($number, %arg);
|
||||
return is_empty($number) ? '' : strval($number, '%');
|
||||
}
|
||||
|
||||
|
||||
|
||||
sub format_date_dmy {
|
||||
my $date = shift;
|
||||
|
||||
return '' if is_empty($date);
|
||||
|
||||
my @date = split('-', $date);
|
||||
return '' if is_empty($date[2]) || is_empty($date[1]) || is_empty($date[0]);
|
||||
return sprintf("%02d/%02d/%04d", $date[2], $date[1], $date[0]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
=head2 get_today_ymd()
|
||||
|
||||
Devuelve la fecha actual en el formato AAAA-MM-DD.
|
||||
|
||||
=cut
|
||||
|
||||
sub get_today_ymd {
|
||||
my ($y, $m, $d) = Today();
|
||||
return ($y, sprintf("%02d", $m), sprintf("%02d", $d));
|
||||
}
|
||||
|
||||
|
||||
|
||||
sub csv_header {
|
||||
my %arg = @_;
|
||||
check_arguments(\%arg,
|
||||
SEPARATOR => [ ARG_DEFAULT, ';' ],
|
||||
REPLACE => [ ARG_DEFAULT, ',' ],
|
||||
HEADER => [ ARG_OPTIONAL ]
|
||||
);
|
||||
|
||||
return __csv_line($arg{SEPARATOR}, $arg{REPLACE}, $arg{HEADER});
|
||||
}
|
||||
|
||||
|
||||
|
||||
sub csv_line {
|
||||
my %arg = @_;
|
||||
check_arguments(\%arg,
|
||||
SEPARATOR => [ ARG_DEFAULT, ';' ],
|
||||
REPLACE => [ ARG_DEFAULT, ',' ],
|
||||
DATA => [ ARG_OPTIONAL ]
|
||||
);
|
||||
|
||||
return __csv_line($arg{SEPARATOR}, $arg{REPLACE}, $arg{DATA});
|
||||
}
|
||||
|
||||
|
||||
|
||||
sub __csv_line {
|
||||
my ($separator, $replace, $data) = @_;
|
||||
|
||||
my $line = '';
|
||||
foreach my $field (@$data) {
|
||||
if (!is_empty($field)) {
|
||||
$field =~ s/$separator/$replace/g;
|
||||
$line .= trim($field);
|
||||
}
|
||||
$line .= $separator;
|
||||
}
|
||||
chop($line);
|
||||
return strval($line, "\r\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
1;
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Manuel Cillero C<< <manuel@cillero.es> >>
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2004-2020 Manuel Cillero. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
=cut
|
Reference in a new issue