sub
HAVE_UTF8 () { $] >= 5.007003 }
BEGIN {
if
( HAVE_UTF8 ) {
eval
"require utf8;"
;
die
"Failed to load UTF-8 support"
if
$@;
}
require
5.004;
$YAML::Tiny::VERSION
=
'1.51'
;
@YAML::Tiny::ISA
=
qw{ Exporter }
;
@YAML::Tiny::EXPORT
=
qw{ Load Dump }
;
@YAML::Tiny::EXPORT_OK
=
qw{ LoadFile DumpFile freeze thaw }
;
$YAML::Tiny::errstr
=
''
;
}
my
@UNPRINTABLE
=
qw(
z x01 x02 x03 x04 x05 x06 a
x08 t n v f r x0e x0f
x10 x11 x12 x13 x14 x15 x16 x17
x18 x19 x1a e x1c x1d x1e x1f
)
;
my
%UNESCAPES
= (
z
=>
"\x00"
,
a
=>
"\x07"
,
t
=>
"\x09"
,
n
=>
"\x0a"
,
v
=>
"\x0b"
,
f
=>
"\x0c"
,
r
=>
"\x0d"
,
e
=>
"\x1b"
,
'\\'
=>
'\\'
,
);
my
%QUOTE
=
map
{
$_
=> 1 }
qw{
null Null NULL
y Y yes Yes YES n N no No NO
true True TRUE false False FALSE
on On ON off Off OFF
}
;
sub
new {
my
$class
=
shift
;
bless
[
@_
],
$class
;
}
sub
read
{
my
$class
=
ref
$_
[0] ?
ref
shift
:
shift
;
my
$file
=
shift
or
return
$class
->_error(
'You did not specify a file name'
);
return
$class
->_error(
"File '$file' does not exist"
)
unless
-e
$file
;
return
$class
->_error(
"'$file' is a directory, not a file"
)
unless
-f _;
return
$class
->_error(
"Insufficient permissions to read '$file'"
)
unless
-r _;
local
$/ =
undef
;
local
*CFG
;
unless
(
open
(CFG,
$file
) ) {
return
$class
->_error(
"Failed to open file '$file': $!"
);
}
my
$contents
= <CFG>;
unless
(
close
(CFG) ) {
return
$class
->_error(
"Failed to close file '$file': $!"
);
}
$class
->read_string(
$contents
);
}
sub
read_string {
my
$class
=
ref
$_
[0] ?
ref
shift
:
shift
;
my
$self
=
bless
[],
$class
;
my
$string
=
$_
[0];
eval
{
unless
(
defined
$string
) {
die
\
"Did not provide a string to load"
;
}
if
(
$string
=~ /^(?:\376\377|\377\376|\377\376\0\0|\0\0\376\377)/ ) {
die
\
"Stream has a non UTF-8 BOM"
;
}
else
{
$string
=~ s/^\357\273\277//;
}
utf8::decode(
$string
)
if
HAVE_UTF8;
return
$self
unless
length
$string
;
unless
(
$string
=~ /[\012\015]+\z/ ) {
die
\
"Stream does not end with newline character"
;
}
my
@lines
=
grep
{ ! /^\s*(?:\
split
/(?:\015{1,2}\012|\015|\012)/,
$string
;
@lines
and
$lines
[0] =~ /^\
%YAML
[: ][\d\.]+.*\z/ and
shift
@lines
;
while
(
@lines
) {
if
(
$lines
[0] =~ /^---\s*(?:(.+)\s*)?\z/ ) {
shift
@lines
;
if
(
defined
$1 and $1 !~ /^(?:\
push
@$self
,
$self
->_read_scalar(
"$1"
, [
undef
], \
@lines
);
next
;
}
}
if
( !
@lines
or
$lines
[0] =~ /^(?:---|\.\.\.)/ ) {
push
@$self
,
undef
;
while
(
@lines
and
$lines
[0] !~ /^---/ ) {
shift
@lines
;
}
}
elsif
(
$lines
[0] =~ /^\s*\-/ ) {
my
$document
= [ ];
push
@$self
,
$document
;
$self
->_read_array(
$document
, [ 0 ], \
@lines
);
}
elsif
(
$lines
[0] =~ /^(\s*)\S/ ) {
my
$document
= { };
push
@$self
,
$document
;
$self
->_read_hash(
$document
, [
length
($1) ], \
@lines
);
}
else
{
die
\
"YAML::Tiny failed to classify the line '$lines[0]'"
;
}
}
};
if
(
ref
$@ eq
'SCALAR'
) {
return
$self
->_error(${$@});
}
elsif
( $@ ) {
Carp::croak($@);
}
return
$self
;
}
sub
_read_scalar {
my
(
$self
,
$string
,
$indent
,
$lines
) =
@_
;
$string
=~ s/\s*\z//;
return
undef
if
$string
eq
'~'
;
if
(
$string
=~ /^\'(.*?)\'(?:\s+\
return
''
unless
defined
$1;
$string
= $1;
$string
=~ s/\'\'/\'/g;
return
$string
;
}
if
(
$string
=~ /^\
"([^\\"
]*(?:\\.[^\\"]*)*)\"(?:\s+\
$string
= $1;
$string
=~ s/\\
"/"
/g;
$string
=~ s/\\([never\\fartz]|x([0-9a-fA-F]{2}))/(
length
($1)>1)?
pack
(
"H2"
,$2):
$UNESCAPES
{$1}/gex;
return
$string
;
}
if
(
$string
=~ /^[\'\"!&]/ ) {
die
\
"YAML::Tiny does not support a feature in line '$string'"
;
}
return
{}
if
$string
=~ /^{}(?:\s+\
return
[]
if
$string
=~ /^\[\](?:\s+\
if
(
$string
!~ /^[>|]/ ) {
if
(
$string
=~ /^(?:-(?:\s|$)|[\@\%\`])/
or
$string
=~ /:(?:\s|$)/
) {
die
\
"YAML::Tiny found illegal characters in plain scalar: '$string'"
;
}
$string
=~ s/\s+
return
$string
;
}
die
\
"YAML::Tiny failed to find multi-line scalar content"
unless
@$lines
;
$lines
->[0] =~ /^(\s*)/;
$indent
->[-1] =
length
(
"$1"
);
if
(
defined
$indent
->[-2] and
$indent
->[-1] <=
$indent
->[-2] ) {
die
\
"YAML::Tiny found bad indenting in line '$lines->[0]'"
;
}
my
@multiline
= ();
while
(
@$lines
) {
$lines
->[0] =~ /^(\s*)/;
last
unless
length
($1) >=
$indent
->[-1];
push
@multiline
,
substr
(
shift
(
@$lines
),
length
($1));
}
my
$j
= (
substr
(
$string
, 0, 1) eq
'>'
) ?
' '
:
"\n"
;
my
$t
= (
substr
(
$string
, 1, 1) eq
'-'
) ?
''
:
"\n"
;
return
join
(
$j
,
@multiline
) .
$t
;
}
sub
_read_array {
my
(
$self
,
$array
,
$indent
,
$lines
) =
@_
;
while
(
@$lines
) {
if
(
$lines
->[0] =~ /^(?:---|\.\.\.)/ ) {
while
(
@$lines
and
$lines
->[0] !~ /^---/ ) {
shift
@$lines
;
}
return
1;
}
$lines
->[0] =~ /^(\s*)/;
if
(
length
($1) <
$indent
->[-1] ) {
return
1;
}
elsif
(
length
($1) >
$indent
->[-1] ) {
die
\
"YAML::Tiny found bad indenting in line '$lines->[0]'"
;
}
if
(
$lines
->[0] =~ /^(\s*\-\s+)[^\'\"]\S*\s*:(?:\s+|$)/ ) {
my
$indent2
=
length
(
"$1"
);
$lines
->[0] =~ s/-/ /;
push
@$array
, { };
$self
->_read_hash(
$array
->[-1], [
@$indent
,
$indent2
],
$lines
);
}
elsif
(
$lines
->[0] =~ /^\s*\-(\s*)(.+?)\s*\z/ ) {
shift
@$lines
;
push
@$array
,
$self
->_read_scalar(
"$2"
, [
@$indent
,
undef
],
$lines
);
}
elsif
(
$lines
->[0] =~ /^\s*\-\s*\z/ ) {
shift
@$lines
;
unless
(
@$lines
) {
push
@$array
,
undef
;
return
1;
}
if
(
$lines
->[0] =~ /^(\s*)\-/ ) {
my
$indent2
=
length
(
"$1"
);
if
(
$indent
->[-1] ==
$indent2
) {
push
@$array
,
undef
;
}
else
{
push
@$array
, [ ];
$self
->_read_array(
$array
->[-1], [
@$indent
,
$indent2
],
$lines
);
}
}
elsif
(
$lines
->[0] =~ /^(\s*)\S/ ) {
push
@$array
, { };
$self
->_read_hash(
$array
->[-1], [
@$indent
,
length
(
"$1"
) ],
$lines
);
}
else
{
die
\
"YAML::Tiny failed to classify line '$lines->[0]'"
;
}
}
elsif
(
defined
$indent
->[-2] and
$indent
->[-1] ==
$indent
->[-2] ) {
return
1;
}
else
{
die
\
"YAML::Tiny failed to classify line '$lines->[0]'"
;
}
}
return
1;
}
sub
_read_hash {
my
(
$self
,
$hash
,
$indent
,
$lines
) =
@_
;
while
(
@$lines
) {
if
(
$lines
->[0] =~ /^(?:---|\.\.\.)/ ) {
while
(
@$lines
and
$lines
->[0] !~ /^---/ ) {
shift
@$lines
;
}
return
1;
}
$lines
->[0] =~ /^(\s*)/;
if
(
length
($1) <
$indent
->[-1] ) {
return
1;
}
elsif
(
length
($1) >
$indent
->[-1] ) {
die
\
"YAML::Tiny found bad indenting in line '$lines->[0]'"
;
}
unless
(
$lines
->[0] =~ s/^\s*([^\'\" ][^\n]*?)\s*:(\s+(?:\
if
(
$lines
->[0] =~ /^\s*[?\'\"]/ ) {
die
\
"YAML::Tiny does not support a feature in line '$lines->[0]'"
;
}
die
\
"YAML::Tiny failed to classify line '$lines->[0]'"
;
}
my
$key
= $1;
if
(
length
$lines
->[0] ) {
$hash
->{
$key
} =
$self
->_read_scalar(
shift
(
@$lines
), [
@$indent
,
undef
],
$lines
);
}
else
{
shift
@$lines
;
unless
(
@$lines
) {
$hash
->{
$key
} =
undef
;
return
1;
}
if
(
$lines
->[0] =~ /^(\s*)-/ ) {
$hash
->{
$key
} = [];
$self
->_read_array(
$hash
->{
$key
}, [
@$indent
,
length
($1) ],
$lines
);
}
elsif
(
$lines
->[0] =~ /^(\s*)./ ) {
my
$indent2
=
length
(
"$1"
);
if
(
$indent
->[-1] >=
$indent2
) {
$hash
->{
$key
} =
undef
;
}
else
{
$hash
->{
$key
} = {};
$self
->_read_hash(
$hash
->{
$key
}, [
@$indent
,
length
($1) ],
$lines
);
}
}
}
}
return
1;
}
sub
write
{
my
$self
=
shift
;
my
$file
=
shift
or
return
$self
->_error(
'No file name provided'
);
open
( CFG,
'>'
.
$file
) or
return
$self
->_error(
"Failed to open file '$file' for writing: $!"
);
print
CFG
$self
->write_string;
close
CFG;
return
1;
}
sub
write_string {
my
$self
=
shift
;
return
''
unless
@$self
;
my
$indent
= 0;
my
@lines
= ();
foreach
my
$cursor
(
@$self
) {
push
@lines
,
'---'
;
if
( !
defined
$cursor
) {
}
elsif
( !
ref
$cursor
) {
$lines
[-1] .=
' '
.
$self
->_write_scalar(
$cursor
,
$indent
);
}
elsif
(
ref
$cursor
eq
'ARRAY'
) {
unless
(
@$cursor
) {
$lines
[-1] .=
' []'
;
next
;
}
push
@lines
,
$self
->_write_array(
$cursor
,
$indent
, {} );
}
elsif
(
ref
$cursor
eq
'HASH'
) {
unless
(
%$cursor
) {
$lines
[-1] .=
' {}'
;
next
;
}
push
@lines
,
$self
->_write_hash(
$cursor
,
$indent
, {} );
}
else
{
Carp::croak(
"Cannot serialize "
.
ref
(
$cursor
));
}
}
join
''
,
map
{
"$_\n"
}
@lines
;
}
sub
_write_scalar {
my
$string
=
$_
[1];
return
'~'
unless
defined
$string
;
return
"''"
unless
length
$string
;
if
(
$string
=~ /[\x00-\x08\x0b-\x0d\x0e-\x1f\"\'\n]/ ) {
$string
=~ s/\\/\\\\/g;
$string
=~ s/
"/\\"
/g;
$string
=~ s/\n/\\n/g;
$string
=~ s/([\x00-\x1f])/\\
$UNPRINTABLE
[
ord
($1)]/g;
return
qq|"$string"|
;
}
if
(
$string
=~ /(?:^\W|\s|:\z)/ or
$QUOTE
{
$string
} ) {
return
"'$string'"
;
}
return
$string
;
}
sub
_write_array {
my
(
$self
,
$array
,
$indent
,
$seen
) =
@_
;
if
(
$seen
->{refaddr(
$array
)}++ ) {
die
"YAML::Tiny does not support circular references"
;
}
my
@lines
= ();
foreach
my
$el
(
@$array
) {
my
$line
= (
' '
x
$indent
) .
'-'
;
my
$type
=
ref
$el
;
if
( !
$type
) {
$line
.=
' '
.
$self
->_write_scalar(
$el
,
$indent
+ 1 );
push
@lines
,
$line
;
}
elsif
(
$type
eq
'ARRAY'
) {
if
(
@$el
) {
push
@lines
,
$line
;
push
@lines
,
$self
->_write_array(
$el
,
$indent
+ 1,
$seen
);
}
else
{
$line
.=
' []'
;
push
@lines
,
$line
;
}
}
elsif
(
$type
eq
'HASH'
) {
if
(
keys
%$el
) {
push
@lines
,
$line
;
push
@lines
,
$self
->_write_hash(
$el
,
$indent
+ 1,
$seen
);
}
else
{
$line
.=
' {}'
;
push
@lines
,
$line
;
}
}
else
{
die
"YAML::Tiny does not support $type references"
;
}
}
@lines
;
}
sub
_write_hash {
my
(
$self
,
$hash
,
$indent
,
$seen
) =
@_
;
if
(
$seen
->{refaddr(
$hash
)}++ ) {
die
"YAML::Tiny does not support circular references"
;
}
my
@lines
= ();
foreach
my
$name
(
sort
keys
%$hash
) {
my
$el
=
$hash
->{
$name
};
my
$line
= (
' '
x
$indent
) .
"$name:"
;
my
$type
=
ref
$el
;
if
( !
$type
) {
$line
.=
' '
.
$self
->_write_scalar(
$el
,
$indent
+ 1 );
push
@lines
,
$line
;
}
elsif
(
$type
eq
'ARRAY'
) {
if
(
@$el
) {
push
@lines
,
$line
;
push
@lines
,
$self
->_write_array(
$el
,
$indent
+ 1,
$seen
);
}
else
{
$line
.=
' []'
;
push
@lines
,
$line
;
}
}
elsif
(
$type
eq
'HASH'
) {
if
(
keys
%$el
) {
push
@lines
,
$line
;
push
@lines
,
$self
->_write_hash(
$el
,
$indent
+ 1,
$seen
);
}
else
{
$line
.=
' {}'
;
push
@lines
,
$line
;
}
}
else
{
die
"YAML::Tiny does not support $type references"
;
}
}
@lines
;
}
sub
_error {
$YAML::Tiny::errstr
=
$_
[1];
undef
;
}
sub
errstr {
$YAML::Tiny::errstr
;
}
sub
Dump {
YAML::Tiny->new(
@_
)->write_string;
}
sub
Load {
my
$self
= YAML::Tiny->read_string(
@_
);
unless
(
$self
) {
Carp::croak(
"Failed to load YAML document from string"
);
}
if
(
wantarray
) {
return
@$self
;
}
else
{
return
$self
->[-1];
}
}
BEGIN {
*freeze
=
*Dump
;
*thaw
=
*Load
;
}
sub
DumpFile {
my
$file
=
shift
;
YAML::Tiny->new(
@_
)->
write
(
$file
);
}
sub
LoadFile {
my
$self
= YAML::Tiny->
read
(
$_
[0]);
unless
(
$self
) {
Carp::croak(
"Failed to load YAML document from '"
. (
$_
[0] || '
') . "'
");
}
if
(
wantarray
) {
return
@$self
;
}
else
{
return
$self
->[-1];
}
}
BEGIN {
local
$@;
eval
{
};
my
$v
=
eval
(
"$Scalar::Util::VERSION"
) || 0;
if
( $@ or
$v
< 1.18 ) {
eval
<<'END_PERL';
# Scalar::Util failed to load or too old
sub refaddr {
my $pkg = ref($_[0]) or return undef;
if ( !! UNIVERSAL::can($_[0], 'can') ) {
bless $_[0], 'Scalar::Util::Fake';
} else {
$pkg = undef;
}
"$_[0]" =~ /0x(\w+)/;
my $i = do { local $^W; hex $1 };
bless $_[0], $pkg if defined $pkg;
$i;
}
END_PERL
}
else
{
*refaddr
=
*Scalar::Util::refaddr
;
}
}
1;