source: ProjectBuilder/devel/pb-modules/lib/ProjectBuilder/YAML.pm@ 2484

Last change on this file since 2484 was 2484, checked in by Bruno Cornec, 4 years ago

Adds an internal YAML module from YAML::Tiny to allow distribution without YAML support to work

File size: 41.6 KB
Line 
1use 5.008001; # sane UTF-8 support
2use strict;
3use warnings;
4package YAML::Tiny; # git description: v1.72-7-g8682f63
5# XXX-INGY is 5.8.1 too old/broken for utf8?
6# XXX-XDG Lancaster consensus was that it was sufficient until
7# proven otherwise
8
9our $VERSION = '1.73';
10
11#####################################################################
12# The YAML::Tiny API.
13#
14# These are the currently documented API functions/methods and
15# exports:
16
17use Exporter;
18our @ISA = qw{ Exporter };
19our @EXPORT = qw{ Load Dump };
20our @EXPORT_OK = qw{ LoadFile DumpFile freeze thaw };
21
22###
23# Functional/Export API:
24
25sub Dump {
26 return YAML::Tiny->new(@_)->_dump_string;
27}
28
29# XXX-INGY Returning last document seems a bad behavior.
30# XXX-XDG I think first would seem more natural, but I don't know
31# that it's worth changing now
32sub Load {
33 my $self = YAML::Tiny->_load_string(@_);
34 if ( wantarray ) {
35 return @$self;
36 } else {
37 # To match YAML.pm, return the last document
38 return $self->[-1];
39 }
40}
41
42# XXX-INGY Do we really need freeze and thaw?
43# XXX-XDG I don't think so. I'd support deprecating them.
44BEGIN {
45 *freeze = \&Dump;
46 *thaw = \&Load;
47}
48
49sub DumpFile {
50 my $file = shift;
51 return YAML::Tiny->new(@_)->_dump_file($file);
52}
53
54sub LoadFile {
55 my $file = shift;
56 my $self = YAML::Tiny->_load_file($file);
57 if ( wantarray ) {
58 return @$self;
59 } else {
60 # Return only the last document to match YAML.pm,
61 return $self->[-1];
62 }
63}
64
65
66###
67# Object Oriented API:
68
69# Create an empty YAML::Tiny object
70# XXX-INGY Why do we use ARRAY object?
71# NOTE: I get it now, but I think it's confusing and not needed.
72# Will change it on a branch later, for review.
73#
74# XXX-XDG I don't support changing it yet. It's a very well-documented
75# "API" of YAML::Tiny. I'd support deprecating it, but Adam suggested
76# we not change it until YAML.pm's own OO API is established so that
77# users only have one API change to digest, not two
78sub new {
79 my $class = shift;
80 bless [ @_ ], $class;
81}
82
83# XXX-INGY It probably doesn't matter, and it's probably too late to
84# change, but 'read/write' are the wrong names. Read and Write
85# are actions that take data from storage to memory
86# characters/strings. These take the data to/from storage to native
87# Perl objects, which the terms dump and load are meant. As long as
88# this is a legacy quirk to YAML::Tiny it's ok, but I'd prefer not
89# to add new {read,write}_* methods to this API.
90
91sub read_string {
92 my $self = shift;
93 $self->_load_string(@_);
94}
95
96sub write_string {
97 my $self = shift;
98 $self->_dump_string(@_);
99}
100
101sub read {
102 my $self = shift;
103 $self->_load_file(@_);
104}
105
106sub write {
107 my $self = shift;
108 $self->_dump_file(@_);
109}
110
111
112
113
114#####################################################################
115# Constants
116
117# Printed form of the unprintable characters in the lowest range
118# of ASCII characters, listed by ASCII ordinal position.
119my @UNPRINTABLE = qw(
120 0 x01 x02 x03 x04 x05 x06 a
121 b t n v f r x0E x0F
122 x10 x11 x12 x13 x14 x15 x16 x17
123 x18 x19 x1A e x1C x1D x1E x1F
124);
125
126# Printable characters for escapes
127my %UNESCAPES = (
128 0 => "\x00", z => "\x00", N => "\x85",
129 a => "\x07", b => "\x08", t => "\x09",
130 n => "\x0a", v => "\x0b", f => "\x0c",
131 r => "\x0d", e => "\x1b", '\\' => '\\',
132);
133
134# XXX-INGY
135# I(ngy) need to decide if these values should be quoted in
136# YAML::Tiny or not. Probably yes.
137
138# These 3 values have special meaning when unquoted and using the
139# default YAML schema. They need quotes if they are strings.
140my %QUOTE = map { $_ => 1 } qw{
141 null true false
142};
143
144# The commented out form is simpler, but overloaded the Perl regex
145# engine due to recursion and backtracking problems on strings
146# larger than 32,000ish characters. Keep it for reference purposes.
147# qr/\"((?:\\.|[^\"])*)\"/
148my $re_capture_double_quoted = qr/\"([^\\"]*(?:\\.[^\\"]*)*)\"/;
149my $re_capture_single_quoted = qr/\'([^\']*(?:\'\'[^\']*)*)\'/;
150# unquoted re gets trailing space that needs to be stripped
151my $re_capture_unquoted_key = qr/([^:]+(?::+\S(?:[^:]*|.*?(?=:)))*)(?=\s*\:(?:\s+|$))/;
152my $re_trailing_comment = qr/(?:\s+\#.*)?/;
153my $re_key_value_separator = qr/\s*:(?:\s+(?:\#.*)?|$)/;
154
155
156
157
158
159#####################################################################
160# YAML::Tiny Implementation.
161#
162# These are the private methods that do all the work. They may change
163# at any time.
164
165
166###
167# Loader functions:
168
169# Create an object from a file
170sub _load_file {
171 my $class = ref $_[0] ? ref shift : shift;
172
173 # Check the file
174 my $file = shift or $class->_error( 'You did not specify a file name' );
175 $class->_error( "File '$file' does not exist" )
176 unless -e $file;
177 $class->_error( "'$file' is a directory, not a file" )
178 unless -f _;
179 $class->_error( "Insufficient permissions to read '$file'" )
180 unless -r _;
181
182 # Open unbuffered with strict UTF-8 decoding and no translation layers
183 open( my $fh, "<:unix:encoding(UTF-8)", $file );
184 unless ( $fh ) {
185 $class->_error("Failed to open file '$file': $!");
186 }
187
188 # flock if available (or warn if not possible for OS-specific reasons)
189 if ( _can_flock() ) {
190 flock( $fh, Fcntl::LOCK_SH() )
191 or warn "Couldn't lock '$file' for reading: $!";
192 }
193
194 # slurp the contents
195 my $contents = eval {
196 use warnings FATAL => 'utf8';
197 local $/;
198 <$fh>
199 };
200 if ( my $err = $@ ) {
201 $class->_error("Error reading from file '$file': $err");
202 }
203
204 # close the file (release the lock)
205 unless ( close $fh ) {
206 $class->_error("Failed to close file '$file': $!");
207 }
208
209 $class->_load_string( $contents );
210}
211
212# Create an object from a string
213sub _load_string {
214 my $class = ref $_[0] ? ref shift : shift;
215 my $self = bless [], $class;
216 my $string = $_[0];
217 eval {
218 unless ( defined $string ) {
219 die \"Did not provide a string to load";
220 }
221
222 # Check if Perl has it marked as characters, but it's internally
223 # inconsistent. E.g. maybe latin1 got read on a :utf8 layer
224 if ( utf8::is_utf8($string) && ! utf8::valid($string) ) {
225 die \<<'...';
226Read an invalid UTF-8 string (maybe mixed UTF-8 and 8-bit character set).
227Did you decode with lax ":utf8" instead of strict ":encoding(UTF-8)"?
228...
229 }
230
231 # Ensure Unicode character semantics, even for 0x80-0xff
232 utf8::upgrade($string);
233
234 # Check for and strip any leading UTF-8 BOM
235 $string =~ s/^\x{FEFF}//;
236
237 # Check for some special cases
238 return $self unless length $string;
239
240 # Split the file into lines
241 my @lines = grep { ! /^\s*(?:\#.*)?\z/ }
242 split /(?:\015{1,2}\012|\015|\012)/, $string;
243
244 # Strip the initial YAML header
245 @lines and $lines[0] =~ /^\%YAML[: ][\d\.]+.*\z/ and shift @lines;
246
247 # A nibbling parser
248 my $in_document = 0;
249 while ( @lines ) {
250 # Do we have a document header?
251 if ( $lines[0] =~ /^---\s*(?:(.+)\s*)?\z/ ) {
252 # Handle scalar documents
253 shift @lines;
254 if ( defined $1 and $1 !~ /^(?:\#.+|\%YAML[: ][\d\.]+)\z/ ) {
255 push @$self,
256 $self->_load_scalar( "$1", [ undef ], \@lines );
257 next;
258 }
259 $in_document = 1;
260 }
261
262 if ( ! @lines or $lines[0] =~ /^(?:---|\.\.\.)/ ) {
263 # A naked document
264 push @$self, undef;
265 while ( @lines and $lines[0] !~ /^---/ ) {
266 shift @lines;
267 }
268 $in_document = 0;
269
270 # XXX The final '-+$' is to look for -- which ends up being an
271 # error later.
272 } elsif ( ! $in_document && @$self ) {
273 # only the first document can be explicit
274 die \"YAML::Tiny failed to classify the line '$lines[0]'";
275 } elsif ( $lines[0] =~ /^\s*\-(?:\s|$|-+$)/ ) {
276 # An array at the root
277 my $document = [ ];
278 push @$self, $document;
279 $self->_load_array( $document, [ 0 ], \@lines );
280
281 } elsif ( $lines[0] =~ /^(\s*)\S/ ) {
282 # A hash at the root
283 my $document = { };
284 push @$self, $document;
285 $self->_load_hash( $document, [ length($1) ], \@lines );
286
287 } else {
288 # Shouldn't get here. @lines have whitespace-only lines
289 # stripped, and previous match is a line with any
290 # non-whitespace. So this clause should only be reachable via
291 # a perlbug where \s is not symmetric with \S
292
293 # uncoverable statement
294 die \"YAML::Tiny failed to classify the line '$lines[0]'";
295 }
296 }
297 };
298 my $err = $@;
299 if ( ref $err eq 'SCALAR' ) {
300 $self->_error(${$err});
301 } elsif ( $err ) {
302 $self->_error($err);
303 }
304
305 return $self;
306}
307
308sub _unquote_single {
309 my ($self, $string) = @_;
310 return '' unless length $string;
311 $string =~ s/\'\'/\'/g;
312 return $string;
313}
314
315sub _unquote_double {
316 my ($self, $string) = @_;
317 return '' unless length $string;
318 $string =~ s/\\"/"/g;
319 $string =~
320 s{\\([Nnever\\fartz0b]|x([0-9a-fA-F]{2}))}
321 {(length($1)>1)?pack("H2",$2):$UNESCAPES{$1}}gex;
322 return $string;
323}
324
325# Load a YAML scalar string to the actual Perl scalar
326sub _load_scalar {
327 my ($self, $string, $indent, $lines) = @_;
328
329 # Trim trailing whitespace
330 $string =~ s/\s*\z//;
331
332 # Explitic null/undef
333 return undef if $string eq '~';
334
335 # Single quote
336 if ( $string =~ /^$re_capture_single_quoted$re_trailing_comment\z/ ) {
337 return $self->_unquote_single($1);
338 }
339
340 # Double quote.
341 if ( $string =~ /^$re_capture_double_quoted$re_trailing_comment\z/ ) {
342 return $self->_unquote_double($1);
343 }
344
345 # Special cases
346 if ( $string =~ /^[\'\"!&]/ ) {
347 die \"YAML::Tiny does not support a feature in line '$string'";
348 }
349 return {} if $string =~ /^{}(?:\s+\#.*)?\z/;
350 return [] if $string =~ /^\[\](?:\s+\#.*)?\z/;
351
352 # Regular unquoted string
353 if ( $string !~ /^[>|]/ ) {
354 die \"YAML::Tiny found illegal characters in plain scalar: '$string'"
355 if $string =~ /^(?:-(?:\s|$)|[\@\%\`])/ or
356 $string =~ /:(?:\s|$)/;
357 $string =~ s/\s+#.*\z//;
358 return $string;
359 }
360
361 # Error
362 die \"YAML::Tiny failed to find multi-line scalar content" unless @$lines;
363
364 # Check the indent depth
365 $lines->[0] =~ /^(\s*)/;
366 $indent->[-1] = length("$1");
367 if ( defined $indent->[-2] and $indent->[-1] <= $indent->[-2] ) {
368 die \"YAML::Tiny found bad indenting in line '$lines->[0]'";
369 }
370
371 # Pull the lines
372 my @multiline = ();
373 while ( @$lines ) {
374 $lines->[0] =~ /^(\s*)/;
375 last unless length($1) >= $indent->[-1];
376 push @multiline, substr(shift(@$lines), $indent->[-1]);
377 }
378
379 my $j = (substr($string, 0, 1) eq '>') ? ' ' : "\n";
380 my $t = (substr($string, 1, 1) eq '-') ? '' : "\n";
381 return join( $j, @multiline ) . $t;
382}
383
384# Load an array
385sub _load_array {
386 my ($self, $array, $indent, $lines) = @_;
387
388 while ( @$lines ) {
389 # Check for a new document
390 if ( $lines->[0] =~ /^(?:---|\.\.\.)/ ) {
391 while ( @$lines and $lines->[0] !~ /^---/ ) {
392 shift @$lines;
393 }
394 return 1;
395 }
396
397 # Check the indent level
398 $lines->[0] =~ /^(\s*)/;
399 if ( length($1) < $indent->[-1] ) {
400 return 1;
401 } elsif ( length($1) > $indent->[-1] ) {
402 die \"YAML::Tiny found bad indenting in line '$lines->[0]'";
403 }
404
405 if ( $lines->[0] =~ /^(\s*\-\s+)[^\'\"]\S*\s*:(?:\s+|$)/ ) {
406 # Inline nested hash
407 my $indent2 = length("$1");
408 $lines->[0] =~ s/-/ /;
409 push @$array, { };
410 $self->_load_hash( $array->[-1], [ @$indent, $indent2 ], $lines );
411
412 } elsif ( $lines->[0] =~ /^\s*\-\s*\z/ ) {
413 shift @$lines;
414 unless ( @$lines ) {
415 push @$array, undef;
416 return 1;
417 }
418 if ( $lines->[0] =~ /^(\s*)\-/ ) {
419 my $indent2 = length("$1");
420 if ( $indent->[-1] == $indent2 ) {
421 # Null array entry
422 push @$array, undef;
423 } else {
424 # Naked indenter
425 push @$array, [ ];
426 $self->_load_array(
427 $array->[-1], [ @$indent, $indent2 ], $lines
428 );
429 }
430
431 } elsif ( $lines->[0] =~ /^(\s*)\S/ ) {
432 push @$array, { };
433 $self->_load_hash(
434 $array->[-1], [ @$indent, length("$1") ], $lines
435 );
436
437 } else {
438 die \"YAML::Tiny failed to classify line '$lines->[0]'";
439 }
440
441 } elsif ( $lines->[0] =~ /^\s*\-(\s*)(.+?)\s*\z/ ) {
442 # Array entry with a value
443 shift @$lines;
444 push @$array, $self->_load_scalar(
445 "$2", [ @$indent, undef ], $lines
446 );
447
448 } elsif ( defined $indent->[-2] and $indent->[-1] == $indent->[-2] ) {
449 # This is probably a structure like the following...
450 # ---
451 # foo:
452 # - list
453 # bar: value
454 #
455 # ... so lets return and let the hash parser handle it
456 return 1;
457
458 } else {
459 die \"YAML::Tiny failed to classify line '$lines->[0]'";
460 }
461 }
462
463 return 1;
464}
465
466# Load a hash
467sub _load_hash {
468 my ($self, $hash, $indent, $lines) = @_;
469
470 while ( @$lines ) {
471 # Check for a new document
472 if ( $lines->[0] =~ /^(?:---|\.\.\.)/ ) {
473 while ( @$lines and $lines->[0] !~ /^---/ ) {
474 shift @$lines;
475 }
476 return 1;
477 }
478
479 # Check the indent level
480 $lines->[0] =~ /^(\s*)/;
481 if ( length($1) < $indent->[-1] ) {
482 return 1;
483 } elsif ( length($1) > $indent->[-1] ) {
484 die \"YAML::Tiny found bad indenting in line '$lines->[0]'";
485 }
486
487 # Find the key
488 my $key;
489
490 # Quoted keys
491 if ( $lines->[0] =~
492 s/^\s*$re_capture_single_quoted$re_key_value_separator//
493 ) {
494 $key = $self->_unquote_single($1);
495 }
496 elsif ( $lines->[0] =~
497 s/^\s*$re_capture_double_quoted$re_key_value_separator//
498 ) {
499 $key = $self->_unquote_double($1);
500 }
501 elsif ( $lines->[0] =~
502 s/^\s*$re_capture_unquoted_key$re_key_value_separator//
503 ) {
504 $key = $1;
505 $key =~ s/\s+$//;
506 }
507 elsif ( $lines->[0] =~ /^\s*\?/ ) {
508 die \"YAML::Tiny does not support a feature in line '$lines->[0]'";
509 }
510 else {
511 die \"YAML::Tiny failed to classify line '$lines->[0]'";
512 }
513
514 if ( exists $hash->{$key} ) {
515 warn "YAML::Tiny found a duplicate key '$key' in line '$lines->[0]'";
516 }
517
518 # Do we have a value?
519 if ( length $lines->[0] ) {
520 # Yes
521 $hash->{$key} = $self->_load_scalar(
522 shift(@$lines), [ @$indent, undef ], $lines
523 );
524 } else {
525 # An indent
526 shift @$lines;
527 unless ( @$lines ) {
528 $hash->{$key} = undef;
529 return 1;
530 }
531 if ( $lines->[0] =~ /^(\s*)-/ ) {
532 $hash->{$key} = [];
533 $self->_load_array(
534 $hash->{$key}, [ @$indent, length($1) ], $lines
535 );
536 } elsif ( $lines->[0] =~ /^(\s*)./ ) {
537 my $indent2 = length("$1");
538 if ( $indent->[-1] >= $indent2 ) {
539 # Null hash entry
540 $hash->{$key} = undef;
541 } else {
542 $hash->{$key} = {};
543 $self->_load_hash(
544 $hash->{$key}, [ @$indent, length($1) ], $lines
545 );
546 }
547 }
548 }
549 }
550
551 return 1;
552}
553
554
555###
556# Dumper functions:
557
558# Save an object to a file
559sub _dump_file {
560 my $self = shift;
561
562 require Fcntl;
563
564 # Check the file
565 my $file = shift or $self->_error( 'You did not specify a file name' );
566
567 my $fh;
568 # flock if available (or warn if not possible for OS-specific reasons)
569 if ( _can_flock() ) {
570 # Open without truncation (truncate comes after lock)
571 my $flags = Fcntl::O_WRONLY()|Fcntl::O_CREAT();
572 sysopen( $fh, $file, $flags )
573 or $self->_error("Failed to open file '$file' for writing: $!");
574
575 # Use no translation and strict UTF-8
576 binmode( $fh, ":raw:encoding(UTF-8)");
577
578 flock( $fh, Fcntl::LOCK_EX() )
579 or warn "Couldn't lock '$file' for reading: $!";
580
581 # truncate and spew contents
582 truncate $fh, 0;
583 seek $fh, 0, 0;
584 }
585 else {
586 open $fh, ">:unix:encoding(UTF-8)", $file;
587 }
588
589 # serialize and spew to the handle
590 print {$fh} $self->_dump_string;
591
592 # close the file (release the lock)
593 unless ( close $fh ) {
594 $self->_error("Failed to close file '$file': $!");
595 }
596
597 return 1;
598}
599
600# Save an object to a string
601sub _dump_string {
602 my $self = shift;
603 return '' unless ref $self && @$self;
604
605 # Iterate over the documents
606 my $indent = 0;
607 my @lines = ();
608
609 eval {
610 foreach my $cursor ( @$self ) {
611 push @lines, '---';
612
613 # An empty document
614 if ( ! defined $cursor ) {
615 # Do nothing
616
617 # A scalar document
618 } elsif ( ! ref $cursor ) {
619 $lines[-1] .= ' ' . $self->_dump_scalar( $cursor );
620
621 # A list at the root
622 } elsif ( ref $cursor eq 'ARRAY' ) {
623 unless ( @$cursor ) {
624 $lines[-1] .= ' []';
625 next;
626 }
627 push @lines, $self->_dump_array( $cursor, $indent, {} );
628
629 # A hash at the root
630 } elsif ( ref $cursor eq 'HASH' ) {
631 unless ( %$cursor ) {
632 $lines[-1] .= ' {}';
633 next;
634 }
635 push @lines, $self->_dump_hash( $cursor, $indent, {} );
636
637 } else {
638 die \("Cannot serialize " . ref($cursor));
639 }
640 }
641 };
642 if ( ref $@ eq 'SCALAR' ) {
643 $self->_error(${$@});
644 } elsif ( $@ ) {
645 $self->_error($@);
646 }
647
648 join '', map { "$_\n" } @lines;
649}
650
651sub _has_internal_string_value {
652 my $value = shift;
653 my $b_obj = B::svref_2object(\$value); # for round trip problem
654 return $b_obj->FLAGS & B::SVf_POK();
655}
656
657sub _dump_scalar {
658 my $string = $_[1];
659 my $is_key = $_[2];
660 # Check this before checking length or it winds up looking like a string!
661 my $has_string_flag = _has_internal_string_value($string);
662 return '~' unless defined $string;
663 return "''" unless length $string;
664 if (Scalar::Util::looks_like_number($string)) {
665 # keys and values that have been used as strings get quoted
666 if ( $is_key || $has_string_flag ) {
667 return qq['$string'];
668 }
669 else {
670 return $string;
671 }
672 }
673 if ( $string =~ /[\x00-\x09\x0b-\x0d\x0e-\x1f\x7f-\x9f\'\n]/ ) {
674 $string =~ s/\\/\\\\/g;
675 $string =~ s/"/\\"/g;
676 $string =~ s/\n/\\n/g;
677 $string =~ s/[\x85]/\\N/g;
678 $string =~ s/([\x00-\x1f])/\\$UNPRINTABLE[ord($1)]/g;
679 $string =~ s/([\x7f-\x9f])/'\x' . sprintf("%X",ord($1))/ge;
680 return qq|"$string"|;
681 }
682 if ( $string =~ /(?:^[~!@#%&*|>?:,'"`{}\[\]]|^-+$|\s|:\z)/ or
683 $QUOTE{$string}
684 ) {
685 return "'$string'";
686 }
687 return $string;
688}
689
690sub _dump_array {
691 my ($self, $array, $indent, $seen) = @_;
692 if ( $seen->{refaddr($array)}++ ) {
693 die \"YAML::Tiny does not support circular references";
694 }
695 my @lines = ();
696 foreach my $el ( @$array ) {
697 my $line = (' ' x $indent) . '-';
698 my $type = ref $el;
699 if ( ! $type ) {
700 $line .= ' ' . $self->_dump_scalar( $el );
701 push @lines, $line;
702
703 } elsif ( $type eq 'ARRAY' ) {
704 if ( @$el ) {
705 push @lines, $line;
706 push @lines, $self->_dump_array( $el, $indent + 1, $seen );
707 } else {
708 $line .= ' []';
709 push @lines, $line;
710 }
711
712 } elsif ( $type eq 'HASH' ) {
713 if ( keys %$el ) {
714 push @lines, $line;
715 push @lines, $self->_dump_hash( $el, $indent + 1, $seen );
716 } else {
717 $line .= ' {}';
718 push @lines, $line;
719 }
720
721 } else {
722 die \"YAML::Tiny does not support $type references";
723 }
724 }
725
726 @lines;
727}
728
729sub _dump_hash {
730 my ($self, $hash, $indent, $seen) = @_;
731 if ( $seen->{refaddr($hash)}++ ) {
732 die \"YAML::Tiny does not support circular references";
733 }
734 my @lines = ();
735 foreach my $name ( sort keys %$hash ) {
736 my $el = $hash->{$name};
737 my $line = (' ' x $indent) . $self->_dump_scalar($name, 1) . ":";
738 my $type = ref $el;
739 if ( ! $type ) {
740 $line .= ' ' . $self->_dump_scalar( $el );
741 push @lines, $line;
742
743 } elsif ( $type eq 'ARRAY' ) {
744 if ( @$el ) {
745 push @lines, $line;
746 push @lines, $self->_dump_array( $el, $indent + 1, $seen );
747 } else {
748 $line .= ' []';
749 push @lines, $line;
750 }
751
752 } elsif ( $type eq 'HASH' ) {
753 if ( keys %$el ) {
754 push @lines, $line;
755 push @lines, $self->_dump_hash( $el, $indent + 1, $seen );
756 } else {
757 $line .= ' {}';
758 push @lines, $line;
759 }
760
761 } else {
762 die \"YAML::Tiny does not support $type references";
763 }
764 }
765
766 @lines;
767}
768
769
770
771#####################################################################
772# DEPRECATED API methods:
773
774# Error storage (DEPRECATED as of 1.57)
775our $errstr = '';
776
777# Set error
778sub _error {
779 require Carp;
780 $errstr = $_[1];
781 $errstr =~ s/ at \S+ line \d+.*//;
782 Carp::croak( $errstr );
783}
784
785# Retrieve error
786my $errstr_warned;
787sub errstr {
788 require Carp;
789 Carp::carp( "YAML::Tiny->errstr and \$YAML::Tiny::errstr is deprecated" )
790 unless $errstr_warned++;
791 $errstr;
792}
793
794
795
796
797#####################################################################
798# Helper functions. Possibly not needed.
799
800
801# Use to detect nv or iv
802use B;
803
804# XXX-INGY Is flock YAML::Tiny's responsibility?
805# Some platforms can't flock :-(
806# XXX-XDG I think it is. When reading and writing files, we ought
807# to be locking whenever possible. People (foolishly) use YAML
808# files for things like session storage, which has race issues.
809my $HAS_FLOCK;
810sub _can_flock {
811 if ( defined $HAS_FLOCK ) {
812 return $HAS_FLOCK;
813 }
814 else {
815 require Config;
816 my $c = \%Config::Config;
817 $HAS_FLOCK = grep { $c->{$_} } qw/d_flock d_fcntl_can_lock d_lockf/;
818 require Fcntl if $HAS_FLOCK;
819 return $HAS_FLOCK;
820 }
821}
822
823
824# XXX-INGY Is this core in 5.8.1? Can we remove this?
825# XXX-XDG Scalar::Util 1.18 didn't land until 5.8.8, so we need this
826#####################################################################
827# Use Scalar::Util if possible, otherwise emulate it
828
829use Scalar::Util ();
830BEGIN {
831 local $@;
832 if ( eval { Scalar::Util->VERSION(1.18); } ) {
833 *refaddr = *Scalar::Util::refaddr;
834 }
835 else {
836 eval <<'END_PERL';
837# Scalar::Util failed to load or too old
838sub refaddr {
839 my $pkg = ref($_[0]) or return undef;
840 if ( !! UNIVERSAL::can($_[0], 'can') ) {
841 bless $_[0], 'Scalar::Util::Fake';
842 } else {
843 $pkg = undef;
844 }
845 "$_[0]" =~ /0x(\w+)/;
846 my $i = do { no warnings 'portable'; hex $1 };
847 bless $_[0], $pkg if defined $pkg;
848 $i;
849}
850END_PERL
851 }
852}
853
854delete $YAML::Tiny::{refaddr};
855
8561;
857
858# XXX-INGY Doc notes I'm putting up here. Changing the doc when it's wrong
859# but leaving grey area stuff up here.
860#
861# I would like to change Read/Write to Load/Dump below without
862# changing the actual API names.
863#
864# It might be better to put Load/Dump API in the SYNOPSIS instead of the
865# dubious OO API.
866#
867# null and bool explanations may be outdated.
868
869__END__
870
871=pod
872
873=head1 NAME
874
875YAML::Tiny - Read/Write YAML files with as little code as possible
876
877=head1 VERSION
878
879version 1.73
880
881=head1 PREAMBLE
882
883The YAML specification is huge. Really, B<really> huge. It contains all the
884functionality of XML, except with flexibility and choice, which makes it
885easier to read, but with a formal specification that is more complex than
886XML.
887
888The original pure-Perl implementation L<YAML> costs just over 4 megabytes
889of memory to load. Just like with Windows F<.ini> files (3 meg to load) and
890CSS (3.5 meg to load) the situation is just asking for a B<YAML::Tiny>
891module, an incomplete but correct and usable subset of the functionality,
892in as little code as possible.
893
894Like the other C<::Tiny> modules, YAML::Tiny has no non-core dependencies,
895does not require a compiler to install, is back-compatible to Perl v5.8.1,
896and can be inlined into other modules if needed.
897
898In exchange for this adding this extreme flexibility, it provides support
899for only a limited subset of YAML. But the subset supported contains most
900of the features for the more common uses of YAML.
901
902=head1 SYNOPSIS
903
904Assuming F<file.yml> like this:
905
906 ---
907 rootproperty: blah
908 section:
909 one: two
910 three: four
911 Foo: Bar
912 empty: ~
913
914
915Read and write F<file.yml> like this:
916
917 use YAML::Tiny;
918
919 # Open the config
920 my $yaml = YAML::Tiny->read( 'file.yml' );
921
922 # Get a reference to the first document
923 my $config = $yaml->[0];
924
925 # Or read properties directly
926 my $root = $yaml->[0]->{rootproperty};
927 my $one = $yaml->[0]->{section}->{one};
928 my $Foo = $yaml->[0]->{section}->{Foo};
929
930 # Change data directly
931 $yaml->[0]->{newsection} = { this => 'that' }; # Add a section
932 $yaml->[0]->{section}->{Foo} = 'Not Bar!'; # Change a value
933 delete $yaml->[0]->{section}; # Delete a value
934
935 # Save the document back to the file
936 $yaml->write( 'file.yml' );
937
938To create a new YAML file from scratch:
939
940 # Create a new object with a single hashref document
941 my $yaml = YAML::Tiny->new( { wibble => "wobble" } );
942
943 # Add an arrayref document
944 push @$yaml, [ 'foo', 'bar', 'baz' ];
945
946 # Save both documents to a file
947 $yaml->write( 'data.yml' );
948
949Then F<data.yml> will contain:
950
951 ---
952 wibble: wobble
953 ---
954 - foo
955 - bar
956 - baz
957
958=head1 DESCRIPTION
959
960B<YAML::Tiny> is a perl class for reading and writing YAML-style files,
961written with as little code as possible, reducing load time and memory
962overhead.
963
964Most of the time it is accepted that Perl applications use a lot
965of memory and modules. The B<::Tiny> family of modules is specifically
966intended to provide an ultralight and zero-dependency alternative to
967many more-thorough standard modules.
968
969This module is primarily for reading human-written files (like simple
970config files) and generating very simple human-readable files. Note that
971I said B<human-readable> and not B<geek-readable>. The sort of files that
972your average manager or secretary should be able to look at and make
973sense of.
974
975=for stopwords normalise
976
977L<YAML::Tiny> does not generate comments, it won't necessarily preserve the
978order of your hashes, and it will normalise if reading in and writing out
979again.
980
981It only supports a very basic subset of the full YAML specification.
982
983=for stopwords embeddable
984
985Usage is targeted at files like Perl's META.yml, for which a small and
986easily-embeddable module is extremely attractive.
987
988Features will only be added if they are human readable, and can be written
989in a few lines of code. Please don't be offended if your request is
990refused. Someone has to draw the line, and for YAML::Tiny that someone
991is me.
992
993If you need something with more power move up to L<YAML> (7 megabytes of
994memory overhead) or L<YAML::XS> (6 megabytes memory overhead and requires
995a C compiler).
996
997To restate, L<YAML::Tiny> does B<not> preserve your comments, whitespace,
998or the order of your YAML data. But it should round-trip from Perl
999structure to file and back again just fine.
1000
1001=head1 METHODS
1002
1003=for Pod::Coverage HAVE_UTF8 refaddr
1004
1005=head2 new
1006
1007The constructor C<new> creates a C<YAML::Tiny> object as a blessed array
1008reference. Any arguments provided are taken as separate documents
1009to be serialized.
1010
1011=head2 read $filename
1012
1013The C<read> constructor reads a YAML file from a file name,
1014and returns a new C<YAML::Tiny> object containing the parsed content.
1015
1016Returns the object on success or throws an error on failure.
1017
1018=head2 read_string $string;
1019
1020The C<read_string> constructor reads YAML data from a character string, and
1021returns a new C<YAML::Tiny> object containing the parsed content. If you have
1022read the string from a file yourself, be sure that you have correctly decoded
1023it into characters first.
1024
1025Returns the object on success or throws an error on failure.
1026
1027=head2 write $filename
1028
1029The C<write> method generates the file content for the properties, and
1030writes it to disk using UTF-8 encoding to the filename specified.
1031
1032Returns true on success or throws an error on failure.
1033
1034=head2 write_string
1035
1036Generates the file content for the object and returns it as a character
1037string. This may contain non-ASCII characters and should be encoded
1038before writing it to a file.
1039
1040Returns true on success or throws an error on failure.
1041
1042=for stopwords errstr
1043
1044=head2 errstr (DEPRECATED)
1045
1046Prior to version 1.57, some errors were fatal and others were available only
1047via the C<$YAML::Tiny::errstr> variable, which could be accessed via the
1048C<errstr()> method.
1049
1050Starting with version 1.57, all errors are fatal and throw exceptions.
1051
1052The C<$errstr> variable is still set when exceptions are thrown, but
1053C<$errstr> and the C<errstr()> method are deprecated and may be removed in a
1054future release. The first use of C<errstr()> will issue a deprecation
1055warning.
1056
1057=head1 FUNCTIONS
1058
1059YAML::Tiny implements a number of functions to add compatibility with
1060the L<YAML> API. These should be a drop-in replacement.
1061
1062=head2 Dump
1063
1064 my $string = Dump(list-of-Perl-data-structures);
1065
1066Turn Perl data into YAML. This function works very much like
1067Data::Dumper::Dumper().
1068
1069It takes a list of Perl data structures and dumps them into a serialized
1070form.
1071
1072It returns a character string containing the YAML stream. Be sure to encode
1073it as UTF-8 before serializing to a file or socket.
1074
1075The structures can be references or plain scalars.
1076
1077Dies on any error.
1078
1079=head2 Load
1080
1081 my @data_structures = Load(string-containing-a-YAML-stream);
1082
1083Turn YAML into Perl data. This is the opposite of Dump.
1084
1085Just like L<Storable>'s thaw() function or the eval() function in relation
1086to L<Data::Dumper>.
1087
1088It parses a character string containing a valid YAML stream into a list of
1089Perl data structures representing the individual YAML documents. Be sure to
1090decode the character string correctly if the string came from a file or
1091socket.
1092
1093 my $last_data_structure = Load(string-containing-a-YAML-stream);
1094
1095For consistency with YAML.pm, when Load is called in scalar context, it
1096returns the data structure corresponding to the last of the YAML documents
1097found in the input stream.
1098
1099Dies on any error.
1100
1101=head2 freeze() and thaw()
1102
1103Aliases to Dump() and Load() for L<Storable> fans. This will also allow
1104YAML::Tiny to be plugged directly into modules like POE.pm, that use the
1105freeze/thaw API for internal serialization.
1106
1107=head2 DumpFile(filepath, list)
1108
1109Writes the YAML stream to a file with UTF-8 encoding instead of just
1110returning a string.
1111
1112Dies on any error.
1113
1114=head2 LoadFile(filepath)
1115
1116Reads the YAML stream from a UTF-8 encoded file instead of a string.
1117
1118Dies on any error.
1119
1120=head1 YAML TINY SPECIFICATION
1121
1122This section of the documentation provides a specification for "YAML Tiny",
1123a subset of the YAML specification.
1124
1125It is based on and described comparatively to the YAML 1.1 Working Draft
11262004-12-28 specification, located at L<http://yaml.org/spec/current.html>.
1127
1128Terminology and chapter numbers are based on that specification.
1129
1130=head2 1. Introduction and Goals
1131
1132The purpose of the YAML Tiny specification is to describe a useful subset
1133of the YAML specification that can be used for typical document-oriented
1134use cases such as configuration files and simple data structure dumps.
1135
1136=for stopwords extensibility
1137
1138Many specification elements that add flexibility or extensibility are
1139intentionally removed, as is support for complex data structures, class
1140and object-orientation.
1141
1142In general, the YAML Tiny language targets only those data structures
1143available in JSON, with the additional limitation that only simple keys
1144are supported.
1145
1146As a result, all possible YAML Tiny documents should be able to be
1147transformed into an equivalent JSON document, although the reverse is
1148not necessarily true (but will be true in simple cases).
1149
1150=for stopwords PCRE
1151
1152As a result of these simplifications the YAML Tiny specification should
1153be implementable in a (relatively) small amount of code in any language
1154that supports Perl Compatible Regular Expressions (PCRE).
1155
1156=head2 2. Introduction
1157
1158YAML Tiny supports three data structures. These are scalars (in a variety
1159of forms), block-form sequences and block-form mappings. Flow-style
1160sequences and mappings are not supported, with some minor exceptions
1161detailed later.
1162
1163The use of three dashes "---" to indicate the start of a new document is
1164supported, and multiple documents per file/stream is allowed.
1165
1166Both line and inline comments are supported.
1167
1168Scalars are supported via the plain style, single quote and double quote,
1169as well as literal-style and folded-style multi-line scalars.
1170
1171The use of explicit tags is not supported.
1172
1173The use of "null" type scalars is supported via the ~ character.
1174
1175The use of "bool" type scalars is not supported.
1176
1177=for stopwords serializer
1178
1179However, serializer implementations should take care to explicitly escape
1180strings that match a "bool" keyword in the following set to prevent other
1181implementations that do support "bool" accidentally reading a string as a
1182boolean
1183
1184 y|Y|yes|Yes|YES|n|N|no|No|NO
1185 |true|True|TRUE|false|False|FALSE
1186 |on|On|ON|off|Off|OFF
1187
1188The use of anchors and aliases is not supported.
1189
1190The use of directives is supported only for the %YAML directive.
1191
1192=head2 3. Processing YAML Tiny Information
1193
1194B<Processes>
1195
1196=for stopwords deserialization
1197
1198The YAML specification dictates three-phase serialization and three-phase
1199deserialization.
1200
1201The YAML Tiny specification does not mandate any particular methodology
1202or mechanism for parsing.
1203
1204Any compliant parser is only required to parse a single document at a
1205time. The ability to support streaming documents is optional and most
1206likely non-typical.
1207
1208=for stopwords acyclic
1209
1210Because anchors and aliases are not supported, the resulting representation
1211graph is thus directed but (unlike the main YAML specification) B<acyclic>.
1212
1213Circular references/pointers are not possible, and any YAML Tiny serializer
1214detecting a circular reference should error with an appropriate message.
1215
1216B<Presentation Stream>
1217
1218=for stopwords unicode
1219
1220YAML Tiny reads and write UTF-8 encoded files. Operations on strings expect
1221or produce Unicode characters not UTF-8 encoded bytes.
1222
1223B<Loading Failure Points>
1224
1225=for stopwords modality
1226
1227=for stopwords parsers
1228
1229YAML Tiny parsers and emitters are not expected to recover from, or
1230adapt to, errors. The specific error modality of any implementation is
1231not dictated (return codes, exceptions, etc.) but is expected to be
1232consistent.
1233
1234=head2 4. Syntax
1235
1236B<Character Set>
1237
1238YAML Tiny streams are processed in memory as Unicode characters and
1239read/written with UTF-8 encoding.
1240
1241The escaping and unescaping of the 8-bit YAML escapes is required.
1242
1243The escaping and unescaping of 16-bit and 32-bit YAML escapes is not
1244required.
1245
1246B<Indicator Characters>
1247
1248Support for the "~" null/undefined indicator is required.
1249
1250Implementations may represent this as appropriate for the underlying
1251language.
1252
1253Support for the "-" block sequence indicator is required.
1254
1255Support for the "?" mapping key indicator is B<not> required.
1256
1257Support for the ":" mapping value indicator is required.
1258
1259Support for the "," flow collection indicator is B<not> required.
1260
1261Support for the "[" flow sequence indicator is B<not> required, with
1262one exception (detailed below).
1263
1264Support for the "]" flow sequence indicator is B<not> required, with
1265one exception (detailed below).
1266
1267Support for the "{" flow mapping indicator is B<not> required, with
1268one exception (detailed below).
1269
1270Support for the "}" flow mapping indicator is B<not> required, with
1271one exception (detailed below).
1272
1273Support for the "#" comment indicator is required.
1274
1275Support for the "&" anchor indicator is B<not> required.
1276
1277Support for the "*" alias indicator is B<not> required.
1278
1279Support for the "!" tag indicator is B<not> required.
1280
1281Support for the "|" literal block indicator is required.
1282
1283Support for the ">" folded block indicator is required.
1284
1285Support for the "'" single quote indicator is required.
1286
1287Support for the """ double quote indicator is required.
1288
1289Support for the "%" directive indicator is required, but only
1290for the special case of a %YAML version directive before the
1291"---" document header, or on the same line as the document header.
1292
1293For example:
1294
1295 %YAML 1.1
1296 ---
1297 - A sequence with a single element
1298
1299Special Exception:
1300
1301To provide the ability to support empty sequences
1302and mappings, support for the constructs [] (empty sequence) and {}
1303(empty mapping) are required.
1304
1305For example,
1306
1307 %YAML 1.1
1308 # A document consisting of only an empty mapping
1309 --- {}
1310 # A document consisting of only an empty sequence
1311 --- []
1312 # A document consisting of an empty mapping within a sequence
1313 - foo
1314 - {}
1315 - bar
1316
1317B<Syntax Primitives>
1318
1319Other than the empty sequence and mapping cases described above, YAML Tiny
1320supports only the indentation-based block-style group of contexts.
1321
1322All five scalar contexts are supported.
1323
1324Indentation spaces work as per the YAML specification in all cases.
1325
1326Comments work as per the YAML specification in all simple cases.
1327Support for indented multi-line comments is B<not> required.
1328
1329Separation spaces work as per the YAML specification in all cases.
1330
1331B<YAML Tiny Character Stream>
1332
1333The only directive supported by the YAML Tiny specification is the
1334%YAML language/version identifier. Although detected, this directive
1335will have no control over the parsing itself.
1336
1337=for stopwords recognise
1338
1339The parser must recognise both the YAML 1.0 and YAML 1.1+ formatting
1340of this directive (as well as the commented form, although no explicit
1341code should be needed to deal with this case, being a comment anyway)
1342
1343That is, all of the following should be supported.
1344
1345 --- #YAML:1.0
1346 - foo
1347
1348 %YAML:1.0
1349 ---
1350 - foo
1351
1352 % YAML 1.1
1353 ---
1354 - foo
1355
1356Support for the %TAG directive is B<not> required.
1357
1358Support for additional directives is B<not> required.
1359
1360Support for the document boundary marker "---" is required.
1361
1362Support for the document boundary market "..." is B<not> required.
1363
1364If necessary, a document boundary should simply by indicated with a
1365"---" marker, with not preceding "..." marker.
1366
1367Support for empty streams (containing no documents) is required.
1368
1369Support for implicit document starts is required.
1370
1371That is, the following must be equivalent.
1372
1373 # Full form
1374 %YAML 1.1
1375 ---
1376 foo: bar
1377
1378 # Implicit form
1379 foo: bar
1380
1381B<Nodes>
1382
1383Support for nodes optional anchor and tag properties is B<not> required.
1384
1385Support for node anchors is B<not> required.
1386
1387Support for node tags is B<not> required.
1388
1389Support for alias nodes is B<not> required.
1390
1391Support for flow nodes is B<not> required.
1392
1393Support for block nodes is required.
1394
1395B<Scalar Styles>
1396
1397Support for all five scalar styles is required as per the YAML
1398specification, although support for quoted scalars spanning more
1399than one line is B<not> required.
1400
1401Support for multi-line scalar documents starting on the header
1402is not required.
1403
1404Support for the chomping indicators on multi-line scalar styles
1405is required.
1406
1407B<Collection Styles>
1408
1409Support for block-style sequences is required.
1410
1411Support for flow-style sequences is B<not> required.
1412
1413Support for block-style mappings is required.
1414
1415Support for flow-style mappings is B<not> required.
1416
1417Both sequences and mappings should be able to be arbitrarily
1418nested.
1419
1420Support for plain-style mapping keys is required.
1421
1422Support for quoted keys in mappings is B<not> required.
1423
1424Support for "?"-indicated explicit keys is B<not> required.
1425
1426=for stopwords endeth
1427
1428Here endeth the specification.
1429
1430=head2 Additional Perl-Specific Notes
1431
1432For some Perl applications, it's important to know if you really have a
1433number and not a string.
1434
1435That is, in some contexts is important that 3 the number is distinctive
1436from "3" the string.
1437
1438Because even Perl itself is not trivially able to understand the difference
1439(certainly without XS-based modules) Perl implementations of the YAML Tiny
1440specification are not required to retain the distinctiveness of 3 vs "3".
1441
1442=head1 SUPPORT
1443
1444Bugs should be reported via the CPAN bug tracker at
1445
1446L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=YAML-Tiny>
1447
1448=begin html
1449
1450For other issues, or commercial enhancement or support, please contact
1451<a href="http://ali.as/">Adam Kennedy</a> directly.
1452
1453=end html
1454
1455=head1 AUTHOR
1456
1457Adam Kennedy E<lt>adamk@cpan.orgE<gt>
1458
1459=head1 SEE ALSO
1460
1461=over 4
1462
1463=item * L<YAML>
1464
1465=item * L<YAML::Syck>
1466
1467=item * L<Config::Tiny>
1468
1469=item * L<CSS::Tiny>
1470
1471=item * L<http://use.perl.org/use.perl.org/_Alias/journal/29427.html>
1472
1473=item * L<http://ali.as/>
1474
1475=back
1476
1477=head1 COPYRIGHT
1478
1479Copyright 2006 - 2013 Adam Kennedy.
1480
1481This program is free software; you can redistribute
1482it and/or modify it under the same terms as Perl itself.
1483
1484The full text of the license can be found in the
1485LICENSE file included with this module.
1486
1487=cut
Note: See TracBrowser for help on using the repository browser.