File Coverage

blib/lib/Data/Nested.pm
Criterion Covered Total %
statement 1114 1386 80.3
branch 688 922 74.6
condition 306 456 67.1
subroutine 70 77 90.9
pod 30 30 100.0
total 2208 2871 76.9


line stmt bran cond sub pod time code
1             package Data::Nested;
2             # Copyright (c) 2008-2010 Sullivan Beck. All rights reserved.
3             # This program is free software; you can redistribute it and/or modify it
4             # under the same terms as Perl itself.
5              
6             ########################################################################
7             # TODO
8             ########################################################################
9              
10             # If no structural information is kept, merge methods can only
11             # keep/replace/append for lists but unordered non-uniform lists
12             # are allowed.
13              
14             # When specifying structure, /foo/* forces uniform if it is not
15             # already specified as non-uniform. If a structure is uniform,
16             # then applying structure to /foo/1 is equivalent to /foo/* (but
17             # a warning may be issued).
18              
19             # Add validity tests for data
20             # see Data::Domain, Data::Validator
21              
22             # Add subtract (to remove items in one NDS from another)
23             # see Data::Validate::XSD
24             # treats all lists as ordered... it's simply too complicated
25             # otherwise
26              
27             # Add clean (to remove empty paths)
28             # a hash key with a value of undef should be deleted
29             # a list element with a value of undef should be deleted if unordered
30             # a list consisting of only undefs should be deleted (and fix parent)
31             # a hash with no keys should be deleted (and fix parent)
32              
33             ########################################################################
34              
35             require 5.000;
36 93     93   37773 use strict;
  93         185  
  93         4636  
37 93     93   119661 use Storable qw(dclone);
  93         508184  
  93         8446  
38 93     93   114720 use Algorithm::Permute;
  93         390314  
  93         13876  
39 93     93   112655 use IO::File;
  93         1344649  
  93         14112  
40 93     93   880 use warnings;
  93         217  
  93         3773  
41              
42 93     93   640 use vars qw($VERSION);
  93         221  
  93         10229  
43             $VERSION = "3.12";
44              
45 93     93   558 use vars qw($_DBG $_DBG_INDENT $_DBG_OUTPUT $_DBG_FH $_DBG_POINT);
  93         195  
  93         1883889  
46             $_DBG = 0;
47             $_DBG_INDENT = 0;
48             $_DBG_OUTPUT = "dbg.out";
49             $_DBG_FH = ();
50             $_DBG_POINT = 0;
51              
52             ###############################################################################
53             # BASE METHODS
54             ###############################################################################
55             #
56             # The Data::Nested object is a hash of the form:
57             #
58             # { warn => FLAG whether to warn
59             # delim => DELIMITER the path delimiter
60             # nds => { NAME => NDS } named NDSes
61             # structure => FLAG whether to do structure
62             # blank => FLAG whether the empty
63             # string is treated as
64             # a keepable value when
65             # merging
66             # struct => { PATH => { ITEM => VAL } } structural information
67             # defstruct => { ITEM => VAL } default structure
68             # ruleset => { RULESET => { def => { ITEM => VAL },
69             # path => { PATH => VAL } } }
70             # default and path
71             # specific ruleset
72             # merge methods
73             # cache => {...} cached information
74             # }
75              
76             sub new {
77 93     93 1 124792 my($class) = @_;
78              
79 93         1506 my $self = {
80             "warn" => 0,
81             "delim" => "/",
82             "nds" => {},
83             "structure" => 1,
84             "blank" => 0,
85             "struct" => {},
86             "defstruct" => {},
87             "ruleset" => {},
88             "err" => "",
89             "errmsg" => "",
90             };
91 93         336 bless $self, $class;
92 93         513 _structure_defaults($self);
93 93         609 _merge_defaults($self);
94              
95 93         998 return $self;
96             }
97              
98             sub version {
99 0     0 1 0 my($self) = @_;
100              
101 0         0 return $VERSION;
102             }
103              
104             sub no_structure {
105 0     0 1 0 my($self) = @_;
106              
107 0         0 $$self{"structure"} = 0;
108             }
109              
110             sub blank {
111 3     3 1 22 my($self,$val) = @_;
112              
113 3         9 $$self{"blank"} = $val;
114             }
115              
116             sub err {
117 36763     36763 1 44343 my($self) = @_;
118              
119 36763         134355 return $$self{"err"};
120             }
121              
122             sub errmsg {
123 0     0 1 0 my($self) = @_;
124              
125 0         0 return $$self{"errmsg"};
126             }
127              
128             ###############################################################################
129             # PATH METHODS
130             ###############################################################################
131              
132             sub delim {
133 37716     37716 1 46053 my($self,$delim) = @_;
134 37716 100       74160 if (! defined $delim) {
135 37715         88107 return $$self{"delim"};
136             }
137 1         3 $$self{"delim"} = $delim;
138             }
139              
140             {
141             my %path = ();
142              
143             sub path {
144 41333     41333 1 56470 my($self,$path) = @_;
145 41333         51917 my $array = wantarray;
146              
147 41333 100       65891 if ($array) {
148 6386 100       13120 return @$path if (ref($path));
149 6100 100       10669 return () if (! $path);
150 6092 100       13459 return @{ $path{$path} } if (exists $path{$path});
  5121         18543  
151              
152 971         2122 my($delim) = $self->delim();
153 971         6115 my @tmp = split(/\Q$delim\E/,$path);
154 971 100 100     5439 shift(@tmp) if (! defined($tmp[0]) || $tmp[0] eq "");
155 971         3652 $path{$path} = [ @tmp ];
156 971         3665 return @tmp;
157              
158             } else {
159 34947         62531 my($delim) = $self->delim();
160 34947 100       73481 if (! ref($path)) {
161 20 50       52 return $delim if (! $path);
162 20         46 return $path;
163             }
164 34927         105957 return $delim . join($delim,@$path);
165             }
166             }
167             }
168              
169             ###############################################################################
170             # RULESET METHODS
171             ###############################################################################
172              
173             sub ruleset {
174 6     6 1 638 my($self,$name) = @_;
175 6         13 $$self{"err"} = "";
176              
177 6 50 66     80 if ($name eq "keep" ||
      66        
      33        
178             $name eq "replace" ||
179             $name eq "default" ||
180             $name eq "override") {
181 1         2 $$self{"err"} = "ndsrul03";
182 1         4 $$self{"errmsg"} = "Unable to create a ruleset using a reserved name " .
183             "[$name]";
184 1         3 return;
185             }
186              
187 5 100       38 if ($name !~ /^[a-zA-Z0-9]+$/) {
188 1         2 $$self{"err"} = "ndsrul01";
189 1         4 $$self{"errmsg"} = "A non-alphanumeric character used in a ruleset name" .
190             "[$name]";
191 1         2 return;
192             }
193              
194 4 100       14 if (exists $$self{"ruleset"}{$name}) {
195 1         3 $$self{"err"} = "ndsrul02";
196 1         3 $$self{"errmsg"} = "Attempt to create ruleset for a name already in use" .
197             " [$name].";
198 1         3 return;
199             }
200              
201 3         15 $$self{"ruleset"}{$name} = { "def" => {},
202             "path" => {} };
203 3         7 return;
204             }
205              
206             sub ruleset_valid {
207 86     86 1 759 my($self,$name) = @_;
208 86 100       544 return 1 if (exists $$self{"ruleset"}{$name});
209 5         26 return 0;
210             }
211              
212             ###############################################################################
213             # NDS METHODS
214             ###############################################################################
215              
216             # This takes $nds (which may be an NDS, or the name of a stored NDS)
217             # and it returns the actual NDS referred to, or undef if there is a
218             # problem.
219             #
220             # If $new is passed in, new structure is allowed.
221             # If $copy is passed in, a copy of the NDS is returned.
222             # If $nocheck is passed in, no structural check is done.
223             #
224             sub _nds {
225 5386     5386   8077 my($self,$nds,$new,$copy,$nocheck) = @_;
226              
227 5386 100       14512 if (! defined($nds)) {
    100          
    100          
228 140         267 return undef;
229              
230             } elsif (ref($nds)) {
231 4174 100 66     18579 if ($$self{"structure"} && ! $nocheck) {
232 728         1357 _check_structure($self,$nds,$new,());
233 728 50       1445 return undef if ($self->err());
234             }
235 4174 50       7012 if ($copy) {
236 0         0 return dclone($nds);
237             } else {
238 4174         8409 return $nds;
239             }
240              
241             } elsif (exists $$self{"nds"}{$nds}) {
242 424 50       725 if ($copy) {
243 0         0 return dclone($$self{"nds"}{$nds});
244             } else {
245 424         1174 return $$self{"nds"}{$nds};
246             }
247             } else {
248 648         946 $$self{"err"} = "ndsnam01";
249 648         1481 $$self{"errmsg"} = "No NDS stored under the name [$nds]";
250 648         1263 return undef;
251             }
252             }
253              
254             sub nds {
255 32     32 1 2493 my($self,$name,$nds,$new) = @_;
256 32         74 $$self{"err"} = "";
257 32         57 $$self{"errmsg"} = "";
258              
259             #
260             # $obj->nds($name);
261             # $obj->nds($name,"_copy");
262             #
263              
264 32 100 100     247 if (! defined $nds || $nds eq "_copy") {
265 4 50       10 if (exists $$self{"nds"}{$name}) {
266 4 100 66     15 if (defined $nds && $nds eq "_copy") {
267 1         28 return dclone($$self{"nds"}{$name});
268             } else {
269 3         18 return $$self{"nds"}{$name};
270             }
271             } else {
272 0         0 return undef;
273             }
274             }
275              
276             #
277             # $obj->nds($name,"_delete");
278             #
279              
280 28 100       100 if ($nds eq "_delete") {
281 2 100       12 delete $$self{"nds"}{$name}, return 1
282             if (exists $$self{"nds"}{$name});
283 1         3 return 0;
284             }
285              
286             #
287             # $obj->nds($name,"_exists");
288             #
289              
290 26 100       109 if ($nds eq "_exists") {
291 2 100       8 return 1 if (exists $$self{"nds"}{$name});
292 1         3 return 0;
293             }
294              
295             #
296             # $obj->nds($name,$nds);
297             # $obj->nds($name,$nds,$new);
298             #
299              
300 24 100       96 if (exists $$self{"nds"}{$name}) {
301 1         2 $$self{"err"} = "ndsnam02";
302 1         4 $$self{"errmsg"} = "Attempt to copy NDS to a name already in use [$name]";
303 1         3 return undef;
304             }
305              
306 23 100       92 if (ref($nds)) {
    100          
307 19         85 $self->check_structure($nds,$new);
308 19 50       59 return undef if ($self->err());
309 19         70 $$self{"nds"}{$name} = $nds;
310 19         90 return undef;
311              
312             } elsif (exists $$self{"nds"}{$nds}) {
313 3         187 $$self{"nds"}{$name} = dclone($$self{"nds"}{$nds});
314 3         10 return undef;
315              
316             } else {
317 1         2 $$self{"err"} = "ndsnam01";
318 1         5 $$self{"errmsg"} = "No NDS stored under the name [$nds]";
319 1         4 return undef;
320             }
321             }
322              
323             sub empty {
324 3314     3314 1 6013 my($self,$nds) = @_;
325 3314         4973 $$self{"err"} = "";
326 3314         4350 $$self{"errmsg"} = "";
327 3314 100       6710 return 1 if (! defined $nds);
328              
329 3155         5843 $nds = _nds($self,$nds,0,0,1);
330 3155 100       6559 return undef if ($self->err());
331              
332 2508         4662 return _empty($self,$nds);
333             }
334              
335             sub _empty {
336 7526     7526   10004 my($self,$nds) = @_;
337              
338 7526 100       22137 if (! defined $nds) {
    100          
    100          
    100          
339 1110         3588 return 1;
340              
341             } elsif (ref($nds) eq "ARRAY") {
342 1649         2496 foreach my $ele (@$nds) {
343 1800 100       2891 return 0 if (! _empty($self,$ele));
344             }
345 410         1696 return 1;
346              
347             } elsif (ref($nds) eq "HASH") {
348 1838         4372 foreach my $key (keys %$nds) {
349 2684 100       5271 return 0 if (! _empty($self,$$nds{$key}));
350             }
351 440         1479 return 1;
352              
353             } elsif ($nds eq "") {
354 399 100       1088 return 0 if ($$self{"blank"});
355 372         1250 return 1;
356              
357             } else {
358 2530         13362 return 0;
359             }
360             }
361              
362             ###############################################################################
363             # GET_STRUCTURE
364             ###############################################################################
365             # Retrieve structural information for a path. Makes use of the default
366             # structural information.
367              
368             sub get_structure {
369 34566     34566 1 58667 my($self,$path,$info) = @_;
370 34566         50119 $$self{"err"} = "";
371 34566         45322 $$self{"errmsg"} = "";
372 34566 100 66     104579 $info = "type" if (! defined $info || ! $info);
373              
374 34566 100 100     175081 if (exists $$self{"cache"}{"get_structure"}{$path} &&
375             exists $$self{"cache"}{"get_structure"}{$path}{$info}) {
376 32205         90084 return $$self{"cache"}{"get_structure"}{$path}{$info};
377             }
378              
379             # Split the path so that we can convert all elements into "*" when
380             # appropriate.
381              
382 2361         5248 my @path = $self->path($path);
383 2361         3917 my @p = ();
384 2361         4034 my $p = "/";
385 2361 100       6236 if (! exists $$self{"struct"}{$p}) {
386 74         192 $$self{"err"} = "ndschk03";
387 74         162 $$self{"errmsg"} = "No structural information available at all.";
388 74         228 return "";
389             }
390              
391 2287         4865 while (@path) {
392 3209         4868 my $ele = shift(@path);
393 3209         9354 my $p1 = $self->path([@p,"*"]);
394 3209         11560 my $p2 = $self->path([@p,$ele]);
395 3209 100       12479 if (exists $$self{"struct"}{$p1}) {
    100          
396 280         431 push(@p,"*");
397 280         816 $p = $p1;
398             } elsif (exists $$self{"struct"}{$p2}) {
399 2309         3782 push(@p,$ele);
400 2309         6570 $p = $p2;
401             } else {
402 620 100       1599 return 0 if ($info eq "valid");
403 617         962 $$self{"err"} = "ndschk04";
404 617         1149 $$self{"errmsg"} = "Invalid path: $p2";
405 617         1750 return "";
406             }
407             }
408              
409             # Return the information about the path.
410              
411 1667 100       3955 if ($info eq "valid") {
412 95         361 $$self{"cache"}{"get_structure"}{$path}{$info} = 1;
413 95         539 return 1;
414             }
415              
416 1572 100       4574 if (exists $$self{"struct"}{$p}{$info}) {
417 1116         2258 my $val = $$self{"struct"}{$p}{$info};
418 1116 50 66     11527 $$self{"cache"}{"get_structure"}{$path}{$info} = $val
      100        
      66        
419             if ( ($info eq "type" && $val =~ /^(hash|list|scalar|other)$/) ||
420             $info eq "uniform" ||
421             $info eq "ordered");
422 1116         3172 return $val;
423             }
424              
425 456 100       1433 if (! exists $$self{"struct"}{$p}{"type"}) {
426 1         3 $$self{"err"} = "ndschk05";
427 1         5 $$self{"errmsg"} = "It is not known what type of data is stored at " .
428             "path: $p";
429 1         5 return ""
430             }
431              
432 455         1009 my $type = $$self{"struct"}{$p}{"type"};
433              
434 455 100       1471 if ($info eq "ordered") {
    100          
    50          
    100          
435 137 100       481 if ($type ne "list") {
436 45         100 $$self{"err"} = "ndschk06";
437 45         121 $$self{"errmsg"} = "Ordered information requested for a non-list " .
438             "structure: $p";
439 45         142 return "";
440             }
441 92         366 return $$self{"defstruct"}{"ordered"};
442              
443             } elsif ($info eq "uniform") {
444 314 100       938 if ($type eq "hash") {
    100          
445 203         788 return $$self{"defstruct"}{"uniform_hash"};
446             } elsif ($type eq "list") {
447 110         614 my $ordered = $self->get_structure($p,"ordered");
448 110 100       1748 if ($ordered) {
449 31         257 return $$self{"defstruct"}{"uniform_ol"};
450             } else {
451 79         410 return 1;
452             }
453              
454             } else {
455 1         3 $$self{"err"} = "ndschk07";
456 1         4 $$self{"errmsg"} = "Uniform information requested for a scalar " .
457             "structure: $p";
458 1         5 return "";
459             }
460              
461             } elsif ($info eq "merge") {
462 0 0       0 if ($type eq "list") {
    0          
463 0         0 my $ordered = $self->get_structure($p,"ordered");
464 0 0       0 if ($ordered) {
465 0         0 return $$self{"defstruct"}{"merge_ol"};
466             } else {
467 0         0 return $$self{"defstruct"}{"merge_ul"};
468             }
469              
470             } elsif ($type eq "hash") {
471 0         0 return $$self{"defstruct"}{"merge_hash"};
472              
473             } else {
474 0         0 return $$self{"defstruct"}{"merge_scalar"};
475             }
476              
477             } elsif ($info eq "keys") {
478 3 100       8 if ($type ne "hash") {
479 1         3 $$self{"err"} = "ndschk08";
480 1         3 $$self{"errmsg"} = "Keys requested for a non-hash structure: $p";
481 1         5 return "";
482             }
483              
484 2 100 66     15 if (exists $$self{"struct"}{$p}{"uniform"} &&
485             $$self{"struct"}{$p}{"uniform"}) {
486 1         3 $$self{"err"} = "ndschk09";
487 1         5 $$self{"errmsg"} = "Keys requested for a uniform hash structure: $p";
488 1         5 return "";
489             }
490              
491 1         2 my @keys = ();
492 1         2 PP: foreach my $pp (CORE::keys %{ $$self{"struct"} }) {
  1         7  
493             # Look for paths of the form: $p/KEY
494 20         38 my @pp = $self->path($pp);
495 20 100       61 next if ($#pp != $#p + 1);
496 8         10 my $key = pop(@pp);
497 8         16 my $tmp = $self->path(\@pp);
498 8 100       24 next if ($tmp ne $p);
499 2         5 push(@keys,$key);
500             }
501 1         14 return sort @keys;
502              
503             } else {
504 1         3 $$self{"err"} = "ndschk99";
505 1         3 $$self{"errmsg"} = "Unknown structural information requested: $info";
506 1         5 return "";
507             }
508             }
509              
510             ###############################################################################
511             # SET_STRUCTURE
512             ###############################################################################
513             # This sets a piece of structural information (and does all error checking
514             # on it).
515              
516             sub set_structure {
517 227     227 1 7653 my($self,$item,$val,$path) = @_;
518 227         352 $$self{"err"} = "";
519 227         410 $$self{"errmsg"} = "";
520              
521 227 100       412 if ($path) {
522 223         400 _set_structure_path($self,$item,$val,$path);
523             } else {
524 4         7 _set_structure_default($self,$item,$val);
525             }
526             }
527              
528             # Set a structural item for a path.
529             #
530             sub _set_structure_path {
531 906     906   2064 my($self,$item,$val,$path) = @_;
532              
533 906         1982 my @path = $self->path($path);
534 906         2216 $path = $self->path(\@path);
535 906         2659 _structure_valid($self,$item,$val,$path,@path);
536             }
537              
538             # Rules for a valid structure:
539             #
540             # If parent is not valid
541             # INVALID
542             # End
543             #
544             # If we're not setting an item
545             # VALID
546             # End
547             #
548             # If type is not set
549             # set it to unknown
550             # End
551             #
552             # INVALID if incompatible with any other options already set
553             # INVALID if path incompatible with type
554             # INVALID if path incompatible with parent
555             # INVALID if any direct childres incompatible
556             #
557             # Set item
558             #
559             sub _structure_valid {
560 2856     2856   6454 my($self,$item,$val,$path,@path) = @_;
561              
562             #
563             # Check for an invalid parent
564             #
565              
566 2856         2869 my (@parent,$parent);
567 2856 100       5808 if (@path) {
568 1618         3120 @parent = @path;
569 1618         1929 pop(@parent);
570 1618         26352 $parent = $self->path([@parent]);
571 1618         5484 _structure_valid($self,"","",$parent,@parent);
572 1618 50       3161 return if ($self->err());
573             }
574              
575             #
576             # If we're not setting a value, then the most we've done is
577             # set defaults (which we know we've done correct), so it's valid
578             # to the extent that we're able to check.
579             #
580              
581 2856 100       6745 return unless ($item);
582              
583             #
584             # Make sure type is set. If it's not, set it to "unknown".
585             #
586              
587 1238 100       5357 $$self{"struct"}{$path}{"type"} = "unknown"
588             if (! exists $$self{"struct"}{$path}{"type"});
589 1238         2355 my $type = $$self{"struct"}{$path}{"type"};
590              
591             #
592             # Check to make sure that $item and $val are valid and that
593             # they don't conflict with other structural settings for
594             # this path.
595             #
596              
597 1238         1412 my $set_ordered = 0;
598 1238         1346 my $set_uniform = 0;
599 1238         1361 my $valid = 0;
600              
601             # Type checks
602 1238 100       2666 if ($item eq "type") {
603 822         1146 $valid = 1;
604 822 100 100     3921 if ($val ne "scalar" &&
      100        
      100        
605             $val ne "list" &&
606             $val ne "hash" &&
607             $val ne "other") {
608 1         2 $$self{"err"} = "ndsstr01";
609 1         2 $$self{"errmsg"} = "Attempt to set type to an invalid value: $val";
610 1         4 return;
611             }
612 821 100 100     2057 if ($type ne "unknown" &&
613             $type ne "list/hash") {
614 1         2 $$self{"err"} = "ndsstr02";
615 1         2 $$self{"errmsg"} = "Once type is set, it may not be reset: $path";
616 1         3 return;
617             }
618 820 100 100     2436 if ($type eq "list/hash" &&
      66        
619             $val ne "list" &&
620             $val ne "hash") {
621 1         2 $$self{"err"} = "ndsstr03";
622 1         4 $$self{"errmsg"} = "Attempt to set type to scalar when a list/hash " .
623             "type is required: $path";
624 1         5 return;
625             }
626             }
627              
628             # Ordered checks
629 1235 100       2731 if ($item eq "ordered") {
630 77         94 $valid = 1;
631 77 100       253 if (exists $$self{"struct"}{$path}{"ordered"}) {
632 2         3 $$self{"err"} = "ndsstr04";
633 2         5 $$self{"errmsg"} = "Attempt to reset ordered: $path";
634 2         8 return;
635             }
636              
637             # only allowed for lists
638 75 50 33     438 if ($type eq "unknown" ||
639             $type eq "list/hash") {
640 0         0 _structure_valid($self,"type","list",$path,@path);
641 0 0       0 return if ($self->err());
642 0         0 $type = "list";
643             }
644 75 100       173 if ($type ne "list") {
645 1         3 $$self{"err"} = "ndsstr05";
646 1         2 $$self{"errmsg"} = "Attempt to set ordered on a non-list structure: " .
647             "$path";
648 1         4 return;
649             }
650 74 100 100     415 if ($val ne "0" &&
651             $val ne "1") {
652 1         2 $$self{"err"} = "ndsstr06";
653 1         4 $$self{"errmsg"} = "Ordered value must be 0 or 1: $path";
654 1         5 return;
655             }
656              
657             # check conflicts with "uniform"
658 73 100       223 if (! exists $$self{"struct"}{$path}{"uniform"}) {
    50          
659 71 100       461 if ($val) {
660             # We're making an unknown list ordered. This can
661             # apply to uniform or non-uniform lists, so nothing
662             # is required.
663             } else {
664             # We're making an unknown list unordered. The
665             # list must be uniform.
666 36         61 $set_uniform = 1;
667             }
668             } elsif ($$self{"struct"}{$path}{"uniform"}) {
669             # We're making an uniform list ordered or non-ordered.
670             # Both are allowed.
671             } else {
672 2 50       6 if ($val) {
673             # We're making an non-uniform list ordered. This is
674             # allowed.
675             } else {
676             # We're trying to make an non-uniform list unordered.
677             # This is NOT allowed.
678              
679             # NOTE: This will never occur. Any time we set a list to
680             # non-uniform, it will automatically set the ordered flag
681             # appropriately, so trying to set it here will result in an
682             # ndsstr04 error.
683 0         0 return;
684             }
685             }
686             }
687              
688             # Uniform checks
689 1231 100       2546 if ($item eq "uniform") {
690 338         447 $valid = 1;
691 338 100       1196 if (exists $$self{"struct"}{$path}{"uniform"}) {
692 2         4 $$self{"err"} = "ndsstr07";
693 2         5 $$self{"errmsg"} = "Attempt to reset uniform: $path";
694 2         7 return;
695             }
696              
697             # only applies to lists and hashes
698 336 50       950 if ($type eq "unknown") {
699 0         0 _structure_valid($self,"type","list/hash",$path,@path);
700 0 0       0 return if ($self->err());
701             }
702 336 100 100     1714 if ($type ne "list" &&
      66        
703             $type ne "hash" &&
704             $type ne "list/hash") {
705 1         3 $$self{"err"} = "ndsstr08";
706 1         3 $$self{"errmsg"} = "Attempt to set uniform on a scalar structure: " .
707             "$path";
708 1         4 return;
709             }
710 335 100 100     1535 if ($val ne "0" &&
711             $val ne "1") {
712 1         3 $$self{"err"} = "ndsstr09";
713 1         3 $$self{"errmsg"} = "Uniform value must be 0 or 1: $path";
714 1         3 return;
715             }
716              
717             # check conflicts with "ordered"
718 334 100 66     2302 if (exists $$self{"struct"}{$path}{"type"} &&
719             $$self{"struct"}{$path}{"type"} eq "list") {
720 128 100       537 if (! exists $$self{"struct"}{$path}{"ordered"}) {
    100          
721 59 100       206 if ($val) {
722             # We're making an unknown list uniform. This can
723             # apply to ordered or unorderd lists, so nothing
724             # is required.
725             } else {
726             # We're making an unknown list non-uniform. The
727             # list must be ordered.
728 2         3 $set_ordered = 1;
729             }
730             } elsif ($$self{"struct"}{$path}{"ordered"}) {
731             # We're making an ordered list uniform or non-uniform.
732             # Both are allowed.
733             } else {
734 36 50       106 if ($val) {
735             # We're making an unordered list uniform. This is
736             # allowed.
737             } else {
738             # We're trying to make an unordered list non-uniform.
739             # This is NOT allowed.
740              
741             # NOTE: This error will never occur. Any time we set a
742             # list to unordered, it will automatically set the
743             # uniform flag appropriately, so trying to set it here
744             # will result in a ndsstr07 error.
745 0         0 return;
746             }
747             }
748             }
749             }
750              
751             # $item is invalid
752 1227 100       3215 if (! $valid) {
753 1         2 $$self{"err"} = "ndsstr98";
754 1         3 $$self{"errmsg"} = "Invalid default structural item: $item";
755 1         3 return;
756             }
757              
758             #
759             # Check to make sure that the current path is valid with
760             # respect to the type of structure we're currently in (this
761             # is defined in the parent element).
762             #
763              
764 1226 100       2595 if (@path) {
765 1056         1735 my $curr_ele = $path[$#path];
766 1056 100       2893 if (exists $$self{"struct"}{$parent}{"type"}) {
767 1046         1977 my $parent_type = $$self{"struct"}{$parent}{"type"};
768              
769 1046 50       2414 if ($parent_type eq "unknown") {
770 0         0 _structure_valid($self,"type","list/hash",$parent,@parent);
771 0 0       0 return if ($self->err());
772             }
773              
774 1046 100 66     15069 if ($parent_type eq "scalar" ||
    100 100        
    100 100        
    100 33        
    100 66        
    50 33        
775             $parent_type eq "other") {
776 1         2 $$self{"err"} = "ndsstr10";
777 1         3 $$self{"errmsg"} = "Trying to set structural information for a " .
778             "child with a scalar parent: $path";
779 1         23 return;
780              
781             } elsif ($parent_type eq "list" &&
782             $curr_ele =~ /^\d+$/) {
783 4 50       18 if (exists $$self{"struct"}{$parent}{"uniform"}) {
784 4 100       14 if ($$self{"struct"}{$parent}{"uniform"}) {
785             # Parent = list,uniform Curr = 2
786 1         2 $$self{"err"} = "ndsstr11";
787 1         5 $$self{"errmsg"} = "Attempt to set structural information " .
788             "for a specific element in a uniform list: $path";
789 1         5 return;
790             }
791             } else {
792             # Parent = list, unknown Curr = 2
793             # => force parent to be non-uniform
794 0         0 _structure_valid($self,"uniform","0",$parent,@parent);
795 0 0       0 return if ($self->err());
796             }
797              
798             } elsif ($parent_type eq "list" &&
799             $curr_ele eq "*") {
800 131 100       475 if (exists $$self{"struct"}{$parent}{"uniform"}) {
801 45 100       171 if (! $$self{"struct"}{$parent}{"uniform"}) {
802             # Parent = list,nonuniform Curr = *
803 1         2 $$self{"err"} = "ndsstr12";
804 1         4 $$self{"errmsg"} = "Attempt to set structural information " .
805             "for all elements in a non-uniform list: $path";
806 1         5 return;
807             }
808             } else {
809             # Parent = list,unknown Curr = *
810             # => force parent to be uniform
811 86         251 _structure_valid($self,"uniform","1",$parent,@parent);
812 86 50       296 return if ($self->err());
813             }
814              
815             } elsif ($parent_type eq "list") {
816 1         2 $$self{"err"} = "ndsstr13";
817 1         4 $$self{"errmsg"} = "Attempt to access a list with a non-integer " .
818             "index.: $path";
819 1         4 return;
820              
821             } elsif (($parent_type eq "hash" || $parent_type eq "list/hash") &&
822             $curr_ele eq "*") {
823 5 50       17 if (exists $$self{"struct"}{$parent}{"uniform"}) {
824 5 100       19 if (! $$self{"struct"}{$parent}{"uniform"}) {
825             # Parent = list/hash,non-uniform Curr = *
826 1         2 $$self{"err"} = "ndsstr15";
827 1         3 $$self{"errmsg"} = "Attempt to set structural information " .
828             "for all elements in a non-uniform structure: $path";
829 1         6 return;
830             }
831             } else {
832             # Parent = hash,unknown Curr = *
833             # => force parent to be uniform
834 0         0 _structure_valid($self,"uniform","1",$parent,@parent);
835 0 0       0 return if ($self->err());
836             }
837              
838             } elsif ($parent_type eq "hash" || $parent_type eq "list/hash") {
839 904 100       2678 if (exists $$self{"struct"}{$parent}{"uniform"}) {
840 706 100       2514 if ($$self{"struct"}{$parent}{"uniform"}) {
841             # Parent = list/hash,uniform Curr = foo
842 1         2 $$self{"err"} = "ndsstr14";
843 1         3 $$self{"errmsg"} = "Attempt to set structural information " .
844             "for a specific element in a uniform structure: $path";
845 1         4 return;
846             }
847             } else {
848             # Parent = hash,unknown Curr = foo
849             # => force parent to be non-uniform
850 198         592 _structure_valid($self,"uniform","0",$parent,@parent);
851 198 50       661 return if ($self->err());
852             }
853             }
854              
855             } else {
856             # Parent is not type'd yet.
857              
858 10 50 33     115 if ($curr_ele eq "*" ||
859             $curr_ele =~ /^\d+$/) {
860 0         0 _structure_valid($self,"type","list/hash",$parent,@parent);
861 0 0       0 return if ($self->err());
862             } else {
863 10         32 _structure_valid($self,"type","hash",$parent,@parent);
864 10 100       25 return if ($self->err());
865             }
866             }
867             }
868              
869             #
870             # Set the item
871             #
872              
873 1219         3894 $$self{"struct"}{$path}{$item} = $val;
874 1219 100       2664 if ($set_ordered) {
875 2         5 _structure_valid($self,"ordered","1",$path,@path);
876 2 50       3 return if ($self->err());
877             }
878 1219 100       4715 if ($set_uniform) {
879 36         85 _structure_valid($self,"uniform","1",$path,@path);
880 36 50       82 return if ($self->err());
881             }
882             }
883              
884             {
885             # Values for the default structural information. First value in the
886             # list is the error code for this item. Second value is the default
887             # for this item.
888              
889             my %def = ( "ordered" => [ "ndsstr16",
890             "Attempt to set the default ordered " .
891             "value to something other than 0/1",
892             qw(0 1) ],
893             "uniform_hash" => [ "ndsstr17",
894             "Attempt to set the default uniform_hash" .
895             " value to something other than 0/1",
896             qw(0 1) ],
897             "uniform_ol" => [ "ndsstr18",
898             "Attempt to set the default uniform_ol " .
899             "value to something other than 0/1",
900             qw(1 0) ],
901             );
902              
903             sub _set_structure_default {
904 4     4   7 my($self,$item,$val) = @_;
905              
906 4 100       9 if (! exists $def{$item}) {
907 1         2 $$self{"err"} = "ndsstr99";
908 1         3 $$self{"errmsg"} = "Invalid structural item for a path: $item";
909 1         2 return;
910             }
911 3         4 my @tmp = @{ $def{$item} };
  3         13  
912 3         5 my $err = shift(@tmp);
913 3         3 my $msg = shift(@tmp);
914 3         5 my %tmp = map { $_,1 } @tmp;
  6         17  
915 3 50       10 if (! exists $tmp{$val}) {
916 3         6 $$self{"err"} = $err;
917 3         7 $$self{"errmsg"} = "$msg: $item = $val";
918 3         12 return;
919             }
920 0         0 $$self{"defstruct"}{$item} = $val;
921 0         0 return;
922             }
923              
924             # Set up the default structure:
925             sub _structure_defaults {
926 93     93   246 my($self) = @_;
927 93         326 my($d) = "defstruct";
928              
929 93 50       1080 $$self{$d} = {} if (! exists $$self{$d});
930 93         584 foreach my $key (CORE::keys %def) {
931 279         1332 $$self{$d}{$key} = $def{$key}[2];
932             }
933             }
934             }
935              
936             ###############################################################################
937             # CHECK_STRUCTURE/CHECK_VALUE
938             ###############################################################################
939             # This checks the structure of an NDS (and may update the structural
940             # information if appropriate).
941              
942             sub check_structure {
943 596     596 1 1113 my($self,$nds,$new) = @_;
944 596         1000 $$self{"err"} = "";
945 596         900 $$self{"errmsg"} = "";
946              
947 596 100       1408 return if (! ref($nds));
948 588 50       1355 return if (! $$self{"structure"});
949              
950 588 100       1189 $new = 0 if (! $new);
951              
952 588         1247 _check_structure($self,$nds,$new,());
953             }
954              
955             sub check_value {
956 5     5 1 576 my($self,$path,$val,$new) = @_;
957 5         8 $$self{"err"} = "";
958 5         6 $$self{"errmsg"} = "";
959 5         10 my(@path) = $self->path($path);
960 5         10 _check_structure($self,$val,$new,@path);
961             }
962              
963             sub _check_structure {
964 26314     26314   53122 my($self,$nds,$new,@path) = @_;
965 26314 100       52617 return if (! defined $nds);
966              
967 24780         68271 my $path = $self->path([@path]);
968              
969             # Check to make sure that it's the correct type of data.
970              
971 24780         62137 my $type = $self->get_structure($path,"type");
972              
973 24780 100       49964 if ($type) {
974 24096         33076 my $ref = lc(ref($nds));
975 24096 100       44546 $ref = "scalar" if (! $ref);
976 24096 100       45726 $ref = "list" if ($ref eq "array");
977              
978 24096 100 100     112094 if ($type eq "hash" || $type eq "list" || $type eq "scalar") {
    50 100        
    50          
    0          
979 24084 100       52548 if ($ref ne $type) {
980 8         15 $$self{"err"} = "ndschk01";
981 8         29 $$self{"errmsg"} = "Invalid type: $path (expected $type, got $ref)";
982 8         23 return;
983             }
984              
985             } elsif ($type eq "list/hash") {
986 0 0 0     0 if ($ref ne "list" && $ref ne "hash") {
987 0         0 $$self{"err"} = "ndschk01";
988 0         0 $$self{"errmsg"} = "Invalid type: $path (expected $type, got $ref)";
989 0         0 return;
990             }
991 0         0 $type = "";
992              
993             } elsif ($type eq "other") {
994 12 50 33     80 if ($ref eq "scalar" ||
      33        
995             $ref eq "hash" ||
996             $ref eq "list") {
997 0         0 $$self{"err"} = "ndschk01";
998 0         0 $$self{"errmsg"} = "Invalid type: $path (expected $type, got $ref)";
999 0         0 return;
1000             }
1001              
1002             } elsif ($type eq "unknown") {
1003 0         0 $type = "";
1004              
1005             } else {
1006 0         0 die "[check_structure] Impossible error: $type";
1007             }
1008             }
1009              
1010 24772 100       42207 if (! $type) {
1011             # If the structure is not previously defined, it will set an
1012             # error code. Erase that one (it's not interesting) and then
1013             # set the structure based on the new value (if allowed).
1014 684         1160 $$self{"err"} = "";
1015 684         948 $$self{"errmsg"} = "";
1016 684 100       1188 if ($new) {
1017 683         1271 $type = lc(ref($nds));
1018 683 100       1431 $type = "list" if ($type eq "array");
1019 683 100 100     2467 if (! $type) {
    100          
1020 451         968 _set_structure_path($self,"type","scalar",$path);
1021             } elsif ($type eq "hash" ||
1022             $type eq "list") {
1023 231         888 _set_structure_path($self,"type",$type,$path);
1024             } else {
1025 1         4 _set_structure_path($self,"type","other",$path);
1026             }
1027              
1028             } else {
1029 1         2 $$self{"err"} = "ndschk02";
1030 1         3 $$self{"errmsg"} = "New structure not allowed";
1031 1         2 return;
1032             }
1033             }
1034              
1035 24771 100 100     105598 return unless ($type eq "list" || $type eq "hash");
1036              
1037             # Recurse into hashes.
1038              
1039 8327         16813 my $uniform = $self->get_structure($path,"uniform");
1040 8327 100       20962 if ($type eq "hash") {
1041 4730         11251 foreach my $key (CORE::keys %$nds) {
1042 13653         22907 my $val = $$nds{$key};
1043 13653 50       20267 if ($uniform) {
1044 0         0 _check_structure($self,$val,$new,@path,"*");
1045 0 0       0 return if ($self->err());
1046             } else {
1047 13653         26366 _check_structure($self,$val,$new,@path,$key);
1048 13653 100       24984 return if ($self->err());
1049             }
1050             }
1051 4722         11591 return;
1052             }
1053              
1054             # Recurse into lists
1055              
1056 3597         8677 for (my $i=0; $i<=$#$nds; $i++) {
1057 11321         15423 my $val = $$nds[$i];
1058 11321 50       16980 if ($uniform) {
1059 11321         30185 _check_structure($self,$val,$new,@path,"*");
1060 11321 100       21270 return if ($self->err());
1061             } else {
1062 0         0 _check_structure($self,$val,$new,@path,$i);
1063 0 0       0 return if ($self->err());
1064             }
1065             }
1066              
1067 3596         10036 return;
1068             }
1069              
1070             ###############################################################################
1071             # VALID/VALUE
1072             ###############################################################################
1073              
1074             sub value {
1075 1738     1738 1 6070 my($self,$nds,$path,$copy,$nocheck) = @_;
1076 1738 100       3732 $nocheck=0 if (! $nocheck);
1077 1738         2571 $$self{"err"} = "";
1078 1738         2480 $$self{"errmsg"} = "";
1079 1738         3362 $nds = _nds($self,$nds,1,0,$nocheck);
1080 1738 100       3717 return undef if ($self->err());
1081              
1082 1737         3483 my($delim) = $self->delim();
1083 1737         3637 my @path = $self->path($path);
1084              
1085 1737         4083 my $val = _value($self,$nds,$delim,"",@path);
1086 1737 100       3613 return undef if ($self->err());
1087              
1088 1434 50 33     9513 if ($copy && ref($val)) {
1089 0         0 return dclone($val);
1090             } else {
1091 1434         4188 return $val;
1092             }
1093             }
1094              
1095             sub _value {
1096 3272     3272   6764 my($self,$nds,$delim,$path,@path) = @_;
1097              
1098             #
1099             # We've traversed as far as @path goes
1100             #
1101              
1102 3272 100       6778 if (! @path) {
1103 1434         4171 return $nds;
1104             }
1105              
1106             #
1107             # Get the next path element.
1108             #
1109              
1110 1838         2809 my $p = shift(@path);
1111 1838 100       4232 $path = ($path ? join($delim,$path,$p) : "$delim$p");
1112              
1113             #
1114             # Handle the case where $nds is a scalar, or not
1115             # a known data type.
1116             #
1117              
1118 1838 100 100     8406 if (! defined($nds)) {
    100          
    100          
1119             # $nds doesn't contain the path
1120 149         276 $$self{"err"} = "ndsdat01";
1121 149         316 $$self{"errmsg"} = "A path does not exist in the NDS: $path";
1122 149         315 return undef;
1123              
1124             } elsif (! ref($nds)) {
1125             # $nds is a scalar
1126 1         4 $$self{"err"} = "ndsdat04";
1127 1         3 $$self{"errmsg"} = "The NDS has a scalar at a point where a hash or " .
1128             "list should be: $path";
1129 1         5 return undef;
1130              
1131             } elsif (ref($nds) ne "HASH" && ref($nds) ne "ARRAY") {
1132             # $nds is an unsupported data type
1133 1         3 $$self{"err"} = "ndsdat05";
1134 1         3 $$self{"errmsg"} = "The NDS has a reference to an unsupported data " .
1135             "type where a hash or list should be: $path";
1136 1         3 return undef;
1137             }
1138              
1139             #
1140             # Handle hash references.
1141             #
1142              
1143 1687 100       3554 if (ref($nds) eq "HASH") {
1144 1670 100       3270 if (exists $$nds{$p}) {
1145 1526         4773 return _value($self,$$nds{$p},$delim,$path,@path);
1146             } else {
1147 144         344 $$self{"err"} = "ndsdat02";
1148 144         320 $$self{"errmsg"} = "A hash key does not exist in the NDS: $path";
1149 144         435 return undef;
1150             }
1151             }
1152              
1153             #
1154             # Handle lists.
1155             #
1156              
1157 17 100       127 if ($p !~ /^\d+$/) {
    100          
1158             # A non-integer list reference
1159 1         3 $$self{"err"} = "ndsdat06";
1160 1         4 $$self{"errmsg"} = "A non-integer index used to access a list: $path";
1161 1         4 return undef;
1162             } elsif ($#$nds < $p) {
1163 7         15 $$self{"err"} = "ndsdat03";
1164 7         15 $$self{"errmsg"} = "A list element does not exist in the NDS: $path";
1165 7         24 return undef;
1166             } else {
1167 9         35 return _value($self,$$nds[$p],$delim,$path,@path);
1168             }
1169             }
1170              
1171             ###############################################################################
1172             # KEYS, VALUES
1173             ###############################################################################
1174              
1175             sub keys {
1176 118     118 1 19987 my($self,$nds,$path) = @_;
1177 118         246 $$self{"err"} = "";
1178 118         191 $$self{"errmsg"} = "";
1179 118         298 $nds = _nds($self,$nds,1,0,0);
1180 118         339 my $val = $self->value($nds,$path);
1181 118 100       250 return undef if ($self->err());
1182              
1183 108 100       597 if (! ref($val)) {
    100          
    50          
1184 1         7 return ();
1185              
1186             } elsif (ref($val) eq "ARRAY") {
1187 57         90 my(@ret);
1188 57         172 foreach my $i (0..$#$val) {
1189 189 100       377 push(@ret,$i) if (! _empty($self,$$val[$i]));
1190             }
1191 57         360 return @ret;
1192              
1193             } elsif (ref($val) eq "HASH") {
1194 50         65 my(@ret);
1195 50         401 foreach my $key (sort(CORE::keys %$val)) {
1196 95 50       228 push(@ret,$key) if (! _empty($self,$$val{$key}));
1197             }
1198 50         257 return @ret;
1199              
1200             } else {
1201 0         0 return undef;
1202             }
1203             }
1204              
1205             sub values {
1206 99     99 1 1068 my($self,$nds,$path) = @_;
1207 99         180 $$self{"err"} = "";
1208 99         327 $$self{"errmsg"} = "";
1209 99         394 $nds = _nds($self,$nds,1,0,0);
1210 99         424 my $val = $self->value($nds,$path);
1211 99 100       240 return undef if ($self->err());
1212              
1213 89 100       370 if (! ref($val)) {
    100          
    50          
1214 1         5 return ($val);
1215              
1216             } elsif (ref($val) eq "ARRAY") {
1217 51         79 my(@ret);
1218 51         144 foreach my $i (0..$#$val) {
1219 177 100       402 push(@ret,$$val[$i]) if (! _empty($self,$$val[$i]));
1220             }
1221 51         360 return @ret;
1222              
1223             } elsif (ref($val) eq "HASH") {
1224 37         50 my(@ret);
1225 37         168 foreach my $key (sort(CORE::keys %$val)) {
1226 73 50       165 push(@ret,$$val{$key}) if (! _empty($self,$$val{$key}));
1227             }
1228 37         242 return @ret;
1229              
1230             } else {
1231 0         0 return undef;
1232             }
1233             }
1234              
1235             ###############################################################################
1236             # SET_MERGE
1237             ###############################################################################
1238              
1239             sub set_merge {
1240 98     98 1 3818 my($self,$item,$val,@args) = @_;
1241 98         159 $$self{"err"} = "";
1242 98         145 $$self{"errmsg"} = "";
1243              
1244 98 100       187 if (_merge_default($self,$item)) {
    50          
1245 9         19 _set_merge_default($self,$item,$val,@args);
1246              
1247             } elsif ($item eq "merge") {
1248 89         277 _set_merge_path($self,$val,@args);
1249              
1250             } else {
1251 0         0 $$self{"err"} = "ndsmer01";
1252 0         0 $$self{"errmsg"} = "Attempt to set a merge setting to an unknown " .
1253             "value: $item";
1254 0         0 return;
1255             }
1256             }
1257              
1258             # Set a merge item for a path.
1259             #
1260             sub _set_merge_path {
1261 89     89   151 my($self,$path,$method,$ruleset) = @_;
1262 89 100       288 $ruleset = "*" if (! $ruleset);
1263              
1264 89         201 my @path = $self->path($path);
1265 89         205 $path = $self->path(\@path);
1266              
1267 89 100       351 if (exists $$self{"ruleset"}{$ruleset}{"path"}{$path}) {
1268 1         3 $$self{"err"} = "ndsmer06";
1269 1         3 $$self{"errmsg"} = "Attempt to reset merge value for a path: $path";
1270 1         4 return;
1271             }
1272              
1273             # Check type vs. method
1274              
1275 88         204 my $type = $self->get_structure($path,"type");
1276              
1277 88 100 66     205 if ($type eq "list") {
    100          
    100          
1278 66         141 my $ordered = $self->get_structure($path,"ordered");
1279              
1280 66 100       148 if (! _merge_allowed($type,$ordered,$method)) {
1281 4 100       10 if ($ordered) {
1282 2         4 $$self{"err"} = "ndsmer08";
1283 2         7 $$self{"errmsg"} = "Invalid merge method for ordered list " .
1284             "merging: $path";
1285 2         9 return;
1286             } else {
1287 2         3 $$self{"err"} = "ndsmer09";
1288 2         7 $$self{"errmsg"} = "Invalid merge method for unordered list " .
1289             "merging: $path";
1290 2         9 return;
1291             }
1292             }
1293              
1294             } elsif ($type eq "hash") {
1295 19 100       57 if (! _merge_allowed($type,0,$method)) {
1296 1         3 $$self{"err"} = "ndsmer10";
1297 1         3 $$self{"errmsg"} = "Invalid merge method for hash merging: $path";
1298 1         5 return;
1299             }
1300              
1301             } elsif ($type eq "scalar" || $type eq "other") {
1302 2 100       6 if (! _merge_allowed($type,0,$method)) {
1303 1         2 $$self{"err"} = "ndsmer11";
1304 1         53 $$self{"errmsg"} = "Invalid merge method for scalar merging: $path";
1305 1         6 return;
1306             }
1307              
1308             } else {
1309 1         3 $$self{"err"} = "ndsmer07";
1310 1         3 $$self{"errmsg"} = "Attempt to set merge for a path with no " .
1311             "known type: $path";
1312 1         4 return;
1313             }
1314              
1315             # Set the method
1316              
1317 81         237 $$self{"ruleset"}{$ruleset}{"path"}{$path} = $method;
1318 81         311 return;
1319             }
1320              
1321             {
1322             # Values for the default structural information. First value in the
1323             # list is the error code for this item. Second value is the default
1324             # for this item.
1325              
1326             my %def = ( "merge_hash" => [ "ndsmer02",
1327             "Attempt to set merge_hash to an " .
1328             "invalid value",
1329             qw(merge
1330             keep keep_warn
1331             replace replace_warn
1332             error) ],
1333             "merge_ol" => [ "ndsmer03",
1334             "Attempt to set merge_ol to an invalid " .
1335             "value",
1336             qw(merge
1337             keep keep_warn
1338             replace replace_warn
1339             error) ],
1340             "merge_ul" => [ "ndsmer04",
1341             "Attempt to set merge_ul to an invalid " .
1342             "value",
1343             qw(append
1344             keep keep_warn
1345             replace replace_warn
1346             error) ],
1347             "merge_scalar" => [ "ndsmer05",
1348             "Attempt to set merge_scalar to an " .
1349             "invalid value",
1350             qw(keep keep_warn
1351             replace replace_warn
1352             error) ],
1353             );
1354              
1355             sub _merge_default {
1356 98     98   216 my($self,$item) = @_;
1357 98 100       379 return 1 if (exists $def{$item});
1358 89         302 return 0;
1359             }
1360              
1361             sub _set_merge_default {
1362 9     9   15 my($self,$item,$val,$ruleset) = @_;
1363 9 50       19 $ruleset = "*" if (! $ruleset);
1364              
1365 9         9 my @tmp = @{ $def{$item} };
  9         32  
1366 9         13 my $err = shift(@tmp);
1367 9         12 my $msg = shift(@tmp);
1368 9         11 my %tmp = map { $_,1 } @tmp;
  52         100  
1369 9 100       25 if (! exists $tmp{$val}) {
1370 4         8 $$self{"err"} = $err;
1371 4         10 $$self{"errmsg"} = "$msg: $item = $val";
1372 4         17 return;
1373             }
1374 5         13 $$self{"ruleset"}{$ruleset}{"def"}{$item} = $val;
1375 5         22 return;
1376             }
1377              
1378             # Set up the default merge:
1379             sub _merge_defaults {
1380 93     93   235 my($self) = @_;
1381              
1382 93         492 foreach my $key (CORE::keys %def) {
1383 372         1637 $$self{"ruleset"}{"*"}{"def"}{$key} = $def{$key}[2];
1384             }
1385              
1386 93         756 $$self{"ruleset"}{"keep"}{"def"} =
1387             { "merge_hash" => "keep",
1388             "merge_ol" => "keep",
1389             "merge_ul" => "keep",
1390             "merge_scalar" => "keep" };
1391              
1392 93         587 $$self{"ruleset"}{"replace"}{"def"} =
1393             { "merge_hash" => "replace",
1394             "merge_ol" => "replace",
1395             "merge_ul" => "replace",
1396             "merge_scalar" => "replace" };
1397              
1398 93         585 $$self{"ruleset"}{"default"}{"def"} =
1399             { "merge_hash" => "merge",
1400             "merge_ol" => "merge",
1401             "merge_ul" => "keep",
1402             "merge_scalar" => "keep" };
1403              
1404 93         562 $$self{"ruleset"}{"override"}{"def"} =
1405             { "merge_hash" => "merge",
1406             "merge_ol" => "merge",
1407             "merge_ul" => "replace",
1408             "merge_scalar" => "replace" };
1409              
1410             }
1411              
1412             sub _merge_allowed {
1413 87     87   135 my($type,$ordered,$val) = @_;
1414              
1415 87         91 my @tmp;
1416 87 100       276 if ($type eq "hash") {
    100          
1417 19         26 @tmp = @{ $def{"merge_hash"} };
  19         260  
1418             } elsif ($type eq "list") {
1419 66 100       215 if ($ordered) {
1420 33         39 @tmp = @{ $def{"merge_ol"} };
  33         135  
1421             } else {
1422 33         36 @tmp = @{ $def{"merge_ul"} };
  33         122  
1423             }
1424             } else {
1425 2         2 @tmp = @{ $def{"merge_scalar"} };
  2         8  
1426             }
1427              
1428 87         142 my $err = shift(@tmp);
1429 87         112 my $msg = shift(@tmp);
1430 87         178 my %tmp = map { $_,1 } @tmp;
  520         1263  
1431 87 100       299 return 0 if (! exists $tmp{$val});
1432 81         367 return 1;
1433             }
1434             }
1435              
1436             ###############################################################################
1437             # GET_MERGE
1438             ###############################################################################
1439              
1440             sub get_merge {
1441 319     319 1 1123 my($self,$path,$ruleset) = @_;
1442 319         488 $$self{"err"} = "";
1443 319         435 $$self{"errmsg"} = "";
1444 319 100       699 $ruleset = "*" if (! $ruleset);
1445 319         622 my @path = $self->path($path);
1446 319         781 $path = $self->path(\@path);
1447              
1448             # Check ruleset
1449              
1450 319 100       1293 return $$self{"ruleset"}{$ruleset}{"path"}{$path}
1451             if (exists $$self{"ruleset"}{$ruleset}{"path"}{$path});
1452              
1453 242         543 my $type = $self->get_structure($path,"type");
1454 242         308 my $ordered;
1455 242 50       531 if ($type eq "list") {
1456 0         0 $ordered = $self->get_structure($path,"ordered");
1457             }
1458              
1459 242 100 33     929 if ($type eq "hash") {
    50 33        
    50          
    50          
1460 151 50       918 return $$self{"ruleset"}{$ruleset}{"def"}{"merge_hash"}
1461             if (exists $$self{"ruleset"}{$ruleset}{"def"}{"merge_hash"});
1462              
1463             } elsif ($type eq "list" && $ordered) {
1464 0 0       0 return $$self{"ruleset"}{$ruleset}{"def"}{"merge_ol"}
1465             if (exists $$self{"ruleset"}{$ruleset}{"def"}{"merge_ol"});
1466              
1467             } elsif ($type eq "list") {
1468 0 0       0 return $$self{"ruleset"}{$ruleset}{"def"}{"merge_ul"}
1469             if (exists $$self{"ruleset"}{$ruleset}{"def"}{"merge_ul"});
1470              
1471             } elsif ($type eq "scalar" || $type eq "other") {
1472 91 100       556 return $$self{"ruleset"}{$ruleset}{"def"}{"merge_scalar"}
1473             if (exists $$self{"ruleset"}{$ruleset}{"def"}{"merge_scalar"});
1474              
1475             } else {
1476 0         0 return "";
1477             }
1478              
1479             # Check "*" (this should always find something)
1480              
1481 1         2 $ruleset = "*";
1482              
1483 1 50       4 return $$self{"ruleset"}{$ruleset}{"path"}{$path}
1484             if (exists $$self{"ruleset"}{$ruleset}{"path"}{$path});
1485              
1486 1 50 33     11 if ($type eq "hash") {
    50 33        
    50          
    50          
1487 0         0 return $$self{"ruleset"}{$ruleset}{"def"}{"merge_hash"};
1488              
1489             } elsif ($type eq "list" && $ordered) {
1490 0         0 return $$self{"ruleset"}{$ruleset}{"def"}{"merge_ol"};
1491              
1492             } elsif ($type eq "list") {
1493 0         0 return $$self{"ruleset"}{$ruleset}{"def"}{"merge_ul"};
1494              
1495             } elsif ($type eq "scalar" || $type eq "other") {
1496 1         5 return $$self{"ruleset"}{$ruleset}{"def"}{"merge_scalar"};
1497             }
1498             }
1499              
1500             ###############################################################################
1501             # MERGE
1502             ###############################################################################
1503             # This merges two NDSes into a single one.
1504              
1505             sub merge {
1506 102     102 1 389 my($self,$nds1,$nds2,@args) = @_;
1507 102         216 $$self{"err"} = "";
1508 102         166 $$self{"errmsg"} = "";
1509 102 50       305 return if (! defined $nds2);
1510              
1511             #
1512             # Parse ruleset and new arguments
1513             #
1514              
1515 102         166 my ($ruleset,$new);
1516 102 50       324 if (! @args) {
    50          
    0          
1517 0         0 $ruleset = "*";
1518 0         0 $new = 0;
1519              
1520             } elsif ($#args == 0) {
1521 102 100 66     530 if ($args[0] eq "0" || $args[0] eq "1") {
1522 4         15 $ruleset = "*";
1523 4         19 $new = $args[0];
1524             } else {
1525 98         131 $ruleset = $args[0];
1526 98         140 $new = 0;
1527             }
1528              
1529             } elsif ($#args == 1) {
1530 0         0 $ruleset = $args[0];
1531 0         0 $new = $args[1];
1532              
1533             } else {
1534 0         0 die "[merge] Unknown argument list";
1535             }
1536              
1537             #
1538             # Get nds1 and nds2 by reference or name
1539             #
1540              
1541 102         227 $nds1 = _nds($self,$nds1,$new);
1542 102 50       276 if (! defined($nds1)) {
1543 0         0 $$self{"err"} = "ndsmer12";
1544 0         0 $$self{"errmsg"} = "While merging, the first NDS is not defined: $nds1";
1545 0         0 return;
1546             }
1547              
1548 102         200 $nds2 = _nds($self,$nds2,$new);
1549 102 50       266 if (! defined($nds2)) {
1550 0         0 $$self{"err"} = "ndsmer13";
1551 0         0 $$self{"errmsg"} = "While merging, the second NDS is not defined: $nds2";
1552 0         0 return;
1553             }
1554              
1555             #
1556             # Check structure
1557             #
1558              
1559 102         310 $self->check_structure($nds1,$new);
1560 102 50       288 if ($$self{"err"}) {
1561 0         0 $$self{"err"} = "ndsmer14";
1562 0         0 $$self{"errmsg"} = "The first NDS has an invalid structure.";
1563 0         0 return;
1564             }
1565 102         317 $self->check_structure($nds2,$new);
1566 102 50       285 if ($$self{"err"}) {
1567 0         0 $$self{"err"} = "ndsmer15";
1568 0         0 $$self{"errmsg"} = "The second NDS has an invalid structure.";
1569 0         0 return;
1570             }
1571              
1572             #
1573             # Merge
1574             #
1575              
1576 102         314 my $tmp = _merge($self,$nds1,$nds2,[],$ruleset);
1577 102 50       285 if (ref($nds1) eq "HASH") {
    0          
1578 102         746 %$nds1 = %$tmp;
1579             } elsif (ref($nds1) eq "ARRAY") {
1580 0         0 @$nds1 = @$tmp;
1581             } else {
1582 0         0 $$self{"err"} = "ndsmer16";
1583 0         0 $$self{"errmsg"} = "The NDS must be a list or hash.";
1584 0         0 return;
1585             }
1586 102         484 return;
1587             }
1588              
1589             sub _merge {
1590 315     315   564 my($self,$nds1,$nds2,$pathref,$ruleset) = @_;
1591 315         614 my $path = $self->path($pathref);
1592              
1593             #
1594             # If $nds2 is empty, we'll always return whatever $nds1 is.
1595             # If $nds1 is empty or "", we'll always return whatever $nds2 is.
1596             #
1597              
1598 315 50       717 return $nds1 if ($self->empty($nds2));
1599 315 50 66     693 if ($self->empty($nds1) ||
      33        
1600             (! ref($nds1) && $nds1 eq "")) {
1601 0         0 return $nds2;
1602             }
1603              
1604             #
1605             # $method can be merge, keep, keep_warn, replace, replace_warn,
1606             # error, append
1607             #
1608             # handle keep*, replace*, and error
1609             #
1610              
1611 315         735 my $type = $self->get_structure($path);
1612 315         809 my $method = $self->get_merge($path,$ruleset);
1613              
1614 315 100 66     2057 if ($method eq "keep" || $method eq "keep_warn") {
    100 66        
    50          
1615 114 50       250 warn($self,"[merge] keeping initial value\n" .
1616             " path: $path",1) if ($method eq "keep_warn");
1617 114         501 return $nds1;
1618              
1619             } elsif ($method eq "replace" || $method eq "replace_warn") {
1620 29 50       70 warn($self,"[merge] replacing initial value\n" .
1621             " path: $path",1) if ($method eq "replace_warn");
1622 29 50       78 if (ref($nds2)) {
1623 29         115 return $nds2;
1624             }
1625 0         0 return $nds2;
1626              
1627             } elsif ($method eq "error") {
1628 0 0       0 if (ref($nds1)) {
    0          
1629 0         0 warn($self,"[merge] multiply defined value\n" .
1630             " path: $path",1);
1631 0         0 exit;
1632             } elsif ($nds1 eq $nds2) {
1633 0         0 return $nds1;
1634             } else {
1635 0         0 warn($self,"[merge] nonidentical values\n" .
1636             " path: $path",1);
1637 0         0 exit;
1638             }
1639             }
1640              
1641             #
1642             # Merge two lists
1643             #
1644              
1645 172 100       420 if (ref($nds1) eq "ARRAY") {
1646 20         57 return _merge_lists($self,$method,$nds1,$nds2,$pathref,$ruleset);
1647             }
1648              
1649             #
1650             # Merge two hashes
1651             #
1652              
1653 152 50       429 if (ref($nds1) eq "HASH") {
1654 152         420 return _merge_hashes($self,$method,$nds1,$nds2,$pathref,$ruleset);
1655             }
1656             }
1657              
1658             # Method is: merge
1659             #
1660             sub _merge_hashes {
1661 152     152   294 my($self,$method,$val1,$val2,$pathref,$ruleset) = @_;
1662              
1663 152         352 foreach my $key (CORE::keys %$val2) {
1664              
1665             #
1666             # If $val2 is empty, we'll keep $val1
1667             # If $val1 is empty or "", we'll always set it to $val2
1668             #
1669              
1670 282 50       631 next if ($self->empty($$val2{$key}));
1671              
1672 282 100 66     1056 if (! exists $$val1{$key} ||
      66        
      66        
1673             $self->empty($$val1{$key}) ||
1674             (! ref($$val1{$key}) && $$val1{$key} eq "")) {
1675 103         324 $$val1{$key} = $$val2{$key};
1676              
1677             } else {
1678 179         812 $$val1{$key} =
1679             _merge($self,$$val1{$key},$$val2{$key},[@$pathref,$key],$ruleset);
1680             }
1681             }
1682              
1683 152         627 return $val1;
1684             }
1685              
1686             # Method is: append, merge
1687             #
1688             sub _merge_lists {
1689 20     20   34 my($self,$method,$val1,$val2,$pathref,$ruleset) = @_;
1690              
1691             # Handle append unordered
1692              
1693 20 100       58 if ($method eq "append") {
1694 10         30 push(@$val1,@$val2);
1695 10         41 return $val1;
1696             }
1697              
1698             # Handle merge ordered (merge each i'th element)
1699              
1700 10         16 my($i);
1701 10         45 for ($i=0; $i<=$#$val2; $i++) {
1702              
1703             # val1[i] val2[i]
1704             # ------- -------
1705             # * empty do nothing
1706             # empty/'' * val1[i] = val2[i]
1707             # * * recurse into (including scalars)
1708              
1709 30 100 66     106 if ($self->empty($$val2[$i])) {
    100 66        
1710 5         21 next;
1711              
1712             } elsif ($self->empty($$val1[$i]) ||
1713             (! ref($$val1[$i]) && $$val1[$i] eq "")) {
1714 10         36 $$val1[$i] = $$val2[$i];
1715              
1716             } else {
1717 15         93 $$val1[$i] =
1718             _merge($self,$$val1[$i],$$val2[$i],[@$pathref,$i],$ruleset);
1719             }
1720             }
1721              
1722 10         43 return $val1;
1723             }
1724              
1725             ###############################################################################
1726             # MERGE_PATH
1727             ###############################################################################
1728              
1729             sub merge_path {
1730 21     21 1 298 my($self,$nds,$val,$path,@args) = @_;
1731 21         49 $$self{"err"} = "";
1732 21         37 $$self{"errmsg"} = "";
1733              
1734 21         55 my @path = $self->path($path);
1735 21         65 $path = $self->path(\@path);
1736              
1737 21 100       88 return merge($self,$nds,$val,@args) if (! @path);
1738              
1739             #
1740             # Parse ruleset and new arguments
1741             #
1742              
1743 19         33 my ($ruleset,$new);
1744 19 50       90 if (! @args) {
    100          
    50          
1745 0         0 $ruleset = "*";
1746 0         0 $new = 0;
1747              
1748             } elsif ($#args == 0) {
1749 15 50 33     77 if ($args[0] eq "0" || $args[0] eq "1") {
1750 15         19 $ruleset = "*";
1751 15         28 $new = $args[0];
1752             } else {
1753 0         0 $ruleset = $args[0];
1754 0         0 $new = 0;
1755             }
1756              
1757             } elsif ($#args == 1) {
1758 4         6 $ruleset = $args[0];
1759 4         14 $new = $args[1];
1760              
1761             } else {
1762 0         0 die "[merge_path] Unknown argument list";
1763             }
1764              
1765             #
1766             # Get nds by reference or name
1767             #
1768              
1769 19         51 $nds = _nds($self,$nds,0,0,1);
1770 19 50       48 if (! defined($nds)) {
1771 0         0 $$self{"err"} = "ndsmer17";
1772 0         0 $$self{"errmsg"} = "Attempt to merge a value into an undefined NDS: $nds";
1773 0         0 return;
1774             }
1775              
1776             #
1777             # Check structure
1778             #
1779              
1780 19         52 $self->check_structure($nds,$new);
1781 19 50       52 if ($self->err()) {
1782 0         0 $$self{"err"} = "ndsmer18";
1783 0         0 $$self{"errmsg"} = "The NDS has an invalid structure: $path";
1784 0         0 return;
1785             }
1786              
1787 19         63 _check_structure($self,$val,$new,@path);
1788 19 50       61 if ($self->err()) {
1789 0         0 $$self{"err"} = "ndsmer19";
1790 0         0 $$self{"errmsg"} = "The value has an invalid structure: $path";
1791 0         0 return;
1792             }
1793              
1794             #
1795             # Get the NDS stored at the path.
1796             #
1797              
1798 19         31 my $ele = pop(@path);
1799 19         78 $nds = _merge_path_nds($self,$nds,[],@path);
1800              
1801             #
1802             # Merge in the value
1803             #
1804              
1805 19 50       64 if (ref($nds) eq "HASH") {
    0          
1806 19         87 $$nds{$ele} = _merge($self,$$nds{$ele},$val,[@path,$ele],$ruleset);
1807              
1808             } elsif (ref($nds) eq "ARRAY") {
1809 0         0 $$nds[$ele] = _merge($self,$$nds[$ele],$val,[@path,$ele],$ruleset);
1810             }
1811 19         106 return;
1812             }
1813              
1814             # This returns the NDS stored at @path in $nds. $pathref is the path
1815             # of $nds with respect to the main NDS structure.
1816             #
1817             # Since we removed the last element of the path in the merge_path
1818             # method, this can ONLY be called with hash/list structures.
1819             #
1820             sub _merge_path_nds {
1821 19     19   39 my($self,$nds,$pathref,@path) = @_;
1822 19 50       65 return $nds if (! @path);
1823 0         0 my($ele) = shift(@path);
1824              
1825             # Easy case: return an existing element
1826              
1827 0 0       0 if (ref($nds) eq "HASH") {
1828 0 0       0 if (exists $$nds{$ele}) {
1829 0         0 return _merge_path_nds($self,$$nds{$ele},[@$pathref,$ele],@path);
1830             }
1831              
1832             } else {
1833 0 0       0 if (defined $$nds[$ele]) {
1834 0         0 return _merge_path_nds($self,$$nds[$ele],[@$pathref,$ele],@path);
1835             }
1836             }
1837              
1838             # Hard case: create new structure
1839              
1840 0         0 my $type = $self->get_structure([@$pathref,$ele]);
1841 0         0 my $new;
1842 0 0       0 if ($type eq "hash") {
1843 0         0 $new = {};
1844             } else {
1845 0         0 $new = [];
1846             }
1847              
1848 0 0       0 if (ref($nds) eq "HASH") {
1849 0         0 $$nds{$ele} = $new;
1850 0         0 return _merge_path_nds($self,$$nds{$ele},[@$pathref,$ele],@path);
1851              
1852             } else {
1853 0         0 $$nds[$ele] = $new;
1854 0         0 return _merge_path_nds($self,$$nds[$ele],[@$pathref,$ele],@path);
1855             }
1856             }
1857              
1858             ###############################################################################
1859             # ERASE
1860             ###############################################################################
1861             # This removes a path from an NDS based on the structural information.
1862             # Hash elements are deleted, ordered elements are cleared, unordered
1863             # elements are deleted.
1864              
1865             sub erase {
1866 10     10 1 63 my($self,$nds,$path) = @_;
1867 10         22 $$self{"err"} = "";
1868 10         17 $$self{"errmsg"} = "";
1869              
1870             #
1871             # Get the NDS
1872             #
1873              
1874 10         23 $nds = _nds($self,$nds,1,0,0);
1875 10 50       23 return undef if ($self->err());
1876              
1877             #
1878             # If $path not passed in, clear the entire NDS
1879             #
1880              
1881 10         26 my(@path) = $self->path($path);
1882 10 100       29 if (! @path) {
1883 4 100       16 if (ref($nds) eq "HASH") {
    50          
1884 2         6 %$nds = ();
1885             } elsif (ref($nds) eq "ARRAY") {
1886 2         5 @$nds = ();
1887             }
1888 4         9 return 1;
1889             }
1890              
1891             #
1892             # Get the parent of $path
1893             #
1894              
1895 6         9 my $ele = pop(@path);
1896 6         20 $nds = $self->value($nds,[@path]);
1897 6 50       16 return undef if ($self->err());
1898              
1899             #
1900             # Delete the element
1901             #
1902              
1903 6 100       17 if (ref($nds) eq "HASH") {
1904 4 50       12 if (exists $$nds{$ele}) {
1905 4         13 delete $$nds{$ele};
1906             } else {
1907 0         0 return 0;
1908             }
1909              
1910             } else {
1911 2         7 my $ordered = $self->get_structure([@path],"ordered");
1912 2 100       6 if ($ordered) {
1913 1 50       5 if (defined $$nds[$ele]) {
1914 1         3 $$nds[$ele] = undef;
1915             } else {
1916 0         0 return 0;
1917             }
1918             } else {
1919 1 50       7 if (defined $$nds[$ele]) {
1920 1         3 splice(@$nds,$ele,1);
1921             } else {
1922 0         0 return 0;
1923             }
1924             }
1925             }
1926              
1927 6         16 return 1;
1928             }
1929              
1930             ###############################################################################
1931             # WHICH
1932             ###############################################################################
1933              
1934             sub which {
1935 3     3 1 1017 my($self,$nds,@crit) = @_;
1936 3         9 $$self{"err"} = "";
1937 3         8 $$self{"errmsg"} = "";
1938              
1939 3         11 $nds = _nds($self,$nds,1,0,0);
1940              
1941 3 100       11 if (! @crit) {
1942 1         2 my %ret;
1943 1         6 _which_scalar($self,$nds,\%ret,{},[]);
1944 1         11 return %ret;
1945             } else {
1946 2         4 my(@re,%vals,%ret);
1947 2         6 foreach my $crit (@crit) {
1948 3 100       12 if (ref($crit) eq "Regexp") {
1949 1         3 push(@re,$crit);
1950             } else {
1951 2         6 $vals{$crit} = 1;
1952             }
1953             }
1954 2         11 _which_scalar($self,$nds,\%ret,\%vals,\@re);
1955 2         19 return %ret;
1956             }
1957             }
1958              
1959             # Sets %ret to be a hash of PATH => VAL for every path which
1960             # passes one of the criteria.
1961             #
1962             # If %vals is not empty, a path passes if it's value is any of
1963             # the keys in %vals.
1964             #
1965             # If @re is not empty, a path passes if it matches any of the
1966             # regular expressions in @re.
1967             #
1968             sub _which_scalar {
1969 24     24   52 my($self,$nds,$ret,$vals,$re,@path) = @_;
1970              
1971 24 100       79 if (ref($nds) eq "HASH") {
    100          
1972 6         16 foreach my $key (CORE::keys %$nds) {
1973 15         59 _which_scalar($self,$$nds{$key},$ret,$vals,$re,@path,$key);
1974             }
1975              
1976             } elsif (ref($nds) eq "ARRAY") {
1977 3         13 foreach (my $i = 0; $i <= $#$nds; $i++) {
1978 6         17 _which_scalar($self,$$nds[$i],$ret,$vals,$re,@path,$i);
1979             }
1980              
1981             } else {
1982 15         52 my $path = $self->path([@path]);
1983 15         27 my $crit = 0;
1984              
1985 15 100       37 if (CORE::keys %$vals) {
1986 5         6 $crit = 1;
1987 5 100       12 if (exists $$vals{$nds}) {
1988 2         4 $$ret{$path} = $nds;
1989 2         9 return;
1990             }
1991             }
1992              
1993 13 100       26 if (@$re) {
1994 5         6 $crit = 1;
1995 5         6 foreach my $re (@$re) {
1996 5 100       28 if ($nds =~ $re) {
1997 2         6 $$ret{$path} = $nds;
1998 2         10 return;
1999             }
2000             }
2001             }
2002              
2003 11 100       35 return if ($crit);
2004              
2005             # No criteria passed in
2006 5 50       15 $$ret{$path} = $nds if (defined $nds);
2007 5         18 return;
2008             }
2009             }
2010              
2011             ###############################################################################
2012             # PATHS
2013             ###############################################################################
2014              
2015             sub paths {
2016 16     16 1 3752 my($self,@args) = @_;
2017 16         30 $$self{"err"} = "";
2018 16         21 $$self{"errmsg"} = "";
2019 16 50       35 @args = ("scalar") if (! @args);
2020              
2021             # Parse parameters
2022              
2023 16         18 my %tmp;
2024 16         24 foreach my $arg (@args) {
2025 31 100 100     224 if ($arg eq "scalar" ||
    100 100        
    100 100        
      100        
2026             $arg eq "list" ||
2027             $arg eq "hash") {
2028 16 100 33     99 if (exists $tmp{"scalar"} ||
      66        
2029             exists $tmp{"list"} ||
2030             exists $tmp{"hash"}) {
2031 1         3 $$self{"err"} = "ndsdat07";
2032 1         5 $$self{"errmsg"} = "Invalid parameter combination in paths " .
2033             "method: @args";
2034 1         51 return undef;
2035             }
2036             } elsif ($arg eq "uniform" ||
2037             $arg eq "nonuniform") {
2038 7 50 33     34 if (exists $tmp{"uniform"} ||
2039             exists $tmp{"nonuniform"}) {
2040 0         0 $$self{"err"} = "ndsdat07";
2041 0         0 $$self{"errmsg"} = "Invalid parameter combination in paths " .
2042             "method: @args";
2043 0         0 return undef;
2044             }
2045             } elsif ($arg eq "ordered" ||
2046             $arg eq "unordered") {
2047 7 50 33     38 if (exists $tmp{"ordered"} ||
2048             exists $tmp{"unordered"}) {
2049 0         0 $$self{"err"} = "ndsdat07";
2050 0         0 $$self{"errmsg"} = "Invalid parameter combination in paths " .
2051             "method: @args";
2052 0         0 return undef;
2053             }
2054             } else {
2055 1         4 $$self{"err"} = "ndsdat08";
2056 1         4 $$self{"errmsg"} = "Invalid parameter in paths method: $arg";
2057 1         5 return undef;
2058             }
2059 29         66 $tmp{$arg} = 1;
2060             }
2061              
2062 14 100 66     57 if (exists $tmp{"scalar"} &&
      66        
2063             (exists $tmp{"uniform"} ||
2064             exists $tmp{"nonuniform"} ||
2065             exists $tmp{"ordered"} ||
2066             exists $tmp{"unordered"})) {
2067 1         2 $$self{"err"} = "ndsdat07";
2068 1         6 $$self{"errmsg"} = "Invalid parameter combination in paths " .
2069             "method: @args";
2070 1         4 return undef;
2071             }
2072              
2073 13 100 66     42 if (exists $tmp{"hash"} &&
      66        
2074             (exists $tmp{"ordered"} ||
2075             exists $tmp{"unordered"})) {
2076 1         2 $$self{"err"} = "ndsdat07";
2077 1         6 $$self{"errmsg"} = "Invalid parameter combination in paths " .
2078             "method: @args";
2079 1         4 return undef;
2080             }
2081              
2082 12 50 100     55 if (exists $tmp{"list"} &&
      66        
2083             exists $tmp{"unordered"} &&
2084             exists $tmp{"nonuniform"}) {
2085 0         0 $$self{"err"} = "ndsdat07";
2086 0         0 $$self{"errmsg"} = "Invalid parameter combination in paths " .
2087             "method: @args";
2088 0         0 return undef;
2089             }
2090              
2091             # Check which paths fit
2092              
2093              
2094 12         14 my @ret = sort(CORE::keys %{ $$self{"struct"} });
  12         143  
2095              
2096 12         29 my $type = "";
2097 12 100       39 if (exists $tmp{"scalar"}) {
    100          
    50          
2098 1         4 $type = "scalar";
2099             } elsif (exists $tmp{"list"}) {
2100 8         10 $type = "list";
2101             } elsif (exists $tmp{"hash"}) {
2102 3         5 $type = "hash";
2103             }
2104 12 50       23 if ($type) {
2105 12         13 my @tmp;
2106 12         17 foreach my $path (@ret) {
2107 216 100       495 push(@tmp,$path) if ($$self{"struct"}{$path}{"type"} eq $type);
2108             }
2109 12         44 @ret = @tmp;
2110             }
2111              
2112 12         16 my $ordered = "";
2113 12 100       34 if (exists $tmp{"ordered"}) {
    100          
2114 3         4 $ordered = 1;
2115             } elsif (exists $tmp{"unordered"}) {
2116 2         3 $ordered = 0;
2117             }
2118 12 100       25 if ($ordered ne "") {
2119 5         5 my @tmp;
2120 5         8 foreach my $path (@ret) {
2121 15 100 66     77 push(@tmp,$path) if (exists $$self{"struct"}{$path}{"ordered"} &&
2122             $$self{"struct"}{$path}{"ordered"} == $ordered);
2123             }
2124 5         12 @ret = @tmp;
2125             }
2126              
2127 12         15 my $uniform = "";
2128 12 100       29 if (exists $tmp{"uniform"}) {
    100          
2129 4         6 $uniform = 1;
2130             } elsif (exists $tmp{"nonuniform"}) {
2131 3         5 $uniform = 0;
2132             }
2133 12 100       25 if ($uniform ne "") {
2134 7         8 my @tmp;
2135 7         9 foreach my $path (@ret) {
2136 23 100 66     121 push(@tmp,$path) if (exists $$self{"struct"}{$path}{"uniform"} &&
2137             $$self{"struct"}{$path}{"uniform"} == $uniform);
2138             }
2139 7         20 @ret = @tmp;
2140             }
2141              
2142 12         59 return @ret;
2143             }
2144              
2145             ###############################################################################
2146             # TEST_CONDITIONS
2147             ###############################################################################
2148              
2149             sub test_conditions {
2150 1181     1181 1 28101 my($self,$nds,@cond) = @_;
2151 1181         1957 $$self{"err"} = "";
2152 1181         1820 $$self{"errmsg"} = "";
2153 1181 100       2600 return 1 if (! @cond);
2154              
2155 1165         2384 COND: while (@cond) {
2156 1165         1848 my $path = shift(@cond);
2157 1165         1531 my $cond = shift(@cond);
2158              
2159             # Get the value at the path. An error code means that the path
2160             # is not defined (but the path is valid in the sense that it COULD
2161             # be there... it just doesn't exist in this NDS).
2162              
2163 1165         2722 my $v = $self->value($nds,$path,0,1);
2164 1165 100       2303 if ($self->err()) {
2165 208         368 $$self{"err"} = "";
2166 208         381 $$self{"errmsg"} = "";
2167 208         268 $v = undef;
2168             }
2169              
2170 1165 100       3475 if (! defined $v) {
    100          
    100          
2171             # no path does NOT automatically mean failure... worse, we
2172             # can't tell whether it should be tested as a hash, list, or
2173             # scalar
2174 356         758 my($valid,$pass) = _test_hash_condition($self,$v,$cond);
2175 356 100       689 if ($valid) {
2176 152 100       602 return 0 if (! $pass);
2177             } else {
2178 204 100 66     466 return 0 if (! _test_list_condition($self,$v,$cond) &&
2179             ! _test_scalar_condition($self,$v,$cond));
2180             }
2181              
2182             } elsif (ref($v) eq "HASH") {
2183 79         163 my($valid,$pass) = _test_hash_condition($self,$v,$cond);
2184 79 100       166 if ($valid) {
2185 78 100       317 return 0 if (! $pass);
2186             } else {
2187             # Set error (invalid condition)
2188 1         3 $$self{"err"} = "ndscon01";
2189 1         4 $$self{"errmsg"} = "Invalid test condition used: $path: $cond";
2190 1         4 return undef;
2191             }
2192              
2193             } elsif (ref($v) eq "ARRAY") {
2194 348 100       1079 return 0 if (! _test_list_condition($self,$v,$cond));
2195              
2196             } else {
2197 382 100       887 return 0 if (! _test_scalar_condition($self,$v,$cond));
2198             }
2199             }
2200              
2201 564         1702 return 1;
2202             }
2203              
2204             # If $nds contains a hash, condition can be any of the following:
2205             #
2206             # exists:VAL : true if a key named VAL exists in the hash
2207             # empty:VAL : true if a key named VAL is empty in the hash (it
2208             # doesn't exist, or has an empty value)
2209             # empty : true if the hash is empty
2210             #
2211             sub _test_hash_condition {
2212 435     435   659 my($self,$nds,$cond) = @_;
2213              
2214             # Make sure it's a valid condition for this data type.
2215              
2216 435 100 66     2543 if ($cond !~ /^\!?empty(:.+)?$/i &&
2217             $cond !~ /^\!?exists:.+$/i) {
2218 205         525 return (0,0);
2219             }
2220              
2221             # An undefined value:
2222             # passes empty
2223             # passes empty:VAL
2224             # passes !exists:VAL
2225             # fails all others
2226              
2227 230 100       523 if (! defined $nds) {
2228 152 100 100     812 return (1,1) if ($cond =~ /^empty/i ||
2229             $cond =~ /^\!exists/i);
2230 76         175 return (1,0);
2231             }
2232              
2233             # A non-hash element should not even be passed in.
2234              
2235 78 50       196 if (ref($nds) ne "HASH") {
2236 0         0 die "ERROR: [_test_hash_condition] impossible: non-hash passed in\n";
2237             }
2238              
2239             # Test for existance of a key or an empty key
2240              
2241 78 100       300 if ($cond =~ /^(\!?)(exists|empty):(.+)$/) {
2242 56         204 my ($not,$op,$key) = ($1,$2,$3);
2243 56         104 my $exists = (exists $$nds{$key});
2244              
2245 56 100       142 if (lc($op) eq "exists") {
2246 28 100 100     224 return (1,1) if ( ($exists && ! $not) ||
      100        
      66        
2247             (! $exists && $not) );
2248 14         41 return (1,0);
2249             }
2250              
2251 28         38 my $empty = 1;
2252 28 100       127 $empty = $self->empty([ $$nds{$key} ]) if ($exists);
2253              
2254 28 100 100     232 return (1,1) if ( ($empty && ! $not) ||
      100        
      66        
2255             (! $empty && $not) );
2256 14         37 return (1,0);
2257             }
2258              
2259             # An empty value:
2260             # passes empty
2261             # fails !empty
2262             # A non-empty value:
2263             # fails empty
2264             # passes !empty
2265              
2266 22         45 $cond = lc($cond);
2267 22 100       53 if ($self->empty($nds)) {
2268 10 100       39 return (1,1) if ($cond eq "empty");
2269 5 50       39 return (1,0) if ($cond eq "!empty");
2270             } else {
2271 12 100       45 return (1,0) if ($cond eq "empty");
2272 6 50       31 return (1,1) if ($cond eq "!empty");
2273             }
2274             }
2275              
2276             # If $path refers to a list, conditions may be any of the following:
2277             #
2278             # empty : true if the list is empty
2279             # defined:VAL : true if the VAL'th (VAL is an integer) element
2280             # is defined
2281             # empty:VAL : true if the VAL'th (VAL is an integer) element
2282             # is empty (or not defined)
2283             # contains:VAL : true if the list contains the element VAL
2284             # <:VAL : true if the list has fewer than VAL (an integer)
2285             # non-empty elements
2286             # <=:VAL
2287             # =:VAL
2288             # >:VAL
2289             # >=:VAL
2290             # VAL : equivalent to contains:VAL
2291             #
2292             sub _test_list_condition {
2293 552     552   910 my($self,$nds,$cond) = @_;
2294              
2295             # An undefined value:
2296             # passes empty
2297             # passes empty:VAL
2298             # passes !defined:VAL
2299             # passes !contains:VAL
2300             # passes =:0
2301             # passes !=:* (not zero)
2302             # passes <:*
2303             # passes <=:*
2304             # passes >=:0
2305             # fails all others
2306              
2307 552 100       1095 if (! defined($nds)) {
2308 204 100 66     3827 return 1 if ($cond =~ /^empty(:.+)?$/i ||
      100        
      66        
      100        
      100        
      100        
      66        
2309             $cond =~ /^\!defined:(.+)$/i ||
2310             $cond =~ /^\!contains:(.+)$/i ||
2311             $cond eq "=:0" ||
2312             $cond =~ /^\!=:(\d*[1-9]\d*)$/ ||
2313             $cond =~ /^<:(\d+)$/ ||
2314             $cond =~ /^<=:(\d+)$/ ||
2315             $cond eq ">=:0");
2316 148         699 return 0;
2317             }
2318              
2319             # A non-list element should not even be passed in.
2320              
2321 348 50       745 if (ref($nds) ne "ARRAY") {
2322 0         0 die "ERROR: [_test_list_condition] impossible: non-list passed in\n";
2323             }
2324              
2325             # Test for defined/empty keys
2326              
2327 348 100       1144 if ($cond =~ /^(\!?)(defined|empty):(\d+)$/i) {
2328 112         325 my ($not,$op,$i) = ($1,$2,$3);
2329 112         285 my $def = (defined $$nds[$i]);
2330              
2331 112 100       256 if (lc($op) eq "defined") {
2332 72 100 100     564 return 1 if ( ($def && ! $not) ||
      100        
      66        
2333             (! $def && $not) );
2334 36         198 return 0;
2335             }
2336              
2337 40         60 my $empty = 1;
2338 40 100       143 $empty = $self->empty([ $$nds[$i] ]) if ($def);
2339              
2340 40 100 100     354 return 1 if ( ($empty && ! $not) ||
      100        
      66        
2341             (! $empty && $not) );
2342 20         103 return 0;
2343             }
2344              
2345             # < <= = > >= tests
2346              
2347 236 100       844 if ($cond =~ /^(\!?)(<=|<|=|>=|>):(\d+)$/) {
2348 126         362 my($not,$op,$val) = ($1,$2,$3);
2349 126         156 my $n = 0;
2350 126         204 foreach my $v (@$nds) {
2351 224 100       793 $n++ if (! $self->empty([ $v ]));
2352             }
2353              
2354 126 100       370 if ($op eq "<") {
    100          
    100          
    100          
2355 38 100 100     337 return 1 if ( ($n < $val && ! $not) ||
      100        
      66        
2356             ($n >= $val && $not) );
2357 19         100 return 0;
2358              
2359             } elsif ($op eq "<=") {
2360 38 100 100     344 return 1 if ( ($n <= $val && ! $not) ||
      100        
      66        
2361             ($n > $val && $not) );
2362 19         106 return 0;
2363              
2364             } elsif ($op eq "=") {
2365 36 100 100     375 return 1 if ( ($n == $val && ! $not) ||
      100        
      66        
2366             ($n != $val && $not) );
2367 18         94 return 0;
2368              
2369             } elsif ($op eq ">=") {
2370 8 100 100     65 return 1 if ( ($n >= $val && ! $not) ||
      100        
      66        
2371             ($n < $val && $not) );
2372 4         15 return 0;
2373              
2374             } else {
2375 6 100 100     48 return 1 if ( ($n > $val && ! $not) ||
      100        
      66        
2376             ($n <= $val && $not) );
2377 3         13 return 0;
2378             }
2379             }
2380              
2381             # contains condition
2382              
2383 110 100       394 if ($cond =~ /^(\!?)contains:(.*)$/i) {
2384 68         191 my($not,$val) = ($1,$2);
2385 68 50       148 $val = "" if (! defined $val);
2386 68         122 foreach my $v (@$nds) {
2387 142 100       277 next if (! defined $v);
2388 118 100       279 if ($v eq $val) {
2389 26 100       113 return 1 if (! $not);
2390 13 50       95 return 0 if ($not);
2391             }
2392             }
2393 42 100       188 return 0 if (! $not);
2394 21         103 return 1;
2395             }
2396              
2397             # An empty list:
2398             # passes empty
2399             # fails !empty
2400             # A non-empty list:
2401             # fails empty
2402             # passes !empty
2403              
2404 42         70 my $c = lc($cond);
2405 42 100       122 if ($self->empty([ $nds ])) {
2406 12 100       66 return 1 if ($c eq "empty");
2407 6 50       47 return 0 if ($c eq "!empty");
2408             } else {
2409 30 100       134 return 0 if ($c eq "empty");
2410 17 100       99 return 1 if ($c eq "!empty");
2411             }
2412              
2413             # VAL test
2414              
2415 4         6 my $not = 0;
2416 4 100       16 $not = 1 if ($cond =~ s/^\!//);
2417              
2418 4         7 foreach my $v (@$nds) {
2419 6 50       10 next if (! defined $v);
2420 6 100       13 if ($v eq $cond) {
2421 2 100       65 return 1 if (! $not);
2422 1 50       7 return 0 if ($not);
2423             }
2424             }
2425 2 100       11 return 0 if (! $not);
2426 1         5 return 1;
2427             }
2428              
2429             # If $path refers to a scalar, conditions may be any of the following:
2430             #
2431             # defined : true if the value is not defined
2432             # empty : true if the value is empty
2433             # zero : true if the value defined and evaluates to 0
2434             # true : true if the value defined and evaluates to true
2435             # =:VAL : true if the the value is VAL
2436             # member:VAL:VAL:...
2437             # : true if the value is any of the values given (in
2438             # this case, ALL of the colons (including the first
2439             # one) can be replace by any other single character
2440             # separator
2441             # VAL : true if the value is equal to VAL
2442             #
2443             sub _test_scalar_condition {
2444 530     530   838 my($self,$nds,$cond) = @_;
2445              
2446             # An undefined value
2447             # passes !defined
2448             # passes !zero
2449             # passes !true
2450             # passes empty
2451             # passes !=:*
2452             # passes !member:*
2453             # fails all others
2454              
2455 530 100       1055 if (! defined $nds) {
2456 148 100 66     2360 return 1 if ($cond =~ /^!defined$/i ||
      100        
      100        
      100        
      100        
2457             $cond =~ /^empty$/i ||
2458             $cond =~ /^\!zero$/i ||
2459             $cond =~ /^\!true$/i ||
2460             $cond =~ /^\!=:/ ||
2461             $cond =~ /^\!member/i);
2462 113         657 return 0;
2463             }
2464              
2465             # A non-scalar element should not even be passed in.
2466              
2467 382 50       702 if (ref($nds)) {
2468 0         0 die "ERROR: [_test_scalar_condition] impossible: non-scalar passed in\n";
2469             }
2470              
2471             # A defined value
2472             # passes defined
2473             # fails ! defined
2474              
2475 382         617 my($c) = lc($cond);
2476 382 100       789 return 1 if ($c eq "defined");
2477 363 100       896 return 0 if ($c eq "!defined");
2478              
2479             # An empty value (must pass it as a structure, NOT a scalar)
2480             # passes empty
2481             # fails !empty
2482             # A non-empty value
2483             # passes !empty
2484             # fails empty
2485              
2486 344 100       1004 if ($self->empty([$nds])) {
2487 38 100       115 return 1 if ($c eq "empty");
2488 33 100       110 return 0 if ($c eq "!empty");
2489             } else {
2490 306 100       666 return 0 if ($c eq "empty");
2491 292 100       647 return 1 if ($c eq "!empty");
2492             }
2493              
2494 306 50       808 $nds = "" if (! defined $nds);
2495              
2496             # zero and true tests
2497              
2498 306 100       1219 if ($c eq "zero") {
    100          
    100          
    100          
2499 3 100 100     22 return 1 if ($nds eq "" || $nds == 0);
2500 1         6 return 0;
2501             } elsif ($c eq "!zero") {
2502 3 100 100     21 return 0 if ($nds eq "" || $nds == 0);
2503 1         6 return 1;
2504             } elsif ($c eq "true") {
2505 3 100       9 return 1 if ($nds);
2506 2         11 return 0;
2507             } elsif ($c eq "!true") {
2508 3 100       11 return 0 if ($nds);
2509 2         11 return 1;
2510             }
2511              
2512             # = test
2513              
2514 294 100       748 if ($cond =~ /^(\!?)=:(.*)/) {
2515 38         116 my($not,$val) = ($1,$2);
2516 38 50       77 $val = "" if (! defined $val);
2517 38 100 100     352 return 1 if ( ($nds eq $val && ! $not) ||
      100        
      66        
2518             ($nds ne $val && $not) );
2519 19         100 return 0;
2520             }
2521              
2522             # member test
2523              
2524 256 100       701 if ($cond =~ /^(\!?)member(.)(.+)$/) {
2525 72         222 my($not,$sep,$vals) = ($1,$2,$3);
2526 72 50       488 my %tmp = map { (defined $_ ? $_ : ""),1 } split(/\Q$sep\E/,$vals);
  116         507  
2527 72 100 100     932 return 1 if ( (exists $tmp{$nds} && ! $not) ||
      100        
      66        
2528             (! exists $tmp{$nds} && $not) );
2529 36         217 return 0;
2530             }
2531              
2532             # VAL test
2533              
2534 184 100       462 if ($cond =~ s/^\!//) {
2535 3 100       14 return 0 if ($nds eq $cond);
2536 1         6 return 1;
2537             }
2538              
2539 181 100       648 return 1 if ($nds eq $cond);
2540 97         695 return 0;
2541             }
2542              
2543             ###############################################################################
2544             # IDENTICAL, CONTAINS
2545             ###############################################################################
2546              
2547             sub identical {
2548 10     10 1 2647 my($self,@args) = @_;
2549 10         32 $$self{"err"} = "";
2550 10         28 $$self{"errmsg"} = "";
2551              
2552 10         35 my($nds1,$nds2,$path) = _ic_args($self,@args);
2553 10 50       24 return if ($self->err());
2554              
2555 10         41 _DBG_begin("Identical");
2556              
2557 10         37 my $flag = _identical_contains($self,$nds1,$nds2,1,$path);
2558              
2559 10         32 _DBG_end($flag);
2560 10         103 return $flag;
2561             }
2562              
2563             sub contains {
2564 10     10 1 91 my($self,@args) = @_;
2565 10         28 $$self{"err"} = "";
2566 10         25 $$self{"errmsg"} = "";
2567              
2568 10         35 my($nds1,$nds2,$path) = _ic_args($self,@args);
2569 10 50       22 return if ($self->err());
2570              
2571 10         38 _DBG_begin("Contains");
2572              
2573 10         31 my $flag = _identical_contains($self,$nds1,$nds2,0,$path);
2574              
2575 10         34 _DBG_end($flag);
2576 10         71 return $flag;
2577             }
2578              
2579             sub _ic_args {
2580 20     20   73 my($self,$nds1,$nds2,@args) = @_;
2581              
2582             #
2583             # Parse $new and $path
2584             #
2585              
2586 20         32 my($new,$path);
2587 20 50       79 if (! @args) {
    50          
    0          
2588 0         0 $new = 0;
2589 0         0 $path = "";
2590             } elsif ($#args == 0) {
2591 20 50 33     113 if ($args[0] eq "0" || $args[0] eq "1") {
2592 0         0 $new = $args[0];
2593 0         0 $path = "";
2594             } else {
2595 20         30 $new = 0;
2596 20         33 $path = $args[0];
2597             }
2598             } elsif ($#args == 1) {
2599 0         0 $new = $args[0];
2600 0         0 $path = $args[1];
2601             } else {
2602 0         0 die "[identical/contains] invalid arguments";
2603             }
2604              
2605             #
2606             # Check the two NDSes for validity, and return them as refs.
2607             #
2608              
2609 20         61 $nds1 = _nds($self,$nds1,$new,0,0);
2610 20 50       61 if ($self->err()) {
2611 0         0 $$self{"err"} = "ndside01";
2612 0         0 $$self{"errmsg"} = "The first NDS is invalid: $nds1";
2613 0         0 return;
2614             }
2615 20         85 $nds2 = _nds($self,$nds2,$new,0,0);
2616 20 50       187 if ($self->err()) {
2617 0         0 $$self{"err"} = "ndside02";
2618 0         0 $$self{"errmsg"} = "The first NDS is invalid: $nds2";
2619 0         0 return;
2620             }
2621              
2622 20         64 return ($nds1,$nds2,$path);
2623             }
2624              
2625             sub _identical_contains {
2626 20     20   41 my($self,$nds1,$nds2,$identical,$path) = @_;
2627 20         47 _DBG_enter("_identical_contains");
2628              
2629             #
2630             # Handle $path
2631             #
2632              
2633 20         45 $path = $self->path($path);
2634 20         46 my @path = $self->path($path);
2635              
2636             #
2637             # We will now recurse through the data structure and get an
2638             # mpath description.
2639             #
2640             # An mpath description will be stored as:
2641             # %desc = ( MPATH => DESC )
2642             #
2643             # An MPATH is related to a PATH, except that every path element that
2644             # contains an index for an unordered list is transformed to illustrate
2645             # this. For example, for the path:
2646             # /foo/1/bar/2
2647             # the mpath is:
2648             # /foo/_ul_1/bar/_ul_2
2649             # (assuming that the 2nd and 4th elements are indices in unorderd
2650             # lists).
2651             #
2652              
2653 20         31 my(%desc1,%desc2);
2654 20 50       52 if ($path ne "/") {
2655 20         69 $nds1 = $self->value($nds1,$path);
2656 20         62 $nds2 = $self->value($nds2,$path);
2657             }
2658 20         117 _ic_desc($self,$nds1,\%desc1,[@path],[@path],0,$self->delim());
2659 20         109 _ic_desc($self,$nds2,\%desc2,[@path],[@path],0,$self->delim());
2660              
2661             #
2662             # Now check these description hashes to see if they are identical
2663             # (or contained). This is done recusively.
2664             #
2665              
2666 20         92 my $flag = _ic_compare($self,\%desc1,\%desc2,$identical,$self->delim());
2667 20         52 _DBG_leave($flag);
2668 20         244 return $flag;
2669             }
2670              
2671             # This compares all elements of two description hashes to see if
2672             # they are identical, or if the second is contained in the first.
2673             #
2674             sub _ic_compare {
2675 187     187   333 my($self,$desc1,$desc2,$identical,$delim) = @_;
2676 187         417 _DBG_enter("_ic_compare");
2677 187 50       376 if ($_DBG) {
2678 0         0 _DBG_line("DESC1 =");
2679 0         0 foreach my $mpath (sort(CORE::keys %$desc1)) {
2680 0         0 my $val = $$desc1{$mpath}{"val"} .
2681 0         0 " [" . join(" ",@{ $$desc1{$mpath}{"meles"} }) . "]";
2682 0         0 _DBG_line(" $mpath\t= $val");
2683             }
2684 0         0 _DBG_line("DESC2 =");
2685 0         0 foreach my $mpath (sort(CORE::keys %$desc2)) {
2686 0         0 my $val = $$desc2{$mpath}{"val"} .
2687 0         0 " [" . join(" ",@{ $$desc2{$mpath}{"meles"} }) . "]";
2688 0         0 _DBG_line(" $mpath\t= $val");
2689             }
2690             }
2691              
2692             #
2693             # Separate %desc into two sections. Move everything containing any
2694             # unordered list induces to %ul. %desc will end up containing
2695             # everything else (which is handled very simply).
2696             #
2697              
2698 187         241 my(%ul1,%ul2);
2699 187         445 _ic_ul($desc1,\%ul1);
2700 187         451 _ic_ul($desc2,\%ul2);
2701              
2702             #
2703             # One trivial case... if %desc2 is bigger than %desc1, (or %ul2
2704             # is bigger than %ul1) it isn't contained in it. If they are not
2705             # equal in size, they can't be identical.
2706             #
2707              
2708 187         2274 my @d1 = CORE::keys %$desc1;
2709 187         521 my @d2 = CORE::keys %$desc2;
2710 187         489 my @u1 = CORE::keys %ul1;
2711 187         438 my @u2 = CORE::keys %ul2;
2712 187 100       373 if ($identical) {
2713 71 100 100     641 _DBG_leave("Not equal"), return 0 if ($#d1 != $#d2 ||
2714             $#u1 != $#u2);
2715             } else {
2716 116 100 66     648 _DBG_leave("Bigger"), return 0 if ($#d1 < $#d2 ||
2717             $#u1 < $#u2);
2718             }
2719              
2720             #
2721             # Do the easy part... elements with no unordered lists. All in
2722             # %desc2 must be in %desc1. For identical tests, nothing else
2723             # can exist.
2724             #
2725              
2726 182         365 foreach my $mpath (@d2) {
2727 192 100 100     1289 if (exists $$desc1{$mpath} &&
2728             $$desc1{$mpath}{"val"} eq $$desc2{$mpath}{"val"}) {
2729 77         216 delete $$desc1{$mpath};
2730 77         211 delete $$desc2{$mpath};
2731 77         132 next;
2732             } else {
2733 115         228 _DBG_leave("Desc differs");
2734 115         2268 return 0;
2735             }
2736             }
2737              
2738 67         151 @d1 = CORE::keys %$desc1;
2739 67 50 66     233 _DBG_leave("Desc not equal"), return 0 if ($identical && @d1);
2740              
2741             #
2742             # Now do elements containing unordered lists.
2743             #
2744              
2745 67 100       179 if ($#u2 == -1) {
2746 15 50 66     71 _DBG_leave("UL not identical"), return 0 if ($identical && $#u1 > -1);
2747 15         33 _DBG_leave(1);
2748 15         415 return 1;
2749             }
2750 52         169 my $flag = _ic_compare_ul($self,\%ul1,\%ul2,$identical,$delim);
2751 52         134 _DBG_leave($flag);
2752 52         575 return $flag;
2753             }
2754              
2755             # This recurses through %ul1 and %ul2 to try all possible combinations
2756             # of indices for unordered elements. At every level of recusion, we do
2757             # the left-most set of indices.
2758             #
2759             sub _ic_compare_ul {
2760 52     52   100 my($self,$ul1,$ul2,$identical,$delim) = @_;
2761 52         146 _DBG_enter("_ic_compare_ul");
2762 52 50       110 if ($_DBG) {
2763 0         0 _DBG_line("UL1 =");
2764 0         0 foreach my $mpath (sort(CORE::keys %$ul1)) {
2765 0         0 my $val = $$ul1{$mpath}{"val"} .
2766 0         0 " [" . join(" ",@{ $$ul1{$mpath}{"meles"} }) . "]";
2767 0         0 _DBG_line(" $mpath\t= $val");
2768             }
2769 0         0 _DBG_line("UL2 =");
2770 0         0 foreach my $mpath (sort(CORE::keys %$ul2)) {
2771 0         0 my $val = $$ul2{$mpath}{"val"} .
2772 0         0 " [" . join(" ",@{ $$ul2{$mpath}{"meles"} }) . "]";
2773 0         0 _DBG_line(" $mpath\t= $val");
2774             }
2775             }
2776              
2777             #
2778             # We need to get a list of all similar mpaths up to this level.
2779             # To determine if two mpaths are similar, look at the first element
2780             # in @meles in each.
2781             #
2782             # If both are unordered list indices (not necessarily identical) or
2783             # both are NOT unordered list indices and are identical, then they
2784             # are similar.
2785             #
2786              
2787 52         60 COMPARE: while (1) {
2788 92         259 my @mpath2 = CORE::keys %$ul2;
2789 92 100       279 last COMPARE if (! @mpath2);
2790              
2791             #
2792             # Look at the first element in @meles in one of the $ul entries.
2793             # It will either be an unordered list index or a set of 1 or more
2794             # path elements which do NOT contain unordered list indices.
2795             #
2796              
2797 58         83 my $mpath = $mpath2[0];
2798 58         129 my $mele = $$ul2{$mpath}{"meles"}[0];
2799              
2800 58 100       202 if ($mele =~ /^_ul_/) {
2801              
2802             # Get a list of all elements with a first $mele an _ul_ and
2803             # move them to a temporary description hash.
2804              
2805 29         36 my(%tmp1,%tmp2);
2806 29         71 _ic_ul2desc($ul1,\%tmp1,$mele,1);
2807 29         89 _ic_ul2desc($ul2,\%tmp2,$mele,1);
2808              
2809             # Find the number of unique $mele in %ul1 and %ul2 . If
2810             # the number in %ul2 is greater, it can't be contained. It
2811             # can't be identical unless the two numbers are the same.
2812              
2813 29         95 my $max1 = _ic_max_idx(\%tmp1);
2814 29         61 my $max2 = _ic_max_idx(\%tmp2);
2815              
2816 29 50       79 _DBG_leave("Bigger"), return 0 if ($max2 > $max1);
2817 29 50 66     103 _DBG_leave("Not equal"), return 0 if ($identical && $max1 != $max2);
2818              
2819             # Copy all elements from %ul1 to %desc1, but change them
2820             # from _ul_I to J (where J is 0..MAX)
2821             #
2822             # After we set a combination, we need to reset MELES.
2823              
2824 29         45 my $desc1 = {};
2825 29         107 _ic_permutation(\%tmp1,$desc1,(0..$max1));
2826 29         197 foreach my $mp (CORE::keys %$desc1) {
2827 132         252 $$desc1{$mp}{"meles"} = _ic_mpath2meles($self,$mp,$delim);
2828             }
2829              
2830             # Try every combination of the elements in %ul2 setting
2831             # _ul_I to J (where J is 1..MAX and MAX comes from %ul1)
2832              
2833 29         329 my $p = new Algorithm::Permute([0..$max1],$max2+1);
2834              
2835 29         174 while (my(@idx) = $p->next) {
2836              
2837 138         588 my $d1 = {};
2838 138         210 my $d2 = {};
2839 138         8102 $d1 = dclone($desc1);
2840 138         554 _ic_permutation(\%tmp2,$d2,@idx);
2841 138         530 foreach my $mp (CORE::keys %$d2) {
2842 538         1018 $$d2{$mp}{"meles"} = _ic_mpath2meles($self,$mp,$delim);
2843             }
2844              
2845             next COMPARE
2846 138 100       388 if (_ic_compare($self,$d1,$d2,$identical,$delim));
2847             }
2848              
2849 9         24 _DBG_leave("Unordered list fails");
2850 9         284 return 0;
2851              
2852             } else {
2853              
2854             #
2855             # Not an unordered list.
2856             #
2857             # Go through all %ul mpaths and take all elements which
2858             # have the same leading $mele and move them to a new
2859             # %desc hash. Then compare the two %desc hashes.
2860             #
2861              
2862 29         41 my(%desc1,%desc2);
2863 29         86 _ic_ul2desc($ul1,\%desc1,$mele,0);
2864 29         76 _ic_ul2desc($ul2,\%desc2,$mele,0);
2865              
2866 29 100       132 _DBG_leave("Desc fails"), return 0
2867             if (! _ic_compare($self,\%desc1,\%desc2,$identical,$delim));
2868              
2869             }
2870             }
2871              
2872 34         60 my @mpath1 = CORE::keys %$ul1;
2873 34 50 33     93 _DBG_leave("Remaining items fail"), return 0 if (@mpath1 && $identical);
2874 34         82 _DBG_leave(1);
2875 34         83 return 1;
2876             }
2877              
2878             # This recurses through a data structure and creates a description of
2879             # every path containing a scalar. The description is a hash of the
2880             # form:
2881             #
2882             # %desc =
2883             # ( MPATH =>
2884             # { val => VAL the scalar at the path
2885             # path => PATH the actual path /a/1
2886             # mpath => MPATH the modified path /a/_ul_1
2887             # ul => N the number of unordered indices in mpath
2888             # meles => MELES a list of modified elements (see below)
2889             # mele => MELE the part of MELES currently being examined
2890             # }
2891             # )
2892             #
2893             # Ths MELES list is a list of "elements" where can be combined to form the
2894             # mpath (using the delimiter). Each element of MELES is either an index of
2895             # an unordered list or all adjacent path elements which are not unordered
2896             # list indices. For example, the mpath:
2897             # /a/_ul_1/b/c/_ul_3/_ul_4
2898             # would become the following MELES
2899             # [ a, _ul_1, b/c, _ul_3, _ul_4 ]
2900             #
2901             # We'll pass both the path and mpath (as listrefs) as arguments as well
2902             # as a flag whether or not we've had any unordered elements in the path
2903             # to this point.
2904             #
2905             sub _ic_desc {
2906 324     324   616 my($self,$nds,$desc,$mpath,$path,$ul,$delim) = @_;
2907              
2908 324 100       1039 if (ref($nds) eq "HASH") {
    100          
    50          
2909 46         119 foreach my $key (CORE::keys %$nds) {
2910 124         530 _ic_desc($self,$$nds{$key},$desc,[@$mpath,$key],[@$path,$key],$ul,
2911             $delim);
2912             }
2913              
2914             } elsif (ref($nds) eq "ARRAY") {
2915 60         216 my $ordered = $self->get_structure([@$path,0],"ordered");
2916              
2917 60 50       150 if ($ordered) {
2918 0         0 for (my $i=0; $i<=$#$nds; $i++) {
2919 0         0 _ic_desc($self,$$nds[$i],$desc,[@$mpath,$i],[@$path,$i],$ul,$delim);
2920             }
2921              
2922             } else {
2923 60         189 for (my $i=0; $i<=$#$nds; $i++) {
2924 160         900 _ic_desc($self,$$nds[$i],$desc,[@$mpath,"_ul_$i"],[@$path,$i],$ul+1,
2925             $delim);
2926             }
2927             }
2928              
2929             } elsif (! $self->empty($nds)) {
2930 218         434 my $p = $self->path($path);
2931 218         405 my $mp = $self->path($mpath);
2932              
2933 218         500 $$desc{$mp} = { "val" => $nds,
2934             "path" => $p,
2935             "mpath" => $mp,
2936             "meles" => _ic_mpath2meles($self,$mpath,$delim),
2937             "ul" => $ul
2938             };
2939             }
2940             }
2941              
2942             # Move all elements from %desc to %ul which have unordered list elements
2943             # in them.
2944             #
2945             sub _ic_ul {
2946 374     374   484 my($desc,$ul) = @_;
2947              
2948 374         958 foreach my $mpath (CORE::keys %$desc) {
2949 1594 100       4034 if ($$desc{$mpath}{"ul"}) {
2950 590         851 $$ul{$mpath} = $$desc{$mpath};
2951 590         1016 delete $$desc{$mpath};
2952             }
2953             }
2954             }
2955              
2956             # This moves moves all elements from %ul to %desc which have the given
2957             # first element in @meles.
2958             #
2959             # $mele can be an unordered list element (in which case all elements
2960             # with unordered list elements are moved) or not (in which case, all
2961             # elements with the same first $mele are moved).
2962             #
2963             sub _ic_ul2desc {
2964 116     116   191 my($ul,$desc,$mele,$isul) = @_;
2965              
2966 116         276 foreach my $mpath (CORE::keys %$ul) {
2967 583 100 66     3834 if ( ($isul && $$ul{$mpath}{"meles"}[0] =~ /^_ul_/) ||
      66        
      66        
2968             (! $isul && $$ul{$mpath}{"meles"}[0] eq $mele) ) {
2969              
2970             # Move the element to %desc
2971              
2972 512         955 $$desc{$mpath} = $$ul{$mpath};
2973 512         696 delete $$ul{$mpath};
2974              
2975             # Fix @meles accordingly
2976              
2977 512         541 my @meles = @{ $$desc{$mpath}{"meles"} };
  512         1329  
2978 512         747 my $m = shift(@meles);
2979              
2980 512         1048 $$desc{$mpath}{"meles"} = [ @meles ];
2981 512         1554 $$desc{$mpath}{"mele"} = $m;
2982             }
2983             }
2984             }
2985              
2986             # This goes through a description hash (%desc) and sets the "meles" value
2987             # for each element.
2988             #
2989             sub _ic_mpath2meles {
2990 888     888   1496 my($self,$mpath,$delim) = @_;
2991 888         1604 my(@mpath) = $self->path($mpath);
2992              
2993 888         1564 my @meles = ();
2994 888         1096 my $tmp = "";
2995 888         1162 foreach my $mele (@mpath) {
2996 2638 100       4766 if ($mele =~ /^_ul_/) {
2997 398 100       715 if ($tmp) {
2998 304         431 push(@meles,$tmp);
2999 304         372 $tmp = "";
3000             }
3001 398         704 push(@meles,$mele);
3002             } else {
3003 2240 100       3325 if ($tmp) {
3004 1254         2397 $tmp .= "$delim$mele";
3005             } else {
3006 986         1781 $tmp = $mele;
3007             }
3008             }
3009             }
3010 888 100       2174 if ($tmp) {
3011 682         1113 push(@meles,$tmp);
3012             }
3013 888         5775 return [ @meles ];
3014             }
3015              
3016             # This goes through all of the elements in a %desc hash. All of them should
3017             # have a descriptor "mele" which is an unordered list index in the form
3018             # _ul_I . Find out how many unique ones there are.
3019             #
3020             sub _ic_max_idx {
3021 58     58   82 my($desc) = @_;
3022              
3023 58         66 my %tmp;
3024 58         142 foreach my $mpath (CORE::keys %$desc) {
3025 256         373 my $mele = $$desc{$mpath}{"mele"};
3026 256         475 $tmp{$mele} = 1;
3027             }
3028              
3029 58         170 my @tmp = CORE::keys %tmp;
3030 58         175 return $#tmp;
3031             }
3032              
3033             # This copies all elements from one description hash (%tmpdesc) to a final
3034             # description hash (%desc). Along the way, it substitutes all leading
3035             # unordered list indices (_ul_i) with the current permutation index.
3036             #
3037             # So if the list of indices (@idx) is (0,2,1) and the current list of
3038             # unorderd indices is (_ul_0, _ul_1, _ul_2), then every element containing
3039             # a leading _ul_1 in the mpath will be modified and that element will be
3040             # replaced by "2".
3041             #
3042             sub _ic_permutation {
3043 167     167   326 my($tmpdesc,$desc,@idx) = @_;
3044              
3045             # Get a sorted list of all unordered indices:
3046             # (_ul_0, _ul_1, _ul_2)
3047              
3048 167         180 my(%tmp);
3049 167         535 foreach my $mpath (CORE::keys %$tmpdesc) {
3050 670         1458 my $mele = $$tmpdesc{$mpath}{"mele"};
3051 670         2240 $tmp{$mele} = 1;
3052             }
3053 167         1628 my @tmp = sort(CORE::keys %tmp);
3054              
3055             # Create a hash of unordered list indices and their
3056             # replacement:
3057             # _ul_0 => 0
3058             # _ul_1 => 2
3059             # _ul_2 => 1
3060              
3061 167         346 %tmp = ();
3062 167         374 while (@tmp) {
3063 464         655 my($ul) = shift(@tmp);
3064 464         604 my($idx) = shift(@idx);
3065 464         1335 $tmp{$ul} = $idx;
3066             }
3067              
3068             # Copy the element from %tmpdesc to %desc
3069             # Substitute the unordered list index with the permutation index
3070             # Clear "mele" value
3071             # Decrement "ul" value
3072              
3073 167         440 foreach my $mpath (CORE::keys %$tmpdesc) {
3074 670         1099 my $mele = $$tmpdesc{$mpath}{"mele"};
3075 670         949 my $idx = $tmp{$mele};
3076 670         868 my $newmp = $mpath;
3077 670         7221 $newmp =~ s/$mele/$idx/;
3078              
3079 670         14453 $$desc{$newmp} = dclone($$tmpdesc{$mpath});
3080 670         1689 $$desc{$newmp}{"mpath"} = $newmp;
3081 670         1157 $$desc{$newmp}{"mele"} = "";
3082 670         1968 $$desc{$newmp}{"ul"}--;
3083             }
3084             }
3085              
3086             ###############################################################################
3087             # PRINT
3088             ###############################################################################
3089              
3090             sub print {
3091 0     0 1 0 my($self,$nds,%opts) = @_;
3092 0         0 $nds = _nds($self,$nds,1,0,1);
3093              
3094 0 0       0 if (exists $opts{"indent"}) {
3095 0         0 my $opt = $opts{"indent"};
3096 0 0 0     0 if ($opt !~ /^\d+$/ ||
3097             $opt < 1) {
3098 0         0 warn($self,"Invalid option: indent: $opt",1);
3099 0         0 return;
3100             }
3101             } else {
3102 0         0 $opts{"indent"} = 3;
3103             }
3104              
3105 0 0       0 if (exists $opts{"width"}) {
3106 0         0 my $opt = $opts{"width"};
3107 0 0 0     0 if ($opt !~ /^\d+$/ ||
      0        
3108             ($opt > 0 && $opt < 20)) {
3109 0         0 warn($self,"Invalid option: width: $opt",1);
3110 0         0 return;
3111             }
3112             } else {
3113 0         0 $opts{"width"} = 79;
3114             }
3115              
3116 0 0       0 my $maxlevel = ($opts{"width"} == 0 ? 0 : int( ($opts{"width"} - 10)/
3117             $opts{"indent"} ) + 1);
3118 0 0       0 if (exists $opts{"maxlevel"}) {
3119 0         0 my $opt = $opts{"maxlevel"};
3120 0 0 0     0 if ($maxlevel != 0 && $opt > $maxlevel) {
3121 0         0 warn($self,"Maxlevel exceeded: $opt > $maxlevel",1);
3122 0         0 $opts{"maxlevel"} = $maxlevel;
3123             }
3124             } else {
3125 0         0 $opts{"maxlevel"} = $maxlevel;
3126             }
3127              
3128 0         0 return _print($nds,0,1,%opts);
3129             }
3130              
3131             sub _print {
3132 0     0   0 my($nds,$indent,$level,%opts) = @_;
3133              
3134 0         0 my $string;
3135 0         0 my $indentstr = " "x$indent;
3136 0         0 my $nextindent = $indent + $opts{"indent"};
3137 0 0       0 my $currwidth = ($opts{"width"} == 0 ? 0 : $opts{"width"} - $indent);
3138              
3139 0 0       0 if (ref($nds) eq "HASH") {
    0          
3140             # Print
3141             # key : val val is a scalar, and it fits
3142             # key : ... we're at maxlevel, val is a ref, and ... fits
3143             # key : otherwise
3144             # val
3145              
3146             # Find the length of the longest key
3147 0         0 my @keys = CORE::keys %$nds;
3148 0         0 @keys = sort _sortByLength(@keys);
3149 0         0 my $maxl = length($keys[0]);
3150 0         0 my $keyl = 0;
3151 0         0 my $vall = 0;
3152              
3153             # Find the length that we'll allocate for keys (the rest if
3154             # for values).
3155 0 0 0     0 if ( $currwidth && ($maxl+1) > $currwidth ) {
3156             # keys won't all fit on the line, so truncate them
3157 0         0 $keyl = $currwidth - 1;
3158             } else {
3159 0         0 $keyl = $maxl;
3160 0 0       0 if ($currwidth == 0) {
3161 0         0 $vall = -1;
3162             } else {
3163 0         0 $vall = $currwidth - ($keyl + 2); # key:_ (include a space)
3164 0 0       0 $vall = 0 if ($vall < 0);
3165             }
3166             }
3167              
3168             # Print each key
3169 0         0 foreach my $key (sort @keys) {
3170 0         0 my $val = $$nds{$key};
3171 0 0       0 $val = "undef" if (! defined $val);
3172 0 0 0     0 $val = "''" if (! ref($val) && $val eq "");
3173 0         0 my $k = $key;
3174 0 0       0 if (length($k) > $keyl) {
    0          
3175 0         0 $k = substr($k,0,$keyl);
3176             } elsif (length($k) < $keyl) {
3177 0         0 $k = $k . " "x($keyl - length($k));
3178             }
3179 0         0 $string .= "$indentstr$k" . ":";
3180              
3181 0 0 0     0 if (! ref($val) && ($vall == -1 || length($val) <= $vall)) {
    0 0        
      0        
      0        
      0        
3182 0         0 $string .= " $val\n";
3183              
3184             } elsif (ref($val) &&
3185             $opts{"maxlevel"} == $level &&
3186             ($vall == -1 || $vall > 3)) {
3187 0         0 $string .= " ...\n";
3188              
3189             } else {
3190 0         0 $string .= "\n";
3191 0         0 $string .= _print($val,$nextindent,$level+1,%opts);
3192             }
3193             }
3194              
3195             } elsif (ref($nds) eq "ARRAY") {
3196             # Print each element as:
3197             # 0 = val val is a scalar, and it fits
3198             # 0 = ... we're at maxlevel, val is a ref, and ... fits
3199             # 0 = otherwise
3200             # val
3201              
3202             # Find the length of the longest index
3203 0         0 my $maxl = length($#$nds + 1);
3204 0         0 my $keyl = 0;
3205 0         0 my $vall = 0;
3206              
3207             # Find the length allocated for indices and the rest for values.
3208 0 0       0 if ( ($maxl + 1) > $currwidth ) {
3209             # keys won't all fit on the line, so truncate them
3210 0         0 $keyl = $currwidth - 1;
3211             } else {
3212 0         0 $keyl = $maxl;
3213 0 0       0 if ($currwidth == 0) {
3214 0         0 $vall = -1;
3215             } else {
3216 0         0 $vall = $currwidth - ($keyl + 2); # key:_ (include a space)
3217 0 0       0 $vall = 0 if ($vall < 0);
3218             }
3219             }
3220              
3221             # Print each index
3222 0         0 for (my $key=0; $key <= $#$nds; $key++) {
3223 0         0 my $val = $$nds[$key];
3224 0 0       0 $val = "undef" if (! defined $val);
3225 0 0 0     0 $val = "''" if (! ref($val) && $val eq "");
3226 0         0 my $k = $key;
3227 0 0       0 if (length($k) > $keyl) {
    0          
3228 0         0 $k = substr($k,0,$keyl);
3229             } elsif (length($k) < $keyl) {
3230 0         0 $k = " "x($keyl - length($k)) . $k;
3231             }
3232 0         0 $string .= "$indentstr$k" . "=";
3233              
3234 0 0 0     0 if (! ref($val) && ($vall == -1 || length($val) <= $vall)) {
    0 0        
      0        
      0        
      0        
3235 0         0 $string .= " $val\n";
3236              
3237             } elsif (ref($val) &&
3238             $opts{"maxlevel"} == $level &&
3239             ($vall == -1 || $vall > 3)) {
3240 0         0 $string .= " ...\n";
3241              
3242             } else {
3243 0         0 $string .= "\n";
3244 0         0 $string .= _print($val,$nextindent,$level+1,%opts);
3245             }
3246             }
3247              
3248             } else {
3249 0 0       0 $nds = "undef" if (! defined $nds);
3250 0 0 0     0 $nds = "''" if (! ref($nds) && $nds eq "");
3251              
3252 0 0       0 if (length($nds) > $currwidth) {
3253 0         0 $nds = substr($nds,0,$currwidth-3) . "...";
3254             }
3255 0         0 $string = "$indentstr$nds\n";
3256             }
3257              
3258 0         0 return $string;
3259             }
3260              
3261 93     93   1513 no strict "vars";
  93         227  
  93         7947  
3262             # This sorts from longest to shortest element
3263             sub _sortByLength {
3264 0     0   0 return (length $b <=> length $a);
3265             }
3266 93     93   658 use strict "vars";
  93         224  
  93         40624  
3267              
3268             ###############################################################################
3269             # DEBUG ROUTINES
3270             ###############################################################################
3271              
3272             # Begin a new debugging session.
3273             sub _DBG_begin {
3274 20     20   34 my($function) = @_;
3275 20 50       56 return unless ($_DBG);
3276              
3277 0         0 $_DBG_FH = new IO::File;
3278 0         0 $_DBG_FH->open(">>$_DBG_OUTPUT");
3279 0         0 $_DBG_INDENT = 0;
3280 0         0 $_DBG_POINT = 0;
3281              
3282 0         0 _DBG_line("#"x70);
3283 0         0 _DBG_line("# $function");
3284 0         0 _DBG_line("#"x70);
3285             }
3286              
3287             # End a debugging session.
3288             sub _DBG_end {
3289 20     20   35 my($value) = @_;
3290 20 50       45 return unless ($_DBG);
3291              
3292 0         0 _DBG_line("# Ending: $value");
3293 0         0 $_DBG_FH->close();
3294             }
3295              
3296             # Enter a routine.
3297             sub _DBG_enter {
3298 259     259   528 my($routine) = @_;
3299 259 50       630 return unless ($_DBG);
3300 0         0 $_DBG_POINT++;
3301 0         0 $_DBG_INDENT += 3;
3302              
3303 0         0 _DBG_line("### Entering[$_DBG_POINT]: $routine");
3304             }
3305              
3306             # Leave a routine.
3307             sub _DBG_leave {
3308 259     259   346 my($value) = @_;
3309 259 50       815 return unless ($_DBG);
3310 0           $_DBG_POINT++;
3311              
3312 0           _DBG_line("### Leaving[$_DBG_POINT]: $value");
3313 0           $_DBG_INDENT -= 3;
3314             }
3315              
3316             # Print a debugging line.
3317             sub _DBG_line {
3318 0     0     my($line) = @_;
3319 0           print $_DBG_FH " "x$_DBG_INDENT,$line,"\n";
3320             }
3321              
3322             ###############################################################################
3323             ###############################################################################
3324              
3325             1;
3326             # Local Variables:
3327             # mode: cperl
3328             # indent-tabs-mode: nil
3329             # cperl-indent-level: 3
3330             # cperl-continued-statement-offset: 2
3331             # cperl-continued-brace-offset: 0
3332             # cperl-brace-offset: 0
3333             # cperl-brace-imaginary-offset: 0
3334             # cperl-label-offset: -2
3335             # End: