| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
### Data::PropertyList - Convert arbitrary objects to/from strings. |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
### Copyright 1996, 1997, 1998 Evolution Online Systems, Inc. |
|
4
|
|
|
|
|
|
|
# You may use this software for free under the terms of the Artistic License |
|
5
|
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
### Change History |
|
7
|
|
|
|
|
|
|
# 1998-12-17 Minor doc cleanup; added makefile and tests for distribution. |
|
8
|
|
|
|
|
|
|
# 1998-10-05 Tweaked output spacing for single-line string arrays. |
|
9
|
|
|
|
|
|
|
# 1998-10-05 Switched from use of String::Escape::add to direct hash access. |
|
10
|
|
|
|
|
|
|
# 1998-07-23 Conventionalized POD, switched to yyyy.mm_dd version numbering. |
|
11
|
|
|
|
|
|
|
# 1998-08-23 On further consideration, this really did belong in Data::*. |
|
12
|
|
|
|
|
|
|
# 1998-06-11 Improved support for <
|
|
13
|
|
|
|
|
|
|
# 1998-05-07 Fixed problem with reading "0 = ..." lines in hashes. |
|
14
|
|
|
|
|
|
|
# 1998-03-03 Replaced $r->class with ref($r) -Simon |
|
15
|
|
|
|
|
|
|
# 1998-02-28 Initialized _parse_multiline $value to '' to run clean under -w. |
|
16
|
|
|
|
|
|
|
# 1998-02-25 Version 1.00 - String::PropertyList |
|
17
|
|
|
|
|
|
|
# 1998-02-25 Moved to String:: and @EXPORT_OK for CPAN distribution - jeremy |
|
18
|
|
|
|
|
|
|
# 1998-01-28 Fixed variable name typo in _parse_array. |
|
19
|
|
|
|
|
|
|
# 1998-01-11 Added rudimentary support for comments: full-line comments only |
|
20
|
|
|
|
|
|
|
# 1998-01-02 Renamed package Data::PropertyList to Text::PropertyList -Simon |
|
21
|
|
|
|
|
|
|
# 1997-12-08 Removed package Data::Types, use UNIVERSAL::isa instead. -Piglet |
|
22
|
|
|
|
|
|
|
# 1997-11-19 Added loopback handling to astext; now Supress as XREF TO |
|
23
|
|
|
|
|
|
|
# 1997-10-28 Updated to use new Text::Escape interface. |
|
24
|
|
|
|
|
|
|
# 1997-10-21 Documentation cleanup. |
|
25
|
|
|
|
|
|
|
# 1997-08-17 Moved string escape/unescape code into new Text::Escape. -Simon |
|
26
|
|
|
|
|
|
|
# 1997-01-2? New fromDictionary parser -Eric |
|
27
|
|
|
|
|
|
|
# 1997-01-14 New asDictionary function provides closer match to NeXT style. |
|
28
|
|
|
|
|
|
|
# 1997-01-11 Cloned & cleaned for Inetics; moved I/O to file.pm. V3.0 -Simon |
|
29
|
|
|
|
|
|
|
# 1996-10-29 Added append flag and trailing \n to write. -Piglet |
|
30
|
|
|
|
|
|
|
# 1996-08-06 Partial fix for blessed data; treat as basic type. V2.05 -Simon |
|
31
|
|
|
|
|
|
|
# 1996-07-13 Cleaned up flow, fixed headers. |
|
32
|
|
|
|
|
|
|
# 1996-06-25 Wrote &write. V2.04 -EJ |
|
33
|
|
|
|
|
|
|
# 1996-06-23 Converted from Perl 4 library to Perl 5 package. V2.03 |
|
34
|
|
|
|
|
|
|
# 1996-06-18 Iterative line parsing replaces raw recursion. V2.02 |
|
35
|
|
|
|
|
|
|
# 1996-06-15 Clean start with support for nested data structures. V2.01 |
|
36
|
|
|
|
|
|
|
# 1996-05-26 Support for =<< multiline values. |
|
37
|
|
|
|
|
|
|
# 1996-05-08 Parse key-value pairs into a flat hash. Version 1. -Simon |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
package Data::PropertyList; |
|
40
|
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
require 5.003; |
|
42
|
3
|
|
|
3
|
|
23164
|
use strict; |
|
|
3
|
|
|
|
|
8
|
|
|
|
3
|
|
|
|
|
134
|
|
|
43
|
|
|
|
|
|
|
|
|
44
|
3
|
|
|
3
|
|
17
|
use vars qw( $VERSION @ISA @EXPORT_OK ); |
|
|
3
|
|
|
|
|
5
|
|
|
|
3
|
|
|
|
|
233
|
|
|
45
|
|
|
|
|
|
|
$VERSION = 1998.12_17; |
|
46
|
|
|
|
|
|
|
|
|
47
|
3
|
|
|
3
|
|
16
|
use Exporter; |
|
|
3
|
|
|
|
|
15
|
|
|
|
3
|
|
|
|
|
187
|
|
|
48
|
|
|
|
|
|
|
push @ISA, qw( Exporter ); |
|
49
|
|
|
|
|
|
|
push @EXPORT_OK, qw( astext fromtext ); |
|
50
|
|
|
|
|
|
|
|
|
51
|
3
|
|
|
3
|
|
20
|
use vars qw( $Separator ); |
|
|
3
|
|
|
|
|
5
|
|
|
|
3
|
|
|
|
|
163
|
|
|
52
|
|
|
|
|
|
|
$Separator = '.'; |
|
53
|
|
|
|
|
|
|
|
|
54
|
3
|
|
|
3
|
|
3192
|
use String::Escape qw( qprintable unprintable ); |
|
|
3
|
|
|
|
|
21697
|
|
|
|
3
|
|
|
|
|
737
|
|
|
55
|
|
|
|
|
|
|
$String::Escape::Escapes{'astext'} = \&astext; |
|
56
|
|
|
|
|
|
|
$String::Escape::Escapes{'fromtext'} = \&fromtext; |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
### Writer |
|
59
|
|
|
|
|
|
|
|
|
60
|
3
|
|
|
3
|
|
34
|
use vars qw( %DRefs %Supress $CurrentDRef $CurrentDepth ); |
|
|
3
|
|
|
|
|
9
|
|
|
|
3
|
|
|
|
|
244
|
|
|
61
|
3
|
|
|
3
|
|
14
|
use vars qw( $Indent $ShowClasses $ShowDRefs $Multiline $MaxItems ); |
|
|
3
|
|
|
|
|
5
|
|
|
|
3
|
|
|
|
|
3570
|
|
|
62
|
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
# $string = astext($value_or_reference); |
|
64
|
|
|
|
|
|
|
# $string = astext($value_or_reference, %options); |
|
65
|
|
|
|
|
|
|
# Write out an object graph in NeXT property list format |
|
66
|
|
|
|
|
|
|
# Numerous variables are localized, then we recurse. |
|
67
|
|
|
|
|
|
|
sub astext { |
|
68
|
23
|
|
|
23
|
1
|
2565
|
my $target = shift; |
|
69
|
23
|
|
|
|
|
52
|
my %options = @_; |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
# Options |
|
72
|
23
|
|
|
|
|
38
|
local $CurrentDRef = ''; |
|
73
|
23
|
|
|
|
|
33
|
local $CurrentDepth = 0; |
|
74
|
|
|
|
|
|
|
|
|
75
|
23
|
|
|
|
|
29
|
local $Indent = 2; |
|
76
|
|
|
|
|
|
|
|
|
77
|
23
|
50
|
|
|
|
62
|
local $ShowClasses = $options{'-classes'} if (exists $options{'-classes'} ); |
|
78
|
23
|
100
|
|
|
|
59
|
local $ShowDRefs = $options{'-drefs'} if (exists $options{'-drefs'} ); |
|
79
|
23
|
50
|
|
|
|
87
|
local $Multiline = $options{'-multiline'} if (exists $options{'-multiline'}); |
|
80
|
23
|
100
|
|
|
|
54
|
local $MaxItems = $options{'-maxitems'} if (exists $options{'-maxitems'}); |
|
81
|
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
# Working scope for this invokation of astext. |
|
83
|
23
|
|
|
|
|
43
|
local %DRefs = (); |
|
84
|
23
|
|
|
|
|
33
|
local %Supress = (); |
|
85
|
|
|
|
|
|
|
|
|
86
|
23
|
|
|
|
|
50
|
_astext( $target ) |
|
87
|
|
|
|
|
|
|
} |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
# $string = _astext($referenceorvalue); |
|
90
|
|
|
|
|
|
|
sub _astext { |
|
91
|
571
|
|
|
571
|
|
903
|
my $target = shift; |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
# Write out an "UNDEFINED" comment to signal undefined values; |
|
94
|
571
|
50
|
|
|
|
1262
|
return '/* UNDEFINED */' if (not defined $target); |
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
# Write out escaped version of non-reference (string or number) values. |
|
97
|
571
|
100
|
|
|
|
1094
|
if ( ! ref($target) ) { |
|
98
|
557
|
50
|
33
|
|
|
1282
|
if ( $Multiline and $target =~ /\n.*?\n/ ) { |
|
99
|
0
|
0
|
|
|
|
0
|
return "<
|
|
100
|
|
|
|
|
|
|
$target . ($target =~ /\n\Z/ ?'':"\n") . |
|
101
|
|
|
|
|
|
|
" END_OF_TEXT_DELIMITER"; |
|
102
|
|
|
|
|
|
|
} else { |
|
103
|
557
|
|
|
|
|
1408
|
return qprintable( $target ); |
|
104
|
|
|
|
|
|
|
} |
|
105
|
|
|
|
|
|
|
} |
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
# If this is a reference an item written out elsewhere, write an XREF comment |
|
108
|
14
|
0
|
66
|
|
|
70
|
return '/* CROSS-REFERENCE TO ' . |
|
|
|
50
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
( length($DRefs{$target}) ? $DRefs{$target} : 'ROOT' ) .' */' |
|
110
|
|
|
|
|
|
|
if ( exists $DRefs{$target} and $Supress{$target} ); |
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
# Store a relative DRef from the root to here, if we haven't already |
|
113
|
14
|
100
|
|
|
|
50
|
$DRefs{$target} = $CurrentDRef if ( not exists $DRefs{$target}); |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
# We're going to show this item, so don't show it again in the future |
|
116
|
14
|
|
|
|
|
28
|
$Supress{$target} ++ ; |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
# Variable to hold the stringified form of $target. |
|
119
|
14
|
|
|
|
|
18
|
my $result = ''; |
|
120
|
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
# Write out DRef if $ShowDRefs is set |
|
122
|
14
|
100
|
100
|
|
|
48
|
$result .= "/* DREF $CurrentDRef */ " |
|
123
|
|
|
|
|
|
|
if ($ShowDRefs and length $CurrentDRef); |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
# Any DRefs after this point are separated by dots. |
|
126
|
14
|
100
|
|
|
|
36
|
local $CurrentDRef = $CurrentDRef . $Separator if ( length $CurrentDRef ); |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
# Write out class of item if it's blessed and $ShowClasses is set |
|
129
|
14
|
0
|
33
|
|
|
34
|
$result .= "/* CLASS " . ref($target) . " */ " if ($ShowClasses and |
|
|
|
|
33
|
|
|
|
|
|
130
|
|
|
|
|
|
|
ref($target) and (ref($target) !~ /\A(ARRAY|HASH|SCALAR|REF|CODE)\Z/)); |
|
131
|
|
|
|
|
|
|
|
|
132
|
14
|
100
|
0
|
|
|
60
|
if ( UNIVERSAL::isa($target, 'HASH') ) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
133
|
6
|
|
|
|
|
5
|
my $key; |
|
134
|
6
|
|
|
|
|
7
|
foreach $key (sort keys %{$target}) { |
|
|
6
|
|
|
|
|
25
|
|
|
135
|
12
|
|
|
|
|
17
|
my $value = $target->{$key}; |
|
136
|
12
|
50
|
|
|
|
29
|
next unless (ref $value); |
|
137
|
0
|
0
|
|
|
|
0
|
$DRefs{$value} = $CurrentDRef . $key unless ( exists $DRefs{$value} ); |
|
138
|
0
|
|
|
|
|
0
|
$Supress{$value} ++; |
|
139
|
|
|
|
|
|
|
} |
|
140
|
6
|
50
|
|
|
|
17
|
$result .= "{" if ($CurrentDepth); |
|
141
|
6
|
50
|
|
|
|
16
|
$result .= "\n" if ($result); |
|
142
|
6
|
|
|
|
|
7
|
$CurrentDepth ++; |
|
143
|
6
|
|
|
|
|
7
|
foreach $key (sort keys %{$target}) { |
|
|
6
|
|
|
|
|
15
|
|
|
144
|
12
|
|
|
|
|
75
|
$result .= ' ' x ( ($CurrentDepth - 1) * $Indent); |
|
145
|
12
|
|
|
|
|
19
|
local $CurrentDRef = $CurrentDRef . $key; |
|
146
|
12
|
50
|
|
|
|
23
|
$Supress{$target->{$key}} -- if ( ref $target->{$key} ); |
|
147
|
12
|
|
|
|
|
31
|
$result .= _astext($key) . ' = ' . _astext($target->{$key}) .";\n"; |
|
148
|
|
|
|
|
|
|
} |
|
149
|
6
|
|
|
|
|
62
|
$CurrentDepth --; |
|
150
|
6
|
50
|
|
|
|
20
|
$result .= ' 'x(($CurrentDepth-1) * $Indent) . "}" if ($CurrentDepth); |
|
151
|
6
|
|
|
|
|
25
|
return $result; |
|
152
|
|
|
|
|
|
|
} |
|
153
|
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
elsif ( UNIVERSAL::isa($target, 'ARRAY') ) { |
|
155
|
8
|
|
|
|
|
11
|
my $key; |
|
156
|
|
|
|
|
|
|
# If $MaxItems is set and there are fewer than that many non-ref items |
|
157
|
8
|
|
66
|
|
|
23
|
my $one_line = ( $MaxItems and $#{$target} <= $MaxItems ); |
|
158
|
8
|
|
|
|
|
13
|
foreach $key (0 .. $#{$target}) { |
|
|
8
|
|
|
|
|
75
|
|
|
159
|
524
|
|
|
|
|
631
|
my $value = $target->[$key]; |
|
160
|
524
|
100
|
|
|
|
1273
|
next unless (ref $value); |
|
161
|
6
|
|
|
|
|
7
|
$one_line = 0; |
|
162
|
6
|
50
|
|
|
|
22
|
$DRefs{$value} = $CurrentDRef . $key unless ( exists $DRefs{$value} ); |
|
163
|
6
|
|
|
|
|
13
|
$Supress{$value} ++; |
|
164
|
|
|
|
|
|
|
} |
|
165
|
8
|
100
|
|
|
|
24
|
my $joiner = ( $one_line ) ? " " : "\n"; |
|
166
|
8
|
50
|
|
|
|
21
|
$result .= "(" if ( $CurrentDepth ); |
|
167
|
8
|
50
|
|
|
|
20
|
$result .= $joiner if ( $result ); |
|
168
|
8
|
|
|
|
|
10
|
$CurrentDepth ++; |
|
169
|
8
|
|
|
|
|
11
|
foreach $key (0 .. $#{$target}) { |
|
|
8
|
|
|
|
|
19
|
|
|
170
|
524
|
100
|
|
|
|
9784
|
$result .= $one_line ? '' : ' ' x ( ($CurrentDepth - 1) * $Indent); |
|
171
|
524
|
|
|
|
|
904
|
local $CurrentDRef = $CurrentDRef . $key; |
|
172
|
524
|
100
|
|
|
|
1053
|
$Supress{$target->[$key]} -- if ( ref $target->[$key] ); |
|
173
|
524
|
|
|
|
|
956
|
$result .= _astext($target->[$key]) . "," . $joiner; |
|
174
|
|
|
|
|
|
|
} |
|
175
|
8
|
|
|
|
|
65
|
$CurrentDepth --; |
|
176
|
8
|
0
|
|
|
|
28
|
$result .= ( ! $one_line ? ' 'x(($CurrentDepth-1) * $Indent) : '' ) . ")" |
|
|
|
50
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
if ( $CurrentDepth ); |
|
178
|
8
|
|
|
|
|
404
|
return $result; |
|
179
|
|
|
|
|
|
|
} |
|
180
|
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
elsif (UNIVERSAL::isa($target, 'REF') or UNIVERSAL::isa($target, 'SCALAR')) { |
|
182
|
0
|
|
|
|
|
0
|
$result .= '/* REFERENCE */ '; |
|
183
|
0
|
|
|
|
|
0
|
local $CurrentDepth = $CurrentDepth + 1; |
|
184
|
0
|
|
|
|
|
0
|
local $CurrentDRef = $CurrentDRef . 0; |
|
185
|
0
|
|
|
|
|
0
|
$result .= _astext($$target); |
|
186
|
0
|
|
|
|
|
0
|
return $result; |
|
187
|
|
|
|
|
|
|
} |
|
188
|
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
# Otherwise it's some unsupported kind of reference; just "" stringify it |
|
190
|
0
|
|
|
|
|
0
|
return "/* REFERENCE TO $target */"; |
|
191
|
|
|
|
|
|
|
} |
|
192
|
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
### Reader |
|
194
|
|
|
|
|
|
|
|
|
195
|
3
|
|
|
3
|
|
88
|
use vars qw( @TextLines $LineNumber $Source ); |
|
|
3
|
|
|
|
|
6
|
|
|
|
3
|
|
|
|
|
3799
|
|
|
196
|
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
# $datastructure = fromtext($string); |
|
198
|
|
|
|
|
|
|
# $datastructure = fromtext($string, %options); |
|
199
|
|
|
|
|
|
|
# reconstruct an object graph from a NeXT property list. |
|
200
|
|
|
|
|
|
|
sub fromtext ($%) { |
|
201
|
16
|
|
|
16
|
1
|
1107
|
my $dictionary_text = shift; |
|
202
|
16
|
|
|
|
|
44
|
my %options = @_; |
|
203
|
|
|
|
|
|
|
|
|
204
|
16
|
|
|
|
|
60
|
local @TextLines = split("\n", $dictionary_text); |
|
205
|
16
|
|
|
|
|
23
|
local $LineNumber = 0; |
|
206
|
16
|
|
100
|
|
|
72
|
local $Source = $options{'-source'} || ''; |
|
207
|
|
|
|
|
|
|
|
|
208
|
16
|
100
|
|
|
|
54
|
if ( $options{'-scalar'} ) { |
|
|
|
50
|
|
|
|
|
|
|
209
|
15
|
|
|
|
|
25
|
return _parse_value( _get_line() . "\000", "\000" ); |
|
210
|
|
|
|
|
|
|
} elsif ( $options{'-array'} ) { |
|
211
|
1
|
|
|
|
|
5
|
return _parse_array(); |
|
212
|
|
|
|
|
|
|
} else { |
|
213
|
0
|
|
|
|
|
0
|
return _parse_hash(); |
|
214
|
|
|
|
|
|
|
} |
|
215
|
|
|
|
|
|
|
} |
|
216
|
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
# _parse_error( $message ); |
|
218
|
|
|
|
|
|
|
sub _parse_error { |
|
219
|
0
|
|
|
0
|
|
0
|
my $message = shift; |
|
220
|
0
|
0
|
|
|
|
0
|
warn 'PropertyList error, ' . $message . |
|
221
|
|
|
|
|
|
|
' at line ' . $LineNumber . ( $Source ? ' in ' . $Source : '' ) ."\n"; |
|
222
|
|
|
|
|
|
|
} |
|
223
|
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
# $text = _get_line; |
|
225
|
|
|
|
|
|
|
sub _get_line { |
|
226
|
27
|
|
|
27
|
|
29
|
$LineNumber++; |
|
227
|
27
|
|
|
|
|
70
|
shift(@TextLines); |
|
228
|
|
|
|
|
|
|
} |
|
229
|
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
# $hash_ref = _parse_hash(); |
|
231
|
|
|
|
|
|
|
sub _parse_hash { |
|
232
|
3
|
|
|
3
|
|
5
|
my $hash = {}; |
|
233
|
3
|
|
|
|
|
3
|
my ($key, $value, $current_line); |
|
234
|
|
|
|
|
|
|
|
|
235
|
3
|
|
|
|
|
8
|
while (@TextLines) { |
|
236
|
9
|
|
|
|
|
14
|
$current_line = _get_line(); |
|
237
|
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
# Ignore comments |
|
239
|
9
|
|
|
|
|
13
|
$current_line =~ s#\Q/*\E.*?\Q*/\E##g; |
|
240
|
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
# Ignore blank lines |
|
242
|
9
|
50
|
|
|
|
23
|
next if ( $current_line =~ /^\s*$/ ); |
|
243
|
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
# If we hit a closing brace, we're done with this hash |
|
245
|
9
|
100
|
|
|
|
23
|
last if ( $current_line =~ /^\s*\}[,;]/o ); |
|
246
|
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
# Extract key and equals sign. |
|
248
|
6
|
50
|
|
|
|
36
|
if ( $current_line =~ s/^\s*\"(([^\"\\]|\\.)+)\"//o ) { |
|
|
|
50
|
|
|
|
|
|
|
249
|
0
|
|
|
|
|
0
|
$key = unprintable( $1 ); |
|
250
|
|
|
|
|
|
|
} elsif ( $current_line =~ s/^\s*(\S+)//o ) { |
|
251
|
6
|
|
|
|
|
14
|
$key = unprintable( $1 ); |
|
252
|
|
|
|
|
|
|
} else { |
|
253
|
0
|
|
|
|
|
0
|
_parse_error("Key not found"); |
|
254
|
0
|
|
|
|
|
0
|
last; |
|
255
|
|
|
|
|
|
|
} |
|
256
|
|
|
|
|
|
|
|
|
257
|
6
|
50
|
|
|
|
54
|
$current_line =~ s/^\s*=\s*//o or _parse_error("= not found"); |
|
258
|
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
# Extract value |
|
260
|
6
|
|
|
|
|
22
|
$value = _parse_value( $current_line, ';' ); |
|
261
|
|
|
|
|
|
|
|
|
262
|
6
|
50
|
|
|
|
77
|
next unless (defined $key); |
|
263
|
|
|
|
|
|
|
|
|
264
|
6
|
|
|
|
|
18
|
$hash->{$key} = $value; |
|
265
|
|
|
|
|
|
|
} |
|
266
|
|
|
|
|
|
|
|
|
267
|
3
|
|
|
|
|
8
|
return $hash; |
|
268
|
|
|
|
|
|
|
} |
|
269
|
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
# $array_ref = _parse_array(); |
|
271
|
|
|
|
|
|
|
sub _parse_array { |
|
272
|
1
|
|
|
1
|
|
18
|
my $array = []; |
|
273
|
1
|
|
|
|
|
1
|
my ($value, $current_line); |
|
274
|
|
|
|
|
|
|
|
|
275
|
1
|
|
|
|
|
4
|
while (@TextLines) { |
|
276
|
3
|
|
|
|
|
7
|
$current_line = _get_line(); |
|
277
|
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
# Ignore comments |
|
279
|
3
|
|
|
|
|
15
|
$current_line =~ s#\Q/*\E.*?\Q*/\E##g; |
|
280
|
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
# Ignore blank lines |
|
282
|
3
|
50
|
|
|
|
12
|
next if ( $current_line =~ /^\s*$/ ); |
|
283
|
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
# If we hit a closing paren, we're done with this hash |
|
285
|
3
|
50
|
|
|
|
8
|
last if ( $current_line =~ /^\s*\)[,;]/o ); |
|
286
|
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
# Extract value |
|
288
|
3
|
|
|
|
|
7
|
$value = _parse_value( $current_line, ',' ); |
|
289
|
|
|
|
|
|
|
|
|
290
|
3
|
|
|
|
|
10
|
push( @$array, $value); |
|
291
|
|
|
|
|
|
|
|
|
292
|
3
|
|
|
|
|
7
|
next; |
|
293
|
|
|
|
|
|
|
} |
|
294
|
|
|
|
|
|
|
|
|
295
|
1
|
|
|
|
|
5
|
return $array; |
|
296
|
|
|
|
|
|
|
} |
|
297
|
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
# $string = _parse_multiline($ender); |
|
299
|
|
|
|
|
|
|
sub _parse_multiline { |
|
300
|
0
|
|
|
0
|
|
0
|
my $ender = shift; |
|
301
|
|
|
|
|
|
|
|
|
302
|
0
|
|
|
|
|
0
|
my $value = ''; |
|
303
|
0
|
|
|
|
|
0
|
my $current_line; |
|
304
|
|
|
|
|
|
|
|
|
305
|
0
|
|
|
|
|
0
|
while (@TextLines) { |
|
306
|
0
|
|
|
|
|
0
|
$current_line = _get_line(); |
|
307
|
0
|
0
|
|
|
|
0
|
last if ($current_line =~ /^\s*\Q$ender\E[\;\,]?\s*$/); |
|
308
|
0
|
|
|
|
|
0
|
$value .= $current_line . "\n"; |
|
309
|
|
|
|
|
|
|
} |
|
310
|
0
|
|
|
|
|
0
|
return $value; |
|
311
|
|
|
|
|
|
|
} |
|
312
|
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
# $value = _parse_value( $value, $terminator ); |
|
314
|
|
|
|
|
|
|
# Extracts a quoted or unquoted string, an array, hash, or a multiline string |
|
315
|
|
|
|
|
|
|
sub _parse_value { |
|
316
|
24
|
|
|
24
|
|
32
|
my $current_line = shift; |
|
317
|
24
|
|
|
|
|
29
|
my $end_value = shift; |
|
318
|
|
|
|
|
|
|
|
|
319
|
24
|
100
|
|
|
|
514
|
if ( $current_line =~ /^\s*\"(([^\"\\]|\\.)*)\"\Q$end_value\E\s*/ ) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
# Extract quoted value |
|
321
|
10
|
|
|
|
|
29
|
return unprintable( $1 ); |
|
322
|
|
|
|
|
|
|
} elsif ( $current_line =~ /^\s*(\S+?)\Q$end_value\E\s*/ ) { |
|
323
|
|
|
|
|
|
|
# Extract unquoted value |
|
324
|
11
|
|
|
|
|
30
|
return unprintable( $1 ); |
|
325
|
|
|
|
|
|
|
} elsif ( $current_line =~ /^\s*(\/\*.*?\*\/)\s*\Q$end_value\E\s*/ ) { |
|
326
|
|
|
|
|
|
|
# Extract comment |
|
327
|
0
|
|
|
|
|
0
|
return undef; |
|
328
|
|
|
|
|
|
|
} elsif ( $current_line =~ /^\s*\{/o ) { |
|
329
|
3
|
|
|
|
|
7
|
return _parse_hash(); |
|
330
|
|
|
|
|
|
|
} elsif ( $current_line =~ /^\s*\(/o ) { |
|
331
|
0
|
|
|
|
|
|
return _parse_array(); |
|
332
|
|
|
|
|
|
|
} elsif ( $current_line =~ /^\s*\<\<(\w+)(?:\Q$end_value\E)?/o ) { |
|
333
|
0
|
|
|
|
|
|
return _parse_multiline($1); |
|
334
|
|
|
|
|
|
|
} else { |
|
335
|
0
|
|
|
|
|
|
_parse_error("value not found in '$current_line' - $end_value"); |
|
336
|
|
|
|
|
|
|
} |
|
337
|
|
|
|
|
|
|
} |
|
338
|
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
1; |
|
340
|
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
__END__ |