File Coverage

blib/lib/Data/Sofu.pm
Criterion Covered Total %
statement 660 894 73.8
branch 286 490 58.3
condition 51 101 50.5
subroutine 53 68 77.9
pod 58 58 100.0
total 1108 1611 68.7


line stmt bran cond sub pod time code
1             ###############################################################################
2             #Sofu.pm
3             #Last Change: 2009-01-28
4             #Copyright (c) 2009 Marc-Seabstian "Maluku" Lucksch
5             #Version 0.3
6             ####################
7             #This file is part of the sofu.pm project, a parser library for an all-purpose
8             #ASCII file format. More information can be found on the project web site
9             #at http://sofu.sourceforge.net/ .
10             #
11             #sofu.pm is published under the terms of the MIT license, which basically means
12             #"Do with it whatever you want". For more information, see the license.txt
13             #file that should be enclosed with libsofu distributions. A copy of the license
14             #is (at the time of this writing) also available at
15             #http://www.opensource.org/licenses/mit-license.php .
16             ###############################################################################
17              
18             package Data::Sofu;
19 5     5   293122 use strict;
  5         17  
  5         206  
20 5     5   26 use warnings;
  5         9  
  5         150  
21 5     5   1089 use utf8;
  5         19  
  5         36  
22             require Exporter;
23 5     5   2490 use Carp qw/croak confess/;
  5         9  
  5         1228  
24             $Carp::Verbose=1;
25 5     5   27 use vars qw($VERSION @EXPORT @ISA @EXPORT_OK %EXPORT_TAGS);
  5         9  
  5         564  
26             @ISA = qw/Exporter/;
27 5     5   1686 use Encode;
  5         34844  
  5         3202  
28 5     5   6033 use Encode::Guess qw/UTF-16BE UTF-16LE UTF-32LE UTF-32BE latin1/;
  5         39373  
  5         40  
29              
30             @EXPORT= qw/readSofu writeSofu getSofucomments writeSofuBinary writeBinarySofu writeSofuML loadSofu/;
31             @EXPORT_OK= qw/readSofu writeSofu getSofucomments writeSofuBinary writeBinarySofu packBinarySofu packSofu unpackSofu getSofu packSofuBinary SofuloadFile getSofuComments writeSofuML packSofuML loadSofu/;
32             %EXPORT_TAGS=("all"=>[@EXPORT_OK]);
33              
34             $VERSION= 0.3;
35             my $sofu;
36             my $bdriver; #Binary Interface (new File)
37             my $mldriver; #SofuML Interface
38             our $fullescape = 0;
39              
40             sub refe {
41 960     960 1 1305 my $ref=shift;
42 960 100       3113 return 0 unless ref $ref;
43 173 100       568 return 1 if ref $ref eq "SCALAR";
44 72 100       203 return 1 if ref $ref eq "Data::Sofu::Reference";
45 57         3810 return 0;
46             }
47              
48             sub readSofu {
49 58 100   58 1 65675 $sofu=Data::Sofu->new() unless $sofu;
50 58 50       196 if (wantarray) {
51 0         0 return $sofu->read(@_);
52             }
53             else {
54 58         993 return scalar $sofu->read(@_);
55             }
56             }
57             sub getSofu {
58 2 50   2 1 17 $sofu=Data::Sofu->new() unless $sofu;
59 2         11 return $sofu->from(@_);
60             }
61             sub loadSofu {
62 35 100   35 1 47333 $sofu=Data::Sofu->new() unless $sofu;
63 35         235 return $sofu->load(@_);
64             }
65             sub SofuloadFile {
66 0 0   0 1 0 $sofu=Data::Sofu->new() unless $sofu;
67 0         0 return $sofu->load(@_);
68             }
69              
70             sub writeSofu {
71 24 100   24 1 3620 $sofu=Data::Sofu->new() unless $sofu;
72 24         135 return $sofu->write(@_);
73             }
74              
75             sub writeSofuML {
76 2 50   2 1 3441 $sofu=Data::Sofu->new() unless $sofu;
77 2         10 return $sofu->writeML(@_);
78             }
79              
80             sub loadFile {
81 0 0   0 1 0 $sofu=Data::Sofu->new() unless $sofu;
82 0         0 my $class=shift;
83 0 0       0 if ($class eq "Data::Sofu") {
84 0         0 return $sofu->load(@_);
85             }
86             #croak ("Usage: Data::Sofu->loadFile(\$file)\nFile can be: Filehandle, Filename or reference to a scalar") if (ref $class or $class ne "Data::Sofu");
87 0         0 return $sofu->load($class,@_);
88              
89             }
90             sub getSofucomments {
91 0 0   0 1 0 $sofu->warn("Can't get comments: No File read") unless $sofu;
92 0         0 return $sofu->comments;
93             }
94              
95             sub getSofuComments {
96 24 50   24 1 563 $sofu->warn("Can't get comments: No File read") unless $sofu;
97 24         136 return $sofu->comments;
98             }
99              
100             sub packSofu {
101 1 50   1 1 13 $sofu=Data::Sofu->new() unless $sofu;
102 1         6 return $sofu->pack(@_);
103             }
104              
105             sub packSofuML {
106 2 50   2 1 2279 $sofu=Data::Sofu->new() unless $sofu;
107 2         12 return $sofu->packML(@_);
108             }
109              
110             sub writeBinarySofu {
111 44 50   44 1 6022 $sofu=Data::Sofu->new() unless $sofu;
112 44         276 return $sofu->writeBinary(@_);
113             }
114              
115             sub writeSofuBinary {
116 0 0   0 1 0 $sofu=Data::Sofu->new() unless $sofu;
117 0         0 return $sofu->writeBinary(@_);
118             }
119              
120             sub packSofuBinary {
121 0 0   0 1 0 $sofu=Data::Sofu->new() unless $sofu;
122 0         0 return $sofu->packBinary(@_);
123             }
124              
125             sub packBinarySofu {
126 8 50   8 1 47 $sofu=Data::Sofu->new() unless $sofu;
127 8         46 return $sofu->packBinary(@_);
128             }
129              
130             sub unpackSofu {
131 2 50   2 1 1143 $sofu=Data::Sofu->new() unless $sofu;
132 2         13 return $sofu->unpack(@_);
133             }
134              
135             sub new {
136 5     5 1 24 my $self={};
137 5         13 shift;
138 5         20 $$self{CurFile}="";
139 5         16 $$self{Counter}=0;
140 5         13 $$self{WARN}=1;
141 5         15 $$self{Debug}=0;
142 5         14 $$self{Ref}={};
143 5         17 $$self{Indent}="";
144 5         13 $self->{String}=0;
145 5         21 $self->{Escape}=0;
146 5         13 $$self{SetIndent}="";
147 5         14 $$self{READLINE}="";
148 5         13 $self->{COUNT}=0;
149 5         15 $$self{Libsofucompat}=0;
150 5         13 $$self{Commentary}={};
151 5         14 $$self{PreserveCommentary}=1;
152 5         13 $$self{TREE}="";
153 5         19 $$self{OBJECT}="";
154 5         15 $self->{COMMENT}=[];
155 5         12 bless $self;
156 5         17 return $self;
157             }
158              
159             sub toObjects {
160 0     0 1 0 my $self=shift;
161 0         0 my $data=shift;
162 0         0 my $comment=shift;
163 0         0 Data::Sofu::Object->clear();
164 0         0 my $tree=Data::Sofu::Object->new($data);
165 0         0 foreach my $key (keys %$comment) {
166 0         0 my $wkey=$key;
167 0         0 $wkey=~s/^->//;
168 0 0       0 $wkey="" if $key eq "=";
169 0         0 $tree->storeComment($wkey,$comment->{$key});
170             }
171 0         0 return $tree;
172             }
173              
174             sub from { #deprecated but still in use requires to runs through the tree :(((
175 2     2 1 1357 require Data::Sofu::Object;
176 2         7 my $self=shift;
177 2         6 my $file=shift;
178 2 50 33     10 if (ref $file and ref $file ne "GLOB") {
179 0         0 carp("Can't call \"from\" on an Object, it is used to create an object tree: my \$tree=Data::Sofu::from(\$file)!");
180             }
181 2         20 Data::Sofu::Object->clear();
182             #$self->object(1); #Use the object parser;
183 2         12 my $tree=$self->read($file);
184 2         35 $tree=Data::Sofu::Object->new($tree);
185 2         9 my $c=$self->comment;
186 2         8 foreach my $key (keys %$c) {
187             #print "Key = $key Comment = @{$c->{$key}}\n";
188 8         11 my $wkey=$key;
189 8         28 $wkey=~s/^->//;
190 8 100       19 $wkey="" if $key eq "=";
191 8         29 $tree->storeComment($wkey,$c->{$key});
192             }
193 2         15 return $tree;
194             }
195              
196             sub wasbinary {
197 58     58 1 141 my $self=shift;
198 58 50       345 if (@_) {
199 58         225 $self->{BINARY}=shift;
200             }
201 58         506 return $self->{BINARY};
202             }
203              
204             sub load {
205 35     35 1 72 my $self=shift;
206             #TODO pure Object Based Parser!! NOT really possible to hack in with Ref-Detection and stuff (Complete rewrite needed, lex based like Sofud)
207             #return $self->from(@_);
208 35         2752 require Data::Sofu::Object;
209             #my $self=shift;
210 35         86 local $_;
211 35         79 my $file=shift;
212 35         74 my $fh;
213 35         206 $$self{TREE}="";
214 35         96 $self->{OBJECT}=1;
215 35         79 $$self{CURRENT}=0;
216 35         114 $$self{References}=[];
217 35         133 $self->{Commentary}={};
218 35         78 %{$$self{Ref}}=();
  35         132  
219 35         71 my $guess=0;
220 35 100       179 unless (ref $file) {
    50          
    0          
221 23         75 $$self{CurFile}=$file;
222 23 50       1870 open $fh,"<:raw",$$self{CurFile} or die "Sofu error open: $$self{CurFile} file: $!";
223 23         52 $guess=1;
224 23         83 binmode $fh;
225             #eval {require File::BOM;my ($e,$sp)=File::BOM::defuse($fh);$$self{Ret}.=$sp;$e=$e;};undef $@;
226             }
227             elsif (ref $file eq "SCALAR") {
228 12         47 $$self{CurFile}="Scalarref";
229 12 50 0     70 open $fh,"<:utf8",$file or die "Can't open perlIO: $!" if utf8::is_utf8($$file);
230 12 50 50     314 open $fh,"<",$file or die "Can't open perlIO: $!" if !utf8::is_utf8($$file);;
231             }
232             elsif (ref $file eq "GLOB") {
233 0         0 $$self{CurFile}="FileHandle";
234 0         0 $fh=$file;
235             }
236             else {
237 0         0 $self->warn("The argument to load or loadfile has to be a filename, reference to a scalar or filehandle");
238 0         0 return;
239             }
240 35         78 my $text=do {local $/,<$fh>};
  35         1073  
241             {
242 35         65 my $b = substr($text,0,2);
  35         99  
243 35         92 my $c= substr($text,2,1);
244 35 100       130 if ($b eq "So") {
245 28         62 $b=substr($text,0,4);
246 28 50       120 if ($b eq "Sofu") {
247 28         72 $b=substr($text,4,2);
248 28         69 $c=substr($text,6,1);
249             }
250             }
251 35 100 100     420 if (($b eq "\x{00}\x{00}" or $b eq "\x{01}\x{00}" or $b eq "\x{00}\x{01}") and $c ne "\x{FE}") { #Assume Binary
      66        
252 32         206 require Data::Sofu::Binary;
253 32 50       115 $bdriver = Data::Sofu::Binary->new() unless $bdriver;
254 32         205 my $tree = $bdriver->load(\$text);
255 32         320 $self->wasbinary(1);
256 32 50       91 if (wantarray) {
257 0         0 return %{$tree};
  0         0  
258             }
259 32         39386 return $tree;
260             }
261              
262             }
263 3 50       13 if ($guess) {
264 3         25 my $enc=guess_encoding($text);
265 3 50       301 $text=$enc->decode($text) if ref $enc;
266 3 50       44 $text=Encode::decode("UTF-8",$text) unless ref $enc;
267             }
268 3 50       31 substr($text,0,1,"") if substr($text,0,1) eq chr(65279); # UTF-8 BOM (Why ain't it removed ?)
269 3 50       11 close $fh if ref $file;
270 3         9 $$self{CurFile}="";
271 3         19 my $u=$self->unpack($text);
272 3         14 $self->{OBJECT}=0;
273 3         164 return $u;
274             }
275              
276             sub noComments {
277 0     0 1 0 my $self=shift;
278 0         0 $$self{PreserveCommentary}=0;
279             }
280             sub object {
281 0     0 1 0 my $self=shift;
282 0         0 $$self{OBJECT}=shift;
283             }
284             sub comment {
285 53     53 1 96 my $self=shift;
286 53         9318 my $data=undef;
287 53 100       167 if ($_[0]) {
288 29 50       120 if (ref $_[0] eq "HASH") {
289 29         75 $data=shift;
290             }
291             else {
292 0         0 $data={@_};
293             }
294             }
295 53 100       263 $$self{Commentary}=$data if $data;;
296 53         153 return $self->{Commentary};
297             }
298             sub comments {
299 24     24 1 60 my $self=shift;
300 24         45 my $data=undef;
301 24 50       76 if ($_[0]) {
302 0 0       0 if (ref $_[0] eq "HASH") {
303 0         0 $data=shift;
304             }
305             else {
306 0         0 $data={@_};
307             }
308             }
309 24 50       965 $$self{Commentary}=$data if $data;;
310 24         198 return $self->{Commentary};
311             }
312             sub setIndent {
313 0     0 1 0 my $self=shift;
314 0         0 local $_;
315 0         0 $$self{SetIndent}=shift;
316             }
317             sub setWarnings {
318 0     0 1 0 my $self=shift;
319 0         0 local $_;
320 0         0 $$self{WARN}=shift;
321             }
322             sub allWarn {
323 0     0 1 0 my $self=shift;
324 0         0 local $_;
325 0         0 $$self{WARN}=1;
326             }
327             sub noWarn {
328 0     0 1 0 my $self=shift;
329 0         0 local $_;
330 0         0 $$self{WARN}=0;
331             }
332             sub iKnowWhatIAmDoing {
333 0     0 1 0 my $self=shift;
334 0         0 local $_;
335 0         0 $$self{WARN}=0;
336             }
337             sub iDontKnowWhatIAmDoing {
338 0     0 1 0 my $self=shift;
339 0         0 local $_;
340 0         0 $$self{WARN}=1;
341             }
342             sub commentary {
343 801     801 1 951 my $self=shift;
344 801 50       1747 return "" unless $self->{PreserveCommentary};
345 801         1364 my $tree=$self->{TREE};
346 801 100       1473 $tree="=" unless $tree;
347 801 100       1742 if ($self->{Commentary}->{$tree}) {
348 7         8 my $res;
349 7 100       19 $res=" " if $self->{TREE};
350 7         10 foreach (@{$self->{Commentary}->{$tree}}) {
  7         23  
351             # print ">>$_<<\n";
352 7 50 66     27 $res.="\n" if $res and $res ne " ";
353 7         24 $res.="# $_";
354             }
355 7         45 return $res;
356             }
357 794         1977 return "";
358             }
359             sub writeList {
360 150     150 1 207 my $self=shift;
361 150         444 local $_;
362 150         163 my $deep=shift;
363 150         146 my $ref=shift;
364 150         269 my $res="";
365 150         206 my $tree=$self->{TREE};
366 150 50 33     479 if ($$self{Ref}->{$ref} and $self->{TREE}) {
367             #confess($tree);
368 0         0 $res.="@".$$self{Ref}->{$ref}."\n";
369             #$self->warn("Cross-reference ignored");
370 0         0 return $res;
371             }
372 150   50     511 $$self{Ref}->{$ref}=($tree || "->");
373 150         282 $res.="(".$self->commentary."\n";
374 150         197 my $i=0;
375 150         150 foreach my $r (@{$ref}) {
  150         266  
376 600         1132 $self->{TREE}=$tree."->$i";
377 600 100       1200 if (not ref($r)) {
    100          
    50          
378 500         1300 $res.=$$self{Indent} x $deep.$self->escape($r).$self->commentary."\n";
379             }
380             elsif (ref $r eq "HASH") {
381 50         94 $res.=$$self{Indent} x $deep;
382 50         164 $res.=$self->writeMap($deep+1,$r);
383             }
384             elsif (ref $r eq "ARRAY") {
385 50         99 $res.=$$self{Indent} x $deep;
386 50         134 $res.=$self->writeList($deep+1,$r);
387             }
388             else {
389 0         0 $self->warn("Non sofu reference");
390             }
391 600         1050 $i++;
392            
393             }
394 150         745 return $res.$$self{Indent} x --$deep.")\n";
395             }
396             sub writeMap {
397 150     150 1 262 my $self=shift;
398 150         200 local $_;
399 150         217 my $deep=shift;
400 150         156 my $ref=shift;
401 150         251 my $tree=$self->{TREE};
402 150         187 my $res="";
403             #print Data::Dumper->Dump([$$self{Ref}]);
404 150 100 66     588 if ($$self{Ref}->{$ref} and $self->{TREE}) {
405             #confess();
406 50         133 $res.="@".$$self{Ref}->{$ref}."\n";
407             #$self->warn("Cross-reference ignored");
408 50         231 return $res;
409             }
410 100   100     564 $$self{Ref}->{$ref}=($tree || "->");
411 100 100 100     370 $res.="{".$self->commentary."\n" if $deep or not $$self{Libsofucompat};
412 100         131 foreach (sort keys %{$ref}) {
  100         423  
413 225         441 my $wkey=$self->keyescape($_);
414 225 50 33     1068 $self->warn("Impossible Name for a Map-Entry: \"$wkey\"") if not $wkey or $wkey=~m/[\=\"\}\{\(\)\s\n]/;
415 225         531 $self->{TREE}=$tree."->$_";
416 225 100       789 unless (ref $$ref{$_}) {
    100          
    50          
417 50         163 $res.=$$self{Indent} x $deep."$wkey = ".$self->escape($$ref{$_}).$self->commentary."\n";
418             }
419             elsif (ref $$ref{$_} eq "HASH") {
420 75         227 $res.=$$self{Indent} x $deep."$wkey = ";
421 75         236 $res.=$self->writeMap($deep+1,$$ref{$_});
422             }
423             elsif (ref $$ref{$_} eq "ARRAY") {
424 100         243 $res.=$$self{Indent} x $deep."$wkey = ";
425 100         313 $res.=$self->writeList($deep+1,$$ref{$_});
426             }
427             else {
428 0         0 $self->warn("non Sofu reference");
429             }
430            
431             }
432 100 100 100     533 $res.=$$self{Indent} x --$deep."}\n" if $deep or not $$self{Libsofucompat};
433 100         655 return $res;
434             }
435             sub write {
436 24     24 1 45 my $self=shift;
437 24         56 local $_;
438 24         35 my $file=shift;
439 24         25 my $fh;
440 24         64 $$self{TREE}="";
441 24 100       158 unless (ref $file) {
    100          
    50          
442 1         3 $$self{CurFile}=$file;
443 1 50       150 open $fh,">:raw:encoding(UTF-16)",$$self{CurFile} or die "Sofu error open: $$self{CurFile} file: $!";
444             }
445             elsif (ref $file eq "SCALAR") {
446 1         3 $$self{CurFile}="Scalarref";
447 1         5 utf8::upgrade($$file);
448 1 50   1   12 open $fh,">:utf8",$file or die "Can't open perlIO: $!";
  1         2  
  1         7  
  1         60  
449             }
450             elsif (ref $file eq "GLOB") {
451 22         56 $$self{CurFile}="FileHandle";
452 22         32 $fh=$file;
453             }
454             else {
455 0         0 $self->warn("The argument to read or write has to be a filename, reference to a scalar or filehandle");
456 0         0 return;
457             }
458 24         1489 my $ref=shift;
459             #use Data::Dumper;
460             #print Data::Dumper->Dump([$ref]);
461 24         74 $self->{Commentary}={};
462 24         99 $self->comment(@_);
463 24 50       151 $$self{Indent}="\t" unless $$self{SetIndent};
464 24         50 $$self{Libsofucompat}=1;
465 24         31 %{$$self{Ref}}=();
  24         456  
466             #$self->{Ref}->{$ref}="->";
467 24         103 print $fh $self->commentary,"\n";
468 24 50       108 unless (ref $ref) {
    50          
    0          
469 0         0 print $fh "Value=".$self->escape($ref);
470             }
471             elsif (ref $ref eq "HASH") {
472 24         121 print $fh $self->writeMap(0,$ref);
473             }
474             elsif (ref $ref eq "ARRAY") {
475 0         0 print $fh "Value=".$self->writeList(0,$ref);
476             }
477             else {
478 0         0 $self->warn("non Sofu reference");
479 0         0 return "";
480             }
481 24         58 $$self{Libsofucompat}=0;
482 24         47 $$self{Indent}="";
483             #close $fh if ref $file;
484 24         35 $$self{CurFile}="";
485 24         184 return 1;
486             }
487              
488              
489             sub read {
490 60     60 1 135 my $self=shift;
491 60         143 local $_;
492 60         104 my $file=shift;
493 60         86 my $fh;
494 60         323 $$self{TREE}="";
495 60         259 $$self{OBJECT}=0;
496 60         120 $$self{CURRENT}=0;
497 60         169 $$self{References}=[];
498 60         393 $self->{Commentary}={};
499 60         208 %{$$self{Ref}}=();
  60         477  
500 60         118 my $guess=0;
501 60 100       243 unless (ref $file) {
    50          
    0          
502 48         112 $$self{CurFile}=$file;
503 48 50       2611 open $fh,$$self{CurFile} or die "Sofu error open: $$self{CurFile} file: $!";
504 48         114 $guess=1;
505 48         170 binmode $fh;
506             #eval {require File::BOM;my ($e,$sp)=File::BOM::defuse($fh);$$self{Ret}.=$sp;$e=$e;};undef $@;
507             }
508             elsif (ref $file eq "SCALAR") {
509 12         46 $$self{CurFile}="Scalarref";
510 12 100 50     126 open $fh,"<:utf8",$file or die "Can't open perlIO: $!" if utf8::is_utf8($$file);
511 12 50 50     298 open $fh,"<",$file or die "Can't open perlIO: $!" if !utf8::is_utf8($$file);
512             }
513             elsif (ref $file eq "GLOB") {
514 0         0 $$self{CurFile}="FileHandle";
515 0         0 $fh=$file;
516             }
517             else {
518 0         0 $self->warn("The argument to read or write has to be a filename, reference to a scalar or filehandle");
519 0         0 return;
520             }
521 60         125 my $text=do {local $/,<$fh>};
  60         1987  
522             {
523 60         106 my $b = substr($text,0,2);
  60         185  
524 60         155 my $u = substr($text,2,1);
525 60 100       357 if ($b eq "So") {
526 23         55 $b=substr($text,0,4);
527 23 50       112 if ($b eq "Sofu") {
528 23         68 $b=substr($text,4,2);
529 23         60 $u=substr($text,6,1);
530             }
531             }
532 60 100 100     755 if (($b eq "\x{00}\x{00}" or $b eq "\x{01}\x{00}" or $b eq "\x{00}\x{01}") and $u ne "\x{fe}") { #Assume Binary
      100        
533 26         14044 require Data::Sofu::Binary;
534 26 50       212 $bdriver = Data::Sofu::Binary->new() unless $bdriver;
535 26         220 my ($tree,$c) = $bdriver->read(\$text);
536 26         139 $self->comment($c);
537 26         130 $self->wasbinary(1);
538 26 50       167 if (wantarray) {
539 0         0 return %{$tree};
  0         0  
540             }
541 26         4115 return $tree;
542             }
543              
544             }
545 34 100       90 if ($guess) {
546 30         158 my $enc=guess_encoding($text);
547 30 100       9963 $text=$enc->decode($text) if ref $enc;
548 30 100       142 $text=Encode::decode("UTF-8",$text) unless ref $enc;
549             }
550 34 100       202 close $fh if ref $file;
551 34         124 $$self{CurFile}="";
552 34 100       294 substr($text,0,1,"") if substr($text,0,1) eq chr(65279); # UTF-8 BOM (Why ain't it removed ?)
553 34         130 my $u=$self->unpack($text);
554             #print Data::Dumper->Dump([$u]);
555 34 50       98 if (wantarray) {
556 0 0       0 return () unless $u;
557 0 0       0 return %{$u} if ref $u eq "HASH";
  0         0  
558 0         0 return (Value=>$u);
559             }
560 34 50       92 return unless $u;
561 34 50       1562 return $u if ref $u eq "HASH";
562 0         0 return {Value=>$u};
563             # $self->warn("Unpack error: $u") unless ref $u;
564             # return %{$u};
565             }
566              
567             sub pack {
568 1     1 1 2 my $self=shift;
569 1         3 my $ref=shift;
570 1         3 local $_;
571 1         5 $self->{Commentary}={};
572 1         4 $self->comment(@_);
573 1         3 $$self{TREE}="";
574 1         3 %{$$self{Ref}}=();
  1         39  
575             #$self->{Ref}->{$ref}="->";
576 1 50       5 $$self{Indent}=$$self{SetIndent} if $$self{SetIndent};
577 1         3 $$self{Counter}=0;
578 1 50       9 unless (ref $ref) {
    50          
    0          
579 0         0 return $self->commentary.$self->escape($ref);
580             }
581             elsif (ref $ref eq "HASH") {
582 1         6 return $self->commentary.$self->writeMap(0,$ref);
583             }
584             elsif (ref $ref eq "ARRAY") {
585 0         0 return $self->commentary.$self->writeList(0,$ref);
586             }
587             else {
588 0         0 $self->warn("non Sofu reference");
589 0         0 return "";
590             }
591             }
592             sub unpack($) {
593 39     39 1 70 my $self=shift;
594 39         58 local $_;
595 39         87 $$self{TREE}="";
596 39         80 $$self{Counter}=0;
597 39         155 ($self->{Escape},$self->{String},$self->{COUNT})=(0,0,0);
598 39         72 $$self{Line}=1;
599 39         165 $$self{READLINE}=shift()."\n";
600 39         185 $$self{LENGTH}=length $$self{READLINE};
601 39         57 %{$$self{Ref}}=();
  39         105  
602 39         92 $$self{CURRENT}=0;
603 39         87 $$self{References}=[];
604 39         208 $self->{Commentary}={};
605 39         66 my $c;
606 39         58 my $bom=chr(65279);
607 39   66     119 1 while ($c=$self->get() and ($c =~ m/\s/ or $c eq $bom));
      33        
608 39 50       93 return unless defined $c;
609 39 100       401 if ($c eq "{") {
    50          
    50          
    50          
    50          
610 2         3 my $result;
611 2         7 $result=$self->parsMap;
612 2         5 $$self{Ref}->{""}=$result;
613 2         9 $self->postprocess();
614 2   66     7 1 while ($c=$self->get() and $c =~ m/\s/);
615 2 50       6 if ($c=$self->get()) {
616 0         0 $self->warn("Trailing Characters: $c");
617             }
618 2         12 return $result;
619             }
620             elsif ($c eq "(") {
621 0         0 my $result;
622 0         0 $result=$self->parsList;
623 0         0 $$self{Ref}->{""}=$result;
624 0         0 $self->postprocess();
625 0   0     0 1 while ($c=$self->get() and $c =~ m/\s/);
626 0 0       0 if ($c=$self->get()) {
627 0         0 $self->warn("Trailing Characters: $c");
628             }
629 0         0 return $result;
630            
631             }
632             elsif ($c eq "\"") {
633 0         0 my $result;
634 0         0 $result=$self->parsValue;
635 0         0 $$self{Ref}->{""}=$result;
636 0         0 $self->postprocess();
637 0   0     0 1 while ($c=$self->get() and $c =~ m/\s/);
638 0 0       0 if ($c=$self->get()) {
639 0         0 $self->warn("Trailing Characters: $c");
640             }
641 0         0 return $result;
642             }
643             elsif ($c eq "<") {
644 0         0 my $x;
645 0   0     0 1 while ($x=$self->get() and $x =~ m/\s/);
646 0 0 0     0 if ($x eq "!" or $x eq "S" or $x eq "?") { #
      0        
647 0         0 require Data::Sofu::SofuML;
648 0 0       0 $mldriver=Data::Sofu::SofuML->new unless $mldriver;
649 0 0       0 if ($$self{OBJECT}) {
650 0         0 return $mldriver->load($$self{READLINE});
651             }
652 0         0 my ($r,$c) = $mldriver->read($$self{READLINE});
653 0         0 $self->{Commentary}=$c;
654 0         0 return $r;
655             }
656             else {
657 0         0 $self->{COUNT}=0;
658 0         0 my $result=$self->parsMap;
659 0         0 $$self{Ref}->{""}=$result;
660 0         0 $self->postprocess();
661 0   0     0 1 while ($c=$self->get() and $c =~ m/\s/);
662 0 0       0 if ($c=$self->get()) {
663 0         0 $self->warn("Trailing Characters: $c");
664             }
665 0         0 return $result;
666             }
667             }
668             elsif ($c!~m/[\=\"\}\{\(\)\s\n]/) {
669 37         83 $$self{Ret}=$c;
670 37         42 my $result;
671 37         182 $result=$self->parsMap;
672 37         127 $$self{Ref}->{""}=$result;
673 37         129 $self->postprocess();
674 37   66     184 1 while ($c=$self->get() and $c =~ m/\s/);
675 37 50       111 if ($c=$self->get()) {
676 0         0 $self->warn("Trailing Characters: $c");
677             }
678 37         214 return $result;
679             }
680             else {
681 0         0 $self->warn("Nothing to unpack: $c");
682 0         0 return 0;
683             }
684             }
685             sub get() {
686 17802     17802 1 28818 my $self=shift;
687 17802         17282 local $_;
688 17802 100       36915 if ($$self{Ret}) {
689 99         321 my $ch=substr($$self{Ret},0,1,"");
690 99         496 return $ch;
691             }
692 17703 0 33     33377 return shift if @_ and $_[0] and $_[0]!="";
      0        
693 17703 50       35317 $self->{LENGTH}=length $$self{READLINE} unless $self->{LENGTH};
694 17703 100 50     43349 $self->storeComment and return undef if $self->{COUNT}>=$self->{LENGTH};
695 17566         51853 my $c=substr($$self{READLINE},$self->{COUNT}++,1);
696 17566 50       43894 print "GET '$c'\n" if $$self{Debug};
697             #print "DEBUG: $self->{COUNT}=$c\n";
698 17566 100       32233 if ($c eq "\"") {
699 1536 50       4361 $self->{String}=!$self->{String} unless $self->{Escape};
700             }
701 17566 100       28716 if ($c eq "\\") {
702 240         1702 $self->{Escape}=!$self->{Escape};
703             }
704             else {
705 17326         46714 $self->{Escape}=0;
706             }
707 17566 50 66     38017 if ($c eq "#" and not $self->{String} and not $self->{Escape}){
      66        
708 78         291 my $i=index($$self{READLINE},"\n",$self->{COUNT});
709 78         225 my $comm = substr($$self{READLINE},$self->{COUNT},$i-$self->{COUNT});
710 78         157 chomp $comm;
711 78         160 $comm=~s/\r//g; #I hate Windows...!
712             #die $comm;
713 78         257 push @{$self->{COMMENT}},$comm;
  78         189  
714             #push @{$self->{COMMENT}},substr($$self{READLINE},$self->{COUNT},$i-$self->{COUNT});
715             #print "DEBUG JUMPING FROM $self->{COUNT} to INDEX=$i";
716 78         139 $self->{COUNT}=$i+1;
717 78         108 $c="\n";
718             }
719 17566         22155 ++$$self{Counter};
720 17566 100 100     65601 if ($c and $c eq "\n") {
721 1678         2228 $$self{Counter}=0;
722 1678         1949 $$self{Line}++;
723             }
724 17566 50 33     39846 print "END" if not defined $c and $$self{Debug} ;
725 17566         55707 return $c;
726             }
727             sub storeComment {
728 2652     2652 1 3333 my $self=shift;
729             #if ($$self{OBJECT}) {
730             # $$self{Ref}->{$self->{TREE}}->appendComment($self->{COMMENT});
731             #}
732 2652         4118 my $tree=$self->{TREE};
733 2652 100       4729 $tree="=" unless $tree;
734             #print "DEBUG: $tree, @{$self->{COMMENT}} , ".join(" | ",caller())."\n";
735 2652 100       2451 push @{$self->{Commentary}->{$tree}},@{$self->{COMMENT}} if @{$self->{COMMENT}};
  73         228  
  73         174  
  2652         6250  
736 2652         6189 $self->{COMMENT}=[];
737             }
738              
739             sub postprocess {
740 39     39 1 58 my $self=shift;
741 39         183 $self->{Ref}->{"="} = $self->{Ref}->{"->"} = $self->{Ref}->{""};
742 39 100       112 if ($$self{OBJECT}) {
743 3         7 foreach my $e (@{$$self{References}}) {
  3         24  
744 15 50       20 next if ${$e}->valid();
  15         63  
745 15         22 my $target = ${$e}->follow()."";
  15         53  
746 15 100 66     117 $target="->".$target if $target and $target !~ m/^->/;
747 15 50       57 ${$e}->dangle($self->{Ref}->{$target}) if $self->{Ref}->{$target};
  15         76  
748             }
749 3         9 foreach my $key (keys %{$$self{Commentary}}) {
  3         18  
750 12 50       162 $self->{Ref}->{$key}->setComment($$self{Commentary}->{$key}) if $self->{Ref}->{$key};
751             }
752             }
753             else {
754 36         57 foreach my $e (@{$$self{References}}) {
  36         132  
755 101         162 my $target = $$$e;
756 101 100 66     1804 $target="->".$target if $target and $target !~ m/^->/;
757 101         127 $$e = undef;
758 101 50       469 $$e = $self->{Ref}->{$target} if $self->{Ref}->{$target};
759             }
760             }
761             }
762             sub warn {
763 5     5   75393 no warnings;
  5         12  
  5         62785  
764 0     0 1 0 my $self=shift;
765 0         0 local $_;
766 0         0 confess "Sofu warning: \"".shift(@_)."\" File: $$self{CurFile}, Line : $$self{Line}, Char : $$self{Counter}, Caller:".join(" ",caller);
767 0         0 1;
768             }
769             sub escape {
770 550     550 1 517 shift;
771 550         590 my $text=shift;
772 550         808 return Sofuescape($text);
773             }
774             sub Sofuescape {
775 607     607 1 634 my $text=shift;
776 607 100       1702 return "UNDEF" unless defined $text; #TODO: UNDEF = Undefined
777 582 50       1040 if ($fullescape) {
778             #print "$text : ";
779 0 0       0 $text=~s/([[:^print:]\s\<\>\=\"\}\{\(\)])/ord($1) > 65535 ? sprintf("\\U%08x",ord($1)) : sprintf("\\u%04x",ord($1))/eg;
  0         0  
780             #print "$text \n";
781 0         0 return "\"$text\"";
782             }
783             else {
784 582         767 $text=~s/\\/\\\\/g;
785 582         780 $text=~s/\n/\\n/g;
786 582         634 $text=~s/\r/\\r/g;
787 582         673 $text=~s/\"/\\\"/g;
788 582         2014 return "\"$text\"";
789             }
790             }
791             sub deescape {
792 960     960 1 1203 my $self=shift;
793 960         957 local $_;
794 960         1068 my $text="";
795 960         1289 my $ttext=shift;
796 960         1056 my $noescape=shift;
797 960 100       1643 if ($noescape) {
798 192 100       750 if ($ttext =~ m/^\@(.+)$/) {
799             #return $$self{Ref}->{$1} || $self->warn("Can't find reference to $1.. References must first defined then called. You can't reference a string or number")
800 116 100       275 if ($$self{OBJECT}) {
801 15         90 return Data::Sofu::Reference->new($1);
802             }
803 101         282 my $text=$1;
804 101         704 return \$text;
805              
806             }
807 76 100       194 if ($$self{OBJECT}) {
808 15 100       68 return Data::Sofu::Undefined->new() if $ttext eq "UNDEF";
809 12         41 return Data::Sofu::Value->new($ttext);
810             }
811 61 100       310 return undef if $ttext eq "UNDEF";
812 24         131 return $ttext;
813             }
814             else {
815 768         1134 my $char;
816 768         968 my $escape=0;
817 768         760 my $count=0;
818 768         1295 my $len=length $ttext;
819 768         1806 while ($count <= $len) {
820 7043         9283 my $char=substr($ttext,$count++,1);
821 7043 100       12653 if ($char eq "\\") {
822 240 50       432 $text.="\\" if $escape;
823 240         489 $escape=!$escape;
824             }
825             else {
826 6803 100       11929 if ($escape) {
827 240 100       595 if (lc($char) eq "n") {
    50          
    0          
    0          
    0          
828 200         253 $text.="\n";
829             }
830             elsif (lc($char) eq "r") {
831 40         72 $text.="\r";
832             }
833             elsif (lc($char) eq "\"") {
834 0         0 $text.="\"";
835             }
836             elsif ($char eq "u") {
837 0         0 my $val=hex(substr($ttext,$count,4));
838 0         0 $text.=chr($val);
839 0         0 $count+=4;
840             }
841             elsif ($char eq "U") {
842 0         0 my $val=hex(substr($ttext,$count,8));
843 0         0 $count+=8;
844 0         0 $text.=chr($val);
845             }
846             else {
847 0         0 $self->warn("Deescape: Can't deescape: \\$char");
848             }
849 240         479 $escape=0;
850             }
851             else {
852 6563         13458 $text.=$char;
853             }
854             }
855             }
856 768 100       1859 return Data::Sofu::Value->new($text) if $self->{OBJECT};;
857 726         2333 return $text;
858             }
859             }
860             sub parsMap {
861 159     159 1 227 my $self=shift;
862 159         191 local $_;
863 159         189 my %result;
864 159         215 my $comp="";
865 159         194 my $eq=0;
866 159         163 my $char;
867 159         222 my $tree=$self->{TREE};
868 159         169 my @order;
869 159         314 while (defined($char=$self->get())) {
870 4587 50       18296 print "ParsCompos $char\n" if $$self{Debug};
871 4587 100       22966 if ($char!~m/[\=\"\}\{\(\)\s\n]/s) {
    100          
    100          
    100          
    100          
    100          
    50          
872 2123 100       3118 if ($eq) {
873 43         101 $self->storeComment;
874 43         108 my $keyu = $self->keyunescape($comp);
875 43         136 $self->{TREE}=$tree."->".$comp;
876             #print ">> > >> > > > > DEBUG: tree=$self->{TREE}\n";
877 43         115 $result{$keyu}=$self->getSingleValue($char);
878 43         100 push @order,$keyu;
879 43 50       129 push @{$$self{References}},\$result{$keyu} if refe $result{$keyu};
  43         247  
880 43         80 $comp="";
881 43         105 $eq=0;
882             }
883             else {
884 2080         4726 $comp.=$char;
885             }
886             }
887             elsif ($char eq "=") {
888 360 50       693 $self->warn("MapEntry unnamed!") if ($comp eq "");
889 360         703 $self->storeComment;
890 360         1385 $self->{TREE}=$tree."->".$comp;
891 360         913 $eq=1;
892             }
893             elsif ($char eq "{") {
894 80 50       185 $self->warn("Missing \"=\"!") unless $eq;
895 80 50       185 $self->warn("MapEntry unnamed!") if ($comp eq "");
896 80         197 $self->storeComment;
897 80         222 $self->{TREE}=$tree."->".$comp;
898 80         140 my $res={};
899 80         261 $res=$self->parsMap();
900 80         311 $$self{Ref}->{$self->{TREE}}=$res;
901 80         184 my $kkey=$self->keyunescape($comp);
902 80         240 push @order,$kkey;
903 80         237 $result{$kkey} = $res;
904 80         135 $comp="";
905 80         239 $eq=0;
906             }
907             elsif ($char eq "}") {
908 124         319 $self->storeComment;
909 124         231 $self->{TREE}=$tree;
910 124 100       385 return Data::Sofu::Map->new(\%result,[@order]) if $self->{OBJECT};
911 115         456 return \%result;
912             }
913             elsif ($char eq "\"") {
914 77 50       364 if (not $eq) {
915 0         0 $self->warn("Unclear Structure detected: was the last entry a value or a key (maybe you forgot either \"=\" before this or the \'\"\' around the value");
916 0         0 $eq=1;
917             }
918 77         183 $self->storeComment;
919 77         216 $self->{TREE}=$tree."->".$comp;
920             #print ">>>>>>>>>>>>>>>>>>>>>>>>DEBUG: tree=$self->{TREE}\n";
921 77 50       154 $self->warn("Missing \"=\"!") unless $eq;
922 77 50       167 $self->warn("MapEntry unnamed!") if ($comp eq "");
923            
924 77         238 my $kkey=$self->keyunescape($comp);
925 77         135 push @order,$kkey;
926 77         228 $result{$kkey}=$self->parsValue();
927 77         124 $comp="";
928 77         186 $eq=0;
929             }
930             elsif ($char eq "(") {
931 160 50       340 if (not $eq) {
932 0         0 return $self->parsList();
933             }
934 160 50       375 $self->warn("Missing \"=\"!") unless $eq;
935 160 50       328 $self->warn("MapEntry unnamed!") if ($comp eq "");
936 160         345 $self->storeComment;
937 160         356 $self->{TREE}=$tree."->".$comp;
938 160         369 my $res=[];
939 160         451 $res=$self->parsList();
940 160         592 $$self{Ref}->{$self->{TREE}}=$res;
941 160         427 my $kkey=$self->keyunescape($comp);
942 160         282 push @order,$kkey;
943 160         466 $result{$kkey} = $res;
944 160         230 $comp="";
945 160         541 $eq=0;
946             }
947             elsif ($char eq ")") {
948 0         0 $self->warn("What's a \"$char\" doing here?");
949             }
950             }
951 35 100       149 return Data::Sofu::Map->new(\%result,[@order]) if $self->{OBJECT};
952 32         146 return \%result;
953             }
954             sub parsValue {
955 77     77 1 107 my $self=shift;
956 77         90 local $_;
957 77         119 my @result;
958 77         116 my $cur="";
959 77         86 my $in=1;
960 77         249 my $escape=0;
961 77         84 my $char;
962 77         83 my $i=0;
963 77         133 my $tree=$self->{TREE};
964 77         117 my $starttree=$self->{TREE};
965 77         151 $self->storeComment;
966 77         367 $self->{TREE}=$tree."->0";
967 77         198 while (defined($char=$self->get())) {
968 915 50       2129 print "ParsValue $char\n" if $$self{Debug};
969 915 100       1551 if ($in) {
970 739 100       2291 if ($char eq "\"") {
    50          
971 77 50       169 if ($escape) {
972 0         0 $escape=0;
973 0         0 $cur.=$char;
974             }
975             else {
976 77         200 push @result,$self->deescape($cur,0);
977 77 50       226 push @{$$self{References}},\$result[-1] if refe $result[-1];
  0         0  
978 77         229 $self->storeComment;
979 77         465 $self->{TREE}=$tree."->".$i++;
980 77         304 $$self{Ref}->{$self->{TREE}}=$result[-1];
981 77         104 $cur="";
982 77         190 $in=0;
983             }
984             }
985             elsif ($char eq "\\") {
986 0 0       0 if ($escape) {
987 0         0 $escape=0;
988             }
989             else {
990 0         0 $escape=1;
991             }
992 0         0 $cur.=$char;
993             }
994             else {
995 662         821 $escape=0;
996 662         1601 $cur.=$char;
997             }
998              
999             }
1000             else {
1001 176 100       1839 if ($char!~m/[\=\"\}\{\(\)\s\n]/s) {
    50          
    50          
    50          
    100          
    50          
    50          
1002 14         29 $$self{Ret}=$char;
1003 14 50       58 if (@result>1) {
    50          
1004 0         0 $self->{TREE}=$tree."->$#result";
1005 0         0 $self->storeComment;
1006 0         0 my $res=[@result];
1007 0 0       0 $res=Data::Sofu::List->new($res) if $self->{OBJECT};
1008 0         0 $$self{Ref}->{$tree}=$res;
1009 0         0 return $res;
1010             }
1011             elsif (@result) {
1012 14         25 $self->{TREE}=$tree;
1013 14         39 $self->storeComment;
1014 14         66 $$self{Ref}->{$tree}=\$result[0];
1015 14         71 return $result[0];
1016             }
1017             else { #This can't happen
1018 0         0 return undef;
1019             }
1020             }
1021             elsif ($char eq "=") {
1022 0         0 $self->warn("What's a \"$char\" doing here?");
1023             }
1024             elsif ($char eq "\"") {
1025 0         0 $in=1;
1026             }
1027             elsif ($char eq "{") {
1028 0         0 $self->storeComment;
1029 0         0 $self->{TREE}=$tree."->".++$i;
1030 0         0 my $res={};
1031 0         0 %{$res}=$self->parsMap();
  0         0  
1032 0         0 $$self{Ref}->{$self->{TREE}}=$res;
1033 0         0 push @result,$res;
1034             }
1035             elsif ($char=~m/[\}\)]/) {
1036 39         89 $$self{Ret}=$char;
1037 39 50       94 if ($cur ne "") {
1038 0 0       0 $cur=Data::Sofu::Value->new($cur) if $self->{OBJECT};
1039 0 0       0 if (@result) {
1040 0         0 $self->{TREE}=$tree."->".$#result+1;
1041 0         0 $self->storeComment;
1042 0         0 my $res={@result,$cur};
1043 0 0       0 $res=Data::Sofu::List->new($res) if $self->{OBJECT};
1044 0         0 $$self{Ref}->{$tree}=$res;
1045 0         0 return $res;
1046             }
1047             else {
1048 0         0 $self->{TREE}=$tree;
1049 0         0 $self->storeComment;
1050             #$self{Ref}->{$tree}=\$cur;
1051 0         0 $$self{Ref}->{$tree}=$cur;
1052 0         0 return $cur;
1053             }
1054             }
1055             else {
1056 39 50       140 if (@result>1) {
    50          
1057 0         0 $self->{TREE}=$tree."->$#result";
1058 0         0 $self->storeComment;
1059 0         0 my $res=[@result];
1060 0 0       0 $res=Data::Sofu::List->new($res) if $self->{OBJECT};
1061 0         0 $$self{Ref}->{$tree}=$res;
1062 0         0 return $res;
1063             }
1064             elsif (@result) {
1065 39         75 $self->{TREE}=$tree;
1066 39         90 $self->storeComment;
1067             #$$self{Ref}->{$tree}=\$result[0];
1068 39         155 $$self{Ref}->{$tree}=$result[0];
1069 39         169 return $result[0];
1070             }
1071             else {
1072             #$$self{Ref}->{$tree}=\$cur;
1073 0 0       0 $cur=Data::Sofu::Value->new($cur) if $self->{OBJECT};
1074 0         0 $$self{Ref}->{$tree}=$cur;
1075 0         0 return $cur;
1076             }
1077             }
1078             }
1079             elsif ($char eq "(") {
1080 0         0 $self->storeComment;
1081 0         0 $self->{TREE}=$tree."->".++$i;
1082 0         0 my $res=[];
1083 0         0 $res=$self->parsList();
1084 0         0 $$self{Ref}->{$self->{TREE}}=$res;
1085 0         0 push @result,$res;
1086             }
1087             elsif ($char eq ")") {
1088 0         0 $self->warn("What's a \"$char\" doing here?");
1089             }
1090             }
1091             }
1092 24 50       62 if ($cur ne "") {
1093 0 0       0 $cur=Data::Sofu::Value->new($cur) if $self->{OBJECT};
1094 0 0       0 if (@result) {
1095 0         0 $self->{TREE}=$tree."->".$#result+1;
1096 0         0 $self->storeComment;
1097 0         0 push @result,$cur;
1098 0         0 my $res=[@result];
1099 0 0       0 $res=Data::Sofu::List->new($res) if $self->{OBJECT};
1100 0         0 $$self{Ref}->{$tree}=$res;
1101 0         0 return $res;
1102             }
1103             else {
1104 0         0 $self->{TREE}=$tree;
1105             #$$self{Ref}->{$tree}=\$cur;
1106 0         0 $$self{Ref}->{$tree}=$cur;
1107 0         0 $self->storeComment;
1108 0         0 return $cur;
1109             }
1110             }
1111             else {
1112 24 50       113 if (@result>1) {
    50          
1113 0         0 $self->{TREE}=$tree."->$#result";
1114 0         0 $self->storeComment;
1115 0         0 my $res=[@result];
1116 0 0       0 $res=Data::Sofu::List->new($res) if $self->{OBJECT};
1117 0         0 $$self{Ref}->{$tree}=$res;
1118 0         0 return $res;
1119             }
1120             elsif (@result) {
1121 24         46 $self->{TREE}=$tree;
1122 24         59 $self->storeComment;
1123             #$$self{Ref}->{$tree}=\$result[0];
1124 24         72 $$self{Ref}->{$tree}=$result[0];
1125 24         174 return $result[0];
1126             }
1127             else {
1128 0 0       0 $cur=Data::Sofu::Value->new($cur) if $self->{OBJECT};
1129 0         0 $$self{Ref}->{$tree}=$cur;
1130 0         0 return $cur;
1131             }
1132             }
1133             }
1134             sub getSingleValue {
1135 192     192 1 232 my $self=shift;
1136 192         207 local $_;
1137 192         277 my $res="";
1138 192 50       445 $res=shift if @_;
1139 192         207 my $char;
1140 192         356 while (defined($char=$self->get())) {
1141 1417 50       2862 print "ParsSingle $char\n" if $$self{Debug};
1142 1417 100       4110 if ($char!~m/[\=\"\}\{\(\)\s]/) {
    50          
    100          
    50          
1143 1225         2587 $res.=$char;
1144             }
1145             elsif ($char=~m/[\=\"\{\(]/) {
1146 0         0 $self->warn("What's a \"$char\" doing here?");
1147             }
1148             elsif ($char=~m/[\}\)]/) {
1149 9         26 $$self{Ret}=$char;
1150 9         32 return $$self{Ref}->{$self->{TREE}}=$self->deescape($res,1);
1151             }
1152             elsif ($char=~m/\s/) {
1153 183         414 return $$self{Ref}->{$self->{TREE}}=$self->deescape($res,1);
1154 0         0 return $res;
1155             }
1156             }
1157 0         0 $self->warn ("Unexpected EOF");
1158 0         0 return $$self{Ref}->{$self->{TREE}}=$self->deescape($res,1);
1159             }
1160             sub parsList {
1161 240     240 1 291 my $self=shift;
1162 240         255 local $_;
1163 240         242 my @result;
1164 240         299 my $cur="";
1165 240         281 my $in=0;
1166 240         259 my $escape=0;
1167 240         211 my $char;
1168 240         217 my $i=0;
1169 240         345 my $tree=$self->{TREE};
1170 240         455 $self->storeComment;
1171             #$self->{TREE}=$tree."->0";
1172 240         494 while (defined($char=$self->get())) {
1173 10649 50       23799 print "ParsList $char\n" if $$self{Debug};
1174 10649 100       18391 if ($in) {
1175 6304 100       13318 if ($char eq "\"") {
    100          
1176 691 50       1180 if ($escape) {
1177 0         0 $escape=0;
1178 0         0 $cur.=$char;
1179             }
1180             else {
1181 691         1839 push @result,$self->deescape($cur,0);
1182 691 50       2050 push @{$$self{References}},\$result[-1] if refe $result[-1];
  0         0  
1183 691         1592 $self->storeComment;
1184 691         2037 $self->{TREE}=$tree."->".$i++;
1185 691         2394 $$self{Ref}->{$self->{TREE}}=$result[-1];
1186 691         857 $cur="";
1187 691         1922 $in=0;
1188             }
1189             }
1190             elsif ($char eq "\\") {
1191 240 50       378 if ($escape) {
1192 0         0 $escape=0;
1193             }
1194             else {
1195 240         439 $escape=1;
1196             }
1197 240         511 $cur.=$char;
1198             }
1199             else {
1200 5373         5593 $escape=0;
1201 5373         11711 $cur.=$char;
1202             }
1203              
1204             }
1205             else {
1206 4345 100       30539 if ($char!~m/[\=\"\}\{\(\)\s\n]/) {
    50          
    100          
    100          
    50          
    100          
    100          
1207 149         328 $self->storeComment;
1208 149         585 $self->{TREE}=$tree."->".$i++;
1209 149         422 push @result,$self->getSingleValue($char);
1210 149 100       376 push @{$$self{References}},\$result[-1] if refe $result[-1];
  73         312  
1211             }
1212             elsif ($char eq "=") {
1213 0         0 $self->warn("What's a \"$char\" doing here?");
1214             }
1215             elsif ($char eq "\"") {
1216 691         3230 $in=1;
1217             }
1218             elsif ($char eq "{") {
1219 40         99 $self->storeComment;
1220 40         132 $self->{TREE}=$tree."->".$i++;
1221 40         163 my $res={};
1222 40         163 $res=$self->parsMap();
1223 40         155 $$self{Ref}->{$self->{TREE}}=$res;
1224 40         144 push @result,$res;
1225             }
1226             elsif ($char eq "}") {
1227 0         0 $self->warn("What's a \"$char\" doing here?");
1228             }
1229             elsif ($char eq "(") {
1230 80         178 $self->storeComment;
1231 80         291 $self->{TREE}=$tree."->".$i++;
1232 80         150 my $res=[];
1233 80         1083 $res=$self->parsList();
1234 80         294 $$self{Ref}->{$self->{TREE}}=$res;
1235 80         244 push @result,$res;
1236             }
1237             elsif ($char eq ")") {
1238 240         493 $self->storeComment;
1239 240         392 $self->{TREE}=$tree;
1240 240 100       574 return Data::Sofu::List->new(\@result) if $self->{OBJECT};
1241 222         653 return \@result;
1242             }
1243             }
1244             }
1245 0         0 $self->warn ("Unexpected EOF");
1246 0 0       0 push @result,$cur if ($cur ne "");
1247 0 0       0 return Data::Sofu::List->new(\@result) if $self->{OBJECT};
1248 0         0 return \@result;
1249             }
1250             sub Sofukeyescape { #Other escaping (can be parsed faster and is Sofu 0.1 compatible)
1251 1341     1341 1 2102 my $key=shift;
1252 1341 50       2972 return "" unless defined $key;
1253 1341 50       2584 return "<>" unless $key;
1254 1341         4642 $key=~s/([[:^print:]\s\<\>\=\"\}\{\(\)])/sprintf("\<\%x\>",ord($1))/eg;
  149         2351  
1255 1341         5131 return $key;
1256             }
1257              
1258             sub Sofukeyunescape { #Other escaping (can be parsed faster)
1259 370     370 1 462 my $key=shift;
1260 370 50       871 return "" if $key eq "<>";
1261 370 50       728 return undef if $key eq "";
1262 370         938 $key=~s/\<([0-9abcdef]*)\>/chr(hex($1))/egi;
  42         313  
1263 370         1400 return $key;
1264             }
1265             sub keyescape { #Other escaping (can be parsed faster and is Sofu 0.1 compatible)
1266 225     225 1 251 my $self=shift;
1267 225         392 return Sofukeyescape(@_);
1268             }
1269              
1270             sub keyunescape { #Other escaping (can be parsed faster)
1271 360     360 1 424 my $self=shift;
1272 360         713 return Sofukeyunescape(@_);
1273             }
1274              
1275             sub packBinary {
1276 8     8 1 16 my $self=shift;
1277 8         110 require Data::Sofu::Binary;
1278 8 50       33 $bdriver = Data::Sofu::Binary->new() unless $bdriver;
1279 8         53 return $bdriver->pack(@_);
1280             }
1281              
1282             sub writeML {
1283 2     2 1 15 my $self=shift;
1284 2         4 my $file=shift;
1285 2         3 my $fh;
1286 2         936 require Data::Sofu::SofuML;
1287 2 100       22 $mldriver = Data::Sofu::SofuML->new() unless $mldriver;
1288 2 50       18 unless (ref $file) {
    50          
    0          
1289 0 0       0 open $fh,">:encoding(UTF-8)",$file or die "Sofu error open: $$self{CurFile} file: $!";
1290             }
1291             elsif (ref $file eq "SCALAR") {
1292 2 50   1   75 open $fh,">:utf8",$file or die "Can't open perlIO: $!";
  1         6  
  1         2  
  1         11  
1293             }
1294             elsif (ref $file eq "GLOB") {
1295 0         0 $fh=$file;
1296             }
1297             else {
1298 0         0 $self->warn("The argument to writeML has to be a filename, reference to a scalar or filehandle");
1299 0         0 return;
1300             }
1301 2         1643 binmode $fh;
1302 2         12 print $fh $mldriver->pack(@_);
1303             #$fh goes out of scope here!
1304             }
1305              
1306             sub packML {
1307 2     2 1 22 require Data::Sofu::SofuML;
1308 2         5 my $self=shift;
1309 2 50       8 $mldriver = Data::Sofu::SofuML->new() unless $mldriver;
1310 2         7 $mldriver->{INDENT} = "";
1311 2         12 my $a=$mldriver->pack(@_);
1312 2         6 $mldriver->{INDENT} = "\t";
1313 2         18 return $a;
1314             }
1315              
1316             sub writeBinary {
1317 44     44 1 173 my $self=shift;
1318 44         236 my $file=shift;
1319 44         78 my $fh;
1320 44         1805 require Data::Sofu::Binary;
1321 44 100       206 $bdriver = Data::Sofu::Binary->new() unless $bdriver;
1322 44 100       226 unless (ref $file) {
    50          
    0          
1323 36 50       16766 open $fh,">:raw",$file or die "Sofu error open: $$self{CurFile} file: $!";
1324             }
1325             elsif (ref $file eq "SCALAR") {
1326 8 50       195 open $fh,">",$file or die "Can't open perlIO: $!";
1327             }
1328             elsif (ref $file eq "GLOB") {
1329 0         0 $fh=$file;
1330             }
1331             else {
1332 0         0 $self->warn("The argument to writeBinary has to be a filename, reference to a scalar or filehandle");
1333 0         0 return;
1334             }
1335 44         295 binmode $fh;
1336 44         465 print $fh $bdriver->pack(@_);
1337             #$fh goes out of scope here!
1338             }
1339              
1340             1;
1341             __END__